diff --git a/Makefile b/Makefile index cf031b0..14409f0 100644 --- a/Makefile +++ b/Makefile @@ -142,9 +142,10 @@ UPROGS=\ $U/_logstress\ $U/_forphan\ $U/_dorphan\ + $U/_find\ -fs.img: mkfs/mkfs README.md $(UPROGS) - mkfs/mkfs fs.img README.md $(UPROGS) +fs.img: mkfs/mkfs README.md $(UPROGS) tests + mkfs/mkfs fs.img README.md $(UPROGS) tests -include kernel/*.d user/*.d diff --git a/docs/find.md b/docs/find.md new file mode 100644 index 0000000..0d3fb04 --- /dev/null +++ b/docs/find.md @@ -0,0 +1,23 @@ +FogOS - Find Implementation + +Build +- Build the OS as usual (make qemu) + +Run +- In the command line type: find +- If "." is given as path, then it will look through current directory +- If 0 args are given it will return an error statement +- If expression/file does not exist in path, then it will return an error statement +- The output will be printed in the following format if successful + - Path: /tests/itests test1.txt + +Test +- Created local testing directory (tests) containing two text files and another directory (itests) with a text file + +Test Input 1: find . test1.txt +Test Output 1: Path: /tests/itest/test1.txt test1.txt /tests/test1.txt test1.txt + +Test Input 2: find /tests test2.txt +Test Output 2: Path: /tests/test2.txt test2.txt + + diff --git a/kernel/syscall.c b/kernel/syscall.c index 076d965..6e0b144 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -101,6 +101,7 @@ extern uint64 sys_unlink(void); extern uint64 sys_link(void); extern uint64 sys_mkdir(void); extern uint64 sys_close(void); +extern uint64 sys_find(void); // An array mapping syscall numbers from syscall.h // to the function that handles the system call. @@ -126,6 +127,7 @@ static uint64 (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, +[SYS_find] sys_find, }; void diff --git a/kernel/syscall.h b/kernel/syscall.h index 3dd926d..9d7bc1b 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -20,3 +20,4 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 +#define SYS_find 22 diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 3044d00..d01522e 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -105,3 +105,11 @@ sys_uptime(void) release(&tickslock); return xticks; } + + +uint64 +sys_find(void) +{ + return 0; +} + diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index 5f080bc..5e3873e 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) @@ -20,9 +26,9 @@ // Disk layout: // [ boot block | sb block | log | inode blocks | free bit map | data blocks ] -int nbitmap = FSSIZE/BPB + 1; +int nbitmap = FSSIZE/(BSIZE*8) + 1; int ninodeblocks = NINODES / IPB + 1; -int nlog = LOGBLOCKS+1; // Header followed by LOGBLOCKS data blocks. +int nlog = LOGBLOCKS; int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap) int nblocks; // Number of data blocks @@ -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) @@ -102,7 +184,7 @@ main(int argc, char *argv[]) sb.inodestart = xint(2+nlog); sb.bmapstart = xint(2+nlog+ninodeblocks); - printf("nmeta %d (boot, super, log blocks %u, inode blocks %u, bitmap blocks %u) blocks %d total %d\n", + printf("nmeta %d (boot, super, log blocks %u inode blocks %u, bitmap blocks %u) blocks %d total %d\n", nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE); freeblock = nmeta; // the first free block that we can allocate @@ -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) @@ -147,8 +236,6 @@ main(int argc, char *argv[]) if(shortname[0] == '_') shortname += 1; - assert(strlen(shortname) <= DIRSIZ); - inum = ialloc(T_FILE); bzero(&de, sizeof(de)); @@ -240,7 +327,7 @@ balloc(int used) int i; printf("balloc: first %d blocks have been allocated\n", used); - assert(used < BPB); + assert(used < BSIZE*8); bzero(buf, BSIZE); for(i = 0; i < used; i++){ buf[i/8] = buf[i/8] | (0x1 << (i%8)); diff --git a/tests/itestdir/itest1.txt b/tests/itestdir/itest1.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/itestdir/itest2.txt b/tests/itestdir/itest2.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/test1.txt b/tests/test1.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/test2.txt b/tests/test2.txt new file mode 100644 index 0000000..e69de29 diff --git a/user/find.c b/user/find.c new file mode 100644 index 0000000..8b7ff9f --- /dev/null +++ b/user/find.c @@ -0,0 +1,137 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "kernel/fcntl.h" +#include "user/user.h" +#include "kernel/fs.h" + +char buf[1024]; +int match(char*, char*); + +char* +fmtname(char *path) +{ + static char buf[DIRSIZ+1]; + char *p; + + // Find first character after last slash. + for(p=path+strlen(path); p >= path && *p != '/'; p--) + ; + p++; + + // Return blank-padded name. + if(strlen(p) >= DIRSIZ) + return p; + memmove(buf, p, strlen(p)); + memset(buf+strlen(p), ' ', DIRSIZ-strlen(p)); + return buf; +} +// ls implementation code +void +findit(char *path, char *expr) +{ + char buf[512], *p; + int fd; + struct dirent de; + struct stat st; + if((fd = open(path, O_RDONLY)) < 0){ + fprintf(2, "find: cannot open %s\n", path); + return; + } + + if(fstat(fd, &st) < 0){ + fprintf(2, "find: cannot stat %s\n", path); + close(fd); + return; + } + + if (st.type == T_FILE) { + if (match(expr, fmtname(path))) { + printf("Path: %s\n", path); + } + } else if (T_DIR) { + if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){ + printf("find: path too long\n"); + close(fd); + return; + } + strcpy(buf, path); + p = buf+strlen(buf); + *p++ = '/'; + + + while(read(fd, &de, sizeof(de)) == sizeof(de)){ + if(de.inum == 0){ + continue; + } + if (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0) { + continue; + } + + memmove(p, de.name, DIRSIZ); + p[DIRSIZ] = 0; + + if(stat(buf, &st) < 0){ + printf("find: cannot stat %s\n", buf); + continue; + } + if(st.type == T_DIR){ + findit(buf, expr); + } else if (match(expr,de.name)) { + printf("Path: %s \nFound!\n", buf); + } + } + close(fd); + } +} + +int +main(int argc, char *argv[]) +{ + if(argc < 3){ + fprintf(2, "usage: find path expression\n"); + exit(1); + } + findit(argv[1], argv[2]); + exit(0); +} +// Regexp matcher from Kernighan & Pike, +// The Practice of Programming, Chapter 9, or +// https://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html + +int matchhere(char*, char*); +int matchstar(int, char*, char*); + +int +match(char *re, char *text) +{ + if(re[0] == '^') + return matchhere(re+1, text); + do{ // must look at empty string + if(matchhere(re, text)) + return 1; + }while(*text++ != '\0'); + return 0; +} +// matchhere: search for re at beginning of text +int matchhere(char *re, char *text) +{ + if(re[0] == '\0') + return 1; + if(re[1] == '*') + return matchstar(re[0], re+2, text); + if(re[0] == '$' && re[1] == '\0') + return *text == '\0'; + if(*text!='\0' && (re[0]=='.' || re[0]==*text)) + return matchhere(re+1, text+1); + return 0; +} + +// matchstar: search for c*re at beginning of text +int matchstar(int c, char *re, char *text) +{ + do{ // a * matches zero or more instances + if(matchhere(re, text)) + return 1; + }while(*text!='\0' && (*text++==c || c=='.')); + return 0; +} diff --git a/user/user.h b/user/user.h index ac84de9..5db0a0c 100644 --- a/user/user.h +++ b/user/user.h @@ -24,6 +24,7 @@ int getpid(void); char* sys_sbrk(int,int); int pause(int); int uptime(void); +void find(char* /*str*/ path, char* /*str*/ expression); // ulib.c int stat(const char*, struct stat*); diff --git a/user/usys.pl b/user/usys.pl index c5d4c3a..8d844c2 100755 --- a/user/usys.pl +++ b/user/usys.pl @@ -42,3 +42,4 @@ sub entry { entry("sbrk"); entry("pause"); entry("uptime"); +entry("find");