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/. */
11 #include <onlineupdate/mar_private.h>
12 #include <onlineupdate/mar.h>
17 #include <netinet/in.h>
21 /* this is the same hash algorithm used by nsZipArchive.cpp */
22 static uint32_t mar_hash_name(const char *name
) {
26 for (c
= (unsigned char *) name
; *c
; ++c
)
29 return val
% TABLESIZE
;
32 static int mar_insert_item(MarFile
*mar
, const char *name
, int namelen
,
33 uint32_t offset
, uint32_t length
, uint32_t flags
) {
37 item
= (MarItem
*) malloc(sizeof(MarItem
) + namelen
);
41 item
->offset
= offset
;
42 item
->length
= length
;
44 memcpy(item
->name
, name
, namelen
+ 1);
46 hash
= mar_hash_name(name
);
48 root
= mar
->item_table
[hash
];
50 mar
->item_table
[hash
] = item
;
60 static int mar_consume_index(MarFile
*mar
, char **buf
, const char *buf_end
) {
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)
75 if ((buf_end
- *buf
) < (int)(3*sizeof(uint32_t) + 2))
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
);
92 /* find namelen; must take care not to read beyond buf_end */
98 namelen
= (*buf
- name
);
99 /* consume null byte */
104 return mar_insert_item(mar
, name
, namelen
, offset
, length
, flags
);
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
;
112 if (fread(id
, MAR_ID_SIZE
, 1, mar
->fp
) != 1)
114 if (memcmp(id
, MAR_ID
, MAR_ID_SIZE
) != 0)
117 if (fread(&offset_to_index
, sizeof(uint32_t), 1, mar
->fp
) != 1)
119 offset_to_index
= ntohl(offset_to_index
);
121 if (fseek(mar
->fp
, offset_to_index
, SEEK_SET
))
123 if (fread(&size_of_index
, sizeof(uint32_t), 1, mar
->fp
) != 1)
125 size_of_index
= ntohl(size_of_index
);
127 buf
= (char *) malloc(size_of_index
);
130 if (fread(buf
, size_of_index
, 1, mar
->fp
) != 1) {
136 bufend
= buf
+ size_of_index
;
137 while (bufptr
< bufend
&& mar_consume_index(mar
, &bufptr
, bufend
) == 0);
140 return (bufptr
== bufend
) ? 0 : -1;
144 * Internal shared code for mar_open and mar_wopen.
145 * On failure, will fclose(fp).
147 static MarFile
*mar_fpopen(FILE *fp
)
151 mar
= (MarFile
*) malloc(sizeof(*mar
));
158 memset(mar
->item_table
, 0, sizeof(mar
->item_table
));
159 if (mar_read_index(mar
)) {
167 MarFile
*mar_open(const char *path
) {
170 fp
= fopen(path
, "rb");
172 fprintf(stderr
, "ERROR: could not open file in mar_open()\n");
177 return mar_fpopen(fp
);
181 MarFile
*mar_wopen(const wchar_t *path
) {
184 _wfopen_s(&fp
, path
, L
"rb");
186 fprintf(stderr
, "ERROR: could not open file in mar_wopen()\n");
191 return mar_fpopen(fp
);
195 void mar_close(MarFile
*mar
) {
201 for (i
= 0; i
< TABLESIZE
; ++i
) {
202 item
= mar
->item_table
[i
];
204 MarItem
*temp
= item
;
214 * Determines the MAR file information.
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.
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
)
238 uint32_t offsetToIndex
, offsetToContent
, signatureCount
, signatureLen
, i
;
240 /* One of hasSignatureBlock or hasAdditionalBlocks must be non NULL */
241 if (!hasSignatureBlock
&& !hasAdditionalBlocks
) {
246 /* Skip to the start of the offset index */
247 if (fseek(fp
, MAR_ID_SIZE
, SEEK_SET
)) {
251 /* Read the offset to the index. */
252 if (fread(&offsetToIndex
, sizeof(offsetToIndex
), 1, fp
) != 1) {
255 offsetToIndex
= ntohl(offsetToIndex
);
258 /* Skip past the MAR file size field */
259 if (fseek(fp
, sizeof(uint64_t), SEEK_CUR
)) {
263 /* Read the number of signatures field */
264 if (fread(numSignatures
, sizeof(*numSignatures
), 1, fp
) != 1) {
267 *numSignatures
= ntohl(*numSignatures
);
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
)) {
277 if (fseek(fp
, sizeof(uint32_t), SEEK_CUR
)) {
281 /* Read the first offset to content field. */
282 if (fread(&offsetToContent
, sizeof(offsetToContent
), 1, fp
) != 1) {
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;
292 *hasSignatureBlock
= 1;
296 /* If the caller doesn't care about the product info block
297 value, then just return */
298 if (!hasAdditionalBlocks
) {
302 /* Skip to the start of the signature block */
303 if (fseeko(fp
, SIGNATURE_BLOCK_OFFSET
, SEEK_SET
)) {
307 /* Get the number of signatures */
308 if (fread(&signatureCount
, sizeof(signatureCount
), 1, fp
) != 1) {
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
) {
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
)) {
326 /* Read the signature length and skip past the signature */
327 if (fread(&signatureLen
, sizeof(uint32_t), 1, fp
) != 1) {
330 signatureLen
= ntohl(signatureLen
);
331 if (fseek(fp
, signatureLen
, SEEK_CUR
)) {
336 if (ftell(fp
) == (long)offsetToContent
) {
337 *hasAdditionalBlocks
= 0;
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) {
346 *numAdditionalBlocks
= ntohl(*numAdditionalBlocks
);
347 if (offsetAdditionalBlocks
) {
348 *offsetAdditionalBlocks
= ftell(fp
);
350 } else if (offsetAdditionalBlocks
) {
351 /* numAdditionalBlocks is not specified but offsetAdditionalBlocks
353 *offsetAdditionalBlocks
= ftell(fp
) + sizeof(uint32_t);
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.
365 * @param infoBlock Out parameter for where to store the result to
366 * @return 0 on success, -1 on failure
369 read_product_info_block(char *path
,
370 struct ProductInformationBlock
*infoBlock
)
374 mar
.fp
= fopen(path
, "rb");
376 fprintf(stderr
, "ERROR: could not open file in read_product_info_block()\n");
380 rv
= mar_read_product_info_block(&mar
, infoBlock
);
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.
390 * @param infoBlock Out parameter for where to store the result to
391 * @return 0 on success, -1 on failure
394 mar_read_product_info_block(MarFile
*mar
,
395 struct ProductInformationBlock
*infoBlock
)
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
);
410 for (i
= 0; i
< numAdditionalBlocks
; ++i
) {
411 /* Read the additional block size */
412 if (fread(&additionalBlockSize
,
413 sizeof(additionalBlockSize
),
417 additionalBlockSize
= ntohl(additionalBlockSize
) -
418 sizeof(additionalBlockSize
) -
419 sizeof(additionalBlockID
);
421 /* Read the additional block ID */
422 if (fread(&additionalBlockID
,
423 sizeof(additionalBlockID
),
427 additionalBlockID
= ntohl(additionalBlockID
);
429 if (PRODUCT_INFO_BLOCK_ID
== additionalBlockID
) {
430 const char *location
;
433 /* This block must be at most 104 bytes.
434 MAR channel name < 64 bytes, and product version < 32 bytes + 3 NULL
435 terminator bytes. We only check for 96 though because we remove 8
436 bytes above from the additionalBlockSize: We subtract
437 sizeof(additionalBlockSize) and sizeof(additionalBlockID) */
438 if (additionalBlockSize
> 96) {
442 if (fread(buf
, additionalBlockSize
, 1, mar
->fp
) != 1) {
446 /* Extract the MAR channel name from the buffer. For now we
447 point to the stack allocated buffer but we strdup this
448 if we are within bounds of each field's max length. */
450 len
= strlen(location
);
451 infoBlock
->MARChannelID
= location
;
454 infoBlock
->MARChannelID
= NULL
;
458 /* Extract the version from the buffer */
459 len
= strlen(location
);
460 infoBlock
->productVersion
= location
;
463 infoBlock
->MARChannelID
= NULL
;
464 infoBlock
->productVersion
= NULL
;
467 infoBlock
->MARChannelID
=
468 strdup(infoBlock
->MARChannelID
);
469 infoBlock
->productVersion
=
470 strdup(infoBlock
->productVersion
);
473 /* This is not the additional block you're looking for. Move along. */
474 if (fseek(mar
->fp
, additionalBlockSize
, SEEK_CUR
)) {
480 /* If we had a product info block we would have already returned */
484 const MarItem
*mar_find_item(MarFile
*mar
, const char *name
) {
488 hash
= mar_hash_name(name
);
490 item
= mar
->item_table
[hash
];
491 while (item
&& strcmp(item
->name
, name
) != 0)
497 int mar_enum_items(MarFile
*mar
, MarItemCallback callback
, void *closure
) {
501 for (i
= 0; i
< TABLESIZE
; ++i
) {
502 item
= mar
->item_table
[i
];
504 int rv
= callback(mar
, item
, closure
);
514 int mar_read(MarFile
*mar
, const MarItem
*item
, int offset
, char *buf
,
518 if (offset
== (int) item
->length
)
520 if (offset
> (int) item
->length
)
523 nr
= item
->length
- offset
;
527 if (fseek(mar
->fp
, item
->offset
+ offset
, SEEK_SET
))
530 return fread(buf
, 1, nr
, mar
->fp
);
534 * Determines the MAR file information.
536 * @param path The path of the MAR file to check.
537 * @param hasSignatureBlock Optional out parameter specifying if the MAR
538 * file has a signature block or not.
539 * @param numSignatures Optional out parameter for storing the number
540 * of signatures in the MAR file.
541 * @param hasAdditionalBlocks Optional out parameter specifying if the MAR
542 * file has additional blocks or not.
543 * @param offsetAdditionalBlocks Optional out parameter for the offset to the
544 * first additional block. Value is only valid if
545 * hasAdditionalBlocks is not equal to 0.
546 * @param numAdditionalBlocks Optional out parameter for the number of
547 * additional blocks. Value is only valid if
548 * has_additional_blocks is not equal to 0.
549 * @return 0 on success and non-zero on failure.
551 int get_mar_file_info(const char *path
,
552 int *hasSignatureBlock
,
553 uint32_t *numSignatures
,
554 int *hasAdditionalBlocks
,
555 uint32_t *offsetAdditionalBlocks
,
556 uint32_t *numAdditionalBlocks
)
559 FILE *fp
= fopen(path
, "rb");
561 fprintf(stderr
, "ERROR: could not open file in get_mar_file_info()\n");
566 rv
= get_mar_file_info_fp(fp
, hasSignatureBlock
,
567 numSignatures
, hasAdditionalBlocks
,
568 offsetAdditionalBlocks
, numAdditionalBlocks
);