| // 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 <fcntl.h> |
| #include <inttypes.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <fbl/algorithm.h> |
| #include <fbl/auto_call.h> |
| #include <fbl/string_buffer.h> |
| #include <fbl/unique_fd.h> |
| #include <fs-management/mount.h> |
| #include <fuchsia/device/c/fidl.h> |
| #include <fuchsia/hardware/block/c/fidl.h> |
| #include <fuchsia/hardware/block/partition/c/fidl.h> |
| #include <gpt/gpt.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/fd.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/fdio/unsafe.h> |
| #include <lib/fdio/watcher.h> |
| #include <lib/fzl/fdio.h> |
| #include <lib/fzl/time.h> |
| #include <lib/zx/channel.h> |
| #include <lib/zx/process.h> |
| #include <lib/zx/time.h> |
| #include <loader-service/loader-service.h> |
| #include <minfs/minfs.h> |
| #include <ramdevice-client/ramdisk.h> |
| #include <zircon/device/block.h> |
| #include <zircon/processargs.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls.h> |
| #include <zxcrypt/fdio-volume.h> |
| |
| #include <utility> |
| |
| #include "fs-manager.h" |
| |
| namespace devmgr { |
| namespace { |
| |
| class BlockWatcher { |
| public: |
| BlockWatcher(fbl::unique_ptr<FsManager> fshost, zx::unowned_job job, bool netboot) |
| : fshost_(std::move(fshost)), job_(job), netboot_(netboot) {} |
| |
| void FuchsiaStart() const { fshost_->FuchsiaStart(); } |
| |
| zx_status_t InstallFs(const char* path, zx::channel h) { |
| return fshost_->InstallFs(path, std::move(h)); |
| } |
| |
| const zx::unowned_job& Job() const { return job_; } |
| |
| bool Netbooting() const { return netboot_; } |
| |
| // Optionally checks the filesystem stored on the device at |device_path|, |
| // if "zircon.system.filesystem-check" is set. |
| zx_status_t CheckFilesystem(const char* device_path, disk_format_t df, |
| const fsck_options_t* options) const; |
| |
| // Attempts to mount a block device backed by |fd| to "/data". |
| // Fails if already mounted. |
| zx_status_t MountData(fbl::unique_fd fd, mount_options_t* options); |
| |
| // Attempts to mount a block device backed by |fd| to "/install". |
| // Fails if already mounted. |
| zx_status_t MountInstall(fbl::unique_fd fd, mount_options_t* options); |
| |
| // Attempts to mount a block device backed by |fd| to "/blob". |
| // Fails if already mounted. |
| zx_status_t MountBlob(fbl::unique_fd fd, mount_options_t* options); |
| |
| private: |
| fbl::unique_ptr<FsManager> fshost_; |
| zx::unowned_job job_; |
| bool netboot_ = false; |
| bool data_mounted_ = false; |
| bool install_mounted_ = false; |
| bool blob_mounted_ = false; |
| }; |
| |
| // TODO(smklein): When launching filesystems can pass a cookie representing a unique |
| // BlockWatcher instance, this global should be removed. |
| zx::unowned_job g_job; |
| |
| void pkgfs_finish(BlockWatcher* watcher, zx::process proc, zx::channel pkgfs_root) { |
| auto deadline = zx::deadline_after(zx::sec(5)); |
| zx_signals_t observed; |
| zx_status_t status = |
| proc.wait_one(ZX_USER_SIGNAL_0 | ZX_PROCESS_TERMINATED, deadline, &observed); |
| if (status != ZX_OK) { |
| printf("fshost: pkgfs did not signal completion: %d (%s)\n", status, |
| zx_status_get_string(status)); |
| return; |
| } |
| if (!(observed & ZX_USER_SIGNAL_0)) { |
| printf("fshost: pkgfs terminated prematurely\n"); |
| return; |
| } |
| // re-export /pkgfs/system as /system |
| zx::channel system_channel, system_req; |
| if (zx::channel::create(0, &system_channel, &system_req) != ZX_OK) { |
| return; |
| } |
| if (fdio_open_at(pkgfs_root.get(), "system", FS_READONLY_DIR_FLAGS, |
| system_req.release()) != ZX_OK) { |
| return; |
| } |
| // re-export /pkgfs/packages/shell-commands/0/bin as /bin |
| zx::channel bin_chan, bin_req; |
| if (zx::channel::create(0, &bin_chan, &bin_req) != ZX_OK) { |
| return; |
| } |
| if (fdio_open_at(pkgfs_root.get(), "packages/shell-commands/0/bin", FS_READONLY_DIR_FLAGS, |
| bin_req.release()) != ZX_OK) { |
| // non-fatal. |
| printf("fshost: failed to install /bin (could not open shell-commands)\n"); |
| } |
| |
| if (watcher->InstallFs("/pkgfs", std::move(pkgfs_root)) != ZX_OK) { |
| printf("fshost: failed to install /pkgfs\n"); |
| return; |
| } |
| |
| if (watcher->InstallFs("/system", std::move(system_channel)) != ZX_OK) { |
| printf("fshost: failed to install /system\n"); |
| return; |
| } |
| |
| // as above, failure of /bin export is non-fatal. |
| if (watcher->InstallFs("/bin", std::move(bin_chan)) != ZX_OK) { |
| printf("fshost: failed to install /bin\n"); |
| } |
| |
| // start the appmgr |
| watcher->FuchsiaStart(); |
| } |
| |
| // Launching pkgfs uses its own loader service and command lookup to run out of |
| // the blobfs without any real filesystem. Files are found by |
| // getenv("zircon.system.pkgfs.file.PATH") returning a blob content ID. |
| // That is, a manifest of name->blob is embedded in /boot/config/devmgr. |
| zx_status_t pkgfs_ldsvc_load_blob(void* ctx, const char* prefix, const char* name, |
| zx_handle_t* vmo) { |
| const int fs_blob_fd = static_cast<int>(reinterpret_cast<intptr_t>(ctx)); |
| char key[256]; |
| if (snprintf(key, sizeof(key), "zircon.system.pkgfs.file.%s%s", prefix, name) >= |
| (int)sizeof(key)) { |
| return ZX_ERR_BAD_PATH; |
| } |
| const char* blob = getenv(key); |
| if (blob == nullptr) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| int fd = openat(fs_blob_fd, blob, O_RDONLY); |
| if (fd < 0) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| zx::vmo nonexec_vmo; |
| zx::vmo exec_vmo; |
| zx_status_t status = fdio_get_vmo_clone(fd, nonexec_vmo.reset_and_get_address()); |
| close(fd); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = nonexec_vmo.replace_as_executable(zx::handle(), &exec_vmo); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = zx_object_set_property(exec_vmo.get(), ZX_PROP_NAME, key, strlen(key)); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| *vmo = exec_vmo.release(); |
| return ZX_OK; |
| } |
| |
| zx_status_t pkgfs_ldsvc_load_object(void* ctx, const char* name, zx_handle_t* vmo) { |
| return pkgfs_ldsvc_load_blob(ctx, "lib/", name, vmo); |
| } |
| |
| zx_status_t pkgfs_ldsvc_load_abspath(void* ctx, const char* name, zx_handle_t* vmo) { |
| return pkgfs_ldsvc_load_blob(ctx, "", name + 1, vmo); |
| } |
| |
| zx_status_t pkgfs_ldsvc_publish_data_sink(void* ctx, const char* name, zx_handle_t vmo) { |
| zx_handle_close(vmo); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| void pkgfs_ldsvc_finalizer(void* ctx) { |
| close(static_cast<int>(reinterpret_cast<intptr_t>(ctx))); |
| } |
| |
| const loader_service_ops_t pkgfs_ldsvc_ops = { |
| .load_object = pkgfs_ldsvc_load_object, |
| .load_abspath = pkgfs_ldsvc_load_abspath, |
| .publish_data_sink = pkgfs_ldsvc_publish_data_sink, |
| .finalizer = pkgfs_ldsvc_finalizer, |
| }; |
| |
| // Create a local loader service with a fixed mapping of names to blobs. |
| zx_status_t pkgfs_ldsvc_start(fbl::unique_fd fs_blob_fd, zx::channel* ldsvc) { |
| loader_service_t* service; |
| zx_status_t status = |
| loader_service_create(nullptr, &pkgfs_ldsvc_ops, |
| reinterpret_cast<void*>(static_cast<intptr_t>(fs_blob_fd.get())), |
| &service); |
| if (status != ZX_OK) { |
| printf("fshost: cannot create pkgfs loader service: %d (%s)\n", status, |
| zx_status_get_string(status)); |
| return status; |
| } |
| // The loader service now owns this FD |
| __UNUSED auto fd = fs_blob_fd.release(); |
| |
| status = loader_service_connect(service, ldsvc->reset_and_get_address()); |
| loader_service_release(service); |
| if (status != ZX_OK) { |
| printf("fshost: cannot connect pkgfs loader service: %d (%s)\n", status, |
| zx_status_get_string(status)); |
| } |
| return status; |
| } |
| |
| bool pkgfs_launch(BlockWatcher* watcher) { |
| const char* cmd = getenv("zircon.system.pkgfs.cmd"); |
| if (cmd == nullptr) { |
| return false; |
| } |
| |
| fbl::unique_fd fs_blob_fd(open("/fs/blob", O_RDONLY | O_DIRECTORY)); |
| if (!fs_blob_fd) { |
| printf("fshost: open(/fs/blob): %m\n"); |
| return false; |
| } |
| |
| zx::channel h0, h1; |
| zx_status_t status = zx::channel::create(0, &h0, &h1); |
| if (status != ZX_OK) { |
| printf("fshost: cannot create pkgfs root channel: %d (%s)\n", status, |
| zx_status_get_string(status)); |
| return false; |
| } |
| |
| auto args = ArgumentVector::FromCmdline(cmd); |
| auto argv = args.argv(); |
| // Remove leading slashes before asking pkgfs_ldsvc_load_blob to load the |
| // file. |
| const char* file = argv[0]; |
| while (file[0] == '/') { |
| ++file; |
| } |
| zx::vmo executable; |
| status = pkgfs_ldsvc_load_blob(reinterpret_cast<void*>(static_cast<intptr_t>(fs_blob_fd.get())), |
| "", argv[0], executable.reset_and_get_address()); |
| if (status != ZX_OK) { |
| printf("fshost: cannot load pkgfs executable: %d (%s)\n", status, |
| zx_status_get_string(status)); |
| return false; |
| } |
| |
| zx::channel loader; |
| status = pkgfs_ldsvc_start(std::move(fs_blob_fd), &loader); |
| if (status != ZX_OK) { |
| printf("fshost: cannot pkgfs loader: %d (%s)\n", status, zx_status_get_string(status)); |
| return false; |
| } |
| |
| const zx_handle_t raw_h1 = h1.release(); |
| zx::process proc; |
| args.Print("fshost"); |
| status = devmgr_launch_with_loader(*watcher->Job(), "pkgfs", |
| std::move(executable), std::move(loader), |
| argv, nullptr, -1, &raw_h1, |
| (const uint32_t[]){PA_HND(PA_USER0, 0)}, 1, &proc, |
| FS_DATA | FS_BLOB | FS_SVC); |
| if (status != ZX_OK) { |
| printf("fshost: failed to launch %s: %d (%s)\n", cmd, status, zx_status_get_string(status)); |
| return false; |
| } |
| |
| pkgfs_finish(watcher, std::move(proc), std::move(h0)); |
| return true; |
| } |
| |
| void LaunchBlobInit(BlockWatcher* watcher) { |
| pkgfs_launch(watcher); |
| } |
| |
| zx_status_t LaunchBlobfs(int argc, const char** argv, zx_handle_t* hnd, uint32_t* ids, |
| size_t len) { |
| return devmgr_launch(*g_job, "blobfs:/blob", argv, nullptr, |
| -1, hnd, ids, len, nullptr, FS_FOR_FSPROC); |
| } |
| |
| zx_status_t LaunchMinfs(int argc, const char** argv, zx_handle_t* hnd, uint32_t* ids, size_t len) { |
| return devmgr_launch(*g_job, "minfs:/data", argv, nullptr, |
| -1, hnd, ids, len, nullptr, FS_FOR_FSPROC); |
| } |
| |
| zx_status_t LaunchFAT(int argc, const char** argv, zx_handle_t* hnd, uint32_t* ids, size_t len) { |
| return devmgr_launch(*g_job, "fatfs:/volume", argv, nullptr, |
| -1, hnd, ids, len, nullptr, FS_FOR_FSPROC); |
| } |
| |
| zx_status_t BlockWatcher::MountData(fbl::unique_fd fd, mount_options_t* options) { |
| if (data_mounted_) { |
| return ZX_ERR_ALREADY_BOUND; |
| } |
| options->wait_until_ready = true; |
| |
| zx_status_t status = |
| mount(fd.release(), "/fs" PATH_DATA, DISK_FORMAT_MINFS, options, LaunchMinfs); |
| if (status != ZX_OK) { |
| printf("fshost: failed to mount %s: %s.\n", PATH_DATA, zx_status_get_string(status)); |
| } else { |
| data_mounted_ = true; |
| } |
| return status; |
| } |
| |
| zx_status_t BlockWatcher::MountInstall(fbl::unique_fd fd, mount_options_t* options) { |
| if (install_mounted_) { |
| return ZX_ERR_ALREADY_BOUND; |
| } |
| options->readonly = true; |
| zx_status_t status = |
| mount(fd.release(), "/fs" PATH_INSTALL, DISK_FORMAT_MINFS, options, LaunchMinfs); |
| if (status != ZX_OK) { |
| printf("fshost: failed to mount %s: %s.\n", PATH_INSTALL, zx_status_get_string(status)); |
| } else { |
| install_mounted_ = true; |
| } |
| return status; |
| } |
| |
| zx_status_t BlockWatcher::MountBlob(fbl::unique_fd fd, mount_options_t* options) { |
| if (blob_mounted_) { |
| return ZX_ERR_ALREADY_BOUND; |
| } |
| zx_status_t status = |
| mount(fd.release(), "/fs" PATH_BLOB, DISK_FORMAT_BLOBFS, options, LaunchBlobfs); |
| if (status != ZX_OK) { |
| printf("fshost: failed to mount %s: %s.\n", PATH_BLOB, zx_status_get_string(status)); |
| } else { |
| blob_mounted_ = true; |
| } |
| return status; |
| } |
| |
| zx_status_t BlockWatcher::CheckFilesystem(const char* device_path, disk_format_t df, |
| const fsck_options_t* options) const { |
| if (!getenv_bool("zircon.system.filesystem-check", false)) { |
| return ZX_OK; |
| } |
| |
| // TODO(ZX-3793): Blobfs' consistency checker is too slow to execute on boot. |
| // With journaling, it is also unnecessary, but would be a nice mechanism for sanity |
| // checking. |
| if (df == DISK_FORMAT_BLOBFS) { |
| fprintf(stderr, "fshost: Skipping blobfs consistency checker\n"); |
| return ZX_OK; |
| } |
| |
| zx::ticks before = zx::ticks::now(); |
| auto timer = fbl::MakeAutoCall([before]() { |
| auto after = zx::ticks::now(); |
| auto duration = fzl::TicksToNs(after - before); |
| printf("fshost: fsck took %" PRId64 ".%" PRId64 " seconds\n", duration.to_secs(), |
| duration.to_msecs() % 1000); |
| }); |
| |
| printf("fshost: fsck of %s started\n", disk_format_string_[df]); |
| |
| auto launch_fsck = [](int argc, const char** argv, zx_handle_t* hnd, uint32_t* ids, |
| size_t len) { |
| zx::process proc; |
| zx_status_t status = devmgr_launch(*g_job, "fsck", argv, |
| nullptr, -1, hnd, ids, len, &proc, FS_FOR_FSPROC); |
| if (status != ZX_OK) { |
| fprintf(stderr, "fshost: Couldn't launch fsck\n"); |
| return status; |
| } |
| status = proc.wait_one(ZX_PROCESS_TERMINATED, zx::time::infinite(), nullptr); |
| if (status != ZX_OK) { |
| fprintf(stderr, "fshost: Error waiting for fsck to terminate\n"); |
| return status; |
| } |
| |
| zx_info_process_t info; |
| status = proc.get_info(ZX_INFO_PROCESS, &info, sizeof(info), nullptr, nullptr); |
| if (status != ZX_OK) { |
| fprintf(stderr, "fshost: Failed to get process info\n"); |
| return status; |
| } |
| |
| if (info.return_code != 0) { |
| fprintf(stderr, "fshost: Fsck return code: %" PRId64 "\n", info.return_code); |
| return ZX_ERR_BAD_STATE; |
| } |
| return ZX_OK; |
| }; |
| |
| zx_status_t status = fsck(device_path, df, options, launch_fsck); |
| if (status != ZX_OK) { |
| fprintf(stderr, "--------------------------------------------------------------\n"); |
| fprintf(stderr, "| \n"); |
| fprintf(stderr, "| WARNING: fshost fsck failure! \n"); |
| fprintf(stderr, "| Corrupt %s @ %s \n", disk_format_string_[df], device_path); |
| fprintf(stderr, "| \n"); |
| fprintf(stderr, "| If your system encountered power-loss due to an unclean \n"); |
| fprintf(stderr, "| shutdown, this error was expected. Journaling in minfs \n"); |
| fprintf(stderr, "| is being tracked by ZX-2093. Re-paving will reset your \n"); |
| fprintf(stderr, "| device. \n"); |
| fprintf(stderr, "| \n"); |
| fprintf(stderr, "| If your system was shutdown cleanly (via 'dm poweroff' \n"); |
| fprintf(stderr, "| or an OTA), report this device to the local-storage \n"); |
| fprintf(stderr, "| team. Please file bugs with logs before and after reboot. \n"); |
| fprintf(stderr, "| Please use the 'filesystem' and 'minfs' component tag. \n"); |
| fprintf(stderr, "| \n"); |
| fprintf(stderr, "--------------------------------------------------------------\n"); |
| } else { |
| printf("fshost: fsck of %s completed OK\n", disk_format_string_[df]); |
| } |
| return status; |
| } |
| |
| // Attempt to mount the device pointed to be the file descriptor at a known |
| // location. |
| // |
| // Returns ZX_ERR_ALREADY_BOUND if the device could be mounted, but something |
| // is already mounted at that location. Returns ZX_ERR_INVALID_ARGS if the |
| // GUID of the device does not match a known valid one. Returns |
| // ZX_ERR_NOT_SUPPORTED if the GUID is a system GUID. Returns ZX_OK if an |
| // attempt to mount is made, without checking mount success. |
| zx_status_t MountMinfs(BlockWatcher* watcher, fbl::unique_fd fd, mount_options_t* options) { |
| fuchsia_hardware_block_partition_GUID type_guid; |
| { |
| fzl::UnownedFdioCaller disk_connection(fd.get()); |
| zx::unowned_channel channel(disk_connection.borrow_channel()); |
| zx_status_t io_status, status; |
| io_status = fuchsia_hardware_block_partition_PartitionGetTypeGuid(channel->get(), &status, |
| &type_guid); |
| if (io_status != ZX_OK) |
| return io_status; |
| if (status != ZX_OK) |
| return status; |
| } |
| |
| if (gpt_is_sys_guid(type_guid.value, GPT_GUID_LEN)) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } else if (gpt_is_data_guid(type_guid.value, GPT_GUID_LEN)) { |
| return watcher->MountData(std::move(fd), options); |
| } else if (gpt_is_install_guid(type_guid.value, GPT_GUID_LEN)) { |
| return watcher->MountInstall(std::move(fd), options); |
| } |
| printf("fshost: Unrecognized partition GUID for minfs; not mounting\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| #define FVM_DRIVER_LIB "/boot/driver/fvm.so" |
| #define GPT_DRIVER_LIB "/boot/driver/gpt.so" |
| #define MBR_DRIVER_LIB "/boot/driver/mbr.so" |
| #define BOOTPART_DRIVER_LIB "/boot/driver/bootpart.so" |
| #define ZXCRYPT_DRIVER_LIB "/boot/driver/zxcrypt.so" |
| #define STRLEN(s) (sizeof(s) / sizeof((s)[0])) |
| |
| // return value is ignored |
| int UnsealZxcrypt(void* arg) { |
| fbl::unique_ptr<int> fd_ptr(static_cast<int*>(arg)); |
| fbl::unique_fd fd(*fd_ptr); |
| |
| zx_status_t rc; |
| fbl::unique_ptr<zxcrypt::FdioVolume> zxcrypt_volume; |
| if ((rc = zxcrypt::FdioVolume::Init(std::move(fd), &zxcrypt_volume)) != ZX_OK) { |
| printf("fshost: couldn't open zxcrypt fdio volume"); |
| return ZX_OK; |
| } |
| |
| zx::channel zxcrypt_volume_manager_chan; |
| if ((rc = zxcrypt_volume->OpenManager(zx::sec(2), zxcrypt_volume_manager_chan.reset_and_get_address())) != ZX_OK) { |
| printf("fshost: couldn't open zxcrypt manager device"); |
| return 0; |
| } |
| |
| zxcrypt::FdioVolumeManager zxcrypt_volume_manager(std::move(zxcrypt_volume_manager_chan)); |
| uint8_t slot = 0; |
| if ((rc = zxcrypt_volume_manager.UnsealWithDeviceKey(slot)) != ZX_OK) { |
| printf("fshost: couldn't unseal zxcrypt manager device"); |
| return 0; |
| } |
| |
| return 0; |
| } |
| |
| zx_status_t FormatMinfs(const fbl::unique_fd& block_device, |
| const fuchsia_hardware_block_BlockInfo& info) { |
| |
| fprintf(stderr, "fshost: Formatting minfs.\n"); |
| uint64_t device_size = info.block_size * info.block_count; |
| fbl::unique_ptr<minfs::Bcache> bc; |
| zx_status_t status; |
| if ((status = minfs::Bcache::Create(&bc, block_device.duplicate(), |
| static_cast<uint32_t>(device_size))) != ZX_OK) { |
| fprintf(stderr, "fshost: Could not initialize minfs bcache.\n"); |
| return status; |
| } |
| minfs::MountOptions options = {}; |
| if ((status = Mkfs(options, std::move(bc))) != ZX_OK) { |
| fprintf(stderr, "fshost: Could not format minfs filesystem.\n"); |
| return status; |
| } |
| printf("fshost: Minfs filesystem re-formatted. Expect data loss.\n"); |
| return ZX_OK; |
| } |
| |
| zx_status_t BlockDeviceAdded(int dirfd, int event, const char* name, void* cookie) { |
| auto watcher = static_cast<BlockWatcher*>(cookie); |
| |
| if (event != WATCH_EVENT_ADD_FILE) { |
| return ZX_OK; |
| } |
| |
| char device_path[PATH_MAX]; |
| sprintf(device_path, "%s/%s", PATH_DEV_BLOCK, name); |
| |
| fbl::unique_fd fd(openat(dirfd, name, O_RDWR)); |
| if (!fd) { |
| return ZX_OK; |
| } |
| |
| disk_format_t df = detect_disk_format(fd.get()); |
| fuchsia_hardware_block_BlockInfo info; |
| fuchsia_hardware_block_partition_GUID guid; |
| { |
| fzl::UnownedFdioCaller disk_connection(fd.get()); |
| zx::unowned_channel disk(disk_connection.borrow_channel()); |
| |
| zx_status_t io_status, call_status; |
| io_status = fuchsia_hardware_block_BlockGetInfo(disk->get(), &call_status, &info); |
| if (io_status != ZX_OK || call_status != ZX_OK) { |
| return ZX_OK; |
| } |
| |
| if (info.flags & BLOCK_FLAG_BOOTPART) { |
| fuchsia_device_ControllerBind(disk->get(), BOOTPART_DRIVER_LIB, |
| STRLEN(BOOTPART_DRIVER_LIB), &call_status); |
| return ZX_OK; |
| } |
| |
| switch (df) { |
| case DISK_FORMAT_GPT: { |
| printf("fshost: %s: GPT?\n", device_path); |
| // probe for partition table |
| fuchsia_device_ControllerBind(disk->get(), GPT_DRIVER_LIB, STRLEN(GPT_DRIVER_LIB), |
| &call_status); |
| return ZX_OK; |
| } |
| case DISK_FORMAT_FVM: { |
| printf("fshost: /dev/class/block/%s: FVM?\n", name); |
| // probe for partition table |
| fuchsia_device_ControllerBind(disk->get(), FVM_DRIVER_LIB, STRLEN(FVM_DRIVER_LIB), |
| &call_status); |
| return ZX_OK; |
| } |
| case DISK_FORMAT_MBR: { |
| printf("fshost: %s: MBR?\n", device_path); |
| // probe for partition table |
| fuchsia_device_ControllerBind(disk->get(), MBR_DRIVER_LIB, STRLEN(MBR_DRIVER_LIB), |
| &call_status); |
| return ZX_OK; |
| } |
| case DISK_FORMAT_ZXCRYPT: { |
| if (!watcher->Netbooting()) { |
| printf("fshost: %s: zxcrypt?\n", device_path); |
| // Bind and unseal the driver from a separate thread, since we |
| // have to wait for a number of devices to do I/O and settle, |
| // and we don't want to block block-watcher for any nontrivial |
| // length of time. |
| |
| // We transfer fd to the spawned thread. Since it's UB to cast |
| // ints to pointers and back, we allocate the fd on the heap. |
| int loose_fd = fd.release(); |
| int* raw_fd_ptr = new int(loose_fd); |
| thrd_t th; |
| int err = thrd_create_with_name(&th, &UnsealZxcrypt, raw_fd_ptr, "zxcrypt-unseal"); |
| if (err != thrd_success) { |
| printf("fshost: failed to spawn zxcrypt unseal thread"); |
| close(loose_fd); |
| delete raw_fd_ptr; |
| } else { |
| thrd_detach(th); |
| } |
| } |
| return ZX_OK; |
| } |
| default: |
| break; |
| } |
| |
| io_status = fuchsia_hardware_block_partition_PartitionGetTypeGuid(disk->get(), &call_status, |
| &guid); |
| if (io_status != ZX_OK || call_status != ZX_OK) { |
| return ZX_OK; |
| } |
| } |
| |
| // If we're in netbooting mode, then only bind drivers for partition |
| // containers and the install partition, not regular filesystems. |
| if (watcher->Netbooting()) { |
| if (gpt_is_install_guid(guid.value, GPT_GUID_LEN)) { |
| printf("fshost: mounting install partition\n"); |
| mount_options_t options = default_mount_options; |
| MountMinfs(watcher, std::move(fd), &options); |
| return ZX_OK; |
| } |
| |
| return ZX_OK; |
| } |
| |
| switch (df) { |
| case DISK_FORMAT_BLOBFS: { |
| const uint8_t expected_guid[GPT_GUID_LEN] = GUID_BLOB_VALUE; |
| |
| if (memcmp(guid.value, expected_guid, GPT_GUID_LEN)) { |
| return ZX_OK; |
| } |
| fsck_options_t fsck_options = default_fsck_options; |
| fsck_options.apply_journal = true; |
| if (watcher->CheckFilesystem(device_path, DISK_FORMAT_BLOBFS, &fsck_options) != ZX_OK) { |
| return ZX_OK; |
| } |
| |
| mount_options_t options = default_mount_options; |
| options.enable_journal = true; |
| options.collect_metrics = true; |
| zx_status_t status = watcher->MountBlob(std::move(fd), &options); |
| if (status != ZX_OK) { |
| printf("fshost: Failed to mount blobfs partition %s at %s: %s.\n", device_path, |
| PATH_BLOB, zx_status_get_string(status)); |
| } else { |
| LaunchBlobInit(watcher); |
| } |
| return ZX_OK; |
| } |
| case DISK_FORMAT_MINFS: { |
| printf("fshost: mounting minfs\n"); |
| fsck_options_t fsck_options = default_fsck_options; |
| if (watcher->CheckFilesystem(device_path, DISK_FORMAT_MINFS, &fsck_options) != ZX_OK) { |
| if (FormatMinfs(fd, info) != ZX_OK) { |
| return ZX_OK; |
| } |
| } |
| mount_options_t options = default_mount_options; |
| MountMinfs(watcher, std::move(fd), &options); |
| return ZX_OK; |
| } |
| case DISK_FORMAT_FAT: { |
| // Use the GUID to avoid auto-mounting the EFI partition |
| bool efi = gpt_is_efi_guid(guid.value, GPT_GUID_LEN); |
| if (efi) { |
| printf("fshost: not automounting efi\n"); |
| return ZX_OK; |
| } |
| mount_options_t options = default_mount_options; |
| options.create_mountpoint = true; |
| static int fat_counter = 0; |
| char mountpath[FDIO_MAX_FILENAME + 64]; |
| snprintf(mountpath, sizeof(mountpath), "%s/fat-%d", "/fs" PATH_VOLUME, fat_counter++); |
| options.wait_until_ready = false; |
| printf("fshost: mounting fatfs\n"); |
| mount(fd.release(), mountpath, df, &options, LaunchFAT); |
| return ZX_OK; |
| } |
| default: |
| return ZX_OK; |
| } |
| } |
| |
| } // namespace |
| |
| void BlockDeviceWatcher(fbl::unique_ptr<FsManager> fshost, zx::unowned_job job, bool netboot) { |
| g_job = job; |
| BlockWatcher watcher(std::move(fshost), std::move(job), netboot); |
| |
| fbl::unique_fd dirfd(open("/dev/class/block", O_DIRECTORY | O_RDONLY)); |
| if (dirfd) { |
| fdio_watch_directory(dirfd.get(), BlockDeviceAdded, ZX_TIME_INFINITE, &watcher); |
| } |
| } |
| |
| } // namespace devmgr |