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