Source code

Revision control

Other Tools

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
5
/**
6
* Manifest Format
7
* ---------------
8
*
9
* contents = 1*( line )
10
* line = method LWS *( param LWS ) CRLF
11
* CRLF = "\r\n"
12
* LWS = 1*( " " | "\t" )
13
*
14
* Available methods for the manifest file:
15
*
16
* updatev3.manifest
17
* -----------------
18
* method = "add" | "add-if" | "add-if-not" | "patch" | "patch-if" |
19
* "remove" | "rmdir" | "rmrfdir" | type
20
*
21
* 'add-if-not' adds a file if it doesn't exist.
22
*
23
* 'type' is the update type (e.g. complete or partial) and when present MUST
24
* be the first entry in the update manifest. The type is used to support
25
* removing files that no longer exist when when applying a complete update by
26
* causing the actions defined in the precomplete file to be performed.
27
*
28
* precomplete
29
* -----------
30
* method = "remove" | "rmdir"
31
*/
32
#include "bspatch.h"
33
#include "progressui.h"
34
#include "archivereader.h"
35
#include "readstrings.h"
36
#include "updatererrors.h"
37
38
#include <stdio.h>
39
#include <string.h>
40
#include <stdlib.h>
41
#include <stdarg.h>
42
43
#include <sys/types.h>
44
#include <sys/stat.h>
45
#include <fcntl.h>
46
#include <limits.h>
47
#include <errno.h>
48
#include <algorithm>
49
50
#include "updatecommon.h"
51
#ifdef XP_MACOSX
52
# include "updaterfileutils_osx.h"
53
#endif // XP_MACOSX
54
55
#include "mozilla/Compiler.h"
56
#include "mozilla/Types.h"
57
#include "mozilla/UniquePtr.h"
58
#ifdef XP_WIN
59
# include "mozilla/WinHeaderOnlyUtils.h"
60
#endif // XP_WIN
61
62
// Amount of the progress bar to use in each of the 3 update stages,
63
// should total 100.0.
64
#define PROGRESS_PREPARE_SIZE 20.0f
65
#define PROGRESS_EXECUTE_SIZE 75.0f
66
#define PROGRESS_FINISH_SIZE 5.0f
67
68
// Maximum amount of time in ms to wait for the parent process to close. The 30
69
// seconds is rather long but there have been bug reports where the parent
70
// process has exited after 10 seconds and it is better to give it a chance.
71
#define PARENT_WAIT 30000
72
73
#if defined(XP_MACOSX)
74
// These functions are defined in launchchild_osx.mm
75
void CleanupElevatedMacUpdate(bool aFailureOccurred);
76
bool IsOwnedByGroupAdmin(const char* aAppBundle);
77
bool IsRecursivelyWritable(const char* aPath);
78
void LaunchChild(int argc, const char** argv);
79
void LaunchMacPostProcess(const char* aAppBundle);
80
bool ObtainUpdaterArguments(int* argc, char*** argv);
81
bool ServeElevatedUpdate(int argc, const char** argv);
82
void SetGroupOwnershipAndPermissions(const char* aAppBundle);
83
struct UpdateServerThreadArgs {
84
int argc;
85
const NS_tchar** argv;
86
};
87
#endif
88
89
#ifndef _O_BINARY
90
# define _O_BINARY 0
91
#endif
92
93
#ifndef NULL
94
# define NULL (0)
95
#endif
96
97
#ifndef SSIZE_MAX
98
# define SSIZE_MAX LONG_MAX
99
#endif
100
101
// We want to use execv to invoke the callback executable on platforms where
102
// we were launched using execv. See nsUpdateDriver.cpp.
103
#if defined(XP_UNIX) && !defined(XP_MACOSX)
104
# define USE_EXECV
105
#endif
106
107
#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX)
108
# include "nss.h"
109
# include "prerror.h"
110
#endif
111
112
#include "crctable.h"
113
114
#ifdef XP_WIN
115
# ifdef MOZ_MAINTENANCE_SERVICE
116
# include "registrycertificates.h"
117
# endif
118
BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra);
119
BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer, LPCWSTR siblingFilePath,
120
LPCWSTR newFileName);
121
# include "updatehelper.h"
122
123
// Closes the handle if valid and if the updater is elevated returns with the
124
// return code specified. This prevents multiple launches of the callback
125
// application by preventing the elevated process from launching the callback.
126
# define EXIT_WHEN_ELEVATED(path, handle, retCode) \
127
{ \
128
if (handle != INVALID_HANDLE_VALUE) { \
129
CloseHandle(handle); \
130
} \
131
if (NS_tremove(path) && errno != ENOENT) { \
132
ImpersonatedLogFinish(); \
133
return retCode; \
134
} \
135
}
136
#endif
137
138
//-----------------------------------------------------------------------------
139
140
// This BZ2_crc32Table variable lives in libbz2. We just took the
141
// data structure from bz2 and created crctables.h
142
143
static unsigned int crc32(const unsigned char* buf, unsigned int len) {
144
unsigned int crc = 0xffffffffL;
145
146
const unsigned char* end = buf + len;
147
for (; buf != end; ++buf)
148
crc = (crc << 8) ^ BZ2_crc32Table[(crc >> 24) ^ *buf];
149
150
crc = ~crc;
151
return crc;
152
}
153
154
//-----------------------------------------------------------------------------
155
156
// A simple stack based container for a FILE struct that closes the
157
// file descriptor from its destructor.
158
class AutoFile {
159
public:
160
explicit AutoFile(FILE* file = nullptr) : mFile(file) {}
161
162
~AutoFile() {
163
if (mFile != nullptr) {
164
fclose(mFile);
165
}
166
}
167
168
AutoFile& operator=(FILE* file) {
169
if (mFile != 0) {
170
fclose(mFile);
171
}
172
mFile = file;
173
return *this;
174
}
175
176
operator FILE*() { return mFile; }
177
178
FILE* get() { return mFile; }
179
180
private:
181
FILE* mFile;
182
};
183
184
struct MARChannelStringTable {
185
MARChannelStringTable() { MARChannelID[0] = '\0'; }
186
187
char MARChannelID[MAX_TEXT_LEN];
188
};
189
190
//-----------------------------------------------------------------------------
191
192
#ifdef XP_MACOSX
193
194
// Just a simple class that sets a umask value in its constructor and resets
195
// it in its destructor.
196
class UmaskContext {
197
public:
198
explicit UmaskContext(mode_t umaskToSet);
199
~UmaskContext();
200
201
private:
202
mode_t mPreviousUmask;
203
};
204
205
UmaskContext::UmaskContext(mode_t umaskToSet) {
206
mPreviousUmask = umask(umaskToSet);
207
}
208
209
UmaskContext::~UmaskContext() { umask(mPreviousUmask); }
210
211
#endif
212
213
//-----------------------------------------------------------------------------
214
215
typedef void (*ThreadFunc)(void* param);
216
217
#ifdef XP_WIN
218
# include <process.h>
219
220
class Thread {
221
public:
222
int Run(ThreadFunc func, void* param) {
223
mThreadFunc = func;
224
mThreadParam = param;
225
226
unsigned int threadID;
227
228
mThread =
229
(HANDLE)_beginthreadex(nullptr, 0, ThreadMain, this, 0, &threadID);
230
231
return mThread ? 0 : -1;
232
}
233
int Join() {
234
WaitForSingleObject(mThread, INFINITE);
235
CloseHandle(mThread);
236
return 0;
237
}
238
239
private:
240
static unsigned __stdcall ThreadMain(void* p) {
241
Thread* self = (Thread*)p;
242
self->mThreadFunc(self->mThreadParam);
243
return 0;
244
}
245
HANDLE mThread;
246
ThreadFunc mThreadFunc;
247
void* mThreadParam;
248
};
249
250
#elif defined(XP_UNIX)
251
# include <pthread.h>
252
253
class Thread {
254
public:
255
int Run(ThreadFunc func, void* param) {
256
return pthread_create(&thr, nullptr, (void* (*)(void*))func, param);
257
}
258
int Join() {
259
void* result;
260
return pthread_join(thr, &result);
261
}
262
263
private:
264
pthread_t thr;
265
};
266
267
#else
268
# error "Unsupported platform"
269
#endif
270
271
//-----------------------------------------------------------------------------
272
273
static NS_tchar gPatchDirPath[MAXPATHLEN];
274
static NS_tchar gInstallDirPath[MAXPATHLEN];
275
static NS_tchar gWorkingDirPath[MAXPATHLEN];
276
static ArchiveReader gArchiveReader;
277
static bool gSucceeded = false;
278
static bool sStagedUpdate = false;
279
static bool sReplaceRequest = false;
280
static bool sUsingService = false;
281
282
#ifdef XP_WIN
283
// The current working directory specified in the command line.
284
static NS_tchar* gDestPath;
285
static NS_tchar gCallbackRelPath[MAXPATHLEN];
286
static NS_tchar gCallbackBackupPath[MAXPATHLEN];
287
static NS_tchar gDeleteDirPath[MAXPATHLEN];
288
289
# ifdef MOZ_MAINTENANCE_SERVICE
290
// Token used to impersonate the original user when running via the
291
// maintenance service. If nullptr no impersonation is performed.
292
static HANDLE gUserToken;
293
294
static void ImpersonatedLogInit(NS_tchar* sourcePath,
295
const NS_tchar* fileName) {
296
ImpersonationScope impersonated(gUserToken);
297
if (!gUserToken || impersonated) {
298
LogInit(sourcePath, fileName);
299
}
300
}
301
302
static void ImpersonatedLogFinish() {
303
ImpersonationScope impersonated(gUserToken);
304
if (!gUserToken || impersonated) {
305
LogFinish();
306
}
307
}
308
309
# endif // MOZ_MAINTENANCE_SERVICE
310
#endif // XP_WIN
311
312
#if !defined(XP_WIN) || !defined(MOZ_MAINTENANCE_SERVICE)
313
# define ImpersonatedLogInit LogInit
314
# define ImpersonatedLogFinish LogFinish
315
#endif
316
317
static const NS_tchar kWhitespace[] = NS_T(" \t");
318
static const NS_tchar kNL[] = NS_T("\r\n");
319
static const NS_tchar kQuote[] = NS_T("\"");
320
321
static inline size_t mmin(size_t a, size_t b) { return (a > b) ? b : a; }
322
323
static NS_tchar* mstrtok(const NS_tchar* delims, NS_tchar** str) {
324
if (!*str || !**str) {
325
*str = nullptr;
326
return nullptr;
327
}
328
329
// skip leading "whitespace"
330
NS_tchar* ret = *str;
331
const NS_tchar* d;
332
do {
333
for (d = delims; *d != NS_T('\0'); ++d) {
334
if (*ret == *d) {
335
++ret;
336
break;
337
}
338
}
339
} while (*d);
340
341
if (!*ret) {
342
*str = ret;
343
return nullptr;
344
}
345
346
NS_tchar* i = ret;
347
do {
348
for (d = delims; *d != NS_T('\0'); ++d) {
349
if (*i == *d) {
350
*i = NS_T('\0');
351
*str = ++i;
352
return ret;
353
}
354
}
355
++i;
356
} while (*i);
357
358
*str = nullptr;
359
return ret;
360
}
361
362
#if defined(TEST_UPDATER)
363
# define HAS_ENV_CHECK 1
364
#elif defined(MOZ_MAINTENANCE_SERVICE)
365
# define HAS_ENV_CHECK 1
366
#endif
367
368
#if defined(HAS_ENV_CHECK)
369
static bool EnvHasValue(const char* name) {
370
const char* val = getenv(name);
371
return (val && *val);
372
}
373
#endif
374
375
/**
376
* Coverts a relative update path to a full path.
377
*
378
* @param relpath
379
* The relative path to convert to a full path.
380
* @return valid filesystem full path or nullptr if memory allocation fails.
381
*/
382
static NS_tchar* get_full_path(const NS_tchar* relpath) {
383
NS_tchar* destpath = sStagedUpdate ? gWorkingDirPath : gInstallDirPath;
384
size_t lendestpath = NS_tstrlen(destpath);
385
size_t lenrelpath = NS_tstrlen(relpath);
386
NS_tchar* s = new NS_tchar[lendestpath + lenrelpath + 2];
387
if (!s) {
388
return nullptr;
389
}
390
391
NS_tchar* c = s;
392
393
NS_tstrcpy(c, destpath);
394
c += lendestpath;
395
NS_tstrcat(c, NS_T("/"));
396
c++;
397
398
NS_tstrcat(c, relpath);
399
c += lenrelpath;
400
*c = NS_T('\0');
401
return s;
402
}
403
404
/**
405
* Converts a full update path into a relative path; reverses get_full_path.
406
*
407
* @param fullpath
408
* The absolute path to convert into a relative path.
409
* return pointer to the location within fullpath where the relative path starts
410
* or fullpath itself if it already looks relative.
411
*/
412
#ifndef XP_WIN
413
static const NS_tchar* get_relative_path(const NS_tchar* fullpath) {
414
if (fullpath[0] != '/') {
415
return fullpath;
416
}
417
418
NS_tchar* prefix = sStagedUpdate ? gWorkingDirPath : gInstallDirPath;
419
420
// If the path isn't long enough to be absolute, return it as-is.
421
if (NS_tstrlen(fullpath) <= NS_tstrlen(prefix)) {
422
return fullpath;
423
}
424
425
return fullpath + NS_tstrlen(prefix) + 1;
426
}
427
#endif
428
429
/**
430
* Gets the platform specific path and performs simple checks to the path. If
431
* the path checks don't pass nullptr will be returned.
432
*
433
* @param line
434
* The line from the manifest that contains the path.
435
* @param isdir
436
* Whether the path is a directory path. Defaults to false.
437
* @return valid filesystem path or nullptr if the path checks fail.
438
*/
439
static NS_tchar* get_valid_path(NS_tchar** line, bool isdir = false) {
440
NS_tchar* path = mstrtok(kQuote, line);
441
if (!path) {
442
LOG(("get_valid_path: unable to determine path: " LOG_S, *line));
443
return nullptr;
444
}
445
446
// All paths must be relative from the current working directory
447
if (path[0] == NS_T('/')) {
448
LOG(("get_valid_path: path must be relative: " LOG_S, path));
449
return nullptr;
450
}
451
452
#ifdef XP_WIN
453
// All paths must be relative from the current working directory
454
if (path[0] == NS_T('\\') || path[1] == NS_T(':')) {
455
LOG(("get_valid_path: path must be relative: " LOG_S, path));
456
return nullptr;
457
}
458
#endif
459
460
if (isdir) {
461
// Directory paths must have a trailing forward slash.
462
if (path[NS_tstrlen(path) - 1] != NS_T('/')) {
463
LOG(
464
("get_valid_path: directory paths must have a trailing forward "
465
"slash: " LOG_S,
466
path));
467
return nullptr;
468
}
469
470
// Remove the trailing forward slash because stat on Windows will return
471
// ENOENT if the path has a trailing slash.
472
path[NS_tstrlen(path) - 1] = NS_T('\0');
473
}
474
475
// Don't allow relative paths that resolve to a parent directory.
476
if (NS_tstrstr(path, NS_T("..")) != nullptr) {
477
LOG(("get_valid_path: paths must not contain '..': " LOG_S, path));
478
return nullptr;
479
}
480
481
return path;
482
}
483
484
/*
485
* Gets a quoted path. The return value is malloc'd and it is the responsibility
486
* of the caller to free it.
487
*
488
* @param path
489
* The path to quote.
490
* @return On success the quoted path and nullptr otherwise.
491
*/
492
static NS_tchar* get_quoted_path(const NS_tchar* path) {
493
size_t lenQuote = NS_tstrlen(kQuote);
494
size_t lenPath = NS_tstrlen(path);
495
size_t len = lenQuote + lenPath + lenQuote + 1;
496
497
NS_tchar* s = (NS_tchar*)malloc(len * sizeof(NS_tchar));
498
if (!s) {
499
return nullptr;
500
}
501
502
NS_tchar* c = s;
503
NS_tstrcpy(c, kQuote);
504
c += lenQuote;
505
NS_tstrcat(c, path);
506
c += lenPath;
507
NS_tstrcat(c, kQuote);
508
c += lenQuote;
509
*c = NS_T('\0');
510
return s;
511
}
512
513
static void ensure_write_permissions(const NS_tchar* path) {
514
#ifdef XP_WIN
515
(void)_wchmod(path, _S_IREAD | _S_IWRITE);
516
#else
517
struct stat fs;
518
if (!stat(path, &fs) && !(fs.st_mode & S_IWUSR)) {
519
(void)chmod(path, fs.st_mode | S_IWUSR);
520
}
521
#endif
522
}
523
524
static int ensure_remove(const NS_tchar* path) {
525
ensure_write_permissions(path);
526
int rv = NS_tremove(path);
527
if (rv) {
528
LOG(("ensure_remove: failed to remove file: " LOG_S ", rv: %d, err: %d",
529
path, rv, errno));
530
}
531
return rv;
532
}
533
534
// Remove the directory pointed to by path and all of its files and
535
// sub-directories.
536
static int ensure_remove_recursive(const NS_tchar* path,
537
bool continueEnumOnFailure = false) {
538
// We use lstat rather than stat here so that we can successfully remove
539
// symlinks.
540
struct NS_tstat_t sInfo;
541
int rv = NS_tlstat(path, &sInfo);
542
if (rv) {
543
// This error is benign
544
return rv;
545
}
546
if (!S_ISDIR(sInfo.st_mode)) {
547
return ensure_remove(path);
548
}
549
550
NS_tDIR* dir;
551
NS_tdirent* entry;
552
553
dir = NS_topendir(path);
554
if (!dir) {
555
LOG(("ensure_remove_recursive: unable to open directory: " LOG_S
556
", rv: %d, err: %d",
557
path, rv, errno));
558
return rv;
559
}
560
561
while ((entry = NS_treaddir(dir)) != 0) {
562
if (NS_tstrcmp(entry->d_name, NS_T(".")) &&
563
NS_tstrcmp(entry->d_name, NS_T(".."))) {
564
NS_tchar childPath[MAXPATHLEN];
565
NS_tsnprintf(childPath, sizeof(childPath) / sizeof(childPath[0]),
566
NS_T("%s/%s"), path, entry->d_name);
567
rv = ensure_remove_recursive(childPath);
568
if (rv && !continueEnumOnFailure) {
569
break;
570
}
571
}
572
}
573
574
NS_tclosedir(dir);
575
576
if (rv == OK) {
577
ensure_write_permissions(path);
578
rv = NS_trmdir(path);
579
if (rv) {
580
LOG(("ensure_remove_recursive: unable to remove directory: " LOG_S
581
", rv: %d, err: %d",
582
path, rv, errno));
583
}
584
}
585
return rv;
586
}
587
588
static bool is_read_only(const NS_tchar* flags) {
589
size_t length = NS_tstrlen(flags);
590
if (length == 0) {
591
return false;
592
}
593
594
// Make sure the string begins with "r"
595
if (flags[0] != NS_T('r')) {
596
return false;
597
}
598
599
// Look for "r+" or "r+b"
600
if (length > 1 && flags[1] == NS_T('+')) {
601
return false;
602
}
603
604
// Look for "rb+"
605
if (NS_tstrcmp(flags, NS_T("rb+")) == 0) {
606
return false;
607
}
608
609
return true;
610
}
611
612
static FILE* ensure_open(const NS_tchar* path, const NS_tchar* flags,
613
unsigned int options) {
614
ensure_write_permissions(path);
615
FILE* f = NS_tfopen(path, flags);
616
if (is_read_only(flags)) {
617
// Don't attempt to modify the file permissions if the file is being opened
618
// in read-only mode.
619
return f;
620
}
621
if (NS_tchmod(path, options) != 0) {
622
if (f != nullptr) {
623
fclose(f);
624
}
625
return nullptr;
626
}
627
struct NS_tstat_t ss;
628
if (NS_tstat(path, &ss) != 0 || ss.st_mode != options) {
629
if (f != nullptr) {
630
fclose(f);
631
}
632
return nullptr;
633
}
634
return f;
635
}
636
637
// Ensure that the directory containing this file exists.
638
static int ensure_parent_dir(const NS_tchar* path) {
639
int rv = OK;
640
641
NS_tchar* slash = (NS_tchar*)NS_tstrrchr(path, NS_T('/'));
642
if (slash) {
643
*slash = NS_T('\0');
644
rv = ensure_parent_dir(path);
645
// Only attempt to create the directory if we're not at the root
646
if (rv == OK && *path) {
647
rv = NS_tmkdir(path, 0755);
648
// If the directory already exists, then ignore the error.
649
if (rv < 0 && errno != EEXIST) {
650
LOG(("ensure_parent_dir: failed to create directory: " LOG_S ", "
651
"err: %d",
652
path, errno));
653
rv = WRITE_ERROR;
654
} else {
655
rv = OK;
656
}
657
}
658
*slash = NS_T('/');
659
}
660
return rv;
661
}
662
663
#ifdef XP_UNIX
664
static int ensure_copy_symlink(const NS_tchar* path, const NS_tchar* dest) {
665
// Copy symlinks by creating a new symlink to the same target
666
NS_tchar target[MAXPATHLEN + 1] = {NS_T('\0')};
667
int rv = readlink(path, target, MAXPATHLEN);
668
if (rv == -1) {
669
LOG(("ensure_copy_symlink: failed to read the link: " LOG_S ", err: %d",
670
path, errno));
671
return READ_ERROR;
672
}
673
rv = symlink(target, dest);
674
if (rv == -1) {
675
LOG(("ensure_copy_symlink: failed to create the new link: " LOG_S
676
", target: " LOG_S " err: %d",
677
dest, target, errno));
678
return READ_ERROR;
679
}
680
return 0;
681
}
682
#endif
683
684
// Copy the file named path onto a new file named dest.
685
static int ensure_copy(const NS_tchar* path, const NS_tchar* dest) {
686
#ifdef XP_WIN
687
// Fast path for Windows
688
bool result = CopyFileW(path, dest, false);
689
if (!result) {
690
LOG(("ensure_copy: failed to copy the file " LOG_S " over to " LOG_S
691
", lasterr: %x",
692
path, dest, GetLastError()));
693
return WRITE_ERROR_FILE_COPY;
694
}
695
return OK;
696
#else
697
struct NS_tstat_t ss;
698
int rv = NS_tlstat(path, &ss);
699
if (rv) {
700
LOG(("ensure_copy: failed to read file status info: " LOG_S ", err: %d",
701
path, errno));
702
return READ_ERROR;
703
}
704
705
# ifdef XP_UNIX
706
if (S_ISLNK(ss.st_mode)) {
707
return ensure_copy_symlink(path, dest);
708
}
709
# endif
710
711
AutoFile infile(ensure_open(path, NS_T("rb"), ss.st_mode));
712
if (!infile) {
713
LOG(("ensure_copy: failed to open the file for reading: " LOG_S ", err: %d",
714
path, errno));
715
return READ_ERROR;
716
}
717
AutoFile outfile(ensure_open(dest, NS_T("wb"), ss.st_mode));
718
if (!outfile) {
719
LOG(("ensure_copy: failed to open the file for writing: " LOG_S ", err: %d",
720
dest, errno));
721
return WRITE_ERROR;
722
}
723
724
// This block size was chosen pretty arbitrarily but seems like a reasonable
725
// compromise. For example, the optimal block size on a modern OS X machine
726
// is 100k */
727
const int blockSize = 32 * 1024;
728
void* buffer = malloc(blockSize);
729
if (!buffer) {
730
return UPDATER_MEM_ERROR;
731
}
732
733
while (!feof(infile.get())) {
734
size_t read = fread(buffer, 1, blockSize, infile);
735
if (ferror(infile.get())) {
736
LOG(("ensure_copy: failed to read the file: " LOG_S ", err: %d", path,
737
errno));
738
free(buffer);
739
return READ_ERROR;
740
}
741
742
size_t written = 0;
743
744
while (written < read) {
745
size_t chunkWritten = fwrite(buffer, 1, read - written, outfile);
746
if (chunkWritten <= 0) {
747
LOG(("ensure_copy: failed to write the file: " LOG_S ", err: %d", dest,
748
errno));
749
free(buffer);
750
return WRITE_ERROR_FILE_COPY;
751
}
752
753
written += chunkWritten;
754
}
755
}
756
757
rv = NS_tchmod(dest, ss.st_mode);
758
759
free(buffer);
760
return rv;
761
#endif
762
}
763
764
template <unsigned N>
765
struct copy_recursive_skiplist {
766
NS_tchar paths[N][MAXPATHLEN];
767
768
void append(unsigned index, const NS_tchar* path, const NS_tchar* suffix) {
769
NS_tsnprintf(paths[index], MAXPATHLEN, NS_T("%s/%s"), path, suffix);
770
}
771
772
bool find(const NS_tchar* path) {
773
for (int i = 0; i < static_cast<int>(N); ++i) {
774
if (!NS_tstricmp(paths[i], path)) {
775
return true;
776
}
777
}
778
return false;
779
}
780
};
781
782
// Copy all of the files and subdirectories under path to a new directory named
783
// dest. The path names in the skiplist will be skipped and will not be copied.
784
template <unsigned N>
785
static int ensure_copy_recursive(const NS_tchar* path, const NS_tchar* dest,
786
copy_recursive_skiplist<N>& skiplist) {
787
struct NS_tstat_t sInfo;
788
int rv = NS_tlstat(path, &sInfo);
789
if (rv) {
790
LOG(("ensure_copy_recursive: path doesn't exist: " LOG_S
791
", rv: %d, err: %d",
792
path, rv, errno));
793
return READ_ERROR;
794
}
795
796
#ifdef XP_UNIX
797
if (S_ISLNK(sInfo.st_mode)) {
798
return ensure_copy_symlink(path, dest);
799
}
800
#endif
801
802
if (!S_ISDIR(sInfo.st_mode)) {
803
return ensure_copy(path, dest);
804
}
805
806
rv = NS_tmkdir(dest, sInfo.st_mode);
807
if (rv < 0 && errno != EEXIST) {
808
LOG(("ensure_copy_recursive: could not create destination directory: " LOG_S
809
", rv: %d, err: %d",
810
path, rv, errno));
811
return WRITE_ERROR;
812
}
813
814
NS_tDIR* dir;
815
NS_tdirent* entry;
816
817
dir = NS_topendir(path);
818
if (!dir) {
819
LOG(("ensure_copy_recursive: path is not a directory: " LOG_S
820
", rv: %d, err: %d",
821
path, rv, errno));
822
return READ_ERROR;
823
}
824
825
while ((entry = NS_treaddir(dir)) != 0) {
826
if (NS_tstrcmp(entry->d_name, NS_T(".")) &&
827
NS_tstrcmp(entry->d_name, NS_T(".."))) {
828
NS_tchar childPath[MAXPATHLEN];
829
NS_tsnprintf(childPath, sizeof(childPath) / sizeof(childPath[0]),
830
NS_T("%s/%s"), path, entry->d_name);
831
if (skiplist.find(childPath)) {
832
continue;
833
}
834
NS_tchar childPathDest[MAXPATHLEN];
835
NS_tsnprintf(childPathDest,
836
sizeof(childPathDest) / sizeof(childPathDest[0]),
837
NS_T("%s/%s"), dest, entry->d_name);
838
rv = ensure_copy_recursive(childPath, childPathDest, skiplist);
839
if (rv) {
840
break;
841
}
842
}
843
}
844
NS_tclosedir(dir);
845
return rv;
846
}
847
848
// Renames the specified file to the new file specified. If the destination file
849
// exists it is removed.
850
static int rename_file(const NS_tchar* spath, const NS_tchar* dpath,
851
bool allowDirs = false) {
852
int rv = ensure_parent_dir(dpath);
853
if (rv) {
854
return rv;
855
}
856
857
struct NS_tstat_t spathInfo;
858
rv = NS_tstat(spath, &spathInfo);
859
if (rv) {
860
LOG(("rename_file: failed to read file status info: " LOG_S ", "
861
"err: %d",
862
spath, errno));
863
return READ_ERROR;
864
}
865
866
if (!S_ISREG(spathInfo.st_mode)) {
867
if (allowDirs && !S_ISDIR(spathInfo.st_mode)) {
868
LOG(("rename_file: path present, but not a file: " LOG_S ", err: %d",
869
spath, errno));
870
return RENAME_ERROR_EXPECTED_FILE;
871
}
872
LOG(("rename_file: proceeding to rename the directory"));
873
}
874
875
if (!NS_taccess(dpath, F_OK)) {
876
if (ensure_remove(dpath)) {
877
LOG(
878
("rename_file: destination file exists and could not be "
879
"removed: " LOG_S,
880
dpath));
881
return WRITE_ERROR_DELETE_FILE;
882
}
883
}
884
885
if (NS_trename(spath, dpath) != 0) {
886
LOG(("rename_file: failed to rename file - src: " LOG_S ", "
887
"dst:" LOG_S ", err: %d",
888
spath, dpath, errno));
889
return WRITE_ERROR;
890
}
891
892
return OK;
893
}
894
895
#ifdef XP_WIN
896
// Remove the directory pointed to by path and all of its files and
897
// sub-directories. If a file is in use move it to the tobedeleted directory
898
// and attempt to schedule removal of the file on reboot
899
static int remove_recursive_on_reboot(const NS_tchar* path,
900
const NS_tchar* deleteDir) {
901
struct NS_tstat_t sInfo;
902
int rv = NS_tlstat(path, &sInfo);
903
if (rv) {
904
// This error is benign
905
return rv;
906
}
907
908
if (!S_ISDIR(sInfo.st_mode)) {
909
NS_tchar tmpDeleteFile[MAXPATHLEN];
910
GetUUIDTempFilePath(deleteDir, L"rep", tmpDeleteFile);
911
if (NS_tremove(tmpDeleteFile) && errno != ENOENT) {
912
LOG(("remove_recursive_on_reboot: failed to remove temporary file: " LOG_S
913
", err: %d",
914
tmpDeleteFile, errno));
915
}
916
rv = rename_file(path, tmpDeleteFile, false);
917
if (MoveFileEx(rv ? path : tmpDeleteFile, nullptr,
918
MOVEFILE_DELAY_UNTIL_REBOOT)) {
919
LOG(
920
("remove_recursive_on_reboot: file will be removed on OS "
921
"reboot: " LOG_S,
922
rv ? path : tmpDeleteFile));
923
} else {
924
LOG((
925
"remove_recursive_on_reboot: failed to schedule OS reboot removal of "
926
"file: " LOG_S,
927
rv ? path : tmpDeleteFile));
928
}
929
return rv;
930
}
931
932
NS_tDIR* dir;
933
NS_tdirent* entry;
934
935
dir = NS_topendir(path);
936
if (!dir) {
937
LOG(("remove_recursive_on_reboot: unable to open directory: " LOG_S
938
", rv: %d, err: %d",
939
path, rv, errno));
940
return rv;
941
}
942
943
while ((entry = NS_treaddir(dir)) != 0) {
944
if (NS_tstrcmp(entry->d_name, NS_T(".")) &&
945
NS_tstrcmp(entry->d_name, NS_T(".."))) {
946
NS_tchar childPath[MAXPATHLEN];
947
NS_tsnprintf(childPath, sizeof(childPath) / sizeof(childPath[0]),
948
NS_T("%s/%s"), path, entry->d_name);
949
// There is no need to check the return value of this call since this
950
// function is only called after an update is successful and there is not
951
// much that can be done to recover if it isn't successful. There is also
952
// no need to log the value since it will have already been logged.
953
remove_recursive_on_reboot(childPath, deleteDir);
954
}
955
}
956
957
NS_tclosedir(dir);
958
959
if (rv == OK) {
960
ensure_write_permissions(path);
961
rv = NS_trmdir(path);
962
if (rv) {
963
LOG(("remove_recursive_on_reboot: unable to remove directory: " LOG_S
964
", rv: %d, err: %d",
965
path, rv, errno));
966
}
967
}
968
return rv;
969
}
970
#endif
971
972
//-----------------------------------------------------------------------------
973
974
// Create a backup of the specified file by renaming it.
975
static int backup_create(const NS_tchar* path) {
976
NS_tchar backup[MAXPATHLEN];
977
NS_tsnprintf(backup, sizeof(backup) / sizeof(backup[0]),
978
NS_T("%s") BACKUP_EXT, path);
979
980
return rename_file(path, backup);
981
}
982
983
// Rename the backup of the specified file that was created by renaming it back
984
// to the original file.
985
static int backup_restore(const NS_tchar* path, const NS_tchar* relPath) {
986
NS_tchar backup[MAXPATHLEN];
987
NS_tsnprintf(backup, sizeof(backup) / sizeof(backup[0]),
988
NS_T("%s") BACKUP_EXT, path);
989
990
NS_tchar relBackup[MAXPATHLEN];
991
NS_tsnprintf(relBackup, sizeof(relBackup) / sizeof(relBackup[0]),
992
NS_T("%s") BACKUP_EXT, relPath);
993
994
if (NS_taccess(backup, F_OK)) {
995
LOG(("backup_restore: backup file doesn't exist: " LOG_S, relBackup));
996
return OK;
997
}
998
999
return rename_file(backup, path);
1000
}
1001
1002
// Discard the backup of the specified file that was created by renaming it.
1003
static int backup_discard(const NS_tchar* path, const NS_tchar* relPath) {
1004
NS_tchar backup[MAXPATHLEN];
1005
NS_tsnprintf(backup, sizeof(backup) / sizeof(backup[0]),
1006
NS_T("%s") BACKUP_EXT, path);
1007
1008
NS_tchar relBackup[MAXPATHLEN];
1009
NS_tsnprintf(relBackup, sizeof(relBackup) / sizeof(relBackup[0]),
1010
NS_T("%s") BACKUP_EXT, relPath);
1011
1012
// Nothing to discard
1013
if (NS_taccess(backup, F_OK)) {
1014
return OK;
1015
}
1016
1017
int rv = ensure_remove(backup);
1018
#if defined(XP_WIN)
1019
if (rv && !sStagedUpdate && !sReplaceRequest) {
1020
LOG(("backup_discard: unable to remove: " LOG_S, relBackup));
1021
NS_tchar path[MAXPATHLEN];
1022
GetUUIDTempFilePath(gDeleteDirPath, L"moz", path);
1023
if (rename_file(backup, path)) {
1024
LOG(("backup_discard: failed to rename file:" LOG_S ", dst:" LOG_S,
1025
relBackup, relPath));
1026
return WRITE_ERROR_DELETE_BACKUP;
1027
}
1028
// The MoveFileEx call to remove the file on OS reboot will fail if the
1029
// process doesn't have write access to the HKEY_LOCAL_MACHINE registry key
1030
// but this is ok since the installer / uninstaller will delete the
1031
// directory containing the file along with its contents after an update is
1032
// applied, on reinstall, and on uninstall.
1033
if (MoveFileEx(path, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT)) {
1034
LOG(
1035
("backup_discard: file renamed and will be removed on OS "
1036
"reboot: " LOG_S,
1037
relPath));
1038
} else {
1039
LOG(
1040
("backup_discard: failed to schedule OS reboot removal of "
1041
"file: " LOG_S,
1042
relPath));
1043
}
1044
}
1045
#else
1046
if (rv) {
1047
return WRITE_ERROR_DELETE_BACKUP;
1048
}
1049
#endif
1050
1051
return OK;
1052
}
1053
1054
// Helper function for post-processing a temporary backup.
1055
static void backup_finish(const NS_tchar* path, const NS_tchar* relPath,
1056
int status) {
1057
if (status == OK) {
1058
backup_discard(path, relPath);
1059
} else {
1060
backup_restore(path, relPath);
1061
}
1062
}
1063
1064
//-----------------------------------------------------------------------------
1065
1066
static int DoUpdate();
1067
1068
class Action {
1069
public:
1070
Action() : mProgressCost(1), mNext(nullptr) {}
1071
virtual ~Action() = default;
1072
1073
virtual int Parse(NS_tchar* line) = 0;
1074
1075
// Do any preprocessing to ensure that the action can be performed. Execute
1076
// will be called if this Action and all others return OK from this method.
1077
virtual int Prepare() = 0;
1078
1079
// Perform the operation. Return OK to indicate success. After all actions
1080
// have been executed, Finish will be called. A requirement of Execute is
1081
// that its operation be reversable from Finish.
1082
virtual int Execute() = 0;
1083
1084
// Finish is called after execution of all actions. If status is OK, then
1085
// all actions were successfully executed. Otherwise, some action failed.
1086
virtual void Finish(int status) = 0;
1087
1088
int mProgressCost;
1089
1090
private:
1091
Action* mNext;
1092
1093
friend class ActionList;
1094
};
1095
1096
class RemoveFile : public Action {
1097
public:
1098
RemoveFile() : mSkip(0) {}
1099
1100
int Parse(NS_tchar* line) override;
1101
int Prepare() override;
1102
int Execute() override;
1103
void Finish(int status) override;
1104
1105
private:
1106
mozilla::UniquePtr<NS_tchar[]> mFile;
1107
mozilla::UniquePtr<NS_tchar[]> mRelPath;
1108
int mSkip;
1109
};
1110
1111
int RemoveFile::Parse(NS_tchar* line) {
1112
// format "<deadfile>"
1113
1114
NS_tchar* validPath = get_valid_path(&line);
1115
if (!validPath) {
1116
return PARSE_ERROR;
1117
}
1118
1119
mRelPath = mozilla::MakeUnique<NS_tchar[]>(MAXPATHLEN);
1120
NS_tstrcpy(mRelPath.get(), validPath);
1121
1122
mFile.reset(get_full_path(validPath));
1123
if (!mFile) {
1124
return PARSE_ERROR;
1125
}
1126
1127
return OK;
1128
}
1129
1130
int RemoveFile::Prepare() {
1131
// Skip the file if it already doesn't exist.
1132
int rv = NS_taccess(mFile.get(), F_OK);
1133
if (rv) {
1134
mSkip = 1;
1135
mProgressCost = 0;
1136
return OK;
1137
}
1138
1139
LOG(("PREPARE REMOVEFILE " LOG_S, mRelPath.get()));
1140
1141
// Make sure that we're actually a file...
1142
struct NS_tstat_t fileInfo;
1143
rv = NS_tstat(mFile.get(), &fileInfo);
1144
if (rv) {
1145
LOG(("failed to read file status info: " LOG_S ", err: %d", mFile.get(),
1146
errno));
1147
return READ_ERROR;
1148
}
1149
1150
if (!S_ISREG(fileInfo.st_mode)) {
1151
LOG(("path present, but not a file: " LOG_S, mFile.get()));
1152
return DELETE_ERROR_EXPECTED_FILE;
1153
}
1154
1155
NS_tchar* slash = (NS_tchar*)NS_tstrrchr(mFile.get(), NS_T('/'));
1156
if (slash) {
1157
*slash = NS_T('\0');
1158
rv = NS_taccess(mFile.get(), W_OK);
1159
*slash = NS_T('/');
1160
} else {
1161
rv = NS_taccess(NS_T("."), W_OK);
1162
}
1163
1164
if (rv) {
1165
LOG(("access failed: %d", errno));
1166
return WRITE_ERROR_FILE_ACCESS_DENIED;
1167
}
1168
1169
return OK;
1170
}
1171
1172
int RemoveFile::Execute() {
1173
if (mSkip) {
1174
return OK;
1175
}
1176
1177
LOG(("EXECUTE REMOVEFILE " LOG_S, mRelPath.get()));
1178
1179
// The file is checked for existence here and in Prepare since it might have
1180
// been removed by a separate instruction: bug 311099.
1181
int rv = NS_taccess(mFile.get(), F_OK);
1182
if (rv) {
1183
LOG(("file cannot be removed because it does not exist; skipping"));
1184
mSkip = 1;
1185
return OK;
1186
}
1187
1188
if (sStagedUpdate) {
1189
// Staged updates don't need backup files so just remove it.
1190
rv = ensure_remove(mFile.get());
1191
if (rv) {
1192
return rv;
1193
}
1194
} else {
1195
// Rename the old file. It will be removed in Finish.
1196
rv = backup_create(mFile.get());
1197
if (rv) {
1198
LOG(("backup_create failed: %d", rv));
1199
return rv;
1200
}
1201
}
1202
1203
return OK;
1204
}
1205
1206
void RemoveFile::Finish(int status) {
1207
if (mSkip) {
1208
return;
1209
}
1210
1211
LOG(("FINISH REMOVEFILE " LOG_S, mRelPath.get()));
1212
1213
// Staged updates don't create backup files.
1214
if (!sStagedUpdate) {
1215
backup_finish(mFile.get(), mRelPath.get(), status);
1216
}
1217
}
1218
1219
class RemoveDir : public Action {
1220
public:
1221
RemoveDir() : mSkip(0) {}
1222
1223
int Parse(NS_tchar* line) override;
1224
int Prepare() override; // check that the source dir exists
1225
int Execute() override;
1226
void Finish(int status) override;
1227
1228
private:
1229
mozilla::UniquePtr<NS_tchar[]> mDir;
1230
mozilla::UniquePtr<NS_tchar[]> mRelPath;
1231
int mSkip;
1232
};
1233
1234
int RemoveDir::Parse(NS_tchar* line) {
1235
// format "<deaddir>/"
1236
1237
NS_tchar* validPath = get_valid_path(&line, true);
1238
if (!validPath) {
1239
return PARSE_ERROR;
1240
}
1241
1242
mRelPath = mozilla::MakeUnique<NS_tchar[]>(MAXPATHLEN);
1243
NS_tstrcpy(mRelPath.get(), validPath);
1244
1245
mDir.reset(get_full_path(validPath));
1246
if (!mDir) {
1247
return PARSE_ERROR;
1248
}
1249
1250
return OK;
1251
}
1252
1253
int RemoveDir::Prepare() {
1254
// We expect the directory to exist if we are to remove it.
1255
int rv = NS_taccess(mDir.get(), F_OK);
1256
if (rv) {
1257
mSkip = 1;
1258
mProgressCost = 0;
1259
return OK;
1260
}
1261
1262
LOG(("PREPARE REMOVEDIR " LOG_S "/", mRelPath.get()));
1263
1264
// Make sure that we're actually a dir.
1265
struct NS_tstat_t dirInfo;
1266
rv = NS_tstat(mDir.get(), &dirInfo);
1267
if (rv) {
1268
LOG(("failed to read directory status info: " LOG_S ", err: %d",
1269
mRelPath.get(), errno));
1270
return READ_ERROR;
1271
}
1272
1273
if (!S_ISDIR(dirInfo.st_mode)) {
1274
LOG(("path present, but not a directory: " LOG_S, mRelPath.get()));
1275
return DELETE_ERROR_EXPECTED_DIR;
1276
}
1277
1278
rv = NS_taccess(mDir.get(), W_OK);
1279
if (rv) {
1280
LOG(("access failed: %d, %d", rv, errno));
1281
return WRITE_ERROR_DIR_ACCESS_DENIED;
1282
}
1283
1284
return OK;
1285
}
1286
1287
int RemoveDir::Execute() {
1288
if (mSkip) {
1289
return OK;
1290
}
1291
1292
LOG(("EXECUTE REMOVEDIR " LOG_S "/", mRelPath.get()));
1293
1294
// The directory is checked for existence at every step since it might have
1295
// been removed by a separate instruction: bug 311099.
1296
int rv = NS_taccess(mDir.get(), F_OK);
1297
if (rv) {
1298
LOG(("directory no longer exists; skipping"));
1299
mSkip = 1;
1300
}
1301
1302
return OK;
1303
}
1304
1305
void RemoveDir::Finish(int status) {
1306
if (mSkip || status != OK) {
1307
return;
1308
}
1309
1310
LOG(("FINISH REMOVEDIR " LOG_S "/", mRelPath.get()));
1311
1312
// The directory is checked for existence at every step since it might have
1313
// been removed by a separate instruction: bug 311099.
1314
int rv = NS_taccess(mDir.get(), F_OK);
1315
if (rv) {
1316
LOG(("directory no longer exists; skipping"));
1317
return;
1318
}
1319
1320
if (status == OK) {
1321
if (NS_trmdir(mDir.get())) {
1322
LOG(("non-fatal error removing directory: " LOG_S "/, rv: %d, err: %d",
1323
mRelPath.get(), rv, errno));
1324
}
1325
}
1326
}
1327
1328
class AddFile : public Action {
1329
public:
1330
AddFile() : mAdded(false) {}
1331
1332
int Parse(NS_tchar* line) override;
1333
int Prepare() override;
1334
int Execute() override;
1335
void Finish(int status) override;
1336
1337
private:
1338
mozilla::UniquePtr<NS_tchar[]> mFile;
1339
mozilla::UniquePtr<NS_tchar[]> mRelPath;
1340
bool mAdded;
1341
};
1342
1343
int AddFile::Parse(NS_tchar* line) {
1344
// format "<newfile>"
1345
1346
NS_tchar* validPath = get_valid_path(&line);
1347
if (!validPath) {
1348
return PARSE_ERROR;
1349
}
1350
1351
mRelPath = mozilla::MakeUnique<NS_tchar[]>(MAXPATHLEN);
1352
NS_tstrcpy(mRelPath.get(), validPath);
1353
1354
mFile.reset(get_full_path(validPath));
1355
if (!mFile) {
1356
return PARSE_ERROR;
1357
}
1358
1359
return OK;
1360
}
1361
1362
int AddFile::Prepare() {
1363
LOG(("PREPARE ADD " LOG_S, mRelPath.get()));
1364
1365
return OK;
1366
}
1367
1368
int AddFile::Execute() {
1369
LOG(("EXECUTE ADD " LOG_S, mRelPath.get()));
1370
1371
int rv;
1372
1373
// First make sure that we can actually get rid of any existing file.
1374
rv = NS_taccess(mFile.get(), F_OK);
1375
if (rv == 0) {
1376
if (sStagedUpdate) {
1377
// Staged updates don't need backup files so just remove it.
1378
rv = ensure_remove(mFile.get());
1379
} else {
1380
rv = backup_create(mFile.get());
1381
}
1382
if (rv) {
1383
return rv;
1384
}
1385
} else {
1386
rv = ensure_parent_dir(mFile.get());
1387
if (rv) {
1388
return rv;
1389
}
1390
}
1391
1392
#ifdef XP_WIN
1393
char sourcefile[MAXPATHLEN];
1394
if (!WideCharToMultiByte(CP_UTF8, 0, mRelPath.get(), -1, sourcefile,
1395
MAXPATHLEN, nullptr, nullptr)) {
1396
LOG(("error converting wchar to utf8: %d", GetLastError()));
1397
return STRING_CONVERSION_ERROR;
1398
}
1399
1400
rv = gArchiveReader.ExtractFile(sourcefile, mFile.get());
1401
#else
1402
rv = gArchiveReader.ExtractFile(mRelPath.get(), mFile.get());
1403
#endif
1404
if (!rv) {
1405
mAdded = true;
1406
}
1407
return rv;
1408
}
1409
1410
void AddFile::Finish(int status) {
1411
LOG(("FINISH ADD " LOG_S, mRelPath.get()));
1412
// Staged updates don't create backup files.
1413
if (!sStagedUpdate) {
1414
// When there is an update failure and a file has been added it is removed
1415
// here since there might not be a backup to replace it.
1416
if (status && mAdded) {
1417
if (NS_tremove(mFile.get()) && errno != ENOENT) {
1418
LOG(("non-fatal error after update failure removing added file: " LOG_S
1419
", err: %d",
1420
mFile.get(), errno));
1421
}
1422
}
1423
backup_finish(mFile.get(), mRelPath.get(), status);
1424
}
1425
}
1426
1427
class PatchFile : public Action {
1428
public:
1429
PatchFile() : mPatchFile(nullptr), mPatchIndex(-1), buf(nullptr) {}
1430
1431
~PatchFile() override;
1432
1433
int Parse(NS_tchar* line) override;
1434
int Prepare() override; // should check for patch file and for checksum here
1435
int Execute() override;
1436
void Finish(int status) override;
1437
1438
private:
1439
int LoadSourceFile(FILE* ofile);
1440
1441
static int sPatchIndex;
1442
1443
const NS_tchar* mPatchFile;
1444
mozilla::UniquePtr<NS_tchar[]> mFile;
1445
mozilla::UniquePtr<NS_tchar[]> mFileRelPath;
1446
int mPatchIndex;
1447
MBSPatchHeader header;
1448
unsigned char* buf;
1449
NS_tchar spath[MAXPATHLEN];
1450
AutoFile mPatchStream;
1451
};
1452
1453
int PatchFile::sPatchIndex = 0;
1454
1455
PatchFile::~PatchFile() {
1456
// Make sure mPatchStream gets unlocked on Windows; the system will do that,
1457
// but not until some indeterminate future time, and we want determinism.
1458
// Normally this happens at the end of Execute, when we close the stream;
1459
// this call is here in case Execute errors out.
1460
#ifdef XP_WIN
1461
if (mPatchStream) {
1462
UnlockFile((HANDLE)_get_osfhandle(fileno(mPatchStream)), 0, 0, -1, -1);
1463
}
1464
#endif
1465
// Patch files are written to the <working_dir>/updating directory which is
1466
// removed after the update has finished so don't delete patch files here.
1467
1468
if (buf) {
1469
free(buf);
1470
}
1471
}
1472
1473
int PatchFile::LoadSourceFile(FILE* ofile) {
1474
struct stat os;
1475
int rv = fstat(fileno((FILE*)ofile), &os);
1476
if (rv) {
1477
LOG(("LoadSourceFile: unable to stat destination file: " LOG_S ", "
1478
"err: %d",
1479
mFileRelPath.get(), errno));
1480
return READ_ERROR;
1481
}
1482
1483
if (uint32_t(os.st_size) != header.slen) {
1484
LOG(
1485
("LoadSourceFile: destination file size %d does not match expected "
1486
"size %d",
1487
uint32_t(os.st_size), header.slen));
1488
return LOADSOURCE_ERROR_WRONG_SIZE;
1489
}
1490
1491
buf = (unsigned char*)malloc(header.slen);
1492
if (!buf) {
1493
return UPDATER_MEM_ERROR;
1494
}
1495
1496
size_t r = header.slen;
1497
unsigned char* rb = buf;
1498
while (r) {
1499
const size_t count = mmin(SSIZE_MAX, r);
1500
size_t c = fread(rb, 1, count, ofile);
1501
if (c != count) {
1502
LOG(("LoadSourceFile: error reading destination file: " LOG_S,
1503
mFileRelPath.get()));
1504
return READ_ERROR;
1505
}
1506
1507
r -= c;
1508
rb += c;
1509
}
1510
1511
// Verify that the contents of the source file correspond to what we expect.
1512
1513
unsigned int crc = crc32(buf, header.slen);
1514
1515
if (crc != header.scrc32) {
1516
LOG(
1517
("LoadSourceFile: destination file crc %d does not match expected "
1518
"crc %d",
1519
crc, header.scrc32));
1520
return CRC_ERROR;
1521
}
1522
1523
return OK;
1524
}
1525
1526
int PatchFile::Parse(NS_tchar* line) {
1527
// format "<patchfile>" "<filetopatch>"
1528
1529
// Get the path to the patch file inside of the mar
1530
mPatchFile = mstrtok(kQuote, &line);
1531
if (!mPatchFile) {
1532
return PARSE_ERROR;
1533
}
1534
1535
// consume whitespace between args
1536
NS_tchar* q = mstrtok(kQuote, &line);
1537
if (!q) {
1538
return PARSE_ERROR;
1539
}
1540
1541
NS_tchar* validPath = get_valid_path(&line);
1542
if (!validPath) {
1543
return PARSE_ERROR;
1544
}
1545
1546
mFileRelPath = mozilla::MakeUnique<NS_tchar[]>(MAXPATHLEN);
1547
NS_tstrcpy(mFileRelPath.get(), validPath);
1548
1549
mFile.reset(get_full_path(validPath));
1550
if (!mFile) {
1551
return PARSE_ERROR;
1552
}
1553
1554
return OK;
1555
}
1556
1557
int PatchFile::Prepare() {
1558
LOG(("PREPARE PATCH " LOG_S, mFileRelPath.get()));
1559
1560
// extract the patch to a temporary file
1561
mPatchIndex = sPatchIndex++;
1562
1563
NS_tsnprintf(spath, sizeof(spath) / sizeof(spath[0]),
1564
NS_T("%s/updating/%d.patch"), gWorkingDirPath, mPatchIndex);
1565
1566
// The removal of pre-existing patch files here is in case a previous update
1567
// crashed and left these files behind.
1568
if (NS_tremove(spath) && errno != ENOENT) {
1569
LOG(("failure removing pre-existing patch file: " LOG_S ", err: %d", spath,
1570
errno));
1571
return WRITE_ERROR;
1572
}
1573
1574
mPatchStream = NS_tfopen(spath, NS_T("wb+"));
1575
if (!mPatchStream) {
1576
return WRITE_ERROR;
1577
}
1578
1579
#ifdef XP_WIN
1580
// Lock the patch file, so it can't be messed with between
1581
// when we're done creating it and when we go to apply it.
1582
if (!LockFile((HANDLE)_get_osfhandle(fileno(mPatchStream)), 0, 0, -1, -1)) {
1583
LOG(("Couldn't lock patch file: %d", GetLastError()));
1584
return LOCK_ERROR_PATCH_FILE;
1585
}
1586
1587
char sourcefile[MAXPATHLEN];
1588
if (!WideCharToMultiByte(CP_UTF8, 0, mPatchFile, -1, sourcefile, MAXPATHLEN,
1589
nullptr, nullptr)) {
1590
LOG(("error converting wchar to utf8: %d", GetLastError()));
1591
return STRING_CONVERSION_ERROR;
1592
}
1593
1594
int rv = gArchiveReader.ExtractFileToStream(sourcefile, mPatchStream);
1595
#else
1596
int rv = gArchiveReader.ExtractFileToStream(mPatchFile, mPatchStream);
1597
#endif
1598
1599
return rv;
1600
}
1601
1602
int PatchFile::Execute() {
1603
LOG(("EXECUTE PATCH " LOG_S, mFileRelPath.get()));
1604
1605
fseek(mPatchStream, 0, SEEK_SET);
1606
1607
int rv = MBS_ReadHeader(mPatchStream, &header);
1608
if (rv) {
1609
return rv;
1610
}
1611
1612
FILE* origfile = nullptr;
1613
#ifdef XP_WIN
1614
if (NS_tstrcmp(mFileRelPath.get(), gCallbackRelPath) == 0) {
1615
// Read from the copy of the callback when patching since the callback can't
1616
// be opened for reading to prevent the application from being launched.
1617
origfile = NS_tfopen(gCallbackBackupPath, NS_T("rb"));
1618
} else {
1619
origfile = NS_tfopen(mFile.get(), NS_T("rb"));
1620
}
1621
#else
1622
origfile = NS_tfopen(mFile.get(), NS_T("rb"));
1623
#endif
1624
1625
if (!origfile) {
1626
LOG(("unable to open destination file: " LOG_S ", err: %d",
1627
mFileRelPath.get(), errno));
1628
return READ_ERROR;
1629
}
1630
1631
rv = LoadSourceFile(origfile);
1632
fclose(origfile);
1633
if (rv) {
1634
LOG(("LoadSourceFile failed"));
1635
return rv;
1636
}
1637
1638
// Rename the destination file if it exists before proceeding so it can be
1639
// used to restore the file to its original state if there is an error.
1640
struct NS_tstat_t ss;
1641
rv = NS_tstat(mFile.get(), &ss);
1642
if (rv) {
1643
LOG(("failed to read file status info: " LOG_S ", err: %d",
1644
mFileRelPath.get(), errno));
1645
return READ_ERROR;
1646
}
1647
1648
// Staged updates don't need backup files.
1649
if (!sStagedUpdate) {
1650
rv = backup_create(mFile.get());
1651
if (rv) {
1652
return rv;
1653
}
1654
}
1655
1656
#if defined(HAVE_POSIX_FALLOCATE)
1657
AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
1658
posix_fallocate(fileno((FILE*)ofile), 0, header.dlen);
1659
#elif defined(XP_WIN)
1660
bool shouldTruncate = true;
1661
// Creating the file, setting the size, and then closing the file handle
1662
// lessens fragmentation more than any other method tested. Other methods that
1663
// have been tested are:
1664
// 1. _chsize / _chsize_s reduced fragmentation though not completely.
1665
// 2. _get_osfhandle and then setting the size reduced fragmentation though
1666
// not completely. There are also reports of _get_osfhandle failing on
1667
// mingw.
1668
HANDLE hfile = CreateFileW(mFile.get(), GENERIC_WRITE, 0, nullptr,
1669
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
1670
1671
if (hfile != INVALID_HANDLE_VALUE) {
1672
if (SetFilePointer(hfile, header.dlen, nullptr, FILE_BEGIN) !=
1673
INVALID_SET_FILE_POINTER &&
1674
SetEndOfFile(hfile) != 0) {
1675
shouldTruncate = false;
1676
}
1677
CloseHandle(hfile);
1678
}
1679
1680
AutoFile ofile(ensure_open(
1681
mFile.get(), shouldTruncate ? NS_T("wb+") : NS_T("rb+"), ss.st_mode));
1682
#elif defined(XP_MACOSX)
1683
AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
1684
// Modified code from FileUtils.cpp
1685
fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, header.dlen};
1686
// Try to get a continous chunk of disk space
1687
rv = fcntl(fileno((FILE*)ofile), F_PREALLOCATE, &store);
1688
if (rv == -1) {
1689
// OK, perhaps we are too fragmented, allocate non-continuous
1690
store.fst_flags = F_ALLOCATEALL;
1691
rv = fcntl(fileno((FILE*)ofile), F_PREALLOCATE, &store);
1692
}
1693
1694
if (rv != -1) {
1695
ftruncate(fileno((FILE*)ofile), header.dlen);
1696
}
1697
#else
1698
AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
1699
#endif
1700
1701
if (ofile == nullptr) {
1702
LOG(("unable to create new file: " LOG_S ", err: %d", mFileRelPath.get(),
1703
errno));
1704
return WRITE_ERROR_OPEN_PATCH_FILE;
1705
}
1706
1707
#ifdef XP_WIN
1708
if (!shouldTruncate) {
1709
fseek(ofile, 0, SEEK_SET);
1710
}
1711
#endif
1712
1713
rv = MBS_ApplyPatch(&header, mPatchStream, buf, ofile);
1714
1715
// Go ahead and do a bit of cleanup now to minimize runtime overhead.
1716
// Make sure mPatchStream gets unlocked on Windows; the system will do that,
1717
// but not until some indeterminate future time, and we want determinism.
1718
#ifdef XP_WIN
1719
UnlockFile((HANDLE)_get_osfhandle(fileno(mPatchStream)), 0, 0, -1, -1);
1720
#endif
1721
// Set mPatchStream to nullptr to make AutoFile close the file,
1722
// so it can be deleted on Windows.
1723
mPatchStream = nullptr;
1724
// Patch files are written to the <working_dir>/updating directory which is
1725
// removed after the update has finished so don't delete patch files here.
1726
spath[0] = NS_T('\0');
1727
free(buf);
1728
buf = nullptr;
1729
1730
return rv;
1731
}
1732
1733
void PatchFile::Finish(int status) {
1734
LOG(("FINISH PATCH " LOG_S, mFileRelPath.get()));
1735
1736
// Staged updates don't create backup files.
1737
if (!sStagedUpdate) {
1738
backup_finish(mFile.get(), mFileRelPath.get(), status);
1739
}
1740
}
1741
1742
class AddIfFile : public AddFile {
1743
public:
1744
int Parse(NS_tchar* line) override;
1745
int Prepare() override;
1746
int Execute() override;
1747
void Finish(int status) override;
1748
1749
protected:
1750
mozilla::UniquePtr<NS_tchar[]> mTestFile;
1751
};
1752
1753
int AddIfFile::Parse(NS_tchar* line) {
1754
// format "<testfile>" "<newfile>"
1755
1756
mTestFile.reset(get_full_path(get_valid_path(&line)));
1757
if (!mTestFile) {
1758
return PARSE_ERROR;
1759
}
1760
1761
// consume whitespace between args
1762
NS_tchar* q = mstrtok(kQuote, &line);
1763
if (!q) {
1764
return PARSE_ERROR;
1765
}
1766
1767
return AddFile::Parse(line);
1768
}
1769
1770
int AddIfFile::Prepare() {
1771
// If the test file does not exist, then skip this action.
1772
if (NS_taccess(mTestFile.get(), F_OK)) {
1773
mTestFile = nullptr;
1774
return OK;
1775
}
1776
1777
return AddFile::Prepare();
1778
}
1779
1780
int AddIfFile::Execute() {
1781
if (!mTestFile) {
1782
return OK;
1783
}
1784
1785
return AddFile::Execute();
1786
}
1787
1788
void AddIfFile::Finish(int status) {
1789
if (!mTestFile) {
1790
return;
1791
}
1792
1793
AddFile::Finish(status);
1794
}
1795
1796
class AddIfNotFile : public AddFile {
1797
public:
1798
int Parse(NS_tchar* line) override;
1799
int Prepare() override;
1800
int Execute() override;
1801
void Finish(int status) override;
1802
1803
protected:
1804
mozilla::UniquePtr<NS_tchar[]> mTestFile;
1805
};
1806
1807
int AddIfNotFile::Parse(NS_tchar* line) {
1808
// format "<testfile>" "<newfile>"
1809
1810
mTestFile.reset(get_full_path(get_valid_path(&line)));
1811
if (!mTestFile) {
1812
return PARSE_ERROR;
1813
}
1814
1815
// consume whitespace between args
1816
NS_tchar* q = mstrtok(kQuote, &line);
1817
if (!q) {
1818
return PARSE_ERROR;
1819
}
1820
1821
return AddFile::Parse(line);
1822
}
1823
1824
int AddIfNotFile::Prepare() {
1825
// If the test file exists, then skip this action.
1826
if (!NS_taccess(mTestFile.get(), F_OK)) {
1827
mTestFile = NULL;
1828
return OK;
1829
}
1830
1831
return AddFile::Prepare();
1832
}
1833
1834
int AddIfNotFile::Execute() {
1835
if (!mTestFile) {
1836
return OK;
1837
}
1838
1839
return AddFile::Execute();
1840
}
1841
1842
void AddIfNotFile::Finish(int status) {
1843
if (!mTestFile) {
1844
return;
1845
}
1846
1847
AddFile::Finish(status);
1848
}
1849
1850
class PatchIfFile : public PatchFile {
1851
public:
1852
int Parse(NS_tchar* line) override;
1853
int Prepare() override; // should check for patch file and for checksum here
1854
int Execute() override;
1855
void Finish(int status) override;
1856
1857
private:
1858
mozilla::UniquePtr<NS_tchar[]> mTestFile;
1859
};
1860
1861
int PatchIfFile::Parse(NS_tchar* line) {
1862
// format "<testfile>" "<patchfile>" "<filetopatch>"
1863
1864
mTestFile.reset(get_full_path(get_valid_path(&line)));
1865
if (!mTestFile) {
1866
return PARSE_ERROR;
1867
}
1868
1869
// consume whitespace between args
1870
NS_tchar* q = mstrtok(kQuote, &line);
1871
if (!q) {
1872
return PARSE_ERROR;
1873
}
1874
1875
return PatchFile::Parse(line);
1876
}
1877
1878
int PatchIfFile::Prepare() {
1879
// If the test file does not exist, then skip this action.
1880
if (NS_taccess(mTestFile.get(), F_OK)) {
1881
mTestFile = nullptr;
1882
return OK;
1883
}
1884
1885
return PatchFile::Prepare();
1886
}
1887
1888
int PatchIfFile::Execute() {
1889
if (!mTestFile) {
1890
return OK;
1891
}
1892
1893
return PatchFile::Execute();
1894
}
1895
1896
void PatchIfFile::Finish(int status) {
1897
if (!mTestFile) {
1898
return;
1899
}
1900
1901
PatchFile::Finish(status);
1902
}
1903
1904
//-----------------------------------------------------------------------------
1905
1906
#ifdef XP_WIN
1907
# include "nsWindowsRestart.cpp"
1908
# include "nsWindowsHelpers.h"
1909
# include "uachelper.h"
1910
# include "pathhash.h"
1911
1912
/**
1913
* Launch the post update application (helper.exe). It takes in the path of the
1914
* callback application to calculate the path of helper.exe. For service updates
1915
* this is called from both the system account and the current user account.
1916
*
1917
* @param installationDir The path to the callback application binary.
1918
* @param updateInfoDir The directory where update info is stored.
1919
* @return true if there was no error starting the process.
1920
*/
1921
bool LaunchWinPostProcess(const WCHAR* installationDir,
1922
const WCHAR* updateInfoDir) {
1923
WCHAR workingDirectory[MAX_PATH + 1] = {L'\0'};
1924
wcsncpy(workingDirectory, installationDir, MAX_PATH);
1925
1926
// Launch helper.exe to perform post processing (e.g. registry and log file
1927
// modifications) for the update.
1928
WCHAR inifile[MAX_PATH + 1] = {L'\0'};
1929
wcsncpy(inifile, installationDir, MAX_PATH);
1930
if (!PathAppendSafe(inifile, L"updater.ini")) {
1931
return false;
1932
}
1933
1934
WCHAR exefile[MAX_PATH + 1];
1935
WCHAR exearg[MAX_PATH + 1];
1936
WCHAR exeasync[10];
1937
bool async = true;
1938
if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeRelPath", nullptr,
1939
exefile, MAX_PATH + 1, inifile)) {
1940
return false;
1941
}
1942
1943
if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeArg", nullptr, exearg,
1944
MAX_PATH + 1, inifile)) {
1945
return