1 /*
2 * Copyright 2014-2017 Frank Hunleth
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <getopt.h>
20 #include <inttypes.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28
29 #include "monocypher.h"
30
31 #include "3rdparty/base64.h"
32 #include "block_cache.h"
33 #include "mmc.h"
34 #include "util.h"
35 #include "fwup_apply.h"
36 #include "fwup_create.h"
37 #include "fwup_list.h"
38 #include "fwup_metadata.h"
39 #include "fwup_genkeys.h"
40 #include "fwup_sign.h"
41 #include "fwup_verify.h"
42 #include "progress.h"
43 #include "simple_string.h"
44 #include "sparse_file.h"
45 #include "config.h"
46
47 // Global options
48 bool fwup_verbose = false;
49 bool fwup_framing = false;
50 bool fwup_unsafe = false;
51 bool fwup_handshake_on_exit = false;
52 enum fwup_progress_option fwup_progress_mode = PROGRESS_MODE_OFF;
53
54 static bool quiet = false;
55
print_usage()56 static void print_usage()
57 {
58 #ifdef FWUP_MINIMAL
59 printf("Documentation not included in minimal builds.\n");
60 #else
61 #if defined(__APPLE__)
62 const char *example_sd = "/dev/rdisk2";
63 #elif defined(__linux__)
64 const char *example_sd = "/dev/sdc";
65 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
66 const char *example_sd = "/dev/da0";
67 #elif defined(__NetBSD__)
68 const char *example_sd = "/dev/rsc0d";
69 #elif defined(_WIN32) || defined(__CYGWIN__)
70 const char *example_sd = "\\\\.\\PhysicalDrive2";
71 #else
72 #error Fill in with an example SDCard/MMC device.
73 #endif
74 const char *program_name = PACKAGE_NAME;
75
76 printf("%s is a self-contained utility for creating and applying firmware update files.\n", program_name);
77 printf("Firmware update (.fw) files are nothing more than zip archives containing metadata,\n");
78 printf("a limited set of instructions, and data. On an embedded device or on a host PC, fwup\n");
79 printf("is used to write nonvolatile memory (eMMC, SDCards, etc.) in such a way as to upgrade\n");
80 printf("the firmware on the device or to completely initialize it.\n");
81 printf("\n");
82 printf("Usage: %s [OPTION]...\n", program_name);
83 printf("\n");
84 printf("Options:\n");
85 printf(" -a, --apply Apply the firmware update\n");
86 printf(" -c, --create Create the firmware update\n");
87 printf(" -d <file> Device file for the memory card\n");
88 printf(" -D, --detect List attached SDCards or MMC devices and their sizes\n");
89 printf(" -E, --eject Eject removable media after successfully writing firmware.\n");
90 printf(" --no-eject Do not eject media after writing firmware\n");
91 printf(" --enable-trim Enable use of the hardware TRIM command\n");
92 printf(" --exit-handshake Send a Ctrl+Z on exit and wait for stdin to close (Erlang)\n");
93 printf(" -f <fwup.conf> Specify the firmware update configuration file\n");
94 printf(" -F, --framing Apply framing on stdin/stdout\n");
95 printf(" -g, --gen-keys Generate firmware signing keys (fwup-key.pub and fwup-key.priv, or specify with -o)\n");
96 printf(" -i <input.fw> Specify the input firmware update file (Use - for stdin)\n");
97 printf(" -l, --list List the available tasks in a firmware update\n");
98 printf(" -m, --metadata Print metadata in the firmware update\n");
99 printf(" --metadata-key <key> Only output the specified key's value when printing metadata\n");
100 printf(" -n Report numeric progress\n");
101 printf(" -o <output.fw> Specify the output file when creating an update (Use - for stdout)\n");
102 printf(" -p, --public-key-file <keyfile> A public key file for verifying firmware updates (can specify multiple times)\n");
103 printf(" --private-key <key> A private key for signing firmware updates\n");
104 printf(" --progress-low <number> When displaying progress, this is the lowest number (normally 0 for 0%%)\n");
105 printf(" --progress-high <number> When displaying progress, this is the highest number (normally 100 for 100%%)\n");
106 printf(" --public-key <key> A public key for verifying firmware updates (can specify multiple times)\n");
107 printf(" -q, --quiet Quiet\n");
108 printf(" -s, --private-key-file <keyfile> A private key file for signing firmware updates\n");
109 printf(" -S, --sign Sign an existing firmware file (specify -i and -o)\n");
110 printf(" --sparse-check <path> Check if the OS and file system supports sparse files at path\n");
111 printf(" --sparse-check-size <bytes> Hole size to check for --sparse-check\n");
112 printf(" -t, --task <task> Task to apply within the firmware update\n");
113 printf(" -u, --unmount Unmount all partitions on device first\n");
114 printf(" -U, --no-unmount Do not try to unmount partitions on device\n");
115 printf(" --unsafe Allow unsafe commands (consider applying only signed archives)\n");
116 printf(" -v, --verbose Verbose\n");
117 printf(" -V, --verify Verify an existing firmware file (specify -i)\n");
118 printf(" --verify-writes Verify writes when applying firmware updates to detect corruption (default for writing to device files)\n");
119 printf(" --no-verify-writes Do not verify writes when applying firmware updates (default for regular files)\n");
120 printf(" --version Print out the version\n");
121 printf(" -y Accept automatically found memory card when applying a firmware update\n");
122 printf(" -z Print the memory card that would be automatically detected and exit\n");
123 printf(" -1 Fast compression (for create)\n");
124 printf(" -9 Best compression (default)\n");
125 printf("\n");
126 printf("Examples:\n");
127 printf("\n");
128 printf("Initialize an attached SDCard using all of the default options:\n");
129 printf("\n");
130 printf(" $ %s myfirmware.fw\n", program_name);
131 printf("\n");
132 printf("Create a firmware update archive:\n");
133 printf("\n");
134 printf(" $ %s -c -f fwup.conf -o myfirmware.fw\n", program_name);
135 printf("\n");
136 printf("Apply the firmware to an attached SDCard. This would normally be run on the host\n");
137 printf("where it would auto-detect an SDCard and initialize it using the 'complete' task:\n");
138 printf("\n");
139 printf(" $ %s -a -i myfirmware.fw -t complete\n", program_name);
140 printf("\n");
141 printf("Apply the firmware update to %s and specify the 'upgrade' task:\n", example_sd);
142 printf("\n");
143 printf(" $ %s -a -d %s -i myfirmware.fw -t upgrade\n", program_name, example_sd);
144 printf("\n");
145 printf("Create an image file from a .fw file for use with dd(1):\n");
146 printf("\n");
147 printf(" $ %s -a -d myimage.img -i myfirmware.fw -t complete\n", program_name);
148 printf("\n");
149 printf("Generate a public/private key pair:\n");
150 printf("\n");
151 printf(" $ %s -g\n", program_name);
152 printf("\n");
153 printf("Store fwup-key.priv in a safe place and fwup-key.pub on the target. To sign\n");
154 printf("an existing archive run:\n");
155 printf("\n");
156 printf(" $ %s -S -s fwup-key.priv -i myfirmware.fw -o signedfirmware.fw\n", program_name);
157 printf("\n");
158 printf("Also see the unit tests that come with fwup source code for more examples.\n");
159 printf("Obtain source code and report bugs at https://github.com/fwup-home/fwup.\n");
160 #endif
161 }
162
print_version()163 static void print_version()
164 {
165 printf("%s\n", PACKAGE_VERSION);
166 }
167
168 enum fwup_long_option_only_value {
169 OPTION_NO_EJECT = 0x1000,
170 OPTION_ENABLE_TRIM,
171 OPTION_EXIT_HANDSHAKE,
172 OPTION_METADATA_KEY,
173 OPTION_PRIVATE_KEY,
174 OPTION_PUBLIC_KEY,
175 OPTION_PROGRESS_LOW,
176 OPTION_PROGRESS_HIGH,
177 OPTION_SPARSE_CHECK,
178 OPTION_SPARSE_CHECK_SIZE,
179 OPTION_UNSAFE,
180 OPTION_VERSION,
181 OPTION_VERIFY_WRITES,
182 OPTION_NO_VERIFY_WRITES
183 };
184
185 static struct option long_options[] = {
186 {"apply", no_argument, 0, 'a'},
187 {"create", no_argument, 0, 'c'},
188 {"detect", no_argument, 0, 'D'},
189 {"eject", no_argument, 0, 'E'},
190 {"no-eject", no_argument, 0, OPTION_NO_EJECT},
191 {"enable-trim", no_argument, 0, OPTION_ENABLE_TRIM},
192 {"exit-handshake", no_argument, 0, OPTION_EXIT_HANDSHAKE},
193 {"framing", no_argument, 0, 'F'},
194 {"gen-keys", no_argument, 0, 'g'},
195 {"help", no_argument, 0, 'h'},
196 {"metadata-key", required_argument, 0, OPTION_METADATA_KEY},
197 {"list", no_argument, 0, 'l'},
198 {"metadata", no_argument, 0, 'm'},
199 {"private-key", required_argument, 0, OPTION_PRIVATE_KEY},
200 {"private-key-file", required_argument, 0, 's'},
201 {"public-key", required_argument, 0, OPTION_PUBLIC_KEY},
202 {"public-key-file ", required_argument, 0, 'p'},
203 {"progress-low", required_argument, 0, OPTION_PROGRESS_LOW},
204 {"progress-high", required_argument, 0, OPTION_PROGRESS_HIGH},
205 {"quiet", no_argument, 0, 'q'},
206 {"sparse-check", required_argument, 0, OPTION_SPARSE_CHECK},
207 {"sparse-check-size", required_argument, 0, OPTION_SPARSE_CHECK_SIZE},
208 {"sign", no_argument, 0, 'S'},
209 {"task", required_argument, 0, 't'},
210 {"unmount", no_argument, 0, 'u'},
211 {"no-unmount", no_argument, 0, 'U'},
212 {"unsafe", no_argument, 0, OPTION_UNSAFE},
213 {"verbose", no_argument, 0, 'v'},
214 {"verify", no_argument, 0, 'V'},
215 {"verify-writes", no_argument, 0, OPTION_VERIFY_WRITES},
216 {"no-verify-writes", no_argument, 0, OPTION_NO_VERIFY_WRITES},
217 {"version", no_argument, 0, OPTION_VERSION},
218 {0, 0, 0, 0 }
219 };
220
221 #define CMD_NONE 0
222 #define CMD_APPLY 1
223 #define CMD_CREATE 2
224 #define CMD_LIST 3
225 #define CMD_METADATA 4
226 #define CMD_GENERATE_KEYS 5
227 #define CMD_SIGN 6
228 #define CMD_VERIFY 7
229 #define CMD_SPARSE_CHECK 8
230
decode_key(const char * buffer,size_t buffer_len,size_t key_len)231 static unsigned char *decode_key(const char *buffer,
232 size_t buffer_len,
233 size_t key_len)
234 {
235 unsigned char *key = (unsigned char *) malloc(key_len);
236
237 // Check for raw key bytes
238 if (buffer_len == key_len) {
239 memcpy(key, buffer, buffer_len);
240 return key;
241 }
242
243 // Check for Base64-encoded key (with or without padding)
244 size_t base64_key_len = base64_raw_to_unpadded_count(key_len);
245 size_t decoded_len = key_len;
246 if (buffer_len >= base64_key_len &&
247 from_base64(key, &decoded_len, buffer) != NULL &&
248 decoded_len == key_len) {
249 return key;
250 }
251
252 // Unexpected length
253 free(key);
254 return NULL;
255 }
256
load_key(const char * path,const char * key_type,size_t key_len)257 static unsigned char *load_key(const char *path, const char *key_type, size_t key_len)
258 {
259 FILE *fp = fopen(path, "rb");
260 if (!fp)
261 fwup_err(EXIT_FAILURE, "Error opening %s key file '%s'", key_type, path);
262
263 size_t base64_size = base64_raw_to_encoded_count(key_len);
264 char buffer[base64_size + 1];
265
266 size_t amount_read = fread(buffer, 1, base64_size, fp);
267 buffer[amount_read] = 0;
268 fclose(fp);
269
270 unsigned char *key = decode_key(buffer, amount_read, key_len);
271 if (key == NULL)
272 fwup_errx(EXIT_FAILURE, "Error reading or decoding %s key from file '%s'", key_type, path);
273
274 return key;
275 }
276
parse_key(const char * buffer,size_t buffer_len,const char * key_type,size_t key_len)277 static unsigned char *parse_key(const char *buffer, size_t buffer_len, const char *key_type, size_t key_len)
278 {
279 unsigned char *key = decode_key(buffer, buffer_len, key_len);
280 if (key == NULL)
281 fwup_errx(EXIT_FAILURE, "Error decoding %s key", key_type);
282 return key;
283 }
284
load_public_key(const char * path)285 static unsigned char *load_public_key(const char *path)
286 {
287 return load_key(path, "public", FWUP_PUBLIC_KEY_LEN);
288 }
289
parse_public_key(const char * buffer,size_t buffer_len)290 static unsigned char *parse_public_key(const char *buffer, size_t buffer_len)
291 {
292 return parse_key(buffer, buffer_len, "public", FWUP_PUBLIC_KEY_LEN);
293 }
294
295 #ifndef FWUP_MINIMAL
load_signing_key(const char * path)296 static unsigned char *load_signing_key(const char *path)
297 {
298 return load_key(path, "private", FWUP_PRIVATE_KEY_LEN + FWUP_PUBLIC_KEY_LEN);
299 }
300
parse_signing_key(const char * buffer,size_t buffer_len)301 static unsigned char *parse_signing_key(const char *buffer, size_t buffer_len)
302 {
303 return parse_key(buffer, buffer_len, "private", FWUP_PRIVATE_KEY_LEN + FWUP_PUBLIC_KEY_LEN);
304 }
305
autoselect_mmc_device(struct mmc_device * device)306 static void autoselect_mmc_device(struct mmc_device *device)
307 {
308 struct mmc_device devices[16];
309 int found_devices = mmc_scan_for_devices(devices, NUM_ELEMENTS(devices));
310 if (found_devices == 1) {
311 *device = devices[0];
312 } else if (found_devices == 0) {
313 fwup_errx(EXIT_FAILURE, "No memory cards found. Try reinserting the card.");
314 } else {
315 fprintf(stderr, "Too many possible memory cards found: \n");
316 for (int i = 0; i < found_devices; i++) {
317 char sizestr[16];
318 format_pretty_auto(devices[i].size, sizestr, sizeof(sizestr));
319 bool print_name = (devices[i].name[0] != '\0');
320 fprintf(stderr, " %s (%s%s%s)\n", devices[i].path, sizestr, print_name ? "; " : "", print_name ? devices[i].name : "");
321 }
322 fprintf(stderr, "Automatic selection not possible. Specify one using the -d option.\n");
323 fwup_exit(EXIT_FAILURE);
324 }
325 }
326
autoselect_and_confirm_mmc_device(bool accept_found_device,const char * input_firmware)327 static char *autoselect_and_confirm_mmc_device(bool accept_found_device, const char *input_firmware)
328 {
329 struct mmc_device device;
330 autoselect_mmc_device(&device);
331 if (!accept_found_device) {
332 if (!input_firmware)
333 fwup_errx(EXIT_FAILURE, "Cannot confirm use of %s when using stdin.\nRerun with -y if location is correct.", device.path);
334
335 char sizestr[16];
336 format_pretty_auto(device.size, sizestr, sizeof(sizestr));
337 fprintf(stderr, "Use %s memory card found at %s? [y/N] ", sizestr, device.path);
338 int response = fgetc(stdin);
339 if (response != 'y' && response != 'Y')
340 fwup_errx(EXIT_FAILURE, "aborted by user");
341 }
342 return strdup(device.path);
343 }
344
print_selected_device()345 static void print_selected_device()
346 {
347 struct mmc_device device;
348 autoselect_mmc_device(&device);
349
350 struct simple_string s;
351 simple_string_init(&s);
352 ssprintf(&s, "%s\n", device.path);
353 fwup_output(FRAMING_TYPE_SUCCESS, 0, s.str);
354 free(s.str);
355 }
356 #endif
357
print_detected_devices()358 static void print_detected_devices()
359 {
360 struct mmc_device devices[16];
361 int found_devices = mmc_scan_for_devices(devices, NUM_ELEMENTS(devices));
362
363 struct simple_string s;
364 simple_string_init(&s);
365 for (int i = 0; i < found_devices; i++)
366 ssprintf(&s, "%s,%" PRId64"\n", devices[i].path, devices[i].size);
367 fwup_output(FRAMING_TYPE_SUCCESS, 0, s.str);
368 free(s.str);
369 }
370
main(int argc,char ** argv)371 int main(int argc, char **argv)
372 {
373 int command = CMD_NONE;
374
375 char *mmc_device_path = NULL;
376 const char *input_filename = NULL;
377 const char *output_filename = NULL;
378 const char *task = NULL;
379 #ifndef FWUP_MINIMAL
380 const char *configfile = "fwupdate.conf";
381 const char *sparse_check = NULL;
382 int sparse_check_size = 4096; // Arbitrary default.
383 int compression_level = 9; // 1 - 9
384 bool accept_found_device = false;
385 #endif
386 unsigned char *signing_key = NULL;
387 unsigned char *public_keys[FWUP_MAX_PUBLIC_KEYS + 1] = {NULL};
388 int num_public_keys = 0;
389 const char *metadata_key = NULL;
390 #if __APPLE__
391 // On hosts, the right behavior for almost all use cases is to eject
392 // so that the user can plug the SDCard into their board. Detecting
393 // that OSX is a host is easy; Linux, not so much. Luckily, Linux doesn't
394 // need an eject.
395 bool eject_on_success = true;
396 #else
397 bool eject_on_success = false;
398 #endif
399 bool enable_trim = false;
400 bool unmount_first = true;
401 bool easy_mode = true;
402 bool numeric_progress = false;
403 int progress_low = 0; // 0%
404 int progress_high = 100; // to 100%
405 int verify_writes = -1; // Use default (yes unless writing to a regular file)
406
407 if (argc == 1) {
408 print_usage();
409 fwup_exit(EXIT_FAILURE);
410 }
411
412 mmc_init();
413 atexit(mmc_finalize);
414
415 int opt;
416 while ((opt = getopt_long(argc, argv, "acd:DEf:Fghi:lmno:p:qSs:t:VvUuyZz123456789", long_options, NULL)) != -1) {
417 switch (opt) {
418 case 'a': // --apply
419 command = CMD_APPLY;
420 easy_mode = false;
421 break;
422 #ifndef FWUP_MINIMAL
423 case 'c': // --create
424 command = CMD_CREATE;
425 easy_mode = false;
426 break;
427 case 'f':
428 configfile = optarg;
429 easy_mode = false;
430 break;
431 case 'g': // --gen-keys
432 command = CMD_GENERATE_KEYS;
433 easy_mode = false;
434 break;
435 case 'S': // --sign
436 command = CMD_SIGN;
437 easy_mode = false;
438 break;
439 case OPTION_SPARSE_CHECK: // --sparse-check
440 sparse_check = optarg;
441 command = CMD_SPARSE_CHECK;
442 easy_mode = false;
443 break;
444 case OPTION_SPARSE_CHECK_SIZE: // --sparse-check-size
445 sparse_check_size = strtol(optarg, 0, 0);
446 break;
447 case 's':
448 signing_key = load_signing_key(optarg);
449 easy_mode = false;
450 break;
451 case 'z':
452 print_selected_device();
453 fwup_exit(EXIT_SUCCESS);
454 case '1':
455 case '2':
456 case '3':
457 case '4':
458 case '5':
459 case '6':
460 case '7':
461 case '8':
462 case '9':
463 compression_level = opt - '0';
464 break;
465 case OPTION_PRIVATE_KEY: // --private-key
466 signing_key = parse_signing_key(optarg, strlen(optarg));
467 easy_mode = false;
468 break;
469 #endif
470 case 'd':
471 mmc_device_path = optarg;
472 break;
473 case 'D': // --detect
474 print_detected_devices();
475 fwup_exit(EXIT_SUCCESS);
476 case 'E': // --eject
477 eject_on_success = true;
478 break;
479 case 'F': // --framing
480 fwup_framing = true;
481 easy_mode = false;
482 break;
483 case 'h':
484 print_usage();
485 fwup_exit(EXIT_SUCCESS);
486 case 'i':
487 input_filename = optarg;
488 easy_mode = false;
489 break;
490 case 'l': // --list
491 command = CMD_LIST;
492 easy_mode = false;
493 break;
494 case 'm': // --metadata
495 command = CMD_METADATA;
496 easy_mode = false;
497 break;
498 case OPTION_METADATA_KEY: // --metadata-key
499 metadata_key = optarg;
500 break;
501 case 'o':
502 output_filename = optarg;
503 easy_mode = false;
504 break;
505 case 'p':
506 if (num_public_keys < FWUP_MAX_PUBLIC_KEYS) {
507 public_keys[num_public_keys] = load_public_key(optarg);
508 num_public_keys++;
509 easy_mode = false;
510 } else
511 fwup_warnx("Ignoring public key since only %d supported", FWUP_MAX_PUBLIC_KEYS);
512
513 break;
514 case 'n':
515 numeric_progress = true;
516 break;
517 case 'q':
518 quiet = true;
519 break;
520 case 't': // --task
521 task = optarg;
522 break;
523 case 'v': // --verbose
524 fwup_verbose = true;
525 break;
526 case 'V': // --verify
527 command = CMD_VERIFY;
528 easy_mode = false;
529 break;
530 case 'u': // --unmount
531 unmount_first = true;
532 break;
533 case 'U': // --no-unmount
534 unmount_first = false;
535 break;
536 case 'y':
537 #ifndef FWUP_MINIMAL
538 accept_found_device = true;
539 #endif
540 break;
541 case OPTION_UNSAFE: // --unsafe
542 fwup_unsafe = true;
543 break;
544 case OPTION_ENABLE_TRIM: // --enable-trim
545 enable_trim = true;
546 break;
547 case OPTION_VERSION: // --version
548 print_version();
549 fwup_exit(EXIT_SUCCESS);
550 case OPTION_NO_EJECT: // --no-eject
551 eject_on_success = false;
552 break;
553 case OPTION_PROGRESS_LOW: // progress-low
554 progress_low = strtol(optarg, 0, 0);
555 break;
556 case OPTION_PROGRESS_HIGH: // progress-high
557 progress_high = strtol(optarg, 0, 0);
558 break;
559 case OPTION_PUBLIC_KEY: // --public-key
560 if (num_public_keys < FWUP_MAX_PUBLIC_KEYS) {
561 public_keys[num_public_keys] = parse_public_key(optarg, strlen(optarg));
562 num_public_keys++;
563 } else
564 fwup_warnx("Ignoring public key since only %d supported", FWUP_MAX_PUBLIC_KEYS);
565 break;
566 case OPTION_EXIT_HANDSHAKE: // --exit-handshake
567 fwup_handshake_on_exit = true;
568 break;
569 case OPTION_VERIFY_WRITES: // --verify-writes
570 verify_writes = true;
571 break;
572 case OPTION_NO_VERIFY_WRITES: // --no-verify-writes
573 verify_writes = false;
574 break;
575 default: /* '?' */
576 print_usage();
577 fwup_exit(EXIT_FAILURE);
578 }
579 }
580
581 #ifdef _WIN32
582 if (fwup_framing) {
583 setmode(STDIN_FILENO, O_BINARY);
584 setmode(STDOUT_FILENO, O_BINARY);
585 }
586 #endif
587
588 if (quiet && numeric_progress)
589 fwup_errx(EXIT_FAILURE, "pick either -n or -q, but not both");
590
591 // Support an easy mode where the user can pass a .fw file
592 // and it will program an attached SDCard, etc. with minimal
593 // fuss. Some options are supported.
594 if (easy_mode && optind == argc - 1) {
595 command = CMD_APPLY;
596 input_filename = argv[optind++];
597 if (!task)
598 task = "complete";
599 }
600
601 if (optind < argc) {
602 fwup_errx(EXIT_FAILURE, "unexpected parameter: %s", argv[optind]);
603 }
604
605 // Normalize the firmware filenames in the case that the user wants
606 // to use stdin/stdout
607 if (input_filename && strcmp(input_filename, "-") == 0)
608 input_filename = 0;
609 if (output_filename && strcmp(output_filename, "-") == 0)
610 output_filename = 0;
611
612 switch (command) {
613 case CMD_NONE:
614 fwup_errx(EXIT_FAILURE, "specify one of -a, -c, -l, -m, -S, -V, or -z");
615 break;
616
617 case CMD_APPLY:
618 {
619 if (!task)
620 fwup_errx(EXIT_FAILURE, "specify a task (-t)");
621
622 if (!mmc_device_path) {
623 #ifndef FWUP_MINIMAL
624 mmc_device_path = autoselect_and_confirm_mmc_device(accept_found_device, input_filename);
625 #else
626 fwup_errx(EXIT_FAILURE, "autodetection compiled out. specify a device (-d)");
627 #endif
628 }
629 if (quiet)
630 fwup_progress_mode = PROGRESS_MODE_OFF;
631 else if (fwup_framing)
632 fwup_progress_mode = PROGRESS_MODE_FRAMING;
633 else if (numeric_progress)
634 fwup_progress_mode = PROGRESS_MODE_NUMERIC;
635 else
636 fwup_progress_mode = PROGRESS_MODE_NORMAL;
637 struct fwup_progress progress;
638 progress_init(&progress, progress_low, progress_high);
639
640 // Check if the mmc_device_path is really a special device. If
641 // we're just creating an image file, then don't try to unmount
642 // everything using it.
643 bool is_regular_file = will_be_regular_file(mmc_device_path);
644 int output_fd = -1;
645 off_t end_offset = -1;
646 if (is_regular_file) {
647 // This is a regular file, so open it the regular way.
648 output_fd = open(mmc_device_path, O_RDWR | O_CREAT | O_WIN32_BINARY, 0644);
649
650 if (output_fd >= 0) {
651 // Get the original file length
652 // NOTE: this call is not so interesting. The interesting one is for real
653 // media, but we need to do it anyway. <= 0 means unknown.
654 end_offset = lseek(output_fd, 0, SEEK_END);
655
656 struct stat st;
657 if (fstat(output_fd, &st) < 0 || (st.st_mode & 0222) == 0) {
658 // The file permissions are read-only, but the user was able to
659 // open it writable. Root can do this. This is almost certainly
660 // a mistake so error out. Changing file permissions to make it
661 // writable is the way to get around this and the error message
662 // below describes it.
663 close(output_fd);
664 output_fd = -1;
665 }
666 }
667 if (enable_trim) {
668 fwup_warnx("ignoring --enable_trim since operating on a regular file");
669 enable_trim = false;
670 }
671 } else {
672 // Attempt to unmount everything using the device to avoid corrupting partitions.
673 // For partial updates, this just unmounts everything that can be unmounted. Errors
674 // are ignored, which is an hacky way of making this do what's necessary automatically.
675 // NOTE: It is possible in the future to scan the config file and just unmount partitions
676 // that overlap what will be written.
677 if (unmount_first) {
678 if (mmc_umount_all(mmc_device_path) < 0)
679 fwup_exit(EXIT_FAILURE);
680 }
681
682 if (mmc_device_size(mmc_device_path, &end_offset) < 0)
683 fwup_warnx("Error deterimining the size of %s", mmc_device_path);
684
685 // Call out to platform-specific code to obtain a filehandle
686 output_fd = mmc_open(mmc_device_path);
687 }
688
689 // Trim the detected image size down to a multiple of the block cache
690 // segment size (128 KB) since since fwup only writes full blocks. If
691 // this isn't done, it is possible to write beyond the end of file.
692 end_offset &= ~(BLOCK_CACHE_SEGMENT_SIZE - 1);
693
694 // Make sure that the output opened successfully and don't allow the
695 // filehandle to be passed to child processes.
696 if (output_fd < 0) {
697 fprintf(stderr, "\n");
698 if (file_exists(mmc_device_path)) {
699 fwup_errx(EXIT_FAILURE, "Cannot open '%s' for output.\nCheck file permissions or the read-only tab if this is an SD Card.",
700 mmc_device_path);
701 } else {
702 fwup_errx(EXIT_FAILURE, "Cannot create '%s'.\nCheck the path and permissions on the containing directory.",
703 mmc_device_path);
704 }
705 }
706 #ifdef HAVE_FCNTL
707 (void) fcntl(output_fd, F_SETFD, FD_CLOEXEC);
708 #endif
709
710 // If verify_writes wasn't set, then verify if not a regular file.
711 if (verify_writes < 0)
712 verify_writes = !is_regular_file;
713
714 if (fwup_apply(input_filename,
715 task,
716 output_fd,
717 end_offset,
718 &progress,
719 public_keys,
720 enable_trim,
721 verify_writes) < 0) {
722 if (!quiet)
723 fprintf(stderr, "\n");
724 fwup_errx(EXIT_FAILURE, "%s", last_error());
725 }
726
727 if (!is_regular_file && eject_on_success) {
728 // On OSX, at least, the system complains bitterly if you don't eject the device when done.
729 // This just does whatever is needed so that the device can be removed.
730 mmc_eject(mmc_device_path);
731 }
732 fwup_output(FRAMING_TYPE_SUCCESS, 0, "");
733 break;
734 }
735
736 #ifndef FWUP_MINIMAL
737 case CMD_CREATE:
738 if (fwup_create(configfile, output_filename, signing_key, compression_level) < 0)
739 fwup_errx(EXIT_FAILURE, "%s", last_error());
740
741 break;
742 case CMD_GENERATE_KEYS:
743 if (fwup_genkeys(output_filename) < 0)
744 fwup_errx(EXIT_FAILURE, "%s", last_error());
745
746 break;
747
748 case CMD_SIGN:
749 if (fwup_sign(input_filename, output_filename, signing_key) < 0)
750 fwup_errx(EXIT_FAILURE, "%s", last_error());
751
752 break;
753
754 case CMD_SPARSE_CHECK:
755 if (sparse_file_is_supported(sparse_check, sparse_check_size) < 0)
756 fwup_errx(EXIT_FAILURE, "%s", last_error());
757 else
758 fwup_output(FRAMING_TYPE_SUCCESS, 0, "Sparse files supported\n");
759 break;
760 #endif
761
762 case CMD_LIST:
763 if (fwup_list(input_filename, public_keys) < 0)
764 fwup_errx(EXIT_FAILURE, "%s", last_error());
765
766 break;
767
768 case CMD_METADATA:
769 if (fwup_metadata(input_filename, public_keys, metadata_key) < 0)
770 fwup_errx(EXIT_FAILURE, "%s", last_error());
771
772 break;
773
774
775 case CMD_VERIFY:
776 if (fwup_verify(input_filename, public_keys) < 0)
777 fwup_errx(EXIT_FAILURE, "%s", last_error());
778
779 break;
780 }
781
782 if (signing_key)
783 free(signing_key);
784 for (int i = 0; i < num_public_keys; i++)
785 free(public_keys[i]);
786
787 fwup_exit(EXIT_SUCCESS);
788 }
789