1 // Copyright 2016 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 <fs-management/mount.h>
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <string.h>
10 #include <unistd.h>
11
12 #include <fbl/algorithm.h>
13 #include <fbl/alloc_checker.h>
14 #include <fbl/auto_call.h>
15 #include <fbl/unique_fd.h>
16 #include <fbl/unique_ptr.h>
17 #include <fs/client.h>
18 #include <fuchsia/io/c/fidl.h>
19 #include <lib/fdio/limits.h>
20 #include <lib/fdio/util.h>
21 #include <lib/fdio/vfs.h>
22 #include <lib/fzl/fdio.h>
23 #include <lib/zx/channel.h>
24 #include <zircon/compiler.h>
25 #include <zircon/device/block.h>
26 #include <zircon/device/vfs.h>
27 #include <zircon/processargs.h>
28 #include <zircon/syscalls.h>
29
30 #include <utility>
31
32 namespace {
33
34 using fbl::unique_fd;
35
MountFs(int fd,zx_handle_t root)36 zx_status_t MountFs(int fd, zx_handle_t root) {
37 zx_status_t status;
38 fzl::FdioCaller caller{fbl::unique_fd(fd)};
39 zx_status_t io_status = fuchsia_io_DirectoryAdminMount(caller.borrow_channel(),
40 root, &status);
41 caller.release().release();
42 if (io_status != ZX_OK) {
43 return io_status;
44 }
45 return status;
46 }
47
UnmountHandle(zx_handle_t root,bool wait_until_ready)48 void UnmountHandle(zx_handle_t root, bool wait_until_ready) {
49 // We've entered a failure case where the filesystem process (which may or may not be alive)
50 // had a *chance* to be spawned, but cannot be attached to a vnode (for whatever reason).
51 // Rather than abandoning the filesystem process (maybe causing dirty bits to be set), give it a
52 // chance to shutdown properly.
53 //
54 // The unmount process is a little atypical, since we're just sending a signal over a handle,
55 // rather than detaching the mounted filesystem from the "parent" filesystem.
56 vfs_unmount_handle(root, wait_until_ready ? ZX_TIME_INFINITE : 0);
57 }
58
59 // Performs the actual work of mounting a volume.
60 class Mounter {
61 public:
62 // The mount point is either a path (to be created) or an existing fd.
Mounter(int fd)63 explicit Mounter(int fd) : path_(nullptr), fd_(fd) {}
Mounter(const char * path)64 explicit Mounter(const char* path) : path_(path), fd_(-1) {}
~Mounter()65 ~Mounter() {}
66
67 // Mounts the given device.
68 zx_status_t Mount(unique_fd device, disk_format_t format, const mount_options_t& options,
69 LaunchCallback cb);
70
71 DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Mounter);
72
73 private:
74 zx_status_t PrepareHandles(unique_fd device);
75 zx_status_t MakeDirAndMount(const mount_options_t& options);
76 zx_status_t LaunchAndMount(LaunchCallback cb, const mount_options_t& options, const char** argv,
77 int argc);
78 zx_status_t MountNativeFs(const char* binary, unique_fd device, const mount_options_t& options,
79 LaunchCallback cb);
80 zx_status_t MountFat(unique_fd device, const mount_options_t& options, LaunchCallback cb);
81
82 zx_handle_t root_ = ZX_HANDLE_INVALID;
83 const char* path_;
84 int fd_;
85 uint32_t flags_ = 0; // Currently not used.
86 size_t num_handles_ = 0;
87 zx_handle_t handles_[FDIO_MAX_HANDLES * 2];
88 uint32_t ids_[FDIO_MAX_HANDLES * 2];
89 };
90
91 // Initializes 'handles_' and 'ids_' with the root handle and block device handle.
PrepareHandles(unique_fd device)92 zx_status_t Mounter::PrepareHandles(unique_fd device) {
93 zx_handle_t mountee_handle;
94 zx_status_t status = zx_channel_create(0, &mountee_handle, &root_);
95 if (status != ZX_OK) {
96 return status;
97 }
98 handles_[0] = mountee_handle;
99 ids_[0] = PA_USER0;
100 num_handles_ = 1;
101
102 int device_fd = device.release();
103 status = fdio_transfer_fd(device_fd, FS_FD_BLOCKDEVICE, &handles_[1], &ids_[1]);
104 if (status < 0) {
105 // Note that fdio_transfer_fd returns > 0 on success :(.
106 fprintf(stderr, "Failed to access device handle\n");
107 zx_handle_close(mountee_handle);
108 zx_handle_close(root_);
109 device.reset(device_fd);
110 return status != 0 ? status : ZX_ERR_BAD_STATE;
111 }
112 num_handles_ += status;
113 return ZX_OK;
114 }
115
MakeDirAndMount(const mount_options_t & options)116 zx_status_t Mounter::MakeDirAndMount(const mount_options_t& options) {
117 auto cleanup =
118 fbl::MakeAutoCall([this, options]() { UnmountHandle(root_, options.wait_until_ready); });
119
120 // Open the parent path as O_ADMIN, and sent the mkdir+mount command
121 // to that directory.
122 char parent_path[PATH_MAX];
123 const char* name;
124 strcpy(parent_path, path_);
125 char* last_slash = strrchr(parent_path, '/');
126 if (last_slash == NULL) {
127 strcpy(parent_path, ".");
128 name = path_;
129 } else {
130 *last_slash = '\0';
131 name = last_slash + 1;
132 if (*name == '\0') {
133 return ZX_ERR_INVALID_ARGS;
134 }
135 }
136
137 unique_fd parent(open(parent_path, O_RDONLY | O_DIRECTORY | O_ADMIN));
138 if (!parent) {
139 return ZX_ERR_IO;
140 }
141
142 cleanup.cancel();
143
144 zx_status_t status;
145 fzl::FdioCaller caller(std::move(parent));
146 zx_status_t io_status = fuchsia_io_DirectoryAdminMountAndCreate(
147 caller.borrow_channel(), root_, name, strlen(name), flags_, &status);
148 if (io_status != ZX_OK) {
149 return io_status;
150 }
151 return status;
152 }
153
154 // Calls the 'launch callback' and mounts the remote handle to the target vnode, if successful.
LaunchAndMount(LaunchCallback cb,const mount_options_t & options,const char ** argv,int argc)155 zx_status_t Mounter::LaunchAndMount(LaunchCallback cb, const mount_options_t& options,
156 const char** argv, int argc) {
157 auto cleanup =
158 fbl::MakeAutoCall([this, options]() { UnmountHandle(root_, options.wait_until_ready); });
159
160 zx_status_t status = cb(argc, argv, handles_, ids_, num_handles_);
161 if (status != ZX_OK) {
162 return status;
163 }
164
165 if (options.wait_until_ready) {
166 // Wait until the filesystem is ready to take incoming requests
167 zx_signals_t observed;
168 status = zx_object_wait_one(root_, ZX_USER_SIGNAL_0 | ZX_CHANNEL_PEER_CLOSED,
169 ZX_TIME_INFINITE, &observed);
170 if ((status != ZX_OK) || (observed & ZX_CHANNEL_PEER_CLOSED)) {
171 status = (status != ZX_OK) ? status : ZX_ERR_BAD_STATE;
172 return status;
173 }
174 }
175 cleanup.cancel();
176
177 // Install remote handle.
178 if (options.create_mountpoint) {
179 return MakeDirAndMount(options);
180 }
181 return MountFs(fd_, root_);
182 }
183
MountNativeFs(const char * binary,unique_fd device,const mount_options_t & options,LaunchCallback cb)184 zx_status_t Mounter::MountNativeFs(const char* binary, unique_fd device,
185 const mount_options_t& options, LaunchCallback cb) {
186 zx_status_t status = PrepareHandles(std::move(device));
187 if (status != ZX_OK) {
188 return status;
189 }
190
191 if (options.verbose_mount) {
192 printf("fs_mount: Launching %s\n", binary);
193 }
194
195 // 1. binary
196 // 2. (optional) readonly
197 // 3. (optional) verbose
198 // 4. (optional) metrics
199 // 5. command
200 const char* argv[5] = {binary};
201 int argc = 1;
202 if (options.readonly) {
203 argv[argc++] = "--readonly";
204 }
205 if (options.verbose_mount) {
206 argv[argc++] = "--verbose";
207 }
208 if (options.collect_metrics) {
209 argv[argc++] = "--metrics";
210 }
211 if (options.enable_journal) {
212 argv[argc++] = "--journal";
213 }
214 argv[argc++] = "mount";
215 return LaunchAndMount(cb, options, argv, argc);
216 }
217
MountFat(unique_fd device,const mount_options_t & options,LaunchCallback cb)218 zx_status_t Mounter::MountFat(unique_fd device, const mount_options_t& options, LaunchCallback cb) {
219 zx_status_t status = PrepareHandles(std::move(device));
220 if (status != ZX_OK) {
221 return status;
222 }
223
224 char readonly_arg[64];
225 snprintf(readonly_arg, sizeof(readonly_arg), "-readonly=%s",
226 options.readonly ? "true" : "false");
227 char blockfd_arg[64];
228 snprintf(blockfd_arg, sizeof(blockfd_arg), "-blockFD=%d", FS_FD_BLOCKDEVICE);
229
230 if (options.verbose_mount) {
231 printf("fs_mount: Launching ThinFS\n");
232 }
233 const char* argv[] = {
234 "/system/bin/thinfs",
235 readonly_arg,
236 blockfd_arg,
237 "mount",
238 };
239 return LaunchAndMount(cb, options, argv, fbl::count_of(argv));
240 }
241
Mount(unique_fd device,disk_format_t format,const mount_options_t & options,LaunchCallback cb)242 zx_status_t Mounter::Mount(unique_fd device, disk_format_t format, const mount_options_t& options,
243 LaunchCallback cb) {
244 switch (format) {
245 case DISK_FORMAT_MINFS:
246 return MountNativeFs("/boot/bin/minfs", std::move(device), options, cb);
247 case DISK_FORMAT_BLOBFS:
248 return MountNativeFs("/boot/bin/blobfs", std::move(device), options, cb);
249 case DISK_FORMAT_FAT:
250 return MountFat(std::move(device), options, cb);
251 default:
252 return ZX_ERR_NOT_SUPPORTED;
253 }
254 }
255
256 } // namespace
257
258 const mount_options_t default_mount_options = {
259 .readonly = false,
260 .verbose_mount = false,
261 .collect_metrics = false,
262 .wait_until_ready = true,
263 .create_mountpoint = false,
264 .enable_journal = false,
265 };
266
267 const mkfs_options_t default_mkfs_options = {
268 .fvm_data_slices = 1,
269 .verbose = false,
270 };
271
272 const fsck_options_t default_fsck_options = {
273 .verbose = false,
274 .never_modify = false,
275 .always_modify = false,
276 .force = false,
277 };
278
detect_disk_format(int fd)279 disk_format_t detect_disk_format(int fd) {
280 if (lseek(fd, 0, SEEK_SET) != 0) {
281 fprintf(stderr, "detect_disk_format: Cannot seek to start of device.\n");
282 return DISK_FORMAT_UNKNOWN;
283 }
284
285 block_info_t info;
286 ssize_t r;
287 if ((r = ioctl_block_get_info(fd, &info)) < 0) {
288 fprintf(stderr, "detect_disk_format: Could not acquire block device info\n");
289 return DISK_FORMAT_UNKNOWN;
290 }
291
292 // We expect to read HEADER_SIZE bytes, but we may need to read
293 // extra to read a multiple of the underlying block size.
294 const size_t buffer_size = fbl::round_up(static_cast<size_t>(HEADER_SIZE),
295 static_cast<size_t>(info.block_size));
296
297 uint8_t data[buffer_size];
298 if (read(fd, data, buffer_size) != static_cast<ssize_t>(buffer_size)) {
299 fprintf(stderr, "detect_disk_format: Error reading block device.\n");
300 return DISK_FORMAT_UNKNOWN;
301 }
302
303 if (!memcmp(data, fvm_magic, sizeof(fvm_magic))) {
304 return DISK_FORMAT_FVM;
305 }
306
307 if (!memcmp(data, zxcrypt_magic, sizeof(zxcrypt_magic))) {
308 return DISK_FORMAT_ZXCRYPT;
309 }
310
311 if (!memcmp(data + 0x200, gpt_magic, sizeof(gpt_magic))) {
312 return DISK_FORMAT_GPT;
313 }
314
315 if (!memcmp(data, minfs_magic, sizeof(minfs_magic))) {
316 return DISK_FORMAT_MINFS;
317 }
318
319 if (!memcmp(data, blobfs_magic, sizeof(blobfs_magic))) {
320 return DISK_FORMAT_BLOBFS;
321 }
322
323 if ((data[510] == 0x55 && data[511] == 0xAA)) {
324 if ((data[38] == 0x29 || data[66] == 0x29)) {
325 // 0x55AA are always placed at offset 510 and 511 for FAT filesystems.
326 // 0x29 is the Boot Signature, but it is placed at either offset 38 or
327 // 66 (depending on FAT type).
328 return DISK_FORMAT_FAT;
329 }
330 return DISK_FORMAT_MBR;
331 }
332 return DISK_FORMAT_UNKNOWN;
333 }
334
fmount(int device_fd,int mount_fd,disk_format_t df,const mount_options_t * options,LaunchCallback cb)335 zx_status_t fmount(int device_fd, int mount_fd, disk_format_t df, const mount_options_t* options,
336 LaunchCallback cb) {
337 Mounter mounter(mount_fd);
338 return mounter.Mount(unique_fd(device_fd), df, *options, cb);
339 }
340
mount(int device_fd,const char * mount_path,disk_format_t df,const mount_options_t * options,LaunchCallback cb)341 zx_status_t mount(int device_fd, const char* mount_path, disk_format_t df,
342 const mount_options_t* options, LaunchCallback cb) {
343 if (!options->create_mountpoint) {
344 // Open mountpoint; use it directly.
345 unique_fd mount_point(open(mount_path, O_RDONLY | O_DIRECTORY | O_ADMIN));
346 if (!mount_point) {
347 return ZX_ERR_BAD_STATE;
348 }
349 return fmount(device_fd, mount_point.get(), df, options, cb);
350 }
351
352 Mounter mounter(mount_path);
353 return mounter.Mount(unique_fd(device_fd), df, *options, cb);
354 }
355
fumount(int mount_fd)356 zx_status_t fumount(int mount_fd) {
357 zx_handle_t h;
358 zx_status_t status;
359 fzl::FdioCaller caller{fbl::unique_fd(mount_fd)};
360 zx_status_t io_status = fuchsia_io_DirectoryAdminUnmountNode(caller.borrow_channel(),
361 &status, &h);
362 caller.release().release();
363 if (io_status != ZX_OK) {
364 return io_status;
365 }
366 zx::channel c(h);
367 if (status != ZX_OK) {
368 return status;
369 }
370 return vfs_unmount_handle(c.release(), ZX_TIME_INFINITE);
371 }
372
umount(const char * mount_path)373 zx_status_t umount(const char* mount_path) {
374 unique_fd fd(open(mount_path, O_DIRECTORY | O_NOREMOTE | O_ADMIN));
375 if (!fd) {
376 fprintf(stderr, "Could not open directory: %s\n", strerror(errno));
377 return ZX_ERR_BAD_STATE;
378 }
379 zx_status_t status = fumount(fd.get());
380 return status;
381 }
382