1 /* 2 * Copyright 2007-2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "compatibility.h" 7 8 #include "fssh.h" 9 10 #include <stdarg.h> 11 #include <stdio.h> 12 #include <string.h> 13 #include <time.h> 14 #include <unistd.h> 15 #include <stdlib.h> 16 17 #include <vector> 18 19 #include "command_cp.h" 20 #include "driver_settings.h" 21 #include "external_commands.h" 22 #include "fd.h" 23 #include "fssh_dirent.h" 24 #include "fssh_errno.h" 25 #include "fssh_errors.h" 26 #include "fssh_fs_info.h" 27 #include "fssh_module.h" 28 #include "fssh_node_monitor.h" 29 #include "fssh_stat.h" 30 #include "fssh_string.h" 31 #include "fssh_type_constants.h" 32 #include "module.h" 33 #include "partition_support.h" 34 #include "path_util.h" 35 #include "syscalls.h" 36 #include "vfs.h" 37 38 39 extern fssh_module_info *modules[]; 40 41 42 extern fssh_file_system_module_info gRootFileSystem; 43 44 namespace FSShell { 45 46 const char* kMountPoint = "/myfs"; 47 48 // command line args 49 static int sArgc; 50 static const char* const* sArgv; 51 52 static mode_t sUmask = 0022; 53 54 55 static fssh_status_t 56 init_kernel() 57 { 58 fssh_status_t error; 59 60 // init module subsystem 61 error = module_init(NULL); 62 if (error != FSSH_B_OK) { 63 fprintf(stderr, "module_init() failed: %s\n", fssh_strerror(error)); 64 return error; 65 } 66 67 // init driver settings 68 error = driver_settings_init(); 69 if (error != FSSH_B_OK) { 70 fprintf(stderr, "initializing driver settings failed: %s\n", 71 fssh_strerror(error)); 72 return error; 73 } 74 75 // register built-in modules, i.e. the rootfs and the client FS 76 register_builtin_module(&gRootFileSystem.info); 77 for (int i = 0; modules[i]; i++) 78 register_builtin_module(modules[i]); 79 80 // init VFS 81 error = vfs_init(NULL); 82 if (error != FSSH_B_OK) { 83 fprintf(stderr, "initializing VFS failed: %s\n", fssh_strerror(error)); 84 return error; 85 } 86 87 // init kernel IO context 88 gKernelIOContext = (io_context*)vfs_new_io_context(NULL); 89 if (!gKernelIOContext) { 90 fprintf(stderr, "creating IO context failed!\n"); 91 return FSSH_B_NO_MEMORY; 92 } 93 94 // mount root FS 95 fssh_dev_t rootDev = _kern_mount("/", NULL, "rootfs", 0, NULL, 0); 96 if (rootDev < 0) { 97 fprintf(stderr, "mounting rootfs failed: %s\n", fssh_strerror(rootDev)); 98 return rootDev; 99 } 100 101 // set cwd to "/" 102 error = _kern_setcwd(-1, "/"); 103 if (error != FSSH_B_OK) { 104 fprintf(stderr, "setting cwd failed: %s\n", fssh_strerror(error)); 105 return error; 106 } 107 108 // create mount point for the client FS 109 error = _kern_create_dir(-1, kMountPoint, 0775); 110 if (error != FSSH_B_OK) { 111 fprintf(stderr, "creating mount point failed: %s\n", 112 fssh_strerror(error)); 113 return error; 114 } 115 116 return FSSH_B_OK; 117 } 118 119 120 // #pragma mark - Command 121 122 Command::Command(const char* name, const char* description) 123 : fName(name), 124 fDescription(description) 125 { 126 } 127 128 129 Command::Command(command_function* function, const char* name, 130 const char* description) 131 : fName(name), 132 fDescription(description), 133 fFunction(function) 134 { 135 } 136 137 138 Command::~Command() 139 { 140 } 141 142 143 const char* 144 Command::Name() const 145 { 146 return fName.c_str(); 147 } 148 149 150 const char* 151 Command::Description() const 152 { 153 return fDescription.c_str(); 154 } 155 156 157 fssh_status_t 158 Command::Do(int argc, const char* const* argv) 159 { 160 if (!fFunction) { 161 fprintf(stderr, "No function given for command \"%s\"\n", Name()); 162 return FSSH_B_BAD_VALUE; 163 } 164 165 return (*fFunction)(argc, argv); 166 } 167 168 169 // #pragma mark - CommandManager 170 171 CommandManager::CommandManager() 172 { 173 } 174 175 176 CommandManager* 177 CommandManager::Default() 178 { 179 if (!sManager) 180 sManager = new CommandManager; 181 return sManager; 182 } 183 184 185 void 186 CommandManager::AddCommand(Command* command) 187 { 188 // The command name may consist of several aliases. Split them and 189 // register the command for each of them. 190 char _names[1024]; 191 char* names = _names; 192 strcpy(names, command->Name()); 193 194 char* cookie; 195 while (char* name = strtok_r(names, " /", &cookie)) { 196 fCommands[name] = command; 197 names = NULL; 198 } 199 } 200 201 202 void 203 CommandManager::AddCommand(command_function* function, const char* name, 204 const char* description) 205 { 206 AddCommand(new Command(function, name, description)); 207 } 208 209 210 void 211 CommandManager::AddCommands(command_function* function, const char* name, 212 const char* description, ...) 213 { 214 va_list args; 215 va_start(args, description); 216 217 while (function) { 218 AddCommand(function, name, description); 219 220 function = va_arg(args, command_function*); 221 if (function) { 222 name = va_arg(args, const char*); 223 description = va_arg(args, const char*); 224 } 225 } 226 227 va_end(args); 228 } 229 230 231 Command* 232 CommandManager::FindCommand(const char* name) const 233 { 234 CommandMap::const_iterator it = fCommands.find(name); 235 if (it == fCommands.end()) 236 return NULL; 237 238 return it->second; 239 } 240 241 242 void 243 CommandManager::ListCommands() const 244 { 245 for (CommandMap::const_iterator it = fCommands.begin(); 246 it != fCommands.end(); ++it) { 247 const char* name = it->first.c_str(); 248 Command* command = it->second; 249 printf("%-16s - %s\n", name, command->Description()); 250 } 251 } 252 253 254 CommandManager* CommandManager::sManager = NULL; 255 256 257 // #pragma mark - Command support functions 258 259 260 static bool 261 get_permissions(const char* modeString, fssh_mode_t& _permissions) 262 { 263 // currently only octal mode is supported 264 if (strlen(modeString) != 3) 265 return false; 266 267 fssh_mode_t permissions = 0; 268 for (int i = 0; i < 3; i++) { 269 char c = modeString[i]; 270 if (c < '0' || c > '7') 271 return false; 272 permissions = (permissions << 3) | (c - '0'); 273 } 274 275 _permissions = permissions; 276 return true; 277 } 278 279 280 static fssh_dev_t 281 get_volume_id() 282 { 283 struct fssh_stat st; 284 fssh_status_t error = _kern_read_stat(-1, kMountPoint, false, &st, 285 sizeof(st)); 286 if (error != FSSH_B_OK) { 287 fprintf(stderr, "Error: Failed to stat() mount point: %s\n", 288 fssh_strerror(error)); 289 return error; 290 } 291 292 return st.fssh_st_dev; 293 } 294 295 296 static const char * 297 byte_string(int64_t numBlocks, int64_t blockSize) 298 { 299 double blocks = 1. * numBlocks * blockSize; 300 static char string[64]; 301 302 if (blocks < 1024) 303 sprintf(string, "%" FSSH_B_PRId64, numBlocks * blockSize); 304 else { 305 const char* units[] = {"K", "M", "G", NULL}; 306 int i = -1; 307 308 do { 309 blocks /= 1024.0; 310 i++; 311 } while (blocks >= 1024 && units[i + 1]); 312 313 sprintf(string, "%.1f%s", blocks, units[i]); 314 } 315 316 return string; 317 } 318 319 320 void 321 print_flag(uint32_t deviceFlags, uint32_t testFlag, const char *yes, 322 const char *no) 323 { 324 printf("%s", (deviceFlags & testFlag) != 0 ? yes : no); 325 } 326 327 328 static void 329 list_entry(const char* file, const char* name = NULL) 330 { 331 // construct path, if a leaf name is given 332 std::string path; 333 if (name) { 334 path = file; 335 path += '/'; 336 path += name; 337 file = path.c_str(); 338 } else 339 name = file; 340 341 // stat the file 342 struct fssh_stat st; 343 fssh_status_t error = _kern_read_stat(-1, file, false, &st, sizeof(st)); 344 if (error != FSSH_B_OK) { 345 fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", file, 346 fssh_strerror(error)); 347 return; 348 } 349 350 // get time 351 struct tm time; 352 time_t fileTime = st.fssh_st_mtime; 353 localtime_r(&fileTime, &time); 354 355 // get permissions 356 std::string permissions; 357 fssh_mode_t mode = st.fssh_st_mode; 358 // user 359 permissions += ((mode & FSSH_S_IRUSR) ? 'r' : '-'); 360 permissions += ((mode & FSSH_S_IWUSR) ? 'w' : '-'); 361 if (mode & FSSH_S_ISUID) 362 permissions += 's'; 363 else 364 permissions += ((mode & FSSH_S_IXUSR) ? 'x' : '-'); 365 // group 366 permissions += ((mode & FSSH_S_IRGRP) ? 'r' : '-'); 367 permissions += ((mode & FSSH_S_IWGRP) ? 'w' : '-'); 368 if (mode & FSSH_S_ISGID) 369 permissions += 's'; 370 else 371 permissions += ((mode & FSSH_S_IXGRP) ? 'x' : '-'); 372 // others 373 permissions += ((mode & FSSH_S_IROTH) ? 'r' : '-'); 374 permissions += ((mode & FSSH_S_IWOTH) ? 'w' : '-'); 375 permissions += ((mode & FSSH_S_IXOTH) ? 'x' : '-'); 376 377 // get file type 378 char fileType = '?'; 379 if (FSSH_S_ISREG(mode)) { 380 fileType = '-'; 381 } else if (FSSH_S_ISLNK(mode)) { 382 fileType = 'l'; 383 } else if (FSSH_S_ISBLK(mode)) { 384 fileType = 'b'; 385 } else if (FSSH_S_ISDIR(mode)) { 386 fileType = 'd'; 387 } else if (FSSH_S_ISCHR(mode)) { 388 fileType = 'c'; 389 } else if (FSSH_S_ISFIFO(mode)) { 390 fileType = 'f'; 391 } else if (FSSH_S_ISINDEX(mode)) { 392 fileType = 'i'; 393 } 394 395 // get link target 396 std::string nameSuffix; 397 if (FSSH_S_ISLNK(mode)) { 398 char buffer[FSSH_B_PATH_NAME_LENGTH]; 399 fssh_size_t size = sizeof(buffer) - 1; 400 error = _kern_read_link(-1, file, buffer, &size); 401 if (error != FSSH_B_OK) 402 snprintf(buffer, sizeof(buffer), "(%s)", fssh_strerror(error)); 403 404 buffer[size] = '\0'; 405 nameSuffix += " -> "; 406 nameSuffix += buffer; 407 } 408 409 printf("%c%s %2d %2d %10" FSSH_B_PRIdOFF 410 " %d-%02d-%02d %02d:%02d:%02d %s%s\n", 411 fileType, permissions.c_str(), (int)st.fssh_st_uid, (int)st.fssh_st_gid, 412 st.fssh_st_size, 413 1900 + time.tm_year, 1 + time.tm_mon, time.tm_mday, 414 time.tm_hour, time.tm_min, time.tm_sec, 415 name, nameSuffix.c_str()); 416 } 417 418 419 static fssh_status_t 420 create_dir(const char *path, bool createParents) 421 { 422 // stat the entry 423 struct fssh_stat st; 424 fssh_status_t error = _kern_read_stat(-1, path, false, &st, sizeof(st)); 425 if (error == FSSH_B_OK) { 426 if (createParents && FSSH_S_ISDIR(st.fssh_st_mode)) 427 return FSSH_B_OK; 428 429 fprintf(stderr, "Error: Cannot make dir, entry \"%s\" is in the way.\n", 430 path); 431 return FSSH_B_FILE_EXISTS; 432 } 433 434 // the dir doesn't exist yet 435 // if we shall create all parents, do that first 436 if (createParents) { 437 // create the parent dir path 438 // eat the trailing '/'s 439 int len = strlen(path); 440 while (len > 0 && path[len - 1] == '/') 441 len--; 442 443 // eat the last path component 444 while (len > 0 && path[len - 1] != '/') 445 len--; 446 447 // eat the trailing '/'s 448 while (len > 0 && path[len - 1] == '/') 449 len--; 450 451 // Now either nothing remains, which means we had a single component, 452 // a root subdir -- in those cases we can just fall through (we should 453 // actually never be here in case of the root dir, but anyway) -- or 454 // there is something left, which we can call a parent directory and 455 // try to create it. 456 if (len > 0) { 457 char *parentPath = (char*)malloc(len + 1); 458 if (!parentPath) { 459 fprintf(stderr, "Error: Failed to allocate memory for parent " 460 "path.\n"); 461 return FSSH_B_NO_MEMORY; 462 } 463 memcpy(parentPath, path, len); 464 parentPath[len] = '\0'; 465 466 error = create_dir(parentPath, createParents); 467 468 free(parentPath); 469 470 if (error != FSSH_B_OK) 471 return error; 472 } 473 } 474 475 // make the directory 476 error = _kern_create_dir(-1, 477 path, (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask); 478 if (error != FSSH_B_OK) { 479 fprintf(stderr, "Error: Failed to make directory \"%s\": %s\n", path, 480 fssh_strerror(error)); 481 return error; 482 } 483 484 return FSSH_B_OK; 485 } 486 487 488 static fssh_status_t remove_entry(int dir, const char *entry, bool recursive, 489 bool force); 490 491 492 static fssh_status_t 493 remove_dir_contents(int parentDir, const char *name, bool force) 494 { 495 // open the dir 496 int dir = _kern_open_dir(parentDir, name); 497 if (dir < 0) { 498 fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", name, 499 fssh_strerror(dir)); 500 return dir; 501 } 502 503 fssh_status_t error = FSSH_B_OK; 504 505 // iterate through the entries 506 fssh_ssize_t numRead; 507 char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; 508 fssh_dirent *entry = (fssh_dirent*)buffer; 509 while ((numRead = _kern_read_dir(dir, entry, sizeof(buffer), 1)) > 0) { 510 // skip "." and ".." 511 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) 512 continue; 513 514 error = remove_entry(dir, entry->d_name, true, force); 515 if (error != FSSH_B_OK) 516 break; 517 } 518 519 if (numRead < 0) { 520 fprintf(stderr, "Error: Failed to read directory \"%s\": %s\n", name, 521 fssh_strerror(numRead)); 522 error = numRead; 523 } 524 525 // close 526 _kern_close(dir); 527 528 return error; 529 } 530 531 532 static fssh_status_t 533 remove_entry(int dir, const char *entry, bool recursive, bool force) 534 { 535 // stat the file 536 struct fssh_stat st; 537 fssh_status_t error = _kern_read_stat(dir, entry, false, &st, sizeof(st)); 538 if (error != FSSH_B_OK) { 539 if (force && error == FSSH_B_ENTRY_NOT_FOUND) 540 return FSSH_B_OK; 541 542 fprintf(stderr, "Error: Failed to remove \"%s\": %s\n", entry, 543 fssh_strerror(error)); 544 return error; 545 } 546 547 if (FSSH_S_ISDIR(st.fssh_st_mode)) { 548 if (!recursive) { 549 fprintf(stderr, "Error: \"%s\" is a directory.\n", entry); 550 // TODO: get the full path 551 return FSSH_EISDIR; 552 } 553 554 // remove the contents 555 error = remove_dir_contents(dir, entry, force); 556 if (error != FSSH_B_OK) 557 return error; 558 559 // remove the directory 560 error = _kern_remove_dir(dir, entry); 561 if (error != FSSH_B_OK) { 562 fprintf(stderr, "Error: Failed to remove directory \"%s\": %s\n", 563 entry, fssh_strerror(error)); 564 return error; 565 } 566 } else { 567 // remove the entry 568 error = _kern_unlink(dir, entry); 569 if (error != FSSH_B_OK) { 570 fprintf(stderr, "Error: Failed to remove entry \"%s\": %s\n", entry, 571 fssh_strerror(error)); 572 return error; 573 } 574 } 575 576 return FSSH_B_OK; 577 } 578 579 580 static fssh_status_t 581 move_entry(int dir, const char *entry, int targetDir, const char* target, 582 bool force) 583 { 584 // stat the file 585 struct fssh_stat st; 586 fssh_status_t status = _kern_read_stat(dir, entry, false, &st, sizeof(st)); 587 if (status != FSSH_B_OK) { 588 if (force && status == FSSH_B_ENTRY_NOT_FOUND) 589 return FSSH_B_OK; 590 591 fprintf(stderr, "Error: Failed to move \"%s\": %s\n", entry, 592 fssh_strerror(status)); 593 return status; 594 } 595 596 return _kern_rename(dir, entry, targetDir, target); 597 } 598 599 600 // #pragma mark - Commands 601 602 603 static fssh_status_t 604 command_cd(int argc, const char* const* argv) 605 { 606 if (argc != 2) { 607 fprintf(stderr, "Usage: %s <directory>\n", argv[0]); 608 return FSSH_B_BAD_VALUE; 609 } 610 const char* directory = argv[1]; 611 612 fssh_status_t error = FSSH_B_OK; 613 if (directory[0] == ':') { 614 if (chdir(directory + 1) < 0) 615 error = fssh_get_errno(); 616 } else 617 error = _kern_setcwd(-1, directory); 618 619 if (error != FSSH_B_OK) { 620 fprintf(stderr, "Error: cd %s: %s\n", directory, fssh_strerror(error)); 621 return error; 622 } 623 624 return FSSH_B_OK; 625 } 626 627 628 static fssh_status_t 629 command_chmod(int argc, const char* const* argv) 630 { 631 bool recursive = false; 632 633 // parse parameters 634 int argi = 1; 635 for (argi = 1; argi < argc; argi++) { 636 const char *arg = argv[argi]; 637 if (arg[0] != '-') 638 break; 639 640 if (arg[1] == '\0') { 641 fprintf(stderr, "Error: Invalid option \"-\"\n"); 642 return FSSH_B_BAD_VALUE; 643 } 644 645 for (int i = 1; arg[i]; i++) { 646 switch (arg[i]) { 647 case 'R': 648 recursive = true; 649 fprintf(stderr, "Sorry, recursive mode not supported " 650 "yet.\n"); 651 return FSSH_B_BAD_VALUE; 652 default: 653 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 654 return FSSH_B_BAD_VALUE; 655 } 656 } 657 } 658 659 // get mode 660 fssh_mode_t permissions; 661 if (argi + 1 >= argc || !get_permissions(argv[argi++], permissions)) { 662 printf("Usage: %s [ -R ] <octal mode> <file>...\n", argv[0]); 663 return FSSH_B_BAD_VALUE; 664 } 665 666 fssh_struct_stat st; 667 st.fssh_st_mode = permissions; 668 669 // chmod loop 670 for (; argi < argc; argi++) { 671 const char *file = argv[argi]; 672 if (strlen(file) == 0) { 673 fprintf(stderr, "Error: An empty path is not a valid argument!\n"); 674 return FSSH_B_BAD_VALUE; 675 } 676 677 fssh_status_t error = _kern_write_stat(-1, file, false, &st, sizeof(st), 678 FSSH_B_STAT_MODE); 679 if (error != FSSH_B_OK) { 680 fprintf(stderr, "Error: Failed to change mode of \"%s\"!\n", file); 681 return error; 682 } 683 } 684 685 return FSSH_B_OK; 686 } 687 688 689 static fssh_status_t 690 command_help(int argc, const char* const* argv) 691 { 692 printf("supported commands:\n"); 693 CommandManager::Default()->ListCommands(); 694 return FSSH_B_OK; 695 } 696 697 698 static fssh_status_t 699 command_info(int argc, const char* const* argv) 700 { 701 fssh_dev_t volumeID = get_volume_id(); 702 if (volumeID < 0) 703 return volumeID; 704 705 fssh_fs_info info; 706 fssh_status_t status = _kern_read_fs_info(volumeID, &info); 707 if (status != FSSH_B_OK) 708 return status; 709 710 printf("root inode: %" FSSH_B_PRIdINO "\n", info.root); 711 printf("flags: "); 712 print_flag(info.flags, FSSH_B_FS_HAS_QUERY, "Q", "-"); 713 print_flag(info.flags, FSSH_B_FS_HAS_ATTR, "A", "-"); 714 print_flag(info.flags, FSSH_B_FS_HAS_MIME, "M", "-"); 715 print_flag(info.flags, FSSH_B_FS_IS_SHARED, "S", "-"); 716 print_flag(info.flags, FSSH_B_FS_IS_PERSISTENT, "P", "-"); 717 print_flag(info.flags, FSSH_B_FS_IS_REMOVABLE, "R", "-"); 718 print_flag(info.flags, FSSH_B_FS_IS_READONLY, "-", "W"); 719 720 printf("\nblock size: %" FSSH_B_PRIdOFF "\n", info.block_size); 721 printf("I/O size: %" FSSH_B_PRIdOFF "\n", info.io_size); 722 printf("total size: %s (%" FSSH_B_PRIdOFF " blocks)\n", 723 byte_string(info.total_blocks, info.block_size), info.total_blocks); 724 printf("free size: %s (%" FSSH_B_PRIdOFF " blocks)\n", 725 byte_string(info.free_blocks, info.block_size), info.free_blocks); 726 printf("total nodes: %" FSSH_B_PRIdOFF "\n", info.total_nodes); 727 printf("free nodes: %" FSSH_B_PRIdOFF "\n", info.free_nodes); 728 printf("volume name: %s\n", info.volume_name); 729 printf("fs name: %s\n", info.fsh_name); 730 731 return FSSH_B_OK; 732 } 733 734 735 static fssh_status_t 736 command_ln(int argc, const char* const* argv) 737 { 738 bool force = false; 739 bool symbolic = false; 740 bool dereference = true; 741 742 // parse parameters 743 int argi = 1; 744 for (argi = 1; argi < argc; argi++) { 745 const char *arg = argv[argi]; 746 if (arg[0] != '-') 747 break; 748 749 if (arg[1] == '\0') { 750 fprintf(stderr, "Error: Invalid option \"-\"\n"); 751 return FSSH_B_BAD_VALUE; 752 } 753 754 for (int i = 1; arg[i]; i++) { 755 switch (arg[i]) { 756 case 'f': 757 force = true; 758 break; 759 case 's': 760 symbolic = true; 761 break; 762 case 'n': 763 dereference = false; 764 break; 765 default: 766 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 767 return FSSH_B_BAD_VALUE; 768 } 769 } 770 } 771 772 if (argc - argi != 2) { 773 fprintf(stderr, "Usage: %s [Options] <source> <target>\n", argv[0]); 774 return FSSH_B_BAD_VALUE; 775 } 776 777 const char *source = argv[argi]; 778 const char *target = argv[argi + 1]; 779 780 // check, if the the target is an existing directory 781 struct fssh_stat st; 782 char targetBuffer[FSSH_B_PATH_NAME_LENGTH]; 783 fssh_status_t error = _kern_read_stat(-1, target, dereference, &st, 784 sizeof(st)); 785 if (error == FSSH_B_OK) { 786 if (FSSH_S_ISDIR(st.fssh_st_mode)) { 787 // get source leaf 788 char leaf[FSSH_B_FILE_NAME_LENGTH]; 789 error = get_last_path_component(source, leaf, sizeof(leaf)); 790 if (error != FSSH_B_OK) { 791 fprintf(stderr, "Error: Failed to get leaf name of source " 792 "path: %s\n", fssh_strerror(error)); 793 return error; 794 } 795 796 // compose a new path 797 int len = strlen(target) + 1 + strlen(leaf); 798 if (len > (int)sizeof(targetBuffer)) { 799 fprintf(stderr, "Error: Resulting target path is too long.\n"); 800 return FSSH_B_BAD_VALUE; 801 } 802 803 strcpy(targetBuffer, target); 804 strcat(targetBuffer, "/"); 805 strcat(targetBuffer, leaf); 806 target = targetBuffer; 807 } 808 } 809 810 // check, if the target exists 811 error = _kern_read_stat(-1, target, false, &st, sizeof(st)); 812 if (error == FSSH_B_OK) { 813 if (!force) { 814 fprintf(stderr, "Error: Can't create link. \"%s\" is in the way.\n", 815 target); 816 return FSSH_B_FILE_EXISTS; 817 } 818 819 // unlink the entry 820 error = _kern_unlink(-1, target); 821 if (error != FSSH_B_OK) { 822 fprintf(stderr, "Error: Failed to remove \"%s\" to make way for " 823 "link: %s\n", target, fssh_strerror(error)); 824 return error; 825 } 826 } 827 828 // finally create the link 829 if (symbolic) { 830 error = _kern_create_symlink(-1, target, source, 831 FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO); 832 } else 833 error = _kern_create_link(target, source); 834 835 if (error != FSSH_B_OK) { 836 fprintf(stderr, "Error: Failed to create link: %s\n", 837 fssh_strerror(error)); 838 } 839 840 return error; 841 } 842 843 844 static fssh_status_t 845 command_ls(int argc, const char* const* argv) 846 { 847 const char* const currentDirFiles[] = { ".", NULL }; 848 const char* const* files; 849 if (argc >= 2) 850 files = argv + 1; 851 else 852 files = currentDirFiles; 853 854 for (; *files; files++) { 855 const char* file = *files; 856 // stat file 857 struct fssh_stat st; 858 fssh_status_t error = _kern_read_stat(-1, file, false, &st, sizeof(st)); 859 if (error != FSSH_B_OK) { 860 fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", file, 861 fssh_strerror(error)); 862 continue; 863 } 864 865 // if it is a directory, print its entries 866 if (FSSH_S_ISDIR(st.fssh_st_mode)) { 867 printf("%s:\n", file); 868 869 // open dir 870 int fd = _kern_open_dir(-1, file); 871 if (fd < 0) { 872 fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", 873 file, fssh_strerror(fd)); 874 continue; 875 } 876 877 // iterate through the entries 878 char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; 879 fssh_dirent* entry = (fssh_dirent*)buffer; 880 fssh_ssize_t entriesRead = 0; 881 while ((entriesRead = _kern_read_dir(fd, entry, sizeof(buffer), 1)) 882 == 1) { 883 list_entry(file, entry->d_name); 884 } 885 886 if (entriesRead < 0) { 887 fprintf(stderr, "Error: reading dir \"%s\" failed: %s\n", 888 file, fssh_strerror(entriesRead)); 889 } 890 891 // close dir 892 error = _kern_close(fd); 893 if (error != FSSH_B_OK) { 894 fprintf(stderr, "Error: Closing dir \"%s\" (fd: %d) failed: " 895 "%s\n", file, fd, fssh_strerror(error)); 896 continue; 897 } 898 } else 899 list_entry(file); 900 } 901 902 return FSSH_B_OK; 903 } 904 905 906 static fssh_status_t 907 command_mkdir(int argc, const char* const* argv) 908 { 909 bool createParents = false; 910 911 // parse parameters 912 int argi = 1; 913 for (argi = 1; argi < argc; argi++) { 914 const char *arg = argv[argi]; 915 if (arg[0] != '-') 916 break; 917 918 if (arg[1] == '\0') { 919 fprintf(stderr, "Error: Invalid option \"-\"\n"); 920 return FSSH_B_BAD_VALUE; 921 } 922 923 for (int i = 1; arg[i]; i++) { 924 switch (arg[i]) { 925 case 'p': 926 createParents = true; 927 break; 928 default: 929 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 930 return FSSH_B_BAD_VALUE; 931 } 932 } 933 } 934 935 if (argi >= argc) { 936 printf("Usage: %s [ -p ] <dir>...\n", argv[0]); 937 return FSSH_B_BAD_VALUE; 938 } 939 940 // create loop 941 for (; argi < argc; argi++) { 942 const char *dir = argv[argi]; 943 if (strlen(dir) == 0) { 944 fprintf(stderr, "Error: An empty path is not a valid argument!\n"); 945 return FSSH_B_BAD_VALUE; 946 } 947 948 fssh_status_t error = create_dir(dir, createParents); 949 if (error != FSSH_B_OK) 950 return error; 951 } 952 953 return FSSH_B_OK; 954 } 955 956 957 static fssh_status_t 958 command_mkindex(int argc, const char* const* argv) 959 { 960 if (argc != 2) { 961 fprintf(stderr, "Usage: %s <index name>\n", argv[0]); 962 return FSSH_B_BAD_VALUE; 963 } 964 965 const char* indexName = argv[1]; 966 967 // get the volume ID 968 fssh_dev_t volumeID = get_volume_id(); 969 if (volumeID < 0) 970 return volumeID; 971 972 // create the index 973 fssh_status_t error =_kern_create_index(volumeID, indexName, 974 FSSH_B_STRING_TYPE, 0); 975 if (error != FSSH_B_OK) { 976 fprintf(stderr, "Error: Failed to create index \"%s\": %s\n", 977 indexName, fssh_strerror(error)); 978 return error; 979 } 980 981 return FSSH_B_OK; 982 } 983 984 985 static fssh_status_t 986 command_mv(int argc, const char* const* argv) 987 { 988 bool force = false; 989 990 // parse parameters 991 int argi = 1; 992 for (argi = 1; argi < argc; argi++) { 993 const char *arg = argv[argi]; 994 if (arg[0] != '-') 995 break; 996 997 if (arg[1] == '\0') { 998 fprintf(stderr, "Error: Invalid option \"-\"\n"); 999 return FSSH_B_BAD_VALUE; 1000 } 1001 1002 for (int i = 1; arg[i]; i++) { 1003 switch (arg[i]) { 1004 case 'f': 1005 force = true; 1006 break; 1007 default: 1008 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 1009 return FSSH_B_BAD_VALUE; 1010 } 1011 } 1012 } 1013 1014 // check params 1015 int count = argc - 1 - argi; 1016 if (count <= 0) { 1017 fprintf(stderr, "Usage: %s [-f] <file>... <target>\n", argv[0]); 1018 return FSSH_B_BAD_VALUE; 1019 } 1020 1021 const char* target = argv[argc - 1]; 1022 1023 // stat the target 1024 struct fssh_stat st; 1025 fssh_status_t status = _kern_read_stat(-1, target, true, &st, sizeof(st)); 1026 if (status != FSSH_B_OK && count != 1) { 1027 fprintf(stderr, "Error: Failed to stat target \"%s\": %s\n", target, 1028 fssh_strerror(status)); 1029 return status; 1030 } 1031 1032 if (status == FSSH_B_OK && FSSH_S_ISDIR(st.fssh_st_mode)) { 1033 // move several entries 1034 int targetDir = _kern_open_dir(-1, target); 1035 if (targetDir < 0) { 1036 fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", target, 1037 fssh_strerror(targetDir)); 1038 return targetDir; 1039 } 1040 1041 // move loop 1042 for (; argi < argc - 1; argi++) { 1043 status = move_entry(-1, argv[argi], targetDir, argv[argi], force); 1044 if (status != FSSH_B_OK) { 1045 _kern_close(targetDir); 1046 return status; 1047 } 1048 } 1049 1050 _kern_close(targetDir); 1051 return FSSH_B_OK; 1052 } 1053 1054 // rename single entry 1055 return move_entry(-1, argv[argi], -1, target, force); 1056 } 1057 1058 1059 static fssh_status_t 1060 command_query(int argc, const char* const* argv) 1061 { 1062 if (argc != 2) { 1063 fprintf(stderr, "Usage: %s <query string>\n", argv[0]); 1064 return FSSH_B_BAD_VALUE; 1065 } 1066 1067 const char* query = argv[1]; 1068 1069 // get the volume ID 1070 fssh_dev_t volumeID = get_volume_id(); 1071 if (volumeID < 0) 1072 return volumeID; 1073 1074 // open query 1075 int fd = _kern_open_query(volumeID, query, strlen(query), 0, -1, -1); 1076 if (fd < 0) { 1077 fprintf(stderr, "Error: Failed to open query: %s\n", fssh_strerror(fd)); 1078 return fd; 1079 } 1080 1081 // iterate through the entries 1082 fssh_status_t error = FSSH_B_OK; 1083 char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; 1084 fssh_dirent* entry = (fssh_dirent*)buffer; 1085 fssh_ssize_t entriesRead = 0; 1086 while ((entriesRead = _kern_read_dir(fd, entry, sizeof(buffer), 1)) == 1) { 1087 char path[FSSH_B_PATH_NAME_LENGTH]; 1088 error = _kern_entry_ref_to_path(volumeID, entry->d_pino, entry->d_name, 1089 path, sizeof(path)); 1090 if (error == FSSH_B_OK) { 1091 printf(" %s\n", path); 1092 } else { 1093 fprintf(stderr, " failed to resolve entry (%8" FSSH_B_PRIdINO 1094 ", \"%s\")\n", entry->d_pino, entry->d_name); 1095 } 1096 } 1097 1098 if (entriesRead < 0) { 1099 fprintf(stderr, "Error: reading query failed: %s\n", 1100 fssh_strerror(entriesRead)); 1101 } 1102 1103 // close query 1104 error = _kern_close(fd); 1105 if (error != FSSH_B_OK) { 1106 fprintf(stderr, "Error: Closing query (fd: %d) failed: %s\n", 1107 fd, fssh_strerror(error)); 1108 } 1109 1110 return error; 1111 } 1112 1113 1114 static fssh_status_t 1115 command_quit(int argc, const char* const* argv) 1116 { 1117 return COMMAND_RESULT_EXIT; 1118 } 1119 1120 1121 static fssh_status_t 1122 command_rm(int argc, const char* const* argv) 1123 { 1124 bool recursive = false; 1125 bool force = false; 1126 1127 // parse parameters 1128 int argi = 1; 1129 for (argi = 1; argi < argc; argi++) { 1130 const char *arg = argv[argi]; 1131 if (arg[0] != '-') 1132 break; 1133 1134 if (arg[1] == '\0') { 1135 fprintf(stderr, "Error: Invalid option \"-\"\n"); 1136 return FSSH_B_BAD_VALUE; 1137 } 1138 1139 for (int i = 1; arg[i]; i++) { 1140 switch (arg[i]) { 1141 case 'f': 1142 force = true; 1143 break; 1144 case 'r': 1145 recursive = true; 1146 break; 1147 default: 1148 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 1149 return FSSH_B_BAD_VALUE; 1150 } 1151 } 1152 } 1153 1154 // check params 1155 if (argi >= argc) { 1156 fprintf(stderr, "Usage: %s [ -r ] <file>...\n", argv[0]); 1157 return FSSH_B_BAD_VALUE; 1158 } 1159 1160 // remove loop 1161 for (; argi < argc; argi++) { 1162 fssh_status_t error = remove_entry(-1, argv[argi], recursive, force); 1163 if (error != FSSH_B_OK) 1164 return error; 1165 } 1166 1167 return FSSH_B_OK; 1168 } 1169 1170 1171 static fssh_status_t 1172 command_sync(int argc, const char* const* argv) 1173 { 1174 fssh_status_t error = _kern_sync(); 1175 if (error != FSSH_B_OK) { 1176 fprintf(stderr, "Error: syncing: %s\n", fssh_strerror(error)); 1177 return error; 1178 } 1179 1180 return FSSH_B_OK; 1181 } 1182 1183 1184 static fssh_status_t 1185 command_ioctl(int argc, const char* const* argv) 1186 { 1187 if (argc != 2) { 1188 fprintf(stderr, "Usage: %s <opcode>\n", argv[0]); 1189 return FSSH_B_BAD_VALUE; 1190 } 1191 1192 int rootDir = _kern_open_dir(-1, "/myfs"); 1193 if (rootDir < 0) 1194 return rootDir; 1195 1196 fssh_status_t status = _kern_ioctl(rootDir, atoi(argv[1]), NULL, 0); 1197 if (status != FSSH_B_OK) { 1198 fprintf(stderr, "Error: ioctl failed: %s\n", fssh_strerror(status)); 1199 return status; 1200 } 1201 1202 return FSSH_B_OK; 1203 } 1204 1205 1206 static void 1207 register_commands() 1208 { 1209 CommandManager::Default()->AddCommands( 1210 command_cd, "cd", "change current directory", 1211 command_chmod, "chmod", "change file permissions", 1212 command_cp, "cp", "copy files and directories", 1213 command_help, "help", "list supported commands", 1214 command_info, "info", "prints volume informations", 1215 command_ioctl, "ioctl", "ioctl() on root, for FS debugging only", 1216 command_ln, "ln", "create a hard or symbolic link", 1217 command_ls, "ls", "list files or directories", 1218 command_mkdir, "mkdir", "create directories", 1219 command_mkindex, "mkindex", "create an index", 1220 command_mv, "mv", "move/rename files and directories", 1221 command_query, "query", "query for files", 1222 command_quit, "quit/exit", "quit the shell", 1223 command_rm, "rm", "remove files and directories", 1224 command_sync, "sync", "syncs the file system", 1225 NULL 1226 ); 1227 } 1228 1229 1230 // #pragma mark - ArgVector 1231 1232 1233 class ArgVector { 1234 public: 1235 ArgVector() 1236 : fArgc(0), 1237 fArgv(NULL) 1238 { 1239 } 1240 1241 ~ArgVector() 1242 { 1243 _Cleanup(); 1244 } 1245 1246 int Argc() const 1247 { 1248 return fArgc; 1249 } 1250 1251 const char* const* Argv() const 1252 { 1253 return fArgv; 1254 } 1255 1256 bool Parse(const char* commandLine) 1257 { 1258 _Cleanup(); 1259 1260 // init temporary arg/argv storage 1261 std::string currentArg; 1262 std::vector<std::string> argVector; 1263 1264 fCurrentArg = ¤tArg; 1265 fCurrentArgStarted = false; 1266 fArgVector = &argVector; 1267 1268 for (; *commandLine; commandLine++) { 1269 char c = *commandLine; 1270 1271 // whitespace delimits args and is otherwise ignored 1272 if (isspace(c)) { 1273 _PushCurrentArg(); 1274 continue; 1275 } 1276 1277 switch (c) { 1278 case '\'': 1279 // quoted string -- no quoting 1280 while (*++commandLine != '\'') { 1281 c = *commandLine; 1282 if (c == '\0') { 1283 fprintf(stderr, "Error: Unterminated quoted " 1284 "string.\n"); 1285 return false; 1286 } 1287 _PushCharacter(c); 1288 } 1289 break; 1290 1291 case '"': 1292 // quoted string -- some quoting 1293 while (*++commandLine != '"') { 1294 c = *commandLine; 1295 if (c == '\0') { 1296 fprintf(stderr, "Error: Unterminated quoted " 1297 "string.\n"); 1298 return false; 1299 } 1300 1301 if (c == '\\') { 1302 c = *++commandLine; 1303 if (c == '\0') { 1304 fprintf(stderr, "Error: Unterminated quoted " 1305 "string.\n"); 1306 return false; 1307 } 1308 1309 // only '\' and '"' can be quoted, otherwise the 1310 // the '\' is treated as a normal char 1311 if (c != '\\' && c != '"') 1312 _PushCharacter('\\'); 1313 } 1314 1315 _PushCharacter(c); 1316 } 1317 break; 1318 1319 case '\\': 1320 // quoted char 1321 c = *++commandLine; 1322 if (c == '\0') { 1323 fprintf(stderr, "Error: Command line ends with " 1324 "'\\'.\n"); 1325 return false; 1326 } 1327 _PushCharacter(c); 1328 break; 1329 1330 default: 1331 // normal char 1332 _PushCharacter(c); 1333 break; 1334 } 1335 } 1336 1337 // commit last arg 1338 _PushCurrentArg(); 1339 1340 // build arg vector 1341 fArgc = argVector.size(); 1342 fArgv = new char*[fArgc + 1]; 1343 for (int i = 0; i < fArgc; i++) { 1344 int len = argVector[i].length(); 1345 fArgv[i] = new char[len + 1]; 1346 memcpy(fArgv[i], argVector[i].c_str(), len + 1); 1347 } 1348 fArgv[fArgc] = NULL; 1349 1350 return true; 1351 } 1352 1353 private: 1354 void _Cleanup() 1355 { 1356 if (fArgv) { 1357 for (int i = 0; i < fArgc; i++) 1358 delete[] fArgv[i]; 1359 delete[] fArgv; 1360 } 1361 } 1362 1363 void _PushCurrentArg() 1364 { 1365 if (fCurrentArgStarted) { 1366 fArgVector->push_back(*fCurrentArg); 1367 fCurrentArgStarted = false; 1368 } 1369 } 1370 1371 void _PushCharacter(char c) 1372 { 1373 if (!fCurrentArgStarted) { 1374 *fCurrentArg = ""; 1375 fCurrentArgStarted = true; 1376 } 1377 1378 *fCurrentArg += c; 1379 } 1380 1381 private: 1382 // temporaries 1383 std::string* fCurrentArg; 1384 bool fCurrentArgStarted; 1385 std::vector<std::string>* fArgVector; 1386 1387 int fArgc; 1388 char** fArgv; 1389 }; 1390 1391 1392 // #pragma mark - input loop 1393 1394 1395 static char* 1396 read_command_line(char* buffer, int bufferSize) 1397 { 1398 // print prompt (including cwd, if available) 1399 char directory[FSSH_B_PATH_NAME_LENGTH]; 1400 if (_kern_getcwd(directory, sizeof(directory)) == FSSH_B_OK) 1401 printf("fssh:%s> ", directory); 1402 else 1403 printf("fssh> "); 1404 fflush(stdout); 1405 1406 // read input line 1407 return fgets(buffer, bufferSize, stdin); 1408 } 1409 1410 1411 static void 1412 input_loop(bool interactive) 1413 { 1414 static const int kInputBufferSize = 100 * 1024; 1415 char* inputBuffer = new char[kInputBufferSize]; 1416 1417 for (;;) { 1418 // read command line 1419 if (interactive) { 1420 if (!read_command_line(inputBuffer, kInputBufferSize)) 1421 break; 1422 } else { 1423 if (!get_external_command(inputBuffer, kInputBufferSize)) 1424 break; 1425 } 1426 1427 // construct argv vector 1428 int result = FSSH_B_BAD_VALUE; 1429 ArgVector argVector; 1430 if (argVector.Parse(inputBuffer) && argVector.Argc() > 0) { 1431 int argc = argVector.Argc(); 1432 const char* const* argv = argVector.Argv(); 1433 1434 // find command 1435 Command* command = CommandManager::Default()->FindCommand(argv[0]); 1436 if (command) { 1437 // execute it 1438 result = command->Do(argc, argv); 1439 if (result == COMMAND_RESULT_EXIT) { 1440 if (!interactive) 1441 reply_to_external_command(0); 1442 break; 1443 } 1444 } else { 1445 fprintf(stderr, "Error: Invalid command \"%s\". Type \"help\" " 1446 "for a list of supported commands\n", argv[0]); 1447 } 1448 } 1449 1450 if (!interactive) 1451 reply_to_external_command(fssh_to_host_error(result)); 1452 } 1453 1454 if (!interactive) 1455 external_command_cleanup(); 1456 1457 delete[] inputBuffer; 1458 } 1459 1460 1461 static int 1462 standard_session(const char* device, const char* fsName, bool interactive) 1463 { 1464 // mount FS 1465 fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0); 1466 if (fsDev < 0) { 1467 fprintf(stderr, "Error: Mounting FS failed: %s\n", 1468 fssh_strerror(fsDev)); 1469 return 1; 1470 } 1471 1472 // register commands 1473 register_commands(); 1474 1475 // process commands 1476 input_loop(interactive); 1477 1478 // unmount FS 1479 _kern_setcwd(-1, "/"); // avoid a "busy" vnode 1480 fssh_status_t error = _kern_unmount(kMountPoint, 0); 1481 if (error != FSSH_B_OK) { 1482 fprintf(stderr, "Error: Unmounting FS failed: %s\n", 1483 fssh_strerror(error)); 1484 return 1; 1485 } 1486 1487 return 0; 1488 } 1489 1490 1491 static int 1492 initialization_session(const char* device, const char* fsName, 1493 const char* volumeName, const char* initParameters) 1494 { 1495 fssh_status_t error = _kern_initialize_volume(fsName, device, 1496 volumeName, initParameters); 1497 if (error != FSSH_B_OK) { 1498 fprintf(stderr, "Error: Initializing volume failed: %s\n", 1499 fssh_strerror(error)); 1500 return 1; 1501 } 1502 1503 return 0; 1504 } 1505 1506 1507 static void 1508 print_usage(bool error) 1509 { 1510 fprintf((error ? stderr : stdout), 1511 "Usage: %s [ --start-offset <startOffset>]\n" 1512 " [ --end-offset <endOffset>] [-n] <device>\n" 1513 " %s [ --start-offset <startOffset>]\n" 1514 " [ --end-offset <endOffset>]\n" 1515 " --initialize [-n] <device> <volume name> " 1516 "[ <init parameters> ]\n", 1517 sArgv[0], sArgv[0] 1518 ); 1519 } 1520 1521 1522 static void 1523 print_usage_and_exit(bool error) 1524 { 1525 print_usage(error); 1526 exit(error ? 1 : 0); 1527 } 1528 1529 1530 } // namespace FSShell 1531 1532 1533 using namespace FSShell; 1534 1535 1536 int 1537 main(int argc, const char* const* argv) 1538 { 1539 sArgc = argc; 1540 sArgv = argv; 1541 1542 // process arguments 1543 bool interactive = true; 1544 bool initialize = false; 1545 const char* device = NULL; 1546 const char* volumeName = NULL; 1547 const char* initParameters = NULL; 1548 fssh_off_t startOffset = 0; 1549 fssh_off_t endOffset = -1; 1550 1551 // eat options 1552 int argi = 1; 1553 while (argi < argc && argv[argi][0] == '-') { 1554 const char* arg = argv[argi++]; 1555 if (strcmp(arg, "--help") == 0) { 1556 print_usage_and_exit(false); 1557 } else if (strcmp(arg, "--initialize") == 0) { 1558 initialize = true; 1559 } else if (strcmp(arg, "-n") == 0) { 1560 interactive = false; 1561 } else if (strcmp(arg, "--start-offset") == 0) { 1562 if (argi >= argc) 1563 print_usage_and_exit(true); 1564 startOffset = atoll(argv[argi++]); 1565 } else if (strcmp(arg, "--end-offset") == 0) { 1566 if (argi >= argc) 1567 print_usage_and_exit(true); 1568 endOffset = atoll(argv[argi++]); 1569 } else { 1570 print_usage_and_exit(true); 1571 } 1572 } 1573 1574 // get device 1575 if (argi >= argc) 1576 print_usage_and_exit(true); 1577 device = argv[argi++]; 1578 1579 // get volume name and init parameters 1580 if (initialize) { 1581 // volume name 1582 if (argi >= argc) 1583 print_usage_and_exit(true); 1584 volumeName = argv[argi++]; 1585 1586 // (optional) init paramaters 1587 if (argi < argc) 1588 initParameters = argv[argi++]; 1589 } 1590 1591 // more parameters are excess 1592 if (argi < argc) 1593 print_usage_and_exit(true); 1594 1595 // get FS module 1596 if (!modules[0]) { 1597 fprintf(stderr, "Error: Couldn't find FS module!\n"); 1598 return 1; 1599 } 1600 const char* fsName = modules[0]->name; 1601 1602 fssh_status_t error; 1603 1604 // init kernel 1605 error = init_kernel(); 1606 if (error != FSSH_B_OK) { 1607 fprintf(stderr, "Error: Initializing kernel failed: %s\n", 1608 fssh_strerror(error)); 1609 return error; 1610 } 1611 1612 // restrict access if requested 1613 if (startOffset != 0 || endOffset != -1) 1614 add_file_restriction(device, startOffset, endOffset); 1615 1616 // start the action 1617 int result; 1618 if (initialize) { 1619 result = initialization_session(device, fsName, volumeName, 1620 initParameters); 1621 } else 1622 result = standard_session(device, fsName, interactive); 1623 1624 return result; 1625 } 1626