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