| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <dirent.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <getopt.h> |
| #include <libgen.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include <fbl/unique_ptr.h> |
| #include <fs/trace.h> |
| #include <fuchsia/hardware/block/c/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/fdio/fd.h> |
| #include <minfs/fsck.h> |
| #include <minfs/minfs.h> |
| #include <trace-provider/provider.h> |
| #include <zircon/compiler.h> |
| #include <zircon/process.h> |
| #include <zircon/processargs.h> |
| |
| #include <utility> |
| |
| namespace { |
| |
| int Fsck(fbl::unique_ptr<minfs::Bcache> bc, const minfs::MountOptions& options) { |
| return Fsck(std::move(bc)); |
| } |
| |
| int Mount(fbl::unique_ptr<minfs::Bcache> bc, const minfs::MountOptions& options) { |
| zx::channel root(zx_take_startup_handle(FS_HANDLE_ROOT_ID)); |
| if (!root) { |
| FS_TRACE_ERROR("minfs: Could not access startup handle to mount point\n"); |
| return ZX_ERR_BAD_STATE; |
| } |
| |
| async::Loop loop(&kAsyncLoopConfigNoAttachToThread); |
| trace::TraceProvider trace_provider(loop.dispatcher()); |
| |
| auto loop_quit = [&loop]() { |
| loop.Quit(); |
| FS_TRACE_WARN("minfs: Unmounted\n"); |
| }; |
| zx_status_t status; |
| if ((status = MountAndServe(options, loop.dispatcher(), std::move(bc), std::move(root), |
| std::move(loop_quit)) != ZX_OK)) { |
| if (options.verbose) { |
| fprintf(stderr, "minfs: Failed to mount: %d\n", status); |
| } |
| return -1; |
| } |
| |
| if (options.verbose) { |
| fprintf(stderr, "minfs: Mounted successfully\n"); |
| } |
| |
| loop.Run(); |
| return 0; |
| } |
| |
| int Mkfs(fbl::unique_ptr<minfs::Bcache> bc, const minfs::MountOptions& options) { |
| return Mkfs(options, std::move(bc)); |
| } |
| |
| struct { |
| const char* name; |
| int (*func)(fbl::unique_ptr<minfs::Bcache> bc, const minfs::MountOptions&); |
| uint32_t flags; |
| const char* help; |
| } CMDS[] = { |
| {"create", Mkfs, O_RDWR | O_CREAT, "initialize filesystem"}, |
| {"mkfs", Mkfs, O_RDWR | O_CREAT, "initialize filesystem"}, |
| {"check", Fsck, O_RDONLY, "check filesystem integrity"}, |
| {"fsck", Fsck, O_RDONLY, "check filesystem integrity"}, |
| }; |
| |
| int usage() { |
| fprintf(stderr, "usage: minfs [ <option>* ] <command> [ <arg>* ]\n" |
| "\n" |
| "options:\n" |
| " -v|--verbose Some debug messages\n" |
| " -r|--readonly Mount filesystem read-only\n" |
| " -m|--metrics Collect filesystem metrics\n" |
| " -s|--fvm_data_slices SLICES When mkfs on top of FVM,\n" |
| " preallocate |SLICES| slices of data. \n" |
| " -h|--help Display this message\n" |
| "\n" |
| "On Fuchsia, MinFS takes the block device argument by handle.\n" |
| "This can make 'minfs' commands hard to invoke from command line.\n" |
| "Try using the [mkfs,fsck,mount,umount] commands instead\n" |
| "\n"); |
| for (unsigned n = 0; n < fbl::count_of(CMDS); n++) { |
| fprintf(stderr, "%9s %-10s %s\n", n ? "" : "commands:", CMDS[n].name, CMDS[n].help); |
| } |
| fprintf(stderr, "%9s %-10s %s\n", "", "mount", "mount filesystem"); |
| fprintf(stderr, "\n"); |
| return -1; |
| } |
| |
| zx_status_t GetInfo(const fbl::unique_fd& fd, off_t* out_size, bool* out_readonly) { |
| fuchsia_hardware_block_BlockInfo info; |
| const fzl::UnownedFdioCaller connection(fd.get()); |
| zx_status_t status; |
| zx_status_t io_status = |
| fuchsia_hardware_block_BlockGetInfo(connection.borrow_channel(), &status, &info); |
| if (io_status != ZX_OK) { |
| status = io_status; |
| } |
| if (status != ZX_OK) { |
| fprintf(stderr, "error: minfs could not find size of device\n"); |
| return status; |
| } |
| *out_size = info.block_size * info.block_count; |
| *out_readonly = info.flags & fuchsia_hardware_block_FLAG_READONLY; |
| return ZX_OK; |
| } |
| |
| } // namespace |
| |
| int main(int argc, char** argv) { |
| minfs::MountOptions options; |
| options.readonly = false; |
| options.metrics = false; |
| options.verbose = false; |
| |
| while (1) { |
| static struct option opts[] = { |
| {"readonly", no_argument, nullptr, 'r'}, |
| {"metrics", no_argument, nullptr, 'm'}, |
| {"journal", no_argument, nullptr, 'j'}, |
| {"verbose", no_argument, nullptr, 'v'}, |
| {"fvm_data_slices", required_argument, nullptr, 's'}, |
| {"help", no_argument, nullptr, 'h'}, |
| {nullptr, 0, nullptr, 0}, |
| }; |
| int opt_index; |
| int c = getopt_long(argc, argv, "rmjvhs:", opts, &opt_index); |
| if (c < 0) { |
| break; |
| } |
| switch (c) { |
| case 'r': |
| options.readonly = true; |
| break; |
| case 'm': |
| options.metrics = true; |
| break; |
| case 'j': |
| // TODO(planders): Enable journaling here once minfs supports it. |
| fprintf(stderr, "minfs: Journaling option not supported\n"); |
| return usage(); |
| case 'v': |
| options.verbose = true; |
| break; |
| case 's': |
| options.fvm_data_slices = static_cast<uint32_t>(strtoul(optarg, NULL, 0)); |
| break; |
| case 'h': |
| default: |
| return usage(); |
| } |
| } |
| |
| argc -= optind; |
| argv += optind; |
| |
| if (argc != 1) { |
| return usage(); |
| } |
| char* cmd = argv[0]; |
| |
| // Block device passed by handle |
| zx::channel device = zx::channel(zx_take_startup_handle(FS_HANDLE_BLOCK_DEVICE_ID)); |
| int device_fd = -1; |
| zx_status_t status = fdio_fd_create(device.release(), &device_fd); |
| if (status != ZX_OK) { |
| fprintf(stderr, "blobfs: Could not access block device\n"); |
| return -1; |
| } |
| fbl::unique_fd fd(device_fd); |
| |
| off_t device_size = 0; |
| bool block_readonly = false; |
| status = GetInfo(fd, &device_size, &block_readonly); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| options.readonly = options.readonly || block_readonly; |
| |
| if (device_size == 0) { |
| fprintf(stderr, "minfs: failed to access block device\n"); |
| return usage(); |
| } |
| size_t block_count = device_size / minfs::kMinfsBlockSize; |
| |
| fbl::unique_ptr<minfs::Bcache> bc; |
| if (minfs::Bcache::Create(&bc, std::move(fd), static_cast<uint32_t>(block_count)) < 0) { |
| fprintf(stderr, "minfs: error: cannot create block cache\n"); |
| return -1; |
| } |
| |
| if (!strcmp(cmd, "mount")) { |
| return Mount(std::move(bc), options); |
| } |
| |
| for (unsigned i = 0; i < fbl::count_of(CMDS); i++) { |
| if (!strcmp(cmd, CMDS[i].name)) { |
| int r = CMDS[i].func(std::move(bc), options); |
| if (options.verbose) { |
| fprintf(stderr, "minfs: %s completed with result: %d\n", cmd, r); |
| } |
| return r; |
| } |
| } |
| return -1; |
| } |