Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / onlineupdate / source / libmar / src / mar_read.c
blob2815d12e7ccbab7b8649766abb61aeebba52c9db
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 <onlineupdate/mar_private.h>
12 #include <onlineupdate/mar.h>
14 #ifdef _WIN32
15 #include <winsock2.h>
16 #else
17 #include <netinet/in.h>
18 #endif
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;
26 for (c = (unsigned char *) name; *c; ++c)
27 val = val*37 + *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) {
34 MarItem *item, *root;
35 uint32_t hash;
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);
46 hash = mar_hash_name(name);
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;
57 return 0;
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)
67 * char null_byte;
69 uint32_t offset;
70 uint32_t length;
71 uint32_t flags;
72 const char *name;
73 int namelen;
75 if ((buf_end - *buf) < (int)(3*sizeof(uint32_t) + 2))
76 return -1;
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);
89 flags = ntohl(flags);
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);
98 namelen = (*buf - name);
99 /* consume null byte */
100 if (*buf == buf_end)
101 return -1;
102 ++(*buf);
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;
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;
117 if (fread(&offset_to_index, sizeof(uint32_t), 1, mar->fp) != 1)
118 return -1;
119 offset_to_index = ntohl(offset_to_index);
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);
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;
135 bufptr = buf;
136 bufend = buf + size_of_index;
137 while (bufptr < bufend && mar_consume_index(mar, &bufptr, bufend) == 0);
139 free(buf);
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)
149 MarFile *mar;
151 mar = (MarFile *) malloc(sizeof(*mar));
152 if (!mar) {
153 fclose(fp);
154 return NULL;
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;
164 return mar;
167 MarFile *mar_open(const char *path) {
168 FILE *fp;
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;
177 return mar_fpopen(fp);
180 #ifdef _WIN32
181 MarFile *mar_wopen(const wchar_t *path) {
182 FILE *fp;
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;
191 return mar_fpopen(fp);
193 #endif
195 void mar_close(MarFile *mar) {
196 MarItem *item;
197 int i;
199 fclose(mar->fp);
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);
210 free(mar);
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) {
242 return -1;
246 /* Skip to the start of the offset index */
247 if (fseek(fp, MAR_ID_SIZE, SEEK_SET)) {
248 return -1;
251 /* Read the offset to the index. */
252 if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fp) != 1) {
253 return -1;
255 offsetToIndex = ntohl(offsetToIndex);
257 if (numSignatures) {
258 /* Skip past the MAR file size field */
259 if (fseek(fp, sizeof(uint64_t), SEEK_CUR)) {
260 return -1;
263 /* Read the number of signatures field */
264 if (fread(numSignatures, sizeof(*numSignatures), 1, fp) != 1) {
265 return -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)) {
274 return -1;
277 if (fseek(fp, sizeof(uint32_t), SEEK_CUR)) {
278 return -1;
281 /* Read the first offset to content field. */
282 if (fread(&offsetToContent, sizeof(offsetToContent), 1, fp) != 1) {
283 return -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;
291 } else {
292 *hasSignatureBlock = 1;
296 /* If the caller doesn't care about the product info block
297 value, then just return */
298 if (!hasAdditionalBlocks) {
299 return 0;
302 /* Skip to the start of the signature block */
303 if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
304 return -1;
307 /* Get the number of signatures */
308 if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) {
309 return -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) {
316 return -1;
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;
326 /* Read the signature length and skip past the signature */
327 if (fread(&signatureLen, sizeof(uint32_t), 1, fp) != 1) {
328 return -1;
330 signatureLen = ntohl(signatureLen);
331 if (fseek(fp, signatureLen, SEEK_CUR)) {
332 return -1;
336 if (ftell(fp) == (long)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;
346 *numAdditionalBlocks = ntohl(*numAdditionalBlocks);
347 if (offsetAdditionalBlocks) {
348 *offsetAdditionalBlocks = ftell(fp);
350 } else if (offsetAdditionalBlocks) {
351 /* numAdditionalBlocks is not specified but offsetAdditionalBlocks
352 is, so fill it! */
353 *offsetAdditionalBlocks = ftell(fp) + sizeof(uint32_t);
357 return 0;
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)
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;
380 rv = mar_read_product_info_block(&mar, infoBlock);
381 fclose(mar.fp);
382 return rv;
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);
408 if (ret)
409 return ret;
410 for (i = 0; i < numAdditionalBlocks; ++i) {
411 /* Read the additional block size */
412 if (fread(&additionalBlockSize,
413 sizeof(additionalBlockSize),
414 1, mar->fp) != 1) {
415 return -1;
417 additionalBlockSize = ntohl(additionalBlockSize) -
418 sizeof(additionalBlockSize) -
419 sizeof(additionalBlockID);
421 /* Read the additional block ID */
422 if (fread(&additionalBlockID,
423 sizeof(additionalBlockID),
424 1, mar->fp) != 1) {
425 return -1;
427 additionalBlockID = ntohl(additionalBlockID);
429 if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
430 const char *location;
431 int len;
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) {
439 return -1;
442 if (fread(buf, additionalBlockSize, 1, mar->fp) != 1) {
443 return -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. */
449 location = buf;
450 len = strlen(location);
451 infoBlock->MARChannelID = location;
452 location += len + 1;
453 if (len >= 64) {
454 infoBlock->MARChannelID = NULL;
455 return -1;
458 /* Extract the version from the buffer */
459 len = strlen(location);
460 infoBlock->productVersion = location;
461 location += len + 1;
462 if (len >= 32) {
463 infoBlock->MARChannelID = NULL;
464 infoBlock->productVersion = NULL;
465 return -1;
467 infoBlock->MARChannelID =
468 strdup(infoBlock->MARChannelID);
469 infoBlock->productVersion =
470 strdup(infoBlock->productVersion);
471 return 0;
472 } else {
473 /* This is not the additional block you're looking for. Move along. */
474 if (fseek(mar->fp, additionalBlockSize, SEEK_CUR)) {
475 return -1;
480 /* If we had a product info block we would have already returned */
481 return -1;
484 const MarItem *mar_find_item(MarFile *mar, const char *name) {
485 uint32_t hash;
486 const MarItem *item;
488 hash = mar_hash_name(name);
490 item = mar->item_table[hash];
491 while (item && strcmp(item->name, name) != 0)
492 item = item->next;
494 return item;
497 int mar_enum_items(MarFile *mar, MarItemCallback callback, void *closure) {
498 MarItem *item;
499 int i;
501 for (i = 0; i < TABLESIZE; ++i) {
502 item = mar->item_table[i];
503 while (item) {
504 int rv = callback(mar, item, closure);
505 if (rv)
506 return rv;
507 item = item->next;
511 return 0;
514 int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf,
515 int bufsize) {
516 int nr;
518 if (offset == (int) item->length)
519 return 0;
520 if (offset > (int) item->length)
521 return -1;
523 nr = item->length - offset;
524 if (nr > bufsize)
525 nr = bufsize;
527 if (fseek(mar->fp, item->offset + offset, SEEK_SET))
528 return -1;
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)
558 int rv;
559 FILE *fp = fopen(path, "rb");
560 if (!fp) {
561 fprintf(stderr, "ERROR: could not open file in get_mar_file_info()\n");
562 perror(path);
563 return -1;
566 rv = get_mar_file_info_fp(fp, hasSignatureBlock,
567 numSignatures, hasAdditionalBlocks,
568 offsetAdditionalBlocks, numAdditionalBlocks);
570 fclose(fp);
571 return rv;