Compare commits

..

7 Commits
util ... fs

Author SHA1 Message Date
ridethepig
4368fbf44c make grade 100/100 2023-02-05 17:35:55 +00:00
ridethepig
f187a07854 bigfile ok 2023-02-05 15:23:55 +00:00
Frans Kaashoek
888b75593b Increase timeouts 2022-11-28 20:58:24 -05:00
Frans Kaashoek
c084dbbffb fs lab 2022-11-14 07:30:00 -05:00
Frans Kaashoek
4b46c0c6eb Use O_RDONLY instead of 0 2022-09-11 13:51:11 -04:00
Robert Morris
463ae0abc3 handle negative arguments to sleep() a little better 2022-09-09 09:17:37 -04:00
Frans Kaashoek
f5b93ef12f Update acks
61810
2022-08-25 14:20:52 -04:00
30 changed files with 466 additions and 360 deletions

View File

@ -106,7 +106,7 @@ endif
ifdef KCSAN
CFLAGS += -DKCSAN
KCSANFLAG = -fsanitize=thread
KCSANFLAG = -fsanitize=thread -fno-inline
endif
# Disable PIE when possible (for Ubuntu 16.10 toolchain)
@ -188,11 +188,7 @@ UPROGS=\
$U/_grind\
$U/_wc\
$U/_zombie\
$U/_sleep\
$U/_pingpong\
$U/_primes\
$U/_find\
$U/_xargs\
$U/_symlinktest\

36
README
View File

