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