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