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
driver_watcher_cb(int dirfd,int event,const char * fn,void * cookie)34 static 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
wait_for_device_impl(char * path,const zx::time & deadline)42 static 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?
wait_for_device(const char * path,zx_duration_t timeout)88 zx_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
open_ramctl(void)101 static 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
finish_create(ramdisk_ioctl_config_response_t * response,char * out_path,ssize_t r)109 static zx_status_t finish_create(ramdisk_ioctl_config_response_t* response, char* out_path,
110 ssize_t r) {
111 if (r < 0) {
112 fprintf(stderr, "Could not configure ramdev\n");
113 return ZX_ERR_INVALID_ARGS;
114 }
115 response->name[r] = 0;
116
117 char path[PATH_MAX];
118 auto cleanup = fbl::MakeAutoCall([&path, response]() {
119 snprintf(path, sizeof(path), "%s/%s", RAMCTL_PATH, response->name);
120 destroy_ramdisk(path);
121 });
122
123 // The ramdisk should have been created instantly, but it may take
124 // a moment for the block device driver to bind to it.
125 snprintf(path, sizeof(path), "%s/%s/%s", RAMCTL_PATH, response->name, BLOCK_EXTENSION);
126 zx_status_t status = wait_for_device(path, ZX_SEC(3));
127 if (status != ZX_OK) {
128 fprintf(stderr, "Error waiting for driver\n");
129 return status;
130 }
131
132 // TODO(security): SEC-70. This may overflow |out_path|.
133 strcpy(out_path, path);
134 cleanup.cancel();
135 return ZX_OK;
136 }
137
create_ramdisk(uint64_t blk_size,uint64_t blk_count,char * out_path)138 zx_status_t create_ramdisk(uint64_t blk_size, uint64_t blk_count, char* out_path) {
139 fbl::unique_fd fd(open_ramctl());
140 if (fd.get() < 0) {
141 return ZX_ERR_BAD_STATE;
142 }
143 ramdisk_ioctl_config_t config = {};
144 config.blk_size = blk_size;
145 config.blk_count = blk_count;
146 memset(config.type_guid, 0, ZBI_PARTITION_GUID_LEN);
147 ramdisk_ioctl_config_response_t response;
148 return finish_create(&response, out_path,
149 ioctl_ramdisk_config(fd.get(), &config, &response));
150 }
151
create_ramdisk_with_guid(uint64_t blk_size,uint64_t blk_count,const uint8_t * type_guid,size_t guid_len,char * out_path)152 zx_status_t create_ramdisk_with_guid(uint64_t blk_size, uint64_t blk_count,
153 const uint8_t* type_guid, size_t guid_len, char* out_path) {
154 fbl::unique_fd fd(open_ramctl());
155 if (fd.get() < 0) {
156 return ZX_ERR_BAD_STATE;
157 }
158 if (type_guid == NULL || guid_len < ZBI_PARTITION_GUID_LEN) {
159 return ZX_ERR_INVALID_ARGS;
160 }
161 ramdisk_ioctl_config_t config = {};
162 config.blk_size = blk_size;
163 config.blk_count = blk_count;
164 memcpy(config.type_guid, type_guid, ZBI_PARTITION_GUID_LEN);
165 ramdisk_ioctl_config_response_t response;
166 return finish_create(&response, out_path,
167 ioctl_ramdisk_config(fd.get(), &config, &response));
168 }
169
create_ramdisk_from_vmo(zx_handle_t vmo,char * out_path)170 zx_status_t create_ramdisk_from_vmo(zx_handle_t vmo, char* out_path) {
171 fbl::unique_fd fd(open_ramctl());
172 if (fd.get() < 0) {
173 return ZX_ERR_BAD_STATE;
174 }
175 ramdisk_ioctl_config_response_t response;
176 return finish_create(&response, out_path,
177 ioctl_ramdisk_config_vmo(fd.get(), &vmo, &response));
178 }
179
sleep_ramdisk(const char * ramdisk_path,uint64_t block_count)180 zx_status_t sleep_ramdisk(const char* ramdisk_path, uint64_t block_count) {
181 fbl::unique_fd fd(open(ramdisk_path, O_RDWR));
182 if (fd.get() < 0) {
183 fprintf(stderr, "Could not open ramdisk\n");
184 return ZX_ERR_BAD_STATE;
185 }
186
187 ssize_t r = ioctl_ramdisk_sleep_after(fd.get(), &block_count);
188 if (r != ZX_OK) {
189 fprintf(stderr, "Could not set ramdisk interrupt on path %s: %ld\n", ramdisk_path, r);
190 return static_cast<zx_status_t>(r);
191 }
192 return ZX_OK;
193 }
194
wake_ramdisk(const char * ramdisk_path)195 zx_status_t wake_ramdisk(const char* ramdisk_path) {
196 fbl::unique_fd fd(open(ramdisk_path, O_RDWR));
197 if (fd.get() < 0) {
198 fprintf(stderr, "Could not open ramdisk\n");
199 return ZX_ERR_BAD_STATE;;
200 }
201
202 ssize_t r = ioctl_ramdisk_wake_up(fd.get());
203 if (r != ZX_OK) {
204 fprintf(stderr, "Could not wake ramdisk\n");
205 return static_cast<zx_status_t>(r);
206 }
207
208 return ZX_OK;
209 }
210
get_ramdisk_blocks(const char * ramdisk_path,ramdisk_blk_counts_t * counts)211 zx_status_t get_ramdisk_blocks(const char* ramdisk_path, ramdisk_blk_counts_t* counts) {
212 fbl::unique_fd fd(open(ramdisk_path, O_RDWR));
213 if (fd.get() < 0) {
214 fprintf(stderr, "Could not open ramdisk\n");
215 return ZX_ERR_BAD_STATE;
216 }
217 ssize_t rc = ioctl_ramdisk_get_blk_counts(fd.get(), counts);
218 if (rc < 0) {
219 fprintf(stderr, "Could not get blk counts\n");
220 return static_cast<zx_status_t>(rc);
221 }
222 return ZX_OK;
223 }
224
destroy_ramdisk(const char * ramdisk_path)225 zx_status_t destroy_ramdisk(const char* ramdisk_path) {
226 fbl::unique_fd ramdisk(open(ramdisk_path, O_RDWR));
227 if (!ramdisk) {
228 fprintf(stderr, "Could not open ramdisk\n");
229 return ZX_ERR_BAD_STATE;
230 }
231 ssize_t r = ioctl_ramdisk_unlink(ramdisk.get());
232 if (r != ZX_OK) {
233 fprintf(stderr, "Could not shut off ramdisk\n");
234 return static_cast<zx_status_t>(r);
235 }
236 return ZX_OK;
237 }
238