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 13 #include "fssh.h" 14 15 #include "driver_settings.h" 16 #include "external_commands.h" 17 #include "fd.h" 18 #include "fssh_dirent.h" 19 #include "fssh_errno.h" 20 #include "fssh_errors.h" 21 #include "fssh_fcntl.h" 22 #include "fssh_fs_info.h" 23 #include "fssh_module.h" 24 #include "fssh_node_monitor.h" 25 #include "fssh_stat.h" 26 #include "fssh_string.h" 27 #include "fssh_type_constants.h" 28 #include "module.h" 29 #include "syscalls.h" 30 #include "vfs.h" 31 32 33 extern fssh_module_info *modules[]; 34 35 extern fssh_file_system_module_info gRootFileSystem; 36 37 namespace FSShell { 38 39 const char* kMountPoint = "/myfs"; 40 41 static mode_t sUmask = 0022; 42 43 #define PRINTD(x) if (gIsDebug) fprintf(stderr, x) 44 45 bool gIsDebug = false; 46 47 static fssh_status_t 48 init_kernel() 49 { 50 fssh_status_t error; 51 52 // init module subsystem 53 error = module_init(NULL); 54 if (error != FSSH_B_OK) { 55 fprintf(stderr, "module_init() failed: %s\n", fssh_strerror(error)); 56 return error; 57 } 58 59 // init driver settings 60 error = driver_settings_init(); 61 if (error != FSSH_B_OK) { 62 fprintf(stderr, "initializing driver settings failed: %s\n", 63 fssh_strerror(error)); 64 return error; 65 } 66 67 // register built-in modules, i.e. the rootfs and the client FS 68 register_builtin_module(&gRootFileSystem.info); 69 for (int i = 0; modules[i]; i++) 70 register_builtin_module(modules[i]); 71 72 // init VFS 73 error = vfs_init(NULL); 74 if (error != FSSH_B_OK) { 75 fprintf(stderr, "initializing VFS failed: %s\n", fssh_strerror(error)); 76 return error; 77 } 78 79 // init kernel IO context 80 gKernelIOContext = (io_context*)vfs_new_io_context(NULL); 81 if (!gKernelIOContext) { 82 fprintf(stderr, "creating IO context failed!\n"); 83 return FSSH_B_NO_MEMORY; 84 } 85 86 // mount root FS 87 fssh_dev_t rootDev = _kern_mount("/", NULL, "rootfs", 0, NULL, 0); 88 if (rootDev < 0) { 89 fprintf(stderr, "mounting rootfs failed: %s\n", fssh_strerror(rootDev)); 90 return rootDev; 91 } 92 93 // set cwd to "/" 94 error = _kern_setcwd(-1, "/"); 95 if (error != FSSH_B_OK) { 96 fprintf(stderr, "setting cwd failed: %s\n", fssh_strerror(error)); 97 return error; 98 } 99 100 // create mount point for the client FS 101 error = _kern_create_dir(-1, kMountPoint, 0775); 102 if (error != FSSH_B_OK) { 103 fprintf(stderr, "creating mount point failed: %s\n", 104 fssh_strerror(error)); 105 return error; 106 } 107 108 return FSSH_B_OK; 109 } 110 111 112 static void 113 fromFsshStatToStat(struct fssh_stat* f_stbuf, struct stat* stbuf) 114 { 115 stbuf->st_dev = f_stbuf->fssh_st_dev; 116 stbuf->st_ino = f_stbuf->fssh_st_ino; 117 stbuf->st_mode = f_stbuf->fssh_st_mode; 118 stbuf->st_nlink = f_stbuf->fssh_st_nlink; 119 stbuf->st_uid = f_stbuf->fssh_st_uid; 120 stbuf->st_gid = f_stbuf->fssh_st_gid; 121 stbuf->st_rdev = f_stbuf->fssh_st_rdev; 122 stbuf->st_size = f_stbuf->fssh_st_size; 123 stbuf->st_blksize = f_stbuf->fssh_st_blksize; 124 stbuf->st_blocks = f_stbuf->fssh_st_blocks; 125 stbuf->st_atime = f_stbuf->fssh_st_atime; 126 stbuf->st_mtime = f_stbuf->fssh_st_mtime; 127 stbuf->st_ctime = f_stbuf->fssh_st_ctime; 128 } 129 130 #define _ERR(x) (-1 * fssh_to_host_error(x)) 131 132 133 // pragma mark - FUSE functions 134 135 136 int 137 fuse_getattr(const char* path, struct stat* stbuf) 138 { 139 PRINTD("##getattr\n"); 140 struct fssh_stat f_stbuf; 141 fssh_status_t status = _kern_read_stat(-1, path, false, &f_stbuf, 142 sizeof(f_stbuf)); 143 fromFsshStatToStat(&f_stbuf, stbuf); 144 if (gIsDebug) 145 printf("GETATTR returned: %d\n", status); 146 return _ERR(status); 147 } 148 149 150 static int 151 fuse_access(const char* path, int mask) 152 { 153 PRINTD("##access\n"); 154 return _ERR(_kern_access(path, mask)); 155 } 156 157 158 static int 159 fuse_readlink(const char* path, char* buffer, size_t size) 160 { 161 PRINTD("##readlink\n"); 162 fssh_size_t n_size = size - 1; 163 fssh_status_t st = _kern_read_link(-1, path, buffer, &n_size); 164 if (st >= FSSH_B_OK) 165 buffer[n_size] = '\0'; 166 return _ERR(st); 167 } 168 169 170 static int 171 fuse_readdir(const char* path, void* buf, fuse_fill_dir_t filler, 172 off_t offset, struct fuse_file_info* fi) 173 { 174 PRINTD("##readdir\n"); 175 int dfp = _kern_open_dir(-1, path); 176 if (dfp < FSSH_B_OK) 177 return _ERR(dfp); 178 179 fssh_ssize_t entriesRead = 0; 180 struct fssh_stat f_st; 181 struct stat st; 182 char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; 183 fssh_dirent* dirEntry = (fssh_dirent*)buffer; 184 while ((entriesRead = _kern_read_dir(dfp, dirEntry, 185 sizeof(buffer), 1)) == 1) { 186 fssh_memset(&st, 0, sizeof(st)); 187 fssh_memset(&f_st, 0, sizeof(f_st)); 188 fssh_status_t status = _kern_read_stat(dfp, dirEntry->d_name, 189 false, &f_st, sizeof(f_st)); 190 if (status >= FSSH_B_OK) { 191 fromFsshStatToStat(&f_st, &st); 192 if (filler(buf, dirEntry->d_name, &st, 0)) 193 break; 194 } 195 } 196 _kern_close(dfp); 197 //TODO: check _kern_close 198 return 0; 199 } 200 201 202 static int 203 fuse_mknod(const char* path, mode_t mode, dev_t rdev) 204 { 205 PRINTD("##mknod\n"); 206 if (S_ISREG(mode)) { 207 int fd = _kern_open(-1, path, 208 FSSH_O_CREAT | FSSH_O_EXCL | FSSH_O_WRONLY, mode); 209 if (fd >= FSSH_B_OK) 210 return _ERR(_kern_close(fd)); 211 return _ERR(fd); 212 } else if (S_ISFIFO(mode)) 213 return _ERR(FSSH_EINVAL); 214 else 215 return _ERR(FSSH_EINVAL); 216 } 217 218 219 static int 220 fuse_mkdir(const char* path, mode_t mode) 221 { 222 PRINTD("##mkdir\n"); 223 return _ERR(_kern_create_dir(-1, path, mode)); 224 } 225 226 227 static int 228 fuse_symlink(const char* from, const char* to) 229 { 230 PRINTD("##symlink\n"); 231 return _ERR(_kern_create_symlink(-1, to, from, 232 FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO)); 233 } 234 235 236 static int 237 fuse_unlink(const char* path) 238 { 239 PRINTD("##unlink\n"); 240 return _ERR(_kern_unlink(-1, path)); 241 } 242 243 244 static int 245 fuse_rmdir(const char* path) 246 { 247 PRINTD("##rmdir\n"); 248 return _ERR(_kern_remove_dir(-1, path)); 249 } 250 251 252 static int 253 fuse_rename(const char* from, const char* to) 254 { 255 PRINTD("##rename\n"); 256 return _ERR(_kern_rename(-1, from, -1, to)); 257 } 258 259 260 static int 261 fuse_link(const char* from, const char* to) 262 { 263 PRINTD("##link\n"); 264 return _ERR(_kern_create_link(to, from)); 265 } 266 267 268 static int 269 fuse_chmod(const char* path, mode_t mode) 270 { 271 PRINTD("##chmod\n"); 272 fssh_struct_stat st; 273 st.fssh_st_mode = mode; 274 return _ERR(_kern_write_stat(-1, path, false, &st, sizeof(st), 275 FSSH_B_STAT_MODE)); 276 } 277 278 279 static int 280 fuse_chown(const char* path, uid_t uid, gid_t gid) 281 { 282 PRINTD("##chown\n"); 283 fssh_struct_stat st; 284 st.fssh_st_uid = uid; 285 st.fssh_st_gid = gid; 286 return _ERR(_kern_write_stat(-1, path, false, &st, sizeof(st), 287 FSSH_B_STAT_UID|FSSH_B_STAT_GID)); 288 } 289 290 291 static int 292 fuse_open(const char* path, struct fuse_file_info* fi) 293 { 294 PRINTD("##open\n"); 295 // TODO: Do we have a syscall similar to the open syscall in linux which 296 // takes only two args: path and flags with no mask/perms? 297 int fd = _kern_open(-1, path, fi->flags, 298 (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask); 299 _kern_close(fd); 300 if (fd < FSSH_B_OK) 301 return _ERR(fd); 302 else 303 return 0; 304 } 305 306 307 static int 308 fuse_read(const char* path, char* buf, size_t size, off_t offset, 309 struct fuse_file_info* fi) 310 { 311 PRINTD("##read\n"); 312 int fd = _kern_open(-1, path, FSSH_O_RDONLY, 313 (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask); 314 if (fd < FSSH_B_OK) 315 return _ERR(fd); 316 317 int res = _kern_read(fd, offset, buf, size); 318 _kern_close(fd); 319 if (res < FSSH_B_OK) 320 res = _ERR(res); 321 return res; 322 } 323 324 325 static int 326 fuse_write(const char* path, const char* buf, size_t size, off_t offset, 327 struct fuse_file_info* fi) 328 { 329 PRINTD("##write\n"); 330 int fd = _kern_open(-1, path, FSSH_O_WRONLY, 331 (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask); 332 if (fd < FSSH_B_OK) 333 return _ERR(fd); 334 335 int res = _kern_write(fd, offset, buf, size); 336 _kern_close(fd); 337 if (res < FSSH_B_OK) 338 res = _ERR(res); 339 return res; 340 } 341 342 343 static void 344 fuse_destroy(void* priv_data) 345 { 346 _kern_sync(); 347 } 348 349 350 static fssh_dev_t 351 get_volume_id() 352 { 353 struct fssh_stat st; 354 fssh_status_t error = _kern_read_stat(-1, kMountPoint, false, &st, 355 sizeof(st)); 356 if (error != FSSH_B_OK) 357 return error; 358 return st.fssh_st_dev; 359 } 360 361 362 static int 363 fuse_statfs(const char *path __attribute__((unused)), 364 struct statvfs *sfs) 365 { 366 PRINTD("##statfs\n"); 367 368 fssh_dev_t volumeID = get_volume_id(); 369 if (volumeID < 0) 370 return _ERR(volumeID); 371 372 fssh_fs_info info; 373 fssh_status_t status = _kern_read_fs_info(volumeID, &info); 374 if (status != FSSH_B_OK) 375 return _ERR(status); 376 377 sfs->f_bsize = sfs->f_frsize = info.block_size; 378 sfs->f_blocks = info.total_blocks; 379 sfs->f_bavail = sfs->f_bfree = info.free_blocks; 380 381 return 0; 382 } 383 384 385 struct fuse_operations gFUSEOperations; 386 387 388 static void 389 initialiseFuseOps(struct fuse_operations* fuseOps) 390 { 391 fuseOps->getattr = fuse_getattr; 392 fuseOps->access = fuse_access; 393 fuseOps->readlink = fuse_readlink; 394 fuseOps->readdir = fuse_readdir; 395 fuseOps->mknod = fuse_mknod; 396 fuseOps->mkdir = fuse_mkdir; 397 fuseOps->symlink = fuse_symlink; 398 fuseOps->unlink = fuse_unlink; 399 fuseOps->rmdir = fuse_rmdir; 400 fuseOps->rename = fuse_rename; 401 fuseOps->link = fuse_link; 402 fuseOps->chmod = fuse_chmod; 403 fuseOps->chown = fuse_chown; 404 fuseOps->truncate = NULL; 405 fuseOps->utimens = NULL; 406 fuseOps->open = fuse_open; 407 fuseOps->read = fuse_read; 408 fuseOps->write = fuse_write; 409 fuseOps->statfs = fuse_statfs; 410 fuseOps->release = NULL; 411 fuseOps->fsync = NULL; 412 fuseOps->destroy = fuse_destroy; 413 } 414 415 416 static int 417 mount_volume(const char* device, const char* mntPoint, const char* fsName) 418 { 419 // Mount the volume in the root FS. 420 fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0); 421 if (fsDev < 0) { 422 fprintf(stderr, "Error: Mounting FS failed: %s\n", 423 fssh_strerror(fsDev)); 424 return 1; 425 } 426 427 if (!gIsDebug) { 428 bool isErr = false; 429 fssh_dev_t volumeID = get_volume_id(); 430 if (volumeID < 0) 431 isErr = true; 432 fssh_fs_info info; 433 if (!isErr) { 434 fssh_status_t status = _kern_read_fs_info(volumeID, &info); 435 if (status != FSSH_B_OK) 436 isErr = true; 437 } 438 syslog(LOG_INFO, "Mounted %s (%s) to %s", 439 device, 440 isErr ? "unknown" : info.volume_name, 441 mntPoint); 442 } 443 444 return 0; 445 } 446 447 448 static int 449 unmount_volume(const char* device, const char* mntPoint) 450 { 451 // Unmount the volume again. 452 // Avoid a "busy" vnode. 453 _kern_setcwd(-1, "/"); 454 fssh_status_t error = _kern_unmount(kMountPoint, 0); 455 if (error != FSSH_B_OK) { 456 if (gIsDebug) 457 fprintf(stderr, "Error: Unmounting FS failed: %s\n", 458 fssh_strerror(error)); 459 else 460 syslog(LOG_INFO, "Error: Unmounting FS failed: %s", 461 fssh_strerror(error)); 462 return 1; 463 } 464 465 if (!gIsDebug) 466 syslog(LOG_INFO, "UnMounted %s from %s", device, mntPoint); 467 468 return 0; 469 } 470 471 472 static int 473 fssh_fuse_session(const char* device, const char* mntPoint, const char* fsName, 474 struct fuse_args& fuseArgs) 475 { 476 int ret; 477 478 ret = mount_volume(device, mntPoint, fsName); 479 if (ret != 0) 480 return ret; 481 482 char* fuseOptions = NULL; 483 484 // default FUSE options 485 char* fsNameOption = NULL; 486 if (fuse_opt_add_opt(&fuseOptions, "allow_other") < 0 487 || asprintf(&fsNameOption, "fsname=%s", device) < 0 488 || fuse_opt_add_opt(&fuseOptions, fsNameOption) < 0) { 489 unmount_volume(device, mntPoint); 490 return 1; 491 } 492 493 struct stat sbuf; 494 if ((stat(device, &sbuf) == 0) && S_ISBLK(sbuf.st_mode)) { 495 int blkSize = 512; 496 fssh_dev_t volumeID = get_volume_id(); 497 if (volumeID >= 0) { 498 fssh_fs_info info; 499 if (_kern_read_fs_info(volumeID, &info) == FSSH_B_OK) 500 blkSize = info.block_size; 501 } 502 503 char* blkSizeOption = NULL; 504 if (fuse_opt_add_opt(&fuseOptions, "blkdev") < 0 505 || asprintf(&blkSizeOption, "blksize=%i", blkSize) < 0 506 || fuse_opt_add_opt(&fuseOptions, blkSizeOption) < 0) { 507 unmount_volume(device, mntPoint); 508 return 1; 509 } 510 } 511 512 // Run the fuse_main() loop. 513 if (fuse_opt_add_arg(&fuseArgs, "-s") < 0 514 || fuse_opt_add_arg(&fuseArgs, "-o") < 0 515 || fuse_opt_add_arg(&fuseArgs, fuseOptions) < 0) { 516 unmount_volume(device, mntPoint); 517 return 1; 518 } 519 520 initialiseFuseOps(&gFUSEOperations); 521 522 int res = fuse_main(fuseArgs.argc, fuseArgs.argv, &gFUSEOperations, NULL); 523 524 ret = unmount_volume(device, mntPoint); 525 if (ret != 0) 526 return ret; 527 528 return res; 529 } 530 531 532 } // namespace FSShell 533 534 535 using namespace FSShell; 536 537 538 static void 539 print_usage_and_exit(const char* binName) 540 { 541 fprintf(stderr,"Usage: %s [-d] <device> <mount point>\n", binName); 542 exit(1); 543 } 544 545 546 struct FsConfig { 547 const char* device; 548 const char* mntPoint; 549 }; 550 551 552 enum { 553 KEY_DEBUG, 554 KEY_HELP 555 }; 556 557 558 static int 559 process_options(void* data, const char* arg, int key, struct fuse_args* outArgs) 560 { 561 struct FsConfig* config = (FsConfig*) data; 562 563 switch (key) { 564 case FUSE_OPT_KEY_NONOPT: 565 if (!config->device) { 566 config->device = arg; 567 return 0; 568 // don't pass the device path to fuse_main() 569 } else if (!config->mntPoint) 570 config->mntPoint = arg; 571 else 572 print_usage_and_exit(outArgs->argv[0]); 573 break; 574 case KEY_DEBUG: 575 gIsDebug = true; 576 break; 577 case KEY_HELP: 578 print_usage_and_exit(outArgs->argv[0]); 579 } 580 581 return 1; 582 } 583 584 585 int 586 main(int argc, char* argv[]) 587 { 588 struct fuse_args fuseArgs = FUSE_ARGS_INIT(argc, argv); 589 struct FsConfig config; 590 memset(&config, 0, sizeof(config)); 591 const struct fuse_opt fsOptions[] = { 592 FUSE_OPT_KEY("uhelper=", FUSE_OPT_KEY_DISCARD), 593 // fuse_main() throws an error about this unknown option 594 // TODO: do not use fuse_main to mount filesystem, instead use 595 // fuse_mount, fuse_new, fuse_set_signal_handlers and fuse_loop 596 FUSE_OPT_KEY("-d", KEY_DEBUG), 597 FUSE_OPT_KEY("-h", KEY_HELP), 598 FUSE_OPT_KEY("--help", KEY_HELP), 599 FUSE_OPT_END 600 }; 601 602 if (fuse_opt_parse(&fuseArgs, &config, fsOptions, process_options) < 0) 603 return 1; 604 605 if (!config.mntPoint) 606 print_usage_and_exit(fuseArgs.argv[0]); 607 608 if (!modules[0]) { 609 fprintf(stderr, "Error: Couldn't find FS module!\n"); 610 return 1; 611 } 612 613 fssh_status_t error = init_kernel(); 614 if (error != FSSH_B_OK) { 615 fprintf(stderr, "Error: Initializing kernel failed: %s\n", 616 fssh_strerror(error)); 617 return error; 618 } 619 620 const char* fsName = modules[0]->name; 621 return fssh_fuse_session(config.device, config.mntPoint, fsName, fuseArgs); 622 } 623 624