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