blob: 23ed715bda5eb7af25238f9bf039901cc1639228 [file] [log] [blame]
The Android Open Source Project66339ad2009-01-15 16:12:07 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 */
19
20#include <errno.h>
21#include <libgen.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/stat.h>
26#include <sys/statfs.h>
27#include <unistd.h>
28
29#include "mincrypt/sha.h"
30#include "applypatch.h"
31
32// Read a file into memory; store it and its associated metadata in
33// *file. Return 0 on success.
34int LoadFileContents(const char* filename, FileContents* file) {
35 file->data = NULL;
36
37 if (stat(filename, &file->st) != 0) {
38 fprintf(stderr, "failed to stat \"%s\": %s\n", filename, strerror(errno));
39 return -1;
40 }
41
42 file->size = file->st.st_size;
43 file->data = malloc(file->size);
44
45 FILE* f = fopen(filename, "rb");
46 if (f == NULL) {
47 fprintf(stderr, "failed to open \"%s\": %s\n", filename, strerror(errno));
48 free(file->data);
49 return -1;
50 }
51
52 size_t bytes_read = fread(file->data, 1, file->size, f);
53 if (bytes_read != file->size) {
54 fprintf(stderr, "short read of \"%s\" (%d bytes of %d)\n",
55 filename, bytes_read, file->size);
56 free(file->data);
57 return -1;
58 }
59 fclose(f);
60
61 SHA(file->data, file->size, file->sha1);
62 return 0;
63}
64
65// Save the contents of the given FileContents object under the given
66// filename. Return 0 on success.
67int SaveFileContents(const char* filename, FileContents file) {
68 FILE* f = fopen(filename, "wb");
69 if (f == NULL) {
70 fprintf(stderr, "failed to open \"%s\" for write: %s\n",
71 filename, strerror(errno));
72 return -1;
73 }
74
75 size_t bytes_written = fwrite(file.data, 1, file.size, f);
76 if (bytes_written != file.size) {
77 fprintf(stderr, "short write of \"%s\" (%d bytes of %d)\n",
78 filename, bytes_written, file.size);
79 return -1;
80 }
81 fflush(f);
82 fsync(fileno(f));
83 fclose(f);
84
85 if (chmod(filename, file.st.st_mode) != 0) {
86 fprintf(stderr, "chmod of \"%s\" failed: %s\n", filename, strerror(errno));
87 return -1;
88 }
89 if (chown(filename, file.st.st_uid, file.st.st_gid) != 0) {
90 fprintf(stderr, "chown of \"%s\" failed: %s\n", filename, strerror(errno));
91 return -1;
92 }
93
94 return 0;
95}
96
97
98// Take a string 'str' of 40 hex digits and parse it into the 20
99// byte array 'digest'. 'str' may contain only the digest or be of
100// the form "<digest>:<anything>". Return 0 on success, -1 on any
101// error.
102int ParseSha1(const char* str, uint8_t* digest) {
103 int i;
104 const char* ps = str;
105 uint8_t* pd = digest;
106 for (i = 0; i < SHA_DIGEST_SIZE * 2; ++i, ++ps) {
107 int digit;
108 if (*ps >= '0' && *ps <= '9') {
109 digit = *ps - '0';
110 } else if (*ps >= 'a' && *ps <= 'f') {
111 digit = *ps - 'a' + 10;
112 } else if (*ps >= 'A' && *ps <= 'F') {
113 digit = *ps - 'A' + 10;
114 } else {
115 return -1;
116 }
117 if (i % 2 == 0) {
118 *pd = digit << 4;
119 } else {
120 *pd |= digit;
121 ++pd;
122 }
123 }
124 if (*ps != '\0' && *ps != ':') return -1;
125 return 0;
126}
127
128// Parse arguments (which should be of the form "<sha1>" or
129// "<sha1>:<filename>" into the array *patches, returning the number
130// of Patch objects in *num_patches. Return 0 on success.
131int ParseShaArgs(int argc, char** argv, Patch** patches, int* num_patches) {
132 *num_patches = argc;
133 *patches = malloc(*num_patches * sizeof(Patch));
134
135 int i;
136 for (i = 0; i < *num_patches; ++i) {
137 if (ParseSha1(argv[i], (*patches)[i].sha1) != 0) {
138 fprintf(stderr, "failed to parse sha1 \"%s\"\n", argv[i]);
139 return -1;
140 }
141 if (argv[i][SHA_DIGEST_SIZE*2] == '\0') {
142 (*patches)[i].patch_filename = NULL;
143 } else if (argv[i][SHA_DIGEST_SIZE*2] == ':') {
144 (*patches)[i].patch_filename = argv[i] + (SHA_DIGEST_SIZE*2+1);
145 } else {
146 fprintf(stderr, "failed to parse filename \"%s\"\n", argv[i]);
147 return -1;
148 }
149 }
150
151 return 0;
152}
153
154// Search an array of Patch objects for one matching the given sha1.
155// Return the Patch object on success, or NULL if no match is found.
156const Patch* FindMatchingPatch(uint8_t* sha1, Patch* patches, int num_patches) {
157 int i;
158 for (i = 0; i < num_patches; ++i) {
159 if (memcmp(patches[i].sha1, sha1, SHA_DIGEST_SIZE) == 0) {
160 return patches+i;
161 }
162 }
163 return NULL;
164}
165
166// Returns 0 if the contents of the file (argv[2]) or the cached file
167// match any of the sha1's on the command line (argv[3:]). Returns
168// nonzero otherwise.
169int CheckMode(int argc, char** argv) {
170 if (argc < 3) {
171 fprintf(stderr, "no filename given\n");
172 return 2;
173 }
174
175 int num_patches;
176 Patch* patches;
177 if (ParseShaArgs(argc-3, argv+3, &patches, &num_patches) != 0) { return 1; }
178
179 FileContents file;
180 file.data = NULL;
181
182 if (LoadFileContents(argv[2], &file) != 0 ||
183 FindMatchingPatch(file.sha1, patches, num_patches) == NULL) {
184 fprintf(stderr, "file \"%s\" doesn't have any of expected "
185 "sha1 sums; checking cache\n", argv[2]);
186
187 free(file.data);
188
189 // If the source file is missing or corrupted, it might be because
190 // we were killed in the middle of patching it. A copy of it
191 // should have been made in CACHE_TEMP_SOURCE. If that file
192 // exists and matches the sha1 we're looking for, the check still
193 // passes.
194
195 if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
196 fprintf(stderr, "failed to load cache file\n");
197 return 1;
198 }
199
200 if (FindMatchingPatch(file.sha1, patches, num_patches) == NULL) {
201 fprintf(stderr, "cache bits don't match any sha1 for \"%s\"\n",
202 argv[2]);
203 return 1;
204 }
205 }
206
207 free(file.data);
208 return 0;
209}
210
211int ShowLicenses() {
212 puts("\nCopyright (C) 2008 The Android Open Source Project\n"
213 "\n"
214 "This program is free software; you can redistribute it and/or\n"
215 "modify it under the terms of the GNU General Public License\n"
216 "as published by the Free Software Foundation; either version 2\n"
217 "of the License, or (at your option) any later version.\n"
218 "\n"
219 "This program is distributed in the hope that it will be useful,\n"
220 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
221 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
222 "GNU General Public License for more details.\n"
223 "\n"
224 "You should have received a copy of the GNU General Public License\n"
225 "along with this program; if not, write to the Free Software\n"
226 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA\n"
227 "02110-1301, USA.\n"
228 "\n------------------\n"
229 );
230 ShowBSDiffLicense();
231 return 0;
232}
233
234// Return the amount of free space (in bytes) on the filesystem
235// containing filename. filename must exist. Return -1 on error.
236size_t FreeSpaceForFile(const char* filename) {
237 struct statfs sf;
238 if (statfs(filename, &sf) != 0) {
239 fprintf(stderr, "failed to statfs %s: %s\n", filename, strerror(errno));
240 return -1;
241 }
242 return sf.f_bsize * sf.f_bfree;
243}
244
245// This program applies binary patches to files in a way that is safe
246// (the original file is not touched until we have the desired
247// replacement for it) and idempotent (it's okay to run this program
248// multiple times).
249//
250// - if the sha1 hash of <file> is <tgt-sha1>, does nothing and exits
251// successfully.
252//
253// - otherwise, if the sha1 hash of <file> is <src-sha1>, applies the
254// xdelta3 or bsdiff <patch> to <file> to produce a new file (the
255// type of patch is automatically detected from the file header).
256// If that new file has sha1 hash <tgt-sha1>, moves it to replace
257// <file>, and exits successfully.
258//
259// - otherwise, or if any error is encountered, exits with non-zero
260// status.
261
262int main(int argc, char** argv) {
263 if (argc < 2) {
264 usage:
265 fprintf(stderr, "usage: %s <file> <tgt-sha1> <tgt-size> [<src-sha1>:<patch> ...]\n"
266 " or %s -c <file> [<sha1> ...]\n"
267 " or %s -s <bytes>\n"
268 " or %s -l\n",
269 argv[0], argv[0], argv[0], argv[0]);
270 return 1;
271 }
272
273 if (strncmp(argv[1], "-l", 3) == 0) {
274 return ShowLicenses();
275 }
276
277 if (strncmp(argv[1], "-c", 3) == 0) {
278 return CheckMode(argc, argv);
279 }
280
281 if (strncmp(argv[1], "-s", 3) == 0) {
282 if (argc != 3) {
283 goto usage;
284 }
285 size_t bytes = strtol(argv[2], NULL, 10);
286 if (MakeFreeSpaceOnCache(bytes) < 0) {
287 printf("unable to make %ld bytes available on /cache\n", (long)bytes);
288 return 1;
289 } else {
290 return 0;
291 }
292 }
293
294 uint8_t target_sha1[SHA_DIGEST_SIZE];
295
296 const char* source_filename = argv[1];
297
298 // assume that source_filename (eg "/system/app/Foo.apk") is located
299 // on the same filesystem as its top-level directory ("/system").
300 // We need something that exists for calling statfs().
301 char* source_fs = strdup(argv[1]);
302 char* slash = strchr(source_fs+1, '/');
303 if (slash != NULL) {
304 *slash = '\0';
305 }
306
307 if (ParseSha1(argv[2], target_sha1) != 0) {
308 fprintf(stderr, "failed to parse tgt-sha1 \"%s\"\n", argv[2]);
309 return 1;
310 }
311
312 unsigned long target_size = strtoul(argv[3], NULL, 0);
313
314 int num_patches;
315 Patch* patches;
316 if (ParseShaArgs(argc-4, argv+4, &patches, &num_patches) < 0) { return 1; }
317
318 FileContents copy_file;
319 FileContents source_file;
320 const char* source_patch_filename = NULL;
321 const char* copy_patch_filename = NULL;
322 int made_copy = 0;
323
324 if (LoadFileContents(source_filename, &source_file) == 0) {
325 if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
326 // The early-exit case: the patch was already applied, this file
327 // has the desired hash, nothing for us to do.
328 fprintf(stderr, "\"%s\" is already target; no patch needed\n",
329 source_filename);
330 return 0;
331 }
332
333 const Patch* to_use =
334 FindMatchingPatch(source_file.sha1, patches, num_patches);
335 if (to_use != NULL) {
336 source_patch_filename = to_use->patch_filename;
337 }
338 }
339
340 if (source_patch_filename == NULL) {
341 free(source_file.data);
342 fprintf(stderr, "source file is bad; trying copy\n");
343
344 if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file) < 0) {
345 // fail.
346 fprintf(stderr, "failed to read copy file\n");
347 return 1;
348 }
349
350 const Patch* to_use =
351 FindMatchingPatch(copy_file.sha1, patches, num_patches);
352 if (to_use != NULL) {
353 copy_patch_filename = to_use->patch_filename;
354 }
355
356 if (copy_patch_filename == NULL) {
357 // fail.
358 fprintf(stderr, "copy file doesn't match source SHA-1s either\n");
359 return 1;
360 }
361 }
362
363 // Is there enough room in the target filesystem to hold the patched file?
364 size_t free_space = FreeSpaceForFile(source_fs);
365 int enough_space = free_space > (target_size * 3 / 2); // 50% margin of error
366 printf("target %ld bytes; free space %ld bytes; enough %d\n",
367 (long)target_size, (long)free_space, enough_space);
368
369 if (!enough_space && source_patch_filename != NULL) {
370 // Using the original source, but not enough free space. First
371 // copy the source file to cache, then delete it from the original
372 // location.
373 if (MakeFreeSpaceOnCache(source_file.size) < 0) {
374 fprintf(stderr, "not enough free space on /cache\n");
375 return 1;
376 }
377
378 if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
379 fprintf(stderr, "failed to back up source file\n");
380 return 1;
381 }
382 made_copy = 1;
383 unlink(source_filename);
384
385 size_t free_space = FreeSpaceForFile(source_fs);
386 printf("(now %ld bytes free for source)\n", (long)free_space);
387 }
388
389 FileContents* source_to_use;
390 const char* patch_filename;
391 if (source_patch_filename != NULL) {
392 source_to_use = &source_file;
393 patch_filename = source_patch_filename;
394 } else {
395 source_to_use = &copy_file;
396 patch_filename = copy_patch_filename;
397 }
398
399 // We write the decoded output to "<file>.patch".
400 char* outname = (char*)malloc(strlen(source_filename) + 10);
401 strcpy(outname, source_filename);
402 strcat(outname, ".patch");
403 FILE* output = fopen(outname, "wb");
404 if (output == NULL) {
405 fprintf(stderr, "failed to patch file %s: %s\n",
406 source_filename, strerror(errno));
407 return 1;
408 }
409
410#define MAX_HEADER_LENGTH 8
411 unsigned char header[MAX_HEADER_LENGTH];
412 FILE* patchf = fopen(patch_filename, "rb");
413 if (patchf == NULL) {
414 fprintf(stderr, "failed to open patch file %s: %s\n",
415 patch_filename, strerror(errno));
416 return 1;
417 }
418 int header_bytes_read = fread(header, 1, MAX_HEADER_LENGTH, patchf);
419 fclose(patchf);
420
421 SHA_CTX ctx;
422 SHA_init(&ctx);
423
424 if (header_bytes_read >= 4 &&
425 header[0] == 0xd6 && header[1] == 0xc3 &&
426 header[2] == 0xc4 && header[3] == 0) {
427 // xdelta3 patches begin "VCD" (with the high bits set) followed
428 // by a zero byte (the version number).
429 int result = ApplyXDelta3Patch(source_to_use->data, source_to_use->size,
430 patch_filename, output, &ctx);
431 if (result != 0) {
432 fprintf(stderr, "ApplyXDelta3Patch failed\n");
433 return result;
434 }
435 } else if (header_bytes_read >= 8 &&
436 memcmp(header, "BSDIFF40", 8) == 0) {
437 int result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
438 patch_filename, output, &ctx);
439 if (result != 0) {
440 fprintf(stderr, "ApplyBSDiffPatch failed\n");
441 return result;
442 }
443 } else {
444 fprintf(stderr, "Unknown patch file format");
445 return 1;
446 }
447
448 fflush(output);
449 fsync(fileno(output));
450 fclose(output);
451
452 const uint8_t* current_target_sha1 = SHA_final(&ctx);
453 if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
454 fprintf(stderr, "patch did not produce expected sha1\n");
455 return 1;
456 }
457
458 // Give the .patch file the same owner, group, and mode of the
459 // original source file.
460 if (chmod(outname, source_to_use->st.st_mode) != 0) {
461 fprintf(stderr, "chmod of \"%s\" failed: %s\n", outname, strerror(errno));
462 return 1;
463 }
464 if (chown(outname, source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) {
465 fprintf(stderr, "chown of \"%s\" failed: %s\n", outname, strerror(errno));
466 return 1;
467 }
468
469 // Finally, rename the .patch file to replace the original source file.
470 if (rename(outname, source_filename) != 0) {
471 fprintf(stderr, "rename of .patch to \"%s\" failed: %s\n",
472 source_filename, strerror(errno));
473 return 1;
474 }
475
476 // If this run of applypatch created the copy, and we're here, we
477 // can delete it.
478 if (made_copy) unlink(CACHE_TEMP_SOURCE);
479
480 // Success!
481 return 0;
482}