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