1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/modules/libmar/src/mar_create.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,397 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include <sys/types.h> 1.11 +#include <sys/stat.h> 1.12 +#include <fcntl.h> 1.13 +#include <stdlib.h> 1.14 +#include <string.h> 1.15 +#include "mar_private.h" 1.16 +#include "mar_cmdline.h" 1.17 +#include "mar.h" 1.18 + 1.19 +#ifdef XP_WIN 1.20 +#include <winsock2.h> 1.21 +#else 1.22 +#include <netinet/in.h> 1.23 +#include <unistd.h> 1.24 +#endif 1.25 + 1.26 +struct MarItemStack { 1.27 + void *head; 1.28 + uint32_t size_used; 1.29 + uint32_t size_allocated; 1.30 + uint32_t last_offset; 1.31 +}; 1.32 + 1.33 +/** 1.34 + * Push a new item onto the stack of items. The stack is a single block 1.35 + * of memory. 1.36 + */ 1.37 +static int mar_push(struct MarItemStack *stack, uint32_t length, uint32_t flags, 1.38 + const char *name) { 1.39 + int namelen; 1.40 + uint32_t n_offset, n_length, n_flags; 1.41 + uint32_t size; 1.42 + char *data; 1.43 + 1.44 + namelen = strlen(name); 1.45 + size = MAR_ITEM_SIZE(namelen); 1.46 + 1.47 + if (stack->size_allocated - stack->size_used < size) { 1.48 + /* increase size of stack */ 1.49 + size_t size_needed = ROUND_UP(stack->size_used + size, BLOCKSIZE); 1.50 + stack->head = realloc(stack->head, size_needed); 1.51 + if (!stack->head) 1.52 + return -1; 1.53 + stack->size_allocated = size_needed; 1.54 + } 1.55 + 1.56 + data = (((char *) stack->head) + stack->size_used); 1.57 + 1.58 + n_offset = htonl(stack->last_offset); 1.59 + n_length = htonl(length); 1.60 + n_flags = htonl(flags); 1.61 + 1.62 + memcpy(data, &n_offset, sizeof(n_offset)); 1.63 + data += sizeof(n_offset); 1.64 + 1.65 + memcpy(data, &n_length, sizeof(n_length)); 1.66 + data += sizeof(n_length); 1.67 + 1.68 + memcpy(data, &n_flags, sizeof(n_flags)); 1.69 + data += sizeof(n_flags); 1.70 + 1.71 + memcpy(data, name, namelen + 1); 1.72 + 1.73 + stack->size_used += size; 1.74 + stack->last_offset += length; 1.75 + return 0; 1.76 +} 1.77 + 1.78 +static int mar_concat_file(FILE *fp, const char *path) { 1.79 + FILE *in; 1.80 + char buf[BLOCKSIZE]; 1.81 + size_t len; 1.82 + int rv = 0; 1.83 + 1.84 + in = fopen(path, "rb"); 1.85 + if (!in) { 1.86 + fprintf(stderr, "ERROR: could not open file in mar_concat_file()\n"); 1.87 + perror(path); 1.88 + return -1; 1.89 + } 1.90 + 1.91 + while ((len = fread(buf, 1, BLOCKSIZE, in)) > 0) { 1.92 + if (fwrite(buf, len, 1, fp) != 1) { 1.93 + rv = -1; 1.94 + break; 1.95 + } 1.96 + } 1.97 + 1.98 + fclose(in); 1.99 + return rv; 1.100 +} 1.101 + 1.102 +/** 1.103 + * Writes out the product information block to the specified file. 1.104 + * 1.105 + * @param fp The opened MAR file being created. 1.106 + * @param stack A pointer to the MAR item stack being used to create 1.107 + * the MAR 1.108 + * @param infoBlock The product info block to store in the file. 1.109 + * @return 0 on success. 1.110 +*/ 1.111 +static int 1.112 +mar_concat_product_info_block(FILE *fp, 1.113 + struct MarItemStack *stack, 1.114 + struct ProductInformationBlock *infoBlock) 1.115 +{ 1.116 + char buf[PIB_MAX_MAR_CHANNEL_ID_SIZE + PIB_MAX_PRODUCT_VERSION_SIZE]; 1.117 + uint32_t additionalBlockID = 1, infoBlockSize, unused; 1.118 + if (!fp || !infoBlock || 1.119 + !infoBlock->MARChannelID || 1.120 + !infoBlock->productVersion) { 1.121 + return -1; 1.122 + } 1.123 + 1.124 + /* The MAR channel name must be < 64 bytes per the spec */ 1.125 + if (strlen(infoBlock->MARChannelID) > PIB_MAX_MAR_CHANNEL_ID_SIZE) { 1.126 + return -1; 1.127 + } 1.128 + 1.129 + /* The product version must be < 32 bytes per the spec */ 1.130 + if (strlen(infoBlock->productVersion) > PIB_MAX_PRODUCT_VERSION_SIZE) { 1.131 + return -1; 1.132 + } 1.133 + 1.134 + /* Although we don't need the product information block size to include the 1.135 + maximum MAR channel name and product version, we allocate the maximum 1.136 + amount to make it easier to modify the MAR file for repurposing MAR files 1.137 + to different MAR channels. + 2 is for the NULL terminators. */ 1.138 + infoBlockSize = sizeof(infoBlockSize) + 1.139 + sizeof(additionalBlockID) + 1.140 + PIB_MAX_MAR_CHANNEL_ID_SIZE + 1.141 + PIB_MAX_PRODUCT_VERSION_SIZE + 2; 1.142 + if (stack) { 1.143 + stack->last_offset += infoBlockSize; 1.144 + } 1.145 + 1.146 + /* Write out the product info block size */ 1.147 + infoBlockSize = htonl(infoBlockSize); 1.148 + if (fwrite(&infoBlockSize, 1.149 + sizeof(infoBlockSize), 1, fp) != 1) { 1.150 + return -1; 1.151 + } 1.152 + infoBlockSize = ntohl(infoBlockSize); 1.153 + 1.154 + /* Write out the product info block ID */ 1.155 + additionalBlockID = htonl(additionalBlockID); 1.156 + if (fwrite(&additionalBlockID, 1.157 + sizeof(additionalBlockID), 1, fp) != 1) { 1.158 + return -1; 1.159 + } 1.160 + additionalBlockID = ntohl(additionalBlockID); 1.161 + 1.162 + /* Write out the channel name and NULL terminator */ 1.163 + if (fwrite(infoBlock->MARChannelID, 1.164 + strlen(infoBlock->MARChannelID) + 1, 1, fp) != 1) { 1.165 + return -1; 1.166 + } 1.167 + 1.168 + /* Write out the product version string and NULL terminator */ 1.169 + if (fwrite(infoBlock->productVersion, 1.170 + strlen(infoBlock->productVersion) + 1, 1, fp) != 1) { 1.171 + return -1; 1.172 + } 1.173 + 1.174 + /* Write out the rest of the block that is unused */ 1.175 + unused = infoBlockSize - (sizeof(infoBlockSize) + 1.176 + sizeof(additionalBlockID) + 1.177 + strlen(infoBlock->MARChannelID) + 1.178 + strlen(infoBlock->productVersion) + 2); 1.179 + memset(buf, 0, sizeof(buf)); 1.180 + if (fwrite(buf, unused, 1, fp) != 1) { 1.181 + return -1; 1.182 + } 1.183 + return 0; 1.184 +} 1.185 + 1.186 +/** 1.187 + * Refreshes the product information block with the new information. 1.188 + * The input MAR must not be signed or the function call will fail. 1.189 + * 1.190 + * @param path The path to the MAR file whose product info block 1.191 + * should be refreshed. 1.192 + * @param infoBlock Out parameter for where to store the result to 1.193 + * @return 0 on success, -1 on failure 1.194 +*/ 1.195 +int 1.196 +refresh_product_info_block(const char *path, 1.197 + struct ProductInformationBlock *infoBlock) 1.198 +{ 1.199 + FILE *fp ; 1.200 + int rv; 1.201 + uint32_t numSignatures, additionalBlockSize, additionalBlockID, 1.202 + offsetAdditionalBlocks, numAdditionalBlocks, i; 1.203 + int additionalBlocks, hasSignatureBlock; 1.204 + int64_t oldPos; 1.205 + 1.206 + rv = get_mar_file_info(path, 1.207 + &hasSignatureBlock, 1.208 + &numSignatures, 1.209 + &additionalBlocks, 1.210 + &offsetAdditionalBlocks, 1.211 + &numAdditionalBlocks); 1.212 + if (rv) { 1.213 + fprintf(stderr, "ERROR: Could not obtain MAR information.\n"); 1.214 + return -1; 1.215 + } 1.216 + 1.217 + if (hasSignatureBlock && numSignatures) { 1.218 + fprintf(stderr, "ERROR: Cannot refresh a signed MAR\n"); 1.219 + return -1; 1.220 + } 1.221 + 1.222 + fp = fopen(path, "r+b"); 1.223 + if (!fp) { 1.224 + fprintf(stderr, "ERROR: could not open target file: %s\n", path); 1.225 + return -1; 1.226 + } 1.227 + 1.228 + if (fseeko(fp, offsetAdditionalBlocks, SEEK_SET)) { 1.229 + fprintf(stderr, "ERROR: could not seek to additional blocks\n"); 1.230 + fclose(fp); 1.231 + return -1; 1.232 + } 1.233 + 1.234 + for (i = 0; i < numAdditionalBlocks; ++i) { 1.235 + /* Get the position of the start of this block */ 1.236 + oldPos = ftello(fp); 1.237 + 1.238 + /* Read the additional block size */ 1.239 + if (fread(&additionalBlockSize, 1.240 + sizeof(additionalBlockSize), 1.241 + 1, fp) != 1) { 1.242 + return -1; 1.243 + } 1.244 + additionalBlockSize = ntohl(additionalBlockSize); 1.245 + 1.246 + /* Read the additional block ID */ 1.247 + if (fread(&additionalBlockID, 1.248 + sizeof(additionalBlockID), 1.249 + 1, fp) != 1) { 1.250 + return -1; 1.251 + } 1.252 + additionalBlockID = ntohl(additionalBlockID); 1.253 + 1.254 + if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) { 1.255 + if (fseeko(fp, oldPos, SEEK_SET)) { 1.256 + fprintf(stderr, "Could not seek back to Product Information Block\n"); 1.257 + fclose(fp); 1.258 + return -1; 1.259 + } 1.260 + 1.261 + if (mar_concat_product_info_block(fp, NULL, infoBlock)) { 1.262 + fprintf(stderr, "Could not concat Product Information Block\n"); 1.263 + fclose(fp); 1.264 + return -1; 1.265 + } 1.266 + 1.267 + fclose(fp); 1.268 + return 0; 1.269 + } else { 1.270 + /* This is not the additional block you're looking for. Move along. */ 1.271 + if (fseek(fp, additionalBlockSize, SEEK_CUR)) { 1.272 + fprintf(stderr, "ERROR: Could not seek past current block.\n"); 1.273 + fclose(fp); 1.274 + return -1; 1.275 + } 1.276 + } 1.277 + } 1.278 + 1.279 + /* If we had a product info block we would have already returned */ 1.280 + fclose(fp); 1.281 + fprintf(stderr, "ERROR: Could not refresh because block does not exist\n"); 1.282 + return -1; 1.283 +} 1.284 + 1.285 +/** 1.286 + * Create a MAR file from a set of files. 1.287 + * @param dest The path to the file to create. This path must be 1.288 + * compatible with fopen. 1.289 + * @param numfiles The number of files to store in the archive. 1.290 + * @param files The list of null-terminated file paths. Each file 1.291 + * path must be compatible with fopen. 1.292 + * @param infoBlock The information to store in the product information block. 1.293 + * @return A non-zero value if an error occurs. 1.294 + */ 1.295 +int mar_create(const char *dest, int 1.296 + num_files, char **files, 1.297 + struct ProductInformationBlock *infoBlock) { 1.298 + struct MarItemStack stack; 1.299 + uint32_t offset_to_index = 0, size_of_index, 1.300 + numSignatures, numAdditionalSections; 1.301 + uint64_t sizeOfEntireMAR = 0; 1.302 + struct stat st; 1.303 + FILE *fp; 1.304 + int i, rv = -1; 1.305 + 1.306 + memset(&stack, 0, sizeof(stack)); 1.307 + 1.308 + fp = fopen(dest, "wb"); 1.309 + if (!fp) { 1.310 + fprintf(stderr, "ERROR: could not create target file: %s\n", dest); 1.311 + return -1; 1.312 + } 1.313 + 1.314 + if (fwrite(MAR_ID, MAR_ID_SIZE, 1, fp) != 1) 1.315 + goto failure; 1.316 + if (fwrite(&offset_to_index, sizeof(uint32_t), 1, fp) != 1) 1.317 + goto failure; 1.318 + 1.319 + stack.last_offset = MAR_ID_SIZE + 1.320 + sizeof(offset_to_index) + 1.321 + sizeof(numSignatures) + 1.322 + sizeof(numAdditionalSections) + 1.323 + sizeof(sizeOfEntireMAR); 1.324 + 1.325 + /* We will circle back on this at the end of the MAR creation to fill it */ 1.326 + if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1) { 1.327 + goto failure; 1.328 + } 1.329 + 1.330 + /* Write out the number of signatures, for now only at most 1 is supported */ 1.331 + numSignatures = 0; 1.332 + if (fwrite(&numSignatures, sizeof(numSignatures), 1, fp) != 1) { 1.333 + goto failure; 1.334 + } 1.335 + 1.336 + /* Write out the number of additional sections, for now just 1 1.337 + for the product info block */ 1.338 + numAdditionalSections = htonl(1); 1.339 + if (fwrite(&numAdditionalSections, 1.340 + sizeof(numAdditionalSections), 1, fp) != 1) { 1.341 + goto failure; 1.342 + } 1.343 + numAdditionalSections = ntohl(numAdditionalSections); 1.344 + 1.345 + if (mar_concat_product_info_block(fp, &stack, infoBlock)) { 1.346 + goto failure; 1.347 + } 1.348 + 1.349 + for (i = 0; i < num_files; ++i) { 1.350 + if (stat(files[i], &st)) { 1.351 + fprintf(stderr, "ERROR: file not found: %s\n", files[i]); 1.352 + goto failure; 1.353 + } 1.354 + 1.355 + if (mar_push(&stack, st.st_size, st.st_mode & 0777, files[i])) 1.356 + goto failure; 1.357 + 1.358 + /* concatenate input file to archive */ 1.359 + if (mar_concat_file(fp, files[i])) 1.360 + goto failure; 1.361 + } 1.362 + 1.363 + /* write out the index (prefixed with length of index) */ 1.364 + size_of_index = htonl(stack.size_used); 1.365 + if (fwrite(&size_of_index, sizeof(size_of_index), 1, fp) != 1) 1.366 + goto failure; 1.367 + if (fwrite(stack.head, stack.size_used, 1, fp) != 1) 1.368 + goto failure; 1.369 + 1.370 + /* To protect against invalid MAR files, we assumes that the MAR file 1.371 + size is less than or equal to MAX_SIZE_OF_MAR_FILE. */ 1.372 + if (ftell(fp) > MAX_SIZE_OF_MAR_FILE) { 1.373 + goto failure; 1.374 + } 1.375 + 1.376 + /* write out offset to index file in network byte order */ 1.377 + offset_to_index = htonl(stack.last_offset); 1.378 + if (fseek(fp, MAR_ID_SIZE, SEEK_SET)) 1.379 + goto failure; 1.380 + if (fwrite(&offset_to_index, sizeof(offset_to_index), 1, fp) != 1) 1.381 + goto failure; 1.382 + offset_to_index = ntohl(stack.last_offset); 1.383 + 1.384 + sizeOfEntireMAR = ((uint64_t)stack.last_offset) + 1.385 + stack.size_used + 1.386 + sizeof(size_of_index); 1.387 + sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR); 1.388 + if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1) 1.389 + goto failure; 1.390 + sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR); 1.391 + 1.392 + rv = 0; 1.393 +failure: 1.394 + if (stack.head) 1.395 + free(stack.head); 1.396 + fclose(fp); 1.397 + if (rv) 1.398 + remove(dest); 1.399 + return rv; 1.400 +}