File: | var/lib/jenkins/workspace/firefox-scan-build/modules/libmar/src/mar_create.c |
Warning: | line 89, column 17 File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 <sys/stat.h> | |||
9 | #include <fcntl.h> | |||
10 | #include <stdlib.h> | |||
11 | #include <string.h> | |||
12 | #include "mar_private.h" | |||
13 | #include "mar_cmdline.h" | |||
14 | #include "mar.h" | |||
15 | ||||
16 | #ifdef XP_WIN | |||
17 | # include <winsock2.h> | |||
18 | #else | |||
19 | # include <netinet/in.h> | |||
20 | # include <unistd.h> | |||
21 | #endif | |||
22 | ||||
23 | struct MarItemStack { | |||
24 | void* head; | |||
25 | uint32_t size_used; | |||
26 | uint32_t size_allocated; | |||
27 | uint32_t last_offset; | |||
28 | }; | |||
29 | ||||
30 | /** | |||
31 | * Push a new item onto the stack of items. The stack is a single block | |||
32 | * of memory. | |||
33 | */ | |||
34 | static int mar_push(struct MarItemStack* stack, uint32_t length, uint32_t flags, | |||
35 | const char* name) { | |||
36 | int namelen; | |||
37 | uint32_t n_offset, n_length, n_flags; | |||
38 | uint32_t size; | |||
39 | char* data; | |||
40 | ||||
41 | namelen = strlen(name); | |||
42 | size = MAR_ITEM_SIZE(namelen)(3 * sizeof(uint32_t) + (namelen) + 1); | |||
43 | ||||
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)(((stack->size_used + size) / (4096) + 1) * (4096)); | |||
47 | stack->head = realloc(stack->head, size_needed); | |||
48 | if (!stack->head) { | |||
49 | return -1; | |||
50 | } | |||
51 | stack->size_allocated = size_needed; | |||
52 | } | |||
53 | ||||
54 | data = (((char*)stack->head) + stack->size_used); | |||
55 | ||||
56 | n_offset = htonl(stack->last_offset)__bswap_32 (stack->last_offset); | |||
57 | n_length = htonl(length)__bswap_32 (length); | |||
58 | n_flags = htonl(flags)__bswap_32 (flags); | |||
59 | ||||
60 | memcpy(data, &n_offset, sizeof(n_offset)); | |||
61 | data += sizeof(n_offset); | |||
62 | ||||
63 | memcpy(data, &n_length, sizeof(n_length)); | |||
64 | data += sizeof(n_length); | |||
65 | ||||
66 | memcpy(data, &n_flags, sizeof(n_flags)); | |||
67 | data += sizeof(n_flags); | |||
68 | ||||
69 | memcpy(data, name, namelen + 1); | |||
70 | ||||
71 | stack->size_used += size; | |||
72 | stack->last_offset += length; | |||
73 | return 0; | |||
74 | } | |||
75 | ||||
76 | static int mar_concat_file(FILE* fp, const char* path) { | |||
77 | FILE* in; | |||
78 | char buf[BLOCKSIZE4096]; | |||
79 | size_t len; | |||
80 | int rv = 0; | |||
81 | ||||
82 | in = fopen(path, "rb"); | |||
83 | if (!in
| |||
84 | fprintf(stderrstderr, "ERROR: could not open file in mar_concat_file()\n"); | |||
85 | perror(path); | |||
86 | return -1; | |||
87 | } | |||
88 | ||||
89 | while ((len = fread(buf, 1, BLOCKSIZE4096, in)) > 0) { | |||
| ||||
90 | if (fwrite(buf, len, 1, fp) != 1) { | |||
91 | rv = -1; | |||
92 | break; | |||
93 | } | |||
94 | } | |||
95 | ||||
96 | fclose(in); | |||
97 | return rv; | |||
98 | } | |||
99 | ||||
100 | /** | |||
101 | * Writes out the product information block to the specified file. | |||
102 | * | |||
103 | * @param fp The opened MAR file being created. | |||
104 | * @param stack A pointer to the MAR item stack being used to create | |||
105 | * the MAR | |||
106 | * @param infoBlock The product info block to store in the file. | |||
107 | * @return 0 on success. | |||
108 | */ | |||
109 | static int mar_concat_product_info_block( | |||
110 | FILE* fp, struct MarItemStack* stack, | |||
111 | struct ProductInformationBlock* infoBlock) { | |||
112 | char buf[PIB_MAX_MAR_CHANNEL_ID_SIZE63 + PIB_MAX_PRODUCT_VERSION_SIZE31]; | |||
113 | uint32_t additionalBlockID = 1, infoBlockSize, unused; | |||
114 | if (!fp || !infoBlock || !infoBlock->MARChannelID || | |||
115 | !infoBlock->productVersion) { | |||
116 | return -1; | |||
117 | } | |||
118 | ||||
119 | /* The MAR channel name must be < 64 bytes per the spec */ | |||
120 | if (strlen(infoBlock->MARChannelID) > PIB_MAX_MAR_CHANNEL_ID_SIZE63) { | |||
121 | return -1; | |||
122 | } | |||
123 | ||||
124 | /* The product version must be < 32 bytes per the spec */ | |||
125 | if (strlen(infoBlock->productVersion) > PIB_MAX_PRODUCT_VERSION_SIZE31) { | |||
126 | return -1; | |||
127 | } | |||
128 | ||||
129 | /* Although we don't need the product information block size to include the | |||
130 | maximum MAR channel name and product version, we allocate the maximum | |||
131 | amount to make it easier to modify the MAR file for repurposing MAR files | |||
132 | to different MAR channels. + 2 is for the NULL terminators. */ | |||
133 | infoBlockSize = sizeof(infoBlockSize) + sizeof(additionalBlockID) + | |||
134 | PIB_MAX_MAR_CHANNEL_ID_SIZE63 + PIB_MAX_PRODUCT_VERSION_SIZE31 + | |||
135 | 2; | |||
136 | if (stack) { | |||
137 | stack->last_offset += infoBlockSize; | |||
138 | } | |||
139 | ||||
140 | /* Write out the product info block size */ | |||
141 | infoBlockSize = htonl(infoBlockSize)__bswap_32 (infoBlockSize); | |||
142 | if (fwrite(&infoBlockSize, sizeof(infoBlockSize), 1, fp) != 1) { | |||
143 | return -1; | |||
144 | } | |||
145 | infoBlockSize = ntohl(infoBlockSize)__bswap_32 (infoBlockSize); | |||
146 | ||||
147 | /* Write out the product info block ID */ | |||
148 | additionalBlockID = htonl(additionalBlockID)__bswap_32 (additionalBlockID); | |||
149 | if (fwrite(&additionalBlockID, sizeof(additionalBlockID), 1, fp) != 1) { | |||
150 | return -1; | |||
151 | } | |||
152 | additionalBlockID = ntohl(additionalBlockID)__bswap_32 (additionalBlockID); | |||
153 | ||||
154 | /* Write out the channel name and NULL terminator */ | |||
155 | if (fwrite(infoBlock->MARChannelID, strlen(infoBlock->MARChannelID) + 1, 1, | |||
156 | fp) != 1) { | |||
157 | return -1; | |||
158 | } | |||
159 | ||||
160 | /* Write out the product version string and NULL terminator */ | |||
161 | if (fwrite(infoBlock->productVersion, strlen(infoBlock->productVersion) + 1, | |||
162 | 1, fp) != 1) { | |||
163 | return -1; | |||
164 | } | |||
165 | ||||
166 | /* Write out the rest of the block that is unused */ | |||
167 | unused = infoBlockSize - (sizeof(infoBlockSize) + sizeof(additionalBlockID) + | |||
168 | strlen(infoBlock->MARChannelID) + | |||
169 | strlen(infoBlock->productVersion) + 2); | |||
170 | memset(buf, 0, sizeof(buf)); | |||
171 | if (fwrite(buf, unused, 1, fp) != 1) { | |||
172 | return -1; | |||
173 | } | |||
174 | return 0; | |||
175 | } | |||
176 | ||||
177 | /** | |||
178 | * Refreshes the product information block with the new information. | |||
179 | * The input MAR must not be signed or the function call will fail. | |||
180 | * | |||
181 | * @param path The path to the MAR file whose product info block | |||
182 | * should be refreshed. | |||
183 | * @param infoBlock Out parameter for where to store the result to | |||
184 | * @return 0 on success, -1 on failure | |||
185 | */ | |||
186 | int refresh_product_info_block(const char* path, | |||
187 | struct ProductInformationBlock* infoBlock) { | |||
188 | FILE* fp; | |||
189 | int rv; | |||
190 | uint32_t numSignatures, additionalBlockSize, additionalBlockID, | |||
191 | offsetAdditionalBlocks, numAdditionalBlocks, i; | |||
192 | int additionalBlocks, hasSignatureBlock; | |||
193 | int64_t oldPos; | |||
194 | ||||
195 | rv = get_mar_file_info(path, &hasSignatureBlock, &numSignatures, | |||
196 | &additionalBlocks, &offsetAdditionalBlocks, | |||
197 | &numAdditionalBlocks); | |||
198 | if (rv) { | |||
199 | fprintf(stderrstderr, "ERROR: Could not obtain MAR information.\n"); | |||
200 | return -1; | |||
201 | } | |||
202 | ||||
203 | if (hasSignatureBlock && numSignatures) { | |||
204 | fprintf(stderrstderr, "ERROR: Cannot refresh a signed MAR\n"); | |||
205 | return -1; | |||
206 | } | |||
207 | ||||
208 | fp = fopen(path, "r+b"); | |||
209 | if (!fp) { | |||
210 | fprintf(stderrstderr, "ERROR: could not open target file: %s\n", path); | |||
211 | return -1; | |||
212 | } | |||
213 | ||||
214 | if (fseeko(fp, offsetAdditionalBlocks, SEEK_SET0)) { | |||
215 | fprintf(stderrstderr, "ERROR: could not seek to additional blocks\n"); | |||
216 | fclose(fp); | |||
217 | return -1; | |||
218 | } | |||
219 | ||||
220 | for (i = 0; i < numAdditionalBlocks; ++i) { | |||
221 | /* Get the position of the start of this block */ | |||
222 | oldPos = ftello(fp); | |||
223 | ||||
224 | /* Read the additional block size */ | |||
225 | if (fread(&additionalBlockSize, sizeof(additionalBlockSize), 1, fp) != 1) { | |||
226 | fclose(fp); | |||
227 | return -1; | |||
228 | } | |||
229 | additionalBlockSize = ntohl(additionalBlockSize)__bswap_32 (additionalBlockSize); | |||
230 | ||||
231 | /* Read the additional block ID */ | |||
232 | if (fread(&additionalBlockID, sizeof(additionalBlockID), 1, fp) != 1) { | |||
233 | fclose(fp); | |||
234 | return -1; | |||
235 | } | |||
236 | additionalBlockID = ntohl(additionalBlockID)__bswap_32 (additionalBlockID); | |||
237 | ||||
238 | if (PRODUCT_INFO_BLOCK_ID1 == additionalBlockID) { | |||
239 | if (fseeko(fp, oldPos, SEEK_SET0)) { | |||
240 | fprintf(stderrstderr, "Could not seek back to Product Information Block\n"); | |||
241 | fclose(fp); | |||
242 | return -1; | |||
243 | } | |||
244 | ||||
245 | if (mar_concat_product_info_block(fp, NULL((void*)0), infoBlock)) { | |||
246 | fprintf(stderrstderr, "Could not concat Product Information Block\n"); | |||
247 | fclose(fp); | |||
248 | return -1; | |||
249 | } | |||
250 | ||||
251 | fclose(fp); | |||
252 | return 0; | |||
253 | } | |||
254 | ||||
255 | /* This is not the additional block you're looking for. Move along. */ | |||
256 | if (fseek(fp, additionalBlockSize, SEEK_CUR1)) { | |||
257 | fprintf(stderrstderr, "ERROR: Could not seek past current block.\n"); | |||
258 | fclose(fp); | |||
259 | return -1; | |||
260 | } | |||
261 | } | |||
262 | ||||
263 | /* If we had a product info block we would have already returned */ | |||
264 | fclose(fp); | |||
265 | fprintf(stderrstderr, "ERROR: Could not refresh because block does not exist\n"); | |||
266 | return -1; | |||
267 | } | |||
268 | ||||
269 | /** | |||
270 | * Create a MAR file from a set of files. | |||
271 | * @param dest The path to the file to create. This path must be | |||
272 | * compatible with fopen. | |||
273 | * @param numfiles The number of files to store in the archive. | |||
274 | * @param files The list of null-terminated file paths. Each file | |||
275 | * path must be compatible with fopen. | |||
276 | * @param infoBlock The information to store in the product information block. | |||
277 | * @return A non-zero value if an error occurs. | |||
278 | */ | |||
279 | int mar_create(const char* dest, int num_files, char** files, | |||
280 | struct ProductInformationBlock* infoBlock) { | |||
281 | struct MarItemStack stack; | |||
282 | uint32_t offset_to_index = 0, size_of_index, numSignatures, | |||
283 | numAdditionalSections; | |||
284 | uint64_t sizeOfEntireMAR = 0; | |||
285 | struct stat st; | |||
286 | FILE* fp; | |||
287 | int i, rv = -1; | |||
288 | ||||
289 | memset(&stack, 0, sizeof(stack)); | |||
290 | ||||
291 | fp = fopen(dest, "wb"); | |||
292 | if (!fp
| |||
| ||||
293 | fprintf(stderrstderr, "ERROR: could not create target file: %s\n", dest); | |||
294 | return -1; | |||
295 | } | |||
296 | ||||
297 | if (fwrite(MAR_ID"MAR1", MAR_ID_SIZE4, 1, fp) != 1) { | |||
298 | goto failure; | |||
299 | } | |||
300 | if (fwrite(&offset_to_index, sizeof(uint32_t), 1, fp) != 1) { | |||
301 | goto failure; | |||
302 | } | |||
303 | ||||
304 | stack.last_offset = MAR_ID_SIZE4 + sizeof(offset_to_index) + | |||
305 | sizeof(numSignatures) + sizeof(numAdditionalSections) + | |||
306 | sizeof(sizeOfEntireMAR); | |||
307 | ||||
308 | /* We will circle back on this at the end of the MAR creation to fill it */ | |||
309 | if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1) { | |||
310 | goto failure; | |||
311 | } | |||
312 | ||||
313 | /* Write out the number of signatures, for now only at most 1 is supported */ | |||
314 | numSignatures = 0; | |||
315 | if (fwrite(&numSignatures, sizeof(numSignatures), 1, fp) != 1) { | |||
316 | goto failure; | |||
317 | } | |||
318 | ||||
319 | /* Write out the number of additional sections, for now just 1 | |||
320 | for the product info block */ | |||
321 | numAdditionalSections = htonl(1)__bswap_32 (1); | |||
322 | if (fwrite(&numAdditionalSections, sizeof(numAdditionalSections), 1, fp) != | |||
323 | 1) { | |||
324 | goto failure; | |||
325 | } | |||
326 | numAdditionalSections = ntohl(numAdditionalSections)__bswap_32 (numAdditionalSections); | |||
327 | ||||
328 | if (mar_concat_product_info_block(fp, &stack, infoBlock)) { | |||
329 | goto failure; | |||
330 | } | |||
331 | ||||
332 | for (i = 0; i < num_files; ++i) { | |||
333 | if (stat(files[i], &st)) { | |||
334 | fprintf(stderrstderr, "ERROR: file not found: %s\n", files[i]); | |||
335 | goto failure; | |||
336 | } | |||
337 | ||||
338 | if (mar_push(&stack, st.st_size, st.st_mode & 0777, files[i])) { | |||
339 | goto failure; | |||
340 | } | |||
341 | ||||
342 | /* concatenate input file to archive */ | |||
343 | if (mar_concat_file(fp, files[i])) { | |||
344 | goto failure; | |||
345 | } | |||
346 | } | |||
347 | ||||
348 | /* write out the index (prefixed with length of index) */ | |||
349 | size_of_index = htonl(stack.size_used)__bswap_32 (stack.size_used); | |||
350 | if (fwrite(&size_of_index, sizeof(size_of_index), 1, fp) != 1) { | |||
351 | goto failure; | |||
352 | } | |||
353 | if (fwrite(stack.head, stack.size_used, 1, fp) != 1) { | |||
354 | goto failure; | |||
355 | } | |||
356 | ||||
357 | /* To protect against invalid MAR files, we assumes that the MAR file | |||
358 | size is less than or equal to MAX_SIZE_OF_MAR_FILE. */ | |||
359 | if (ftell(fp) > MAX_SIZE_OF_MAR_FILE((int64_t)524288000)) { | |||
360 | goto failure; | |||
361 | } | |||
362 | ||||
363 | /* write out offset to index file in network byte order */ | |||
364 | offset_to_index = htonl(stack.last_offset)__bswap_32 (stack.last_offset); | |||
365 | if (fseek(fp, MAR_ID_SIZE4, SEEK_SET0)) { | |||
366 | goto failure; | |||
367 | } | |||
368 | if (fwrite(&offset_to_index, sizeof(offset_to_index), 1, fp) != 1) { | |||
369 | goto failure; | |||
370 | } | |||
371 | offset_to_index = ntohl(stack.last_offset)__bswap_32 (stack.last_offset); | |||
372 | ||||
373 | sizeOfEntireMAR = | |||
374 | ((uint64_t)stack.last_offset) + stack.size_used + sizeof(size_of_index); | |||
375 | sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR)(((((uint64_t)sizeOfEntireMAR) & 0xFF) << 56) | ((( (uint64_t)sizeOfEntireMAR) >> 8) & 0xFF) << 48 ) | (((((uint64_t)sizeOfEntireMAR) >> 16) & 0xFF) << 40) | (((((uint64_t)sizeOfEntireMAR) >> 24) & 0xFF ) << 32) | (((((uint64_t)sizeOfEntireMAR) >> 32) & 0xFF) << 24) | (((((uint64_t)sizeOfEntireMAR) >> 40) & 0xFF) << 16) | (((((uint64_t)sizeOfEntireMAR ) >> 48) & 0xFF) << 8) | (((uint64_t)sizeOfEntireMAR ) >> 56); | |||
376 | if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1) { | |||
377 | goto failure; | |||
378 | } | |||
379 | sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR)(((((uint64_t)sizeOfEntireMAR) & 0xFF) << 56) | ((( (uint64_t)sizeOfEntireMAR) >> 8) & 0xFF) << 48 ) | (((((uint64_t)sizeOfEntireMAR) >> 16) & 0xFF) << 40) | (((((uint64_t)sizeOfEntireMAR) >> 24) & 0xFF ) << 32) | (((((uint64_t)sizeOfEntireMAR) >> 32) & 0xFF) << 24) | (((((uint64_t)sizeOfEntireMAR) >> 40) & 0xFF) << 16) | (((((uint64_t)sizeOfEntireMAR ) >> 48) & 0xFF) << 8) | (((uint64_t)sizeOfEntireMAR ) >> 56); | |||
380 | ||||
381 | rv = 0; | |||
382 | failure: | |||
383 | if (stack.head) { | |||
384 | free(stack.head); | |||
385 | } | |||
386 | fclose(fp); | |||
387 | if (rv) { | |||
388 | remove(dest); | |||
389 | } | |||
390 | return rv; | |||
391 | } |