1/*
2  This file contains a simple file system "shell" that lets you
3  manipulate a file system.  There is a simple command table that
4  contains the available functions.  It is very easy to extend.
5
6
7  THIS CODE COPYRIGHT DOMINIC GIAMPAOLO.  NO WARRANTY IS EXPRESSED
8  OR IMPLIED.  YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR
9  NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED.
10
11  FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com).
12
13  Dominic Giampaolo
14  dbg@be.com
15*/
16
17#include "compat.h"
18
19#include <errno.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <ctype.h>
23#include <string.h>
24#include <sys/time.h>
25
26#include "additional_commands.h"
27#include "argv.h"
28#include "external_commands.h"
29#include "kprotos.h"
30#include "path_util.h"
31#include "tracker.h"
32#include "xcp.h"
33
34#include <fs_attr.h>
35#include <fs_query.h>
36
37static bool sInteractiveMode = true;
38
39
40static int do_lat_fs(int argc, char **argv);
41static void do_fsh(void);
42static int remove_entry(int dir, const char *entry, bool recursive, bool force);
43
44
45static void
46print_usage(const char *program)
47{
48	printf("----------------------------------------------------------------------\n");
49	printf("Ultra neat-o userland filesystem testing shell thingy\n");
50	printf("----------------------------------------------------------------------\n");
51	printf("usage: %s [-n] [%%s:DISK_IMAGE=big_file|%%d:RANDOM_SEED]\n",
52		program);
53	printf("       %s --initialize [-n] %%s:DISK_IMAGE FS_PARAMETERS...\n",
54		program);
55	printf("\n");
56}
57
58
59int
60main(int argc, char **argv)
61{
62	int seed = 0;
63	char *disk_name = "big_file";
64	myfs_info  *myfs;
65	char *arg;
66	int argi = 1;
67	bool initialize = false;
68
69	if (argv[1] && strcmp(argv[1], "--help") == 0) {
70		print_usage(argv[0]);
71		exit(0);
72	}
73
74	// eat options
75	while (argi < argc && argv[argi][0] == '-') {
76		arg = argv[argi++];
77		if (strcmp(arg, "-n") == 0) {
78			sInteractiveMode = false;
79		} else if (strcmp(arg, "--initialize") == 0) {
80			initialize = true;
81		} else {
82	    	print_usage(argv[0]);
83			exit(1);
84		}
85	}
86
87	if (argi >= argc) {
88    	print_usage(argv[0]);
89		exit(1);
90	}
91
92	arg = argv[argi];
93
94	if (initialize) {
95		const char *deviceName = arg;
96		initialize_fs(deviceName, argv + argi, argc - argi);
97	} else {
98		if (arg != NULL && !isdigit(arg[0]))
99			disk_name = arg;
100		else if (arg && isdigit(arg[0]))
101			seed = strtoul(arg, NULL, 0);
102		else
103			seed = getpid() * time(NULL) | 1;
104		printf("random seed == 0x%x\n", seed);
105
106		srand(seed);
107
108	    myfs = init_fs(disk_name);
109
110	    do_fsh();
111
112		sys_chdir(1, -1, "/");
113		if (sys_unmount(1, -1, "/myfs") != 0) {
114			printf("could not un-mount /myfs\n");
115			return 5;
116		}
117
118	    shutdown_block_cache();
119	}
120
121    return 0;
122}
123
124
125static void
126make_random_name(char *buf, int len)
127{
128    int i, max = (rand() % (len - 5 - 3)) + 2;
129
130    strcpy(buf, "/myfs/");
131    for(i=0; i < max; i++) {
132        buf[i+6] = 'a' + (rand() % 26);
133    }
134
135    buf[i] = '\0';
136}
137
138
139static void
140SubTime(struct timeval *a, struct timeval *b, struct timeval *c)
141{
142  if ((long)(a->tv_usec - b->tv_usec) < 0)
143   {
144     a->tv_sec--;
145     a->tv_usec += 1000000;
146   }
147
148  c->tv_sec  = a->tv_sec  - b->tv_sec;
149  c->tv_usec = a->tv_usec - b->tv_usec;
150}
151
152
153
154int cur_fd = -1;
155
156
157static int
158do_close(int argc, char **argv)
159{
160    int err;
161
162    err = sys_close(1, cur_fd);
163/*  printf("close of fd %d returned: %d\n", cur_fd, err); */
164    cur_fd = -1;
165
166	return err;
167}
168
169static int
170do_open(int argc, char **argv)
171{
172    char name[64];
173
174    if (cur_fd >= 0)
175        do_close(0, NULL);
176
177    if (argc < 2)
178        make_random_name(name, sizeof(name));
179    else
180        sprintf(name, "/myfs/%s", &argv[1][0]);
181
182    cur_fd = sys_open(1, -1, name, MY_O_RDWR, MY_S_IFREG, 0);
183    if (cur_fd < 0) {
184        printf("error opening %s : %s (%d)\n", name, fs_strerror(cur_fd), cur_fd);
185    	return cur_fd;
186    } else {
187        printf("opened: %s\n", name);
188		return 0;
189    }
190}
191
192static int
193do_make(int argc, char **argv)
194{
195    char name[64];
196
197    if (cur_fd >= 0)
198        do_close(0, NULL);
199
200    if (argc < 2)
201        make_random_name(name, sizeof(name));
202    else if (argv[1][0] != '/')
203        sprintf(name, "/myfs/%s", &argv[1][0]);
204    else
205        strcpy(name, &argv[1][0]);
206
207    cur_fd = sys_open(1, -1, name, MY_O_RDWR | MY_O_CREAT, 0666, 0);
208    if (cur_fd < 0) {
209        printf("error creating: %s: %s\n", name, fs_strerror(cur_fd));
210        return cur_fd;
211    }
212
213/*  printf("created: %s (fd %d)\n", name, cur_fd); */
214
215	return 0;
216}
217
218
219static status_t
220create_dir(const char *path, bool createParents)
221{
222	// stat the entry
223	struct my_stat st;
224	status_t error = sys_rstat(true, -1, path, &st, false);
225	if (error == FS_OK) {
226		if (createParents && MY_S_ISDIR(st.mode))
227			return FS_OK;
228
229		fprintf(stderr, "Cannot make dir, entry `%s' is in the way.\n", path);
230		return FS_FILE_EXISTS;
231	}
232
233	// the dir doesn't exist yet
234	// if we shall create all parents, do that first
235	if (createParents) {
236		// create the parent dir path
237		// eat the trailing '/'s
238		int len = strlen(path);
239		while (len > 0 && path[len - 1] == '/')
240			len--;
241
242		// eat the last path component
243		while (len > 0 && path[len - 1] != '/')
244			len--;
245
246		// eat the trailing '/'s
247		while (len > 0 && path[len - 1] == '/')
248			len--;
249
250		// Now either nothing remains, which means we had a single component,
251		// a root subdir -- in those cases we can just fall through (we should
252		// actually never be here in case of the root dir, but anyway) -- or
253		// there is something left, which we can call a parent directory and
254		// try to create it.
255		if (len > 0) {
256			char *parentPath = (char*)malloc(len + 1);
257			if (!parentPath) {
258				fprintf(stderr, "Failed to allocate memory for parent path.\n");
259				return FS_NO_MEMORY;
260			}
261			memcpy(parentPath, path, len);
262			parentPath[len] = '\0';
263
264			error = create_dir(parentPath, createParents);
265
266			free(parentPath);
267
268			if (error != FS_OK)
269				return error;
270		}
271	}
272
273	// make the directory
274	error = sys_mkdir(true, -1, path, MY_S_IRWXU);
275	if (error != FS_OK) {
276		fprintf(stderr, "Failed to make directory `%s': %s\n", path,
277			fs_strerror(error));
278		return error;
279	}
280
281	return FS_OK;
282}
283
284
285static int
286do_mkdir(int argc, char **argv)
287{
288	bool createParents = false;
289
290	// parse parameters
291	int argi = 1;
292	for (argi = 1; argi < argc; argi++) {
293		const char *arg = argv[argi];
294		if (arg[0] != '-')
295			break;
296
297		if (arg[1] == '\0') {
298			fprintf(stderr, "Invalid option `-'\n");
299			return FS_EINVAL;
300		}
301
302		for (int i = 1; arg[i]; i++) {
303			switch (arg[i]) {
304				case 'p':
305					createParents = true;
306					break;
307				default:
308					fprintf(stderr, "Unknown option `-%c'\n", arg[i]);
309					return FS_EINVAL;
310			}
311		}
312	}
313
314	if (argi >= argc) {
315		printf("usage: %s [ -p ] <dir>...\n", argv[0]);
316		return FS_EINVAL;
317	}
318
319	// create loop
320	for (; argi < argc; argi++) {
321		const char *dir = argv[argi];
322		if (strlen(dir) == 0) {
323			fprintf(stderr, "An empty path is not a valid argument!\n");
324			return FS_EINVAL;
325		}
326
327		status_t error = create_dir(dir, createParents);
328		if (error != FS_OK)
329			return error;
330	}
331
332	return FS_OK;
333}
334
335
336static int
337do_read_test(int argc, char **argv)
338{
339    int    i, err;
340    char   *buff;
341    size_t len = 256;
342
343
344    if (cur_fd < 0) {
345        printf("no file open! (open or create one with open or make)\n");
346        return FS_EINVAL;
347    }
348
349    if (argv[1] && isdigit(argv[1][0]))
350        len = strtoul(&argv[1][0], NULL, 0);
351
352    buff = (char*)malloc(len);
353    if (buff == NULL) {
354        printf("no memory for write buffer of %lu bytes\n", (unsigned long)len);
355        return FS_ENOMEM;
356    }
357
358    for (i = 0; (size_t)i < len; i++)
359        buff[i] = (char)0xff;
360
361    err = sys_read(1, cur_fd, buff, len);
362
363	if (len <= 1024)
364		hexdump(buff, len);
365	else {
366		printf("Hex dump shows only the first 1024 bytes:\n");
367		hexdump(buff, 1024);
368	}
369
370	free(buff);
371	printf("read read %lu bytes and returned %d\n", (unsigned long)len, err);
372
373	return (err < 0 ? err : 0);
374}
375
376
377static int
378do_write_test(int argc, char **argv)
379{
380    int    i, err;
381    char   *buff;
382    size_t len = 256;
383
384    if (cur_fd < 0) {
385        printf("no file open! (open or create one with open or make)\n");
386        return FS_EINVAL;
387    }
388
389    if (argv[1] && isdigit(argv[1][0]))
390        len = strtoul(&argv[1][0], NULL, 0);
391
392    buff = (char*)malloc(len);
393    if (buff == NULL) {
394        printf("no memory for write buffer of %lu bytes\n", (unsigned long)len);
395        return FS_ENOMEM;
396    }
397
398    for (i = 0; (size_t)i < len; i++)
399        buff[i] = i;
400
401    err = sys_write(1, cur_fd, buff, len);
402    free(buff);
403
404    printf("write wrote %lu bytes and returned %d\n", (unsigned long)len, err);
405
406	return (err < 0 ? err : 0);
407}
408
409
410static int
411do_write_stream(int argc, char **argv)
412{
413    size_t amount = 100000;
414	char buffer[4096];
415	int length = sizeof(buffer);
416    int i, err = FS_OK;
417
418    if (cur_fd < 0) {
419        printf("no file open! (open or create one with open or make)\n");
420        return FS_EINVAL;
421    }
422
423    if (argv[1] && isdigit(argv[1][0]))
424        amount = strtoul(&argv[1][0], NULL, 0);
425
426    for(i = 0; (size_t)i < sizeof(buffer); i++)
427        buffer[i] = i;
428
429	for (i = 0; (size_t)i < amount; i++) {
430		err = sys_write(1, cur_fd, buffer, length);
431		if (err < FS_OK)
432			break;
433	}
434
435    printf("write wrote %d bytes and returned %d\n", length, err);
436
437	return (err < 0 ? err : 0);
438}
439
440
441static int
442do_read_attr(int argc, char **argv)
443{
444    char *attribute,*buffer;
445    size_t len = 256;
446    int i, err;
447
448    if (cur_fd < 0) {
449        printf("no file open! (open or create one with open or make)\n");
450        return FS_EINVAL;
451    }
452
453	if (argc < 2) {
454        printf("usage: rdattr <attribute name> [bytes to write]\n");
455		return FS_EINVAL;
456	}
457
458	attribute = argv[1];
459    if (argv[2] && isdigit(*argv[2]))
460        len = strtoul(argv[2], NULL, 0);
461	if (len < 0) {
462		printf("invalid length for write attribute!\n");
463		return FS_EINVAL;
464	}
465
466    buffer = (char*)malloc(len);
467    if (buffer == NULL) {
468        printf("no memory for write buffer of %lu bytes\n", (unsigned long)len);
469        return FS_ENOMEM;
470    }
471
472	for (i = 0; (size_t)i < len; i++)
473		buffer[i] = (char)0xff;
474
475    err = sys_read_attr(1, cur_fd, attribute, 'CSTR', buffer, len, 0);
476
477    if (err >= 0)
478        hexdump(buffer, err < 512 ? err : 512);
479
480    free(buffer);
481    printf("read read %lu bytes and returned %d (%s)\n", (unsigned long)len, err, fs_strerror(err));
482
483	return (err < 0 ? err : 0);
484}
485
486
487static int
488do_write_attr(int argc, char **argv)
489{
490    char *attribute,*buffer;
491    size_t len = 256;
492    int i, err;
493
494    if (cur_fd < 0) {
495        printf("no file open! (open or create one with open or make)\n");
496        return FS_EINVAL;
497    }
498
499	if (argc < 2) {
500        printf("usage: wrattr <attribute name> [bytes to write]\n");
501		return FS_EINVAL;
502	}
503
504	attribute = argv[1];
505    if (argv[2] && isdigit(*argv[2]))
506        len = strtoul(argv[2], NULL, 0);
507	if (len < 0) {
508		printf("invalid length for write attribute!\n");
509		return FS_EINVAL;
510	}
511
512    buffer = (char*)malloc(len);
513    if (buffer == NULL) {
514        printf("no memory for write buffer of %lu bytes\n", (unsigned long)len);
515        return FS_ENOMEM;
516    }
517
518    for (i = 0; (size_t)i < len; i++)
519        buffer[i] = i;
520
521    err = sys_write_attr(1, cur_fd, attribute, 'CSTR', buffer, len, 0);
522    free(buffer);
523
524    printf("write wrote %lu bytes and returned %d\n", (unsigned long)len, err);
525
526	return (err < 0 ? err : 0);
527}
528
529
530static int
531do_remove_attr(int argc, char **argv)
532{
533    int err;
534
535    if (cur_fd < 0) {
536        printf("no file open! (open or create one with open or make)\n");
537        return FS_EINVAL;
538    }
539
540	if (argc < 2) {
541        printf("usage: rmattr <attribute name>\n");
542		return FS_EINVAL;
543	}
544
545    err = sys_remove_attr(1, cur_fd, argv[1]);
546
547    printf("remove_attr returned %d\n", err);
548
549	return err;
550}
551
552
553static int
554do_list_attr(int argc, char **argv)
555{
556	if (argc < 2 && cur_fd < 0) {
557		fprintf(stderr, "Must specify a file!\n");
558		return FS_EINVAL;
559	}
560
561	// open attr dir
562	int attrDir = -1;
563	if (argc >= 2) {
564		attrDir = sys_open_attr_dir(true, -1, argv[1]);
565	} else if (cur_fd >= 0) {
566		attrDir = sys_open_attr_dir(true, cur_fd, NULL);
567	} else {
568		fprintf(stderr, "Must specify a file!\n");
569		return FS_EINVAL;
570	}
571	if (attrDir < 0) {
572		fprintf(stderr, "Failed to open attribute directory: %s\n",
573			fs_strerror(attrDir));
574		return attrDir;
575	}
576
577	printf("  Type        Size                  Name\n");
578	printf("----------  --------  --------------------------------\n");
579
580	// iterate through the attributes
581	char entryBuffer[sizeof(my_dirent) + B_ATTR_NAME_LENGTH];
582	my_dirent *entry = (my_dirent *)entryBuffer;
583	while (sys_readdir(true, attrDir, entry, sizeof(entryBuffer), 1) > 0) {
584		// stat the attribute
585		my_attr_info info;
586		int error = sys_stat_attr(true, attrDir, NULL, entry->d_name, &info);
587		if (error == FS_OK) {
588			printf("0x%08lx  %8lld  %32s\n", info.type, (long long)info.size,
589				entry->d_name);
590		} else {
591			fprintf(stderr, "Failed to stat attribute `%s': %s\n",
592				entry->d_name, fs_strerror(error));
593		}
594	}
595
596	// close the attr dir
597	sys_closedir(true, attrDir);
598
599	return FS_OK;
600}
601
602
603static void
604mode_bits_to_str(int mode, char *str)
605{
606    int i;
607
608    strcpy(str, "----------");
609
610    if (MY_S_ISDIR(mode))
611        str[0] = 'd';
612    else if (MY_S_ISLNK(mode))
613        str[0] = 'l';
614    else if (MY_S_ISBLK(mode))
615        str[0] = 'b';
616    else if (MY_S_ISCHR(mode))
617        str[0] = 'c';
618
619    for(i=7; i > 0; i-=3, mode >>= 3) {
620        if (mode & MY_S_IROTH)
621            str[i]   = 'r';
622        if (mode & MY_S_IWOTH)
623            str[i+1] = 'w';
624        if (mode & MY_S_IXOTH)
625            str[i+2] = 'x';
626    }
627}
628
629
630static int
631do_chdir(int argc, char **argv)
632{
633	if (argc <= 1) {
634		fprintf(stderr, "No directory specified!\n");
635		return FS_EINVAL;
636	}
637
638	// change dir
639	const char *dir = argv[1];
640	int error = 0;
641	if (dir[0] == ':') {
642		// host environment
643		if (chdir(dir + 1) < 0)
644			error = from_platform_error(errno);
645	} else {
646		// emulated environment
647		error = sys_chdir(1, -1, dir);
648	}
649
650	if (error != 0) {
651		fprintf(stderr, "Failed to cd into `%s': %s\n", argv[1],
652			fs_strerror(error));
653	}
654
655	return error;
656}
657
658
659static int
660do_dir(int argc, char **argv)
661{
662	int dirfd, err, max_err = 10;
663	char dirname[128], buff[512], time_buf[64] = { '\0', };
664	size_t len, count = 0;
665	struct my_dirent *dent;
666	struct my_stat st;
667	struct tm *tm;
668	char mode_str[16];
669
670	dent = (struct my_dirent *)buff;
671	if (argc > 1)
672		strcpy(dirname, &argv[1][0]);
673	else
674		strcpy(dirname, ".");
675
676	if ((dirfd = sys_opendir(1, -1, dirname, 0)) < 0) {
677		printf("dir: error opening: %s\n", dirname);
678		return dirfd;
679	}
680
681	printf("Directory listing for: %s\n", dirname);
682	printf("      inode#  mode bits     uid    gid        size    "
683		"Date      Name\n");
684
685	while (1) {
686		len = 1;
687		err = sys_readdir(1, dirfd, dent, sizeof(buff), len);
688		if (err < 0) {
689			printf("readdir failed for: %s\n", dent->d_name);
690			if (max_err-- <= 0)
691				break;
692
693			continue;
694		}
695
696		if (err == 0)
697			break;
698
699		err = sys_rstat(1, dirfd, dent->d_name, &st, false);
700		if (err != 0) {
701			// may happen for unresolvable links
702			printf("stat failed for: %s (%Ld)\n", dent->d_name, dent->d_ino);
703			continue;
704		}
705
706		tm = localtime(&st.mtime);
707		strftime(time_buf, sizeof(time_buf), "%b %d %I:%M", tm);
708
709		mode_bits_to_str(st.mode, mode_str);
710
711		printf("%12Ld %s %6d %6d %12Ld %s %s", st.ino, mode_str,
712			st.uid, st.gid, st.size, time_buf, dent->d_name);
713
714		// if the entry is a symlink, print its target
715		if (MY_S_ISLNK(st.mode)) {
716			char linkTo[B_PATH_NAME_LENGTH];
717			ssize_t bytesRead = sys_readlink(true, dirfd, dent->d_name, linkTo,
718					sizeof(linkTo) - 1);
719			if (bytesRead >= 0) {
720				linkTo[bytesRead] = '\0';
721				printf(" -> %s", linkTo);
722			} else {
723				printf(" -> (%s)", fs_strerror(bytesRead));
724			}
725		}
726
727		printf("\n");
728
729		count++;
730	}
731
732	if (err != 0)
733		printf("readdir failed on: %s\n", dent->d_name);
734
735	printf("%ld files in directory!\n", (long)count);
736
737printf("sys_closedir()...\n");
738	sys_closedir(1, dirfd);
739printf("sys_closedir() done\n");
740
741	return 0;	// not really correct, but anyway...
742}
743
744
745static int
746do_ioctl(int argc, char **argv)
747{
748	int fd;
749	int err;
750
751	if (argc < 2) {
752		printf("ioctl: too few arguments\n");
753		return FS_EINVAL;
754	}
755
756	if ((fd = sys_open(1, -1, "/myfs/.", MY_O_RDONLY, S_IFREG, 0)) < 0) {
757	    printf("ioctl: error opening '.'\n");
758	    return fd;
759	}
760
761	err = sys_ioctl(1, fd, atoi(argv[1]), 0, 0);
762	if (err < 0) {
763	    printf("ioctl: error!\n");
764	}
765
766	sys_close(1, fd);
767
768	return err;
769}
770
771
772static int
773do_fcntl(int argc, char **argv)
774{
775	int err;
776
777    if (cur_fd < 0) {
778        printf("no file open! (open or create one with open or make)\n");
779        return FS_EINVAL;
780    }
781
782	if (argc < 2) {
783		printf("fcntl: too few arguments\n");
784		return FS_EINVAL;
785	}
786
787	err = sys_ioctl(1, cur_fd, atoi(argv[1]), 0, 0);
788	if (err < 0) {
789	    printf("fcntl: error!\n");
790	}
791
792	return err;
793}
794
795
796static int
797do_rmall(int argc, char **argv)
798{
799    int               dirfd, err, max_err = 10;
800    char              dirname[128], fname[512], buff[512];
801    size_t            len,count = 0;
802    struct my_dirent *dent;
803
804    dent = (struct my_dirent *)buff;
805
806    strcpy(dirname, "/myfs/");
807    if (argc > 1)
808        strcat(dirname, &argv[1][0]);
809
810    if ((dirfd = sys_opendir(1, -1, dirname, 0)) < 0) {
811        printf("dir: error opening: %s\n", dirname);
812        return dirfd;
813    }
814
815    while(1) {
816        len = 1;
817        err = sys_readdir(1, dirfd, dent, sizeof(buff), len);
818        if (err < 0) {
819            printf("readdir failed for: %s\n", dent->d_name);
820            if (max_err-- <= 0)
821                break;
822
823            continue;
824        }
825
826        if (err == 0)
827            break;
828
829        if (strcmp(dent->d_name, "..") == 0 || strcmp(dent->d_name, ".") == 0)
830            continue;
831
832        sprintf(fname, "%s/%s", dirname, dent->d_name);
833        err = sys_unlink(1, -1, fname);
834        if (err != 0) {
835            printf("unlink failed for: %s (%Ld)\n", fname, dent->d_ino);
836        } else
837	        count++;
838    }
839
840    if (err != 0) {
841        printf("readdir failed on: %s\n", dent->d_name);
842    }
843	printf("%ld files removed!\n", (long)count);
844
845    sys_closedir(1, dirfd);
846
847	return 0;	// not really correct, but anyway...
848}
849
850
851static int
852do_trunc(int argc, char **argv)
853{
854    int             err, new_size;
855    char            fname[256];
856    struct my_stat  st;
857
858    strcpy(fname, "/myfs/");
859    if (argc < 3) {
860        printf("usage: trunc fname newsize\n");
861        return FS_EINVAL;
862    }
863    strcat(fname, argv[1]);
864    new_size = strtoul(argv[2], NULL, 0);
865
866    st.size = new_size;
867    err = sys_wstat(1, -1, fname, &st, WSTAT_SIZE, 0);
868    if (err != 0) {
869        printf("truncate to %d bytes failed for %s\n", new_size, fname);
870    }
871
872    return err;
873}
874
875
876static int
877do_seek(int argc, char **argv)
878{
879    fs_off_t err;
880    fs_off_t pos;
881
882    if (cur_fd < 0) {
883        printf("no file open! (open or create one with open or make)\n");
884        return FS_EINVAL;
885    }
886
887
888    if (argc < 2) {
889        printf("usage: seek pos\n");
890        return FS_EINVAL;
891    }
892    pos = strtoul(&argv[1][0], NULL, 0);
893
894    err = sys_lseek(1, cur_fd, pos, MY_SEEK_SET);
895    if (err != pos) {
896        printf("seek to %Ld failed (%Ld)\n", pos, err);
897    }
898
899	return err;
900}
901
902
903int
904remove_dir_contents(int parentDir, const char *name, bool force)
905{
906	// open the dir
907	int dir = sys_opendir(true, parentDir, name, true);
908	if (dir < 0) {
909		fprintf(stderr, "Failed to open dir `%s': %s\n", name,
910			fs_strerror(dir));
911		return dir;
912	}
913
914	status_t error = FS_OK;
915
916	// iterate through the entries
917	ssize_t numRead;
918	char buffer[sizeof(my_dirent) + B_FILE_NAME_LENGTH];
919	my_dirent *entry = (my_dirent*)buffer;
920	while ((numRead = sys_readdir(true, dir, entry, sizeof(buffer), 1)) > 0) {
921		// skip "." and ".."
922		if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
923			continue;
924
925		error = remove_entry(dir, entry->d_name, true, force);
926		if (error != FS_OK)
927			break;
928	}
929
930	if (numRead < 0) {
931		fprintf(stderr, "Error while reading directory `%s': %s\n", name,
932			fs_strerror(numRead));
933		return numRead;
934	}
935
936	// close
937	sys_closedir(true, dir);
938
939	return error;
940}
941
942
943static int
944remove_entry(int dir, const char *entry, bool recursive, bool force)
945{
946	// stat the file
947	struct my_stat st;
948	status_t error = sys_rstat(true, dir, entry, &st, false);
949	if (error != FS_OK) {
950		if (force && error == FS_ENTRY_NOT_FOUND)
951			return FS_OK;
952
953		fprintf(stderr, "Failed to remove `%s': %s\n", entry,
954			fs_strerror(error));
955		return error;
956	}
957
958	if (MY_S_ISDIR(st.mode)) {
959		if (!recursive) {
960			fprintf(stderr, "`%s' is a directory.\n", entry);
961				// TODO: get the full path
962			return FS_EISDIR;
963		}
964
965		// remove the contents
966		error = remove_dir_contents(dir, entry, force);
967		if (error != FS_OK)
968			return error;
969
970		// remove the directory
971		error = sys_rmdir(true, dir, entry);
972		if (error != FS_OK) {
973			fprintf(stderr, "Failed to remove directory `%s': %s\n", entry,
974				fs_strerror(error));
975			return error;
976		}
977	} else {
978		// remove the entry
979		error = sys_unlink(true, dir, entry);
980		if (error != FS_OK) {
981			fprintf(stderr, "Failed to remove entry `%s': %s\n", entry,
982				fs_strerror(error));
983			return error;
984		}
985	}
986
987	return FS_OK;
988}
989
990
991static int
992do_rm(int argc, char **argv)
993{
994    if (cur_fd >= 0)
995        do_close(0, NULL);
996
997	bool recursive = false;
998	bool force = false;
999
1000	// parse parameters
1001	int argi = 1;
1002	for (argi = 1; argi < argc; argi++) {
1003		const char *arg = argv[argi];
1004		if (arg[0] != '-')
1005			break;
1006
1007		if (arg[1] == '\0') {
1008			fprintf(stderr, "Invalid option `-'\n");
1009			return FS_EINVAL;
1010		}
1011
1012		for (int i = 1; arg[i]; i++) {
1013			switch (arg[i]) {
1014				case 'f':
1015					force = true;
1016					break;
1017				case 'r':
1018					recursive = true;
1019					break;
1020				default:
1021					fprintf(stderr, "Unknown option `-%c'\n", arg[i]);
1022					return FS_EINVAL;
1023			}
1024		}
1025	}
1026
1027	// check params
1028	if (argi >= argc) {
1029		fprintf(stderr, "Usage: %s [ -r ] <file>...\n", argv[0]);
1030		return FS_EINVAL;
1031	}
1032
1033	// remove loop
1034	for (; argi < argc; argi++) {
1035		status_t error = remove_entry(-1, argv[argi], recursive, force);
1036		if (error != FS_OK)
1037			return error;
1038	}
1039
1040	return FS_OK;
1041}
1042
1043
1044static int
1045do_rmdir(int argc, char **argv)
1046{
1047    int err;
1048    char name[256];
1049
1050    if (cur_fd >= 0)
1051        do_close(0, NULL);
1052
1053    if (argc < 2) {
1054        printf("rm: need a file name to remove\n");
1055        return FS_EINVAL;
1056    }
1057
1058    sprintf(name, "/myfs/%s", &argv[1][0]);
1059
1060    err = sys_rmdir(1, -1, name);
1061    if (err != 0) {
1062        printf("rmdir: error removing: %s: %s\n", name, fs_strerror(err));
1063    }
1064
1065	return err;
1066}
1067
1068
1069static int
1070do_copy_to_myfs(char *host_file, char *bfile)
1071{
1072    int     bfd, err = 0;
1073    char    myfs_name[1024];
1074    FILE   *fp;
1075    size_t  amt;
1076    static char buff[4 * 1024];
1077
1078    sprintf(myfs_name, "/myfs/%s", bfile);
1079
1080    fp = fopen(host_file, "rb");
1081    if (fp == NULL) {
1082        printf("can't open host file: %s\n", host_file);
1083        return from_platform_error(errno);
1084    }
1085
1086    if ((bfd = sys_open(1, -1, myfs_name, MY_O_RDWR|MY_O_CREAT,
1087                        MY_S_IFREG|MY_S_IRWXU, 0)) < 0) {
1088        fclose(fp);
1089        printf("error opening: %s\n", myfs_name);
1090        return bfd;
1091    }
1092
1093    while((amt = fread(buff, 1, sizeof(buff), fp)) == sizeof(buff)) {
1094        err = sys_write(1, bfd, buff, amt);
1095        if (err < 0)
1096            break;
1097    }
1098
1099    if (amt && err >= 0) {
1100        err = sys_write(1, bfd, buff, amt);
1101    }
1102
1103    if (err < 0) {
1104        printf("err == %d, amt == %ld\n", err, (long)amt);
1105        perror("write error");
1106    }
1107
1108    sys_close(1, bfd);
1109    fclose(fp);
1110
1111	return (err < 0 ? err : 0);
1112}
1113
1114
1115static int
1116do_copy_from_myfs(char *bfile, char *host_file)
1117{
1118    int     bfd,err = 0;
1119    char    myfs_name[1024];
1120    FILE   *fp;
1121    size_t  amt;
1122    static char buff[4 * 1024];
1123
1124    sprintf(myfs_name, "/myfs/%s", bfile);
1125
1126    fp = fopen(host_file, "wb");
1127    if (fp == NULL) {
1128        printf("can't open host file: %s\n", host_file);
1129        return from_platform_error(errno);
1130    }
1131
1132    if ((bfd = sys_open(1, -1, myfs_name, MY_O_RDONLY, MY_S_IFREG, 0)) < 0) {
1133        fclose(fp);
1134        printf("error opening: %s\n", myfs_name);
1135        return bfd;
1136    }
1137
1138    while(1) {
1139        amt = sizeof(buff);
1140        err = sys_read(1, bfd, buff, amt);
1141        if (err < 0)
1142            break;
1143
1144        if (fwrite(buff, 1, err, fp) != amt)
1145            break;
1146    }
1147
1148
1149    if (err < 0)
1150        perror("read error");
1151
1152    sys_close(1, bfd);
1153    fclose(fp);
1154
1155	return (err < 0 ? err : 0);
1156}
1157
1158
1159static int
1160do_copy(int argc, char **argv)
1161{
1162    if (cur_fd >= 0)
1163        do_close(0, NULL);
1164
1165    if (argc < 3) {
1166        printf("copy needs two arguments!\n");
1167        return FS_EINVAL;
1168    }
1169
1170    if (argv[1][0] == ':' && argv[2][0] != ':') {
1171        return do_copy_to_myfs(&argv[1][1], &argv[2][0]);
1172    }
1173
1174    if (argv[2][0] == ':' && argv[1][0] != ':') {
1175        return do_copy_from_myfs(&argv[1][0], &argv[2][1]);
1176    }
1177
1178    printf("can't copy around inside of the file system (only in and out)\n");
1179
1180	return FS_EINVAL;
1181}
1182
1183
1184static int
1185copydir(char *fromPath,char *toPath)
1186{
1187    int     bfd, err = 0;
1188    char    *myfs_name; //[1024];
1189    char	*from_name; //[1024];
1190	int		fd;
1191    size_t  amt;
1192    char	*buff; //[4 * 1024];
1193    size_t  bufferSize = 4 * 1024;
1194	DIR		*from;
1195	dirent_t	*dirent;
1196
1197	from = opendir(fromPath);
1198	if (from == NULL) {
1199		printf("could not open %s\n", fromPath);
1200		return FS_ENTRY_NOT_FOUND;
1201	}
1202
1203	myfs_name = (char*)malloc(1024);
1204	from_name = (char*)malloc(1024);
1205	buff = (char*)malloc(bufferSize);
1206	if (myfs_name == NULL || from_name == NULL || buff == NULL) {
1207		printf("out of memory\n");
1208		return FS_NO_MEMORY;
1209	}
1210
1211	while ((dirent = readdir(from)) != NULL) {
1212		DIR *attrDirectory;
1213		dirent_t *attr;
1214		struct stat st;
1215
1216		if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, ".."))
1217			continue;
1218
1219		strcpy(from_name, fromPath);
1220		if (from_name[strlen(from_name) - 1] != '/')
1221			strcat(from_name, "/");
1222		strcat(from_name, dirent->d_name);
1223
1224		if (stat(from_name, &st) != 0)
1225			continue;
1226
1227		if (st.st_mode & S_IFDIR) {
1228			char path[1024];
1229			strcpy(path, toPath);
1230			strcat(path, "/");
1231			strcat(path, dirent->d_name);
1232
1233			if ((err = sys_mkdir(1, -1, path, MY_S_IRWXU)) == FS_OK)
1234				copydir(from_name, path);
1235			else
1236				printf("Could not create directory %s: (%s)\n", path, fs_strerror(err));
1237		} else {
1238			fd = open(from_name, O_RDONLY);
1239			if (fd < 0) {
1240				printf("can't open host file: %s\n", from_name);
1241				return fd;
1242			}
1243
1244			sprintf(myfs_name, "%s/%s", toPath, dirent->d_name);
1245			if ((bfd = sys_open(1, -1, myfs_name, MY_O_RDWR|MY_O_CREAT,
1246								MY_S_IFREG|MY_S_IRWXU, 0)) < 0) {
1247		        close(fd);
1248		        printf("error opening: %s\n", myfs_name);
1249		        return bfd;
1250		    }
1251
1252			// copy attributes first!
1253			if ((attrDirectory = fs_fopen_attr_dir(fd)) != NULL) {
1254				while ((attr = fs_read_attr_dir(attrDirectory)) != NULL) {
1255					struct attr_info attrInfo;
1256					int32 size = bufferSize, bytesRead;
1257
1258					if (fs_stat_attr(fd, attr->d_name, &attrInfo) != 0)
1259						continue;
1260
1261					if (attrInfo.size <= size)
1262						size = attrInfo.size - 1;
1263					else
1264						printf("truncating attribute: %s\n", attr->d_name);
1265
1266					bytesRead = fs_read_attr(fd, attr->d_name, attrInfo.type, 0, buff, size);
1267					if (bytesRead < size) {
1268						printf("could not read attribute %s: %s\n", attr->d_name, fs_strerror(bytesRead));
1269						continue;
1270					}
1271					buff[size] = '\0';
1272
1273				    err = sys_write_attr(1, bfd, attr->d_name, attrInfo.type, buff, size, 0);
1274					if (err < FS_OK) {
1275						printf("write attr (\"%s\", type = %p, %p, %ld bytes) failed: %s\n",
1276							attr->d_name, (void *)attrInfo.type, buff, size, fs_strerror(err));
1277						continue;
1278					}
1279				}
1280				fs_close_attr_dir(attrDirectory);
1281			} else
1282				puts("could not open attr-dir");
1283
1284			while ((amt = read(fd, buff, bufferSize)) == bufferSize) {
1285				err = sys_write(1, bfd, buff, amt);
1286				if (err < 0)
1287					break;
1288			}
1289
1290			if (amt && err >= 0) {
1291				err = sys_write(1, bfd, buff, amt);
1292			}
1293
1294			if (err < 0) {
1295				printf("write error: err == %d, amt == %ld\n", err, (long)amt);
1296			}
1297
1298			sys_close(1, bfd);
1299			close(fd);
1300		}
1301	}
1302	closedir(from);
1303
1304	free(myfs_name);  free(from_name);  free(buff);
1305	return FS_OK;
1306}
1307
1308
1309static int
1310do_copytest(int argc, char **argv)
1311{
1312    char *fromPath = "/boot/apps/internet/mozilla";
1313
1314	if (argc > 2) {
1315		printf("usage: copytest <path>");
1316		return FS_EINVAL;
1317	} else if (argc == 2)
1318		fromPath = argv[1];
1319	else
1320		printf("copying from: %s\n", fromPath);
1321
1322	return copydir(fromPath, "/myfs");
1323}
1324
1325
1326static int32
1327copydirthread(void *data)
1328{
1329	char *args[] = {"cptest", (char *)data, NULL};
1330
1331	do_copytest(2, args);
1332	return 0;
1333}
1334
1335
1336static int
1337do_threadtest(int argc, char **argv)
1338{
1339	char *paths[] = {"/boot/home/mail/pinc - axeld/in", "/boot/home/mail/pinc - axeld/out", NULL};
1340	int32 i;
1341
1342	for (i = 0; paths[i] != NULL; i++) {
1343		thread_id thread = spawn_thread(copydirthread, "copythread", B_NORMAL_PRIORITY, paths[i]);
1344		resume_thread(thread);
1345	}
1346
1347	//do_lat_fs(0,NULL);
1348
1349	return 0;
1350}
1351
1352
1353int32 gCopyNum;
1354
1355
1356static int32
1357copyfilethread(void *data)
1358{
1359	char *args[] = {"cp", (char *)data, NULL, NULL};
1360
1361	char name[32];
1362	sprintf(name, "target%ld", gCopyNum++);
1363	args[2] = name;
1364
1365	do_copy(3, args);
1366	return 0;
1367}
1368
1369
1370static int
1371do_threadfiletest(int argc, char **argv)
1372{
1373	char *paths[] = {":/video/Filme/Wallace & Gromit - A Close Shave.mpg",":/video/Filme/Wallace & Gromit - The Wrong Trousers.mpg",":/stuff/Simpsons/The Simpsons - 13.01 - Treehouse Of Horror XII.mpg",NULL};
1374	int32 i;
1375
1376	for (i = 0;paths[i] != NULL;i++) {
1377		thread_id thread = spawn_thread(copyfilethread,"copythread",B_NORMAL_PRIORITY,paths[i]);
1378		resume_thread(thread);
1379	}
1380
1381	do_lat_fs(0,NULL);
1382
1383	return 0;
1384}
1385
1386
1387static int
1388do_tracker(int argc, char **argv)
1389{
1390	static thread_id thread = -1;
1391
1392	if (thread < FS_OK) {
1393		puts("starting tracker...");
1394
1395		thread = spawn_thread(tracker_loop, "tracker", B_NORMAL_PRIORITY, NULL);
1396		resume_thread(thread);
1397	} else {
1398		status_t status;
1399		puts("stopping tracker.");
1400
1401		write_port(gTrackerPort, FSH_KILL_TRACKER, NULL, 0);
1402		wait_for_thread(thread, &status);
1403
1404		thread = -1;
1405	}
1406
1407	return 0;
1408}
1409
1410
1411//	#pragma mark -
1412
1413
1414static bool sRunStatTest;
1415static const char *sStatTestFile = "/myfs/stattest-file";
1416
1417
1418static int32
1419stat_thread(void *data)
1420{
1421	(void)data;
1422
1423	while (sRunStatTest) {
1424		struct my_stat st;
1425		sys_rstat(1, -1, sStatTestFile, &st, 0);
1426	}
1427	return 0;
1428}
1429
1430
1431static int32
1432create_remove_thread(void *data)
1433{
1434	(void)data;
1435
1436	while (sRunStatTest) {
1437		int fd;
1438		if ((fd = sys_open(1, -1, sStatTestFile, MY_O_RDWR | MY_O_CREAT,
1439				S_IFREG | S_IRWXU, 0)) >= 0) {
1440			sys_close(1, fd);
1441		}
1442
1443		snooze(10000);
1444		sys_unlink(1, -1, sStatTestFile);
1445	}
1446	return 0;
1447}
1448
1449
1450static int
1451do_stattest(int argc, char **argv)
1452{
1453	thread_id a = spawn_thread(stat_thread, "stat_thread", B_NORMAL_PRIORITY, NULL);
1454	thread_id b = spawn_thread(create_remove_thread, "create_remove_thread", B_NORMAL_PRIORITY, NULL);
1455	char buffer[1];
1456	status_t status;
1457
1458	sRunStatTest = true;
1459
1460	resume_thread(a);
1461	resume_thread(b);
1462
1463	printf("This test will stop when you press <Enter>: ");
1464	fflush(stdout);
1465
1466	fgets(buffer, 1, stdin);
1467
1468	sRunStatTest = false;
1469
1470	wait_for_thread(a, &status);
1471	wait_for_thread(b, &status);
1472
1473	return 0;
1474}
1475
1476
1477//	#pragma mark -
1478
1479
1480static int
1481do_attrtest(int argc, char **argv)
1482{
1483	int iterations = 10240;
1484	int maxSize = 1024;
1485	char *buffer;
1486	char name[2];
1487	int i;
1488
1489    if (cur_fd < 0) {
1490        printf("no file open! (open or create one with open or make)\n");
1491        return FS_EINVAL;
1492    }
1493    if (argc > 1 && isdigit(*argv[1]))
1494    	iterations = atol(argv[1]);
1495    if (argc > 2 && isdigit(*argv[2]))
1496    	maxSize = atol(argv[2]);
1497    if ((argc > 1 && !isdigit(*argv[1])) || (argc > 2 && !isdigit(*argv[2]))) {
1498    	printf("usage: attrs [number of iterations] [max attr size]\n");
1499    	return FS_EINVAL;
1500    }
1501
1502	buffer = (char*)malloc(1024);
1503	for (i = 0; i < 1024; i++)
1504		buffer[i] = i;
1505
1506	name[1] = '\0';
1507
1508	printf("%d iterations...\n", iterations);
1509
1510	for (i = 0; i < iterations; i++) {
1511		int length = rand() % maxSize;
1512		int err;
1513
1514		name[0] = rand() % 26 + 'a';
1515
1516		err = sys_write_attr(1, cur_fd, name, 'CSTR', buffer, length, 0);
1517		if (err < length)
1518			printf("error writing attribute: %s\n", fs_strerror(err));
1519	}
1520
1521	free(buffer);
1522
1523	return 0;
1524}
1525
1526
1527static int
1528do_rename(int argc, char **argv)
1529{
1530    int  err;
1531    char oldname[128], newname[128];
1532
1533    if (cur_fd >= 0)
1534        do_close(0, NULL);
1535
1536    if (argc < 3) {
1537        printf("rename needs two arguments!\n");
1538        return FS_EINVAL;
1539    }
1540
1541    strcpy(oldname, "/myfs/");
1542    strcpy(newname, "/myfs/");
1543
1544    strcat(oldname, &argv[1][0]);
1545    strcat(newname, &argv[2][0]);
1546
1547    err = sys_rename(1, -1, oldname, -1, newname);
1548    if (err)
1549        printf("rename failed with err: %s\n", fs_strerror(err));
1550
1551	return err;
1552}
1553
1554
1555static int
1556do_link(int argc, char **argv)
1557{
1558	bool force = false;
1559	bool symbolic = false;
1560	bool dereference = true;
1561
1562	// parse parameters
1563	int argi = 1;
1564	for (argi = 1; argi < argc; argi++) {
1565		const char *arg = argv[argi];
1566		if (arg[0] != '-')
1567			break;
1568
1569		if (arg[1] == '\0') {
1570			fprintf(stderr, "Invalid option `-'\n");
1571			return FS_EINVAL;
1572		}
1573
1574		for (int i = 1; arg[i]; i++) {
1575			switch (arg[i]) {
1576				case 'f':
1577					force = true;
1578					break;
1579				case 's':
1580					symbolic = true;
1581					break;
1582				case 'n':
1583					dereference = false;
1584					break;
1585				default:
1586					fprintf(stderr, "Unknown option `-%c'\n", arg[i]);
1587					return FS_EINVAL;
1588			}
1589		}
1590	}
1591
1592	if (argc - argi != 2) {
1593		printf("usage: %s [Options] <source> <target>\n", argv[0]);
1594		return FS_EINVAL;
1595	}
1596
1597	const char *source = argv[argi];
1598	const char *target = argv[argi + 1];
1599
1600	// check, if the the target is an existing directory
1601	struct my_stat st;
1602	char targetBuffer[B_PATH_NAME_LENGTH];
1603	status_t error = sys_rstat(true, -1, target, &st, dereference);
1604	if (error == FS_OK) {
1605		if (MY_S_ISDIR(st.mode)) {
1606			// get source leaf
1607			char leaf[B_FILE_NAME_LENGTH];
1608			error = get_last_path_component(source, leaf, sizeof(leaf));
1609			if (error != FS_OK) {
1610				fprintf(stderr, "Failed to get leaf name of source path: %s\n",
1611					fs_strerror(error));
1612				return error;
1613			}
1614
1615			// compose a new path
1616			int len = strlen(target) + 1 + strlen(leaf);
1617			if (len > (int)sizeof(targetBuffer)) {
1618				fprintf(stderr, "Resulting target path is too long.\n");
1619				return FS_EINVAL;
1620			}
1621
1622			strcpy(targetBuffer, target);
1623			strcat(targetBuffer, "/");
1624			strcat(targetBuffer, leaf);
1625			target = targetBuffer;
1626		}
1627	}
1628
1629	// check, if the target exists
1630	error = sys_rstat(true, -1, target, &st, false);
1631	if (error == FS_OK) {
1632		if (!force) {
1633			fprintf(stderr, "Can't create link. `%s' is in the way.\n", target);
1634			return FS_FILE_EXISTS;
1635		}
1636
1637		// unlink the entry
1638		error = sys_unlink(true, -1, target);
1639		if (error != FS_OK) {
1640			fprintf(stderr, "Failed to remove `%s' to make way for link: "
1641				"%s\n", target, fs_strerror(error));
1642			return error;
1643		}
1644	}
1645
1646	// finally create the link
1647	if (symbolic)
1648		error = sys_symlink(1, source, -1, target);
1649	else
1650		error = sys_link(1, -1, source, -1, target);
1651	if (error != FS_OK)
1652		printf("Failed to create link: %s\n", fs_strerror(error));
1653
1654	return error;
1655}
1656
1657
1658static int
1659do_sync(int argc, char **argv)
1660{
1661    int err;
1662
1663    err = sys_sync();
1664
1665    if (err)
1666        printf("sync failed with err %s\n", fs_strerror(err));
1667
1668	return err;
1669}
1670
1671
1672static int
1673do_relabel(int argc, char **argv)
1674{
1675	if (argc < 2) {
1676		printf("usage: relabel <volume name>\n");
1677		return FS_EINVAL;
1678	}
1679
1680	struct my_stat st;
1681	status_t error = sys_rstat(true, -1, "/myfs", &st, true);
1682	if (error == FS_OK) {
1683		fs_info info;
1684		strncpy(info.volume_name, argv[1], sizeof(info.volume_name));
1685		info.volume_name[sizeof(info.volume_name) - 1] = '\0';
1686
1687		error = sys_write_fs_info(true, st.dev, &info, WFSSTAT_NAME);
1688	}
1689
1690	if (error != FS_OK)
1691		printf("Could not create index: %s\n", fs_strerror(error));
1692
1693	return error;
1694}
1695
1696
1697static int
1698do_mkindex(int argc, char **argv)
1699{
1700	if (argc < 2) {
1701		printf("usage: mkindex <index>\n");
1702		return FS_EINVAL;
1703	}
1704
1705	struct my_stat st;
1706	status_t error = sys_rstat(true, -1, "/myfs", &st, true);
1707	if (error == FS_OK) {
1708		error = sys_mkindex(true, st.dev, argv[1], B_STRING_TYPE, 0);
1709	}
1710
1711	if (error != FS_OK)
1712		printf("Could not create index: %s\n", fs_strerror(error));
1713
1714	return error;
1715}
1716
1717
1718#define MAX_LIVE_QUERIES	10
1719void *gQueryCookie[MAX_LIVE_QUERIES] = {NULL};
1720char *gQueryString[MAX_LIVE_QUERIES] = {NULL};
1721
1722
1723static int
1724do_listqueries(int argc, char **argv)
1725{
1726	int min, max;
1727
1728	if (argc == 2)
1729		min = max = atol(argv[1]);
1730	else {
1731		min = 0;
1732		max = MAX_LIVE_QUERIES - 1;
1733	}
1734
1735	// list all queries (or only the one asked for)
1736
1737	for (; min <= max; min++) {
1738		if (gQueryCookie[min] == NULL)
1739			continue;
1740
1741		printf("%d. (%p) %s\n", min, gQueryCookie[min], gQueryString[min]);
1742	}
1743
1744	return 0;
1745}
1746
1747
1748static int
1749do_stopquery(int argc, char **argv)
1750{
1751	int err;
1752	int min, max;
1753
1754	if (gQueryCookie == NULL) {
1755		printf("no query running (use the 'startquery' command to start a query).\n");
1756		return FS_EINVAL;
1757	}
1758
1759	if (argc == 2)
1760		min = max = atol(argv[1]);
1761	else {
1762		min = 0;
1763		max = MAX_LIVE_QUERIES - 1;
1764	}
1765
1766	// close all queries (or only the one asked for)
1767
1768	for (; min <= max; min++) {
1769		if (gQueryCookie[min] == NULL)
1770			continue;
1771
1772		err = sys_close_query(true, -1, "/myfs/.", gQueryCookie[min]);
1773		if (err < 0) {
1774			printf("could not close query: %s\n", fs_strerror(err));
1775			return err;
1776		}
1777		gQueryCookie[min] = NULL;
1778		free(gQueryString[min]);
1779	}
1780
1781	return 0;
1782}
1783
1784
1785static int
1786do_startquery(int argc, char **argv)
1787{
1788	char *query;
1789	int freeSlot;
1790	int err;
1791
1792	for (freeSlot = 0; freeSlot < MAX_LIVE_QUERIES; freeSlot++) {
1793		if (gQueryCookie[freeSlot] == NULL)
1794			break;
1795	}
1796	if (freeSlot == MAX_LIVE_QUERIES) {
1797		printf("All query slots are used, stop some\n");
1798		return FS_EINVAL;
1799	}
1800
1801	if (argc != 2) {
1802		printf("query string expected\n");
1803		return FS_EINVAL;
1804	}
1805	query = argv[1];
1806
1807	err = sys_open_query(true, -1, "/myfs/.", query, B_LIVE_QUERY, freeSlot, freeSlot, &gQueryCookie[freeSlot]);
1808	if (err < 0) {
1809		printf("could not open query: %s\n", fs_strerror(err));
1810		return err;
1811	}
1812
1813	printf("query number %d started - use the 'stopquery' command to stop it.\n", freeSlot);
1814	gQueryString[freeSlot] = strdup(query);
1815
1816	return 0;
1817}
1818
1819
1820static int
1821do_query(int argc, char **argv)
1822{
1823	char buffer[2048];
1824	struct my_dirent *dent = (struct my_dirent *)buffer;
1825	void *cookie;
1826	char *query;
1827	int max_err = 10;
1828	int err;
1829
1830	if (argc != 2) {
1831		printf("query string expected");
1832		return FS_EINVAL;
1833	}
1834	query = argv[1];
1835
1836	err = sys_open_query(true, -1, "/myfs/.", query, 0, 42, 42, &cookie);
1837	if (err < 0) {
1838		printf("could not open query: %s\n", fs_strerror(err));
1839		return FS_EINVAL;
1840	}
1841
1842	while (true) {
1843		err = sys_read_query(true, -1, "/myfs/.", cookie, dent, sizeof(buffer), 1);
1844		if (err < 0) {
1845			printf("readdir failed for: %s\n", dent->d_name);
1846			if (max_err-- <= 0)
1847				break;
1848
1849			continue;
1850		}
1851
1852		if (err == 0)
1853			break;
1854
1855		printf("%s\n", dent->d_name);
1856	}
1857
1858	err = sys_close_query(true, -1, "/myfs/.", cookie);
1859	if (err < 0) {
1860		printf("could not close query: %s\n", fs_strerror(err));
1861		return err;
1862	}
1863
1864	return 0;
1865}
1866
1867
1868#define MAX_ITER  512
1869#define NUM_READS 16
1870#define READ_SIZE 4096
1871
1872static int
1873do_cio(int argc, char **argv)
1874{
1875    int            i, j, fd;
1876    char           fname[64];
1877    fs_off_t       pos;
1878    size_t         len;
1879    static char    buff[READ_SIZE];
1880    struct timeval start, end, result;
1881
1882    strcpy(fname, "/myfs/");
1883    if (argc == 1)
1884        strcpy(fname, "/myfs/fsh");
1885    else
1886        strcat(fname, &argv[1][0]);
1887
1888    fd = sys_open(1, -1, fname, MY_O_RDONLY, MY_S_IFREG, 0);
1889    if (fd < 0) {
1890        printf("can't open %s\n", fname);
1891        return fd;
1892    }
1893
1894    gettimeofday(&start, NULL);
1895
1896	for (i = 0; i < MAX_ITER; i++) {
1897		for (j = 0; j < NUM_READS; j++) {
1898			len = sizeof(buff);
1899			if (sys_read(1, fd, buff, len) != (ssize_t)len) {
1900				perror("cio read");
1901				break;
1902			}
1903		}
1904
1905		pos = 0;
1906		if (sys_lseek(1, fd, pos, MY_SEEK_SET) != pos) {
1907			perror("cio lseek");
1908			break;
1909		}
1910	}
1911
1912    gettimeofday(&end, NULL);
1913    SubTime(&end, &start, &result);
1914
1915    printf("read %lu bytes in %2ld.%.6ld seconds\n",
1916           (unsigned long)((MAX_ITER * NUM_READS * sizeof(buff)) / 1024),
1917           result.tv_sec, result.tv_usec);
1918
1919
1920    sys_close(1, fd);
1921
1922
1923	return 0;
1924}
1925
1926
1927static int
1928mkfile(char *s, int sz)
1929{
1930    int  fd, len;
1931    int err = 0;
1932	char *buffer;
1933
1934    if ((fd = sys_open(1, -1, s, MY_O_RDWR|MY_O_CREAT,
1935                       MY_S_IFREG|MY_S_IRWXU, 0)) < 0) {
1936        printf("error creating: %s\n", s);
1937        return fd;
1938    }
1939
1940	buffer = (char*)malloc(16 * 1024);
1941	if (buffer == NULL)
1942		return FS_ENOMEM;
1943
1944    len = sz;
1945    if (sz) {
1946    	err = sys_write(1, fd, buffer, len);
1947        if (err != len)
1948            printf("error writing %d bytes to %s\n", sz, s);
1949    }
1950    if (sys_close(1, fd) != 0)
1951        printf("close failed?\n");
1952
1953	free(buffer);
1954
1955	return (err < 0 ? err : 0);
1956}
1957
1958#define LAT_FS_ITER   1000
1959
1960
1961static int
1962do_lat_fs(int argc, char **argv)
1963{
1964    int  i, j, iter;
1965/*  int  sizes[] = { 0, 1024, 4096, 10*1024 }; */
1966    int  sizes[] = { 0, 1024 };
1967    char name[64];
1968
1969    iter = LAT_FS_ITER;
1970
1971    if (argc > 1)
1972        iter = strtoul(&argv[1][0], NULL, 0);
1973
1974    for (i = 0; (size_t)i < sizeof(sizes)/sizeof(int); i++) {
1975        printf("CREATING: %d files of %5d bytes each\n", iter, sizes[i]);
1976        for (j = 0; j < iter; ++j) {
1977            sprintf(name, "/myfs/%.5d", j);
1978            mkfile(name, sizes[i]);
1979        }
1980
1981        printf("DELETING: %d files of %5d bytes each\n", iter, sizes[i]);
1982        for (j = 0; j < iter; ++j) {
1983            sprintf(name, "/myfs/%.5d", j);
1984            if (sys_unlink(1, -1, name) != 0)
1985                printf("lat_fs: failed to remove: %s\n", name);
1986        }
1987    }
1988
1989	return 0;
1990}
1991
1992
1993static int
1994do_create(int argc, char **argv)
1995{
1996    int  j, iter = 100, err;
1997    int  size = 0;
1998    char name[64];
1999
2000    sprintf(name, "/myfs/test");
2001    err = sys_mkdir(1, -1, name, MY_S_IRWXU);
2002    if (err && err != FS_EEXIST)
2003        printf("mkdir of %s returned: %s (%d)\n", name, fs_strerror(err), err);
2004
2005    if (argc > 1)
2006        iter = strtoul(&argv[1][0], NULL, 0);
2007    if (argc > 2)
2008        size = strtoul(&argv[2][0], NULL, 0);
2009
2010    printf("creating %d files (each %d bytes long)...\n", iter, size);
2011
2012    for (j = 0; j < iter; ++j) {
2013        sprintf(name, "/myfs/test/%.5d", j);
2014        /* printf("CREATING: %s (%5d)\n", name, size); */
2015        mkfile(name, size);
2016    }
2017
2018	return 0;
2019}
2020
2021
2022static int
2023do_delete(int argc, char **argv)
2024{
2025    int  j, iter = 100;
2026    char name[64];
2027
2028    if (argc > 1)
2029        iter = strtoul(&argv[1][0], NULL, 0);
2030
2031    for (j = 0; j < iter; ++j) {
2032        sprintf(name, "/myfs/test/%.5d", j);
2033        printf("DELETING: %s\n", name);
2034        if (sys_unlink(1, -1, name) != 0)
2035            printf("lat_fs: failed to remove: %s\n", name);
2036    }
2037
2038	return 0;
2039}
2040
2041
2042static int do_help(int argc, char **argv);
2043
2044
2045static cmd_entry builtin_commands[] =
2046{
2047    { "cd",      do_chdir, "change current directory" },
2048    { "ls",      do_dir, "print a directory listing" },
2049    { "dir",     do_dir, "print a directory listing (same as ls)" },
2050    { "open",    do_open, "open an existing file for read/write access" },
2051    { "make",    do_make, "create a file (optionally specifying a name)" },
2052    { "close",   do_close, "close the currently open file" },
2053    { "mkdir",   do_mkdir, "create a directory" },
2054    { "rdtest",  do_read_test, "read N bytes from the current file. default is 256" },
2055    { "wrtest",  do_write_test, "write N bytes to the current file. default is 256" },
2056    { "wrstream",  do_write_stream, "write N blocks of 4096 bytes to the current file. default is 100000" },
2057    { "rm",      do_rm, "remove the named file" },
2058    { "rmall",   do_rmall, "remove all the files. if no dirname, use '.'" },
2059    { "rmdir",   do_rmdir, "remove the named directory" },
2060    { "cp",	     do_xcp, "similar to shell cp, can copy in, out, within, and outside the FS, supports attributes." },
2061    { "copy",    do_xcp, "same as cp" },
2062    { "trunc",   do_trunc, "truncate a file to the size specified" },
2063    { "seek",    do_seek, "seek to the position specified" },
2064    { "mv",      do_rename, "rename a file or directory" },
2065    { "sync",    do_sync, "call sync" },
2066    { "relabel", do_relabel, "rename the volume" },
2067    { "wrattr",  do_write_attr, "write attribute \"name\" to the current file (N bytes [256])." },
2068    { "rdattr",  do_read_attr, "read attribute \"name\" from the current file (N bytes [256])." },
2069    { "rmattr",  do_remove_attr, "remove attribute \"name\" from the current file." },
2070    { "listattr", do_list_attr, "lists all attributes of the given or current file." },
2071    { "attrs",   do_attrtest, "writes random attributes [a-z] up to 1023 bytes, N [10240] iterations." },
2072    { "lat_fs",  do_lat_fs, "simulate what the lmbench test lat_fs does" },
2073    { "create",  do_create, "create N files. default is 100" },
2074    { "delete",  do_delete, "delete N files. default is 100" },
2075    { "mkindex", do_mkindex, "creates an index (type is currently always string)" },
2076    { "query",   do_query, "run a query on the file system" },
2077    { "startquery", do_startquery, "run a live query on the file system (up to 10)" },
2078    { "stopquery",  do_stopquery, "stops live query N, or all of them if none is specified" },
2079    { "lsquery",  do_listqueries, "list all live queries" },
2080    { "ioctl",   do_ioctl, "execute ioctl() without an inode (okay, with the root node)" },
2081    { "fcntl",   do_fcntl, "execute ioctl() with the active inode" },
2082    { "cptest",  do_copytest, "copies all files from the given path" },
2083    { "threads", do_threadtest, "copies several files, and does a lat_fs simulaneously" },
2084    { "mfile",	 do_threadfiletest, "copies several big files simulaneously" },
2085    { "tracker", do_tracker, "starts a Tracker like background thread that will listen to fs updates" },
2086    { "cio",	 do_cio, "does a I/O speed test" },
2087    { "stattest", do_stattest, "does an \"early\"/\"late\" stat test for files that are created or deleted" },
2088    { "link", do_link, "creates the specified [sym]link on the device" },
2089    { "ln", do_link, "creates the specified [sym]link on the device" },
2090
2091    { NULL, NULL }
2092};
2093
2094static cmd_entry help_commands[] =
2095{
2096    { "help",    do_help, "print this help message" },
2097    { "?",       do_help, "print this help message" },
2098    { NULL, NULL }
2099};
2100
2101static cmd_entry *fsh_commands[] =
2102{
2103	builtin_commands,
2104	additional_commands,
2105	help_commands,
2106	NULL
2107};
2108
2109static int
2110do_help(int argc, char **argv)
2111{
2112    printf("commands fsh understands:\n");
2113    for (cmd_entry **commands = fsh_commands; *commands; commands++) {
2114	    cmd_entry *cmd = *commands;
2115	    for (; cmd->name != NULL; cmd++) {
2116	        printf("%8s - %s\n", cmd->name, cmd->help);
2117	    }
2118    }
2119
2120	return 0;
2121}
2122
2123static char *
2124getline(char *prompt, char *input, int len)
2125{
2126	if (sInteractiveMode) {
2127	    printf("%s", prompt); fflush(stdout);
2128
2129    	return fgets(input, len, stdin);
2130	} else
2131		return get_external_command(prompt, input, len);
2132}
2133
2134
2135// This might have been defined by additional_commands.h - so
2136// we can't do it earlier.
2137#ifndef FS_SHELL_PROMPT
2138#	define FS_SHELL_PROMPT	"fsh"
2139#endif
2140
2141
2142static void
2143do_fsh(void)
2144{
2145#if 0
2146	char stack_filler[1024 * 1024 * 16 - 32 * 1024];
2147		// actually emulating 12kB stack size (BeOS has 16 MB - ~20kB for the main thread)
2148#endif
2149    int   argc, len;
2150    char *prompt = FS_SHELL_PROMPT ">> ";
2151    char  input[20480], **argv;
2152    cmd_entry *cmd = NULL;
2153
2154    while(getline(prompt, input, sizeof(input)) != NULL) {
2155        argc = 0;
2156        argv = build_argv(input, &argc);
2157        if (argv == NULL || argc == 0) {
2158            continue;
2159        }
2160
2161        len = strlen(&argv[0][0]);
2162
2163	    bool done = false;
2164		for (cmd_entry **commands = fsh_commands;
2165			 !done && *commands;
2166			 commands++) {
2167		    cmd = *commands;
2168		    for (; cmd->name != NULL; cmd++) {
2169				if (strncmp(cmd->name, &argv[0][0], len) == 0) {
2170	                int result = cmd->func(argc, argv);
2171
2172					if (!sInteractiveMode)
2173						reply_to_external_command(to_platform_error(result));
2174
2175					done = true;
2176					break;
2177	            }
2178	        }
2179	    }
2180
2181        if (strncmp(&argv[0][0], "quit", 4) == 0
2182            || strncmp(&argv[0][0], "exit", 4) == 0) {
2183			if (!sInteractiveMode)
2184				reply_to_external_command(0);
2185            break;
2186        }
2187
2188        if ((cmd == NULL || cmd->name == NULL) && argv[0][0] != '\0') {
2189            printf("command `%s' not understood\n", &argv[0][0]);
2190
2191			if (!sInteractiveMode)
2192				reply_to_external_command(EINVAL);
2193        }
2194
2195        free(argv);
2196    }
2197
2198	if (!sInteractiveMode)
2199		external_command_cleanup();
2200
2201    if (feof(stdin))
2202        printf("\n");
2203
2204    if (cur_fd != -1)
2205        do_close(0, NULL);
2206}
2207
2208