kernel/sysfile.c

505 lines
1//2// File-system system calls.3// Mostly argument checking, since we don't trust4// user code, and calls into file.c and fs.c.5//67#include "types.h"8#include "riscv.h"9#include "defs.h"10#include "param.h"11#include "stat.h"12#include "spinlock.h"13#include "proc.h"14#include "fs.h"15#include "sleeplock.h"16#include "file.h"17#include "fcntl.h"1819// Fetch the nth word-sized system call argument as a file descriptor20// and return both the descriptor and the corresponding struct file.21static int22argfd(int n, int *pfd, struct file **pf)23{24  int fd;25  struct file *f;2627  argint(n, &fd);28  if(fd < 0 || fd >= NOFILE || (f=myproc()->ofile[fd]) == 0)29    return -1;30  if(pfd)31    *pfd = fd;32  if(pf)33    *pf = f;34  return 0;35}3637// Allocate a file descriptor for the given file.38// Takes over file reference from caller on success.39static int40fdalloc(struct file *f)41{42  int fd;43  struct proc *p = myproc();4445  for(fd = 0; fd < NOFILE; fd++){46    if(p->ofile[fd] == 0){47      p->ofile[fd] = f;48      return fd;49    }50  }51  return -1;52}5354uint6455sys_dup(void)56{57  struct file *f;58  int fd;5960  if(argfd(0, 0, &f) < 0)61    return -1;62  if((fd=fdalloc(f)) < 0)63    return -1;64  filedup(f);65  return fd;66}6768uint6469sys_read(void)70{71  struct file *f;72  int n;73  uint64 p;7475  argaddr(1, &p);76  argint(2, &n);77  if(argfd(0, 0, &f) < 0)78    return -1;79  return fileread(f, p, n);80}8182uint6483sys_write(void)84{85  struct file *f;86  int n;87  uint64 p;88  89  argaddr(1, &p);90  argint(2, &n);91  if(argfd(0, 0, &f) < 0)92    return -1;9394  return filewrite(f, p, n);95}9697uint6498sys_close(void)99{100  int fd;101  struct file *f;102103  if(argfd(0, &fd, &f) < 0)104    return -1;105  myproc()->ofile[fd] = 0;106  fileclose(f);107  return 0;108}109110uint64111sys_fstat(void)112{113  struct file *f;114  uint64 st; // user pointer to struct stat115116  argaddr(1, &st);117  if(argfd(0, 0, &f) < 0)118    return -1;119  return filestat(f, st);120}121122// Create the path new as a link to the same inode as old.123uint64124sys_link(void)125{126  char name[DIRSIZ], new[MAXPATH], old[MAXPATH];127  struct inode *dp, *ip;128129  if(argstr(0, old, MAXPATH) < 0 || argstr(1, new, MAXPATH) < 0)130    return -1;131132  begin_op();133  if((ip = namei(old)) == 0){134    end_op();135    return -1;136  }137138  ilock(ip);139  if(ip->type == T_DIR){140    iunlockput(ip);141    end_op();142    return -1;143  }144145  ip->nlink++;146  iupdate(ip);147  iunlock(ip);148149  if((dp = nameiparent(new, name)) == 0)150    goto bad;151  ilock(dp);152  if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){153    iunlockput(dp);154    goto bad;155  }156  iunlockput(dp);157  iput(ip);158159  end_op();160161  return 0;162163bad:164  ilock(ip);165  ip->nlink--;166  iupdate(ip);167  iunlockput(ip);168  end_op();169  return -1;170}171172// Is the directory dp empty except for "." and ".." ?173static int174isdirempty(struct inode *dp)175{176  int off;177  struct dirent de;178179  for(off=2*sizeof(de); off<dp->size; off+=sizeof(de)){180    if(readi(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))181      panic("isdirempty: readi");182    if(de.inum != 0)183      return 0;184  }185  return 1;186}187188uint64189sys_unlink(void)190{191  struct inode *ip, *dp;192  struct dirent de;193  char name[DIRSIZ], path[MAXPATH];194  uint off;195196  if(argstr(0, path, MAXPATH) < 0)197    return -1;198199  begin_op();200  if((dp = nameiparent(path, name)) == 0){201    end_op();202    return -1;203  }204205  ilock(dp);206207  // Cannot unlink "." or "..".208  if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0)209    goto bad;210211  if((ip = dirlookup(dp, name, &off)) == 0)212    goto bad;213  ilock(ip);214215  if(ip->nlink < 1)216    panic("unlink: nlink < 1");217  if(ip->type == T_DIR && !isdirempty(ip)){218    iunlockput(ip);219    goto bad;220  }221222  memset(&de, 0, sizeof(de));223  if(writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))224    panic("unlink: writei");225  if(ip->type == T_DIR){226    dp->nlink--;227    iupdate(dp);228  }229  iunlockput(dp);230231  ip->nlink--;232  iupdate(ip);233  iunlockput(ip);234235  end_op();236237  return 0;238239bad:240  iunlockput(dp);241  end_op();242  return -1;243}244245static struct inode*246create(char *path, short type, short major, short minor)247{248  struct inode *ip, *dp;249  char name[DIRSIZ];250251  if((dp = nameiparent(path, name)) == 0)252    return 0;253254  ilock(dp);255256  if((ip = dirlookup(dp, name, 0)) != 0){257    iunlockput(dp);258    ilock(ip);259    if(type == T_FILE && (ip->type == T_FILE || ip->type == T_DEVICE))260      return ip;261    iunlockput(ip);262    return 0;263  }264265  if((ip = ialloc(dp->dev, type)) == 0){266    iunlockput(dp);267    return 0;268  }269270  ilock(ip);271  ip->major = major;272  ip->minor = minor;273  ip->nlink = 1;274  iupdate(ip);275276  if(type == T_DIR){  // Create . and .. entries.277    // No ip->nlink++ for ".": avoid cyclic ref count.278    if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0)279      goto fail;280  }281282  if(dirlink(dp, name, ip->inum) < 0)283    goto fail;284285  if(type == T_DIR){286    // now that success is guaranteed:287    dp->nlink++;  // for ".."288    iupdate(dp);289  }290291  iunlockput(dp);292293  return ip;294295 fail:296  // something went wrong. de-allocate ip.297  ip->nlink = 0;298  iupdate(ip);299  iunlockput(ip);300  iunlockput(dp);301  return 0;302}303304uint64305sys_open(void)306{307  char path[MAXPATH];308  int fd, omode;309  struct file *f;310  struct inode *ip;311  int n;312313  argint(1, &omode);314  if((n = argstr(0, path, MAXPATH)) < 0)315    return -1;316317  begin_op();318319  if(omode & O_CREATE){320    ip = create(path, T_FILE, 0, 0);321    if(ip == 0){322      end_op();323      return -1;324    }325  } else {326    if((ip = namei(path)) == 0){327      end_op();328      return -1;329    }330    ilock(ip);331    if(ip->type == T_DIR && omode != O_RDONLY){332      iunlockput(ip);333      end_op();334      return -1;335    }336  }337338  if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){339    iunlockput(ip);340    end_op();341    return -1;342  }343344  if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){345    if(f)346      fileclose(f);347    iunlockput(ip);348    end_op();349    return -1;350  }351352  if(ip->type == T_DEVICE){353    f->type = FD_DEVICE;354    f->major = ip->major;355  } else {356    f->type = FD_INODE;357    f->off = 0;358  }359  f->ip = ip;360  f->readable = !(omode & O_WRONLY);361  f->writable = (omode & O_WRONLY) || (omode & O_RDWR);362363  if((omode & O_TRUNC) && ip->type == T_FILE){364    itrunc(ip);365  }366367  iunlock(ip);368  end_op();369370  return fd;371}372373uint64374sys_mkdir(void)375{376  char path[MAXPATH];377  struct inode *ip;378379  begin_op();380  if(argstr(0, path, MAXPATH) < 0 || (ip = create(path, T_DIR, 0, 0)) == 0){381    end_op();382    return -1;383  }384  iunlockput(ip);385  end_op();386  return 0;387}388389uint64390sys_mknod(void)391{392  struct inode *ip;393  char path[MAXPATH];394  int major, minor;395396  begin_op();397  argint(1, &major);398  argint(2, &minor);399  if((argstr(0, path, MAXPATH)) < 0 ||400     (ip = create(path, T_DEVICE, major, minor)) == 0){401    end_op();402    return -1;403  }404  iunlockput(ip);405  end_op();406  return 0;407}408409uint64410sys_chdir(void)411{412  char path[MAXPATH];413  struct inode *ip;414  struct proc *p = myproc();415  416  begin_op();417  if(argstr(0, path, MAXPATH) < 0 || (ip = namei(path)) == 0){418    end_op();419    return -1;420  }421  ilock(ip);422  if(ip->type != T_DIR){423    iunlockput(ip);424    end_op();425    return -1;426  }427  iunlock(ip);428  iput(p->cwd);429  end_op();430  p->cwd = ip;431  return 0;432}433434uint64435sys_exec(void)436{437  char path[MAXPATH], *argv[MAXARG];438  int i;439  uint64 uargv, uarg;440441  argaddr(1, &uargv);442  if(argstr(0, path, MAXPATH) < 0) {443    return -1;444  }445  memset(argv, 0, sizeof(argv));446  for(i=0;; i++){447    if(i >= NELEM(argv)){448      goto bad;449    }450    if(fetchaddr(uargv+sizeof(uint64)*i, (uint64*)&uarg) < 0){451      goto bad;452    }453    if(uarg == 0){454      argv[i] = 0;455      break;456    }457    argv[i] = kalloc();458    if(argv[i] == 0)459      goto bad;460    if(fetchstr(uarg, argv[i], PGSIZE) < 0)461      goto bad;462  }463464  int ret = kexec(path, argv);465466  for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)467    kfree(argv[i]);468469  return ret;470471 bad:472  for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)473    kfree(argv[i]);474  return -1;475}476477uint64478sys_pipe(void)479{480  uint64 fdarray; // user pointer to array of two integers481  struct file *rf, *wf;482  int fd0, fd1;483  struct proc *p = myproc();484485  argaddr(0, &fdarray);486  if(pipealloc(&rf, &wf) < 0)487    return -1;488  fd0 = -1;489  if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){490    if(fd0 >= 0)491      p->ofile[fd0] = 0;492    fileclose(rf);493    fileclose(wf);494    return -1;495  }496  if(copyout(p->pagetable, fdarray, (char*)&fd0, sizeof(fd0)) < 0 ||497     copyout(p->pagetable, fdarray+sizeof(fd0), (char *)&fd1, sizeof(fd1)) < 0){498    p->ofile[fd0] = 0;499    p->ofile[fd1] = 0;500    fileclose(rf);501    fileclose(wf);502    return -1;503  }504  return 0;505}506