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