LibreOffice Module onlineupdate (master) 1
updater.cxx
Go to the documentation of this file.
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
36#include "bspatch.h"
37#include "progressui.h"
38#include "archivereader.h"
39#include "readstrings.h"
40#include "errors.h"
41#include "bzlib.h"
42#include <thread>
43#include <vector>
44
45#include <stdio.h>
46#include <string.h>
47#include <stdlib.h>
48#include <stdarg.h>
49
50#include <sys/types.h>
51#include <sys/stat.h>
52#include <fcntl.h>
53#include <limits.h>
54#include <errno.h>
55#include <algorithm>
56#include <memory>
57
58#include <config_version.h>
59
60#include "updatelogging.h"
61
64
65#ifdef _WIN32
67#include "uachelper.h"
68#include "pathhash.h"
69
70// TODO:moggi taken from the mozilla code -- find a better solution
71#define INVALID_APPLYTO_DIR_ERROR 74
72#define REMOVE_FILE_SPEC_ERROR 71
73#define INVALID_APPLYTO_DIR_STAGED_ERROR 72
74
75#endif
76
77
78// Amount of the progress bar to use in each of the 3 update stages,
79// should total 100.0.
80#define PROGRESS_PREPARE_SIZE 20.0f
81#define PROGRESS_EXECUTE_SIZE 75.0f
82#define PROGRESS_FINISH_SIZE 5.0f
83
84// Amount of time in ms to wait for the parent process to close
85#ifdef _WIN32
86#define PARENT_WAIT 5000
87#endif
88
89#if defined(MACOSX)
90// These functions are defined in launchchild_osx.mm
91void CleanupElevatedMacUpdate(bool aFailureOccurred);
92bool IsOwnedByGroupAdmin(const char* aAppBundle);
93bool IsRecursivelyWritable(const char* aPath);
94void LaunchChild(int argc, const char** argv);
95void LaunchMacPostProcess(const char* aAppBundle);
96bool ObtainUpdaterArguments(int* argc, char*** argv);
97bool ServeElevatedUpdate(int argc, const char** argv);
98void SetGroupOwnershipAndPermissions(const char* aAppBundle);
99struct UpdateServerThreadArgs
100{
101 int argc;
102 const NS_tchar** argv;
103};
104#endif
105
106#ifndef SSIZE_MAX
107# define SSIZE_MAX LONG_MAX
108#endif
109
110// We want to use execv to invoke the callback executable on platforms where
111// we were launched using execv. See nsUpdateDriver.cpp.
112#if defined(UNIX) && !defined(MACOSX)
113#define USE_EXECV
114#endif
115
116#if defined(VERIFY_MAR_SIGNATURE) && !defined(_WIN32) && !defined(MACOSX)
117#include <nss.h>
118#include <nspr.h>
119#endif
120
121#ifdef _WIN32
122#ifdef MAINTENANCE_SERVICE
123#include "registrycertificates.h"
124#endif
125BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra);
126BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer,
127 LPCWSTR siblingFilePath,
128 LPCWSTR newFileName);
129#include "updatehelper.h"
130
131// Closes the handle if valid and if the updater is elevated returns with the
132// return code specified. This prevents multiple launches of the callback
133// application by preventing the elevated process from launching the callback.
134#define EXIT_WHEN_ELEVATED(path, handle, retCode) \
135{ \
136 if (handle != INVALID_HANDLE_VALUE) { \
137 CloseHandle(handle); \
138 } \
139 if (_waccess(path, F_OK) == 0 && NS_tremove(path) != 0) { \
140 LogFinish(); \
141 return retCode; \
142 } \
143}
144#endif
145
146//-----------------------------------------------------------------------------
147
148// This variable lives in libbz2. It's declared in bzlib_private.h, so we just
149// declare it here to avoid including that entire header file.
150#if defined __GNUC__
151extern "C" __attribute__((visibility("default"))) unsigned int BZ2_crc32Table[256];
152#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
153extern "C" __global unsigned int BZ2_crc32Table[256];
154#else
155extern "C" unsigned int BZ2_crc32Table[256];
156#endif
157
158static unsigned int
159crc32(const unsigned char *buf, unsigned int len)
160{
161 unsigned int crc = 0xffffffffL;
162
163 const unsigned char *end = buf + len;
164 for (; buf != end; ++buf)
165 crc = (crc << 8) ^ BZ2_crc32Table[(crc >> 24) ^ *buf];
166
167 crc = ~crc;
168 return crc;
169}
170
171//-----------------------------------------------------------------------------
172
173// A simple stack based container for a FILE struct that closes the
174// file descriptor from its destructor.
176{
177public:
178 explicit AutoFile(FILE* file = nullptr)
179 : mFile(file)
180 {
181 }
182
184 {
185 if (mFile != nullptr)
186 fclose(mFile);
187 }
188
189 AutoFile &operator=(FILE* file)
190 {
191 if (mFile != 0)
192 fclose(mFile);
193 mFile = file;
194 return *this;
195 }
196
197 operator FILE*()
198 {
199 return mFile;
200 }
201
203 {
204 return mFile;
205 }
206
207private:
209};
210
212{
214 {
215 MARChannelID[0] = '\0';
216 }
217
219};
220
221//-----------------------------------------------------------------------------
222
226static bool gSucceeded = false;
227static bool sStagedUpdate = false;
228static bool sReplaceRequest = false;
229static bool sUsingService = false;
230
231#ifdef _WIN32
232// The current working directory specified in the command line.
233static NS_tchar* gDestPath;
234static NS_tchar gCallbackRelPath[MAXPATHLEN];
235static NS_tchar gCallbackBackupPath[MAXPATHLEN];
236static NS_tchar gDeleteDirPath[MAXPATHLEN];
237#endif
238
239static const NS_tchar kWhitespace[] = NS_T(" \t");
240static const NS_tchar kNL[] = NS_T("\r\n");
241static const NS_tchar kQuote[] = NS_T("\"");
242
243static NS_tchar*
244mstrtok(const NS_tchar *delims, NS_tchar **str)
245{
246 if (!*str || !**str)
247 {
248 *str = nullptr;
249 return nullptr;
250 }
251
252 // skip leading "whitespace"
253 NS_tchar *ret = *str;
254 const NS_tchar *d;
255 do
256 {
257 for (d = delims; *d != NS_T('\0'); ++d)
258 {
259 if (*ret == *d)
260 {
261 ++ret;
262 break;
263 }
264 }
265 }
266 while (*d);
267
268 if (!*ret)
269 {
270 *str = ret;
271 return nullptr;
272 }
273
274 NS_tchar *i = ret;
275 do
276 {
277 for (d = delims; *d != NS_T('\0'); ++d)
278 {
279 if (*i == *d)
280 {
281 *i = NS_T('\0');
282 *str = ++i;
283 return ret;
284 }
285 }
286 ++i;
287 }
288 while (*i);
289
290 *str = nullptr;
291 return ret;
292}
293
294#if defined(_WIN32) && defined(MAINTENANCE_SERVICE)
295static bool
296EnvHasValue(const char *name)
297{
298 const char *val = getenv(name);
299 return (val && *val);
300}
301#endif
302
311static NS_tchar*
313{
315 size_t lendestpath = NS_tstrlen(destpath);
316 size_t lenrelpath = NS_tstrlen(relpath);
317 NS_tchar *s = new NS_tchar[lendestpath + lenrelpath + 2];
318
319 NS_tchar *c = s;
320
321 NS_tstrcpy(c, destpath);
322 c += lendestpath;
323 NS_tstrcat(c, NS_T("/"));
324 c++;
325
326 NS_tstrcat(c, relpath);
327 c += lenrelpath;
328 *c = NS_T('\0');
329 return s;
330}
331
332namespace {
333
334bool is_userprofile_in_instdir()
335{
336 return false;
337 /*
338 // the algorithm is:
339 // 1.) if userprofile path length is smaller than installation dir,
340 // the profile is surely not in instdir
341 // 2.) else comparing the two paths looking only at the installation dir
342 // characters should yield an equal string
343 NS_tchar userprofile[MAXPATHLEN];
344 NS_tstrcpy(userprofile, gPatchDirPath);
345 NS_tchar *slash = (NS_tchar *) NS_tstrrchr(userprofile, NS_T('/'));
346 if (slash)
347 *slash = NS_T('\0');
348
349 size_t userprofile_len = NS_tstrlen(userprofile);
350 size_t installdir_len = NS_tstrlen(gInstallDirPath);
351
352 if (userprofile_len < installdir_len)
353 return false;
354
355 return NS_tstrncmp(userprofile, gInstallDirPath, installdir_len) == 0;
356 */
357}
358
359}
360
370static const NS_tchar*
372{
373 // If the path isn't absolute, just return it as-is.
374#ifdef _WIN32
375 if (abs_path[1] != ':' && abs_path[2] != '\\')
376 {
377#else
378 if (abs_path[0] != '/')
379 {
380#endif
381 return abs_path;
382 }
383
385
386 size_t len = NS_tstrlen(prefix);
387 if (NS_tstrlen(abs_path) <= len)
388 return abs_path;
389 if (0 != strncmp(abs_path, prefix, len))
390 return abs_path;
391 return abs_path + len + 1;
392}
393
404static NS_tchar*
405get_valid_path(NS_tchar **line, bool isdir = false)
406{
407 NS_tchar *path = mstrtok(kQuote, line);
408 if (!path)
409 {
410 LOG(("get_valid_path: unable to determine path: " LOG_S, line));
411 return nullptr;
412 }
413
414 // All paths must be relative from the current working directory
415 if (path[0] == NS_T('/'))
416 {
417 LOG(("get_valid_path: path must be relative: " LOG_S, path));
418 return nullptr;
419 }
420
421#ifdef _WIN32
422 // All paths must be relative from the current working directory
423 if (path[0] == NS_T('\\') || path[1] == NS_T(':'))
424 {
425 LOG(("get_valid_path: path must be relative: " LOG_S, path));
426 return nullptr;
427 }
428#endif
429
430 if (isdir)
431 {
432 // Directory paths must have a trailing forward slash.
433 if (path[NS_tstrlen(path) - 1] != NS_T('/'))
434 {
435 LOG(("get_valid_path: directory paths must have a trailing forward " \
436 "slash: " LOG_S, path));
437 return nullptr;
438 }
439
440 // Remove the trailing forward slash because stat on Windows will return
441 // ENOENT if the path has a trailing slash.
442 path[NS_tstrlen(path) - 1] = NS_T('\0');
443 }
444
445 // Don't allow relative paths that resolve to a parent directory.
446 if (NS_tstrstr(path, NS_T("..")) != nullptr)
447 {
448 LOG(("get_valid_path: paths must not contain '..': " LOG_S, path));
449 return nullptr;
450 }
451
452 return path;
453}
454
455static NS_tchar*
457{
458 size_t lenQuote = NS_tstrlen(kQuote);
459 size_t lenPath = NS_tstrlen(path);
460 size_t len = lenQuote + lenPath + lenQuote + 1;
461
462 NS_tchar *s = (NS_tchar *) malloc(len * sizeof(NS_tchar));
463 if (!s)
464 return nullptr;
465
466 NS_tchar *c = s;
467 NS_tstrcpy(c, kQuote);
468 c += lenQuote;
469 NS_tstrcat(c, path);
470 c += lenPath;
471 NS_tstrcat(c, kQuote);
472 c += lenQuote;
473 *c = NS_T('\0');
474 c++;
475 return s;
476}
477
478static void ensure_write_permissions(const NS_tchar *path)
479{
480#ifdef _WIN32
481 (void) _wchmod(path, _S_IREAD | _S_IWRITE);
482#else
483 struct stat fs;
484 if (!stat(path, &fs) && !(fs.st_mode & S_IWUSR))
485 {
486 (void)chmod(path, fs.st_mode | S_IWUSR);
487 }
488#endif
489}
490
491static int ensure_remove(const NS_tchar *path)
492{
494 int rv = NS_tremove(path);
495 if (rv)
496 LOG(("ensure_remove: failed to remove file: " LOG_S ", rv: %d, err: %d",
497 path, rv, errno));
498 return rv;
499}
500
501// Remove the directory pointed to by path and all of its files and sub-directories.
502static int ensure_remove_recursive(const NS_tchar *path,
503 bool continueEnumOnFailure = false)
504{
505 // We use lstat rather than stat here so that we can successfully remove
506 // symlinks.
507 struct NS_tstat_t sInfo;
508 int rv = NS_tlstat(path, &sInfo);
509 if (rv)
510 {
511 // This error is benign
512 return rv;
513 }
514 if (!S_ISDIR(sInfo.st_mode))
515 {
516 return ensure_remove(path);
517 }
518
519 NS_tDIR *dir;
520 NS_tdirent *entry;
521
522 dir = NS_topendir(path);
523 if (!dir)
524 {
525 LOG(("ensure_remove_recursive: unable to open directory: " LOG_S
526 ", rv: %d, err: %d", path, rv, errno));
527 return rv;
528 }
529
530 while ((entry = NS_treaddir(dir)) != 0)
531 {
532 if (NS_tstrcmp(entry->d_name, NS_T(".")) &&
533 NS_tstrcmp(entry->d_name, NS_T("..")))
534 {
535 NS_tchar childPath[MAXPATHLEN];
536 NS_tsnprintf(childPath, sizeof(childPath)/sizeof(childPath[0]),
537 NS_T("%s/%s"), path, entry->d_name);
538 rv = ensure_remove_recursive(childPath);
539 if (rv && !continueEnumOnFailure)
540 {
541 break;
542 }
543 }
544 }
545
546 NS_tclosedir(dir);
547
548 if (rv == OK)
549 {
551 rv = NS_trmdir(path);
552 if (rv)
553 {
554 LOG(("ensure_remove_recursive: unable to remove directory: " LOG_S
555 ", rv: %d, err: %d", path, rv, errno));
556 }
557 }
558 return rv;
559}
560
561static bool is_read_only(const NS_tchar *flags)
562{
563 size_t length = NS_tstrlen(flags);
564 if (length == 0)
565 return false;
566
567 // Make sure the string begins with "r"
568 if (flags[0] != NS_T('r'))
569 return false;
570
571 // Look for "r+" or "r+b"
572 if (length > 1 && flags[1] == NS_T('+'))
573 return false;
574
575 // Look for "rb+"
576 if (NS_tstrcmp(flags, NS_T("rb+")) == 0)
577 return false;
578
579 return true;
580}
581
582static FILE* ensure_open(const NS_tchar *path, const NS_tchar *flags, unsigned int options)
583{
585 FILE* f = NS_tfopen(path, flags);
586 if (is_read_only(flags))
587 {
588 // Don't attempt to modify the file permissions if the file is being opened
589 // in read-only mode.
590 return f;
591 }
592 if (NS_tchmod(path, options) != 0)
593 {
594 if (f != nullptr)
595 {
596 fclose(f);
597 }
598 return nullptr;
599 }
600 struct NS_tstat_t ss;
601 if (NS_tstat(path, &ss) != 0 || ss.st_mode != options)
602 {
603 if (f != nullptr)
604 {
605 fclose(f);
606 }
607 return nullptr;
608 }
609 return f;
610}
611
612// Ensure that the directory containing this file exists.
613static int ensure_parent_dir(const NS_tchar *path)
614{
615 int rv = OK;
616
617 NS_tchar *slash = (NS_tchar *) NS_tstrrchr(path, NS_T('/'));
618 if (slash)
619 {
620 *slash = NS_T('\0');
621 rv = ensure_parent_dir(path);
622 // Only attempt to create the directory if we're not at the root
623 if (rv == OK && *path)
624 {
625 rv = NS_tmkdir(path, 0755);
626 // If the directory already exists, then ignore the error.
627 if (rv < 0 && errno != EEXIST)
628 {
629 LOG(("ensure_parent_dir: failed to create directory: " LOG_S ", " \
630 "err: %d", path, errno));
631 rv = WRITE_ERROR;
632 }
633 else
634 {
635 rv = OK;
636 }
637 }
638 *slash = NS_T('/');
639 }
640 return rv;
641}
642
643#ifdef UNIX
644static int ensure_copy_symlink(const NS_tchar *path, const NS_tchar *dest)
645{
646 // Copy symlinks by creating a new symlink to the same target
647 NS_tchar target[MAXPATHLEN + 1] = {NS_T('\0')};
648 int rv = readlink(path, target, MAXPATHLEN);
649 if (rv == -1)
650 {
651 LOG(("ensure_copy_symlink: failed to read the link: " LOG_S ", err: %d",
652 path, errno));
653 return READ_ERROR;
654 }
655 rv = symlink(target, dest);
656 if (rv == -1)
657 {
658 LOG(("ensure_copy_symlink: failed to create the new link: " LOG_S ", target: " LOG_S " err: %d",
659 dest, target, errno));
660 return READ_ERROR;
661 }
662 return 0;
663}
664#endif
665
666// Copy the file named path onto a new file named dest.
667static int ensure_copy(const NS_tchar *path, const NS_tchar *dest)
668{
669#ifdef _WIN32
670 // Fast path for Windows
671 bool result = CopyFileW(path, dest, false);
672 if (!result)
673 {
674 LOG(("ensure_copy: failed to copy the file " LOG_S " over to " LOG_S ", lasterr: %x",
675 path, dest, GetLastError()));
677 }
678 return OK;
679#else
680 struct NS_tstat_t ss;
681 int rv = NS_tlstat(path, &ss);
682 if (rv)
683 {
684 LOG(("ensure_copy: failed to read file status info: " LOG_S ", err: %d",
685 path, errno));
686 return READ_ERROR;
687 }
688
689#ifdef UNIX
690 if (S_ISLNK(ss.st_mode))
691 {
692 return ensure_copy_symlink(path, dest);
693 }
694#endif
695
696 AutoFile infile(ensure_open(path, NS_T("rb"), ss.st_mode));
697 if (!infile)
698 {
699 LOG(("ensure_copy: failed to open the file for reading: " LOG_S ", err: %d",
700 path, errno));
701 return READ_ERROR;
702 }
703 AutoFile outfile(ensure_open(dest, NS_T("wb"), ss.st_mode));
704 if (!outfile)
705 {
706 LOG(("ensure_copy: failed to open the file for writing: " LOG_S ", err: %d",
707 dest, errno));
708 return WRITE_ERROR;
709 }
710
711 // This block size was chosen pretty arbitrarily but seems like a reasonable
712 // compromise. For example, the optimal block size on a modern macOS machine
713 // is 100k */
714 const int blockSize = 32 * 1024;
715 void* buffer = malloc(blockSize);
716 if (!buffer)
717 return UPDATER_MEM_ERROR;
718
719 while (!feof(infile.get()))
720 {
721 size_t read = fread(buffer, 1, blockSize, infile);
722 if (ferror(infile.get()))
723 {
724 LOG(("ensure_copy: failed to read the file: " LOG_S ", err: %d",
725 path, errno));
726 free(buffer);
727 return READ_ERROR;
728 }
729
730 size_t written = 0;
731
732 while (written < read)
733 {
734 size_t nCount = read - written;
735 size_t chunkWritten = fwrite(buffer, 1, nCount, outfile);
736 if (chunkWritten != nCount)
737 {
738 LOG(("ensure_copy: failed to write the file: " LOG_S ", err: %d",
739 dest, errno));
740 free(buffer);
742 }
743
744 written += chunkWritten;
745 }
746 }
747
748 rv = NS_tchmod(dest, ss.st_mode);
749
750 free(buffer);
751 return rv;
752#endif
753}
754
755template <unsigned N>
757{
759
760 void append(unsigned index, const NS_tchar *path, const NS_tchar *suffix)
761 {
762 NS_tsnprintf(paths[index], MAXPATHLEN, NS_T("%s/%s"), path, suffix);
763 }
764
765 void append(unsigned index, const NS_tchar* path)
766 {
767 NS_tstrcpy(paths[index], path);
768 }
769
770 bool find(const NS_tchar *path)
771 {
772 for (int i = 0; i < static_cast<int>(N); ++i)
773 {
774 if (!NS_tstricmp(paths[i], path))
775 {
776 return true;
777 }
778 }
779 return false;
780 }
781};
782
783// Copy all of the files and subdirectories under path to a new directory named dest.
784// The path names in the skiplist will be skipped and will not be copied.
785template <unsigned N>
786static int ensure_copy_recursive(const NS_tchar *path, const NS_tchar *dest,
788{
789 struct NS_tstat_t sInfo;
790 int rv = NS_tlstat(path, &sInfo);
791 if (rv)
792 {
793 LOG(("ensure_copy_recursive: path doesn't exist: " LOG_S ", rv: %d, err: %d",
794 path, rv, errno));
795 return READ_ERROR;
796 }
797
798#ifdef UNIX
799 if (S_ISLNK(sInfo.st_mode))
800 {
801 return ensure_copy_symlink(path, dest);
802 }
803#endif
804
805 if (!S_ISDIR(sInfo.st_mode))
806 {
807 return ensure_copy(path, dest);
808 }
809
810 rv = NS_tmkdir(dest, sInfo.st_mode);
811 if (rv < 0 && errno != EEXIST)
812 {
813 LOG(("ensure_copy_recursive: could not create destination directory: " LOG_S ", rv: %d, err: %d",
814 path, rv, errno));
815 return WRITE_ERROR;
816 }
817
818 NS_tDIR *dir;
819 NS_tdirent *entry;
820
821 dir = NS_topendir(path);
822 if (!dir)
823 {
824 LOG(("ensure_copy_recursive: path is not a directory: " LOG_S ", rv: %d, err: %d",
825 path, rv, errno));
826 return READ_ERROR;
827 }
828
829 while ((entry = NS_treaddir(dir)) != 0)
830 {
831 if (NS_tstrcmp(entry->d_name, NS_T(".")) &&
832 NS_tstrcmp(entry->d_name, NS_T("..")))
833 {
834 NS_tchar childPath[MAXPATHLEN];
835 NS_tsnprintf(childPath, sizeof(childPath)/sizeof(childPath[0]),
836 NS_T("%s/%s"), path, entry->d_name);
837 if (skiplist.find(childPath))
838 {
839 continue;
840 }
841 NS_tchar childPathDest[MAXPATHLEN];
842 NS_tsnprintf(childPathDest, sizeof(childPathDest)/sizeof(childPathDest[0]),
843 NS_T("%s/%s"), dest, entry->d_name);
844 rv = ensure_copy_recursive(childPath, childPathDest, skiplist);
845 if (rv)
846 {
847 break;
848 }
849 }
850 }
851 NS_tclosedir(dir);
852 return rv;
853}
854
855// Renames the specified file to the new file specified. If the destination file
856// exists it is removed.
857static int rename_file(const NS_tchar *spath, const NS_tchar *dpath,
858 bool allowDirs = false)
859{
860 int rv = ensure_parent_dir(dpath);
861 if (rv)
862 return rv;
863
864 struct NS_tstat_t spathInfo;
865 rv = NS_tstat(spath, &spathInfo);
866 if (rv)
867 {
868 LOG(("rename_file: failed to read file status info: " LOG_S ", " \
869 "err: %d", spath, errno));
870 return READ_ERROR;
871 }
872
873 if (!S_ISREG(spathInfo.st_mode))
874 {
875 if (allowDirs && !S_ISDIR(spathInfo.st_mode))
876 {
877 LOG(("rename_file: path present, but not a file: " LOG_S ", err: %d",
878 spath, errno));
880 }
881 else
882 {
883 LOG(("rename_file: proceeding to rename the directory"));
884 }
885 }
886
887 if (!NS_taccess(dpath, F_OK))
888 {
889 if (ensure_remove(dpath))
890 {
891 LOG(("rename_file: destination file exists and could not be " \
892 "removed: " LOG_S, dpath));
894 }
895 }
896
897 if (NS_trename(spath, dpath) != 0)
898 {
899 LOG(("rename_file: failed to rename file - src: " LOG_S ", " \
900 "dst:" LOG_S ", err: %d", spath, dpath, errno));
901 return WRITE_ERROR;
902 }
903
904 return OK;
905}
906
907#ifdef _WIN32
908// Remove the directory pointed to by path and all of its files and
909// sub-directories. If a file is in use move it to the tobedeleted directory
910// and attempt to schedule removal of the file on reboot
911static int remove_recursive_on_reboot(const NS_tchar *path, const NS_tchar *deleteDir)
912{
913 struct NS_tstat_t sInfo;
914 int rv = NS_tlstat(path, &sInfo);
915 if (rv)
916 {
917 // This error is benign
918 return rv;
919 }
920
921 if (!S_ISDIR(sInfo.st_mode))
922 {
923 NS_tchar tmpDeleteFile[MAXPATHLEN];
924 GetTempFileNameW(deleteDir, L"rep", 0, tmpDeleteFile);
925 NS_tremove(tmpDeleteFile);
926 rv = rename_file(path, tmpDeleteFile, false);
927 if (MoveFileEx(rv ? path : tmpDeleteFile, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT))
928 {
929 LOG(("remove_recursive_on_reboot: file will be removed on OS reboot: "
930 LOG_S, rv ? path : tmpDeleteFile));
931 }
932 else
933 {
934 LOG(("remove_recursive_on_reboot: failed to schedule OS reboot removal of "
935 "file: " LOG_S, rv ? path : tmpDeleteFile));
936 }
937 return rv;
938 }
939
940 NS_tDIR *dir;
941 NS_tdirent *entry;
942
943 dir = NS_topendir(path);
944 if (!dir)
945 {
946 LOG(("remove_recursive_on_reboot: unable to open directory: " LOG_S
947 ", rv: %d, err: %d",
948 path, rv, errno));
949 return rv;
950 }
951
952 while ((entry = NS_treaddir(dir)) != 0)
953 {
954 if (NS_tstrcmp(entry->d_name, NS_T(".")) &&
955 NS_tstrcmp(entry->d_name, NS_T("..")))
956 {
957 NS_tchar childPath[MAXPATHLEN];
958 NS_tsnprintf(childPath, sizeof(childPath)/sizeof(childPath[0]),
959 NS_T("%s/%s"), path, entry->d_name);
960 // There is no need to check the return value of this call since this
961 // function is only called after an update is successful and there is not
962 // much that can be done to recover if it isn't successful. There is also
963 // no need to log the value since it will have already been logged.
964 remove_recursive_on_reboot(childPath, deleteDir);
965 }
966 }
967
968 NS_tclosedir(dir);
969
970 if (rv == OK)
971 {
973 rv = NS_trmdir(path);
974 if (rv)
975 {
976 LOG(("remove_recursive_on_reboot: unable to remove directory: " LOG_S
977 ", rv: %d, err: %d", path, rv, errno));
978 }
979 }
980 return rv;
981}
982#endif
983
984//-----------------------------------------------------------------------------
985
986// Create a backup of the specified file by renaming it.
987static int backup_create(const NS_tchar *path)
988{
989 NS_tchar backup[MAXPATHLEN];
990 NS_tsnprintf(backup, sizeof(backup)/sizeof(backup[0]),
991 NS_T("%s") BACKUP_EXT, path);
992
993 return rename_file(path, backup);
994}
995
996// Rename the backup of the specified file that was created by renaming it back
997// to the original file.
998static int backup_restore(const NS_tchar *path, const NS_tchar *relPath)
999{
1000 NS_tchar backup[MAXPATHLEN];
1001 NS_tsnprintf(backup, sizeof(backup)/sizeof(backup[0]),
1002 NS_T("%s") BACKUP_EXT, path);
1003
1004 NS_tchar relBackup[MAXPATHLEN];
1005 NS_tsnprintf(relBackup, sizeof(relBackup) / sizeof(relBackup[0]),
1006 NS_T("%s") BACKUP_EXT, relPath);
1007
1008 if (NS_taccess(backup, F_OK))
1009 {
1010 LOG(("backup_restore: backup file doesn't exist: " LOG_S, relBackup));
1011 return OK;
1012 }
1013
1014 return rename_file(backup, path);
1015}
1016
1017// Discard the backup of the specified file that was created by renaming it.
1018static int backup_discard(const NS_tchar *path, const NS_tchar *relPath)
1019{
1020 NS_tchar backup[MAXPATHLEN];
1021 NS_tsnprintf(backup, sizeof(backup)/sizeof(backup[0]),
1022 NS_T("%s") BACKUP_EXT, path);
1023
1024 NS_tchar relBackup[MAXPATHLEN];
1025 NS_tsnprintf(relBackup, sizeof(relBackup) / sizeof(relBackup[0]),
1026 NS_T("%s") BACKUP_EXT, relPath);
1027
1028 // Nothing to discard
1029 if (NS_taccess(backup, F_OK))
1030 {
1031 return OK;
1032 }
1033
1034 int rv = ensure_remove(backup);
1035#if defined(_WIN32)
1036 if (rv && !sStagedUpdate && !sReplaceRequest)
1037 {
1038 LOG(("backup_discard: unable to remove: " LOG_S, relBackup));
1039 NS_tchar path[MAXPATHLEN];
1040 GetTempFileNameW(gDeleteDirPath, L"moz", 0, path);
1041 if (rename_file(backup, path))
1042 {
1043 LOG(("backup_discard: failed to rename file:" LOG_S ", dst:" LOG_S,
1044 relBackup, relPath));
1046 }
1047 // The MoveFileEx call to remove the file on OS reboot will fail if the
1048 // process doesn't have write access to the HKEY_LOCAL_MACHINE registry key
1049 // but this is ok since the installer / uninstaller will delete the
1050 // directory containing the file along with its contents after an update is
1051 // applied, on reinstall, and on uninstall.
1052 if (MoveFileEx(path, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT))
1053 {
1054 LOG(("backup_discard: file renamed and will be removed on OS " \
1055 "reboot: " LOG_S, relPath));
1056 }
1057 else
1058 {
1059 LOG(("backup_discard: failed to schedule OS reboot removal of " \
1060 "file: " LOG_S, relPath));
1061 }
1062 }
1063#else
1064 if (rv)
1066#endif
1067
1068 return OK;
1069}
1070
1071// Helper function for post-processing a temporary backup.
1072static void backup_finish(const NS_tchar *path, const NS_tchar *relPath,
1073 int status)
1074{
1075 if (status == OK)
1076 backup_discard(path, relPath);
1077 else
1078 backup_restore(path, relPath);
1079}
1080
1081//-----------------------------------------------------------------------------
1082
1084
1086{
1087public:
1088 Action() : mProgressCost(1), mNext(nullptr) { }
1089 virtual ~Action() { }
1090
1091 virtual int Parse(NS_tchar *line) = 0;
1092
1093 // Do any preprocessing to ensure that the action can be performed. Execute
1094 // will be called if this Action and all others return OK from this method.
1095 virtual int Prepare() = 0;
1096
1097 // Perform the operation. Return OK to indicate success. After all actions
1098 // have been executed, Finish will be called. A requirement of Execute is
1099 // that its operation be reversible from Finish.
1100 virtual int Execute() = 0;
1101
1102 // Finish is called after execution of all actions. If status is OK, then
1103 // all actions were successfully executed. Otherwise, some action failed.
1104 virtual void Finish(int status) = 0;
1105
1107private:
1109
1110 friend class ActionList;
1111};
1112
1113class RemoveFile : public Action
1114{
1115public:
1117
1118 int Parse(NS_tchar *line);
1119 int Prepare();
1120 int Execute();
1121 void Finish(int status);
1122
1123private:
1124 std::unique_ptr<const NS_tchar[]> mFile;
1125 std::unique_ptr<NS_tchar[]> mRelPath;
1127};
1128
1129int
1131{
1132 // format "<deadfile>"
1133
1134 NS_tchar* validPath = get_valid_path(&line);
1135 if (!validPath)
1136 return PARSE_ERROR;
1137
1138 mRelPath.reset(new NS_tchar[MAXPATHLEN]);
1139 NS_tstrcpy(mRelPath.get(), validPath);
1140
1141 mFile.reset(new_absolute_path(validPath));
1142 if (!mFile)
1143 {
1144 return PARSE_ERROR;
1145 }
1146
1147 return OK;
1148}
1149
1150int
1152{
1153 // Skip the file if it already doesn't exist.
1154 int rv = NS_taccess(mFile.get(), F_OK);
1155 if (rv)
1156 {
1157 mSkip = 1;
1158 mProgressCost = 0;
1159 return OK;
1160 }
1161
1162 LOG(("PREPARE REMOVEFILE " LOG_S, mRelPath.get()));
1163
1164 // Make sure that we're actually a file...
1165 struct NS_tstat_t fileInfo;
1166 rv = NS_tstat(mFile.get(), &fileInfo);
1167 if (rv)
1168 {
1169 LOG(("failed to read file status info: " LOG_S ", err: %d", mFile.get(),
1170 errno));
1171 return READ_ERROR;
1172 }
1173
1174 if (!S_ISREG(fileInfo.st_mode))
1175 {
1176 LOG(("path present, but not a file: " LOG_S, mFile.get()));
1178 }
1179
1180 NS_tchar *slash = (NS_tchar *) NS_tstrrchr(mFile.get(), NS_T('/'));
1181 if (slash)
1182 {
1183 *slash = NS_T('\0');
1184 rv = NS_taccess(mFile.get(), W_OK);
1185 *slash = NS_T('/');
1186 }
1187 else
1188 {
1189 rv = NS_taccess(NS_T("."), W_OK);
1190 }
1191
1192 if (rv)
1193 {
1194 LOG(("access failed: %d", errno));
1196 }
1197
1198 return OK;
1199}
1200
1201int
1203{
1204 if (mSkip)
1205 return OK;
1206
1207 LOG(("EXECUTE REMOVEFILE " LOG_S, mRelPath.get()));
1208
1209 // The file is checked for existence here and in Prepare since it might have
1210 // been removed by a separate instruction: bug 311099.
1211 int rv = NS_taccess(mFile.get(), F_OK);
1212 if (rv)
1213 {
1214 LOG(("file cannot be removed because it does not exist; skipping"));
1215 mSkip = 1;
1216 return OK;
1217 }
1218
1219 // Rename the old file. It will be removed in Finish.
1220 rv = backup_create(mFile.get());
1221 if (rv)
1222 {
1223 LOG(("backup_create failed: %d", rv));
1224 return rv;
1225 }
1226
1227 return OK;
1228}
1229
1230void
1232{
1233 if (mSkip)
1234 return;
1235
1236 LOG(("FINISH REMOVEFILE " LOG_S, mRelPath.get()));
1237
1238 backup_finish(mFile.get(), mRelPath.get(), status);
1239}
1240
1241class RemoveDir : public Action
1242{
1243public:
1244 RemoveDir() : mSkip(0) { }
1245
1246 virtual int Parse(NS_tchar *line);
1247 virtual int Prepare(); // check that the source dir exists
1248 virtual int Execute();
1249 virtual void Finish(int status);
1250
1251private:
1252 std::unique_ptr<NS_tchar[]> mDir;
1253 std::unique_ptr<NS_tchar[]> mRelPath;
1255};
1256
1257int
1259{
1260 // format "<deaddir>/"
1261
1262 NS_tchar* validPath = get_valid_path(&line, true);
1263 if (!validPath)
1264 return PARSE_ERROR;
1265
1266 mRelPath.reset(new NS_tchar[MAXPATHLEN]);
1267 NS_tstrcpy(mRelPath.get(), validPath);
1268
1269 mDir.reset(new_absolute_path(validPath));
1270 if (!mDir)
1271 {
1272 return PARSE_ERROR;
1273 }
1274
1275 return OK;
1276}
1277
1278int
1280{
1281 // We expect the directory to exist if we are to remove it.
1282 int rv = NS_taccess(mDir.get(), F_OK);
1283 if (rv)
1284 {
1285 mSkip = 1;
1286 mProgressCost = 0;
1287 return OK;
1288 }
1289
1290 LOG(("PREPARE REMOVEDIR " LOG_S "/", mRelPath.get()));
1291
1292 // Make sure that we're actually a dir.
1293 struct NS_tstat_t dirInfo;
1294 rv = NS_tstat(mDir.get(), &dirInfo);
1295 if (rv)
1296 {
1297 LOG(("failed to read directory status info: " LOG_S ", err: %d", mRelPath.get(),
1298 errno));
1299 return READ_ERROR;
1300 }
1301
1302 if (!S_ISDIR(dirInfo.st_mode))
1303 {
1304 LOG(("path present, but not a directory: " LOG_S, mRelPath.get()));
1306 }
1307
1308 rv = NS_taccess(mDir.get(), W_OK);
1309 if (rv)
1310 {
1311 LOG(("access failed: %d, %d", rv, errno));
1313 }
1314
1315 return OK;
1316}
1317
1318int
1320{
1321 if (mSkip)
1322 return OK;
1323
1324 LOG(("EXECUTE REMOVEDIR " LOG_S "/", mRelPath.get()));
1325
1326 // The directory is checked for existence at every step since it might have
1327 // been removed by a separate instruction: bug 311099.
1328 int rv = NS_taccess(mDir.get(), F_OK);
1329 if (rv)
1330 {
1331 LOG(("directory no longer exists; skipping"));
1332 mSkip = 1;
1333 }
1334
1335 return OK;
1336}
1337
1338void
1340{
1341 if (mSkip || status != OK)
1342 return;
1343
1344 LOG(("FINISH REMOVEDIR " LOG_S "/", mRelPath.get()));
1345
1346 // The directory is checked for existence at every step since it might have
1347 // been removed by a separate instruction: bug 311099.
1348 int rv = NS_taccess(mDir.get(), F_OK);
1349 if (rv)
1350 {
1351 LOG(("directory no longer exists; skipping"));
1352 return;
1353 }
1354
1355
1356 if (status == OK)
1357 {
1358 if (NS_trmdir(mDir.get()))
1359 {
1360 LOG(("non-fatal error removing directory: " LOG_S "/, rv: %d, err: %d",
1361 mRelPath.get(), rv, errno));
1362 }
1363 }
1364}
1365
1366class AddFile : public Action
1367{
1368public:
1370
1371 virtual int Parse(NS_tchar *line);
1372 virtual int Prepare();
1373 virtual int Execute();
1374 virtual void Finish(int status);
1375
1376private:
1377 std::unique_ptr<NS_tchar[]> mFile;
1378 std::unique_ptr<NS_tchar[]> mRelPath;
1381};
1382
1383int
1385{
1386 // format "<newfile>"
1387
1388 NS_tchar* validPath = get_valid_path(&line);
1389 if (!validPath)
1390 return PARSE_ERROR;
1391
1392 mRelPath.reset(new NS_tchar[MAXPATHLEN]);
1393 NS_tstrcpy(mRelPath.get(), validPath);
1394
1395 mFile.reset(new_absolute_path(validPath));
1396 if (!mFile)
1397 {
1398 return PARSE_ERROR;
1399 }
1400
1401 return OK;
1402}
1403
1404int
1406{
1407 LOG(("PREPARE ADD " LOG_S, mRelPath.get()));
1408
1409 return OK;
1410}
1411
1412int
1414{
1415 LOG(("EXECUTE ADD " LOG_S, mRelPath.get()));
1416
1417 int rv;
1418
1419 // First make sure that we can actually get rid of any existing file.
1420 rv = NS_taccess(mFile.get(), F_OK);
1421 if (rv == 0)
1422 {
1423 rv = backup_create(mFile.get());
1424 if (rv)
1425 return rv;
1426 }
1427 else
1428 {
1429 rv = ensure_parent_dir(mFile.get());
1430 if (rv)
1431 return rv;
1432 }
1433
1434#ifdef _WIN32
1435 char sourcefile[MAXPATHLEN];
1436 if (!WideCharToMultiByte(CP_UTF8, 0, mRelPath.get(), -1, sourcefile,
1437 MAXPATHLEN, nullptr, nullptr))
1438 {
1439 LOG(("error converting wchar to utf8: %d", GetLastError()));
1441 }
1442
1443 rv = mArchiveReader.ExtractFile(sourcefile, mFile.get());
1444#else
1445 rv = mArchiveReader.ExtractFile(mRelPath.get(), mFile.get());
1446#endif
1447 if (!rv)
1448 {
1449 mAdded = true;
1450 }
1451 return rv;
1452}
1453
1454void
1456{
1457 LOG(("FINISH ADD " LOG_S, mRelPath.get()));
1458 // When there is an update failure and a file has been added it is removed
1459 // here since there might not be a backup to replace it.
1460 if (status && mAdded)
1461 NS_tremove(mFile.get());
1462 backup_finish(mFile.get(), mRelPath.get(), status);
1463}
1464
1465class PatchFile : public Action
1466{
1467public:
1468 PatchFile(ArchiveReader& ar) : mPatchFile(nullptr), mPatchIndex(-1), buf(nullptr), mArchiveReader(ar) { }
1469
1470 virtual ~PatchFile();
1471
1472 virtual int Parse(NS_tchar *line);
1473 virtual int Prepare(); // should check for patch file and for checksum here
1474 virtual int Execute();
1475 virtual void Finish(int status);
1476
1477private:
1478 int LoadSourceFile(FILE* ofile);
1479
1480 static int sPatchIndex;
1481
1483 std::unique_ptr<NS_tchar> mFile;
1484 std::unique_ptr<NS_tchar> mFileRelPath;
1487 unsigned char *buf;
1491};
1492
1494
1496{
1497 // Make sure mPatchStream gets unlocked on Windows; the system will do that,
1498 // but not until some indeterminate future time, and we want determinism.
1499 // Normally this happens at the end of Execute, when we close the stream;
1500 // this call is here in case Execute errors out.
1501#ifdef _WIN32
1502 if (mPatchStream)
1503 {
1504 UnlockFile((HANDLE)_get_osfhandle(fileno(mPatchStream)), (DWORD)0, (DWORD)0, (DWORD)-1, (DWORD)-1);
1505 }
1506#endif
1507
1508 // delete the temporary patch file
1509 if (spath[0])
1511
1512 if (buf)
1513 free(buf);
1514}
1515
1516int
1518{
1519 struct stat os;
1520 int rv = fstat(fileno((FILE *)ofile), &os);
1521 if (rv)
1522 {
1523 LOG(("LoadSourceFile: unable to stat destination file: " LOG_S ", " \
1524 "err: %d", mFileRelPath.get(), errno));
1525 return READ_ERROR;
1526 }
1527
1528 if (uint32_t(os.st_size) != header.slen)
1529 {
1530 LOG(("LoadSourceFile: destination file size %d does not match expected size %d",
1531 uint32_t(os.st_size), header.slen));
1533 }
1534
1535 buf = (unsigned char *) malloc(header.slen);
1536 if (!buf)
1537 return UPDATER_MEM_ERROR;
1538
1539 size_t r = header.slen;
1540 unsigned char *rb = buf;
1541 while (r)
1542 {
1543 const size_t count = std::min<size_t>(SSIZE_MAX, r);
1544 size_t c = fread(rb, 1, count, ofile);
1545 if (c != count)
1546 {
1547 LOG(("LoadSourceFile: error reading destination file: " LOG_S,
1548 mFileRelPath.get()));
1549 return READ_ERROR;
1550 }
1551
1552 r -= c;
1553 rb += c;
1554 }
1555
1556 // Verify that the contents of the source file correspond to what we expect.
1557
1558 unsigned int crc = crc32(buf, header.slen);
1559
1560 if (crc != header.scrc32)
1561 {
1562 LOG(("LoadSourceFile: destination file crc %d does not match expected " \
1563 "crc %d", crc, header.scrc32));
1564 return CRC_ERROR;
1565 }
1566
1567 return OK;
1568}
1569
1570int
1572{
1573 // format "<patchfile>" "<filetopatch>"
1574
1575 // Get the path to the patch file inside of the mar
1577 if (!mPatchFile)
1578 return PARSE_ERROR;
1579
1580 // consume whitespace between args
1581 NS_tchar *q = mstrtok(kQuote, &line);
1582 if (!q)
1583 return PARSE_ERROR;
1584
1585 NS_tchar* validPath = get_valid_path(&line);
1586 if (!validPath)
1587 return PARSE_ERROR;
1588 mFileRelPath.reset(new NS_tchar[MAXPATHLEN]);
1589 NS_tstrcpy(mFileRelPath.get(), validPath);
1590
1591 mFile.reset(new_absolute_path(validPath));
1592 if (!mFile)
1593 {
1594 return PARSE_ERROR;
1595 }
1596
1597 return OK;
1598}
1599
1600int
1602{
1603 LOG(("PREPARE PATCH " LOG_S, mFileRelPath.get()));
1604
1605 // extract the patch to a temporary file
1607
1608 int nWrittenBytes = NS_tsnprintf(spath, sizeof(spath)/sizeof(spath[0]),
1609 NS_T("%s/updating/%d.patch"), gWorkingDirPath, mPatchIndex);
1610 (void) nWrittenBytes;
1611
1613
1614 mPatchStream = NS_tfopen(spath, NS_T("wb+"));
1615 if (!mPatchStream)
1616 return WRITE_ERROR;
1617
1618#ifdef _WIN32
1619 // Lock the patch file, so it can't be messed with between
1620 // when we're done creating it and when we go to apply it.
1621 if (!LockFile((HANDLE)_get_osfhandle(fileno(mPatchStream)), (DWORD)0, (DWORD)0, (DWORD)-1, (DWORD)-1))
1622 {
1623 LOG(("Couldn't lock patch file: %d", GetLastError()));
1624 // TODO: moggi: fix the build problem with LOCK_ERROR_PATCH_FILE
1625 return WRITE_ERROR; //return LOCK_ERROR_PATCH_FILE;
1626 }
1627 char sourcefile[MAXPATHLEN];
1628 if (!WideCharToMultiByte(CP_UTF8, 0, mPatchFile, -1, sourcefile, MAXPATHLEN,
1629 nullptr, nullptr))
1630 {
1631 LOG(("error converting wchar to utf8: %d", GetLastError()));
1633 }
1634
1635 int rv = mArchiveReader.ExtractFileToStream(sourcefile, mPatchStream);
1636#else
1638#endif
1639
1640 return rv;
1641}
1642
1643int
1645{
1646 LOG(("EXECUTE PATCH " LOG_S, mFileRelPath.get()));
1647
1648 fseek(mPatchStream, 0, SEEK_SET);
1649
1651 if (rv)
1652 return rv;
1653
1654 FILE *origfile = nullptr;
1655#ifdef _WIN32
1656 if (NS_tstrcmp(mFileRelPath.get(), gCallbackRelPath) == 0)
1657 {
1658 // Read from the copy of the callback when patching since the callback can't
1659 // be opened for reading to prevent the application from being launched.
1660 origfile = NS_tfopen(gCallbackBackupPath, NS_T("rb"));
1661 }
1662 else
1663 {
1664 origfile = NS_tfopen(mFile.get(), NS_T("rb"));
1665 }
1666#else
1667 origfile = NS_tfopen(mFile.get(), NS_T("rb"));
1668#endif
1669
1670 if (!origfile)
1671 {
1672 LOG(("unable to open destination file: " LOG_S ", err: %d",
1673 mFileRelPath.get(), errno));
1674 return READ_ERROR;
1675 }
1676
1677 rv = LoadSourceFile(origfile);
1678 fclose(origfile);
1679 if (rv)
1680 {
1681 LOG(("LoadSourceFile failed"));
1682 return rv;
1683 }
1684
1685 // Rename the destination file if it exists before proceeding so it can be
1686 // used to restore the file to its original state if there is an error.
1687 struct NS_tstat_t ss;
1688 rv = NS_tstat(mFile.get(), &ss);
1689 if (rv)
1690 {
1691 LOG(("failed to read file status info: " LOG_S ", err: %d",
1692 mFileRelPath.get(), errno));
1693 return READ_ERROR;
1694 }
1695
1696 rv = backup_create(mFile.get());
1697 if (rv)
1698 return rv;
1699
1700#if defined(HAVE_POSIX_FALLOCATE)
1701 AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
1702 posix_fallocate(fileno((FILE *)ofile), 0, header.dlen);
1703#elif defined(_WIN32)
1704 bool shouldTruncate = true;
1705 // Creating the file, setting the size, and then closing the file handle
1706 // lessens fragmentation more than any other method tested. Other methods that
1707 // have been tested are:
1708 // 1. _chsize / _chsize_s reduced fragmentation though not completely.
1709 // 2. _get_osfhandle and then setting the size reduced fragmentation though
1710 // not completely. There are also reports of _get_osfhandle failing on
1711 // mingw.
1712 HANDLE hfile = CreateFileW(mFile.get(),
1713 GENERIC_WRITE,
1714 0,
1715 nullptr,
1716 CREATE_ALWAYS,
1717 FILE_ATTRIBUTE_NORMAL,
1718 nullptr);
1719
1720 if (hfile != INVALID_HANDLE_VALUE)
1721 {
1722 if (SetFilePointer(hfile, header.dlen,
1723 nullptr, FILE_BEGIN) != INVALID_SET_FILE_POINTER &&
1724 SetEndOfFile(hfile) != 0)
1725 {
1726 shouldTruncate = false;
1727 }
1728 CloseHandle(hfile);
1729 }
1730
1731 AutoFile ofile(ensure_open(mFile.get(), shouldTruncate ? NS_T("wb+") : NS_T("rb+"),
1732 ss.st_mode));
1733#elif defined(MACOSX)
1734 AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
1735 // Modified code from FileUtils.cpp
1736 fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, header.dlen};
1737 // Try to get a continuous chunk of disk space
1738 rv = fcntl(fileno((FILE *)ofile), F_PREALLOCATE, &store);
1739 if (rv == -1)
1740 {
1741 // OK, perhaps we are too fragmented, allocate non-continuous
1742 store.fst_flags = F_ALLOCATEALL;
1743 rv = fcntl(fileno((FILE *)ofile), F_PREALLOCATE, &store);
1744 }
1745
1746 if (rv != -1)
1747 {
1748 ftruncate(fileno((FILE *)ofile), header.dlen);
1749 }
1750#else
1751 AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
1752#endif
1753
1754 if (ofile == nullptr)
1755 {
1756 LOG(("unable to create new file: " LOG_S ", err: %d", mFileRelPath.get(),
1757 errno));
1759 }
1760
1761#ifdef _WIN32
1762 if (!shouldTruncate)
1763 {
1764 fseek(ofile, 0, SEEK_SET);
1765 }
1766#endif
1767
1768 rv = MBS_ApplyPatch(&header, mPatchStream, buf, ofile);
1769
1770 // Go ahead and do a bit of cleanup now to minimize runtime overhead.
1771 // Make sure mPatchStream gets unlocked on Windows; the system will do that,
1772 // but not until some indeterminate future time, and we want determinism.
1773#ifdef _WIN32
1774 UnlockFile((HANDLE)_get_osfhandle(fileno(mPatchStream)), (DWORD)0, (DWORD)0, (DWORD)-1, (DWORD)-1);
1775#endif
1776 // Set mPatchStream to nullptr to make AutoFile close the file,
1777 // so it can be deleted on Windows.
1778 mPatchStream = nullptr;
1780 spath[0] = NS_T('\0');
1781 free(buf);
1782 buf = nullptr;
1783 return rv;
1784}
1785
1786void
1788{
1789 LOG(("FINISH PATCH " LOG_S, mFileRelPath.get()));
1790
1791 backup_finish(mFile.get(), mFileRelPath.get(), status);
1792}
1793
1794class AddIfFile : public AddFile
1795{
1796public:
1797 AddIfFile(ArchiveReader& archiveReader);
1798
1799 virtual int Parse(NS_tchar *line);
1800 virtual int Prepare();
1801 virtual int Execute();
1802 virtual void Finish(int status);
1803
1804protected:
1805 std::unique_ptr<NS_tchar[]> mTestFile;
1806};
1807
1809 AddFile(archiveReader)
1810{
1811}
1812
1813int
1815{
1816 // format "<testfile>" "<newfile>"
1817
1819 if (!mTestFile)
1820 return PARSE_ERROR;
1821
1822 // consume whitespace between args
1823 NS_tchar *q = mstrtok(kQuote, &line);
1824 if (!q)
1825 return PARSE_ERROR;
1826
1827 return AddFile::Parse(line);
1828}
1829
1830int
1832{
1833 // If the test file does not exist, then skip this action.
1834 if (NS_taccess(mTestFile.get(), F_OK))
1835 {
1836 mTestFile = nullptr;
1837 return OK;
1838 }
1839
1840 return AddFile::Prepare();
1841}
1842
1843int
1845{
1846 if (!mTestFile)
1847 return OK;
1848
1849 return AddFile::Execute();
1850}
1851
1852void
1854{
1855 if (!mTestFile)
1856 return;
1857
1858 AddFile::Finish(status);
1859}
1860
1861class AddIfNotFile : public AddFile
1862{
1863public:
1864 AddIfNotFile(ArchiveReader& archiveReader);
1865
1866 virtual int Parse(NS_tchar *line);
1867 virtual int Prepare();
1868 virtual int Execute();
1869 virtual void Finish(int status);
1870
1871protected:
1872 std::unique_ptr<NS_tchar[]> mTestFile;
1873};
1874
1876 AddFile(archiveReader)
1877{
1878}
1879
1880int
1882{
1883 // format "<testfile>" "<newfile>"
1884
1886 if (!mTestFile)
1887 return PARSE_ERROR;
1888
1889 // consume whitespace between args
1890 NS_tchar *q = mstrtok(kQuote, &line);
1891 if (!q)
1892 return PARSE_ERROR;
1893
1894 return AddFile::Parse(line);
1895}
1896
1897int
1899{
1900 // If the test file exists, then skip this action.
1901 if (!NS_taccess(mTestFile.get(), F_OK))
1902 {
1903 mTestFile = NULL;
1904 return OK;
1905 }
1906
1907 return AddFile::Prepare();
1908}
1909
1910int
1912{
1913 if (!mTestFile)
1914 return OK;
1915
1916 return AddFile::Execute();
1917}
1918
1919void
1921{
1922 if (!mTestFile)
1923 return;
1924
1925 AddFile::Finish(status);
1926}
1927
1929{
1930public:
1931 PatchIfFile(ArchiveReader& archiveReader);
1932
1933 virtual int Parse(NS_tchar *line);
1934 virtual int Prepare(); // should check for patch file and for checksum here
1935 virtual int Execute();
1936 virtual void Finish(int status);
1937
1938private:
1939 std::unique_ptr<NS_tchar[]> mTestFile;
1940};
1941
1943 PatchFile(archiveReader)
1944{
1945}
1946
1947int
1949{
1950 // format "<testfile>" "<patchfile>" "<filetopatch>"
1951
1953 if (!mTestFile)
1954 return PARSE_ERROR;
1955
1956 // consume whitespace between args
1957 NS_tchar *q = mstrtok(kQuote, &line);
1958 if (!q)
1959 return PARSE_ERROR;
1960
1961 return PatchFile::Parse(line);
1962}
1963
1964int
1966{
1967 // If the test file does not exist, then skip this action.
1968 if (NS_taccess(mTestFile.get(), F_OK))
1969 {
1970 mTestFile = nullptr;
1971 return OK;
1972 }
1973
1974 return PatchFile::Prepare();
1975}
1976
1977int
1979{
1980 if (!mTestFile)
1981 return OK;
1982
1983 return PatchFile::Execute();
1984}
1985
1986void
1988{
1989 if (!mTestFile)
1990 return;
1991
1992 PatchFile::Finish(status);
1993}
1994
1995//-----------------------------------------------------------------------------
1996
1997#ifdef _WIN32
1998
2008bool
2009LaunchWinPostProcess(const WCHAR *installationDir,
2010 const WCHAR *updateInfoDir)
2011{
2012 WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' };
2013 wcsncpy(workingDirectory, installationDir, MAX_PATH);
2014
2015 // TODO: moggi: needs adaptation for LibreOffice
2016 // Most likely we don't have the helper method yet. Check if we really need it.
2017
2018 // Launch helper.exe to perform post processing (e.g. registry and log file
2019 // modifications) for the update.
2020 WCHAR inifile[MAX_PATH + 1] = { L'\0' };
2021 wcsncpy(inifile, installationDir, MAX_PATH);
2022 if (!PathAppendSafe(inifile, L"updater.ini"))
2023 {
2024 return false;
2025 }
2026
2027 WCHAR exefile[MAX_PATH + 1];
2028 WCHAR exearg[MAX_PATH + 1];
2029 WCHAR exeasync[10];
2030 bool async = true;
2031 if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeRelPath", nullptr,
2032 exefile, MAX_PATH + 1, inifile))
2033 {
2034 return false;
2035 }
2036
2037 if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeArg", nullptr, exearg,
2038 MAX_PATH + 1, inifile))
2039 {
2040 return false;
2041 }
2042
2043 if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeAsync", L"TRUE",
2044 exeasync,
2045 sizeof(exeasync)/sizeof(exeasync[0]),
2046 inifile))
2047 {
2048 return false;
2049 }
2050
2051 // Verify that exeFile doesn't contain relative paths
2052 if (wcsstr(exefile, L"..") != nullptr)
2053 {
2054 return false;
2055 }
2056
2057 WCHAR exefullpath[MAX_PATH + 1] = { L'\0' };
2058 wcsncpy(exefullpath, installationDir, MAX_PATH);
2059 if (!PathAppendSafe(exefullpath, exefile))
2060 {
2061 return false;
2062 }
2063
2064#if !defined(TEST_UPDATER) && defined(MAINTENANCE_SERVICE)
2065 if (sUsingService &&
2066 !DoesBinaryMatchAllowedCertificates(installationDir, exefullpath))
2067 {
2068 return false;
2069 }
2070#endif
2071
2072 WCHAR dlogFile[MAX_PATH + 1];
2073 if (!PathGetSiblingFilePath(dlogFile, exefullpath, L"uninstall.update"))
2074 {
2075 return false;
2076 }
2077
2078 WCHAR slogFile[MAX_PATH + 1] = { L'\0' };
2079 wcsncpy(slogFile, updateInfoDir, MAX_PATH);
2080 if (!PathAppendSafe(slogFile, L"update.log"))
2081 {
2082 return false;
2083 }
2084
2085 WCHAR dummyArg[14] = { L'\0' };
2086 wcsncpy(dummyArg, L"argv0ignored ", sizeof(dummyArg) / sizeof(dummyArg[0]) - 1);
2087
2088 size_t len = wcslen(exearg) + wcslen(dummyArg);
2089 WCHAR *cmdline = (WCHAR *) malloc((len + 1) * sizeof(WCHAR));
2090 if (!cmdline)
2091 {
2092 return false;
2093 }
2094
2095 wcsncpy(cmdline, dummyArg, len);
2096 wcscat(cmdline, exearg);
2097
2098 if (sUsingService ||
2099 !_wcsnicmp(exeasync, L"false", 6) ||
2100 !_wcsnicmp(exeasync, L"0", 2))
2101 {
2102 async = false;
2103 }
2104
2105 // We want to launch the post update helper app to update the Windows
2106 // registry even if there is a failure with removing the uninstall.update
2107 // file or copying the update.log file.
2108 CopyFileW(slogFile, dlogFile, false);
2109
2110 STARTUPINFOW si = {sizeof(si), 0};
2111 si.lpDesktop = L"";
2112 PROCESS_INFORMATION pi = {0};
2113
2114 bool ok = CreateProcessW(exefullpath,
2115 cmdline,
2116 nullptr, // no special security attributes
2117 nullptr, // no special thread attributes
2118 false, // don't inherit filehandles
2119 0, // No special process creation flags
2120 nullptr, // inherit my environment
2121 workingDirectory,
2122 &si,
2123 &pi);
2124 free(cmdline);
2125 if (ok)
2126 {
2127 if (!async)
2128 {
2129 WaitForSingleObject(pi.hProcess, INFINITE);
2130 }
2131 CloseHandle(pi.hProcess);
2132 CloseHandle(pi.hThread);
2133 }
2134 return ok;
2135}
2136
2137#endif
2138
2139static void
2140LaunchCallbackApp(const NS_tchar *workingDir,
2141 int argc,
2142 NS_tchar **argv,
2143 bool usingService)
2144{
2145 putenv(const_cast<char*>("NO_EM_RESTART="));
2146 putenv(const_cast<char*>("MOZ_LAUNCHED_CHILD=1"));
2147
2148 // Run from the specified working directory (see bug 312360). This is not
2149 // necessary on Windows CE since the application that launches the updater
2150 // passes the working directory as an --environ: command line argument.
2151 if (NS_tchdir(workingDir) != 0)
2152 {
2153 LOG(("Warning: chdir failed"));
2154 }
2155
2156#if defined(USE_EXECV)
2157 (void) argc;
2158 (void) usingService; // avoid warnings
2159 execv(argv[0], argv);
2160#elif defined(MACOSX)
2161 LaunchChild(argc, (const char**)argv);
2162#elif defined(_WIN32)
2163 // Do not allow the callback to run when running an update through the
2164 // service as session 0. The unelevated updater.exe will do the launching.
2165 if (!usingService)
2166 {
2167 WinLaunchChild(argv[0], argc, argv, nullptr);
2168 }
2169#else
2170# warning "Need implementation of LaunchCallbackApp"
2171#endif
2172}
2173
2174static bool
2175WriteStatusFile(const char* aStatus)
2176{
2177 NS_tchar filename[MAXPATHLEN] = {NS_T('\0')};
2178#if defined(_WIN32)
2179 // The temp file is not removed on failure since there is client code that
2180 // will remove it.
2181 GetTempFileNameW(gPatchDirPath, L"sta", 0, filename);
2182#else
2183 NS_tsnprintf(filename, sizeof(filename)/sizeof(filename[0]),
2184 NS_T("%s/update.status"), gPatchDirPath);
2185#endif
2186
2187 // Make sure that the directory for the update status file exists
2188 if (ensure_parent_dir(filename))
2189 return false;
2190
2191 // This is scoped to make the AutoFile close the file so it is possible to
2192 // move the temp file to the update.status file on Windows.
2193 {
2194 AutoFile file(NS_tfopen(filename, NS_T("wb+")));
2195 if (file == nullptr)
2196 {
2197 return false;
2198 }
2199
2200 if (fwrite(aStatus, strlen(aStatus), 1, file) != 1)
2201 {
2202 return false;
2203 }
2204 }
2205
2206#if defined(_WIN32)
2207 NS_tchar dstfilename[MAXPATHLEN] = {NS_T('\0')};
2208 NS_tsnprintf(dstfilename, sizeof(dstfilename)/sizeof(dstfilename[0]),
2209 NS_T("%s\\update.status"), gPatchDirPath);
2210 if (MoveFileExW(filename, dstfilename, MOVEFILE_REPLACE_EXISTING) == 0)
2211 {
2212 return false;
2213 }
2214#endif
2215
2216 return true;
2217}
2218
2219static void
2221{
2222 const char *text;
2223
2224 char buf[32];
2225 if (status == OK)
2226 {
2227 if (sStagedUpdate)
2228 {
2229 text = "applied\n";
2230 }
2231 else
2232 {
2233 text = "succeeded\n";
2234 }
2235 }
2236 else
2237 {
2238 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "failed: %d\n", status);
2239 text = buf;
2240 }
2241
2243}
2244
2245#ifdef MAINTENANCE_SERVICE
2246/*
2247 * Read the update.status file and sets isPendingService to true if
2248 * the status is set to pending-service.
2249 *
2250 * @param isPendingService Out parameter for specifying if the status
2251 * is set to pending-service or not.
2252 * @return true if the information was retrieved and it is pending
2253 * or pending-service.
2254 */
2255static bool
2256IsUpdateStatusPendingService()
2257{
2258 NS_tchar filename[MAXPATHLEN];
2259 NS_tsnprintf(filename, sizeof(filename)/sizeof(filename[0]),
2260 NS_T("%s/update.status"), gPatchDirPath);
2261
2262 AutoFile file(NS_tfopen(filename, NS_T("rb")));
2263 if (file == nullptr)
2264 return false;
2265
2266 char buf[32] = { 0 };
2267 fread(buf, sizeof(buf), 1, file);
2268
2269 const char kPendingService[] = "pending-service";
2270 const char kAppliedService[] = "applied-service";
2271
2272 return (strncmp(buf, kPendingService,
2273 sizeof(kPendingService) - 1) == 0) ||
2274 (strncmp(buf, kAppliedService,
2275 sizeof(kAppliedService) - 1) == 0);
2276}
2277#endif
2278
2279#ifdef _WIN32
2280/*
2281 * Read the update.status file and sets isSuccess to true if
2282 * the status is set to succeeded.
2283 *
2284 * @param isSucceeded Out parameter for specifying if the status
2285 * is set to succeeded or not.
2286 * @return true if the information was retrieved and it is succeeded.
2287 */
2288static bool
2289IsUpdateStatusSucceeded(bool &isSucceeded)
2290{
2291 isSucceeded = false;
2292 NS_tchar filename[MAXPATHLEN];
2293 NS_tsnprintf(filename, sizeof(filename)/sizeof(filename[0]),
2294 NS_T("%s/update.status"), gPatchDirPath);
2295
2296 AutoFile file(NS_tfopen(filename, NS_T("rb")));
2297 if (file == nullptr)
2298 return false;
2299
2300 char buf[32] = { 0 };
2301 fread(buf, sizeof(buf), 1, file);
2302
2303 const char kSucceeded[] = "succeeded";
2304 isSucceeded = strncmp(buf, kSucceeded,
2305 sizeof(kSucceeded) - 1) == 0;
2306 return true;
2307}
2308#endif
2309
2310/*
2311 * Copy the entire contents of the application installation directory to the
2312 * destination directory for the update process.
2313 *
2314 * @return 0 if successful, an error code otherwise.
2315 */
2316static int
2318{
2319 // These files should not be copied over to the updated app
2320#ifdef _WIN32
2321#define SKIPLIST_COUNT 4
2322#elif defined(MACOSX)
2323#define SKIPLIST_COUNT 1
2324#else
2325#define SKIPLIST_COUNT 3
2326#endif
2328
2329 std::unique_ptr<NS_tchar[]> pUserProfile(new NS_tchar[MAXPATHLEN]);
2330 NS_tstrcpy(pUserProfile.get(), gPatchDirPath);
2331 NS_tchar *slash = (NS_tchar *) NS_tstrrchr(pUserProfile.get(), NS_T('/'));
2332 if (slash)
2333 *slash = NS_T('\0');
2334
2335 LOG(("ignore user profile directory during copy: " LOG_S, pUserProfile.get()));
2336
2337 skiplist.append(0, pUserProfile.get());
2338#ifndef MACOSX
2339 skiplist.append(1, gInstallDirPath, NS_T("updated"));
2340 skiplist.append(2, gInstallDirPath, NS_T("updates/0"));
2341#ifdef _WIN32
2342 skiplist.append(4, gInstallDirPath, NS_T("updated.update_in_progress.lock"));
2343#endif
2344#endif
2345
2347}
2348
2349/*
2350 * Replace the application installation directory with the destination
2351 * directory in order to finish a staged update task
2352 *
2353 * @return 0 if successful, an error code otherwise.
2354 */
2355static int
2357{
2358 // TODO: moggi: handle the user profile in the installation dir also
2359 // during the replacement request
2360 // The replacement algorithm is like this:
2361 // 1. Move destDir to tmpDir. In case of failure, abort.
2362 // 2. Move newDir to destDir. In case of failure, revert step 1 and abort.
2363 // 3. Delete tmpDir (or defer it to the next reboot).
2364
2365#ifdef MACOSX
2366 NS_tchar destDir[MAXPATHLEN];
2367 NS_tsnprintf(destDir, sizeof(destDir)/sizeof(destDir[0]),
2368 NS_T("%s/Contents"), gInstallDirPath);
2369#elif defined(_WIN32)
2370 // Windows preserves the case of the file/directory names. We use the
2371 // GetLongPathName API in order to get the correct case for the directory
2372 // name, so that if the user has used a different case when launching the
2373 // application, the installation directory's name does not change.
2374 NS_tchar destDir[MAXPATHLEN];
2375 if (!GetLongPathNameW(gInstallDirPath, destDir,
2376 sizeof(destDir)/sizeof(destDir[0])))
2377 {
2378 return NO_INSTALLDIR_ERROR;
2379 }
2380#else
2381 NS_tchar* destDir = gInstallDirPath;
2382#endif
2383
2384 NS_tchar tmpDir[MAXPATHLEN];
2385 NS_tsnprintf(tmpDir, sizeof(tmpDir)/sizeof(tmpDir[0]),
2386 NS_T("%s.bak"), destDir);
2387
2388 // First try to remove the possibly existing temp directory, because if this
2389 // directory exists, we will fail to rename destDir.
2390 // No need to error check here because if this fails, we will fail in the
2391 // next step anyways.
2393
2394 LOG(("Begin moving destDir (" LOG_S ") to tmpDir (" LOG_S ")",
2395 destDir, tmpDir));
2396 LogFlush();
2397 int rv = rename_file(destDir, tmpDir, true);
2398#ifdef _WIN32
2399 // On Windows, if Firefox is launched using the shortcut, it will hold a handle
2400 // to its installation directory open, which might not get released in time.
2401 // Therefore we wait a little bit here to see if the handle is released.
2402 // If it's not released, we just fail to perform the replace request.
2403 const int max_retries = 10;
2404 int retries = 0;
2405 while (rv == WRITE_ERROR && (retries++ < max_retries))
2406 {
2407 LOG(("PerformReplaceRequest: destDir rename attempt %d failed. " \
2408 "File: " LOG_S ". Last error: %d, err: %d", retries,
2409 destDir, GetLastError(), rv));
2410
2411 Sleep(100);
2412
2413 rv = rename_file(destDir, tmpDir, true);
2414 }
2415#endif
2416 if (rv)
2417 {
2418 // The status file will have 'pending' written to it so there is no value in
2419 // returning an error specific for this failure.
2420 LOG(("Moving destDir to tmpDir failed, err: %d", rv));
2421 return rv;
2422 }
2423
2424 NS_tchar newDir[MAXPATHLEN];
2425 if (is_userprofile_in_instdir())
2426 {
2427 LOG(("user profile in instdir"));
2428 NS_tstrcpy(newDir, tmpDir);
2430 LOG((LOG_S, newDir));
2431 }
2432 else
2433 {
2434 NS_tsnprintf(newDir, sizeof(newDir)/sizeof(newDir[0]),
2435 NS_T("%s"),
2437 }
2438
2439 LOG(("Begin moving newDir (" LOG_S ") to destDir (" LOG_S ")",
2440 newDir, destDir));
2441 rv = rename_file(newDir, destDir, true);
2442#ifdef MACOSX
2443 if (rv)
2444 {
2445 LOG(("Moving failed. Begin copying newDir (" LOG_S ") to destDir (" LOG_S ")",
2446 newDir, destDir));
2448 rv = ensure_copy_recursive(newDir, destDir, skiplist);
2449 }
2450#endif
2451 if (rv)
2452 {
2453 LOG(("Moving newDir to destDir failed, err: %d", rv));
2454 LOG(("Now, try to move tmpDir back to destDir"));
2455 ensure_remove_recursive(destDir);
2456 int rv2 = rename_file(tmpDir, destDir, true);
2457 if (rv2)
2458 {
2459 LOG(("Moving tmpDir back to destDir failed, err: %d", rv2));
2460 }
2461 // The status file will be have 'pending' written to it so there is no value
2462 // in returning an error specific for this failure.
2463 return rv;
2464 }
2465
2466 if (is_userprofile_in_instdir())
2467 {
2468 // 1.) calculate path of the user profile in the backup directory
2469 // 2.) move the user profile from the backup to the install directory
2470 NS_tchar backup_user_profile[MAXPATHLEN];
2471 NS_tchar userprofile[MAXPATHLEN];
2472
2473 NS_tstrcpy(userprofile, gPatchDirPath);
2474 NS_tchar* slash = (NS_tchar *) NS_tstrrchr(userprofile, NS_T('/'));
2475 if (slash)
2476 *slash = NS_T('\0');
2477 NS_tstrcpy(backup_user_profile, tmpDir);
2478 size_t installdir_len = NS_tstrlen(destDir);
2479
2480 NS_tstrcat(backup_user_profile, userprofile + installdir_len);
2481 LOG(("copy user profile back from " LOG_S " to " LOG_S, backup_user_profile, userprofile));
2482 int rv2 = rename_file(backup_user_profile, userprofile);
2483 if (rv2)
2484 {
2485 LOG(("failed to copy user profile back"));
2486 }
2487 if (slash)
2488 *slash = NS_T('/');
2489 }
2490
2491#if !defined(_WIN32) && !defined(MACOSX)
2492 // Platforms that have their updates directory in the installation directory
2493 // need to have the last-update.log and backup-update.log files moved from the
2494 // old installation directory to the new installation directory.
2495 NS_tchar tmpLog[MAXPATHLEN];
2496 int nWrittenBytes = NS_tsnprintf(tmpLog, sizeof(tmpLog)/sizeof(tmpLog[0]),
2497 NS_T("%s/updates/last-update.log"), tmpDir);
2498 (void) nWrittenBytes;
2499 if (!NS_taccess(tmpLog, F_OK))
2500 {
2501 NS_tchar destLog[MAXPATHLEN];
2502 NS_tsnprintf(destLog, sizeof(destLog)/sizeof(destLog[0]),
2503 NS_T("%s/updates/last-update.log"), destDir);
2504 NS_tremove(destLog);
2505 NS_trename(tmpLog, destLog);
2506 }
2507#endif
2508
2509 LOG(("Now, remove the tmpDir"));
2510 rv = ensure_remove_recursive(tmpDir, true);
2511 if (rv)
2512 {
2513 LOG(("Removing tmpDir failed, err: %d", rv));
2514#ifdef _WIN32
2515 NS_tchar deleteDir[MAXPATHLEN];
2516 NS_tsnprintf(deleteDir, sizeof(deleteDir)/sizeof(deleteDir[0]),
2517 NS_T("%s\\%s"), destDir, DELETE_DIR);
2518 // Attempt to remove the tobedeleted directory and then recreate it if it
2519 // was successfully removed.
2520 _wrmdir(deleteDir);
2521 if (NS_taccess(deleteDir, F_OK))
2522 {
2523 NS_tmkdir(deleteDir, 0755);
2524 }
2525 remove_recursive_on_reboot(tmpDir, deleteDir);
2526#endif
2527 }
2528
2529#ifdef MACOSX
2530 // On macOS, we need to remove the staging directory after its Contents
2531 // directory has been moved.
2532 NS_tchar updatedAppDir[MAXPATHLEN];
2533 NS_tsnprintf(updatedAppDir, sizeof(updatedAppDir)/sizeof(updatedAppDir[0]),
2534 NS_T("%s/Updated.app"), gPatchDirPath);
2535 ensure_remove_recursive(updatedAppDir);
2536#endif
2537
2538 gSucceeded = true;
2539
2540 return 0;
2541}
2542
2543#ifdef _WIN32
2544static void
2545WaitForServiceFinishThread(void* /*param*/)
2546{
2547 // We wait at most 10 minutes, we already waited 5 seconds previously
2548 // before deciding to show this UI.
2551}
2552#endif
2553
2554#ifdef VERIFY_MAR_SIGNATURE
2562static int
2563ReadMARChannelIDs(const NS_tchar *path, MARChannelStringTable *results)
2564{
2565 // TODO: moggi: needs adaptation for LibreOffice
2566 // Check where this function gets its parameters from
2567 const unsigned int kNumStrings = 1;
2568 const char *kUpdaterKeys = "ACCEPTED_MAR_CHANNEL_IDS\0";
2569 char updater_strings[kNumStrings][MAX_TEXT_LEN];
2570
2571 int result = ReadStrings(path, kUpdaterKeys, kNumStrings,
2572 updater_strings, "Settings");
2573
2574 strncpy(results->MARChannelID, updater_strings[0], MAX_TEXT_LEN - 1);
2575 results->MARChannelID[MAX_TEXT_LEN - 1] = 0;
2576
2577 return result;
2578}
2579#endif
2580
2581static int
2582GetUpdateFileNames(std::vector<tstring>& fileNames)
2583{
2584 NS_tchar fileName[MAXPATHLEN];
2585 NS_tsnprintf(fileName, MAXPATHLEN,
2586 NS_T("%s/update.mar"), gPatchDirPath);
2587 fileNames.push_back(fileName);
2588
2589 // add the language packs
2591 if (!dir)
2592 {
2593 LOG(("Could not open directory " LOG_S, gPatchDirPath));
2594 return READ_ERROR;
2595 }
2596
2597 NS_tdirent* entry;
2598 while ((entry = NS_treaddir(dir)) != nullptr)
2599 {
2600 if (NS_tstrcmp(entry->d_name, NS_T(".")) &&
2601 NS_tstrcmp(entry->d_name, NS_T("..")) &&
2602 NS_tstrcmp(entry->d_name, NS_T("update.mar")))
2603 {
2604 if (NS_tstrncmp(entry->d_name, NS_T("update"), 6) == 0)
2605 {
2606 NS_tchar *dot = NS_tstrrchr(entry->d_name, NS_T('.'));
2607 if (dot && !NS_tstrcmp(dot, NS_T(".mar")))
2608 {
2609 NS_tchar updatePath[MAXPATHLEN];
2610 NS_tsnprintf(updatePath, sizeof(updatePath)/sizeof(updatePath[0]),
2611 NS_T("%s/%s"), gPatchDirPath, entry->d_name);
2612
2613 LOG (("Found language update file: " LOG_S, updatePath));
2614 fileNames.push_back(updatePath);
2615 }
2616 }
2617 }
2618 }
2619 return OK;
2620}
2621
2622static int
2624{
2625#ifdef VERIFY_MAR_SIGNATURE
2626#ifdef _WIN32
2627 HKEY baseKey = nullptr;
2628 wchar_t valueName[] = L"Image Path";
2629 wchar_t rasenh[] = L"rsaenh.dll";
2630 bool reset = false;
2631 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2632 L"SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider\\Microsoft Enhanced Cryptographic Provider v1.0",
2633 0, KEY_READ | KEY_WRITE,
2634 &baseKey) == ERROR_SUCCESS)
2635 {
2636 wchar_t path[MAX_PATH + 1];
2637 DWORD size = sizeof(path);
2638 DWORD type;
2639 if (RegQueryValueExW(baseKey, valueName, 0, &type,
2640 (LPBYTE)path, &size) == ERROR_SUCCESS)
2641 {
2642 if (type == REG_SZ && wcscmp(path, rasenh) == 0)
2643 {
2644 wchar_t rasenhFullPath[] = L"%SystemRoot%\\System32\\rsaenh.dll";
2645 if (RegSetValueExW(baseKey, valueName, 0, REG_SZ,
2646 (const BYTE*)rasenhFullPath,
2647 sizeof(rasenhFullPath)) == ERROR_SUCCESS)
2648 {
2649 reset = true;
2650 }
2651 }
2652 }
2653 }
2654#endif
2655 int rv = archiveReader.VerifySignature();
2656#ifdef _WIN32
2657 if (baseKey)
2658 {
2659 if (reset)
2660 {
2661 RegSetValueExW(baseKey, valueName, 0, REG_SZ,
2662 (const BYTE*)rasenh,
2663 sizeof(rasenh));
2664 }
2665 RegCloseKey(baseKey);
2666 }
2667#endif
2668
2669
2670 if (rv == OK)
2671 {
2672 if (rv == OK)
2673 {
2674 NS_tchar updateSettingsPath[MAX_TEXT_LEN];
2675
2676 // TODO: moggi: needs adaptation for LibreOffice
2677 // These paths need to be adapted for us.
2678 int nWrittenBytes = NS_tsnprintf(updateSettingsPath,
2679 sizeof(updateSettingsPath) / sizeof(updateSettingsPath[0]),
2680#ifdef MACOSX
2681 NS_T("%s/Contents/Resources/update-settings.ini"),
2682#else
2683 NS_T("%s/update-settings.ini"),
2684#endif
2686 (void) nWrittenBytes;
2687 MARChannelStringTable MARStrings;
2688 if (ReadMARChannelIDs(updateSettingsPath, &MARStrings) != OK)
2689 {
2690 // If we can't read from update-settings.ini then we shouldn't impose
2691 // a MAR restriction. Some installations won't even include this file.
2692 MARStrings.MARChannelID[0] = '\0';
2693 }
2694
2695 rv = archiveReader.VerifyProductInformation(MARStrings.MARChannelID,
2696 LIBO_VERSION_DOTTED);
2697 }
2698 }
2699#endif
2700
2701 return rv;
2702}
2703
2704static void
2705UpdateThreadFunc(void * /*param*/)
2706{
2707 // open ZIP archive and process...
2708 int rv = OK;
2709 if (sReplaceRequest)
2710 {
2711 rv = ProcessReplaceRequest();
2712 }
2713 else
2714 {
2715 std::vector<tstring> fileNames;
2716 GetUpdateFileNames(fileNames);
2717
2718 for (auto& fileName: fileNames)
2719 {
2720 ArchiveReader archiveReader;
2721 rv = archiveReader.Open(fileName.c_str());
2722 if (rv != OK)
2723 {
2724 LOG(("Could not open " LOG_S, fileName.c_str()));
2725 break;
2726 }
2727
2728 rv = CheckSignature(archiveReader);
2729 if (rv != OK)
2730 {
2731 LOG(("Could not verify the signature of " LOG_S, fileName.c_str()));
2732 break;
2733 }
2734 }
2735
2736 if (rv == OK && sStagedUpdate)
2737 {
2739 }
2740
2741 if (rv == OK)
2742 {
2743 for (auto& fileName: fileNames)
2744 {
2745 ArchiveReader archiveReader;
2746 archiveReader.Open(fileName.c_str());
2747 rv = DoUpdate(archiveReader);
2748 }
2749 NS_tchar updatingDir[MAXPATHLEN];
2750 int nWrittenBytes = NS_tsnprintf(updatingDir, sizeof(updatingDir)/sizeof(updatingDir[0]),
2751 NS_T("%s/updating"), gWorkingDirPath);
2752 (void) nWrittenBytes;
2753 ensure_remove_recursive(updatingDir);
2754 }
2755 }
2756
2757 if (rv && (sReplaceRequest || sStagedUpdate))
2758 {
2759#ifdef _WIN32
2760 // On Windows, the current working directory of the process should be changed
2761 // so that it's not locked.
2762 if (sStagedUpdate)
2763 {
2764 NS_tchar sysDir[MAX_PATH + 1] = { L'\0' };
2765 if (GetSystemDirectoryW(sysDir, MAX_PATH + 1))
2766 {
2767 NS_tchdir(sysDir);
2768 }
2769 }
2770#endif
2772 // When attempting to replace the application, we should fall back
2773 // to non-staged updates in case of a failure. We do this by
2774 // setting the status to pending, exiting the updater, and
2775 // launching the callback application. The callback application's
2776 // startup path will see the pending status, and will start the
2777 // updater application again in order to apply the update without
2778 // staging.
2779 if (sReplaceRequest)
2780 {
2781 WriteStatusFile(sUsingService ? "pending-service" : "pending");
2782 }
2783 else
2784 {
2785 WriteStatusFile(rv);
2786 }
2787#ifdef TEST_UPDATER
2788 // Some tests need to use --test-process-updates again.
2789 putenv(const_cast<char*>("MOZ_TEST_PROCESS_UPDATES="));
2790#endif
2791 }
2792 else
2793 {
2794 if (rv)
2795 {
2796 LOG(("failed: %d", rv));
2797 }
2798 else
2799 {
2800#ifdef MACOSX
2801 // If the update was successful we need to update the timestamp on the
2802 // top-level macOS bundle directory so that macOS's Launch Services
2803 // picks up any major changes when the bundle is updated.
2804 if (!sStagedUpdate && utimes(gInstallDirPath, nullptr) != 0)
2805 {
2806 LOG(("Couldn't set access/modification time on application bundle."));
2807 }
2808#endif
2809
2810 LOG(("succeeded"));
2811 }
2812 WriteStatusFile(rv);
2813 }
2814
2815 LOG(("calling QuitProgressUI"));
2817}
2818
2819#ifdef MACOSX
2820static void
2821ServeElevatedUpdateThreadFunc(void* param)
2822{
2823 UpdateServerThreadArgs* threadArgs = (UpdateServerThreadArgs*)param;
2824 gSucceeded = ServeElevatedUpdate(threadArgs->argc, threadArgs->argv);
2825 if (!gSucceeded)
2826 {
2828 }
2830}
2831
2832void freeArguments(int argc, char** argv)
2833{
2834 for (int i = 0; i < argc; i++)
2835 {
2836 free(argv[i]);
2837 }
2838 free(argv);
2839}
2840#endif
2841
2843 int callbackIndex
2844#ifdef _WIN32
2845 , const WCHAR* elevatedLockFilePath
2846 , HANDLE updateLockFileHandle
2847#elif defined(MACOSX)
2848 , bool isElevated
2849#endif
2850 )
2851{
2852 if (argc > callbackIndex)
2853 {
2854#if defined(_WIN32)
2855 if (gSucceeded)
2856 {
2858 {
2859 fprintf(stderr, "The post update process was not launched");
2860 }
2861
2862 // The service update will only be executed if it is already installed.
2863 // For first time installs of the service, the install will happen from
2864 // the PostUpdate process. We do the service update process here
2865 // because it's possible we are updating with updater.exe without the
2866 // service if the service failed to apply the update. We want to update
2867 // the service to a newer version in that case. If we are not running
2868 // through the service, then USING_SERVICE will not exist.
2869 if (!sUsingService)
2870 {
2872 }
2873 }
2874 EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 0);
2875#elif defined(MACOSX)
2876 if (!isElevated)
2877 {
2878 if (gSucceeded)
2879 {
2881 }
2882#endif
2883
2884 LaunchCallbackApp(argv[5],
2885 argc - callbackIndex,
2886 argv + callbackIndex,
2888#ifdef XP_MACOSX
2889 } // if (!isElevated)
2890#endif /* XP_MACOSX */
2891}
2892return 0;
2893}
2894
2895int NS_main(int argc, NS_tchar **argv)
2896{
2897 // The callback is the remaining arguments starting at callbackIndex.
2898 // The argument specified by callbackIndex is the callback executable and the
2899 // argument prior to callbackIndex is the working directory.
2900 const int callbackIndex = 6;
2901
2902#ifdef MACOSX
2903 // TODO: moggi: needs adaptation for LibreOffice
2904 bool isElevated =
2905 strstr(argv[0], "/Library/PrivilegedHelperTools/org.mozilla.updater") != 0;
2906 if (isElevated)
2907 {
2908 if (!ObtainUpdaterArguments(&argc, &argv))
2909 {
2910 // Won't actually get here because ObtainUpdaterArguments will terminate
2911 // the current process on failure.
2912 return 1;
2913 }
2914 }
2915#endif
2916
2917#if defined(VERIFY_MAR_SIGNATURE) && !defined(_WIN32) && !defined(MACOSX)
2918 // On Windows and Mac we rely on native APIs to do verifications so we don't
2919 // need to initialize NSS at all there.
2920 // Otherwise, minimize the amount of NSS we depend on by avoiding all the NSS
2921 // databases.
2922 if (!NSS_IsInitialized())
2923 {
2924 if (NSS_NoDB_Init(NULL) != SECSuccess)
2925 {
2926 PRErrorCode error = PR_GetError();
2927 fprintf(stderr, "Could not initialize NSS: %s (%d)",
2928 PR_ErrorToName(error), (int) error);
2929 _exit(1);
2930 }
2931 }
2932#endif
2933
2934#ifdef MACOSX
2935 if (!isElevated)
2936 {
2937#endif
2938 InitProgressUI(&argc, &argv);
2939#ifdef MACOSX
2940 }
2941#endif
2942
2943 // To process an update the updater command line must at a minimum have the
2944 // directory path containing the updater.mar file to process as the first
2945 // argument, the install directory as the second argument, and the directory
2946 // to apply the update to as the third argument. When the updater is launched
2947 // by another process the PID of the parent process should be provided in the
2948 // optional fourth argument and the updater will wait on the parent process to
2949 // exit if the value is non-zero and the process is present. This is necessary
2950 // due to not being able to update files that are in use on Windows. The
2951 // optional fifth argument is the callback's working directory and the
2952 // optional sixth argument is the callback path. The callback is the
2953 // application to launch after updating and it will be launched when these
2954 // arguments are provided whether the update was successful or not. All
2955 // remaining arguments are optional and are passed to the callback when it is
2956 // launched.
2957 if (argc < 4)
2958 {
2959 fprintf(stderr, "Usage: updater patch-dir install-dir apply-to-dir [wait-pid [callback-working-dir callback-path args...]]\n");
2960#ifdef MACOSX
2961 if (isElevated)
2962 {
2963 freeArguments(argc, argv);
2964 CleanupElevatedMacUpdate(true);
2965 }
2966#endif
2967 return 1;
2968 }
2969
2970 // The directory containing the update information.
2971 gPatchDirPath = argv[1];
2972
2973 // The directory we're going to update to.
2974 // We copy this string because we need to remove trailing slashes. The C++
2975 // standard says that it's always safe to write to strings pointed to by argv
2976 // elements, but I don't necessarily believe it.
2978 gInstallDirPath[MAXPATHLEN - 1] = NS_T('\0');
2980 if (slash && !slash[1])
2981 {
2982 *slash = NS_T('\0');
2983 }
2984
2985#ifdef _WIN32
2986 bool useService = false;
2987 bool testOnlyFallbackKeyExists = false;
2988 bool noServiceFallback = false;
2989
2990 // We never want the service to be used unless we build with
2991 // the maintenance service.
2992#ifdef MAINTENANCE_SERVICE
2993 useService = IsUpdateStatusPendingService();
2994 // Our tests run with a different apply directory for each test.
2995 // We use this registry key on our test slaves to store the
2996 // allowed name/issuers.
2997 testOnlyFallbackKeyExists = DoesFallbackKeyExist();
2998#endif
2999
3000 // Remove everything except close window from the context menu
3001 {
3002 // TODO: moggi: needs adaptation for LibreOffice
3003 HKEY hkApp = nullptr;
3004 RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Classes\\Applications",
3005 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, nullptr,
3006 &hkApp, nullptr);
3007 RegCloseKey(hkApp);
3008 if (RegCreateKeyExW(HKEY_CURRENT_USER,
3009 L"Software\\Classes\\Applications\\updater.exe",
3010 0, nullptr, REG_OPTION_VOLATILE, KEY_SET_VALUE, nullptr,
3011 &hkApp, nullptr) == ERROR_SUCCESS)
3012 {
3013 RegSetValueExW(hkApp, L"IsHostApp", 0, REG_NONE, 0, 0);
3014 RegSetValueExW(hkApp, L"NoOpenWith", 0, REG_NONE, 0, 0);
3015 RegSetValueExW(hkApp, L"NoStartPage", 0, REG_NONE, 0, 0);
3016 RegCloseKey(hkApp);
3017 }
3018 }
3019#endif
3020
3021 // If there is a PID specified and it is not '0' then wait for the process to exit.
3022#ifdef _WIN32
3023 __int64 pid = 0;
3024#else
3025 int pid = 0;
3026#endif
3027 if (argc > 4)
3028 {
3029#ifdef _WIN32
3030 pid = _wtoi64(argv[4]);
3031#else
3032 pid = atoi(argv[4]);
3033#endif
3034 if (pid == -1)
3035 {
3036 // This is a signal from the parent process that the updater should stage
3037 // the update.
3038 sStagedUpdate = true;
3039 }
3040 else if (NS_tstrstr(argv[4], NS_T("/replace")))
3041 {
3042 // We're processing a request to replace the application with a staged
3043 // update.
3044 sReplaceRequest = true;
3045 }
3046 }
3047
3048 // The directory we're going to update to.
3049 // We copy this string because we need to remove trailing slashes. The C++
3050 // standard says that it's always safe to write to strings pointed to by argv
3051 // elements, but I don't necessarily believe it.
3053 gWorkingDirPath[MAXPATHLEN - 1] = NS_T('\0');
3055 if (slash && !slash[1])
3056 {
3057 *slash = NS_T('\0');
3058 }
3059
3060#ifdef MACOSX
3061 if (!isElevated && !IsRecursivelyWritable(argv[2]))
3062 {
3063 // If the app directory isn't recursively writeable, an elevated update is
3064 // required.
3065 UpdateServerThreadArgs threadArgs;
3066 threadArgs.argc = argc;
3067 threadArgs.argv = const_cast<const NS_tchar**>(argv);
3068
3069 Thread t1;
3070 if (t1.Run(ServeElevatedUpdateThreadFunc, &threadArgs) == 0)
3071 {
3072 // Show an indeterminate progress bar while an elevated update is in
3073 // progress.
3074 ShowProgressUI(true);
3075 }
3076 t1.Join();
3077
3078 LaunchCallbackAndPostProcessApps(argc, argv, callbackIndex, false);
3079 return gSucceeded ? 0 : 1;
3080 }
3081#endif
3082
3083 LogInit(gPatchDirPath, NS_T("update.log"));
3084
3085 if (!WriteStatusFile("applying"))
3086 {
3087 LOG(("failed setting status to 'applying'"));
3088#ifdef MACOSX
3089 if (isElevated)
3090 {
3091 freeArguments(argc, argv);
3092 CleanupElevatedMacUpdate(true);
3093 }
3094#endif
3095 return 1;
3096 }
3097
3098 if (sStagedUpdate)
3099 {
3100 LOG(("Performing a staged update"));
3101 }
3102 else if (sReplaceRequest)
3103 {
3104 LOG(("Performing a replace request"));
3105 }
3106
3107 LOG(("PATCH DIRECTORY " LOG_S, gPatchDirPath));
3108 LOG(("INSTALLATION DIRECTORY " LOG_S, gInstallDirPath));
3109 LOG(("WORKING DIRECTORY " LOG_S, gWorkingDirPath));
3110
3111#ifdef _WIN32
3112 if (_wcsnicmp(gWorkingDirPath, gInstallDirPath, MAX_PATH) != 0)
3113 {
3115 {
3116 WriteStatusFile(INVALID_APPLYTO_DIR_ERROR);
3117 LOG(("Installation directory and working directory must be the same "
3118 "for non-staged updates. Exiting."));
3119 LogFinish();
3120 return 1;
3121 }
3122
3123 NS_tchar workingDirParent[MAX_PATH];
3124 NS_tsnprintf(workingDirParent,
3125 sizeof(workingDirParent) / sizeof(workingDirParent[0]),
3126 NS_T("%s"), gWorkingDirPath);
3127 if (!PathRemoveFileSpecW(workingDirParent))
3128 {
3129 WriteStatusFile(REMOVE_FILE_SPEC_ERROR);
3130 LOG(("Error calling PathRemoveFileSpecW: %d", GetLastError()));
3131 LogFinish();
3132 return 1;
3133 }
3134
3135 if (_wcsnicmp(workingDirParent, gInstallDirPath, MAX_PATH) != 0)
3136 {
3137 WriteStatusFile(INVALID_APPLYTO_DIR_STAGED_ERROR);
3138 LOG(("The apply-to directory must be the same as or "
3139 "a child of the installation directory! Exiting."));
3140 LogFinish();
3141 return 1;
3142 }
3143 }
3144#endif
3145
3146
3147#ifdef _WIN32
3148 if (pid > 0)
3149 {
3150 HANDLE parent = OpenProcess(SYNCHRONIZE, false, (DWORD) pid);
3151 // May return nullptr if the parent process has already gone away.
3152 // Otherwise, wait for the parent process to exit before starting the
3153 // update.
3154 if (parent)
3155 {
3156 DWORD waitTime = PARENT_WAIT;
3157 DWORD result = WaitForSingleObject(parent, waitTime);
3158 CloseHandle(parent);
3159 if (result != WAIT_OBJECT_0)
3160 return 1;
3161 }
3162 }
3163#else
3164 if (pid > 0)
3165 waitpid(pid, nullptr, 0);
3166#endif
3167
3168#if defined(_WIN32)
3169#ifdef MAINTENANCE_SERVICE
3170 sUsingService = EnvHasValue("USING_SERVICE");
3171 putenv(const_cast<char*>("USING_SERVICE="));
3172#endif
3173 // lastFallbackError keeps track of the last error for the service not being
3174 // used, in case of an error when fallback is not enabled we write the
3175 // error to the update.status file.
3176 // When fallback is disabled (MOZ_NO_SERVICE_FALLBACK does not exist) then
3177 // we will instead fallback to not using the service and display a UAC prompt.
3178 int lastFallbackError = FALLBACKKEY_UNKNOWN_ERROR;
3179
3180 // Launch a second instance of the updater with the runas verb on Windows
3181 // when write access is denied to the installation directory.
3182 HANDLE updateLockFileHandle = INVALID_HANDLE_VALUE;
3183 NS_tchar elevatedLockFilePath[MAXPATHLEN] = {NS_T('\0')};
3184 if (!sUsingService &&
3185 (argc > callbackIndex || sStagedUpdate || sReplaceRequest))
3186 {
3187 NS_tchar updateLockFilePath[MAXPATHLEN];
3188 if (sStagedUpdate)
3189 {
3190 // When staging an update, the lock file is:
3191 // <install_dir>\updated.update_in_progress.lock
3192 NS_tsnprintf(updateLockFilePath,
3193 sizeof(updateLockFilePath)/sizeof(updateLockFilePath[0]),
3194 NS_T("%s/updated.update_in_progress.lock"), gInstallDirPath);
3195 }
3196 else if (sReplaceRequest)
3197 {
3198 // When processing a replace request, the lock file is:
3199 // <install_dir>\..\moz_update_in_progress.lock
3200 NS_tchar installDir[MAXPATHLEN];
3201 NS_tstrcpy(installDir, gInstallDirPath);
3202 NS_tchar *slash = (NS_tchar *) NS_tstrrchr(installDir, NS_SLASH);
3203 *slash = NS_T('\0');
3204 NS_tsnprintf(updateLockFilePath,
3205 sizeof(updateLockFilePath)/sizeof(updateLockFilePath[0]),
3206 NS_T("%s\\moz_update_in_progress.lock"), installDir);
3207 }
3208 else
3209 {
3210 // In the non-staging update case, the lock file is:
3211 // <install_dir><app_name>.exe.update_in_progress.lock
3212 NS_tsnprintf(updateLockFilePath,
3213 sizeof(updateLockFilePath)/sizeof(updateLockFilePath[0]),
3214 NS_T("%s.update_in_progress.lock"), argv[callbackIndex]);
3215 }
3216
3217 // The update_in_progress.lock file should only exist during an update. In
3218 // case it exists attempt to remove it and exit if that fails to prevent
3219 // simultaneous updates occurring.
3220 if (!_waccess(updateLockFilePath, F_OK) &&
3221 NS_tremove(updateLockFilePath) != 0)
3222 {
3223 // Try to fall back to the old way of doing updates if a staged
3224 // update fails.
3226 {
3227 // Note that this could fail, but if it does, there isn't too much we
3228 // can do in order to recover anyways.
3229 WriteStatusFile("pending");
3230 }
3231 LOG(("Update already in progress! Exiting"));
3232 return 1;
3233 }
3234
3235 updateLockFileHandle = CreateFileW(updateLockFilePath,
3236 GENERIC_READ | GENERIC_WRITE,
3237 0,
3238 nullptr,
3239 OPEN_ALWAYS,
3240 FILE_FLAG_DELETE_ON_CLOSE,
3241 nullptr);
3242
3243 NS_tsnprintf(elevatedLockFilePath,
3244 sizeof(elevatedLockFilePath)/sizeof(elevatedLockFilePath[0]),
3245 NS_T("%s/update_elevated.lock"), gPatchDirPath);
3246
3247 // Even if a file has no sharing access, you can still get its attributes
3248 bool startedFromUnelevatedUpdater =
3249 GetFileAttributesW(elevatedLockFilePath) != INVALID_FILE_ATTRIBUTES;
3250
3251 // If we're running from the service, then we were started with the same
3252 // token as the service so the permissions are already dropped. If we're
3253 // running from an elevated updater that was started from an unelevated
3254 // updater, then we drop the permissions here. We do not drop the
3255 // permissions on the originally called updater because we use its token
3256 // to start the callback application.
3257 if (startedFromUnelevatedUpdater)
3258 {
3259 // Disable every privilege we don't need. Processes started using
3260 // CreateProcess will use the same token as this process.
3262 }
3263
3264 if (updateLockFileHandle == INVALID_HANDLE_VALUE ||
3265 (useService && testOnlyFallbackKeyExists && noServiceFallback))
3266 {
3267 if (!_waccess(elevatedLockFilePath, F_OK) &&
3268 NS_tremove(elevatedLockFilePath) != 0)
3269 {
3270 fprintf(stderr, "Unable to create elevated lock file! Exiting\n");
3271 return 1;
3272 }
3273
3274 HANDLE elevatedFileHandle;
3275 elevatedFileHandle = CreateFileW(elevatedLockFilePath,
3276 GENERIC_READ | GENERIC_WRITE,
3277 0,
3278 nullptr,
3279 OPEN_ALWAYS,
3280 FILE_FLAG_DELETE_ON_CLOSE,
3281 nullptr);
3282
3283 if (elevatedFileHandle == INVALID_HANDLE_VALUE)
3284 {
3285 LOG(("Unable to create elevated lock file! Exiting"));
3286 return 1;
3287 }
3288
3289 wchar_t *cmdLine = MakeCommandLine(argc - 1, argv + 1);
3290 if (!cmdLine)
3291 {
3292 CloseHandle(elevatedFileHandle);
3293 return 1;
3294 }
3295
3296 // Make sure the path to the updater to use for the update is on local.
3297 // We do this check to make sure that file locking is available for
3298 // race condition security checks.
3299 if (useService)
3300 {
3301 BOOL isLocal = FALSE;
3302 useService = IsLocalFile(argv[0], isLocal) && isLocal;
3303 }
3304
3305 // If we have unprompted elevation we should NOT use the service
3306 // for the update. Service updates happen with the SYSTEM account
3307 // which has more privs than we need to update with.
3308 // Windows 8 provides a user interface so users can configure this
3309 // behavior and it can be configured in the registry in all Windows
3310 // versions that support UAC.
3311 if (useService)
3312 {
3313 BOOL unpromptedElevation;
3314 if (IsUnpromptedElevation(unpromptedElevation))
3315 {
3316 useService = !unpromptedElevation;
3317 }
3318 }
3319
3320 // Make sure the service registry entries for the installation path
3321 // are available. If not don't use the service.
3322 if (useService)
3323 {
3324 WCHAR maintenanceServiceKey[MAX_PATH + 1];
3325 // TODO: moggi: needs adaptation for LibreOffice
3326 // Most likely the registry part is not correct yet
3328 maintenanceServiceKey))
3329 {
3330 HKEY baseKey = nullptr;
3331 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
3332 maintenanceServiceKey, 0,
3333 KEY_READ | KEY_WOW64_64KEY,
3334 &baseKey) == ERROR_SUCCESS)
3335 {
3336 RegCloseKey(baseKey);
3337 }
3338 else
3339 {
3340#ifdef TEST_UPDATER
3341 useService = testOnlyFallbackKeyExists;
3342#endif
3343 if (!useService)
3344 {
3345 lastFallbackError = FALLBACKKEY_NOKEY_ERROR;
3346 }
3347 }
3348 }
3349 else
3350 {
3351 useService = false;
3352 lastFallbackError = FALLBACKKEY_REGPATH_ERROR;
3353 }
3354 }
3355
3356 // Originally we used to write "pending" to update.status before
3357 // launching the service command. This is no longer needed now
3358 // since the service command is launched from updater.exe. If anything
3359 // fails in between, we can fall back to using the normal update process
3360 // on our own.
3361
3362 // If we still want to use the service try to launch the service
3363 // command for the update.
3364 if (useService)
3365 {
3366 // If the update couldn't be started, then set useService to false so
3367 // we do the update the old way.
3368 DWORD ret = LaunchServiceSoftwareUpdateCommand(argc, (LPCWSTR *)argv);
3369 useService = (ret == ERROR_SUCCESS);
3370 // If the command was launched then wait for the service to be done.
3371 if (useService)
3372 {
3373 bool showProgressUI = false;
3374 // Never show the progress UI when staging updates.
3375 if (!sStagedUpdate)
3376 {
3377 // We need to call this separately instead of allowing ShowProgressUI
3378 // to initialize the strings because the service will move the
3379 // ini file out of the way when running updater.
3380 showProgressUI = !InitProgressUIStrings();
3381 }
3382
3383 // Wait for the service to stop for 5 seconds. If the service
3384 // has still not stopped then show an indeterminate progress bar.
3385 DWORD lastState = WaitForServiceStop(SVC_NAME, 5);
3386 if (lastState != SERVICE_STOPPED)
3387 {
3388 std::thread waitThread(WaitForServiceFinishThread, nullptr);
3389 if (showProgressUI)
3390 {
3391 ShowProgressUI(true, false);
3392 }
3393 waitThread.join();
3394 }
3395
3396 lastState = WaitForServiceStop(SVC_NAME, 1);
3397 if (lastState != SERVICE_STOPPED)
3398 {
3399 // If the service doesn't stop after 10 minutes there is
3400 // something seriously wrong.
3401 lastFallbackError = FALLBACKKEY_SERVICE_NO_STOP_ERROR;
3402 useService = false;
3403 }
3404 }
3405 else
3406 {
3407 lastFallbackError = FALLBACKKEY_LAUNCH_ERROR;
3408 }
3409 }
3410
3411 // If the service can't be used when staging and update, make sure that
3412 // the UAC prompt is not shown! In this case, just set the status to
3413 // pending and the update will be applied during the next startup.
3414 if (!useService && sStagedUpdate)
3415 {
3416 if (updateLockFileHandle != INVALID_HANDLE_VALUE)
3417 {
3418 CloseHandle(updateLockFileHandle);
3419 }
3420 WriteStatusFile("pending");
3421 return 0;
3422 }
3423
3424 // If we started the service command, and it finished, check the
3425 // update.status file to make sure it succeeded, and if it did
3426 // we need to manually start the PostUpdate process from the
3427 // current user's session of this unelevated updater.exe the
3428 // current process is running as.
3429 // Note that we don't need to do this if we're just staging the update,
3430 // as the PostUpdate step runs when performing the replacing in that case.
3431 if (useService && !sStagedUpdate)
3432 {
3433 bool updateStatusSucceeded = false;
3434 if (IsUpdateStatusSucceeded(updateStatusSucceeded) &&
3435 updateStatusSucceeded)
3436 {
3438 {
3439 fprintf(stderr, "The post update process which runs as the user"
3440 " for service update could not be launched.");
3441 }
3442 }
3443 }
3444
3445 // If we didn't want to use the service at all, or if an update was
3446 // already happening, or launching the service command failed, then
3447 // launch the elevated updater.exe as we do without the service.
3448 // We don't launch the elevated updater in the case that we did have
3449 // write access all along because in that case the only reason we're
3450 // using the service is because we are testing.
3451 if (!useService && !noServiceFallback &&
3452 updateLockFileHandle == INVALID_HANDLE_VALUE)
3453 {
3454 SHELLEXECUTEINFO sinfo;
3455 memset(&sinfo, 0, sizeof(SHELLEXECUTEINFO));
3456 sinfo.cbSize = sizeof(SHELLEXECUTEINFO);
3457 sinfo.fMask = SEE_MASK_FLAG_NO_UI |
3458 SEE_MASK_FLAG_DDEWAIT |
3459 SEE_MASK_NOCLOSEPROCESS;
3460 sinfo.hwnd = nullptr;
3461 sinfo.lpFile = argv[0];
3462 sinfo.lpParameters = cmdLine;
3463 sinfo.lpVerb = L"runas";
3464 sinfo.nShow = SW_SHOWNORMAL;
3465
3466 bool result = ShellExecuteEx(&sinfo);
3467 free(cmdLine);
3468
3469 if (result)
3470 {
3471 WaitForSingleObject(sinfo.hProcess, INFINITE);
3472 CloseHandle(sinfo.hProcess);
3473 }
3474 else
3475 {
3477 }
3478 }
3479
3480 if (argc > callbackIndex)
3481 {
3482 LaunchCallbackApp(argv[5], argc - callbackIndex,
3483 argv + callbackIndex, sUsingService);
3484 }
3485
3486 CloseHandle(elevatedFileHandle);
3487
3488 if (!useService && !noServiceFallback &&
3489 INVALID_HANDLE_VALUE == updateLockFileHandle)
3490 {
3491 // We didn't use the service and we did run the elevated updater.exe.
3492 // The elevated updater.exe is responsible for writing out the
3493 // update.status file.
3494 return 0;
3495 }
3496 else if (useService)
3497 {
3498 // The service command was launched. The service is responsible for
3499 // writing out the update.status file.
3500 if (updateLockFileHandle != INVALID_HANDLE_VALUE)
3501 {
3502 CloseHandle(updateLockFileHandle);
3503 }
3504 return 0;
3505 }
3506 else
3507 {
3508 // Otherwise the service command was not launched at all.
3509 // We are only reaching this code path because we had write access
3510 // all along to the directory and a fallback key existed, and we
3511 // have fallback disabled (MOZ_NO_SERVICE_FALLBACK env var exists).
3512 // We only currently use this env var from XPCShell tests.
3513 CloseHandle(updateLockFileHandle);
3514 WriteStatusFile(lastFallbackError);
3515 return 0;
3516 }
3517 }
3518 }
3519#endif
3520
3521 if (sStagedUpdate)
3522 {
3523 // When staging updates, blow away the old installation directory and create
3524 // it from scratch.
3526 }
3527 if (!sReplaceRequest)
3528 {
3529 // Try to create the destination directory if it doesn't exist
3530 int rv = NS_tmkdir(gWorkingDirPath, 0755);
3531 if (rv != OK && errno != EEXIST)
3532 {
3533#ifdef MACOSX
3534 if (isElevated)
3535 {
3536 freeArguments(argc, argv);
3537 CleanupElevatedMacUpdate(true);
3538 }
3539#endif
3540 return 1;
3541 }
3542 }
3543
3544#ifdef _WIN32
3545 // For replace requests, we don't need to do any real updates, so this is not
3546 // necessary.
3547 if (!sReplaceRequest)
3548 {
3549 // Allocate enough space for the length of the path an optional additional
3550 // trailing slash and null termination.
3551 NS_tchar *destpath = (NS_tchar *) malloc((NS_tstrlen(gWorkingDirPath) + 2) * sizeof(NS_tchar));
3552 if (!destpath)
3553 return 1;
3554
3555 NS_tchar *c = destpath;
3558 if (gWorkingDirPath[NS_tstrlen(gWorkingDirPath) - 1] != NS_T('/') &&
3560 {
3561 NS_tstrcat(c, NS_T("/"));
3562 c += NS_tstrlen(NS_T("/"));
3563 }
3564 *c = NS_T('\0');
3565 c++;
3566
3567 gDestPath = destpath;
3568 }
3569
3570 NS_tchar applyDirLongPath[MAXPATHLEN];
3571 if (!GetLongPathNameW(gWorkingDirPath, applyDirLongPath,
3572 sizeof(applyDirLongPath)/sizeof(applyDirLongPath[0])))
3573 {
3574 LOG(("NS_main: unable to find apply to dir: " LOG_S, gWorkingDirPath));
3575 LogFinish();
3577 EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
3578 if (argc > callbackIndex)
3579 {
3580 LaunchCallbackApp(argv[5], argc - callbackIndex,
3581 argv + callbackIndex, sUsingService);
3582 }
3583 return 1;
3584 }
3585
3586 HANDLE callbackFile = INVALID_HANDLE_VALUE;
3587 if (argc > callbackIndex)
3588 {
3589 // If the callback executable is specified it must exist for a successful
3590 // update. It is important we null out the whole buffer here because later
3591 // we make the assumption that the callback application is inside the
3592 // apply-to dir. If we don't have a fully null'ed out buffer it can lead
3593 // to stack corruption which causes crashes and other problems.
3594 NS_tchar callbackLongPath[MAXPATHLEN];
3595 ZeroMemory(callbackLongPath, sizeof(callbackLongPath));
3596 NS_tchar *targetPath = argv[callbackIndex];
3597 NS_tchar buffer[MAXPATHLEN * 2] = { NS_T('\0') };
3598 size_t bufferLeft = MAXPATHLEN * 2;
3599 if (sReplaceRequest)
3600 {
3601 // In case of replace requests, we should look for the callback file in
3602 // the destination directory.
3603 size_t commonPrefixLength = PathCommonPrefixW(argv[callbackIndex],
3605 nullptr);
3606 NS_tchar *p = buffer;
3607 NS_tstrncpy(p, argv[callbackIndex], commonPrefixLength);
3608 p += commonPrefixLength;
3609 bufferLeft -= commonPrefixLength;
3610 NS_tstrncpy(p, gInstallDirPath + commonPrefixLength, bufferLeft);
3611
3612 size_t len = NS_tstrlen(gInstallDirPath + commonPrefixLength);
3613 p += len;
3614 bufferLeft -= len;
3615 *p = NS_T('\\');
3616 ++p;
3617 bufferLeft--;
3618 *p = NS_T('\0');
3619 NS_tchar installDir[MAXPATHLEN];
3620 NS_tstrcpy(installDir, gInstallDirPath);
3621 size_t callbackPrefixLength = PathCommonPrefixW(argv[callbackIndex],
3622 installDir,
3623 nullptr);
3624 NS_tstrncpy(p, argv[callbackIndex] + std::max(callbackPrefixLength,
3625 commonPrefixLength), bufferLeft);
3626 targetPath = buffer;
3627 }
3628 if (!GetLongPathNameW(targetPath, callbackLongPath,
3629 sizeof(callbackLongPath)/sizeof(callbackLongPath[0])))
3630 {
3631 LOG(("NS_main: unable to find callback file: " LOG_S, targetPath));
3632 LogFinish();
3634 EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
3635 if (argc > callbackIndex)
3636 {
3637 LaunchCallbackApp(argv[5],
3638 argc - callbackIndex,
3639 argv + callbackIndex,
3641 }
3642 return 1;
3643 }
3644
3645 // Doing this is only necessary when we're actually applying a patch.
3646 if (!sReplaceRequest)
3647 {
3648 int len = NS_tstrlen(applyDirLongPath);
3649 NS_tchar *s = callbackLongPath;
3650 NS_tchar *d = gCallbackRelPath;
3651 // advance to the apply to directory and advance past the trailing backslash
3652 // if present.
3653 s += len;
3654 if (*s == NS_T('\\'))
3655 ++s;
3656
3657 // Copy the string and replace backslashes with forward slashes along the
3658 // way.
3659 do
3660 {
3661 if (*s == NS_T('\\'))
3662 *d = NS_T('/');
3663 else
3664 *d = *s;
3665 ++s;
3666 ++d;
3667 }
3668 while (*s);
3669 *d = NS_T('\0');
3670 ++d;
3671
3672 // Make a copy of the callback executable so it can be read when patching.
3673 NS_tsnprintf(gCallbackBackupPath,
3674 sizeof(gCallbackBackupPath)/sizeof(gCallbackBackupPath[0]),
3675 NS_T("%s" CALLBACK_BACKUP_EXT), argv[callbackIndex]);
3676 NS_tremove(gCallbackBackupPath);
3677 if (!CopyFileW(argv[callbackIndex], gCallbackBackupPath, true))
3678 {
3679 DWORD copyFileError = GetLastError();
3680 LOG(("NS_main: failed to copy callback file " LOG_S
3681 " into place at " LOG_S, argv[callbackIndex], gCallbackBackupPath));
3682 LogFinish();
3683 if (copyFileError == ERROR_ACCESS_DENIED)
3684 {
3686 }
3687 else
3688 {
3690 }
3691
3692 EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
3693 LaunchCallbackApp(argv[callbackIndex],
3694 argc - callbackIndex,
3695 argv + callbackIndex,
3697 return 1;
3698 }
3699
3700 // Since the process may be signaled as exited by WaitForSingleObject before
3701 // the release of the executable image try to lock the main executable file
3702 // multiple times before giving up. If we end up giving up, we won't
3703 // fail the update.
3704 const int max_retries = 10;
3705 int retries = 1;
3706 DWORD lastWriteError = 0;
3707 do
3708 {
3709 // By opening a file handle without FILE_SHARE_READ to the callback
3710 // executable, the OS will prevent launching the process while it is
3711 // being updated.
3712 callbackFile = CreateFileW(targetPath,
3713 DELETE | GENERIC_WRITE,
3714 // allow delete, rename, and write
3715 FILE_SHARE_DELETE | FILE_SHARE_WRITE,
3716 nullptr, OPEN_EXISTING, 0, nullptr);
3717 if (callbackFile != INVALID_HANDLE_VALUE)
3718 break;
3719
3720 lastWriteError = GetLastError();
3721 LOG(("NS_main: callback app file open attempt %d failed. " \
3722 "File: " LOG_S ". Last error: %d", retries,
3723 targetPath, lastWriteError));
3724
3725 Sleep(100);
3726 }
3727 while (++retries <= max_retries);
3728
3729 // CreateFileW will fail if the callback executable is already in use.
3730 if (callbackFile == INVALID_HANDLE_VALUE)
3731 {
3732 // Only fail the update if the last error was not a sharing violation.
3733 if (lastWriteError != ERROR_SHARING_VIOLATION)
3734 {
3735 LOG(("NS_main: callback app file in use, failed to exclusively open " \
3736 "executable file: " LOG_S, argv[callbackIndex]));
3737 LogFinish();
3738 if (lastWriteError == ERROR_ACCESS_DENIED)
3739 {
3741 }
3742 else
3743 {
3745 }
3746
3747 NS_tremove(gCallbackBackupPath);
3748 EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
3749 LaunchCallbackApp(argv[5],
3750 argc - callbackIndex,
3751 argv + callbackIndex,
3753 return 1;
3754 }
3755 LOG(("NS_main: callback app file in use, continuing without " \
3756 "exclusive access for executable file: " LOG_S,
3757 argv[callbackIndex]));
3758 }
3759 }
3760 }
3761
3762 // DELETE_DIR is not required when performing a staged update or replace
3763 // request; it can be used during a replace request but then it doesn't
3764 // use gDeleteDirPath.
3766 {
3767 // The directory to move files that are in use to on Windows. This directory
3768 // will be deleted after the update is finished, on OS reboot using
3769 // MoveFileEx if it contains files that are in use, or by the post update
3770 // process after the update finishes. On Windows when performing a normal
3771 // update (e.g. the update is not a staged update and is not a replace
3772 // request) gWorkingDirPath is the same as gInstallDirPath and
3773 // gWorkingDirPath is used because it is the destination directory.
3774 NS_tsnprintf(gDeleteDirPath,
3775 sizeof(gDeleteDirPath) / sizeof(gDeleteDirPath[0]),
3776 NS_T("%s/%s"), gWorkingDirPath, DELETE_DIR);
3777
3778 if (NS_taccess(gDeleteDirPath, F_OK))
3779 {
3780 NS_tmkdir(gDeleteDirPath, 0755);
3781 }
3782 }
3783#endif /* _WIN32 */
3784
3785 // Run update process on a background thread. ShowProgressUI may return
3786 // before QuitProgressUI has been called, so wait for UpdateThreadFunc to
3787 // terminate. Avoid showing the progress UI when staging an update, or if this
3788 // is an elevated process on OSX.
3789 std::thread t(UpdateThreadFunc, nullptr);
3791#ifdef XP_MACOSX
3792 && !isElevated
3793#endif
3794 )
3795 {
3797 }
3798 t.join();
3799
3800#ifdef _WIN32
3801 if (argc > callbackIndex && !sReplaceRequest)
3802 {
3803 if (callbackFile != INVALID_HANDLE_VALUE)
3804 {
3805 CloseHandle(callbackFile);
3806 }
3807 // Remove the copy of the callback executable.
3808 NS_tremove(gCallbackBackupPath);
3809 }
3810
3811 if (!sStagedUpdate && !sReplaceRequest && _wrmdir(gDeleteDirPath))
3812 {
3813 LOG(("NS_main: unable to remove directory: " LOG_S ", err: %d",
3814 DELETE_DIR, errno));
3815 // The directory probably couldn't be removed due to it containing files
3816 // that are in use and will be removed on OS reboot. The call to remove the
3817 // directory on OS reboot is done after the calls to remove the files so the
3818 // files are removed first on OS reboot since the directory must be empty
3819 // for the directory removal to be successful. The MoveFileEx call to remove
3820 // the directory on OS reboot will fail if the process doesn't have write
3821 // access to the HKEY_LOCAL_MACHINE registry key but this is ok since the
3822 // installer / uninstaller will delete the directory along with its contents
3823 // after an update is applied, on reinstall, and on uninstall.
3824 if (MoveFileEx(gDeleteDirPath, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT))
3825 {
3826 LOG(("NS_main: directory will be removed on OS reboot: " LOG_S,
3827 DELETE_DIR));
3828 }
3829 else
3830 {
3831 LOG(("NS_main: failed to schedule OS reboot removal of " \
3832 "directory: " LOG_S, DELETE_DIR));
3833 }
3834 }
3835#endif /* _WIN32 */
3836
3837
3838#ifdef MACOSX
3839 // When the update is successful remove the precomplete file in the root of
3840 // the application bundle and move the distribution directory from
3841 // Contents/MacOS to Contents/Resources and if both exist delete the
3842 // directory under Contents/MacOS (see Bug 1068439).
3843 if (gSucceeded && !sStagedUpdate)
3844 {
3845 NS_tchar oldPrecomplete[MAXPATHLEN];
3846 NS_tsnprintf(oldPrecomplete, sizeof(oldPrecomplete)/sizeof(oldPrecomplete[0]),
3847 NS_T("%s/precomplete"), gInstallDirPath);
3848 NS_tremove(oldPrecomplete);
3849
3850 NS_tchar oldDistDir[MAXPATHLEN];
3851 NS_tsnprintf(oldDistDir, sizeof(oldDistDir)/sizeof(oldDistDir[0]),
3852 NS_T("%s/Contents/MacOS/distribution"), gInstallDirPath);
3853 int rv = NS_taccess(oldDistDir, F_OK);
3854 if (!rv)
3855 {
3856 NS_tchar newDistDir[MAXPATHLEN];
3857 NS_tsnprintf(newDistDir, sizeof(newDistDir)/sizeof(newDistDir[0]),
3858 NS_T("%s/Contents/Resources/distribution"), gInstallDirPath);
3859 rv = NS_taccess(newDistDir, F_OK);
3860 if (!rv)
3861 {
3862 LOG(("New distribution directory already exists... removing old " \
3863 "distribution directory: " LOG_S, oldDistDir));
3864 rv = ensure_remove_recursive(oldDistDir);
3865 if (rv)
3866 {
3867 LOG(("Removing old distribution directory failed - err: %d", rv));
3868 }
3869 }
3870 else
3871 {
3872 LOG(("Moving old distribution directory to new location. src: " LOG_S \
3873 ", dst:" LOG_S, oldDistDir, newDistDir));
3874 rv = rename_file(oldDistDir, newDistDir, true);
3875 if (rv)
3876 {
3877 LOG(("Moving old distribution directory to new location failed - " \
3878 "err: %d", rv));
3879 }
3880 }
3881 }
3882 }
3883
3884 if (isElevated)
3885 {
3886 SetGroupOwnershipAndPermissions(gInstallDirPath);
3887 freeArguments(argc, argv);
3888 CleanupElevatedMacUpdate(false);
3889 }
3890 else if (IsOwnedByGroupAdmin(gInstallDirPath))
3891 {
3892 // If the group ownership of the Firefox .app bundle was set to the "admin"
3893 // group during a previous elevated update, we need to ensure that all files
3894 // in the bundle have group ownership of "admin" as well as write permission
3895 // for the group to not break updates in the future.
3896 SetGroupOwnershipAndPermissions(gInstallDirPath);
3897 }
3898#endif /* MACOSX */
3899
3900 LogFinish();
3901
3902 int retVal = LaunchCallbackAndPostProcessApps(argc, argv, callbackIndex
3903#ifdef _WIN32
3904 , elevatedLockFilePath
3905 , updateLockFileHandle
3906#elif defined(MACOSX)
3907 , isElevated
3908#endif
3909 );
3910
3911 return retVal ? retVal : (gSucceeded ? 0 : 1);
3912}
3913
3915{
3916public:
3917 ActionList() : mFirst(nullptr), mLast(nullptr), mCount(0) { }
3918 ~ActionList();
3919
3920 void Append(Action* action);
3921 int Prepare();
3922 int Execute();
3923 void Finish(int status);
3924
3925private:
3929};
3930
3932{
3933 Action* a = mFirst;
3934 while (a)
3935 {
3936 Action *b = a;
3937 a = a->mNext;
3938 delete b;
3939 }
3940}
3941
3942void
3944{
3945 if (mLast)
3946 mLast->mNext = action;
3947 else
3948 mFirst = action;
3949
3950 mLast = action;
3951 mCount++;
3952}
3953
3954int
3956{
3957 // If the action list is empty then we should fail in order to signal that
3958 // something has gone wrong. Otherwise we report success when nothing is
3959 // actually done. See bug 327140.
3960 if (mCount == 0)
3961 {
3962 LOG(("empty action list"));
3964 }
3965
3966 Action *a = mFirst;
3967 int i = 0;
3968 while (a)
3969 {
3970 int rv = a->Prepare();
3971 if (rv)
3972 return rv;
3973
3974 float percent = float(++i) / float(mCount);
3976
3977 a = a->mNext;
3978 }
3979
3980 return OK;
3981}
3982
3983int
3985{
3986 int currentProgress = 0, maxProgress = 0;
3987 Action *a = mFirst;
3988 while (a)
3989 {
3990 maxProgress += a->mProgressCost;
3991 a = a->mNext;
3992 }
3993
3994 a = mFirst;
3995 while (a)
3996 {
3997 int rv = a->Execute();
3998 if (rv)
3999 {
4000 LOG(("### execution failed"));
4001 return rv;
4002 }
4003
4004 currentProgress += a->mProgressCost;
4005 float percent = float(currentProgress) / float(maxProgress);
4008
4009 a = a->mNext;
4010 }
4011
4012 return OK;
4013}
4014
4015void
4017{
4018 Action *a = mFirst;
4019 int i = 0;
4020 while (a)
4021 {
4022 a->Finish(status);
4023
4024 float percent = float(++i) / float(mCount);
4028
4029 a = a->mNext;
4030 }
4031
4032 if (status == OK)
4033 gSucceeded = true;
4034}
4035
4036
4037#ifdef _WIN32
4038int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
4039{
4040 int rv = OK;
4041 WIN32_FIND_DATAW finddata;
4042 HANDLE hFindFile;
4043 NS_tchar searchspec[MAXPATHLEN];
4044 NS_tchar foundpath[MAXPATHLEN];
4045
4046 NS_tsnprintf(searchspec, sizeof(searchspec)/sizeof(searchspec[0]),
4047 NS_T("%s*"), dirpath);
4048 std::unique_ptr<const NS_tchar[]> pszSpec(new_absolute_path(searchspec));
4049
4050 hFindFile = FindFirstFileW(pszSpec.get(), &finddata);
4051 if (hFindFile != INVALID_HANDLE_VALUE)
4052 {
4053 do
4054 {
4055 // Don't process the current or parent directory.
4056 if (NS_tstrcmp(finddata.cFileName, NS_T(".")) == 0 ||
4057 NS_tstrcmp(finddata.cFileName, NS_T("..")) == 0)
4058 continue;
4059
4060 NS_tsnprintf(foundpath, sizeof(foundpath)/sizeof(foundpath[0]),
4061 NS_T("%s%s"), dirpath, finddata.cFileName);
4062 if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
4063 {
4064 NS_tsnprintf(foundpath, sizeof(foundpath)/sizeof(foundpath[0]),
4065 NS_T("%s/"), foundpath);
4066 // Recurse into the directory.
4067 rv = add_dir_entries(foundpath, list);
4068 if (rv)
4069 {
4070 LOG(("add_dir_entries error: " LOG_S ", err: %d", foundpath, rv));
4071 return rv;
4072 }
4073 }
4074 else
4075 {
4076 // Add the file to be removed to the ActionList.
4077 NS_tchar *quotedpath = get_quoted_path(foundpath);
4078 if (!quotedpath)
4079 return PARSE_ERROR;
4080
4081 Action *action = new RemoveFile();
4082 rv = action->Parse(quotedpath);
4083 if (rv)
4084 {
4085 LOG(("add_dir_entries Parse error on recurse: " LOG_S ", err: %d",
4086 quotedpath, rv));
4087 return rv;
4088 }
4089 free(quotedpath);
4090
4091 list->Append(action);
4092 }
4093 }
4094 while (FindNextFileW(hFindFile, &finddata) != 0);
4095
4096 FindClose(hFindFile);
4097 {
4098 // Add the directory to be removed to the ActionList.
4099 NS_tchar *quotedpath = get_quoted_path(dirpath);
4100 if (!quotedpath)
4101 return PARSE_ERROR;
4102
4103 Action *action = new RemoveDir();
4104 rv = action->Parse(quotedpath);
4105 if (rv)
4106 LOG(("add_dir_entries Parse error on close: " LOG_S ", err: %d",
4107 quotedpath, rv));
4108 else
4109 list->Append(action);
4110 free(quotedpath);
4111 }
4112 }
4113
4114 return rv;
4115}
4116
4117#elif defined(__sun)
4118int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
4119{
4120 int rv = OK;
4121 NS_tchar foundpath[MAXPATHLEN];
4122 struct
4123 {
4124 dirent dent_buffer;
4125 char chars[MAXNAMLEN];
4126 } ent_buf;
4127 struct dirent* ent;
4128 std::unique_ptr<NS_tchar[]> searchpath(new_absolute_path(dirpath));
4129
4130 DIR* dir = opendir(searchpath.get());
4131 if (!dir)
4132 {
4133 LOG(("add_dir_entries error on opendir: " LOG_S ", err: %d", searchpath.get(),
4134 errno));
4136 }
4137
4138 while (readdir_r(dir, (dirent *)&ent_buf, &ent) == 0 && ent)
4139 {
4140 if ((strcmp(ent->d_name, ".") == 0) ||
4141 (strcmp(ent->d_name, "..") == 0))
4142 continue;
4143
4144 NS_tsnprintf(foundpath, sizeof(foundpath)/sizeof(foundpath[0]),
4145 NS_T("%s%s"), searchpath.get(), ent->d_name);
4146 struct stat64 st_buf;
4147 int test = stat64(foundpath, &st_buf);
4148 if (test)
4149 {
4150 closedir(dir);
4152 }
4153 if (S_ISDIR(st_buf.st_mode))
4154 {
4155 NS_tsnprintf(foundpath, sizeof(foundpath)/sizeof(foundpath[0]),
4156 NS_T("%s/"), foundpath);
4157 // Recurse into the directory.
4158 rv = add_dir_entries(foundpath, list);
4159 if (rv)
4160 {
4161 LOG(("add_dir_entries error: " LOG_S ", err: %d", foundpath, rv));
4162 closedir(dir);
4163 return rv;
4164 }
4165 }
4166 else
4167 {
4168 // Add the file to be removed to the ActionList.
4169 NS_tchar *quotedpath = get_quoted_path(get_relative_offset(foundpath));
4170 if (!quotedpath)
4171 {
4172 closedir(dir);
4173 return PARSE_ERROR;
4174 }
4175
4176 Action *action = new RemoveFile();
4177 rv = action->Parse(quotedpath);
4178 if (rv)
4179 {
4180 LOG(("add_dir_entries Parse error on recurse: " LOG_S ", err: %d",
4181 quotedpath, rv));
4182 closedir(dir);
4183 return rv;
4184 }
4185
4186 list->Append(action);
4187 }
4188 }
4189 closedir(dir);
4190
4191 // Add the directory to be removed to the ActionList.
4192 NS_tchar *quotedpath = get_quoted_path(get_relative_offset(dirpath));
4193 if (!quotedpath)
4194 return PARSE_ERROR;
4195
4196 Action *action = new RemoveDir();
4197 rv = action->Parse(quotedpath);
4198 if (rv)
4199 {
4200 LOG(("add_dir_entries Parse error on close: " LOG_S ", err: %d",
4201 quotedpath, rv));
4202 }
4203 else
4204 {
4205 list->Append(action);
4206 }
4207
4208 return rv;
4209}
4210
4211#else
4212
4213int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
4214{
4215 int rv = OK;
4216 FTS *ftsdir;
4217 FTSENT *ftsdirEntry;
4218 std::unique_ptr<NS_tchar[]> searchpath(new_absolute_path(dirpath));
4219
4220 // Remove the trailing slash so the paths don't contain double slashes. The
4221 // existence of the slash has already been checked in DoUpdate.
4222 searchpath.get()[NS_tstrlen(searchpath.get()) - 1] = NS_T('\0');
4223 char* const pathargv[] = {searchpath.get(), nullptr};
4224
4225 // FTS_NOCHDIR is used so relative paths from the destination directory are
4226 // returned.
4227 if (!(ftsdir = fts_open(pathargv,
4228 FTS_PHYSICAL | FTS_NOSTAT | FTS_XDEV | FTS_NOCHDIR,
4229 nullptr)))
4231
4232 while ((ftsdirEntry = fts_read(ftsdir)) != nullptr)
4233 {
4234 NS_tchar foundpath[MAXPATHLEN];
4235 NS_tchar *quotedpath = nullptr;
4236 Action *action = nullptr;
4237
4238 switch (ftsdirEntry->fts_info)
4239 {
4240 // Filesystem objects that shouldn't be in the application's directories
4241 case FTS_SL:
4242 case FTS_SLNONE:
4243 case FTS_DEFAULT:
4244 LOG(("add_dir_entries: found a non-standard file: " LOG_S,
4245 ftsdirEntry->fts_path));
4246 /* Fall through */ // and try to remove as a file
4247
4248 // Files
4249 case FTS_F:
4250 case FTS_NSOK:
4251 // Add the file to be removed to the ActionList.
4252 NS_tsnprintf(foundpath, sizeof(foundpath)/sizeof(foundpath[0]),
4253 NS_T("%s"), ftsdirEntry->fts_accpath);
4254 quotedpath = get_quoted_path(get_relative_offset(foundpath));
4255 if (!quotedpath)
4256 {
4258 break;
4259 }
4260 action = new RemoveFile();
4261 rv = action->Parse(quotedpath);
4262 free(quotedpath);
4263 if (!rv)
4264 list->Append(action);
4265 break;
4266
4267 // Directories
4268 case FTS_DP:
4269 rv = OK;
4270 // Add the directory to be removed to the ActionList.
4271 NS_tsnprintf(foundpath, sizeof(foundpath)/sizeof(foundpath[0]),
4272 NS_T("%s/"), ftsdirEntry->fts_accpath);
4273 quotedpath = get_quoted_path(get_relative_offset(foundpath));
4274 if (!quotedpath)
4275 {
4277 break;
4278 }
4279
4280 action = new RemoveDir();
4281 rv = action->Parse(quotedpath);
4282 free(quotedpath);
4283 if (!rv)
4284 list->Append(action);
4285 break;
4286
4287 // Errors
4288 case FTS_DNR:
4289 case FTS_NS:
4290 // ENOENT is an acceptable error for FTS_DNR and FTS_NS and means that
4291 // we're racing with ourselves. Though strange, the entry will be
4292 // removed anyway.
4293 if (ENOENT == ftsdirEntry->fts_errno)
4294 {
4295 rv = OK;
4296 break;
4297 }
4298 // Fall through
4299
4300 case FTS_ERR:
4302 LOG(("add_dir_entries: fts_read() error: " LOG_S ", err: %d",
4303 ftsdirEntry->fts_path, ftsdirEntry->fts_errno));
4304 break;
4305
4306 case FTS_DC:
4308 LOG(("add_dir_entries: fts_read() returned FT_DC: " LOG_S,
4309 ftsdirEntry->fts_path));
4310 break;
4311
4312 default:
4313 // FTS_D is ignored and FTS_DP is used instead (post-order).
4314 rv = OK;
4315 break;
4316 }
4317
4318 if (rv != OK)
4319 break;
4320 }
4321
4322 fts_close(ftsdir);
4323
4324 return rv;
4325}
4326#endif
4327
4328static NS_tchar*
4330{
4331 AutoFile mfile(NS_tfopen(manifest, NS_T("rb")));
4332 if (mfile == nullptr)
4333 {
4334 LOG(("GetManifestContents: error opening manifest file: " LOG_S, manifest));
4335 return nullptr;
4336 }
4337
4338 struct stat ms;
4339 int rv = fstat(fileno((FILE *)mfile), &ms);
4340 if (rv)
4341 {
4342 LOG(("GetManifestContents: error stating manifest file: " LOG_S, manifest));
4343 return nullptr;
4344 }
4345
4346 char *mbuf = (char *) malloc(ms.st_size + 1);
4347 if (!mbuf)
4348 return nullptr;
4349
4350 size_t r = ms.st_size;
4351 char *rb = mbuf;
4352 while (r)
4353 {
4354 const size_t count = std::min<size_t>(SSIZE_MAX, r);
4355 size_t c = fread(rb, 1, count, mfile);
4356 if (c != count)
4357 {
4358 LOG(("GetManifestContents: error reading manifest file: " LOG_S, manifest));
4359 free(mbuf);
4360 return nullptr;
4361 }
4362
4363 r -= c;
4364 rb += c;
4365 }
4366 mbuf[ms.st_size] = '\0';
4367 rb = mbuf;
4368
4369#ifndef _WIN32
4370 return rb;
4371#else
4372 NS_tchar *wrb = (NS_tchar *) malloc((ms.st_size + 1) * sizeof(NS_tchar));
4373 if (!wrb)
4374 {
4375 free(mbuf);
4376 return nullptr;
4377 }
4378
4379 if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, rb, -1, wrb,
4380 ms.st_size + 1))
4381 {
4382 LOG(("GetManifestContents: error converting utf8 to utf16le: %d", GetLastError()));
4383 free(mbuf);
4384 free(wrb);
4385 return nullptr;
4386 }
4387 free(mbuf);
4388
4389 return wrb;
4390#endif
4391}
4392
4394{
4395#ifdef MACOSX
4396 std::unique_ptr<NS_tchar[]> manifestPath(new_absolute_path(
4397 NS_T("Contents/Resources/precomplete")));
4398#else
4399 std::unique_ptr<NS_tchar[]> manifestPath(new_absolute_path(
4400 NS_T("precomplete")));
4401#endif
4402
4403 NS_tchar *rb = GetManifestContents(manifestPath.get());
4404 if (rb == nullptr)
4405 {
4406 LOG(("AddPreCompleteActions: error getting contents of precomplete " \
4407 "manifest"));
4408 // Applications aren't required to have a precomplete manifest. The mar
4409 // generation scripts enforce the presence of a precomplete manifest.
4410 return OK;
4411 }
4412
4413 int rv;
4414 NS_tchar *line;
4415 while ((line = mstrtok(kNL, &rb)) != 0)
4416 {
4417 // skip comments
4418 if (*line == NS_T('#'))
4419 continue;
4420
4421 NS_tchar *token = mstrtok(kWhitespace, &line);
4422 if (!token)
4423 {
4424 LOG(("AddPreCompleteActions: token not found in manifest"));
4425 return PARSE_ERROR;
4426 }
4427
4428 Action *action = nullptr;
4429 if (NS_tstrcmp(token, NS_T("remove")) == 0) // rm file
4430 {
4431 action = new RemoveFile();
4432 }
4433 else if (NS_tstrcmp(token, NS_T("remove-cc")) == 0) // no longer supported
4434 {
4435 continue;
4436 }
4437 else if (NS_tstrcmp(token, NS_T("rmdir")) == 0) // rmdir if empty
4438 {
4439 action = new RemoveDir();
4440 }
4441 else
4442 {
4443 LOG(("AddPreCompleteActions: unknown token: " LOG_S, token));
4444 return PARSE_ERROR;
4445 }
4446
4447 if (!action)
4448 return BAD_ACTION_ERROR;
4449
4450 rv = action->Parse(line);
4451 if (rv)
4452 return rv;
4453
4454 list->Append(action);
4455 }
4456
4457 return OK;
4458}
4459
4460int DoUpdate(ArchiveReader& archiveReader)
4461{
4462 NS_tchar manifest[MAXPATHLEN];
4463 int nWrittenBytes = NS_tsnprintf(manifest, sizeof(manifest)/sizeof(manifest[0]),
4464 NS_T("%s/updating/update.manifest"), gWorkingDirPath);
4465 (void) nWrittenBytes;
4466 ensure_parent_dir(manifest);
4467
4468 // extract the manifest
4469 // TODO: moggi: needs adaptation for LibreOffice
4470 // Why would we need the manifest? Even if we need it why would we need 2?
4471 int rv = archiveReader.ExtractFile("updatev3.manifest", manifest);
4472 if (rv)
4473 {
4474 rv = archiveReader.ExtractFile("updatev2.manifest", manifest);
4475 if (rv)
4476 {
4477 LOG(("DoUpdate: error extracting manifest file"));
4478 return rv;
4479 }
4480 }
4481
4482 NS_tchar *rb = GetManifestContents(manifest);
4483 NS_tremove(manifest);
4484 if (rb == nullptr)
4485 {
4486 LOG(("DoUpdate: error opening manifest file: " LOG_S, manifest));
4487 return READ_ERROR;
4488 }
4489
4490 ActionList list;
4491 NS_tchar *line;
4492 bool isFirstAction = true;
4493
4494 while ((line = mstrtok(kNL, &rb)) != 0)
4495 {
4496 // skip comments
4497 if (*line == NS_T('#'))
4498 continue;
4499
4500 NS_tchar *token = mstrtok(kWhitespace, &line);
4501 if (!token)
4502 {
4503 LOG(("DoUpdate: token not found in manifest"));
4504 return PARSE_ERROR;
4505 }
4506
4507 if (isFirstAction)
4508 {
4509 isFirstAction = false;
4510 // The update manifest isn't required to have a type declaration. The mar
4511 // generation scripts enforce the presence of the type declaration.
4512 if (NS_tstrcmp(token, NS_T("type")) == 0)
4513 {
4514 const NS_tchar *type = mstrtok(kQuote, &line);
4515 LOG(("UPDATE TYPE " LOG_S, type));
4516 if (NS_tstrcmp(type, NS_T("complete")) == 0)
4517 {
4518 rv = AddPreCompleteActions(&list);
4519 if (rv)
4520 return rv;
4521 }
4522 continue;
4523 }
4524 }
4525
4526 Action *action = nullptr;
4527 if (NS_tstrcmp(token, NS_T("remove")) == 0) // rm file
4528 {
4529 action = new RemoveFile();
4530 }
4531 else if (NS_tstrcmp(token, NS_T("rmdir")) == 0) // rmdir if empty
4532 {
4533 action = new RemoveDir();
4534 }
4535 else if (NS_tstrcmp(token, NS_T("rmrfdir")) == 0) // rmdir recursive
4536 {
4537 const NS_tchar *reldirpath = mstrtok(kQuote, &line);
4538 if (!reldirpath)
4539 return PARSE_ERROR;
4540
4541 if (reldirpath[NS_tstrlen(reldirpath) - 1] != NS_T('/'))
4542 return PARSE_ERROR;
4543
4544 rv = add_dir_entries(reldirpath, &list);
4545 if (rv)
4546 return rv;
4547
4548 continue;
4549 }
4550 else if (NS_tstrcmp(token, NS_T("add")) == 0)
4551 {
4552 action = new AddFile(archiveReader);
4553 }
4554 else if (NS_tstrcmp(token, NS_T("patch")) == 0)
4555 {
4556 action = new PatchFile(archiveReader);
4557 }
4558 else if (NS_tstrcmp(token, NS_T("add-if")) == 0) // Add if exists
4559 {
4560 action = new AddIfFile(archiveReader);
4561 }
4562 else if (NS_tstrcmp(token, NS_T("add-if-not")) == 0) // Add if not exists
4563 {
4564 action = new AddIfNotFile(archiveReader);
4565 }
4566 else if (NS_tstrcmp(token, NS_T("patch-if")) == 0) // Patch if exists
4567 {
4568 action = new PatchIfFile(archiveReader);
4569 }
4570 else
4571 {
4572 LOG(("DoUpdate: unknown token: " LOG_S, token));
4573 return PARSE_ERROR;
4574 }
4575
4576 if (!action)
4577 return BAD_ACTION_ERROR;
4578
4579 rv = action->Parse(line);
4580 if (rv)
4581 return rv;
4582
4583 list.Append(action);
4584 }
4585
4586 rv = list.Prepare();
4587 if (rv)
4588 return rv;
4589
4590 rv = list.Execute();
4591
4592 list.Finish(rv);
4593 return rv;
4594}
XPropertyListType t
double d
int MBS_ApplyPatch(const MBSPatchHeader *header, FILE *patchFile, unsigned char *fbuffer, FILE *file)
Apply a patch.
Definition: bspatch.cxx:91
int MBS_ReadHeader(FILE *file, MBSPatchHeader *header)
Read the header of a patch file into the MBSPatchHeader structure.
Definition: bspatch.cxx:60
int Prepare()
Definition: updater.cxx:3955
int Execute()
Definition: updater.cxx:3984
Action * mFirst
Definition: updater.cxx:3926
void Finish(int status)
Definition: updater.cxx:4016
Action * mLast
Definition: updater.cxx:3927
void Append(Action *action)
Definition: updater.cxx:3943
virtual int Parse(NS_tchar *line)=0
Action()
Definition: updater.cxx:1088
int mProgressCost
Definition: updater.cxx:1106
Action * mNext
Definition: updater.cxx:1108
virtual ~Action()
Definition: updater.cxx:1089
virtual int Prepare()=0
virtual int Execute()=0
virtual void Finish(int status)=0
virtual int Execute()
Definition: updater.cxx:1413
AddFile(ArchiveReader &ar)
Definition: updater.cxx:1369
virtual void Finish(int status)
Definition: updater.cxx:1455
virtual int Prepare()
Definition: updater.cxx:1405
ArchiveReader & mArchiveReader
Definition: updater.cxx:1380
bool mAdded
Definition: updater.cxx:1379
virtual int Parse(NS_tchar *line)
Definition: updater.cxx:1384
std::unique_ptr< NS_tchar[]> mRelPath
Definition: updater.cxx:1378
std::unique_ptr< NS_tchar[]> mFile
Definition: updater.cxx:1377
virtual int Execute()
Definition: updater.cxx:1844
virtual void Finish(int status)
Definition: updater.cxx:1853
std::unique_ptr< NS_tchar[]> mTestFile
Definition: updater.cxx:1805
virtual int Parse(NS_tchar *line)
Definition: updater.cxx:1814
AddIfFile(ArchiveReader &archiveReader)
Definition: updater.cxx:1808
virtual int Prepare()
Definition: updater.cxx:1831
virtual int Parse(NS_tchar *line)
Definition: updater.cxx:1881
AddIfNotFile(ArchiveReader &archiveReader)
Definition: updater.cxx:1875
virtual int Execute()
Definition: updater.cxx:1911
virtual int Prepare()
Definition: updater.cxx:1898
virtual void Finish(int status)
Definition: updater.cxx:1920
std::unique_ptr< NS_tchar[]> mTestFile
Definition: updater.cxx:1872
int Open(const NS_tchar *path)
int ExtractFile(const char *item, const NS_tchar *destination)
int VerifySignature()
Performs a verification on the opened MAR file.
int ExtractFileToStream(const char *item, FILE *fp)
int VerifyProductInformation(const char *MARChannelID, const char *appVersion)
Verifies that the MAR file matches the current product, channel, and version.
FILE * mFile
Definition: updater.cxx:208
~AutoFile()
Definition: updater.cxx:183
AutoFile(FILE *file=nullptr)
Definition: updater.cxx:178
AutoFile & operator=(FILE *file)
Definition: updater.cxx:189
FILE * get()
Definition: updater.cxx:202
int LoadSourceFile(FILE *ofile)
Definition: updater.cxx:1517
virtual void Finish(int status)
Definition: updater.cxx:1787
virtual int Parse(NS_tchar *line)
Definition: updater.cxx:1571
std::unique_ptr< NS_tchar > mFileRelPath
Definition: updater.cxx:1484
MBSPatchHeader header
Definition: updater.cxx:1486
int mPatchIndex
Definition: updater.cxx:1485
AutoFile mPatchStream
Definition: updater.cxx:1489
virtual int Execute()
Definition: updater.cxx:1644
virtual int Prepare()
Definition: updater.cxx:1601
PatchFile(ArchiveReader &ar)
Definition: updater.cxx:1468
virtual ~PatchFile()
Definition: updater.cxx:1495
NS_tchar spath[MAXPATHLEN]
Definition: updater.cxx:1488
ArchiveReader & mArchiveReader
Definition: updater.cxx:1490
unsigned char * buf
Definition: updater.cxx:1487
std::unique_ptr< NS_tchar > mFile
Definition: updater.cxx:1483
const NS_tchar * mPatchFile
Definition: updater.cxx:1482
static int sPatchIndex
Definition: updater.cxx:1480
virtual void Finish(int status)
Definition: updater.cxx:1987
std::unique_ptr< NS_tchar[]> mTestFile
Definition: updater.cxx:1939
virtual int Execute()
Definition: updater.cxx:1978
virtual int Parse(NS_tchar *line)
Definition: updater.cxx:1948
PatchIfFile(ArchiveReader &archiveReader)
Definition: updater.cxx:1942
virtual int Prepare()
Definition: updater.cxx:1965
virtual void Finish(int status)
Definition: updater.cxx:1339
virtual int Parse(NS_tchar *line)
Definition: updater.cxx:1258
std::unique_ptr< NS_tchar[]> mDir
Definition: updater.cxx:1252
std::unique_ptr< NS_tchar[]> mRelPath
Definition: updater.cxx:1253
virtual int Prepare()
Definition: updater.cxx:1279
virtual int Execute()
Definition: updater.cxx:1319
int Execute()
Definition: updater.cxx:1202
void Finish(int status)
Definition: updater.cxx:1231
std::unique_ptr< NS_tchar[]> mRelPath
Definition: updater.cxx:1125
std::unique_ptr< const NS_tchar[]> mFile
Definition: updater.cxx:1124
int Parse(NS_tchar *line)
Definition: updater.cxx:1130
int Prepare()
Definition: updater.cxx:1151
static BOOL DisablePrivileges(HANDLE token)
int nCount
#define WRITE_ERROR_CALLBACK_PATH
Definition: errors.h:80
#define FALLBACKKEY_NOKEY_ERROR
Definition: errors.h:92
#define WRITE_ERROR_FILE_ACCESS_DENIED
Definition: errors.h:81
#define UNEXPECTED_FILE_OPERATION_ERROR
Definition: errors.h:64
#define CRC_ERROR
Definition: errors.h:21
#define WRITE_ERROR_DIR_ACCESS_DENIED
Definition: errors.h:82
#define DELETE_ERROR_EXPECTED_DIR
Definition: errors.h:66
#define FALLBACKKEY_LAUNCH_ERROR
Definition: errors.h:94
#define BAD_ACTION_ERROR
Definition: errors.h:32
#define NO_INSTALLDIR_ERROR
Definition: errors.h:57
#define LOADSOURCE_ERROR_WRONG_SIZE
Definition: errors.h:17
#define DELETE_ERROR_EXPECTED_FILE
Definition: errors.h:67
#define WRITE_ERROR
Definition: errors.h:24
#define MAR_ERROR_EMPTY_ACTION_LIST
Definition: errors.h:16
#define WRITE_ERROR_OPEN_PATCH_FILE
Definition: errors.h:77
#define PARSE_ERROR
Definition: errors.h:22
#define FALLBACKKEY_REGPATH_ERROR
Definition: errors.h:91
#define UPDATER_MEM_ERROR
Definition: errors.h:30
#define WRITE_ERROR_ACCESS_DENIED
Definition: errors.h:58
#define UPDATER_QUOTED_PATH_MEM_ERROR
Definition: errors.h:31
#define WRITE_ERROR_DELETE_BACKUP
Definition: errors.h:83
#define OK
Definition: errors.h:10
#define READ_ERROR
Definition: errors.h:23
#define STRING_CONVERSION_ERROR
Definition: errors.h:33
#define WRITE_ERROR_DELETE_FILE
Definition: errors.h:76
#define FALLBACKKEY_UNKNOWN_ERROR
Definition: errors.h:90
#define WRITE_ERROR_FILE_COPY
Definition: errors.h:75
#define WRITE_ERROR_CALLBACK_APP
Definition: errors.h:60
#define ELEVATION_CANCELED
Definition: errors.h:26
#define RENAME_ERROR_EXPECTED_FILE
Definition: errors.h:68
#define WRITE_ERROR_APPLY_DIR_PATH
Definition: errors.h:79
#define FALLBACKKEY_SERVICE_NO_STOP_ERROR
Definition: errors.h:93
#define FALSE
#define MAX_PATH
void * p
uno_Any a
void LaunchChild(int argc, char **argv)
void LaunchMacPostProcess(const char *aAppBundle)
return NULL
def text(shape, orig_st)
size
int i
line
index
outfile
infile
action
end
#define N
BOOL CalculateRegistryPathFromFilePath(const LPCWSTR filePath, LPWSTR registryPath)
Converts a file path into a unique registry location for cert storage.
int InitProgressUI(int *argc, char ***argv)
int ShowProgressUI()
void QuitProgressUI()
void UpdateProgressUI(float progress)
#define NS_tfopen
Definition: readstrings.cxx:17
int ReadStrings(const NS_tchar *path, const char *keyList, unsigned int numStrings, char results[][MAX_TEXT_LEN], const char *section)
A very basic parser for updater.ini taken mostly from nsINIParser.cpp that can be used by standalone ...
#define MAX_TEXT_LEN
Definition: readstrings.h:10
const wchar_t *typedef BOOL
BOOL DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate, LPCWSTR filePath)
Verifies if the file path matches any certificate stored in the registry.
BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra)
char MARChannelID[MAX_TEXT_LEN]
Definition: updater.cxx:218
uint32_t dlen
Definition: bspatch.h:50
uint32_t scrc32
Definition: bspatch.h:47
uint32_t slen
Definition: bspatch.h:44
bool find(const NS_tchar *path)
Definition: updater.cxx:770
void append(unsigned index, const NS_tchar *path, const NS_tchar *suffix)
Definition: updater.cxx:760
void append(unsigned index, const NS_tchar *path)
Definition: updater.cxx:765
NS_tchar paths[N][MAXPATHLEN]
Definition: updater.cxx:758
char NS_tchar
Definition: types.hxx:20
Any result
ResultType type
unsigned char BYTE
unsigned _Unwind_Word __attribute__((__mode__(__word__)))
#define NS_tstrcmp
#define NS_tclosedir
#define LOG_S
#define BACKUP_EXT
#define NS_tstat
#define NS_tDIR
#define NS_tstricmp
#define NS_SLASH
#define NS_tstrncmp
#define NS_tlstat
#define NS_tsnprintf
#define NS_tstat_t
#define NS_tstrrchr
#define NS_tstrstr
#define NS_treaddir
#define NS_T(str)
#define NS_tchdir
#define NS_trmdir
#define NS_tstrcpy
#define NS_topendir
#define NS_taccess
#define NS_tstrncpy
#define MAXPATHLEN
Definition: updatedefines.h:20
#define NS_tdirent
#define NS_tchmod
#define NS_tstrlen
#define NS_tremove
#define NS_trename
#define NS_tstrcat
#define NS_tmkdir
BOOL LaunchWinPostProcess(const WCHAR *installationDir, const WCHAR *updateInfoDir, bool forceSync, HANDLE userToken)
BOOL DoesFallbackKeyExist()
BOOL StartServiceUpdate(LPCWSTR installDir)
BOOL IsUnpromptedElevation(BOOL &isUnpromptedElevation)
#define SVC_NAME
Definition: updatehelper.h:21
BOOL IsLocalFile(LPCWSTR file, BOOL &isLocal)
DWORD WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
DWORD LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR *argv)
#define LogInit(PATHNAME_, FILENAME_)
Definition: updatelogging.h:40
#define LogFinish()
Definition: updatelogging.h:44
#define LogFlush()
Definition: updatelogging.h:45
#define LOG(args)
Definition: updatelogging.h:39
static int ProcessReplaceRequest()
Definition: updater.cxx:2356
int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
Definition: updater.cxx:4213
static int CopyInstallDirToDestDir()
Definition: updater.cxx:2317
static NS_tchar gWorkingDirPath[MAXPATHLEN]
Definition: updater.cxx:225
static void backup_finish(const NS_tchar *path, const NS_tchar *relPath, int status)
Definition: updater.cxx:1072
static int ensure_copy_recursive(const NS_tchar *path, const NS_tchar *dest, copy_recursive_skiplist< N > &skiplist)
Definition: updater.cxx:786
int AddPreCompleteActions(ActionList *list)
Definition: updater.cxx:4393
static NS_tchar * mstrtok(const NS_tchar *delims, NS_tchar **str)
Definition: updater.cxx:244
static int ensure_parent_dir(const NS_tchar *path)
Definition: updater.cxx:613
static const NS_tchar kNL[]
Definition: updater.cxx:240
#define SSIZE_MAX
Definition: updater.cxx:107
static void ensure_write_permissions(const NS_tchar *path)
Definition: updater.cxx:478
static int ensure_copy(const NS_tchar *path, const NS_tchar *dest)
Definition: updater.cxx:667
static NS_tchar * get_valid_path(NS_tchar **line, bool isdir=false)
Gets the platform specific path and performs simple checks to the path.
Definition: updater.cxx:405
static NS_tchar * gPatchDirPath
Definition: updater.cxx:223
static bool sReplaceRequest
Definition: updater.cxx:228
static void UpdateThreadFunc(void *)
Definition: updater.cxx:2705
static NS_tchar gInstallDirPath[MAXPATHLEN]
Definition: updater.cxx:224
static int CheckSignature(ArchiveReader &archiveReader)
Definition: updater.cxx:2623
int NS_main(int argc, NS_tchar **argv)
Definition: updater.cxx:2895
static bool is_read_only(const NS_tchar *flags)
Definition: updater.cxx:561
static int ensure_remove(const NS_tchar *path)
Definition: updater.cxx:491
static NS_tchar * get_quoted_path(const NS_tchar *path)
Definition: updater.cxx:456
static int backup_restore(const NS_tchar *path, const NS_tchar *relPath)
Definition: updater.cxx:998
#define PROGRESS_FINISH_SIZE
Definition: updater.cxx:82
static int GetUpdateFileNames(std::vector< tstring > &fileNames)
Definition: updater.cxx:2582
static int DoUpdate(ArchiveReader &ArchiveReader)
Definition: updater.cxx:4460
static bool sStagedUpdate
Definition: updater.cxx:227
static bool gSucceeded
Definition: updater.cxx:226
#define PROGRESS_PREPARE_SIZE
Definition: updater.cxx:80
static NS_tchar * new_absolute_path(const NS_tchar *relpath)
Converts a relative update path to an absolute path related to the working or install directory.
Definition: updater.cxx:312
static const NS_tchar kQuote[]
Definition: updater.cxx:241
static const NS_tchar * get_relative_offset(const NS_tchar *abs_path)
Get a pointer in the absolute path, relative to the working or install directory.
Definition: updater.cxx:371
static int ensure_remove_recursive(const NS_tchar *path, bool continueEnumOnFailure=false)
Definition: updater.cxx:502
#define PROGRESS_EXECUTE_SIZE
Definition: updater.cxx:81
static void LaunchCallbackApp(const NS_tchar *workingDir, int argc, NS_tchar **argv, bool usingService)
Definition: updater.cxx:2140
static unsigned int crc32(const unsigned char *buf, unsigned int len)
Definition: updater.cxx:159
static int backup_create(const NS_tchar *path)
Definition: updater.cxx:987
static const NS_tchar kWhitespace[]
Definition: updater.cxx:239
static bool WriteStatusFile(const char *aStatus)
Definition: updater.cxx:2175
int LaunchCallbackAndPostProcessApps(int argc, NS_tchar **argv, int callbackIndex)
Definition: updater.cxx:2842
static FILE * ensure_open(const NS_tchar *path, const NS_tchar *flags, unsigned int options)
Definition: updater.cxx:582
static bool sUsingService
Definition: updater.cxx:229
unsigned int BZ2_crc32Table[256]
Definition: updater.cxx:155
static int ensure_copy_symlink(const NS_tchar *path, const NS_tchar *dest)
Definition: updater.cxx:644
static int backup_discard(const NS_tchar *path, const NS_tchar *relPath)
Definition: updater.cxx:1018
static int rename_file(const NS_tchar *spath, const NS_tchar *dpath, bool allowDirs=false)
Definition: updater.cxx:857
static NS_tchar * GetManifestContents(const NS_tchar *manifest)
Definition: updater.cxx:4329
BOOL WinLaunchChild(const wchar_t *exePath, int argc, wchar_t **argv, HANDLE userToken, HANDLE *hProcess)
BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer, LPCWSTR siblingFilePath, LPCWSTR newFileName)
wchar_t * MakeCommandLine(int argc, wchar_t **argv)