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