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 void 345 fuse_destroy(void* priv_data) 346 { 347 _kern_sync(); 348 } 349 350 351 static fssh_dev_t 352 get_volume_id() 353 { 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; 360 } 361 362 363 static int 364 fuse_statfs(const char *path __attribute__((unused)), 365 struct statvfs *sfs) 366 { 367 PRINTD("##statfs\n"); 368 369 fssh_dev_t volumeID = get_volume_id(); 370 if (volumeID < 0) 371 return _ERR(volumeID); 372 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); 377 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; 381 382 return 0; 383 } 384 385 386 struct fuse_operations gFUSEOperations; 387 388 389 static void 390 initialiseFuseOps(struct fuse_operations* fuseOps) 391 { 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; 414 } 415 416 417 static int 418 mount_volume(const char* device, const char* mntPoint, const char* fsName) 419 { 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; 426 } 427 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; 438 } 439 syslog(LOG_INFO, "Mounted %s (%s) to %s", 440 device, 441 isErr ? "unknown" : info.volume_name, 442 mntPoint); 443 } 444 445 return 0; 446 } 447 448 449 static int 450 unmount_volume(const char* device, const char* mntPoint) 451 { 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; 464 } 465 466 if (!gIsDebug) 467 syslog(LOG_INFO, "UnMounted %s from %s", device, mntPoint); 468 469 return 0; 470 } 471 472 473 static int 474 fssh_fuse_session(const char* device, const char* mntPoint, const char* fsName, 475 struct fuse_args& fuseArgs) 476 { 477 int ret; 478 479 ret = mount_volume(device, mntPoint, fsName); 480 if (ret != 0) 481 return ret; 482 483 if (getuid() == 0 && geteuid() == 0 && getgid() == 0 && getegid() == 0) { 484 // only add FUSE options when user is root 485 486 char* fuseOptions = NULL; 487 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; 495 } 496 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; 505 } 506 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; 513 } 514 } 515 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; 520 } 521 } 522 523 // Run the fuse_main() loop. 524 if (fuse_opt_add_arg(&fuseArgs, "-s") < 0) { 525 unmount_volume(device, mntPoint); 526 return 1; 527 } 528 529 initialiseFuseOps(&gFUSEOperations); 530 531 int res = fuse_main(fuseArgs.argc, fuseArgs.argv, &gFUSEOperations, NULL); 532 533 ret = unmount_volume(device, mntPoint); 534 if (ret != 0) 535 return ret; 536 537 return res; 538 } 539 540 541 } // namespace FSShell 542 543 544 using namespace FSShell; 545 546 547 static void 548 print_usage_and_exit(const char* binName) 549 { 550 fprintf(stderr,"Usage: %s [-d] <device> <mount point>\n", binName); 551 exit(1); 552 } 553 554 555 struct FsConfig { 556 const char* device; 557 const char* mntPoint; 558 }; 559 560 561 enum { 562 KEY_DEBUG, 563 KEY_HELP 564 }; 565 566 567 static int 568 process_options(void* data, const char* arg, int key, struct fuse_args* outArgs) 569 { 570 struct FsConfig* config = (FsConfig*) data; 571 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]); 588 } 589 590 return 1; 591 } 592 593 594 int 595 main(int argc, char* argv[]) 596 { 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 609 }; 610 611 if (fuse_opt_parse(&fuseArgs, &config, fsOptions, process_options) < 0) 612 return 1; 613 614 if (!config.mntPoint) 615 print_usage_and_exit(fuseArgs.argv[0]); 616 617 if (!modules[0]) { 618 fprintf(stderr, "Error: Couldn't find FS module!\n"); 619 return 1; 620 } 621 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; 627 } 628 629 const char* fsName = modules[0]->name; 630 return fssh_fuse_session(config.device, config.mntPoint, fsName, fuseArgs); 631 } 632 633