2 * Copyright 2007-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
7 #include "compatibility.h"
20 #include "command_cp.h"
21 #include "driver_settings.h"
22 #include "external_commands.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"
34 #include "partition_support.h"
35 #include "path_util.h"
40 extern fssh_module_info
*modules
[];
43 extern fssh_file_system_module_info gRootFileSystem
;
47 const char* kMountPoint
= "/myfs";
51 static const char* const* sArgv
;
53 static mode_t sUmask
= 0022;
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
));
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
));
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
]);
82 error
= vfs_init(NULL
);
83 if (error
!= FSSH_B_OK
) {
84 fprintf(stderr
, "initializing VFS failed: %s\n", fssh_strerror(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
;
96 fssh_dev_t rootDev
= _kern_mount("/", NULL
, "rootfs", 0, NULL
, 0);
98 fprintf(stderr
, "mounting rootfs failed: %s\n", fssh_strerror(rootDev
));
103 error
= _kern_setcwd(-1, "/");
104 if (error
!= FSSH_B_OK
) {
105 fprintf(stderr
, "setting cwd failed: %s\n", fssh_strerror(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
));
121 // #pragma mark - Command
123 Command::Command(const char* name
, const char* description
)
125 fDescription(description
)
130 Command::Command(command_function
* function
, const char* name
,
131 const char* description
)
133 fDescription(description
),
145 Command::Name() const
147 return fName
.c_str();
152 Command::Description() const
154 return fDescription
.c_str();
159 Command::Do(int argc
, const char* const* argv
)
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()
178 CommandManager::Default()
181 sManager
= new CommandManager
;
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.
192 char* names
= _names
;
193 strcpy(names
, command
->Name());
196 while (char* name
= strtok_r(names
, " /", &cookie
)) {
197 fCommands
[name
] = command
;
204 CommandManager::AddCommand(command_function
* function
, const char* name
,
205 const char* description
)
207 AddCommand(new Command(function
, name
, description
));
212 CommandManager::AddCommands(command_function
* function
, const char* name
,
213 const char* description
, ...)
216 va_start(args
, description
);
219 AddCommand(function
, name
, description
);
221 function
= va_arg(args
, command_function
*);
223 name
= va_arg(args
, const char*);
224 description
= va_arg(args
, const char*);
233 CommandManager::FindCommand(const char* name
) const
235 CommandMap::const_iterator it
= fCommands
.find(name
);
236 if (it
== fCommands
.end())
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
262 get_permissions(const char* modeString
, fssh_mode_t
& _permissions
)
264 // currently only octal mode is supported
265 if (strlen(modeString
) != 3)
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')
273 permissions
= (permissions
<< 3) | (c
- '0');
276 _permissions
= permissions
;
285 fssh_status_t error
= _kern_read_stat(-1, kMountPoint
, false, &st
,
287 if (error
!= FSSH_B_OK
) {
288 fprintf(stderr
, "Error: Failed to stat() mount point: %s\n",
289 fssh_strerror(error
));
293 return st
.fssh_st_dev
;
298 byte_string(int64_t numBlocks
, int64_t blockSize
)
300 double blocks
= 1. * numBlocks
* blockSize
;
301 static char string
[64];
304 sprintf(string
, "%" FSSH_B_PRId64
, numBlocks
* blockSize
);
306 const char* units
[] = {"K", "M", "G", NULL
};
312 } while (blocks
>= 1024 && units
[i
+ 1]);
314 sprintf(string
, "%.1f%s", blocks
, units
[i
]);
322 print_flag(uint32_t deviceFlags
, uint32_t testFlag
, const char *yes
,
325 printf("%s", (deviceFlags
& testFlag
) != 0 ? yes
: no
);
330 list_entry(const char* file
, const char* name
= NULL
)
332 // construct path, if a leaf name is given
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
));
353 time_t fileTime
= st
.fssh_st_mtime
;
354 localtime_r(&fileTime
, &time
);
357 std::string permissions
;
358 fssh_mode_t mode
= st
.fssh_st_mode
;
360 permissions
+= ((mode
& FSSH_S_IRUSR
) ? 'r' : '-');
361 permissions
+= ((mode
& FSSH_S_IWUSR
) ? 'w' : '-');
362 if (mode
& FSSH_S_ISUID
)
365 permissions
+= ((mode
& FSSH_S_IXUSR
) ? 'x' : '-');
367 permissions
+= ((mode
& FSSH_S_IRGRP
) ? 'r' : '-');
368 permissions
+= ((mode
& FSSH_S_IWGRP
) ? 'w' : '-');
369 if (mode
& FSSH_S_ISGID
)
372 permissions
+= ((mode
& FSSH_S_IXGRP
) ? 'x' : '-');
374 permissions
+= ((mode
& FSSH_S_IROTH
) ? 'r' : '-');
375 permissions
+= ((mode
& FSSH_S_IWOTH
) ? 'w' : '-');
376 permissions
+= ((mode
& FSSH_S_IXOTH
) ? 'x' : '-');
380 if (FSSH_S_ISREG(mode
)) {
382 } else if (FSSH_S_ISLNK(mode
)) {
384 } else if (FSSH_S_ISBLK(mode
)) {
386 } else if (FSSH_S_ISDIR(mode
)) {
388 } else if (FSSH_S_ISCHR(mode
)) {
390 } else if (FSSH_S_ISFIFO(mode
)) {
392 } else if (FSSH_S_ISINDEX(mode
)) {
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
));
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
,
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());
421 create_dir(const char *path
, bool createParents
)
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
))
430 fprintf(stderr
, "Error: Cannot make dir, entry \"%s\" is in the way.\n",
432 return FSSH_B_FILE_EXISTS
;
435 // the dir doesn't exist yet
436 // if we shall create all parents, do that first
438 // create the parent dir path
439 // eat the trailing '/'s
440 int len
= strlen(path
);
441 while (len
> 0 && path
[len
- 1] == '/')
444 // eat the last path component
445 while (len
> 0 && path
[len
- 1] != '/')
448 // eat the trailing '/'s
449 while (len
> 0 && path
[len
- 1] == '/')
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
458 char *parentPath
= (char*)malloc(len
+ 1);
460 fprintf(stderr
, "Error: Failed to allocate memory for parent "
462 return FSSH_B_NO_MEMORY
;
464 memcpy(parentPath
, path
, len
);
465 parentPath
[len
] = '\0';
467 error
= create_dir(parentPath
, createParents
);
471 if (error
!= FSSH_B_OK
)
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
));
489 static fssh_status_t
remove_entry(int dir
, const char *entry
, bool recursive
,
494 remove_dir_contents(int parentDir
, const char *name
, bool force
)
497 int dir
= _kern_open_dir(parentDir
, name
);
499 fprintf(stderr
, "Error: Failed to open dir \"%s\": %s\n", name
,
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) {
512 if (strcmp(entry
->d_name
, ".") == 0 || strcmp(entry
->d_name
, "..") == 0)
515 error
= remove_entry(dir
, entry
->d_name
, true, force
);
516 if (error
!= FSSH_B_OK
)
521 fprintf(stderr
, "Error: Failed to read directory \"%s\": %s\n", name
,
522 fssh_strerror(numRead
));
534 remove_entry(int dir
, const char *entry
, bool recursive
, bool force
)
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
)
543 fprintf(stderr
, "Error: Failed to remove \"%s\": %s\n", entry
,
544 fssh_strerror(error
));
548 if (FSSH_S_ISDIR(st
.fssh_st_mode
)) {
550 fprintf(stderr
, "Error: \"%s\" is a directory.\n", entry
);
551 // TODO: get the full path
555 // remove the contents
556 error
= remove_dir_contents(dir
, entry
, force
);
557 if (error
!= FSSH_B_OK
)
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
));
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
));
582 move_entry(int dir
, const char *entry
, int targetDir
, const char* target
,
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
)
592 fprintf(stderr
, "Error: Failed to move \"%s\": %s\n", entry
,
593 fssh_strerror(status
));
597 return _kern_rename(dir
, entry
, targetDir
, target
);
601 // #pragma mark - Commands
605 command_cd(int argc
, const char* const* argv
)
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();
618 error
= _kern_setcwd(-1, directory
);
620 if (error
!= FSSH_B_OK
) {
621 fprintf(stderr
, "Error: cd %s: %s\n", directory
, fssh_strerror(error
));
630 command_chmod(int argc
, const char* const* argv
)
632 bool recursive
= false;
636 for (argi
= 1; argi
< argc
; argi
++) {
637 const char *arg
= argv
[argi
];
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
++) {
650 fprintf(stderr
, "Sorry, recursive mode not supported "
652 return FSSH_B_BAD_VALUE
;
654 fprintf(stderr
, "Error: Unknown option \"-%c\"\n", arg
[i
]);
655 return FSSH_B_BAD_VALUE
;
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
;
668 st
.fssh_st_mode
= permissions
;
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
),
680 if (error
!= FSSH_B_OK
) {
681 fprintf(stderr
, "Error: Failed to change mode of \"%s\"!\n", file
);
691 command_help(int argc
, const char* const* argv
)
693 printf("supported commands:\n");
694 CommandManager::Default()->ListCommands();
700 command_info(int argc
, const char* const* argv
)
702 fssh_dev_t volumeID
= get_volume_id();
707 fssh_status_t status
= _kern_read_fs_info(volumeID
, &info
);
708 if (status
!= FSSH_B_OK
)
711 printf("root inode: %" FSSH_B_PRIdINO
"\n", info
.root
);
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
);
737 command_ln(int argc
, const char* const* argv
)
740 bool symbolic
= false;
741 bool dereference
= true;
745 for (argi
= 1; argi
< argc
; argi
++) {
746 const char *arg
= argv
[argi
];
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
++) {
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
783 char targetBuffer
[FSSH_B_PATH_NAME_LENGTH
];
784 fssh_status_t error
= _kern_read_stat(-1, target
, dereference
, &st
,
786 if (error
== FSSH_B_OK
) {
787 if (FSSH_S_ISDIR(st
.fssh_st_mode
)) {
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
));
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
) {
815 fprintf(stderr
, "Error: Can't create link. \"%s\" is in the way.\n",
817 return FSSH_B_FILE_EXISTS
;
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
));
829 // finally create the link
831 error
= _kern_create_symlink(-1, target
, source
,
832 FSSH_S_IRWXU
| FSSH_S_IRWXG
| FSSH_S_IRWXO
);
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
));
846 command_ls(int argc
, const char* const* argv
)
848 const char* const currentDirFiles
[] = { ".", NULL
};
849 const char* const* files
;
853 files
= currentDirFiles
;
855 for (; *files
; files
++) {
856 const char* file
= *files
;
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
));
866 // if it is a directory, print its entries
867 if (FSSH_S_ISDIR(st
.fssh_st_mode
)) {
868 printf("%s:\n", file
);
871 int fd
= _kern_open_dir(-1, file
);
873 fprintf(stderr
, "Error: Failed to open dir \"%s\": %s\n",
874 file
, fssh_strerror(fd
));
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))
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
));
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
));
908 command_mkdir(int argc
, const char* const* argv
)
910 bool createParents
= false;
914 for (argi
= 1; argi
< argc
; argi
++) {
915 const char *arg
= argv
[argi
];
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
++) {
927 createParents
= true;
930 fprintf(stderr
, "Error: Unknown option \"-%c\"\n", arg
[i
]);
931 return FSSH_B_BAD_VALUE
;
937 printf("Usage: %s [ -p ] <dir>...\n", argv
[0]);
938 return FSSH_B_BAD_VALUE
;
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
)
959 command_mkindex(int argc
, const char* const* argv
)
962 fprintf(stderr
, "Usage: %s <index name>\n", argv
[0]);
963 return FSSH_B_BAD_VALUE
;
966 const char* indexName
= argv
[1];
969 fssh_dev_t volumeID
= get_volume_id();
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
));
987 command_mv(int argc
, const char* const* argv
)
993 for (argi
= 1; argi
< argc
; argi
++) {
994 const char *arg
= argv
[argi
];
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
++) {
1009 fprintf(stderr
, "Error: Unknown option \"-%c\"\n", arg
[i
]);
1010 return FSSH_B_BAD_VALUE
;
1016 int count
= argc
- 1 - argi
;
1018 fprintf(stderr
, "Usage: %s [-f] <file>... <target>\n", argv
[0]);
1019 return FSSH_B_BAD_VALUE
;
1022 const char* target
= argv
[argc
- 1];
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
));
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
));
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
);
1051 _kern_close(targetDir
);
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
)
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();
1076 int fd
= _kern_open_query(volumeID
, query
, strlen(query
), 0, -1, -1);
1078 fprintf(stderr
, "Error: Failed to open query: %s\n", fssh_strerror(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
);
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
));
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
));
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;
1130 for (argi
= 1; argi
< argc
; argi
++) {
1131 const char *arg
= argv
[argi
];
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
++) {
1149 fprintf(stderr
, "Error: Unknown option \"-%c\"\n", arg
[i
]);
1150 return FSSH_B_BAD_VALUE
;
1157 fprintf(stderr
, "Usage: %s [ -r ] <file>...\n", argv
[0]);
1158 return FSSH_B_BAD_VALUE
;
1162 for (; argi
< argc
; argi
++) {
1163 fssh_status_t error
= remove_entry(-1, argv
[argi
], recursive
, force
);
1164 if (error
!= 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
));
1185 static fssh_status_t
1186 command_ioctl(int argc
, const char* const* argv
)
1189 fprintf(stderr
, "Usage: %s <opcode>\n", argv
[0]);
1190 return FSSH_B_BAD_VALUE
;
1193 int rootDir
= _kern_open_dir(-1, "/myfs");
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
));
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",
1234 // #pragma mark - ArgVector
1255 const char* const* Argv() const
1260 bool Parse(const char* commandLine
)
1264 // init temporary arg/argv storage
1265 std::string currentArg
;
1266 std::vector
<std::string
> argVector
;
1268 fCurrentArg
= ¤tArg
;
1269 fCurrentArgStarted
= false;
1270 fArgVector
= &argVector
;
1272 for (; *commandLine
; commandLine
++) {
1273 char c
= *commandLine
;
1275 // whitespace delimits args and is otherwise ignored
1283 // quoted string -- no quoting
1284 while (*++commandLine
!= '\'') {
1287 fprintf(stderr
, "Error: Unterminated quoted "
1296 // quoted string -- some quoting
1297 while (*++commandLine
!= '"') {
1300 fprintf(stderr
, "Error: Unterminated quoted "
1308 fprintf(stderr
, "Error: Unterminated quoted "
1313 // only '\' and '"' can be quoted, otherwise the
1314 // the '\' is treated as a normal char
1315 if (c
!= '\\' && c
!= '"')
1316 _PushCharacter('\\');
1327 fprintf(stderr
, "Error: Command line ends with "
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
;
1361 for (int i
= 0; i
< fArgc
; i
++)
1367 void _PushCurrentArg()
1369 if (fCurrentArgStarted
) {
1370 fArgVector
->push_back(*fCurrentArg
);
1371 fCurrentArgStarted
= false;
1375 void _PushCharacter(char c
)
1377 if (!fCurrentArgStarted
) {
1379 fCurrentArgStarted
= true;
1387 std::string
* fCurrentArg
;
1388 bool fCurrentArgStarted
;
1389 std::vector
<std::string
>* fArgVector
;
1396 // #pragma mark - input loop
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
);
1411 return fgets(buffer
, bufferSize
, stdin
);
1416 input_loop(bool interactive
)
1418 static const int kInputBufferSize
= 100 * 1024;
1419 char* inputBuffer
= new char[kInputBufferSize
];
1422 // read command line
1424 if (!read_command_line(inputBuffer
, kInputBufferSize
))
1427 if (!get_external_command(inputBuffer
, kInputBufferSize
))
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();
1439 Command
* command
= CommandManager::Default()->FindCommand(argv
[0]);
1442 result
= command
->Do(argc
, argv
);
1443 if (result
== COMMAND_RESULT_EXIT
) {
1445 reply_to_external_command(0);
1449 fprintf(stderr
, "Error: Invalid command \"%s\". Type \"help\" "
1450 "for a list of supported commands\n", argv
[0]);
1455 reply_to_external_command(fssh_to_host_error(result
));
1459 external_command_cleanup();
1461 delete[] inputBuffer
;
1466 standard_session(const char* device
, const char* fsName
, bool interactive
)
1469 fssh_dev_t fsDev
= _kern_mount(kMountPoint
, device
, fsName
, 0, NULL
, 0);
1471 fprintf(stderr
, "Error: Mounting FS failed: %s\n",
1472 fssh_strerror(fsDev
));
1476 // register commands
1477 register_commands();
1478 register_additional_commands();
1481 input_loop(interactive
);
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
));
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
));
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",
1528 print_usage_and_exit(bool error
)
1531 exit(error
? 1 : 0);
1535 } // namespace FSShell
1538 using namespace FSShell
;
1542 main(int argc
, const char* const* 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;
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) {
1564 } else if (strcmp(arg
, "-n") == 0) {
1565 interactive
= false;
1566 } else if (strcmp(arg
, "--start-offset") == 0) {
1568 print_usage_and_exit(true);
1569 startOffset
= atoll(argv
[argi
++]);
1570 } else if (strcmp(arg
, "--end-offset") == 0) {
1572 print_usage_and_exit(true);
1573 endOffset
= atoll(argv
[argi
++]);
1575 print_usage_and_exit(true);
1581 print_usage_and_exit(true);
1582 device
= argv
[argi
++];
1584 // get volume name and init parameters
1588 print_usage_and_exit(true);
1589 volumeName
= argv
[argi
++];
1591 // (optional) init paramaters
1593 initParameters
= argv
[argi
++];
1596 // more parameters are excess
1598 print_usage_and_exit(true);
1602 fprintf(stderr
, "Error: Couldn't find FS module!\n");
1605 const char* fsName
= modules
[0]->name
;
1607 fssh_status_t error
;
1610 error
= init_kernel();
1611 if (error
!= FSSH_B_OK
) {
1612 fprintf(stderr
, "Error: Initializing kernel failed: %s\n",
1613 fssh_strerror(error
));
1617 // restrict access if requested
1618 if (startOffset
!= 0 || endOffset
!= -1)
1619 add_file_restriction(device
, startOffset
, endOffset
);
1624 result
= initialization_session(device
, fsName
, volumeName
,
1627 result
= standard_session(device
, fsName
, interactive
);