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