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
30 #include <utility>
31
32 namespace {
33
Mount(fbl::unique_fd fd,blobfs::MountOptions * options)34 int Mount(fbl::unique_fd fd, blobfs::MountOptions* options) {
35 if (!options->readonly) {
36 block_info_t block_info;
37 zx_status_t status = static_cast<zx_status_t>(ioctl_block_get_info(fd.get(), &block_info));
38 if (status < ZX_OK) {
39 FS_TRACE_ERROR("blobfs: Unable to query block device, fd: %d status: 0x%x\n",
40 fd.get(), status);
41 return -1;
42 }
43 options->readonly = block_info.flags & BLOCK_FLAG_READONLY;
44 }
45
46 zx::channel root = zx::channel(zx_take_startup_handle(PA_HND(PA_USER0, 0)));
47 if (!root.is_valid()) {
48 FS_TRACE_ERROR("blobfs: Could not access startup handle to mount point\n");
49 return -1;
50 }
51
52 async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
53 trace::TraceProvider provider(loop.dispatcher());
54 auto loop_quit = [&loop]() { loop.Quit(); };
55 if (blobfs::Mount(loop.dispatcher(), std::move(fd), *options,
56 std::move(root), std::move(loop_quit)) != ZX_OK) {
57 return -1;
58 }
59 loop.Run();
60 return ZX_OK;
61 }
62
Mkfs(fbl::unique_fd fd,blobfs::MountOptions * options)63 int Mkfs(fbl::unique_fd fd, blobfs::MountOptions* options) {
64 uint64_t block_count;
65 if (blobfs::GetBlockCount(fd.get(), &block_count)) {
66 fprintf(stderr, "blobfs: cannot find end of underlying device\n");
67 return -1;
68 }
69
70 return blobfs::Mkfs(fd.get(), block_count);
71 }
72
Fsck(fbl::unique_fd fd,blobfs::MountOptions * options)73 int Fsck(fbl::unique_fd fd, blobfs::MountOptions* options) {
74 fbl::unique_ptr<blobfs::Blobfs> blobfs;
75 if (blobfs::Initialize(std::move(fd), *options, &blobfs) != ZX_OK) {
76 return -1;
77 }
78
79 return blobfs::Fsck(std::move(blobfs));
80 }
81
82 typedef int (*CommandFunction)(fbl::unique_fd fd, blobfs::MountOptions* options);
83
84 const struct {
85 const char* name;
86 CommandFunction func;
87 const char* help;
88 } kCmds[] = {
89 {"create", Mkfs, "initialize filesystem"},
90 {"mkfs", Mkfs, "initialize filesystem"},
91 {"check", Fsck, "check filesystem integrity"},
92 {"fsck", Fsck, "check filesystem integrity"},
93 {"mount", Mount, "mount filesystem"},
94 };
95
usage()96 int 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(kCmds) / sizeof(kCmds[0])); n++) {
109 fprintf(stderr, "%9s %-10s %s\n", n ? "" : "commands:",
110 kCmds[n].name, kCmds[n].help);
111 }
112 fprintf(stderr, "\n");
113 return -1;
114 }
115
116 // Process options/commands and return open fd to device
ProcessArgs(int argc,char ** argv,CommandFunction * func,blobfs::MountOptions * options)117 int ProcessArgs(int argc, char** argv, CommandFunction* func, blobfs::MountOptions* options) {
118 while (1) {
119 static struct option opts[] = {
120 {"readonly", no_argument, nullptr, 'r'},
121 {"metrics", no_argument, nullptr, 'm'},
122 {"journal", no_argument, nullptr, 'j'},
123 {"help", no_argument, nullptr, 'h'},
124 {nullptr, 0, nullptr, 0},
125 };
126 int opt_index;
127 int c = getopt_long(argc, argv, "rmjh", opts, &opt_index);
128 if (c < 0) {
129 break;
130 }
131 switch (c) {
132 case 'r':
133 options->readonly = true;
134 break;
135 case 'm':
136 options->metrics = true;
137 break;
138 case 'j':
139 options->journal = true;
140 break;
141 case 'h':
142 default:
143 return usage();
144 }
145 }
146
147 argc -= optind;
148 argv += optind;
149
150 if (argc < 1) {
151 return usage();
152 }
153 const char* command = argv[0];
154
155 // Validate command
156 for (unsigned i = 0; i < sizeof(kCmds) / sizeof(kCmds[0]); i++) {
157 if (!strcmp(command, kCmds[i].name)) {
158 *func = kCmds[i].func;
159 }
160 }
161
162 if (*func == nullptr) {
163 fprintf(stderr, "Unknown command: %s\n", command);
164 return usage();
165 }
166
167 // Block device passed by handle
168 return FS_FD_BLOCKDEVICE;
169 }
170 } // namespace
171
main(int argc,char ** argv)172 int main(int argc, char** argv) {
173 CommandFunction func = nullptr;
174 blobfs::MountOptions options;
175 fbl::unique_fd fd(ProcessArgs(argc, argv, &func, &options));
176
177 if (!fd) {
178 return -1;
179 }
180
181 return func(std::move(fd), &options);
182 }
183