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