Bug 641212 - Create and extract update files using XZ compression instead of BZ2; r?rstrong draft
authorMatt Howell <mhowell@mozilla.com>
Wed, 14 Dec 2016 13:27:28 -0800
changeset 459403 f1b51d7494d5fa1944053f21d0b94b664d0176fb
parent 459259 63ad56438630b389efdb01632e153143cf3008ec
child 459404 adfffe1b7be46602f9aa39782d54392268a1a4d2
push id41213
push usermhowell@mozilla.com
push dateWed, 11 Jan 2017 22:06:28 +0000
reviewersrstrong
bugs641212
milestone53.0a1
Bug 641212 - Create and extract update files using XZ compression instead of BZ2; r?rstrong This commit replaces the MAR (update archive) compression scheme. Previously, each individual file in an update was compressed separately using BZip2. Now, the entire stream of all file updates is compressed using XZ/LZMA2. This gets us substantially better compression ratios, and therefore smaller updates. Support for BZ2-based MAR files has been removed. That means this change will require an update watershed. It also means the test data files for libmar and the updater need to be rebuilt before those tests can be run; those changes will be in a later commit. MozReview-Commit-ID: Gy9Ansrh2qn
config/external/moz.build
modules/libmar/src/mar.h
modules/libmar/src/mar_create.c
modules/libmar/src/mar_decompress.c
modules/libmar/src/mar_extract.c
modules/libmar/src/mar_private.h
modules/libmar/src/mar_read.c
modules/libmar/src/moz.build
modules/libmar/tool/moz.build
modules/xz-embedded/moz.build
toolkit/mozapps/update/common/errors.h
toolkit/mozapps/update/common/readstrings.h
toolkit/mozapps/update/common/updatedefines.h
toolkit/mozapps/update/updater/archivereader.cpp
toolkit/mozapps/update/updater/archivereader.h
toolkit/mozapps/update/updater/updater-common.build
toolkit/mozapps/update/updater/updater.cpp
tools/update-packaging/common.sh
tools/update-packaging/make_full_update.sh
tools/update-packaging/make_incremental_update.sh
--- a/config/external/moz.build
+++ b/config/external/moz.build
@@ -16,16 +16,18 @@ if not CONFIG['MOZ_SYSTEM_JPEG']:
 if CONFIG['MOZ_UPDATER']:
     if not CONFIG['MOZ_SYSTEM_BZ2']:
         external_dirs += ['modules/libbz2']
 
 # There's no "native" brotli or woff2 yet, but probably in the future...
 external_dirs += ['modules/brotli']
 external_dirs += ['modules/woff2']
 
+external_dirs += ['modules/xz-embedded']
+
 if CONFIG['MOZ_VORBIS']:
     external_dirs += ['media/libvorbis']
 
 if CONFIG['MOZ_TREMOR']:
     external_dirs += ['media/libtremor']
 
 if CONFIG['MOZ_WEBM_ENCODER']:
     external_dirs += ['media/libmkv']
@@ -56,12 +58,9 @@ external_dirs += [
     'media/libopus',
     'media/libtheora',
     'media/libspeex_resampler',
     'media/libstagefright',
     'media/libsoundtouch',
     'media/psshparser'
 ]
 
-if CONFIG['MOZ_LINKER']:
-    external_dirs += ['modules/xz-embedded']
-
 DIRS += ['../../' + i for i in external_dirs]
--- a/modules/libmar/src/mar.h
+++ b/modules/libmar/src/mar.h
@@ -42,16 +42,22 @@ typedef struct MarItem_ {
   uint32_t flags;         /* contains file mode bits */
   char name[1];           /* file path */
 } MarItem;
 
 #define TABLESIZE 256
 
 struct MarFile_ {
   FILE *fp;
+  bool decompressed;
+#ifdef XP_WIN
+  wchar_t* name;
+#else
+  char* name;
+#endif
   MarItem *item_table[TABLESIZE];
 };
 
 typedef struct MarFile_ MarFile;
 
 /**
  * Signature of callback function passed to mar_enum_items.
  * @param mar       The MAR file being visited.
@@ -186,13 +192,34 @@ int mar_verify_signatures(MarFile *mar,
  *
  * @param infoBlock Out parameter for where to store the result to
  * @return 0 on success, -1 on failure
 */
 int
 mar_read_product_info_block(MarFile *mar, 
                             struct ProductInformationBlock *infoBlock);
 
+/**
+ * Finds the start and length of the content area of the MAR file.
+ *
+ * @param      mar            The already opened MAR file
+ * @param[out] contentOffset  File offset in bytes of the start of the content,
+ *                            or -1 if there is no content
+ * @param[out] contentLength  Total length of all content files
+*/
+void
+mar_get_content_extent(MarFile* mar, int* contentOffset, int* contentLength);
+
+/**
+* Decompress the content area of a MAR file in place.
+*
+* @param      mar     The already opened MAR file
+*
+* @return 0 if successful, 1 if the MAR is not compressed, -1 on error
+*/
+int
+mar_decompress(MarFile* mar);
+
 #ifdef __cplusplus
 }
 #endif
 
 #endif  /* MAR_H__ */
--- a/modules/libmar/src/mar_create.c
+++ b/modules/libmar/src/mar_create.c
@@ -7,16 +7,17 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include "mar_private.h"
 #include "mar_cmdline.h"
 #include "mar.h"
+#include "updatedefines.h"
 
 #ifdef XP_WIN
 #include <winsock2.h>
 #else
 #include <netinet/in.h>
 #include <unistd.h>
 #endif
 
