tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

mar_read.c (23440B)


      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/. */
      6 
      7 #include <sys/types.h>
      8 #include <fcntl.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include "city.h"
     12 #include "mar_private.h"
     13 #include "mar.h"
     14 #ifdef XP_WIN
     15 #  define strdup _strdup
     16 #endif
     17 
     18 /* This block must be at most 104 bytes.
     19   MAR channel name < 64 bytes, and product version < 32 bytes + 3 NULL
     20   terminator bytes. We only check for 96 though because we remove 8
     21   bytes above from the additionalBlockSize: We subtract
     22   sizeof(additionalBlockSize) and sizeof(additionalBlockID) */
     23 #define MAXADDITIONALBLOCKSIZE 96
     24 
     25 static uint32_t mar_hash_name(const char* name) {
     26  return CityHash64(name, strlen(name)) % TABLESIZE;
     27 }
     28 
     29 static int mar_insert_item(MarFile* mar, const char* name, uint32_t namelen,
     30                           uint32_t offset, uint32_t length, uint32_t flags) {
     31  MarItem *item, *root;
     32  uint32_t hash;
     33 
     34  item = (MarItem*)malloc(sizeof(MarItem) + namelen);
     35  if (!item) {
     36    return -1;
     37  }
     38  item->next = NULL;
     39  item->offset = offset;
     40  item->length = length;
     41  item->flags = flags;
     42  memcpy(item->name, name, namelen + 1);
     43 
     44  hash = mar_hash_name(name);
     45 
     46  root = mar->item_table[hash];
     47  if (!root) {
     48    mar->item_table[hash] = item;
     49  } else {
     50    /* append item */
     51    while (root->next) root = root->next;
     52    root->next = item;
     53  }
     54  return 0;
     55 }
     56 
     57 static int mar_consume_index(MarFile* mar, char** buf, const char* buf_end) {
     58  /*
     59   * Each item has the following structure:
     60   *   uint32_t offset      (network byte order)
     61   *   uint32_t length      (network byte order)
     62   *   uint32_t flags       (network byte order)
     63   *   char     name[N]     (where N >= 1)
     64   *   char     null_byte;
     65   */
     66  uint32_t offset;
     67  uint32_t length;
     68  uint32_t flags;
     69  const char* name;
     70  int namelen;
     71 
     72  if ((buf_end - *buf) < (int)(3 * sizeof(uint32_t) + 2)) {
     73    return -1;
     74  }
     75 
     76  memcpy(&offset, *buf, sizeof(offset));
     77  *buf += sizeof(offset);
     78 
     79  memcpy(&length, *buf, sizeof(length));
     80  *buf += sizeof(length);
     81 
     82  memcpy(&flags, *buf, sizeof(flags));
     83  *buf += sizeof(flags);
     84 
     85  offset = ntohl(offset);
     86  length = ntohl(length);
     87  flags = ntohl(flags);
     88 
     89  name = *buf;
     90  /* find namelen; must take care not to read beyond buf_end */
     91  while (**buf) {
     92    /* buf_end points one byte past the end of buf's allocation */
     93    if (*buf == (buf_end - 1)) {
     94      return -1;
     95    }
     96    ++(*buf);
     97  }
     98  namelen = (*buf - name);
     99  /* must ensure that namelen is valid */
    100  if (namelen < 0) {
    101    return -1;
    102  }
    103  /* consume null byte */
    104  if (*buf == buf_end) {
    105    return -1;
    106  }
    107  ++(*buf);
    108 
    109  return mar_insert_item(mar, name, namelen, offset, length, flags);
    110 }
    111 
    112 static int mar_read_index(MarFile* mar) {
    113  char id[MAR_ID_SIZE], *buf, *bufptr, *bufend;
    114  uint32_t offset_to_index, size_of_index;
    115  size_t mar_position = 0;
    116 
    117  /* verify MAR ID */
    118  if (mar_read_buffer(mar, id, &mar_position, MAR_ID_SIZE) != 0) {
    119    return -1;
    120  }
    121  if (memcmp(id, MAR_ID, MAR_ID_SIZE) != 0) {
    122    return -1;
    123  }
    124 
    125  if (mar_read_buffer(mar, &offset_to_index, &mar_position, sizeof(uint32_t)) !=
    126      0) {
    127    return -1;
    128  }
    129  offset_to_index = ntohl(offset_to_index);
    130 
    131  mar_position = 0;
    132  if (mar_buffer_seek(mar, &mar_position, offset_to_index) != 0) {
    133    return -1;
    134  }
    135  if (mar_read_buffer(mar, &size_of_index, &mar_position, sizeof(uint32_t)) !=
    136      0) {
    137    return -1;
    138  }
    139  size_of_index = ntohl(size_of_index);
    140 
    141  buf = (char*)malloc(size_of_index);
    142  if (!buf) {
    143    return -1;
    144  }
    145  if (mar_read_buffer(mar, buf, &mar_position, size_of_index) != 0) {
    146    free(buf);
    147    return -1;
    148  }
    149 
    150  bufptr = buf;
    151  bufend = buf + size_of_index;
    152  while (bufptr < bufend && mar_consume_index(mar, &bufptr, bufend) == 0);
    153 
    154  free(buf);
    155  return (bufptr == bufend) ? 0 : -1;
    156 }
    157 
    158 /**
    159 * Adds an offset and length to the MarFile's index_list
    160 * @param mar     The MarFile that owns this offset length pair
    161 * @param offset  The byte offset in the archive to be marked as processed
    162 * @param length  The length corresponding to this byte offset
    163 * @return int    1 on success, 0 if offset has been previously processed
    164 *                -1 if unable to allocate space for the SeenIndexes
    165 */
    166 static int mar_insert_offset(MarFile* mar, uint32_t offset, uint32_t length) {
    167  /* Ignore files with no length */
    168  if (length == 0) {
    169    return 1;
    170  }
    171 
    172  SeenIndex* index = (SeenIndex*)malloc(sizeof(SeenIndex));
    173  if (!index) {
    174    return -1;
    175  }
    176  index->next = NULL;
    177  index->offset = offset;
    178  index->length = length;
    179  uint32_t index_end = index->offset + index->length - 1;
    180 
    181  /* If this is our first index store it at the front */
    182  if (mar->index_list == NULL) {
    183    mar->index_list = index;
    184    return 1;
    185  }
    186 
    187  /* Search for matching indexes in the list of those previously visited */
    188  SeenIndex* previous;
    189  SeenIndex* current = mar->index_list;
    190  while (current != NULL) {
    191    uint32_t current_end = current->offset + current->length - 1;
    192 
    193    /* If index has collided with the front or end of current or if current has
    194       collided with the front or end of index return false */
    195    if ((index->offset >= current->offset && index->offset <= current_end) ||
    196        (index_end >= current->offset && index_end <= current_end) ||
    197        (current->offset >= index->offset && current->offset <= index_end) ||
    198        (current_end >= index->offset && current_end <= index_end)) {
    199      free(index);
    200      return 0;
    201    }
    202 
    203    /* else move to the next in the list */
    204    previous = current;
    205    current = current->next;
    206  }
    207 
    208  /* These indexes are valid, track them */
    209  previous->next = index;
    210  return 1;
    211 }
    212 
    213 /**
    214 * Internal shared code for mar_open and mar_wopen.
    215 * Reads the entire MAR into memory. Fails if it is bigger than
    216 * MAX_SIZE_OF_MAR_FILE bytes.
    217 */
    218 static MarReadResult mar_fpopen(FILE* fp, MarFile** out_mar) {
    219  *out_mar = NULL;
    220  MarFile* mar;
    221 
    222  mar = (MarFile*)malloc(sizeof(*mar));
    223  if (!mar) {
    224    return MAR_MEM_ERROR;
    225  }
    226 
    227  off_t buffer_size = -1;
    228  if (fseeko(fp, 0, SEEK_END) == 0) {
    229    buffer_size = ftello(fp);
    230  }
    231  rewind(fp);
    232  if (buffer_size < 0) {
    233    fprintf(stderr, "Warning: MAR size could not be determined\n");
    234    buffer_size = MAX_SIZE_OF_MAR_FILE;
    235  }
    236  if (buffer_size > MAX_SIZE_OF_MAR_FILE) {
    237    fprintf(stderr, "ERROR: MAR exceeds maximum size (%lli)\n",
    238            (long long int)buffer_size);
    239    free(mar);
    240    return MAR_FILE_TOO_BIG_ERROR;
    241  }
    242 
    243  mar->buffer = malloc(buffer_size);
    244  if (!mar->buffer) {
    245    fprintf(stderr, "ERROR: MAR buffer could not be allocated\n");
    246    free(mar);
    247    return MAR_MEM_ERROR;
    248  }
    249  mar->data_len = fread(mar->buffer, 1, buffer_size, fp);
    250  if (fgetc(fp) != EOF) {
    251    fprintf(stderr, "ERROR: File is larger than buffer (%lli)\n",
    252            (long long int)buffer_size);
    253    free(mar->buffer);
    254    free(mar);
    255    return MAR_IO_ERROR;
    256  }
    257  if (ferror(fp)) {
    258    fprintf(stderr, "ERROR: Failed to read MAR\n");
    259    free(mar->buffer);
    260    free(mar);
    261    return MAR_IO_ERROR;
    262  }
    263 
    264  mar->item_table_is_valid = 0;
    265  memset(mar->item_table, 0, sizeof(mar->item_table));
    266  mar->index_list = NULL;
    267 
    268  *out_mar = mar;
    269  return MAR_READ_SUCCESS;
    270 }
    271 
    272 MarReadResult mar_open(const char* path, MarFile** out_mar) {
    273  *out_mar = NULL;
    274 
    275  FILE* fp;
    276 
    277  fp = fopen(path, "rb");
    278  if (!fp) {
    279    fprintf(stderr, "ERROR: could not open file in mar_open()\n");
    280    perror(path);
    281    return MAR_IO_ERROR;
    282  }
    283 
    284  MarReadResult result = mar_fpopen(fp, out_mar);
    285  fclose(fp);
    286  return result;
    287 }
    288 
    289 #ifdef XP_WIN
    290 MarReadResult mar_wopen(const wchar_t* path, MarFile** out_mar) {
    291  *out_mar = NULL;
    292 
    293  FILE* fp;
    294 
    295  _wfopen_s(&fp, path, L"rb");
    296  if (!fp) {
    297    fprintf(stderr, "ERROR: could not open file in mar_wopen()\n");
    298    _wperror(path);
    299    return MAR_IO_ERROR;
    300  }
    301 
    302  MarReadResult result = mar_fpopen(fp, out_mar);
    303  fclose(fp);
    304  return result;
    305 }
    306 #endif
    307 
    308 void mar_close(MarFile* mar) {
    309  MarItem* item;
    310  SeenIndex* index;
    311  int i;
    312 
    313  free(mar->buffer);
    314 
    315  for (i = 0; i < TABLESIZE; ++i) {
    316    item = mar->item_table[i];
    317    while (item) {
    318      MarItem* temp = item;
    319      item = item->next;
    320      free(temp);
    321    }
    322  }
    323 
    324  while (mar->index_list != NULL) {
    325    index = mar->index_list;
    326    mar->index_list = index->next;
    327    free(index);
    328  }
    329 
    330  free(mar);
    331 }
    332 
    333 int mar_read_buffer(MarFile* mar, void* dest, size_t* position, size_t size) {
    334  // size may be provided by the MAR, which we may not have finished validating
    335  // the signature on yet. Make sure not to trust it in a way that could
    336  // cause an overflow.
    337  if (size > mar->data_len) {
    338    return -1;
    339  }
    340  if (*position > mar->data_len - size) {
    341    return -1;
    342  }
    343  memcpy(dest, mar->buffer + *position, size);
    344  *position += size;
    345  return 0;
    346 }
    347 
    348 int mar_read_buffer_max(MarFile* mar, void* dest, size_t* position,
    349                        size_t size) {
    350  // size may be provided by the MAR, which we may not have finished validating
    351  // the signature on yet. Make sure not to trust it in a way that could
    352  // cause an overflow.
    353  if (mar->data_len <= *position) {
    354    return 0;
    355  }
    356  size_t read_count = mar->data_len - *position;
    357  if (read_count > size) {
    358    read_count = size;
    359  }
    360  memcpy(dest, mar->buffer + *position, read_count);
    361  *position += read_count;
    362  return read_count;
    363 }
    364 
    365 int mar_buffer_seek(MarFile* mar, size_t* position, size_t distance) {
    366  // distance may be provided by the MAR, which we may not have finished
    367  // validating the signature on yet. Make sure not to trust it in a way that
    368  // could cause an overflow.
    369  if (distance > mar->data_len) {
    370    return -1;
    371  }
    372  if (*position > mar->data_len - distance) {
    373    return -1;
    374  }
    375  *position += distance;
    376  return 0;
    377 }
    378 
    379 /**
    380 * Determines the MAR file information.
    381 *
    382 * @param mar                    An open MAR file.
    383 * @param mar_position           The current position in the MAR.
    384 *                               Its value will be updated to the current
    385 *                               position in the MAR after the function exits.
    386 *                               Since its initial value will never actually be
    387 *                               used, this is effectively an outparam.
    388 * @param hasSignatureBlock      Optional out parameter specifying if the MAR
    389 *                               file has a signature block or not.
    390 * @param numSignatures          Optional out parameter for storing the number
    391 *                               of signatures in the MAR file.
    392 * @param hasAdditionalBlocks    Optional out parameter specifying if the MAR
    393 *                               file has additional blocks or not.
    394 * @param offsetAdditionalBlocks Optional out parameter for the offset to the
    395 *                               first additional block. Value is only valid if
    396 *                               hasAdditionalBlocks is not equal to 0.
    397 * @param numAdditionalBlocks    Optional out parameter for the number of
    398 *                               additional blocks.  Value is only valid if
    399 *                               hasAdditionalBlocks is not equal to 0.
    400 * @return 0 on success and non-zero on failure.
    401 */
    402 int get_open_mar_file_info(MarFile* mar, size_t* mar_position,
    403                           int* hasSignatureBlock, uint32_t* numSignatures,
    404                           int* hasAdditionalBlocks,
    405                           uint32_t* offsetAdditionalBlocks,
    406                           uint32_t* numAdditionalBlocks) {
    407  uint32_t offsetToIndex, offsetToContent, signatureCount, signatureLen, i;
    408 
    409  /* One of hasSignatureBlock or hasAdditionalBlocks must be non NULL */
    410  if (!hasSignatureBlock && !hasAdditionalBlocks) {
    411    return -1;
    412  }
    413 
    414  /* Skip to the start of the offset index */
    415  *mar_position = 0;
    416  if (mar_buffer_seek(mar, mar_position, MAR_ID_SIZE) != 0) {
    417    return -1;
    418  }
    419 
    420  /* Read the offset to the index. */
    421  if (mar_read_buffer(mar, &offsetToIndex, mar_position,
    422                      sizeof(offsetToIndex)) != 0) {
    423    return -1;
    424  }
    425  offsetToIndex = ntohl(offsetToIndex);
    426 
    427  if (numSignatures) {
    428    /* Skip past the MAR file size field */
    429    if (mar_buffer_seek(mar, mar_position, sizeof(uint64_t)) != 0) {
    430      return -1;
    431    }
    432 
    433    /* Read the number of signatures field */
    434    if (mar_read_buffer(mar, numSignatures, mar_position,
    435                        sizeof(*numSignatures)) != 0) {
    436      return -1;
    437    }
    438    *numSignatures = ntohl(*numSignatures);
    439  }
    440 
    441  /* Skip to the first index entry past the index size field
    442     We do it in 2 calls because offsetToIndex + sizeof(uint32_t)
    443     could overflow in theory. */
    444  *mar_position = 0;
    445  if (mar_buffer_seek(mar, mar_position, offsetToIndex) != 0) {
    446    return -1;
    447  }
    448 
    449  if (mar_buffer_seek(mar, mar_position, sizeof(uint32_t)) != 0) {
    450    return -1;
    451  }
    452 
    453  /* Read the first offset to content field. */
    454  if (mar_read_buffer(mar, &offsetToContent, mar_position,
    455                      sizeof(offsetToContent)) != 0) {
    456    return -1;
    457  }
    458  offsetToContent = ntohl(offsetToContent);
    459 
    460  /* Check if we have a new or old MAR file */
    461  if (hasSignatureBlock) {
    462    if (offsetToContent == MAR_ID_SIZE + sizeof(uint32_t)) {
    463      *hasSignatureBlock = 0;
    464    } else {
    465      *hasSignatureBlock = 1;
    466    }
    467  }
    468 
    469  /* If the caller doesn't care about the product info block
    470     value, then just return */
    471  if (!hasAdditionalBlocks) {
    472    return 0;
    473  }
    474 
    475  /* Skip to the start of the signature block */
    476  *mar_position = 0;
    477  if (mar_buffer_seek(mar, mar_position, SIGNATURE_BLOCK_OFFSET) != 0) {
    478    return -1;
    479  }
    480 
    481  /* Get the number of signatures */
    482  if (mar_read_buffer(mar, &signatureCount, mar_position,
    483                      sizeof(signatureCount)) != 0) {
    484    return -1;
    485  }
    486  signatureCount = ntohl(signatureCount);
    487 
    488  /* Check that we have less than the max amount of signatures so we don't
    489     waste too much of either updater's or signmar's time. */
    490  if (signatureCount > MAX_SIGNATURES) {
    491    return -1;
    492  }
    493 
    494  /* Skip past the whole signature block */
    495  for (i = 0; i < signatureCount; i++) {
    496    /* Skip past the signature algorithm ID */
    497    if (mar_buffer_seek(mar, mar_position, sizeof(uint32_t)) != 0) {
    498      return -1;
    499    }
    500 
    501    /* Read the signature length and skip past the signature */
    502    if (mar_read_buffer(mar, &signatureLen, mar_position, sizeof(uint32_t)) !=
    503        0) {
    504      return -1;
    505    }
    506    signatureLen = ntohl(signatureLen);
    507    if (mar_buffer_seek(mar, mar_position, signatureLen) != 0) {
    508      return -1;
    509    }
    510  }
    511 
    512  if (*mar_position <= (size_t)INT64_MAX &&
    513      (int64_t)mar_position == (int64_t)offsetToContent) {
    514    *hasAdditionalBlocks = 0;
    515  } else {
    516    if (numAdditionalBlocks) {
    517      /* We have an additional block, so read in the number of additional blocks
    518         and set the offset. */
    519      *hasAdditionalBlocks = 1;
    520      if (mar_read_buffer(mar, numAdditionalBlocks, mar_position,
    521                          sizeof(uint32_t)) != 0) {
    522        return -1;
    523      }
    524      *numAdditionalBlocks = ntohl(*numAdditionalBlocks);
    525      if (offsetAdditionalBlocks) {
    526        if (*mar_position > (size_t)UINT32_MAX) {
    527          return -1;
    528        }
    529        *offsetAdditionalBlocks = (uint32_t)*mar_position;
    530      }
    531    } else if (offsetAdditionalBlocks) {
    532      /* numAdditionalBlocks is not specified but offsetAdditionalBlocks
    533         is, so fill it! */
    534      if (mar_buffer_seek(mar, mar_position, sizeof(uint32_t)) != 0) {
    535        return -1;
    536      }
    537      if (*mar_position > (size_t)UINT32_MAX) {
    538        return -1;
    539      }
    540      *offsetAdditionalBlocks = (uint32_t)*mar_position;
    541    }
    542  }
    543 
    544  return 0;
    545 }
    546 
    547 /**
    548 * Reads the product info block from the MAR file's additional block section.
    549 * The caller is responsible for freeing the fields in infoBlock
    550 * if the return is successful.
    551 *
    552 * @param infoBlock Out parameter for where to store the result to
    553 * @return 0 on success, -1 on failure
    554 */
    555 int read_product_info_block(char* path,
    556                            struct ProductInformationBlock* infoBlock) {
    557  int rv;
    558  MarFile* mar;
    559  MarReadResult result = mar_open(path, &mar);
    560  if (result != MAR_READ_SUCCESS) {
    561    fprintf(stderr,
    562            "ERROR: could not open file in read_product_info_block()\n");
    563    return -1;
    564  }
    565  rv = mar_read_product_info_block(mar, infoBlock);
    566  mar_close(mar);
    567  return rv;
    568 }
    569 
    570 /**
    571 * Reads the product info block from the MAR file's additional block section.
    572 * The caller is responsible for freeing the fields in infoBlock
    573 * if the return is successful.
    574 *
    575 * @param infoBlock Out parameter for where to store the result to
    576 * @return 0 on success, -1 on failure
    577 */
    578 int mar_read_product_info_block(MarFile* mar,
    579                                struct ProductInformationBlock* infoBlock) {
    580  uint32_t offsetAdditionalBlocks, numAdditionalBlocks, additionalBlockSize,
    581      additionalBlockID;
    582  int hasAdditionalBlocks;
    583  size_t mar_position = 0;
    584 
    585  /* The buffer size is 97 bytes because the MAR channel name < 64 bytes, and
    586     product version < 32 bytes + 3 NULL terminator bytes. */
    587  char buf[MAXADDITIONALBLOCKSIZE + 1] = {'\0'};
    588  if (get_open_mar_file_info(mar, &mar_position, NULL, NULL,
    589                             &hasAdditionalBlocks, &offsetAdditionalBlocks,
    590                             &numAdditionalBlocks) != 0) {
    591    return -1;
    592  }
    593 
    594  /* We only have the one additional block type and only one is expected to be
    595     in a MAR file so check if any exist and process the first found */
    596  if (numAdditionalBlocks > 0) {
    597    /* Read the additional block size */
    598    if (mar_read_buffer(mar, &additionalBlockSize, &mar_position,
    599                        sizeof(additionalBlockSize)) != 0) {
    600      return -1;
    601    }
    602    additionalBlockSize = ntohl(additionalBlockSize) -
    603                          sizeof(additionalBlockSize) -
    604                          sizeof(additionalBlockID);
    605 
    606    /* Additional Block sizes should only be 96 bytes long */
    607    if (additionalBlockSize > MAXADDITIONALBLOCKSIZE) {
    608      return -1;
    609    }
    610 
    611    /* Read the additional block ID */
    612    if (mar_read_buffer(mar, &additionalBlockID, &mar_position,
    613                        sizeof(additionalBlockID)) != 0) {
    614      return -1;
    615    }
    616    additionalBlockID = ntohl(additionalBlockID);
    617 
    618    if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
    619      const char* location;
    620      int len;
    621 
    622      if (mar_read_buffer(mar, buf, &mar_position, additionalBlockSize) != 0) {
    623        return -1;
    624      }
    625 
    626      /* Extract the MAR channel name from the buffer.  For now we
    627         point to the stack allocated buffer but we strdup this
    628         if we are within bounds of each field's max length. */
    629      location = buf;
    630      len = strlen(location);
    631      infoBlock->MARChannelID = location;
    632      location += len + 1;
    633      if (len >= 64) {
    634        infoBlock->MARChannelID = NULL;
    635        return -1;
    636      }
    637 
    638      /* Extract the version from the buffer */
    639      len = strlen(location);
    640      infoBlock->productVersion = location;
    641      if (len >= 32) {
    642        infoBlock->MARChannelID = NULL;
    643        infoBlock->productVersion = NULL;
    644        return -1;
    645      }
    646      infoBlock->MARChannelID = strdup(infoBlock->MARChannelID);
    647      infoBlock->productVersion = strdup(infoBlock->productVersion);
    648      return 0;
    649    } else {
    650      /* This is not the additional block you're looking for. Move along. */
    651      if (mar_buffer_seek(mar, &mar_position, additionalBlockSize) != 0) {
    652        return -1;
    653      }
    654    }
    655  }
    656 
    657  /* If we had a product info block we would have already returned */
    658  return -1;
    659 }
    660 
    661 const MarItem* mar_find_item(MarFile* mar, const char* name) {
    662  uint32_t hash;
    663  const MarItem* item;
    664 
    665  if (!mar->item_table_is_valid) {
    666    if (mar_read_index(mar)) {
    667      return NULL;
    668    } else {
    669      mar->item_table_is_valid = 1;
    670    }
    671  }
    672 
    673  hash = mar_hash_name(name);
    674 
    675  item = mar->item_table[hash];
    676  while (item && strcmp(item->name, name) != 0) {
    677    item = item->next;
    678  }
    679 
    680  /* If this is the first time seeing this item's indexes, return it */
    681  if (mar_insert_offset(mar, item->offset, item->length) == 1) {
    682    return item;
    683  } else {
    684    fprintf(stderr, "ERROR: file content collision in mar_find_item()\n");
    685    return NULL;
    686  }
    687 }
    688 
    689 int mar_enum_items(MarFile* mar, MarItemCallback callback, void* closure) {
    690  MarItem* item;
    691  int i, rv;
    692 
    693  if (!mar->item_table_is_valid) {
    694    if (mar_read_index(mar)) {
    695      return -1;
    696    } else {
    697      mar->item_table_is_valid = 1;
    698    }
    699  }
    700 
    701  for (i = 0; i < TABLESIZE; ++i) {
    702    item = mar->item_table[i];
    703    while (item) {
    704      /* if this is the first time seeing this item's indexes, process it */
    705      if (mar_insert_offset(mar, item->offset, item->length) == 1) {
    706        rv = callback(mar, item, closure);
    707        if (rv) {
    708          return rv;
    709        }
    710      } else {
    711        fprintf(stderr, "ERROR: file content collision in mar_enum_items()\n");
    712        return 1;
    713      }
    714      item = item->next;
    715    }
    716  }
    717 
    718  return 0;
    719 }
    720 
    721 int mar_read(MarFile* mar, const MarItem* item, int offset, uint8_t* buf,
    722             int bufsize) {
    723  int nr;
    724  size_t mar_position = 0;
    725 
    726  if (offset == (int)item->length) {
    727    return 0;
    728  }
    729  if (offset > (int)item->length) {
    730    return -1;
    731  }
    732 
    733  nr = item->length - offset;
    734  if (nr > bufsize) {
    735    nr = bufsize;
    736  }
    737 
    738  // Avoid adding item->offset and offset directly, just in case of overflow.
    739  if (mar_buffer_seek(mar, &mar_position, item->offset)) {
    740    return -1;
    741  }
    742  if (mar_buffer_seek(mar, &mar_position, offset)) {
    743    return -1;
    744  }
    745 
    746  return mar_read_buffer_max(mar, buf, &mar_position, nr);
    747 }
    748 
    749 /**
    750 * Determines the MAR file information.
    751 *
    752 * @param path                   The path of the MAR file to check.
    753 * @param hasSignatureBlock      Optional out parameter specifying if the MAR
    754 *                               file has a signature block or not.
    755 * @param numSignatures          Optional out parameter for storing the number
    756 *                               of signatures in the MAR file.
    757 * @param hasAdditionalBlocks    Optional out parameter specifying if the MAR
    758 *                               file has additional blocks or not.
    759 * @param offsetAdditionalBlocks Optional out parameter for the offset to the
    760 *                               first additional block. Value is only valid if
    761 *                               hasAdditionalBlocks is not equal to 0.
    762 * @param numAdditionalBlocks    Optional out parameter for the number of
    763 *                               additional blocks.  Value is only valid if
    764 *                               has_additional_blocks is not equal to 0.
    765 * @return 0 on success and non-zero on failure.
    766 */
    767 int get_mar_file_info(const char* path, int* hasSignatureBlock,
    768                      uint32_t* numSignatures, int* hasAdditionalBlocks,
    769                      uint32_t* offsetAdditionalBlocks,
    770                      uint32_t* numAdditionalBlocks) {
    771  int rv;
    772  MarFile* mar;
    773  size_t mar_position = 0;
    774  MarReadResult result = mar_open(path, &mar);
    775  if (result != MAR_READ_SUCCESS) {
    776    fprintf(stderr, "ERROR: could not read file in get_mar_file_info()\n");
    777    return -1;
    778  }
    779 
    780  rv = get_open_mar_file_info(mar, &mar_position, hasSignatureBlock,
    781                              numSignatures, hasAdditionalBlocks,
    782                              offsetAdditionalBlocks, numAdditionalBlocks);
    783 
    784  mar_close(mar);
    785  return rv;
    786 }