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 if (_kern_read(fd, pos, buffer, numBytes) != (ssize_t)numBytes) { 765 fprintf(stderr, "error reading: %s\n", fssh_strerror(fd)); 766 _kern_close(fd); 767 return FSSH_B_BAD_VALUE; 768 } 769 buffer[numBytes] = '\0'; 770 printf("%s", buffer); 771 pos += numBytes; 772 fileLengthToRead -= numBytes; 773 } 774 printf("\n"); 775 _kern_close(fd); 776 } 777 778 return FSSH_B_OK; 779 } 780 781 782 static fssh_status_t 783 command_help(int argc, const char* const* argv) 784 { 785 printf("supported commands:\n"); 786 CommandManager::Default()->ListCommands(); 787 return FSSH_B_OK; 788 } 789 790 791 static fssh_status_t 792 command_info(int argc, const char* const* argv) 793 { 794 fssh_dev_t volumeID = get_volume_id(); 795 if (volumeID < 0) 796 return volumeID; 797 798 fssh_fs_info info; 799 fssh_status_t status = _kern_read_fs_info(volumeID, &info); 800 if (status != FSSH_B_OK) 801 return status; 802 803 printf("root inode: %" FSSH_B_PRIdINO "\n", info.root); 804 printf("flags: "); 805 print_flag(info.flags, FSSH_B_FS_HAS_QUERY, "Q", "-"); 806 print_flag(info.flags, FSSH_B_FS_HAS_ATTR, "A", "-"); 807 print_flag(info.flags, FSSH_B_FS_HAS_MIME, "M", "-"); 808 print_flag(info.flags, FSSH_B_FS_IS_SHARED, "S", "-"); 809 print_flag(info.flags, FSSH_B_FS_IS_PERSISTENT, "P", "-"); 810 print_flag(info.flags, FSSH_B_FS_IS_REMOVABLE, "R", "-"); 811 print_flag(info.flags, FSSH_B_FS_IS_READONLY, "-", "W"); 812 813 printf("\nblock size: %" FSSH_B_PRIdOFF "\n", info.block_size); 814 printf("I/O size: %" FSSH_B_PRIdOFF "\n", info.io_size); 815 printf("total size: %s (%" FSSH_B_PRIdOFF " blocks)\n", 816 byte_string(info.total_blocks, info.block_size), info.total_blocks); 817 printf("free size: %s (%" FSSH_B_PRIdOFF " blocks)\n", 818 byte_string(info.free_blocks, info.block_size), info.free_blocks); 819 printf("total nodes: %" FSSH_B_PRIdOFF "\n", info.total_nodes); 820 printf("free nodes: %" FSSH_B_PRIdOFF "\n", info.free_nodes); 821 printf("volume name: %s\n", info.volume_name); 822 printf("fs name: %s\n", info.fsh_name); 823 824 return FSSH_B_OK; 825 } 826 827 828 static fssh_status_t 829 command_ln(int argc, const char* const* argv) 830 { 831 bool force = false; 832 bool symbolic = false; 833 bool dereference = true; 834 835 // parse parameters 836 int argi = 1; 837 for (argi = 1; argi < argc; argi++) { 838 const char *arg = argv[argi]; 839 if (arg[0] != '-') 840 break; 841 842 if (arg[1] == '\0') { 843 fprintf(stderr, "Error: Invalid option \"-\"\n"); 844 return FSSH_B_BAD_VALUE; 845 } 846 847 for (int i = 1; arg[i]; i++) { 848 switch (arg[i]) { 849 case 'f': 850 force = true; 851 break; 852 case 's': 853 symbolic = true; 854 break; 855 case 'n': 856 dereference = false; 857 break; 858 default: 859 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 860 return FSSH_B_BAD_VALUE; 861 } 862 } 863 } 864 865 if (argc - argi != 2) { 866 fprintf(stderr, "Usage: %s [Options] <source> <target>\n", argv[0]); 867 return FSSH_B_BAD_VALUE; 868 } 869 870 const char *source = argv[argi]; 871 const char *target = argv[argi + 1]; 872 873 // check, if the the target is an existing directory 874 struct fssh_stat st; 875 char targetBuffer[FSSH_B_PATH_NAME_LENGTH]; 876 fssh_status_t error = _kern_read_stat(-1, target, dereference, &st, 877 sizeof(st)); 878 if (error == FSSH_B_OK) { 879 if (FSSH_S_ISDIR(st.fssh_st_mode)) { 880 // get source leaf 881 char leaf[FSSH_B_FILE_NAME_LENGTH]; 882 error = get_last_path_component(source, leaf, sizeof(leaf)); 883 if (error != FSSH_B_OK) { 884 fprintf(stderr, "Error: Failed to get leaf name of source " 885 "path: %s\n", fssh_strerror(error)); 886 return error; 887 } 888 889 // compose a new path 890 int len = strlen(target) + 1 + strlen(leaf); 891 if (len > (int)sizeof(targetBuffer)) { 892 fprintf(stderr, "Error: Resulting target path is too long.\n"); 893 return FSSH_B_BAD_VALUE; 894 } 895 896 strcpy(targetBuffer, target); 897 strcat(targetBuffer, "/"); 898 strcat(targetBuffer, leaf); 899 target = targetBuffer; 900 } 901 } 902 903 // check, if the target exists 904 error = _kern_read_stat(-1, target, false, &st, sizeof(st)); 905 if (error == FSSH_B_OK) { 906 if (!force) { 907 fprintf(stderr, "Error: Can't create link. \"%s\" is in the way.\n", 908 target); 909 return FSSH_B_FILE_EXISTS; 910 } 911 912 // unlink the entry 913 error = _kern_unlink(-1, target); 914 if (error != FSSH_B_OK) { 915 fprintf(stderr, "Error: Failed to remove \"%s\" to make way for " 916 "link: %s\n", target, fssh_strerror(error)); 917 return error; 918 } 919 } 920 921 // finally create the link 922 if (symbolic) { 923 error = _kern_create_symlink(-1, target, source, 924 FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO); 925 } else 926 error = _kern_create_link(target, source); 927 928 if (error != FSSH_B_OK) { 929 fprintf(stderr, "Error: Failed to create link: %s\n", 930 fssh_strerror(error)); 931 } 932 933 return error; 934 } 935 936 937 static fssh_status_t 938 command_ls(int argc, const char* const* argv) 939 { 940 const char* const currentDirFiles[] = { ".", NULL }; 941 const char* const* files; 942 if (argc >= 2) 943 files = argv + 1; 944 else 945 files = currentDirFiles; 946 947 for (; *files; files++) { 948 const char* file = *files; 949 // stat file 950 struct fssh_stat st; 951 fssh_status_t error = _kern_read_stat(-1, file, false, &st, sizeof(st)); 952 if (error != FSSH_B_OK) { 953 fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", file, 954 fssh_strerror(error)); 955 continue; 956 } 957 958 // if it is a directory, print its entries 959 if (FSSH_S_ISDIR(st.fssh_st_mode)) { 960 printf("%s:\n", file); 961 962 // open dir 963 int fd = _kern_open_dir(-1, file); 964 if (fd < 0) { 965 fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", 966 file, fssh_strerror(fd)); 967 continue; 968 } 969 970 // iterate through the entries 971 char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; 972 fssh_dirent* entry = (fssh_dirent*)buffer; 973 fssh_ssize_t entriesRead = 0; 974 while ((entriesRead = _kern_read_dir(fd, entry, sizeof(buffer), 1)) 975 == 1) { 976 list_entry(file, entry->d_name); 977 } 978 979 if (entriesRead < 0) { 980 fprintf(stderr, "Error: reading dir \"%s\" failed: %s\n", 981 file, fssh_strerror(entriesRead)); 982 } 983 984 // close dir 985 error = _kern_close(fd); 986 if (error != FSSH_B_OK) { 987 fprintf(stderr, "Error: Closing dir \"%s\" (fd: %d) failed: " 988 "%s\n", file, fd, fssh_strerror(error)); 989 continue; 990 } 991 } else 992 list_entry(file); 993 } 994 995 return FSSH_B_OK; 996 } 997 998 999 static fssh_status_t 1000 command_touch(int argc, const char* const* argv) 1001 { 1002 int argi = 1; 1003 if (argi >= argc) { 1004 printf("Usage: %s <file>...\n", argv[0]); 1005 return FSSH_B_BAD_VALUE; 1006 } 1007 1008 for (; argi < argc; argi++) { 1009 const char* file = argv[argi]; 1010 if (strlen(file) == 0) { 1011 fprintf(stderr, "Error: An empty path is not a valid argument!\n"); 1012 return FSSH_B_BAD_VALUE; 1013 } 1014 1015 fprintf(stderr, "creating file: %s\n", file); 1016 fssh_status_t error = create_file(file); 1017 if (error != FSSH_B_OK) 1018 return error; 1019 } 1020 1021 return FSSH_B_OK; 1022 } 1023 1024 1025 static fssh_status_t 1026 command_mkdir(int argc, const char* const* argv) 1027 { 1028 bool createParents = false; 1029 1030 // parse parameters 1031 int argi = 1; 1032 for (argi = 1; argi < argc; argi++) { 1033 const char *arg = argv[argi]; 1034 if (arg[0] != '-') 1035 break; 1036 1037 if (arg[1] == '\0') { 1038 fprintf(stderr, "Error: Invalid option \"-\"\n"); 1039 return FSSH_B_BAD_VALUE; 1040 } 1041 1042 for (int i = 1; arg[i]; i++) { 1043 switch (arg[i]) { 1044 case 'p': 1045 createParents = true; 1046 break; 1047 default: 1048 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 1049 return FSSH_B_BAD_VALUE; 1050 } 1051 } 1052 } 1053 1054 if (argi >= argc) { 1055 printf("Usage: %s [ -p ] <dir>...\n", argv[0]); 1056 return FSSH_B_BAD_VALUE; 1057 } 1058 1059 // create loop 1060 for (; argi < argc; argi++) { 1061 const char *dir = argv[argi]; 1062 if (strlen(dir) == 0) { 1063 fprintf(stderr, "Error: An empty path is not a valid argument!\n"); 1064 return FSSH_B_BAD_VALUE; 1065 } 1066 1067 fssh_status_t error = create_dir(dir, createParents); 1068 if (error != FSSH_B_OK) 1069 return error; 1070 } 1071 1072 return FSSH_B_OK; 1073 } 1074 1075 1076 static fssh_status_t 1077 command_mkindex(int argc, const char* const* argv) 1078 { 1079 if (argc < 2) { 1080 fprintf(stderr, "Usage: %s [-t <type>] <index name>\n", argv[0]); 1081 return FSSH_B_BAD_VALUE; 1082 } 1083 1084 int fileArg = 1; 1085 int type = FSSH_B_STRING_TYPE; 1086 1087 if (argc > 3 && strcmp(argv[1], "-t") == 0) { 1088 fileArg = 3; 1089 if (strcmp(argv[2], "string") == 0) 1090 type = FSSH_B_STRING_TYPE; 1091 else if (strcmp(argv[2], "int32") == 0) 1092 type = FSSH_B_INT32_TYPE; 1093 else { 1094 fprintf(stderr, "Unhandled attribute type %s\n", argv[2]); 1095 return FSSH_B_BAD_VALUE; 1096 } 1097 } 1098 1099 const char* indexName = argv[fileArg]; 1100 1101 // get the volume ID 1102 fssh_dev_t volumeID = get_volume_id(); 1103 if (volumeID < 0) 1104 return volumeID; 1105 1106 // create the index 1107 fssh_status_t error =_kern_create_index(volumeID, indexName, type, 0); 1108 if (error != FSSH_B_OK) { 1109 fprintf(stderr, "Error: Failed to create index \"%s\": %s\n", 1110 indexName, fssh_strerror(error)); 1111 return error; 1112 } 1113 1114 return FSSH_B_OK; 1115 } 1116 1117 1118 static fssh_status_t 1119 command_mv(int argc, const char* const* argv) 1120 { 1121 bool force = false; 1122 1123 // parse parameters 1124 int argi = 1; 1125 for (argi = 1; argi < argc; argi++) { 1126 const char *arg = argv[argi]; 1127 if (arg[0] != '-') 1128 break; 1129 1130 if (arg[1] == '\0') { 1131 fprintf(stderr, "Error: Invalid option \"-\"\n"); 1132 return FSSH_B_BAD_VALUE; 1133 } 1134 1135 for (int i = 1; arg[i]; i++) { 1136 switch (arg[i]) { 1137 case 'f': 1138 force = true; 1139 break; 1140 default: 1141 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 1142 return FSSH_B_BAD_VALUE; 1143 } 1144 } 1145 } 1146 1147 // check params 1148 int count = argc - 1 - argi; 1149 if (count <= 0) { 1150 fprintf(stderr, "Usage: %s [-f] <file>... <target>\n", argv[0]); 1151 return FSSH_B_BAD_VALUE; 1152 } 1153 1154 const char* target = argv[argc - 1]; 1155 1156 // stat the target 1157 struct fssh_stat st; 1158 fssh_status_t status = _kern_read_stat(-1, target, true, &st, sizeof(st)); 1159 if (status != FSSH_B_OK && count != 1) { 1160 fprintf(stderr, "Error: Failed to stat target \"%s\": %s\n", target, 1161 fssh_strerror(status)); 1162 return status; 1163 } 1164 1165 if (status == FSSH_B_OK && FSSH_S_ISDIR(st.fssh_st_mode)) { 1166 // move several entries 1167 int targetDir = _kern_open_dir(-1, target); 1168 if (targetDir < 0) { 1169 fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", target, 1170 fssh_strerror(targetDir)); 1171 return targetDir; 1172 } 1173 1174 // move loop 1175 for (; argi < argc - 1; argi++) { 1176 status = move_entry(-1, argv[argi], targetDir, argv[argi], force); 1177 if (status != FSSH_B_OK) { 1178 _kern_close(targetDir); 1179 return status; 1180 } 1181 } 1182 1183 _kern_close(targetDir); 1184 return FSSH_B_OK; 1185 } 1186 1187 // rename single entry 1188 return move_entry(-1, argv[argi], -1, target, force); 1189 } 1190 1191 1192 static fssh_status_t 1193 command_query(int argc, const char* const* argv) 1194 { 1195 if (argc != 2) { 1196 fprintf(stderr, "Usage: %s <query string>\n", argv[0]); 1197 return FSSH_B_BAD_VALUE; 1198 } 1199 1200 const char* query = argv[1]; 1201 1202 // get the volume ID 1203 fssh_dev_t volumeID = get_volume_id(); 1204 if (volumeID < 0) 1205 return volumeID; 1206 1207 // open query 1208 int fd = _kern_open_query(volumeID, query, strlen(query), 0, -1, -1); 1209 if (fd < 0) { 1210 fprintf(stderr, "Error: Failed to open query: %s\n", fssh_strerror(fd)); 1211 return fd; 1212 } 1213 1214 // iterate through the entries 1215 fssh_status_t error = FSSH_B_OK; 1216 char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH]; 1217 fssh_dirent* entry = (fssh_dirent*)buffer; 1218 fssh_ssize_t entriesRead = 0; 1219 while ((entriesRead = _kern_read_dir(fd, entry, sizeof(buffer), 1)) == 1) { 1220 char path[FSSH_B_PATH_NAME_LENGTH]; 1221 error = _kern_entry_ref_to_path(volumeID, entry->d_pino, entry->d_name, 1222 path, sizeof(path)); 1223 if (error == FSSH_B_OK) { 1224 printf(" %s\n", path); 1225 } else { 1226 fprintf(stderr, " failed to resolve entry (%8" FSSH_B_PRIdINO 1227 ", \"%s\")\n", entry->d_pino, entry->d_name); 1228 } 1229 } 1230 1231 if (entriesRead < 0) { 1232 fprintf(stderr, "Error: reading query failed: %s\n", 1233 fssh_strerror(entriesRead)); 1234 } 1235 1236 // close query 1237 error = _kern_close(fd); 1238 if (error != FSSH_B_OK) { 1239 fprintf(stderr, "Error: Closing query (fd: %d) failed: %s\n", 1240 fd, fssh_strerror(error)); 1241 } 1242 1243 return error; 1244 } 1245 1246 1247 static fssh_status_t 1248 command_quit(int argc, const char* const* argv) 1249 { 1250 return COMMAND_RESULT_EXIT; 1251 } 1252 1253 1254 static fssh_status_t 1255 command_rm(int argc, const char* const* argv) 1256 { 1257 bool recursive = false; 1258 bool force = false; 1259 1260 // parse parameters 1261 int argi = 1; 1262 for (argi = 1; argi < argc; argi++) { 1263 const char *arg = argv[argi]; 1264 if (arg[0] != '-') 1265 break; 1266 1267 if (arg[1] == '\0') { 1268 fprintf(stderr, "Error: Invalid option \"-\"\n"); 1269 return FSSH_B_BAD_VALUE; 1270 } 1271 1272 for (int i = 1; arg[i]; i++) { 1273 switch (arg[i]) { 1274 case 'f': 1275 force = true; 1276 break; 1277 case 'r': 1278 recursive = true; 1279 break; 1280 default: 1281 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]); 1282 return FSSH_B_BAD_VALUE; 1283 } 1284 } 1285 } 1286 1287 // check params 1288 if (argi >= argc) { 1289 fprintf(stderr, "Usage: %s [ -r ] <file>...\n", argv[0]); 1290 return FSSH_B_BAD_VALUE; 1291 } 1292 1293 // remove loop 1294 for (; argi < argc; argi++) { 1295 fssh_status_t error = remove_entry(-1, argv[argi], recursive, force); 1296 if (error != FSSH_B_OK) 1297 return error; 1298 } 1299 1300 return FSSH_B_OK; 1301 } 1302 1303 1304 static fssh_status_t 1305 command_sync(int argc, const char* const* argv) 1306 { 1307 fssh_status_t error = _kern_sync(); 1308 if (error != FSSH_B_OK) { 1309 fprintf(stderr, "Error: syncing: %s\n", fssh_strerror(error)); 1310 return error; 1311 } 1312 1313 return FSSH_B_OK; 1314 } 1315 1316 1317 static fssh_status_t 1318 command_ioctl(int argc, const char* const* argv) 1319 { 1320 if (argc != 2) { 1321 fprintf(stderr, "Usage: %s <opcode>\n", argv[0]); 1322 return FSSH_B_BAD_VALUE; 1323 } 1324 1325 int rootDir = _kern_open_dir(-1, "/myfs"); 1326 if (rootDir < 0) 1327 return rootDir; 1328 1329 fssh_status_t status = _kern_ioctl(rootDir, atoi(argv[1]), NULL, 0); 1330 1331 _kern_close(rootDir); 1332 1333 if (status != FSSH_B_OK) { 1334 fprintf(stderr, "Error: ioctl failed: %s\n", fssh_strerror(status)); 1335 return status; 1336 } 1337 1338 return FSSH_B_OK; 1339 } 1340 1341 1342 static void 1343 register_commands() 1344 { 1345 CommandManager::Default()->AddCommands( 1346 command_cd, "cd", "change current directory", 1347 command_chmod, "chmod", "change file permissions", 1348 command_cp, "cp", "copy files and directories", 1349 command_cat, "cat", "concatenate file(s) to stdout", 1350 command_help, "help", "list supported commands", 1351 command_info, "info", "prints volume informations", 1352 command_ioctl, "ioctl", "ioctl() on root, for FS debugging only", 1353 command_ln, "ln", "create a hard or symbolic link", 1354 command_ls, "ls", "list files or directories", 1355 command_mkdir, "mkdir", "create directories", 1356 command_mkindex, "mkindex", "create an index", 1357 command_mv, "mv", "move/rename files and directories", 1358 command_query, "query", "query for files", 1359 command_quit, "quit/exit", "quit the shell", 1360 command_rm, "rm", "remove files and directories", 1361 command_sync, "sync", "syncs the file system", 1362 command_touch, "touch", "create empty file", 1363 NULL 1364 ); 1365 } 1366 1367 1368 // #pragma mark - ArgVector 1369 1370 1371 class ArgVector { 1372 public: 1373 ArgVector() 1374 : fArgc(0), 1375 fArgv(NULL) 1376 { 1377 } 1378 1379 ~ArgVector() 1380 { 1381 _Cleanup(); 1382 } 1383 1384 int Argc() const 1385 { 1386 return fArgc; 1387 } 1388 1389 const char* const* Argv() const 1390 { 1391 return fArgv; 1392 } 1393 1394 bool Parse(const char* commandLine) 1395 { 1396 _Cleanup(); 1397 1398 // init temporary arg/argv storage 1399 std::string currentArg; 1400 std::vector<std::string> argVector; 1401 1402 fCurrentArg = ¤tArg; 1403 fCurrentArgStarted = false; 1404 fArgVector = &argVector; 1405 1406 for (; *commandLine; commandLine++) { 1407 char c = *commandLine; 1408 1409 // whitespace delimits args and is otherwise ignored 1410 if (isspace(c)) { 1411 _PushCurrentArg(); 1412 continue; 1413 } 1414 1415 switch (c) { 1416 case '\'': 1417 // quoted string -- no quoting 1418 while (*++commandLine != '\'') { 1419 c = *commandLine; 1420 if (c == '\0') { 1421 fprintf(stderr, "Error: Unterminated quoted " 1422 "string.\n"); 1423 return false; 1424 } 1425 _PushCharacter(c); 1426 } 1427 break; 1428 1429 case '"': 1430 // quoted string -- some quoting 1431 while (*++commandLine != '"') { 1432 c = *commandLine; 1433 if (c == '\0') { 1434 fprintf(stderr, "Error: Unterminated quoted " 1435 "string.\n"); 1436 return false; 1437 } 1438 1439 if (c == '\\') { 1440 c = *++commandLine; 1441 if (c == '\0') { 1442 fprintf(stderr, "Error: Unterminated quoted " 1443 "string.\n"); 1444 return false; 1445 } 1446 1447 // only '\' and '"' can be quoted, otherwise the 1448 // the '\' is treated as a normal char 1449 if (c != '\\' && c != '"') 1450 _PushCharacter('\\'); 1451 } 1452 1453 _PushCharacter(c); 1454 } 1455 break; 1456 1457 case '\\': 1458 // quoted char 1459 c = *++commandLine; 1460 if (c == '\0') { 1461 fprintf(stderr, "Error: Command line ends with " 1462 "'\\'.\n"); 1463 return false; 1464 } 1465 _PushCharacter(c); 1466 break; 1467 1468 default: 1469 // normal char 1470 _PushCharacter(c); 1471 break; 1472 } 1473 } 1474 1475 // commit last arg 1476 _PushCurrentArg(); 1477 1478 // build arg vector 1479 fArgc = argVector.size(); 1480 fArgv = new char*[fArgc + 1]; 1481 for (int i = 0; i < fArgc; i++) { 1482 int len = argVector[i].length(); 1483 fArgv[i] = new char[len + 1]; 1484 memcpy(fArgv[i], argVector[i].c_str(), len + 1); 1485 } 1486 fArgv[fArgc] = NULL; 1487 1488 return true; 1489 } 1490 1491 private: 1492 void _Cleanup() 1493 { 1494 if (fArgv) { 1495 for (int i = 0; i < fArgc; i++) 1496 delete[] fArgv[i]; 1497 delete[] fArgv; 1498 } 1499 } 1500 1501 void _PushCurrentArg() 1502 { 1503 if (fCurrentArgStarted) { 1504 fArgVector->push_back(*fCurrentArg); 1505 fCurrentArgStarted = false; 1506 } 1507 } 1508 1509 void _PushCharacter(char c) 1510 { 1511 if (!fCurrentArgStarted) { 1512 *fCurrentArg = ""; 1513 fCurrentArgStarted = true; 1514 } 1515 1516 *fCurrentArg += c; 1517 } 1518 1519 private: 1520 // temporaries 1521 std::string* fCurrentArg; 1522 bool fCurrentArgStarted; 1523 std::vector<std::string>* fArgVector; 1524 1525 int fArgc; 1526 char** fArgv; 1527 }; 1528 1529 1530 // #pragma mark - input loop 1531 1532 1533 static char* 1534 read_command_line(char* buffer, int bufferSize) 1535 { 1536 // print prompt (including cwd, if available) 1537 char directory[FSSH_B_PATH_NAME_LENGTH]; 1538 if (_kern_getcwd(directory, sizeof(directory)) == FSSH_B_OK) 1539 printf("fssh:%s> ", directory); 1540 else 1541 printf("fssh> "); 1542 fflush(stdout); 1543 1544 // read input line 1545 return fgets(buffer, bufferSize, stdin); 1546 } 1547 1548 1549 static void 1550 input_loop(bool interactive) 1551 { 1552 static const int kInputBufferSize = 100 * 1024; 1553 char* inputBuffer = new char[kInputBufferSize]; 1554 1555 for (;;) { 1556 // read command line 1557 if (interactive) { 1558 if (!read_command_line(inputBuffer, kInputBufferSize)) 1559 break; 1560 } else { 1561 if (!get_external_command(inputBuffer, kInputBufferSize)) 1562 break; 1563 } 1564 1565 // construct argv vector 1566 int result = FSSH_B_BAD_VALUE; 1567 ArgVector argVector; 1568 if (argVector.Parse(inputBuffer) && argVector.Argc() > 0) { 1569 int argc = argVector.Argc(); 1570 const char* const* argv = argVector.Argv(); 1571 1572 // find command 1573 Command* command = CommandManager::Default()->FindCommand(argv[0]); 1574 if (command) { 1575 // execute it 1576 result = command->Do(argc, argv); 1577 if (result == COMMAND_RESULT_EXIT) { 1578 if (!interactive) 1579 reply_to_external_command(0); 1580 break; 1581 } 1582 } else { 1583 fprintf(stderr, "Error: Invalid command \"%s\". Type \"help\" " 1584 "for a list of supported commands\n", argv[0]); 1585 } 1586 } 1587 1588 if (!interactive) 1589 reply_to_external_command(fssh_to_host_error(result)); 1590 } 1591 1592 if (!interactive) 1593 external_command_cleanup(); 1594 1595 delete[] inputBuffer; 1596 } 1597 1598 1599 static int 1600 standard_session(const char* device, const char* fsName, bool interactive) 1601 { 1602 // mount FS 1603 fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0); 1604 if (fsDev < 0) { 1605 fprintf(stderr, "Error: Mounting FS failed: %s\n", 1606 fssh_strerror(fsDev)); 1607 return 1; 1608 } 1609 1610 // register commands 1611 register_commands(); 1612 register_additional_commands(); 1613 1614 // process commands 1615 input_loop(interactive); 1616 1617 // unmount FS 1618 _kern_setcwd(-1, "/"); // avoid a "busy" vnode 1619 fssh_status_t error = _kern_unmount(kMountPoint, 0); 1620 if (error != FSSH_B_OK) { 1621 fprintf(stderr, "Error: Unmounting FS failed: %s\n", 1622 fssh_strerror(error)); 1623 return 1; 1624 } 1625 1626 return 0; 1627 } 1628 1629 1630 static int 1631 initialization_session(const char* device, const char* fsName, 1632 const char* volumeName, const char* initParameters) 1633 { 1634 fssh_status_t error = _kern_initialize_volume(fsName, device, 1635 volumeName, initParameters); 1636 if (error != FSSH_B_OK) { 1637 fprintf(stderr, "Error: Initializing volume failed: %s\n", 1638 fssh_strerror(error)); 1639 return 1; 1640 } 1641 1642 return 0; 1643 } 1644 1645 1646 static void 1647 print_usage(bool error) 1648 { 1649 fprintf((error ? stderr : stdout), 1650 "Usage: %s [ --start-offset <startOffset>]\n" 1651 " [ --end-offset <endOffset>] [-n] <device>\n" 1652 " %s [ --start-offset <startOffset>]\n" 1653 " [ --end-offset <endOffset>]\n" 1654 " --initialize [-n] <device> <volume name> " 1655 "[ <init parameters> ]\n", 1656 sArgv[0], sArgv[0] 1657 ); 1658 } 1659 1660 1661 static void 1662 print_usage_and_exit(bool error) 1663 { 1664 print_usage(error); 1665 exit(error ? 1 : 0); 1666 } 1667 1668 1669 } // namespace FSShell 1670 1671 1672 using namespace FSShell; 1673 1674 1675 int 1676 main(int argc, const char* const* argv) 1677 { 1678 sArgc = argc; 1679 sArgv = argv; 1680 1681 // process arguments 1682 bool interactive = true; 1683 bool initialize = false; 1684 const char* device = NULL; 1685 const char* volumeName = NULL; 1686 const char* initParameters = NULL; 1687 fssh_off_t startOffset = 0; 1688 fssh_off_t endOffset = -1; 1689 1690 // eat options 1691 int argi = 1; 1692 while (argi < argc && argv[argi][0] == '-') { 1693 const char* arg = argv[argi++]; 1694 if (strcmp(arg, "--help") == 0) { 1695 print_usage_and_exit(false); 1696 } else if (strcmp(arg, "--initialize") == 0) { 1697 initialize = true; 1698 } else if (strcmp(arg, "-n") == 0) { 1699 interactive = false; 1700 } else if (strcmp(arg, "--start-offset") == 0) { 1701 if (argi >= argc) 1702 print_usage_and_exit(true); 1703 startOffset = atoll(argv[argi++]); 1704 } else if (strcmp(arg, "--end-offset") == 0) { 1705 if (argi >= argc) 1706 print_usage_and_exit(true); 1707 endOffset = atoll(argv[argi++]); 1708 } else { 1709 print_usage_and_exit(true); 1710 } 1711 } 1712 1713 // get device 1714 if (argi >= argc) 1715 print_usage_and_exit(true); 1716 device = argv[argi++]; 1717 1718 // get volume name and init parameters 1719 if (initialize) { 1720 // volume name 1721 if (argi >= argc) 1722 print_usage_and_exit(true); 1723 volumeName = argv[argi++]; 1724 1725 // (optional) init paramaters 1726 if (argi < argc) 1727 initParameters = argv[argi++]; 1728 } 1729 1730 // more parameters are excess 1731 if (argi < argc) 1732 print_usage_and_exit(true); 1733 1734 // get FS module 1735 if (!modules[0]) { 1736 fprintf(stderr, "Error: Couldn't find FS module!\n"); 1737 return 1; 1738 } 1739 const char* fsName = modules[0]->name; 1740 1741 fssh_status_t error; 1742 1743 // init kernel 1744 error = init_kernel(); 1745 if (error != FSSH_B_OK) { 1746 fprintf(stderr, "Error: Initializing kernel failed: %s\n", 1747 fssh_strerror(error)); 1748 return error; 1749 } 1750 1751 // restrict access if requested 1752 if (startOffset != 0 || endOffset != -1) 1753 add_file_restriction(device, startOffset, endOffset); 1754 1755 // start the action 1756 int result; 1757 if (initialize) { 1758 result = initialization_session(device, fsName, volumeName, 1759 initParameters); 1760 } else 1761 result = standard_session(device, fsName, interactive); 1762 1763 return result; 1764 } 1765