@ -6,7 +6,7 @@ ACKNOWLEDGMENTS
xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer
to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14,
2000)). See also https://pdos.csail.mit.edu/6.828/, which provides
2000)). See also https://pdos.csail.mit.edu/6.1810/, which provides
pointers to on-line resources for v6.
The following people have made contributions: Russ Cox (context switching,
@ -14,29 +14,31 @@ locking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin
Clements.
We are also grateful for the bug reports and patches contributed by
Takahiro Aoyagi, Silas Boyd-Wickizer, Anton Burtsev, Ian Chen, Dan
Cross, Cody Cutler, Mike CAT, Tej Chajed, Asami Doi, eyalz800, Nelson
Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel Filardo, flespark,
Peter Froehlich, Yakir Goaron, Shivam Handa, Matt Harvey, Bryan Henry,
jaichenhengjie, Jim Huang, Matúš Jókay, Alexander Kapshuk, Anders
Kaseorg, kehao95, Wolfgang Keller, Jungwoo Kim, Jonathan Kimmitt,
Eddie Kohler, Vadim Kolontsov, Austin Liew, l0stman, Pavan
Maddamsetti, Imbar Marinescu, Yandong Mao, Matan Shabtay, Hitoshi
Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel Nider,
OptimisticSide, Greg Price, Jude Rich, Ayan Shafqat, Eldar Sehayek,
Yongming Shen, Fumiya Shigemitsu, Cam Tenny, tyfkda, Warren Toomey,
Stephen Tu, Rafael Ubal, Amane Uehara, Pablo Ventura, Xi Wang, Keiichi
Watanabe, Nicolas Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy
Zheng, ZhUyU1997, and Zou Chang Wei.
Takahiro Aoyagi, Silas Boyd-Wickizer, Anton Burtsev, carlclone, Ian
Chen, Dan Cross, Cody Cutler, Mike CAT, Tej Chajed, Asami Doi,
eyalz800, Nelson Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel
Filardo, flespark, Peter Froehlich, Yakir Goaron, Shivam Handa, Matt
Harvey, Bryan Henry, jaichenhengjie, Jim Huang, Matúš Jókay, John
Jolly, Alexander Kapshuk, Anders Kaseorg, kehao95, Wolfgang Keller,
Jungwoo Kim, Jonathan Kimmitt, Eddie Kohler, Vadim Kolontsov, Austin
Liew, l0stman, Pavan Maddamsetti, Imbar Marinescu, Yandong Mao, Matan
Shabtay, Hitoshi Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel
Nider, Hayato Ohhashi, OptimisticSide, Harry Porter, Greg Price, Jude
Rich, segfault, Ayan Shafqat, Eldar Sehayek, Yongming Shen, Fumiya
Shigemitsu, Cam Tenny, tyfkda, Warren Toomey, Stephen Tu, Rafael Ubal,
Amane Uehara, Pablo Ventura, Xi Wang, WaheedHafez, Keiichi Watanabe,
Nicolas Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng,
ZhUyU1997, and Zou Chang Wei.
The code in the files that constitute xv6 is
Copyright 2006-2020 Frans Kaashoek, Robert Morris, and Russ Cox.
Copyright 2006-2022 Frans Kaashoek, Robert Morris, and Russ Cox.
ERROR REPORTS
Please send errors and suggestions to Frans Kaashoek and Robert Morris
(kaashoek,rtm@mit.edu). The main purpose of xv6 is as a teaching
operating system for MIT's 6.S081, so we are more interested in
operating system for MIT's 6.1810, so we are more interested in
simplifications and clarifications than new features.
BUILDING AND RUNNING XV6

View File

@ -1 +1 @@
LAB=util
LAB=fs

41
grade-lab-fs Executable file
View File

@ -0,0 +1,41 @@
#!/usr/bin/env python3
import re
from gradelib import *
r = Runner(save("xv6.out"))
@test(40, "running bigfile")
def test_bigfile():
r.run_qemu(shell_script([
'bigfile'
]), timeout=400)
r.match('^wrote 65803 blocks$')
r.match('^bigfile done; ok$')
@test(0, "running symlinktest")
def test_symlinktest():
r.run_qemu(shell_script([
'symlinktest'
]), timeout=20)
@test(20, "symlinktest: symlinks", parent=test_symlinktest)
def test_symlinktest_symlinks():
r.match("^test symlinks: ok$")
@test(20, "symlinktest: concurrent symlinks", parent=test_symlinktest)
def test_symlinktest_symlinks():
r.match("^test concurrent symlinks: ok$")
@test(19, "usertests")
def test_usertests():
r.run_qemu(shell_script([
'usertests -q'
]), timeout=600)
r.match('^ALL TESTS PASSED$')
@test(1, "time")
def test_time():
check_time()
run_tests()

View File

@ -1,86 +0,0 @@
#!/usr/bin/env python3
import re
from gradelib import *
r = Runner(save("xv6.out"))
@test(5, "sleep, no arguments")
def test_sleep_no_args():
r.run_qemu(shell_script([
'sleep'
]))
r.match(no=["exec .* failed", "$ sleep\n$"])
@test(5, "sleep, returns")
def test_sleep_no_args():
r.run_qemu(shell_script([
'sleep',
'echo OK'
]))
r.match('^OK$', no=["exec .* failed", "$ sleep\n$"])
@test(10, "sleep, makes syscall")
def test_sleep():
r.run_qemu(shell_script([
'sleep 10',
'echo FAIL'
]), stop_breakpoint('sys_sleep'))
r.match('\\$ sleep 10', no=['FAIL'])
@test(20, "pingpong")
def test_pingpong():
r.run_qemu(shell_script([
'pingpong', 'echo OK'
]))
r.match('^\\d+: received ping$', '^\\d+: received pong$', '^OK$')
@test(20, "primes")
def test_primes():
r.run_qemu(shell_script([
'primes', 'echo OK'
]))
args = ['prime %d' % i for i in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]]
args.append('^OK$')
r.match(*args)
@test(10, "find, in current directory")
def test_find_curdir():
fn = random_str()
r.run_qemu(shell_script([
'echo > %s' % fn,
'find . %s' % fn
]))
r.match('./%s' % fn)
@test(10, "find, recursive")
def test_find_recursive():
needle = random_str()
dirs = [random_str() for _ in range(3)]
r.run_qemu(shell_script([
'mkdir %s' % dirs[0],
'echo > %s/%s' % (dirs[0], needle),
'mkdir %s/%s' % (dirs[0], dirs[1]),
'echo > %s/%s/%s' % (dirs[0], dirs[1], needle),
'mkdir %s' % dirs[2],
'echo > %s/%s' % (dirs[2], needle),
'find . %s' % needle
]))
r.match('./%s/%s' % (dirs[0], needle),
'./%s/%s/%s' % (dirs[0], dirs[1], needle),
'./%s/%s' % (dirs[2], needle))
@test(19, "xargs")
def test_xargs():
r.run_qemu(shell_script([
'sh < xargstest.sh',
'echo DONE',
], 'DONE'))
matches = re.findall("hello", r.qemu.output)
assert_equal(len(matches), 3, "Number of appearances of 'hello'")
@test(1, "time")
def test_time():
check_time()
run_tests()

