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