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: */
4 * This file is part of Evergreen.
6 * Evergreen is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation, either version 2 of the License,
9 * or (at your option) any later version.
11 * Evergreen is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Evergreen. If not, see <http://www.gnu.org/licenses/>.
19 * This Source Code Form is derived from code that was originally
20 * subject to the terms of the Mozilla Public License, v. 2.0 and
21 * included in Evergreen. You may, therefore, use this Source Code
22 * Form under the terms of the Mozilla Public License 2.0. This
23 * licensing option does not affect the larger Evergreen project, only
24 * the Source Code Forms bearing this exception are affected. If a
25 * copy of the MPL was not distributed with this file, You can obtain
26 * one at http://mozilla.org/MPL/2.0/.
29 #include <sys/types.h>
33 #include "mar_private.h"
39 #include <netinet/in.h>
43 /* this is the same hash algorithm used by nsZipArchive.cpp */
44 static uint32_t mar_hash_name(const char *name) {
48 for (c = (unsigned char *) name; *c; ++c)
51 return val % TABLESIZE;
54 static int mar_insert_item(MarFile *mar, const char *name, int namelen,
55 uint32_t offset, uint32_t length, uint32_t flags) {
59 item = (MarItem *) malloc(sizeof(MarItem) + namelen);
63 item->offset = offset;
64 item->length = length;
66 memcpy(item->name, name, namelen + 1);
68 hash = mar_hash_name(name);
70 root = mar->item_table[hash];
72 mar->item_table[hash] = item;
82 static int mar_consume_index(MarFile *mar, char **buf, const char *buf_end) {
84 * Each item has the following structure:
85 * uint32_t offset (network byte order)
86 * uint32_t length (network byte order)
87 * uint32_t flags (network byte order)
88 * char name[N] (where N >= 1)
97 if ((buf_end - *buf) < (int)(3*sizeof(uint32_t) + 2))
100 memcpy(&offset, *buf, sizeof(offset));
101 *buf += sizeof(offset);
103 memcpy(&length, *buf, sizeof(length));
104 *buf += sizeof(length);
106 memcpy(&flags, *buf, sizeof(flags));
107 *buf += sizeof(flags);
109 offset = ntohl(offset);
110 length = ntohl(length);
111 flags = ntohl(flags);
114 /* find namelen; must take care not to read beyond buf_end */
120 namelen = (*buf - name);
121 /* consume null byte */
126 return mar_insert_item(mar, name, namelen, offset, length, flags);
129 static int mar_read_index(MarFile *mar) {
130 char id[MAR_ID_SIZE], *buf, *bufptr, *bufend;
131 uint32_t offset_to_index, size_of_index;
134 if (fread(id, MAR_ID_SIZE, 1, mar->fp) != 1)
136 if (memcmp(id, MAR_ID, MAR_ID_SIZE) != 0)
139 if (fread(&offset_to_index, sizeof(uint32_t), 1, mar->fp) != 1)
141 offset_to_index = ntohl(offset_to_index);
143 if (fseek(mar->fp, offset_to_index, SEEK_SET))
145 if (fread(&size_of_index, sizeof(uint32_t), 1, mar->fp) != 1)
147 size_of_index = ntohl(size_of_index);
149 buf = (char *) malloc(size_of_index);
152 if (fread(buf, size_of_index, 1, mar->fp) != 1) {
158 bufend = buf + size_of_index;
159 while (bufptr < bufend && mar_consume_index(mar, &bufptr, bufend) == 0);
162 return (bufptr == bufend) ? 0 : -1;
166 * Internal shared code for mar_open and mar_wopen.
167 * On failure, will fclose(fp).
169 static MarFile *mar_fpopen(FILE *fp)
173 mar = (MarFile *) malloc(sizeof(*mar));
180 memset(mar->item_table, 0, sizeof(mar->item_table));
181 if (mar_read_index(mar)) {
189 MarFile *mar_open(const char *path) {
192 fp = fopen(path, "rb");
196 return mar_fpopen(fp);
200 MarFile *mar_wopen(const PRUnichar *path) {
203 fp = _wfopen(path, L"rb");
207 return mar_fpopen(fp);
211 void mar_close(MarFile *mar) {
217 for (i = 0; i < TABLESIZE; ++i) {
218 item = mar->item_table[i];
220 MarItem *temp = item;
230 * Determines the MAR file information.
232 * @param fp An opened MAR file in read mode.
233 * @param hasSignatureBlock Optional out parameter specifying if the MAR
234 * file has a signature block or not.
235 * @param numSignatures Optional out parameter for storing the number
236 * of signatures in the MAR file.
237 * @param hasAdditionalBlocks Optional out parameter specifying if the MAR
238 * file has additional blocks or not.
239 * @param offsetAdditionalBlocks Optional out parameter for the offset to the
240 * first additional block. Value is only valid if
241 * hasAdditionalBlocks is not equal to 0.
242 * @param numAdditionalBlocks Optional out parameter for the number of
243 * additional blocks. Value is only valid if
244 * hasAdditionalBlocks is not equal to 0.
245 * @return 0 on success and non-zero on failure.
247 int get_mar_file_info_fp(FILE *fp,
248 int *hasSignatureBlock,
250 int *hasAdditionalBlocks,
251 int *offsetAdditionalBlocks,
252 int *numAdditionalBlocks)
254 uint32_t offsetToIndex, offsetToContent, signatureCount, signatureLen, i;
256 /* One of hasSignatureBlock or hasAdditionalBlocks must be non NULL */
257 if (!hasSignatureBlock && !hasAdditionalBlocks) {
262 /* Skip to the start of the offset index */
263 if (fseek(fp, MAR_ID_SIZE, SEEK_SET)) {
267 /* Read the offset to the index. */
268 if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fp) != 1) {
271 offsetToIndex = ntohl(offsetToIndex);
274 /* Skip past the MAR file size field */
275 if (fseek(fp, sizeof(uint64_t), SEEK_CUR)) {
279 /* Read the number of signatures field */
280 if (fread(numSignatures, sizeof(*numSignatures), 1, fp) != 1) {
283 *numSignatures = ntohl(*numSignatures);
286 /* Skip to the first index entry past the index size field
287 We do it in 2 calls because offsetToIndex + sizeof(uint32_t)
288 could oerflow in theory. */
289 if (fseek(fp, offsetToIndex, SEEK_SET)) {
293 if (fseek(fp, sizeof(uint32_t), SEEK_CUR)) {
297 /* Read the first offset to content field. */
298 if (fread(&offsetToContent, sizeof(offsetToContent), 1, fp) != 1) {
301 offsetToContent = ntohl(offsetToContent);
303 /* Check if we have a new or old MAR file */
304 if (hasSignatureBlock) {
305 if (offsetToContent == MAR_ID_SIZE + sizeof(uint32_t)) {
306 *hasSignatureBlock = 0;
308 *hasSignatureBlock = 1;
312 /* If the caller doesn't care about the product info block
313 value, then just return */
314 if (!hasAdditionalBlocks) {
318 /* Skip to the start of the signature block */
319 if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
323 /* Get the number of signatures */
324 if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) {
327 signatureCount = ntohl(signatureCount);
329 /* Check that we have less than the max amount of signatures so we don't
330 waste too much of either updater's or signmar's time. */
331 if (signatureCount > MAX_SIGNATURES) {
335 /* Skip past the whole signature block */
336 for (i = 0; i < signatureCount; i++) {
337 /* Skip past the signature algorithm ID */
338 if (fseek(fp, sizeof(uint32_t), SEEK_CUR)) {
342 /* Read the signature length and skip past the signature */
343 if (fread(&signatureLen, sizeof(uint32_t), 1, fp) != 1) {
346 signatureLen = ntohl(signatureLen);
347 if (fseek(fp, signatureLen, SEEK_CUR)) {
352 if (ftell(fp) == offsetToContent) {
353 *hasAdditionalBlocks = 0;
355 if (numAdditionalBlocks) {
356 /* We have an additional block, so read in the number of additional blocks
357 and set the offset. */
358 *hasAdditionalBlocks = 1;
359 if (fread(numAdditionalBlocks, sizeof(uint32_t), 1, fp) != 1) {
362 *numAdditionalBlocks = ntohl(*numAdditionalBlocks);
363 if (offsetAdditionalBlocks) {
364 *offsetAdditionalBlocks = ftell(fp);
366 } else if (offsetAdditionalBlocks) {
367 /* numAdditionalBlocks is not specified but offsetAdditionalBlocks
369 *offsetAdditionalBlocks = ftell(fp) + sizeof(uint32_t);
377 * Reads the product info block from the MAR file's additional block section.
378 * The caller is responsible for freeing the fields in infoBlock
379 * if the return is successful.
381 * @param infoBlock Out parameter for where to store the result to
382 * @return 0 on success, -1 on failure
385 read_product_info_block(char *path,
386 struct ProductInformationBlock *infoBlock)
390 mar.fp = fopen(path, "rb");
394 rv = mar_read_product_info_block(&mar, infoBlock);
400 * Reads the product info block from the MAR file's additional block section.
401 * The caller is responsible for freeing the fields in infoBlock
402 * if the return is successful.
404 * @param infoBlock Out parameter for where to store the result to
405 * @return 0 on success, -1 on failure
408 mar_read_product_info_block(MarFile *mar,
409 struct ProductInformationBlock *infoBlock)
411 int i, hasAdditionalBlocks, offset,
412 offsetAdditionalBlocks, numAdditionalBlocks,
413 additionalBlockSize, additionalBlockID;
414 /* The buffer size is 97 bytes because the MAR channel name < 64 bytes, and
415 product version < 32 bytes + 3 NULL terminator bytes. */
416 char buf[97] = { '\0' };
417 int ret = get_mar_file_info_fp(mar->fp, NULL, NULL,
418 &hasAdditionalBlocks,
419 &offsetAdditionalBlocks,
420 &numAdditionalBlocks);
421 for (i = 0; i < numAdditionalBlocks; ++i) {
422 /* Read the additional block size */
423 if (fread(&additionalBlockSize,
424 sizeof(additionalBlockSize),
428 additionalBlockSize = ntohl(additionalBlockSize) -
429 sizeof(additionalBlockSize) -
430 sizeof(additionalBlockID);
432 /* Read the additional block ID */
433 if (fread(&additionalBlockID,
434 sizeof(additionalBlockID),
438 additionalBlockID = ntohl(additionalBlockID);
440 if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
441 const char *location;
444 /* This block must be at most 104 bytes.
445 MAR channel name < 64 bytes, and product version < 32 bytes + 3 NULL
446 terminator bytes. We only check for 96 though because we remove 8
447 bytes above from the additionalBlockSize: We subtract
448 sizeof(additionalBlockSize) and sizeof(additionalBlockID) */
449 if (additionalBlockSize > 96) {
453 if (fread(buf, additionalBlockSize, 1, mar->fp) != 1) {
457 /* Extract the MAR channel name from the buffer. For now we
458 point to the stack allocated buffer but we strdup this
459 if we are within bounds of each field's max length. */
461 len = strlen(location);
462 infoBlock->MARChannelID = location;
465 infoBlock->MARChannelID = NULL;
469 /* Extract the version from the buffer */
470 len = strlen(location);
471 infoBlock->productVersion = location;
474 infoBlock->MARChannelID = NULL;
475 infoBlock->productVersion = NULL;
478 infoBlock->MARChannelID =
479 strdup(infoBlock->MARChannelID);
480 infoBlock->productVersion =
481 strdup(infoBlock->productVersion);
484 /* This is not the additional block you're looking for. Move along. */
485 if (fseek(mar->fp, additionalBlockSize, SEEK_CUR)) {
491 /* If we had a product info block we would have already returned */
495 const MarItem *mar_find_item(MarFile *mar, const char *name) {
499 hash = mar_hash_name(name);
501 item = mar->item_table[hash];
502 while (item && strcmp(item->name, name) != 0)
508 int mar_enum_items(MarFile *mar, MarItemCallback callback, void *closure) {
512 for (i = 0; i < TABLESIZE; ++i) {
513 item = mar->item_table[i];
515 int rv = callback(mar, item, closure);
525 int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf,
529 if (offset == (int) item->length)
531 if (offset > (int) item->length)
534 nr = item->length - offset;
538 if (fseek(mar->fp, item->offset + offset, SEEK_SET))
541 return fread(buf, 1, nr, mar->fp);
545 * Determines the MAR file information.
547 * @param path The path of the MAR file to check.
548 * @param hasSignatureBlock Optional out parameter specifying if the MAR
549 * file has a signature block or not.
550 * @param numSignatures Optional out parameter for storing the number
551 * of signatures in the MAR file.
552 * @param hasAdditionalBlocks Optional out parameter specifying if the MAR
553 * file has additional blocks or not.
554 * @param offsetAdditionalBlocks Optional out parameter for the offset to the
555 * first additional block. Value is only valid if
556 * hasAdditionalBlocks is not equal to 0.
557 * @param numAdditionalBlocks Optional out parameter for the number of
558 * additional blocks. Value is only valid if
559 * has_additional_blocks is not equal to 0.
560 * @return 0 on success and non-zero on failure.
562 int get_mar_file_info(const char *path,
563 int *hasSignatureBlock,
565 int *hasAdditionalBlocks,
566 int *offsetAdditionalBlocks,
567 int *numAdditionalBlocks)
570 FILE *fp = fopen(path, "rb");
575 rv = get_mar_file_info_fp(fp, hasSignatureBlock,
576 numSignatures, hasAdditionalBlocks,
577 offsetAdditionalBlocks, numAdditionalBlocks);