View File

@ -3,3 +3,4 @@
#define O_RDWR 0x002
#define O_CREATE 0x200
#define O_TRUNC 0x400
#define O_NOFOLLOW 0x800

View File

@ -26,7 +26,7 @@ struct inode {
short minor;
short nlink;
uint size;
uint addrs[NDIRECT+1];
uint addrs[NDIRECT+2];
};
// map major device number to device functions.

View File

@ -416,7 +416,43 @@ bmap(struct inode *ip, uint bn)
brelse(bp);
return addr;
}
bn -= NINDIRECT;
if (bn < NINDIRECT2) {
// Load doubly indirect block L1
if ((addr = ip->addrs[NDIRECT+1]) == 0){
addr = balloc(ip->dev);
if(addr == 0)
return 0;
ip->addrs[NDIRECT+1] = addr;
}
// load doubly indirect block L2
// we have NINDIRECT blocks for per L1 entry
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if ((addr = a[bn / NINDIRECT]) == 0) {
addr = balloc(ip->dev);
if (addr == 0) {
brelse(bp);
return 0;
}
a[bn / NINDIRECT] = addr;
log_write(bp);
}
brelse(bp);
// Load finally the target block
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if ((addr = a[bn % NINDIRECT]) == 0) {
addr = balloc(ip->dev);
if (addr) {
a[bn % NINDIRECT] = addr;
log_write(bp);
}
}
brelse(bp);
return addr;
}
printf("bn=%d\n", bn);
panic("bmap: out of range");
}
@ -425,9 +461,9 @@ bmap(struct inode *ip, uint bn)
void
itrunc(struct inode *ip)
{
int i, j;
struct buf *bp;
uint *a;
int i, j, k;
struct buf *bp, *bp1;
uint *a, *a1;
for(i = 0; i < NDIRECT; i++){
if(ip->addrs[i]){
@ -448,6 +484,26 @@ itrunc(struct inode *ip)
ip->addrs[NDIRECT] = 0;
}
if (ip->addrs[NDIRECT+1]) {
bp = bread(ip->dev, ip->addrs[NDIRECT+1]);
a = (uint*)bp->data;
for(j = 0; j < NINDIRECT; j++){
if(a[j]) {
bp1 = bread(ip->dev, a[j]);
a1 = (uint*)bp1->data;
for (k = 0; k < NINDIRECT; ++ k) {
if (a1[k]) {
bfree(ip->dev, a1[k]);
}
}
brelse(bp1);
bfree(ip->dev, a[j]);
}
}
brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT+1]);
ip->addrs[NDIRECT+1] = 0;
}
ip->size = 0;
iupdate(ip);
}

View File

