LibreOffice Module onlineupdate (master) 1
mar_create.c
Go to the documentation of this file.
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>
14#include <onlineupdate/mar.h>
15
16#ifdef _WIN32
17#include <winsock2.h>
18#else
19#include <netinet/in.h>
20#include <unistd.h>
21#endif
22
24 void *head;
25 uint32_t size_used;
27 uint32_t last_offset;
28};
29
34static 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);
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);
47 stack->head = realloc(stack->head, size_needed);
48 if (!stack->head)
49 return -1;
50 stack->size_allocated = size_needed;
51 }
52
53 data = (((char *) stack->head) + stack->size_used);
54
55 n_offset = htonl(stack->last_offset);
56 n_length = htonl(length);
57 n_flags = htonl(flags);
58
59 memcpy(data, &n_offset, sizeof(n_offset));
60 data += sizeof(n_offset);
61
62 memcpy(data, &n_length, sizeof(n_length));
63 data += sizeof(n_length);
64
65 memcpy(data, &n_flags, sizeof(n_flags));
66 data += sizeof(n_flags);
67
68 memcpy(data, name, namelen + 1);
69
70 stack->size_used += size;
71 stack->last_offset += length;
72 return 0;
73}
74
75static int mar_concat_file(FILE *fp, const char *path) {
76 FILE *in;
77 char buf[BLOCKSIZE];
78 size_t len;
79 int rv = 0;
80
81 in = fopen(path, "rb");
82 if (!in) {
83 fprintf(stderr, "ERROR: could not open file in mar_concat_file()\n");
84 perror(path);
85 return -1;
86 }
87
88 while ((len = fread(buf, 1, BLOCKSIZE, in)) > 0) {
89 if (fwrite(buf, len, 1, fp) != 1) {
90 rv = -1;
91 break;
92 }
93 }
94
95 fclose(in);
96 return rv;
97}
98
108static int
110 struct MarItemStack *stack,
111 struct ProductInformationBlock *infoBlock)
112{
114 uint32_t additionalBlockID = 1, infoBlockSize, unused;
115 if (!fp || !infoBlock ||
116 !infoBlock->MARChannelID ||
117 !infoBlock->productVersion) {
118 return -1;
119 }
120
121 /* The MAR channel name must be < 64 bytes per the spec */
122 if (strlen(infoBlock->MARChannelID) > PIB_MAX_MAR_CHANNEL_ID_SIZE) {
123 return -1;
124 }
125
126 /* The product version must be < 32 bytes per the spec */
127 if (strlen(infoBlock->productVersion) > PIB_MAX_PRODUCT_VERSION_SIZE) {
128 return -1;
129 }
130
131 /* Although we don't need the product information block size to include the
132 maximum MAR channel name and product version, we allocate the maximum
133 amount to make it easier to modify the MAR file for repurposing MAR files
134 to different MAR channels. + 2 is for the NULL terminators. */
135 infoBlockSize = sizeof(infoBlockSize) +
136 sizeof(additionalBlockID) +
139 if (stack) {
140 stack->last_offset += infoBlockSize;
141 }
142
143 /* Write out the product info block size */
144 infoBlockSize = htonl(infoBlockSize);
145 if (fwrite(&infoBlockSize,
146 sizeof(infoBlockSize), 1, fp) != 1) {
147 return -1;
148 }
149 infoBlockSize = ntohl(infoBlockSize);
150
151 /* Write out the product info block ID */
152 additionalBlockID = htonl(additionalBlockID);
153 if (fwrite(&additionalBlockID,
154 sizeof(additionalBlockID), 1, fp) != 1) {
155 return -1;
156 }
157 additionalBlockID = ntohl(additionalBlockID);
158
159 /* Write out the channel name and NULL terminator */
160 if (fwrite(infoBlock->MARChannelID,
161 strlen(infoBlock->MARChannelID) + 1, 1, fp) != 1) {
162 return -1;
163 }
164
165 /* Write out the product version string and NULL terminator */
166 if (fwrite(infoBlock->productVersion,
167 strlen(infoBlock->productVersion) + 1, 1, fp) != 1) {
168 return -1;
169 }
170
171 /* Write out the rest of the block that is unused */
172 unused = infoBlockSize - (sizeof(infoBlockSize) +
173 sizeof(additionalBlockID) +
174 strlen(infoBlock->MARChannelID) +
175 strlen(infoBlock->productVersion) + 2);
176 memset(buf, 0, sizeof(buf));
177 if (fwrite(buf, unused, 1, fp) != 1) {
178 return -1;
179 }
180 return 0;
181}
182
192int
194 struct ProductInformationBlock *infoBlock)
195{
196 FILE *fp ;
197 int rv;
198 uint32_t numSignatures, additionalBlockSize, additionalBlockID,
199 offsetAdditionalBlocks, numAdditionalBlocks, i;
200 int additionalBlocks, hasSignatureBlock;
201
202 rv = get_mar_file_info(path,
203 &hasSignatureBlock,
204 &numSignatures,
205 &additionalBlocks,
206 &offsetAdditionalBlocks,
207 &numAdditionalBlocks);
208 if (rv) {
209 fprintf(stderr, "ERROR: Could not obtain MAR information.\n");
210 return -1;
211 }
212
213 if (hasSignatureBlock && numSignatures) {
214 fprintf(stderr, "ERROR: Cannot refresh a signed MAR\n");
215 return -1;
216 }
217
218 fp = fopen(path, "r+b");
219 if (!fp) {
220 fprintf(stderr, "ERROR: could not open target file: %s\n", path);
221 return -1;
222 }
223
224 if (fseeko(fp, offsetAdditionalBlocks, SEEK_SET)) {
225 fprintf(stderr, "ERROR: could not seek to additional blocks\n");
226 fclose(fp);
227 return -1;
228 }
229
230 for (i = 0; i < numAdditionalBlocks; ++i) {
231 /* Get the position of the start of this block */
232 int64_t oldPos = ftello(fp);
233
234 /* Read the additional block size */
235 if (fread(&additionalBlockSize,
236 sizeof(additionalBlockSize),
237 1, fp) != 1) {
238 fclose(fp);
239 return -1;
240 }
241 additionalBlockSize = ntohl(additionalBlockSize);
242
243 /* Read the additional block ID */
244 if (fread(&additionalBlockID,
245 sizeof(additionalBlockID),
246 1, fp) != 1) {
247 fclose(fp);
248 return -1;
249 }
250 additionalBlockID = ntohl(additionalBlockID);
251
252 if (PRODUCT_INFO_BLOCK_ID == additionalBlockID) {
253 if (fseeko(fp, oldPos, SEEK_SET)) {
254 fprintf(stderr, "Could not seek back to Product Information Block\n");
255 fclose(fp);
256 return -1;
257 }
258
259 if (mar_concat_product_info_block(fp, NULL, infoBlock)) {
260 fprintf(stderr, "Could not concat Product Information Block\n");
261 fclose(fp);
262 return -1;
263 }
264
265 fclose(fp);
266 return 0;
267 } else {
268 /* This is not the additional block you're looking for. Move along. */
269 if (fseek(fp, additionalBlockSize, SEEK_CUR)) {
270 fprintf(stderr, "ERROR: Could not seek past current block.\n");
271 fclose(fp);
272 return -1;
273 }
274 }
275 }
276
277 /* If we had a product info block we would have already returned */
278 fclose(fp);
279 fprintf(stderr, "ERROR: Could not refresh because block does not exist\n");
280 return -1;
281}
282
293int mar_create(const char *dest, int
294 num_files, char **files,
295 struct ProductInformationBlock *infoBlock) {
296 struct MarItemStack stack;
297 uint32_t offset_to_index = 0, size_of_index,
298 numSignatures, numAdditionalSections;
299 uint64_t sizeOfEntireMAR = 0;
300 struct stat st;
301 FILE *fp;
302 int i, rv = -1;
303
304 memset(&stack, 0, sizeof(stack));
305
306 fp = fopen(dest, "wb");
307 if (!fp) {
308 fprintf(stderr, "ERROR: could not create target file: %s\n", dest);
309 return -1;
310 }
311
312 if (fwrite(MAR_ID, MAR_ID_SIZE, 1, fp) != 1)
313 goto failure;
314 if (fwrite(&offset_to_index, sizeof(uint32_t), 1, fp) != 1)
315 goto failure;
316
317 stack.last_offset = MAR_ID_SIZE +
318 sizeof(offset_to_index) +
319 sizeof(numSignatures) +
320 sizeof(numAdditionalSections) +
321 sizeof(sizeOfEntireMAR);
322
323 /* We will circle back on this at the end of the MAR creation to fill it */
324 if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1) {
325 goto failure;
326 }
327
328 /* Write out the number of signatures, for now only at most 1 is supported */
329 numSignatures = 0;
330 if (fwrite(&numSignatures, sizeof(numSignatures), 1, fp) != 1) {
331 goto failure;
332 }
333
334 /* Write out the number of additional sections, for now just 1
335 for the product info block */
336 numAdditionalSections = htonl(1);
337 if (fwrite(&numAdditionalSections,
338 sizeof(numAdditionalSections), 1, fp) != 1) {
339 goto failure;
340 }
341 numAdditionalSections = ntohl(numAdditionalSections);
342
343 if (mar_concat_product_info_block(fp, &stack, infoBlock)) {
344 goto failure;
345 }
346
347 for (i = 0; i < num_files; ++i) {
348 if (stat(files[i], &st)) {
349 fprintf(stderr, "ERROR: file not found: %s\n", files[i]);
350 goto failure;
351 }
352
353 if (mar_push(&stack, st.st_size, st.st_mode & 0777, files[i]))
354 goto failure;
355
356 /* concatenate input file to archive */
357 if (mar_concat_file(fp, files[i]))
358 goto failure;
359 }
360
361 /* write out the index (prefixed with length of index) */
362 size_of_index = htonl(stack.size_used);
363 if (fwrite(&size_of_index, sizeof(size_of_index), 1, fp) != 1)
364 goto failure;
365 if (fwrite(stack.head, stack.size_used, 1, fp) != 1)
366 goto failure;
367
368 /* To protect against invalid MAR files, we assume that the MAR file
369 size is less than or equal to MAX_SIZE_OF_MAR_FILE. */
370 if (ftell(fp) > MAX_SIZE_OF_MAR_FILE) {
371 goto failure;
372 }
373
374 /* write out offset to index file in network byte order */
375 offset_to_index = htonl(stack.last_offset);
376 if (fseek(fp, MAR_ID_SIZE, SEEK_SET))
377 goto failure;
378 if (fwrite(&offset_to_index, sizeof(offset_to_index), 1, fp) != 1)
379 goto failure;
380 offset_to_index = ntohl(stack.last_offset);
381
382 sizeOfEntireMAR = ((uint64_t)stack.last_offset) +
383 stack.size_used +
384 sizeof(size_of_index);
385 sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR);
386 if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fp) != 1)
387 goto failure;
388 sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR);
389
390 rv = 0;
391failure:
392 if (stack.head)
393 free(stack.head);
394 fclose(fp);
395 if (rv)
396 remove(dest);
397 return rv;
398}
const char * name
return NULL
static int mar_push(struct MarItemStack *stack, uint32_t length, uint32_t flags, const char *name)
Push a new item onto the stack of items.
Definition: mar_create.c:34
static int mar_concat_file(FILE *fp, const char *path)
Definition: mar_create.c:75
static int mar_concat_product_info_block(FILE *fp, struct MarItemStack *stack, struct ProductInformationBlock *infoBlock)
Writes out the product information block to the specified file.
Definition: mar_create.c:109
int refresh_product_info_block(const char *path, struct ProductInformationBlock *infoBlock)
Refreshes the product information block with the new information.
Definition: mar_create.c:193
int mar_create(const char *dest, int num_files, char **files, struct ProductInformationBlock *infoBlock)
Create a MAR file from a set of files.
Definition: mar_create.c:293
#define NETWORK_TO_HOST64
Definition: mar_private.h:77
#define MAX_SIZE_OF_MAR_FILE
Definition: mar_private.h:26
#define PRODUCT_INFO_BLOCK_ID
Definition: mar_private.h:45
#define PIB_MAX_PRODUCT_VERSION_SIZE
Definition: mar_private.h:51
#define MAR_ID_SIZE
Definition: mar_private.h:18
#define ROUND_UP(n, incr)
Definition: mar_private.h:15
#define MAR_ITEM_SIZE(namelen)
Definition: mar_private.h:47
#define HOST_TO_NETWORK64(x)
Definition: mar_private.h:68
#define MAR_ID
Definition: mar_private.h:17
#define PIB_MAX_MAR_CHANNEL_ID_SIZE
Definition: mar_private.h:50
#define BLOCKSIZE
Definition: mar_private.h:14
int get_mar_file_info(const char *path, int *hasSignatureBlock, uint32_t *numSignatures, int *hasAdditionalBlocks, uint32_t *offsetAdditionalBlocks, uint32_t *numAdditionalBlocks)
Determines the MAR file information.
Definition: mar_read.c:551
size
OUStringBuffer & remove(OUStringBuffer &rIn, sal_Unicode c)
int i
sal_uInt32 htonl(sal_uInt32 h)
sal_uInt32 ntohl(sal_uInt32 n)
uint32_t size_used
Definition: mar_create.c:25
uint32_t size_allocated
Definition: mar_create.c:26
void * head
Definition: mar_create.c:24
uint32_t last_offset
Definition: mar_create.c:27
const char * productVersion
Definition: mar.h:32
const char * MARChannelID
Definition: mar.h:31