2 * Copyright 2009, Raghuram Nagireddy <raghuram87@gmail.com>.
3 * Distributed under the terms of the MIT License.
6 #define FUSE_USE_VERSION 27
16 #include "driver_settings.h"
17 #include "external_commands.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"
34 extern fssh_module_info
*modules
[];
36 extern fssh_file_system_module_info gRootFileSystem
;
40 const char* kMountPoint
= "/myfs";
42 static mode_t sUmask
= 0022;
44 #define PRINTD(x) if (gIsDebug) fprintf(stderr, x)
46 bool gIsDebug
= false;
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
));
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
));
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
]);
74 error
= vfs_init(NULL
);
75 if (error
!= FSSH_B_OK
) {
76 fprintf(stderr
, "initializing VFS failed: %s\n", fssh_strerror(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
;
88 fssh_dev_t rootDev
= _kern_mount("/", NULL
, "rootfs", 0, NULL
, 0);
90 fprintf(stderr
, "mounting rootfs failed: %s\n", fssh_strerror(rootDev
));
95 error
= _kern_setcwd(-1, "/");
96 if (error
!= FSSH_B_OK
) {
97 fprintf(stderr
, "setting cwd failed: %s\n", fssh_strerror(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
));
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
,
144 fromFsshStatToStat(&f_stbuf
, stbuf
);
146 printf("GETATTR returned: %d\n", status
);
152 fuse_access(const char* path
, int mask
)
154 PRINTD("##access\n");
155 return _ERR(_kern_access(path
, mask
));
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
);
166 buffer
[n_size
] = '\0';
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
);
180 fssh_ssize_t entriesRead
= 0;
181 struct fssh_stat f_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))
198 //TODO: check _kern_close
204 fuse_mknod(const char* path
, mode_t mode
, dev_t rdev
)
208 int fd
= _kern_open(-1, path
,
209 FSSH_O_CREAT
| FSSH_O_EXCL
| FSSH_O_WRONLY
, mode
);
211 return _ERR(_kern_close(fd
));
213 } else if (S_ISFIFO(mode
))
214 return _ERR(FSSH_EINVAL
);
216 return _ERR(FSSH_EINVAL
);
221 fuse_mkdir(const char* path
, mode_t mode
)
224 return _ERR(_kern_create_dir(-1, path
, mode
));
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
));
238 fuse_unlink(const char* path
)
240 PRINTD("##unlink\n");
241 return _ERR(_kern_unlink(-1, path
));
246 fuse_rmdir(const char* path
)
249 return _ERR(_kern_remove_dir(-1, path
));
254 fuse_rename(const char* from
, const char* to
)
256 PRINTD("##rename\n");
257 return _ERR(_kern_rename(-1, from
, -1, to
));
262 fuse_link(const char* from
, const char* to
)
265 return _ERR(_kern_create_link(to
, from
));
270 fuse_chmod(const char* path
, mode_t mode
)
274 st
.fssh_st_mode
= mode
;
275 return _ERR(_kern_write_stat(-1, path
, false, &st
, sizeof(st
),
281 fuse_chown(const char* path
, uid_t uid
, gid_t gid
)
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
));
293 fuse_open(const char* path
, struct fuse_file_info
* fi
)
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
);
309 fuse_read(const char* path
, char* buf
, size_t size
, off_t offset
,
310 struct fuse_file_info
* fi
)
313 int fd
= _kern_open(-1, path
, FSSH_O_RDONLY
,
314 (FSSH_S_IRWXU
| FSSH_S_IRWXG
| FSSH_S_IRWXO
) & ~sUmask
);
318 int res
= _kern_read(fd
, offset
, buf
, size
);
327 fuse_write(const char* path
, const char* buf
, size_t size
, off_t offset
,
328 struct fuse_file_info
* fi
)
331 int fd
= _kern_open(-1, path
, FSSH_O_WRONLY
,
332 (FSSH_S_IRWXU
| FSSH_S_IRWXG
| FSSH_S_IRWXO
) & ~sUmask
);
336 int res
= _kern_write(fd
, offset
, buf
, size
);
345 fuse_destroy(void* priv_data
)
355 fssh_status_t error
= _kern_read_stat(-1, kMountPoint
, false, &st
,
357 if (error
!= FSSH_B_OK
)
359 return st
.fssh_st_dev
;
364 fuse_statfs(const char *path
__attribute__((unused
)),
367 PRINTD("##statfs\n");
369 fssh_dev_t volumeID
= get_volume_id();
371 return _ERR(volumeID
);
374 fssh_status_t status
= _kern_read_fs_info(volumeID
, &info
);
375 if (status
!= FSSH_B_OK
)
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
;
386 struct fuse_operations gFUSEOperations
;
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
;
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);
423 fprintf(stderr
, "Error: Mounting FS failed: %s\n",
424 fssh_strerror(fsDev
));
430 fssh_dev_t volumeID
= get_volume_id();
435 fssh_status_t status
= _kern_read_fs_info(volumeID
, &info
);
436 if (status
!= FSSH_B_OK
)
439 syslog(LOG_INFO
, "Mounted %s (%s) to %s",
441 isErr
? "unknown" : info
.volume_name
,
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
) {
458 fprintf(stderr
, "Error: Unmounting FS failed: %s\n",
459 fssh_strerror(error
));
461 syslog(LOG_INFO
, "Error: Unmounting FS failed: %s",
462 fssh_strerror(error
));
467 syslog(LOG_INFO
, "UnMounted %s from %s", device
, mntPoint
);
474 fssh_fuse_session(const char* device
, const char* mntPoint
, const char* fsName
,
475 struct fuse_args
& fuseArgs
)
479 ret
= mount_volume(device
, mntPoint
, fsName
);
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
);
498 if ((stat(device
, &sbuf
) == 0) && S_ISBLK(sbuf
.st_mode
)) {
500 fssh_dev_t volumeID
= get_volume_id();
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
);
516 if (fuse_opt_add_arg(&fuseArgs
, "-o") < 0
517 || fuse_opt_add_arg(&fuseArgs
, fuseOptions
) < 0) {
518 unmount_volume(device
, mntPoint
);
523 // Run the fuse_main() loop.
524 if (fuse_opt_add_arg(&fuseArgs
, "-s") < 0) {
525 unmount_volume(device
, mntPoint
);
529 initialiseFuseOps(&gFUSEOperations
);
531 int res
= fuse_main(fuseArgs
.argc
, fuseArgs
.argv
, &gFUSEOperations
, NULL
);
533 ret
= unmount_volume(device
, mntPoint
);
541 } // namespace FSShell
544 using namespace FSShell
;
548 print_usage_and_exit(const char* binName
)
550 fprintf(stderr
,"Usage: %s [-d] <device> <mount point>\n", binName
);
557 const char* mntPoint
;
568 process_options(void* data
, const char* arg
, int key
, struct fuse_args
* outArgs
)
570 struct FsConfig
* config
= (FsConfig
*) data
;
573 case FUSE_OPT_KEY_NONOPT
:
574 if (!config
->device
) {
575 config
->device
= arg
;
577 // don't pass the device path to fuse_main()
578 } else if (!config
->mntPoint
)
579 config
->mntPoint
= arg
;
581 print_usage_and_exit(outArgs
->argv
[0]);
587 print_usage_and_exit(outArgs
->argv
[0]);
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
),
611 if (fuse_opt_parse(&fuseArgs
, &config
, fsOptions
, process_options
) < 0)
614 if (!config
.mntPoint
)
615 print_usage_and_exit(fuseArgs
.argv
[0]);
618 fprintf(stderr
, "Error: Couldn't find FS module!\n");
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
));
629 const char* fsName
= modules
[0]->name
;
630 return fssh_fuse_session(config
.device
, config
.mntPoint
, fsName
, fuseArgs
);