Fix FreeBSD build.
[haiku.git] / src / tools / fs_shell / fuse.cpp
blobd975e4e4cbe8fbe9157b40ddd2016b176ae853ad
1 /*
2 * Copyright 2009, Raghuram Nagireddy <raghuram87@gmail.com>.
3 * Distributed under the terms of the MIT License.
4 */
6 #define FUSE_USE_VERSION 27
8 #include <fuse/fuse.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <syslog.h>
12 #include <unistd.h>
14 #include "fssh.h"
16 #include "driver_settings.h"
17 #include "external_commands.h"
18 #include "fd.h"
19 #include "fssh_dirent.h"
20 #include "fssh_errno.h"
21 #include "fssh_errors.h"
22 #include "fssh_fcntl.h"
23 #include "fssh_fs_info.h"
24 #include "fssh_module.h"
25 #include "fssh_node_monitor.h"
26 #include "fssh_stat.h"
27 #include "fssh_string.h"
28 #include "fssh_type_constants.h"
29 #include "module.h"
30 #include "syscalls.h"
31 #include "vfs.h"
34 extern fssh_module_info *modules[];
36 extern fssh_file_system_module_info gRootFileSystem;
38 namespace FSShell {
40 const char* kMountPoint = "/myfs";
42 static mode_t sUmask = 0022;
44 #define PRINTD(x) if (gIsDebug) fprintf(stderr, x)
46 bool gIsDebug = false;
48 static fssh_status_t
49 init_kernel()
51 fssh_status_t error;
53 // init module subsystem
54 error = module_init(NULL);
55 if (error != FSSH_B_OK) {
56 fprintf(stderr, "module_init() failed: %s\n", fssh_strerror(error));
57 return error;
60 // init driver settings
61 error = driver_settings_init();
62 if (error != FSSH_B_OK) {
63 fprintf(stderr, "initializing driver settings failed: %s\n",
64 fssh_strerror(error));
65 return error;
68 // register built-in modules, i.e. the rootfs and the client FS
69 register_builtin_module(&gRootFileSystem.info);
70 for (int i = 0; modules[i]; i++)
71 register_builtin_module(modules[i]);
73 // init VFS
74 error = vfs_init(NULL);
75 if (error != FSSH_B_OK) {
76 fprintf(stderr, "initializing VFS failed: %s\n", fssh_strerror(error));
77 return error;
80 // init kernel IO context
81 gKernelIOContext = (io_context*)vfs_new_io_context(NULL);
82 if (!gKernelIOContext) {
83 fprintf(stderr, "creating IO context failed!\n");
84 return FSSH_B_NO_MEMORY;
87 // mount root FS
88 fssh_dev_t rootDev = _kern_mount("/", NULL, "rootfs", 0, NULL, 0);
89 if (rootDev < 0) {
90 fprintf(stderr, "mounting rootfs failed: %s\n", fssh_strerror(rootDev));
91 return rootDev;
94 // set cwd to "/"
95 error = _kern_setcwd(-1, "/");
96 if (error != FSSH_B_OK) {
97 fprintf(stderr, "setting cwd failed: %s\n", fssh_strerror(error));
98 return error;
101 // create mount point for the client FS
102 error = _kern_create_dir(-1, kMountPoint, 0775);
103 if (error != FSSH_B_OK) {
104 fprintf(stderr, "creating mount point failed: %s\n",
105 fssh_strerror(error));
106 return error;
109 return FSSH_B_OK;
113 static void
114 fromFsshStatToStat(struct fssh_stat* f_stbuf, struct stat* stbuf)
116 stbuf->st_dev = f_stbuf->fssh_st_dev;
117 stbuf->st_ino = f_stbuf->fssh_st_ino;
118 stbuf->st_mode = f_stbuf->fssh_st_mode;
119 stbuf->st_nlink = f_stbuf->fssh_st_nlink;
120 stbuf->st_uid = f_stbuf->fssh_st_uid;
121 stbuf->st_gid = f_stbuf->fssh_st_gid;
122 stbuf->st_rdev = f_stbuf->fssh_st_rdev;
123 stbuf->st_size = f_stbuf->fssh_st_size;
124 stbuf->st_blksize = f_stbuf->fssh_st_blksize;
125 stbuf->st_blocks = f_stbuf->fssh_st_blocks;
126 stbuf->st_atime = f_stbuf->fssh_st_atime;
127 stbuf->st_mtime = f_stbuf->fssh_st_mtime;
128 stbuf->st_ctime = f_stbuf->fssh_st_ctime;
131 #define _ERR(x) (-1 * fssh_to_host_error(x))
134 // pragma mark - FUSE functions
138 fuse_getattr(const char* path, struct stat* stbuf)
140 PRINTD("##getattr\n");
141 struct fssh_stat f_stbuf;
142 fssh_status_t status = _kern_read_stat(-1, path, false, &f_stbuf,
143 sizeof(f_stbuf));
144 fromFsshStatToStat(&f_stbuf, stbuf);
145 if (gIsDebug)
146 printf("GETATTR returned: %d\n", status);
147 return _ERR(status);
151 static int
152 fuse_access(const char* path, int mask)
154 PRINTD("##access\n");
155 return _ERR(_kern_access(path, mask));
159 static int
160 fuse_readlink(const char* path, char* buffer, size_t size)
162 PRINTD("##readlink\n");
163 fssh_size_t n_size = size - 1;
164 fssh_status_t st = _kern_read_link(-1, path, buffer, &n_size);
165 if (st >= FSSH_B_OK)
166 buffer[n_size] = '\0';
167 return _ERR(st);
171 static int
172 fuse_readdir(const char* path, void* buf, fuse_fill_dir_t filler,
173 off_t offset, struct fuse_file_info* fi)
175 PRINTD("##readdir\n");
176 int dfp = _kern_open_dir(-1, path);
177 if (dfp < FSSH_B_OK)
178 return _ERR(dfp);
180 fssh_ssize_t entriesRead = 0;
181 struct fssh_stat f_st;
182 struct stat st;
183 char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
184 fssh_dirent* dirEntry = (fssh_dirent*)buffer;
185 while ((entriesRead = _kern_read_dir(dfp, dirEntry,
186 sizeof(buffer), 1)) == 1) {
187 fssh_memset(&st, 0, sizeof(st));
188 fssh_memset(&f_st, 0, sizeof(f_st));
189 fssh_status_t status = _kern_read_stat(dfp, dirEntry->d_name,
190 false, &f_st, sizeof(f_st));
191 if (status >= FSSH_B_OK) {
192 fromFsshStatToStat(&f_st, &st);
193 if (filler(buf, dirEntry->d_name, &st, 0))
194 break;
197 _kern_close(dfp);
198 //TODO: check _kern_close
199 return 0;
203 static int
204 fuse_mknod(const char* path, mode_t mode, dev_t rdev)
206 PRINTD("##mknod\n");
207 if (S_ISREG(mode)) {
208 int fd = _kern_open(-1, path,
209 FSSH_O_CREAT | FSSH_O_EXCL | FSSH_O_WRONLY, mode);
210 if (fd >= FSSH_B_OK)
211 return _ERR(_kern_close(fd));
212 return _ERR(fd);
213 } else if (S_ISFIFO(mode))
214 return _ERR(FSSH_EINVAL);
215 else
216 return _ERR(FSSH_EINVAL);
220 static int
221 fuse_mkdir(const char* path, mode_t mode)
223 PRINTD("##mkdir\n");
224 return _ERR(_kern_create_dir(-1, path, mode));
228 static int
229 fuse_symlink(const char* from, const char* to)
231 PRINTD("##symlink\n");
232 return _ERR(_kern_create_symlink(-1, to, from,
233 FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO));
237 static int
238 fuse_unlink(const char* path)
240 PRINTD("##unlink\n");
241 return _ERR(_kern_unlink(-1, path));
245 static int
246 fuse_rmdir(const char* path)
248 PRINTD("##rmdir\n");
249 return _ERR(_kern_remove_dir(-1, path));
253 static int
254 fuse_rename(const char* from, const char* to)
256 PRINTD("##rename\n");
257 return _ERR(_kern_rename(-1, from, -1, to));
261 static int
262 fuse_link(const char* from, const char* to)
264 PRINTD("##link\n");
265 return _ERR(_kern_create_link(to, from));
269 static int
270 fuse_chmod(const char* path, mode_t mode)
272 PRINTD("##chmod\n");
273 fssh_struct_stat st;
274 st.fssh_st_mode = mode;
275 return _ERR(_kern_write_stat(-1, path, false, &st, sizeof(st),
276 FSSH_B_STAT_MODE));
280 static int
281 fuse_chown(const char* path, uid_t uid, gid_t gid)
283 PRINTD("##chown\n");
284 fssh_struct_stat st;
285 st.fssh_st_uid = uid;
286 st.fssh_st_gid = gid;
287 return _ERR(_kern_write_stat(-1, path, false, &st, sizeof(st),
288 FSSH_B_STAT_UID|FSSH_B_STAT_GID));
292 static int
293 fuse_open(const char* path, struct fuse_file_info* fi)
295 PRINTD("##open\n");
296 // TODO: Do we have a syscall similar to the open syscall in linux which
297 // takes only two args: path and flags with no mask/perms?
298 int fd = _kern_open(-1, path, fi->flags,
299 (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask);
300 _kern_close(fd);
301 if (fd < FSSH_B_OK)
302 return _ERR(fd);
303 else
304 return 0;
308 static int
309 fuse_read(const char* path, char* buf, size_t size, off_t offset,
310 struct fuse_file_info* fi)
312 PRINTD("##read\n");
313 int fd = _kern_open(-1, path, FSSH_O_RDONLY,
314 (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask);
315 if (fd < FSSH_B_OK)
316 return _ERR(fd);
318 int res = _kern_read(fd, offset, buf, size);
319 _kern_close(fd);
320 if (res < FSSH_B_OK)
321 res = _ERR(res);
322 return res;
326 static int
327 fuse_write(const char* path, const char* buf, size_t size, off_t offset,
328 struct fuse_file_info* fi)
330 PRINTD("##write\n");
331 int fd = _kern_open(-1, path, FSSH_O_WRONLY,
332 (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask);
333 if (fd < FSSH_B_OK)
334 return _ERR(fd);
336 int res = _kern_write(fd, offset, buf, size);
337 _kern_close(fd);
338 if (res < FSSH_B_OK)
339 res = _ERR(res);
340 return res;
344 static void
345 fuse_destroy(void* priv_data)
347 _kern_sync();
351 static fssh_dev_t
352 get_volume_id()
354 struct fssh_stat st;
355 fssh_status_t error = _kern_read_stat(-1, kMountPoint, false, &st,
356 sizeof(st));
357 if (error != FSSH_B_OK)
358 return error;
359 return st.fssh_st_dev;
363 static int
364 fuse_statfs(const char *path __attribute__((unused)),
365 struct statvfs *sfs)
367 PRINTD("##statfs\n");
369 fssh_dev_t volumeID = get_volume_id();
370 if (volumeID < 0)
371 return _ERR(volumeID);
373 fssh_fs_info info;
374 fssh_status_t status = _kern_read_fs_info(volumeID, &info);
375 if (status != FSSH_B_OK)
376 return _ERR(status);
378 sfs->f_bsize = sfs->f_frsize = info.block_size;
379 sfs->f_blocks = info.total_blocks;
380 sfs->f_bavail = sfs->f_bfree = info.free_blocks;
382 return 0;
386 struct fuse_operations gFUSEOperations;
389 static void
390 initialiseFuseOps(struct fuse_operations* fuseOps)
392 fuseOps->getattr = fuse_getattr;
393 fuseOps->access = fuse_access;
394 fuseOps->readlink = fuse_readlink;
395 fuseOps->readdir = fuse_readdir;
396 fuseOps->mknod = fuse_mknod;
397 fuseOps->mkdir = fuse_mkdir;
398 fuseOps->symlink = fuse_symlink;
399 fuseOps->unlink = fuse_unlink;
400 fuseOps->rmdir = fuse_rmdir;
401 fuseOps->rename = fuse_rename;
402 fuseOps->link = fuse_link;
403 fuseOps->chmod = fuse_chmod;
404 fuseOps->chown = fuse_chown;
405 fuseOps->truncate = NULL;
406 fuseOps->utimens = NULL;
407 fuseOps->open = fuse_open;
408 fuseOps->read = fuse_read;
409 fuseOps->write = fuse_write;
410 fuseOps->statfs = fuse_statfs;
411 fuseOps->release = NULL;
412 fuseOps->fsync = NULL;
413 fuseOps->destroy = fuse_destroy;
417 static int
418 mount_volume(const char* device, const char* mntPoint, const char* fsName)
420 // Mount the volume in the root FS.
421 fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0);
422 if (fsDev < 0) {
423 fprintf(stderr, "Error: Mounting FS failed: %s\n",
424 fssh_strerror(fsDev));
425 return 1;
428 if (!gIsDebug) {
429 bool isErr = false;
430 fssh_dev_t volumeID = get_volume_id();
431 if (volumeID < 0)
432 isErr = true;
433 fssh_fs_info info;
434 if (!isErr) {
435 fssh_status_t status = _kern_read_fs_info(volumeID, &info);
436 if (status != FSSH_B_OK)
437 isErr = true;
439 syslog(LOG_INFO, "Mounted %s (%s) to %s",
440 device,
441 isErr ? "unknown" : info.volume_name,
442 mntPoint);
445 return 0;
449 static int
450 unmount_volume(const char* device, const char* mntPoint)
452 // Unmount the volume again.
453 // Avoid a "busy" vnode.
454 _kern_setcwd(-1, "/");
455 fssh_status_t error = _kern_unmount(kMountPoint, 0);
456 if (error != FSSH_B_OK) {
457 if (gIsDebug)
458 fprintf(stderr, "Error: Unmounting FS failed: %s\n",
459 fssh_strerror(error));
460 else
461 syslog(LOG_INFO, "Error: Unmounting FS failed: %s",
462 fssh_strerror(error));
463 return 1;
466 if (!gIsDebug)
467 syslog(LOG_INFO, "UnMounted %s from %s", device, mntPoint);
469 return 0;
473 static int
474 fssh_fuse_session(const char* device, const char* mntPoint, const char* fsName,
475 struct fuse_args& fuseArgs)
477 int ret;
479 ret = mount_volume(device, mntPoint, fsName);
480 if (ret != 0)
481 return ret;
483 if (getuid() == 0 && geteuid() == 0 && getgid() == 0 && getegid() == 0) {
484 // only add FUSE options when user is root
486 char* fuseOptions = NULL;
488 // default FUSE options
489 char* fsNameOption = NULL;
490 if (fuse_opt_add_opt(&fuseOptions, "allow_other") < 0
491 || asprintf(&fsNameOption, "fsname=%s", device) < 0
492 || fuse_opt_add_opt(&fuseOptions, fsNameOption) < 0) {
493 unmount_volume(device, mntPoint);
494 return 1;
497 struct stat sbuf;
498 if ((stat(device, &sbuf) == 0) && S_ISBLK(sbuf.st_mode)) {
499 int blkSize = 512;
500 fssh_dev_t volumeID = get_volume_id();
501 if (volumeID >= 0) {
502 fssh_fs_info info;
503 if (_kern_read_fs_info(volumeID, &info) == FSSH_B_OK)
504 blkSize = info.block_size;
507 char* blkSizeOption = NULL;
508 if (fuse_opt_add_opt(&fuseOptions, "blkdev") < 0
509 || asprintf(&blkSizeOption, "blksize=%i", blkSize) < 0
510 || fuse_opt_add_opt(&fuseOptions, blkSizeOption) < 0) {
511 unmount_volume(device, mntPoint);
512 return 1;
516 if (fuse_opt_add_arg(&fuseArgs, "-o") < 0
517 || fuse_opt_add_arg(&fuseArgs, fuseOptions) < 0) {
518 unmount_volume(device, mntPoint);
519 return 1;
523 // Run the fuse_main() loop.
524 if (fuse_opt_add_arg(&fuseArgs, "-s") < 0) {
525 unmount_volume(device, mntPoint);
526 return 1;
529 initialiseFuseOps(&gFUSEOperations);
531 int res = fuse_main(fuseArgs.argc, fuseArgs.argv, &gFUSEOperations, NULL);
533 ret = unmount_volume(device, mntPoint);
534 if (ret != 0)
535 return ret;
537 return res;
541 } // namespace FSShell
544 using namespace FSShell;
547 static void
548 print_usage_and_exit(const char* binName)
550 fprintf(stderr,"Usage: %s [-d] <device> <mount point>\n", binName);
551 exit(1);
555 struct FsConfig {
556 const char* device;
557 const char* mntPoint;
561 enum {
562 KEY_DEBUG,
563 KEY_HELP
567 static int
568 process_options(void* data, const char* arg, int key, struct fuse_args* outArgs)
570 struct FsConfig* config = (FsConfig*) data;
572 switch (key) {
573 case FUSE_OPT_KEY_NONOPT:
574 if (!config->device) {
575 config->device = arg;
576 return 0;
577 // don't pass the device path to fuse_main()
578 } else if (!config->mntPoint)
579 config->mntPoint = arg;
580 else
581 print_usage_and_exit(outArgs->argv[0]);
582 break;
583 case KEY_DEBUG:
584 gIsDebug = true;
585 break;
586 case KEY_HELP:
587 print_usage_and_exit(outArgs->argv[0]);
590 return 1;
595 main(int argc, char* argv[])
597 struct fuse_args fuseArgs = FUSE_ARGS_INIT(argc, argv);
598 struct FsConfig config;
599 memset(&config, 0, sizeof(config));
600 const struct fuse_opt fsOptions[] = {
601 FUSE_OPT_KEY("uhelper=", FUSE_OPT_KEY_DISCARD),
602 // fuse_main() throws an error about this unknown option
603 // TODO: do not use fuse_main to mount filesystem, instead use
604 // fuse_mount, fuse_new, fuse_set_signal_handlers and fuse_loop
605 FUSE_OPT_KEY("-d", KEY_DEBUG),
606 FUSE_OPT_KEY("-h", KEY_HELP),
607 FUSE_OPT_KEY("--help", KEY_HELP),
608 FUSE_OPT_END
611 if (fuse_opt_parse(&fuseArgs, &config, fsOptions, process_options) < 0)
612 return 1;
614 if (!config.mntPoint)
615 print_usage_and_exit(fuseArgs.argv[0]);
617 if (!modules[0]) {
618 fprintf(stderr, "Error: Couldn't find FS module!\n");
619 return 1;
622 fssh_status_t error = init_kernel();
623 if (error != FSSH_B_OK) {
624 fprintf(stderr, "Error: Initializing kernel failed: %s\n",
625 fssh_strerror(error));
626 return error;
629 const char* fsName = modules[0]->name;
630 return fssh_fuse_session(config.device, config.mntPoint, fsName, fuseArgs);