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 <fcntl.h> 7#include <getopt.h> 8#include <libgen.h> 9#include <stdarg.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <string.h> 13#include <sys/stat.h> 14#include <unistd.h> 15 16#include <lib/async-loop/cpp/loop.h> 17#include <lib/zx/channel.h> 18#include <blobfs/blobfs.h> 19#include <blobfs/fsck.h> 20#include <fbl/auto_call.h> 21#include <fbl/string.h> 22#include <fbl/unique_fd.h> 23#include <fbl/unique_ptr.h> 24#include <fbl/vector.h> 25#include <fs/vfs.h> 26#include <trace-provider/provider.h> 27#include <zircon/process.h> 28#include <zircon/processargs.h> 29 30namespace { 31 32int do_blobfs_mount(fbl::unique_fd fd, blobfs::blob_options_t* options) { 33 if (!options->readonly) { 34 block_info_t block_info; 35 zx_status_t status = static_cast<zx_status_t>(ioctl_block_get_info(fd.get(), &block_info)); 36 if (status < ZX_OK) { 37 FS_TRACE_ERROR("blobfs: Unable to query block device, fd: %d status: 0x%x\n", 38 fd.get(), status); 39 return -1; 40 } 41 options->readonly = block_info.flags & BLOCK_FLAG_READONLY; 42 } 43 44 zx::channel root = zx::channel(zx_take_startup_handle(PA_HND(PA_USER0, 0))); 45 if (!root.is_valid()) { 46 FS_TRACE_ERROR("blobfs: Could not access startup handle to mount point\n"); 47 return -1; 48 } 49 50 async::Loop loop(&kAsyncLoopConfigNoAttachToThread); 51 trace::TraceProvider provider(loop.dispatcher()); 52 auto loop_quit = [&loop]() { loop.Quit(); }; 53 if (blobfs::blobfs_mount(loop.dispatcher(), fbl::move(fd), options, 54 fbl::move(root), fbl::move(loop_quit)) != ZX_OK) { 55 return -1; 56 } 57 loop.Run(); 58 return ZX_OK; 59} 60 61int do_blobfs_mkfs(fbl::unique_fd fd, blobfs::blob_options_t* options) { 62 uint64_t block_count; 63 if (blobfs::blobfs_get_blockcount(fd.get(), &block_count)) { 64 fprintf(stderr, "blobfs: cannot find end of underlying device\n"); 65 return -1; 66 } 67 68 int r = blobfs::blobfs_mkfs(fd.get(), block_count); 69 70 return r; 71} 72 73int do_blobfs_check(fbl::unique_fd fd, blobfs::blob_options_t* options) { 74 fbl::unique_ptr<blobfs::Blobfs> blobfs; 75 if (blobfs::blobfs_create(&blobfs, fbl::move(fd)) < 0) { 76 return -1; 77 } 78 79 return blobfs::blobfs_check(fbl::move(blobfs)); 80} 81 82typedef int (*CommandFunction)(fbl::unique_fd fd, blobfs::blob_options_t* options); 83 84struct { 85 const char* name; 86 CommandFunction func; 87 const char* help; 88} CMDS[] = { 89 {"create", do_blobfs_mkfs, "initialize filesystem"}, 90 {"mkfs", do_blobfs_mkfs, "initialize filesystem"}, 91 {"check", do_blobfs_check, "check filesystem integrity"}, 92 {"fsck", do_blobfs_check, "check filesystem integrity"}, 93 {"mount", do_blobfs_mount, "mount filesystem"}, 94}; 95 96int usage() { 97 fprintf(stderr, 98 "usage: blobfs [ <options>* ] <command> [ <arg>* ]\n" 99 "\n" 100 "options: -r|--readonly Mount filesystem read-only\n" 101 " -m|--metrics Collect filesystem metrics\n" 102 " -h|--help Display this message\n" 103 "\n" 104 "On Fuchsia, blobfs takes the block device argument by handle.\n" 105 "This can make 'blobfs' commands hard to invoke from command line.\n" 106 "Try using the [mkfs,fsck,mount,umount] commands instead\n" 107 "\n"); 108 for (unsigned n = 0; n < (sizeof(CMDS) / sizeof(CMDS[0])); n++) { 109 fprintf(stderr, "%9s %-10s %s\n", n ? "" : "commands:", 110 CMDS[n].name, CMDS[n].help); 111 } 112 fprintf(stderr, "\n"); 113 return -1; 114} 115 116// Process options/commands and return open fd to device 117int process_args(int argc, char** argv, CommandFunction* func, blobfs::blob_options_t* options) { 118 while (1) { 119 static struct option opts[] = { 120 {"readonly", no_argument, nullptr, 'r'}, 121 {"metrics", no_argument, nullptr, 'm'}, 122 {"help", no_argument, nullptr, 'h'}, 123 {nullptr, 0, nullptr, 0}, 124 }; 125 int opt_index; 126 int c = getopt_long(argc, argv, "rmh", opts, &opt_index); 127 if (c < 0) { 128 break; 129 } 130 switch (c) { 131 case 'r': 132 options->readonly = true; 133 break; 134 case 'm': 135 options->metrics = true; 136 break; 137 case 'h': 138 default: 139 return usage(); 140 } 141 } 142 143 argc -= optind; 144 argv += optind; 145 146 if (argc < 1) { 147 return usage(); 148 } 149 const char* command = argv[0]; 150 151 // Validate command 152 for (unsigned i = 0; i < sizeof(CMDS) / sizeof(CMDS[0]); i++) { 153 if (!strcmp(command, CMDS[i].name)) { 154 *func = CMDS[i].func; 155 } 156 } 157 158 if (*func == nullptr) { 159 fprintf(stderr, "Unknown command: %s\n", command); 160 return usage(); 161 } 162 163 // Block device passed by handle 164 return FS_FD_BLOCKDEVICE; 165} 166} // namespace 167 168int main(int argc, char** argv) { 169 CommandFunction func = nullptr; 170 blobfs::blob_options_t options; 171 fbl::unique_fd fd(process_args(argc, argv, &func, &options)); 172 173 if (!fd) { 174 return -1; 175 } 176 177 return func(fbl::move(fd), &options); 178} 179