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