summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Morris <[email protected]>2020-07-16 11:38:08 -0400
committerFrans Kaashoek <[email protected]>2020-08-10 11:19:10 -0400
commitaf9eb9114c2f8700d4315eaa1e2d637c2aaaf210 (patch)
tree9e2e5cc663d2a3d56358121761755a4d3e878d6b
parent672217ae2a3d68b73b2229ab368979ef7790e28a (diff)
downloadxv6-labs-af9eb9114c2f8700d4315eaa1e2d637c2aaaf210.tar.gz
xv6-labs-af9eb9114c2f8700d4315eaa1e2d637c2aaaf210.tar.bz2
xv6-labs-af9eb9114c2f8700d4315eaa1e2d637c2aaaf210.zip
make "echo hello > x" truncate file x.
-rw-r--r--kernel/defs.h1
-rw-r--r--kernel/fcntl.h1
-rw-r--r--kernel/fs.c10
-rw-r--r--kernel/sysfile.c4
-rw-r--r--user/sh.c2
-rw-r--r--user/usertests.c138
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" },