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