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