modules/libmar/src/mar_create.c

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial