modules/libmar/src/mar_read.c

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:a20adfa9a9ed
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 "mar_private.h"
12 #include "mar.h"
13
14 #ifdef XP_WIN
15 #include <winsock2.h>
16 #else
17 #include <netinet/in.h>
18 #endif
19
20
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;
25
26 for (c = (unsigned char *) name; *c; ++c)
27 val = val*37 + *c;
28
29 return val % TABLESIZE;
30 }
31
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;
36
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);
45
46 hash = mar_hash_name(name);
47
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 }
59
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;
74
75 if ((buf_end - *buf) < (int)(3*sizeof(uint32_t) + 2))
76 return -1;
77
78 memcpy(&offset, *buf, sizeof(offset));
79 *buf += sizeof(offset);
80
81 memcpy(&length, *buf, sizeof(length));
82 *buf += sizeof(length);
83
84 memcpy(&flags, *buf, sizeof(flags));
85 *buf += sizeof(flags);
86
87 offset = ntohl(offset);
88 length = ntohl(length);
89 flags = ntohl(flags);
90
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);
103
104 return mar_insert_item(mar, name, namelen, offset, length, flags);
105 }
106
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;
110
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;
116
117 if (fread(&offset_to_index, sizeof(uint32_t), 1, mar->fp) != 1)
118 return -1;
119 offset_to_index = ntohl(offset_to_index);
120
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);
126
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 }
134
135 bufptr = buf;
136 bufend = buf + size_of_index;
137 while (bufptr < bufend && mar_consume_index(mar, &bufptr, bufend) == 0);
138
139 free(buf);
140 return (bufptr == bufend) ? 0 : -1;
141 }
142
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;
150
151 mar = (MarFile *) malloc(sizeof(*mar));
152 if (!mar) {
153 fclose(fp);
154 return NULL;
155 }
156
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 }
163
164 return mar;
165 }
166
167 MarFile *mar_open(const char *path) {
168 FILE *fp;
169
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 }
176
177 return mar_fpopen(fp);
178 }
179
180 #ifdef XP_WIN
181 MarFile *mar_wopen(const wchar_t *path) {
182 FILE *fp;
183
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 }
190
191 return mar_fpopen(fp);
192 }
193 #endif
194
195 void mar_close(MarFile *mar) {
196 MarItem *item;
197 int i;
198
199 fclose(mar->fp);
200
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 }
209
210 free(mar);
211 }
212
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;
239
240 /* One of hasSignatureBlock or hasAdditionalBlocks must be non NULL */
241 if (!hasSignatureBlock && !hasAdditionalBlocks) {
242 return -1;
243 }
244
245
246 /* Skip to the start of the offset index */
247 if (fseek(fp, MAR_ID_SIZE, SEEK_SET)) {
248 return -1;
249 }
250
251 /* Read the offset to the index. */
252 if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fp) != 1) {
253 return -1;
254 }
255 offsetToIndex = ntohl(offsetToIndex);
256
257 if (numSignatures) {
258 /* Skip past the MAR file size field */
259 if (fseek(fp, sizeof(uint64_t), SEEK_CUR)) {
260 return -1;
261 }
262
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 }
269
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 }
276
277 if (fseek(fp, sizeof(uint32_t), SEEK_CUR)) {
278 return -1;
279 }
280
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);
286
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 }
295
296 /* If the caller doesn't care about the product info block
297 value, then just return */
298 if (!hasAdditionalBlocks) {
299 return 0;
300 }
301
302 /* Skip to the start of the signature block */
303 if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
304 return -1;
305 }
306
307 /* Get the number of signatures */
308 if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) {
309 return -1;
310 }
311 signatureCount = ntohl(signatureCount);
312
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 }
318
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 }
325
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 }
335
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 }
356
357 return 0;
358 }
359
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 }
384
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;
400
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);
418
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);
426
427 if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
428 const char *location;
429 int len;
430
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 }
439
440 if (fread(buf, additionalBlockSize, 1, mar->fp) != 1) {
441 return -1;
442 }
443
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 }
455
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 }
477
478 /* If we had a product info block we would have already returned */
479 return -1;
480 }
481
482 const MarItem *mar_find_item(MarFile *mar, const char *name) {
483 uint32_t hash;
484 const MarItem *item;
485
486 hash = mar_hash_name(name);
487
488 item = mar->item_table[hash];
489 while (item && strcmp(item->name, name) != 0)
490 item = item->next;
491
492 return item;
493 }
494
495 int mar_enum_items(MarFile *mar, MarItemCallback callback, void *closure) {
496 MarItem *item;
497 int i;
498
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 }
508
509 return 0;
510 }
511
512 int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf,
513 int bufsize) {
514 int nr;
515
516 if (offset == (int) item->length)
517 return 0;
518 if (offset > (int) item->length)
519 return -1;
520
521 nr = item->length - offset;
522 if (nr > bufsize)
523 nr = bufsize;
524
525 if (fseek(mar->fp, item->offset + offset, SEEK_SET))
526 return -1;
527
528 return fread(buf, 1, nr, mar->fp);
529 }
530
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 }
563
564 rv = get_mar_file_info_fp(fp, hasSignatureBlock,
565 numSignatures, hasAdditionalBlocks,
566 offsetAdditionalBlocks, numAdditionalBlocks);
567
568 fclose(fp);
569 return rv;
570 }

mercurial