diff options
Diffstat (limited to 'user')
| -rw-r--r-- | user/alarmtest.c | 88 | ||||
| -rw-r--r-- | user/cat.c | 43 | ||||
| -rw-r--r-- | user/cow.c | 196 | ||||
| -rw-r--r-- | user/echo.c | 13 | ||||
| -rw-r--r-- | user/forktest.c | 56 | ||||
| -rw-r--r-- | user/grep.c | 107 | ||||
| -rw-r--r-- | user/init.c | 38 | ||||
| -rw-r--r-- | user/initcode.S | 28 | ||||
| -rw-r--r-- | user/kill.c | 17 | ||||
| -rw-r--r-- | user/ln.c | 15 | ||||
| -rw-r--r-- | user/ls.c | 85 | ||||
| -rw-r--r-- | user/mkdir.c | 23 | ||||
| -rw-r--r-- | user/printf.c | 97 | ||||
| -rw-r--r-- | user/rm.c | 23 | ||||
| -rw-r--r-- | user/sh.c | 493 | ||||
| -rw-r--r-- | user/stressfs.c | 49 | ||||
| -rw-r--r-- | user/ulib.c | 109 | ||||
| -rw-r--r-- | user/umalloc.c | 90 | ||||
| -rw-r--r-- | user/user.h | 39 | ||||
| -rw-r--r-- | user/usertests.c | 2018 | ||||
| -rwxr-xr-x | user/usys.pl | 38 | ||||
| -rw-r--r-- | user/wc.c | 54 | ||||
| -rw-r--r-- | user/zombie.c | 14 | 
23 files changed, 3733 insertions, 0 deletions
| diff --git a/user/alarmtest.c b/user/alarmtest.c new file mode 100644 index 0000000..c6da547 --- /dev/null +++ b/user/alarmtest.c @@ -0,0 +1,88 @@ +// +// test program for the alarm lab. +// you can modify this file for testing, +// but please make sure your kernel +// modifications pass the original +// versions of these tests. +// + +#include "kernel/param.h" +#include "kernel/types.h" +#include "kernel/stat.h" +#include "kernel/riscv.h" +#include "user/user.h" + +void test0(); +void test1(); +void periodic(); + +int +main(int argc, char *argv[]) +{ +  test0(); +  test1(); +  exit(); +} + +volatile static int count; + +void +periodic() +{ +  count = count + 1; +  printf(1, "alarm!\n"); +  sigreturn(); +} + +// tests whether the kernel calls +// the alarm handler even a single time. +void +test0() +{ +  int i; +  printf(1, "test0 start\n"); +  count = 0; +  sigalarm(2, periodic); +  for(i = 0; i < 1000*500000; i++){ +    if((i % 250000) == 0) +      write(2, ".", 1); +    if(count > 0) +      break; +  } +  sigalarm(0, 0); +  if(count > 0){ +    printf(1, "test0 passed\n"); +  } else { +    printf(1, "test0 failed\n"); +  } +} + +void __attribute__ ((noinline)) foo(int i, int *j) { +  if((i % 2500000) == 0) { +    write(2, ".", 1); +  } +  *j += 1; +} + +void +test1() +{ +  int i; +  int j; + +  printf(1, "test1 start\n"); +  count = 0; +  j = 0; +  sigalarm(2, periodic); +  for(i = 0; i < 500000000; i++){ +    if(count >= 10) +      break; +    foo(i, &j); +  } +  if(i != j || count < 10){ +    // i should equal j +    printf(1, "test1 failed\n"); +  } else { +    printf(1, "test1 passed\n"); +  } +} diff --git a/user/cat.c b/user/cat.c new file mode 100644 index 0000000..d3d16c4 --- /dev/null +++ b/user/cat.c @@ -0,0 +1,43 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +char buf[512]; + +void +cat(int fd) +{ +  int n; + +  while((n = read(fd, buf, sizeof(buf))) > 0) { +    if (write(1, buf, n) != n) { +      printf(1, "cat: write error\n"); +      exit(); +    } +  } +  if(n < 0){ +    printf(1, "cat: read error\n"); +    exit(); +  } +} + +int +main(int argc, char *argv[]) +{ +  int fd, i; + +  if(argc <= 1){ +    cat(0); +    exit(); +  } + +  for(i = 1; i < argc; i++){ +    if((fd = open(argv[i], 0)) < 0){ +      printf(1, "cat: cannot open %s\n", argv[i]); +      exit(); +    } +    cat(fd); +    close(fd); +  } +  exit(); +} diff --git a/user/cow.c b/user/cow.c new file mode 100644 index 0000000..0426600 --- /dev/null +++ b/user/cow.c @@ -0,0 +1,196 @@ +// +// tests for copy-on-write fork() assignment. +// + +#include "kernel/types.h" +#include "kernel/memlayout.h" +#include "user/user.h" + +// allocate more than half of physical memory, +// then fork. this will fail in the default +// kernel, which does not support copy-on-write. +void +simpletest() +{ +  uint64 phys_size = PHYSTOP - KERNBASE; +  int sz = (phys_size / 3) * 2; + +  printf(1, "simple: "); +   +  char *p = sbrk(sz); +  if(p == (char*)0xffffffffffffffffL){ +    printf(1, "sbrk(%d) failed\n", sz); +    exit(); +  } + +  for(char *q = p; q < p + sz; q += 4096){ +    *(int*)q = getpid(); +  } + +  int pid = fork(); +  if(pid < 0){ +    printf(1, "fork() failed\n"); +    exit(); +  } + +  if(pid == 0) +    exit(); + +  wait(); + +  if(sbrk(-sz) == (char*)0xffffffffffffffffL){ +    printf(1, "sbrk(-%d) failed\n", sz); +    exit(); +  } + +  printf(1, "ok\n"); +} + +// three processes all write COW memory. +// this causes more than half of physical memory +// to be allocated, so it also checks whether +// copied pages are freed. +void +threetest() +{ +  uint64 phys_size = PHYSTOP - KERNBASE; +  int sz = phys_size / 4; +  int pid1, pid2; + +  printf(1, "three: "); +   +  char *p = sbrk(sz); +  if(p == (char*)0xffffffffffffffffL){ +    printf(1, "sbrk(%d) failed\n", sz); +    exit(); +  } + +  pid1 = fork(); +  if(pid1 < 0){ +    printf(1, "fork failed\n"); +    exit(); +  } +  if(pid1 == 0){ +    pid2 = fork(); +    if(pid2 < 0){ +      printf(1, "fork failed"); +      exit(); +    } +    if(pid2 == 0){ +      for(char *q = p; q < p + (sz/5)*4; q += 4096){ +        *(int*)q = getpid(); +      } +      for(char *q = p; q < p + (sz/5)*4; q += 4096){ +        if(*(int*)q != getpid()){ +          printf(1, "wrong content\n"); +          exit(); +        } +      } +      exit(); +    } +    for(char *q = p; q < p + (sz/2); q += 4096){ +      *(int*)q = 9999; +    } +    exit(); +  } + +  for(char *q = p; q < p + sz; q += 4096){ +    *(int*)q = getpid(); +  } + +  wait(); + +  sleep(1); + +  for(char *q = p; q < p + sz; q += 4096){ +    if(*(int*)q != getpid()){ +      printf(1, "wrong content\n"); +      exit(); +    } +  } + +  if(sbrk(-sz) == (char*)0xffffffffffffffffL){ +    printf(1, "sbrk(-%d) failed\n", sz); +    exit(); +  } + +  printf(1, "ok\n"); +} + +char junk1[4096]; +int fds[2]; +char junk2[4096]; +char buf[4096]; +char junk3[4096]; + +// test whether copyout() simulates COW faults. +void +filetest() +{ +  int parent = getpid(); +   +  printf(1, "file: "); +   +  buf[0] = 99; + +  for(int i = 0; i < 4; i++){ +    if(pipe(fds) != 0){ +      printf(1, "pipe() failed\n"); +      exit(); +    } +    int pid = fork(); +    if(pid < 0){ +      printf(1, "fork failed\n"); +      exit(); +    } +    if(pid == 0){ +      sleep(1); +      if(read(fds[0], buf, sizeof(i)) != sizeof(i)){ +        printf(1, "read failed\n"); +        kill(parent); +        exit(); +      } +      sleep(1); +      int j = *(int*)buf; +      if(j != i){ +        printf(1, "read the wrong value\n"); +        kill(parent); +        exit(); +      } +      exit(); +    } +    if(write(fds[1], &i, sizeof(i)) != sizeof(i)){ +      printf(1, "write failed\n"); +      exit(); +    } +  } + +  for(int i = 0; i < 4; i++) +    wait(); + +  if(buf[0] != 99){ +    printf(1, "child overwrote parent\n"); +    exit(); +  } + +  printf(1, "ok\n"); +} + +int +main(int argc, char *argv[]) +{ +  simpletest(); + +  // check that the first simpletest() freed the physical memory. +  simpletest(); + +  threetest(); +  threetest(); +  threetest(); + +  filetest(); + +  printf(1, "ALL COW TESTS PASSED\n"); + +  exit(); +} diff --git a/user/echo.c b/user/echo.c new file mode 100644 index 0000000..ef744ab --- /dev/null +++ b/user/echo.c @@ -0,0 +1,13 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char *argv[]) +{ +  int i; + +  for(i = 1; i < argc; i++) +    printf(1, "%s%s", argv[i], i+1 < argc ? " " : "\n"); +  exit(); +} diff --git a/user/forktest.c b/user/forktest.c new file mode 100644 index 0000000..be4915e --- /dev/null +++ b/user/forktest.c @@ -0,0 +1,56 @@ +// Test that fork fails gracefully. +// Tiny executable so that the limit can be filling the proc table. + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +#define N  1000 + +void +printf(int fd, const char *s, ...) +{ +  write(fd, s, strlen(s)); +} + +void +forktest(void) +{ +  int n, pid; + +  printf(1, "fork test\n"); + +  for(n=0; n<N; n++){ +    pid = fork(); +    if(pid < 0) +      break; +    if(pid == 0) +      exit(); +  } + +  if(n == N){ +    printf(1, "fork claimed to work N times!\n", N); +    exit(); +  } + +  for(; n > 0; n--){ +    if(wait() < 0){ +      printf(1, "wait stopped early\n"); +      exit(); +    } +  } + +  if(wait() != -1){ +    printf(1, "wait got too many\n"); +    exit(); +  } + +  printf(1, "fork test OK\n"); +} + +int +main(void) +{ +  forktest(); +  exit(); +} diff --git a/user/grep.c b/user/grep.c new file mode 100644 index 0000000..b5fdfc2 --- /dev/null +++ b/user/grep.c @@ -0,0 +1,107 @@ +// Simple grep.  Only supports ^ . * $ operators. + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +char buf[1024]; +int match(char*, char*); + +void +grep(char *pattern, int fd) +{ +  int n, m; +  char *p, *q; + +  m = 0; +  while((n = read(fd, buf+m, sizeof(buf)-m-1)) > 0){ +    m += n; +    buf[m] = '\0'; +    p = buf; +    while((q = strchr(p, '\n')) != 0){ +      *q = 0; +      if(match(pattern, p)){ +        *q = '\n'; +        write(1, p, q+1 - p); +      } +      p = q+1; +    } +    if(p == buf) +      m = 0; +    if(m > 0){ +      m -= p - buf; +      memmove(buf, p, m); +    } +  } +} + +int +main(int argc, char *argv[]) +{ +  int fd, i; +  char *pattern; + +  if(argc <= 1){ +    printf(2, "usage: grep pattern [file ...]\n"); +    exit(); +  } +  pattern = argv[1]; + +  if(argc <= 2){ +    grep(pattern, 0); +    exit(); +  } + +  for(i = 2; i < argc; i++){ +    if((fd = open(argv[i], 0)) < 0){ +      printf(1, "grep: cannot open %s\n", argv[i]); +      exit(); +    } +    grep(pattern, fd); +    close(fd); +  } +  exit(); +} + +// Regexp matcher from Kernighan & Pike, +// The Practice of Programming, Chapter 9. + +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/init.c b/user/init.c new file mode 100644 index 0000000..03e60da --- /dev/null +++ b/user/init.c @@ -0,0 +1,38 @@ +// init: The initial user-level program + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fcntl.h" + +char *argv[] = { "sh", 0 }; + +int +main(void) +{ +  int pid, wpid; + +  if(open("console", O_RDWR) < 0){ +    mknod("console", 1, 1); +    open("console", O_RDWR); +  } +  dup(0);  // stdout +  dup(0);  // stderr + +  for(;;){ +    printf(1, "init: starting sh\n"); +    pid = fork(); +    if(pid < 0){ +      printf(1, "init: fork failed\n"); +      exit(); +    } +    if(pid == 0){ +      exec("sh", argv); +      printf(1, "init: exec sh failed\n"); +      exit(); +    } +    while((wpid=wait()) >= 0 && wpid != pid){ +      //printf(1, "zombie!\n"); +    } +  } +} diff --git a/user/initcode.S b/user/initcode.S new file mode 100644 index 0000000..ca76972 --- /dev/null +++ b/user/initcode.S @@ -0,0 +1,28 @@ +# Initial process execs /init. +# This code runs in user space. + +#include "syscall.h" + +# exec(init, argv) +.globl start +start: +        la a0, init +        la a1, argv +        li a7, SYS_exec +        ecall + +# for(;;) exit(); +exit: +        li a7, SYS_exit +        ecall +        jal exit + +# char init[] = "/init\0"; +init: +  .string "/init\0" + +# char *argv[] = { init, 0 }; +.p2align 2 +argv: +  .long init +  .long 0 diff --git a/user/kill.c b/user/kill.c new file mode 100644 index 0000000..4b19d3c --- /dev/null +++ b/user/kill.c @@ -0,0 +1,17 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char **argv) +{ +  int i; + +  if(argc < 2){ +    printf(2, "usage: kill pid...\n"); +    exit(); +  } +  for(i=1; i<argc; i++) +    kill(atoi(argv[i])); +  exit(); +} diff --git a/user/ln.c b/user/ln.c new file mode 100644 index 0000000..482dd79 --- /dev/null +++ b/user/ln.c @@ -0,0 +1,15 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char *argv[]) +{ +  if(argc != 3){ +    printf(2, "Usage: ln old new\n"); +    exit(); +  } +  if(link(argv[1], argv[2]) < 0) +    printf(2, "link %s %s: failed\n", argv[1], argv[2]); +  exit(); +} diff --git a/user/ls.c b/user/ls.c new file mode 100644 index 0000000..3511d87 --- /dev/null +++ b/user/ls.c @@ -0,0 +1,85 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fs.h" + +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; +} + +void +ls(char *path) +{ +  char buf[512], *p; +  int fd; +  struct dirent de; +  struct stat st; + +  if((fd = open(path, 0)) < 0){ +    printf(2, "ls: cannot open %s\n", path); +    return; +  } + +  if(fstat(fd, &st) < 0){ +    printf(2, "ls: cannot stat %s\n", path); +    close(fd); +    return; +  } + +  switch(st.type){ +  case T_FILE: +    printf(1, "%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size); +    break; + +  case T_DIR: +    if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){ +      printf(1, "ls: path too long\n"); +      break; +    } +    strcpy(buf, path); +    p = buf+strlen(buf); +    *p++ = '/'; +    while(read(fd, &de, sizeof(de)) == sizeof(de)){ +      if(de.inum == 0) +        continue; +      memmove(p, de.name, DIRSIZ); +      p[DIRSIZ] = 0; +      if(stat(buf, &st) < 0){ +        printf(1, "ls: cannot stat %s\n", buf); +        continue; +      } +      printf(1, "%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size); +    } +    break; +  } +  close(fd); +} + +int +main(int argc, char *argv[]) +{ +  int i; + +  if(argc < 2){ +    ls("."); +    exit(); +  } +  for(i=1; i<argc; i++) +    ls(argv[i]); +  exit(); +} diff --git a/user/mkdir.c b/user/mkdir.c new file mode 100644 index 0000000..5f1e155 --- /dev/null +++ b/user/mkdir.c @@ -0,0 +1,23 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char *argv[]) +{ +  int i; + +  if(argc < 2){ +    printf(2, "Usage: mkdir files...\n"); +    exit(); +  } + +  for(i = 1; i < argc; i++){ +    if(mkdir(argv[i]) < 0){ +      printf(2, "mkdir: %s failed to create\n", argv[i]); +      break; +    } +  } + +  exit(); +} diff --git a/user/printf.c b/user/printf.c new file mode 100644 index 0000000..f3b3282 --- /dev/null +++ b/user/printf.c @@ -0,0 +1,97 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +#include <stdarg.h> + +static char digits[] = "0123456789ABCDEF"; + +static void +putc(int fd, char c) +{ +  write(fd, &c, 1); +} + +static void +printint(int fd, int xx, int base, int sgn) +{ +  char buf[16]; +  int i, neg; +  uint x; + +  neg = 0; +  if(sgn && xx < 0){ +    neg = 1; +    x = -xx; +  } else { +    x = xx; +  } + +  i = 0; +  do{ +    buf[i++] = digits[x % base]; +  }while((x /= base) != 0); +  if(neg) +    buf[i++] = '-'; + +  while(--i >= 0) +    putc(fd, buf[i]); +} + +static void +printptr(int fd, uint64 x) { +  int i; +  putc(fd, '0'); +  putc(fd, 'x'); +  for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4) +    putc(fd, digits[x >> (sizeof(uint64) * 8 - 4)]); +} + +// Print to the given fd. Only understands %d, %x, %p, %s. +void +printf(int fd, const char *fmt, ...) +{ +  va_list ap; +  char *s; +  int c, i, state; + +  va_start(ap, fmt); +  state = 0; +  for(i = 0; fmt[i]; i++){ +    c = fmt[i] & 0xff; +    if(state == 0){ +      if(c == '%'){ +        state = '%'; +      } else { +        putc(fd, c); +      } +    } else if(state == '%'){ +      if(c == 'd'){ +        printint(fd, va_arg(ap, int), 10, 1); +      } else if(c == 'l') { +        printint(fd, va_arg(ap, uint64), 10, 0); +      } else if(c == 'x') { +        printint(fd, va_arg(ap, int), 16, 0); +      } else if(c == 'p') { +        printptr(fd, va_arg(ap, uint64)); +      } else if(c == 's'){ +        s = va_arg(ap, char*); +        if(s == 0) +          s = "(null)"; +        while(*s != 0){ +          putc(fd, *s); +          s++; +        } +      } else if(c == 'c'){ +        putc(fd, va_arg(ap, uint)); +      } else if(c == '%'){ +        putc(fd, c); +      } else { +        // Unknown % sequence.  Print it to draw attention. +        putc(fd, '%'); +        putc(fd, c); +      } +      state = 0; +    } +  } +} diff --git a/user/rm.c b/user/rm.c new file mode 100644 index 0000000..3076d83 --- /dev/null +++ b/user/rm.c @@ -0,0 +1,23 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char *argv[]) +{ +  int i; + +  if(argc < 2){ +    printf(2, "Usage: rm files...\n"); +    exit(); +  } + +  for(i = 1; i < argc; i++){ +    if(unlink(argv[i]) < 0){ +      printf(2, "rm: %s failed to delete\n", argv[i]); +      break; +    } +  } + +  exit(); +} diff --git a/user/sh.c b/user/sh.c new file mode 100644 index 0000000..13c7883 --- /dev/null +++ b/user/sh.c @@ -0,0 +1,493 @@ +// Shell. + +#include "kernel/types.h" +#include "user/user.h" +#include "kernel/fcntl.h" + +// Parsed command representation +#define EXEC  1 +#define REDIR 2 +#define PIPE  3 +#define LIST  4 +#define BACK  5 + +#define MAXARGS 10 + +struct cmd { +  int type; +}; + +struct execcmd { +  int type; +  char *argv[MAXARGS]; +  char *eargv[MAXARGS]; +}; + +struct redircmd { +  int type; +  struct cmd *cmd; +  char *file; +  char *efile; +  int mode; +  int fd; +}; + +struct pipecmd { +  int type; +  struct cmd *left; +  struct cmd *right; +}; + +struct listcmd { +  int type; +  struct cmd *left; +  struct cmd *right; +}; + +struct backcmd { +  int type; +  struct cmd *cmd; +}; + +int fork1(void);  // Fork but panics on failure. +void panic(char*); +struct cmd *parsecmd(char*); + +// Execute cmd.  Never returns. +void +runcmd(struct cmd *cmd) +{ +  int p[2]; +  struct backcmd *bcmd; +  struct execcmd *ecmd; +  struct listcmd *lcmd; +  struct pipecmd *pcmd; +  struct redircmd *rcmd; + +  if(cmd == 0) +    exit(); + +  switch(cmd->type){ +  default: +    panic("runcmd"); + +  case EXEC: +    ecmd = (struct execcmd*)cmd; +    if(ecmd->argv[0] == 0) +      exit(); +    exec(ecmd->argv[0], ecmd->argv); +    printf(2, "exec %s failed\n", ecmd->argv[0]); +    break; + +  case REDIR: +    rcmd = (struct redircmd*)cmd; +    close(rcmd->fd); +    if(open(rcmd->file, rcmd->mode) < 0){ +      printf(2, "open %s failed\n", rcmd->file); +      exit(); +    } +    runcmd(rcmd->cmd); +    break; + +  case LIST: +    lcmd = (struct listcmd*)cmd; +    if(fork1() == 0) +      runcmd(lcmd->left); +    wait(); +    runcmd(lcmd->right); +    break; + +  case PIPE: +    pcmd = (struct pipecmd*)cmd; +    if(pipe(p) < 0) +      panic("pipe"); +    if(fork1() == 0){ +      close(1); +      dup(p[1]); +      close(p[0]); +      close(p[1]); +      runcmd(pcmd->left); +    } +    if(fork1() == 0){ +      close(0); +      dup(p[0]); +      close(p[0]); +      close(p[1]); +      runcmd(pcmd->right); +    } +    close(p[0]); +    close(p[1]); +    wait(); +    wait(); +    break; + +  case BACK: +    bcmd = (struct backcmd*)cmd; +    if(fork1() == 0) +      runcmd(bcmd->cmd); +    break; +  } +  exit(); +} + +int +getcmd(char *buf, int nbuf) +{ +  printf(2, "$ "); +  memset(buf, 0, nbuf); +  gets(buf, nbuf); +  if(buf[0] == 0) // EOF +    return -1; +  return 0; +} + +int +main(void) +{ +  static char buf[100]; +  int fd; + +  // Ensure that three file descriptors are open. +  while((fd = open("console", O_RDWR)) >= 0){ +    if(fd >= 3){ +      close(fd); +      break; +    } +  } + +  // Read and run input commands. +  while(getcmd(buf, sizeof(buf)) >= 0){ +    if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){ +      // Chdir must be called by the parent, not the child. +      buf[strlen(buf)-1] = 0;  // chop \n +      if(chdir(buf+3) < 0) +        printf(2, "cannot cd %s\n", buf+3); +      continue; +    } +    if(fork1() == 0) +      runcmd(parsecmd(buf)); +    wait(); +  } +  exit(); +} + +void +panic(char *s) +{ +  printf(2, "%s\n", s); +  exit(); +} + +int +fork1(void) +{ +  int pid; + +  pid = fork(); +  if(pid == -1) +    panic("fork"); +  return pid; +} + +//PAGEBREAK! +// Constructors + +struct cmd* +execcmd(void) +{ +  struct execcmd *cmd; + +  cmd = malloc(sizeof(*cmd)); +  memset(cmd, 0, sizeof(*cmd)); +  cmd->type = EXEC; +  return (struct cmd*)cmd; +} + +struct cmd* +redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd) +{ +  struct redircmd *cmd; + +  cmd = malloc(sizeof(*cmd)); +  memset(cmd, 0, sizeof(*cmd)); +  cmd->type = REDIR; +  cmd->cmd = subcmd; +  cmd->file = file; +  cmd->efile = efile; +  cmd->mode = mode; +  cmd->fd = fd; +  return (struct cmd*)cmd; +} + +struct cmd* +pipecmd(struct cmd *left, struct cmd *right) +{ +  struct pipecmd *cmd; + +  cmd = malloc(sizeof(*cmd)); +  memset(cmd, 0, sizeof(*cmd)); +  cmd->type = PIPE; +  cmd->left = left; +  cmd->right = right; +  return (struct cmd*)cmd; +} + +struct cmd* +listcmd(struct cmd *left, struct cmd *right) +{ +  struct listcmd *cmd; + +  cmd = malloc(sizeof(*cmd)); +  memset(cmd, 0, sizeof(*cmd)); +  cmd->type = LIST; +  cmd->left = left; +  cmd->right = right; +  return (struct cmd*)cmd; +} + +struct cmd* +backcmd(struct cmd *subcmd) +{ +  struct backcmd *cmd; + +  cmd = malloc(sizeof(*cmd)); +  memset(cmd, 0, sizeof(*cmd)); +  cmd->type = BACK; +  cmd->cmd = subcmd; +  return (struct cmd*)cmd; +} +//PAGEBREAK! +// Parsing + +char whitespace[] = " \t\r\n\v"; +char symbols[] = "<|>&;()"; + +int +gettoken(char **ps, char *es, char **q, char **eq) +{ +  char *s; +  int ret; + +  s = *ps; +  while(s < es && strchr(whitespace, *s)) +    s++; +  if(q) +    *q = s; +  ret = *s; +  switch(*s){ +  case 0: +    break; +  case '|': +  case '(': +  case ')': +  case ';': +  case '&': +  case '<': +    s++; +    break; +  case '>': +    s++; +    if(*s == '>'){ +      ret = '+'; +      s++; +    } +    break; +  default: +    ret = 'a'; +    while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) +      s++; +    break; +  } +  if(eq) +    *eq = s; + +  while(s < es && strchr(whitespace, *s)) +    s++; +  *ps = s; +  return ret; +} + +int +peek(char **ps, char *es, char *toks) +{ +  char *s; + +  s = *ps; +  while(s < es && strchr(whitespace, *s)) +    s++; +  *ps = s; +  return *s && strchr(toks, *s); +} + +struct cmd *parseline(char**, char*); +struct cmd *parsepipe(char**, char*); +struct cmd *parseexec(char**, char*); +struct cmd *nulterminate(struct cmd*); + +struct cmd* +parsecmd(char *s) +{ +  char *es; +  struct cmd *cmd; + +  es = s + strlen(s); +  cmd = parseline(&s, es); +  peek(&s, es, ""); +  if(s != es){ +    printf(2, "leftovers: %s\n", s); +    panic("syntax"); +  } +  nulterminate(cmd); +  return cmd; +} + +struct cmd* +parseline(char **ps, char *es) +{ +  struct cmd *cmd; + +  cmd = parsepipe(ps, es); +  while(peek(ps, es, "&")){ +    gettoken(ps, es, 0, 0); +    cmd = backcmd(cmd); +  } +  if(peek(ps, es, ";")){ +    gettoken(ps, es, 0, 0); +    cmd = listcmd(cmd, parseline(ps, es)); +  } +  return cmd; +} + +struct cmd* +parsepipe(char **ps, char *es) +{ +  struct cmd *cmd; + +  cmd = parseexec(ps, es); +  if(peek(ps, es, "|")){ +    gettoken(ps, es, 0, 0); +    cmd = pipecmd(cmd, parsepipe(ps, es)); +  } +  return cmd; +} + +struct cmd* +parseredirs(struct cmd *cmd, char **ps, char *es) +{ +  int tok; +  char *q, *eq; + +  while(peek(ps, es, "<>")){ +    tok = gettoken(ps, es, 0, 0); +    if(gettoken(ps, es, &q, &eq) != 'a') +      panic("missing file for redirection"); +    switch(tok){ +    case '<': +      cmd = redircmd(cmd, q, eq, O_RDONLY, 0); +      break; +    case '>': +      cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); +      break; +    case '+':  // >> +      cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); +      break; +    } +  } +  return cmd; +} + +struct cmd* +parseblock(char **ps, char *es) +{ +  struct cmd *cmd; + +  if(!peek(ps, es, "(")) +    panic("parseblock"); +  gettoken(ps, es, 0, 0); +  cmd = parseline(ps, es); +  if(!peek(ps, es, ")")) +    panic("syntax - missing )"); +  gettoken(ps, es, 0, 0); +  cmd = parseredirs(cmd, ps, es); +  return cmd; +} + +struct cmd* +parseexec(char **ps, char *es) +{ +  char *q, *eq; +  int tok, argc; +  struct execcmd *cmd; +  struct cmd *ret; + +  if(peek(ps, es, "(")) +    return parseblock(ps, es); + +  ret = execcmd(); +  cmd = (struct execcmd*)ret; + +  argc = 0; +  ret = parseredirs(ret, ps, es); +  while(!peek(ps, es, "|)&;")){ +    if((tok=gettoken(ps, es, &q, &eq)) == 0) +      break; +    if(tok != 'a') +      panic("syntax"); +    cmd->argv[argc] = q; +    cmd->eargv[argc] = eq; +    argc++; +    if(argc >= MAXARGS) +      panic("too many args"); +    ret = parseredirs(ret, ps, es); +  } +  cmd->argv[argc] = 0; +  cmd->eargv[argc] = 0; +  return ret; +} + +// NUL-terminate all the counted strings. +struct cmd* +nulterminate(struct cmd *cmd) +{ +  int i; +  struct backcmd *bcmd; +  struct execcmd *ecmd; +  struct listcmd *lcmd; +  struct pipecmd *pcmd; +  struct redircmd *rcmd; + +  if(cmd == 0) +    return 0; + +  switch(cmd->type){ +  case EXEC: +    ecmd = (struct execcmd*)cmd; +    for(i=0; ecmd->argv[i]; i++) +      *ecmd->eargv[i] = 0; +    break; + +  case REDIR: +    rcmd = (struct redircmd*)cmd; +    nulterminate(rcmd->cmd); +    *rcmd->efile = 0; +    break; + +  case PIPE: +    pcmd = (struct pipecmd*)cmd; +    nulterminate(pcmd->left); +    nulterminate(pcmd->right); +    break; + +  case LIST: +    lcmd = (struct listcmd*)cmd; +    nulterminate(lcmd->left); +    nulterminate(lcmd->right); +    break; + +  case BACK: +    bcmd = (struct backcmd*)cmd; +    nulterminate(bcmd->cmd); +    break; +  } +  return cmd; +} diff --git a/user/stressfs.c b/user/stressfs.c new file mode 100644 index 0000000..ef8f1cd --- /dev/null +++ b/user/stressfs.c @@ -0,0 +1,49 @@ +// Demonstrate that moving the "acquire" in iderw after the loop that +// appends to the idequeue results in a race. + +// For this to work, you should also add a spin within iderw's +// idequeue traversal loop.  Adding the following demonstrated a panic +// after about 5 runs of stressfs in QEMU on a 2.1GHz CPU: +//    for (i = 0; i < 40000; i++) +//      asm volatile(""); + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fs.h" +#include "kernel/fcntl.h" + +int +main(int argc, char *argv[]) +{ +  int fd, i; +  char path[] = "stressfs0"; +  char data[512]; + +  printf(1, "stressfs starting\n"); +  memset(data, 'a', sizeof(data)); + +  for(i = 0; i < 4; i++) +    if(fork() > 0) +      break; + +  printf(1, "write %d\n", i); + +  path[8] += i; +  fd = open(path, O_CREATE | O_RDWR); +  for(i = 0; i < 20; i++) +//    printf(fd, "%d\n", i); +    write(fd, data, sizeof(data)); +  close(fd); + +  printf(1, "read\n"); + +  fd = open(path, O_RDONLY); +  for (i = 0; i < 20; i++) +    read(fd, data, sizeof(data)); +  close(fd); + +  wait(); + +  exit(); +} diff --git a/user/ulib.c b/user/ulib.c new file mode 100644 index 0000000..ddda0f5 --- /dev/null +++ b/user/ulib.c @@ -0,0 +1,109 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "kernel/fcntl.h" +#include "user/user.h" + +char* +strcpy(char *s, const char *t) +{ +  char *os; + +  os = s; +  while((*s++ = *t++) != 0) +    ; +  return os; +} + +int +strcmp(const char *p, const char *q) +{ +  while(*p && *p == *q) +    p++, q++; +  return (uchar)*p - (uchar)*q; +} + +uint +strlen(const char *s) +{ +  int n; + +  for(n = 0; s[n]; n++) +    ; +  return n; +} + +void* +memset(void *dst, int c, uint n) +{ +  char *cdst = (char *) dst; +  int i; +  for(i = 0; i < n; i++){ +    cdst[i] = c; +  } +  return dst; +} + +char* +strchr(const char *s, char c) +{ +  for(; *s; s++) +    if(*s == c) +      return (char*)s; +  return 0; +} + +char* +gets(char *buf, int max) +{ +  int i, cc; +  char c; + +  for(i=0; i+1 < max; ){ +    cc = read(0, &c, 1); +    if(cc < 1) +      break; +    buf[i++] = c; +    if(c == '\n' || c == '\r') +      break; +  } +  buf[i] = '\0'; +  return buf; +} + +int +stat(const char *n, struct stat *st) +{ +  int fd; +  int r; + +  fd = open(n, O_RDONLY); +  if(fd < 0) +    return -1; +  r = fstat(fd, st); +  close(fd); +  return r; +} + +int +atoi(const char *s) +{ +  int n; + +  n = 0; +  while('0' <= *s && *s <= '9') +    n = n*10 + *s++ - '0'; +  return n; +} + +void* +memmove(void *vdst, const void *vsrc, int n) +{ +  char *dst; +  const char *src; + +  dst = vdst; +  src = vsrc; +  while(n-- > 0) +    *dst++ = *src++; +  return vdst; +} diff --git a/user/umalloc.c b/user/umalloc.c new file mode 100644 index 0000000..2092a32 --- /dev/null +++ b/user/umalloc.c @@ -0,0 +1,90 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/param.h" + +// Memory allocator by Kernighan and Ritchie, +// The C programming Language, 2nd ed.  Section 8.7. + +typedef long Align; + +union header { +  struct { +    union header *ptr; +    uint size; +  } s; +  Align x; +}; + +typedef union header Header; + +static Header base; +static Header *freep; + +void +free(void *ap) +{ +  Header *bp, *p; + +  bp = (Header*)ap - 1; +  for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) +    if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) +      break; +  if(bp + bp->s.size == p->s.ptr){ +    bp->s.size += p->s.ptr->s.size; +    bp->s.ptr = p->s.ptr->s.ptr; +  } else +    bp->s.ptr = p->s.ptr; +  if(p + p->s.size == bp){ +    p->s.size += bp->s.size; +    p->s.ptr = bp->s.ptr; +  } else +    p->s.ptr = bp; +  freep = p; +} + +static Header* +morecore(uint nu) +{ +  char *p; +  Header *hp; + +  if(nu < 4096) +    nu = 4096; +  p = sbrk(nu * sizeof(Header)); +  if(p == (char*)-1) +    return 0; +  hp = (Header*)p; +  hp->s.size = nu; +  free((void*)(hp + 1)); +  return freep; +} + +void* +malloc(uint nbytes) +{ +  Header *p, *prevp; +  uint nunits; + +  nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; +  if((prevp = freep) == 0){ +    base.s.ptr = freep = prevp = &base; +    base.s.size = 0; +  } +  for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ +    if(p->s.size >= nunits){ +      if(p->s.size == nunits) +        prevp->s.ptr = p->s.ptr; +      else { +        p->s.size -= nunits; +        p += p->s.size; +        p->s.size = nunits; +      } +      freep = prevp; +      return (void*)(p + 1); +    } +    if(p == freep) +      if((p = morecore(nunits)) == 0) +        return 0; +  } +} diff --git a/user/user.h b/user/user.h new file mode 100644 index 0000000..4f99c52 --- /dev/null +++ b/user/user.h @@ -0,0 +1,39 @@ +struct stat; +struct rtcdate; + +// system calls +int fork(void); +int exit(void) __attribute__((noreturn)); +int wait(void); +int pipe(int*); +int write(int, const void*, int); +int read(int, void*, int); +int close(int); +int kill(int); +int exec(char*, char**); +int open(const char*, int); +int mknod(const char*, short, short); +int unlink(const char*); +int fstat(int fd, struct stat*); +int link(const char*, const char*); +int mkdir(const char*); +int chdir(const char*); +int dup(int); +int getpid(void); +char* sbrk(int); +int sleep(int); +int uptime(void); + +// ulib.c +int stat(const char*, struct stat*); +char* strcpy(char*, const char*); +void *memmove(void*, const void*, int); +char* strchr(const char*, char c); +int strcmp(const char*, const char*); +void printf(int, const char*, ...); +char* gets(char*, int max); +uint strlen(const char*); +void* memset(void*, int, uint); +void* malloc(uint); +void free(void*); +int atoi(const char*); diff --git a/user/usertests.c b/user/usertests.c new file mode 100644 index 0000000..f74b88c --- /dev/null +++ b/user/usertests.c @@ -0,0 +1,2018 @@ +#include "kernel/param.h" +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fs.h" +#include "kernel/fcntl.h" +#include "kernel/syscall.h" +#include "kernel/memlayout.h" +#include "kernel/riscv.h" + +char buf[8192]; +char name[3]; +char *echoargv[] = { "echo", "ALL", "TESTS", "PASSED", 0 }; +int stdout = 1; + +// does chdir() call iput(p->cwd) in a transaction? +void +iputtest(void) +{ +  printf(stdout, "iput test\n"); + +  if(mkdir("iputdir") < 0){ +    printf(stdout, "mkdir failed\n"); +    exit(); +  } +  if(chdir("iputdir") < 0){ +    printf(stdout, "chdir iputdir failed\n"); +    exit(); +  } +  if(unlink("../iputdir") < 0){ +    printf(stdout, "unlink ../iputdir failed\n"); +    exit(); +  } +  if(chdir("/") < 0){ +    printf(stdout, "chdir / failed\n"); +    exit(); +  } +  printf(stdout, "iput test ok\n"); +} + +// does exit() call iput(p->cwd) in a transaction? +void +exitiputtest(void) +{ +  int pid; + +  printf(stdout, "exitiput test\n"); + +  pid = fork(); +  if(pid < 0){ +    printf(stdout, "fork failed\n"); +    exit(); +  } +  if(pid == 0){ +    if(mkdir("iputdir") < 0){ +      printf(stdout, "mkdir failed\n"); +      exit(); +    } +    if(chdir("iputdir") < 0){ +      printf(stdout, "child chdir failed\n"); +      exit(); +    } +    if(unlink("../iputdir") < 0){ +      printf(stdout, "unlink ../iputdir failed\n"); +      exit(); +    } +    exit(); +  } +  wait(); +  printf(stdout, "exitiput test ok\n"); +} + +// does the error path in open() for attempt to write a +// directory call iput() in a transaction? +// needs a hacked kernel that pauses just after the namei() +// call in sys_open(): +//    if((ip = namei(path)) == 0) +//      return -1; +//    { +//      int i; +//      for(i = 0; i < 10000; i++) +//        yield(); +//    } +void +openiputtest(void) +{ +  int pid; + +  printf(stdout, "openiput test\n"); +  if(mkdir("oidir") < 0){ +    printf(stdout, "mkdir oidir failed\n"); +    exit(); +  } +  pid = fork(); +  if(pid < 0){ +    printf(stdout, "fork failed\n"); +    exit(); +  } +  if(pid == 0){ +    int fd = open("oidir", O_RDWR); +    if(fd >= 0){ +      printf(stdout, "open directory for write succeeded\n"); +      exit(); +    } +    exit(); +  } +  sleep(1); +  if(unlink("oidir") != 0){ +    printf(stdout, "unlink failed\n"); +    exit(); +  } +  wait(); +  printf(stdout, "openiput test ok\n"); +} + +// simple file system tests + +void +opentest(void) +{ +  int fd; + +  printf(stdout, "open test\n"); +  fd = open("echo", 0); +  if(fd < 0){ +    printf(stdout, "open echo failed!\n"); +    exit(); +  } +  close(fd); +  fd = open("doesnotexist", 0); +  if(fd >= 0){ +    printf(stdout, "open doesnotexist succeeded!\n"); +    exit(); +  } +  printf(stdout, "open test ok\n"); +} + +void +writetest(void) +{ +  int fd; +  int i; + +  printf(stdout, "small file test\n"); +  fd = open("small", O_CREATE|O_RDWR); +  if(fd >= 0){ +    printf(stdout, "creat small succeeded; ok\n"); +  } else { +    printf(stdout, "error: creat small failed!\n"); +    exit(); +  } +  for(i = 0; i < 100; i++){ +    if(write(fd, "aaaaaaaaaa", 10) != 10){ +      printf(stdout, "error: write aa %d new file failed\n", i); +      exit(); +    } +    if(write(fd, "bbbbbbbbbb", 10) != 10){ +      printf(stdout, "error: write bb %d new file failed\n", i); +      exit(); +    } +  } +  printf(stdout, "writes ok\n"); +  close(fd); +  fd = open("small", O_RDONLY); +  if(fd >= 0){ +    printf(stdout, "open small succeeded ok\n"); +  } else { +    printf(stdout, "error: open small failed!\n"); +    exit(); +  } +  i = read(fd, buf, 2000); +  if(i == 2000){ +    printf(stdout, "read succeeded ok\n"); +  } else { +    printf(stdout, "read failed\n"); +    exit(); +  } +  close(fd); + +  if(unlink("small") < 0){ +    printf(stdout, "unlink small failed\n"); +    exit(); +  } +  printf(stdout, "small file test ok\n"); +} + +void +writetest1(void) +{ +  int i, fd, n; + +  printf(stdout, "big files test\n"); + +  fd = open("big", O_CREATE|O_RDWR); +  if(fd < 0){ +    printf(stdout, "error: creat big failed!\n"); +    exit(); +  } + +  for(i = 0; i < MAXFILE; i++){ +    ((int*)buf)[0] = i; +    if(write(fd, buf, 512) != 512){ +      printf(stdout, "error: write big file failed\n", i); +      exit(); +    } +  } + +  close(fd); + +  fd = open("big", O_RDONLY); +  if(fd < 0){ +    printf(stdout, "error: open big failed!\n"); +    exit(); +  } + +  n = 0; +  for(;;){ +    i = read(fd, buf, 512); +    if(i == 0){ +      if(n == MAXFILE - 1){ +        printf(stdout, "read only %d blocks from big", n); +        exit(); +      } +      break; +    } else if(i != 512){ +      printf(stdout, "read failed %d\n", i); +      exit(); +    } +    if(((int*)buf)[0] != n){ +      printf(stdout, "read content of block %d is %d\n", +             n, ((int*)buf)[0]); +      exit(); +    } +    n++; +  } +  close(fd); +  if(unlink("big") < 0){ +    printf(stdout, "unlink big failed\n"); +    exit(); +  } +  printf(stdout, "big files ok\n"); +} + +void +createtest(void) +{ +  int i, fd; + +  printf(stdout, "many creates, followed by unlink test\n"); + +  name[0] = 'a'; +  name[2] = '\0'; +  for(i = 0; i < 52; i++){ +    name[1] = '0' + i; +    fd = open(name, O_CREATE|O_RDWR); +    close(fd); +  } +  name[0] = 'a'; +  name[2] = '\0'; +  for(i = 0; i < 52; i++){ +    name[1] = '0' + i; +    unlink(name); +  } +  printf(stdout, "many creates, followed by unlink; ok\n"); +} + +void dirtest(void) +{ +  printf(stdout, "mkdir test\n"); + +  if(mkdir("dir0") < 0){ +    printf(stdout, "mkdir failed\n"); +    exit(); +  } + +  if(chdir("dir0") < 0){ +    printf(stdout, "chdir dir0 failed\n"); +    exit(); +  } + +  if(chdir("..") < 0){ +    printf(stdout, "chdir .. failed\n"); +    exit(); +  } + +  if(unlink("dir0") < 0){ +    printf(stdout, "unlink dir0 failed\n"); +    exit(); +  } +  printf(stdout, "mkdir test ok\n"); +} + +void +exectest(void) +{ +  printf(stdout, "exec test\n"); +  if(exec("echo", echoargv) < 0){ +    printf(stdout, "exec echo failed\n"); +    exit(); +  } +} + +// simple fork and pipe read/write + +void +pipe1(void) +{ +  int fds[2], pid; +  int seq, i, n, cc, total; + +  if(pipe(fds) != 0){ +    printf(1, "pipe() failed\n"); +    exit(); +  } +  pid = fork(); +  seq = 0; +  if(pid == 0){ +    close(fds[0]); +    for(n = 0; n < 5; n++){ +      for(i = 0; i < 1033; i++) +        buf[i] = seq++; +      if(write(fds[1], buf, 1033) != 1033){ +        printf(1, "pipe1 oops 1\n"); +        exit(); +      } +    } +    exit(); +  } else if(pid > 0){ +    close(fds[1]); +    total = 0; +    cc = 1; +    while((n = read(fds[0], buf, cc)) > 0){ +      for(i = 0; i < n; i++){ +        if((buf[i] & 0xff) != (seq++ & 0xff)){ +          printf(1, "pipe1 oops 2\n"); +          return; +        } +      } +      total += n; +      cc = cc * 2; +      if(cc > sizeof(buf)) +        cc = sizeof(buf); +    } +    if(total != 5 * 1033){ +      printf(1, "pipe1 oops 3 total %d\n", total); +      exit(); +    } +    close(fds[0]); +    wait(); +  } else { +    printf(1, "fork() failed\n"); +    exit(); +  } +  printf(1, "pipe1 ok\n"); +} + +// meant to be run w/ at most two CPUs +void +preempt(void) +{ +  int pid1, pid2, pid3; +  int pfds[2]; + +  printf(1, "preempt: "); +  pid1 = fork(); +  if(pid1 < 0) { +    printf(1, "fork failed"); +    exit(); +  } +  if(pid1 == 0) +    for(;;) +      ; + +  pid2 = fork(); +  if(pid2 < 0) { +    printf(1, "fork failed\n"); +    exit(); +  } +  if(pid2 == 0) +    for(;;) +      ; + +  pipe(pfds); +  pid3 = fork(); +  if(pid3 < 0) { +     printf(1, "fork failed\n"); +     exit(); +  } +  if(pid3 == 0){ +    close(pfds[0]); +    if(write(pfds[1], "x", 1) != 1) +      printf(1, "preempt write error"); +    close(pfds[1]); +    for(;;) +      ; +  } + +  close(pfds[1]); +  if(read(pfds[0], buf, sizeof(buf)) != 1){ +    printf(1, "preempt read error"); +    return; +  } +  close(pfds[0]); +  printf(1, "kill... "); +  kill(pid1); +  kill(pid2); +  kill(pid3); +  printf(1, "wait... "); +  wait(); +  wait(); +  wait(); +  printf(1, "preempt ok\n"); +} + +// try to find any races between exit and wait +void +exitwait(void) +{ +  int i, pid; + +  printf(1, "exitwait test\n"); + +  for(i = 0; i < 100; i++){ +    pid = fork(); +    if(pid < 0){ +      printf(1, "fork failed\n"); +      exit(); +    } +    if(pid){ +      if(wait() != pid){ +        printf(1, "wait wrong pid\n"); +        exit(); +      } +    } else { +      exit(); +    } +  } +  printf(1, "exitwait ok\n"); +} + +// try to find races in the reparenting +// code that handles a parent exiting +// when it still has live children. +void +reparent(void) +{ +  int master_pid = getpid(); +   +  printf(1, "reparent test\n"); + +  for(int i = 0; i < 200; i++){ +    int pid = fork(); +    if(pid < 0){ +      printf(1, "fork failed\n"); +      exit(); +    } +    if(pid){ +      if(wait() != pid){ +        printf(1, "wait wrong pid\n"); +        exit(); +      } +    } else { +      int pid2 = fork(); +      if(pid2 < 0){ +        printf(1, "fork failed\n"); +        kill(master_pid); +        exit(); +      } +      if(pid2 == 0){ +        exit(); +      } else { +        exit(); +      } +    } +  } +  printf(1, "reparent ok\n"); +} + +// what if two children exit() at the same time? +void +twochildren(void) +{ +  printf(1, "twochildren test\n"); + +  for(int i = 0; i < 1000; i++){ +    int pid1 = fork(); +    if(pid1 < 0){ +      printf(1, "fork failed\n"); +      exit(); +    } +    if(pid1 == 0){ +      exit(); +    } else { +      int pid2 = fork(); +      if(pid2 < 0){ +        printf(1, "fork failed\n"); +        exit(); +      } +      if(pid2 == 0){ +        exit(); +      } else { +        wait(); +        wait(); +      } +    } +  } +  printf(1, "twochildren ok\n"); +} + +// concurrent forks to try to expose locking bugs. +void +forkfork(void) +{ +  int ppid = getpid(); +   +  printf(1, "forkfork test\n"); + +  for(int i = 0; i < 2; i++){ +    int pid = fork(); +    if(pid < 0){ +      printf(1, "fork failed"); +      exit(); +    } +    if(pid == 0){ +      for(int j = 0; j < 200; j++){ +        int pid1 = fork(); +        if(pid1 < 0){ +          printf(1, "fork failed\n"); +          kill(ppid); +          exit(); +        } +        if(pid1 == 0){ +          exit(); +        } +        wait(); +      } +      exit(); +    } +  } + +  for(int i = 0; i < 2; i++){ +    wait(); +  } + +  printf(1, "forkfork ok\n"); +} + +void +forkforkfork(void) +{ +  printf(1, "forkforkfork test\n"); + +  unlink("stopforking"); + +  int pid = fork(); +  if(pid < 0){ +    printf(1, "fork failed"); +    exit(); +  } +  if(pid == 0){ +    while(1){ +      int fd = open("stopforking", 0); +      if(fd >= 0){ +        exit(); +      } +      if(fork() < 0){ +        close(open("stopforking", O_CREATE|O_RDWR)); +      } +    } + +    exit(); +  } + +  sleep(20); // two seconds +  close(open("stopforking", O_CREATE|O_RDWR)); +  wait(); +  sleep(10); // one second + +  printf(1, "forkforkfork ok\n"); +} + +void +mem(void) +{ +  void *m1, *m2; +  int pid, ppid; + +  printf(1, "mem test\n"); +  ppid = getpid(); +  if((pid = fork()) == 0){ +    m1 = 0; +    while((m2 = malloc(10001)) != 0){ +      *(char**)m2 = m1; +      m1 = m2; +    } +    while(m1){ +      m2 = *(char**)m1; +      free(m1); +      m1 = m2; +    } +    m1 = malloc(1024*20); +    if(m1 == 0){ +      printf(1, "couldn't allocate mem?!!\n"); +      kill(ppid); +      exit(); +    } +    free(m1); +    printf(1, "mem ok\n"); +    exit(); +  } else { +    wait(); +  } +} + +// More file system tests + +// two processes write to the same file descriptor +// is the offset shared? does inode locking work? +void +sharedfd(void) +{ +  int fd, pid, i, n, nc, np; +  char buf[10]; + +  printf(1, "sharedfd test\n"); + +  unlink("sharedfd"); +  fd = open("sharedfd", O_CREATE|O_RDWR); +  if(fd < 0){ +    printf(1, "fstests: cannot open sharedfd for writing"); +    return; +  } +  pid = fork(); +  memset(buf, pid==0?'c':'p', sizeof(buf)); +  for(i = 0; i < 1000; i++){ +    if(write(fd, buf, sizeof(buf)) != sizeof(buf)){ +      printf(1, "fstests: write sharedfd failed\n"); +      break; +    } +  } +  if(pid == 0) +    exit(); +  else +    wait(); +  close(fd); +  fd = open("sharedfd", 0); +  if(fd < 0){ +    printf(1, "fstests: cannot open sharedfd for reading\n"); +    return; +  } +  nc = np = 0; +  while((n = read(fd, buf, sizeof(buf))) > 0){ +    for(i = 0; i < sizeof(buf); i++){ +      if(buf[i] == 'c') +        nc++; +      if(buf[i] == 'p') +        np++; +    } +  } +  close(fd); +  unlink("sharedfd"); +  if(nc == 10000 && np == 10000){ +    printf(1, "sharedfd ok\n"); +  } else { +    printf(1, "sharedfd oops %d %d\n", nc, np); +    exit(); +  } +} + +// four processes write different files at the same +// time, to test block allocation. +void +fourfiles(void) +{ +  int fd, pid, i, j, n, total, pi; +  char *names[] = { "f0", "f1", "f2", "f3" }; +  char *fname; + +  printf(1, "fourfiles test\n"); + +  for(pi = 0; pi < 4; pi++){ +    fname = names[pi]; +    unlink(fname); + +    pid = fork(); +    if(pid < 0){ +      printf(1, "fork failed\n"); +      exit(); +    } + +    if(pid == 0){ +      fd = open(fname, O_CREATE | O_RDWR); +      if(fd < 0){ +        printf(1, "create failed\n"); +        exit(); +      } + +      memset(buf, '0'+pi, 512); +      for(i = 0; i < 12; i++){ +        if((n = write(fd, buf, 500)) != 500){ +          printf(1, "write failed %d\n", n); +          exit(); +        } +      } +      exit(); +    } +  } + +  for(pi = 0; pi < 4; pi++){ +    wait(); +  } + +  for(i = 0; i < 2; i++){ +    fname = names[i]; +    fd = open(fname, 0); +    total = 0; +    while((n = read(fd, buf, sizeof(buf))) > 0){ +      for(j = 0; j < n; j++){ +        if(buf[j] != '0'+i){ +          printf(1, "wrong char\n"); +          exit(); +        } +      } +      total += n; +    } +    close(fd); +    if(total != 12*500){ +      printf(1, "wrong length %d\n", total); +      exit(); +    } +    unlink(fname); +  } + +  printf(1, "fourfiles ok\n"); +} + +// four processes create and delete different files in same directory +void +createdelete(void) +{ +  enum { N = 20, NCHILD=4 }; +  int pid, i, fd, pi; +  char name[32]; + +  printf(1, "createdelete test\n"); + +  for(pi = 0; pi < NCHILD; pi++){ +    pid = fork(); +    if(pid < 0){ +      printf(1, "fork failed\n"); +      exit(); +    } + +    if(pid == 0){ +      name[0] = 'p' + pi; +      name[2] = '\0'; +      for(i = 0; i < N; i++){ +        name[1] = '0' + i; +        fd = open(name, O_CREATE | O_RDWR); +        if(fd < 0){ +          printf(1, "create failed\n"); +          exit(); +        } +        close(fd); +        if(i > 0 && (i % 2 ) == 0){ +          name[1] = '0' + (i / 2); +          if(unlink(name) < 0){ +            printf(1, "unlink failed\n"); +            exit(); +          } +        } +      } +      exit(); +    } +  } + +  for(pi = 0; pi < NCHILD; pi++){ +    wait(); +  } + +  name[0] = name[1] = name[2] = 0; +  for(i = 0; i < N; i++){ +    for(pi = 0; pi < NCHILD; pi++){ +      name[0] = 'p' + pi; +      name[1] = '0' + i; +      fd = open(name, 0); +      if((i == 0 || i >= N/2) && fd < 0){ +        printf(1, "oops createdelete %s didn't exist\n", name); +        exit(); +      } else if((i >= 1 && i < N/2) && fd >= 0){ +        printf(1, "oops createdelete %s did exist\n", name); +        exit(); +      } +      if(fd >= 0) +        close(fd); +    } +  } + +  for(i = 0; i < N; i++){ +    for(pi = 0; pi < NCHILD; pi++){ +      name[0] = 'p' + i; +      name[1] = '0' + i; +      unlink(name); +    } +  } + +  printf(1, "createdelete ok\n"); +} + +// can I unlink a file and still read it? +void +unlinkread(void) +{ +  int fd, fd1; + +  printf(1, "unlinkread test\n"); +  fd = open("unlinkread", O_CREATE | O_RDWR); +  if(fd < 0){ +    printf(1, "create unlinkread failed\n"); +    exit(); +  } +  write(fd, "hello", 5); +  close(fd); + +  fd = open("unlinkread", O_RDWR); +  if(fd < 0){ +    printf(1, "open unlinkread failed\n"); +    exit(); +  } +  if(unlink("unlinkread") != 0){ +    printf(1, "unlink unlinkread failed\n"); +    exit(); +  } + +  fd1 = open("unlinkread", O_CREATE | O_RDWR); +  write(fd1, "yyy", 3); +  close(fd1); + +  if(read(fd, buf, sizeof(buf)) != 5){ +    printf(1, "unlinkread read failed"); +    exit(); +  } +  if(buf[0] != 'h'){ +    printf(1, "unlinkread wrong data\n"); +    exit(); +  } +  if(write(fd, buf, 10) != 10){ +    printf(1, "unlinkread write failed\n"); +    exit(); +  } +  close(fd); +  unlink("unlinkread"); +  printf(1, "unlinkread ok\n"); +} + +void +linktest(void) +{ +  int fd; + +  printf(1, "linktest\n"); + +  unlink("lf1"); +  unlink("lf2"); + +  fd = open("lf1", O_CREATE|O_RDWR); +  if(fd < 0){ +    printf(1, "create lf1 failed\n"); +    exit(); +  } +  if(write(fd, "hello", 5) != 5){ +    printf(1, "write lf1 failed\n"); +    exit(); +  } +  close(fd); + +  if(link("lf1", "lf2") < 0){ +    printf(1, "link lf1 lf2 failed\n"); +    exit(); +  } +  unlink("lf1"); + +  if(open("lf1", 0) >= 0){ +    printf(1, "unlinked lf1 but it is still there!\n"); +    exit(); +  } + +  fd = open("lf2", 0); +  if(fd < 0){ +    printf(1, "open lf2 failed\n"); +    exit(); +  } +  if(read(fd, buf, sizeof(buf)) != 5){ +    printf(1, "read lf2 failed\n"); +    exit(); +  } +  close(fd); + +  if(link("lf2", "lf2") >= 0){ +    printf(1, "link lf2 lf2 succeeded! oops\n"); +    exit(); +  } + +  unlink("lf2"); +  if(link("lf2", "lf1") >= 0){ +    printf(1, "link non-existant succeeded! oops\n"); +    exit(); +  } + +  if(link(".", "lf1") >= 0){ +    printf(1, "link . lf1 succeeded! oops\n"); +    exit(); +  } + +  printf(1, "linktest ok\n"); +} + +// test concurrent create/link/unlink of the same file +void +concreate(void) +{ +  char file[3]; +  int i, pid, n, fd; +  char fa[40]; +  struct { +    ushort inum; +    char name[14]; +  } de; + +  printf(1, "concreate test\n"); +  file[0] = 'C'; +  file[2] = '\0'; +  for(i = 0; i < 40; i++){ +    file[1] = '0' + i; +    unlink(file); +    pid = fork(); +    if(pid && (i % 3) == 1){ +      link("C0", file); +    } else if(pid == 0 && (i % 5) == 1){ +      link("C0", file); +    } else { +      fd = open(file, O_CREATE | O_RDWR); +      if(fd < 0){ +        printf(1, "concreate create %s failed\n", file); +        exit(); +      } +      close(fd); +    } +    if(pid == 0) +      exit(); +    else +      wait(); +  } + +  memset(fa, 0, sizeof(fa)); +  fd = open(".", 0); +  n = 0; +  while(read(fd, &de, sizeof(de)) > 0){ +    if(de.inum == 0) +      continue; +    if(de.name[0] == 'C' && de.name[2] == '\0'){ +      i = de.name[1] - '0'; +      if(i < 0 || i >= sizeof(fa)){ +        printf(1, "concreate weird file %s\n", de.name); +        exit(); +      } +      if(fa[i]){ +        printf(1, "concreate duplicate file %s\n", de.name); +        exit(); +      } +      fa[i] = 1; +      n++; +    } +  } +  close(fd); + +  if(n != 40){ +    printf(1, "concreate not enough files in directory listing\n"); +    exit(); +  } + +  for(i = 0; i < 40; i++){ +    file[1] = '0' + i; +    pid = fork(); +    if(pid < 0){ +      printf(1, "fork failed\n"); +      exit(); +    } +    if(((i % 3) == 0 && pid == 0) || +       ((i % 3) == 1 && pid != 0)){ +      close(open(file, 0)); +      close(open(file, 0)); +      close(open(file, 0)); +      close(open(file, 0)); +    } else { +      unlink(file); +      unlink(file); +      unlink(file); +      unlink(file); +    } +    if(pid == 0) +      exit(); +    else +      wait(); +  } + +  printf(1, "concreate ok\n"); +} + +// another concurrent link/unlink/create test, +// to look for deadlocks. +void +linkunlink() +{ +  int pid, i; + +  printf(1, "linkunlink test\n"); + +  unlink("x"); +  pid = fork(); +  if(pid < 0){ +    printf(1, "fork failed\n"); +    exit(); +  } + +  unsigned int x = (pid ? 1 : 97); +  for(i = 0; i < 100; i++){ +    x = x * 1103515245 + 12345; +    if((x % 3) == 0){ +      close(open("x", O_RDWR | O_CREATE)); +    } else if((x % 3) == 1){ +      link("cat", "x"); +    } else { +      unlink("x"); +    } +  } + +  if(pid) +    wait(); +  else +    exit(); + +  printf(1, "linkunlink ok\n"); +} + +// directory that uses indirect blocks +void +bigdir(void) +{ +  int i, fd; +  char name[10]; + +  printf(1, "bigdir test\n"); +  unlink("bd"); + +  fd = open("bd", O_CREATE); +  if(fd < 0){ +    printf(1, "bigdir create failed\n"); +    exit(); +  } +  close(fd); + +  for(i = 0; i < 500; i++){ +    name[0] = 'x'; +    name[1] = '0' + (i / 64); +    name[2] = '0' + (i % 64); +    name[3] = '\0'; +    if(link("bd", name) != 0){ +      printf(1, "bigdir link failed\n"); +      exit(); +    } +  } + +  unlink("bd"); +  for(i = 0; i < 500; i++){ +    name[0] = 'x'; +    name[1] = '0' + (i / 64); +    name[2] = '0' + (i % 64); +    name[3] = '\0'; +    if(unlink(name) != 0){ +      printf(1, "bigdir unlink failed"); +      exit(); +    } +  } + +  printf(1, "bigdir ok\n"); +} + +void +subdir(void) +{ +  int fd, cc; + +  printf(1, "subdir test\n"); + +  unlink("ff"); +  if(mkdir("dd") != 0){ +    printf(1, "subdir mkdir dd failed\n"); +    exit(); +  } + +  fd = open("dd/ff", O_CREATE | O_RDWR); +  if(fd < 0){ +    printf(1, "create dd/ff failed\n"); +    exit(); +  } +  write(fd, "ff", 2); +  close(fd); + +  if(unlink("dd") >= 0){ +    printf(1, "unlink dd (non-empty dir) succeeded!\n"); +    exit(); +  } + +  if(mkdir("/dd/dd") != 0){ +    printf(1, "subdir mkdir dd/dd failed\n"); +    exit(); +  } + +  fd = open("dd/dd/ff", O_CREATE | O_RDWR); +  if(fd < 0){ +    printf(1, "create dd/dd/ff failed\n"); +    exit(); +  } +  write(fd, "FF", 2); +  close(fd); + +  fd = open("dd/dd/../ff", 0); +  if(fd < 0){ +    printf(1, "open dd/dd/../ff failed\n"); +    exit(); +  } +  cc = read(fd, buf, sizeof(buf)); +  if(cc != 2 || buf[0] != 'f'){ +    printf(1, "dd/dd/../ff wrong content\n"); +    exit(); +  } +  close(fd); + +  if(link("dd/dd/ff", "dd/dd/ffff") != 0){ +    printf(1, "link dd/dd/ff dd/dd/ffff failed\n"); +    exit(); +  } + +  if(unlink("dd/dd/ff") != 0){ +    printf(1, "unlink dd/dd/ff failed\n"); +    exit(); +  } +  if(open("dd/dd/ff", O_RDONLY) >= 0){ +    printf(1, "open (unlinked) dd/dd/ff succeeded\n"); +    exit(); +  } + +  if(chdir("dd") != 0){ +    printf(1, "chdir dd failed\n"); +    exit(); +  } +  if(chdir("dd/../../dd") != 0){ +    printf(1, "chdir dd/../../dd failed\n"); +    exit(); +  } +  if(chdir("dd/../../../dd") != 0){ +    printf(1, "chdir dd/../../dd failed\n"); +    exit(); +  } +  if(chdir("./..") != 0){ +    printf(1, "chdir ./.. failed\n"); +    exit(); +  } + +  fd = open("dd/dd/ffff", 0); +  if(fd < 0){ +    printf(1, "open dd/dd/ffff failed\n"); +    exit(); +  } +  if(read(fd, buf, sizeof(buf)) != 2){ +    printf(1, "read dd/dd/ffff wrong len\n"); +    exit(); +  } +  close(fd); + +  if(open("dd/dd/ff", O_RDONLY) >= 0){ +    printf(1, "open (unlinked) dd/dd/ff succeeded!\n"); +    exit(); +  } + +  if(open("dd/ff/ff", O_CREATE|O_RDWR) >= 0){ +    printf(1, "create dd/ff/ff succeeded!\n"); +    exit(); +  } +  if(open("dd/xx/ff", O_CREATE|O_RDWR) >= 0){ +    printf(1, "create dd/xx/ff succeeded!\n"); +    exit(); +  } +  if(open("dd", O_CREATE) >= 0){ +    printf(1, "create dd succeeded!\n"); +    exit(); +  } +  if(open("dd", O_RDWR) >= 0){ +    printf(1, "open dd rdwr succeeded!\n"); +    exit(); +  } +  if(open("dd", O_WRONLY) >= 0){ +    printf(1, "open dd wronly succeeded!\n"); +    exit(); +  } +  if(link("dd/ff/ff", "dd/dd/xx") == 0){ +    printf(1, "link dd/ff/ff dd/dd/xx succeeded!\n"); +    exit(); +  } +  if(link("dd/xx/ff", "dd/dd/xx") == 0){ +    printf(1, "link dd/xx/ff dd/dd/xx succeeded!\n"); +    exit(); +  } +  if(link("dd/ff", "dd/dd/ffff") == 0){ +    printf(1, "link dd/ff dd/dd/ffff succeeded!\n"); +    exit(); +  } +  if(mkdir("dd/ff/ff") == 0){ +    printf(1, "mkdir dd/ff/ff succeeded!\n"); +    exit(); +  } +  if(mkdir("dd/xx/ff") == 0){ +    printf(1, "mkdir dd/xx/ff succeeded!\n"); +    exit(); +  } +  if(mkdir("dd/dd/ffff") == 0){ +    printf(1, "mkdir dd/dd/ffff succeeded!\n"); +    exit(); +  } +  if(unlink("dd/xx/ff") == 0){ +    printf(1, "unlink dd/xx/ff succeeded!\n"); +    exit(); +  } +  if(unlink("dd/ff/ff") == 0){ +    printf(1, "unlink dd/ff/ff succeeded!\n"); +    exit(); +  } +  if(chdir("dd/ff") == 0){ +    printf(1, "chdir dd/ff succeeded!\n"); +    exit(); +  } +  if(chdir("dd/xx") == 0){ +    printf(1, "chdir dd/xx succeeded!\n"); +    exit(); +  } + +  if(unlink("dd/dd/ffff") != 0){ +    printf(1, "unlink dd/dd/ff failed\n"); +    exit(); +  } +  if(unlink("dd/ff") != 0){ +    printf(1, "unlink dd/ff failed\n"); +    exit(); +  } +  if(unlink("dd") == 0){ +    printf(1, "unlink non-empty dd succeeded!\n"); +    exit(); +  } +  if(unlink("dd/dd") < 0){ +    printf(1, "unlink dd/dd failed\n"); +    exit(); +  } +  if(unlink("dd") < 0){ +    printf(1, "unlink dd failed\n"); +    exit(); +  } + +  printf(1, "subdir ok\n"); +} + +// test writes that are larger than the log. +void +bigwrite(void) +{ +  int fd, sz; + +  printf(1, "bigwrite test\n"); + +  unlink("bigwrite"); +  for(sz = 499; sz < 12*512; sz += 471){ +    fd = open("bigwrite", O_CREATE | O_RDWR); +    if(fd < 0){ +      printf(1, "cannot create bigwrite\n"); +      exit(); +    } +    int i; +    for(i = 0; i < 2; i++){ +      int cc = write(fd, buf, sz); +      if(cc != sz){ +        printf(1, "write(%d) ret %d\n", sz, cc); +        exit(); +      } +    } +    close(fd); +    unlink("bigwrite"); +  } + +  printf(1, "bigwrite ok\n"); +} + +void +bigfile(void) +{ +  int fd, i, total, cc; + +  printf(1, "bigfile test\n"); + +  unlink("bigfile"); +  fd = open("bigfile", O_CREATE | O_RDWR); +  if(fd < 0){ +    printf(1, "cannot create bigfile"); +    exit(); +  } +  for(i = 0; i < 20; i++){ +    memset(buf, i, 600); +    if(write(fd, buf, 600) != 600){ +      printf(1, "write bigfile failed\n"); +      exit(); +    } +  } +  close(fd); + +  fd = open("bigfile", 0); +  if(fd < 0){ +    printf(1, "cannot open bigfile\n"); +    exit(); +  } +  total = 0; +  for(i = 0; ; i++){ +    cc = read(fd, buf, 300); +    if(cc < 0){ +      printf(1, "read bigfile failed\n"); +      exit(); +    } +    if(cc == 0) +      break; +    if(cc != 300){ +      printf(1, "short read bigfile\n"); +      exit(); +    } +    if(buf[0] != i/2 || buf[299] != i/2){ +      printf(1, "read bigfile wrong data\n"); +      exit(); +    } +    total += cc; +  } +  close(fd); +  if(total != 20*600){ +    printf(1, "read bigfile wrong total\n"); +    exit(); +  } +  unlink("bigfile"); + +  printf(1, "bigfile test ok\n"); +} + +void +fourteen(void) +{ +  int fd; + +  // DIRSIZ is 14. +  printf(1, "fourteen test\n"); + +  if(mkdir("12345678901234") != 0){ +    printf(1, "mkdir 12345678901234 failed\n"); +    exit(); +  } +  if(mkdir("12345678901234/123456789012345") != 0){ +    printf(1, "mkdir 12345678901234/123456789012345 failed\n"); +    exit(); +  } +  fd = open("123456789012345/123456789012345/123456789012345", O_CREATE); +  if(fd < 0){ +    printf(1, "create 123456789012345/123456789012345/123456789012345 failed\n"); +    exit(); +  } +  close(fd); +  fd = open("12345678901234/12345678901234/12345678901234", 0); +  if(fd < 0){ +    printf(1, "open 12345678901234/12345678901234/12345678901234 failed\n"); +    exit(); +  } +  close(fd); + +  if(mkdir("12345678901234/12345678901234") == 0){ +    printf(1, "mkdir 12345678901234/12345678901234 succeeded!\n"); +    exit(); +  } +  if(mkdir("123456789012345/12345678901234") == 0){ +    printf(1, "mkdir 12345678901234/123456789012345 succeeded!\n"); +    exit(); +  } + +  printf(1, "fourteen ok\n"); +} + +void +rmdot(void) +{ +  printf(1, "rmdot test\n"); +  if(mkdir("dots") != 0){ +    printf(1, "mkdir dots failed\n"); +    exit(); +  } +  if(chdir("dots") != 0){ +    printf(1, "chdir dots failed\n"); +    exit(); +  } +  if(unlink(".") == 0){ +    printf(1, "rm . worked!\n"); +    exit(); +  } +  if(unlink("..") == 0){ +    printf(1, "rm .. worked!\n"); +    exit(); +  } +  if(chdir("/") != 0){ +    printf(1, "chdir / failed\n"); +    exit(); +  } +  if(unlink("dots/.") == 0){ +    printf(1, "unlink dots/. worked!\n"); +    exit(); +  } +  if(unlink("dots/..") == 0){ +    printf(1, "unlink dots/.. worked!\n"); +    exit(); +  } +  if(unlink("dots") != 0){ +    printf(1, "unlink dots failed!\n"); +    exit(); +  } +  printf(1, "rmdot ok\n"); +} + +void +dirfile(void) +{ +  int fd; + +  printf(1, "dir vs file\n"); + +  fd = open("dirfile", O_CREATE); +  if(fd < 0){ +    printf(1, "create dirfile failed\n"); +    exit(); +  } +  close(fd); +  if(chdir("dirfile") == 0){ +    printf(1, "chdir dirfile succeeded!\n"); +    exit(); +  } +  fd = open("dirfile/xx", 0); +  if(fd >= 0){ +    printf(1, "create dirfile/xx succeeded!\n"); +    exit(); +  } +  fd = open("dirfile/xx", O_CREATE); +  if(fd >= 0){ +    printf(1, "create dirfile/xx succeeded!\n"); +    exit(); +  } +  if(mkdir("dirfile/xx") == 0){ +    printf(1, "mkdir dirfile/xx succeeded!\n"); +    exit(); +  } +  if(unlink("dirfile/xx") == 0){ +    printf(1, "unlink dirfile/xx succeeded!\n"); +    exit(); +  } +  if(link("README", "dirfile/xx") == 0){ +    printf(1, "link to dirfile/xx succeeded!\n"); +    exit(); +  } +  if(unlink("dirfile") != 0){ +    printf(1, "unlink dirfile failed!\n"); +    exit(); +  } + +  fd = open(".", O_RDWR); +  if(fd >= 0){ +    printf(1, "open . for writing succeeded!\n"); +    exit(); +  } +  fd = open(".", 0); +  if(write(fd, "x", 1) > 0){ +    printf(1, "write . succeeded!\n"); +    exit(); +  } +  close(fd); + +  printf(1, "dir vs file OK\n"); +} + +// test that iput() is called at the end of _namei() +void +iref(void) +{ +  int i, fd; + +  printf(1, "empty file name\n"); + +  // the 50 is NINODE +  for(i = 0; i < 50 + 1; i++){ +    if(mkdir("irefd") != 0){ +      printf(1, "mkdir irefd failed\n"); +      exit(); +    } +    if(chdir("irefd") != 0){ +      printf(1, "chdir irefd failed\n"); +      exit(); +    } + +    mkdir(""); +    link("README", ""); +    fd = open("", O_CREATE); +    if(fd >= 0) +      close(fd); +    fd = open("xx", O_CREATE); +    if(fd >= 0) +      close(fd); +    unlink("xx"); +  } + +  chdir("/"); +  printf(1, "empty file name OK\n"); +} + +// test that fork fails gracefully +// the forktest binary also does this, but it runs out of proc entries first. +// inside the bigger usertests binary, we run out of memory first. +void +forktest(void) +{ +  int n, pid; + +  printf(1, "fork test\n"); + +  for(n=0; n<1000; n++){ +    pid = fork(); +    if(pid < 0) +      break; +    if(pid == 0) +      exit(); +  } + +  if (n == 0) { +    printf(1, "no fork at all!\n"); +    exit(); +  } + +  if(n == 1000){ +    printf(1, "fork claimed to work 1000 times!\n"); +    exit(); +  } + +  for(; n > 0; n--){ +    if(wait() < 0){ +      printf(1, "wait stopped early\n"); +      exit(); +    } +  } + +  if(wait() != -1){ +    printf(1, "wait got too many\n"); +    exit(); +  } + +  printf(1, "fork test OK\n"); +} + +void +sbrktest(void) +{ +  int i, fds[2], pids[10], pid, ppid; +  char *c, *oldbrk, scratch, *a, *b, *lastaddr, *p; +  uint64 amt; +  int fd; +  int n; +  #define BIG (100*1024*1024) + +  printf(stdout, "sbrk test\n"); +  oldbrk = sbrk(0); + +  // does sbrk() return the expected failure value? +  a = sbrk(1024*1024*1024); +  if(a != (char*)0xffffffffffffffffL){ +    printf(stdout, "sbrk(<toomuch>) returned %p\n", a); +    exit(); +  } + +  // can one sbrk() less than a page? +  a = sbrk(0); +  for(i = 0; i < 5000; i++){ +    b = sbrk(1); +    if(b != a){ +      printf(stdout, "sbrk test failed %d %x %x\n", i, a, b); +      exit(); +    } +    *b = 1; +    a = b + 1; +  } +  pid = fork(); +  if(pid < 0){ +    printf(stdout, "sbrk test fork failed\n"); +    exit(); +  } +  c = sbrk(1); +  c = sbrk(1); +  if(c != a + 1){ +    printf(stdout, "sbrk test failed post-fork\n"); +    exit(); +  } +  if(pid == 0) +    exit(); +  wait(); + +  // can one grow address space to something big? +  a = sbrk(0); +  amt = BIG - (uint64)a; +  p = sbrk(amt); +  if (p != a) { +    printf(stdout, "sbrk test failed to grow big address space; enough phys mem?\n"); +    exit(); +  } +  lastaddr = (char*) (BIG-1); +  *lastaddr = 99; + +  // can one de-allocate? +  a = sbrk(0); +  c = sbrk(-4096); +  if(c == (char*)0xffffffffffffffffL){ +    printf(stdout, "sbrk could not deallocate\n"); +    exit(); +  } +  c = sbrk(0); +  if(c != a - 4096){ +    printf(stdout, "sbrk deallocation produced wrong address, a %x c %x\n", a, c); +    exit(); +  } + +  // can one re-allocate that page? +  a = sbrk(0); +  c = sbrk(4096); +  if(c != a || sbrk(0) != a + 4096){ +    printf(stdout, "sbrk re-allocation failed, a %x c %x\n", a, c); +    exit(); +  } +  if(*lastaddr == 99){ +    // should be zero +    printf(stdout, "sbrk de-allocation didn't really deallocate\n"); +    exit(); +  } + +  a = sbrk(0); +  c = sbrk(-(sbrk(0) - oldbrk)); +  if(c != a){ +    printf(stdout, "sbrk downsize failed, a %x c %x\n", a, c); +    exit(); +  } + +  // can we read the kernel's memory? +  for(a = (char*)(KERNBASE); a < (char*) (KERNBASE+2000000); a += 50000){ +    ppid = getpid(); +    pid = fork(); +    if(pid < 0){ +      printf(stdout, "fork failed\n"); +      exit(); +    } +    if(pid == 0){ +      printf(stdout, "oops could read %x = %x\n", a, *a); +      kill(ppid); +      exit(); +    } +    wait(); +  } +     +  // if we run the system out of memory, does it clean up the last +  // failed allocation? +  if(pipe(fds) != 0){ +    printf(1, "pipe() failed\n"); +    exit(); +  } +  for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ +    if((pids[i] = fork()) == 0){ +      // allocate a lot of memory +      sbrk(BIG - (uint64)sbrk(0)); +      write(fds[1], "x", 1); +      // sit around until killed +      for(;;) sleep(1000); +    } +    if(pids[i] != -1) +      read(fds[0], &scratch, 1); +  } + +  // if those failed allocations freed up the pages they did allocate, +  // we'll be able to allocate here +  c = sbrk(4096); +  for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ +    if(pids[i] == -1) +      continue; +    kill(pids[i]); +    wait(); +  } +  if(c == (char*)0xffffffffffffffffL){ +    printf(stdout, "failed sbrk leaked memory\n"); +    exit(); +  } + +  // test running fork with the above allocated page  +  ppid = getpid(); +  pid = fork(); +  if(pid < 0){ +    printf(stdout, "fork failed\n"); +    exit(); +  } + +  // test out of memory during sbrk +  if(pid == 0){ +    // allocate a lot of memory +    a = sbrk(0); +    sbrk(10*BIG); +    int n = 0; +    for (i = 0; i < 10*BIG; i += 4096) { +      n += *(a+i); +    } +    printf(stdout, "allocate a lot of memory succeeded %d\n", n); +    kill(ppid); +    exit(); +  } +  wait(); + +  // test reads from allocated memory +  a = sbrk(4096); +  fd = open("sbrk", O_CREATE|O_WRONLY); +  unlink("sbrk"); +  if(fd < 0)  { +    printf(stdout, "open sbrk failed\n"); +    exit(); +  } +  if ((n = write(fd, a, 10)) < 0) { +    printf(stdout, "write sbrk failed\n"); +    exit(); +  } +  close(fd); + +  // test writes to allocated memory +  a = sbrk(4096); +  if(pipe((int *) a) != 0){ +    printf(1, "pipe() failed\n"); +    exit(); +  }  + +  if(sbrk(0) > oldbrk) +    sbrk(-(sbrk(0) - oldbrk)); + +  printf(stdout, "sbrk test OK\n"); +} + +void +validateint(int *p) +{ +  /* XXX int res; +  asm("mov %%esp, %%ebx\n\t" +      "mov %3, %%esp\n\t" +      "int %2\n\t" +      "mov %%ebx, %%esp" : +      "=a" (res) : +      "a" (SYS_sleep), "n" (T_SYSCALL), "c" (p) : +      "ebx"); +  */ +} + +void +validatetest(void) +{ +  int hi, pid; +  uint64 p; + +  printf(stdout, "validate test\n"); +  hi = 1100*1024; + +  for(p = 0; p <= (uint)hi; p += 4096){ +    if((pid = fork()) == 0){ +      // try to crash the kernel by passing in a badly placed integer +      validateint((int*)p); +      exit(); +    } +    sleep(0); +    sleep(0); +    kill(pid); +    wait(); + +    // try to crash the kernel by passing in a bad string pointer +    if(link("nosuchfile", (char*)p) != -1){ +      printf(stdout, "link should not succeed\n"); +      exit(); +    } +  } + +  printf(stdout, "validate ok\n"); +} + +// does unintialized data start out zero? +char uninit[10000]; +void +bsstest(void) +{ +  int i; + +  printf(stdout, "bss test\n"); +  for(i = 0; i < sizeof(uninit); i++){ +    if(uninit[i] != '\0'){ +      printf(stdout, "bss test failed\n"); +      exit(); +    } +  } +  printf(stdout, "bss test ok\n"); +} + +// does exec return an error if the arguments +// are larger than a page? or does it write +// below the stack and wreck the instructions/data? +void +bigargtest(void) +{ +  int pid, fd; + +  unlink("bigarg-ok"); +  pid = fork(); +  if(pid == 0){ +    static char *args[MAXARG]; +    int i; +    for(i = 0; i < MAXARG-1; i++) +      args[i] = "bigargs test: failed\n                                                                                                                                                                                                       "; +    args[MAXARG-1] = 0; +    printf(stdout, "bigarg test\n"); +    exec("echo", args); +    printf(stdout, "bigarg test ok\n"); +    fd = open("bigarg-ok", O_CREATE); +    close(fd); +    exit(); +  } else if(pid < 0){ +    printf(stdout, "bigargtest: fork failed\n"); +    exit(); +  } +  wait(); +  fd = open("bigarg-ok", 0); +  if(fd < 0){ +    printf(stdout, "bigarg test failed!\n"); +    exit(); +  } +  close(fd); +  unlink("bigarg-ok"); +} + +// what happens when the file system runs out of blocks? +// answer: balloc panics, so this test is not useful. +void +fsfull() +{ +  int nfiles; +  int fsblocks = 0; + +  printf(1, "fsfull test\n"); + +  for(nfiles = 0; ; nfiles++){ +    char name[64]; +    name[0] = 'f'; +    name[1] = '0' + nfiles / 1000; +    name[2] = '0' + (nfiles % 1000) / 100; +    name[3] = '0' + (nfiles % 100) / 10; +    name[4] = '0' + (nfiles % 10); +    name[5] = '\0'; +    printf(1, "writing %s\n", name); +    int fd = open(name, O_CREATE|O_RDWR); +    if(fd < 0){ +      printf(1, "open %s failed\n", name); +      break; +    } +    int total = 0; +    while(1){ +      int cc = write(fd, buf, 512); +      if(cc < 512) +        break; +      total += cc; +      fsblocks++; +    } +    printf(1, "wrote %d bytes\n", total); +    close(fd); +    if(total == 0) +      break; +  } + +  while(nfiles >= 0){ +    char name[64]; +    name[0] = 'f'; +    name[1] = '0' + nfiles / 1000; +    name[2] = '0' + (nfiles % 1000) / 100; +    name[3] = '0' + (nfiles % 100) / 10; +    name[4] = '0' + (nfiles % 10); +    name[5] = '\0'; +    unlink(name); +    nfiles--; +  } + +  printf(1, "fsfull test finished\n"); +} + +void argptest() +{ +  int fd; +  fd = open("init", O_RDONLY); +  if (fd < 0) { +    printf(2, "open failed\n"); +    exit(); +  } +  read(fd, sbrk(0) - 1, -1); +  close(fd); +  printf(1, "arg test passed\n"); +} + +unsigned long randstate = 1; +unsigned int +rand() +{ +  randstate = randstate * 1664525 + 1013904223; +  return randstate; +} + +// check that there's an invalid page beneath +// the user stack, to catch stack overflow. +void +stacktest() +{ +  int pid; +  int ppid = getpid(); +   +  printf(1, "stack guard test\n"); +  pid = fork(); +  if(pid == 0) { +    char *sp = (char *) r_sp(); +    sp -= 4096; +    // the *sp should cause a trap. +    printf(1, "stacktest: read below stack %p\n", *sp); +    printf(1, "stacktest: test FAILED\n"); +    kill(ppid); +    exit(); +  } else if(pid < 0){ +    printf (1, "fork failed\n"); +    exit(); +  } +  wait(); +  printf(1, "stack guard test ok\n"); +} + +int +main(int argc, char *argv[]) +{ +  printf(1, "usertests starting\n"); + +  if(open("usertests.ran", 0) >= 0){ +    printf(1, "already ran user tests -- rebuild fs.img\n"); +    exit(); +  } +  close(open("usertests.ran", O_CREATE)); + +  reparent(); +  twochildren(); +  forkfork(); +  forkforkfork(); +   +  argptest(); +  createdelete(); +  linkunlink(); +  concreate(); +  fourfiles(); +  sharedfd(); + +  bigargtest(); +  bigwrite(); +  bigargtest(); +  bsstest(); +  sbrktest(); +  validatetest(); +  stacktest(); +   +  opentest(); +  writetest(); +  writetest1(); +  createtest(); + +  openiputtest(); +  exitiputtest(); +  iputtest(); + +  mem(); +  pipe1(); +  preempt(); +  exitwait(); + +  rmdot(); +  fourteen(); +  bigfile(); +  subdir(); +  linktest(); +  unlinkread(); +  dirfile(); +  iref(); +  forktest(); +  bigdir(); // slow + +  exectest(); + +  exit(); +} diff --git a/user/usys.pl b/user/usys.pl new file mode 100755 index 0000000..01e426e --- /dev/null +++ b/user/usys.pl @@ -0,0 +1,38 @@ +#!/usr/bin/perl -w + +# Generate usys.S, the stubs for syscalls. + +print "# generated by usys.pl - do not edit\n"; + +print "#include \"kernel/syscall.h\"\n"; + +sub entry { +    my $name = shift; +    print ".global $name\n"; +    print "${name}:\n"; +    print " li a7, SYS_${name}\n"; +    print " ecall\n"; +    print " ret\n"; +} +	 +entry("fork"); +entry("exit"); +entry("wait"); +entry("pipe"); +entry("read"); +entry("write"); +entry("close"); +entry("kill"); +entry("exec"); +entry("open"); +entry("mknod"); +entry("unlink"); +entry("fstat"); +entry("link"); +entry("mkdir"); +entry("chdir"); +entry("dup"); +entry("getpid"); +entry("sbrk"); +entry("sleep"); +entry("uptime"); diff --git a/user/wc.c b/user/wc.c new file mode 100644 index 0000000..e2543a6 --- /dev/null +++ b/user/wc.c @@ -0,0 +1,54 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +char buf[512]; + +void +wc(int fd, char *name) +{ +  int i, n; +  int l, w, c, inword; + +  l = w = c = 0; +  inword = 0; +  while((n = read(fd, buf, sizeof(buf))) > 0){ +    for(i=0; i<n; i++){ +      c++; +      if(buf[i] == '\n') +        l++; +      if(strchr(" \r\t\n\v", buf[i])) +        inword = 0; +      else if(!inword){ +        w++; +        inword = 1; +      } +    } +  } +  if(n < 0){ +    printf(1, "wc: read error\n"); +    exit(); +  } +  printf(1, "%d %d %d %s\n", l, w, c, name); +} + +int +main(int argc, char *argv[]) +{ +  int fd, i; + +  if(argc <= 1){ +    wc(0, ""); +    exit(); +  } + +  for(i = 1; i < argc; i++){ +    if((fd = open(argv[i], 0)) < 0){ +      printf(1, "wc: cannot open %s\n", argv[i]); +      exit(); +    } +    wc(fd, argv[i]); +    close(fd); +  } +  exit(); +} diff --git a/user/zombie.c b/user/zombie.c new file mode 100644 index 0000000..b56231a --- /dev/null +++ b/user/zombie.c @@ -0,0 +1,14 @@ +// Create a zombie process that +// must be reparented at exit. + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(void) +{ +  if(fork() > 0) +    sleep(5);  // Let child exit before parent. +  exit(); +} | 
