diff --git a/Makefile b/Makefile index 2584e4a..4316f23 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ K=kernel U=user +DIR = dir1 dir2 + OBJS = \ $K/entry.o \ $K/start.o \ @@ -128,13 +130,14 @@ UPROGS=\ $U/_rm\ $U/_sh\ $U/_stressfs\ + $U/_tree\ $U/_usertests\ $U/_grind\ $U/_wc\ $U/_zombie\ -fs.img: mkfs/mkfs README.md $(UPROGS) - mkfs/mkfs fs.img README.md $(UPROGS) +fs.img: mkfs/mkfs README.md $(UPROGS) $(DIR) + mkfs/mkfs fs.img README.md $(UPROGS) $(DIR) -include kernel/*.d user/*.d diff --git a/dir1/test.c b/dir1/test.c new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/dir1/test.c @@ -0,0 +1 @@ +hello diff --git a/dir2/test.txt b/dir2/test.txt new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/dir2/test.txt @@ -0,0 +1 @@ +hello diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index 1ec326b..9bbc090 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -4,12 +4,18 @@ #include #include #include +#include +#include +#include +#define dirent xv6_dirent #define stat xv6_stat // avoid clash with host struct stat #include "kernel/types.h" #include "kernel/fs.h" #include "kernel/stat.h" #include "kernel/param.h" +#undef dirent +#undef stat #ifndef static_assert #define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0) @@ -65,12 +71,88 @@ xint(uint x) return y; } +void +add_file(int parent_fd, uint parentino, char *filename) +{ + uint inum; + int fd; + char buf[BSIZE]; + struct xv6_dirent de; + + if((fd = openat(parent_fd, filename, 0)) < 0) + die(filename); + + inum = ialloc(T_FILE); + + bzero(&de, sizeof(de)); + de.inum = xshort(inum); + strncpy(de.name, filename, DIRSIZ); + iappend(parentino, &de, sizeof(de)); + + ssize_t cc; + while((cc = read(fd, buf, sizeof(buf))) > 0) + iappend(inum, buf, cc); + + close(fd); +} + +uint +add_dir(int level, int parent_fd, uint parentino, char *dirname) +{ + struct xv6_dirent de; + uint dino = ialloc(T_DIR); + bzero(&de, sizeof(de)); + de.inum = xshort(dino); + strcpy(de.name, dirname); + iappend(parentino, &de, sizeof(de)); + + bzero(&de, sizeof(de)); + de.inum = xshort(dino); + strcpy(de.name, "."); + iappend(dino, &de, sizeof(de)); + + bzero(&de, sizeof(de)); + de.inum = xshort(parentino); + strcpy(de.name, ".."); + iappend(dino, &de, sizeof(de)); + + int dir_fd = -1; + if ((dir_fd = openat(parent_fd, dirname, O_RDONLY)) == -1) { + perror("open"); + return dino; + } + + DIR *d = fdopendir(dir_fd); + if (d == NULL) { + perror("fdopendir"); + return dino; + } + + struct dirent *e; + while ((e = readdir(d)) != NULL) { + if (e->d_name[0] == '.') { + continue; + } + + if (e->d_type == DT_REG) { + printf("%*s+ %s\n", level * 2, "", e->d_name); + add_file(dir_fd, dino, e->d_name); + } else if (e->d_type == DT_DIR) { + printf("%*s+ /%s\n", level * 2, "", e->d_name); + add_dir(level + 1, dir_fd, dino, e->d_name); + } + } + close(dir_fd); + + return dino; +} + int main(int argc, char *argv[]) { int i, cc, fd; uint rootino, inum, off; - struct dirent de; + struct xv6_dirent de; char buf[BSIZE]; struct dinode din; @@ -83,7 +165,7 @@ main(int argc, char *argv[]) } assert((BSIZE % sizeof(struct dinode)) == 0); - assert((BSIZE % sizeof(struct dirent)) == 0); + assert((BSIZE % sizeof(struct xv6_dirent)) == 0); fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); if(fsfd < 0) @@ -128,6 +210,13 @@ main(int argc, char *argv[]) iappend(rootino, &de, sizeof(de)); for(i = 2; i < argc; i++){ + struct stat sb; + stat(argv[i], &sb); + if (S_ISDIR(sb.st_mode)) { + add_dir(0, AT_FDCWD, rootino, argv[i]); + continue; + } + // get rid of "user/" char *shortname; if(strncmp(argv[i], "user/", 5) == 0) diff --git a/testfile.txt b/testfile.txt new file mode 100644 index 0000000..b14df64 --- /dev/null +++ b/testfile.txt @@ -0,0 +1 @@ +Hi diff --git a/user/tree.c b/user/tree.c new file mode 100644 index 0000000..814f719 --- /dev/null +++ b/user/tree.c @@ -0,0 +1,310 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fs.h" +#include "kernel/fcntl.h" + +/** + * prints the prefix for the current tree level based on depth + * + * @param depth - current depth in the directory tree + * @param last - array indicating if the current node is the last in its level + */ +void +print_tree_prefix(int depth, int *last) { + for (int i = 0; i < depth - 1; i++) { + if (last[i]) + printf(" "); + else + printf("│ "); + } + if (depth > 0) { + if (last[depth - 1]) + printf("└── "); + else + printf("├── "); + } +} + +/** + * checks if the directory name is a special directory like "." or ".." + * + * @param name - name of dir to check + * @return 1 - if it is a special dir + */ +int +is_special_dir(const char *name) { + return strcmp(name, ".") == 0 || strcmp(name, "..") == 0; +} + +/** + * opens the specified dir to read + * + * @param path - path of the dir to open + * @return - file descriptor for the opened dir + */ +int +open_directory(const char *path) { + int fd = open(path, 0); + if (fd < 0) { + fprintf(2, "tree: cannot open %s\n", path); + } + return fd; +} + +/** + * finds the last occurrence of a char in a string + * + * @param s - string to be searched in + * @param c - character to find + * @return - pointer to the last occurrence of the char in the string + */ +char* +strrchr(const char* s, int c) { + const char* last = 0; + for (; *s; s++) { + if (*s == (char)c) { + last = s; + } + } + return (char*)last; +} + +/** + * checks if a dir contains a valid file matching the ext + * + * @param path - path of the dir to check + * @param file_ext - file ext to look for + * @return - 1 if a valid file is found and 0 otherwise + */ +int +contains_valid_file(char *path, char *file_ext) { + char *buf = malloc(512); + if (!buf) { + fprintf(2, "tree: memory allocation failed\n"); + return 0; + } + char *p; + int fd = open_directory(path); + if (fd < 0) { + free(buf); + return 0; + } + + struct dirent de; + struct stat st; + strcpy(buf, path); + p = buf + strlen(buf); + *p++ = '/'; + + while (read(fd, &de, sizeof(de)) == sizeof(de)) { + if (de.inum == 0 || is_special_dir(de.name)) + continue; + + memmove(p, de.name, strlen(de.name)); + p[strlen(de.name)] = 0; + + if (stat(buf, &st) < 0) continue; + + if (st.type != T_DIR) { + if (file_ext) { + char *dot = strrchr(buf, '.'); + if (dot && strcmp(dot, file_ext) == 0) { + close(fd); + return 1; + } + } else { + close(fd); + return 1; + } + } else if (contains_valid_file(buf, file_ext)) { + close(fd); + return 1; + } + } + free(buf); + close(fd); + return 0; +} + + +/** + * recursively traverses and prints the directory structure + * + * @param path - curr directory path + * @param depth - curr depth in the directory tree + * @param last - array indicating if the current node is the last in its level + * @param file_ext - file ext to filter files + * @param show_size - flag indicating whether to display file sizes + * @param show_count - flag indicating whether to display counts of directories and files + */ +void +tree(char *path, int depth, int *last, char *file_ext, int show_size, int show_count, int limit_depth) { + if (limit_depth != -1 && depth > limit_depth) return; + + int fd = open_directory(path); + if (fd < 0) return; + + struct dirent de; + struct stat st; + if (fstat(fd, &st) < 0) { + fprintf(2, "tree: cannot stat %s\n", path); + close(fd); + return; + } + + + char *buf = malloc(512); + if (!buf) { + fprintf(2, "tree: memory allocation failed\n"); + close(fd); + return; + } + char *p; + strcpy(buf, path); + p = buf + strlen(buf); + *p++ = '/'; + + if (st.type == T_DIR) { + int valid_for_print = (file_ext == 0 || contains_valid_file(path, file_ext)); + if (!show_count && valid_for_print) { + print_tree_prefix(depth, last); + printf("%s/\n", strrchr(path, '/')); + } + + int file_count = 0, dir_count = 0; + + while (read(fd, &de, sizeof(de)) == sizeof(de)) { + if (de.inum == 0 || is_special_dir(de.name)) continue; + + memmove(p, de.name, strlen(de.name)); + p[strlen(de.name)] = 0; + + if (stat(buf, &st) < 0) continue; + + if (st.type == T_DIR) dir_count++; + else if (!file_ext || (strrchr(buf, '.') && strcmp(strrchr(buf, '.'), file_ext) == 0)) file_count++; + } + + if (show_count && show_size) { + print_tree_prefix(depth, last); + printf("%s/ [%d directories, %d files]\n", path, dir_count, file_count); + + close(fd); + fd = open_directory(path); + if (fd < 0) return; + + while (read(fd, &de, sizeof(de)) == sizeof(de)) { + if (de.inum == 0 || is_special_dir(de.name)) continue; + + memmove(p, de.name, strlen(de.name)); + p[strlen(de.name)] = 0; + + if (stat(buf, &st) < 0) continue; + + if (st.type != T_DIR) { + print_tree_prefix(depth + 1, last); + printf("└── %s (size: %d bytes)\n", de.name, st.size); + } + } + } else if (show_count) { + print_tree_prefix(depth, last); + printf("%s/ [%d directories, %d files]\n", path, dir_count, file_count); + } else if (show_size) { + print_tree_prefix(depth, last); + printf("%s (size: %d bytes)\n", strrchr(path, '/'), st.size); + } + + close(fd); + fd = open_directory(path); + if (fd < 0) return; + + int i = 0; + while (read(fd, &de, sizeof(de)) == sizeof(de)) { + if (de.inum == 0 || is_special_dir(de.name)) continue; + + char *saved_p = p; + memmove(p, de.name, strlen(de.name)); + p[strlen(de.name)] = 0; + + last[depth] = (i == dir_count + file_count - 1); + tree(buf, depth + 1, last, file_ext, show_size, show_count, limit_depth); + p = saved_p; + i++; + } + } else { + if (file_ext) { + char *dot = strrchr(path, '.'); + if (!dot || strcmp(dot, file_ext) != 0) { + close(fd); + return; + } + } + + if (!show_count) { + print_tree_prefix(depth, last); + if (show_size) { + printf("%s (size: %d bytes)\n", strrchr(path, '/'), st.size); + } else { + printf("%s/\n", strrchr(path, '/') ); + } + } + } + free(buf); + close(fd); +} + + +/** + * main function to parse command-line arguments and initiate the tree traversal + * + * @param argc - number of command-line arguments + * @param argv - array of command-line argument strings + * @return - 0 on successful tree traversal and printing + */ +int +main(int argc, char *argv[]) { + char *start_dir = "."; + char *file_ext = 0; + int show_size = 0; + int show_count = 0; + int limit_depth = -1; + + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-F") == 0) { + if (i + 1 < argc) { + if (argv[i+1][0] == '.') { + file_ext = argv[++i]; + } else { + fprintf(2, "tree: invalid value for -F\n"); + exit(1); + } + } else { + fprintf(2, "tree: missing argument for -F\n"); + exit(1); + } + } else if (strcmp(argv[i], "-S") == 0) { + show_size = 1; + } else if (strcmp(argv[i], "-C") == 0) { + show_count = 1; + } else if (strcmp(argv[i], "-L") == 0) { + if (i + 1 < argc) { + limit_depth = atoi(argv[++i]); + } else { + fprintf(2, "tree: missing argument for -L\n"); + exit(1); + } + } else if (argv[i][0] == '-') { + fprintf(2, "tree: invalid flag %s\n", argv[i]); + exit(1); + } else { + start_dir = argv[i]; + } + } + + int last[128]; + memset(last, 0, sizeof(last)); + tree(start_dir, 0, last, file_ext, show_size, show_count, limit_depth); + exit(0); +}