1 /*
2 * Copyright 2009, Raghuram Nagireddy <raghuram87@gmail.com>.
3 * Distributed under the terms of the MIT License.
4 */
5
6 #define FUSE_USE_VERSION 27
7
8 #include <fuse/fuse.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <syslog.h>
12 #include <unistd.h>
13
14 #include "fssh.h"
15
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"
32
33
34 extern fssh_module_info *modules[];
35
36 extern fssh_file_system_module_info gRootFileSystem;
37
38 namespace FSShell {
39
40 const char* kMountPoint = "/myfs";
41
42 static mode_t sUmask = 0022;
43
44 #define PRINTD(x) if (gIsDebug) fprintf(stderr, x)
45
46 bool gIsDebug = false;
47
48 static fssh_status_t
init_kernel()49 init_kernel()
50 {
51 fssh_status_t error;
52
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;
58 }
59
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;
66 }
67
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]);
72
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;
78 }
79
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;
85 }
86
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;
92 }
93
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;
99 }
100
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;
107 }
108
109 return FSSH_B_OK;
110 }
111
112
113 static void
fromFsshStatToStat(struct fssh_stat * f_stbuf,struct stat * stbuf)114 fromFsshStatToStat(struct fssh_stat* f_stbuf, struct stat* stbuf)
115 {
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;
129 }
130
131 #define _ERR(x) (-1 * fssh_to_host_error(x))
132
133
134 // pragma mark - FUSE functions
135
136
137 int
fuse_getattr(const char * path,struct stat * stbuf)138 fuse_getattr(const char* path, struct stat* stbuf)
139 {
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);
148 }
149
150
151 static int
fuse_access(const char * path,int mask)152 fuse_access(const char* path, int mask)
153 {
154 PRINTD("##access\n");
155 return _ERR(_kern_access(path, mask));
156 }
157
158
159 static int
fuse_readlink(const char * path,char * buffer,size_t size)160 fuse_readlink(const char* path, char* buffer, size_t size)
161 {
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);
168 }
169
170
171 static int
fuse_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)172 fuse_readdir(const char* path, void* buf, fuse_fill_dir_t filler,
173 off_t offset, struct fuse_file_info* fi)
174 {
175 PRINTD("##readdir\n");
176 int dfp = _kern_open_dir(-1, path);
177 if (dfp < FSSH_B_OK)
178 return _ERR(dfp);
179
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;
195 }
196 }
197 _kern_close(dfp);
198 //TODO: check _kern_close
199 return 0;
200 }
201
202
203 static int
fuse_mknod(const char * path,mode_t mode,dev_t rdev)204 fuse_mknod(const char* path, mode_t mode, dev_t rdev)
205 {
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);
217 }
218
219
220 static int
fuse_mkdir(const char * path,mode_t mode)221 fuse_mkdir(const char* path, mode_t mode)
222 {
223 PRINTD("##mkdir\n");
224 return _ERR(_kern_create_dir(-1, path, mode));
225 }
226
227
228 static int
fuse_symlink(const char * from,const char * to)229 fuse_symlink(const char* from, const char* to)
230 {
231 PRINTD("##symlink\n");
232 return _ERR(_kern_create_symlink(-1, to, from,
233 FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO));
234 }
235
236
237 static int
fuse_unlink(const char * path)238 fuse_unlink(const char* path)
239 {
240 PRINTD("##unlink\n");
241 return _ERR(_kern_unlink(-1, path));
242 }
243
244
245 static int
fuse_rmdir(const char * path)246 fuse_rmdir(const char* path)
247 {
248 PRINTD("##rmdir\n");
249 return _ERR(_kern_remove_dir(-1, path));
250 }
251
252
253 static int
fuse_rename(const char * from,const char * to)254 fuse_rename(const char* from, const char* to)
255 {
256 PRINTD("##rename\n");
257 return _ERR(_kern_rename(-1, from, -1, to));
258 }
259
260
261 static int
fuse_link(const char * from,const char * to)262 fuse_link(const char* from, const char* to)
263 {
264 PRINTD("##link\n");
265 return _ERR(_kern_create_link(to, from));
266 }
267
268
269 static int
fuse_chmod(const char * path,mode_t mode)270 fuse_chmod(const char* path, mode_t mode)
271 {
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));
277 }
278
279
280 static int
fuse_chown(const char * path,uid_t uid,gid_t gid)281 fuse_chown(const char* path, uid_t uid, gid_t gid)
282 {
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));
289 }
290
291
292 static int
fuse_open(const char * path,struct fuse_file_info * fi)293 fuse_open(const char* path, struct fuse_file_info* fi)
294 {
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;
305 }
306
307
308 static int
fuse_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)309 fuse_read(const char* path, char* buf, size_t size, off_t offset,
310 struct fuse_file_info* fi)
311 {
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);
317
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;
323 }
324
325
326 static int
fuse_write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)327 fuse_write(const char* path, const char* buf, size_t size, off_t offset,
328 struct fuse_file_info* fi)
329 {
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);
335
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;
341 }
342
343
344 static int
fuse_truncate(const char * path,off_t off)345 fuse_truncate(const char *path, off_t off)
346 {
347 PRINTD("##truncate\n");
348
349 struct fssh_stat st;
350 st.fssh_st_size = off;
351 fssh_status_t res = _kern_write_stat(-1, path, true, &st, sizeof(st), FSSH_B_STAT_SIZE);
352 if (res < FSSH_B_OK)
353 return _ERR(res);
354 return 0;
355 }
356
357
358 static void
fuse_destroy(void * priv_data)359 fuse_destroy(void* priv_data)
360 {
361 _kern_sync();
362 }
363
364
365 static fssh_dev_t
get_volume_id()366 get_volume_id()
367 {
368 struct fssh_stat st;
369 fssh_status_t error = _kern_read_stat(-1, kMountPoint, false, &st,
370 sizeof(st));
371 if (error != FSSH_B_OK)
372 return error;
373 return st.fssh_st_dev;
374 }
375
376
377 static int
fuse_statfs(const char * path,struct statvfs * sfs)378 fuse_statfs(const char *path __attribute__((unused)),
379 struct statvfs *sfs)
380 {
381 PRINTD("##statfs\n");
382
383 fssh_dev_t volumeID = get_volume_id();
384 if (volumeID < 0)
385 return _ERR(volumeID);
386
387 fssh_fs_info info;
388 fssh_status_t status = _kern_read_fs_info(volumeID, &info);
389 if (status != FSSH_B_OK)
390 return _ERR(status);
391
392 sfs->f_bsize = sfs->f_frsize = info.block_size;
393 sfs->f_blocks = info.total_blocks;
394 sfs->f_bavail = sfs->f_bfree = info.free_blocks;
395
396 return 0;
397 }
398
399
400 struct fuse_operations gFUSEOperations;
401
402
403 static void
initialiseFuseOps(struct fuse_operations * fuseOps)404 initialiseFuseOps(struct fuse_operations* fuseOps)
405 {
406 fuseOps->getattr = fuse_getattr;
407 fuseOps->access = fuse_access;
408 fuseOps->readlink = fuse_readlink;
409 fuseOps->readdir = fuse_readdir;
410 fuseOps->mknod = fuse_mknod;
411 fuseOps->mkdir = fuse_mkdir;
412 fuseOps->symlink = fuse_symlink;
413 fuseOps->unlink = fuse_unlink;
414 fuseOps->rmdir = fuse_rmdir;
415 fuseOps->rename = fuse_rename;
416 fuseOps->link = fuse_link;
417 fuseOps->chmod = fuse_chmod;
418 fuseOps->chown = fuse_chown;
419 fuseOps->truncate = fuse_truncate;
420 fuseOps->utimens = NULL;
421 fuseOps->open = fuse_open;
422 fuseOps->read = fuse_read;
423 fuseOps->write = fuse_write;
424 fuseOps->statfs = fuse_statfs;
425 fuseOps->release = NULL;
426 fuseOps->fsync = NULL;
427 fuseOps->destroy = fuse_destroy;
428 }
429
430
431 static int
mount_volume(const char * device,const char * mntPoint,const char * fsName)432 mount_volume(const char* device, const char* mntPoint, const char* fsName)
433 {
434 // Mount the volume in the root FS.
435 fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0);
436 if (fsDev < 0) {
437 fprintf(stderr, "Error: Mounting FS failed: %s\n",
438 fssh_strerror(fsDev));
439 return 1;
440 }
441
442 if (!gIsDebug) {
443 bool isErr = false;
444 fssh_dev_t volumeID = get_volume_id();
445 if (volumeID < 0)
446 isErr = true;
447 fssh_fs_info info;
448 if (!isErr) {
449 fssh_status_t status = _kern_read_fs_info(volumeID, &info);
450 if (status != FSSH_B_OK)
451 isErr = true;
452 }
453 syslog(LOG_INFO, "Mounted %s (%s) to %s",
454 device,
455 isErr ? "unknown" : info.volume_name,
456 mntPoint);
457 }
458
459 return 0;
460 }
461
462
463 static int
unmount_volume(const char * device,const char * mntPoint)464 unmount_volume(const char* device, const char* mntPoint)
465 {
466 // Unmount the volume again.
467 // Avoid a "busy" vnode.
468 _kern_setcwd(-1, "/");
469 fssh_status_t error = _kern_unmount(kMountPoint, 0);
470 if (error != FSSH_B_OK) {
471 if (gIsDebug)
472 fprintf(stderr, "Error: Unmounting FS failed: %s\n",
473 fssh_strerror(error));
474 else
475 syslog(LOG_INFO, "Error: Unmounting FS failed: %s",
476 fssh_strerror(error));
477 return 1;
478 }
479
480 if (!gIsDebug)
481 syslog(LOG_INFO, "UnMounted %s from %s", device, mntPoint);
482
483 return 0;
484 }
485
486
487 static int
fssh_fuse_session(const char * device,const char * mntPoint,const char * fsName,struct fuse_args & fuseArgs)488 fssh_fuse_session(const char* device, const char* mntPoint, const char* fsName,
489 struct fuse_args& fuseArgs)
490 {
491 int ret;
492
493 ret = mount_volume(device, mntPoint, fsName);
494 if (ret != 0)
495 return ret;
496
497 if (getuid() == 0 && geteuid() == 0 && getgid() == 0 && getegid() == 0) {
498 // only add FUSE options when user is root
499
500 char* fuseOptions = NULL;
501
502 // default FUSE options
503 char* fsNameOption = NULL;
504 if (fuse_opt_add_opt(&fuseOptions, "allow_other") < 0
505 || asprintf(&fsNameOption, "fsname=%s", device) < 0
506 || fuse_opt_add_opt(&fuseOptions, fsNameOption) < 0) {
507 unmount_volume(device, mntPoint);
508 return 1;
509 }
510
511 struct stat sbuf;
512 if ((stat(device, &sbuf) == 0) && S_ISBLK(sbuf.st_mode)) {
513 int blkSize = 512;
514 fssh_dev_t volumeID = get_volume_id();
515 if (volumeID >= 0) {
516 fssh_fs_info info;
517 if (_kern_read_fs_info(volumeID, &info) == FSSH_B_OK)
518 blkSize = info.block_size;
519 }
520
521 char* blkSizeOption = NULL;
522 if (fuse_opt_add_opt(&fuseOptions, "blkdev") < 0
523 || asprintf(&blkSizeOption, "blksize=%i", blkSize) < 0
524 || fuse_opt_add_opt(&fuseOptions, blkSizeOption) < 0) {
525 unmount_volume(device, mntPoint);
526 return 1;
527 }
528 }
529
530 if (fuse_opt_add_arg(&fuseArgs, "-o") < 0
531 || fuse_opt_add_arg(&fuseArgs, fuseOptions) < 0) {
532 unmount_volume(device, mntPoint);
533 return 1;
534 }
535 }
536
537 // Run the fuse_main() loop.
538 if (fuse_opt_add_arg(&fuseArgs, "-s") < 0) {
539 unmount_volume(device, mntPoint);
540 return 1;
541 }
542
543 initialiseFuseOps(&gFUSEOperations);
544
545 int res = fuse_main(fuseArgs.argc, fuseArgs.argv, &gFUSEOperations, NULL);
546
547 ret = unmount_volume(device, mntPoint);
548 if (ret != 0)
549 return ret;
550
551 return res;
552 }
553
554
555 } // namespace FSShell
556
557
558 using namespace FSShell;
559
560
561 static void
print_usage_and_exit(const char * binName)562 print_usage_and_exit(const char* binName)
563 {
564 fprintf(stderr,"Usage: %s [-d] <device> <mount point>\n", binName);
565 exit(1);
566 }
567
568
569 struct FsConfig {
570 const char* device;
571 const char* mntPoint;
572 };
573
574
575 enum {
576 KEY_DEBUG,
577 KEY_HELP
578 };
579
580
581 static int
process_options(void * data,const char * arg,int key,struct fuse_args * outArgs)582 process_options(void* data, const char* arg, int key, struct fuse_args* outArgs)
583 {
584 struct FsConfig* config = (FsConfig*) data;
585
586 switch (key) {
587 case FUSE_OPT_KEY_NONOPT:
588 if (!config->device) {
589 config->device = arg;
590 return 0;
591 // don't pass the device path to fuse_main()
592 } else if (!config->mntPoint)
593 config->mntPoint = arg;
594 else
595 print_usage_and_exit(outArgs->argv[0]);
596 break;
597 case KEY_DEBUG:
598 gIsDebug = true;
599 break;
600 case KEY_HELP:
601 print_usage_and_exit(outArgs->argv[0]);
602 }
603
604 return 1;
605 }
606
607
608 int
main(int argc,char * argv[])609 main(int argc, char* argv[])
610 {
611 struct fuse_args fuseArgs = FUSE_ARGS_INIT(argc, argv);
612 struct FsConfig config;
613 memset(&config, 0, sizeof(config));
614 const struct fuse_opt fsOptions[] = {
615 FUSE_OPT_KEY("uhelper=", FUSE_OPT_KEY_DISCARD),
616 // fuse_main() throws an error about this unknown option
617 // TODO: do not use fuse_main to mount filesystem, instead use
618 // fuse_mount, fuse_new, fuse_set_signal_handlers and fuse_loop
619 FUSE_OPT_KEY("-d", KEY_DEBUG),
620 FUSE_OPT_KEY("-h", KEY_HELP),
621 FUSE_OPT_KEY("--help", KEY_HELP),
622 FUSE_OPT_END
623 };
624
625 if (fuse_opt_parse(&fuseArgs, &config, fsOptions, process_options) < 0)
626 return 1;
627
628 if (!config.mntPoint)
629 print_usage_and_exit(fuseArgs.argv[0]);
630
631 if (!modules[0]) {
632 fprintf(stderr, "Error: Couldn't find FS module!\n");
633 return 1;
634 }
635
636 fssh_status_t error = init_kernel();
637 if (error != FSSH_B_OK) {
638 fprintf(stderr, "Error: Initializing kernel failed: %s\n",
639 fssh_strerror(error));
640 return error;
641 }
642
643 const char* fsName = modules[0]->name;
644 return fssh_fuse_session(config.device, config.mntPoint, fsName, fuseArgs);
645 }
646
647