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