LibreOffice Module onlineupdate (master) 1
mar_read.c
Go to the documentation of this file.
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>
12#include <onlineupdate/mar.h>
13
14#ifdef _WIN32
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 */
22static 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
32static 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
60static 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);
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
107static 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
147static 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
167MarFile *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 _WIN32
181MarFile *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
195void 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
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 overflow 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) == (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;
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
368int
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
393int
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 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;
416 }
417 additionalBlockSize = ntohl(additionalBlockSize) -
418 sizeof(additionalBlockSize) -
419 sizeof(additionalBlockID);
420
421 /* Read the additional block ID */
422 if (fread(&additionalBlockID,
423 sizeof(additionalBlockID),
424 1, mar->fp) != 1) {
425 return -1;
426 }
427 additionalBlockID = ntohl(additionalBlockID);
428
429 if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
430 const char *location;
431 int len;
432
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;
440 }
441
442 if (fread(buf, additionalBlockSize, 1, mar->fp) != 1) {
443 return -1;
444 }
445
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;
456 }
457
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;
466 }
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;
476 }
477 }
478 }
479
480 /* If we had a product info block we would have already returned */
481 return -1;
482}
483
484const MarItem *mar_find_item(MarFile *mar, const char *name) {
485 uint32_t hash;
486 const MarItem *item;
487
488 hash = mar_hash_name(name);
489
490 item = mar->item_table[hash];
491 while (item && strcmp(item->name, name) != 0)
492 item = item->next;
493
494 return item;
495}
496
497int mar_enum_items(MarFile *mar, MarItemCallback callback, void *closure) {
498 MarItem *item;
499 int i;
500
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;
508 }
509 }
510
511 return 0;
512}
513
514int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf,
515 int bufsize) {
516 int nr;
517
518 if (offset == (int) item->length)
519 return 0;
520 if (offset > (int) item->length)
521 return -1;
522
523 nr = item->length - offset;
524 if (nr > bufsize)
525 nr = bufsize;
526
527 if (fseek(mar->fp, item->offset + offset, SEEK_SET))
528 return -1;
529
530 return fread(buf, 1, nr, mar->fp);
531}
532
551int 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)
557{
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;
564 }
565
566 rv = get_mar_file_info_fp(fp, hasSignatureBlock,
567 numSignatures, hasAdditionalBlocks,
568 offsetAdditionalBlocks, numAdditionalBlocks);
569
570 fclose(fp);
571 return rv;
572}
const char * name
return NULL
#define TABLESIZE
Definition: mar.h:46
#define MAX_SIGNATURES
Definition: mar.h:23
int(* MarItemCallback)(MarFile *mar, const MarItem *item, void *data)
Signature of callback function passed to mar_enum_items.
Definition: mar.h:62
#define PRODUCT_INFO_BLOCK_ID
Definition: mar_private.h:45
#define SIGNATURE_BLOCK_OFFSET
Definition: mar_private.h:22
#define MAR_ID_SIZE
Definition: mar_private.h:18
#define MAR_ID
Definition: mar_private.h:17
int get_mar_file_info(const char *path, int *hasSignatureBlock, uint32_t *numSignatures, int *hasAdditionalBlocks, uint32_t *offsetAdditionalBlocks, uint32_t *numAdditionalBlocks)
Determines the MAR file information.
Definition: mar_read.c:551
static uint32_t mar_hash_name(const char *name)
Definition: mar_read.c:22
static int mar_read_index(MarFile *mar)
Definition: mar_read.c:107
int mar_read(MarFile *mar, const MarItem *item, int offset, char *buf, int bufsize)
Read from MAR item at given offset up to bufsize bytes.
Definition: mar_read.c:514
void mar_close(MarFile *mar)
Close a MAR file that was opened using mar_open.
Definition: mar_read.c:195
int mar_enum_items(MarFile *mar, MarItemCallback callback, void *closure)
Enumerate all MAR items via callback function.
Definition: mar_read.c:497
int get_mar_file_info_fp(FILE *fp, int *hasSignatureBlock, uint32_t *numSignatures, int *hasAdditionalBlocks, uint32_t *offsetAdditionalBlocks, uint32_t *numAdditionalBlocks)
Determines the MAR file information.
Definition: mar_read.c:231
MarFile * mar_open(const char *path)
Open a MAR file for reading.
Definition: mar_read.c:167
int read_product_info_block(char *path, struct ProductInformationBlock *infoBlock)
Reads the product info block from the MAR file's additional block section.
Definition: mar_read.c:369
static MarFile * mar_fpopen(FILE *fp)
Internal shared code for mar_open and mar_wopen.
Definition: mar_read.c:147
const MarItem * mar_find_item(MarFile *mar, const char *name)
Find an item in the MAR file by name.
Definition: mar_read.c:484
static int mar_insert_item(MarFile *mar, const char *name, int namelen, uint32_t offset, uint32_t length, uint32_t flags)
Definition: mar_read.c:32
int mar_read_product_info_block(MarFile *mar, struct ProductInformationBlock *infoBlock)
Reads the product info block from the MAR file's additional block section.
Definition: mar_read.c:394
static int mar_consume_index(MarFile *mar, char **buf, const char *buf_end)
Definition: mar_read.c:60
int i
sal_uInt32 ntohl(sal_uInt32 n)
Definition: mar.h:48
FILE * fp
Definition: mar.h:49
MarItem * item_table[TABLESIZE]
Definition: mar.h:50
The MAR item data structure.
Definition: mar.h:38
uint32_t flags
Definition: mar.h:42
char name[1]
Definition: mar.h:43
uint32_t length
Definition: mar.h:41
uint32_t offset
Definition: mar.h:40
struct MarItem_ * next
Definition: mar.h:39
const char * productVersion
Definition: mar.h:32
const char * MARChannelID
Definition: mar.h:31