Fix FreeBSD build.
[haiku.git] / src / tools / fs_shell / fssh.cpp
blobbcff629a7120d35a91efb738b6caf1674ff51825
1 /*
2 * Copyright 2007-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "compatibility.h"
9 #include "fssh.h"
11 #include <stdarg.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <time.h>
15 #include <unistd.h>
16 #include <stdlib.h>
18 #include <vector>
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_module.h"
29 #include "fssh_node_monitor.h"
30 #include "fssh_stat.h"
31 #include "fssh_string.h"
32 #include "fssh_type_constants.h"
33 #include "module.h"
34 #include "partition_support.h"
35 #include "path_util.h"
36 #include "syscalls.h"
37 #include "vfs.h"
40 extern fssh_module_info *modules[];
43 extern fssh_file_system_module_info gRootFileSystem;
45 namespace FSShell {
47 const char* kMountPoint = "/myfs";
49 // command line args
50 static int sArgc;
51 static const char* const* sArgv;
53 static mode_t sUmask = 0022;
56 static fssh_status_t
57 init_kernel()
59 fssh_status_t error;
61 // init module subsystem
62 error = module_init(NULL);
63 if (error != FSSH_B_OK) {
64 fprintf(stderr, "module_init() failed: %s\n", fssh_strerror(error));
65 return error;
68 // init driver settings
69 error = driver_settings_init();
70 if (error != FSSH_B_OK) {
71 fprintf(stderr, "initializing driver settings failed: %s\n",
72 fssh_strerror(error));
73 return error;
76 // register built-in modules, i.e. the rootfs and the client FS
77 register_builtin_module(&gRootFileSystem.info);
78 for (int i = 0; modules[i]; i++)
79 register_builtin_module(modules[i]);
81 // init VFS
82 error = vfs_init(NULL);
83 if (error != FSSH_B_OK) {
84 fprintf(stderr, "initializing VFS failed: %s\n", fssh_strerror(error));
85 return error;
88 // init kernel IO context
89 gKernelIOContext = (io_context*)vfs_new_io_context(NULL);
90 if (!gKernelIOContext) {
91 fprintf(stderr, "creating IO context failed!\n");
92 return FSSH_B_NO_MEMORY;
95 // mount root FS
96 fssh_dev_t rootDev = _kern_mount("/", NULL, "rootfs", 0, NULL, 0);
97 if (rootDev < 0) {
98 fprintf(stderr, "mounting rootfs failed: %s\n", fssh_strerror(rootDev));
99 return rootDev;
102 // set cwd to "/"
103 error = _kern_setcwd(-1, "/");
104 if (error != FSSH_B_OK) {
105 fprintf(stderr, "setting cwd failed: %s\n", fssh_strerror(error));
106 return error;
109 // create mount point for the client FS
110 error = _kern_create_dir(-1, kMountPoint, 0775);
111 if (error != FSSH_B_OK) {
112 fprintf(stderr, "creating mount point failed: %s\n",
113 fssh_strerror(error));
114 return error;
117 return FSSH_B_OK;
121 // #pragma mark - Command
123 Command::Command(const char* name, const char* description)
124 : fName(name),
125 fDescription(description)
130 Command::Command(command_function* function, const char* name,
131 const char* description)
132 : fName(name),
133 fDescription(description),
134 fFunction(function)
139 Command::~Command()
144 const char*
145 Command::Name() const
147 return fName.c_str();
151 const char*
152 Command::Description() const
154 return fDescription.c_str();
158 fssh_status_t
159 Command::Do(int argc, const char* const* argv)
161 if (!fFunction) {
162 fprintf(stderr, "No function given for command \"%s\"\n", Name());
163 return FSSH_B_BAD_VALUE;
166 return (*fFunction)(argc, argv);
170 // #pragma mark - CommandManager
172 CommandManager::CommandManager()
177 CommandManager*
178 CommandManager::Default()
180 if (!sManager)
181 sManager = new CommandManager;
182 return sManager;
186 void
187 CommandManager::AddCommand(Command* command)
189 // The command name may consist of several aliases. Split them and
190 // register the command for each of them.
191 char _names[1024];
192 char* names = _names;
193 strcpy(names, command->Name());
195 char* cookie;
196 while (char* name = strtok_r(names, " /", &cookie)) {
197 fCommands[name] = command;
198 names = NULL;
203 void
204 CommandManager::AddCommand(command_function* function, const char* name,
205 const char* description)
207 AddCommand(new Command(function, name, description));
211 void
212 CommandManager::AddCommands(command_function* function, const char* name,
213 const char* description, ...)
215 va_list args;
216 va_start(args, description);
218 while (function) {
219 AddCommand(function, name, description);
221 function = va_arg(args, command_function*);
222 if (function) {
223 name = va_arg(args, const char*);
224 description = va_arg(args, const char*);
228 va_end(args);
232 Command*
233 CommandManager::FindCommand(const char* name) const
235 CommandMap::const_iterator it = fCommands.find(name);
236 if (it == fCommands.end())
237 return NULL;
239 return it->second;
243 void
244 CommandManager::ListCommands() const
246 for (CommandMap::const_iterator it = fCommands.begin();
247 it != fCommands.end(); ++it) {
248 const char* name = it->first.c_str();
249 Command* command = it->second;
250 printf("%-16s - %s\n", name, command->Description());
255 CommandManager* CommandManager::sManager = NULL;
258 // #pragma mark - Command support functions
261 static bool
262 get_permissions(const char* modeString, fssh_mode_t& _permissions)
264 // currently only octal mode is supported
265 if (strlen(modeString) != 3)
266 return false;
268 fssh_mode_t permissions = 0;
269 for (int i = 0; i < 3; i++) {
270 char c = modeString[i];
271 if (c < '0' || c > '7')
272 return false;
273 permissions = (permissions << 3) | (c - '0');
276 _permissions = permissions;
277 return true;
281 static fssh_dev_t
282 get_volume_id()
284 struct fssh_stat st;
285 fssh_status_t error = _kern_read_stat(-1, kMountPoint, false, &st,
286 sizeof(st));
287 if (error != FSSH_B_OK) {
288 fprintf(stderr, "Error: Failed to stat() mount point: %s\n",
289 fssh_strerror(error));
290 return error;
293 return st.fssh_st_dev;
297 static const char *
298 byte_string(int64_t numBlocks, int64_t blockSize)
300 double blocks = 1. * numBlocks * blockSize;
301 static char string[64];
303 if (blocks < 1024)
304 sprintf(string, "%" FSSH_B_PRId64, numBlocks * blockSize);
305 else {
306 const char* units[] = {"K", "M", "G", NULL};
307 int i = -1;
309 do {
310 blocks /= 1024.0;
311 i++;
312 } while (blocks >= 1024 && units[i + 1]);
314 sprintf(string, "%.1f%s", blocks, units[i]);
317 return string;
321 void
322 print_flag(uint32_t deviceFlags, uint32_t testFlag, const char *yes,
323 const char *no)
325 printf("%s", (deviceFlags & testFlag) != 0 ? yes : no);
329 static void
330 list_entry(const char* file, const char* name = NULL)
332 // construct path, if a leaf name is given
333 std::string path;
334 if (name) {
335 path = file;
336 path += '/';
337 path += name;
338 file = path.c_str();
339 } else
340 name = file;
342 // stat the file
343 struct fssh_stat st;
344 fssh_status_t error = _kern_read_stat(-1, file, false, &st, sizeof(st));
345 if (error != FSSH_B_OK) {
346 fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", file,
347 fssh_strerror(error));
348 return;
351 // get time
352 struct tm time;
353 time_t fileTime = st.fssh_st_mtime;
354 localtime_r(&fileTime, &time);
356 // get permissions
357 std::string permissions;
358 fssh_mode_t mode = st.fssh_st_mode;
359 // user
360 permissions += ((mode & FSSH_S_IRUSR) ? 'r' : '-');
361 permissions += ((mode & FSSH_S_IWUSR) ? 'w' : '-');
362 if (mode & FSSH_S_ISUID)
363 permissions += 's';
364 else
365 permissions += ((mode & FSSH_S_IXUSR) ? 'x' : '-');
366 // group
367 permissions += ((mode & FSSH_S_IRGRP) ? 'r' : '-');
368 permissions += ((mode & FSSH_S_IWGRP) ? 'w' : '-');
369 if (mode & FSSH_S_ISGID)
370 permissions += 's';
371 else
372 permissions += ((mode & FSSH_S_IXGRP) ? 'x' : '-');
373 // others
374 permissions += ((mode & FSSH_S_IROTH) ? 'r' : '-');
375 permissions += ((mode & FSSH_S_IWOTH) ? 'w' : '-');
376 permissions += ((mode & FSSH_S_IXOTH) ? 'x' : '-');
378 // get file type
379 char fileType = '?';
380 if (FSSH_S_ISREG(mode)) {
381 fileType = '-';
382 } else if (FSSH_S_ISLNK(mode)) {
383 fileType = 'l';
384 } else if (FSSH_S_ISBLK(mode)) {
385 fileType = 'b';
386 } else if (FSSH_S_ISDIR(mode)) {
387 fileType = 'd';
388 } else if (FSSH_S_ISCHR(mode)) {
389 fileType = 'c';
390 } else if (FSSH_S_ISFIFO(mode)) {
391 fileType = 'f';
392 } else if (FSSH_S_ISINDEX(mode)) {
393 fileType = 'i';
396 // get link target
397 std::string nameSuffix;
398 if (FSSH_S_ISLNK(mode)) {
399 char buffer[FSSH_B_PATH_NAME_LENGTH];
400 fssh_size_t size = sizeof(buffer) - 1;
401 error = _kern_read_link(-1, file, buffer, &size);
402 if (error != FSSH_B_OK)
403 snprintf(buffer, sizeof(buffer), "(%s)", fssh_strerror(error));
405 buffer[size] = '\0';
406 nameSuffix += " -> ";
407 nameSuffix += buffer;
410 printf("%c%s %2d %2d %10" FSSH_B_PRIdOFF
411 " %d-%02d-%02d %02d:%02d:%02d %s%s\n",
412 fileType, permissions.c_str(), (int)st.fssh_st_uid, (int)st.fssh_st_gid,
413 st.fssh_st_size,
414 1900 + time.tm_year, 1 + time.tm_mon, time.tm_mday,
415 time.tm_hour, time.tm_min, time.tm_sec,
416 name, nameSuffix.c_str());
420 static fssh_status_t
421 create_dir(const char *path, bool createParents)
423 // stat the entry
424 struct fssh_stat st;
425 fssh_status_t error = _kern_read_stat(-1, path, false, &st, sizeof(st));
426 if (error == FSSH_B_OK) {
427 if (createParents && FSSH_S_ISDIR(st.fssh_st_mode))
428 return FSSH_B_OK;
430 fprintf(stderr, "Error: Cannot make dir, entry \"%s\" is in the way.\n",
431 path);
432 return FSSH_B_FILE_EXISTS;
435 // the dir doesn't exist yet
436 // if we shall create all parents, do that first
437 if (createParents) {
438 // create the parent dir path
439 // eat the trailing '/'s
440 int len = strlen(path);
441 while (len > 0 && path[len - 1] == '/')
442 len--;
444 // eat the last path component
445 while (len > 0 && path[len - 1] != '/')
446 len--;
448 // eat the trailing '/'s
449 while (len > 0 && path[len - 1] == '/')
450 len--;
452 // Now either nothing remains, which means we had a single component,
453 // a root subdir -- in those cases we can just fall through (we should
454 // actually never be here in case of the root dir, but anyway) -- or
455 // there is something left, which we can call a parent directory and
456 // try to create it.
457 if (len > 0) {
458 char *parentPath = (char*)malloc(len + 1);
459 if (!parentPath) {
460 fprintf(stderr, "Error: Failed to allocate memory for parent "
461 "path.\n");
462 return FSSH_B_NO_MEMORY;
464 memcpy(parentPath, path, len);
465 parentPath[len] = '\0';
467 error = create_dir(parentPath, createParents);
469 free(parentPath);
471 if (error != FSSH_B_OK)
472 return error;
476 // make the directory
477 error = _kern_create_dir(-1,
478 path, (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask);
479 if (error != FSSH_B_OK) {
480 fprintf(stderr, "Error: Failed to make directory \"%s\": %s\n", path,
481 fssh_strerror(error));
482 return error;
485 return FSSH_B_OK;
489 static fssh_status_t remove_entry(int dir, const char *entry, bool recursive,
490 bool force);
493 static fssh_status_t
494 remove_dir_contents(int parentDir, const char *name, bool force)
496 // open the dir
497 int dir = _kern_open_dir(parentDir, name);
498 if (dir < 0) {
499 fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", name,
500 fssh_strerror(dir));
501 return dir;
504 fssh_status_t error = FSSH_B_OK;
506 // iterate through the entries
507 fssh_ssize_t numRead;
508 char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
509 fssh_dirent *entry = (fssh_dirent*)buffer;
510 while ((numRead = _kern_read_dir(dir, entry, sizeof(buffer), 1)) > 0) {
511 // skip "." and ".."
512 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
513 continue;
515 error = remove_entry(dir, entry->d_name, true, force);
516 if (error != FSSH_B_OK)
517 break;
520 if (numRead < 0) {
521 fprintf(stderr, "Error: Failed to read directory \"%s\": %s\n", name,
522 fssh_strerror(numRead));
523 error = numRead;
526 // close
527 _kern_close(dir);
529 return error;
533 static fssh_status_t
534 remove_entry(int dir, const char *entry, bool recursive, bool force)
536 // stat the file
537 struct fssh_stat st;
538 fssh_status_t error = _kern_read_stat(dir, entry, false, &st, sizeof(st));
539 if (error != FSSH_B_OK) {
540 if (force && error == FSSH_B_ENTRY_NOT_FOUND)
541 return FSSH_B_OK;
543 fprintf(stderr, "Error: Failed to remove \"%s\": %s\n", entry,
544 fssh_strerror(error));
545 return error;
548 if (FSSH_S_ISDIR(st.fssh_st_mode)) {
549 if (!recursive) {
550 fprintf(stderr, "Error: \"%s\" is a directory.\n", entry);
551 // TODO: get the full path
552 return FSSH_EISDIR;
555 // remove the contents
556 error = remove_dir_contents(dir, entry, force);
557 if (error != FSSH_B_OK)
558 return error;
560 // remove the directory
561 error = _kern_remove_dir(dir, entry);
562 if (error != FSSH_B_OK) {
563 fprintf(stderr, "Error: Failed to remove directory \"%s\": %s\n",
564 entry, fssh_strerror(error));
565 return error;
567 } else {
568 // remove the entry
569 error = _kern_unlink(dir, entry);
570 if (error != FSSH_B_OK) {
571 fprintf(stderr, "Error: Failed to remove entry \"%s\": %s\n", entry,
572 fssh_strerror(error));
573 return error;
577 return FSSH_B_OK;
581 static fssh_status_t
582 move_entry(int dir, const char *entry, int targetDir, const char* target,
583 bool force)
585 // stat the file
586 struct fssh_stat st;
587 fssh_status_t status = _kern_read_stat(dir, entry, false, &st, sizeof(st));
588 if (status != FSSH_B_OK) {
589 if (force && status == FSSH_B_ENTRY_NOT_FOUND)
590 return FSSH_B_OK;
592 fprintf(stderr, "Error: Failed to move \"%s\": %s\n", entry,
593 fssh_strerror(status));
594 return status;
597 return _kern_rename(dir, entry, targetDir, target);
601 // #pragma mark - Commands
604 static fssh_status_t
605 command_cd(int argc, const char* const* argv)
607 if (argc != 2) {
608 fprintf(stderr, "Usage: %s <directory>\n", argv[0]);
609 return FSSH_B_BAD_VALUE;
611 const char* directory = argv[1];
613 fssh_status_t error = FSSH_B_OK;
614 if (directory[0] == ':') {
615 if (chdir(directory + 1) < 0)
616 error = fssh_get_errno();
617 } else
618 error = _kern_setcwd(-1, directory);
620 if (error != FSSH_B_OK) {
621 fprintf(stderr, "Error: cd %s: %s\n", directory, fssh_strerror(error));
622 return error;
625 return FSSH_B_OK;
629 static fssh_status_t
630 command_chmod(int argc, const char* const* argv)
632 bool recursive = false;
634 // parse parameters
635 int argi = 1;
636 for (argi = 1; argi < argc; argi++) {
637 const char *arg = argv[argi];
638 if (arg[0] != '-')
639 break;
641 if (arg[1] == '\0') {
642 fprintf(stderr, "Error: Invalid option \"-\"\n");
643 return FSSH_B_BAD_VALUE;
646 for (int i = 1; arg[i]; i++) {
647 switch (arg[i]) {
648 case 'R':
649 recursive = true;
650 fprintf(stderr, "Sorry, recursive mode not supported "
651 "yet.\n");
652 return FSSH_B_BAD_VALUE;
653 default:
654 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
655 return FSSH_B_BAD_VALUE;
660 // get mode
661 fssh_mode_t permissions;
662 if (argi + 1 >= argc || !get_permissions(argv[argi++], permissions)) {
663 printf("Usage: %s [ -R ] <octal mode> <file>...\n", argv[0]);
664 return FSSH_B_BAD_VALUE;
667 fssh_struct_stat st;
668 st.fssh_st_mode = permissions;
670 // chmod loop
671 for (; argi < argc; argi++) {
672 const char *file = argv[argi];
673 if (strlen(file) == 0) {
674 fprintf(stderr, "Error: An empty path is not a valid argument!\n");
675 return FSSH_B_BAD_VALUE;
678 fssh_status_t error = _kern_write_stat(-1, file, false, &st, sizeof(st),
679 FSSH_B_STAT_MODE);
680 if (error != FSSH_B_OK) {
681 fprintf(stderr, "Error: Failed to change mode of \"%s\"!\n", file);
682 return error;
686 return FSSH_B_OK;
690 static fssh_status_t
691 command_help(int argc, const char* const* argv)
693 printf("supported commands:\n");
694 CommandManager::Default()->ListCommands();
695 return FSSH_B_OK;
699 static fssh_status_t
700 command_info(int argc, const char* const* argv)
702 fssh_dev_t volumeID = get_volume_id();
703 if (volumeID < 0)
704 return volumeID;
706 fssh_fs_info info;
707 fssh_status_t status = _kern_read_fs_info(volumeID, &info);
708 if (status != FSSH_B_OK)
709 return status;
711 printf("root inode: %" FSSH_B_PRIdINO "\n", info.root);
712 printf("flags: ");
713 print_flag(info.flags, FSSH_B_FS_HAS_QUERY, "Q", "-");
714 print_flag(info.flags, FSSH_B_FS_HAS_ATTR, "A", "-");
715 print_flag(info.flags, FSSH_B_FS_HAS_MIME, "M", "-");
716 print_flag(info.flags, FSSH_B_FS_IS_SHARED, "S", "-");
717 print_flag(info.flags, FSSH_B_FS_IS_PERSISTENT, "P", "-");
718 print_flag(info.flags, FSSH_B_FS_IS_REMOVABLE, "R", "-");
719 print_flag(info.flags, FSSH_B_FS_IS_READONLY, "-", "W");
721 printf("\nblock size: %" FSSH_B_PRIdOFF "\n", info.block_size);
722 printf("I/O size: %" FSSH_B_PRIdOFF "\n", info.io_size);
723 printf("total size: %s (%" FSSH_B_PRIdOFF " blocks)\n",
724 byte_string(info.total_blocks, info.block_size), info.total_blocks);
725 printf("free size: %s (%" FSSH_B_PRIdOFF " blocks)\n",
726 byte_string(info.free_blocks, info.block_size), info.free_blocks);
727 printf("total nodes: %" FSSH_B_PRIdOFF "\n", info.total_nodes);
728 printf("free nodes: %" FSSH_B_PRIdOFF "\n", info.free_nodes);
729 printf("volume name: %s\n", info.volume_name);
730 printf("fs name: %s\n", info.fsh_name);
732 return FSSH_B_OK;
736 static fssh_status_t
737 command_ln(int argc, const char* const* argv)
739 bool force = false;
740 bool symbolic = false;
741 bool dereference = true;
743 // parse parameters
744 int argi = 1;
745 for (argi = 1; argi < argc; argi++) {
746 const char *arg = argv[argi];
747 if (arg[0] != '-')
748 break;
750 if (arg[1] == '\0') {
751 fprintf(stderr, "Error: Invalid option \"-\"\n");
752 return FSSH_B_BAD_VALUE;
755 for (int i = 1; arg[i]; i++) {
756 switch (arg[i]) {
757 case 'f':
758 force = true;
759 break;
760 case 's':
761 symbolic = true;
762 break;
763 case 'n':
764 dereference = false;
765 break;
766 default:
767 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
768 return FSSH_B_BAD_VALUE;
773 if (argc - argi != 2) {
774 fprintf(stderr, "Usage: %s [Options] <source> <target>\n", argv[0]);
775 return FSSH_B_BAD_VALUE;
778 const char *source = argv[argi];
779 const char *target = argv[argi + 1];
781 // check, if the the target is an existing directory
782 struct fssh_stat st;
783 char targetBuffer[FSSH_B_PATH_NAME_LENGTH];
784 fssh_status_t error = _kern_read_stat(-1, target, dereference, &st,
785 sizeof(st));
786 if (error == FSSH_B_OK) {
787 if (FSSH_S_ISDIR(st.fssh_st_mode)) {
788 // get source leaf
789 char leaf[FSSH_B_FILE_NAME_LENGTH];
790 error = get_last_path_component(source, leaf, sizeof(leaf));
791 if (error != FSSH_B_OK) {
792 fprintf(stderr, "Error: Failed to get leaf name of source "
793 "path: %s\n", fssh_strerror(error));
794 return error;
797 // compose a new path
798 int len = strlen(target) + 1 + strlen(leaf);
799 if (len > (int)sizeof(targetBuffer)) {
800 fprintf(stderr, "Error: Resulting target path is too long.\n");
801 return FSSH_B_BAD_VALUE;
804 strcpy(targetBuffer, target);
805 strcat(targetBuffer, "/");
806 strcat(targetBuffer, leaf);
807 target = targetBuffer;
811 // check, if the target exists
812 error = _kern_read_stat(-1, target, false, &st, sizeof(st));
813 if (error == FSSH_B_OK) {
814 if (!force) {
815 fprintf(stderr, "Error: Can't create link. \"%s\" is in the way.\n",
816 target);
817 return FSSH_B_FILE_EXISTS;
820 // unlink the entry
821 error = _kern_unlink(-1, target);
822 if (error != FSSH_B_OK) {
823 fprintf(stderr, "Error: Failed to remove \"%s\" to make way for "
824 "link: %s\n", target, fssh_strerror(error));
825 return error;
829 // finally create the link
830 if (symbolic) {
831 error = _kern_create_symlink(-1, target, source,
832 FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO);
833 } else
834 error = _kern_create_link(target, source);
836 if (error != FSSH_B_OK) {
837 fprintf(stderr, "Error: Failed to create link: %s\n",
838 fssh_strerror(error));
841 return error;
845 static fssh_status_t
846 command_ls(int argc, const char* const* argv)
848 const char* const currentDirFiles[] = { ".", NULL };
849 const char* const* files;
850 if (argc >= 2)
851 files = argv + 1;
852 else
853 files = currentDirFiles;
855 for (; *files; files++) {
856 const char* file = *files;
857 // stat file
858 struct fssh_stat st;
859 fssh_status_t error = _kern_read_stat(-1, file, false, &st, sizeof(st));
860 if (error != FSSH_B_OK) {
861 fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", file,
862 fssh_strerror(error));
863 continue;
866 // if it is a directory, print its entries
867 if (FSSH_S_ISDIR(st.fssh_st_mode)) {
868 printf("%s:\n", file);
870 // open dir
871 int fd = _kern_open_dir(-1, file);
872 if (fd < 0) {
873 fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n",
874 file, fssh_strerror(fd));
875 continue;
878 // iterate through the entries
879 char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
880 fssh_dirent* entry = (fssh_dirent*)buffer;
881 fssh_ssize_t entriesRead = 0;
882 while ((entriesRead = _kern_read_dir(fd, entry, sizeof(buffer), 1))
883 == 1) {
884 list_entry(file, entry->d_name);
887 if (entriesRead < 0) {
888 fprintf(stderr, "Error: reading dir \"%s\" failed: %s\n",
889 file, fssh_strerror(entriesRead));
892 // close dir
893 error = _kern_close(fd);
894 if (error != FSSH_B_OK) {
895 fprintf(stderr, "Error: Closing dir \"%s\" (fd: %d) failed: "
896 "%s\n", file, fd, fssh_strerror(error));
897 continue;
899 } else
900 list_entry(file);
903 return FSSH_B_OK;
907 static fssh_status_t
908 command_mkdir(int argc, const char* const* argv)
910 bool createParents = false;
912 // parse parameters
913 int argi = 1;
914 for (argi = 1; argi < argc; argi++) {
915 const char *arg = argv[argi];
916 if (arg[0] != '-')
917 break;
919 if (arg[1] == '\0') {
920 fprintf(stderr, "Error: Invalid option \"-\"\n");
921 return FSSH_B_BAD_VALUE;
924 for (int i = 1; arg[i]; i++) {
925 switch (arg[i]) {
926 case 'p':
927 createParents = true;
928 break;
929 default:
930 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
931 return FSSH_B_BAD_VALUE;
936 if (argi >= argc) {
937 printf("Usage: %s [ -p ] <dir>...\n", argv[0]);
938 return FSSH_B_BAD_VALUE;
941 // create loop
942 for (; argi < argc; argi++) {
943 const char *dir = argv[argi];
944 if (strlen(dir) == 0) {
945 fprintf(stderr, "Error: An empty path is not a valid argument!\n");
946 return FSSH_B_BAD_VALUE;
949 fssh_status_t error = create_dir(dir, createParents);
950 if (error != FSSH_B_OK)
951 return error;
954 return FSSH_B_OK;
958 static fssh_status_t
959 command_mkindex(int argc, const char* const* argv)
961 if (argc != 2) {
962 fprintf(stderr, "Usage: %s <index name>\n", argv[0]);
963 return FSSH_B_BAD_VALUE;
966 const char* indexName = argv[1];
968 // get the volume ID
969 fssh_dev_t volumeID = get_volume_id();
970 if (volumeID < 0)
971 return volumeID;
973 // create the index
974 fssh_status_t error =_kern_create_index(volumeID, indexName,
975 FSSH_B_STRING_TYPE, 0);
976 if (error != FSSH_B_OK) {
977 fprintf(stderr, "Error: Failed to create index \"%s\": %s\n",
978 indexName, fssh_strerror(error));
979 return error;
982 return FSSH_B_OK;
986 static fssh_status_t
987 command_mv(int argc, const char* const* argv)
989 bool force = false;
991 // parse parameters
992 int argi = 1;
993 for (argi = 1; argi < argc; argi++) {
994 const char *arg = argv[argi];
995 if (arg[0] != '-')
996 break;
998 if (arg[1] == '\0') {
999 fprintf(stderr, "Error: Invalid option \"-\"\n");
1000 return FSSH_B_BAD_VALUE;
1003 for (int i = 1; arg[i]; i++) {
1004 switch (arg[i]) {
1005 case 'f':
1006 force = true;
1007 break;
1008 default:
1009 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
1010 return FSSH_B_BAD_VALUE;
1015 // check params
1016 int count = argc - 1 - argi;
1017 if (count <= 0) {
1018 fprintf(stderr, "Usage: %s [-f] <file>... <target>\n", argv[0]);
1019 return FSSH_B_BAD_VALUE;
1022 const char* target = argv[argc - 1];
1024 // stat the target
1025 struct fssh_stat st;
1026 fssh_status_t status = _kern_read_stat(-1, target, true, &st, sizeof(st));
1027 if (status != FSSH_B_OK && count != 1) {
1028 fprintf(stderr, "Error: Failed to stat target \"%s\": %s\n", target,
1029 fssh_strerror(status));
1030 return status;
1033 if (status == FSSH_B_OK && FSSH_S_ISDIR(st.fssh_st_mode)) {
1034 // move several entries
1035 int targetDir = _kern_open_dir(-1, target);
1036 if (targetDir < 0) {
1037 fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", target,
1038 fssh_strerror(targetDir));
1039 return targetDir;
1042 // move loop
1043 for (; argi < argc - 1; argi++) {
1044 status = move_entry(-1, argv[argi], targetDir, argv[argi], force);
1045 if (status != FSSH_B_OK) {
1046 _kern_close(targetDir);
1047 return status;
1051 _kern_close(targetDir);
1052 return FSSH_B_OK;
1055 // rename single entry
1056 return move_entry(-1, argv[argi], -1, target, force);
1060 static fssh_status_t
1061 command_query(int argc, const char* const* argv)
1063 if (argc != 2) {
1064 fprintf(stderr, "Usage: %s <query string>\n", argv[0]);
1065 return FSSH_B_BAD_VALUE;
1068 const char* query = argv[1];
1070 // get the volume ID
1071 fssh_dev_t volumeID = get_volume_id();
1072 if (volumeID < 0)
1073 return volumeID;
1075 // open query
1076 int fd = _kern_open_query(volumeID, query, strlen(query), 0, -1, -1);
1077 if (fd < 0) {
1078 fprintf(stderr, "Error: Failed to open query: %s\n", fssh_strerror(fd));
1079 return fd;
1082 // iterate through the entries
1083 fssh_status_t error = FSSH_B_OK;
1084 char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
1085 fssh_dirent* entry = (fssh_dirent*)buffer;
1086 fssh_ssize_t entriesRead = 0;
1087 while ((entriesRead = _kern_read_dir(fd, entry, sizeof(buffer), 1)) == 1) {
1088 char path[FSSH_B_PATH_NAME_LENGTH];
1089 error = _kern_entry_ref_to_path(volumeID, entry->d_pino, entry->d_name,
1090 path, sizeof(path));
1091 if (error == FSSH_B_OK) {
1092 printf(" %s\n", path);
1093 } else {
1094 fprintf(stderr, " failed to resolve entry (%8" FSSH_B_PRIdINO
1095 ", \"%s\")\n", entry->d_pino, entry->d_name);
1099 if (entriesRead < 0) {
1100 fprintf(stderr, "Error: reading query failed: %s\n",
1101 fssh_strerror(entriesRead));
1104 // close query
1105 error = _kern_close(fd);
1106 if (error != FSSH_B_OK) {
1107 fprintf(stderr, "Error: Closing query (fd: %d) failed: %s\n",
1108 fd, fssh_strerror(error));
1111 return error;
1115 static fssh_status_t
1116 command_quit(int argc, const char* const* argv)
1118 return COMMAND_RESULT_EXIT;
1122 static fssh_status_t
1123 command_rm(int argc, const char* const* argv)
1125 bool recursive = false;
1126 bool force = false;
1128 // parse parameters
1129 int argi = 1;
1130 for (argi = 1; argi < argc; argi++) {
1131 const char *arg = argv[argi];
1132 if (arg[0] != '-')
1133 break;
1135 if (arg[1] == '\0') {
1136 fprintf(stderr, "Error: Invalid option \"-\"\n");
1137 return FSSH_B_BAD_VALUE;
1140 for (int i = 1; arg[i]; i++) {
1141 switch (arg[i]) {
1142 case 'f':
1143 force = true;
1144 break;
1145 case 'r':
1146 recursive = true;
1147 break;
1148 default:
1149 fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
1150 return FSSH_B_BAD_VALUE;
1155 // check params
1156 if (argi >= argc) {
1157 fprintf(stderr, "Usage: %s [ -r ] <file>...\n", argv[0]);
1158 return FSSH_B_BAD_VALUE;
1161 // remove loop
1162 for (; argi < argc; argi++) {
1163 fssh_status_t error = remove_entry(-1, argv[argi], recursive, force);
1164 if (error != FSSH_B_OK)
1165 return error;
1168 return FSSH_B_OK;
1172 static fssh_status_t
1173 command_sync(int argc, const char* const* argv)
1175 fssh_status_t error = _kern_sync();
1176 if (error != FSSH_B_OK) {
1177 fprintf(stderr, "Error: syncing: %s\n", fssh_strerror(error));
1178 return error;
1181 return FSSH_B_OK;
1185 static fssh_status_t
1186 command_ioctl(int argc, const char* const* argv)
1188 if (argc != 2) {
1189 fprintf(stderr, "Usage: %s <opcode>\n", argv[0]);
1190 return FSSH_B_BAD_VALUE;
1193 int rootDir = _kern_open_dir(-1, "/myfs");
1194 if (rootDir < 0)
1195 return rootDir;
1197 fssh_status_t status = _kern_ioctl(rootDir, atoi(argv[1]), NULL, 0);
1199 _kern_close(rootDir);
1201 if (status != FSSH_B_OK) {
1202 fprintf(stderr, "Error: ioctl failed: %s\n", fssh_strerror(status));
1203 return status;
1206 return FSSH_B_OK;
1210 static void
1211 register_commands()
1213 CommandManager::Default()->AddCommands(
1214 command_cd, "cd", "change current directory",
1215 command_chmod, "chmod", "change file permissions",
1216 command_cp, "cp", "copy files and directories",
1217 command_help, "help", "list supported commands",
1218 command_info, "info", "prints volume informations",
1219 command_ioctl, "ioctl", "ioctl() on root, for FS debugging only",
1220 command_ln, "ln", "create a hard or symbolic link",
1221 command_ls, "ls", "list files or directories",
1222 command_mkdir, "mkdir", "create directories",
1223 command_mkindex, "mkindex", "create an index",
1224 command_mv, "mv", "move/rename files and directories",
1225 command_query, "query", "query for files",
1226 command_quit, "quit/exit", "quit the shell",
1227 command_rm, "rm", "remove files and directories",
1228 command_sync, "sync", "syncs the file system",
1229 NULL
1234 // #pragma mark - ArgVector
1237 class ArgVector {
1238 public:
1239 ArgVector()
1240 : fArgc(0),
1241 fArgv(NULL)
1245 ~ArgVector()
1247 _Cleanup();
1250 int Argc() const
1252 return fArgc;
1255 const char* const* Argv() const
1257 return fArgv;
1260 bool Parse(const char* commandLine)
1262 _Cleanup();
1264 // init temporary arg/argv storage
1265 std::string currentArg;
1266 std::vector<std::string> argVector;
1268 fCurrentArg = &currentArg;
1269 fCurrentArgStarted = false;
1270 fArgVector = &argVector;
1272 for (; *commandLine; commandLine++) {
1273 char c = *commandLine;
1275 // whitespace delimits args and is otherwise ignored
1276 if (isspace(c)) {
1277 _PushCurrentArg();
1278 continue;
1281 switch (c) {
1282 case '\'':
1283 // quoted string -- no quoting
1284 while (*++commandLine != '\'') {
1285 c = *commandLine;
1286 if (c == '\0') {
1287 fprintf(stderr, "Error: Unterminated quoted "
1288 "string.\n");
1289 return false;
1291 _PushCharacter(c);
1293 break;
1295 case '"':
1296 // quoted string -- some quoting
1297 while (*++commandLine != '"') {
1298 c = *commandLine;
1299 if (c == '\0') {
1300 fprintf(stderr, "Error: Unterminated quoted "
1301 "string.\n");
1302 return false;
1305 if (c == '\\') {
1306 c = *++commandLine;
1307 if (c == '\0') {
1308 fprintf(stderr, "Error: Unterminated quoted "
1309 "string.\n");
1310 return false;
1313 // only '\' and '"' can be quoted, otherwise the
1314 // the '\' is treated as a normal char
1315 if (c != '\\' && c != '"')
1316 _PushCharacter('\\');
1319 _PushCharacter(c);
1321 break;
1323 case '\\':
1324 // quoted char
1325 c = *++commandLine;
1326 if (c == '\0') {
1327 fprintf(stderr, "Error: Command line ends with "
1328 "'\\'.\n");
1329 return false;
1331 _PushCharacter(c);
1332 break;
1334 default:
1335 // normal char
1336 _PushCharacter(c);
1337 break;
1341 // commit last arg
1342 _PushCurrentArg();
1344 // build arg vector
1345 fArgc = argVector.size();
1346 fArgv = new char*[fArgc + 1];
1347 for (int i = 0; i < fArgc; i++) {
1348 int len = argVector[i].length();
1349 fArgv[i] = new char[len + 1];
1350 memcpy(fArgv[i], argVector[i].c_str(), len + 1);
1352 fArgv[fArgc] = NULL;
1354 return true;
1357 private:
1358 void _Cleanup()
1360 if (fArgv) {
1361 for (int i = 0; i < fArgc; i++)
1362 delete[] fArgv[i];
1363 delete[] fArgv;
1367 void _PushCurrentArg()
1369 if (fCurrentArgStarted) {
1370 fArgVector->push_back(*fCurrentArg);
1371 fCurrentArgStarted = false;
1375 void _PushCharacter(char c)
1377 if (!fCurrentArgStarted) {
1378 *fCurrentArg = "";
1379 fCurrentArgStarted = true;
1382 *fCurrentArg += c;
1385 private:
1386 // temporaries
1387 std::string* fCurrentArg;
1388 bool fCurrentArgStarted;
1389 std::vector<std::string>* fArgVector;
1391 int fArgc;
1392 char** fArgv;
1396 // #pragma mark - input loop
1399 static char*
1400 read_command_line(char* buffer, int bufferSize)
1402 // print prompt (including cwd, if available)
1403 char directory[FSSH_B_PATH_NAME_LENGTH];
1404 if (_kern_getcwd(directory, sizeof(directory)) == FSSH_B_OK)
1405 printf("fssh:%s> ", directory);
1406 else
1407 printf("fssh> ");
1408 fflush(stdout);
1410 // read input line
1411 return fgets(buffer, bufferSize, stdin);
1415 static void
1416 input_loop(bool interactive)
1418 static const int kInputBufferSize = 100 * 1024;
1419 char* inputBuffer = new char[kInputBufferSize];
1421 for (;;) {
1422 // read command line
1423 if (interactive) {
1424 if (!read_command_line(inputBuffer, kInputBufferSize))
1425 break;
1426 } else {
1427 if (!get_external_command(inputBuffer, kInputBufferSize))
1428 break;
1431 // construct argv vector
1432 int result = FSSH_B_BAD_VALUE;
1433 ArgVector argVector;
1434 if (argVector.Parse(inputBuffer) && argVector.Argc() > 0) {
1435 int argc = argVector.Argc();
1436 const char* const* argv = argVector.Argv();
1438 // find command
1439 Command* command = CommandManager::Default()->FindCommand(argv[0]);
1440 if (command) {
1441 // execute it
1442 result = command->Do(argc, argv);
1443 if (result == COMMAND_RESULT_EXIT) {
1444 if (!interactive)
1445 reply_to_external_command(0);
1446 break;
1448 } else {
1449 fprintf(stderr, "Error: Invalid command \"%s\". Type \"help\" "
1450 "for a list of supported commands\n", argv[0]);
1454 if (!interactive)
1455 reply_to_external_command(fssh_to_host_error(result));
1458 if (!interactive)
1459 external_command_cleanup();
1461 delete[] inputBuffer;
1465 static int
1466 standard_session(const char* device, const char* fsName, bool interactive)
1468 // mount FS
1469 fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0);
1470 if (fsDev < 0) {
1471 fprintf(stderr, "Error: Mounting FS failed: %s\n",
1472 fssh_strerror(fsDev));
1473 return 1;
1476 // register commands
1477 register_commands();
1478 register_additional_commands();
1480 // process commands
1481 input_loop(interactive);
1483 // unmount FS
1484 _kern_setcwd(-1, "/"); // avoid a "busy" vnode
1485 fssh_status_t error = _kern_unmount(kMountPoint, 0);
1486 if (error != FSSH_B_OK) {
1487 fprintf(stderr, "Error: Unmounting FS failed: %s\n",
1488 fssh_strerror(error));
1489 return 1;
1492 return 0;
1496 static int
1497 initialization_session(const char* device, const char* fsName,
1498 const char* volumeName, const char* initParameters)
1500 fssh_status_t error = _kern_initialize_volume(fsName, device,
1501 volumeName, initParameters);
1502 if (error != FSSH_B_OK) {
1503 fprintf(stderr, "Error: Initializing volume failed: %s\n",
1504 fssh_strerror(error));
1505 return 1;
1508 return 0;
1512 static void
1513 print_usage(bool error)
1515 fprintf((error ? stderr : stdout),
1516 "Usage: %s [ --start-offset <startOffset>]\n"
1517 " [ --end-offset <endOffset>] [-n] <device>\n"
1518 " %s [ --start-offset <startOffset>]\n"
1519 " [ --end-offset <endOffset>]\n"
1520 " --initialize [-n] <device> <volume name> "
1521 "[ <init parameters> ]\n",
1522 sArgv[0], sArgv[0]
1527 static void
1528 print_usage_and_exit(bool error)
1530 print_usage(error);
1531 exit(error ? 1 : 0);
1535 } // namespace FSShell
1538 using namespace FSShell;
1542 main(int argc, const char* const* argv)
1544 sArgc = argc;
1545 sArgv = argv;
1547 // process arguments
1548 bool interactive = true;
1549 bool initialize = false;
1550 const char* device = NULL;
1551 const char* volumeName = NULL;
1552 const char* initParameters = NULL;
1553 fssh_off_t startOffset = 0;
1554 fssh_off_t endOffset = -1;
1556 // eat options
1557 int argi = 1;
1558 while (argi < argc && argv[argi][0] == '-') {
1559 const char* arg = argv[argi++];
1560 if (strcmp(arg, "--help") == 0) {
1561 print_usage_and_exit(false);
1562 } else if (strcmp(arg, "--initialize") == 0) {
1563 initialize = true;
1564 } else if (strcmp(arg, "-n") == 0) {
1565 interactive = false;
1566 } else if (strcmp(arg, "--start-offset") == 0) {
1567 if (argi >= argc)
1568 print_usage_and_exit(true);
1569 startOffset = atoll(argv[argi++]);
1570 } else if (strcmp(arg, "--end-offset") == 0) {
1571 if (argi >= argc)
1572 print_usage_and_exit(true);
1573 endOffset = atoll(argv[argi++]);
1574 } else {
1575 print_usage_and_exit(true);
1579 // get device
1580 if (argi >= argc)
1581 print_usage_and_exit(true);
1582 device = argv[argi++];
1584 // get volume name and init parameters
1585 if (initialize) {
1586 // volume name
1587 if (argi >= argc)
1588 print_usage_and_exit(true);
1589 volumeName = argv[argi++];
1591 // (optional) init paramaters
1592 if (argi < argc)
1593 initParameters = argv[argi++];
1596 // more parameters are excess
1597 if (argi < argc)
1598 print_usage_and_exit(true);
1600 // get FS module
1601 if (!modules[0]) {
1602 fprintf(stderr, "Error: Couldn't find FS module!\n");
1603 return 1;
1605 const char* fsName = modules[0]->name;
1607 fssh_status_t error;
1609 // init kernel
1610 error = init_kernel();
1611 if (error != FSSH_B_OK) {
1612 fprintf(stderr, "Error: Initializing kernel failed: %s\n",
1613 fssh_strerror(error));
1614 return error;
1617 // restrict access if requested
1618 if (startOffset != 0 || endOffset != -1)
1619 add_file_restriction(device, startOffset, endOffset);
1621 // start the action
1622 int result;
1623 if (initialize) {
1624 result = initialization_session(device, fsName, volumeName,
1625 initParameters);
1626 } else
1627 result = standard_session(device, fsName, interactive);
1629 return result;