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_file(const char* path)
423{
424	// stat the entry
425	struct fssh_stat st;
426	fssh_status_t error = _kern_read_stat(-1, path, false, &st, sizeof(st));
427
428	if (error == FSSH_B_OK) {
429
430		// TODO file/directory exists. Update access/modification time
431		fprintf(stderr, "TODO file/directory exists. update time stamp\n", path);
432		return FSSH_B_FILE_EXISTS;
433	}
434
435	// create the file
436	int fd_or_error = _kern_open(-1, path, FSSH_O_CREAT, 0);
437	if (fd_or_error <= 0) {
438		fprintf(stderr, "Error: Failed to make file \"%s\": %s\n", path,
439			fssh_strerror(fd_or_error));
440		return fd_or_error;
441	}
442
443	// TODO update access/modification time?
444
445	// close file
446	_kern_close(fd_or_error);
447	return FSSH_B_OK;
448}
449
450
451static fssh_status_t
452create_dir(const char* path, bool createParents)
453{
454	// stat the entry
455	struct fssh_stat st;
456	fssh_status_t error = _kern_read_stat(-1, path, false, &st, sizeof(st));
457	if (error == FSSH_B_OK) {
458		if (createParents && FSSH_S_ISDIR(st.fssh_st_mode))
459			return FSSH_B_OK;
460
461		fprintf(stderr, "Error: Cannot make dir, entry \"%s\" is in the way.\n",
462			path);
463		return FSSH_B_FILE_EXISTS;
464	}
465
466	// the dir doesn't exist yet
467	// if we shall create all parents, do that first
468	if (createParents) {
469		// create the parent dir path
470		// eat the trailing '/'s
471		int len = strlen(path);
472		while (len > 0 && path[len - 1] == '/')
473			len--;
474
475		// eat the last path component
476		while (len > 0 && path[len - 1] != '/')
477			len--;
478
479		// eat the trailing '/'s
480		while (len > 0 && path[len - 1] == '/')
481			len--;
482
483		// Now either nothing remains, which means we had a single component,
484		// a root subdir -- in those cases we can just fall through (we should
485		// actually never be here in case of the root dir, but anyway) -- or
486		// there is something left, which we can call a parent directory and
487		// try to create it.
488		if (len > 0) {
489			char *parentPath = (char*)malloc(len + 1);
490			if (!parentPath) {
491				fprintf(stderr, "Error: Failed to allocate memory for parent "
492					"path.\n");
493				return FSSH_B_NO_MEMORY;
494			}
495			memcpy(parentPath, path, len);
496			parentPath[len] = '\0';
497
498			error = create_dir(parentPath, createParents);
499
500			free(parentPath);
501
502			if (error != FSSH_B_OK)
503				return error;
504		}
505	}
506
507	// make the directory
508	error = _kern_create_dir(-1,
509		path, (FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO) & ~sUmask);
510	if (error != FSSH_B_OK) {
511		fprintf(stderr, "Error: Failed to make directory \"%s\": %s\n", path,
512			fssh_strerror(error));
513		return error;
514	}
515
516	return FSSH_B_OK;
517}
518
519
520static fssh_status_t remove_entry(int dir, const char *entry, bool recursive,
521	bool force);
522
523
524static fssh_status_t
525remove_dir_contents(int parentDir, const char *name, bool force)
526{
527	// open the dir
528	int dir = _kern_open_dir(parentDir, name);
529	if (dir < 0) {
530		fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", name,
531			fssh_strerror(dir));
532		return dir;
533	}
534
535	fssh_status_t error = FSSH_B_OK;
536
537	// iterate through the entries
538	fssh_ssize_t numRead;
539	char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
540	fssh_dirent *entry = (fssh_dirent*)buffer;
541	while ((numRead = _kern_read_dir(dir, entry, sizeof(buffer), 1)) > 0) {
542		// skip "." and ".."
543		if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
544			continue;
545
546		error = remove_entry(dir, entry->d_name, true, force);
547		if (error != FSSH_B_OK)
548			break;
549	}
550
551	if (numRead < 0) {
552		fprintf(stderr, "Error: Failed to read directory \"%s\": %s\n", name,
553			fssh_strerror(numRead));
554		error = numRead;
555	}
556
557	// close
558	_kern_close(dir);
559
560	return error;
561}
562
563
564static fssh_status_t
565remove_entry(int dir, const char *entry, bool recursive, bool force)
566{
567	// stat the file
568	struct fssh_stat st;
569	fssh_status_t error = _kern_read_stat(dir, entry, false, &st, sizeof(st));
570	if (error != FSSH_B_OK) {
571		if (force && error == FSSH_B_ENTRY_NOT_FOUND)
572			return FSSH_B_OK;
573
574		fprintf(stderr, "Error: Failed to remove \"%s\": %s\n", entry,
575			fssh_strerror(error));
576		return error;
577	}
578
579	if (FSSH_S_ISDIR(st.fssh_st_mode)) {
580		if (!recursive) {
581			fprintf(stderr, "Error: \"%s\" is a directory.\n", entry);
582				// TODO: get the full path
583			return FSSH_EISDIR;
584		}
585
586		// remove the contents
587		error = remove_dir_contents(dir, entry, force);
588		if (error != FSSH_B_OK)
589			return error;
590
591		// remove the directory
592		error = _kern_remove_dir(dir, entry);
593		if (error != FSSH_B_OK) {
594			fprintf(stderr, "Error: Failed to remove directory \"%s\": %s\n",
595				entry, fssh_strerror(error));
596			return error;
597		}
598	} else {
599		// remove the entry
600		error = _kern_unlink(dir, entry);
601		if (error != FSSH_B_OK) {
602			fprintf(stderr, "Error: Failed to remove entry \"%s\": %s\n", entry,
603				fssh_strerror(error));
604			return error;
605		}
606	}
607
608	return FSSH_B_OK;
609}
610
611
612static fssh_status_t
613move_entry(int dir, const char *entry, int targetDir, const char* target,
614	bool force)
615{
616	// stat the file
617	struct fssh_stat st;
618	fssh_status_t status = _kern_read_stat(dir, entry, false, &st, sizeof(st));
619	if (status != FSSH_B_OK) {
620		if (force && status == FSSH_B_ENTRY_NOT_FOUND)
621			return FSSH_B_OK;
622
623		fprintf(stderr, "Error: Failed to move \"%s\": %s\n", entry,
624			fssh_strerror(status));
625		return status;
626	}
627
628	return _kern_rename(dir, entry, targetDir, target);
629}
630
631
632// #pragma mark - Commands
633
634
635static fssh_status_t
636command_cd(int argc, const char* const* argv)
637{
638	if (argc != 2) {
639		fprintf(stderr, "Usage: %s <directory>\n", argv[0]);
640		return FSSH_B_BAD_VALUE;
641	}
642	const char* directory = argv[1];
643
644	fssh_status_t error = FSSH_B_OK;
645	if (directory[0] == ':') {
646		if (chdir(directory + 1) < 0)
647			error = fssh_get_errno();
648	} else
649		error = _kern_setcwd(-1, directory);
650
651	if (error != FSSH_B_OK) {
652		fprintf(stderr, "Error: cd %s: %s\n", directory, fssh_strerror(error));
653		return error;
654	}
655
656	return FSSH_B_OK;
657}
658
659
660static fssh_status_t
661command_chmod(int argc, const char* const* argv)
662{
663	// parse parameters
664	int argi = 1;
665	for (argi = 1; argi < argc; argi++) {
666		const char *arg = argv[argi];
667		if (arg[0] != '-')
668			break;
669
670		if (arg[1] == '\0') {
671			fprintf(stderr, "Error: Invalid option \"-\"\n");
672			return FSSH_B_BAD_VALUE;
673		}
674
675		for (int i = 1; arg[i]; i++) {
676			switch (arg[i]) {
677				case 'R':
678					fprintf(stderr, "Sorry, recursive mode not supported "
679						"yet.\n");
680					return FSSH_B_BAD_VALUE;
681				default:
682					fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
683					return FSSH_B_BAD_VALUE;
684			}
685		}
686	}
687
688	// get mode
689	fssh_mode_t permissions;
690	if (argi + 1 >= argc || !get_permissions(argv[argi++], permissions)) {
691		printf("Usage: %s [ -R ] <octal mode> <file>...\n", argv[0]);
692		return FSSH_B_BAD_VALUE;
693	}
694
695	fssh_struct_stat st;
696	st.fssh_st_mode = permissions;
697
698	// chmod loop
699	for (; argi < argc; argi++) {
700		const char *file = argv[argi];
701		if (strlen(file) == 0) {
702			fprintf(stderr, "Error: An empty path is not a valid argument!\n");
703			return FSSH_B_BAD_VALUE;
704		}
705
706		fssh_status_t error = _kern_write_stat(-1, file, false, &st, sizeof(st),
707			FSSH_B_STAT_MODE);
708		if (error != FSSH_B_OK) {
709			fprintf(stderr, "Error: Failed to change mode of \"%s\"!\n", file);
710			return error;
711		}
712	}
713
714	return FSSH_B_OK;
715}
716
717
718static fssh_status_t
719command_cat(int argc, const char* const* argv)
720{
721	size_t numBytes = 4096;
722	int fileStart = 1;
723	if (argc < 2 || strcmp(argv[1], "--help") == 0) {
724		printf("Usage: %s [ -n ] [FILE]...\n"
725			"\t -n\tNumber of bytes to read\n",
726			argv[0]);
727		return FSSH_B_OK;
728	}
729
730	bool isReadLengthGiven = false;
731	if (argc > 3 && strcmp(argv[1], "-n") == 0) {
732		fileStart += 2;
733		numBytes = strtol(argv[2], NULL, 10);
734		isReadLengthGiven = true;
735	}
736
737	const char* const* files = argv + fileStart;
738	for (; *files; files++) {
739		const char* file = *files;
740		int fd = _kern_open(-1, file, FSSH_O_RDONLY, FSSH_O_RDONLY);
741		if (fd < 0) {
742			fprintf(stderr, "error: %s\n", fssh_strerror(fd));
743			return FSSH_B_BAD_VALUE;
744		}
745		struct fssh_stat st;
746		fssh_status_t error = _kern_read_stat(-1, file, false, &st, sizeof(st));
747		if (error != FSSH_B_OK) {
748			fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", file,
749				fssh_strerror(error));
750			return error;
751		}
752		size_t fileLengthToRead;
753		if (!isReadLengthGiven) {
754			fileLengthToRead = st.fssh_st_size;
755			numBytes = 4096;
756		} else
757			fileLengthToRead = numBytes;
758		size_t pos = 0;
759
760		char buffer[numBytes + 1];
761		while (fileLengthToRead > 0) {
762			if (fileLengthToRead < numBytes)
763				numBytes = fileLengthToRead;
764
765			ssize_t statusOrNumBytes = _kern_read(fd, pos, buffer, numBytes);
766			if (statusOrNumBytes != (ssize_t)numBytes) {
767				fprintf(stderr, "error: %s\n", fssh_strerror(statusOrNumBytes));
768				_kern_close(fd);
769				return FSSH_B_BAD_VALUE;
770			}
771			buffer[numBytes] = '\0';
772			printf("%s", buffer);
773			pos += numBytes;
774			fileLengthToRead -= numBytes;
775		}
776		printf("\n");
777		_kern_close(fd);
778	}
779
780	return FSSH_B_OK;
781}
782
783
784static fssh_status_t
785command_help(int argc, const char* const* argv)
786{
787	printf("supported commands:\n");
788	CommandManager::Default()->ListCommands();
789	return FSSH_B_OK;
790}
791
792
793static fssh_status_t
794command_info(int argc, const char* const* argv)
795{
796	fssh_dev_t volumeID = get_volume_id();
797	if (volumeID < 0)
798		return volumeID;
799
800	fssh_fs_info info;
801	fssh_status_t status = _kern_read_fs_info(volumeID, &info);
802	if (status != FSSH_B_OK)
803		return status;
804
805	printf("root inode:   %" FSSH_B_PRIdINO "\n", info.root);
806	printf("flags:        ");
807	print_flag(info.flags, FSSH_B_FS_HAS_QUERY, "Q", "-");
808	print_flag(info.flags, FSSH_B_FS_HAS_ATTR, "A", "-");
809	print_flag(info.flags, FSSH_B_FS_HAS_MIME, "M", "-");
810	print_flag(info.flags, FSSH_B_FS_IS_SHARED, "S", "-");
811	print_flag(info.flags, FSSH_B_FS_IS_PERSISTENT, "P", "-");
812	print_flag(info.flags, FSSH_B_FS_IS_REMOVABLE, "R", "-");
813	print_flag(info.flags, FSSH_B_FS_IS_READONLY, "-", "W");
814
815	printf("\nblock size:   %" FSSH_B_PRIdOFF "\n", info.block_size);
816	printf("I/O size:     %" FSSH_B_PRIdOFF "\n", info.io_size);
817	printf("total size:   %s (%" FSSH_B_PRIdOFF " blocks)\n",
818		byte_string(info.total_blocks, info.block_size), info.total_blocks);
819	printf("free size:    %s (%" FSSH_B_PRIdOFF " blocks)\n",
820		byte_string(info.free_blocks, info.block_size), info.free_blocks);
821	printf("total nodes:  %" FSSH_B_PRIdOFF "\n", info.total_nodes);
822	printf("free nodes:   %" FSSH_B_PRIdOFF "\n", info.free_nodes);
823	printf("volume name:  %s\n", info.volume_name);
824	printf("fs name:      %s\n", info.fsh_name);
825
826	return FSSH_B_OK;
827}
828
829
830static fssh_status_t
831command_ln(int argc, const char* const* argv)
832{
833	bool force = false;
834	bool symbolic = false;
835	bool dereference = true;
836
837	// parse parameters
838	int argi = 1;
839	for (argi = 1; argi < argc; argi++) {
840		const char *arg = argv[argi];
841		if (arg[0] != '-')
842			break;
843
844		if (arg[1] == '\0') {
845			fprintf(stderr, "Error: Invalid option \"-\"\n");
846			return FSSH_B_BAD_VALUE;
847		}
848
849		for (int i = 1; arg[i]; i++) {
850			switch (arg[i]) {
851				case 'f':
852					force = true;
853					break;
854				case 's':
855					symbolic = true;
856					break;
857				case 'n':
858					dereference = false;
859					break;
860				default:
861					fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
862					return FSSH_B_BAD_VALUE;
863			}
864		}
865	}
866
867	if (argc - argi != 2) {
868		fprintf(stderr, "Usage: %s [Options] <source> <target>\n", argv[0]);
869		return FSSH_B_BAD_VALUE;
870	}
871
872	const char *source = argv[argi];
873	const char *target = argv[argi + 1];
874
875	// check, if the the target is an existing directory
876	struct fssh_stat st;
877	char targetBuffer[FSSH_B_PATH_NAME_LENGTH];
878	fssh_status_t error = _kern_read_stat(-1, target, dereference, &st,
879		sizeof(st));
880	if (error == FSSH_B_OK) {
881		if (FSSH_S_ISDIR(st.fssh_st_mode)) {
882			// get source leaf
883			char leaf[FSSH_B_FILE_NAME_LENGTH];
884			error = get_last_path_component(source, leaf, sizeof(leaf));
885			if (error != FSSH_B_OK) {
886				fprintf(stderr, "Error: Failed to get leaf name of source "
887					"path: %s\n", fssh_strerror(error));
888				return error;
889			}
890
891			// compose a new path
892			int len = strlen(target) + 1 + strlen(leaf);
893			if (len > (int)sizeof(targetBuffer)) {
894				fprintf(stderr, "Error: Resulting target path is too long.\n");
895				return FSSH_B_BAD_VALUE;
896			}
897
898			strcpy(targetBuffer, target);
899			strcat(targetBuffer, "/");
900			strcat(targetBuffer, leaf);
901			target = targetBuffer;
902		}
903	}
904
905	// check, if the target exists
906	error = _kern_read_stat(-1, target, false, &st, sizeof(st));
907	if (error == FSSH_B_OK) {
908		if (!force) {
909			fprintf(stderr, "Error: Can't create link. \"%s\" is in the way.\n",
910				target);
911			return FSSH_B_FILE_EXISTS;
912		}
913
914		// unlink the entry
915		error = _kern_unlink(-1, target);
916		if (error != FSSH_B_OK) {
917			fprintf(stderr, "Error: Failed to remove \"%s\" to make way for "
918				"link: %s\n", target, fssh_strerror(error));
919			return error;
920		}
921	}
922
923	// finally create the link
924	if (symbolic) {
925		error = _kern_create_symlink(-1, target, source,
926			FSSH_S_IRWXU | FSSH_S_IRWXG | FSSH_S_IRWXO);
927	} else
928		error = _kern_create_link(target, source);
929
930	if (error != FSSH_B_OK) {
931		fprintf(stderr, "Error: Failed to create link: %s\n",
932			fssh_strerror(error));
933	}
934
935	return error;
936}
937
938
939static fssh_status_t
940command_ls(int argc, const char* const* argv)
941{
942	const char* const currentDirFiles[] = { ".", NULL };
943	const char* const* files;
944	if (argc >= 2)
945		files = argv + 1;
946	else
947		files = currentDirFiles;
948
949	for (; *files; files++) {
950		const char* file = *files;
951		// stat file
952		struct fssh_stat st;
953		fssh_status_t error = _kern_read_stat(-1, file, false, &st, sizeof(st));
954		if (error != FSSH_B_OK) {
955			fprintf(stderr, "Error: Failed to stat() \"%s\": %s\n", file,
956				fssh_strerror(error));
957			continue;
958		}
959
960		// if it is a directory, print its entries
961		if (FSSH_S_ISDIR(st.fssh_st_mode)) {
962			printf("%s:\n", file);
963
964			// open dir
965			int fd = _kern_open_dir(-1, file);
966			if (fd < 0) {
967				fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n",
968					file, fssh_strerror(fd));
969				continue;
970			}
971
972			// iterate through the entries
973			char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
974			fssh_dirent* entry = (fssh_dirent*)buffer;
975			fssh_ssize_t entriesRead = 0;
976			while ((entriesRead = _kern_read_dir(fd, entry, sizeof(buffer), 1))
977					== 1) {
978				list_entry(file, entry->d_name);
979			}
980
981			if (entriesRead < 0) {
982				fprintf(stderr, "Error: reading dir \"%s\" failed: %s\n",
983					file, fssh_strerror(entriesRead));
984			}
985
986			// close dir
987			error = _kern_close(fd);
988			if (error != FSSH_B_OK) {
989				fprintf(stderr, "Error: Closing dir \"%s\" (fd: %d) failed: "
990					"%s\n", file, fd, fssh_strerror(error));
991				continue;
992			}
993		} else
994			list_entry(file);
995	}
996
997	return FSSH_B_OK;
998}
999
1000
1001static fssh_status_t
1002command_touch(int argc, const char* const* argv)
1003{
1004	int argi = 1;
1005	if (argi >= argc) {
1006		printf("Usage: %s <file>...\n", argv[0]);
1007		return FSSH_B_BAD_VALUE;
1008	}
1009
1010	for (; argi < argc; argi++) {
1011		const char* file = argv[argi];
1012		if (strlen(file) == 0) {
1013			fprintf(stderr, "Error: An empty path is not a valid argument!\n");
1014			return FSSH_B_BAD_VALUE;
1015		}
1016
1017		fprintf(stderr, "creating file: %s\n", file);
1018		fssh_status_t error = create_file(file);
1019		if (error != FSSH_B_OK)
1020			return error;
1021	}
1022
1023	return FSSH_B_OK;
1024}
1025
1026
1027static fssh_status_t
1028command_mkdir(int argc, const char* const* argv)
1029{
1030	bool createParents = false;
1031
1032	// parse parameters
1033	int argi = 1;
1034	for (argi = 1; argi < argc; argi++) {
1035		const char *arg = argv[argi];
1036		if (arg[0] != '-')
1037			break;
1038
1039		if (arg[1] == '\0') {
1040			fprintf(stderr, "Error: Invalid option \"-\"\n");
1041			return FSSH_B_BAD_VALUE;
1042		}
1043
1044		for (int i = 1; arg[i]; i++) {
1045			switch (arg[i]) {
1046				case 'p':
1047					createParents = true;
1048					break;
1049				default:
1050					fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
1051					return FSSH_B_BAD_VALUE;
1052			}
1053		}
1054	}
1055
1056	if (argi >= argc) {
1057		printf("Usage: %s [ -p ] <dir>...\n", argv[0]);
1058		return FSSH_B_BAD_VALUE;
1059	}
1060
1061	// create loop
1062	for (; argi < argc; argi++) {
1063		const char *dir = argv[argi];
1064		if (strlen(dir) == 0) {
1065			fprintf(stderr, "Error: An empty path is not a valid argument!\n");
1066			return FSSH_B_BAD_VALUE;
1067		}
1068
1069		fssh_status_t error = create_dir(dir, createParents);
1070		if (error != FSSH_B_OK)
1071			return error;
1072	}
1073
1074	return FSSH_B_OK;
1075}
1076
1077
1078static fssh_status_t
1079command_mkindex(int argc, const char* const* argv)
1080{
1081	if (argc < 2) {
1082		fprintf(stderr, "Usage: %s [-t <type>] <index name>\n", argv[0]);
1083		return FSSH_B_BAD_VALUE;
1084	}
1085
1086	int fileArg = 1;
1087	int type = FSSH_B_STRING_TYPE;
1088
1089	if (argc > 3 && strcmp(argv[1], "-t") == 0) {
1090		fileArg = 3;
1091		if (strcmp(argv[2], "string") == 0)
1092			type = FSSH_B_STRING_TYPE;
1093		else if (strcmp(argv[2], "int32") == 0)
1094			type = FSSH_B_INT32_TYPE;
1095		else {
1096			fprintf(stderr, "Unhandled attribute type %s\n", argv[2]);
1097			return FSSH_B_BAD_VALUE;
1098		}
1099	}
1100
1101	const char* indexName = argv[fileArg];
1102
1103	// get the volume ID
1104	fssh_dev_t volumeID = get_volume_id();
1105	if (volumeID < 0)
1106		return volumeID;
1107
1108	// create the index
1109	fssh_status_t error =_kern_create_index(volumeID, indexName, type, 0);
1110	if (error != FSSH_B_OK) {
1111		fprintf(stderr, "Error: Failed to create index \"%s\": %s\n",
1112			indexName, fssh_strerror(error));
1113		return error;
1114	}
1115
1116	return FSSH_B_OK;
1117}
1118
1119
1120static fssh_status_t
1121command_mv(int argc, const char* const* argv)
1122{
1123	bool force = false;
1124
1125	// parse parameters
1126	int argi = 1;
1127	for (argi = 1; argi < argc; argi++) {
1128		const char *arg = argv[argi];
1129		if (arg[0] != '-')
1130			break;
1131
1132		if (arg[1] == '\0') {
1133			fprintf(stderr, "Error: Invalid option \"-\"\n");
1134			return FSSH_B_BAD_VALUE;
1135		}
1136
1137		for (int i = 1; arg[i]; i++) {
1138			switch (arg[i]) {
1139				case 'f':
1140					force = true;
1141					break;
1142				default:
1143					fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
1144					return FSSH_B_BAD_VALUE;
1145			}
1146		}
1147	}
1148
1149	// check params
1150	int count = argc - 1 - argi;
1151	if (count <= 0) {
1152		fprintf(stderr, "Usage: %s [-f] <file>... <target>\n", argv[0]);
1153		return FSSH_B_BAD_VALUE;
1154	}
1155
1156	const char* target = argv[argc - 1];
1157
1158	// stat the target
1159	struct fssh_stat st;
1160	fssh_status_t status = _kern_read_stat(-1, target, true, &st, sizeof(st));
1161	if (status != FSSH_B_OK && count != 1) {
1162		fprintf(stderr, "Error: Failed to stat target \"%s\": %s\n", target,
1163			fssh_strerror(status));
1164		return status;
1165	}
1166
1167	if (status == FSSH_B_OK && FSSH_S_ISDIR(st.fssh_st_mode)) {
1168		// move several entries
1169		int targetDir = _kern_open_dir(-1, target);
1170		if (targetDir < 0) {
1171			fprintf(stderr, "Error: Failed to open dir \"%s\": %s\n", target,
1172				fssh_strerror(targetDir));
1173			return targetDir;
1174		}
1175
1176		// move loop
1177		for (; argi < argc - 1; argi++) {
1178			status = move_entry(-1, argv[argi], targetDir, argv[argi], force);
1179			if (status != FSSH_B_OK) {
1180				_kern_close(targetDir);
1181				return status;
1182			}
1183		}
1184
1185		_kern_close(targetDir);
1186		return FSSH_B_OK;
1187	}
1188
1189	// rename single entry
1190	return move_entry(-1, argv[argi], -1, target, force);
1191}
1192
1193
1194static fssh_status_t
1195command_query(int argc, const char* const* argv)
1196{
1197	if (argc != 2) {
1198		fprintf(stderr, "Usage: %s <query string>\n", argv[0]);
1199		return FSSH_B_BAD_VALUE;
1200	}
1201
1202	const char* query = argv[1];
1203
1204	// get the volume ID
1205	fssh_dev_t volumeID = get_volume_id();
1206	if (volumeID < 0)
1207		return volumeID;
1208
1209	// open query
1210	int fd = _kern_open_query(volumeID, query, strlen(query), 0, -1, -1);
1211	if (fd < 0) {
1212		fprintf(stderr, "Error: Failed to open query: %s\n", fssh_strerror(fd));
1213		return fd;
1214	}
1215
1216	// iterate through the entries
1217	fssh_status_t error = FSSH_B_OK;
1218	char buffer[sizeof(fssh_dirent) + FSSH_B_FILE_NAME_LENGTH];
1219	fssh_dirent* entry = (fssh_dirent*)buffer;
1220	fssh_ssize_t entriesRead = 0;
1221	while ((entriesRead = _kern_read_dir(fd, entry, sizeof(buffer), 1)) == 1) {
1222		char path[FSSH_B_PATH_NAME_LENGTH];
1223		error = _kern_entry_ref_to_path(volumeID, entry->d_pino, entry->d_name,
1224			path, sizeof(path));
1225		if (error == FSSH_B_OK) {
1226			printf("  %s\n", path);
1227		} else {
1228			fprintf(stderr, "  failed to resolve entry (%8" FSSH_B_PRIdINO
1229				", \"%s\")\n", entry->d_pino, entry->d_name);
1230		}
1231	}
1232
1233	if (entriesRead < 0) {
1234		fprintf(stderr, "Error: reading query failed: %s\n",
1235			fssh_strerror(entriesRead));
1236	}
1237
1238	// close query
1239	error = _kern_close(fd);
1240	if (error != FSSH_B_OK) {
1241		fprintf(stderr, "Error: Closing query (fd: %d) failed: %s\n",
1242			fd, fssh_strerror(error));
1243	}
1244
1245	return error;
1246}
1247
1248
1249static fssh_status_t
1250command_quit(int argc, const char* const* argv)
1251{
1252	return COMMAND_RESULT_EXIT;
1253}
1254
1255
1256static fssh_status_t
1257command_rm(int argc, const char* const* argv)
1258{
1259	bool recursive = false;
1260	bool force = false;
1261
1262	// parse parameters
1263	int argi = 1;
1264	for (argi = 1; argi < argc; argi++) {
1265		const char *arg = argv[argi];
1266		if (arg[0] != '-')
1267			break;
1268
1269		if (arg[1] == '\0') {
1270			fprintf(stderr, "Error: Invalid option \"-\"\n");
1271			return FSSH_B_BAD_VALUE;
1272		}
1273
1274		for (int i = 1; arg[i]; i++) {
1275			switch (arg[i]) {
1276				case 'f':
1277					force = true;
1278					break;
1279				case 'r':
1280					recursive = true;
1281					break;
1282				default:
1283					fprintf(stderr, "Error: Unknown option \"-%c\"\n", arg[i]);
1284					return FSSH_B_BAD_VALUE;
1285			}
1286		}
1287	}
1288
1289	// check params
1290	if (argi >= argc) {
1291		fprintf(stderr, "Usage: %s [ -r ] <file>...\n", argv[0]);
1292		return FSSH_B_BAD_VALUE;
1293	}
1294
1295	// remove loop
1296	for (; argi < argc; argi++) {
1297		fssh_status_t error = remove_entry(-1, argv[argi], recursive, force);
1298		if (error != FSSH_B_OK)
1299			return error;
1300	}
1301
1302	return FSSH_B_OK;
1303}
1304
1305
1306static fssh_status_t
1307command_sync(int argc, const char* const* argv)
1308{
1309	fssh_status_t error = _kern_sync();
1310	if (error != FSSH_B_OK) {
1311		fprintf(stderr, "Error: syncing: %s\n", fssh_strerror(error));
1312		return error;
1313	}
1314
1315	return FSSH_B_OK;
1316}
1317
1318
1319static fssh_status_t
1320command_ioctl(int argc, const char* const* argv)
1321{
1322	if (argc != 2) {
1323		fprintf(stderr, "Usage: %s <opcode>\n", argv[0]);
1324		return FSSH_B_BAD_VALUE;
1325	}
1326
1327	int rootDir = _kern_open_dir(-1, "/myfs");
1328	if (rootDir < 0)
1329		return rootDir;
1330
1331	fssh_status_t status = _kern_ioctl(rootDir, atoi(argv[1]), NULL, 0);
1332
1333	_kern_close(rootDir);
1334
1335	if (status != FSSH_B_OK) {
1336		fprintf(stderr, "Error: ioctl failed: %s\n", fssh_strerror(status));
1337		return status;
1338	}
1339
1340	return FSSH_B_OK;
1341}
1342
1343
1344static void
1345register_commands()
1346{
1347	CommandManager::Default()->AddCommands(
1348		command_cd,			"cd",			"change current directory",
1349		command_chmod,		"chmod",		"change file permissions",
1350		command_cp,			"cp",			"copy files and directories",
1351		command_cat,		"cat",	"concatenate file(s) to stdout",
1352		command_help,		"help",			"list supported commands",
1353		command_info,		"info",			"prints volume informations",
1354		command_ioctl,		"ioctl",		"ioctl() on root, for FS debugging only",
1355		command_ln,			"ln",			"create a hard or symbolic link",
1356		command_ls,			"ls",			"list files or directories",
1357		command_mkdir,		"mkdir",		"create directories",
1358		command_mkindex,	"mkindex",		"create an index",
1359		command_mv,			"mv",			"move/rename files and directories",
1360		command_query,		"query",		"query for files",
1361		command_quit,		"quit/exit",	"quit the shell",
1362		command_rm,			"rm",			"remove files and directories",
1363		command_sync,		"sync",			"syncs the file system",
1364		command_touch,		"touch",		"create empty file",
1365		NULL
1366	);
1367}
1368
1369
1370// #pragma mark - ArgVector
1371
1372
1373class ArgVector {
1374public:
1375	ArgVector()
1376		: fArgc(0),
1377		  fArgv(NULL)
1378	{
1379	}
1380
1381	~ArgVector()
1382	{
1383		_Cleanup();
1384	}
1385
1386	int Argc() const
1387	{
1388		return fArgc;
1389	}
1390
1391	const char* const* Argv() const
1392	{
1393		return fArgv;
1394	}
1395
1396	bool Parse(const char* commandLine)
1397	{
1398		_Cleanup();
1399
1400		// init temporary arg/argv storage
1401		std::string currentArg;
1402		std::vector<std::string> argVector;
1403
1404		fCurrentArg = &currentArg;
1405		fCurrentArgStarted = false;
1406		fArgVector = &argVector;
1407
1408		for (; *commandLine; commandLine++) {
1409			char c = *commandLine;
1410
1411			// whitespace delimits args and is otherwise ignored
1412			if (isspace(c)) {
1413				_PushCurrentArg();
1414				continue;
1415			}
1416
1417			switch (c) {
1418				case '\'':
1419					// quoted string -- no quoting
1420					while (*++commandLine != '\'') {
1421						c = *commandLine;
1422						if (c == '\0') {
1423							fprintf(stderr, "Error: Unterminated quoted "
1424								"string.\n");
1425							return false;
1426						}
1427						_PushCharacter(c);
1428					}
1429					break;
1430
1431				case '"':
1432					// quoted string -- some quoting
1433					while (*++commandLine != '"') {
1434						c = *commandLine;
1435						if (c == '\0') {
1436							fprintf(stderr, "Error: Unterminated quoted "
1437								"string.\n");
1438							return false;
1439						}
1440
1441						if (c == '\\') {
1442							c = *++commandLine;
1443							if (c == '\0') {
1444								fprintf(stderr, "Error: Unterminated quoted "
1445									"string.\n");
1446								return false;
1447							}
1448
1449							// only '\' and '"' can be quoted, otherwise the
1450							// the '\' is treated as a normal char
1451							if (c != '\\' && c != '"')
1452								_PushCharacter('\\');
1453						}
1454
1455						_PushCharacter(c);
1456					}
1457					break;
1458
1459				case '\\':
1460					// quoted char
1461					c = *++commandLine;
1462					if (c == '\0') {
1463						fprintf(stderr, "Error: Command line ends with "
1464							"'\\'.\n");
1465						return false;
1466					}
1467					_PushCharacter(c);
1468					break;
1469
1470				default:
1471					// normal char
1472					_PushCharacter(c);
1473					break;
1474			}
1475		}
1476
1477		// commit last arg
1478		_PushCurrentArg();
1479
1480		// build arg vector
1481		fArgc = argVector.size();
1482		fArgv = new char*[fArgc + 1];
1483		for (int i = 0; i < fArgc; i++) {
1484			int len = argVector[i].length();
1485			fArgv[i] = new char[len + 1];
1486			memcpy(fArgv[i], argVector[i].c_str(), len + 1);
1487		}
1488		fArgv[fArgc] = NULL;
1489
1490		return true;
1491	}
1492
1493private:
1494	void _Cleanup()
1495	{
1496		if (fArgv) {
1497			for (int i = 0; i < fArgc; i++)
1498				delete[] fArgv[i];
1499			delete[] fArgv;
1500		}
1501	}
1502
1503	void _PushCurrentArg()
1504	{
1505		if (fCurrentArgStarted) {
1506			fArgVector->push_back(*fCurrentArg);
1507			fCurrentArgStarted = false;
1508		}
1509	}
1510
1511	void _PushCharacter(char c)
1512	{
1513		if (!fCurrentArgStarted) {
1514			*fCurrentArg = "";
1515			fCurrentArgStarted = true;
1516		}
1517
1518		*fCurrentArg += c;
1519	}
1520
1521private:
1522	// temporaries
1523	std::string*				fCurrentArg;
1524	bool						fCurrentArgStarted;
1525	std::vector<std::string>*	fArgVector;
1526
1527	int							fArgc;
1528	char**						fArgv;
1529};
1530
1531
1532// #pragma mark - input loop
1533
1534
1535static char*
1536read_command_line(char* buffer, int bufferSize)
1537{
1538	// print prompt (including cwd, if available)
1539	char directory[FSSH_B_PATH_NAME_LENGTH];
1540	if (_kern_getcwd(directory, sizeof(directory)) == FSSH_B_OK)
1541		printf("fssh:%s> ", directory);
1542	else
1543		printf("fssh> ");
1544	fflush(stdout);
1545
1546	// read input line
1547	return fgets(buffer, bufferSize, stdin);
1548}
1549
1550
1551static void
1552input_loop(bool interactive)
1553{
1554	static const int kInputBufferSize = 100 * 1024;
1555	char* inputBuffer = new char[kInputBufferSize];
1556
1557	for (;;) {
1558		// read command line
1559		if (interactive) {
1560			if (!read_command_line(inputBuffer, kInputBufferSize))
1561				break;
1562		} else {
1563			if (!get_external_command(inputBuffer, kInputBufferSize))
1564				break;
1565		}
1566
1567		// construct argv vector
1568		int result = FSSH_B_BAD_VALUE;
1569		ArgVector argVector;
1570		if (argVector.Parse(inputBuffer) && argVector.Argc() > 0) {
1571			int argc = argVector.Argc();
1572			const char* const* argv = argVector.Argv();
1573
1574			// find command
1575			Command* command = CommandManager::Default()->FindCommand(argv[0]);
1576			if (command) {
1577				// execute it
1578				result = command->Do(argc, argv);
1579				if (result == COMMAND_RESULT_EXIT) {
1580					if (!interactive)
1581						reply_to_external_command(0);
1582					break;
1583				}
1584			} else {
1585				fprintf(stderr, "Error: Invalid command \"%s\". Type \"help\" "
1586					"for a list of supported commands\n", argv[0]);
1587			}
1588		}
1589
1590		if (!interactive)
1591			reply_to_external_command(fssh_to_host_error(result));
1592	}
1593
1594	if (!interactive)
1595		external_command_cleanup();
1596
1597	delete[] inputBuffer;
1598}
1599
1600
1601static int
1602standard_session(const char* device, const char* fsName, bool interactive)
1603{
1604	// mount FS
1605	fssh_dev_t fsDev = _kern_mount(kMountPoint, device, fsName, 0, NULL, 0);
1606	if (fsDev < 0) {
1607		fprintf(stderr, "Error: Mounting FS failed: %s\n",
1608			fssh_strerror(fsDev));
1609		return 1;
1610	}
1611
1612	// register commands
1613	register_commands();
1614	register_additional_commands();
1615
1616	// process commands
1617	input_loop(interactive);
1618
1619	// unmount FS
1620	_kern_setcwd(-1, "/");	// avoid a "busy" vnode
1621	fssh_status_t error = _kern_unmount(kMountPoint, 0);
1622	if (error != FSSH_B_OK) {
1623		fprintf(stderr, "Error: Unmounting FS failed: %s\n",
1624			fssh_strerror(error));
1625		return 1;
1626	}
1627
1628	return 0;
1629}
1630
1631
1632static int
1633initialization_session(const char* device, const char* fsName,
1634	const char* volumeName, const char* initParameters)
1635{
1636	fssh_status_t error = _kern_initialize_volume(fsName, device,
1637		volumeName, initParameters);
1638	if (error != FSSH_B_OK) {
1639		fprintf(stderr, "Error: Initializing volume failed: %s\n",
1640			fssh_strerror(error));
1641		return 1;
1642	}
1643
1644	return 0;
1645}
1646
1647
1648static void
1649print_usage(bool error)
1650{
1651	fprintf((error ? stderr : stdout),
1652		"Usage: %s [ --start-offset <startOffset>]\n"
1653		"          [ --end-offset <endOffset>] [-n] <device>\n"
1654		"       %s [ --start-offset <startOffset>]\n"
1655		"          [ --end-offset <endOffset>]\n"
1656		"          --initialize [-n] <device> <volume name> "
1657			"[ <init parameters> ]\n",
1658		sArgv[0], sArgv[0]
1659	);
1660}
1661
1662
1663static void
1664print_usage_and_exit(bool error)
1665{
1666	print_usage(error);
1667	exit(error ? 1 : 0);
1668}
1669
1670
1671}	// namespace FSShell
1672
1673
1674using namespace FSShell;
1675
1676
1677int
1678main(int argc, const char* const* argv)
1679{
1680	sArgc = argc;
1681	sArgv = argv;
1682
1683	// process arguments
1684	bool interactive = true;
1685	bool initialize = false;
1686	const char* device = NULL;
1687	const char* volumeName = NULL;
1688	const char* initParameters = NULL;
1689	fssh_off_t startOffset = 0;
1690	fssh_off_t endOffset = -1;
1691
1692	// eat options
1693	int argi = 1;
1694	while (argi < argc && argv[argi][0] == '-') {
1695		const char* arg = argv[argi++];
1696		if (strcmp(arg, "--help") == 0) {
1697			print_usage_and_exit(false);
1698		} else if (strcmp(arg, "--initialize") == 0) {
1699			initialize = true;
1700		} else if (strcmp(arg, "-n") == 0) {
1701			interactive = false;
1702		} else if (strcmp(arg, "--start-offset") == 0) {
1703			if (argi >= argc)
1704				print_usage_and_exit(true);
1705			startOffset = atoll(argv[argi++]);
1706		} else if (strcmp(arg, "--end-offset") == 0) {
1707			if (argi >= argc)
1708				print_usage_and_exit(true);
1709			endOffset = atoll(argv[argi++]);
1710		} else {
1711			print_usage_and_exit(true);
1712		}
1713	}
1714
1715	// get device
1716	if (argi >= argc)
1717		print_usage_and_exit(true);
1718	device = argv[argi++];
1719
1720	// get volume name and init parameters
1721	if (initialize) {
1722		// volume name
1723		if (argi >= argc)
1724			print_usage_and_exit(true);
1725		volumeName = argv[argi++];
1726
1727		// (optional) init paramaters
1728		if (argi < argc)
1729			initParameters = argv[argi++];
1730	}
1731
1732	// more parameters are excess
1733	if (argi < argc)
1734		print_usage_and_exit(true);
1735
1736	// get FS module
1737	if (!modules[0]) {
1738		fprintf(stderr, "Error: Couldn't find FS module!\n");
1739		return 1;
1740	}
1741	const char* fsName = modules[0]->name;
1742
1743	fssh_status_t error;
1744
1745	// init kernel
1746	error = init_kernel();
1747	if (error != FSSH_B_OK) {
1748		fprintf(stderr, "Error: Initializing kernel failed: %s\n",
1749			fssh_strerror(error));
1750		return error;
1751	}
1752
1753	// restrict access if requested
1754	if (startOffset != 0 || endOffset != -1)
1755		add_file_restriction(device, startOffset, endOffset);
1756
1757	// start the action
1758	int result;
1759	if (initialize) {
1760		result = initialization_session(device, fsName, volumeName,
1761			initParameters);
1762	} else
1763		result = standard_session(device, fsName, interactive);
1764
1765	return result;
1766}
1767