xref: /xv6-public/sysfile.c (revision dec637bc)
112abb1a5SRobert Morris //
212abb1a5SRobert Morris // File-system system calls.
312abb1a5SRobert Morris // Mostly argument checking, since we don't trust
412abb1a5SRobert Morris // user code, and calls into file.c and fs.c.
512abb1a5SRobert Morris //
612abb1a5SRobert Morris 
72685309fSrsc #include "types.h"
8558ab49fSrsc #include "defs.h"
92685309fSrsc #include "param.h"
10558ab49fSrsc #include "stat.h"
112685309fSrsc #include "mmu.h"
122685309fSrsc #include "proc.h"
132685309fSrsc #include "fs.h"
14*dec637bcSFrans Kaashoek #include "spinlock.h"
15*dec637bcSFrans Kaashoek #include "sleeplock.h"
169936bffaSrsc #include "file.h"
172685309fSrsc #include "fcntl.h"
182685309fSrsc 
19224f6598Srsc // Fetch the nth word-sized system call argument as a file descriptor
20224f6598Srsc // and return both the descriptor and the corresponding struct file.
21224f6598Srsc static int
2264c47374Srsc argfd(int n, int *pfd, struct file **pf)
23224f6598Srsc {
24224f6598Srsc   int fd;
25224f6598Srsc   struct file *f;
26224f6598Srsc 
273a2310f7Srsc   if(argint(n, &fd) < 0)
28224f6598Srsc     return -1;
2948755214SRuss Cox   if(fd < 0 || fd >= NOFILE || (f=proc->ofile[fd]) == 0)
30224f6598Srsc     return -1;
31224f6598Srsc   if(pfd)
32224f6598Srsc     *pfd = fd;
33224f6598Srsc   if(pf)
34224f6598Srsc     *pf = f;
35224f6598Srsc   return 0;
36224f6598Srsc }
37224f6598Srsc 
38224f6598Srsc // Allocate a file descriptor for the given file.
39224f6598Srsc // Takes over file reference from caller on success.
40224f6598Srsc static int
41224f6598Srsc fdalloc(struct file *f)
42224f6598Srsc {
43224f6598Srsc   int fd;
449583b476Srsc 
45224f6598Srsc   for(fd = 0; fd < NOFILE; fd++){
4648755214SRuss Cox     if(proc->ofile[fd] == 0){
4748755214SRuss Cox       proc->ofile[fd] = f;
48224f6598Srsc       return fd;
49224f6598Srsc     }
50224f6598Srsc   }
51224f6598Srsc   return -1;
52224f6598Srsc }
53224f6598Srsc 
542685309fSrsc int
55f9a06440SRuss Cox sys_dup(void)
56f9a06440SRuss Cox {
57f9a06440SRuss Cox   struct file *f;
58f9a06440SRuss Cox   int fd;
59f9a06440SRuss Cox 
60f9a06440SRuss Cox   if(argfd(0, 0, &f) < 0)
61f9a06440SRuss Cox     return -1;
62f9a06440SRuss Cox   if((fd=fdalloc(f)) < 0)
63f9a06440SRuss Cox     return -1;
64f9a06440SRuss Cox   filedup(f);
65f9a06440SRuss Cox   return fd;
66f9a06440SRuss Cox }
67f9a06440SRuss Cox 
68f9a06440SRuss Cox int
69eaea18cbSrsc sys_read(void)
702685309fSrsc {
71eaea18cbSrsc   struct file *f;
72eaea18cbSrsc   int n;
73666f58c7Srsc   char *p;
742685309fSrsc 
75666f58c7Srsc   if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0)
762685309fSrsc     return -1;
77666f58c7Srsc   return fileread(f, p, n);
782685309fSrsc }
792685309fSrsc 
802685309fSrsc int
812685309fSrsc sys_write(void)
822685309fSrsc {
83224f6598Srsc   struct file *f;
84224f6598Srsc   int n;
85666f58c7Srsc   char *p;
862685309fSrsc 
87666f58c7Srsc   if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0)
882685309fSrsc     return -1;
89666f58c7Srsc   return filewrite(f, p, n);
902685309fSrsc }
912685309fSrsc 
922685309fSrsc int
932685309fSrsc sys_close(void)
942685309fSrsc {
952685309fSrsc   int fd;
96224f6598Srsc   struct file *f;
972685309fSrsc 
98224f6598Srsc   if(argfd(0, &fd, &f) < 0)
992685309fSrsc     return -1;
10048755214SRuss Cox   proc->ofile[fd] = 0;
101224f6598Srsc   fileclose(f);
1022685309fSrsc   return 0;
1032685309fSrsc }
1042685309fSrsc 
1058d2e9a48Srsc int
1068d2e9a48Srsc sys_fstat(void)
1078d2e9a48Srsc {
1088d2e9a48Srsc   struct file *f;
1098d2e9a48Srsc   struct stat *st;
1108d2e9a48Srsc 
1118d2e9a48Srsc   if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0)
1128d2e9a48Srsc     return -1;
1138d2e9a48Srsc   return filestat(f, st);
1148d2e9a48Srsc }
1158d2e9a48Srsc 
116eaea18cbSrsc // Create the path new as a link to the same inode as old.
117eaea18cbSrsc int
118eaea18cbSrsc sys_link(void)
119eaea18cbSrsc {
120eaea18cbSrsc   char name[DIRSIZ], *new, *old;
121eaea18cbSrsc   struct inode *dp, *ip;
122eaea18cbSrsc 
123eaea18cbSrsc   if(argstr(0, &old) < 0 || argstr(1, &new) < 0)
124eaea18cbSrsc     return -1;
1255053dd6aSRobert Morris 
12671453f72SRobert Morris   begin_op();
1272c565472SRobert Morris   if((ip = namei(old)) == 0){
12871453f72SRobert Morris     end_op();
1292c565472SRobert Morris     return -1;
1302c565472SRobert Morris   }
1315053dd6aSRobert Morris 
13207090dd7Srsc   ilock(ip);
133eaea18cbSrsc   if(ip->type == T_DIR){
13407090dd7Srsc     iunlockput(ip);
13571453f72SRobert Morris     end_op();
136eaea18cbSrsc     return -1;
137eaea18cbSrsc   }
1382e590463SRobert Morris 
139eaea18cbSrsc   ip->nlink++;
140eaea18cbSrsc   iupdate(ip);
14107090dd7Srsc   iunlock(ip);
142eaea18cbSrsc 
14307090dd7Srsc   if((dp = nameiparent(new, name)) == 0)
14407090dd7Srsc     goto bad;
14507090dd7Srsc   ilock(dp);
146f3685aa3Srsc   if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){
147f3685aa3Srsc     iunlockput(dp);
14807090dd7Srsc     goto bad;
149f3685aa3Srsc   }
15007090dd7Srsc   iunlockput(dp);
15107090dd7Srsc   iput(ip);
1522e590463SRobert Morris 
15371453f72SRobert Morris   end_op();
1542e590463SRobert Morris 
15507090dd7Srsc   return 0;
15607090dd7Srsc 
15707090dd7Srsc bad:
15807090dd7Srsc   ilock(ip);
159eaea18cbSrsc   ip->nlink--;
160eaea18cbSrsc   iupdate(ip);
16107090dd7Srsc   iunlockput(ip);
16271453f72SRobert Morris   end_op();
163eaea18cbSrsc   return -1;
164eaea18cbSrsc }
165eaea18cbSrsc 
166eaea18cbSrsc // Is the directory dp empty except for "." and ".." ?
167eaea18cbSrsc static int
168eaea18cbSrsc isdirempty(struct inode *dp)
169eaea18cbSrsc {
170eaea18cbSrsc   int off;
171eaea18cbSrsc   struct dirent de;
172eaea18cbSrsc 
173eaea18cbSrsc   for(off=2*sizeof(de); off<dp->size; off+=sizeof(de)){
174eaea18cbSrsc     if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
175eaea18cbSrsc       panic("isdirempty: readi");
176eaea18cbSrsc     if(de.inum != 0)
177eaea18cbSrsc       return 0;
178eaea18cbSrsc   }
179eaea18cbSrsc   return 1;
180eaea18cbSrsc }
181eaea18cbSrsc 
1828d2e9a48Srsc //PAGEBREAK!
183eaea18cbSrsc int
184eaea18cbSrsc sys_unlink(void)
185eaea18cbSrsc {
186eaea18cbSrsc   struct inode *ip, *dp;
187eaea18cbSrsc   struct dirent de;
188eaea18cbSrsc   char name[DIRSIZ], *path;
189eaea18cbSrsc   uint off;
190eaea18cbSrsc 
191eaea18cbSrsc   if(argstr(0, &path) < 0)
192eaea18cbSrsc     return -1;
1935053dd6aSRobert Morris 
19471453f72SRobert Morris   begin_op();
1952c565472SRobert Morris   if((dp = nameiparent(path, name)) == 0){
19671453f72SRobert Morris     end_op();
1972c565472SRobert Morris     return -1;
1982c565472SRobert Morris   }
1995053dd6aSRobert Morris 
20007090dd7Srsc   ilock(dp);
201eaea18cbSrsc 
202eaea18cbSrsc   // Cannot unlink "." or "..".
2034e015d81SAustin Clements   if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0)
2044e015d81SAustin Clements     goto bad;
205eaea18cbSrsc 
2064e015d81SAustin Clements   if((ip = dirlookup(dp, name, &off)) == 0)
2074e015d81SAustin Clements     goto bad;
20807090dd7Srsc   ilock(ip);
209eaea18cbSrsc 
210eaea18cbSrsc   if(ip->nlink < 1)
211eaea18cbSrsc     panic("unlink: nlink < 1");
212eaea18cbSrsc   if(ip->type == T_DIR && !isdirempty(ip)){
21307090dd7Srsc     iunlockput(ip);
2144e015d81SAustin Clements     goto bad;
215eaea18cbSrsc   }
216eaea18cbSrsc 
217eaea18cbSrsc   memset(&de, 0, sizeof(de));
218eaea18cbSrsc   if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
219eaea18cbSrsc     panic("unlink: writei");
22015a26936Skolya   if(ip->type == T_DIR){
22115a26936Skolya     dp->nlink--;
22215a26936Skolya     iupdate(dp);
22315a26936Skolya   }
22407090dd7Srsc   iunlockput(dp);
225eaea18cbSrsc 
226eaea18cbSrsc   ip->nlink--;
227eaea18cbSrsc   iupdate(ip);
22807090dd7Srsc   iunlockput(ip);
2292e590463SRobert Morris 
23071453f72SRobert Morris   end_op();
2312e590463SRobert Morris 
232eaea18cbSrsc   return 0;
2334e015d81SAustin Clements 
2344e015d81SAustin Clements bad:
2354e015d81SAustin Clements   iunlockput(dp);
23671453f72SRobert Morris   end_op();
2374e015d81SAustin Clements   return -1;
238eaea18cbSrsc }
239eaea18cbSrsc 
240eaea18cbSrsc static struct inode*
241f3685aa3Srsc create(char *path, short type, short major, short minor)
242eaea18cbSrsc {
243eaea18cbSrsc   uint off;
244eaea18cbSrsc   struct inode *ip, *dp;
245eaea18cbSrsc   char name[DIRSIZ];
246eaea18cbSrsc 
24707090dd7Srsc   if((dp = nameiparent(path, name)) == 0)
248eaea18cbSrsc     return 0;
24907090dd7Srsc   ilock(dp);
250eaea18cbSrsc 
251f3685aa3Srsc   if((ip = dirlookup(dp, name, &off)) != 0){
25207090dd7Srsc     iunlockput(dp);
25307090dd7Srsc     ilock(ip);
254f9a06440SRuss Cox     if(type == T_FILE && ip->type == T_FILE)
255f9a06440SRuss Cox       return ip;
25607090dd7Srsc     iunlockput(ip);
257eaea18cbSrsc     return 0;
258eaea18cbSrsc   }
259eaea18cbSrsc 
260f9a06440SRuss Cox   if((ip = ialloc(dp->dev, type)) == 0)
261f9a06440SRuss Cox     panic("create: ialloc");
262f9a06440SRuss Cox 
26307090dd7Srsc   ilock(ip);
264eaea18cbSrsc   ip->major = major;
265eaea18cbSrsc   ip->minor = minor;
266603deefcSrsc   ip->nlink = 1;
267eaea18cbSrsc   iupdate(ip);
268eaea18cbSrsc 
269eaea18cbSrsc   if(type == T_DIR){  // Create . and .. entries.
270eaea18cbSrsc     dp->nlink++;  // for ".."
271eaea18cbSrsc     iupdate(dp);
272eaea18cbSrsc     // No ip->nlink++ for ".": avoid cyclic ref count.
273eaea18cbSrsc     if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0)
274e79b1659Srsc       panic("create dots");
275eaea18cbSrsc   }
276f3e87bc8Skolya 
277f3685aa3Srsc   if(dirlink(dp, name, ip->inum) < 0)
278f3685aa3Srsc     panic("create: dirlink");
279f3e87bc8Skolya 
28007090dd7Srsc   iunlockput(dp);
2812e590463SRobert Morris 
282eaea18cbSrsc   return ip;
283eaea18cbSrsc }
284eaea18cbSrsc 
2852685309fSrsc int
2862685309fSrsc sys_open(void)
2872685309fSrsc {
288f32f3638Srsc   char *path;
289f32f3638Srsc   int fd, omode;
290224f6598Srsc   struct file *f;
291f32f3638Srsc   struct inode *ip;
2922685309fSrsc 
293224f6598Srsc   if(argstr(0, &path) < 0 || argint(1, &omode) < 0)
2942685309fSrsc     return -1;
2952c565472SRobert Morris 
29671453f72SRobert Morris   begin_op();
2972c565472SRobert Morris 
2982c565472SRobert Morris   if(omode & O_CREATE){
2992e590463SRobert Morris     ip = create(path, T_FILE, 0, 0);
3002c565472SRobert Morris     if(ip == 0){
30171453f72SRobert Morris       end_op();
302e2a620daSrsc       return -1;
3032c565472SRobert Morris     }
304eaea18cbSrsc   } else {
3052c565472SRobert Morris     if((ip = namei(path)) == 0){
30671453f72SRobert Morris       end_op();
307eaea18cbSrsc       return -1;
3082c565472SRobert Morris     }
30907090dd7Srsc     ilock(ip);
310f3685aa3Srsc     if(ip->type == T_DIR && omode != O_RDONLY){
31107090dd7Srsc       iunlockput(ip);
31271453f72SRobert Morris       end_op();
3132685309fSrsc       return -1;
3142685309fSrsc     }
315eaea18cbSrsc   }
3162685309fSrsc 
317eaea18cbSrsc   if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
318eaea18cbSrsc     if(f)
319224f6598Srsc       fileclose(f);
32007090dd7Srsc     iunlockput(ip);
32171453f72SRobert Morris     end_op();
3222685309fSrsc     return -1;
3232685309fSrsc   }
32407090dd7Srsc   iunlock(ip);
32571453f72SRobert Morris   end_op();
3262685309fSrsc 
327eaea18cbSrsc   f->type = FD_INODE;
32807090dd7Srsc   f->ip = ip;
329eaea18cbSrsc   f->off = 0;
330e79b1659Srsc   f->readable = !(omode & O_WRONLY);
331e79b1659Srsc   f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
332224f6598Srsc   return fd;
3332685309fSrsc }
3342685309fSrsc 
3352685309fSrsc int
336f9a06440SRuss Cox sys_mkdir(void)
337f9a06440SRuss Cox {
338f9a06440SRuss Cox   char *path;
339f9a06440SRuss Cox   struct inode *ip;
340f9a06440SRuss Cox 
34171453f72SRobert Morris   begin_op();
3422e590463SRobert Morris   if(argstr(0, &path) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){
34371453f72SRobert Morris     end_op();
344f9a06440SRuss Cox     return -1;
3452e590463SRobert Morris   }
346f9a06440SRuss Cox   iunlockput(ip);
34771453f72SRobert Morris   end_op();
348f9a06440SRuss Cox   return 0;
349f9a06440SRuss Cox }
350f9a06440SRuss Cox 
351f9a06440SRuss Cox int
3522685309fSrsc sys_mknod(void)
3532685309fSrsc {
354eaea18cbSrsc   struct inode *ip;
355224f6598Srsc   char *path;
356aa6824abSrsc   int major, minor;
3572685309fSrsc 
35871453f72SRobert Morris   begin_op();
3591ccb5a6fSPeter H. Froehlich   if((argstr(0, &path)) < 0 ||
36007090dd7Srsc      argint(1, &major) < 0 ||
36107090dd7Srsc      argint(2, &minor) < 0 ||
3622e590463SRobert Morris      (ip = create(path, T_DEV, major, minor)) == 0){
36371453f72SRobert Morris     end_op();
3642685309fSrsc     return -1;
3652e590463SRobert Morris   }
36607090dd7Srsc   iunlockput(ip);
36771453f72SRobert Morris   end_op();
368224f6598Srsc   return 0;
3692685309fSrsc }
3702685309fSrsc 
3712685309fSrsc int
3722685309fSrsc sys_chdir(void)
3732685309fSrsc {
374eaea18cbSrsc   char *path;
3752685309fSrsc   struct inode *ip;
3762685309fSrsc 
37771453f72SRobert Morris   begin_op();
3782c565472SRobert Morris   if(argstr(0, &path) < 0 || (ip = namei(path)) == 0){
37971453f72SRobert Morris     end_op();
3802685309fSrsc     return -1;
3812c565472SRobert Morris   }
38207090dd7Srsc   ilock(ip);
3832685309fSrsc   if(ip->type != T_DIR){
38407090dd7Srsc     iunlockput(ip);
38571453f72SRobert Morris     end_op();
3862685309fSrsc     return -1;
3872685309fSrsc   }
38807090dd7Srsc   iunlock(ip);
38948755214SRuss Cox   iput(proc->cwd);
39071453f72SRobert Morris   end_op();
39148755214SRuss Cox   proc->cwd = ip;
3922685309fSrsc   return 0;
3932685309fSrsc }
3942685309fSrsc 
3952685309fSrsc int
3962685309fSrsc sys_exec(void)
3972685309fSrsc {
3984655d42eSRobert Morris   char *path, *argv[MAXARG];
399f32f3638Srsc   int i;
400f32f3638Srsc   uint uargv, uarg;
4012685309fSrsc 
40240889627SFrans Kaashoek   if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){
4032685309fSrsc     return -1;
40440889627SFrans Kaashoek   }
405eaea18cbSrsc   memset(argv, 0, sizeof(argv));
4062685309fSrsc   for(i=0;; i++){
407eaea18cbSrsc     if(i >= NELEM(argv))
4082685309fSrsc       return -1;
4099d59eb01SAustin Clements     if(fetchint(uargv+4*i, (int*)&uarg) < 0)
410f32f3638Srsc       return -1;
411f32f3638Srsc     if(uarg == 0){
412f32f3638Srsc       argv[i] = 0;
413f32f3638Srsc       break;
4142685309fSrsc     }
4159d59eb01SAustin Clements     if(fetchstr(uarg, &argv[i]) < 0)
416f32f3638Srsc       return -1;
417f32f3638Srsc   }
418f32f3638Srsc   return exec(path, argv);
419f32f3638Srsc }
420f32f3638Srsc 
421eaea18cbSrsc int
422eaea18cbSrsc sys_pipe(void)
423eaea18cbSrsc {
424eaea18cbSrsc   int *fd;
425eaea18cbSrsc   struct file *rf, *wf;
426eaea18cbSrsc   int fd0, fd1;
427eaea18cbSrsc 
428eaea18cbSrsc   if(argptr(0, (void*)&fd, 2*sizeof(fd[0])) < 0)
429eaea18cbSrsc     return -1;
4307834cca6Srsc   if(pipealloc(&rf, &wf) < 0)
431eaea18cbSrsc     return -1;
432eaea18cbSrsc   fd0 = -1;
433eaea18cbSrsc   if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){
434eaea18cbSrsc     if(fd0 >= 0)
43548755214SRuss Cox       proc->ofile[fd0] = 0;
436eaea18cbSrsc     fileclose(rf);
437eaea18cbSrsc     fileclose(wf);
438eaea18cbSrsc     return -1;
439eaea18cbSrsc   }
440eaea18cbSrsc   fd[0] = fd0;
441eaea18cbSrsc   fd[1] = fd1;
442eaea18cbSrsc   return 0;
443eaea18cbSrsc }
444