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