1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <assert.h>
6 #include <getopt.h>
7 #include <inttypes.h>
8 #include <libgen.h>
9 #include <limits.h>
10 #include <sys/stat.h>
11 
12 #include <fbl/algorithm.h>
13 #include <fbl/auto_call.h>
14 
15 #include "fs-host/common.h"
16 
17 #define MIN_ARGS 3
18 
19 // Options struct.
20 struct {
21     const char* name;
22     Option option;
23     const char* argument;
24     const char* default_value;
25     const char* help;
26 } OPTS[] = {
27     {"depfile",  Option::kDepfile,  "",        nullptr,
28         "Produce a depfile"},
29     {"readonly", Option::kReadonly, "",        nullptr,
30         "Mount filesystem read-only"},
31     {"offset",   Option::kOffset,   "[bytes]", "0",
32         "Byte offset at which minfs partition starts"},
33     {"length",   Option::kLength,   "[bytes]", "Remaining Length",
34         "Length in bytes of minfs partition"},
35     {"compress", Option::kCompress, "",        nullptr,
36         "Compress files before adding them to blobfs"},
37     {"help",     Option::kHelp,     "",        nullptr,
38         "Display this message"},
39 };
40 
41 // Commands struct.
42 struct {
43     const char* name;
44     Command command;
45     uint32_t flags;
46     ArgType arg_type;
47     const char* help;
48 } CMDS[] = {
49     {"create",   Command::kMkfs,     O_RDWR | O_CREAT, ArgType::kOptional,
50         "Initialize filesystem."},
51     {"mkfs",     Command::kMkfs,     O_RDWR | O_CREAT, ArgType::kOptional,
52         "Initialize filesystem."},
53     {"check",    Command::kFsck,     O_RDONLY,         ArgType::kNone,
54         "Check filesystem integrity."},
55     {"fsck",     Command::kFsck,     O_RDONLY,         ArgType::kNone,
56         "Check filesystem integrity."},
57     {"add",      Command::kAdd,      O_RDWR,           ArgType::kMany,
58         "Add files to an fs image (additional arguments required)."},
59     {"cp",       Command::kCp,       O_RDWR,           ArgType::kTwo,
60         "Copy to/from fs."},
61     {"mkdir",    Command::kMkdir,    O_RDWR,           ArgType::kOne,
62         "Create directory."},
63     {"ls",       Command::kLs,       O_RDONLY,         ArgType::kOne,
64         "List contents of directory."},
65     {"manifest", Command::kManifest, O_RDWR,           ArgType::kOne,
66         "Add files to fs as specified in manifest (deprecated)."},
67 };
68 
69 // Arguments struct.
70 struct {
71     const char* name;
72     Argument argument;
73 } ARGS[] = {
74     {"--manifest", Argument::kManifest},
75     {"--blob",     Argument::kBlob},
76 };
77 
ProcessAndRun(int argc,char ** argv)78 zx_status_t FsCreator::ProcessAndRun(int argc, char** argv) {
79     zx_status_t status;
80     if ((status = ProcessArgs(argc, argv)) != ZX_OK) {
81         return status;
82     }
83 
84     return RunCommand();
85 }
86 
Usage()87 zx_status_t FsCreator::Usage() {
88     fprintf(stderr,
89             "usage: %s [ <option>* ] <file-or-device>[@<size>] <command> [ <arg>* ]\n\n",
90             GetToolName());
91 
92     // Display all valid pre-command options.
93     bool first = true;
94     for (unsigned n = 0; n < fbl::count_of(OPTS); n++) {
95         if (IsOptionValid(OPTS[n].option)) {
96             fprintf(stderr, "%-8s -%c|--%-8s ", first ? "options:" : "",
97                     OPTS[n].name[0], OPTS[n].name);
98 
99             fprintf(stderr, "%-8s", OPTS[n].argument);
100 
101             fprintf(stderr, "\t%s\n", OPTS[n].help);
102             if (OPTS[n].default_value != nullptr) {
103                 fprintf(stderr, "%33s(Default = %s)\n", "", OPTS[n].default_value);
104             }
105             first = false;
106         }
107     }
108     fprintf(stderr, "\n");
109 
110     // Display all valid commands.
111     first = true;
112     for (unsigned n = 0; n < fbl::count_of(CMDS); n++) {
113         if (IsCommandValid(CMDS[n].command)) {
114             fprintf(stderr, "%9s %-10s %s\n", first ? "commands:" : "",
115                     CMDS[n].name, CMDS[n].help);
116             first = false;
117         }
118     }
119     fprintf(stderr, "\n");
120 
121     // Display all valid '--' arguments.
122     fprintf(stderr, "arguments (valid for create, one or more required for add):\n");
123     for (unsigned n = 0; n < fbl::count_of(ARGS); n++) {
124         if (IsArgumentValid(ARGS[n].argument)) {
125             fprintf(stderr, "\t%-10s <path>\n", ARGS[n].name);
126         }
127     }
128 
129     return ZX_ERR_INVALID_ARGS;
130 }
131 
ProcessManifest(char * manifest_path)132 zx_status_t FsCreator::ProcessManifest(char* manifest_path) {
133     fbl::unique_fd manifest_fd(open(manifest_path, O_RDONLY, 0644));
134     if (!manifest_fd) {
135         fprintf(stderr, "error: cannot open '%s'\n", manifest_path);
136         return ZX_ERR_IO;
137     }
138 
139     char dir_path[PATH_MAX];
140     strncpy(dir_path, dirname(manifest_path), PATH_MAX);
141     FILE* manifest = fdopen(manifest_fd.release(), "r");
142     while (true) {
143         // Keep processing lines in the manifest until we have reached EOF.
144         zx_status_t status = ProcessManifestLine(manifest, dir_path);
145         if (status == ZX_ERR_OUT_OF_RANGE) {
146             fclose(manifest);
147             return ZX_OK;
148         } else if (status != ZX_OK) {
149             fclose(manifest);
150             return status;
151         }
152     }
153 }
154 
ParseManifestLine(FILE * manifest,const char * dir_path,char * src,char * dst)155 zx_status_t FsCreator::ParseManifestLine(FILE* manifest, const char* dir_path, char* src, char* dst) {
156     size_t size = 0;
157     char* line = nullptr;
158 
159     // Always free the line on exiting this method.
160     auto cleanup = fbl::MakeAutoCall([&line]() { if (line) free(line); });
161 
162     // Retrieve the next line from the manifest.
163     ssize_t r = getline(&line, &size, manifest);
164     if (r < 0) {
165         return ZX_ERR_OUT_OF_RANGE;
166     }
167 
168     // Exit early if line is commented out
169     if (line[0] == '#') {
170         return ZX_OK;
171     }
172 
173     char* equals = strchr(line, '=');
174     char* src_start = line;
175 
176     if (equals != nullptr) {
177         // If we found an '=', there is a destination in this line.
178         // (Note that destinations are allowed but not required for blobfs.)
179         if (strchr(equals + 1, '=') != nullptr) {
180             fprintf(stderr, "Too many '=' in input\n");
181             return ZX_ERR_INVALID_ARGS;
182         }
183 
184         src_start = equals + 1;
185         equals[0] = '\0';
186 
187         strncat(dst, line, PATH_MAX - strlen(dst));
188     }
189 
190     // If the source is not an absolute path, append the manifest's local directory.
191     if (src_start[0] != '/') {
192         strncpy(src, dir_path, PATH_MAX);
193         strncat(src, "/", PATH_MAX - strlen(src));
194     }
195 
196     strncat(src, src_start, PATH_MAX - strlen(src));
197 
198     // Set the source path to terminate if it currently ends in a new line.
199     char* new_line = strchr(src, '\n');
200     if (new_line != nullptr) {
201         *new_line = '\0';
202     }
203 
204     return ZX_OK;
205 }
206 
ProcessArgs(int argc,char ** argv)207 zx_status_t FsCreator::ProcessArgs(int argc, char** argv) {
208     if (argc < MIN_ARGS) {
209         fprintf(stderr, "Not enough args\n");
210         return Usage();
211     }
212 
213     bool depfile_needed = false;
214 
215     // Read options.
216     while (true) {
217         // Set up options struct for pre-device option processing.
218         unsigned index = 0;
219         struct option opts[fbl::count_of(OPTS) + 1];
220         for (unsigned n = 0; n < fbl::count_of(OPTS); n++) {
221             if (IsOptionValid(OPTS[n].option)) {
222                 opts[index].name = OPTS[n].name;
223                 opts[index].has_arg = strlen(OPTS[n].argument) ? required_argument : no_argument;
224                 opts[index].flag = nullptr;
225                 opts[index].val = OPTS[n].name[0];
226                 index++;
227             }
228         }
229 
230         opts[index] = {nullptr, 0, nullptr, 0};
231 
232         int opt_index;
233         int c = getopt_long(argc, argv, "+dro:l:ch", opts, &opt_index);
234         if (c < 0) {
235             break;
236         }
237 
238         switch (c) {
239         case 'd':
240             depfile_needed = true;
241             break;
242         case 'r':
243             read_only_ = true;
244             break;
245         case 'o':
246             offset_ = atoll(optarg);
247             break;
248         case 'l':
249             length_ = atoll(optarg);
250             break;
251         case 'c':
252             compress_ = true;
253             break;
254         case 'h':
255         default:
256             return Usage();
257         }
258 
259     }
260 
261     argc -= optind;
262     argv += optind;
263 
264     if (argc < 2) {
265         fprintf(stderr, "Not enough arguments\n");
266         return Usage();
267     }
268 
269     // Read device name.
270     char* device = argv[0];
271     argc--;
272     argv++;
273 
274     // Read command name.
275     char* command = argv[0];
276     argc--;
277     argv++;
278 
279     uint32_t open_flags = 0;
280     ArgType arg_type = ArgType::kNone;
281 
282     // Validate command.
283     for (unsigned i = 0; i < sizeof(CMDS) / sizeof(CMDS[0]); i++) {
284         if (!strcmp(command, CMDS[i].name)) {
285             if (!IsCommandValid(CMDS[i].command)) {
286                 fprintf(stderr, "Invalid command %s\n", command);
287                 return Usage();
288             }
289 
290             command_ = CMDS[i].command;
291             open_flags = read_only_ ? O_RDONLY : CMDS[i].flags;
292             arg_type = CMDS[i].arg_type;
293         }
294     }
295 
296     if (command_ == Command::kNone) {
297         fprintf(stderr, "Unknown command: %s\n", argv[0]);
298         return Usage();
299     }
300 
301     // Parse the size argument (if any) from the device string.
302     size_t requested_size = 0;
303     if (ParseSize(device, &requested_size) != ZX_OK) {
304         return Usage();
305     }
306 
307     // Open the target device. Do this before we continue processing arguments, in case we are
308     // copying directories from a minfs image and need to pre-process them.
309     fd_.reset(open(device, open_flags, 0644));
310     if (!fd_) {
311         fprintf(stderr, "error: cannot open '%s'\n", device);
312         return ZX_ERR_IO;
313     }
314 
315     struct stat stats;
316     if (fstat(fd_.get(), &stats) < 0) {
317         fprintf(stderr, "Failed to stat device %s\n", device);
318         return ZX_ERR_IO;
319     }
320 
321     // Unless we are creating an image, the length_ has already been decided.
322     if (command_ != Command::kMkfs) {
323         if (length_) {
324             if (offset_ + length_ > stats.st_size) {
325                 fprintf(stderr, "Must specify offset + length <= file size\n");
326                 return ZX_ERR_INVALID_ARGS;
327             }
328         } else {
329             length_ = stats.st_size - offset_;
330         }
331     }
332 
333     // Verify that we've received a valid number of arguments for the given command.
334     bool valid = true;
335     switch (arg_type) {
336     case ArgType::kNone:
337         valid = argc == 0;
338         break;
339     case ArgType::kOne:
340         valid = argc == 1;
341         break;
342     case ArgType::kTwo:
343         valid = argc == 2;
344         break;
345     case ArgType::kMany:
346         valid = argc;
347         break;
348     case ArgType::kOptional:
349         break;
350     default:
351         return ZX_ERR_INTERNAL;
352     }
353 
354     if (!valid) {
355         fprintf(stderr, "Invalid arguments\n");
356         return Usage();
357     }
358 
359     // Process remaining arguments.
360     while (argc > 0) {
361         // Default to 2 arguments processed for manifest. If ProcessCustom is called, processed
362         // will be populated with the actual number of arguments used.
363         uint8_t processed = 2;
364         if (!strcmp(argv[0], "--manifest")) {
365             if (argc < 2) {
366                 return ZX_ERR_INVALID_ARGS;
367             }
368 
369             zx_status_t status;
370             if ((status = ProcessManifest(argv[1])) != ZX_OK) {
371                 return status;
372             }
373         } else if (ProcessCustom(argc, argv, &processed) != ZX_OK) {
374             return Usage();
375         }
376 
377         argc -= processed;
378         argv += processed;
379     }
380 
381     // Resize the file if we need to.
382     zx_status_t status;
383     if ((status = ResizeFile(requested_size, stats)) != ZX_OK) {
384         return status;
385     }
386 
387     if (depfile_needed) {
388         size_t len = strlen(device);
389         assert(len+2 < PATH_MAX);
390         char buf[PATH_MAX] = {0};
391         memcpy(&buf[0], device, strlen(device));
392         buf[len++] = '.';
393         buf[len++] = 'd';
394 
395         depfile_.reset(open(buf, O_CREAT|O_TRUNC|O_WRONLY, 0644));
396         if (!depfile_) {
397             fprintf(stderr, "error: cannot open '%s'\n", buf);
398             return ZX_ERR_IO;
399         }
400 
401         // update the buf to be suitable to pass to AppendDepfile.
402         buf[len-2] = ':';
403         buf[len-1] = 0;
404 
405         status = AppendDepfile(&buf[0]);
406     }
407 
408     return status;
409 }
410 
AppendDepfile(const char * str)411 zx_status_t FsCreator::AppendDepfile(const char* str) {
412     if (!depfile_) {
413         return ZX_OK;
414     }
415 
416     size_t len = strlen(str);
417     assert(len < PATH_MAX);
418     char buf[PATH_MAX] = {0};
419     memcpy(&buf[0], str, len);
420     buf[len++] = ' ';
421 
422     std::lock_guard<std::mutex> lock(depfile_lock_);
423 
424     // this code makes assumptions about the size of atomic writes on target
425     // platforms which currently hold true, but are not part of e.g. POSIX.
426     if (write(depfile_.get(), buf, len) != len) {
427         fprintf(stderr, "error: depfile append error\n");
428         return ZX_ERR_IO;
429     }
430     return ZX_OK;
431 }
432 
RunCommand()433 zx_status_t FsCreator::RunCommand() {
434     if (!fd_) {
435         fprintf(stderr, "Failed to open fd before running command\n");
436         return ZX_ERR_INTERNAL;
437     }
438 
439     switch (command_) {
440     case Command::kMkfs:
441         return Mkfs();
442     case Command::kFsck:
443         return Fsck();
444     case Command::kAdd:
445     case Command::kCp:
446     case Command::kManifest:
447     case Command::kMkdir:
448         return Add();
449     case Command::kLs:
450         return Ls();
451     default:
452         fprintf(stderr, "Error: Command not defined\n");
453         return ZX_ERR_INTERNAL;
454     }
455 }
456 
ParseSize(char * device,size_t * out)457 zx_status_t FsCreator::ParseSize(char* device, size_t* out) {
458     char* sizestr = nullptr;
459     if ((sizestr = strchr(device, '@')) != nullptr) {
460         if (command_ != Command::kMkfs) {
461             fprintf(stderr, "Cannot specify size for this command\n");
462             return ZX_ERR_INVALID_ARGS;
463         }
464         // Create a file with an explicitly requested size
465         *sizestr++ = 0;
466         char* end;
467         size_t size = strtoull(sizestr, &end, 10);
468         if (end == sizestr) {
469             fprintf(stderr, "%s: bad size: %s\n", GetToolName(), sizestr);
470             return ZX_ERR_INVALID_ARGS;
471         }
472         switch (end[0]) {
473         case 'M':
474         case 'm':
475             size *= (1024 * 1024);
476             end++;
477             break;
478         case 'G':
479         case 'g':
480             size *= (1024 * 1024 * 1024);
481             end++;
482             break;
483         }
484         if (end[0] || size == 0) {
485             fprintf(stderr, "%s: bad size: %s\n", GetToolName(), sizestr);
486             return ZX_ERR_INVALID_ARGS;
487         }
488 
489         if (length_ && offset_ + length_ > size) {
490             fprintf(stderr, "Must specify size > offset + length\n");
491             return ZX_ERR_INVALID_ARGS;
492         }
493         *out = size;
494     }
495 
496     return ZX_OK;
497 }
498 
ResizeFile(off_t requested_size,struct stat stats)499 zx_status_t FsCreator::ResizeFile(off_t requested_size, struct stat stats) {
500     if (command_ != Command::kMkfs) {
501         // This method is only valid on creation of the fs image.
502         return ZX_OK;
503     }
504 
505     // Calculate the total required size for the fs image, given all files that have been processed
506     // up to this point.
507     off_t required_size;
508     zx_status_t status = CalculateRequiredSize(&required_size);
509     if (status != ZX_OK) {
510         return status;
511     }
512 
513     bool is_block = S_ISBLK(stats.st_mode);
514 
515     if (requested_size) {
516         if (requested_size < required_size) {
517             // If the size requested by @ is smaller than the size required, return an error.
518             fprintf(stderr, "Must specify size larger than required size %" PRIu64 "\n",
519                     required_size);
520             return ZX_ERR_INVALID_ARGS;
521         } else if (is_block) {
522             // Do not allow re-sizing for block devices.
523             fprintf(stderr, "%s: @size argument is not supported for block device targets\n",
524                     GetToolName());
525             return ZX_ERR_INVALID_ARGS;
526         }
527     }
528 
529     if (command_ == Command::kMkfs && !is_block &&
530         (stats.st_size != required_size || requested_size)) {
531         // Only truncate the file size under the following conditions:
532         // 1.  We are creating the fs store for the first time.
533         // 2.  We are not operating on a block device.
534         // 3a. The current file size is different than the size required for the specified files, OR
535         // 3b. The user has requested a particular size using the @ argument.
536         off_t truncate_size = requested_size ? requested_size : required_size;
537 
538         if (length_ && (offset_ + length_) > truncate_size) {
539             // If an offset+length were specified and they are smaller than the minimum required,
540             // return an error.
541             fprintf(stderr, "Length %" PRIu64 " too small for required size %" PRIu64 "\n", length_,
542                     truncate_size);
543             return ZX_ERR_INVALID_ARGS;
544         }
545 
546         if (ftruncate(fd_.get(), truncate_size)) {
547             fprintf(stderr, "error: cannot truncate device\n");
548             return ZX_ERR_IO;
549         }
550 
551         if (!length_) {
552             length_ = truncate_size - offset_;
553         }
554     } else if (!length_) {
555         // If not otherwise specified, update length to be equal to the size of the image.
556         length_ = stats.st_size - offset_;
557     }
558 
559     return ZX_OK;
560 }
561