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