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