@ -24,9 +24,10 @@ struct superblock {
#define FSMAGIC 0x10203040
#define NDIRECT 12
#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT)
#define NINDIRECT2 (NINDIRECT * NINDIRECT)
#define MAXFILE (NDIRECT + NINDIRECT + NINDIRECT2)
// On-disk inode structure
struct dinode {
@ -35,7 +36,7 @@ struct dinode {
short minor; // Minor device number (T_DEVICE only)
short nlink; // Number of links to inode in file system
uint size; // Size of file (bytes)
uint addrs[NDIRECT+1]; // Data block addresses
uint addrs[NDIRECT+2]; // Data block addresses
};
// Inodes per block.

View File

@ -1,4 +1,8 @@
#define NPROC 64 // maximum number of processes
#ifdef LAB_FS
#define NPROC 10 // maximum number of processes
#else
#define NPROC 64 // maximum number of processes (speedsup bigfile)
#endif
#define NCPU 8 // maximum number of CPUs
#define NOFILE 16 // open files per process
#define NFILE 100 // open files per system
@ -9,5 +13,15 @@
#define MAXOPBLOCKS 10 // max # of blocks any FS op writes
#define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log
#define NBUF (MAXOPBLOCKS*3) // size of disk block cache
#define FSSIZE 2000 // size of file system in blocks
#ifdef LAB_FS
#define FSSIZE 200000 // size of file system in blocks
#else
#ifdef LAB_LOCK
#define FSSIZE 10000 // size of file system in blocks
#else
#define FSSIZE 2000 // size of file system in blocks
#endif
#endif
#define MAXPATH 128 // maximum file path name

View File

@ -1,7 +1,7 @@
#define T_DIR 1 // Directory
#define T_FILE 2 // File
#define T_DEVICE 3 // Device
#define T_SYMLINK 4
struct stat {
int dev; // File system's disk device
uint ino; // Inode number

View File

@ -101,6 +101,7 @@ extern uint64 sys_unlink(void);
extern uint64 sys_link(void);
extern uint64 sys_mkdir(void);
extern uint64 sys_close(void);
extern uint64 sys_symlink(void);
// An array mapping syscall numbers from syscall.h
// to the function that handles the system call.
@ -126,6 +127,7 @@ static uint64 (*syscalls[])(void) = {
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
[SYS_symlink] sys_symlink,
};
void

View File

@ -20,3 +20,4 @@
#define SYS_link 19
#define SYS_mkdir 20
#define SYS_close 21
#define SYS_symlink 22

View File

@ -335,6 +335,36 @@ sys_open(void)
}
}
if (ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)) {
int symlink_step = 0;
while (ip->type == T_SYMLINK) {
if (symlink_step++ > 10) {
// printf("\x1b[031mFailed\x1b[0m due to deep symlink\n");
iunlockput(ip); // on error, put inode 'cause only success open need to use the file after the syscall
end_op();
return -1;
}
int len_target = readi(ip, 0, (uint64)path, 0, MAXPATH);
iunlockput(ip); // no more need to use this inode
if (len_target == 0) {
printf("\x1b[031mFailed\x1b[0m to read symlink %s\n", path);
end_op();
return -1;
}
// get target's inode
if((ip = namei(path)) == 0){
// printf("\x1b[031mFailed\x1b[0m open symlink target %s\n", path);
end_op();
return -1;
}
ilock(ip);
if(ip->type == T_DIR && omode != O_RDONLY){
iunlockput(ip);
end_op();
return -1;
}
}
}
if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
iunlockput(ip);
end_op();
@ -503,3 +533,32 @@ sys_pipe(void)
}
return 0;
}
uint64
sys_symlink(void)
{
char target[MAXPATH], path[MAXPATH];
int len_target, len_path;
struct inode* ip;
if ((len_target = argstr(0, target, MAXPATH)) < 0)
return -1;
if ((len_path = argstr(1, path, MAXPATH)) < 0)
return -1;
// printf("\x1b[033msys_symlink\x1b[0m %s -> %s\n", path, target);
begin_op();
ip = create(path, T_SYMLINK, 0, 0);
if (ip == 0) {
end_op();
// printf("\x1b[031mFailed\x1b[0m to create path %s\n", path);
return -1;
}
if (writei(ip, 0, (uint64)target, 0, len_target) != len_target) {
iunlockput(ip);
end_op();
printf("\x1b[031mFailed\x1b[0m to write symlink target %s to %s\n", target, path);
return -1;
}
iunlockput(ip); // no need to keep this inode ref, if not put, in-RAM inode pool will run out
end_op();
return 0;
}

View File

