Bug 1826682 - Re-Configure() webgpu canvases on resize. r=webgpu-reviewers,nical...
[gecko.git] / modules / libmar / src / mar_read.c
blob679dc258d6ae525a31b3f4dba815e73c182eec6b
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 "city.h"
12 #include "mar_private.h"
13 #include "mar.h"
14 #ifdef XP_WIN
15 # define strdup _strdup
16 #endif
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
25 static uint32_t mar_hash_name(const char* name) {
26 return CityHash64(name, strlen(name)) % TABLESIZE;
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;
34 item = (MarItem*)malloc(sizeof(MarItem) + namelen);
35 if (!item) {
36 return -1;
38 item->next = NULL;
39 item->offset = offset;
40 item->length = length;
41 item->flags = flags;
42 memcpy(item->name, name, namelen + 1);
44 hash = mar_hash_name(name);
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;
54 return 0;
57 static int mar_consume_index(MarFile* mar, char** buf, const char* buf_end) {
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;
66 uint32_t offset;
67 uint32_t length;
68 uint32_t flags;
69 const char* name;
70 int namelen;
72 if ((buf_end - *buf) < (int)(3 * sizeof(uint32_t) + 2)) {
73 return -1;
76 memcpy(&offset, *buf, sizeof(offset));
77 *buf += sizeof(offset);
79 memcpy(&length, *buf, sizeof(length));
80 *buf += sizeof(length);
82 memcpy(&flags, *buf, sizeof(flags));
83 *buf += sizeof(flags);
85 offset = ntohl(offset);
86 length = ntohl(length);
87 flags = ntohl(flags);
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;
96 ++(*buf);
98 namelen = (*buf - name);
99 /* must ensure that namelen is valid */
100 if (namelen < 0) {
101 return -1;
103 /* consume null byte */
104 if (*buf == buf_end) {
105 return -1;
107 ++(*buf);
109 return mar_insert_item(mar, name, namelen, offset, length, flags);
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;
117 /* verify MAR ID */
118 if (mar_read_buffer(mar, id, &mar_position, MAR_ID_SIZE) != 0) {
119 return -1;
121 if (memcmp(id, MAR_ID, MAR_ID_SIZE) != 0) {
122 return -1;
125 if (mar_read_buffer(mar, &offset_to_index, &mar_position, sizeof(uint32_t)) !=
126 0) {
127 return -1;
129 offset_to_index = ntohl(offset_to_index);
131 mar_position = 0;
132 if (mar_buffer_seek(mar, &mar_position, offset_to_index) != 0) {
133 return -1;
135 if (mar_read_buffer(mar, &size_of_index, &mar_position, sizeof(uint32_t)) !=
136 0) {
137 return -1;
139 size_of_index = ntohl(size_of_index);
141 buf = (char*)malloc(size_of_index);
142 if (!buf) {
143 return -1;
145 if (mar_read_buffer(mar, buf, &mar_position, size_of_index) != 0) {
146 free(buf);
147 return -1;
150 bufptr = buf;
151 bufend = buf + size_of_index;
152 while (bufptr < bufend && mar_consume_index(mar, &bufptr, bufend) == 0)
155 free(buf);
156 return (bufptr == bufend) ? 0 : -1;
160 * Adds an offset and length to the MarFile's index_list
161 * @param mar The MarFile that owns this offset length pair
162 * @param offset The byte offset in the archive to be marked as processed
163 * @param length The length corresponding to this byte offset
164 * @return int 1 on success, 0 if offset has been previously processed
165 * -1 if unable to allocate space for the SeenIndexes
167 static int mar_insert_offset(MarFile* mar, uint32_t offset, uint32_t length) {
168 /* Ignore files with no length */
169 if (length == 0) {
170 return 1;
173 SeenIndex* index = (SeenIndex*)malloc(sizeof(SeenIndex));
174 if (!index) {
175 return -1;
177 index->next = NULL;
178 index->offset = offset;
179 index->length = length;
180 uint32_t index_end = index->offset + index->length - 1;
182 /* If this is our first index store it at the front */
183 if (mar->index_list == NULL) {
184 mar->index_list = index;
185 return 1;
188 /* Search for matching indexes in the list of those previously visited */
189 SeenIndex* previous;
190 SeenIndex* current = mar->index_list;
191 while (current != NULL) {
192 uint32_t current_end = current->offset + current->length - 1;
194 /* If index has collided with the front or end of current or if current has
195 collided with the front or end of index return false */
196 if ((index->offset >= current->offset && index->offset <= current_end) ||
197 (index_end >= current->offset && index_end <= current_end) ||
198 (current->offset >= index->offset && current->offset <= index_end) ||
199 (current_end >= index->offset && current_end <= index_end)) {
200 free(index);
201 return 0;
204 /* else move to the next in the list */
205 previous = current;
206 current = current->next;
209 /* These indexes are valid, track them */
210 previous->next = index;
211 return 1;
215 * Internal shared code for mar_open and mar_wopen.
216 * Reads the entire MAR into memory. Fails if it is bigger than
217 * MAX_SIZE_OF_MAR_FILE bytes.
219 static MarReadResult mar_fpopen(FILE* fp, MarFile** out_mar) {
220 *out_mar = NULL;
221 MarFile* mar;
223 mar = (MarFile*)malloc(sizeof(*mar));
224 if (!mar) {
225 return MAR_MEM_ERROR;
228 off_t buffer_size = -1;
229 if (fseeko(fp, 0, SEEK_END) == 0) {
230 buffer_size = ftello(fp);
232 rewind(fp);
233 if (buffer_size < 0) {
234 fprintf(stderr, "Warning: MAR size could not be determined\n");
235 buffer_size = MAX_SIZE_OF_MAR_FILE;
237 if (buffer_size > MAX_SIZE_OF_MAR_FILE) {
238 fprintf(stderr, "ERROR: MAR exceeds maximum size (%lli)\n",
239 (long long int)buffer_size);
240 free(mar);
241 return MAR_FILE_TOO_BIG_ERROR;
244 mar->buffer = malloc(buffer_size);
245 if (!mar->buffer) {
246 fprintf(stderr, "ERROR: MAR buffer could not be allocated\n");
247 free(mar);
248 return MAR_MEM_ERROR;
250 mar->data_len = fread(mar->buffer, 1, buffer_size, fp);
251 if (fgetc(fp) != EOF) {
252 fprintf(stderr, "ERROR: File is larger than buffer (%lli)\n",
253 (long long int)buffer_size);
254 free(mar->buffer);
255 free(mar);
256 return MAR_IO_ERROR;
258 if (ferror(fp)) {
259 fprintf(stderr, "ERROR: Failed to read MAR\n");
260 free(mar->buffer);
261 free(mar);
262 return MAR_IO_ERROR;
265 mar->item_table_is_valid = 0;
266 memset(mar->item_table, 0, sizeof(mar->item_table));
267 mar->index_list = NULL;
269 *out_mar = mar;
270 return MAR_READ_SUCCESS;
273 MarReadResult mar_open(const char* path, MarFile** out_mar) {
274 *out_mar = NULL;
276 FILE* fp;
278 fp = fopen(path, "rb");
279 if (!fp) {
280 fprintf(stderr, "ERROR: could not open file in mar_open()\n");
281 perror(path);
282 return MAR_IO_ERROR;
285 MarReadResult result = mar_fpopen(fp, out_mar);
286 fclose(fp);
287 return result;
290 #ifdef XP_WIN
291 MarReadResult mar_wopen(const wchar_t* path, MarFile** out_mar) {
292 *out_mar = NULL;
294 FILE* fp;
296 _wfopen_s(&fp, path, L"rb");
297 if (!fp) {
298 fprintf(stderr, "ERROR: could not open file in mar_wopen()\n");
299 _wperror(path);
300 return MAR_IO_ERROR;
303 MarReadResult result = mar_fpopen(fp, out_mar);
304 fclose(fp);
305 return result;
307 #endif
309 void mar_close(MarFile* mar) {
310 MarItem* item;
311 SeenIndex* index;
312 int i;
314 free(mar->buffer);
316 for (i = 0; i < TABLESIZE; ++i) {
317 item = mar->item_table[i];
318 while (item) {
319 MarItem* temp = item;
320 item = item->next;
321 free(temp);
325 while (mar->index_list != NULL) {
326 index = mar->index_list;
327 mar->index_list = index->next;
328 free(index);
331 free(mar);
334 int mar_read_buffer(MarFile* mar, void* dest, size_t* position, size_t size) {
335 // size may be provided by the MAR, which we may not have finished validating
336 // the signature on yet. Make sure not to trust it in a way that could
337 // cause an overflow.
338 if (size > mar->data_len) {
339 return -1;
341 if (*position > mar->data_len - size) {
342 return -1;
344 memcpy(dest, mar->buffer + *position, size);
345 *position += size;
346 return 0;
349 int mar_read_buffer_max(MarFile* mar, void* dest, size_t* position,
350 size_t size) {
351 // size may be provided by the MAR, which we may not have finished validating
352 // the signature on yet. Make sure not to trust it in a way that could
353 // cause an overflow.
354 if (mar->data_len <= *position) {
355 return 0;
357 size_t read_count = mar->data_len - *position;
358 if (read_count > size) {
359 read_count = size;
361 memcpy(dest, mar->buffer + *position, read_count);
362 *position += read_count;
363 return read_count;
366 int mar_buffer_seek(MarFile* mar, size_t* position, size_t distance) {
367 // distance may be provided by the MAR, which we may not have finished
368 // validating the signature on yet. Make sure not to trust it in a way that
369 // could cause an overflow.
370 if (distance > mar->data_len) {
371 return -1;
373 if (*position > mar->data_len - distance) {
374 return -1;
376 *position += distance;
377 return 0;
381 * Determines the MAR file information.
383 * @param mar An open MAR file.
384 * @param mar_position The current position in the MAR.
385 * Its value will be updated to the current
386 * position in the MAR after the function exits.
387 * Since its initial value will never actually be
388 * used, this is effectively an outparam.
389 * @param hasSignatureBlock Optional out parameter specifying if the MAR
390 * file has a signature block or not.
391 * @param numSignatures Optional out parameter for storing the number
392 * of signatures in the MAR file.
393 * @param hasAdditionalBlocks Optional out parameter specifying if the MAR
394 * file has additional blocks or not.
395 * @param offsetAdditionalBlocks Optional out parameter for the offset to the
396 * first additional block. Value is only valid if
397 * hasAdditionalBlocks is not equal to 0.
398 * @param numAdditionalBlocks Optional out parameter for the number of
399 * additional blocks. Value is only valid if
400 * hasAdditionalBlocks is not equal to 0.
401 * @return 0 on success and non-zero on failure.
403 int get_open_mar_file_info(MarFile* mar, size_t* mar_position,
404 int* hasSignatureBlock, uint32_t* numSignatures,
405 int* hasAdditionalBlocks,
406 uint32_t* offsetAdditionalBlocks,
407 uint32_t* numAdditionalBlocks) {
408 uint32_t offsetToIndex, offsetToContent, signatureCount, signatureLen, i;
410 /* One of hasSignatureBlock or hasAdditionalBlocks must be non NULL */
411 if (!hasSignatureBlock && !hasAdditionalBlocks) {
412 return -1;
415 /* Skip to the start of the offset index */
416 *mar_position = 0;
417 if (mar_buffer_seek(mar, mar_position, MAR_ID_SIZE) != 0) {
418 return -1;
421 /* Read the offset to the index. */
422 if (mar_read_buffer(mar, &offsetToIndex, mar_position,
423 sizeof(offsetToIndex)) != 0) {
424 return -1;
426 offsetToIndex = ntohl(offsetToIndex);
428 if (numSignatures) {
429 /* Skip past the MAR file size field */
430 if (mar_buffer_seek(mar, mar_position, sizeof(uint64_t)) != 0) {
431 return -1;
434 /* Read the number of signatures field */
435 if (mar_read_buffer(mar, numSignatures, mar_position,
436 sizeof(*numSignatures)) != 0) {
437 return -1;
439 *numSignatures = ntohl(*numSignatures);
442 /* Skip to the first index entry past the index size field
443 We do it in 2 calls because offsetToIndex + sizeof(uint32_t)
444 could overflow in theory. */
445 *mar_position = 0;
446 if (mar_buffer_seek(mar, mar_position, offsetToIndex) != 0) {
447 return -1;
450 if (mar_buffer_seek(mar, mar_position, sizeof(uint32_t)) != 0) {
451 return -1;
454 /* Read the first offset to content field. */
455 if (mar_read_buffer(mar, &offsetToContent, mar_position,
456 sizeof(offsetToContent)) != 0) {
457 return -1;
459 offsetToContent = ntohl(offsetToContent);
461 /* Check if we have a new or old MAR file */
462 if (hasSignatureBlock) {
463 if (offsetToContent == MAR_ID_SIZE + sizeof(uint32_t)) {
464 *hasSignatureBlock = 0;
465 } else {
466 *hasSignatureBlock = 1;
470 /* If the caller doesn't care about the product info block
471 value, then just return */
472 if (!hasAdditionalBlocks) {
473 return 0;
476 /* Skip to the start of the signature block */
477 *mar_position = 0;
478 if (mar_buffer_seek(mar, mar_position, SIGNATURE_BLOCK_OFFSET) != 0) {
479 return -1;
482 /* Get the number of signatures */
483 if (mar_read_buffer(mar, &signatureCount, mar_position,
484 sizeof(signatureCount)) != 0) {
485 return -1;
487 signatureCount = ntohl(signatureCount);
489 /* Check that we have less than the max amount of signatures so we don't
490 waste too much of either updater's or signmar's time. */
491 if (signatureCount > MAX_SIGNATURES) {
492 return -1;
495 /* Skip past the whole signature block */
496 for (i = 0; i < signatureCount; i++) {
497 /* Skip past the signature algorithm ID */
498 if (mar_buffer_seek(mar, mar_position, sizeof(uint32_t)) != 0) {
499 return -1;
502 /* Read the signature length and skip past the signature */
503 if (mar_read_buffer(mar, &signatureLen, mar_position, sizeof(uint32_t)) !=
504 0) {
505 return -1;
507 signatureLen = ntohl(signatureLen);
508 if (mar_buffer_seek(mar, mar_position, signatureLen) != 0) {
509 return -1;
513 if (*mar_position <= (size_t)INT64_MAX &&
514 (int64_t)mar_position == (int64_t)offsetToContent) {
515 *hasAdditionalBlocks = 0;
516 } else {
517 if (numAdditionalBlocks) {
518 /* We have an additional block, so read in the number of additional blocks
519 and set the offset. */
520 *hasAdditionalBlocks = 1;
521 if (mar_read_buffer(mar, numAdditionalBlocks, mar_position,
522 sizeof(uint32_t)) != 0) {
523 return -1;
525 *numAdditionalBlocks = ntohl(*numAdditionalBlocks);
526 if (offsetAdditionalBlocks) {
527 if (*mar_position > (size_t)UINT32_MAX) {
528 return -1;
530 *offsetAdditionalBlocks = (uint32_t)*mar_position;
532 } else if (offsetAdditionalBlocks) {
533 /* numAdditionalBlocks is not specified but offsetAdditionalBlocks
534 is, so fill it! */
535 if (mar_buffer_seek(mar, mar_position, sizeof(uint32_t)) != 0) {
536 return -1;
538 if (*mar_position > (size_t)UINT32_MAX) {
539 return -1;
541 *offsetAdditionalBlocks = (uint32_t)*mar_position;
545 return 0;
549 * Reads the product info block from the MAR file's additional block section.
550 * The caller is responsible for freeing the fields in infoBlock
551 * if the return is successful.
553 * @param infoBlock Out parameter for where to store the result to
554 * @return 0 on success, -1 on failure
556 int read_product_info_block(char* path,
557 struct ProductInformationBlock* infoBlock) {
558 int rv;
559 MarFile* mar;
560 MarReadResult result = mar_open(path, &mar);
561 if (result != MAR_READ_SUCCESS) {
562 fprintf(stderr,
563 "ERROR: could not open file in read_product_info_block()\n");
564 return -1;
566 rv = mar_read_product_info_block(mar, infoBlock);
567 mar_close(mar);
568 return rv;
572 * Reads the product info block from the MAR file's additional block section.
573 * The caller is responsible for freeing the fields in infoBlock
574 * if the return is successful.
576 * @param infoBlock Out parameter for where to store the result to
577 * @return 0 on success, -1 on failure
579 int mar_read_product_info_block(MarFile* mar,
580 struct ProductInformationBlock* infoBlock) {
581 uint32_t offsetAdditionalBlocks, numAdditionalBlocks, additionalBlockSize,
582 additionalBlockID;
583 int hasAdditionalBlocks;
584 size_t mar_position = 0;
586 /* The buffer size is 97 bytes because the MAR channel name < 64 bytes, and
587 product version < 32 bytes + 3 NULL terminator bytes. */
588 char buf[MAXADDITIONALBLOCKSIZE + 1] = {'\0'};
589 if (get_open_mar_file_info(mar, &mar_position, NULL, NULL,
590 &hasAdditionalBlocks, &offsetAdditionalBlocks,
591 &numAdditionalBlocks) != 0) {
592 return -1;
595 /* We only have the one additional block type and only one is expected to be
596 in a MAR file so check if any exist and process the first found */
597 if (numAdditionalBlocks > 0) {
598 /* Read the additional block size */
599 if (mar_read_buffer(mar, &additionalBlockSize, &mar_position,
600 sizeof(additionalBlockSize)) != 0) {
601 return -1;
603 additionalBlockSize = ntohl(additionalBlockSize) -
604 sizeof(additionalBlockSize) -
605 sizeof(additionalBlockID);
607 /* Additional Block sizes should only be 96 bytes long */
608 if (additionalBlockSize > MAXADDITIONALBLOCKSIZE) {
609 return -1;
612 /* Read the additional block ID */
613 if (mar_read_buffer(mar, &additionalBlockID, &mar_position,
614 sizeof(additionalBlockID)) != 0) {
615 return -1;
617 additionalBlockID = ntohl(additionalBlockID);
619 if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
620 const char* location;
621 int len;
623 if (mar_read_buffer(mar, buf, &mar_position, additionalBlockSize) != 0) {
624 return -1;
627 /* Extract the MAR channel name from the buffer. For now we
628 point to the stack allocated buffer but we strdup this
629 if we are within bounds of each field's max length. */
630 location = buf;
631 len = strlen(location);
632 infoBlock->MARChannelID = location;
633 location += len + 1;
634 if (len >= 64) {
635 infoBlock->MARChannelID = NULL;
636 return -1;
639 /* Extract the version from the buffer */
640 len = strlen(location);
641 infoBlock->productVersion = location;
642 if (len >= 32) {
643 infoBlock->MARChannelID = NULL;
644 infoBlock->productVersion = NULL;
645 return -1;
647 infoBlock->MARChannelID = strdup(infoBlock->MARChannelID);
648 infoBlock->productVersion = strdup(infoBlock->productVersion);
649 return 0;
650 } else {
651 /* This is not the additional block you're looking for. Move along. */
652 if (mar_buffer_seek(mar, &mar_position, additionalBlockSize) != 0) {
653 return -1;
658 /* If we had a product info block we would have already returned */
659 return -1;
662 const MarItem* mar_find_item(MarFile* mar, const char* name) {
663 uint32_t hash;
664 const MarItem* item;
666 if (!mar->item_table_is_valid) {
667 if (mar_read_index(mar)) {
668 return NULL;
669 } else {
670 mar->item_table_is_valid = 1;
674 hash = mar_hash_name(name);
676 item = mar->item_table[hash];
677 while (item && strcmp(item->name, name) != 0) {
678 item = item->next;
681 /* If this is the first time seeing this item's indexes, return it */
682 if (mar_insert_offset(mar, item->offset, item->length) == 1) {
683 return item;
684 } else {
685 fprintf(stderr, "ERROR: file content collision in mar_find_item()\n");
686 return NULL;
690 int mar_enum_items(MarFile* mar, MarItemCallback callback, void* closure) {
691 MarItem* item;
692 int i, rv;
694 if (!mar->item_table_is_valid) {
695 if (mar_read_index(mar)) {
696 return -1;
697 } else {
698 mar->item_table_is_valid = 1;
702 for (i = 0; i < TABLESIZE; ++i) {
703 item = mar->item_table[i];
704 while (item) {
705 /* if this is the first time seeing this item's indexes, process it */
706 if (mar_insert_offset(mar, item->offset, item->length) == 1) {
707 rv = callback(mar, item, closure);
708 if (rv) {
709 return rv;
711 } else {
712 fprintf(stderr, "ERROR: file content collision in mar_enum_items()\n");
713 return 1;
715 item = item->next;
719 return 0;
722 int mar_read(MarFile* mar, const MarItem* item, int offset, uint8_t* buf,
723 int bufsize) {
724 int nr;
725 size_t mar_position = 0;
727 if (offset == (int)item->length) {
728 return 0;
730 if (offset > (int)item->length) {
731 return -1;
734 nr = item->length - offset;
735 if (nr > bufsize) {
736 nr = bufsize;
739 // Avoid adding item->offset and offset directly, just in case of overflow.
740 if (mar_buffer_seek(mar, &mar_position, item->offset)) {
741 return -1;
743 if (mar_buffer_seek(mar, &mar_position, offset)) {
744 return -1;
747 return mar_read_buffer_max(mar, buf, &mar_position, nr);
751 * Determines the MAR file information.
753 * @param path The path of the MAR file to check.
754 * @param hasSignatureBlock Optional out parameter specifying if the MAR
755 * file has a signature block or not.
756 * @param numSignatures Optional out parameter for storing the number
757 * of signatures in the MAR file.
758 * @param hasAdditionalBlocks Optional out parameter specifying if the MAR
759 * file has additional blocks or not.
760 * @param offsetAdditionalBlocks Optional out parameter for the offset to the
761 * first additional block. Value is only valid if
762 * hasAdditionalBlocks is not equal to 0.
763 * @param numAdditionalBlocks Optional out parameter for the number of
764 * additional blocks. Value is only valid if
765 * has_additional_blocks is not equal to 0.
766 * @return 0 on success and non-zero on failure.
768 int get_mar_file_info(const char* path, int* hasSignatureBlock,
769 uint32_t* numSignatures, int* hasAdditionalBlocks,
770 uint32_t* offsetAdditionalBlocks,
771 uint32_t* numAdditionalBlocks) {
772 int rv;
773 MarFile* mar;
774 size_t mar_position = 0;
775 MarReadResult result = mar_open(path, &mar);
776 if (result != MAR_READ_SUCCESS) {
777 fprintf(stderr, "ERROR: could not read file in get_mar_file_info()\n");
778 return -1;
781 rv = get_open_mar_file_info(mar, &mar_position, hasSignatureBlock,
782 numSignatures, hasAdditionalBlocks,
783 offsetAdditionalBlocks, numAdditionalBlocks);
785 mar_close(mar);
786 return rv;