xref: /xv6-public/sysfile.c (revision 8d2e9a48)
12685309fSrsc #include "types.h"
22685309fSrsc #include "stat.h"
32685309fSrsc #include "param.h"
42685309fSrsc #include "mmu.h"
52685309fSrsc #include "proc.h"
62685309fSrsc #include "defs.h"
72685309fSrsc #include "x86.h"
82685309fSrsc #include "traps.h"
92685309fSrsc #include "syscall.h"
102685309fSrsc #include "spinlock.h"
112685309fSrsc #include "buf.h"
122685309fSrsc #include "fs.h"
132685309fSrsc #include "fsvar.h"
149936bffaSrsc #include "file.h"
152685309fSrsc #include "fcntl.h"
162685309fSrsc 
17224f6598Srsc // Fetch the nth word-sized system call argument as a file descriptor
18224f6598Srsc // and return both the descriptor and the corresponding struct file.
19224f6598Srsc static int
20224f6598Srsc argfd(int argno, int *pfd, struct file **pf)
21224f6598Srsc {
22224f6598Srsc   int fd;
23224f6598Srsc   struct file *f;
24224f6598Srsc 
25224f6598Srsc   if(argint(argno, &fd) < 0)
26224f6598Srsc     return -1;
279583b476Srsc   if(fd < 0 || fd >= NOFILE || (f=cp->ofile[fd]) == 0)
28224f6598Srsc     return -1;
29224f6598Srsc   if(pfd)
30224f6598Srsc     *pfd = fd;
31224f6598Srsc   if(pf)
32224f6598Srsc     *pf = f;
33224f6598Srsc   return 0;
34224f6598Srsc }
35224f6598Srsc 
36224f6598Srsc // Allocate a file descriptor for the given file.
37224f6598Srsc // Takes over file reference from caller on success.
38224f6598Srsc static int
39224f6598Srsc fdalloc(struct file *f)
40224f6598Srsc {
41224f6598Srsc   int fd;
429583b476Srsc 
43224f6598Srsc   for(fd = 0; fd < NOFILE; fd++){
449583b476Srsc     if(cp->ofile[fd] == 0){
459583b476Srsc       cp->ofile[fd] = f;
46224f6598Srsc       return fd;
47224f6598Srsc     }
48224f6598Srsc   }
49224f6598Srsc   return -1;
50224f6598Srsc }
51224f6598Srsc 
522685309fSrsc int
53eaea18cbSrsc sys_read(void)
542685309fSrsc {
55eaea18cbSrsc   struct file *f;
56eaea18cbSrsc   int n;
57eaea18cbSrsc   char *cp;
582685309fSrsc 
59eaea18cbSrsc   if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &cp, n) < 0)
602685309fSrsc     return -1;
61eaea18cbSrsc   return fileread(f, cp, n);
622685309fSrsc }
632685309fSrsc 
642685309fSrsc int
652685309fSrsc sys_write(void)
662685309fSrsc {
67224f6598Srsc   struct file *f;
68224f6598Srsc   int n;
69224f6598Srsc   char *cp;
702685309fSrsc 
71224f6598Srsc   if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &cp, n) < 0)
722685309fSrsc     return -1;
73224f6598Srsc   return filewrite(f, cp, n);
742685309fSrsc }
752685309fSrsc 
762685309fSrsc int
77*8d2e9a48Srsc sys_dup(void)
782685309fSrsc {
79224f6598Srsc   struct file *f;
80*8d2e9a48Srsc   int fd;
812685309fSrsc 
82*8d2e9a48Srsc   if(argfd(0, 0, &f) < 0)
832685309fSrsc     return -1;
84*8d2e9a48Srsc   if((fd=fdalloc(f)) < 0)
85*8d2e9a48Srsc     return -1;
86*8d2e9a48Srsc   fileincref(f);
87*8d2e9a48Srsc   return fd;
882685309fSrsc }
892685309fSrsc 
902685309fSrsc int
912685309fSrsc sys_close(void)
922685309fSrsc {
932685309fSrsc   int fd;
94224f6598Srsc   struct file *f;
952685309fSrsc 
96224f6598Srsc   if(argfd(0, &fd, &f) < 0)
972685309fSrsc     return -1;
98b6095304Srsc   cp->ofile[fd] = 0;
99224f6598Srsc   fileclose(f);
1002685309fSrsc   return 0;
1012685309fSrsc }
1022685309fSrsc 
103*8d2e9a48Srsc int
104*8d2e9a48Srsc sys_fstat(void)
105*8d2e9a48Srsc {
106*8d2e9a48Srsc   struct file *f;
107*8d2e9a48Srsc   struct stat *st;
108*8d2e9a48Srsc 
109*8d2e9a48Srsc   if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0)
110*8d2e9a48Srsc     return -1;
111*8d2e9a48Srsc   return filestat(f, st);
112*8d2e9a48Srsc }
113*8d2e9a48Srsc 
114eaea18cbSrsc // Create the path new as a link to the same inode as old.
115eaea18cbSrsc int
116eaea18cbSrsc sys_link(void)
117eaea18cbSrsc {
118eaea18cbSrsc   char name[DIRSIZ], *new, *old;
119eaea18cbSrsc   struct inode *dp, *ip;
120eaea18cbSrsc 
121eaea18cbSrsc   if(argstr(0, &old) < 0 || argstr(1, &new) < 0)
122eaea18cbSrsc     return -1;
12307090dd7Srsc   if((ip = namei(old)) == 0)
124eaea18cbSrsc     return -1;
12507090dd7Srsc   ilock(ip);
126eaea18cbSrsc   if(ip->type == T_DIR){
12707090dd7Srsc     iunlockput(ip);
128eaea18cbSrsc     return -1;
129eaea18cbSrsc   }
130eaea18cbSrsc   ip->nlink++;
131eaea18cbSrsc   iupdate(ip);
13207090dd7Srsc   iunlock(ip);
133eaea18cbSrsc 
13407090dd7Srsc   if((dp = nameiparent(new, name)) == 0)
13507090dd7Srsc     goto  bad;
13607090dd7Srsc   ilock(dp);
13707090dd7Srsc   if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0)
13807090dd7Srsc     goto bad;
13907090dd7Srsc   iunlockput(dp);
14007090dd7Srsc   iput(ip);
14107090dd7Srsc   return 0;
14207090dd7Srsc 
14307090dd7Srsc bad:
144eaea18cbSrsc   if(dp)
14507090dd7Srsc     iunlockput(dp);
14607090dd7Srsc   ilock(ip);
147eaea18cbSrsc   ip->nlink--;
148eaea18cbSrsc   iupdate(ip);
14907090dd7Srsc   iunlockput(ip);
150eaea18cbSrsc   return -1;
151eaea18cbSrsc }
152eaea18cbSrsc 
153eaea18cbSrsc // Is the directory dp empty except for "." and ".." ?
154eaea18cbSrsc static int
155eaea18cbSrsc isdirempty(struct inode *dp)
156eaea18cbSrsc {
157eaea18cbSrsc   int off;
158eaea18cbSrsc   struct dirent de;
159eaea18cbSrsc 
160eaea18cbSrsc   for(off=2*sizeof(de); off<dp->size; off+=sizeof(de)){
161eaea18cbSrsc     if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
162eaea18cbSrsc       panic("isdirempty: readi");
163eaea18cbSrsc     if(de.inum != 0)
164eaea18cbSrsc       return 0;
165eaea18cbSrsc   }
166eaea18cbSrsc   return 1;
167eaea18cbSrsc }
168eaea18cbSrsc 
169*8d2e9a48Srsc //PAGEBREAK!
170eaea18cbSrsc int
171eaea18cbSrsc sys_unlink(void)
172eaea18cbSrsc {
173eaea18cbSrsc   struct inode *ip, *dp;
174eaea18cbSrsc   struct dirent de;
175eaea18cbSrsc   char name[DIRSIZ], *path;
176eaea18cbSrsc   uint off;
177eaea18cbSrsc 
178eaea18cbSrsc   if(argstr(0, &path) < 0)
179eaea18cbSrsc     return -1;
18007090dd7Srsc   if((dp = nameiparent(path, name)) == 0)
181eaea18cbSrsc     return -1;
18207090dd7Srsc   ilock(dp);
183eaea18cbSrsc 
184eaea18cbSrsc   // Cannot unlink "." or "..".
185eaea18cbSrsc   if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0){
18607090dd7Srsc     iunlockput(dp);
187eaea18cbSrsc     return -1;
188eaea18cbSrsc   }
189eaea18cbSrsc 
19007090dd7Srsc   if((ip = dirlookup(dp, name, &off)) == 0){
19107090dd7Srsc     iunlockput(dp);
192eaea18cbSrsc     return -1;
193eaea18cbSrsc   }
19407090dd7Srsc   ilock(ip);
195eaea18cbSrsc 
196eaea18cbSrsc   if(ip->nlink < 1)
197eaea18cbSrsc     panic("unlink: nlink < 1");
198eaea18cbSrsc   if(ip->type == T_DIR && !isdirempty(ip)){
19907090dd7Srsc     iunlockput(ip);
20007090dd7Srsc     iunlockput(dp);
201eaea18cbSrsc     return -1;
202eaea18cbSrsc   }
203eaea18cbSrsc 
204eaea18cbSrsc   memset(&de, 0, sizeof(de));
205eaea18cbSrsc   if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
206eaea18cbSrsc     panic("unlink: writei");
20707090dd7Srsc   iunlockput(dp);
208eaea18cbSrsc 
209eaea18cbSrsc   ip->nlink--;
210eaea18cbSrsc   iupdate(ip);
21107090dd7Srsc   iunlockput(ip);
212eaea18cbSrsc   return 0;
213eaea18cbSrsc }
214eaea18cbSrsc 
215eaea18cbSrsc static struct inode*
216eaea18cbSrsc mkpath(char *path, int canexist, short type, short major, short minor)
217eaea18cbSrsc {
218eaea18cbSrsc   uint off;
219eaea18cbSrsc   struct inode *ip, *dp;
220eaea18cbSrsc   char name[DIRSIZ];
221eaea18cbSrsc 
22207090dd7Srsc   if((dp = nameiparent(path, name)) == 0)
223eaea18cbSrsc     return 0;
22407090dd7Srsc   ilock(dp);
225eaea18cbSrsc 
22607090dd7Srsc   if(canexist && (ip = dirlookup(dp, name, &off)) != 0){
22707090dd7Srsc     iunlockput(dp);
22807090dd7Srsc     ilock(ip);
229eaea18cbSrsc     if(ip->type != type || ip->major != major || ip->minor != minor){
23007090dd7Srsc       iunlockput(ip);
231eaea18cbSrsc       return 0;
232eaea18cbSrsc     }
233eaea18cbSrsc     return ip;
234eaea18cbSrsc   }
235eaea18cbSrsc 
23607090dd7Srsc   if((ip = ialloc(dp->dev, type)) == 0){
23707090dd7Srsc     iunlockput(dp);
238eaea18cbSrsc     return 0;
239eaea18cbSrsc   }
24007090dd7Srsc   ilock(ip);
241eaea18cbSrsc   ip->major = major;
242eaea18cbSrsc   ip->minor = minor;
243eaea18cbSrsc   ip->size = 0;
244eaea18cbSrsc   ip->nlink = 1;
245eaea18cbSrsc   iupdate(ip);
246eaea18cbSrsc 
247eaea18cbSrsc   if(dirlink(dp, name, ip->inum) < 0){
248eaea18cbSrsc     ip->nlink = 0;
24907090dd7Srsc     iunlockput(ip);
25007090dd7Srsc     iunlockput(dp);
251eaea18cbSrsc     return 0;
252eaea18cbSrsc   }
253eaea18cbSrsc 
254eaea18cbSrsc   if(type == T_DIR){  // Create . and .. entries.
255eaea18cbSrsc     dp->nlink++;  // for ".."
256eaea18cbSrsc     iupdate(dp);
257eaea18cbSrsc     // No ip->nlink++ for ".": avoid cyclic ref count.
258eaea18cbSrsc     if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0)
259eaea18cbSrsc       panic("mkpath dots");
260eaea18cbSrsc   }
26107090dd7Srsc   iunlockput(dp);
262eaea18cbSrsc   return ip;
263eaea18cbSrsc }
264eaea18cbSrsc 
2652685309fSrsc int
2662685309fSrsc sys_open(void)
2672685309fSrsc {
268f32f3638Srsc   char *path;
269f32f3638Srsc   int fd, omode;
270224f6598Srsc   struct file *f;
271f32f3638Srsc   struct inode *ip;
2722685309fSrsc 
273224f6598Srsc   if(argstr(0, &path) < 0 || argint(1, &omode) < 0)
2742685309fSrsc     return -1;
2752685309fSrsc 
276eaea18cbSrsc   if(omode & O_CREATE){
277eaea18cbSrsc     if((ip = mkpath(path, 1, T_FILE, 0, 0)) == 0)
278e2a620daSrsc       return -1;
279eaea18cbSrsc   }else{
28007090dd7Srsc     if((ip = namei(path)) == 0)
281eaea18cbSrsc       return -1;
28207090dd7Srsc     ilock(ip);
283f32f3638Srsc     if(ip->type == T_DIR && (omode & (O_RDWR|O_WRONLY))){
28407090dd7Srsc       iunlockput(ip);
2852685309fSrsc       return -1;
2862685309fSrsc     }
287eaea18cbSrsc   }
2882685309fSrsc 
289eaea18cbSrsc   if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
290eaea18cbSrsc     if(f)
291224f6598Srsc       fileclose(f);
29207090dd7Srsc     iunlockput(ip);
2932685309fSrsc     return -1;
2942685309fSrsc   }
29507090dd7Srsc   iunlock(ip);
2962685309fSrsc 
297eaea18cbSrsc   f->type = FD_INODE;
29807090dd7Srsc   f->ip = ip;
299eaea18cbSrsc   f->off = 0;
300224f6598Srsc   if(omode & O_RDWR) {
301224f6598Srsc     f->readable = 1;
302224f6598Srsc     f->writable = 1;
303224f6598Srsc   } else if(omode & O_WRONLY) {
304224f6598Srsc     f->readable = 0;
305224f6598Srsc     f->writable = 1;
3062685309fSrsc   } else {
307224f6598Srsc     f->readable = 1;
308224f6598Srsc     f->writable = 0;
3092685309fSrsc   }
3102685309fSrsc 
311224f6598Srsc   return fd;
3122685309fSrsc }
3132685309fSrsc 
3142685309fSrsc int
3152685309fSrsc sys_mknod(void)
3162685309fSrsc {
317eaea18cbSrsc   struct inode *ip;
318224f6598Srsc   char *path;
319224f6598Srsc   int len;
320aa6824abSrsc   int major, minor;
3212685309fSrsc 
32207090dd7Srsc   if((len=argstr(0, &path)) < 0 ||
32307090dd7Srsc      argint(1, &major) < 0 ||
32407090dd7Srsc      argint(2, &minor) < 0 ||
32507090dd7Srsc      (ip = mkpath(path, 0, T_DEV, major, minor)) == 0)
3262685309fSrsc     return -1;
32707090dd7Srsc   iunlockput(ip);
328224f6598Srsc   return 0;
3292685309fSrsc }
3302685309fSrsc 
3312685309fSrsc int
3322685309fSrsc sys_mkdir(void)
3332685309fSrsc {
334f32f3638Srsc   char *path;
335eaea18cbSrsc   struct inode *ip;
3362685309fSrsc 
337eaea18cbSrsc   if(argstr(0, &path) < 0 || (ip = mkpath(path, 0, T_DIR, 0, 0)) == 0)
3382685309fSrsc     return -1;
33907090dd7Srsc   iunlockput(ip);
340eaea18cbSrsc   return 0;
3412685309fSrsc }
3422685309fSrsc 
3432685309fSrsc int
3442685309fSrsc sys_chdir(void)
3452685309fSrsc {
346eaea18cbSrsc   char *path;
3472685309fSrsc   struct inode *ip;
3482685309fSrsc 
34907090dd7Srsc   if(argstr(0, &path) < 0 || (ip = namei(path)) == 0)
3502685309fSrsc     return -1;
35107090dd7Srsc   ilock(ip);
3522685309fSrsc   if(ip->type != T_DIR) {
35307090dd7Srsc     iunlockput(ip);
3542685309fSrsc     return -1;
3552685309fSrsc   }
35607090dd7Srsc   iunlock(ip);
357eaea18cbSrsc   iput(cp->cwd);
35807090dd7Srsc   cp->cwd = ip;
3592685309fSrsc   return 0;
3602685309fSrsc }
3612685309fSrsc 
3622685309fSrsc int
3632685309fSrsc sys_exec(void)
3642685309fSrsc {
365eaea18cbSrsc   char *path, *argv[20];
366f32f3638Srsc   int i;
367f32f3638Srsc   uint uargv, uarg;
3682685309fSrsc 
369f32f3638Srsc   if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0)
3702685309fSrsc     return -1;
371eaea18cbSrsc   memset(argv, 0, sizeof(argv));
3722685309fSrsc   for(i=0;; i++){
373eaea18cbSrsc     if(i >= NELEM(argv))
3742685309fSrsc       return -1;
375f32f3638Srsc     if(fetchint(cp, uargv+4*i, (int*)&uarg) < 0)
376f32f3638Srsc       return -1;
377f32f3638Srsc     if(uarg == 0){
378f32f3638Srsc       argv[i] = 0;
379f32f3638Srsc       break;
3802685309fSrsc     }
381f32f3638Srsc     if(fetchstr(cp, uarg, &argv[i]) < 0)
382f32f3638Srsc       return -1;
383f32f3638Srsc   }
384f32f3638Srsc   return exec(path, argv);
385f32f3638Srsc }
386f32f3638Srsc 
387eaea18cbSrsc int
388eaea18cbSrsc sys_pipe(void)
389eaea18cbSrsc {
390eaea18cbSrsc   int *fd;
391eaea18cbSrsc   struct file *rf, *wf;
392eaea18cbSrsc   int fd0, fd1;
393eaea18cbSrsc 
394eaea18cbSrsc   if(argptr(0, (void*)&fd, 2*sizeof(fd[0])) < 0)
395eaea18cbSrsc     return -1;
396eaea18cbSrsc   if(pipe_alloc(&rf, &wf) < 0)
397eaea18cbSrsc     return -1;
398eaea18cbSrsc   fd0 = -1;
399eaea18cbSrsc   if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){
400eaea18cbSrsc     if(fd0 >= 0)
401eaea18cbSrsc       cp->ofile[fd0] = 0;
402eaea18cbSrsc     fileclose(rf);
403eaea18cbSrsc     fileclose(wf);
404eaea18cbSrsc     return -1;
405eaea18cbSrsc   }
406eaea18cbSrsc   fd[0] = fd0;
407eaea18cbSrsc   fd[1] = fd1;
408eaea18cbSrsc   return 0;
409eaea18cbSrsc }
410