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