@@ -294,17 +295,17 @@ refresh_product_info_block(const char *p
 int mar_create(const char *dest, int 
                num_files, char **files, 
                struct ProductInformationBlock *infoBlock) {
   struct MarItemStack stack;
   uint32_t offset_to_index = 0, size_of_index, 
     numSignatures, numAdditionalSections;
   uint64_t sizeOfEntireMAR = 0;
   struct stat st;
-  FILE *fp;
+  FILE *fp, *contentFile = NULL;
   int i, rv = -1;
 
   memset(&stack, 0, sizeof(stack));
 
   fp = fopen(dest, "wb");
   if (!fp) {
     fprintf(stderr, "ERROR: could not create target file: %s\n", dest);
     return -1;
@@ -340,60 +341,102 @@ int mar_create(const char *dest, int
     goto failure;
   }
   numAdditionalSections = ntohl(numAdditionalSections);
 
   if (mar_concat_product_info_block(fp, &stack, infoBlock)) {
     goto failure;
   }
 
+  // Create a separate file for the content, so we can get it compressed.
+  char contentPath[MAXPATHLEN] = "";
+  snprintf(contentPath, MAXPATHLEN, "%s.content", dest);
+  contentFile = fopen(contentPath, "wb+");
+  if (!contentFile) {
+    goto failure;
+  }
+
   for (i = 0; i < num_files; ++i) {
     if (stat(files[i], &st)) {
       fprintf(stderr, "ERROR: file not found: %s\n", files[i]);
       goto failure;
     }
 
     if (mar_push(&stack, st.st_size, st.st_mode & 0777, files[i]))
       goto failure;
 
     /* concatenate input file to archive */
-    if (mar_concat_file(fp, files[i]))
+    if (mar_concat_file(contentFile, files[i]))
       goto failure;
   }
 
+  // Compress the content.
+  fclose(contentFile);
+  const char command[] = "xz --compress --x86 --lzma2 --format=xz --check=crc64 --force %s";
+  char commandStr[MAXPATHLEN + sizeof(command)] = "";
+  snprintf(commandStr, MAXPATHLEN + sizeof(command), command, contentPath);
+  if (system(commandStr)) {
+    goto failure;
+  }
+
+  // Copy the compressed content into the destination MAR.
+  strncat(contentPath, ".xz", MAXPATHLEN);
+  contentFile = fopen(contentPath, "rb");
+  if (!contentFile) {
+    goto failure;
+  }
+  while (true) {
+    static uint8_t buffer[BLOCKSIZE];
+    size_t bytesRead = fread(buffer, 1, BLOCKSIZE, contentFile);
+    if (bytesRead > 0) {
+      if (fwrite(buffer, 1, bytesRead, fp) != bytesRead) {
+        goto failure;
+      }
+    } else if (feof(contentFile)) {
+      break;
+    } else {
+      goto failure;
+    }
+  }
+
   /* write out the index (prefixed with length of index) */
+  offset_to_index = htonl(ftell(fp));
   size_of_index = htonl(stack.size_used);
-  if (fwrite(&size_of_index, sizeof(size_of_index), 1, fp) != 1)
+  if (fwrite(&size_of_index, sizeof(size_of_index), 1, fp) != 1 ||
+      fwrite(stack.head, stack.size_used, 1, fp) != 1) {
     goto failure;
-  if (fwrite(stack.head, stack.size_used, 1, fp) != 1)
-    goto failure;
+  }
 
   /* To protect against invalid MAR files, we assumes that the MAR file 
      size is less than or equal to MAX_SIZE_OF_MAR_FILE. */
   if (ftell(fp) > MAX_SIZE_OF_MAR_FILE) {
     goto failure;
   }
 
   /* write out offset to index file in network byte order */
-  offset_to_index = htonl(stack.last_offset);
-  if (fseek(fp, MAR_ID_SIZE, SEEK_SET))
-    goto failure;
-  if (fwrite(&offset_to_index, sizeof(offset_to_index), 1, fp) != 1)
+  if (fseek(fp, MAR_ID_SIZE, SEEK_SET) ||
+      fwrite(&offset_to_index, sizeof(offset_to_index), 1, fp) != 1) {
     goto failure;
-  offset_to_index = ntohl(stack.last_offset);
-  
-  sizeOfEntireMAR = ((uint64_t)stack.last_offset) +
-                    stack.size_used +
-                    sizeof(size_of_index);
-  sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR);
-  if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1)
+  }
+
+  offset_to_index = ntohl(offset_to_index);
+  size_of_index = ntohl(size_of_index);
+  // The +4 is because the size_of_index field does not count itself.
+  sizeOfEntireMAR = HOST_TO_NETWORK64(offset_to_index + size_of_index + 4);
+  if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1) {
     goto failure;
-  sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
+  }
 
   rv = 0;
 failure: 
-  if (stack.head)
+  if (stack.head) {
     free(stack.head);
+  }
+  if (contentFile) {
+    fclose(contentFile);
+    remove(contentPath);
+  }
   fclose(fp);
-  if (rv)
+  if (rv) {
     remove(dest);
+  }
   return rv;
 }
