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