#include "kernel/param.h" #include "kernel/types.h" #include "kernel/stat.h" #include "kernel/riscv.h" #include "kernel/fcntl.h" #include "kernel/spinlock.h" #include "kernel/sleeplock.h" #include "kernel/fs.h" #include "kernel/file.h" #include "user/user.h" #define fail(msg) do {printf("FAILURE: " msg "\n"); failed = 1; goto done;} while (0); static int failed = 0; static void testsymlink(void); static void concur(void); static void cleanup(void); int main(int argc, char *argv[]) { cleanup(); testsymlink(); concur(); exit(failed); } static void cleanup(void) { unlink("/testsymlink/a"); unlink("/testsymlink/b"); unlink("/testsymlink/c"); unlink("/testsymlink/1"); unlink("/testsymlink/2"); unlink("/testsymlink/3"); unlink("/testsymlink/4"); unlink("/testsymlink/z"); unlink("/testsymlink/y"); unlink("/testsymlink"); } // stat a symbolic link using O_NOFOLLOW static int stat_slink(char *pn, struct stat *st) { int fd = open(pn, O_RDONLY | O_NOFOLLOW); if(fd < 0) return -1; if(fstat(fd, st) != 0) return -1; return 0; } static void testsymlink(void) { int r, fd1 = -1, fd2 = -1; char buf[4] = {'a', 'b', 'c', 'd'}; char c = 0, c2 = 0; struct stat st; printf("Start: test symlinks\n"); mkdir("/testsymlink"); fd1 = open("/testsymlink/a", O_CREATE | O_RDWR); if(fd1 < 0) fail("failed to open a"); r = symlink("/testsymlink/a", "/testsymlink/b"); if(r < 0) fail("symlink b -> a failed"); if(write(fd1, buf, sizeof(buf)) != 4) fail("failed to write to a"); if (stat_slink("/testsymlink/b", &st) != 0) fail("failed to stat b"); if(st.type != T_SYMLINK) fail("b isn't a symlink"); fd2 = open("/testsymlink/b", O_RDWR); if(fd2 < 0) fail("failed to open b"); read(fd2, &c, 1); if (c != 'a') fail("failed to read bytes from b"); unlink("/testsymlink/a"); if(open("/testsymlink/b", O_RDWR) >= 0) fail("Should not be able to open b after deleting a"); r = symlink("/testsymlink/b", "/testsymlink/a"); if(r < 0) fail("symlink a -> b failed"); r = open("/testsymlink/b", O_RDWR); if(r >= 0) fail("Should not be able to open b (cycle b->a->b->..)\n"); r = symlink("/testsymlink/nonexistent", "/testsymlink/c"); if(r != 0) fail("Symlinking to nonexistent file should succeed\n"); r = symlink("/testsymlink/2", "/testsymlink/1"); if(r) fail("Failed to link 1->2"); r = symlink("/testsymlink/3", "/testsymlink/2"); if(r) fail("Failed to link 2->3"); r = symlink("/testsymlink/4", "/testsymlink/3"); if(r) fail("Failed to link 3->4"); close(fd1); close(fd2); fd1 = open("/testsymlink/4", O_CREATE | O_RDWR); if(fd1<0) fail("Failed to create 4\n"); fd2 = open("/testsymlink/1", O_RDWR); if(fd2<0) fail("Failed to open 1\n"); c = '#'; r = write(fd2, &c, 1); if(r!=1) fail("Failed to write to 1\n"); r = read(fd1, &c2, 1); if(r!=1) fail("Failed to read from 4\n"); if(c!=c2) fail("Value read from 4 differed from value written to 1\n"); printf("test symlinks: ok\n"); done: close(fd1); close(fd2); } static void concur(void) { int pid, i; int fd; struct stat st; int nchild = 2; printf("Start: test concurrent symlinks\n"); fd = open("/testsymlink/z", O_CREATE | O_RDWR); if(fd < 0) { printf("FAILED: open failed"); exit(1); } close(fd); for(int j = 0; j < nchild; j++) { pid = fork(); if(pid < 0){ printf("FAILED: fork failed\n"); exit(1); } if(pid == 0) { int m = 0; unsigned int x = (pid ? 1 : 97); for(i = 0; i < 100; i++){ x = x * 1103515245 + 12345; if((x % 3) == 0) { symlink("/testsymlink/z", "/testsymlink/y"); if (stat_slink("/testsymlink/y", &st) == 0) { m++; if(st.type != T_SYMLINK) { printf("FAILED: not a symbolic link\n", st.type); exit(1); } } } else { unlink("/testsymlink/y"); } } exit(0); } } int r; for(int j = 0; j < nchild; j++) { wait(&r); if(r != 0) { printf("test concurrent symlinks: failed\n"); exit(1); } } printf("test concurrent symlinks: ok\n"); }