1// Copyright 2017 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 <dirent.h> 6#include <errno.h> 7#include <fcntl.h> 8#include <getopt.h> 9#include <libgen.h> 10#include <stdarg.h> 11#include <stdio.h> 12#include <stdlib.h> 13#include <string.h> 14#include <sys/stat.h> 15#include <unistd.h> 16 17#include <fbl/unique_free_ptr.h> 18#include <fbl/unique_ptr.h> 19#include <fs/trace.h> 20#include <lib/async-loop/cpp/loop.h> 21#include <minfs/fsck.h> 22#include <minfs/minfs.h> 23#include <trace-provider/provider.h> 24#include <zircon/compiler.h> 25#include <zircon/process.h> 26#include <zircon/processargs.h> 27 28namespace { 29 30int do_minfs_check(fbl::unique_ptr<minfs::Bcache> bc, const minfs::Options& options) { 31 return minfs_check(fbl::move(bc)); 32} 33 34int do_minfs_mount(fbl::unique_ptr<minfs::Bcache> bc, const minfs::Options& options) { 35 zx_handle_t h = zx_take_startup_handle(PA_HND(PA_USER0, 0)); 36 if (h == ZX_HANDLE_INVALID) { 37 FS_TRACE_ERROR("minfs: Could not access startup handle to mount point\n"); 38 return ZX_ERR_BAD_STATE; 39 } 40 41 async::Loop loop(&kAsyncLoopConfigNoAttachToThread); 42 trace::TraceProvider trace_provider(loop.dispatcher()); 43 44 auto loop_quit = [&loop]() { loop.Quit(); }; 45 zx_status_t status; 46 if ((status = MountAndServe(&options, loop.dispatcher(), fbl::move(bc), zx::channel(h), 47 fbl::move(loop_quit)) != ZX_OK)) { 48 if (options.verbose) { 49 fprintf(stderr, "minfs: Failed to mount: %d\n", status); 50 } 51 return -1; 52 } 53 54 if (options.verbose) { 55 fprintf(stderr, "minfs: Mounted successfully\n"); 56 } 57 58 loop.Run(); 59 return 0; 60} 61 62int do_minfs_mkfs(fbl::unique_ptr<minfs::Bcache> bc, const minfs::Options& options) { 63 return Mkfs(options, fbl::move(bc)); 64} 65 66struct { 67 const char* name; 68 int (*func)(fbl::unique_ptr<minfs::Bcache> bc, const minfs::Options&); 69 uint32_t flags; 70 const char* help; 71} CMDS[] = { 72 {"create", do_minfs_mkfs, O_RDWR | O_CREAT, "initialize filesystem"}, 73 {"mkfs", do_minfs_mkfs, O_RDWR | O_CREAT, "initialize filesystem"}, 74 {"check", do_minfs_check, O_RDONLY, "check filesystem integrity"}, 75 {"fsck", do_minfs_check, O_RDONLY, "check filesystem integrity"}, 76}; 77 78int usage() { 79 fprintf(stderr, "usage: minfs [ <option>* ] <command> [ <arg>* ]\n" 80 "\n" 81 "options:\n" 82 " -v|--verbose Some debug messages\n" 83 " -r|--readonly Mount filesystem read-only\n" 84 " -m|--metrics Collect filesystem metrics\n" 85 " -s|--fvm_data_slices SLICES When mkfs on top of FVM,\n" 86 " preallocate |SLICES| slices of data. \n" 87 " -h|--help Display this message\n" 88 "\n" 89 "On Fuchsia, MinFS takes the block device argument by handle.\n" 90 "This can make 'minfs' commands hard to invoke from command line.\n" 91 "Try using the [mkfs,fsck,mount,umount] commands instead\n" 92 "\n"); 93 for (unsigned n = 0; n < fbl::count_of(CMDS); n++) { 94 fprintf(stderr, "%9s %-10s %s\n", n ? "" : "commands:", CMDS[n].name, CMDS[n].help); 95 } 96 fprintf(stderr, "%9s %-10s %s\n", "", "mount", "mount filesystem"); 97 fprintf(stderr, "\n"); 98 return -1; 99} 100 101off_t get_size(int fd) { 102 block_info_t info; 103 if (ioctl_block_get_info(fd, &info) != sizeof(info)) { 104 fprintf(stderr, "error: minfs could not find size of device\n"); 105 return 0; 106 } 107 return info.block_size * info.block_count; 108} 109 110} // namespace 111 112int main(int argc, char** argv) { 113 minfs::minfs_options_t options; 114 options.readonly = false; 115 options.metrics = false; 116 options.verbose = false; 117 118 while (1) { 119 static struct option opts[] = { 120 {"readonly", no_argument, nullptr, 'r'}, 121 {"metrics", no_argument, nullptr, 'm'}, 122 {"verbose", no_argument, nullptr, 'v'}, 123 {"fvm_data_slices", required_argument, nullptr, 's'}, 124 {"help", no_argument, nullptr, 'h'}, 125 {nullptr, 0, nullptr, 0}, 126 }; 127 int opt_index; 128 int c = getopt_long(argc, argv, "rmvhs:", opts, &opt_index); 129 if (c < 0) { 130 break; 131 } 132 switch (c) { 133 case 'r': 134 options.readonly = true; 135 break; 136 case 'm': 137 options.metrics = true; 138 break; 139 case 'v': 140 options.verbose = true; 141 break; 142 case 's': 143 options.fvm_data_slices = static_cast<uint32_t>(strtoul(optarg, NULL, 0)); 144 break; 145 case 'h': 146 default: 147 return usage(); 148 } 149 } 150 151 argc -= optind; 152 argv += optind; 153 154 // Block device passed by handle 155 if (argc != 1) { 156 return usage(); 157 } 158 char* cmd = argv[0]; 159 160 fbl::unique_fd fd; 161 fd.reset(FS_FD_BLOCKDEVICE); 162 if (!options.readonly) { 163 block_info_t block_info; 164 zx_status_t status = static_cast<zx_status_t>(ioctl_block_get_info(fd.get(), &block_info)); 165 if (status < ZX_OK) { 166 fprintf(stderr, "minfs: Unable to query block device, fd: %d status: 0x%x\n", fd.get(), 167 status); 168 return -1; 169 } 170 options.readonly = block_info.flags & BLOCK_FLAG_READONLY; 171 } 172 173 off_t size = get_size(fd.get()); 174 if (size == 0) { 175 fprintf(stderr, "minfs: failed to access block device\n"); 176 return usage(); 177 } 178 size /= minfs::kMinfsBlockSize; 179 180 fbl::unique_ptr<minfs::Bcache> bc; 181 if (minfs::Bcache::Create(&bc, fbl::move(fd), (uint32_t)size) < 0) { 182 fprintf(stderr, "minfs: error: cannot create block cache\n"); 183 return -1; 184 } 185 186 if (!strcmp(cmd, "mount")) { 187 return do_minfs_mount(fbl::move(bc), options); 188 } 189 190 for (unsigned i = 0; i < fbl::count_of(CMDS); i++) { 191 if (!strcmp(cmd, CMDS[i].name)) { 192 int r = CMDS[i].func(fbl::move(bc), options); 193 if (options.verbose) { 194 fprintf(stderr, "minfs: %s completed with result: %d\n", cmd, r); 195 } 196 return r; 197 } 198 } 199 return -1; 200} 201