modules/libmar/src/mar_read.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include <sys/types.h>
     8 #include <fcntl.h>
     9 #include <stdlib.h>
    10 #include <string.h>
    11 #include "mar_private.h"
    12 #include "mar.h"
    14 #ifdef XP_WIN
    15 #include <winsock2.h>
    16 #else
    17 #include <netinet/in.h>
    18 #endif
    21 /* this is the same hash algorithm used by nsZipArchive.cpp */
    22 static uint32_t mar_hash_name(const char *name) {
    23   uint32_t val = 0;
    24   unsigned char* c;
    26   for (c = (unsigned char *) name; *c; ++c)
    27     val = val*37 + *c;
    29   return val % TABLESIZE;
    30 }
    32 static int mar_insert_item(MarFile *mar, const char *name, int namelen,
    33                            uint32_t offset, uint32_t length, uint32_t flags) {
    34   MarItem *item, *root;
    35   uint32_t hash;
    37   item = (MarItem *) malloc(sizeof(MarItem) + namelen);
    38   if (!item)
    39     return -1;
    40   item->next = NULL;
    41   item->offset = offset;
    42   item->length = length;
    43   item->flags = flags;
    44   memcpy(item->name, name, namelen + 1);
    46   hash = mar_hash_name(name);
    48   root = mar->item_table[hash];
    49   if (!root) {
    50     mar->item_table[hash] = item;
    51   } else {
    52     /* append item */
    53     while (root->next)
    54       root = root->next;
    55     root->next = item;
    56   }
    57   return 0;
    58 }
    60 static int mar_consume_index(MarFile *mar, char **buf, const char *buf_end) {
    61   /*
    62    * Each item has the following structure:
    63    *   uint32_t offset      (network byte order)
    64    *   uint32_t length      (network byte order)
    65    *   uint32_t flags       (network byte order)
    66    *   char     name[N]     (where N >= 1)
    67    *   char     null_byte;
    68    */
    69   uint32_t offset;
    70   uint32_t length;
    71   uint32_t flags;
    72   const char *name;
    73   int namelen;
    75   if ((buf_end - *buf) < (int)(3*sizeof(uint32_t) + 2))
    76     return -1;
    78   memcpy(&offset, *buf, sizeof(offset));
    79   *buf += sizeof(offset);
    81   memcpy(&length, *buf, sizeof(length));
    82   *buf += sizeof(length);
    84   memcpy(&flags, *buf, sizeof(flags));
    85   *buf += sizeof(flags);
    87   offset = ntohl(offset);
    88   length = ntohl(length);
    89   flags = ntohl(flags);
    91   name = *buf;
    92   /* find namelen; must take care not to read beyond buf_end */
    93   while (**buf) {
    94     if (*buf == buf_end)
    95       return -1;
    96     ++(*buf);
    97   }
    98   namelen = (*buf - name);
    99   /* consume null byte */
   100   if (*buf == buf_end)
   101     return -1;
   102   ++(*buf);
   104   return mar_insert_item(mar, name, namelen, offset, length, flags);
   105 }
   107 static int mar_read_index(MarFile *mar) {
   108   char id[MAR_ID_SIZE], *buf, *bufptr, *bufend;
   109   uint32_t offset_to_index, size_of_index;
   111   /* verify MAR ID */
   112   if (fread(id, MAR_ID_SIZE, 1, mar->fp) != 1)
   113     return -1;
   114   if (memcmp(id, MAR_ID, MAR_ID_SIZE) != 0)
   115     return -1;
   117   if (fread(&offset_to_index, sizeof(uint32_t), 1, mar->fp) != 1)
   118     return -1;
   119   offset_to_index = ntohl(offset_to_index);
   121   if (fseek(mar->fp, offset_to_index, SEEK_SET))
   122     return -1;
   123   if (fread(&size_of_index, sizeof(uint32_t), 1, mar->fp) != 1)
   124     return -1;
   125   size_of_index = ntohl(size_of_index);
   127   buf = (char *) malloc(size_of_index);
   128   if (!buf)
   129     return -1;
   130   if (fread(buf, size_of_index, 1, mar->fp) != 1) {
   131     free(buf);
   132     return -1;
   133   }
   135   bufptr = buf;
   136   bufend = buf + size_of_index;
   137   while (bufptr < bufend && mar_consume_index(mar, &bufptr, bufend) == 0);
   139   free(buf);
   140   return (bufptr == bufend) ? 0 : -1;
   141 }
   143 /**
   144  * Internal shared code for mar_open and mar_wopen.
   145  * On failure, will fclose(fp).
   146  */
   147 static MarFile *mar_fpopen(FILE *fp)
   148 {
   149   MarFile *mar;
   151   mar = (MarFile *) malloc(sizeof(*mar));
   152   if (!mar) {
   153     fclose(fp);
   154     return NULL;
   155   }
   157   mar->fp = fp;
   158   memset(mar->item_table, 0, sizeof(mar->item_table));
   159   if (mar_read_index(mar)) {
   160     mar_close(mar);
   161     return NULL;
   162   }
   164   return mar;
   165 }
   167 MarFile *mar_open(const char *path) {
   168   FILE *fp;
   170   fp = fopen(path, "rb");
   171   if (!fp) {
   172     fprintf(stderr, "ERROR: could not open file in mar_open()\n");
   173     perror(path);
   174     return NULL;
   175   }
   177   return mar_fpopen(fp);
   178 }
   180 #ifdef XP_WIN
   181 MarFile *mar_wopen(const wchar_t *path) {
   182   FILE *fp;
   184   _wfopen_s(&fp, path, L"rb");
   185   if (!fp) {
   186     fprintf(stderr, "ERROR: could not open file in mar_wopen()\n");
   187     _wperror(path);
   188     return NULL;
   189   }
   191   return mar_fpopen(fp);
   192 }
   193 #endif
   195 void mar_close(MarFile *mar) {
   196   MarItem *item;
   197   int i;
   199   fclose(mar->fp);
   201   for (i = 0; i < TABLESIZE; ++i) {
   202     item = mar->item_table[i];
   203     while (item) {
   204       MarItem *temp = item;
   205       item = item->next;
   206       free(temp);
   207     }
   208   }
   210   free(mar);
   211 }
   213 /**
   214  * Determines the MAR file information.
   215  *
   216  * @param fp                     An opened MAR file in read mode.
   217  * @param hasSignatureBlock      Optional out parameter specifying if the MAR
   218  *                               file has a signature block or not.
   219  * @param numSignatures          Optional out parameter for storing the number
   220  *                               of signatures in the MAR file.
   221  * @param hasAdditionalBlocks    Optional out parameter specifying if the MAR
   222  *                               file has additional blocks or not.
   223  * @param offsetAdditionalBlocks Optional out parameter for the offset to the 
   224  *                               first additional block. Value is only valid if
   225  *                               hasAdditionalBlocks is not equal to 0.
   226  * @param numAdditionalBlocks    Optional out parameter for the number of
   227  *                               additional blocks.  Value is only valid if
   228  *                               hasAdditionalBlocks is not equal to 0.
   229  * @return 0 on success and non-zero on failure.
   230  */
   231 int get_mar_file_info_fp(FILE *fp, 
   232                          int *hasSignatureBlock,
   233                          uint32_t *numSignatures,
   234                          int *hasAdditionalBlocks,
   235                          uint32_t *offsetAdditionalBlocks,
   236                          uint32_t *numAdditionalBlocks)
   237 {
   238   uint32_t offsetToIndex, offsetToContent, signatureCount, signatureLen, i;
   240   /* One of hasSignatureBlock or hasAdditionalBlocks must be non NULL */
   241   if (!hasSignatureBlock && !hasAdditionalBlocks) {
   242     return -1;
   243   }
   246   /* Skip to the start of the offset index */
   247   if (fseek(fp, MAR_ID_SIZE, SEEK_SET)) {
   248     return -1;
   249   }
   251   /* Read the offset to the index. */
   252   if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fp) != 1) {
   253     return -1;
   254   }
   255   offsetToIndex = ntohl(offsetToIndex);
   257   if (numSignatures) {
   258      /* Skip past the MAR file size field */
   259     if (fseek(fp, sizeof(uint64_t), SEEK_CUR)) {
   260       return -1;
   261     }
   263     /* Read the number of signatures field */
   264     if (fread(numSignatures, sizeof(*numSignatures), 1, fp) != 1) {
   265       return -1;
   266     }
   267     *numSignatures = ntohl(*numSignatures);
   268   }
   270   /* Skip to the first index entry past the index size field 
   271      We do it in 2 calls because offsetToIndex + sizeof(uint32_t) 
   272      could oerflow in theory. */
   273   if (fseek(fp, offsetToIndex, SEEK_SET)) {
   274     return -1;
   275   }
   277   if (fseek(fp, sizeof(uint32_t), SEEK_CUR)) {
   278     return -1;
   279   }
   281   /* Read the first offset to content field. */
   282   if (fread(&offsetToContent, sizeof(offsetToContent), 1, fp) != 1) {
   283     return -1;
   284   }
   285   offsetToContent = ntohl(offsetToContent);
   287   /* Check if we have a new or old MAR file */
   288   if (hasSignatureBlock) {
   289     if (offsetToContent == MAR_ID_SIZE + sizeof(uint32_t)) {
   290       *hasSignatureBlock = 0;
   291     } else {
   292       *hasSignatureBlock = 1;
   293     }
   294   }
   296   /* If the caller doesn't care about the product info block 
   297      value, then just return */
   298   if (!hasAdditionalBlocks) {
   299     return 0;
   300   }
   302    /* Skip to the start of the signature block */
   303   if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
   304     return -1;
   305   }
   307   /* Get the number of signatures */
   308   if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) {
   309     return -1;
   310   }
   311   signatureCount = ntohl(signatureCount);
   313   /* Check that we have less than the max amount of signatures so we don't
   314      waste too much of either updater's or signmar's time. */
   315   if (signatureCount > MAX_SIGNATURES) {
   316     return -1;
   317   }
   319   /* Skip past the whole signature block */
   320   for (i = 0; i < signatureCount; i++) {
   321     /* Skip past the signature algorithm ID */
   322     if (fseek(fp, sizeof(uint32_t), SEEK_CUR)) {
   323       return -1;
   324     }
   326     /* Read the signature length and skip past the signature */
   327     if (fread(&signatureLen, sizeof(uint32_t), 1, fp) != 1) {
   328       return -1;
   329     }
   330     signatureLen = ntohl(signatureLen);
   331     if (fseek(fp, signatureLen, SEEK_CUR)) {
   332       return -1;
   333     }
   334   }
   336   if (ftell(fp) == offsetToContent) {
   337     *hasAdditionalBlocks = 0;
   338   } else {
   339     if (numAdditionalBlocks) {
   340       /* We have an additional block, so read in the number of additional blocks
   341          and set the offset. */
   342       *hasAdditionalBlocks = 1;
   343       if (fread(numAdditionalBlocks, sizeof(uint32_t), 1, fp) != 1) {
   344         return -1;
   345       }
   346       *numAdditionalBlocks = ntohl(*numAdditionalBlocks);
   347       if (offsetAdditionalBlocks) {
   348         *offsetAdditionalBlocks = ftell(fp);
   349       }
   350     } else if (offsetAdditionalBlocks) {
   351       /* numAdditionalBlocks is not specified but offsetAdditionalBlocks 
   352          is, so fill it! */
   353       *offsetAdditionalBlocks = ftell(fp) + sizeof(uint32_t);
   354     }
   355   }
   357   return 0;
   358 }
   360 /** 
   361  * Reads the product info block from the MAR file's additional block section.
   362  * The caller is responsible for freeing the fields in infoBlock
   363  * if the return is successful.
   364  *
   365  * @param infoBlock Out parameter for where to store the result to
   366  * @return 0 on success, -1 on failure
   367 */
   368 int
   369 read_product_info_block(char *path, 
   370                         struct ProductInformationBlock *infoBlock)
   371 {
   372   int rv;
   373   MarFile mar;
   374   mar.fp = fopen(path, "rb");
   375   if (!mar.fp) {
   376     fprintf(stderr, "ERROR: could not open file in read_product_info_block()\n");
   377     perror(path);
   378     return -1;
   379   }
   380   rv = mar_read_product_info_block(&mar, infoBlock);
   381   fclose(mar.fp);
   382   return rv;
   383 }
   385 /** 
   386  * Reads the product info block from the MAR file's additional block section.
   387  * The caller is responsible for freeing the fields in infoBlock
   388  * if the return is successful.
   389  *
   390  * @param infoBlock Out parameter for where to store the result to
   391  * @return 0 on success, -1 on failure
   392 */
   393 int
   394 mar_read_product_info_block(MarFile *mar, 
   395                             struct ProductInformationBlock *infoBlock)
   396 {
   397   uint32_t i, offsetAdditionalBlocks, numAdditionalBlocks,
   398     additionalBlockSize, additionalBlockID;
   399   int hasAdditionalBlocks;
   401   /* The buffer size is 97 bytes because the MAR channel name < 64 bytes, and 
   402      product version < 32 bytes + 3 NULL terminator bytes. */
   403   char buf[97] = { '\0' };
   404   int ret = get_mar_file_info_fp(mar->fp, NULL, NULL,
   405                                  &hasAdditionalBlocks, 
   406                                  &offsetAdditionalBlocks, 
   407                                  &numAdditionalBlocks);
   408   for (i = 0; i < numAdditionalBlocks; ++i) {
   409     /* Read the additional block size */
   410     if (fread(&additionalBlockSize, 
   411               sizeof(additionalBlockSize), 
   412               1, mar->fp) != 1) {
   413       return -1;
   414     }
   415     additionalBlockSize = ntohl(additionalBlockSize) - 
   416                           sizeof(additionalBlockSize) - 
   417                           sizeof(additionalBlockID);
   419     /* Read the additional block ID */
   420     if (fread(&additionalBlockID, 
   421               sizeof(additionalBlockID), 
   422               1, mar->fp) != 1) {
   423       return -1;
   424     }
   425     additionalBlockID = ntohl(additionalBlockID);
   427     if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
   428       const char *location;
   429       int len;
   431       /* This block must be at most 104 bytes.
   432          MAR channel name < 64 bytes, and product version < 32 bytes + 3 NULL 
   433          terminator bytes. We only check for 96 though because we remove 8 
   434          bytes above from the additionalBlockSize: We subtract 
   435          sizeof(additionalBlockSize) and sizeof(additionalBlockID) */
   436       if (additionalBlockSize > 96) {
   437         return -1;
   438       }
   440     if (fread(buf, additionalBlockSize, 1, mar->fp) != 1) {
   441         return -1;
   442       }
   444       /* Extract the MAR channel name from the buffer.  For now we
   445          point to the stack allocated buffer but we strdup this
   446          if we are within bounds of each field's max length. */
   447       location = buf;
   448       len = strlen(location);
   449       infoBlock->MARChannelID = location;
   450       location += len + 1;
   451       if (len >= 64) {
   452         infoBlock->MARChannelID = NULL;
   453         return -1;
   454       }
   456       /* Extract the version from the buffer */
   457       len = strlen(location);
   458       infoBlock->productVersion = location;
   459       location += len + 1;
   460       if (len >= 32) {
   461         infoBlock->MARChannelID = NULL;
   462         infoBlock->productVersion = NULL;
   463         return -1;
   464       }
   465       infoBlock->MARChannelID = 
   466         strdup(infoBlock->MARChannelID);
   467       infoBlock->productVersion = 
   468         strdup(infoBlock->productVersion);
   469       return 0;
   470     } else {
   471       /* This is not the additional block you're looking for. Move along. */
   472       if (fseek(mar->fp, additionalBlockSize, SEEK_CUR)) {
   473         return -1;
   474       }
   475     }
   476   }
   478   /* If we had a product info block we would have already returned */
   479   return -1;
   480 }
   482 const MarItem *mar_find_item(MarFile *mar, const char *name) {
   483   uint32_t hash;
   484   const MarItem *item;
   486   hash = mar_hash_name(name);
   488   item = mar->item_table[hash];
   489   while (item && strcmp(item->name, name) != 0)
   490     item = item->next;
   492   return item;
   493 }
   495 int mar_enum_items(MarFile *mar, MarItemCallback callback, void *closure) {
   496   MarItem *item;
   497   int i;
   499   for (i = 0; i < TABLESIZE; ++i) {
   500     item = mar->item_table[i];
   501     while (item) {
   502       int rv = callback(mar, item, closure);
   503       if (rv)
   504         return rv;
   505       item = item->next;
   506     }
   507   }
   509   return 0;
   510 }
   512 int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf,
   513              int bufsize) {
   514   int nr;
   516   if (offset == (int) item->length)
   517     return 0;
   518   if (offset > (int) item->length)
   519     return -1;
   521   nr = item->length - offset;
   522   if (nr > bufsize)
   523     nr = bufsize;
   525   if (fseek(mar->fp, item->offset + offset, SEEK_SET))
   526     return -1;
   528   return fread(buf, 1, nr, mar->fp);
   529 }
   531 /**
   532  * Determines the MAR file information.
   533  *
   534  * @param path                   The path of the MAR file to check.
   535  * @param hasSignatureBlock      Optional out parameter specifying if the MAR
   536  *                               file has a signature block or not.
   537  * @param numSignatures          Optional out parameter for storing the number
   538  *                               of signatures in the MAR file.
   539  * @param hasAdditionalBlocks    Optional out parameter specifying if the MAR
   540  *                               file has additional blocks or not.
   541  * @param offsetAdditionalBlocks Optional out parameter for the offset to the 
   542  *                               first additional block. Value is only valid if
   543  *                               hasAdditionalBlocks is not equal to 0.
   544  * @param numAdditionalBlocks    Optional out parameter for the number of
   545  *                               additional blocks.  Value is only valid if
   546  *                               has_additional_blocks is not equal to 0.
   547  * @return 0 on success and non-zero on failure.
   548  */
   549 int get_mar_file_info(const char *path, 
   550                       int *hasSignatureBlock,
   551                       uint32_t *numSignatures,
   552                       int *hasAdditionalBlocks,
   553                       uint32_t *offsetAdditionalBlocks,
   554                       uint32_t *numAdditionalBlocks)
   555 {
   556   int rv;
   557   FILE *fp = fopen(path, "rb");
   558   if (!fp) {
   559     fprintf(stderr, "ERROR: could not open file in get_mar_file_info()\n");
   560     perror(path);
   561     return -1;
   562   }
   564   rv = get_mar_file_info_fp(fp, hasSignatureBlock, 
   565                             numSignatures, hasAdditionalBlocks,
   566                             offsetAdditionalBlocks, numAdditionalBlocks);
   568   fclose(fp);
   569   return rv;
   570 }

mercurial