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/. */
12 #include <onlineupdate/mar_private.h>
13 #include <onlineupdate/mar_cmdline.h>
14 #include <onlineupdate/mar.h>
19 #include <netinet/in.h>
26 uint32_t size_allocated
;
31 * Push a new item onto the stack of items. The stack is a single block
34 static int mar_push(struct MarItemStack
*stack
, uint32_t length
, uint32_t flags
,
37 uint32_t n_offset
, n_length
, n_flags
;
41 namelen
= strlen(name
);
42 size
= MAR_ITEM_SIZE(namelen
);
44 if (stack
->size_allocated
- stack
->size_used
< size
) {
45 /* increase size of stack */
46 size_t size_needed
= ROUND_UP(stack
->size_used
+ size
, BLOCKSIZE
);
47 stack
->head
= realloc(stack
->head
, size_needed
);
50 stack
->size_allocated
= size_needed
;
53 data
= (((char *) stack
->head
) + stack
->size_used
);
55 n_offset
= htonl(stack
->last_offset
);
56 n_length
= htonl(length
);
57 n_flags
= htonl(flags
);
59 memcpy(data
, &n_offset
, sizeof(n_offset
));
60 data
+= sizeof(n_offset
);
62 memcpy(data
, &n_length
, sizeof(n_length
));
63 data
+= sizeof(n_length
);
65 memcpy(data
, &n_flags
, sizeof(n_flags
));
66 data
+= sizeof(n_flags
);
68 memcpy(data
, name
, namelen
+ 1);
70 stack
->size_used
+= size
;
71 stack
->last_offset
+= length
;
75 static int mar_concat_file(FILE *fp
, const char *path
) {
81 in
= fopen(path
, "rb");
83 fprintf(stderr
, "ERROR: could not open file in mar_concat_file()\n");
88 while ((len
= fread(buf
, 1, BLOCKSIZE
, in
)) > 0) {
89 if (fwrite(buf
, len
, 1, fp
) != 1) {
100 * Writes out the product information block to the specified file.
102 * @param fp The opened MAR file being created.
103 * @param stack A pointer to the MAR item stack being used to create
105 * @param infoBlock The product info block to store in the file.
106 * @return 0 on success.
109 mar_concat_product_info_block(FILE *fp
,
110 struct MarItemStack
*stack
,
111 struct ProductInformationBlock
*infoBlock
)
113 char buf
[PIB_MAX_MAR_CHANNEL_ID_SIZE
+ PIB_MAX_PRODUCT_VERSION_SIZE
];
114 uint32_t additionalBlockID
= 1, infoBlockSize
, unused
;
115 if (!fp
|| !infoBlock
||
116 !infoBlock
->MARChannelID
||
117 !infoBlock
->productVersion
) {
121 /* The MAR channel name must be < 64 bytes per the spec */
122 if (strlen(infoBlock
->MARChannelID
) > PIB_MAX_MAR_CHANNEL_ID_SIZE
) {
126 /* The product version must be < 32 bytes per the spec */
127 if (strlen(infoBlock
->productVersion
) > PIB_MAX_PRODUCT_VERSION_SIZE
) {
131 /* Although we don't need the product information block size to include the
132 maximum MAR channel name and product version, we allocate the maximum
133 amount to make it easier to modify the MAR file for repurposing MAR files
134 to different MAR channels. + 2 is for the NULL terminators. */
135 infoBlockSize
= sizeof(infoBlockSize
) +
136 sizeof(additionalBlockID
) +
137 PIB_MAX_MAR_CHANNEL_ID_SIZE
+
138 PIB_MAX_PRODUCT_VERSION_SIZE
+ 2;
140 stack
->last_offset
+= infoBlockSize
;
143 /* Write out the product info block size */
144 infoBlockSize
= htonl(infoBlockSize
);
145 if (fwrite(&infoBlockSize
,
146 sizeof(infoBlockSize
), 1, fp
) != 1) {
149 infoBlockSize
= ntohl(infoBlockSize
);
151 /* Write out the product info block ID */
152 additionalBlockID
= htonl(additionalBlockID
);
153 if (fwrite(&additionalBlockID
,
154 sizeof(additionalBlockID
), 1, fp
) != 1) {
157 additionalBlockID
= ntohl(additionalBlockID
);
159 /* Write out the channel name and NULL terminator */
160 if (fwrite(infoBlock
->MARChannelID
,
161 strlen(infoBlock
->MARChannelID
) + 1, 1, fp
) != 1) {
165 /* Write out the product version string and NULL terminator */
166 if (fwrite(infoBlock
->productVersion
,
167 strlen(infoBlock
->productVersion
) + 1, 1, fp
) != 1) {
171 /* Write out the rest of the block that is unused */
172 unused
= infoBlockSize
- (sizeof(infoBlockSize
) +
173 sizeof(additionalBlockID
) +
174 strlen(infoBlock
->MARChannelID
) +
175 strlen(infoBlock
->productVersion
) + 2);
176 memset(buf
, 0, sizeof(buf
));
177 if (fwrite(buf
, unused
, 1, fp
) != 1) {
184 * Refreshes the product information block with the new information.
185 * The input MAR must not be signed or the function call will fail.
187 * @param path The path to the MAR file whose product info block
188 * should be refreshed.
189 * @param infoBlock Out parameter for where to store the result to
190 * @return 0 on success, -1 on failure
193 refresh_product_info_block(const char *path
,
194 struct ProductInformationBlock
*infoBlock
)
198 uint32_t numSignatures
, additionalBlockSize
, additionalBlockID
,
199 offsetAdditionalBlocks
, numAdditionalBlocks
, i
;
200 int additionalBlocks
, hasSignatureBlock
;
202 rv
= get_mar_file_info(path
,
206 &offsetAdditionalBlocks
,
207 &numAdditionalBlocks
);
209 fprintf(stderr
, "ERROR: Could not obtain MAR information.\n");
213 if (hasSignatureBlock
&& numSignatures
) {
214 fprintf(stderr
, "ERROR: Cannot refresh a signed MAR\n");
218 fp
= fopen(path
, "r+b");
220 fprintf(stderr
, "ERROR: could not open target file: %s\n", path
);
224 if (fseeko(fp
, offsetAdditionalBlocks
, SEEK_SET
)) {
225 fprintf(stderr
, "ERROR: could not seek to additional blocks\n");
230 for (i
= 0; i
< numAdditionalBlocks
; ++i
) {
231 /* Get the position of the start of this block */
232 int64_t oldPos
= ftello(fp
);
234 /* Read the additional block size */
235 if (fread(&additionalBlockSize
,
236 sizeof(additionalBlockSize
),
241 additionalBlockSize
= ntohl(additionalBlockSize
);
243 /* Read the additional block ID */
244 if (fread(&additionalBlockID
,
245 sizeof(additionalBlockID
),
250 additionalBlockID
= ntohl(additionalBlockID
);
252 if (PRODUCT_INFO_BLOCK_ID
== additionalBlockID
) {
253 if (fseeko(fp
, oldPos
, SEEK_SET
)) {
254 fprintf(stderr
, "Could not seek back to Product Information Block\n");
259 if (mar_concat_product_info_block(fp
, NULL
, infoBlock
)) {
260 fprintf(stderr
, "Could not concat Product Information Block\n");
268 /* This is not the additional block you're looking for. Move along. */
269 if (fseek(fp
, additionalBlockSize
, SEEK_CUR
)) {
270 fprintf(stderr
, "ERROR: Could not seek past current block.\n");
277 /* If we had a product info block we would have already returned */
279 fprintf(stderr
, "ERROR: Could not refresh because block does not exist\n");
284 * Create a MAR file from a set of files.
285 * @param dest The path to the file to create. This path must be
286 * compatible with fopen.
287 * @param numfiles The number of files to store in the archive.
288 * @param files The list of null-terminated file paths. Each file
289 * path must be compatible with fopen.
290 * @param infoBlock The information to store in the product information block.
291 * @return A non-zero value if an error occurs.
293 int mar_create(const char *dest
, int
294 num_files
, char **files
,
295 struct ProductInformationBlock
*infoBlock
) {
296 struct MarItemStack stack
;
297 uint32_t offset_to_index
= 0, size_of_index
,
298 numSignatures
, numAdditionalSections
;
299 uint64_t sizeOfEntireMAR
= 0;
304 memset(&stack
, 0, sizeof(stack
));
306 fp
= fopen(dest
, "wb");
308 fprintf(stderr
, "ERROR: could not create target file: %s\n", dest
);
312 if (fwrite(MAR_ID
, MAR_ID_SIZE
, 1, fp
) != 1)
314 if (fwrite(&offset_to_index
, sizeof(uint32_t), 1, fp
) != 1)
317 stack
.last_offset
= MAR_ID_SIZE
+
318 sizeof(offset_to_index
) +
319 sizeof(numSignatures
) +
320 sizeof(numAdditionalSections
) +
321 sizeof(sizeOfEntireMAR
);
323 /* We will circle back on this at the end of the MAR creation to fill it */
324 if (fwrite(&sizeOfEntireMAR
, sizeof(sizeOfEntireMAR
), 1, fp
) != 1) {
328 /* Write out the number of signatures, for now only at most 1 is supported */
330 if (fwrite(&numSignatures
, sizeof(numSignatures
), 1, fp
) != 1) {
334 /* Write out the number of additional sections, for now just 1
335 for the product info block */
336 numAdditionalSections
= htonl(1);
337 if (fwrite(&numAdditionalSections
,
338 sizeof(numAdditionalSections
), 1, fp
) != 1) {
341 numAdditionalSections
= ntohl(numAdditionalSections
);
343 if (mar_concat_product_info_block(fp
, &stack
, infoBlock
)) {
347 for (i
= 0; i
< num_files
; ++i
) {
348 if (stat(files
[i
], &st
)) {
349 fprintf(stderr
, "ERROR: file not found: %s\n", files
[i
]);
353 if (mar_push(&stack
, st
.st_size
, st
.st_mode
& 0777, files
[i
]))
356 /* concatenate input file to archive */
357 if (mar_concat_file(fp
, files
[i
]))
361 /* write out the index (prefixed with length of index) */
362 size_of_index
= htonl(stack
.size_used
);
363 if (fwrite(&size_of_index
, sizeof(size_of_index
), 1, fp
) != 1)
365 if (fwrite(stack
.head
, stack
.size_used
, 1, fp
) != 1)
368 /* To protect against invalid MAR files, we assume that the MAR file
369 size is less than or equal to MAX_SIZE_OF_MAR_FILE. */
370 if (ftell(fp
) > MAX_SIZE_OF_MAR_FILE
) {
374 /* write out offset to index file in network byte order */
375 offset_to_index
= htonl(stack
.last_offset
);
376 if (fseek(fp
, MAR_ID_SIZE
, SEEK_SET
))
378 if (fwrite(&offset_to_index
, sizeof(offset_to_index
), 1, fp
) != 1)
380 offset_to_index
= ntohl(stack
.last_offset
);
382 sizeOfEntireMAR
= ((uint64_t)stack
.last_offset
) +
384 sizeof(size_of_index
);
385 sizeOfEntireMAR
= HOST_TO_NETWORK64(sizeOfEntireMAR
);
386 if (fwrite(&sizeOfEntireMAR
, sizeof(sizeOfEntireMAR
), 1, fp
) != 1)
388 sizeOfEntireMAR
= NETWORK_TO_HOST64(sizeOfEntireMAR
);