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