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 <inttypes.h> 9#include <stdbool.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <string.h> 13#include <sys/stat.h> 14#include <sys/types.h> 15#include <unistd.h> 16 17#include <fbl/auto_call.h> 18#include <fbl/unique_fd.h> 19#include <lib/fdio/watcher.h> 20#include <lib/zx/time.h> 21#include <zircon/device/block.h> 22#include <zircon/device/ramdisk.h> 23#include <zircon/device/vfs.h> 24#include <zircon/process.h> 25#include <zircon/status.h> 26#include <zircon/syscalls.h> 27#include <zircon/types.h> 28 29#include <fs-management/ramdisk.h> 30 31#define RAMCTL_PATH "/dev/misc/ramctl" 32#define BLOCK_EXTENSION "block" 33 34static zx_status_t driver_watcher_cb(int dirfd, int event, const char* fn, void* cookie) { 35 char* wanted = static_cast<char*>(cookie); 36 if (event == WATCH_EVENT_ADD_FILE && strcmp(fn, wanted) == 0) { 37 return ZX_ERR_STOP; 38 } 39 return ZX_OK; 40} 41 42static zx_status_t wait_for_device_impl(char* path, const zx::time& deadline) { 43 zx_status_t rc; 44 45 // Peel off last path segment 46 char* sep = strrchr(path, '/'); 47 if (path[0] == '\0' || (!sep)) { 48 fprintf(stderr, "invalid device path '%s'\n", path); 49 return ZX_ERR_BAD_PATH; 50 } 51 char* last = sep + 1; 52 53 *sep = '\0'; 54 auto restore_path = fbl::MakeAutoCall([sep] { *sep = '/'; }); 55 56 // Recursively check the path up to this point 57 struct stat buf; 58 if (stat(path, &buf) != 0 && (rc = wait_for_device_impl(path, deadline)) != ZX_OK) { 59 fprintf(stderr, "failed to bind '%s': %s\n", path, zx_status_get_string(rc)); 60 return rc; 61 } 62 63 // Early exit if this segment is empty 64 if (last[0] == '\0') { 65 return ZX_OK; 66 } 67 68 // Open the parent directory 69 DIR* dir = opendir(path); 70 if (!dir) { 71 fprintf(stderr, "unable to open '%s'\n", path); 72 return ZX_ERR_NOT_FOUND; 73 } 74 auto close_dir = fbl::MakeAutoCall([&] { closedir(dir); }); 75 76 // Wait for the next path segment to show up 77 rc = fdio_watch_directory(dirfd(dir), driver_watcher_cb, deadline.get(), last); 78 if (rc != ZX_ERR_STOP) { 79 fprintf(stderr, "error when waiting for '%s': %s\n", last, zx_status_get_string(rc)); 80 return rc; 81 } 82 83 return ZX_OK; 84} 85 86// TODO(aarongreen): This is more generic than just fs-management, or even block devices. Move this 87// (and its tests) out of ramdisk and to somewhere else? 88zx_status_t wait_for_device(const char* path, zx_duration_t timeout) { 89 if (!path || timeout == 0) { 90 fprintf(stderr, "invalid args: path='%s', timeout=%" PRIu64 "\n", path, timeout); 91 return ZX_ERR_INVALID_ARGS; 92 } 93 94 // Make a mutable copy 95 char tmp[PATH_MAX]; 96 snprintf(tmp, sizeof(tmp), "%s", path); 97 zx::time deadline = zx::deadline_after(zx::duration(timeout)); 98 return wait_for_device_impl(tmp, deadline); 99} 100 101static int open_ramctl(void) { 102 int fd = open(RAMCTL_PATH, O_RDWR); 103 if (fd < 0) { 104 fprintf(stderr, "Could not open ramctl\n"); 105 } 106 return fd; 107} 108 109static int finish_create(ramdisk_ioctl_config_response_t* response, char* out_path, ssize_t r) { 110 if (r < 0) { 111 fprintf(stderr, "Could not configure ramdev\n"); 112 return -1; 113 } 114 response->name[r] = 0; 115 116 char path[PATH_MAX]; 117 auto cleanup = fbl::MakeAutoCall([&path, response]() { 118 snprintf(path, sizeof(path), "%s/%s", RAMCTL_PATH, response->name); 119 destroy_ramdisk(path); 120 }); 121 122 // The ramdisk should have been created instantly, but it may take 123 // a moment for the block device driver to bind to it. 124 snprintf(path, sizeof(path), "%s/%s/%s", RAMCTL_PATH, response->name, BLOCK_EXTENSION); 125 if (wait_for_device(path, ZX_SEC(3)) != ZX_OK) { 126 fprintf(stderr, "Error waiting for driver\n"); 127 return -1; 128 } 129 130 // TODO(security): SEC-70. This may overflow |out_path|. 131 strcpy(out_path, path); 132 cleanup.cancel(); 133 return 0; 134} 135 136int create_ramdisk(uint64_t blk_size, uint64_t blk_count, char* out_path) { 137 fbl::unique_fd fd(open_ramctl()); 138 if (fd.get() < 0) { 139 return -1; 140 } 141 ramdisk_ioctl_config_t config = {}; 142 config.blk_size = blk_size; 143 config.blk_count = blk_count; 144 memset(config.type_guid, 0, ZBI_PARTITION_GUID_LEN); 145 ramdisk_ioctl_config_response_t response; 146 return finish_create(&response, out_path, 147 ioctl_ramdisk_config(fd.get(), &config, &response)); 148} 149 150int create_ramdisk_with_guid(uint64_t blk_size, uint64_t blk_count, const uint8_t* type_guid, 151 size_t guid_len, char* out_path) { 152 fbl::unique_fd fd(open_ramctl()); 153 if (fd.get() < 0) { 154 return -1; 155 } 156 if (type_guid == NULL || guid_len < ZBI_PARTITION_GUID_LEN) { 157 return -1; 158 } 159 ramdisk_ioctl_config_t config = {}; 160 config.blk_size = blk_size; 161 config.blk_count = blk_count; 162 memcpy(config.type_guid, type_guid, ZBI_PARTITION_GUID_LEN); 163 ramdisk_ioctl_config_response_t response; 164 return finish_create(&response, out_path, 165 ioctl_ramdisk_config(fd.get(), &config, &response)); 166} 167 168int create_ramdisk_from_vmo(zx_handle_t vmo, char* out_path) { 169 fbl::unique_fd fd(open_ramctl()); 170 if (fd.get() < 0) { 171 return -1; 172 } 173 ramdisk_ioctl_config_response_t response; 174 return finish_create(&response, out_path, 175 ioctl_ramdisk_config_vmo(fd.get(), &vmo, &response)); 176} 177 178int sleep_ramdisk(const char* ramdisk_path, uint64_t block_count) { 179 fbl::unique_fd fd(open(ramdisk_path, O_RDWR)); 180 if (fd.get() < 0) { 181 fprintf(stderr, "Could not open ramdisk\n"); 182 return -1; 183 } 184 185 ssize_t r = ioctl_ramdisk_sleep_after(fd.get(), &block_count); 186 if (r != ZX_OK) { 187 fprintf(stderr, "Could not set ramdisk interrupt on path %s: %ld\n", ramdisk_path, r); 188 return -1; 189 } 190 return 0; 191} 192 193int wake_ramdisk(const char* ramdisk_path) { 194 fbl::unique_fd fd(open(ramdisk_path, O_RDWR)); 195 if (fd.get() < 0) { 196 fprintf(stderr, "Could not open ramdisk\n"); 197 return -1; 198 } 199 200 ssize_t r = ioctl_ramdisk_wake_up(fd.get()); 201 if (r != ZX_OK) { 202 fprintf(stderr, "Could not wake ramdisk\n"); 203 return -1; 204 } 205 206 return 0; 207} 208 209int get_ramdisk_blocks(const char* ramdisk_path, ramdisk_blk_counts_t* counts) { 210 fbl::unique_fd fd(open(ramdisk_path, O_RDWR)); 211 if (fd.get() < 0) { 212 fprintf(stderr, "Could not open ramdisk\n"); 213 return -1; 214 } 215 if (ioctl_ramdisk_get_blk_counts(fd.get(), counts) < 0) { 216 fprintf(stderr, "Could not get blk counts\n"); 217 return -1; 218 } 219 return 0; 220} 221 222int destroy_ramdisk(const char* ramdisk_path) { 223 fbl::unique_fd ramdisk(open(ramdisk_path, O_RDWR)); 224 if (!ramdisk) { 225 fprintf(stderr, "Could not open ramdisk\n"); 226 return -1; 227 } 228 ssize_t r = ioctl_ramdisk_unlink(ramdisk.get()); 229 if (r != ZX_OK) { 230 fprintf(stderr, "Could not shut off ramdisk\n"); 231 return -1; 232 } 233 return 0; 234} 235