-/* vfs_syscalls.c 4.31 82/07/25 */
+/* vfs_syscalls.c 6.11 84/07/08 */
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/user.h"
+#include "../h/kernel.h"
#include "../h/file.h"
#include "../h/stat.h"
#include "../h/inode.h"
#include "../h/fs.h"
#include "../h/buf.h"
#include "../h/proc.h"
-#include "../h/inline.h"
-#ifdef EFS
-#include "../net/in.h"
-#include "../h/efs.h"
-#endif
#include "../h/quota.h"
-#include "../h/descrip.h"
+#include "../h/uio.h"
+#include "../h/socket.h"
+#include "../h/socketvar.h"
+#include "../h/mount.h"
+
+extern struct fileops inodeops;
+struct file *getinode();
+/*
+ * Change current working directory (``.'').
+ */
chdir()
{
chdirec(&u.u_cdir);
}
+/*
+ * Change notion of root (``/'') directory.
+ */
chroot()
{
chdirec(&u.u_rdir);
}
+/*
+ * Common routine for chroot and chdir.
+ */
chdirec(ipp)
-register struct inode **ipp;
+ register struct inode **ipp;
{
register struct inode *ip;
struct a {
char *fname;
- };
+ } *uap = (struct a *)u.u_ap;
+ register struct nameidata *ndp = &u.u_nd;
- ip = namei(uchar, 0, 1);
- if(ip == NULL)
+ ndp->ni_nameiop = LOOKUP | FOLLOW;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->fname;
+ ip = namei(ndp);
+ if (ip == NULL)
return;
- if((ip->i_mode&IFMT) != IFDIR) {
+ if ((ip->i_mode&IFMT) != IFDIR) {
u.u_error = ENOTDIR;
goto bad;
}
- if(access(ip, IEXEC))
+ if (access(ip, IEXEC))
goto bad;
- iunlock(ip);
+ IUNLOCK(ip);
if (*ipp)
irele(*ipp);
*ipp = ip;
*/
open()
{
- register struct inode *ip;
- register struct a {
+ struct a {
char *fname;
- int rwmode;
- } *uap;
+ int mode;
+ int crtmode;
+ } *uap = (struct a *) u.u_ap;
- uap = (struct a *)u.u_ap;
- ip = namei(uchar, 0, 1);
- if (ip == NULL)
- return;
- open1(ip, ++uap->rwmode, 0);
+ copen(uap->mode-FOPEN, uap->crtmode, uap->fname);
}
/*
* Creat system call.
*/
-ocreat()
+creat()
{
- register struct inode *ip;
- register struct a {
+ struct a {
char *fname;
int fmode;
- } *uap;
+ } *uap = (struct a *)u.u_ap;
- uap = (struct a *)u.u_ap;
- ip = namei(uchar, 1, 1);
- if (ip == NULL) {
- if (u.u_error)
- return;
- ip = maknode(uap->fmode&07777&(~ISVTX));
- if (ip==NULL)
- return;
- open1(ip, FWRITE, 2);
- } else
- open1(ip, FWRITE, 1);
+ copen(FWRITE|FCREAT|FTRUNC, uap->fmode, uap->fname);
}
/*
* Check permissions, allocate an open file structure,
* and call the device open routine if any.
*/
-open1(ip, mode, trf)
- register struct inode *ip;
- register mode;
+copen(mode, arg, fname)
+ register int mode;
+ int arg;
+ caddr_t fname;
{
+ register struct inode *ip;
register struct file *fp;
+ register struct nameidata *ndp = &u.u_nd;
int i;
- if (trf != 2) {
+#ifdef notdef
+ if ((mode&(FREAD|FWRITE)) == 0) {
+ u.u_error = EINVAL;
+ return;
+ }
+#endif
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = fname;
+ if (mode&FCREAT) {
+ ndp->ni_nameiop = CREATE | FOLLOW;
+ ip = namei(ndp);
+ if (ip == NULL) {
+ if (u.u_error)
+ return;
+ ip = maknode(arg&07777&(~ISVTX), ndp);
+ if (ip == NULL)
+ return;
+ mode &= ~FTRUNC;
+ } else {
+ if (mode&FEXCL) {
+ u.u_error = EEXIST;
+ iput(ip);
+ return;
+ }
+ mode &= ~FCREAT;
+ }
+ } else {
+ ndp->ni_nameiop = LOOKUP | FOLLOW;
+ ip = namei(ndp);
+ if (ip == NULL)
+ return;
+ }
+ if ((ip->i_mode & IFMT) == IFSOCK) {
+ u.u_error = EOPNOTSUPP;
+ goto bad;
+ }
+ if ((mode&FCREAT) == 0) {
if (mode&FREAD)
- (void) access(ip, IREAD);
- if (mode&FWRITE) {
- (void) access(ip, IWRITE);
- if ((ip->i_mode&IFMT) == IFDIR)
+ if (access(ip, IREAD))
+ goto bad;
+ if (mode&(FWRITE|FTRUNC)) {
+ if (access(ip, IWRITE))
+ goto bad;
+ if ((ip->i_mode&IFMT) == IFDIR) {
u.u_error = EISDIR;
+ goto bad;
+ }
}
}
- if (u.u_error) {
- iput(ip);
+ fp = falloc();
+ if (fp == NULL)
+ goto bad;
+ if (mode&FTRUNC)
+ itrunc(ip, (u_long)0);
+ IUNLOCK(ip);
+ fp->f_flag = mode&FMASK;
+ fp->f_type = DTYPE_INODE;
+ fp->f_ops = &inodeops;
+ fp->f_data = (caddr_t)ip;
+ i = u.u_r.r_val1;
+ if (setjmp(&u.u_qsave)) {
+ if (u.u_error == 0)
+ u.u_error = EINTR;
+ u.u_ofile[i] = NULL;
+ closef(fp);
return;
}
- if (trf == 1)
- itrunc(ip);
- iunlock(ip);
- if ((fp = falloc()) == NULL)
- goto out;
- fp->f_flag = mode&(FREAD|FWRITE);
- fp->f_type = DTYPE_FILE;
- i = u.u_r.r_val1;
- fp->f_inode = ip;
-#ifdef EFS
- openi(ip, mode&(FREAD|FWRITE), trf);
-#else
- openi(ip, mode&(FREAD|FWRITE));
-#endif
+ u.u_error = openi(ip, mode);
if (u.u_error == 0)
return;
u.u_ofile[i] = NULL;
fp->f_count--;
-out:
irele(ip);
+ return;
+bad:
+ iput(ip);
}
/*
char *fname;
int fmode;
int dev;
- } *uap;
+ } *uap = (struct a *)u.u_ap;
+ register struct nameidata *ndp = &u.u_nd;
- uap = (struct a *)u.u_ap;
- if (suser()) {
- ip = namei(uchar, 1, 0);
- if (ip != NULL) {
- u.u_error = EEXIST;
- goto out;
- }
+ if (!suser())
+ return;
+ ndp->ni_nameiop = CREATE;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->fname;
+ ip = namei(ndp);
+ if (ip != NULL) {
+ u.u_error = EEXIST;
+ goto out;
}
if (u.u_error)
return;
- ip = maknode(uap->fmode);
+ ip = maknode(uap->fmode, ndp);
if (ip == NULL)
return;
- if (uap->dev) {
- /*
- * Want to be able to use this to make badblock
- * inodes, so don't truncate the dev number.
- */
- ip->i_rdev = uap->dev;
- ip->i_flag |= IACC|IUPD|ICHG;
+ switch (ip->i_mode & IFMT) {
+
+ case IFMT: /* used by badsect to flag bad sectors */
+ case IFCHR:
+ case IFBLK:
+ if (uap->dev) {
+ /*
+ * Want to be able to use this to make badblock
+ * inodes, so don't truncate the dev number.
+ */
+ ip->i_rdev = uap->dev;
+ ip->i_flag |= IACC|IUPD|ICHG;
+ }
}
out:
register struct a {
char *target;
char *linkname;
- } *uap;
+ } *uap = (struct a *)u.u_ap;
+ register struct nameidata *ndp = &u.u_nd;
- uap = (struct a *)u.u_ap;
- ip = namei(uchar, 0, 1); /* well, this routine is doomed anyhow */
+ ndp->ni_nameiop = LOOKUP | FOLLOW;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->target;
+ ip = namei(ndp); /* well, this routine is doomed anyhow */
if (ip == NULL)
return;
- if ((ip->i_mode&IFMT)==IFDIR && !suser()) {
+ if ((ip->i_mode&IFMT) == IFDIR && !suser()) {
iput(ip);
return;
}
ip->i_nlink++;
ip->i_flag |= ICHG;
iupdat(ip, &time, &time, 1);
- iunlock(ip);
- u.u_dirp = (caddr_t)uap->linkname;
- xp = namei(uchar, 1, 0);
+ IUNLOCK(ip);
+ ndp->ni_nameiop = CREATE;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = (caddr_t)uap->linkname;
+ xp = namei(ndp);
if (xp != NULL) {
u.u_error = EEXIST;
iput(xp);
}
if (u.u_error)
goto out;
- if (u.u_pdir->i_dev != ip->i_dev) {
- iput(u.u_pdir);
+ if (ndp->ni_pdir->i_dev != ip->i_dev) {
+ iput(ndp->ni_pdir);
u.u_error = EXDEV;
goto out;
}
- direnter(ip);
+ u.u_error = direnter(ip, ndp);
out:
if (u.u_error) {
ip->i_nlink--;
ip->i_flag |= ICHG;
}
-out1:
irele(ip);
}
register struct a {
char *target;
char *linkname;
- } *uap;
+ } *uap = (struct a *)u.u_ap;
register struct inode *ip;
register char *tp;
register c, nc;
+ register struct nameidata *ndp = &u.u_nd;
- uap = (struct a *)u.u_ap;
tp = uap->target;
nc = 0;
while (c = fubyte(tp)) {
tp++;
nc++;
}
- u.u_dirp = uap->linkname;
- ip = namei(uchar, 1, 0);
+ ndp->ni_nameiop = CREATE;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->linkname;
+ ip = namei(ndp);
if (ip) {
iput(ip);
u.u_error = EEXIST;
}
if (u.u_error)
return;
- ip = maknode(IFLNK | 0777);
+ ip = maknode(IFLNK | 0777, ndp);
if (ip == NULL)
return;
- u.u_base = uap->target;
- u.u_count = nc;
- u.u_offset = 0;
- u.u_segflg = 0;
- writei(ip);
+ u.u_error = rdwri(UIO_WRITE, ip, uap->target, nc, 0, 0, (int *)0);
+ /* handle u.u_error != 0 */
iput(ip);
}
*/
unlink()
{
- register struct inode *ip, *pp;
struct a {
char *fname;
- };
- struct fs *fs;
- struct buf *bp;
- int lbn, bn, base;
- int unlinkingdot = 0;
-
- pp = namei(uchar, 2, 0);
- if (pp == NULL)
- return;
-#ifdef EFS
- /* divert to extended file system if off machine. */
- if (efsinode(pp)) {
- dev_t ndev = pp->i_rdev;
-
- iput(pp); /* avoid recursive hang on inode */
- efsunlink(ndev);
- if (u.u_error != EEXIST)
- return;
-
- /*
- * If a null pathname remainder, then do
- * the unlink locally after restoring state.
- */
- u.u_error = 0;
- u.u_dirp = (caddr_t)u.u_arg[0];
- pp = namei(uchar, 2, 0);
- }
-#endif
+ } *uap = (struct a *)u.u_ap;
+ register struct inode *ip, *dp;
+ register struct nameidata *ndp = &u.u_nd;
- /*
- * Check for unlink(".")
- * to avoid hanging on the iget
- */
- if (pp->i_number == u.u_dent.d_ino) {
- ip = pp;
- ip->i_count++;
- unlinkingdot++;
- } else
- ip = iget(pp->i_dev, pp->i_fs, u.u_dent.d_ino);
- if(ip == NULL)
- goto out1;
- if((ip->i_mode&IFMT)==IFDIR && !suser())
+ ndp->ni_nameiop = DELETE | LOCKPARENT;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->fname;
+ ip = namei(ndp);
+ if (ip == NULL)
+ return;
+ dp = ndp->ni_pdir;
+ if ((ip->i_mode&IFMT) == IFDIR && !suser())
goto out;
/*
* Don't unlink a mounted file.
*/
- if (ip->i_dev != pp->i_dev) {
+ if (ip->i_dev != dp->i_dev) {
u.u_error = EBUSY;
goto out;
}
if (ip->i_flag&ITEXT)
xrele(ip); /* try once to free text */
- if (dirremove()) {
+ if (dirremove(ndp)) {
ip->i_nlink--;
ip->i_flag |= ICHG;
}
out:
- if (unlinkingdot)
+ if (dp == ip)
irele(ip);
else
iput(ip);
-out1:
- iput(pp);
+ iput(dp);
}
/*
* Seek system call
*/
-seek()
+lseek()
{
register struct file *fp;
register struct a {
- int fdes;
+ int fd;
off_t off;
int sbase;
- } *uap;
+ } *uap = (struct a *)u.u_ap;
- uap = (struct a *)u.u_ap;
- fp = getf(uap->fdes);
- if (fp == NULL)
- return;
- if (fp->f_type == DTYPE_SOCKET) {
+ GETF(fp, uap->fd);
+ if (fp->f_type != DTYPE_INODE) {
u.u_error = ESPIPE;
return;
}
- if (uap->sbase == 1)
- uap->off += fp->f_offset;
- else if (uap->sbase == 2) {
-#ifdef EFS
- struct inode *ip = fp->f_inode;
- uap->off += efsinode(ip) ? efsfilesize(fp) : ip->i_size;
-#else
- uap->off += fp->f_inode->i_size;
-#endif
+ switch (uap->sbase) {
+
+ case L_INCR:
+ fp->f_offset += uap->off;
+ break;
+
+ case L_XTND:
+ fp->f_offset = uap->off + ((struct inode *)fp->f_data)->i_size;
+ break;
+
+ case L_SET:
+ fp->f_offset = uap->off;
+ break;
+
+ default:
+ u.u_error = EINVAL;
+ return;
}
- fp->f_offset = uap->off;
- u.u_r.r_off = uap->off;
+ u.u_r.r_off = fp->f_offset;
}
/*
register struct a {
char *fname;
int fmode;
- } *uap;
+ } *uap = (struct a *)u.u_ap;
+ register struct nameidata *ndp = &u.u_nd;
- uap = (struct a *)u.u_ap;
svuid = u.u_uid;
svgid = u.u_gid;
u.u_uid = u.u_ruid;
u.u_gid = u.u_rgid;
- ip = namei(uchar, 0, 1);
-#ifdef EFS
- if (efsinode(ip)) {
- dev_t ndev = ip->i_rdev;
-
- iput(ip);
- efssaccess(ndev);
- if (u.u_error != EEXIST)
- return;
- u.u_error = 0;
- u.u_dirp = (caddr_t)u.u_arg[0];
- ip = namei(uchar, 0, 1);
- }
-#endif
+ ndp->ni_nameiop = LOOKUP | FOLLOW;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->fname;
+ ip = namei(ndp);
if (ip != NULL) {
- if (uap->fmode&(IREAD>>6))
- (void) access(ip, IREAD);
- if (uap->fmode&(IWRITE>>6))
- (void) access(ip, IWRITE);
- if (uap->fmode&(IEXEC>>6))
- (void) access(ip, IEXEC);
+ if ((uap->fmode&R_OK) && access(ip, IREAD))
+ goto done;
+ if ((uap->fmode&W_OK) && access(ip, IWRITE))
+ goto done;
+ if ((uap->fmode&X_OK) && access(ip, IEXEC))
+ goto done;
+done:
iput(ip);
}
u.u_uid = svuid;
u.u_gid = svgid;
}
-/*
- * the fstat system call.
- */
-fstat()
-{
- register struct file *fp;
- register struct a {
- int fdes;
- struct stat *sb;
- } *uap;
-
- uap = (struct a *)u.u_ap;
- fp = getf(uap->fdes);
- if (fp == NULL)
- return;
-#ifdef EFS
- if (efsinode(fp->f_inode)) {
- efsfstat(fp->f_inode->i_rdev, fp);
- return;
- }
-#endif
- if (fp->f_type == DTYPE_SOCKET)
- u.u_error = sostat(fp->f_socket, uap->sb);
- else
- stat1(fp->f_inode, uap->sb);
-}
-
/*
* Stat system call. This version follows links.
*/
stat()
{
- register struct inode *ip;
- register struct a {
- char *fname;
- struct stat *sb;
- } *uap;
- uap = (struct a *)u.u_ap;
- ip = namei(uchar, 0, 1);
- if (ip == NULL)
- return;
-#ifdef EFS
- if (efsinode(ip)) {
- dev_t ndev = ip->i_rdev;
-
- iput(ip);
- efsstat(ndev);
- if (u.u_error != EEXIST)
- return;
- u.u_error = 0;
- u.u_dirp = (caddr_t)u.u_arg[0];
- ip = namei(uchar, 0, 1);
- }
-#endif
- stat1(ip, uap->sb);
- iput(ip);
+ stat1(FOLLOW);
}
/*
* Lstat system call. This version does not follow links.
*/
lstat()
+{
+
+ stat1(NOFOLLOW);
+}
+
+stat1(follow)
+ int follow;
{
register struct inode *ip;
register struct a {
char *fname;
- struct stat *sb;
- } *uap;
+ struct stat *ub;
+ } *uap = (struct a *)u.u_ap;
+ struct stat sb;
+ register struct nameidata *ndp = &u.u_nd;
- uap = (struct a *)u.u_ap;
- ip = namei(uchar, 0, 0);
+ ndp->ni_nameiop = LOOKUP | follow;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->fname;
+ ip = namei(ndp);
if (ip == NULL)
return;
-#ifdef EFS
- if (efsinode(ip)) {
- dev_t ndev = ip->i_rdev;
-
- iput(ip);
- efslstat(ndev);
- if (u.u_error != EEXIST)
- return;
- u.u_error = 0;
- u.u_dirp = (caddr_t)u.u_arg[0];
- ip = namei(uchar, 0, 0);
- }
-#endif
- stat1(ip, uap->sb);
+ (void) ino_stat(ip, &sb);
iput(ip);
-}
-
-/*
- * The basic routine for fstat and stat:
- * get the inode and pass appropriate parts back.
- */
-stat1(ip, ub)
- register struct inode *ip;
- struct stat *ub;
-{
- struct stat ds;
-
- IUPDAT(ip, &time, &time, 0);
- /*
- * Copy from inode table
- */
- ds.st_dev = ip->i_dev;
- ds.st_ino = ip->i_number;
- ds.st_mode = ip->i_mode;
- ds.st_nlink = ip->i_nlink;
- ds.st_uid = ip->i_uid;
- ds.st_gid = ip->i_gid;
- ds.st_rdev = (dev_t)ip->i_rdev;
- ds.st_size = ip->i_size;
- ds.st_atime = ip->i_atime;
- ds.st_mtime = ip->i_mtime;
- ds.st_ctime = ip->i_ctime;
- ds.st_blksize = ip->i_fs->fs_bsize;
- if (copyout((caddr_t)&ds, (caddr_t)ub, sizeof(ds)) < 0)
- u.u_error = EFAULT;
+ u.u_error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb));
}
/*
char *name;
char *buf;
int count;
- } *uap;
+ } *uap = (struct a *)u.u_ap;
+ register struct nameidata *ndp = &u.u_nd;
+ int resid;
- ip = namei(uchar, 0, 0);
+ ndp->ni_nameiop = LOOKUP;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->name;
+ ip = namei(ndp);
if (ip == NULL)
return;
-#ifdef EFS
- if (efsinode(ip)) {
- dev_t ndev = ip->i_rdev;
-
- iput(ip);
- efsreadlink(ndev);
- if (u.u_error != EEXIST)
- return;
- u.u_error = 0;
- u.u_dirp = (caddr_t)u.u_arg[0];
- ip = namei(uchar, 0, 0);
- return (0);
- }
-#endif
if ((ip->i_mode&IFMT) != IFLNK) {
u.u_error = ENXIO;
goto out;
}
- uap = (struct a *)u.u_ap;
- u.u_offset = 0;
- u.u_base = uap->buf;
- u.u_count = uap->count;
- u.u_segflg = 0;
- readi(ip);
+ u.u_error = rdwri(UIO_READ, ip, uap->buf, uap->count, 0, 0, &resid);
out:
iput(ip);
- u.u_r.r_val1 = uap->count - u.u_count;
+ u.u_r.r_val1 = uap->count - resid;
}
+/*
+ * Change mode of a file given path name.
+ */
chmod()
{
- register struct inode *ip;
- register struct a {
+ struct inode *ip;
+ struct a {
char *fname;
int fmode;
- } *uap;
+ } *uap = (struct a *)u.u_ap;
- uap = (struct a *)u.u_ap;
- if ((ip = owner(1)) == NULL)
+ if ((ip = owner(uap->fname, FOLLOW)) == NULL)
return;
-#ifdef EFS
- if (efsinode(ip)) {
- dev_t ndev = ip->i_rdev;
+ chmod1(ip, uap->fmode);
+ iput(ip);
+}
+
+/*
+ * Change mode of a file given a file descriptor.
+ */
+fchmod()
+{
+ struct a {
+ int fd;
+ int fmode;
+ } *uap = (struct a *)u.u_ap;
+ register struct inode *ip;
+ register struct file *fp;
+
+ fp = getinode(uap->fd);
+ if (fp == NULL)
+ return;
+ ip = (struct inode *)fp->f_data;
+ if (u.u_uid != ip->i_uid && !suser())
+ return;
+ ILOCK(ip);
+ chmod1(ip, uap->fmode);
+ IUNLOCK(ip);
+}
+
+/*
+ * Change the mode on a file.
+ * Inode must be locked before calling.
+ */
+chmod1(ip, mode)
+ register struct inode *ip;
+ register int mode;
+{
- iput(ip);
- efschmod(ndev);
- if (u.u_error != EEXIST)
- return;
- u.u_error = 0;
- u.u_dirp = (caddr_t)u.u_arg[0];
- ip = owner(1);
- }
-#endif
ip->i_mode &= ~07777;
if (u.u_uid) {
- uap->fmode &= ~ISVTX;
- if (ip->i_gid >= NGRPS ||
- (u.u_grps[ip->i_gid/(sizeof(int)*8)] &
- (1 << ip->i_gid%(sizeof(int)*8))) == 0)
- uap->fmode &= ~ISGID;
-#if MUSH
- if (u.u_quota->q_syflags & QF_UMASK && u.u_uid != 0 &&
- (ip->i_mode & IFMT) != IFCHR)
- uap->fmode &= ~u.u_cmask;
-#endif
+ mode &= ~ISVTX;
+ if (!groupmember(ip->i_gid))
+ mode &= ~ISGID;
}
- ip->i_mode |= uap->fmode&07777;
+ ip->i_mode |= mode&07777;
ip->i_flag |= ICHG;
if (ip->i_flag&ITEXT && (ip->i_mode&ISVTX)==0)
xrele(ip);
-#ifdef MELB
- if ((ip->i_mode & ISUID) && ip->i_uid == 0)
- printf("%s: ino %d (%s) setuid root\n"
- , getfs(ip->i_dev)->s_fsmnt
- , ip->i_number
- , u.u_dent.d_name
- );
-#endif
- iput(ip);
}
+/*
+ * Set ownership given a path name.
+ */
chown()
{
- register struct inode *ip;
- register struct a {
+ struct inode *ip;
+ struct a {
char *fname;
int uid;
int gid;
- } *uap;
-#if QUOTA
- register long change;
-#endif
+ } *uap = (struct a *)u.u_ap;
- uap = (struct a *)u.u_ap;
- if (!suser() || (ip = owner(0)) == NULL)
+ if (!suser() || (ip = owner(uap->fname, NOFOLLOW)) == NULL)
return;
-#ifdef EFS
- if (efsinode(ip)) {
- dev_t ndev = ip->i_rdev;
+ u.u_error = chown1(ip, uap->uid, uap->gid);
+ iput(ip);
+}
- iput(ip);
- efschown(ndev);
- if (u.u_error != EEXIST)
- return;
- u.u_error = 0;
- u.u_dirp = (caddr_t)u.u_arg[0];
- ip = owner(0);
- }
+/*
+ * Set ownership given a file descriptor.
+ */
+fchown()
+{
+ struct a {
+ int fd;
+ int uid;
+ int gid;
+ } *uap = (struct a *)u.u_ap;
+ register struct inode *ip;
+ register struct file *fp;
+
+ fp = getinode(uap->fd);
+ if (fp == NULL)
+ return;
+ ip = (struct inode *)fp->f_data;
+ if (!suser())
+ return;
+ ILOCK(ip);
+ u.u_error = chown1(ip, uap->uid, uap->gid);
+ IUNLOCK(ip);
+}
+
+/*
+ * Perform chown operation on inode ip;
+ * inode must be locked prior to call.
+ */
+chown1(ip, uid, gid)
+ register struct inode *ip;
+ int uid, gid;
+{
+#ifdef QUOTA
+ register long change;
#endif
-#if QUOTA
- /*
- * This doesn't allow for holes in files (which hopefully don't
- * happen often in files that we chown), and is not accurate anyway
- * (eg: it totally ignores 3 level indir blk files - but hopefully
- * noone who can make a file that big will have a quota)
- */
- if (ip->i_uid == uap->uid)
+
+ if (uid == -1)
+ uid = ip->i_uid;
+ if (gid == -1)
+ gid = ip->i_gid;
+#ifdef QUOTA
+ if (ip->i_uid == uid) /* this just speeds things a little */
change = 0;
- else {
- register struct fs *fs = ip->i_fs;
-
- if (ip->i_size > (change = NDADDR * fs->fs_bsize)) {
- register off_t size;
-
- size = blkroundup(fs, ip->i_size) - change;
- change += size;
- change += fs->fs_bsize;
- /* This assumes NIADDR <= 2 */
- if (size > NINDIR(fs) * fs->fs_bsize)
- change += fs->fs_bsize;
- } else
- change = fragroundup(fs, ip->i_size);
- change /= DEV_BSIZE;
- }
- chkdq(ip, -change, 1);
- chkiq(ip->i_dev, ip, ip->i_uid, 1);
+ else
+ change = ip->i_blocks;
+ (void) chkdq(ip, -change, 1);
+ (void) chkiq(ip->i_dev, ip, ip->i_uid, 1);
dqrele(ip->i_dquot);
#endif
- /*
- * keep uid/gid's in sane range - no err, so chown(file, uid, -1)
- * will do something useful
- */
- if (uap->uid >= 0 && uap->uid <= 32767) /* should have a const */
- ip->i_uid = uap->uid;
- if (uap->gid >= 0 && uap->gid <= 32767) /* same here */
- ip->i_gid = uap->gid;
+ ip->i_uid = uid;
+ ip->i_gid = gid;
ip->i_flag |= ICHG;
if (u.u_ruid != 0)
ip->i_mode &= ~(ISUID|ISGID);
-#if QUOTA
+#ifdef QUOTA
ip->i_dquot = inoquota(ip);
- chkdq(ip, change, 1);
- chkiq(ip->i_dev, NULL, uap->uid, 1);
+ (void) chkdq(ip, change, 1);
+ (void) chkiq(ip->i_dev, (struct inode *)NULL, uid, 1);
+ return (u.u_error); /* should == 0 ALWAYS !! */
+#else
+ return (0);
#endif
- iput(ip);
}
-/*
- * Set IUPD and IACC times on file.
- * Can't set ICHG.
- */
-utime()
+utimes()
{
register struct a {
char *fname;
- time_t *tptr;
- } *uap;
+ struct timeval *tptr;
+ } *uap = (struct a *)u.u_ap;
register struct inode *ip;
- time_t tv[2];
+ struct timeval tv[2];
- uap = (struct a *)u.u_ap;
- if ((ip = owner(1)) == NULL)
+ if ((ip = owner(uap->fname, FOLLOW)) == NULL)
return;
- if (copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof(tv))) {
- u.u_error = EFAULT;
- } else {
-#ifdef EFS
- if (efsinode(ip)) {
- dev_t ndev = ip->i_rdev;
-
- iput(ip);
- efsutime(ndev, uap->fname, tv);
- if (u.u_error != EEXIST)
- return;
- u.u_error = 0;
- u.u_dirp = (caddr_t)u.u_arg[0];
- ip = owner(1);
- }
-#endif
+ u.u_error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv));
+ if (u.u_error == 0) {
ip->i_flag |= IACC|IUPD|ICHG;
iupdat(ip, &tv[0], &tv[1], 0);
}
iput(ip);
}
+/*
+ * Flush any pending I/O.
+ */
sync()
{
- update(0);
+ update();
+}
+
+/*
+ * Truncate a file given its path name.
+ */
+truncate()
+{
+ struct a {
+ char *fname;
+ u_long length;
+ } *uap = (struct a *)u.u_ap;
+ struct inode *ip;
+ register struct nameidata *ndp = &u.u_nd;
+
+ ndp->ni_nameiop = LOOKUP | FOLLOW;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->fname;
+ ip = namei(ndp);
+ if (ip == NULL)
+ return;
+ if (access(ip, IWRITE))
+ goto bad;
+ if ((ip->i_mode&IFMT) == IFDIR) {
+ u.u_error = EISDIR;
+ goto bad;
+ }
+ itrunc(ip, uap->length);
+bad:
+ iput(ip);
+}
+
+/*
+ * Truncate a file given a file descriptor.
+ */
+ftruncate()
+{
+ struct a {
+ int fd;
+ u_long length;
+ } *uap = (struct a *)u.u_ap;
+ struct inode *ip;
+ struct file *fp;
+
+ fp = getinode(uap->fd);
+ if (fp == NULL)
+ return;
+ if ((fp->f_flag&FWRITE) == 0) {
+ u.u_error = EINVAL;
+ return;
+ }
+ ip = (struct inode *)fp->f_data;
+ ILOCK(ip);
+ itrunc(ip, uap->length);
+ IUNLOCK(ip);
+}
+
+/*
+ * Synch an open file.
+ */
+fsync()
+{
+ struct a {
+ int fd;
+ } *uap = (struct a *)u.u_ap;
+ struct inode *ip;
+ struct file *fp;
+
+ fp = getinode(uap->fd);
+ if (fp == NULL)
+ return;
+ ip = (struct inode *)fp->f_data;
+ ILOCK(ip);
+ syncip(ip);
+ IUNLOCK(ip);
+}
+
+/*
+ * Rename system call.
+ * rename("foo", "bar");
+ * is essentially
+ * unlink("bar");
+ * link("foo", "bar");
+ * unlink("foo");
+ * but ``atomically''. Can't do full commit without saving state in the
+ * inode on disk which isn't feasible at this time. Best we can do is
+ * always guarantee the target exists.
+ *
+ * Basic algorithm is:
+ *
+ * 1) Bump link count on source while we're linking it to the
+ * target. This also insure the inode won't be deleted out
+ * from underneath us while we work.
+ * 2) Link source to destination. If destination already exists,
+ * delete it first.
+ * 3) Unlink source reference to inode if still around.
+ * 4) If a directory was moved and the parent of the destination
+ * is different from the source, patch the ".." entry in the
+ * directory.
+ *
+ * Source and destination must either both be directories, or both
+ * not be directories. If target is a directory, it must be empty.
+ */
+rename()
+{
+ struct a {
+ char *from;
+ char *to;
+ } *uap = (struct a *)u.u_ap;
+ register struct inode *ip, *xp, *dp;
+ struct inode *zp;
+ int oldparent, parentdifferent, doingdirectory;
+ register struct nameidata *ndp = &u.u_nd;
+ int error = 0;
+
+ ndp->ni_nameiop = DELETE | LOCKPARENT;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->from;
+ ip = namei(ndp);
+ if (ip == NULL)
+ return;
+ dp = ndp->ni_pdir;
+ oldparent = 0, doingdirectory = 0;
+ if ((ip->i_mode&IFMT) == IFDIR) {
+ register struct direct *d;
+
+ d = &ndp->ni_dent;
+ /*
+ * Avoid ".", "..", and aliases of "." for obvious reasons.
+ */
+ if ((d->d_namlen == 1 && d->d_name[0] == '.') ||
+ (d->d_namlen == 2 && bcmp(d->d_name, "..", 2) == 0) ||
+ (dp == ip)) {
+ iput(dp);
+ if (dp == ip)
+ irele(ip);
+ else
+ iput(ip);
+ u.u_error = EINVAL;
+ return;
+ }
+ oldparent = dp->i_number;
+ doingdirectory++;
+ }
+ iput(dp);
+
+ /*
+ * 1) Bump link count while we're moving stuff
+ * around. If we crash somewhere before
+ * completing our work, the link count
+ * may be wrong, but correctable.
+ */
+ ip->i_nlink++;
+ ip->i_flag |= ICHG;
+ iupdat(ip, &time, &time, 1);
+ IUNLOCK(ip);
+
+ /*
+ * When the target exists, both the directory
+ * and target inodes are returned locked.
+ */
+ ndp->ni_nameiop = CREATE | LOCKPARENT | NOCACHE;
+ ndp->ni_dirp = (caddr_t)uap->to;
+ xp = namei(ndp);
+ if (u.u_error) {
+ error = u.u_error;
+ goto out;
+ }
+ dp = ndp->ni_pdir;
+ /*
+ * If ".." must be changed (ie the directory gets a new
+ * parent) then the source directory must not be in the
+ * directory heirarchy above the target, as this would
+ * orphan everything below the source directory. Also
+ * the user must have write permission in the source so
+ * as to be able to change "..". We must repeat the call
+ * to namei, as the parent directory is unlocked by the
+ * call to checkpath().
+ */
+ parentdifferent = oldparent != dp->i_number;
+ if (doingdirectory && parentdifferent) {
+ if (access(ip, IWRITE))
+ goto bad;
+ do {
+ dp = ndp->ni_pdir;
+ if (xp != NULL)
+ iput(xp);
+ u.u_error = checkpath(ip, dp);
+ if (u.u_error)
+ goto out;
+ xp = namei(ndp);
+ if (u.u_error) {
+ error = u.u_error;
+ goto out;
+ }
+ } while (dp != ndp->ni_pdir);
+ }
+ /*
+ * 2) If target doesn't exist, link the target
+ * to the source and unlink the source.
+ * Otherwise, rewrite the target directory
+ * entry to reference the source inode and
+ * expunge the original entry's existence.
+ */
+ if (xp == NULL) {
+ if (dp->i_dev != ip->i_dev) {
+ error = EXDEV;
+ goto bad;
+ }
+ /*
+ * Account for ".." in directory.
+ * When source and destination have the
+ * same parent we don't fool with the
+ * link count -- this isn't required
+ * because we do a similar check below.
+ */
+ if (doingdirectory && parentdifferent) {
+ dp->i_nlink++;
+ dp->i_flag |= ICHG;
+ iupdat(dp, &time, &time, 1);
+ }
+ error = direnter(ip, ndp);
+ if (error)
+ goto out;
+ } else {
+ if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) {
+ error = EXDEV;
+ goto bad;
+ }
+ /*
+ * Short circuit rename(foo, foo).
+ */
+ if (xp->i_number == ip->i_number)
+ goto bad;
+ /*
+ * Target must be empty if a directory
+ * and have no links to it.
+ * Also, insure source and target are
+ * compatible (both directories, or both
+ * not directories).
+ */
+ if ((xp->i_mode&IFMT) == IFDIR) {
+ if (!dirempty(xp) || xp->i_nlink > 2) {
+ error = ENOTEMPTY;
+ goto bad;
+ }
+ if (!doingdirectory) {
+ error = ENOTDIR;
+ goto bad;
+ }
+ } else if (doingdirectory) {
+ error = EISDIR;
+ goto bad;
+ }
+ dirrewrite(dp, ip, ndp);
+ if (u.u_error) {
+ error = u.u_error;
+ goto bad1;
+ }
+ /*
+ * Adjust the link count of the target to
+ * reflect the dirrewrite above. If this is
+ * a directory it is empty and there are
+ * no links to it, so we can squash the inode and
+ * any space associated with it. We disallowed
+ * renaming over top of a directory with links to
+ * it above, as we've no way to determine if
+ * we've got a link or the directory itself, and
+ * if we get a link, then ".." will be screwed up.
+ */
+ xp->i_nlink--;
+ if (doingdirectory) {
+ if (--xp->i_nlink != 0)
+ panic("rename: linked directory");
+ itrunc(xp, (u_long)0);
+ }
+ xp->i_flag |= ICHG;
+ iput(xp);
+ xp = NULL;
+ }
+
+ /*
+ * 3) Unlink the source.
+ */
+ ndp->ni_nameiop = DELETE | LOCKPARENT;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->from;
+ zp = namei(ndp);
+ dp = ndp->ni_pdir;
+ /*
+ * Insure directory entry still exists and
+ * has not changed since the start of all
+ * this. If either has occured, forget about
+ * about deleting the original entry.
+ */
+ if (dp != NULL && zp == ip) {
+ /*
+ * If source is a directory, must adjust
+ * link count of parent directory also.
+ * If target didn't exist and source and
+ * target have the same parent, then we
+ * needn't touch the link count, it all
+ * balances out in the end. Otherwise, we
+ * must do so to reflect deletion of ".."
+ * done above.
+ */
+ if (doingdirectory && (xp != NULL || parentdifferent)) {
+ dp->i_nlink--;
+ dp->i_flag |= ICHG;
+ }
+ if (dirremove(ndp)) {
+ zp->i_nlink--;
+ zp->i_flag |= ICHG;
+ }
+ if (error == 0) /* conservative */
+ error = u.u_error;
+ }
+ if (zp != NULL)
+ iput(zp);
+ irele(ip);
+ if (dp)
+ iput(dp);
+
+ /*
+ * 4) Renaming a directory with the parent
+ * different requires ".." to be rewritten.
+ * The window is still there for ".." to
+ * be inconsistent, but this is unavoidable,
+ * and a lot shorter than when it was done
+ * in a user process.
+ */
+ if (doingdirectory && parentdifferent && error == 0) {
+ struct dirtemplate dirbuf;
+
+ ndp->ni_nameiop = LOOKUP | LOCKPARENT;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->to;
+ ip = namei(ndp);
+ if (ip == NULL) {
+ printf("rename: .. went away\n");
+ return;
+ }
+ dp = ndp->ni_pdir;
+ if ((ip->i_mode&IFMT) != IFDIR) {
+ printf("rename: .. not a directory\n");
+ goto stuck;
+ }
+ error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate), (off_t)0, 1, (int *)0);
+ if (error == 0) {
+ dirbuf.dotdot_ino = dp->i_number;
+ dp->i_id = ++nextinodeid;
+ (void) rdwri(UIO_WRITE, ip, (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate), (off_t)0, 1, (int *)0);
+ }
+stuck:
+ irele(dp);
+ iput(ip);
+ }
+ goto done;
+
+bad:
+ iput(dp);
+bad1:
+ if (xp)
+ iput(xp);
+out:
+ ip->i_nlink--;
+ ip->i_flag |= ICHG;
+ irele(ip);
+done:
+ if (error)
+ u.u_error = error;
}
/*
* Make a new file.
*/
struct inode *
-maknode(mode)
+maknode(mode, ndp)
int mode;
+ register struct nameidata *ndp;
{
register struct inode *ip;
+ register struct inode *pdir = ndp->ni_pdir;
ino_t ipref;
if ((mode & IFMT) == IFDIR)
- ipref = dirpref(u.u_pdir->i_fs);
+ ipref = dirpref(pdir->i_fs);
else
- ipref = u.u_pdir->i_number;
- ip = ialloc(u.u_pdir, ipref, mode);
+ ipref = pdir->i_number;
+ ip = ialloc(pdir, ipref, mode);
if (ip == NULL) {
- iput(u.u_pdir);
- return(NULL);
+ iput(pdir);
+ return (NULL);
}
-#ifdef QUOTA
+#ifdef QUOTA
if (ip->i_dquot != NODQUOT)
panic("maknode: dquot");
#endif
ip->i_mode = mode & ~u.u_cmask;
ip->i_nlink = 1;
ip->i_uid = u.u_uid;
- ip->i_gid = u.u_pdir->i_gid;
-#ifdef QUOTA
+ ip->i_gid = pdir->i_gid;
+ if (ip->i_mode & ISGID && !groupmember(ip->i_gid))
+ ip->i_mode &= ~ISGID;
+#ifdef QUOTA
ip->i_dquot = inoquota(ip);
#endif
* Make sure inode goes to disk before directory entry.
*/
iupdat(ip, &time, &time, 1);
- direnter(ip);
+ u.u_error = direnter(ip, ndp);
if (u.u_error) {
/*
- * write error occurred trying to update directory
- * so must deallocate the inode
+ * Write error occurred trying to update directory
+ * so must deallocate the inode.
*/
ip->i_nlink = 0;
ip->i_flag |= ICHG;
iput(ip);
- return(NULL);
+ return (NULL);
}
- return(ip);
+ return (ip);
+}
+
+/*
+ * A virgin directory (no blushing please).
+ */
+struct dirtemplate mastertemplate = {
+ 0, 12, 1, ".",
+ 0, DIRBLKSIZ - 12, 2, ".."
+};
+
+/*
+ * Mkdir system call
+ */
+mkdir()
+{
+ struct a {
+ char *name;
+ int dmode;
+ } *uap = (struct a *)u.u_ap;
+ register struct inode *ip, *dp;
+ struct dirtemplate dirtemplate;
+ register struct nameidata *ndp = &u.u_nd;
+
+ ndp->ni_nameiop = CREATE;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->name;
+ ip = namei(ndp);
+ if (u.u_error)
+ return;
+ if (ip != NULL) {
+ iput(ip);
+ u.u_error = EEXIST;
+ return;
+ }
+ dp = ndp->ni_pdir;
+ uap->dmode &= 0777;
+ uap->dmode |= IFDIR;
+ /*
+ * Must simulate part of maknode here
+ * in order to acquire the inode, but
+ * not have it entered in the parent
+ * directory. The entry is made later
+ * after writing "." and ".." entries out.
+ */
+ ip = ialloc(dp, dirpref(dp->i_fs), uap->dmode);
+ if (ip == NULL) {
+ iput(dp);
+ return;
+ }
+#ifdef QUOTA
+ if (ip->i_dquot != NODQUOT)
+ panic("mkdir: dquot");
+#endif
+ ip->i_flag |= IACC|IUPD|ICHG;
+ ip->i_mode = uap->dmode & ~u.u_cmask;
+ ip->i_nlink = 2;
+ ip->i_uid = u.u_uid;
+ ip->i_gid = dp->i_gid;
+#ifdef QUOTA
+ ip->i_dquot = inoquota(ip);
+#endif
+ iupdat(ip, &time, &time, 1);
+
+ /*
+ * Bump link count in parent directory
+ * to reflect work done below. Should
+ * be done before reference is created
+ * so reparation is possible if we crash.
+ */
+ dp->i_nlink++;
+ dp->i_flag |= ICHG;
+ iupdat(dp, &time, &time, 1);
+
+ /*
+ * Initialize directory with "."
+ * and ".." from static template.
+ */
+ dirtemplate = mastertemplate;
+ dirtemplate.dot_ino = ip->i_number;
+ dirtemplate.dotdot_ino = dp->i_number;
+ u.u_error = rdwri(UIO_WRITE, ip, (caddr_t)&dirtemplate,
+ sizeof (dirtemplate), (off_t)0, 1, (int *)0);
+ if (u.u_error) {
+ dp->i_nlink--;
+ dp->i_flag |= ICHG;
+ goto bad;
+ }
+ /*
+ * Directory all set up, now
+ * install the entry for it in
+ * the parent directory.
+ */
+ u.u_error = direnter(ip, ndp);
+ dp = NULL;
+ if (u.u_error) {
+ ndp->ni_nameiop = LOOKUP | NOCACHE;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->name;
+ dp = namei(ndp);
+ if (dp) {
+ dp->i_nlink--;
+ dp->i_flag |= ICHG;
+ }
+ }
+bad:
+ /*
+ * No need to do an explicit itrunc here,
+ * irele will do this for us because we set
+ * the link count to 0.
+ */
+ if (u.u_error) {
+ ip->i_nlink = 0;
+ ip->i_flag |= ICHG;
+ }
+ if (dp)
+ iput(dp);
+ iput(ip);
+}
+
+/*
+ * Rmdir system call.
+ */
+rmdir()
+{
+ struct a {
+ char *name;
+ } *uap = (struct a *)u.u_ap;
+ register struct inode *ip, *dp;
+ register struct nameidata *ndp = &u.u_nd;
+
+ ndp->ni_nameiop = DELETE | LOCKPARENT;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->name;
+ ip = namei(ndp);
+ if (ip == NULL)
+ return;
+ dp = ndp->ni_pdir;
+ /*
+ * No rmdir "." please.
+ */
+ if (dp == ip) {
+ irele(dp);
+ iput(ip);
+ u.u_error = EINVAL;
+ return;
+ }
+ if ((ip->i_mode&IFMT) != IFDIR) {
+ u.u_error = ENOTDIR;
+ goto out;
+ }
+ /*
+ * Don't remove a mounted on directory.
+ */
+ if (ip->i_dev != dp->i_dev) {
+ u.u_error = EBUSY;
+ goto out;
+ }
+ /*
+ * Verify the directory is empty (and valid).
+ * (Rmdir ".." won't be valid since
+ * ".." will contain a reference to
+ * the current directory and thus be
+ * non-empty.)
+ */
+ if (ip->i_nlink != 2 || !dirempty(ip)) {
+ u.u_error = ENOTEMPTY;
+ goto out;
+ }
+ /*
+ * Delete reference to directory before purging
+ * inode. If we crash in between, the directory
+ * will be reattached to lost+found,
+ */
+ if (dirremove(ndp) == 0)
+ goto out;
+ dp->i_nlink--;
+ dp->i_flag |= ICHG;
+ iput(dp);
+ dp = NULL;
+ /*
+ * Truncate inode. The only stuff left
+ * in the directory is "." and "..". The
+ * "." reference is inconsequential since
+ * we're quashing it. The ".." reference
+ * has already been adjusted above. We've
+ * removed the "." reference and the reference
+ * in the parent directory, but there may be
+ * other hard links so decrement by 2 and
+ * worry about them later.
+ */
+ ip->i_nlink -= 2;
+ itrunc(ip, (u_long)0);
+out:
+ if (dp)
+ iput(dp);
+ iput(ip);
+}
+
+struct file *
+getinode(fdes)
+ int fdes;
+{
+ struct file *fp;
+
+ if ((unsigned)fdes >= NOFILE || (fp = u.u_ofile[fdes]) == NULL) {
+ u.u_error = EBADF;
+ return ((struct file *)0);
+ }
+ if (fp->f_type != DTYPE_INODE) {
+ u.u_error = EINVAL;
+ return ((struct file *)0);
+ }
+ return (fp);
+}
+
+/*
+ * mode mask for creation of files
+ */
+umask()
+{
+ register struct a {
+ int mask;
+ } *uap = (struct a *)u.u_ap;
+
+ u.u_r.r_val1 = u.u_cmask;
+ u.u_cmask = uap->mask & 07777;
}