Back to index

lightning-sunbird  0.9+nobinonly
updater.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla Application Update.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Benjamin Smedberg <benjamin@smedbergs.us>
00018  *
00019  * Portions created by the Initial Developer are Copyright (C) 2005
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Darin Fisher <darin@meer.net>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00050 #include "bspatch.h"
00051 #include "progressui.h"
00052 #include "archivereader.h"
00053 #include "errors.h"
00054 #include "bzlib.h"
00055 
00056 #include <stdio.h>
00057 #include <string.h>
00058 #include <stdlib.h>
00059 #include <stdarg.h>
00060 
00061 #include <sys/types.h>
00062 #include <sys/stat.h>
00063 #include <fcntl.h>
00064 #include <limits.h>
00065 #include <errno.h>
00066 
00067 #if defined(XP_WIN)
00068 # include <windows.h>
00069 # include <direct.h>
00070 # include <io.h>
00071 # define F_OK 00
00072 # define W_OK 02
00073 # define R_OK 04
00074 # define access _access
00075 # define snprintf _snprintf
00076 # define putenv _putenv
00077 # define fchmod(a,b)
00078 # define mkdir(path, perm) _mkdir(path)
00079 # define chdir(path) _chdir(path)
00080 #else
00081 # include <sys/wait.h>
00082 # include <unistd.h>
00083 #endif
00084 
00085 #if defined(XP_MACOSX)
00086 // This function is defined in launchchild_osx.mm
00087 void LaunchChild(int argc, char **argv);
00088 #endif
00089 
00090 #ifndef _O_BINARY
00091 # define _O_BINARY 0
00092 #endif
00093 
00094 #ifndef NULL
00095 # define NULL (0)
00096 #endif
00097 
00098 #ifndef SSIZE_MAX
00099 # define SSIZE_MAX LONG_MAX
00100 #endif
00101 
00102 #ifndef MAXPATHLEN
00103 # ifdef MAX_PATH
00104 #  define MAXPATHLEN MAX_PATH
00105 # elif defined(_MAX_PATH)
00106 #  define MAXPATHLEN _MAX_PATH
00107 # elif defined(CCHMAXPATH)
00108 #  define MAXPATHLEN CCHMAXPATH
00109 # else
00110 #  define MAXPATHLEN 1024
00111 # endif
00112 #endif
00113 
00114 // We want to use execv to invoke the callback executable on platforms where
00115 // we were launched using execv.  See nsUpdateDriver.cpp.
00116 #if defined(XP_UNIX) && !defined(XP_MACOSX)
00117 #define USE_EXECV
00118 #endif
00119 
00120 //-----------------------------------------------------------------------------
00121 
00122 // This variable lives in libbz2.  It's declared in bzlib_private.h, so we just
00123 // declare it here to avoid including that entire header file.
00124 extern "C" unsigned int BZ2_crc32Table[256];
00125 
00126 static unsigned int
00127 crc32(const unsigned char *buf, unsigned int len)
00128 {
00129   unsigned int crc = 0xffffffffL;
00130 
00131   const unsigned char *end = buf + len;
00132   for (; buf != end; ++buf)
00133     crc = (crc << 8) ^ BZ2_crc32Table[(crc >> 24) ^ *buf];
00134 
00135   crc = ~crc;
00136   return crc;
00137 }
00138 
00139 //-----------------------------------------------------------------------------
00140 
00141 // A simple stack based container for a file descriptor (int) that closes the
00142 // file descriptor from its destructor.
00143 class AutoFD
00144 {
00145 public:
00146   AutoFD(int fd = -1)
00147     : mFD(fd) {
00148   }
00149 
00150   ~AutoFD() {
00151     if (mFD != -1)
00152       close(mFD);
00153   }
00154 
00155   AutoFD &operator=(int fd) {
00156     if (mFD != -1)
00157       close(mFD);
00158     mFD = fd;
00159     return *this;
00160   }
00161 
00162   operator int() {
00163     return mFD;
00164   }
00165 
00166 private:
00167   int mFD;
00168 };
00169 
00170 //-----------------------------------------------------------------------------
00171 
00172 typedef void (* ThreadFunc)(void *param);
00173 
00174 #ifdef XP_WIN
00175 #include <process.h>
00176 
00177 class Thread
00178 {
00179 public:
00180   int Run(ThreadFunc func, void *param)
00181   {
00182     mThreadFunc = func;
00183     mThreadParam = param;
00184 
00185     unsigned threadID;
00186     mThread = (HANDLE) _beginthreadex(NULL, 0, ThreadMain, this, 0, &threadID);
00187     
00188     return mThread ? 0 : -1;
00189   }
00190   int Join()
00191   {
00192     WaitForSingleObject(mThread, INFINITE);
00193     CloseHandle(mThread);
00194     return 0;
00195   }
00196 private:
00197   static unsigned __stdcall ThreadMain(void *p)
00198   {
00199     Thread *self = (Thread *) p;
00200     self->mThreadFunc(self->mThreadParam);
00201     return 0;
00202   }
00203   HANDLE     mThread;
00204   ThreadFunc mThreadFunc;
00205   void      *mThreadParam;
00206 };
00207 
00208 #elif defined(XP_UNIX)
00209 #include <pthread.h>
00210 
00211 class Thread
00212 {
00213 public:
00214   int Run(ThreadFunc func, void *param)
00215   {
00216     return pthread_create(&thr, NULL, (void* (*)(void *)) func, param);
00217   }
00218   int Join()
00219   {
00220     void *result;
00221     return pthread_join(thr, &result);
00222   }
00223 private:
00224   pthread_t thr;
00225 };
00226 
00227 #elif defined(XP_OS2)
00228 
00229 class Thread
00230 {
00231 public:
00232   int Run(ThreadFunc func, void *param)
00233   {
00234     mThreadFunc = func;
00235     mThreadParam = param;
00236 
00237     mThread = _beginthread(ThreadMain, NULL, 16384, (void *)this);
00238     
00239     return mThread ? 0 : -1;
00240   }
00241   int Join()
00242   {
00243     int status;
00244     waitpid(mThread, &status, 0);
00245     return 0;
00246   }
00247 private:
00248   static void ThreadMain(void *p)
00249   {
00250     Thread *self = (Thread *) p;
00251     self->mThreadFunc(self->mThreadParam);
00252   }
00253   int        mThread;
00254   ThreadFunc mThreadFunc;
00255   void      *mThreadParam;
00256 };
00257 
00258 #else
00259 #error "Unsupported platform"
00260 #endif
00261 
00262 //-----------------------------------------------------------------------------
00263 
00264 static char* gSourcePath;
00265 static ArchiveReader gArchiveReader;
00266 #ifdef XP_WIN
00267 static bool gSucceeded = FALSE;
00268 #endif
00269 
00270 static const char kWhitespace[] = " \t";
00271 static const char kNL[] = "\r\n";
00272 static const char kQuote[] = "\"";
00273 
00274 //-----------------------------------------------------------------------------
00275 // LOGGING
00276 
00277 static FILE *gLogFP = NULL;
00278 
00279 static void LogInit()
00280 {
00281   if (gLogFP)
00282     return;
00283 
00284   char logFile[MAXPATHLEN];
00285   snprintf(logFile, MAXPATHLEN, "%s/update.log", gSourcePath);
00286 
00287   gLogFP = fopen(logFile, "w");
00288 }
00289 
00290 static void LogFinish()
00291 {
00292   if (!gLogFP)
00293     return;
00294 
00295   fclose(gLogFP);
00296   gLogFP = NULL;
00297 }
00298 
00299 static void LogPrintf(const char *fmt, ... )
00300 {
00301   if (!gLogFP)
00302     return;
00303 
00304   va_list ap;
00305   va_start(ap, fmt);
00306   vfprintf(gLogFP, fmt, ap);
00307   va_end(ap);
00308 }
00309 
00310 #define LOG(args) LogPrintf args
00311 
00312 //-----------------------------------------------------------------------------
00313 
00314 static inline PRUint32
00315 mmin(PRUint32 a, PRUint32 b)
00316 {
00317   return (a > b) ? b : a;
00318 }
00319 
00320 static char*
00321 mstrtok(const char *delims, char **str)
00322 {
00323   if (!*str || !**str)
00324     return NULL;
00325 
00326   // skip leading "whitespace"
00327   char *ret = *str;
00328   const char *d;
00329   do {
00330     for (d = delims; *d != '\0'; ++d) {
00331       if (*ret == *d) {
00332         ++ret;
00333         break;
00334       }
00335     }
00336   } while (*d);
00337 
00338   if (!*ret) {
00339     *str = ret;
00340     return NULL;
00341   }
00342 
00343   char *i = ret;
00344   do {
00345     for (d = delims; *d != '\0'; ++d) {
00346       if (*i == *d) {
00347         *i = '\0';
00348         *str = ++i;
00349         return ret;
00350       }
00351     }
00352     ++i;
00353   } while (*i);
00354 
00355   *str = NULL;
00356   return ret;
00357 }
00358 
00359 static void ensure_write_permissions(const char *path)
00360 {
00361 #ifdef XP_WIN
00362   (void)_chmod(path, _S_IREAD | _S_IWRITE);
00363 #else
00364   struct stat fs;
00365   if (!stat(path, &fs) && !(fs.st_mode & S_IWUSR)) {
00366     (void)chmod(path, fs.st_mode | S_IWUSR);
00367   }
00368 #endif
00369 }
00370 
00371 static int ensure_remove(const char *path)
00372 {
00373   ensure_write_permissions(path);
00374   int rv = remove(path);
00375   if (rv)
00376     LOG(("remove failed: %d,%d (%s)\n", rv, errno, path));
00377   return rv;
00378 }
00379 
00380 static int ensure_open(const char *path, int flags, int options)
00381 {
00382   ensure_write_permissions(path);
00383   return open(path, flags, options);
00384 }
00385 
00386 // Ensure that the directory containing this file exists.
00387 static int ensure_parent_dir(const char *path)
00388 {
00389   int rv = OK;
00390 
00391   char *slash = (char *) strrchr(path, '/');
00392   if (slash)
00393   {
00394     *slash = '\0';
00395     rv = ensure_parent_dir(path);
00396     if (rv == OK) {
00397       rv = mkdir(path, 0755);
00398       // If the directory already exists, then ignore the error.
00399       if (rv < 0 && errno != EEXIST) {
00400         rv = WRITE_ERROR;
00401       } else {
00402         rv = OK;
00403       }
00404     }
00405     *slash = '/';
00406   }
00407   return rv;
00408 }
00409 
00410 static int copy_file(const char *spath, const char *dpath)
00411 {
00412   int rv = ensure_parent_dir(dpath);
00413   if (rv)
00414     return rv;
00415 
00416   struct stat ss;
00417 
00418   AutoFD sfd = open(spath, O_RDONLY | _O_BINARY);
00419   if (sfd < 0 || fstat(sfd, &ss)) {
00420     LOG(("copy_file: failed to open or stat: %d,%s,%d\n", (int) sfd, spath, errno));
00421     return READ_ERROR;
00422   }
00423 
00424   AutoFD dfd = ensure_open(dpath, O_WRONLY | O_TRUNC | O_CREAT | _O_BINARY, ss.st_mode);
00425   if (dfd < 0) {
00426     LOG(("copy_file: failed to open: %s,%d\n", dpath, errno));
00427     return WRITE_ERROR;
00428   }
00429 
00430   char buf[BUFSIZ];
00431   int sc;
00432   while ((sc = read(sfd, buf, sizeof(buf))) > 0) {
00433     int dc;
00434     char *bp = buf;
00435     while ((dc = write(dfd, bp, (unsigned int) sc)) > 0) {
00436       if ((sc -= dc) == 0)
00437         break;
00438       bp += dc;
00439     }
00440     if (dc < 0) {
00441       LOG(("copy_file: failed to write: %d\n", errno));
00442       return WRITE_ERROR;
00443     }
00444   }
00445   if (sc < 0) {
00446     LOG(("copy_file: failed to read: %d\n", errno));
00447     return READ_ERROR;
00448   }
00449 
00450   return OK;
00451 }
00452 
00453 //-----------------------------------------------------------------------------
00454 
00455 #define BACKUP_EXT ".moz-backup"
00456 
00457 // Create a backup copy of the specified file alongside it.
00458 static int backup_create(const char *path)
00459 {
00460   char backup[MAXPATHLEN];
00461   snprintf(backup, sizeof(backup), "%s" BACKUP_EXT, path);
00462 
00463   return copy_file(path, backup);
00464 }
00465 
00466 // Copy the backup copy of the specified file back overtop
00467 // the specified file.
00468 // XXX should be a file move instead
00469 static int backup_restore(const char *path)
00470 {
00471   char backup[MAXPATHLEN];
00472   snprintf(backup, sizeof(backup), "%s" BACKUP_EXT, path);
00473 
00474   int rv = copy_file(backup, path);
00475   if (rv)
00476     return rv;
00477 
00478   rv = ensure_remove(backup);
00479   if (rv)
00480     return WRITE_ERROR;
00481 
00482   return OK;
00483 }
00484 
00485 // Discard the backup copy of the specified file.
00486 static int backup_discard(const char *path)
00487 {
00488   char backup[MAXPATHLEN];
00489   snprintf(backup, sizeof(backup), "%s" BACKUP_EXT, path);
00490 
00491   int rv = ensure_remove(backup);
00492   if (rv)
00493     return WRITE_ERROR;
00494 
00495   return OK;
00496 }
00497 
00498 // Helper function for post-processing a temporary backup.
00499 static void backup_finish(const char *path, int status)
00500 {
00501   if (status == OK)
00502     backup_discard(path);
00503   else
00504     backup_restore(path);
00505 }
00506 
00507 //-----------------------------------------------------------------------------
00508 
00509 static int DoUpdate();
00510 
00511 static const int ACTION_DESCRIPTION_BUFSIZE = 256;
00512 
00513 class Action
00514 {
00515 public:
00516   Action() : mNext(NULL) { }
00517   virtual ~Action() { }
00518 
00519   virtual int Parse(char *line) = 0;
00520 
00521   // Do any preprocessing to ensure that the action can be performed.  Execute
00522   // will be called if this Action and all others return OK from this method.
00523   virtual int Prepare() = 0;
00524 
00525   // Perform the operation.  Return OK to indicate success.  After all actions
00526   // have been executed, Finish will be called.  A requirement of Execute is
00527   // that it's operation be reversable from Finish.
00528   virtual int Execute() = 0;
00529   
00530   // Finish is called after execution of all actions.  If status is OK, then
00531   // all actions were successfully executed.  Otherwise, some action failed.
00532   virtual void Finish(int status) = 0;
00533 
00534 private:
00535   Action* mNext;
00536 
00537   friend class ActionList;
00538 };
00539 
00540 class RemoveFile : public Action
00541 {
00542 public:
00543   RemoveFile() : mFile(NULL), mSkip(0) { }
00544 
00545   int Parse(char *line);
00546   int Prepare();
00547   int Execute();
00548   void Finish(int status);
00549 
00550 private:
00551   const char* mFile;
00552   int mSkip;
00553 };
00554 
00555 int
00556 RemoveFile::Parse(char *line)
00557 {
00558   // format "<deadfile>"
00559 
00560   mFile = mstrtok(kQuote, &line);
00561   if (!mFile)
00562     return PARSE_ERROR;
00563 
00564   return OK;
00565 }
00566 
00567 int
00568 RemoveFile::Prepare()
00569 {
00570   LOG(("PREPARE REMOVE %s\n", mFile));
00571 
00572   // We expect the file to exist if we are to remove it.
00573   int rv = access(mFile, F_OK);
00574   if (rv) {
00575     LOG(("file cannot be removed because it does not exist; skipping\n"));
00576     mSkip = 1;
00577     return OK;
00578   }
00579 
00580   char *slash = (char *) strrchr(mFile, '/');
00581   if (slash) {
00582     *slash = '\0';
00583     rv = access(mFile, W_OK);
00584     *slash = '/';
00585   } else {
00586     rv = access(".", W_OK);
00587   }
00588 
00589   if (rv) {
00590     LOG(("access failed: %d\n", errno));
00591     return WRITE_ERROR;
00592   }
00593 
00594   return OK;
00595 }
00596 
00597 int
00598 RemoveFile::Execute()
00599 {
00600   LOG(("EXECUTE REMOVE %s\n", mFile));
00601 
00602   if (mSkip)
00603     return OK;
00604 
00605   // We expect the file to exist if we are to remove it.  We check here as well
00606   // as in PREPARE since we might have been asked to remove the same file more
00607   // than once: bug 311099.
00608   int rv = access(mFile, F_OK);
00609   if (rv) {
00610     LOG(("file cannot be removed because it does not exist; skipping\n"));
00611     mSkip = 1;
00612     return OK;
00613   }
00614 
00615   // save a complete copy of the old file, and then remove the
00616   // old file.  we'll clean up the copy in Finish.
00617 
00618   rv = backup_create(mFile);
00619   if (rv) {
00620     LOG(("backup_create failed: %d\n", rv));
00621     return rv;
00622   }
00623 
00624   rv = ensure_remove(mFile);
00625   if (rv)
00626     return WRITE_ERROR;
00627 
00628   return OK;
00629 }
00630 
00631 void
00632 RemoveFile::Finish(int status)
00633 {
00634   LOG(("FINISH REMOVE %s\n", mFile));
00635 
00636   if (mSkip)
00637     return;
00638 
00639   backup_finish(mFile, status);
00640 }
00641 
00642 class AddFile : public Action
00643 {
00644 public:
00645   AddFile() : mFile(NULL) { }
00646 
00647   virtual int Parse(char *line);
00648   virtual int Prepare(); // check that the source file exists
00649   virtual int Execute();
00650   virtual void Finish(int status);
00651 
00652 private:
00653   const char *mFile;
00654 };
00655 
00656 int
00657 AddFile::Parse(char *line)
00658 {
00659   // format "<newfile>"
00660 
00661   mFile = mstrtok(kQuote, &line);
00662   if (!mFile)
00663     return PARSE_ERROR;
00664 
00665   return OK;
00666 }
00667 
00668 int
00669 AddFile::Prepare()
00670 {
00671   LOG(("PREPARE ADD %s\n", mFile));
00672 
00673   return OK;
00674 }
00675 
00676 int
00677 AddFile::Execute()
00678 {
00679   LOG(("EXECUTE ADD %s\n", mFile));
00680 
00681   int rv;
00682 
00683   // First make sure that we can actually get rid of any existing file.
00684   if (access(mFile, F_OK) == 0)
00685   {
00686     rv = backup_create(mFile);
00687     if (rv)
00688       return rv;
00689 
00690     rv = ensure_remove(mFile);
00691     if (rv)
00692       return WRITE_ERROR;
00693   }
00694   else
00695   {
00696     rv = ensure_parent_dir(mFile);
00697     if (rv)
00698       return rv;
00699   }
00700     
00701   return gArchiveReader.ExtractFile(mFile, mFile);
00702 }
00703 
00704 void
00705 AddFile::Finish(int status)
00706 {
00707   LOG(("FINISH ADD %s\n", mFile));
00708 
00709   backup_finish(mFile, status);
00710 }
00711 
00712 class PatchFile : public Action
00713 {
00714 public:
00715   PatchFile() : mPatchIndex(-1), pfd(-1), buf(NULL) { }
00716   virtual ~PatchFile();
00717 
00718   virtual int Parse(char *line);
00719   virtual int Prepare(); // check for the patch file and for checksums
00720   virtual int Execute();
00721   virtual void Finish(int status);
00722 
00723 private:
00724   int LoadSourceFile(int ofd);
00725 
00726   static int sPatchIndex;
00727 
00728   const char *mPatchFile;
00729   const char *mFile;
00730   int mPatchIndex;
00731   MBSPatchHeader header;
00732   int pfd;
00733   unsigned char *buf;
00734 };
00735 
00736 int PatchFile::sPatchIndex = 0;
00737 
00738 PatchFile::~PatchFile()
00739 {
00740   if (pfd >= 0)
00741     close(pfd);
00742 
00743   // delete the temporary patch file
00744   char spath[MAXPATHLEN];
00745   snprintf(spath, MAXPATHLEN, "%s/%d.patch", gSourcePath, mPatchIndex);
00746   ensure_remove(spath);
00747 
00748   free(buf);
00749 }
00750 
00751 int
00752 PatchFile::LoadSourceFile(int ofd)
00753 {
00754   struct stat os;
00755   int rv = fstat(ofd, &os);
00756   if (rv)
00757     return READ_ERROR;
00758 
00759   if (PRUint32(os.st_size) != header.slen)
00760     return UNEXPECTED_ERROR;
00761 
00762   buf = (unsigned char*) malloc(header.slen);
00763   if (!buf)
00764     return MEM_ERROR;
00765 
00766   int r = header.slen;
00767   unsigned char *rb = buf;
00768   while (r) {
00769     int c = read(ofd, rb, mmin(BUFSIZ,r));
00770     if (c < 0)
00771       return READ_ERROR;
00772 
00773     r -= c;
00774     rb += c;
00775 
00776     if (c == 0 && r)
00777       return UNEXPECTED_ERROR;
00778   }
00779 
00780   // Verify that the contents of the source file correspond to what we expect.
00781 
00782   unsigned int crc = crc32(buf, header.slen);
00783 
00784   if (crc != header.scrc32) {
00785     LOG(("CRC check failed\n"));
00786     return CRC_ERROR;
00787   }
00788   
00789   return OK;
00790 }
00791 
00792 int
00793 PatchFile::Parse(char *line)
00794 {
00795   // format "<patchfile>" "<filetopatch>"
00796 
00797   mPatchFile = mstrtok(kQuote, &line);
00798   if (!mPatchFile)
00799     return PARSE_ERROR;
00800 
00801   // consume whitespace between args
00802   char *q = mstrtok(kQuote, &line);
00803   if (!q)
00804     return PARSE_ERROR;
00805 
00806   mFile = mstrtok(kQuote, &line);
00807   if (!mFile)
00808     return PARSE_ERROR;
00809 
00810   return OK;
00811 }
00812 
00813 int
00814 PatchFile::Prepare()
00815 {
00816   LOG(("PREPARE PATCH %s\n", mFile));
00817 
00818   // extract the patch to a temporary file
00819   mPatchIndex = sPatchIndex++;
00820 
00821   char spath[MAXPATHLEN];
00822   snprintf(spath, MAXPATHLEN, "%s/%d.patch", gSourcePath, mPatchIndex);
00823 
00824   ensure_remove(spath);
00825 
00826   int rv = gArchiveReader.ExtractFile(mPatchFile, spath);
00827   if (rv)
00828     return rv;
00829 
00830   // XXXdarin from here down should be moved into the Execute command.
00831   //          no need to open all of the patch files and read all of 
00832   //          the source files before applying any patches.
00833 
00834   pfd = open(spath, O_RDONLY | _O_BINARY);
00835   if (pfd < 0)
00836     return READ_ERROR;
00837 
00838   rv = MBS_ReadHeader(pfd, &header);
00839   if (rv)
00840     return rv;
00841 
00842   AutoFD ofd = open(mFile, O_RDONLY | _O_BINARY);
00843   if (ofd < 0)
00844     return READ_ERROR;
00845 
00846   rv = LoadSourceFile(ofd);
00847   if (rv)
00848     LOG(("LoadSourceFile failed\n"));
00849   return rv;
00850 }
00851 
00852 int
00853 PatchFile::Execute()
00854 {
00855   LOG(("EXECUTE PATCH %s\n", mFile));
00856 
00857   // Create backup copy of the destination file before proceeding.
00858 
00859   struct stat ss;
00860   if (stat(mFile, &ss))
00861     return READ_ERROR;
00862 
00863   int rv = backup_create(mFile);
00864   if (rv)
00865     return rv;
00866 
00867   rv = ensure_remove(mFile);
00868   if (rv)
00869     return WRITE_ERROR;
00870 
00871   AutoFD ofd = ensure_open(mFile, O_WRONLY | O_TRUNC | O_CREAT | _O_BINARY, ss.st_mode);
00872   if (ofd < 0)
00873     return WRITE_ERROR;
00874 
00875   return MBS_ApplyPatch(&header, pfd, buf, ofd);
00876 }
00877 
00878 void
00879 PatchFile::Finish(int status)
00880 {
00881   LOG(("FINISH PATCH %s\n", mFile));
00882 
00883   backup_finish(mFile, status);
00884 }
00885 
00886 class AddIfFile : public AddFile
00887 {
00888 public:
00889   AddIfFile() : mTestFile(NULL) { }
00890 
00891   virtual int Parse(char *line);
00892   virtual int Prepare(); // check that the source file exists
00893   virtual int Execute();
00894   virtual void Finish(int status);
00895 
00896 protected:
00897   const char *mTestFile;
00898 };
00899 
00900 int
00901 AddIfFile::Parse(char *line)
00902 {
00903   // format "<testfile>" "<newfile>"
00904 
00905   mTestFile = mstrtok(kQuote, &line);
00906   if (!mTestFile)
00907     return PARSE_ERROR;
00908 
00909   // consume whitespace between args
00910   char *q = mstrtok(kQuote, &line);
00911   if (!q)
00912     return PARSE_ERROR;
00913 
00914   return AddFile::Parse(line);
00915 }
00916 
00917 int
00918 AddIfFile::Prepare()
00919 {
00920   // If the test file does not exist, then turn disable this action.
00921   if (access(mTestFile, F_OK)) {
00922     mTestFile = NULL;
00923     return OK;
00924   }
00925 
00926   return AddFile::Prepare();
00927 }
00928 
00929 int
00930 AddIfFile::Execute()
00931 {
00932   if (!mTestFile)
00933     return OK;
00934 
00935   return AddFile::Execute();
00936 }
00937 
00938 void
00939 AddIfFile::Finish(int status)
00940 {
00941   if (!mTestFile)
00942     return;
00943 
00944   AddFile::Finish(status);
00945 }
00946 
00947 class PatchIfFile : public PatchFile
00948 {
00949 public:
00950   PatchIfFile() : mTestFile(NULL) { }
00951 
00952   virtual int Parse(char *line);
00953   virtual int Prepare(); // check for the patch file and for checksums
00954   virtual int Execute();
00955   virtual void Finish(int status);
00956 
00957 private:
00958   const char *mTestFile;
00959 };
00960 
00961 int
00962 PatchIfFile::Parse(char *line)
00963 {
00964   // format "<testfile>" "<patchfile>" "<filetopatch>"
00965 
00966   mTestFile = mstrtok(kQuote, &line);
00967   if (!mTestFile)
00968     return PARSE_ERROR;
00969 
00970   // consume whitespace between args
00971   char *q = mstrtok(kQuote, &line);
00972   if (!q)
00973     return PARSE_ERROR;
00974 
00975   return PatchFile::Parse(line);
00976 }
00977 
00978 int
00979 PatchIfFile::Prepare()
00980 {
00981   // If the test file does not exist, then turn disable this action.
00982   if (access(mTestFile, F_OK)) {
00983     mTestFile = NULL;
00984     return OK;
00985   }
00986 
00987   return PatchFile::Prepare();
00988 }
00989 
00990 int
00991 PatchIfFile::Execute()
00992 {
00993   if (!mTestFile)
00994     return OK;
00995 
00996   return PatchFile::Execute();
00997 }
00998 
00999 void
01000 PatchIfFile::Finish(int status)
01001 {
01002   if (!mTestFile)
01003     return;
01004 
01005   PatchFile::Finish(status);
01006 }
01007 
01008 //-----------------------------------------------------------------------------
01009 
01010 #ifdef XP_WIN
01011 #include "nsWindowsRestart.cpp"
01012 
01013 static void
01014 LaunchWinPostProcess(const char *appExe)
01015 {
01016   // Launch helper.exe to perform post processing (e.g. registry and log file
01017   // modifications) for the update.
01018   char inifile[MAXPATHLEN];
01019   strcpy(inifile, appExe);
01020 
01021   char *slash = strrchr(inifile, '\\');
01022   if (!slash)
01023     return;
01024 
01025   strcpy(slash + 1, "updater.ini");
01026 
01027   char exefile[MAXPATHLEN];
01028   char exearg[MAXPATHLEN];
01029   if (!GetPrivateProfileString("PostUpdateWin", "ExeRelPath", NULL, exefile,
01030       sizeof(exefile), inifile))
01031     return;
01032 
01033   if (!GetPrivateProfileString("PostUpdateWin", "ExeArg", NULL, exearg,
01034       sizeof(exearg), inifile))
01035     return;
01036 
01037   char exefullpath[MAXPATHLEN];
01038   strcpy(exefullpath, appExe);
01039 
01040   slash = strrchr(exefullpath, '\\');
01041   strcpy(slash + 1, exefile);
01042 
01043   char dlogFile[MAXPATHLEN];
01044   strcpy(dlogFile, exefullpath);
01045 
01046   slash = strrchr(dlogFile, '\\');
01047   strcpy(slash + 1, "uninstall.update");
01048 
01049   char slogFile[MAXPATHLEN];
01050   snprintf(slogFile, MAXPATHLEN, "%s/update.log", gSourcePath);
01051 
01052   // We want to launch the post update helper app to update the Windows
01053   // registry even if there is a failure with removing the uninstall.update
01054   // file or copying the update.log file.
01055   ensure_remove(dlogFile);
01056   copy_file(slogFile, dlogFile);
01057 
01058   static int    argc = 2;
01059   static char **argv = (char**) malloc(sizeof(char*) * (argc + 1));
01060   argv[0] = "argv0ignoredbywinlaunchchild";
01061   argv[1] = exearg;
01062   argv[2] = "\0";
01063 
01064   WinLaunchChild(exefullpath, argc, argv, 1);
01065   free(argv);
01066 }
01067 #endif
01068 
01069 static void
01070 LaunchCallbackApp(const char *workingDir, int argc, char **argv)
01071 {
01072   putenv("NO_EM_RESTART=");
01073   putenv("MOZ_LAUNCHED_CHILD=1");
01074 
01075   // Run from the specified working directory (see bug 312360).
01076   chdir(workingDir);
01077 
01078 #if defined(USE_EXECV)
01079   execv(argv[0], argv);
01080 #elif defined(XP_MACOSX)
01081   LaunchChild(argc, argv);
01082 #elif defined(XP_WIN)
01083   WinLaunchChild(argv[0], argc, argv, -1);
01084 #else
01085 # warning "Need implementaton of LaunchCallbackApp"
01086 #endif
01087 }
01088 
01089 static void
01090 WriteStatusFile(int status)
01091 {
01092   // This is how we communicate our completion status to the main application.
01093 
01094   char filename[MAXPATHLEN];
01095   snprintf(filename, MAXPATHLEN, "%s/update.status", gSourcePath);
01096 
01097   AutoFD fd = ensure_open(filename, O_WRONLY | O_TRUNC | O_CREAT | _O_BINARY, 0644);
01098   if (fd < 0)
01099     return;
01100 
01101   const char *text;
01102 
01103   char buf[32];
01104   if (status == OK) {
01105     text = "succeeded\n";
01106   } else {
01107     snprintf(buf, sizeof(buf), "failed: %d\n", status);
01108     text = buf;
01109   }
01110   write(fd, text, strlen(text));
01111 }
01112 
01113 static void
01114 UpdateThreadFunc(void *param)
01115 {
01116   // open ZIP archive and process...
01117 
01118   char dataFile[MAXPATHLEN];
01119   snprintf(dataFile, MAXPATHLEN, "%s/update.mar", gSourcePath);
01120 
01121   int rv = gArchiveReader.Open(dataFile);
01122   if (rv == OK) {
01123     rv = DoUpdate();
01124     gArchiveReader.Close();
01125   }
01126 
01127   if (rv)
01128     LOG(("failed: %d\n", rv));
01129   else
01130     LOG(("succeeded\n"));
01131   WriteStatusFile(rv);
01132 
01133   LOG(("calling QuitProgressUI\n"));
01134   QuitProgressUI();
01135 }
01136 
01137 int main(int argc, char **argv)
01138 {
01139   InitProgressUI(&argc, &argv);
01140 
01141   // The updater command line consists of the directory path containing the
01142   // updater.mar file to process followed by the PID of the calling process.
01143   // The updater will wait on the parent process to exit if the PID is non-
01144   // zero.  This is leveraged on platforms such as Windows where it is
01145   // necessary for the parent process to exit before its executable image may
01146   // be altered.
01147 
01148   if (argc < 3) {
01149     fprintf(stderr, "Usage: updater <dir-path> <parent-pid> [working-dir callback args...]\n");
01150     return 1;
01151   }
01152 
01153   int pid = atoi(argv[2]);
01154   if (pid) {
01155 #ifdef XP_WIN
01156     HANDLE parent = OpenProcess(SYNCHRONIZE, FALSE, (DWORD) pid);
01157     // May return NULL if the parent process has already gone away.
01158     // Otherwise, wait for the parent process to exit before starting the
01159     // update.
01160     if (parent) {
01161       DWORD result = WaitForSingleObject(parent, 5000);
01162       CloseHandle(parent);
01163       if (result != WAIT_OBJECT_0)
01164         return 1;
01165       // The process may be signaled before it releases the executable image.
01166       // This is a terrible hack, but it'll have to do for now :-(
01167       Sleep(50);
01168     }
01169 #else
01170     int status;
01171     waitpid(pid, &status, 0);
01172 #endif
01173   }
01174 
01175   gSourcePath = argv[1];
01176 
01177   LogInit();
01178 
01179   // Run update process on a background thread.  ShowProgressUI may return
01180   // before QuitProgressUI has been called, so wait for UpdateThreadFunc to
01181   // terminate.
01182   Thread t;
01183   if (t.Run(UpdateThreadFunc, NULL) == 0)
01184     ShowProgressUI();
01185   t.Join();
01186 
01187   LogFinish();
01188 
01189 #ifdef XP_WIN
01190   if (gSucceeded && argc > 4)
01191     LaunchWinPostProcess(argv[4]);
01192 #endif
01193 
01194   // The callback to execute is given as the last N arguments of our command
01195   // line.  The first of those arguments specifies the working directory for
01196   // the callback.
01197   if (argc > 4)
01198     LaunchCallbackApp(argv[3], argc - 4, argv + 4);
01199 
01200   return 0;
01201 }
01202 
01203 class ActionList
01204 {
01205 public:
01206   ActionList() : mFirst(NULL), mLast(NULL), mCount(0) { }
01207   ~ActionList();
01208 
01209   void Append(Action* action);
01210   int Prepare();
01211   int Execute();
01212   void Finish(int status);
01213 
01214 private:
01215   Action *mFirst;
01216   Action *mLast;
01217   int     mCount;
01218 };
01219 
01220 ActionList::~ActionList()
01221 {
01222   Action* a = mFirst;
01223   while (a) {
01224     Action *b = a;
01225     a = a->mNext;
01226     delete b;
01227   }
01228 }
01229 
01230 void
01231 ActionList::Append(Action *action)
01232 {
01233   if (mLast)
01234     mLast->mNext = action;
01235   else
01236     mFirst = action;
01237 
01238   mLast = action;
01239   mCount++;
01240 }
01241 
01242 int
01243 ActionList::Prepare()
01244 {
01245   // If the action list is empty then we should fail in order to signal that
01246   // something has gone wrong. Otherwise we report success when nothing is
01247   // actually done. See bug 327140.
01248   if (mCount == 0) {
01249     LOG(("empty action list\n"));
01250     return UNEXPECTED_ERROR;
01251   }
01252 
01253   Action *a = mFirst;
01254   while (a) {
01255     int rv = a->Prepare();
01256     if (rv)
01257       return rv;
01258 
01259     a = a->mNext;
01260   }
01261 
01262   UpdateProgressUI(1.0f);
01263 
01264   return OK;
01265 }
01266 
01267 int
01268 ActionList::Execute()
01269 {
01270   int i = 0;
01271   float divisor = mCount / 98.0f;
01272 
01273   Action *a = mFirst;
01274   while (a) {
01275     UpdateProgressUI(1.0f + float(i++) / divisor);
01276 
01277     int rv = a->Execute();
01278     if (rv)
01279     {
01280       LOG(("### execution failed\n"));
01281       return rv;
01282     }
01283 
01284     a = a->mNext;
01285   }
01286 
01287   return OK;
01288 }
01289 
01290 void
01291 ActionList::Finish(int status)
01292 {
01293   Action *a = mFirst;
01294   while (a) {
01295     a->Finish(status);
01296     a = a->mNext;
01297   }
01298 
01299 #ifdef XP_WIN
01300   if (status == OK)
01301     gSucceeded = TRUE;
01302 #endif
01303 
01304   UpdateProgressUI(100.0f);
01305 }
01306 
01307 int DoUpdate()
01308 {
01309   char manifest[MAXPATHLEN];
01310   snprintf(manifest, MAXPATHLEN, "%s/update.manifest", gSourcePath);
01311 
01312   // extract the manifest
01313   int rv = gArchiveReader.ExtractFile("update.manifest", manifest);
01314   if (rv)
01315     return rv;
01316 
01317   AutoFD mfd = open(manifest, O_RDONLY | _O_BINARY);
01318   if (mfd < 0)
01319     return READ_ERROR;
01320 
01321   struct stat ms;
01322   rv = fstat(mfd, &ms);
01323   if (rv)
01324     return READ_ERROR;
01325 
01326   char *mbuf = (char*) malloc(ms.st_size + 1);
01327   if (!mbuf)
01328     return MEM_ERROR;
01329 
01330   int r = ms.st_size;
01331   char *rb = mbuf;
01332   while (r) {
01333     int c = read(mfd, rb, mmin(SSIZE_MAX,r));
01334     if (c < 0)
01335       return READ_ERROR;
01336 
01337     r -= c;
01338     rb += c;
01339 
01340     if (c == 0 && r)
01341       return UNEXPECTED_ERROR;
01342   }
01343   mbuf[ms.st_size] = '\0';
01344 
01345   ActionList list;
01346 
01347   rb = mbuf;
01348   char *line;
01349   while((line = mstrtok(kNL, &rb)) != 0) {
01350     // skip comments
01351     if (*line == '#')
01352       continue;
01353 
01354     char *token = mstrtok(kWhitespace, &line);
01355     if (!token)
01356       return PARSE_ERROR;
01357 
01358     Action *action = NULL;
01359     if (strcmp(token, "remove") == 0) {
01360       action = new RemoveFile();
01361     }
01362     else if (strcmp(token, "add") == 0) {
01363       action = new AddFile();
01364     }
01365     else if (strcmp(token, "patch") == 0) {
01366       action = new PatchFile();
01367     }
01368     else if (strcmp(token, "add-if") == 0) {
01369       action = new AddIfFile();
01370     }
01371     else if (strcmp(token, "patch-if") == 0) {
01372       action = new PatchIfFile();
01373     }
01374     else {
01375       return PARSE_ERROR;
01376     }
01377 
01378     if (!action)
01379       return MEM_ERROR;
01380 
01381     rv = action->Parse(line);
01382     if (rv)
01383       return rv;
01384 
01385     list.Append(action);
01386   }
01387 
01388   rv = list.Prepare();
01389   if (rv)
01390     return rv;
01391 
01392   rv = list.Execute();
01393 
01394   list.Finish(rv);
01395   return rv;
01396 }
01397 
01398 #if defined(XP_WIN) && !defined(DEBUG) && !defined(__GNUC__)
01399 // We need WinMain in order to not be a console app.  This function is unused
01400 // if we are a console application.
01401 int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR args, int )
01402 {
01403   // Do the real work.
01404   return main(__argc, __argv);
01405 }
01406 #endif