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 "functions.h"
18 #include "util.h"
19 #include "disk_crypto.h"
20 #include "fatfs.h"
21 #include "mbr.h"
22 #include "gpt.h"
23 #include "fwfile.h"
24 #include "block_cache.h"
25 #include "uboot_env.h"
26 #include "sparse_file.h"
27 #include "progress.h"
28 #include "pad_to_block_writer.h"
29
30 #include <assert.h>
31 #include <errno.h>
32 #include <inttypes.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39
40 #include "monocypher.h"
41
42 #define DECLARE_FUN(FUN) \
43 static int FUN ## _validate(struct fun_context *fctx); \
44 static int FUN ## _compute_progress(struct fun_context *fctx); \
45 static int FUN ## _run(struct fun_context *fctx)
46
47 DECLARE_FUN(raw_write);
48 DECLARE_FUN(raw_memset);
49 DECLARE_FUN(fat_attrib);
50 DECLARE_FUN(fat_mkfs);
51 DECLARE_FUN(fat_write);
52 DECLARE_FUN(fat_mv);
53 DECLARE_FUN(fat_rm);
54 DECLARE_FUN(fat_cp);
55 DECLARE_FUN(fat_mkdir);
56 DECLARE_FUN(fat_setlabel);
57 DECLARE_FUN(fat_touch);
58 DECLARE_FUN(gpt_write);
59 DECLARE_FUN(mbr_write);
60 DECLARE_FUN(trim);
61 DECLARE_FUN(uboot_clearenv);
62 DECLARE_FUN(uboot_setenv);
63 DECLARE_FUN(uboot_unsetenv);
64 DECLARE_FUN(uboot_recover);
65 DECLARE_FUN(error);
66 DECLARE_FUN(info);
67 DECLARE_FUN(path_write);
68 DECLARE_FUN(pipe_write);
69 DECLARE_FUN(execute);
70
71 struct fun_info {
72 const char *name;
73 int (*validate)(struct fun_context *fctx);
74 int (*compute_progress)(struct fun_context *fctx);
75 int (*run)(struct fun_context *fctx);
76 };
77
78 #define FUN_INFO(FUN) {#FUN, FUN ## _validate, FUN ## _compute_progress, FUN ## _run}
79 #define FUN_BANG_INFO(FUN) {#FUN "!", FUN ## _validate, FUN ## _compute_progress, FUN ## _run}
80 static struct fun_info fun_table[] = {
81 FUN_INFO(raw_write),
82 FUN_INFO(raw_memset),
83 FUN_INFO(fat_attrib),
84 FUN_INFO(fat_mkfs),
85 FUN_INFO(fat_write),
86 FUN_INFO(fat_mv),
87 FUN_BANG_INFO(fat_mv),
88 FUN_INFO(fat_rm),
89 FUN_BANG_INFO(fat_rm),
90 FUN_INFO(fat_cp),
91 FUN_INFO(fat_mkdir),
92 FUN_INFO(fat_setlabel),
93 FUN_INFO(fat_touch),
94 FUN_INFO(gpt_write),
95 FUN_INFO(mbr_write),
96 FUN_INFO(trim),
97 FUN_INFO(uboot_clearenv),
98 FUN_INFO(uboot_setenv),
99 FUN_INFO(uboot_unsetenv),
100 FUN_INFO(uboot_recover),
101 FUN_INFO(error),
102 FUN_INFO(info),
103 FUN_INFO(path_write),
104 FUN_INFO(pipe_write),
105 FUN_INFO(execute),
106 };
107
lookup(int argc,const char ** argv)108 static struct fun_info *lookup(int argc, const char **argv)
109 {
110 if (argc < 1) {
111 set_last_error("Not enough parameters");
112 return 0;
113 }
114
115 size_t i;
116 for (i = 0; i < NUM_ELEMENTS(fun_table); i++) {
117 if (strcmp(argv[0], fun_table[i].name) == 0) {
118 return &fun_table[i];
119 }
120 }
121
122 set_last_error("Unknown function");
123 return 0;
124 }
125
126 /**
127 * @brief Validate the parameters passed to the function
128 *
129 * This is called when creating the firmware file.
130 *
131 * @param fctx the function context
132 * @return 0 if ok
133 */
fun_validate(struct fun_context * fctx)134 int fun_validate(struct fun_context *fctx)
135 {
136 struct fun_info *fun = lookup(fctx->argc, fctx->argv);
137 if (!fun)
138 return -1;
139
140 return fun->validate(fctx);
141 }
142
143 /**
144 * @brief Compute the total progress units expected
145 *
146 * This is called before running.
147 *
148 * @param fctx the function context
149 * @return 0 if ok
150 */
fun_compute_progress(struct fun_context * fctx)151 int fun_compute_progress(struct fun_context *fctx)
152 {
153 struct fun_info *fun = lookup(fctx->argc, fctx->argv);
154 if (!fun)
155 return -1;
156
157 return fun->compute_progress(fctx);
158 }
159
160 /**
161 * @brief Run a function
162 *
163 * This is called when applying the firmware.
164 *
165 * @param fctx the function context
166 * @return 0 if ok
167 */
fun_run(struct fun_context * fctx)168 int fun_run(struct fun_context *fctx)
169 {
170 struct fun_info *fun = lookup(fctx->argc, fctx->argv);
171 if (!fun)
172 return -1;
173
174 return fun->run(fctx);
175 }
176
177
178 /**
179 * @brief Run all of the functions in a funlist
180 * @param fctx the context to use (argc and argv will be updated in it)
181 * @param funlist the list
182 * @param fun the function to execute (either fun_run or fun_compute_progress)
183 * @return 0 if ok
184 */
fun_apply_funlist(struct fun_context * fctx,cfg_opt_t * funlist,int (* fun)(struct fun_context * fctx))185 int fun_apply_funlist(struct fun_context *fctx, cfg_opt_t *funlist, int (*fun)(struct fun_context *fctx))
186 {
187 int ix = 0;
188 char *aritystr;
189 while ((aritystr = cfg_opt_getnstr(funlist, ix++)) != NULL) {
190 fctx->argc = strtoul(aritystr, NULL, 0);
191 if (fctx->argc <= 0 || fctx->argc > FUN_MAX_ARGS) {
192 set_last_error("Unexpected argc value in funlist");
193 return -1;
194 }
195 int i;
196 for (i = 0; i < fctx->argc; i++) {
197 fctx->argv[i] = cfg_opt_getnstr(funlist, ix++);
198 if (fctx->argv[i] == NULL) {
199 set_last_error("Unexpected error with funlist");
200 return -1;
201 }
202 }
203 // Clear out the rest of the argv entries to avoid confusion when debugging.
204 for (; i < FUN_MAX_ARGS; i++)
205 fctx->argv[i] = 0;
206
207 if (fun(fctx) < 0)
208 return -1;
209 }
210 return 0;
211 }
212
213 /**
214 * Helper function that is paired with process_resource() to compute
215 * progress.
216 *
217 * Set count_holes to true if holes in sparse files are manually written
218 * to the destination as zeros. E.g., holes can't be optimized out by the
219 * OS.
220 */
process_resource_compute_progress(struct fun_context * fctx,bool count_holes)221 static int process_resource_compute_progress(struct fun_context *fctx, bool count_holes)
222 {
223 assert(fctx->type == FUN_CONTEXT_FILE);
224 assert(fctx->on_event);
225
226 struct sparse_file_map sfm;
227 sparse_file_init(&sfm);
228 OK_OR_RETURN(sparse_file_get_map_from_config(fctx->cfg, fctx->on_event->title, &sfm));
229 off_t expected_length = count_holes ? sparse_file_size(&sfm) : sparse_file_data_size(&sfm);
230 sparse_file_free(&sfm);
231
232 // Count each byte as a progress unit
233 fctx->progress->total_units += expected_length;
234
235 return 0;
236 }
237 /**
238 * This is a helper function for reading a resource out of a file and doing
239 * something with it. (Like write it somewhere)
240 *
241 * It handles the following:
242 * 1. Finds the resource
243 * 2. Verifies that the resource contents pass the checksums
244 * 3. Handles sparse resources
245 * 4. Checks nit-picky issues and returns errors when detected
246 *
247 * NOTE: count_holes must match the value passed to process_resource_compute_progress.
248 */
process_resource(struct fun_context * fctx,bool count_holes,int (* pwrite_callback)(void * cookie,const void * buf,size_t count,off_t offset),int (* final_hole_callback)(void * cookie,off_t hole_size,off_t file_size),void * cookie)249 static int process_resource(struct fun_context *fctx,
250 bool count_holes,
251 int (*pwrite_callback)(void *cookie, const void *buf, size_t count, off_t offset),
252 int (*final_hole_callback)(void *cookie, off_t hole_size, off_t file_size),
253 void *cookie)
254 {
255 assert(fctx->type == FUN_CONTEXT_FILE);
256 assert(fctx->on_event);
257
258 int rc = 0;
259 struct sparse_file_map sfm;
260 sparse_file_init(&sfm);
261
262 cfg_t *resource = cfg_gettsec(fctx->cfg, "file-resource", fctx->on_event->title);
263 if (!resource)
264 ERR_CLEANUP_MSG("%s can't find file-resource '%s'", fctx->argv[0], fctx->on_event->title);
265
266 char *expected_hash = cfg_getstr(resource, "blake2b-256");
267 if (!expected_hash || strlen(expected_hash) != FWUP_BLAKE2b_256_LEN * 2)
268 ERR_CLEANUP_MSG("invalid blake2b hash for '%s'", fctx->on_event->title);
269
270 OK_OR_CLEANUP(sparse_file_get_map_from_resource(resource, &sfm));
271 off_t expected_data_length = sparse_file_data_size(&sfm);
272
273 off_t total_data_read = 0;
274
275 crypto_blake2b_ctx hash_state;
276 crypto_blake2b_general_init(&hash_state, FWUP_BLAKE2b_256_LEN, NULL, 0);
277
278 off_t last_offset = 0;
279 for (;;) {
280 off_t offset;
281 size_t len;
282 const void *buffer;
283
284 OK_OR_CLEANUP(fctx->read(fctx, &buffer, &len, &offset));
285
286 // Check if done.
287 if (len == 0)
288 break;
289
290 crypto_blake2b_update(&hash_state, (const uint8_t*) buffer, len);
291
292 OK_OR_CLEANUP(pwrite_callback(cookie, buffer, len, offset));
293
294 total_data_read += len;
295 if (!count_holes) {
296 // If not counting holes for progress reporting, then report
297 // that we wrote exactly what was read.
298 progress_report(fctx->progress, len);
299 } else {
300 // If counting holes for progress reporting, then report
301 // everything since the last time.
302 off_t next_offset_to_write = offset + len;
303 progress_report(fctx->progress, next_offset_to_write - last_offset);
304 last_offset = next_offset_to_write;
305 }
306 }
307
308 // Handle a final hole in a sparse file
309 off_t ending_hole = sparse_ending_hole_size(&sfm);
310 if (ending_hole > 0) {
311 OK_OR_CLEANUP(final_hole_callback(cookie, ending_hole, sparse_file_size(&sfm)));
312
313 if (count_holes)
314 progress_report(fctx->progress, ending_hole);
315 }
316
317 if (total_data_read != expected_data_length) {
318 if (total_data_read == 0)
319 ERR_CLEANUP_MSG("%s didn't write anything and was likely called twice in an on-resource for '%s'. Try a \"cp\" function.", fctx->argv[0], fctx->on_event->title);
320 else
321 ERR_CLEANUP_MSG("%s wrote %" PRId64" bytes for '%s', but should have written %" PRId64, fctx->argv[0], total_data_read, fctx->on_event->title, expected_data_length);
322 }
323
324 // Verify hash
325 unsigned char hash[32];
326 crypto_blake2b_final(&hash_state, hash);
327 char hash_str[sizeof(hash) * 2 + 1];
328 bytes_to_hex(hash, hash_str, sizeof(hash));
329 if (memcmp(hash_str, expected_hash, sizeof(hash_str)) != 0)
330 ERR_CLEANUP_MSG("%s detected blake2b mismatch on '%s'", fctx->argv[0], fctx->on_event->title);
331
332 cleanup:
333 sparse_file_free(&sfm);
334 return rc;
335 }
336
337 struct raw_write_options {
338 const char *cipher;
339 const char *secret;
340 };
parse_raw_write_options(const struct fun_context * fctx,struct raw_write_options * options)341 static int parse_raw_write_options(const struct fun_context *fctx, struct raw_write_options *options)
342 {
343 memset(options, 0, sizeof(*options));
344
345 for (int i = 2; i < fctx->argc; i++) {
346 const char *key;
347 const char *value;
348
349 key = fctx->argv[i];
350 value = strchr(key, '=');
351 if (!value)
352 ERR_RETURN("Expecting '=' for optional raw_write parameter");
353
354 value++;
355
356 if (strncmp(key, "cipher=", 7) == 0)
357 options->cipher = value;
358 else if (strncmp(key, "secret=", 7) == 0)
359 options->secret = value;
360 else
361 ERR_RETURN("Unexpected parameter to raw_write: %s", key);
362 }
363 return 0;
364 }
raw_write_validate(struct fun_context * fctx)365 int raw_write_validate(struct fun_context *fctx)
366 {
367 if (fctx->type != FUN_CONTEXT_FILE)
368 ERR_RETURN("raw_write only usable in on-resource");
369
370 if (fctx->argc < 2)
371 ERR_RETURN("raw_write requires a block offset");
372
373 CHECK_ARG_UINT64(fctx->argv[1], "raw_write requires a non-negative integer block offset");
374
375 // Check the options
376 struct raw_write_options options;
377 OK_OR_RETURN(parse_raw_write_options(fctx, &options));
378
379 // If there's a cipher, then there must be a secret
380 if ((options.cipher && !options.secret) ||
381 (options.secret && !options.cipher))
382 ERR_RETURN("raw_write requires both a cipher and secret if one is supplied");
383
384 return 0;
385 }
raw_write_compute_progress(struct fun_context * fctx)386 int raw_write_compute_progress(struct fun_context *fctx)
387 {
388 return process_resource_compute_progress(fctx, false);
389 }
390 struct raw_write_cookie {
391 off_t dest_offset;
392 struct pad_to_block_writer ptbw;
393 };
raw_write_pwrite_callback(void * cookie,const void * buf,size_t count,off_t offset)394 static int raw_write_pwrite_callback(void *cookie, const void *buf, size_t count, off_t offset)
395 {
396 struct raw_write_cookie *rwc = (struct raw_write_cookie *) cookie;
397 return ptbw_pwrite(&rwc->ptbw, buf, count, rwc->dest_offset + offset);
398 }
raw_write_final_hole_callback(void * cookie,off_t hole_size,off_t file_size)399 static int raw_write_final_hole_callback(void *cookie, off_t hole_size, off_t file_size)
400 {
401 struct raw_write_cookie *rwc = (struct raw_write_cookie *) cookie;
402
403 // If this is a regular file, seeking is insufficient in making the file
404 // the right length, so write a block of zeros to the end.
405 uint8_t zeros[FWUP_BLOCK_SIZE];
406 memset(zeros, 0, sizeof(zeros));
407 off_t to_write = sizeof(zeros);
408 if (hole_size < to_write)
409 to_write = hole_size;
410 off_t offset = file_size - to_write;
411 return ptbw_pwrite(&rwc->ptbw, zeros, to_write, rwc->dest_offset + offset);
412 }
raw_write_run(struct fun_context * fctx)413 int raw_write_run(struct fun_context *fctx)
414 {
415 int rc = 0;
416
417 // Raw write runs all writes through pad_to_block_writer to guarantee
418 // block size writes to the caching code no matter how the input resources
419 // get decompressed.
420
421 struct raw_write_cookie rwc;
422 rwc.dest_offset = strtoull(fctx->argv[1], NULL, 0) * FWUP_BLOCK_SIZE;
423
424 struct raw_write_options options;
425 OK_OR_RETURN(parse_raw_write_options(fctx, &options));
426
427 struct disk_crypto dc_info;
428 struct disk_crypto *dc = NULL;
429 if (options.cipher) {
430 if (disk_crypto_init(&dc_info, options.cipher, options.secret, rwc.dest_offset) < 0)
431 return -1;
432 dc = &dc_info;
433 }
434
435 ptbw_init(&rwc.ptbw, fctx->output, dc);
436
437 OK_OR_CLEANUP(process_resource(fctx,
438 false,
439 raw_write_pwrite_callback,
440 raw_write_final_hole_callback,
441 &rwc));
442
443 rc = ptbw_flush(&rwc.ptbw);
444
445 cleanup:
446 if (dc)
447 disk_crypto_free(dc);
448
449 return rc;
450 }
451
raw_memset_validate(struct fun_context * fctx)452 int raw_memset_validate(struct fun_context *fctx)
453 {
454 if (fctx->argc != 4)
455 ERR_RETURN("raw_memset requires a block offset, count, and value");
456
457 CHECK_ARG_UINT64(fctx->argv[1], "raw_memset requires a non-negative integer block offset");
458 CHECK_ARG_UINT64_RANGE(fctx->argv[2], 1, INT32_MAX / FWUP_BLOCK_SIZE, "raw_memset requires a positive integer block count");
459
460 int value = strtol(fctx->argv[3], NULL, 0);
461 if (value < 0 || value > 255)
462 ERR_RETURN("raw_memset requires value to be between 0 and 255");
463
464 return 0;
465 }
raw_memset_compute_progress(struct fun_context * fctx)466 int raw_memset_compute_progress(struct fun_context *fctx)
467 {
468 int count = strtol(fctx->argv[2], NULL, 0);
469
470 // Count each byte as a progress unit
471 fctx->progress->total_units += count * FWUP_BLOCK_SIZE;
472
473 return 0;
474 }
raw_memset_run(struct fun_context * fctx)475 int raw_memset_run(struct fun_context *fctx)
476 {
477 const size_t block_size = FWUP_BLOCK_SIZE;
478
479 off_t dest_offset = strtoull(fctx->argv[1], NULL, 0) * FWUP_BLOCK_SIZE;
480 off_t count = strtoull(fctx->argv[2], NULL, 0) * FWUP_BLOCK_SIZE;
481 int value = strtol(fctx->argv[3], NULL, 0);
482 char buffer[block_size];
483 memset(buffer, value, sizeof(buffer));
484
485 off_t len_written = 0;
486 off_t offset;
487 for (offset = 0; offset < count; offset += block_size) {
488 OK_OR_RETURN_MSG(block_cache_pwrite(fctx->output, buffer, block_size, dest_offset + offset, true),
489 "raw_memset couldn't write %d bytes to offset %" PRId64, block_size, dest_offset + offset);
490
491 len_written += block_size;
492 progress_report(fctx->progress, block_size);
493 }
494
495 return 0;
496 }
497
fat_mkfs_validate(struct fun_context * fctx)498 int fat_mkfs_validate(struct fun_context *fctx)
499 {
500 if (fctx->argc != 3)
501 ERR_RETURN("fat_mkfs requires a block offset and block count");
502
503 CHECK_ARG_UINT64(fctx->argv[1], "fat_mkfs requires a non-negative integer block offset");
504 CHECK_ARG_UINT64(fctx->argv[2], "fat_mkfs requires a non-negative integer block count");
505
506 return 0;
507 }
fat_mkfs_compute_progress(struct fun_context * fctx)508 int fat_mkfs_compute_progress(struct fun_context *fctx)
509 {
510 fctx->progress->total_units += FWUP_BLOCK_SIZE; // Arbitarily count as 1 block
511 return 0;
512 }
fat_mkfs_run(struct fun_context * fctx)513 int fat_mkfs_run(struct fun_context *fctx)
514 {
515 off_t block_offset = strtoull(fctx->argv[1], NULL, 0);
516 size_t block_count = strtoul(fctx->argv[2], NULL, 0);
517
518 if (fatfs_mkfs(fctx->output, block_offset, block_count) < 0)
519 return -1;
520
521 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
522 return 0;
523 }
524
fat_attrib_validate(struct fun_context * fctx)525 int fat_attrib_validate(struct fun_context *fctx)
526 {
527 if (fctx->argc != 4)
528 ERR_RETURN("fat_attrib requires a block offset, filename, and attributes (SHR)");
529
530 CHECK_ARG_UINT64(fctx->argv[1], "fat_mkfs requires a non-negative integer block offset");
531
532 const char *attrib = fctx->argv[3];
533 while (*attrib) {
534 switch (*attrib) {
535 case 'S':
536 case 's':
537 case 'H':
538 case 'h':
539 case 'R':
540 case 'r':
541 break;
542
543 default:
544 ERR_RETURN("fat_attrib only supports R, H, and S attributes");
545 }
546 attrib++;
547 }
548 return 0;
549 }
fat_attrib_compute_progress(struct fun_context * fctx)550 int fat_attrib_compute_progress(struct fun_context *fctx)
551 {
552 fctx->progress->total_units += FWUP_BLOCK_SIZE; // Arbitarily count as 1 block
553 return 0;
554 }
fat_attrib_run(struct fun_context * fctx)555 int fat_attrib_run(struct fun_context *fctx)
556 {
557 if (fatfs_attrib(fctx->output, strtoull(fctx->argv[1], NULL, 0), fctx->argv[2], fctx->argv[3]) < 0)
558 return 1;
559
560 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
561 return 0;
562 }
563
fat_write_validate(struct fun_context * fctx)564 int fat_write_validate(struct fun_context *fctx)
565 {
566 if (fctx->type != FUN_CONTEXT_FILE)
567 ERR_RETURN("fat_write only usable in on-resource");
568
569 if (fctx->argc != 3)
570 ERR_RETURN("fat_write requires a block offset and destination filename");
571
572 CHECK_ARG_UINT64(fctx->argv[1], "fat_write requires a non-negative integer block offset");
573
574 return 0;
575 }
fat_write_compute_progress(struct fun_context * fctx)576 int fat_write_compute_progress(struct fun_context *fctx)
577 {
578 return process_resource_compute_progress(fctx, true);
579 }
580 struct fat_write_cookie {
581 struct fun_context *fctx;
582 off_t block_offset;
583 };
fat_write_pwrite_callback(void * cookie,const void * buf,size_t count,off_t offset)584 static int fat_write_pwrite_callback(void *cookie, const void *buf, size_t count, off_t offset)
585 {
586 struct fat_write_cookie *fwc = (struct fat_write_cookie *) cookie;
587 struct fun_context *fctx = fwc->fctx;
588
589 return fatfs_pwrite(fctx->output, fwc->block_offset, fctx->argv[2], (int) offset, buf, count);
590 }
fat_write_final_hole_callback(void * cookie,off_t hole_size,off_t file_size)591 static int fat_write_final_hole_callback(void *cookie, off_t hole_size, off_t file_size)
592 {
593 (void) hole_size;
594
595 struct fat_write_cookie *fwc = (struct fat_write_cookie *) cookie;
596 struct fun_context *fctx = fwc->fctx;
597
598 // If the file ends in a hole, fatfs_pwrite can be used to grow it.
599 return fatfs_pwrite(fctx->output, fwc->block_offset, fctx->argv[2], (int) file_size, NULL, 0);
600 }
fat_write_run(struct fun_context * fctx)601 int fat_write_run(struct fun_context *fctx)
602 {
603 struct fat_write_cookie fwc;
604 fwc.fctx = fctx;
605 fwc.block_offset = strtoull(fctx->argv[1], NULL, 0);
606
607 // Enforce truncation semantics if the file exists
608 OK_OR_RETURN(fatfs_truncate(fctx->output, fwc.block_offset, fctx->argv[2]));
609
610 return process_resource(fctx,
611 true,
612 fat_write_pwrite_callback,
613 fat_write_final_hole_callback,
614 &fwc);
615 }
616
fat_mv_validate(struct fun_context * fctx)617 int fat_mv_validate(struct fun_context *fctx)
618 {
619 if (fctx->argc != 4)
620 ERR_RETURN("fat_mv requires a block offset, old filename, new filename");
621
622 CHECK_ARG_UINT64(fctx->argv[1], "fat_mv requires a non-negative integer block offset");
623 return 0;
624 }
fat_mv_compute_progress(struct fun_context * fctx)625 int fat_mv_compute_progress(struct fun_context *fctx)
626 {
627 fctx->progress->total_units += FWUP_BLOCK_SIZE; // Arbitarily count as 1 block
628 return 0;
629 }
fat_mv_run(struct fun_context * fctx)630 int fat_mv_run(struct fun_context *fctx)
631 {
632 off_t block_offset = strtoull(fctx->argv[1], NULL, 0);
633
634 bool force = (fctx->argv[0][6] == '!');
635 OK_OR_RETURN(fatfs_mv(fctx->output, block_offset, fctx->argv[0], fctx->argv[2], fctx->argv[3], force));
636
637 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
638 return 0;
639 }
640
fat_rm_validate(struct fun_context * fctx)641 int fat_rm_validate(struct fun_context *fctx)
642 {
643 if (fctx->argc != 3)
644 ERR_RETURN("fat_rm requires a block offset and filename");
645
646 CHECK_ARG_UINT64(fctx->argv[1], "fat_rm requires a non-negative integer block offset");
647
648 return 0;
649 }
fat_rm_compute_progress(struct fun_context * fctx)650 int fat_rm_compute_progress(struct fun_context *fctx)
651 {
652 fctx->progress->total_units += FWUP_BLOCK_SIZE; // Arbitarily count as 1 block
653 return 0;
654 }
fat_rm_run(struct fun_context * fctx)655 int fat_rm_run(struct fun_context *fctx)
656 {
657 off_t block_offset = strtoull(fctx->argv[1], NULL, 0);
658
659 bool file_must_exist = (fctx->argv[0][6] == '!');
660 OK_OR_RETURN(fatfs_rm(fctx->output, block_offset, fctx->argv[0], fctx->argv[2], file_must_exist));
661
662 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
663 return 0;
664 }
665
fat_cp_validate(struct fun_context * fctx)666 int fat_cp_validate(struct fun_context *fctx)
667 {
668 if (fctx->argc != 4)
669 ERR_RETURN("fat_cp requires a block offset, from filename, and to filename");
670
671 CHECK_ARG_UINT64(fctx->argv[1], "fat_cp requires a non-negative integer block offset");
672
673 return 0;
674 }
fat_cp_compute_progress(struct fun_context * fctx)675 int fat_cp_compute_progress(struct fun_context *fctx)
676 {
677 fctx->progress->total_units += FWUP_BLOCK_SIZE; // Arbitarily count as 1 block
678 return 0;
679 }
fat_cp_run(struct fun_context * fctx)680 int fat_cp_run(struct fun_context *fctx)
681 {
682 off_t block_offset = strtoull(fctx->argv[1], NULL, 0);
683
684 OK_OR_RETURN(fatfs_cp(fctx->output, block_offset, fctx->argv[2], fctx->argv[3]));
685
686 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
687 return 0;
688 }
689
fat_mkdir_validate(struct fun_context * fctx)690 int fat_mkdir_validate(struct fun_context *fctx)
691 {
692 if (fctx->argc != 3)
693 ERR_RETURN("fat_mkdir requires a block offset and directory name");
694
695 CHECK_ARG_UINT64(fctx->argv[1], "fat_mkdir requires a non-negative integer block offset");
696
697 return 0;
698 }
fat_mkdir_compute_progress(struct fun_context * fctx)699 int fat_mkdir_compute_progress(struct fun_context *fctx)
700 {
701 fctx->progress->total_units += FWUP_BLOCK_SIZE; // Arbitarily count as 1 block
702 return 0;
703 }
fat_mkdir_run(struct fun_context * fctx)704 int fat_mkdir_run(struct fun_context *fctx)
705 {
706 off_t block_offset = strtoull(fctx->argv[1], NULL, 0);
707
708 OK_OR_RETURN(fatfs_mkdir(fctx->output, block_offset, fctx->argv[2]));
709
710 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
711 return 0;
712 }
713
fat_setlabel_validate(struct fun_context * fctx)714 int fat_setlabel_validate(struct fun_context *fctx)
715 {
716 if (fctx->argc != 3)
717 ERR_RETURN("fat_setlabel requires a block offset and name");
718
719 CHECK_ARG_UINT64(fctx->argv[1], "fat_setlabel requires a non-negative integer block offset");
720
721 return 0;
722 }
fat_setlabel_compute_progress(struct fun_context * fctx)723 int fat_setlabel_compute_progress(struct fun_context *fctx)
724 {
725 fctx->progress->total_units += FWUP_BLOCK_SIZE; // Arbitarily count as 1 block
726 return 0;
727 }
fat_setlabel_run(struct fun_context * fctx)728 int fat_setlabel_run(struct fun_context *fctx)
729 {
730 off_t block_offset = strtoull(fctx->argv[1], NULL, 0);
731
732 OK_OR_RETURN(fatfs_setlabel(fctx->output, block_offset, fctx->argv[2]));
733
734 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
735 return 0;
736 }
737
fat_touch_validate(struct fun_context * fctx)738 int fat_touch_validate(struct fun_context *fctx)
739 {
740 if (fctx->argc != 3)
741 ERR_RETURN("fat_touch requires a block offset and filename");
742
743 CHECK_ARG_UINT64(fctx->argv[1], "fat_touch requires a non-negative integer block offset");
744
745 return 0;
746 }
fat_touch_compute_progress(struct fun_context * fctx)747 int fat_touch_compute_progress(struct fun_context *fctx)
748 {
749 fctx->progress->total_units += FWUP_BLOCK_SIZE; // Arbitarily count as 1 block
750 return 0;
751 }
fat_touch_run(struct fun_context * fctx)752 int fat_touch_run(struct fun_context *fctx)
753 {
754 off_t block_offset = strtoull(fctx->argv[1], NULL, 0);
755
756 OK_OR_RETURN(fatfs_touch(fctx->output, block_offset, fctx->argv[2]));
757
758 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
759 return 0;
760 }
761
762 #define GPT_UNITS (GPT_SIZE * 2) // primary and secondary GPT
763
gpt_write_validate(struct fun_context * fctx)764 int gpt_write_validate(struct fun_context *fctx)
765 {
766 if (fctx->argc != 2)
767 ERR_RETURN("gpt_write requires a gpt");
768
769 const char *gpt_name = fctx->argv[1];
770 cfg_t *gptsec = cfg_gettsec(fctx->cfg, "gpt", gpt_name);
771
772 if (!gptsec)
773 ERR_RETURN("gpt_write can't find gpt reference");
774
775 return 0;
776 }
gpt_write_compute_progress(struct fun_context * fctx)777 int gpt_write_compute_progress(struct fun_context *fctx)
778 {
779 fctx->progress->total_units += GPT_UNITS;
780 return 0;
781 }
gpt_write_run(struct fun_context * fctx)782 int gpt_write_run(struct fun_context *fctx)
783 {
784 const char *gpt_name = fctx->argv[1];
785 cfg_t *gptsec = cfg_gettsec(fctx->cfg, "gpt", gpt_name);
786 int rc = 0;
787
788 uint8_t *mbr_and_primary_gpt = malloc(FWUP_BLOCK_SIZE + GPT_SIZE);
789 uint8_t *secondary_gpt = malloc(GPT_SIZE);
790
791 off_t secondary_gpt_offset;
792 OK_OR_CLEANUP(gpt_create_cfg(gptsec, fctx->output->num_blocks, mbr_and_primary_gpt, secondary_gpt, &secondary_gpt_offset));
793
794 OK_OR_CLEANUP_MSG(block_cache_pwrite(fctx->output, mbr_and_primary_gpt, FWUP_BLOCK_SIZE + GPT_SIZE, 0, false),
795 "unexpected error writing protective mbr and primary gpt: %s", strerror(errno));
796 OK_OR_CLEANUP_MSG(block_cache_pwrite(fctx->output, secondary_gpt, GPT_SIZE, secondary_gpt_offset, false),
797 "unexpected error writing secondary gpt: %s", strerror(errno));
798
799 progress_report(fctx->progress, GPT_UNITS);
800
801 cleanup:
802 free(mbr_and_primary_gpt);
803 free(secondary_gpt);
804 return rc;
805 }
806
mbr_write_validate(struct fun_context * fctx)807 int mbr_write_validate(struct fun_context *fctx)
808 {
809 if (fctx->argc != 2)
810 ERR_RETURN("mbr_write requires an mbr");
811
812 const char *mbr_name = fctx->argv[1];
813 cfg_t *mbrsec = cfg_gettsec(fctx->cfg, "mbr", mbr_name);
814
815 if (!mbrsec)
816 ERR_RETURN("mbr_write can't find mbr reference");
817
818 return 0;
819 }
mbr_write_compute_progress(struct fun_context * fctx)820 int mbr_write_compute_progress(struct fun_context *fctx)
821 {
822 fctx->progress->total_units += FWUP_BLOCK_SIZE;
823 return 0;
824 }
mbr_write_run(struct fun_context * fctx)825 int mbr_write_run(struct fun_context *fctx)
826 {
827 const char *mbr_name = fctx->argv[1];
828 cfg_t *mbrsec = cfg_gettsec(fctx->cfg, "mbr", mbr_name);
829
830 uint8_t buffer[FWUP_BLOCK_SIZE];
831 OK_OR_RETURN(mbr_create_cfg(mbrsec, fctx->output->num_blocks, buffer));
832
833 OK_OR_RETURN_MSG(block_cache_pwrite(fctx->output, buffer, FWUP_BLOCK_SIZE, 0, false),
834 "unexpected error writing mbr: %s", strerror(errno));
835
836 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
837 return 0;
838 }
839
trim_validate(struct fun_context * fctx)840 int trim_validate(struct fun_context *fctx)
841 {
842 if (fctx->argc != 3)
843 ERR_RETURN("trim requires a block offset and count");
844
845 CHECK_ARG_UINT64(fctx->argv[1], "trim requires a non-negative integer block offset");
846 CHECK_ARG_UINT64_RANGE(fctx->argv[2], 1, INT64_MAX / FWUP_BLOCK_SIZE, "trim requires a block count >1");
847
848 return 0;
849 }
trim_compute_progress(struct fun_context * fctx)850 int trim_compute_progress(struct fun_context *fctx)
851 {
852 off_t block_count = strtoull(fctx->argv[2], NULL, 0);
853
854 // Use a heuristic for counting trim progress units -> 1 per 128KB
855 fctx->progress->total_units += block_count / 256;
856
857 return 0;
858 }
trim_run(struct fun_context * fctx)859 int trim_run(struct fun_context *fctx)
860 {
861 off_t block_offset = strtoull(fctx->argv[1], NULL, 0);
862 off_t block_count = strtoull(fctx->argv[2], NULL, 0);
863
864 off_t offset = block_offset * FWUP_BLOCK_SIZE;
865 off_t count = block_count * FWUP_BLOCK_SIZE;
866
867 OK_OR_RETURN(block_cache_trim(fctx->output, offset, count, true));
868
869 progress_report(fctx->progress, block_count / 256);
870 return 0;
871 }
872
873
uboot_recover_validate(struct fun_context * fctx)874 int uboot_recover_validate(struct fun_context *fctx)
875 {
876 if (fctx->argc != 2)
877 ERR_RETURN("uboot_recover requires a uboot-environment reference");
878
879 const char *uboot_env_name = fctx->argv[1];
880 cfg_t *ubootsec = cfg_gettsec(fctx->cfg, "uboot-environment", uboot_env_name);
881
882 if (!ubootsec)
883 ERR_RETURN("uboot_recover can't find uboot-environment reference");
884
885 return 0;
886 }
uboot_recover_compute_progress(struct fun_context * fctx)887 int uboot_recover_compute_progress(struct fun_context *fctx)
888 {
889 fctx->progress->total_units += FWUP_BLOCK_SIZE; // Arbitarily count as 1 block
890 return 0;
891 }
uboot_recover_run(struct fun_context * fctx)892 int uboot_recover_run(struct fun_context *fctx)
893 {
894 int rc = 0;
895 const char *uboot_env_name = fctx->argv[1];
896 cfg_t *ubootsec = cfg_gettsec(fctx->cfg, "uboot-environment", uboot_env_name);
897 struct uboot_env env;
898 struct uboot_env clean_env;
899
900 if (uboot_env_create_cfg(ubootsec, &env) < 0 ||
901 uboot_env_create_cfg(ubootsec, &clean_env) < 0)
902 return -1;
903
904 if (uboot_env_read(fctx->output, &env) < 0) {
905 // Corrupt, so make a clean environment and write it.
906 OK_OR_CLEANUP(uboot_env_write(fctx->output, &clean_env));
907 }
908
909 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
910
911 cleanup:
912 uboot_env_free(&env);
913 uboot_env_free(&clean_env);
914 return rc;
915 }
916
uboot_clearenv_validate(struct fun_context * fctx)917 int uboot_clearenv_validate(struct fun_context *fctx)
918 {
919 if (fctx->argc != 2)
920 ERR_RETURN("uboot_clearenv requires a uboot-environment reference");
921
922 const char *uboot_env_name = fctx->argv[1];
923 cfg_t *ubootsec = cfg_gettsec(fctx->cfg, "uboot-environment", uboot_env_name);
924
925 if (!ubootsec)
926 ERR_RETURN("uboot_clearenv can't find uboot-environment reference");
927
928 return 0;
929 }
uboot_clearenv_compute_progress(struct fun_context * fctx)930 int uboot_clearenv_compute_progress(struct fun_context *fctx)
931 {
932 fctx->progress->total_units += FWUP_BLOCK_SIZE; // Arbitarily count as 1 block
933 return 0;
934 }
uboot_clearenv_run(struct fun_context * fctx)935 int uboot_clearenv_run(struct fun_context *fctx)
936 {
937 int rc = 0;
938 const char *uboot_env_name = fctx->argv[1];
939 cfg_t *ubootsec = cfg_gettsec(fctx->cfg, "uboot-environment", uboot_env_name);
940 struct uboot_env env;
941
942 if (uboot_env_create_cfg(ubootsec, &env) < 0)
943 return -1;
944
945 OK_OR_CLEANUP(uboot_env_write(fctx->output, &env));
946
947 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
948
949 cleanup:
950 uboot_env_free(&env);
951 return rc;
952 }
953
uboot_setenv_validate(struct fun_context * fctx)954 int uboot_setenv_validate(struct fun_context *fctx)
955 {
956 if (fctx->argc != 4)
957 ERR_RETURN("uboot_setenv requires a uboot-environment reference, variable name and value");
958
959 const char *uboot_env_name = fctx->argv[1];
960 cfg_t *ubootsec = cfg_gettsec(fctx->cfg, "uboot-environment", uboot_env_name);
961
962 if (!ubootsec)
963 ERR_RETURN("uboot_setenv can't find uboot-environment reference");
964
965 return 0;
966 }
uboot_setenv_compute_progress(struct fun_context * fctx)967 int uboot_setenv_compute_progress(struct fun_context *fctx)
968 {
969 fctx->progress->total_units += FWUP_BLOCK_SIZE; // Arbitarily count as 1 block
970 return 0;
971 }
uboot_setenv_run(struct fun_context * fctx)972 int uboot_setenv_run(struct fun_context *fctx)
973 {
974 int rc = 0;
975 const char *uboot_env_name = fctx->argv[1];
976 cfg_t *ubootsec = cfg_gettsec(fctx->cfg, "uboot-environment", uboot_env_name);
977 struct uboot_env env;
978
979 if (uboot_env_create_cfg(ubootsec, &env) < 0)
980 return -1;
981
982 OK_OR_CLEANUP(uboot_env_read(fctx->output, &env));
983
984 OK_OR_CLEANUP(uboot_env_setenv(&env, fctx->argv[2], fctx->argv[3]));
985
986 OK_OR_CLEANUP(uboot_env_write(fctx->output, &env));
987
988 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
989
990 cleanup:
991 uboot_env_free(&env);
992 return rc;
993 }
994
uboot_unsetenv_validate(struct fun_context * fctx)995 int uboot_unsetenv_validate(struct fun_context *fctx)
996 {
997 if (fctx->argc != 3)
998 ERR_RETURN("uboot_unsetenv requires a uboot-environment reference and a variable name");
999
1000 const char *uboot_env_name = fctx->argv[1];
1001 cfg_t *ubootsec = cfg_gettsec(fctx->cfg, "uboot-environment", uboot_env_name);
1002
1003 if (!ubootsec)
1004 ERR_RETURN("uboot_unsetenv can't find uboot-environment reference");
1005
1006 return 0;
1007 }
uboot_unsetenv_compute_progress(struct fun_context * fctx)1008 int uboot_unsetenv_compute_progress(struct fun_context *fctx)
1009 {
1010 fctx->progress->total_units += FWUP_BLOCK_SIZE; // Arbitarily count as 1 block
1011 return 0;
1012 }
uboot_unsetenv_run(struct fun_context * fctx)1013 int uboot_unsetenv_run(struct fun_context *fctx)
1014 {
1015 int rc = 0;
1016 const char *uboot_env_name = fctx->argv[1];
1017 cfg_t *ubootsec = cfg_gettsec(fctx->cfg, "uboot-environment", uboot_env_name);
1018 struct uboot_env env;
1019
1020 OK_OR_RETURN(uboot_env_create_cfg(ubootsec, &env));
1021
1022 OK_OR_CLEANUP(uboot_env_read(fctx->output, &env));
1023
1024 OK_OR_CLEANUP(uboot_env_unsetenv(&env, fctx->argv[2]));
1025
1026 OK_OR_CLEANUP(uboot_env_write(fctx->output, &env));
1027
1028 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
1029
1030 cleanup:
1031 uboot_env_free(&env);
1032 return rc;
1033 }
1034
error_validate(struct fun_context * fctx)1035 int error_validate(struct fun_context *fctx)
1036 {
1037 if (fctx->argc != 2)
1038 ERR_RETURN("error() requires a message parameter");
1039
1040 return 0;
1041 }
error_compute_progress(struct fun_context * fctx)1042 int error_compute_progress(struct fun_context *fctx)
1043 {
1044 (void) fctx; // UNUSED
1045 return 0;
1046 }
error_run(struct fun_context * fctx)1047 int error_run(struct fun_context *fctx)
1048 {
1049 ERR_RETURN("%s", fctx->argv[1]);
1050 }
1051
info_validate(struct fun_context * fctx)1052 int info_validate(struct fun_context *fctx)
1053 {
1054 if (fctx->argc != 2)
1055 ERR_RETURN("info() requires a message parameter");
1056
1057 return 0;
1058 }
info_compute_progress(struct fun_context * fctx)1059 int info_compute_progress(struct fun_context *fctx)
1060 {
1061 (void) fctx; // UNUSED
1062 return 0;
1063 }
info_run(struct fun_context * fctx)1064 int info_run(struct fun_context *fctx)
1065 {
1066 fwup_warnx("%s", fctx->argv[1]);
1067 return 0;
1068 }
1069
check_unsafe(struct fun_context * fctx)1070 static int check_unsafe(struct fun_context *fctx)
1071 {
1072 if (!fwup_unsafe)
1073 ERR_RETURN("%s requires --unsafe", fctx->argv[0]);
1074 return 0;
1075 }
1076
path_write_validate(struct fun_context * fctx)1077 int path_write_validate(struct fun_context *fctx)
1078 {
1079 if (fctx->type != FUN_CONTEXT_FILE)
1080 ERR_RETURN("path_write only usable in on-resource");
1081
1082 if (fctx->argc != 2)
1083 ERR_RETURN("path_write requires a file path");
1084
1085 return 0;
1086 }
path_write_compute_progress(struct fun_context * fctx)1087 int path_write_compute_progress(struct fun_context *fctx)
1088 {
1089 return process_resource_compute_progress(fctx, false);
1090 }
1091 struct path_write_cookie {
1092 const char *output_filename;
1093 FILE *fp;
1094 };
path_write_pwrite_callback(void * cookie,const void * buf,size_t count,off_t offset)1095 static int path_write_pwrite_callback(void *cookie, const void *buf, size_t count, off_t offset)
1096 {
1097 struct path_write_cookie *fwc = (struct path_write_cookie *) cookie;
1098
1099 OK_OR_RETURN_MSG(fseek(fwc->fp, (long) offset, SEEK_SET), "seek to offset %ld failed on '%s'", (long) offset, fwc->output_filename);
1100
1101 size_t written = fwrite(buf, 1, count, fwc->fp);
1102 if (written != count)
1103 ERR_RETURN("path_write failed to write '%s'", fwc->output_filename);
1104 return 0;
1105 }
path_write_final_hole_callback(void * cookie,off_t hole_size,off_t file_size)1106 static int path_write_final_hole_callback(void *cookie, off_t hole_size, off_t file_size)
1107 {
1108 (void) hole_size;
1109
1110 // Write a zero at the last offset to force the file to expand to the proper size
1111 char zero = 0;
1112 return path_write_pwrite_callback(cookie, &zero, 1, file_size - 1);
1113 }
path_write_run(struct fun_context * fctx)1114 int path_write_run(struct fun_context *fctx)
1115 {
1116 OK_OR_RETURN(check_unsafe(fctx));
1117
1118 struct path_write_cookie pwc;
1119 pwc.output_filename = fctx->argv[1];
1120 pwc.fp = fopen(pwc.output_filename, "wb");
1121 if (pwc.fp == NULL)
1122 ERR_RETURN("path_write can't open '%s'", pwc.output_filename);
1123
1124 int rc = 0;
1125 OK_OR_CLEANUP(process_resource(fctx,
1126 false,
1127 path_write_pwrite_callback,
1128 path_write_final_hole_callback,
1129 &pwc));
1130
1131 cleanup:
1132 fclose(pwc.fp);
1133 return rc;
1134 }
1135
pipe_write_validate(struct fun_context * fctx)1136 int pipe_write_validate(struct fun_context *fctx)
1137 {
1138 if (fctx->type != FUN_CONTEXT_FILE)
1139 ERR_RETURN("pipe_write only usable in on-resource");
1140
1141 if (fctx->argc != 2)
1142 ERR_RETURN("pipe_write requires a command to execute");
1143
1144 return 0;
1145 }
pipe_write_compute_progress(struct fun_context * fctx)1146 int pipe_write_compute_progress(struct fun_context *fctx)
1147 {
1148 return process_resource_compute_progress(fctx, true);
1149 }
1150 struct pipe_write_cookie {
1151 const char *pipe_command;
1152 off_t last_offset;
1153 FILE *fp;
1154 };
pipe_write_pwrite_callback(void * cookie,const void * buf,size_t count,off_t offset)1155 static int pipe_write_pwrite_callback(void *cookie, const void *buf, size_t count, off_t offset)
1156 {
1157 struct pipe_write_cookie *pwc = (struct pipe_write_cookie *) cookie;
1158
1159 if (pwc->last_offset != offset) {
1160 // Fill in the gap with zeros
1161 char zeros[FWUP_BLOCK_SIZE];
1162 memset(zeros, 0, sizeof(zeros));
1163
1164 while (pwc->last_offset < offset) {
1165 size_t to_write = offset - pwc->last_offset;
1166 if (to_write > sizeof(zeros))
1167 to_write = sizeof(zeros);
1168 size_t written = fwrite(zeros, 1, to_write, pwc->fp);
1169 if (written != count)
1170 ERR_RETURN("pipe_write failed to write '%s'", pwc->pipe_command);
1171 pwc->last_offset += to_write;
1172 }
1173 }
1174 size_t written = fwrite(buf, 1, count, pwc->fp);
1175 if (written != count)
1176 ERR_RETURN("pipe_write failed to write '%s'", pwc->pipe_command);
1177 pwc->last_offset += count;
1178 return 0;
1179 }
pipe_write_final_hole_callback(void * cookie,off_t hole_size,off_t file_size)1180 static int pipe_write_final_hole_callback(void *cookie, off_t hole_size, off_t file_size)
1181 {
1182 (void) hole_size;
1183
1184 // Write a zero at the last offset to force the file to expand to the proper size
1185 char zero = 0;
1186 return pipe_write_pwrite_callback(cookie, &zero, 1, file_size - 1);
1187 }
pipe_write_run(struct fun_context * fctx)1188 int pipe_write_run(struct fun_context *fctx)
1189 {
1190 OK_OR_RETURN(check_unsafe(fctx));
1191
1192 struct pipe_write_cookie pwc;
1193 pwc.pipe_command = fctx->argv[1];
1194 pwc.last_offset = 0;
1195 #if defined(_WIN32) || defined(__CYGWIN__)
1196 pwc.fp = popen(pwc.pipe_command, "wb");
1197 #else
1198 pwc.fp = popen(pwc.pipe_command, "w");
1199 #endif
1200 if (!pwc.fp)
1201 ERR_RETURN("pipe_write can't run '%s'", pwc.pipe_command);
1202
1203 int rc = 0;
1204 OK_OR_CLEANUP(process_resource(fctx,
1205 true,
1206 pipe_write_pwrite_callback,
1207 pipe_write_final_hole_callback,
1208 &pwc));
1209
1210 cleanup:
1211 if (pclose(pwc.fp) != 0)
1212 ERR_RETURN("command '%s' returned an error to pipe_write", pwc.pipe_command);
1213
1214 return rc;
1215 }
1216
execute_validate(struct fun_context * fctx)1217 int execute_validate(struct fun_context *fctx)
1218 {
1219 if (fctx->argc != 2)
1220 ERR_RETURN("execute requires a command to execute");
1221
1222 return 0;
1223 }
execute_compute_progress(struct fun_context * fctx)1224 int execute_compute_progress(struct fun_context *fctx)
1225 {
1226 fctx->progress->total_units += FWUP_BLOCK_SIZE; // Arbitarily count as 1 block
1227 return 0;
1228 }
execute_run(struct fun_context * fctx)1229 int execute_run(struct fun_context *fctx)
1230 {
1231 assert(fctx->on_event);
1232 OK_OR_RETURN(check_unsafe(fctx));
1233
1234 char const *cmd_name = fctx->argv[1];
1235 int status = system(cmd_name);
1236 if (status < 0)
1237 ERR_RETURN("execute couldn't run '%s'", cmd_name);
1238 if (status != 0)
1239 ERR_RETURN("'%s' failed with exit status %d", cmd_name, status);
1240
1241 progress_report(fctx->progress, FWUP_BLOCK_SIZE);
1242 return 0;
1243 }
1244