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