diff --git a/Makefile b/Makefile index 2584e4a..62960c4 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ K=kernel U=user +DIR = example empty + OBJS = \ $K/entry.o \ $K/start.o \ @@ -133,8 +135,8 @@ UPROGS=\ $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) test4.txt + mkfs/mkfs fs.img README.md $(UPROGS) $(DIR) test4.txt -include kernel/*.d user/*.d @@ -170,4 +172,3 @@ qemu: $K/kernel fs.img qemu-gdb: $K/kernel .gdbinit fs.img @echo "*** Now run 'gdb' in another window." 1>&2 $(QEMU) $(QEMUOPTS) -S $(QEMUGDB) - diff --git a/example/test1.txt b/example/test1.txt new file mode 100644 index 0000000..980a0d5 --- /dev/null +++ b/example/test1.txt @@ -0,0 +1 @@ +Hello World! diff --git a/example/test2.c b/example/test2.c new file mode 100644 index 0000000..0acb6df --- /dev/null +++ b/example/test2.c @@ -0,0 +1 @@ +Unrunnable c file diff --git a/example/test3.txt b/example/test3.txt new file mode 100644 index 0000000..1ce3f81 --- /dev/null +++ b/example/test3.txt @@ -0,0 +1 @@ +Goodbye World! 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/test4.txt b/test4.txt new file mode 100644 index 0000000..46d9f10 --- /dev/null +++ b/test4.txt @@ -0,0 +1 @@ +You can delete me diff --git a/user/rm.c b/user/rm.c index 26b8f1f..ed890a3 100644 --- a/user/rm.c +++ b/user/rm.c @@ -1,22 +1,203 @@ #include "kernel/types.h" #include "kernel/stat.h" #include "user/user.h" +#include "kernel/fs.h" + +const char *usageMsg = "Usage: rm file/directory.\n" +"Possible flags: -v, -r, -f, -i, -d\n" +"-v file/directory\n" +"-r directory\n" +"-f file/directory\n" +"-i file/directory\n" +"-d directory\n"; + + +#define MAX_PATH 512 + +// Boolean variables for flag processing, defaulted to false +int v, r, f, i, d = 0; + +/* + Deletes files/directories. Can verbose + + @param path path to delete +*/ +void +delete(char* path) +{ + if(unlink(path) < 0) { + if (!f) { + fprintf(2, "rm: %s failed to delete\n", path); + exit(1); + } + } + + if (v) { + printf("rm: deleted %s\n", path); + } +} + +/* + Helper function to concat two string paths together to a destination string + + @param dest destination string + @param path1 first path + @param path2 second path +*/ +void +concat_paths(char* dest, char* path1, char* path2) { + int i = 0, j = 0; + + while (path1[i] != '\0' && i < MAX_PATH - 1) { + dest[i] = path1[i]; + i++; + } + + if (i > 0 && dest[i-1] != '/') { + dest[i] = '/'; + i++; + } + + while (path2[j] != '\0' && i < MAX_PATH - 1) { + dest[i] = path2[j]; + i++; + j++; + } + + dest[i] = '\0'; +} + +/* + Main function of rm. Checks for flags and appropriately rm + files/directories using delete(). Possible flags include: + -r recursive + -f force + -i interactive + -d empty directory + + @param path path to process +*/ +void +rm(char *path) +{ + // Cannot rm an empty directory recursively + if (d && r) { + fprintf(2, "rm: Cannot use flags '-d' and '-r' together.\n"); + exit(1); + } + + // Cannot rm with interactive and force + if (i && f) { + fprintf(2, "rm: Cannot use flags '-i' and '-f' together.\n"); + exit(1); + } + + // Interactive + if (i) { + while (1) { + printf("rm: Delete '%s'?\nEnter [y] for yes or [n] for no\n", path); + char answer[32]; + read(0, answer, 32 - 1); + answer[31] = '\0'; + if (answer[0] == 'y') { + break; + } else if (answer[0] == 'n') { + return; + } else { + fprintf(2, "Invalid answer.\n"); + } + } + } + + // Empty directory, just delete it + if (d) { + delete(path); + return; + } + + // Directory processing + struct stat st; + if (stat(path, &st) < 0) { + fprintf(2, "rm: (%s) does not exist\n", path); + } + if (st.type == T_DIR) { + // Recursively delete all files/directories + if (r) { + int dir = open(path, 0); + if (dir < 0) { + fprintf(2, "rm: open(%s) error\n", path); + exit(1); + } + struct dirent de; + int n; + while ((n = read(dir, &de, sizeof(de))) > 0) { + if (de.inum == 0 || strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0) { + continue; // Skip empty entries and . or .. + } + char entry[512]; + concat_paths(entry, path, de.name); + rm(entry); + } + close(dir); + } + } + delete(path); +} + int main(int argc, char *argv[]) { - int i; + int index; + int start = 1; if(argc < 2){ - fprintf(2, "Usage: rm files...\n"); + fprintf(2, "%s", usageMsg); exit(1); } - for(i = 1; i < argc; i++){ - if(unlink(argv[i]) < 0){ - fprintf(2, "rm: %s failed to delete\n", argv[i]); - break; - } + // Parse the flags + for (index = 1; index < argc; index++) { + if (argv[index][0] == '-') { // Check if argv is even a flag + // Read the flag(s) + char *flag = argv[index] + 1; + while (*flag) { + switch (*flag) { + case 'r': + r = 1; + break; + case 'v': + v = 1; + break; + case 'f': + f = 1; + break; + case 'i': + i = 1; + break; + case 'd': + d = 1; + break; + default: + fprintf(2, "rm: invalid flag\n"); + exit(1); + } + flag++; + } + // Check if there is path after flag + if (index + 1 >= argc) { + fprintf(2, "No path found after flag.\n"); + exit(1); + } + } else { // Not a flag + start = index; + break; + } + } + + // Parse the files/directories + for (index = start; index < argc; index++) { + rm(argv[index]); } exit(0);