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 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 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 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 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 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 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 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 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 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 238 fuse_unlink(const char* path) 239 { 240 PRINTD("##unlink\n"); 241 return _ERR(_kern_unlink(-1, path)); 242 } 243 244 245 static int 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 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 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 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 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 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 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 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 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 359 fuse_destroy(void* priv_data) 360 { 361 _kern_sync(); 362 } 363 364 365 static fssh_dev_t 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 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 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 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 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 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 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 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 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