xref: /xv6-public/sysfile.c (revision 5053dd6a)
12685309fSrsc #include "types.h"
2558ab49fSrsc #include "defs.h"
32685309fSrsc #include "param.h"
4558ab49fSrsc #include "stat.h"
52685309fSrsc #include "mmu.h"
62685309fSrsc #include "proc.h"
72685309fSrsc #include "fs.h"
89936bffaSrsc #include "file.h"
92685309fSrsc #include "fcntl.h"
102685309fSrsc 
11224f6598Srsc // Fetch the nth word-sized system call argument as a file descriptor
12224f6598Srsc // and return both the descriptor and the corresponding struct file.
13224f6598Srsc static int
1464c47374Srsc argfd(int n, int *pfd, struct file **pf)
15224f6598Srsc {
16224f6598Srsc   int fd;
17224f6598Srsc   struct file *f;
18224f6598Srsc 
193a2310f7Srsc   if(argint(n, &fd) < 0)
20224f6598Srsc     return -1;
2148755214SRuss Cox   if(fd < 0 || fd >= NOFILE || (f=proc->ofile[fd]) == 0)
22224f6598Srsc     return -1;
23224f6598Srsc   if(pfd)
24224f6598Srsc     *pfd = fd;
25224f6598Srsc   if(pf)
26224f6598Srsc     *pf = f;
27224f6598Srsc   return 0;
28224f6598Srsc }
29224f6598Srsc 
30224f6598Srsc // Allocate a file descriptor for the given file.
31224f6598Srsc // Takes over file reference from caller on success.
32224f6598Srsc static int
33224f6598Srsc fdalloc(struct file *f)
34224f6598Srsc {
35224f6598Srsc   int fd;
369583b476Srsc 
37224f6598Srsc   for(fd = 0; fd < NOFILE; fd++){
3848755214SRuss Cox     if(proc->ofile[fd] == 0){
3948755214SRuss Cox       proc->ofile[fd] = f;
40224f6598Srsc       return fd;
41224f6598Srsc     }
42224f6598Srsc   }
43224f6598Srsc   return -1;
44224f6598Srsc }
45224f6598Srsc 
462685309fSrsc int
47f9a06440SRuss Cox sys_dup(void)
48f9a06440SRuss Cox {
49f9a06440SRuss Cox   struct file *f;
50f9a06440SRuss Cox   int fd;
51f9a06440SRuss Cox 
52f9a06440SRuss Cox   if(argfd(0, 0, &f) < 0)
53f9a06440SRuss Cox     return -1;
54f9a06440SRuss Cox   if((fd=fdalloc(f)) < 0)
55f9a06440SRuss Cox     return -1;
56f9a06440SRuss Cox   filedup(f);
57f9a06440SRuss Cox   return fd;
58f9a06440SRuss Cox }
59f9a06440SRuss Cox 
60f9a06440SRuss Cox int
61eaea18cbSrsc sys_read(void)
622685309fSrsc {
63eaea18cbSrsc   struct file *f;
64eaea18cbSrsc   int n;
65666f58c7Srsc   char *p;
662685309fSrsc 
67666f58c7Srsc   if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0)
682685309fSrsc     return -1;
69666f58c7Srsc   return fileread(f, p, n);
702685309fSrsc }
712685309fSrsc 
722685309fSrsc int
732685309fSrsc sys_write(void)
742685309fSrsc {
75224f6598Srsc   struct file *f;
76224f6598Srsc   int n;
77666f58c7Srsc   char *p;
782685309fSrsc 
79666f58c7Srsc   if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0)
802685309fSrsc     return -1;
81666f58c7Srsc   return filewrite(f, p, n);
822685309fSrsc }
832685309fSrsc 
842685309fSrsc int
852685309fSrsc sys_close(void)
862685309fSrsc {
872685309fSrsc   int fd;
88224f6598Srsc   struct file *f;
892685309fSrsc 
90224f6598Srsc   if(argfd(0, &fd, &f) < 0)
912685309fSrsc     return -1;
9248755214SRuss Cox   proc->ofile[fd] = 0;
93224f6598Srsc   fileclose(f);
942685309fSrsc   return 0;
952685309fSrsc }
962685309fSrsc 
978d2e9a48Srsc int
988d2e9a48Srsc sys_fstat(void)
998d2e9a48Srsc {
1008d2e9a48Srsc   struct file *f;
1018d2e9a48Srsc   struct stat *st;
1028d2e9a48Srsc 
1038d2e9a48Srsc   if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0)
1048d2e9a48Srsc     return -1;
1058d2e9a48Srsc   return filestat(f, st);
1068d2e9a48Srsc }
1078d2e9a48Srsc 
108eaea18cbSrsc // Create the path new as a link to the same inode as old.
109eaea18cbSrsc int
110eaea18cbSrsc sys_link(void)
111eaea18cbSrsc {
112eaea18cbSrsc   char name[DIRSIZ], *new, *old;
113eaea18cbSrsc   struct inode *dp, *ip;
114eaea18cbSrsc 
115eaea18cbSrsc   if(argstr(0, &old) < 0 || argstr(1, &new) < 0)
116eaea18cbSrsc     return -1;
11707090dd7Srsc   if((ip = namei(old)) == 0)
118eaea18cbSrsc     return -1;
119*5053dd6aSRobert Morris 
120*5053dd6aSRobert Morris   begin_trans();
121*5053dd6aSRobert Morris 
12207090dd7Srsc   ilock(ip);
123eaea18cbSrsc   if(ip->type == T_DIR){
12407090dd7Srsc     iunlockput(ip);
125*5053dd6aSRobert Morris     commit_trans();
126eaea18cbSrsc     return -1;
127eaea18cbSrsc   }
1282e590463SRobert Morris 
129eaea18cbSrsc   ip->nlink++;
130eaea18cbSrsc   iupdate(ip);
13107090dd7Srsc   iunlock(ip);
132eaea18cbSrsc 
13307090dd7Srsc   if((dp = nameiparent(new, name)) == 0)
13407090dd7Srsc     goto bad;
13507090dd7Srsc   ilock(dp);
136f3685aa3Srsc   if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){
137f3685aa3Srsc     iunlockput(dp);
13807090dd7Srsc     goto bad;
139f3685aa3Srsc   }
14007090dd7Srsc   iunlockput(dp);
14107090dd7Srsc   iput(ip);
1422e590463SRobert Morris 
1432e590463SRobert Morris   commit_trans();
1442e590463SRobert Morris 
14507090dd7Srsc   return 0;
14607090dd7Srsc 
14707090dd7Srsc bad:
14807090dd7Srsc   ilock(ip);
149eaea18cbSrsc   ip->nlink--;
150eaea18cbSrsc   iupdate(ip);
15107090dd7Srsc   iunlockput(ip);
1522e590463SRobert Morris   commit_trans();
153eaea18cbSrsc   return -1;
154eaea18cbSrsc }
155eaea18cbSrsc 
156eaea18cbSrsc // Is the directory dp empty except for "." and ".." ?
157eaea18cbSrsc static int
158eaea18cbSrsc isdirempty(struct inode *dp)
159eaea18cbSrsc {
160eaea18cbSrsc   int off;
161eaea18cbSrsc   struct dirent de;
162eaea18cbSrsc 
163eaea18cbSrsc   for(off=2*sizeof(de); off<dp->size; off+=sizeof(de)){
164eaea18cbSrsc     if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
165eaea18cbSrsc       panic("isdirempty: readi");
166eaea18cbSrsc     if(de.inum != 0)
167eaea18cbSrsc       return 0;
168eaea18cbSrsc   }
169eaea18cbSrsc   return 1;
170eaea18cbSrsc }
171eaea18cbSrsc 
1728d2e9a48Srsc //PAGEBREAK!
173eaea18cbSrsc int
174eaea18cbSrsc sys_unlink(void)
175eaea18cbSrsc {
176eaea18cbSrsc   struct inode *ip, *dp;
177eaea18cbSrsc   struct dirent de;
178eaea18cbSrsc   char name[DIRSIZ], *path;
179eaea18cbSrsc   uint off;
180eaea18cbSrsc 
181eaea18cbSrsc   if(argstr(0, &path) < 0)
182eaea18cbSrsc     return -1;
18307090dd7Srsc   if((dp = nameiparent(path, name)) == 0)
184eaea18cbSrsc     return -1;
185*5053dd6aSRobert Morris 
186*5053dd6aSRobert Morris   begin_trans();
187*5053dd6aSRobert Morris 
18807090dd7Srsc   ilock(dp);
189eaea18cbSrsc 
190eaea18cbSrsc   // Cannot unlink "." or "..".
191eaea18cbSrsc   if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0){
19207090dd7Srsc     iunlockput(dp);
193*5053dd6aSRobert Morris     commit_trans();
194eaea18cbSrsc     return -1;
195eaea18cbSrsc   }
196eaea18cbSrsc 
19707090dd7Srsc   if((ip = dirlookup(dp, name, &off)) == 0){
19807090dd7Srsc     iunlockput(dp);
199*5053dd6aSRobert Morris     commit_trans();
200eaea18cbSrsc     return -1;
201eaea18cbSrsc   }
20207090dd7Srsc   ilock(ip);
203eaea18cbSrsc 
204eaea18cbSrsc   if(ip->nlink < 1)
205eaea18cbSrsc     panic("unlink: nlink < 1");
206eaea18cbSrsc   if(ip->type == T_DIR && !isdirempty(ip)){
20707090dd7Srsc     iunlockput(ip);
20807090dd7Srsc     iunlockput(dp);
209*5053dd6aSRobert Morris     commit_trans();
210eaea18cbSrsc     return -1;
211eaea18cbSrsc   }
212eaea18cbSrsc 
213eaea18cbSrsc   memset(&de, 0, sizeof(de));
214eaea18cbSrsc   if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
215eaea18cbSrsc     panic("unlink: writei");
21615a26936Skolya   if(ip->type == T_DIR){
21715a26936Skolya     dp->nlink--;
21815a26936Skolya     iupdate(dp);
21915a26936Skolya   }
22007090dd7Srsc   iunlockput(dp);
221eaea18cbSrsc 
222eaea18cbSrsc   ip->nlink--;
223eaea18cbSrsc   iupdate(ip);
22407090dd7Srsc   iunlockput(ip);
2252e590463SRobert Morris 
2262e590463SRobert Morris   commit_trans();
2272e590463SRobert Morris 
228eaea18cbSrsc   return 0;
229eaea18cbSrsc }
230eaea18cbSrsc 
231eaea18cbSrsc static struct inode*
232f3685aa3Srsc create(char *path, short type, short major, short minor)
233eaea18cbSrsc {
234eaea18cbSrsc   uint off;
235eaea18cbSrsc   struct inode *ip, *dp;
236eaea18cbSrsc   char name[DIRSIZ];
237eaea18cbSrsc 
23807090dd7Srsc   if((dp = nameiparent(path, name)) == 0)
239eaea18cbSrsc     return 0;
24007090dd7Srsc   ilock(dp);
241eaea18cbSrsc 
242f3685aa3Srsc   if((ip = dirlookup(dp, name, &off)) != 0){
24307090dd7Srsc     iunlockput(dp);
24407090dd7Srsc     ilock(ip);
245f9a06440SRuss Cox     if(type == T_FILE && ip->type == T_FILE)
246f9a06440SRuss Cox       return ip;
24707090dd7Srsc     iunlockput(ip);
248eaea18cbSrsc     return 0;
249eaea18cbSrsc   }
250eaea18cbSrsc 
251f9a06440SRuss Cox   if((ip = ialloc(dp->dev, type)) == 0)
252f9a06440SRuss Cox     panic("create: ialloc");
253f9a06440SRuss Cox 
25407090dd7Srsc   ilock(ip);
255eaea18cbSrsc   ip->major = major;
256eaea18cbSrsc   ip->minor = minor;
257603deefcSrsc   ip->nlink = 1;
258eaea18cbSrsc   iupdate(ip);
259eaea18cbSrsc 
260eaea18cbSrsc   if(type == T_DIR){  // Create . and .. entries.
261eaea18cbSrsc     dp->nlink++;  // for ".."
262eaea18cbSrsc     iupdate(dp);
263eaea18cbSrsc     // No ip->nlink++ for ".": avoid cyclic ref count.
264eaea18cbSrsc     if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0)
265e79b1659Srsc       panic("create dots");
266eaea18cbSrsc   }
267f3e87bc8Skolya 
268f3685aa3Srsc   if(dirlink(dp, name, ip->inum) < 0)
269f3685aa3Srsc     panic("create: dirlink");
270f3e87bc8Skolya 
27107090dd7Srsc   iunlockput(dp);
2722e590463SRobert Morris 
273eaea18cbSrsc   return ip;
274eaea18cbSrsc }
275eaea18cbSrsc 
2762685309fSrsc int
2772685309fSrsc sys_open(void)
2782685309fSrsc {
279f32f3638Srsc   char *path;
280f32f3638Srsc   int fd, omode;
281224f6598Srsc   struct file *f;
282f32f3638Srsc   struct inode *ip;
2832685309fSrsc 
284224f6598Srsc   if(argstr(0, &path) < 0 || argint(1, &omode) < 0)
2852685309fSrsc     return -1;
286eaea18cbSrsc   if(omode & O_CREATE){
2872e590463SRobert Morris     begin_trans();
2882e590463SRobert Morris     ip = create(path, T_FILE, 0, 0);
2892e590463SRobert Morris     commit_trans();
2902e590463SRobert Morris     if(ip == 0)
291e2a620daSrsc       return -1;
292eaea18cbSrsc   } else {
29307090dd7Srsc     if((ip = namei(path)) == 0)
294eaea18cbSrsc       return -1;
29507090dd7Srsc     ilock(ip);
296f3685aa3Srsc     if(ip->type == T_DIR && omode != O_RDONLY){
29707090dd7Srsc       iunlockput(ip);
2982685309fSrsc       return -1;
2992685309fSrsc     }
300eaea18cbSrsc   }
3012685309fSrsc 
302eaea18cbSrsc   if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
303eaea18cbSrsc     if(f)
304224f6598Srsc       fileclose(f);
30507090dd7Srsc     iunlockput(ip);
3062685309fSrsc     return -1;
3072685309fSrsc   }
30807090dd7Srsc   iunlock(ip);
3092685309fSrsc 
310eaea18cbSrsc   f->type = FD_INODE;
31107090dd7Srsc   f->ip = ip;
312eaea18cbSrsc   f->off = 0;
313e79b1659Srsc   f->readable = !(omode & O_WRONLY);
314e79b1659Srsc   f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
315224f6598Srsc   return fd;
3162685309fSrsc }
3172685309fSrsc 
3182685309fSrsc int
319f9a06440SRuss Cox sys_mkdir(void)
320f9a06440SRuss Cox {
321f9a06440SRuss Cox   char *path;
322f9a06440SRuss Cox   struct inode *ip;
323f9a06440SRuss Cox 
3242e590463SRobert Morris   begin_trans();
3252e590463SRobert Morris   if(argstr(0, &path) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){
3262e590463SRobert Morris     commit_trans();
327f9a06440SRuss Cox     return -1;
3282e590463SRobert Morris   }
329f9a06440SRuss Cox   iunlockput(ip);
3302e590463SRobert Morris   commit_trans();
331f9a06440SRuss Cox   return 0;
332f9a06440SRuss Cox }
333f9a06440SRuss Cox 
334f9a06440SRuss Cox int
3352685309fSrsc sys_mknod(void)
3362685309fSrsc {
337eaea18cbSrsc   struct inode *ip;
338224f6598Srsc   char *path;
339224f6598Srsc   int len;
340aa6824abSrsc   int major, minor;
3412685309fSrsc 
3422e590463SRobert Morris   begin_trans();
34307090dd7Srsc   if((len=argstr(0, &path)) < 0 ||
34407090dd7Srsc      argint(1, &major) < 0 ||
34507090dd7Srsc      argint(2, &minor) < 0 ||
3462e590463SRobert Morris      (ip = create(path, T_DEV, major, minor)) == 0){
3472e590463SRobert Morris     commit_trans();
3482685309fSrsc     return -1;
3492e590463SRobert Morris   }
35007090dd7Srsc   iunlockput(ip);
3512e590463SRobert Morris   commit_trans();
352224f6598Srsc   return 0;
3532685309fSrsc }
3542685309fSrsc 
3552685309fSrsc int
3562685309fSrsc sys_chdir(void)
3572685309fSrsc {
358eaea18cbSrsc   char *path;
3592685309fSrsc   struct inode *ip;
3602685309fSrsc 
36107090dd7Srsc   if(argstr(0, &path) < 0 || (ip = namei(path)) == 0)
3622685309fSrsc     return -1;
36307090dd7Srsc   ilock(ip);
3642685309fSrsc   if(ip->type != T_DIR){
36507090dd7Srsc     iunlockput(ip);
3662685309fSrsc     return -1;
3672685309fSrsc   }
36807090dd7Srsc   iunlock(ip);
36948755214SRuss Cox   iput(proc->cwd);
37048755214SRuss Cox   proc->cwd = ip;
3712685309fSrsc   return 0;
3722685309fSrsc }
3732685309fSrsc 
3742685309fSrsc int
3752685309fSrsc sys_exec(void)
3762685309fSrsc {
3774655d42eSRobert Morris   char *path, *argv[MAXARG];
378f32f3638Srsc   int i;
379f32f3638Srsc   uint uargv, uarg;
3802685309fSrsc 
38140889627SFrans Kaashoek   if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){
3822685309fSrsc     return -1;
38340889627SFrans Kaashoek   }
384eaea18cbSrsc   memset(argv, 0, sizeof(argv));
3852685309fSrsc   for(i=0;; i++){
386eaea18cbSrsc     if(i >= NELEM(argv))
3872685309fSrsc       return -1;
38848755214SRuss Cox     if(fetchint(proc, uargv+4*i, (int*)&uarg) < 0)
389f32f3638Srsc       return -1;
390f32f3638Srsc     if(uarg == 0){
391f32f3638Srsc       argv[i] = 0;
392f32f3638Srsc       break;
3932685309fSrsc     }
39448755214SRuss Cox     if(fetchstr(proc, uarg, &argv[i]) < 0)
395f32f3638Srsc       return -1;
396f32f3638Srsc   }
397f32f3638Srsc   return exec(path, argv);
398f32f3638Srsc }
399f32f3638Srsc 
400eaea18cbSrsc int
401eaea18cbSrsc sys_pipe(void)
402eaea18cbSrsc {
403eaea18cbSrsc   int *fd;
404eaea18cbSrsc   struct file *rf, *wf;
405eaea18cbSrsc   int fd0, fd1;
406eaea18cbSrsc 
407eaea18cbSrsc   if(argptr(0, (void*)&fd, 2*sizeof(fd[0])) < 0)
408eaea18cbSrsc     return -1;
4097834cca6Srsc   if(pipealloc(&rf, &wf) < 0)
410eaea18cbSrsc     return -1;
411eaea18cbSrsc   fd0 = -1;
412eaea18cbSrsc   if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){
413eaea18cbSrsc     if(fd0 >= 0)
41448755214SRuss Cox       proc->ofile[fd0] = 0;
415eaea18cbSrsc     fileclose(rf);
416eaea18cbSrsc     fileclose(wf);
417eaea18cbSrsc     return -1;
418eaea18cbSrsc   }
419eaea18cbSrsc   fd[0] = fd0;
420eaea18cbSrsc   fd[1] = fd1;
421eaea18cbSrsc   return 0;
422eaea18cbSrsc }
423