#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"
//
// Tests xv6 system calls. usertests without arguments runs them all
// and usertests <name> runs <name> test. The test runner creates for
// each test a process and based on the exit status of the process,
// the test runner reports "OK" or "FAILED". Some tests result in
// kernel printing usertrap messages, which can be ignored if test
// prints "OK".
//
#define BUFSZ (MAXOPBLOCKS+2)*BSIZE
char buf[BUFSZ];
char name[3];
// what if you pass ridiculous pointers to system calls
// that read user memory with copyin?
void
copyin(char *s)
{
uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff };
for(int ai = 0; ai < 2; ai++){
uint64 addr = addrs[ai];
int fd = open("copyin1", O_CREATE|O_WRONLY);
if(fd < 0){
printf("open(copyin1) failed\n");
exit(1);
}
int n = write(fd, (void*)addr, 8192);
if(n >= 0){
printf("write(fd, %p, 8192) returned %d, not -1\n", addr, n);
exit(1);
}
close(fd);
unlink("copyin1");
n = write(1, (char*)addr, 8192);
if(n > 0){
printf("write(1, %p, 8192) returned %d, not -1 or 0\n", addr, n);
exit(1);
}
int fds[2];
if(pipe(fds) < 0){
printf("pipe() failed\n");
exit(1);
}
n = write(fds[1], (char*)addr, 8192);
if(n > 0){
printf("write(pipe, %p, 8192) returned %d, not -1 or 0\n", addr, n);
exit(1);
}
close(fds[0]);
close(fds[1]);
}
}
// what if you pass ridiculous pointers to system calls
// that write user memory with copyout?
void
copyout(char *s)
{
uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff };
for(int ai = 0; ai < 2; ai++){
uint64 addr = addrs[ai];
int fd = open("README", 0);
if(fd < 0){
printf("open(README) failed\n");
exit(1);
}
int n = read(fd, (void*)addr, 8192);
if(n > 0){
printf("read(fd, %p, 8192) returned %d, not -1 or 0\n", addr, n);
exit(1);
}
close(fd);
int fds[2];
if(pipe(fds) < 0){
printf("pipe() failed\n");
exit(1);
}
n = write(fds[1], "x", 1);
if(n != 1){
printf("pipe write failed\n");
exit(1);
}
n = read(fds[0], (void*)addr, 8192);
if(n > 0){
printf("read(pipe, %p, 8192) returned %d, not -1 or 0\n", addr, n);
exit(1);
}
close(fds[0]);
close(fds[1]);
}
}
// what if you pass ridiculous string pointers to system calls?
void
copyinstr1(char *s)
{
uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff };
for(int ai = 0; ai < 2; ai++){
uint64 addr = addrs[ai];
int fd = open((char *)addr, O_CREATE|O_WRONLY);
if(fd >= 0){
printf("open(%p) returned %d, not -1\n", addr, fd);
exit(1);
}
}
}
// what if a string system call argument is exactly the size
// of the kernel buffer it is copied into, so that the null
// would fall just beyond the end of the kernel buffer?
void
copyinstr2(char *s)
{
char b[MAXPATH+1];
for(int i = 0; i < MAXPATH; i++)
b[i] = 'x';
b[MAXPATH] = '\0';
int ret = unlink(b);
if(ret != -1){
printf("unlink(%s) returned %d, not -1\n", b, ret);
exit(1);
}
int fd = open(b, O_CREATE | O_WRONLY);
if(fd != -1){
printf("open(%s) returned %d, not -1\n", b, fd);
exit(1);
}
ret = link(b, b);
if(ret != -1){
printf("link(%s, %s) returned %d, not -1\n", b, b, ret);
exit(1);
}
char *args[] = { "xx", 0 };
ret = exec(b, args);
if(ret != -1){
printf("exec(%s) returned %d, not -1\n", b, fd);
exit(1);
}
int pid = fork();
if(pid < 0){
printf("fork failed\n");
exit(1);
}
if(pid == 0){
static char big[PGSIZE+1];
for(int i = 0; i < PGSIZE; i++)
big[i] = 'x';
big[PGSIZE] = '\0';
char *args2[] = { big, big, big, 0 };
ret = exec("echo", args2);
if(ret != -1){
printf("exec(echo, BIG) returned %d, not -1\n", fd);
exit(1);
}
exit(747); // OK
}
int st = 0;
wait(&st);
if(st != 747){
printf("exec(echo, BIG) succeeded, should have failed\n");
exit(1);
}
}
// what if a string argument crosses over the end of last user page?
void
copyinstr3(char *s)
{
sbrk(8192);
uint64 top = (uint64) sbrk(0);
if((top % PGSIZE) != 0){
sbrk(PGSIZE - (top % PGSIZE));
}
top = (uint64) sbrk(0);
if(top % PGSIZE){
printf("oops\n");
exit(1);
}
char *b = (char *) (top - 1);
*b = 'x';
int ret = unlink(b);
if(ret != -1){
printf("unlink(%s) returned %d, not -1\n", b, ret);
exit(1);
}
int fd = open(b, O_CREATE | O_WRONLY);
if(fd != -1){
printf("open(%s) returned %d, not -1\n", b, fd);
exit(1);
}
ret = link(b, b);
if(ret != -1){
printf("link(%s, %s) returned %d, not -1\n", b, b, ret);
exit(1);
}
char *args[] = { "xx", 0 };
ret = exec(b, args);
if(ret != -1){
printf("exec(%s) returned %d, not -1\n", b, fd);
exit(1);
}
}
// test O_TRUNC.
void
truncate1(char *s)
{
char buf[32];
unlink("truncfile");
int fd1 = open("truncfile", O_CREATE|O_WRONLY|O_TRUNC);
write(fd1, "abcd", 4);
close(fd1);
int fd2 = open("truncfile", O_RDONLY);
int n = read(fd2, buf, sizeof(buf));
if(n != 4){
printf("%s: read %d bytes, wanted 4\n", s, n);
exit(1);
}
fd1 = open("truncfile", O_WRONLY|O_TRUNC);
int fd3 = open("truncfile", O_RDONLY);
n = read(fd3, buf, sizeof(buf));
if(n != 0){
printf("aaa fd3=%d\n", fd3);
printf("%s: read %d bytes, wanted 0\n", s, n);
exit(1);
}
n = read(fd2, buf, sizeof(buf));
if(n != 0){
printf("bbb fd2=%d\n", fd2);
printf("%s: read %d bytes, wanted 0\n", s, n);
exit(1);
}
write(fd1, "abcdef", 6);
n = read(fd3, buf, sizeof(buf));
if(n != 6){
printf("%s: read %d bytes, wanted 6\n", s, n);
exit(1);
}
n = read(fd2, buf, sizeof(buf));
if(n != 2){
printf("%s: read %d bytes, wanted 2\n", s, n);
exit(1);
}
unlink("truncfile");
close(fd1);
close(fd2);
close(fd3);
}
// write to an open FD whose file has just been truncated.
// this causes a write at an offset beyond the end of the file.
// such writes fail on xv6 (unlike POSIX) but at least
// they don't crash.
void
truncate2(char *s)
{
unlink("truncfile");
int fd1 = open("truncfile", O_CREATE|O_TRUNC|O_WRONLY);
write(fd1, "abcd", 4);
int fd2 = open("truncfile", O_TRUNC|O_WRONLY);
int n = write(fd1, "x", 1);
if(n != -1){
printf("%s: write returned %d, expected -1\n", s, n);
exit(1);
}
unlink("truncfile");
close(fd1);
close(fd2);
}
void
truncate3(char *s)
{
int pid, xstatus;
close(open("truncfile", O_CREATE|O_TRUNC|O_WRONLY));
pid = fork();
if(pid < 0){
printf("%s: fork failed\n", s);
exit(1);
}
if(pid == 0){
for(int i = 0; i < 100; i++){
char buf[32];
int fd = open("truncfile", O_WRONLY);
if(fd < 0){
printf("%s: open failed\n", s);
exit(1);
}
int n = write(fd, "1234567890", 10);
if(n != 10){
printf("%s: write got %d, expected 10\n", s, n);
exit(1);
}
close(fd);
fd = open("truncfile", O_RDONLY);
read(fd, buf, sizeof(buf));
close(fd);
}
exit(0);
}
for(int i = 0; i < 150; i++){
int fd = open("truncfile", O_CREATE|O_WRONLY|O_TRUNC);
if(fd < 0){
printf("%s: open failed\n", s);
exit(1);
}
int n = write(fd, "xxx", 3);
if(n != 3){
printf("%s: write got %d, expected 3\n", s, n);
exit(1);
}
close(fd);
}
wait(&xstatus);
unlink("truncfile");
exit(xstatus);
}
// does chdir() call iput(p->cwd) in a transaction?
void
iputtest(char *s)
{
if(mkdir("iputdir") < 0){
printf("%s: mkdir failed\n", s);
exit(1);
}
if(chdir("iputdir") < 0){
printf("%s: chdir iputdir fa