blob: bbcc4f5b071321fdb4e85aa0186e932ddaaf145e [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <dirent.h>
#include <fcntl.h>
#include <getopt.h>
#include <libgen.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <blobfs/blobfs.h>
#include <blobfs/fsck.h>
#include <fbl/auto_call.h>
#include <fbl/string.h>
#include <fbl/unique_fd.h>
#include <fbl/unique_ptr.h>
#include <fbl/vector.h>
#include <fs/vfs.h>
#include <fuchsia/hardware/block/c/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/fzl/fdio.h>
#include <lib/zx/channel.h>
#include <trace-provider/provider.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <utility>
namespace {
int Mount(fbl::unique_fd fd, blobfs::MountOptions* options) {
if (options->writability == blobfs::Writability::Writable) {
fzl::UnownedFdioCaller caller(fd.get());
fuchsia_hardware_block_BlockInfo block_info;
zx_status_t status;
zx_status_t io_status = fuchsia_hardware_block_BlockGetInfo(caller.borrow_channel(),
&status, &block_info);
if (io_status != ZX_OK) {
status = io_status;
}
if (status != ZX_OK) {
FS_TRACE_ERROR("blobfs: Unable to query block device, fd: %d status: 0x%x\n",
fd.get(), status);
return -1;
}
if (block_info.flags & BLOCK_FLAG_READONLY) {
FS_TRACE_WARN("blobfs: Mounting as read-only. WARNING: Journal will not be applied\n");
options->writability = blobfs::Writability::ReadOnlyDisk;
}
}
zx::channel root = zx::channel(zx_take_startup_handle(PA_HND(PA_USER0, 0)));
if (!root.is_valid()) {
FS_TRACE_ERROR("blobfs: Could not access startup handle to mount point\n");
return -1;
}
async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
trace::TraceProvider provider(loop.dispatcher());
auto loop_quit = [&loop]() {
loop.Quit();
FS_TRACE_WARN("blobfs: Unmounted\n");
};
if (blobfs::Mount(loop.dispatcher(), std::move(fd), *options, std::move(root),
std::move(loop_quit)) != ZX_OK) {
return -1;
}
loop.Run();
return ZX_OK;
}
int Mkfs(fbl::unique_fd fd, blobfs::MountOptions* options) {
uint64_t block_count;
if (blobfs::GetBlockCount(fd.get(), &block_count)) {
fprintf(stderr, "blobfs: cannot find end of underlying device\n");
return -1;
}
return blobfs::Mkfs(fd.get(), block_count);
}
int Fsck(fbl::unique_fd fd, blobfs::MountOptions* options) {
fbl::unique_ptr<blobfs::Blobfs> blobfs;
if (blobfs::Initialize(std::move(fd), *options, &blobfs) != ZX_OK) {
return -1;
}
return blobfs::Fsck(std::move(blobfs), options->journal);
}
typedef int (*CommandFunction)(fbl::unique_fd fd, blobfs::MountOptions* options);
const struct {
const char* name;
CommandFunction func;
const char* help;
} kCmds[] = {
{"create", Mkfs, "initialize filesystem"},
{"mkfs", Mkfs, "initialize filesystem"},
{"check", Fsck, "check filesystem integrity"},
{"fsck", Fsck, "check filesystem integrity"},
{"mount", Mount, "mount filesystem"},
};
int usage() {
fprintf(stderr,
"usage: blobfs [ <options>* ] <command> [ <arg>* ]\n"
"\n"
"options: -r|--readonly Mount filesystem read-only\n"
" -m|--metrics Collect filesystem metrics\n"
" -j|--journal Utilize the blobfs journal\n"
" For fsck, the journal is replayed before verification\n"
" -h|--help Display this message\n"
"\n"
"On Fuchsia, blobfs takes the block device argument by handle.\n"
"This can make 'blobfs' commands hard to invoke from command line.\n"
"Try using the [mkfs,fsck,mount,umount] commands instead\n"
"\n");
for (unsigned n = 0; n < (sizeof(kCmds) / sizeof(kCmds[0])); n++) {
fprintf(stderr, "%9s %-10s %s\n", n ? "" : "commands:",
kCmds[n].name, kCmds[n].help);
}
fprintf(stderr, "\n");
return -1;
}
// Process options/commands and return open fd to device
int ProcessArgs(int argc, char** argv, CommandFunction* func, blobfs::MountOptions* options) {
while (1) {
static struct option opts[] = {
{"readonly", no_argument, nullptr, 'r'},
{"metrics", no_argument, nullptr, 'm'},
{"journal", no_argument, nullptr, 'j'},
{"help", no_argument, nullptr, 'h'},
{nullptr, 0, nullptr, 0},
};
int opt_index;
int c = getopt_long(argc, argv, "rmjh", opts, &opt_index);
if (c < 0) {
break;
}
switch (c) {
case 'r':
options->writability = blobfs::Writability::ReadOnlyFilesystem;
break;
case 'm':
options->metrics = true;
break;
case 'j':
options->journal = true;
break;
case 'h':
default:
return usage();
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
return usage();
}
const char* command = argv[0];
// Validate command
for (unsigned i = 0; i < sizeof(kCmds) / sizeof(kCmds[0]); i++) {
if (!strcmp(command, kCmds[i].name)) {
*func = kCmds[i].func;
}
}
if (*func == nullptr) {
fprintf(stderr, "Unknown command: %s\n", command);
return usage();
}
// Block device passed by handle
return FS_FD_BLOCKDEVICE;
}
} // namespace
int main(int argc, char** argv) {
CommandFunction func = nullptr;
blobfs::MountOptions options;
fbl::unique_fd fd(ProcessArgs(argc, argv, &func, &options));
if (!fd) {
return -1;
}
return func(std::move(fd), &options);
}