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