xref: /xv6-public/sysfile.c (revision 2c565472)
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"
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
2064c47374Srsc argfd(int n, int *pfd, struct file **pf)
21224f6598Srsc {
22224f6598Srsc   int fd;
23224f6598Srsc   struct file *f;
24224f6598Srsc 
253a2310f7Srsc   if(argint(n, &fd) < 0)
26224f6598Srsc     return -1;
2748755214SRuss Cox   if(fd < 0 || fd >= NOFILE || (f=proc->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++){
4448755214SRuss Cox     if(proc->ofile[fd] == 0){
4548755214SRuss Cox       proc->ofile[fd] = f;
46224f6598Srsc       return fd;
47224f6598Srsc     }
48224f6598Srsc   }
49224f6598Srsc   return -1;
50224f6598Srsc }
51224f6598Srsc 
522685309fSrsc int
53f9a06440SRuss Cox sys_dup(void)
54f9a06440SRuss Cox {
55f9a06440SRuss Cox   struct file *f;
56f9a06440SRuss Cox   int fd;
57f9a06440SRuss Cox 
58f9a06440SRuss Cox   if(argfd(0, 0, &f) < 0)
59f9a06440SRuss Cox     return -1;
60f9a06440SRuss Cox   if((fd=fdalloc(f)) < 0)
61f9a06440SRuss Cox     return -1;
62f9a06440SRuss Cox   filedup(f);
63f9a06440SRuss Cox   return fd;
64f9a06440SRuss Cox }
65f9a06440SRuss Cox 
66f9a06440SRuss Cox int
67eaea18cbSrsc sys_read(void)
682685309fSrsc {
69eaea18cbSrsc   struct file *f;
70eaea18cbSrsc   int n;
71666f58c7Srsc   char *p;
722685309fSrsc 
73666f58c7Srsc   if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0)
742685309fSrsc     return -1;
75666f58c7Srsc   return fileread(f, p, n);
762685309fSrsc }
772685309fSrsc 
782685309fSrsc int
792685309fSrsc sys_write(void)
802685309fSrsc {
81224f6598Srsc   struct file *f;
82224f6598Srsc   int n;
83666f58c7Srsc   char *p;
842685309fSrsc 
85666f58c7Srsc   if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0)
862685309fSrsc     return -1;
87666f58c7Srsc   return filewrite(f, p, n);
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;
9848755214SRuss Cox   proc->ofile[fd] = 0;
99224f6598Srsc   fileclose(f);
1002685309fSrsc   return 0;
1012685309fSrsc }
1022685309fSrsc 
1038d2e9a48Srsc int
1048d2e9a48Srsc sys_fstat(void)
1058d2e9a48Srsc {
1068d2e9a48Srsc   struct file *f;
1078d2e9a48Srsc   struct stat *st;
1088d2e9a48Srsc 
1098d2e9a48Srsc   if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0)
1108d2e9a48Srsc     return -1;
1118d2e9a48Srsc   return filestat(f, st);
1128d2e9a48Srsc }
1138d2e9a48Srsc 
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;
1235053dd6aSRobert Morris 
1245053dd6aSRobert Morris   begin_trans();
125*2c565472SRobert Morris   if((ip = namei(old)) == 0){
126*2c565472SRobert Morris     commit_trans();
127*2c565472SRobert Morris     return -1;
128*2c565472SRobert Morris   }
1295053dd6aSRobert Morris 
13007090dd7Srsc   ilock(ip);
131eaea18cbSrsc   if(ip->type == T_DIR){
13207090dd7Srsc     iunlockput(ip);
1335053dd6aSRobert Morris     commit_trans();
134eaea18cbSrsc     return -1;
135eaea18cbSrsc   }
1362e590463SRobert Morris 
137eaea18cbSrsc   ip->nlink++;
138eaea18cbSrsc   iupdate(ip);
13907090dd7Srsc   iunlock(ip);
140eaea18cbSrsc 
14107090dd7Srsc   if((dp = nameiparent(new, name)) == 0)
14207090dd7Srsc     goto bad;
14307090dd7Srsc   ilock(dp);
144f3685aa3Srsc   if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){
145f3685aa3Srsc     iunlockput(dp);
14607090dd7Srsc     goto bad;
147f3685aa3Srsc   }
14807090dd7Srsc   iunlockput(dp);
14907090dd7Srsc   iput(ip);
1502e590463SRobert Morris 
1512e590463SRobert Morris   commit_trans();
1522e590463SRobert Morris 
15307090dd7Srsc   return 0;
15407090dd7Srsc 
15507090dd7Srsc bad:
15607090dd7Srsc   ilock(ip);
157eaea18cbSrsc   ip->nlink--;
158eaea18cbSrsc   iupdate(ip);
15907090dd7Srsc   iunlockput(ip);
1602e590463SRobert Morris   commit_trans();
161eaea18cbSrsc   return -1;
162eaea18cbSrsc }
163eaea18cbSrsc 
164eaea18cbSrsc // Is the directory dp empty except for "." and ".." ?
165eaea18cbSrsc static int
166eaea18cbSrsc isdirempty(struct inode *dp)
167eaea18cbSrsc {
168eaea18cbSrsc   int off;
169eaea18cbSrsc   struct dirent de;
170eaea18cbSrsc 
171eaea18cbSrsc   for(off=2*sizeof(de); off<dp->size; off+=sizeof(de)){
172eaea18cbSrsc     if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
173eaea18cbSrsc       panic("isdirempty: readi");
174eaea18cbSrsc     if(de.inum != 0)
175eaea18cbSrsc       return 0;
176eaea18cbSrsc   }
177eaea18cbSrsc   return 1;
178eaea18cbSrsc }
179eaea18cbSrsc 
1808d2e9a48Srsc //PAGEBREAK!
181eaea18cbSrsc int
182eaea18cbSrsc sys_unlink(void)
183eaea18cbSrsc {
184eaea18cbSrsc   struct inode *ip, *dp;
185eaea18cbSrsc   struct dirent de;
186eaea18cbSrsc   char name[DIRSIZ], *path;
187eaea18cbSrsc   uint off;
188eaea18cbSrsc 
189eaea18cbSrsc   if(argstr(0, &path) < 0)
190eaea18cbSrsc     return -1;
1915053dd6aSRobert Morris 
1925053dd6aSRobert Morris   begin_trans();
193*2c565472SRobert Morris   if((dp = nameiparent(path, name)) == 0){
194*2c565472SRobert Morris     commit_trans();
195*2c565472SRobert Morris     return -1;
196*2c565472SRobert Morris   }
1975053dd6aSRobert Morris 
19807090dd7Srsc   ilock(dp);
199eaea18cbSrsc 
200eaea18cbSrsc   // Cannot unlink "." or "..".
2014e015d81SAustin Clements   if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0)
2024e015d81SAustin Clements     goto bad;
203eaea18cbSrsc 
2044e015d81SAustin Clements   if((ip = dirlookup(dp, name, &off)) == 0)
2054e015d81SAustin Clements     goto bad;
20607090dd7Srsc   ilock(ip);
207eaea18cbSrsc 
208eaea18cbSrsc   if(ip->nlink < 1)
209eaea18cbSrsc     panic("unlink: nlink < 1");
210eaea18cbSrsc   if(ip->type == T_DIR && !isdirempty(ip)){
21107090dd7Srsc     iunlockput(ip);
2124e015d81SAustin Clements     goto bad;
213eaea18cbSrsc   }
214eaea18cbSrsc 
215eaea18cbSrsc   memset(&de, 0, sizeof(de));
216eaea18cbSrsc   if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
217eaea18cbSrsc     panic("unlink: writei");
21815a26936Skolya   if(ip->type == T_DIR){
21915a26936Skolya     dp->nlink--;
22015a26936Skolya     iupdate(dp);
22115a26936Skolya   }
22207090dd7Srsc   iunlockput(dp);
223eaea18cbSrsc 
224eaea18cbSrsc   ip->nlink--;
225eaea18cbSrsc   iupdate(ip);
22607090dd7Srsc   iunlockput(ip);
2272e590463SRobert Morris 
2282e590463SRobert Morris   commit_trans();
2292e590463SRobert Morris 
230eaea18cbSrsc   return 0;
2314e015d81SAustin Clements 
2324e015d81SAustin Clements bad:
2334e015d81SAustin Clements   iunlockput(dp);
2344e015d81SAustin Clements   commit_trans();
2354e015d81SAustin Clements   return -1;
236eaea18cbSrsc }
237eaea18cbSrsc 
238eaea18cbSrsc static struct inode*
239f3685aa3Srsc create(char *path, short type, short major, short minor)
240eaea18cbSrsc {
241eaea18cbSrsc   uint off;
242eaea18cbSrsc   struct inode *ip, *dp;
243eaea18cbSrsc   char name[DIRSIZ];
244eaea18cbSrsc 
24507090dd7Srsc   if((dp = nameiparent(path, name)) == 0)
246eaea18cbSrsc     return 0;
24707090dd7Srsc   ilock(dp);
248eaea18cbSrsc 
249f3685aa3Srsc   if((ip = dirlookup(dp, name, &off)) != 0){
25007090dd7Srsc     iunlockput(dp);
25107090dd7Srsc     ilock(ip);
252f9a06440SRuss Cox     if(type == T_FILE && ip->type == T_FILE)
253f9a06440SRuss Cox       return ip;
25407090dd7Srsc     iunlockput(ip);
255eaea18cbSrsc     return 0;
256eaea18cbSrsc   }
257eaea18cbSrsc 
258f9a06440SRuss Cox   if((ip = ialloc(dp->dev, type)) == 0)
259f9a06440SRuss Cox     panic("create: ialloc");
260f9a06440SRuss Cox 
26107090dd7Srsc   ilock(ip);
262eaea18cbSrsc   ip->major = major;
263eaea18cbSrsc   ip->minor = minor;
264603deefcSrsc   ip->nlink = 1;
265eaea18cbSrsc   iupdate(ip);
266eaea18cbSrsc 
267eaea18cbSrsc   if(type == T_DIR){  // Create . and .. entries.
268eaea18cbSrsc     dp->nlink++;  // for ".."
269eaea18cbSrsc     iupdate(dp);
270eaea18cbSrsc     // No ip->nlink++ for ".": avoid cyclic ref count.
271eaea18cbSrsc     if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0)
272e79b1659Srsc       panic("create dots");
273eaea18cbSrsc   }
274f3e87bc8Skolya 
275f3685aa3Srsc   if(dirlink(dp, name, ip->inum) < 0)
276f3685aa3Srsc     panic("create: dirlink");
277f3e87bc8Skolya 
27807090dd7Srsc   iunlockput(dp);
2792e590463SRobert Morris 
280eaea18cbSrsc   return ip;
281eaea18cbSrsc }
282eaea18cbSrsc 
2832685309fSrsc int
2842685309fSrsc sys_open(void)
2852685309fSrsc {
286f32f3638Srsc   char *path;
287f32f3638Srsc   int fd, omode;
288224f6598Srsc   struct file *f;
289f32f3638Srsc   struct inode *ip;
2902685309fSrsc 
291224f6598Srsc   if(argstr(0, &path) < 0 || argint(1, &omode) < 0)
2922685309fSrsc     return -1;
293*2c565472SRobert Morris 
2942e590463SRobert Morris   begin_trans();
295*2c565472SRobert Morris 
296*2c565472SRobert Morris   if(omode & O_CREATE){
2972e590463SRobert Morris     ip = create(path, T_FILE, 0, 0);
298*2c565472SRobert Morris     if(ip == 0){
2992e590463SRobert Morris       commit_trans();
300e2a620daSrsc       return -1;
301*2c565472SRobert Morris     }
302eaea18cbSrsc   } else {
303*2c565472SRobert Morris     if((ip = namei(path)) == 0){
304*2c565472SRobert Morris       commit_trans();
305eaea18cbSrsc       return -1;
306*2c565472SRobert Morris     }
30707090dd7Srsc     ilock(ip);
308f3685aa3Srsc     if(ip->type == T_DIR && omode != O_RDONLY){
30907090dd7Srsc       iunlockput(ip);
310*2c565472SRobert Morris       commit_trans();
3112685309fSrsc       return -1;
3122685309fSrsc     }
313eaea18cbSrsc   }
3142685309fSrsc 
315eaea18cbSrsc   if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
316eaea18cbSrsc     if(f)
317224f6598Srsc       fileclose(f);
31807090dd7Srsc     iunlockput(ip);
319*2c565472SRobert Morris     commit_trans();
3202685309fSrsc     return -1;
3212685309fSrsc   }
32207090dd7Srsc   iunlock(ip);
323*2c565472SRobert Morris   commit_trans();
3242685309fSrsc 
325eaea18cbSrsc   f->type = FD_INODE;
32607090dd7Srsc   f->ip = ip;
327eaea18cbSrsc   f->off = 0;
328e79b1659Srsc   f->readable = !(omode & O_WRONLY);
329e79b1659Srsc   f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
330224f6598Srsc   return fd;
3312685309fSrsc }
3322685309fSrsc 
3332685309fSrsc int
334f9a06440SRuss Cox sys_mkdir(void)
335f9a06440SRuss Cox {
336f9a06440SRuss Cox   char *path;
337f9a06440SRuss Cox   struct inode *ip;
338f9a06440SRuss Cox 
3392e590463SRobert Morris   begin_trans();
3402e590463SRobert Morris   if(argstr(0, &path) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){
3412e590463SRobert Morris     commit_trans();
342f9a06440SRuss Cox     return -1;
3432e590463SRobert Morris   }
344f9a06440SRuss Cox   iunlockput(ip);
3452e590463SRobert Morris   commit_trans();
346f9a06440SRuss Cox   return 0;
347f9a06440SRuss Cox }
348f9a06440SRuss Cox 
349f9a06440SRuss Cox int
3502685309fSrsc sys_mknod(void)
3512685309fSrsc {
352eaea18cbSrsc   struct inode *ip;
353224f6598Srsc   char *path;
354224f6598Srsc   int len;
355aa6824abSrsc   int major, minor;
3562685309fSrsc 
3572e590463SRobert Morris   begin_trans();
35807090dd7Srsc   if((len=argstr(0, &path)) < 0 ||
35907090dd7Srsc      argint(1, &major) < 0 ||
36007090dd7Srsc      argint(2, &minor) < 0 ||
3612e590463SRobert Morris      (ip = create(path, T_DEV, major, minor)) == 0){
3622e590463SRobert Morris     commit_trans();
3632685309fSrsc     return -1;
3642e590463SRobert Morris   }
36507090dd7Srsc   iunlockput(ip);
3662e590463SRobert Morris   commit_trans();
367224f6598Srsc   return 0;
3682685309fSrsc }
3692685309fSrsc 
3702685309fSrsc int
3712685309fSrsc sys_chdir(void)
3722685309fSrsc {
373eaea18cbSrsc   char *path;
3742685309fSrsc   struct inode *ip;
3752685309fSrsc 
376*2c565472SRobert Morris   begin_trans();
377*2c565472SRobert Morris   if(argstr(0, &path) < 0 || (ip = namei(path)) == 0){
378*2c565472SRobert Morris     commit_trans();
3792685309fSrsc     return -1;
380*2c565472SRobert Morris   }
38107090dd7Srsc   ilock(ip);
3822685309fSrsc   if(ip->type != T_DIR){
38307090dd7Srsc     iunlockput(ip);
384*2c565472SRobert Morris     commit_trans();
3852685309fSrsc     return -1;
3862685309fSrsc   }
38707090dd7Srsc   iunlock(ip);
38848755214SRuss Cox   iput(proc->cwd);
389*2c565472SRobert Morris   commit_trans();
39048755214SRuss Cox   proc->cwd = ip;
3912685309fSrsc   return 0;
3922685309fSrsc }
3932685309fSrsc 
3942685309fSrsc int
3952685309fSrsc sys_exec(void)
3962685309fSrsc {
3974655d42eSRobert Morris   char *path, *argv[MAXARG];
398f32f3638Srsc   int i;
399f32f3638Srsc   uint uargv, uarg;
4002685309fSrsc 
40140889627SFrans Kaashoek   if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){
4022685309fSrsc     return -1;
40340889627SFrans Kaashoek   }
404eaea18cbSrsc   memset(argv, 0, sizeof(argv));
4052685309fSrsc   for(i=0;; i++){
406eaea18cbSrsc     if(i >= NELEM(argv))
4072685309fSrsc       return -1;
4089d59eb01SAustin Clements     if(fetchint(uargv+4*i, (int*)&uarg) < 0)
409f32f3638Srsc       return -1;
410f32f3638Srsc     if(uarg == 0){
411f32f3638Srsc       argv[i] = 0;
412f32f3638Srsc       break;
4132685309fSrsc     }
4149d59eb01SAustin Clements     if(fetchstr(uarg, &argv[i]) < 0)
415f32f3638Srsc       return -1;
416f32f3638Srsc   }
417f32f3638Srsc   return exec(path, argv);
418f32f3638Srsc }
419f32f3638Srsc 
420eaea18cbSrsc int
421eaea18cbSrsc sys_pipe(void)
422eaea18cbSrsc {
423eaea18cbSrsc   int *fd;
424eaea18cbSrsc   struct file *rf, *wf;
425eaea18cbSrsc   int fd0, fd1;
426eaea18cbSrsc 
427eaea18cbSrsc   if(argptr(0, (void*)&fd, 2*sizeof(fd[0])) < 0)
428eaea18cbSrsc     return -1;
4297834cca6Srsc   if(pipealloc(&rf, &wf) < 0)
430eaea18cbSrsc     return -1;
431eaea18cbSrsc   fd0 = -1;
432eaea18cbSrsc   if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){
433eaea18cbSrsc     if(fd0 >= 0)
43448755214SRuss Cox       proc->ofile[fd0] = 0;
435eaea18cbSrsc     fileclose(rf);
436eaea18cbSrsc     fileclose(wf);
437eaea18cbSrsc     return -1;
438eaea18cbSrsc   }
439eaea18cbSrsc   fd[0] = fd0;
440eaea18cbSrsc   fd[1] = fd1;
441eaea18cbSrsc   return 0;
442eaea18cbSrsc }
443