@ -55,6 +55,8 @@ sys_sleep(void)
uint ticks0;
argint(0, &n);
if(n < 0)
n = 0;
acquire(&tickslock);
ticks0 = ticks;
while(ticks - ticks0 < n){

View File

@ -1 +1 @@
6
7

57
user/bigfile.c Normal file
View File

@ -0,0 +1,57 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fcntl.h"
#include "kernel/fs.h"
int
main()
{
char buf[BSIZE];
int fd, i, blocks;
fd = open("big.file", O_CREATE | O_WRONLY);
if(fd < 0){
printf("bigfile: cannot open big.file for writing\n");
exit(-1);
}
blocks = 0;
while(1){
*(int*)buf = blocks;
int cc = write(fd, buf, sizeof(buf));
if(cc <= 0)
break;
blocks++;
if (blocks % 100 == 0)
printf(".");
}
printf("\nwrote %d blocks\n", blocks);
if(blocks != 65803) {
printf("bigfile: file is too small\n");
exit(-1);
}
close(fd);
fd = open("big.file", O_RDONLY);
if(fd < 0){
printf("bigfile: cannot re-open big.file for reading\n");
exit(-1);
}
for(i = 0; i < blocks; i++){
int cc = read(fd, buf, sizeof(buf));
if(cc <= 0){
printf("bigfile: read error at block %d\n", i);
exit(-1);
}
if(*(int*)buf != i){
printf("bigfile: read the wrong data (%d) for block %d\n",
*(int*)buf, i);
exit(-1);
}
}
printf("bigfile done; ok\n");
exit(0);
}

View File

@ -1,5 +1,6 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h"
char buf[512];
@ -32,7 +33,7 @@ main(int argc, char *argv[])
}
for(i = 1; i < argc; i++){
if((fd = open(argv[i], 0)) < 0){
if((fd = open(argv[i], O_RDONLY)) < 0){
fprintf(2, "cat: cannot open %s\n", argv[i]);
exit(1);
}

View File

@ -1,74 +0,0 @@
#include "kernel/types.h"
#include "user/user.h"
#include "kernel/stat.h"
#include "kernel/fs.h"
int fmtname(const char *path) {
int p;
for(p=strlen(path); p >= 0 && *(path + p) != '/'; p--);
p++;
return p;
}
void find(const char *path, const char* target) {
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
if((fd = open(path, 0)) < 0){
fprintf(2, "find: cannot open %s\n", path);
return;
}
if(fstat(fd, &st) < 0){
fprintf(2, "find: cannot stat %s\n", path);
close(fd);
return;
}
switch(st.type){
case T_DEVICE:
case T_FILE:
if (strcmp(path + fmtname(path), target) == 0) {
printf("%s\n", path);
}
break;
case T_DIR:
if (strcmp(path + fmtname(path), target) == 0) {
printf("%s\n", path);
}
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("find: 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;
if (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0)
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if(stat(buf, &st) < 0){
printf("find: cannot stat %s\n", buf);
continue;
}
find(buf, target);
}
break;
}
close(fd);
}
int main(int argc, char** argv) {
if (argc != 3) {
printf("\x1b[31mInvalid arguments\x1b[0m\n");
printf("\x1b[32musage\x1b[0m: find <base directory> <file name to find>\n");
exit(0);
}
find(argv[1], argv[2]);
exit(0);
}

View File

@ -2,6 +2,7 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h"
char buf[1024];
@ -51,7 +52,7 @@ main(int argc, char *argv[])
}
for(i = 2; i < argc; i++){
if((fd = open(argv[i], 0)) < 0){
if((fd = open(argv[i], O_RDONLY)) < 0){
printf("grep: cannot open %s\n", argv[i]);
exit(1);
}

View File

@ -2,6 +2,7 @@
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
#include "kernel/fcntl.h"
char*
fmtname(char *path)
@ -30,7 +31,7 @@ ls(char *path)
struct dirent de;
struct stat st;
if((fd = open(path, 0)) < 0){
if((fd = open(path, O_RDONLY)) < 0){
fprintf(2, "ls: cannot open %s\n", path);
return;
}

View File

@ -1,27 +0,0 @@
#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char** argv) {
int p[2]; // hell, read from p[0] and write to p[1] no matter in pa/ch
if (pipe(p) == -1) exit(1);
uint8 senddata[] = {0xAC};
uint8 recvdata[] = {0};
int pid = fork();
if (pid == 0) {
// child
if (read(p[0], recvdata, 1) != 1) printf("error child read\n");
close(p[0]);
printf("%d: received ping\n", getpid());
if (write(p[1], senddata, 1) != 1) printf("error child write\n");
close(p[1]);
exit(0);
}
else {
if (write(p[1], senddata, 1) != 1) printf("error parent write\n");
close(p[1]);
if (read(p[0], recvdata, 1) != 1) printf("error parent read\n");
printf("%d: received pong\n", getpid());
close(p[0]);
exit(0);
}
}

View File

@ -1,51 +0,0 @@
#include "kernel/types.h"
#include "user/user.h"
void pipeline(int leftfd) {
int pipefd[2];
int p; read(leftfd, &p, sizeof(p));
printf("prime %d\n", p);
int n;
int has_right = 0;
while (read(leftfd, &n, sizeof(n)) > 0) {
if (n % p != 0) {
if (!has_right) {
has_right = 1;
pipe(pipefd);
if (fork() == 0) {
close(pipefd[1]);
pipeline(pipefd[0]);
} else {
close(pipefd[0]);
}
}
write(pipefd[1], &n, sizeof(n));
}
}
close(leftfd);
if (has_right) {
close(pipefd[1]);
while(wait(0) != -1);
}
exit(0);
}
int main(int argc, char** argv) {
int pipefd[2];
pipe(pipefd);
int pid = fork();
if (pid != 0) {
close(pipefd[0]); // no need to read in the feeding proc
for (int i = 2; i <= 35; ++ i) {
write(pipefd[1], &i, sizeof(i));
}
close(pipefd[1]);
while(wait(0) != -1);
exit(0);
}
else {
close(pipefd[1]); // pipeline proc will have its own write pipe
pipeline(pipefd[0]);
}
exit(0);
}

View File

@ -1,17 +0,0 @@
#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char** argv) {
if (argc != 2) {
printf("\x1b[31mUnexpected argument\x1b[0m\n");
printf("usage: sleep <ticks>\n");
exit(0);
}
int ticks = atoi(argv[1]);
int ret = sleep(ticks);
if (ret) {
printf("\x1b[31mFailed to sleep\x1b[0m\n");
exit(-1);
}
exit(0);
}

188
user/symlinktest.c Normal file
View File

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

View File

@ -22,6 +22,7 @@ int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
int symlink(char* target, char* path);
// ulib.c
int stat(const char*, struct stat*);

View File

@ -36,3 +36,4 @@ entry("getpid");
entry("sbrk");
entry("sleep");
entry("uptime");
entry("symlink");

View File

@ -1,5 +1,6 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h"
char buf[512];
@ -43,7 +44,7 @@ main(int argc, char *argv[])
}
for(i = 1; i < argc; i++){
if((fd = open(argv[i], 0)) < 0){
if((fd = open(argv[i], O_RDONLY)) < 0){
printf("wc: cannot open %s\n", argv[i]);
exit(1);
}

View File

@ -1,59 +0,0 @@
#include "kernel/param.h"
#include "kernel/types.h"
#include "user/user.h"
static inline int is_ws(const char ch) {
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
}
int main(int argc, char** argv) {
int STDIN = 0;
char linebuf[1024];
char* p = linebuf;
char* _argv[MAXARG];
while (read(STDIN, p, 1) > 0) {
if (*p == '\n') {
int i;
int in_ws = 1;
for (i = 0; i < argc - 1; ++i) {
_argv[i] = argv[i + 1];
}
for (char* p0 = linebuf; p0 <= p; ++p0) {
if (is_ws(*p0)) {
if (!in_ws) {
in_ws = 1;
i++;
}
*p0 = '\0';
} else if (in_ws) {
in_ws = 0;
_argv[i] = p0;
}
if (i >= MAXARG) {
printf("warning: too many arguments");
break;
}
}
*p = '\0';
_argv[i] = 0;
// for (int j = 0; j < i; ++ j) {
// printf("%s\n", _argv[j]);
// }
if (fork() == 0) {
exec(_argv[0], _argv);
exit(0);
} else {
p = linebuf;
while (wait(0) != -1)
;
}
} else {
p++;
if (p >= linebuf + sizeof(linebuf)) {
printf("line buffer exceeded\n");
exit(0);
}
}
}
exit(0);
}

View File

@ -1,6 +0,0 @@
mkdir a
echo hello > a/b
mkdir c
echo hello > c/b
echo hello > b
find . b | xargs grep hello