| File: | var/lib/jenkins/workspace/firefox-scan-build/modules/libmar/src/mar_create.c |
| Warning: | line 89, column 17 Read function called when stream is in EOF state. Function has no effect |
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 | } |