xref: /haiku/src/tools/fs_shell/fssh.cpp (revision 9f3bdf3d039430b5172c424def20ce5d9f7367d4)
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 
41 extern fssh_module_info *modules[];
42 
43 
44 extern fssh_file_system_module_info gRootFileSystem;
45 
46 namespace FSShell {
47 
48 const char* kMountPoint = "/myfs";
49 
50 // command line args
51 static	int					sArgc;
52 static	const char* const*	sArgv;
53 
54 static mode_t sUmask = 0022;
55 
56 
57 static fssh_status_t
58 init_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 
124 Command::Command(const char* name, const char* description)
125 	: fName(name),
126 	  fDescription(description)
127 {
128 }
129 
130 
131 Command::Command(command_function* function, const char* name,
132 	const char* description)
133 	: fName(name),
134 	  fDescription(description),
135 	  fFunction(function)
136 {
137 }
138 
139 
140 Command::~Command()
141 {
142 }
143 
144 
145 const char*
146 Command::Name() const
147 {
148 	return fName.c_str();
149 }
150 
151 
152 const char*
153 Command::Description() const
154 {
155 	return fDescription.c_str();
156 }
157 
158 
159 fssh_status_t
160 Command::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 
173 CommandManager::CommandManager()
174 {
175 }
176 
177 
178 CommandManager*
179 CommandManager::Default()
180 {
181 	if (!sManager)
182 		sManager = new CommandManager;
183 	return sManager;
184 }
185 
186 
187 void
188 CommandManager::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 
204 void
205 CommandManager::AddCommand(command_function* function, const char* name,
206 	const char* description)
207 {
208 	AddCommand(new Command(function, name, description));
209 }
210 
211 
212 void
213 CommandManager::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 
233 Command*
234 CommandManager::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 
244 void
245 CommandManager::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 
256 CommandManager*	CommandManager::sManager = NULL;
257 
258 
259 // #pragma mark - Command support functions
260 
261 
262 static bool
263 get_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 
282 static fssh_dev_t
283 get_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 
298 static const char *
299 byte_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 
322 void
323 print_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 
330 static void
331 list_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 
421 static fssh_status_t
422 create_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 
451 static fssh_status_t
452 create_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 
520 static fssh_status_t remove_entry(int dir, const char *entry, bool recursive,
521 	bool force);
522 
523 
524 static fssh_status_t
525 remove_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 
564 static fssh_status_t
565 remove_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 
612 static fssh_status_t
613 move_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 
635 static fssh_status_t
636 command_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 
660 static fssh_status_t
661 command_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 
718 static fssh_status_t
719 command_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 
784 static fssh_status_t
785 command_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 
793 static fssh_status_t
794 command_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 
830 static fssh_status_t
831 command_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 
939 static fssh_status_t
940 command_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 
1001 static fssh_status_t
1002 command_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 
1027 static fssh_status_t
1028 command_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 
1078 static fssh_status_t
1079 command_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 
1120 static fssh_status_t
1121 command_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 
1194 static fssh_status_t
1195 command_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 
1249 static fssh_status_t
1250 command_quit(int argc, const char* const* argv)
1251 {
1252 	return COMMAND_RESULT_EXIT;
1253 }
1254 
1255 
1256 static fssh_status_t
1257 command_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 
1306 static fssh_status_t
1307 command_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 
1319 static fssh_status_t
1320 command_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 
1344 static void
1345 register_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 
1373 class ArgVector {
1374 public:
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 
1493 private:
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 
1521 private:
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 
1535 static char*
1536 read_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 
1551 static void
1552 input_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 
1601 static int
1602 standard_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 
1632 static int
1633 initialization_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 
1648 static void
1649 print_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 
1663 static void
1664 print_usage_and_exit(bool error)
1665 {
1666 	print_usage(error);
1667 	exit(error ? 1 : 0);
1668 }
1669 
1670 
1671 }	// namespace FSShell
1672 
1673 
1674 using namespace FSShell;
1675 
1676 
1677 int
1678 main(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