new file mode 100644
--- /dev/null
+++ b/modules/libmar/src/mar_decompress.c
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// for ntohl
+#ifdef XP_WIN
+#include <winsock2.h>
+#else
+#include <netinet/in.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "updatedefines.h"
+#include "mar_private.h"
+#define XZ_USE_CRC64
+#include "xz.h"
+
+#define FILE_BUFFER_SIZE 262144
+static uint8_t inBuf[FILE_BUFFER_SIZE];
+static uint8_t outBuf[FILE_BUFFER_SIZE];
+
+static int
+mar_decompress_xz_archive(FILE* from, FILE* to)
+{
+  xz_crc32_init();
+  xz_crc64_init();
+
+  // 64 MB is the *maximum* dictionary size; the decoder will allocate up to
+  // that amount, but only if it really needs that much.
+  struct xz_dec * dec = xz_dec_init(XZ_DYNALLOC, 64 * 1024 * 1024);
+  if (!dec) {
+    return -1;
+  }
+
+  struct xz_buf strm = { 0 };
+  strm.in = inBuf;
+  strm.out = outBuf;
+  strm.out_size = FILE_BUFFER_SIZE;
+  
+  int rv = 0, bytesRead = 0;
+  enum xz_ret xz_rv = XZ_OK;
+  while (xz_rv == XZ_OK) {
+    if (strm.in_pos >= strm.in_size) {
+      size_t inlen = fread(inBuf, 1, FILE_BUFFER_SIZE, from);
+      if (inlen <= 0) {
+        rv = -1;
+        break;
+      }
+      bytesRead += inlen;
+      strm.in_size = inlen;
+      strm.in_pos = 0;
+    }
+
+    strm.out_pos = 0;
+    xz_rv = xz_dec_run(dec, &strm);
+    if (xz_rv != XZ_OK && xz_rv != XZ_STREAM_END) {
+      rv = -1;
+      break;
+    }
+
+    if (strm.out_pos != 0) {
+      if (fwrite(outBuf, strm.out_pos, 1, to) != 1) {
+        rv = -1;
+        break;
+      }
+    }
+  }
+
+  xz_dec_end(dec);
+  return rv;
+}
+
+static int
+mar_is_compressed(FILE* file)
+{
+  static const char XZ_MAGIC[] = { 0xFD, '7', 'z', 'X', 'Z', 0x00 };
+  char buffer[sizeof(XZ_MAGIC)];
+  if (fread(buffer, sizeof(XZ_MAGIC), 1, file) != 1) {
+    return 0;
+  }
+  fseek(file, -(int)sizeof(XZ_MAGIC), SEEK_CUR);
+  return (memcmp(buffer, XZ_MAGIC, sizeof(XZ_MAGIC)) == 0) ? 1 : 0;
+}
+
+int
+mar_decompress(MarFile* file)
+{
+  int contentOffset = 0, contentLength = 0;
+  mar_get_content_extent(file, &contentOffset, &contentLength);
+
+  fseek(file->fp, contentOffset, SEEK_SET);
+
+  if (!mar_is_compressed(file->fp)) {
+    return 1;
+  }
+  
+  // Create a temporary archive file.
+  NS_tchar extractedPath[MAXPATHLEN] = NS_T("");
+  int sprintfLen = NS_tsnprintf(extractedPath, MAXPATHLEN, NS_T("%s.extracted"), file->name);
+  // Make sure the path wasn't truncated before opening the file.
+  if (sprintfLen != NS_tstrlen(extractedPath)) {
+    return -1;
+  }
+  FILE * extractedFile = NS_tfopen(extractedPath, NS_T("wb+"));
+  if (!extractedFile) {
+    return -1;
+  }
+
+  // Copy from the archive file into the temp file, up to the content area.
+  int rv = 0;
+  int bytesRead = 0;
+  fseek(file->fp, 0, SEEK_SET);
+  while (bytesRead < contentOffset) {
+    int bytesToRead = FILE_BUFFER_SIZE;
+    if ((contentOffset - bytesRead) < FILE_BUFFER_SIZE) {
+      bytesToRead = contentOffset - bytesRead;
+    }
+    if (fread(inBuf, 1, bytesToRead, file->fp) == 0) {
+      rv = -1;
+      break;
+    }
+    bytesRead += bytesToRead;
+    if (fwrite(outBuf, bytesToRead, 1, extractedFile) != 1) {
+      rv = -1;
+      break;
+    }
+  }
+  if (rv) {
+    fclose(extractedFile);
+    NS_tremove(extractedPath);
+    return rv;
+  }
+
+  // Extract the compressed content into the temp file.
+  if (mar_decompress_xz_archive(file->fp, extractedFile) != 0) {
+    fclose(extractedFile);
+    NS_tremove(extractedPath);
+    return -1;
+  }
+
+  // Reset the file stream to the end of the content, because the extraction
+  // might have read past the end of the actual XZ stream.
+  fseek(file->fp, contentOffset + contentLength, SEEK_SET);
+
+  // Copy over the index from the MAR, which is all that's left past the content.
+  while (true) {
+    size_t bytesRead = fread(inBuf, 1, FILE_BUFFER_SIZE, file->fp);
+    if (bytesRead == 0) {
+      rv = feof(file->fp) ? 0 : -1;
+      break;
+    }
+    if (fwrite(outBuf, bytesRead, 1, extractedFile) != 1) {
+      rv = -1;
+      break;
+    }
+  }
+  if (rv) {
+    fclose(extractedFile);
+    NS_tremove(extractedPath);
+    return rv;
+  }
+
+  fclose(file->fp);
+  file->fp = extractedFile;
+  file->decompressed = true;
+  return 0;
+}
+
+void
+mar_decompress_cleanup(MarFile* file)
+{
+  NS_tchar extractedPath[MAXPATHLEN];
+  NS_tsnprintf(extractedPath, MAXPATHLEN, NS_T("%s.extracted"), file->name);
+  NS_tremove(extractedPath);
+}
--- a/modules/libmar/src/mar_extract.c
+++ b/modules/libmar/src/mar_extract.c
@@ -71,13 +71,18 @@ static int mar_test_callback(MarFile *ma
 int mar_extract(const char *path) {
   MarFile *mar;
   int rv;
 
   mar = mar_open(path);
   if (!mar)
     return -1;
 
+  if (mar_decompress(mar) < 0) {
+    mar_close(mar);
+    return -1;
+  }
+
   rv = mar_enum_items(mar, mar_test_callback, NULL);
 
   mar_close(mar);
   return rv;
 }
--- a/modules/libmar/src/mar_private.h
+++ b/modules/libmar/src/mar_private.h
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MAR_PRIVATE_H__
 #define MAR_PRIVATE_H__
 
+#include "mar.h"
 #include "limits.h"
 #include "mozilla/Assertions.h"
 #include <stdint.h>
 
 #define BLOCKSIZE 4096
 #define ROUND_UP(n, incr) (((n) / (incr) + 1) * (incr))
 
 #define MAR_ID "MAR1"
@@ -71,9 +72,16 @@ MOZ_STATIC_ASSERT(sizeof(BLOCKSIZE) < \
   (((((uint64_t) x) >> 16) & 0xFF) << 40) | \
   (((((uint64_t) x) >> 24) & 0xFF) << 32) | \
   (((((uint64_t) x) >> 32) & 0xFF) << 24) | \
   (((((uint64_t) x) >> 40) & 0xFF) << 16) | \
   (((((uint64_t) x) >> 48) & 0xFF) << 8) | \
   (((uint64_t) x) >> 56)
 #define NETWORK_TO_HOST64 HOST_TO_NETWORK64
 
+/**
+ * Clean up after mar_decompress().
+ * Should be called after the MarFile fp has been closed.
+ */
+void
+mar_decompress_cleanup(MarFile* file);
+
 #endif  /* MAR_PRIVATE_H__ */
--- a/modules/libmar/src/mar_read.c
+++ b/modules/libmar/src/mar_read.c
@@ -28,17 +28,17 @@ static uint32_t mar_hash_name(const char
 
   return val % TABLESIZE;
 }
 
 static int mar_insert_item(MarFile *mar, const char *name, int namelen,
                            uint32_t offset, uint32_t length, uint32_t flags) {
   MarItem *item, *root;
   uint32_t hash;
-  
+
   item = (MarItem *) malloc(sizeof(MarItem) + namelen);
   if (!item)
     return -1;
   item->next = NULL;
   item->offset = offset;
   item->length = length;
   item->flags = flags;
   memcpy(item->name, name, namelen + 1);
@@ -155,16 +155,17 @@ static MarFile *mar_fpopen(FILE *fp)
 
   mar = (MarFile *) malloc(sizeof(*mar));
   if (!mar) {
     fclose(fp);
     return NULL;
   }
 
   mar->fp = fp;
+  mar->decompressed = false;
   memset(mar->item_table, 0, sizeof(mar->item_table));
   if (mar_read_index(mar)) {
     mar_close(mar);
     return NULL;
   }
 
   return mar;
 }
@@ -174,31 +175,46 @@ MarFile *mar_open(const char *path) {
 
   fp = fopen(path, "rb");
   if (!fp) {
     fprintf(stderr, "ERROR: could not open file in mar_open()\n");
     perror(path);
     return NULL;
   }
 
-  return mar_fpopen(fp);
+  MarFile *rv = mar_fpopen(fp);
+  if (rv) {
+#ifdef XP_WIN
+    rv->name = malloc((strlen(path) + 1) * sizeof(wchar_t));
+    MultiByteToWideChar(CP_UTF8, 0, path, -1, rv->name, strlen(path) + 1);
+#else
+    rv->name = malloc(strlen(path));
+    strcpy(rv->name, path);
+#endif
+  }
+  return rv;
 }
 
 #ifdef XP_WIN
 MarFile *mar_wopen(const wchar_t *path) {
   FILE *fp;
 
   _wfopen_s(&fp, path, L"rb");
   if (!fp) {
     fprintf(stderr, "ERROR: could not open file in mar_wopen()\n");
     _wperror(path);
     return NULL;
   }
 
-  return mar_fpopen(fp);
+  MarFile *rv = mar_fpopen(fp);
+  if (rv) {
+    rv->name = malloc(wcslen(path) * sizeof(wchar_t));
+    wcscpy(rv->name, path);
+  }
+  return rv;
 }
 #endif
 
 void mar_close(MarFile *mar) {
   MarItem *item;
   int i;
 
   fclose(mar->fp);
@@ -207,46 +223,51 @@ void mar_close(MarFile *mar) {
     item = mar->item_table[i];
     while (item) {
       MarItem *temp = item;
       item = item->next;
       free(temp);
     }
   }
 
+  if (mar->decompressed) {
+    mar_decompress_cleanup(mar);
+  }
+
+  free(mar->name);
   free(mar);
 }
 
 /**
  * Determines the MAR file information.
  *
  * @param fp                     An opened MAR file in read mode.
  * @param hasSignatureBlock      Optional out parameter specifying if the MAR
  *                               file has a signature block or not.
  * @param numSignatures          Optional out parameter for storing the number
  *                               of signatures in the MAR file.
  * @param hasAdditionalBlocks    Optional out parameter specifying if the MAR
  *                               file has additional blocks or not.
- * @param offsetAdditionalBlocks Optional out parameter for the offset to the 
+ * @param offsetAdditionalBlocks Optional out parameter for the offset to the
  *                               first additional block. Value is only valid if
  *                               hasAdditionalBlocks is not equal to 0.
  * @param numAdditionalBlocks    Optional out parameter for the number of
  *                               additional blocks.  Value is only valid if
  *                               hasAdditionalBlocks is not equal to 0.
  * @return 0 on success and non-zero on failure.
  */
-int get_mar_file_info_fp(FILE *fp, 
+int get_mar_file_info_fp(FILE *fp,
                          int *hasSignatureBlock,
                          uint32_t *numSignatures,
                          int *hasAdditionalBlocks,
                          uint32_t *offsetAdditionalBlocks,
                          uint32_t *numAdditionalBlocks)
 {
   uint32_t offsetToIndex, offsetToContent, signatureCount, signatureLen, i;
-  
+
   /* One of hasSignatureBlock or hasAdditionalBlocks must be non NULL */
   if (!hasSignatureBlock && !hasAdditionalBlocks) {
     return -1;
   }
 
 
   /* Skip to the start of the offset index */
   if (fseek(fp, MAR_ID_SIZE, SEEK_SET)) {
@@ -267,18 +288,18 @@ int get_mar_file_info_fp(FILE *fp,
 
     /* Read the number of signatures field */
     if (fread(numSignatures, sizeof(*numSignatures), 1, fp) != 1) {
       return -1;
     }
     *numSignatures = ntohl(*numSignatures);
   }
 
-  /* Skip to the first index entry past the index size field 
-     We do it in 2 calls because offsetToIndex + sizeof(uint32_t) 
+  /* Skip to the first index entry past the index size field
+     We do it in 2 calls because offsetToIndex + sizeof(uint32_t)
      could oerflow in theory. */
   if (fseek(fp, offsetToIndex, SEEK_SET)) {
     return -1;
   }
 
   if (fseek(fp, sizeof(uint32_t), SEEK_CUR)) {
     return -1;
   }
@@ -293,17 +314,17 @@ int get_mar_file_info_fp(FILE *fp,
   if (hasSignatureBlock) {
     if (offsetToContent == MAR_ID_SIZE + sizeof(uint32_t)) {
       *hasSignatureBlock = 0;
     } else {
       *hasSignatureBlock = 1;
     }
   }
 
-  /* If the caller doesn't care about the product info block 
+  /* If the caller doesn't care about the product info block
      value, then just return */
   if (!hasAdditionalBlocks) {
     return 0;
   }
 
    /* Skip to the start of the signature block */
   if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
     return -1;
@@ -348,102 +369,102 @@ int get_mar_file_info_fp(FILE *fp,
       if (fread(numAdditionalBlocks, sizeof(uint32_t), 1, fp) != 1) {
         return -1;
       }
       *numAdditionalBlocks = ntohl(*numAdditionalBlocks);
       if (offsetAdditionalBlocks) {
         *offsetAdditionalBlocks = ftell(fp);
       }
     } else if (offsetAdditionalBlocks) {
-      /* numAdditionalBlocks is not specified but offsetAdditionalBlocks 
+      /* numAdditionalBlocks is not specified but offsetAdditionalBlocks
          is, so fill it! */
       *offsetAdditionalBlocks = ftell(fp) + sizeof(uint32_t);
     }
   }
 
   return 0;
 }
 
-/** 
+/**
  * Reads the product info block from the MAR file's additional block section.
  * The caller is responsible for freeing the fields in infoBlock
  * if the return is successful.
  *
  * @param infoBlock Out parameter for where to store the result to
  * @return 0 on success, -1 on failure
 */
 int
-read_product_info_block(char *path, 
+read_product_info_block(char *path,
                         struct ProductInformationBlock *infoBlock)
 {
   int rv;
   MarFile mar;
   mar.fp = fopen(path, "rb");
   if (!mar.fp) {
     fprintf(stderr, "ERROR: could not open file in read_product_info_block()\n");
     perror(path);
     return -1;
   }
   rv = mar_read_product_info_block(&mar, infoBlock);
   fclose(mar.fp);
   return rv;
 }
 
-/** 
+/**
  * Reads the product info block from the MAR file's additional block section.
  * The caller is responsible for freeing the fields in infoBlock
  * if the return is successful.
  *
  * @param infoBlock Out parameter for where to store the result to
  * @return 0 on success, -1 on failure
 */
 int
-mar_read_product_info_block(MarFile *mar, 
+mar_read_product_info_block(MarFile *mar,
                             struct ProductInformationBlock *infoBlock)
 {
   uint32_t i, offsetAdditionalBlocks, numAdditionalBlocks,
     additionalBlockSize, additionalBlockID;
   int hasAdditionalBlocks;
 
-  /* The buffer size is 97 bytes because the MAR channel name < 64 bytes, and 
+  /* The buffer size is 97 bytes because the MAR channel name < 64 bytes, and
      product version < 32 bytes + 3 NULL terminator bytes. */
   char buf[97] = { '\0' };
   if (get_mar_file_info_fp(mar->fp, NULL, NULL,
                            &hasAdditionalBlocks,
                            &offsetAdditionalBlocks,
                            &numAdditionalBlocks) != 0) {
     return -1;
   }
   for (i = 0; i < numAdditionalBlocks; ++i) {
     /* Read the additional block size */
-    if (fread(&additionalBlockSize, 
-              sizeof(additionalBlockSize), 
+    if (fread(&additionalBlockSize,
+              sizeof(additionalBlockSize),
               1, mar->fp) != 1) {
       return -1;
     }
-    additionalBlockSize = ntohl(additionalBlockSize) - 
-                          sizeof(additionalBlockSize) - 
+    additionalBlockSize = ntohl(additionalBlockSize) -
+                          sizeof(additionalBlockSize) -
                           sizeof(additionalBlockID);
 
     /* Read the additional block ID */
-    if (fread(&additionalBlockID, 
-              sizeof(additionalBlockID), 
+    if (fread(&additionalBlockID,
+              sizeof(additionalBlockID),
               1, mar->fp) != 1) {
       return -1;
     }
     additionalBlockID = ntohl(additionalBlockID);
 
     if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
       const char *location;
       int len;
 
       /* This block must be at most 104 bytes.
-         MAR channel name < 64 bytes, and product version < 32 bytes + 3 NULL 
-         terminator bytes. We only check for 96 though because we remove 8 
-         bytes above from the additionalBlockSize: We subtract 
+         MAR channel name < 64 bytes, and product version < 32 bytes + 3 NULL
+         terminator bytes. We only check for 96 though because we remove 8
+         bytes above from the additionalBlockSize: We subtract
          sizeof(additionalBlockSize) and sizeof(additionalBlockID) */
       if (additionalBlockSize > 96) {
         return -1;
       }
 
     if (fread(buf, additionalBlockSize, 1, mar->fp) != 1) {
         return -1;
       }
@@ -464,19 +485,19 @@ mar_read_product_info_block(MarFile *mar
       len = strlen(location);
       infoBlock->productVersion = location;
       location += len + 1;
       if (len >= 32) {
         infoBlock->MARChannelID = NULL;
         infoBlock->productVersion = NULL;
         return -1;
       }
-      infoBlock->MARChannelID = 
+      infoBlock->MARChannelID =
         strdup(infoBlock->MARChannelID);
-      infoBlock->productVersion = 
+      infoBlock->productVersion =
         strdup(infoBlock->productVersion);
       return 0;
     } else {
       /* This is not the additional block you're looking for. Move along. */
       if (fseek(mar->fp, additionalBlockSize, SEEK_CUR)) {
         return -1;
       }
     }
@@ -540,38 +561,73 @@ int mar_read(MarFile *mar, const MarItem
  *
  * @param path                   The path of the MAR file to check.
  * @param hasSignatureBlock      Optional out parameter specifying if the MAR
  *                               file has a signature block or not.
  * @param numSignatures          Optional out parameter for storing the number
  *                               of signatures in the MAR file.
  * @param hasAdditionalBlocks    Optional out parameter specifying if the MAR
  *                               file has additional blocks or not.
- * @param offsetAdditionalBlocks Optional out parameter for the offset to the 
+ * @param offsetAdditionalBlocks Optional out parameter for the offset to the
  *                               first additional block. Value is only valid if
  *                               hasAdditionalBlocks is not equal to 0.
  * @param numAdditionalBlocks    Optional out parameter for the number of
  *                               additional blocks.  Value is only valid if
  *                               has_additional_blocks is not equal to 0.
  * @return 0 on success and non-zero on failure.
  */
-int get_mar_file_info(const char *path, 
+int get_mar_file_info(const char *path,
                       int *hasSignatureBlock,
                       uint32_t *numSignatures,
                       int *hasAdditionalBlocks,
                       uint32_t *offsetAdditionalBlocks,
                       uint32_t *numAdditionalBlocks)
 {
   int rv;
   FILE *fp = fopen(path, "rb");
   if (!fp) {
     fprintf(stderr, "ERROR: could not open file in get_mar_file_info()\n");
     perror(path);
     return -1;
   }
 
-  rv = get_mar_file_info_fp(fp, hasSignatureBlock, 
+  rv = get_mar_file_info_fp(fp, hasSignatureBlock,
                             numSignatures, hasAdditionalBlocks,
                             offsetAdditionalBlocks, numAdditionalBlocks);
 
   fclose(fp);
   return rv;
 }
+
+
+void
+mar_get_content_extent(MarFile* mar,
+                       int* contentOffset,
+                       int* contentLength)
+{
+  // Skip to the offset to index and read it.
+  uint32_t indexOffset;
+  if (fseek(mar->fp, MAR_ID_SIZE, SEEK_SET)) {
+    return;
+  }
+  if (fread(&indexOffset, sizeof(indexOffset), 1, mar->fp) != 1) {
+    return;
+  }
+  indexOffset = ntohl(indexOffset);
+
+  // Skip to the first index entry past the index size field.
+  // We do it in 2 calls because offsetToIndex + sizeof(uint32_t)
+  // could oerflow in theory.
+  if (fseek(mar->fp, indexOffset, SEEK_SET)) {
+    return;
+  }
+  if (fseek(mar->fp, sizeof(uint32_t), SEEK_CUR)) {
+    return;
+  }
+
+  // Read the first offset to content field, which will be the offset to the
+  // start of the content area since the index is always written in order.
+  if (fread(contentOffset, sizeof(*contentOffset), 1, mar->fp) != 1) {
+    return;
+  }
+  *contentOffset = ntohl(*contentOffset);
+  *contentLength = indexOffset - *contentOffset;
+}
--- a/modules/libmar/src/moz.build
+++ b/modules/libmar/src/moz.build
@@ -6,25 +6,31 @@
 
 EXPORTS += [
     'mar.h',
     'mar_cmdline.h',
 ]
 
 HOST_SOURCES += [
     'mar_create.c',
+    'mar_decompress.c',
     'mar_extract.c',
     'mar_read.c',
 ]
 HostLibrary('hostmar')
 
 Library('mar')
 
 UNIFIED_SOURCES += [
     'mar_create.c',
+    'mar_decompress.c',
     'mar_extract.c',
     'mar_read.c',
 ]
 
+LOCAL_INCLUDES += [
+    '/toolkit/mozapps/update/common',
+]
+
 FORCE_STATIC_LIB = True
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     USE_STATIC_LIBS = True
--- a/modules/libmar/tool/moz.build
+++ b/modules/libmar/tool/moz.build
@@ -7,27 +7,29 @@
 HOST_SOURCES += [
     'mar.c',
 ]
 
 HostProgram('mar')
 
 HOST_USE_LIBS += [
     'hostmar',
+    'hostxz-embedded',
 ]
 
 if CONFIG['MOZ_ENABLE_SIGNMAR']:
     Program('signmar')
 
     SOURCES += HOST_SOURCES
 
     USE_LIBS += [
         'mar',
         'signmar',
         'verifymar',
+        'xz-embedded',
     ]
 
 for var in ('MAR_CHANNEL_ID', 'MOZ_APP_VERSION'):
     DEFINES[var] = '"%s"' % CONFIG[var]
     HOST_DEFINES[var] = DEFINES[var]
 
 if CONFIG['MOZ_ENABLE_SIGNMAR']:
     USE_LIBS += [
--- a/modules/xz-embedded/moz.build
+++ b/modules/xz-embedded/moz.build
@@ -3,31 +3,44 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += [
     'src/xz.h',
 ]
 
-UNIFIED_SOURCES += [
+common_sources = [
     'src/xz_crc32.c',
     'src/xz_crc64.c',
     'src/xz_dec_bcj.c',
     'src/xz_dec_lzma2.c',
     'src/xz_dec_stream.c',
 ]
 
+UNIFIED_SOURCES += common_sources
+
 ALLOW_COMPILER_WARNINGS = True
 
 if CONFIG['TARGET_CPU'].startswith('arm'):
     # Accept arm, armv7, etc.
     if CONFIG['MOZ_THUMB2']:
         DEFINES['XZ_DEC_ARMTHUMB'] = 1
+        HOST_DEFINES['XZ_DEC_ARMTHUMB'] = 1
     else:
         DEFINES['XZ_DEC_ARM'] = 1
+        HOST_DEFINES['XZ_DEC_ARM'] = 1
 elif '86' in CONFIG['TARGET_CPU']:
     # Accept x86, x86_64, i386, i686, etc.
     DEFINES['XZ_DEC_X86'] = 1
+    HOST_DEFINES['XZ_DEC_X86'] = 1
 
 DEFINES['XZ_USE_CRC64'] = 1
+HOST_DEFINES['XZ_USE_CRC64'] = 1
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+    USE_STATIC_LIBS = True
 
 Library('xz-embedded')
+
+HOST_SOURCES += common_sources
+
+HostLibrary('hostxz-embedded')
--- a/toolkit/mozapps/update/common/errors.h
+++ b/toolkit/mozapps/update/common/errors.h
@@ -66,16 +66,18 @@
 #define DELETE_ERROR_EXPECTED_FILE 47
 #define RENAME_ERROR_EXPECTED_FILE 48
 
 // Error codes 24-33 and 49-51 are for the Windows maintenance service.
 #define SERVICE_COULD_NOT_COPY_UPDATER 49
 #define SERVICE_STILL_APPLYING_TERMINATED 50
 #define SERVICE_STILL_APPLYING_NO_EXIT_CODE 51
 
+#define UNEXPECTED_XZ_ERROR 52
+
 #define WRITE_ERROR_FILE_COPY 61
 #define WRITE_ERROR_DELETE_FILE 62
 #define WRITE_ERROR_OPEN_PATCH_FILE 63
 #define WRITE_ERROR_PATCH_FILE 64
 #define WRITE_ERROR_APPLY_DIR_PATH 65
 #define WRITE_ERROR_CALLBACK_PATH 66
 #define WRITE_ERROR_FILE_ACCESS_DENIED 67
 #define WRITE_ERROR_DIR_ACCESS_DENIED 68
--- a/toolkit/mozapps/update/common/readstrings.h
+++ b/toolkit/mozapps/update/common/readstrings.h
@@ -2,24 +2,19 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef READSTRINGS_H__
 #define READSTRINGS_H__
 
-#define MAX_TEXT_LEN 600
+#include "updatedefines.h"
 
-#ifdef XP_WIN
-# include <windows.h>
-  typedef WCHAR NS_tchar;
-#else
-  typedef char NS_tchar;
-#endif
+#define MAX_TEXT_LEN 600
 
 #ifndef NULL
 #define NULL 0
 #endif
 
 struct StringTable 
 {
   char title[MAX_TEXT_LEN];
--- a/toolkit/mozapps/update/common/updatedefines.h
+++ b/toolkit/mozapps/update/common/updatedefines.h
@@ -1,17 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef UPDATEDEFINES_H
 #define UPDATEDEFINES_H
 
-#include "readstrings.h"
-
 #ifndef MAXPATHLEN
 # ifdef PATH_MAX
 #  define MAXPATHLEN PATH_MAX
 # elif defined(MAX_PATH)
 #  define MAXPATHLEN MAX_PATH
 # elif defined(_MAX_PATH)
 #  define MAXPATHLEN _MAX_PATH
 # elif defined(CCHMAXPATH)
@@ -24,16 +22,18 @@
 #if defined(XP_WIN)
 # include <windows.h>
 # include <shlwapi.h>
 # include <direct.h>
 # include <io.h>
 # include <stdio.h>
 # include <stdarg.h>
 
+typedef WCHAR NS_tchar;
+
 # ifndef F_OK
 #   define F_OK 00
 # endif
 # ifndef W_OK
 #   define W_OK 02
 # endif
 # ifndef R_OK
 #   define R_OK 04
@@ -72,62 +72,72 @@ static inline int mywcsprintf(WCHAR* des
 # define NS_tmkdir(path, perms) _wmkdir(path)
 # define NS_tremove _wremove
 // _wrename is used to avoid the link tracking service.
 # define NS_trename _wrename
 # define NS_trmdir _wrmdir
 # define NS_tstat _wstat
 # define NS_tlstat _wstat // No symlinks on Windows
 # define NS_tstat_t _stat
+# define NS_tsystem _wsystem
 # define NS_tstrcat wcscat
+# define NS_tstrncat wcsncat
 # define NS_tstrcmp wcscmp
 # define NS_tstricmp wcsicmp
 # define NS_tstrcpy wcscpy
 # define NS_tstrncpy wcsncpy
 # define NS_tstrlen wcslen
 # define NS_tstrchr wcschr
 # define NS_tstrrchr wcsrchr
 # define NS_tstrstr wcsstr
+
+#ifdef __cplusplus
 # include "win_dirent.h"
 # define NS_tDIR DIR
 # define NS_tdirent dirent
 # define NS_topendir opendir
 # define NS_tclosedir closedir
 # define NS_treaddir readdir
+#endif
+
 #else
 # include <sys/wait.h>
 # include <unistd.h>
 
 #ifdef SOLARIS
 # include <sys/stat.h>
 #else
 # include <fts.h>
 #endif
 # include <dirent.h>
 
 #ifdef XP_MACOSX
 # include <sys/time.h>
 #endif
 
+typedef char NS_tchar;
+
 # define LOG_S "%s"
 # define NS_T(str) str
 # define NS_SLASH NS_T('/')
 # define NS_tsnprintf snprintf
 # define NS_taccess access
 # define NS_tchdir chdir
 # define NS_tchmod chmod
 # define NS_tfopen fopen
 # define NS_tmkdir mkdir
 # define NS_tremove remove
 # define NS_trename rename
 # define NS_trmdir rmdir
 # define NS_tstat stat
 # define NS_tstat_t stat
 # define NS_tlstat lstat
+# define NS_tsystem system
 # define NS_tstrcat strcat
+# define NS_tstrncat strncat
 # define NS_tstrcmp strcmp
 # define NS_tstricmp strcasecmp
 # define NS_tstrcpy strcpy
 # define NS_tstrncpy strncpy
 # define NS_tstrlen strlen
 # define NS_tstrrchr strrchr
 # define NS_tstrstr strstr
 # define NS_tDIR DIR
--- a/toolkit/mozapps/update/updater/archivereader.cpp
+++ b/toolkit/mozapps/update/updater/archivereader.cpp
@@ -2,17 +2,16 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <string.h>
 #include <stdlib.h>
 #include <fcntl.h>
-#include "bzlib.h"
 #include "archivereader.h"
 #include "errors.h"
 #ifdef XP_WIN
 #include "nsAlgorithm.h" // Needed by nsVersionComparator.cpp
 #include "updatehelper.h"
 #endif
 
 // These are generated at compile time based on the DER file for the channel
@@ -266,59 +265,33 @@ ArchiveReader::ExtractFileToStream(const
     return READ_ERROR;
 
   return ExtractItemToStream(item, fp);
 }
 
 int
 ArchiveReader::ExtractItemToStream(const MarItem *item, FILE *fp)
 {
-  /* decompress the data chunk by chunk */
-
-  bz_stream strm;
-  int offset, inlen, outlen, ret = OK;
-
-  memset(&strm, 0, sizeof(strm));
-  if (BZ2_bzDecompressInit(&strm, 0, 0) != BZ_OK)
-    return UNEXPECTED_BZIP_ERROR;
-
-  offset = 0;
-  for (;;) {
-    if (!item->length) {
-      ret = UNEXPECTED_MAR_ERROR;
-      break;
-    }
+  // We need to wait to do this until after the certificate has been checked,
+  // so that we don't do a bunch of processing on a file of unknown validity.
+  // At this point, the check has to have been done if it's going to be.
+  if (mar_decompress(mArchive) == -1) {
+    return UNEXPECTED_XZ_ERROR;
+  }
 
-    if (offset < (int) item->length && strm.avail_in == 0) {
-      inlen = mar_read(mArchive, item, offset, inbuf, inbuf_size);
-      if (inlen <= 0)
-        return READ_ERROR;
-      offset += inlen;
-      strm.next_in = inbuf;
-      strm.avail_in = inlen;
-    }
-
-    strm.next_out = outbuf;
-    strm.avail_out = outbuf_size;
+  if (item->length == 0) {
+    return UNEXPECTED_MAR_ERROR;
+  }
 
-    ret = BZ2_bzDecompress(&strm);
-    if (ret != BZ_OK && ret != BZ_STREAM_END) {
-      ret = UNEXPECTED_BZIP_ERROR;
-      break;
+  int offset = 0;
+  while (offset < (int)(item->length)) {
+    int inlen = mar_read(mArchive, item, offset, inbuf, inbuf_size);
+    if (inlen <= 0) {
+      return READ_ERROR;
     }
-
-    outlen = outbuf_size - strm.avail_out;
-    if (outlen) {
-      if (fwrite(outbuf, outlen, 1, fp) != 1) {
-        ret = WRITE_ERROR_EXTRACT;
-        break;
-      }
-    }
-
-    if (ret == BZ_STREAM_END) {
-      ret = OK;
-      break;
+    offset += inlen;
+    if (fwrite(inbuf, inlen, 1, fp) != 1) {
+      return WRITE_ERROR_EXTRACT;
     }
   }
 
-  BZ2_bzDecompressEnd(&strm);
-  return ret;
+  return OK;
 }
--- a/toolkit/mozapps/update/updater/archivereader.h
+++ b/toolkit/mozapps/update/updater/archivereader.h
@@ -6,19 +6,20 @@
 
 #ifndef ArchiveReader_h__
 #define ArchiveReader_h__
 
 #include <stdio.h>
 #include "mar.h"
 
 #ifdef XP_WIN
-  typedef WCHAR NS_tchar;
+#include <windows.h>
+typedef WCHAR NS_tchar;
 #else
-  typedef char NS_tchar;
+typedef char NS_tchar;
 #endif
 
 // This class provides an API to extract files from an update archive.
 class ArchiveReader
 {
 public:
   ArchiveReader() : mArchive(nullptr) {}
   ~ArchiveReader() { Close(); }
--- a/toolkit/mozapps/update/updater/updater-common.build
+++ b/toolkit/mozapps/update/updater/updater-common.build
@@ -53,25 +53,19 @@ elif CONFIG['OS_ARCH'] == 'Linux' and CO
     OS_LIBS += CONFIG['NSPR_LIBS']
 else:
     USE_LIBS += [
         'updatecommon',
     ]
 
 USE_LIBS += [
     'mar',
+    'xz-embedded',
 ]
 
-if CONFIG['MOZ_SYSTEM_BZ2']:
-    OS_LIBS += CONFIG['MOZ_BZ2_LIBS']
-else:
-    USE_LIBS += [
-        'bz2',
-    ]
-
 if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
     have_progressui = 1
     srcs += [
         'progressui_gtk.cpp',
     ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     have_progressui = 1
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -33,17 +33,17 @@
  *  -----------
  *  method   = "remove" | "rmdir"
  */
 #include "bspatch.h"
 #include "progressui.h"
 #include "archivereader.h"
 #include "readstrings.h"
 #include "errors.h"
-#include "bzlib.h"
+#include "xz.h"
 
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdarg.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -163,47 +163,16 @@ BOOL PathGetSiblingFilePath(LPWSTR desti
         LogFinish(); \
         return retCode; \
       } \
   }
 #endif
 
 //-----------------------------------------------------------------------------
 
-// This variable lives in libbz2.  It's declared in bzlib_private.h, so we just
-// declare it here to avoid including that entire header file.
-#define BZ2_CRC32TABLE_UNDECLARED
-
-#if MOZ_IS_GCC || defined(__clang__)
-extern "C"  __attribute__((visibility("default"))) unsigned int BZ2_crc32Table[256];
-#undef BZ2_CRC32TABLE_UNDECLARED
-#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-extern "C" __global unsigned int BZ2_crc32Table[256];
-#undef BZ2_CRC32TABLE_UNDECLARED
-#endif
-#if defined(BZ2_CRC32TABLE_UNDECLARED)
-extern "C" unsigned int BZ2_crc32Table[256];
-#undef BZ2_CRC32TABLE_UNDECLARED
-#endif
-
-static unsigned int
-crc32(const unsigned char *buf, unsigned int len)
-{
-  unsigned int crc = 0xffffffffL;
-
-  const unsigned char *end = buf + len;
-  for (; buf != end; ++buf)
-    crc = (crc << 8) ^ BZ2_crc32Table[(crc >> 24) ^ *buf];
-
-  crc = ~crc;
-  return crc;
-}
-
-//-----------------------------------------------------------------------------
-
 // A simple stack based container for a FILE struct that closes the
 // file descriptor from its destructor.
 class AutoFile
 {
 public:
   explicit AutoFile(FILE* file = nullptr)
     : mFile(file) {
   }
@@ -1545,19 +1514,17 @@ PatchFile::LoadSourceFile(FILE* ofile)
       return READ_ERROR;
     }
 
     r -= c;
     rb += c;
   }
 
   // Verify that the contents of the source file correspond to what we expect.
-
-  unsigned int crc = crc32(buf, header.slen);
-
+  uint32_t crc = xz_crc32(buf, header.slen, 0);
   if (crc != header.scrc32) {
     LOG(("LoadSourceFile: destination file crc %d does not match expected " \
          "crc %d", crc, header.scrc32));
     return CRC_ERROR;
   }
 
   return OK;
 }
--- a/tools/update-packaging/common.sh
+++ b/tools/update-packaging/common.sh
@@ -6,42 +6,30 @@
 #
 # Code shared by update packaging scripts.
 # Author: Darin Fisher
 #
 
 # -----------------------------------------------------------------------------
 # By default just assume that these tools exist on our path
 MAR=${MAR:-mar}
-BZIP2=${BZIP2:-bzip2}
 MBSDIFF=${MBSDIFF:-mbsdiff}
 
 # -----------------------------------------------------------------------------
 # Helper routines
 
 notice() {
   echo "$*" 1>&2
 }
 
 get_file_size() {
   info=($(ls -ln "$1"))
   echo ${info[4]}
 }
 
-copy_perm() {
-  reference="$1"
-  target="$2"
-
-  if [ -x "$reference" ]; then
-    chmod 0755 "$target"
-  else
-    chmod 0644 "$target"
-  fi
-}
-
 make_add_instruction() {
   f="$1"
   filev2="$2"
   # The third param will be an empty string when a file add instruction is only
   # needed in the version 2 manifest. This only happens when the file has an
   # add-if-not instruction in the version 3 manifest. This is due to the
   # precomplete file prior to the version 3 manifest having a remove instruction
   # for this file so the file is removed before applying a complete update.
--- a/tools/update-packaging/make_full_update.sh
+++ b/tools/update-packaging/make_full_update.sh
@@ -88,30 +88,26 @@ for ((i=0; $i<$num_files; i=$i+1)); do
       make_add_instruction "$f" "$updatemanifestv2" "" 1
     fi
   else
     make_add_instruction "$f" "$updatemanifestv2" "$updatemanifestv3"
   fi
 
   dir=$(dirname "$f")
   mkdir -p "$workdir/$dir"
-  $BZIP2 -cz9 "$targetdir/$f" > "$workdir/$f"
-  copy_perm "$targetdir/$f" "$workdir/$f"
+  cp -p "$targetdir/$f" "$workdir/$f"
 
   targetfiles="$targetfiles \"$f\""
 done
 
 # Append remove instructions for any dead files.
 notice ""
 notice "Adding file and directory remove instructions from file 'removed-files'"
 append_remove_instructions "$targetdir" "$updatemanifestv2" "$updatemanifestv3"
 
-$BZIP2 -z9 "$updatemanifestv2" && mv -f "$updatemanifestv2.bz2" "$updatemanifestv2"
-$BZIP2 -z9 "$updatemanifestv3" && mv -f "$updatemanifestv3.bz2" "$updatemanifestv3"
-
 eval "$MAR -C \"$workdir\" -c output.mar $targetfiles"
 mv -f "$workdir/output.mar" "$archive"
 
 # cleanup
 rm -fr "$workdir"
 
 notice ""
 notice "Finished"
--- a/tools/update-packaging/make_incremental_update.sh
+++ b/tools/update-packaging/make_incremental_update.sh
@@ -164,28 +164,26 @@ for ((i=0; $i<$num_oldfiles; i=$i+1)); d
   f="${oldfiles[$i]}"
 
   # If this file exists in the new directory as well, then check if it differs.
   if [ -f "$newdir/$f" ]; then
 
     if check_for_add_if_not_update "$f"; then
       # The full workdir may not exist yet, so create it if necessary.
       mkdir -p `dirname "$workdir/$f"`
-      $BZIP2 -cz9 "$newdir/$f" > "$workdir/$f"
-      copy_perm "$newdir/$f" "$workdir/$f"
+      cp -p "$newdir/$f" "$workdir/$f"
       make_add_if_not_instruction "$f" "$updatemanifestv3"
       archivefiles="$archivefiles \"$f\""
       continue 1
     fi
 
     if check_for_forced_update "$requested_forced_updates" "$f"; then
       # The full workdir may not exist yet, so create it if necessary.
       mkdir -p `dirname "$workdir/$f"`
-      $BZIP2 -cz9 "$newdir/$f" > "$workdir/$f"
-      copy_perm "$newdir/$f" "$workdir/$f"
+      cp -p "$newdir/$f" "$workdir/$f"
       make_add_instruction "$f" "$updatemanifestv2" "$updatemanifestv3" 1
       archivefiles="$archivefiles \"$f\""
       continue 1
     fi
 
     if ! diff "$olddir/$f" "$newdir/$f" > /dev/null; then
       # Compute both the compressed binary diff and the compressed file, and
       # compare the sizes.  Then choose the smaller of the two to package.
@@ -202,31 +200,28 @@ for ((i=0; $i<$num_oldfiles; i=$i+1)); d
       # myscript.sh -A SERVER-URL [-c LOCAL-CACHE-DIR-PATH] [-g] [-u] \
       #   PATH-FROM-URL PATH-TO-URL PATH-PATCH SERVER-URL
       #
       # Note: patches are bzipped stashed in funsize to gain more speed
 
       # if service is not enabled then default to old behavior
       if [ -z "$MBSDIFF_HOOK" ]; then
         $MBSDIFF "$olddir/$f" "$newdir/$f" "$workdir/$f.patch"
-        $BZIP2 -z9 "$workdir/$f.patch"
       else
         # if service enabled then check patch existence for retrieval
-        if $MBSDIFF_HOOK -g "$olddir/$f" "$newdir/$f" "$workdir/$f.patch.bz2"; then
+        if $MBSDIFF_HOOK -g "$olddir/$f" "$newdir/$f" "$workdir/$f.patch"; then
           notice "file \"$f\" found in funsize, diffing skipped"
         else
           # if not found already - compute it and cache it for future use
           $MBSDIFF "$olddir/$f" "$newdir/$f" "$workdir/$f.patch"
-          $BZIP2 -z9 "$workdir/$f.patch"
-          $MBSDIFF_HOOK -u "$olddir/$f" "$newdir/$f" "$workdir/$f.patch.bz2"
+          $MBSDIFF_HOOK -u "$olddir/$f" "$newdir/$f" "$workdir/$f.patch"
         fi
       fi
-      $BZIP2 -cz9 "$newdir/$f" > "$workdir/$f"
-      copy_perm "$newdir/$f" "$workdir/$f"
-      patchfile="$workdir/$f.patch.bz2"
+      cp -p "$newdir/$f" "$workdir/$f"
+      patchfile="$workdir/$f.patch"
       patchsize=$(get_file_size "$patchfile")
       fullsize=$(get_file_size "$workdir/$f")
 
       if [ $patchsize -lt $fullsize ]; then
         make_patch_instruction "$f" "$updatemanifestv2" "$updatemanifestv3"
         mv -f "$patchfile" "$workdir/$f.patch"
         rm -f "$workdir/$f"
         archivefiles="$archivefiles \"$f.patch\""
@@ -257,18 +252,17 @@ for ((i=0; $i<$num_newfiles; i=$i+1)); d
     if [ "$f" = "${oldfiles[j]}" ]; then
       continue 2
     fi
   done
 
   dir=$(dirname "$workdir/$f")
   mkdir -p "$dir"
 
-  $BZIP2 -cz9 "$newdir/$f" > "$workdir/$f"
-  copy_perm "$newdir/$f" "$workdir/$f"
+  cp -p "$newdir/$f" "$workdir/$f"
 
   if check_for_add_if_not_update "$f"; then
     make_add_if_not_instruction "$f" "$updatemanifestv3"
   else
     make_add_instruction "$f" "$updatemanifestv2" "$updatemanifestv3"
   fi
 
 
@@ -298,19 +292,16 @@ for ((i=0; $i<$num_olddirs; i=$i+1)); do
   # If this dir doesn't exist in the new directory remove it.
   if [ ! -d "$newdir/$f" ]; then
     notice "      rmdir $f/"
     echo "rmdir \"$f/\"" >> $updatemanifestv2
     echo "rmdir \"$f/\"" >> $updatemanifestv3
   fi
 done
 
-$BZIP2 -z9 "$updatemanifestv2" && mv -f "$updatemanifestv2.bz2" "$updatemanifestv2"
-$BZIP2 -z9 "$updatemanifestv3" && mv -f "$updatemanifestv3.bz2" "$updatemanifestv3"
-
 mar_command="$MAR"
 if [[ -n $MOZ_PRODUCT_VERSION ]]
 then
   mar_command="$mar_command -V $MOZ_PRODUCT_VERSION"
 fi
 if [[ -n $MOZ_CHANNEL_ID ]]
 then
   mar_command="$mar_command -H $MOZ_CHANNEL_ID"