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