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 bool recursive = false; 634 635 // parse parameters 636 int argi = 1; 637 for (argi = 1; argi < argc; argi++) { 638 const char *arg = argv[argi]; 639 if (arg[0] != '-') 640 break; 641 642 if (arg[1] == '\0') { 643 fprintf(stderr, "Error: Invalid option \"-\"\n"); 644 return FSSH_B_BAD_VALUE; 645 } 646 647 for (int i = 1; arg[i]; i++) { 648 switch (arg[i]) { 649 case 'R': 650 recursive = true; 651 fprintf(stderr, "Sorry, recursive mode not supported " 652 "yet.\n"); 653 return FSSH_B_BAD_VALUE; 654 default: 655 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 656 return FSSH_B_BAD_VALUE; 657 } 658 } 659 } 660 661 // get mode 662 fssh_mode_t permissions; 663 if (argi + 1 >= argc || !get_permissions(argv[argi++], permissions)) { 664 printf("Usage: %s [ -R ] <octal mode> <file>...\n", argv[0]); 665 return FSSH_B_BAD_VALUE; 666 } 667 668 fssh_struct_stat st; 669 st.fssh_st_mode = permissions; 670 671 // chmod loop 672 for (; argi < argc; argi++) { 673 const char *file = argv[argi]; 674 if (strlen(file) == 0) { 675 fprintf(stderr, "Error: An empty path is not a valid argument!\n"); 676 return FSSH_B_BAD_VALUE; 677 } 678 679 fssh_status_t error = _kern_write_stat(-1, file, false, &st, sizeof(st), 680 FSSH_B_STAT_MODE); 681 if (error != FSSH_B_OK) { 682 fprintf(stderr, "Error: Failed to change mode of \"%s\"!\n", file); 683 return error; 684 } 685 } 686 687 return FSSH_B_OK; 688 } 689 690 691 static fssh_status_t 692 command_cat(int argc, const char* const* argv) 693 { 694 size_t numBytes = 4096; 695 int fileStart = 1; 696 if (argc < 2 || strcmp(argv[1], "--help") == 0) { 697 printf("Usage: %s [ -n ] [FILE]...\n" 698 "\t -n\tNumber of bytes to read\n", 699 argv[0]); 700 return FSSH_B_OK; 701 } 702 703 bool isReadLengthGiven = false; 704 if (argc > 3 && strcmp(argv[1], "-n") == 0) { 705 fileStart += 2; 706 numBytes = strtol(argv[2], NULL, 10); 707 isReadLengthGiven = true; 708 } 709 710 const char* const* files = argv + fileStart; 711 for (; *files; files++) { 712 const char* file = *files; 713 int fd = _kern_open(-1, file, FSSH_O_RDONLY, FSSH_O_RDONLY); 714 if (fd < 0) { 715 fprintf(stderr, "error: %s\n", fssh_strerror(fd)); 716 return FSSH_B_BAD_VALUE; 717 } 718 struct fssh_stat st; 719 fssh_status_t error = _kern_read_stat(-1, file, false, &st, sizeof(st)); 720 if (error != FSSH_B_OK) { 721 fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", file, 722 fssh_strerror(error)); 723 return error; 724 } 725 size_t fileLengthToRead; 726 if (!isReadLengthGiven) { 727 fileLengthToRead = st.fssh_st_size; 728 numBytes = 4096; 729 } else 730 fileLengthToRead = numBytes; 731 size_t pos = 0; 732 733 char buffer[numBytes + 1]; 734 while (fileLengthToRead > 0) { 735 if (fileLengthToRead < numBytes) 736 numBytes = fileLengthToRead; 737 if (_kern_read(fd, pos, buffer, numBytes) != (ssize_t)numBytes) { 738 fprintf(stderr, "error reading: %s\n", fssh_strerror(fd)); 739 _kern_close(fd); 740 return FSSH_B_BAD_VALUE; 741 } 742 buffer[numBytes] = '\0'; 743 printf("%s", buffer); 744 pos += numBytes; 745 fileLengthToRead -= numBytes; 746 } 747 printf("\n"); 748 _kern_close(fd); 749 } 750 751 return FSSH_B_OK; 752 } 753 754 755 static fssh_status_t 756 command_help(int argc, const char* const* argv) 757 { 758 printf("supported commands:\n"); 759 CommandManager::Default()->ListCommands(); 760 return FSSH_B_OK; 761 } 762 763 764 static fssh_status_t 765 command_info(int argc, const char* const* argv) 766 { 767 fssh_dev_t volumeID = get_volume_id(); 768 if (volumeID < 0) 769 return volumeID; 770 771 fssh_fs_info info; 772 fssh_status_t status = _kern_read_fs_info(volumeID, &info); 773 if (status != FSSH_B_OK) 774 return status; 775 776 printf("root inode: %" FSSH_B_PRIdINO "\n", info.root); 777 printf("flags: "); 778 print_flag(info.flags, FSSH_B_FS_HAS_QUERY, "Q", "-"); 779 print_flag(info.flags, FSSH_B_FS_HAS_ATTR, "A", "-"); 780 print_flag(info.flags, FSSH_B_FS_HAS_MIME, "M", "-"); 781 print_flag(info.flags, FSSH_B_FS_IS_SHARED, "S", "-"); 782 print_flag(info.flags, FSSH_B_FS_IS_PERSISTENT, "P", "-"); 783 print_flag(info.flags, FSSH_B_FS_IS_REMOVABLE, "R", "-"); 784 print_flag(info.flags, FSSH_B_FS_IS_READONLY, "-", "W"); 785 786 printf("\nblock size: %" FSSH_B_PRIdOFF "\n", info.block_size); 787 printf("I/O size: %" FSSH_B_PRIdOFF "\n", info.io_size); 788 printf("total size: %s (%" FSSH_B_PRIdOFF " blocks)\n", 789 byte_string(info.total_blocks, info.block_size), info.total_blocks); 790 printf("free size: %s (%" FSSH_B_PRIdOFF " blocks)\n", 791 byte_string(info.free_blocks, info.block_size), info.free_blocks); 792 printf("total nodes: %" FSSH_B_PRIdOFF "\n", info.total_nodes); 793 printf("free nodes: %" FSSH_B_PRIdOFF "\n", info.free_nodes); 794 printf("volume name: %s\n", info.volume_name); 795 printf("fs name: %s\n", info.fsh_name); 796 797 return FSSH_B_OK; 798 } 799 800 801 static fssh_status_t 802 command_ln(int argc, const char* const* argv) 803 { 804 bool force = false; 805 bool symbolic = false; 806 bool dereference = true; 807 808 // parse parameters 809 int argi = 1; 810 for (argi = 1; argi < argc; argi++) { 811 const char *arg = argv[argi]; 812 if (arg[0] != '-') 813 break; 814 815 if (arg[1] == '\0') { 816 fprintf(stderr, "Error: Invalid option \"-\"\n"); 817 return FSSH_B_BAD_VALUE; 818 } 819 820 for (int i = 1; arg[i]; i++) { 821 switch (arg[i]) { 822 case 'f': 823 force = true; 824 break; 825 case 's': 826 symbolic = true; 827 break; 828 case 'n': 829 dereference = false; 830 break; 831 default: 832 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 833 return FSSH_B_BAD_VALUE; 834 } 835 } 836 } 837 838 if (argc - argi != 2) { 839 fprintf(stderr, "Usage: %s [Options] <source> <target>\n", argv[0]); 840 return FSSH_B_BAD_VALUE; 841 } 842 843 const char *source = argv[argi]; 844 const char *target = argv[argi + 1]; 845 846 // check, if the the target is an existing directory 847 struct fssh_stat st; 848 char targetBuffer[FSSH_B_PATH_NAME_LENGTH]; 849 fssh_status_t error = _kern_read_stat(-1, target, dereference, &st, 850 sizeof(st)); 851 if (error == FSSH_B_OK) { 852 if (FSSH_S_ISDIR(st.fssh_st_mode)) { 853 // get source leaf 854 char leaf[FSSH_B_FILE_NAME_LENGTH]; 855 error = get_last_path_component(source, leaf, sizeof(leaf)); 856 if (error != FSSH_B_OK) { 857 fprintf(stderr, "Error: Failed to get leaf name of source " 858 "path: %s\n", fssh_strerror(error)); 859 return error; 860 } 861 862 // compose a new path 863 int len = strlen(target) + 1 + strlen(leaf); 864 if (len > (int)sizeof(targetBuffer)) { 865 fprintf(stderr, "Error: Resulting target path is too long.\n"); 866 return FSSH_B_BAD_VALUE; 867 } 868 869 strcpy(targetBuffer, target); 870 strcat(targetBuffer, "/"); 871 strcat(targetBuffer, leaf); 872 target = targetBuffer; 873 } 874 } 875 876 // check, if the target exists 877 error = _kern_read_stat(-1, target, false, &st, sizeof(st)); 878 if (error == FSSH_B_OK) { 879 if (!force) { 880 fprintf(stderr, "Error: Can't create link. \"%s\" is in the way.\n", 881 target); 882 return FSSH_B_FILE_EXISTS; 883 } 884 885 // unlink the entry 886 error = _kern_unlink(-1, target); 887 if (error != FSSH_B_OK) { 888 fprintf(stderr, "Error: Failed to remove \"%s\" to make way for " 889 "link: %s\n", target, fssh_strerror(error)); 890 return error; 891 } 892 } 893 894 // finally create the link 895 if (symbolic) { 896 error = _kern_create_symlink(-1, target, source, 897 FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO); 898 } else 899 error = _kern_create_link(target, source); 900 901 if (error != FSSH_B_OK) { 902 fprintf(stderr, "Error: Failed to create link: %s\n", 903 fssh_strerror(error)); 904 } 905 906 return error; 907 } 908 909 910 static fssh_status_t 911 command_ls(int argc, const char* const* argv) 912 { 913 const char* const currentDirFiles[] = { ".", NULL }; 914 const char* const* files; 915 if (argc >= 2) 916 files = argv + 1; 917 else 918 files = currentDirFiles; 919 920 for (; *files; files++) { 921 const char* file = *files; 922 // stat file 923 struct fssh_stat st; 924 fssh_status_t error = _kern_read_stat(-1, file, false, &st, sizeof(st)); 925 if (error != FSSH_B_OK) { 926 fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", file, 927 fssh_strerror(error)); 928 continue; 929 } 930 931 // if it is a directory, print its entries 932 if (FSSH_S_ISDIR(st.fssh_st_mode)) { 933 printf("%s:\n", file); 934 935 // open dir 936 int fd = _kern_open_dir(-1, file); 937 if (fd < 0) { 938 fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", 939 file, fssh_strerror(fd)); 940 continue; 941 } 942 943 // iterate through the entries 944 char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; 945 fssh_dirent* entry = (fssh_dirent*)buffer; 946 fssh_ssize_t entriesRead = 0; 947 while ((entriesRead = _kern_read_dir(fd, entry, sizeof(buffer), 1)) 948 == 1) { 949 list_entry(file, entry->d_name); 950 } 951 952 if (entriesRead < 0) { 953 fprintf(stderr, "Error: reading dir \"%s\" failed: %s\n", 954 file, fssh_strerror(entriesRead)); 955 } 956 957 // close dir 958 error = _kern_close(fd); 959 if (error != FSSH_B_OK) { 960 fprintf(stderr, "Error: Closing dir \"%s\" (fd: %d) failed: " 961 "%s\n", file, fd, fssh_strerror(error)); 962 continue; 963 } 964 } else 965 list_entry(file); 966 } 967 968 return FSSH_B_OK; 969 } 970 971 972 static fssh_status_t 973 command_mkdir(int argc, const char* const* argv) 974 { 975 bool createParents = false; 976 977 // parse parameters 978 int argi = 1; 979 for (argi = 1; argi < argc; argi++) { 980 const char *arg = argv[argi]; 981 if (arg[0] != '-') 982 break; 983 984 if (arg[1] == '\0') { 985 fprintf(stderr, "Error: Invalid option \"-\"\n"); 986 return FSSH_B_BAD_VALUE; 987 } 988 989 for (int i = 1; arg[i]; i++) { 990 switch (arg[i]) { 991 case 'p': 992 createParents = true; 993 break; 994 default: 995 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 996 return FSSH_B_BAD_VALUE; 997 } 998 } 999 } 1000 1001 if (argi >= argc) { 1002 printf("Usage: %s [ -p ] <dir>...\n", argv[0]); 1003 return FSSH_B_BAD_VALUE; 1004 } 1005 1006 // create loop 1007 for (; argi < argc; argi++) { 1008 const char *dir = argv[argi]; 1009 if (strlen(dir) == 0) { 1010 fprintf(stderr, "Error: An empty path is not a valid argument!\n"); 1011 return FSSH_B_BAD_VALUE; 1012 } 1013 1014 fssh_status_t error = create_dir(dir, createParents); 1015 if (error != FSSH_B_OK) 1016 return error; 1017 } 1018 1019 return FSSH_B_OK; 1020 } 1021 1022 1023 static fssh_status_t 1024 command_mkindex(int argc, const char* const* argv) 1025 { 1026 if (argc < 2) { 1027 fprintf(stderr, "Usage: %s [-t <type>] <index name>\n", argv[0]); 1028 return FSSH_B_BAD_VALUE; 1029 } 1030 1031 int fileArg = 1; 1032 int type = FSSH_B_STRING_TYPE; 1033 1034 if (argc > 3 && strcmp(argv[1], "-t") == 0) { 1035 fileArg = 3; 1036 if (strcmp(argv[2], "string") == 0) 1037 type = FSSH_B_STRING_TYPE; 1038 else if (strcmp(argv[2], "int32") == 0) 1039 type = FSSH_B_INT32_TYPE; 1040 else { 1041 fprintf(stderr, "Unhandled attribute type %s\n", argv[2]); 1042 return FSSH_B_BAD_VALUE; 1043 } 1044 } 1045 1046 const char* indexName = argv[fileArg]; 1047 1048 // get the volume ID 1049 fssh_dev_t volumeID = get_volume_id(); 1050 if (volumeID < 0) 1051 return volumeID; 1052 1053 // create the index 1054 fssh_status_t error =_kern_create_index(volumeID, indexName, type, 0); 1055 if (error != FSSH_B_OK) { 1056 fprintf(stderr, "Error: Failed to create index \"%s\": %s\n", 1057 indexName, fssh_strerror(error)); 1058 return error; 1059 } 1060 1061 return FSSH_B_OK; 1062 } 1063 1064 1065 static fssh_status_t 1066 command_mv(int argc, const char* const* argv) 1067 { 1068 bool force = false; 1069 1070 // parse parameters 1071 int argi = 1; 1072 for (argi = 1; argi < argc; argi++) { 1073 const char *arg = argv[argi]; 1074 if (arg[0] != '-') 1075 break; 1076 1077 if (arg[1] == '\0') { 1078 fprintf(stderr, "Error: Invalid option \"-\"\n"); 1079 return FSSH_B_BAD_VALUE; 1080 } 1081 1082 for (int i = 1; arg[i]; i++) { 1083 switch (arg[i]) { 1084 case 'f': 1085 force = true; 1086 break; 1087 default: 1088 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 1089 return FSSH_B_BAD_VALUE; 1090 } 1091 } 1092 } 1093 1094 // check params 1095 int count = argc - 1 - argi; 1096 if (count <= 0) { 1097 fprintf(stderr, "Usage: %s [-f] <file>... <target>\n", argv[0]); 1098 return FSSH_B_BAD_VALUE; 1099 } 1100 1101 const char* target = argv[argc - 1]; 1102 1103 // stat the target 1104 struct fssh_stat st; 1105 fssh_status_t status = _kern_read_stat(-1, target, true, &st, sizeof(st)); 1106 if (status != FSSH_B_OK && count != 1) { 1107 fprintf(stderr, "Error: Failed to stat target \"%s\": %s\n", target, 1108 fssh_strerror(status)); 1109 return status; 1110 } 1111 1112 if (status == FSSH_B_OK && FSSH_S_ISDIR(st.fssh_st_mode)) { 1113 // move several entries 1114 int targetDir = _kern_open_dir(-1, target); 1115 if (targetDir < 0) { 1116 fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", target, 1117 fssh_strerror(targetDir)); 1118 return targetDir; 1119 } 1120 1121 // move loop 1122 for (; argi < argc - 1; argi++) { 1123 status = move_entry(-1, argv[argi], targetDir, argv[argi], force); 1124 if (status != FSSH_B_OK) { 1125 _kern_close(targetDir); 1126 return status; 1127 } 1128 } 1129 1130 _kern_close(targetDir); 1131 return FSSH_B_OK; 1132 } 1133 1134 // rename single entry 1135 return move_entry(-1, argv[argi], -1, target, force); 1136 } 1137 1138 1139 static fssh_status_t 1140 command_query(int argc, const char* const* argv) 1141 { 1142 if (argc != 2) { 1143 fprintf(stderr, "Usage: %s <query string>\n", argv[0]); 1144 return FSSH_B_BAD_VALUE; 1145 } 1146 1147 const char* query = argv[1]; 1148 1149 // get the volume ID 1150 fssh_dev_t volumeID = get_volume_id(); 1151 if (volumeID < 0) 1152 return volumeID; 1153 1154 // open query 1155 int fd = _kern_open_query(volumeID, query, strlen(query), 0, -1, -1); 1156 if (fd < 0) { 1157 fprintf(stderr, "Error: Failed to open query: %s\n", fssh_strerror(fd)); 1158 return fd; 1159 } 1160 1161 // iterate through the entries 1162 fssh_status_t error = FSSH_B_OK; 1163 char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; 1164 fssh_dirent* entry = (fssh_dirent*)buffer; 1165 fssh_ssize_t entriesRead = 0; 1166 while ((entriesRead = _kern_read_dir(fd, entry, sizeof(buffer), 1)) == 1) { 1167 char path[FSSH_B_PATH_NAME_LENGTH]; 1168 error = _kern_entry_ref_to_path(volumeID, entry->d_pino, entry->d_name, 1169 path, sizeof(path)); 1170 if (error == FSSH_B_OK) { 1171 printf(" %s\n", path); 1172 } else { 1173 fprintf(stderr, " failed to resolve entry (%8" FSSH_B_PRIdINO 1174 ", \"%s\")\n", entry->d_pino, entry->d_name); 1175 } 1176 } 1177 1178 if (entriesRead < 0) { 1179 fprintf(stderr, "Error: reading query failed: %s\n", 1180 fssh_strerror(entriesRead)); 1181 } 1182 1183 // close query 1184 error = _kern_close(fd); 1185 if (error != FSSH_B_OK) { 1186 fprintf(stderr, "Error: Closing query (fd: %d) failed: %s\n", 1187 fd, fssh_strerror(error)); 1188 } 1189 1190 return error; 1191 } 1192 1193 1194 static fssh_status_t 1195 command_quit(int argc, const char* const* argv) 1196 { 1197 return COMMAND_RESULT_EXIT; 1198 } 1199 1200 1201 static fssh_status_t 1202 command_rm(int argc, const char* const* argv) 1203 { 1204 bool recursive = false; 1205 bool force = false; 1206 1207 // parse parameters 1208 int argi = 1; 1209 for (argi = 1; argi < argc; argi++) { 1210 const char *arg = argv[argi]; 1211 if (arg[0] != '-') 1212 break; 1213 1214 if (arg[1] == '\0') { 1215 fprintf(stderr, "Error: Invalid option \"-\"\n"); 1216 return FSSH_B_BAD_VALUE; 1217 } 1218 1219 for (int i = 1; arg[i]; i++) { 1220 switch (arg[i]) { 1221 case 'f': 1222 force = true; 1223 break; 1224 case 'r': 1225 recursive = true; 1226 break; 1227 default: 1228 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 1229 return FSSH_B_BAD_VALUE; 1230 } 1231 } 1232 } 1233 1234 // check params 1235 if (argi >= argc) { 1236 fprintf(stderr, "Usage: %s [ -r ] <file>...\n", argv[0]); 1237 return FSSH_B_BAD_VALUE; 1238 } 1239 1240 // remove loop 1241 for (; argi < argc; argi++) { 1242 fssh_status_t error = remove_entry(-1, argv[argi], recursive, force); 1243 if (error != FSSH_B_OK) 1244 return error; 1245 } 1246 1247 return FSSH_B_OK; 1248 } 1249 1250 1251 static fssh_status_t 1252 command_sync(int argc, const char* const* argv) 1253 { 1254 fssh_status_t error = _kern_sync(); 1255 if (error != FSSH_B_OK) { 1256 fprintf(stderr, "Error: syncing: %s\n", fssh_strerror(error)); 1257 return error; 1258 } 1259 1260 return FSSH_B_OK; 1261 } 1262 1263 1264 static fssh_status_t 1265 command_ioctl(int argc, const char* const* argv) 1266 { 1267 if (argc != 2) { 1268 fprintf(stderr, "Usage: %s <opcode>\n", argv[0]); 1269 return FSSH_B_BAD_VALUE; 1270 } 1271 1272 int rootDir = _kern_open_dir(-1, "/myfs"); 1273 if (rootDir < 0) 1274 return rootDir; 1275 1276 fssh_status_t status = _kern_ioctl(rootDir, atoi(argv[1]), NULL, 0); 1277 1278 _kern_close(rootDir); 1279 1280 if (status != FSSH_B_OK) { 1281 fprintf(stderr, "Error: ioctl failed: %s\n", fssh_strerror(status)); 1282 return status; 1283 } 1284 1285 return FSSH_B_OK; 1286 } 1287 1288 1289 static void 1290 register_commands() 1291 { 1292 CommandManager::Default()->AddCommands( 1293 command_cd, "cd", "change current directory", 1294 command_chmod, "chmod", "change file permissions", 1295 command_cp, "cp", "copy files and directories", 1296 command_cat, "cat", "concatenate file(s) to stdout", 1297 command_help, "help", "list supported commands", 1298 command_info, "info", "prints volume informations", 1299 command_ioctl, "ioctl", "ioctl() on root, for FS debugging only", 1300 command_ln, "ln", "create a hard or symbolic link", 1301 command_ls, "ls", "list files or directories", 1302 command_mkdir, "mkdir", "create directories", 1303 command_mkindex, "mkindex", "create an index", 1304 command_mv, "mv", "move/rename files and directories", 1305 command_query, "query", "query for files", 1306 command_quit, "quit/exit", "quit the shell", 1307 command_rm, "rm", "remove files and directories", 1308 command_sync, "sync", "syncs the file system", 1309 NULL 1310 ); 1311 } 1312 1313 1314 // #pragma mark - ArgVector 1315 1316 1317 class ArgVector { 1318 public: 1319 ArgVector() 1320 : fArgc(0), 1321 fArgv(NULL) 1322 { 1323 } 1324 1325 ~ArgVector() 1326 { 1327 _Cleanup(); 1328 } 1329 1330 int Argc() const 1331 { 1332 return fArgc; 1333 } 1334 1335 const char* const* Argv() const 1336 { 1337 return fArgv; 1338 } 1339 1340 bool Parse(const char* commandLine) 1341 { 1342 _Cleanup(); 1343 1344 // init temporary arg/argv storage 1345 std::string currentArg; 1346 std::vector<std::string> argVector; 1347 1348 fCurrentArg = ¤tArg; 1349 fCurrentArgStarted = false; 1350 fArgVector = &argVector; 1351 1352 for (; *commandLine; commandLine++) { 1353 char c = *commandLine; 1354 1355 // whitespace delimits args and is otherwise ignored 1356 if (isspace(c)) { 1357 _PushCurrentArg(); 1358 continue; 1359 } 1360 1361 switch (c) { 1362 case '\'': 1363 // quoted string -- no quoting 1364 while (*++commandLine != '\'') { 1365 c = *commandLine; 1366 if (c == '\0') { 1367 fprintf(stderr, "Error: Unterminated quoted " 1368 "string.\n"); 1369 return false; 1370 } 1371 _PushCharacter(c); 1372 } 1373 break; 1374 1375 case '"': 1376 // quoted string -- some quoting 1377 while (*++commandLine != '"') { 1378 c = *commandLine; 1379 if (c == '\0') { 1380 fprintf(stderr, "Error: Unterminated quoted " 1381 "string.\n"); 1382 return false; 1383 } 1384 1385 if (c == '\\') { 1386 c = *++commandLine; 1387 if (c == '\0') { 1388 fprintf(stderr, "Error: Unterminated quoted " 1389 "string.\n"); 1390 return false; 1391 } 1392 1393 // only '\' and '"' can be quoted, otherwise the 1394 // the '\' is treated as a normal char 1395 if (c != '\\' && c != '"') 1396 _PushCharacter('\\'); 1397 } 1398 1399 _PushCharacter(c); 1400 } 1401 break; 1402 1403 case '\\': 1404 // quoted char 1405 c = *++commandLine; 1406 if (c == '\0') { 1407 fprintf(stderr, "Error: Command line ends with " 1408 "'\\'.\n"); 1409 return false; 1410 } 1411 _PushCharacter(c); 1412 break; 1413 1414 default: 1415 // normal char 1416 _PushCharacter(c); 1417 break; 1418 } 1419 } 1420 1421 // commit last arg 1422 _PushCurrentArg(); 1423 1424 // build arg vector 1425 fArgc = argVector.size(); 1426 fArgv = new char*[fArgc + 1]; 1427 for (int i = 0; i < fArgc; i++) { 1428 int len = argVector[i].length(); 1429 fArgv[i] = new char[len + 1]; 1430 memcpy(fArgv[i], argVector[i].c_str(), len + 1); 1431 } 1432 fArgv[fArgc] = NULL; 1433 1434 return true; 1435 } 1436 1437 private: 1438 void _Cleanup() 1439 { 1440 if (fArgv) { 1441 for (int i = 0; i < fArgc; i++) 1442 delete[] fArgv[i]; 1443 delete[] fArgv; 1444 } 1445 } 1446 1447 void _PushCurrentArg() 1448 { 1449 if (fCurrentArgStarted) { 1450 fArgVector->push_back(*fCurrentArg); 1451 fCurrentArgStarted = false; 1452 } 1453 } 1454 1455 void _PushCharacter(char c) 1456 { 1457 if (!fCurrentArgStarted) { 1458 *fCurrentArg = ""; 1459 fCurrentArgStarted = true; 1460 } 1461 1462 *fCurrentArg += c; 1463 } 1464 1465 private: 1466 // temporaries 1467 std::string* fCurrentArg; 1468 bool fCurrentArgStarted; 1469 std::vector<std::string>* fArgVector; 1470 1471 int fArgc; 1472 char** fArgv; 1473 }; 1474 1475 1476 // #pragma mark - input loop 1477 1478 1479 static char* 1480 read_command_line(char* buffer, int bufferSize) 1481 { 1482 // print prompt (including cwd, if available) 1483 char directory[FSSH_B_PATH_NAME_LENGTH]; 1484 if (_kern_getcwd(directory, sizeof(directory)) == FSSH_B_OK) 1485 printf("fssh:%s> ", directory); 1486 else 1487 printf("fssh> "); 1488 fflush(stdout); 1489 1490 // read input line 1491 return fgets(buffer, bufferSize, stdin); 1492 } 1493 1494 1495 static void 1496 input_loop(bool interactive) 1497 { 1498 static const int kInputBufferSize = 100 * 1024; 1499 char* inputBuffer = new char[kInputBufferSize]; 1500 1501 for (;;) { 1502 // read command line 1503 if (interactive) { 1504 if (!read_command_line(inputBuffer, kInputBufferSize)) 1505 break; 1506 } else { 1507 if (!get_external_command(inputBuffer, kInputBufferSize)) 1508 break; 1509 } 1510 1511 // construct argv vector 1512 int result = FSSH_B_BAD_VALUE; 1513 ArgVector argVector; 1514 if (argVector.Parse(inputBuffer) && argVector.Argc() > 0) { 1515 int argc = argVector.Argc(); 1516 const char* const* argv = argVector.Argv(); 1517 1518 // find command 1519 Command* command = CommandManager::Default()->FindCommand(argv[0]); 1520 if (command) { 1521 // execute it 1522 result = command->Do(argc, argv); 1523 if (result == COMMAND_RESULT_EXIT) { 1524 if (!interactive) 1525 reply_to_external_command(0); 1526 break; 1527 } 1528 } else { 1529 fprintf(stderr, "Error: Invalid command \"%s\". Type \"help\" " 1530 "for a list of supported commands\n", argv[0]); 1531 } 1532 } 1533 1534 if (!interactive) 1535 reply_to_external_command(fssh_to_host_error(result)); 1536 } 1537 1538 if (!interactive) 1539 external_command_cleanup(); 1540 1541 delete[] inputBuffer; 1542 } 1543 1544 1545 static int 1546 standard_session(const char* device, const char* fsName, bool interactive) 1547 { 1548 // mount FS 1549 fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0); 1550 if (fsDev < 0) { 1551 fprintf(stderr, "Error: Mounting FS failed: %s\n", 1552 fssh_strerror(fsDev)); 1553 return 1; 1554 } 1555 1556 // register commands 1557 register_commands(); 1558 register_additional_commands(); 1559 1560 // process commands 1561 input_loop(interactive); 1562 1563 // unmount FS 1564 _kern_setcwd(-1, "/"); // avoid a "busy" vnode 1565 fssh_status_t error = _kern_unmount(kMountPoint, 0); 1566 if (error != FSSH_B_OK) { 1567 fprintf(stderr, "Error: Unmounting FS failed: %s\n", 1568 fssh_strerror(error)); 1569 return 1; 1570 } 1571 1572 return 0; 1573 } 1574 1575 1576 static int 1577 initialization_session(const char* device, const char* fsName, 1578 const char* volumeName, const char* initParameters) 1579 { 1580 fssh_status_t error = _kern_initialize_volume(fsName, device, 1581 volumeName, initParameters); 1582 if (error != FSSH_B_OK) { 1583 fprintf(stderr, "Error: Initializing volume failed: %s\n", 1584 fssh_strerror(error)); 1585 return 1; 1586 } 1587 1588 return 0; 1589 } 1590 1591 1592 static void 1593 print_usage(bool error) 1594 { 1595 fprintf((error ? stderr : stdout), 1596 "Usage: %s [ --start-offset <startOffset>]\n" 1597 " [ --end-offset <endOffset>] [-n] <device>\n" 1598 " %s [ --start-offset <startOffset>]\n" 1599 " [ --end-offset <endOffset>]\n" 1600 " --initialize [-n] <device> <volume name> " 1601 "[ <init parameters> ]\n", 1602 sArgv[0], sArgv[0] 1603 ); 1604 } 1605 1606 1607 static void 1608 print_usage_and_exit(bool error) 1609 { 1610 print_usage(error); 1611 exit(error ? 1 : 0); 1612 } 1613 1614 1615 } // namespace FSShell 1616 1617 1618 using namespace FSShell; 1619 1620 1621 int 1622 main(int argc, const char* const* argv) 1623 { 1624 sArgc = argc; 1625 sArgv = argv; 1626 1627 // process arguments 1628 bool interactive = true; 1629 bool initialize = false; 1630 const char* device = NULL; 1631 const char* volumeName = NULL; 1632 const char* initParameters = NULL; 1633 fssh_off_t startOffset = 0; 1634 fssh_off_t endOffset = -1; 1635 1636 // eat options 1637 int argi = 1; 1638 while (argi < argc && argv[argi][0] == '-') { 1639 const char* arg = argv[argi++]; 1640 if (strcmp(arg, "--help") == 0) { 1641 print_usage_and_exit(false); 1642 } else if (strcmp(arg, "--initialize") == 0) { 1643 initialize = true; 1644 } else if (strcmp(arg, "-n") == 0) { 1645 interactive = false; 1646 } else if (strcmp(arg, "--start-offset") == 0) { 1647 if (argi >= argc) 1648 print_usage_and_exit(true); 1649 startOffset = atoll(argv[argi++]); 1650 } else if (strcmp(arg, "--end-offset") == 0) { 1651 if (argi >= argc) 1652 print_usage_and_exit(true); 1653 endOffset = atoll(argv[argi++]); 1654 } else { 1655 print_usage_and_exit(true); 1656 } 1657 } 1658 1659 // get device 1660 if (argi >= argc) 1661 print_usage_and_exit(true); 1662 device = argv[argi++]; 1663 1664 // get volume name and init parameters 1665 if (initialize) { 1666 // volume name 1667 if (argi >= argc) 1668 print_usage_and_exit(true); 1669 volumeName = argv[argi++]; 1670 1671 // (optional) init paramaters 1672 if (argi < argc) 1673 initParameters = argv[argi++]; 1674 } 1675 1676 // more parameters are excess 1677 if (argi < argc) 1678 print_usage_and_exit(true); 1679 1680 // get FS module 1681 if (!modules[0]) { 1682 fprintf(stderr, "Error: Couldn't find FS module!\n"); 1683 return 1; 1684 } 1685 const char* fsName = modules[0]->name; 1686 1687 fssh_status_t error; 1688 1689 // init kernel 1690 error = init_kernel(); 1691 if (error != FSSH_B_OK) { 1692 fprintf(stderr, "Error: Initializing kernel failed: %s\n", 1693 fssh_strerror(error)); 1694 return error; 1695 } 1696 1697 // restrict access if requested 1698 if (startOffset != 0 || endOffset != -1) 1699 add_file_restriction(device, startOffset, endOffset); 1700 1701 // start the action 1702 int result; 1703 if (initialize) { 1704 result = initialization_session(device, fsName, volumeName, 1705 initParameters); 1706 } else 1707 result = standard_session(device, fsName, interactive); 1708 1709 return result; 1710 } 1711