From f3979b7212f1bb9d72947f54abead5be973c6aed Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Thu, 16 Jul 2020 11:38:08 -0400 Subject: [PATCH] make "echo hello > x" truncate file x. --- kernel/defs.h | 1 + kernel/fcntl.h | 1 + kernel/fs.c | 10 ++-- kernel/sysfile.c | 4 ++ user/sh.c | 2 +- user/usertests.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+), 8 deletions(-) diff --git a/kernel/defs.h b/kernel/defs.h index 9c5f643..f33f1f6 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -52,6 +52,7 @@ struct inode* nameiparent(char*, char*); int readi(struct inode*, int, uint64, uint, uint); void stati(struct inode*, struct stat*); int writei(struct inode*, int, uint64, uint, uint); +void itrunc(struct inode*); // ramdisk.c void ramdiskinit(void); diff --git a/kernel/fcntl.h b/kernel/fcntl.h index d565483..44861b9 100644 --- a/kernel/fcntl.h +++ b/kernel/fcntl.h @@ -2,3 +2,4 @@ #define O_WRONLY 0x001 #define O_RDWR 0x002 #define O_CREATE 0x200 +#define O_TRUNC 0x400 diff --git a/kernel/fs.c b/kernel/fs.c index 53586d5..e33ec30 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -22,7 +22,6 @@ #include "file.h" #define min(a, b) ((a) < (b) ? (a) : (b)) -static void itrunc(struct inode*); // there should be one superblock per disk device, but we run with // only one device struct superblock sb; @@ -406,11 +405,8 @@ bmap(struct inode *ip, uint bn) } // Truncate inode (discard contents). -// Only called when the inode has no links -// to it (no directory entries referring to it) -// and has no in-memory reference to it (is -// not an open file or current directory). -static void +// Caller must hold ip->lock. +void itrunc(struct inode *ip) { int i, j; @@ -463,7 +459,7 @@ readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n) struct buf *bp; if(off > ip->size || off + n < off) - return -1; + return 0; if(off + n > ip->size) n = ip->size - off; diff --git a/kernel/sysfile.c b/kernel/sysfile.c index 7768d20..015c942 100644 --- a/kernel/sysfile.c +++ b/kernel/sysfile.c @@ -341,6 +341,10 @@ sys_open(void) f->readable = !(omode & O_WRONLY); f->writable = (omode & O_WRONLY) || (omode & O_RDWR); + if((omode & O_TRUNC) && ip->type == T_FILE){ + itrunc(ip); + } + iunlock(ip); end_op(); diff --git a/user/sh.c b/user/sh.c index a593bc0..83dd513 100644 --- a/user/sh.c +++ b/user/sh.c @@ -386,7 +386,7 @@ parseredirs(struct cmd *cmd, char **ps, char *es) cmd = redircmd(cmd, q, eq, O_RDONLY, 0); break; case '>': - cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); + cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE|O_TRUNC, 1); break; case '+': // >> cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1); diff --git a/user/usertests.c b/user/usertests.c index 9aa0ed4..f83a060 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -22,6 +22,141 @@ char buf[BUFSZ]; char name[3]; +// 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) @@ -2169,6 +2304,9 @@ main(int argc, char *argv[]) void (*f)(char *); char *s; } tests[] = { + {truncate1, "truncate1"}, + {truncate2, "truncate2"}, + {truncate3, "truncate3"}, {reparent2, "reparent2"}, {pgbug, "pgbug" }, {sbrkbugs, "sbrkbugs" },