don't clear set-gid when su creates file when not groupmember
[unix-history] / usr / src / sys / kern / vfs_syscalls.c
index b4fc1a9..a5e4d5d 100644 (file)
@@ -1,22 +1,27 @@
-/*     vfs_syscalls.c  6.10    84/07/04        */
-
-#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/quota.h"
-#include "../h/uio.h"
-#include "../h/socket.h"
-#include "../h/socketvar.h"
-#include "../h/nami.h"
-#include "../h/mount.h"
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ *
+ *     @(#)vfs_syscalls.c      7.3 (Berkeley) %G%
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "dir.h"
+#include "user.h"
+#include "kernel.h"
+#include "file.h"
+#include "stat.h"
+#include "inode.h"
+#include "fs.h"
+#include "buf.h"
+#include "proc.h"
+#include "quota.h"
+#include "uio.h"
+#include "socket.h"
+#include "socketvar.h"
+#include "mount.h"
 
 extern struct fileops inodeops;
 struct file *getinode();
 
 extern struct fileops inodeops;
 struct file *getinode();
@@ -49,9 +54,13 @@ chdirec(ipp)
        register struct inode *ip;
        struct a {
                char    *fname;
        register struct inode *ip;
        struct a {
                char    *fname;
-       };
+       } *uap = (struct a *)u.u_ap;
+       register struct nameidata *ndp = &u.u_nd;
 
 
-       ip = namei(uchar, LOOKUP, 1);
+       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 == NULL)
                return;
        if ((ip->i_mode&IFMT) != IFDIR) {
@@ -81,7 +90,7 @@ open()
                int     crtmode;
        } *uap = (struct a *) u.u_ap;
 
                int     crtmode;
        } *uap = (struct a *) u.u_ap;
 
-       copen(uap->mode-FOPEN, uap->crtmode);
+       copen(uap->mode-FOPEN, uap->crtmode, uap->fname);
 }
 
 /*
 }
 
 /*
@@ -94,7 +103,7 @@ creat()
                int     fmode;
        } *uap = (struct a *)u.u_ap;
 
                int     fmode;
        } *uap = (struct a *)u.u_ap;
 
-       copen(FWRITE|FCREAT|FTRUNC, uap->fmode);
+       copen(FWRITE|FCREAT|FTRUNC, uap->fmode, uap->fname);
 }
 
 /*
 }
 
 /*
@@ -102,41 +111,47 @@ creat()
  * Check permissions, allocate an open file structure,
  * and call the device open routine if any.
  */
  * Check permissions, allocate an open file structure,
  * and call the device open routine if any.
  */
-copen(mode, arg)
+copen(mode, arg, fname)
        register int mode;
        int arg;
        register int mode;
        int arg;
+       caddr_t fname;
 {
        register struct inode *ip;
        register struct file *fp;
 {
        register struct inode *ip;
        register struct file *fp;
-       int i;
+       register struct nameidata *ndp = &u.u_nd;
+       int indx;
 
 
-#ifdef notdef
-       if ((mode&(FREAD|FWRITE)) == 0) {
-               u.u_error = EINVAL;
+       fp = falloc();
+       if (fp == NULL)
                return;
                return;
-       }
-#endif
+       indx = u.u_r.r_val1;
+       ndp->ni_segflg = UIO_USERSPACE;
+       ndp->ni_dirp = fname;
        if (mode&FCREAT) {
        if (mode&FCREAT) {
-               ip = namei(uchar, CREATE, 1);
+               if (mode & FEXCL)
+                       ndp->ni_nameiop = CREATE;
+               else
+                       ndp->ni_nameiop = CREATE | FOLLOW;
+               ip = namei(ndp);
                if (ip == NULL) {
                        if (u.u_error)
                if (ip == NULL) {
                        if (u.u_error)
-                               return;
-                       ip = maknode(arg&07777&(~ISVTX));
+                               goto bad1;
+                       ip = maknode(arg&07777&(~ISVTX), ndp);
                        if (ip == NULL)
                        if (ip == NULL)
-                               return;
+                               goto bad1;
                        mode &= ~FTRUNC;
                } else {
                        if (mode&FEXCL) {
                                u.u_error = EEXIST;
                        mode &= ~FTRUNC;
                } else {
                        if (mode&FEXCL) {
                                u.u_error = EEXIST;
-                               iput(ip);
-                               return;
+                               goto bad;
                        }
                        mode &= ~FCREAT;
                }
        } else {
                        }
                        mode &= ~FCREAT;
                }
        } else {
-               ip = namei(uchar, LOOKUP, 1);
+               ndp->ni_nameiop = LOOKUP | FOLLOW;
+               ip = namei(ndp);
                if (ip == NULL)
                if (ip == NULL)
-                       return;
+                       goto bad1;
        }
        if ((ip->i_mode & IFMT) == IFSOCK) {
                u.u_error = EOPNOTSUPP;
        }
        if ((ip->i_mode & IFMT) == IFSOCK) {
                u.u_error = EOPNOTSUPP;
@@ -155,9 +170,6 @@ copen(mode, arg)
                        }
                }
        }
                        }
                }
        }
-       fp = falloc();
-       if (fp == NULL)
-               goto bad;
        if (mode&FTRUNC)
                itrunc(ip, (u_long)0);
        IUNLOCK(ip);
        if (mode&FTRUNC)
                itrunc(ip, (u_long)0);
        IUNLOCK(ip);
@@ -165,23 +177,22 @@ copen(mode, arg)
        fp->f_type = DTYPE_INODE;
        fp->f_ops = &inodeops;
        fp->f_data = (caddr_t)ip;
        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;
        if (setjmp(&u.u_qsave)) {
                if (u.u_error == 0)
                        u.u_error = EINTR;
-               u.u_ofile[i] = NULL;
+               u.u_ofile[indx] = NULL;
                closef(fp);
                return;
        }
        u.u_error = openi(ip, mode);
        if (u.u_error == 0)
                return;
                closef(fp);
                return;
        }
        u.u_error = openi(ip, mode);
        if (u.u_error == 0)
                return;
-       u.u_ofile[i] = NULL;
-       fp->f_count--;
-       irele(ip);
-       return;
+       ILOCK(ip);
 bad:
        iput(ip);
 bad:
        iput(ip);
+bad1:
+       u.u_ofile[indx] = NULL;
+       fp->f_count--;
 }
 
 /*
 }
 
 /*
@@ -194,19 +205,22 @@ mknod()
                char    *fname;
                int     fmode;
                int     dev;
                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())
                return;
        if (!suser())
                return;
-       ip = namei(uchar, CREATE, 0);
+       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;
        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;
        switch (ip->i_mode & IFMT) {
        if (ip == NULL)
                return;
        switch (ip->i_mode & IFMT) {
@@ -237,10 +251,13 @@ link()
        register struct a {
                char    *target;
                char    *linkname;
        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, LOOKUP, 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 == NULL)
                return;
        if ((ip->i_mode&IFMT) == IFDIR && !suser()) {
@@ -251,8 +268,10 @@ link()
        ip->i_flag |= ICHG;
        iupdat(ip, &time, &time, 1);
        IUNLOCK(ip);
        ip->i_flag |= ICHG;
        iupdat(ip, &time, &time, 1);
        IUNLOCK(ip);
-       u.u_dirp = (caddr_t)uap->linkname;
-       xp = namei(uchar, CREATE, 0);
+       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 (xp != NULL) {
                u.u_error = EEXIST;
                iput(xp);
@@ -260,12 +279,12 @@ link()
        }
        if (u.u_error)
                goto out;
        }
        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;
        }
                u.u_error = EXDEV;
                goto out;
        }
-       u.u_error = direnter(ip);
+       u.u_error = direnter(ip, ndp);
 out:
        if (u.u_error) {
                ip->i_nlink--;
 out:
        if (u.u_error) {
                ip->i_nlink--;
@@ -282,12 +301,12 @@ symlink()
        register struct a {
                char    *target;
                char    *linkname;
        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 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 = uap->target;
        nc = 0;
        while (c = fubyte(tp)) {
@@ -298,8 +317,10 @@ symlink()
                tp++;
                nc++;
        }
                tp++;
                nc++;
        }
-       u.u_dirp = uap->linkname;
-       ip = namei(uchar, CREATE, 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 (ip) {
                iput(ip);
                u.u_error = EEXIST;
@@ -307,10 +328,11 @@ symlink()
        }
        if (u.u_error)
                return;
        }
        if (u.u_error)
                return;
-       ip = maknode(IFLNK | 0777);
+       ip = maknode(IFLNK | 0777, ndp);
        if (ip == NULL)
                return;
        if (ip == NULL)
                return;
-       u.u_error = rdwri(UIO_WRITE, ip, uap->target, nc, 0, 0, (int *)0);
+       u.u_error = rdwri(UIO_WRITE, ip, uap->target, nc, (off_t)0, 0,
+           (int *)0);
        /* handle u.u_error != 0 */
        iput(ip);
 }
        /* handle u.u_error != 0 */
        iput(ip);
 }
@@ -324,13 +346,17 @@ unlink()
 {
        struct a {
                char    *fname;
 {
        struct a {
                char    *fname;
-       };
+       } *uap = (struct a *)u.u_ap;
        register struct inode *ip, *dp;
        register struct inode *ip, *dp;
+       register struct nameidata *ndp = &u.u_nd;
 
 
-       ip = namei(uchar, DELETE | LOCKPARENT, 0);
+       ndp->ni_nameiop = DELETE | LOCKPARENT;
+       ndp->ni_segflg = UIO_USERSPACE;
+       ndp->ni_dirp = uap->fname;
+       ip = namei(ndp);
        if (ip == NULL)
                return;
        if (ip == NULL)
                return;
-       dp = u.u_pdir;
+       dp = ndp->ni_pdir;
        if ((ip->i_mode&IFMT) == IFDIR && !suser())
                goto out;
        /*
        if ((ip->i_mode&IFMT) == IFDIR && !suser())
                goto out;
        /*
@@ -342,7 +368,7 @@ unlink()
        }
        if (ip->i_flag&ITEXT)
                xrele(ip);      /* try once to free text */
        }
        if (ip->i_flag&ITEXT)
                xrele(ip);      /* try once to free text */
-       if (dirremove()) {
+       if (dirremove(ndp)) {
                ip->i_nlink--;
                ip->i_flag |= ICHG;
        }
                ip->i_nlink--;
                ip->i_flag |= ICHG;
        }
@@ -364,9 +390,8 @@ lseek()
                int     fd;
                off_t   off;
                int     sbase;
                int     fd;
                off_t   off;
                int     sbase;
-       } *uap;
+       } *uap = (struct a *)u.u_ap;
 
 
-       uap = (struct a *)u.u_ap;
        GETF(fp, uap->fd);
        if (fp->f_type != DTYPE_INODE) {
                u.u_error = ESPIPE;
        GETF(fp, uap->fd);
        if (fp->f_type != DTYPE_INODE) {
                u.u_error = ESPIPE;
@@ -403,14 +428,17 @@ saccess()
        register struct a {
                char    *fname;
                int     fmode;
        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;
        svuid = u.u_uid;
        svgid = u.u_gid;
        u.u_uid = u.u_ruid;
        u.u_gid = u.u_rgid;
-       ip = namei(uchar, LOOKUP, 1);
+       ndp->ni_nameiop = LOOKUP | FOLLOW;
+       ndp->ni_segflg = UIO_USERSPACE;
+       ndp->ni_dirp = uap->fname;
+       ip = namei(ndp);
        if (ip != NULL) {
                if ((uap->fmode&R_OK) && access(ip, IREAD))
                        goto done;
        if (ip != NULL) {
                if ((uap->fmode&R_OK) && access(ip, IREAD))
                        goto done;
@@ -431,7 +459,7 @@ done:
 stat()
 {
 
 stat()
 {
 
-       stat1(1);
+       stat1(FOLLOW);
 }
 
 /*
 }
 
 /*
@@ -440,7 +468,7 @@ stat()
 lstat()
 {
 
 lstat()
 {
 
-       stat1(0);
+       stat1(NOFOLLOW);
 }
 
 stat1(follow)
 }
 
 stat1(follow)
@@ -450,11 +478,14 @@ stat1(follow)
        register struct a {
                char    *fname;
                struct stat *ub;
        register struct a {
                char    *fname;
                struct stat *ub;
-       } *uap;
+       } *uap = (struct a *)u.u_ap;
        struct stat sb;
        struct stat sb;
+       register struct nameidata *ndp = &u.u_nd;
 
 
-       uap = (struct a *)u.u_ap;
-       ip = namei(uchar, LOOKUP, follow);
+       ndp->ni_nameiop = LOOKUP | follow;
+       ndp->ni_segflg = UIO_USERSPACE;
+       ndp->ni_dirp = uap->fname;
+       ip = namei(ndp);
        if (ip == NULL)
                return;
        (void) ino_stat(ip, &sb);
        if (ip == NULL)
                return;
        (void) ino_stat(ip, &sb);
@@ -473,16 +504,21 @@ readlink()
                char    *buf;
                int     count;
        } *uap = (struct a *)u.u_ap;
                char    *buf;
                int     count;
        } *uap = (struct a *)u.u_ap;
+       register struct nameidata *ndp = &u.u_nd;
        int resid;
 
        int resid;
 
-       ip = namei(uchar, LOOKUP, 0);
+       ndp->ni_nameiop = LOOKUP;
+       ndp->ni_segflg = UIO_USERSPACE;
+       ndp->ni_dirp = uap->name;
+       ip = namei(ndp);
        if (ip == NULL)
                return;
        if ((ip->i_mode&IFMT) != IFLNK) {
        if (ip == NULL)
                return;
        if ((ip->i_mode&IFMT) != IFLNK) {
-               u.u_error = ENXIO;
+               u.u_error = EINVAL;
                goto out;
        }
                goto out;
        }
-       u.u_error = rdwri(UIO_READ, ip, uap->buf, uap->count, 0, 0, &resid);
+       u.u_error = rdwri(UIO_READ, ip, uap->buf, uap->count, (off_t)0, 0,
+           &resid);
 out:
        iput(ip);
        u.u_r.r_val1 = uap->count - resid;
 out:
        iput(ip);
        u.u_r.r_val1 = uap->count - resid;
@@ -497,12 +533,11 @@ chmod()
        struct a {
                char    *fname;
                int     fmode;
        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;
                return;
-       chmod1(ip, uap->fmode);
+       u.u_error = chmod1(ip, uap->fmode);
        iput(ip);
 }
 
        iput(ip);
 }
 
@@ -514,11 +549,10 @@ fchmod()
        struct a {
                int     fd;
                int     fmode;
        struct a {
                int     fd;
                int     fmode;
-       } *uap;
+       } *uap = (struct a *)u.u_ap;
        register struct inode *ip;
        register struct file *fp;
 
        register struct inode *ip;
        register struct file *fp;
 
-       uap = (struct a *)u.u_ap;
        fp = getinode(uap->fd);
        if (fp == NULL)
                return;
        fp = getinode(uap->fd);
        if (fp == NULL)
                return;
@@ -526,7 +560,7 @@ fchmod()
        if (u.u_uid != ip->i_uid && !suser())
                return;
        ILOCK(ip);
        if (u.u_uid != ip->i_uid && !suser())
                return;
        ILOCK(ip);
-       chmod1(ip, uap->fmode);
+       u.u_error = chmod1(ip, uap->fmode);
        IUNLOCK(ip);
 }
 
        IUNLOCK(ip);
 }
 
@@ -539,9 +573,12 @@ chmod1(ip, mode)
        register int mode;
 {
 
        register int mode;
 {
 
+       if (ip->i_fs->fs_ronly)
+               return (EROFS);
        ip->i_mode &= ~07777;
        if (u.u_uid) {
        ip->i_mode &= ~07777;
        if (u.u_uid) {
-               mode &= ~ISVTX;
+               if ((ip->i_mode & IFMT) != IFDIR)
+                       mode &= ~ISVTX;
                if (!groupmember(ip->i_gid))
                        mode &= ~ISGID;
        }
                if (!groupmember(ip->i_gid))
                        mode &= ~ISGID;
        }
@@ -549,6 +586,7 @@ chmod1(ip, mode)
        ip->i_flag |= ICHG;
        if (ip->i_flag&ITEXT && (ip->i_mode&ISVTX)==0)
                xrele(ip);
        ip->i_flag |= ICHG;
        if (ip->i_flag&ITEXT && (ip->i_mode&ISVTX)==0)
                xrele(ip);
+       return (0);
 }
 
 /*
 }
 
 /*
@@ -561,10 +599,9 @@ chown()
                char    *fname;
                int     uid;
                int     gid;
                char    *fname;
                int     uid;
                int     gid;
-       } *uap;
+       } *uap = (struct a *)u.u_ap;
 
 
-       uap = (struct a *)u.u_ap;
-       if (!suser() || (ip = owner(0)) == NULL)
+       if ((ip = owner(uap->fname, NOFOLLOW)) == NULL)
                return;
        u.u_error = chown1(ip, uap->uid, uap->gid);
        iput(ip);
                return;
        u.u_error = chown1(ip, uap->uid, uap->gid);
        iput(ip);
@@ -579,17 +616,14 @@ fchown()
                int     fd;
                int     uid;
                int     gid;
                int     fd;
                int     uid;
                int     gid;
-       } *uap;
+       } *uap = (struct a *)u.u_ap;
        register struct inode *ip;
        register struct file *fp;
 
        register struct inode *ip;
        register struct file *fp;
 
-       uap = (struct a *)u.u_ap;
        fp = getinode(uap->fd);
        if (fp == NULL)
                return;
        ip = (struct inode *)fp->f_data;
        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);
        ILOCK(ip);
        u.u_error = chown1(ip, uap->uid, uap->gid);
        IUNLOCK(ip);
@@ -607,10 +641,16 @@ chown1(ip, uid, gid)
        register long change;
 #endif
 
        register long change;
 #endif
 
+       if (ip->i_fs->fs_ronly)
+               return (EROFS);
        if (uid == -1)
                uid = ip->i_uid;
        if (gid == -1)
                gid = ip->i_gid;
        if (uid == -1)
                uid = ip->i_uid;
        if (gid == -1)
                gid = ip->i_gid;
+       if (uid != ip->i_uid && !suser())
+               return (u.u_error);
+       if (gid != ip->i_gid && !groupmember((gid_t)gid) && !suser())
+               return (u.u_error);
 #ifdef QUOTA
        if (ip->i_uid == uid)           /* this just speeds things a little */
                change = 0;
 #ifdef QUOTA
        if (ip->i_uid == uid)           /* this just speeds things a little */
                change = 0;
@@ -628,7 +668,7 @@ chown1(ip, uid, gid)
 #ifdef QUOTA
        ip->i_dquot = inoquota(ip);
        (void) chkdq(ip, change, 1);
 #ifdef QUOTA
        ip->i_dquot = inoquota(ip);
        (void) chkdq(ip, change, 1);
-       (void) chkiq(ip->i_dev, (struct inode *)NULL, uid, 1);
+       (void) chkiq(ip->i_dev, (struct inode *)NULL, (uid_t)uid, 1);
        return (u.u_error);             /* should == 0 ALWAYS !! */
 #else
        return (0);
        return (u.u_error);             /* should == 0 ALWAYS !! */
 #else
        return (0);
@@ -644,8 +684,13 @@ utimes()
        register struct inode *ip;
        struct timeval tv[2];
 
        register struct inode *ip;
        struct timeval tv[2];
 
-       if ((ip = owner(1)) == NULL)
+       if ((ip = owner(uap->fname, FOLLOW)) == NULL)
+               return;
+       if (ip->i_fs->fs_ronly) {
+               u.u_error = EROFS;
+               iput(ip);
                return;
                return;
+       }
        u.u_error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv));
        if (u.u_error == 0) {
                ip->i_flag |= IACC|IUPD|ICHG;
        u.u_error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv));
        if (u.u_error == 0) {
                ip->i_flag |= IACC|IUPD|ICHG;
@@ -670,11 +715,15 @@ truncate()
 {
        struct a {
                char    *fname;
 {
        struct a {
                char    *fname;
-               u_long  length;
+               off_t   length;
        } *uap = (struct a *)u.u_ap;
        struct inode *ip;
        } *uap = (struct a *)u.u_ap;
        struct inode *ip;
+       register struct nameidata *ndp = &u.u_nd;
 
 
-       ip = namei(uchar, LOOKUP, 1);
+       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))
        if (ip == NULL)
                return;
        if (access(ip, IWRITE))
@@ -683,7 +732,7 @@ truncate()
                u.u_error = EISDIR;
                goto bad;
        }
                u.u_error = EISDIR;
                goto bad;
        }
-       itrunc(ip, uap->length);
+       itrunc(ip, (u_long)uap->length);
 bad:
        iput(ip);
 }
 bad:
        iput(ip);
 }
@@ -695,7 +744,7 @@ ftruncate()
 {
        struct a {
                int     fd;
 {
        struct a {
                int     fd;
-               u_long  length;
+               off_t   length;
        } *uap = (struct a *)u.u_ap;
        struct inode *ip;
        struct file *fp;
        } *uap = (struct a *)u.u_ap;
        struct inode *ip;
        struct file *fp;
@@ -709,7 +758,7 @@ ftruncate()
        }
        ip = (struct inode *)fp->f_data;
        ILOCK(ip);
        }
        ip = (struct inode *)fp->f_data;
        ILOCK(ip);
-       itrunc(ip, uap->length);
+       itrunc(ip, (u_long)uap->length);
        IUNLOCK(ip);
 }
 
        IUNLOCK(ip);
 }
 
@@ -729,6 +778,8 @@ fsync()
                return;
        ip = (struct inode *)fp->f_data;
        ILOCK(ip);
                return;
        ip = (struct inode *)fp->f_data;
        ILOCK(ip);
+       if (fp->f_flag&FWRITE)
+               ip->i_flag |= ICHG;
        syncip(ip);
        IUNLOCK(ip);
 }
        syncip(ip);
        IUNLOCK(ip);
 }
@@ -748,11 +799,12 @@ fsync()
  *
  * 1) Bump link count on source while we're linking it to the
  *    target.  This also insure the inode won't be deleted out
  *
  * 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.
+ *    from underneath us while we work (it may be truncated by
+ *    a concurrent `trunc' or `open' for creation).
  * 2) Link source to destination.  If destination already exists,
  *    delete it first.
  * 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
+ * 3) Unlink source reference to inode if still around. If a
+ *    directory was moved and the parent of the destination
  *    is different from the source, patch the ".." entry in the
  *    directory.
  *
  *    is different from the source, patch the ".." entry in the
  *    directory.
  *
@@ -764,28 +816,30 @@ rename()
        struct a {
                char    *from;
                char    *to;
        struct a {
                char    *from;
                char    *to;
-       } *uap;
+       } *uap = (struct a *)u.u_ap;
        register struct inode *ip, *xp, *dp;
        register struct inode *ip, *xp, *dp;
-       struct inode *zp;
-       int oldparent, parentdifferent, doingdirectory;
+       struct dirtemplate dirbuf;
+       int doingdirectory = 0, oldparent = 0, newparent = 0;
+       register struct nameidata *ndp = &u.u_nd;
        int error = 0;
 
        int error = 0;
 
-       uap = (struct a *)u.u_ap;
-       ip = namei(uchar, DELETE | LOCKPARENT, 0);
+       ndp->ni_nameiop = DELETE | LOCKPARENT;
+       ndp->ni_segflg = UIO_USERSPACE;
+       ndp->ni_dirp = uap->from;
+       ip = namei(ndp);
        if (ip == NULL)
                return;
        if (ip == NULL)
                return;
-       dp = u.u_pdir;
-       oldparent = 0, doingdirectory = 0;
+       dp = ndp->ni_pdir;
        if ((ip->i_mode&IFMT) == IFDIR) {
                register struct direct *d;
 
        if ((ip->i_mode&IFMT) == IFDIR) {
                register struct direct *d;
 
-               d = &u.u_dent;
+               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) ||
                /*
                 * 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)) {
+                   (dp == ip) || (ip->i_flag & IRENAME)) {
                        iput(dp);
                        if (dp == ip)
                                irele(ip);
                        iput(dp);
                        if (dp == ip)
                                irele(ip);
@@ -794,6 +848,7 @@ rename()
                        u.u_error = EINVAL;
                        return;
                }
                        u.u_error = EINVAL;
                        return;
                }
+               ip->i_flag |= IRENAME;
                oldparent = dp->i_number;
                doingdirectory++;
        }
                oldparent = dp->i_number;
                doingdirectory++;
        }
@@ -814,13 +869,14 @@ rename()
         * When the target exists, both the directory
         * and target inodes are returned locked.
         */
         * When the target exists, both the directory
         * and target inodes are returned locked.
         */
-       u.u_dirp = (caddr_t)uap->to;
-       xp = namei(uchar, CREATE | LOCKPARENT | NOCACHE, 0);
+       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;
        }
        if (u.u_error) {
                error = u.u_error;
                goto out;
        }
-       dp = u.u_pdir;
+       dp = ndp->ni_pdir;
        /*
         * If ".." must be changed (ie the directory gets a new
         * parent) then the source directory must not be in the
        /*
         * If ".." must be changed (ie the directory gets a new
         * parent) then the source directory must not be in the
@@ -831,24 +887,24 @@ rename()
         * to namei, as the parent directory is unlocked by the
         * call to checkpath().
         */
         * to namei, as the parent directory is unlocked by the
         * call to checkpath().
         */
-       parentdifferent = oldparent != dp->i_number;
-       if (doingdirectory && parentdifferent) {
+       if (oldparent != dp->i_number)
+               newparent = dp->i_number;
+       if (doingdirectory && newparent) {
                if (access(ip, IWRITE))
                        goto bad;
                do {
                if (access(ip, IWRITE))
                        goto bad;
                do {
-                       dp = u.u_pdir;
+                       dp = ndp->ni_pdir;
                        if (xp != NULL)
                                iput(xp);
                        u.u_error = checkpath(ip, dp);
                        if (u.u_error)
                                goto out;
                        if (xp != NULL)
                                iput(xp);
                        u.u_error = checkpath(ip, dp);
                        if (u.u_error)
                                goto out;
-                       u.u_dirp = (caddr_t)uap->to;
-                       xp = namei(uchar, CREATE | LOCKPARENT | NOCACHE, 0);
+                       xp = namei(ndp);
                        if (u.u_error) {
                                error = u.u_error;
                                goto out;
                        }
                        if (u.u_error) {
                                error = u.u_error;
                                goto out;
                        }
-               } while (dp != u.u_pdir);
+               } while (dp != ndp->ni_pdir);
        }
        /*
         * 2) If target doesn't exist, link the target
        }
        /*
         * 2) If target doesn't exist, link the target
@@ -863,18 +919,16 @@ rename()
                        goto bad;
                }
                /*
                        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.
+                * Account for ".." in new directory.
+                * When source and destination have the same
+                * parent we don't fool with the link count.
                 */
                 */
-               if (doingdirectory && parentdifferent) {
+               if (doingdirectory && newparent) {
                        dp->i_nlink++;
                        dp->i_flag |= ICHG;
                        iupdat(dp, &time, &time, 1);
                }
                        dp->i_nlink++;
                        dp->i_flag |= ICHG;
                        iupdat(dp, &time, &time, 1);
                }
-               error = direnter(ip);
+               error = direnter(ip, ndp);
                if (error)
                        goto out;
        } else {
                if (error)
                        goto out;
        } else {
@@ -887,6 +941,17 @@ rename()
                 */
                if (xp->i_number == ip->i_number)
                        goto bad;
                 */
                if (xp->i_number == ip->i_number)
                        goto bad;
+               /*
+                * If the parent directory is "sticky", then the user must
+                * own the parent directory, or the destination of the rename,
+                * otherwise the destination may not be changed (except by
+                * root). This implements append-only directories.
+                */
+               if ((dp->i_mode & ISVTX) && u.u_uid != 0 &&
+                   u.u_uid != dp->i_uid && xp->i_uid != u.u_uid) {
+                       error = EPERM;
+                       goto bad;
+               }
                /*
                 * Target must be empty if a directory
                 * and have no links to it.
                /*
                 * Target must be empty if a directory
                 * and have no links to it.
@@ -895,7 +960,7 @@ rename()
                 * not directories).
                 */
                if ((xp->i_mode&IFMT) == IFDIR) {
                 * not directories).
                 */
                if ((xp->i_mode&IFMT) == IFDIR) {
-                       if (!dirempty(xp) || xp->i_nlink > 2) {
+                       if (!dirempty(xp, dp->i_number) || xp->i_nlink > 2) {
                                error = ENOTEMPTY;
                                goto bad;
                        }
                                error = ENOTEMPTY;
                                goto bad;
                        }
@@ -903,11 +968,12 @@ rename()
                                error = ENOTDIR;
                                goto bad;
                        }
                                error = ENOTDIR;
                                goto bad;
                        }
+                       cacheinval(dp);
                } else if (doingdirectory) {
                        error = EISDIR;
                        goto bad;
                }
                } else if (doingdirectory) {
                        error = EISDIR;
                        goto bad;
                }
-               dirrewrite(dp, ip);
+               dirrewrite(dp, ip, ndp);
                if (u.u_error) {
                        error = u.u_error;
                        goto bad1;
                if (u.u_error) {
                        error = u.u_error;
                        goto bad1;
@@ -919,9 +985,8 @@ rename()
                 * 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
                 * 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.
+                * it above, as the remaining link would point to
+                * a directory without "." or ".." entries.
                 */
                xp->i_nlink--;
                if (doingdirectory) {
                 */
                xp->i_nlink--;
                if (doingdirectory) {
@@ -937,78 +1002,71 @@ rename()
        /*
         * 3) Unlink the source.
         */
        /*
         * 3) Unlink the source.
         */
-       u.u_dirp = uap->from;
-       zp = namei(uchar, DELETE | LOCKPARENT, 0);
-       dp = u.u_pdir;
+       ndp->ni_nameiop = DELETE | LOCKPARENT;
+       ndp->ni_segflg = UIO_USERSPACE;
+       ndp->ni_dirp = uap->from;
+       xp = namei(ndp);
+       if (xp != NULL)
+               dp = ndp->ni_pdir;
+       else
+               dp = NULL;
        /*
        /*
-        * 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.
+        * Insure that the directory entry still exists and has not
+        * changed while the new name has been entered. If the source is
+        * a file then the entry may have been unlinked or renamed. In
+        * either case there is no further work to be done. If the source
+        * is a directory then it cannot have been rmdir'ed; its link
+        * count of three would cause a rmdir to fail with ENOTEMPTY.
+        * The IRENAME flag insures that it cannot be moved by another
+        * rename.
         */
         */
-       if (dp != NULL && zp == ip) {
+       if (xp != ip) {
+               if (doingdirectory)
+                       panic("rename: lost dir entry");
+       } else {
                /*
                /*
-                * 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 the source is a directory with a
+                * new parent, the link count of the old
+                * parent directory must be decremented
+                * and ".." set to point to the new parent.
                 */
                 */
-               if (doingdirectory && (xp != NULL || parentdifferent)) {
+               if (doingdirectory && newparent) {
                        dp->i_nlink--;
                        dp->i_flag |= ICHG;
                        dp->i_nlink--;
                        dp->i_flag |= ICHG;
+                       error = rdwri(UIO_READ, xp, (caddr_t)&dirbuf,
+                               sizeof (struct dirtemplate), (off_t)0, 1,
+                               (int *)0);
+                       if (error == 0) {
+                               if (dirbuf.dotdot_namlen != 2 ||
+                                   dirbuf.dotdot_name[0] != '.' ||
+                                   dirbuf.dotdot_name[1] != '.') {
+                                       printf("rename: mangled dir\n");
+                               } else {
+                                       dirbuf.dotdot_ino = newparent;
+                                       (void) rdwri(UIO_WRITE, xp,
+                                           (caddr_t)&dirbuf,
+                                           sizeof (struct dirtemplate),
+                                           (off_t)0, 1, (int *)0);
+                                       cacheinval(dp);
+                               }
+                       }
                }
                }
-               if (dirremove()) {
-                       zp->i_nlink--;
-                       zp->i_flag |= ICHG;
+               if (dirremove(ndp)) {
+                       xp->i_nlink--;
+                       xp->i_flag |= ICHG;
                }
                }
-               if (error == 0)         /* conservative */
+               xp->i_flag &= ~IRENAME;
+               if (error == 0)         /* XXX conservative */
                        error = u.u_error;
        }
                        error = u.u_error;
        }
-       if (zp != NULL)
-               iput(zp);
-       irele(ip);
        if (dp)
                iput(dp);
        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;
-
-               u.u_dirp = uap->to;
-               ip = namei(uchar, LOOKUP | LOCKPARENT, 0);
-               if (ip == NULL) {
-                       printf("rename: .. went away\n");
-                       return;
-               }
-               dp = u.u_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;
+       if (xp)
+               iput(xp);
+       irele(ip);
+       if (error)
+               u.u_error = error;
+       return;
 
 bad:
        iput(dp);
 
 bad:
        iput(dp);
@@ -1019,7 +1077,6 @@ out:
        ip->i_nlink--;
        ip->i_flag |= ICHG;
        irele(ip);
        ip->i_nlink--;
        ip->i_flag |= ICHG;
        irele(ip);
-done:
        if (error)
                u.u_error = error;
 }
        if (error)
                u.u_error = error;
 }
@@ -1028,19 +1085,21 @@ done:
  * Make a new file.
  */
 struct inode *
  * Make a new file.
  */
 struct inode *
-maknode(mode)
+maknode(mode, ndp)
        int mode;
        int mode;
+       register struct nameidata *ndp;
 {
        register struct inode *ip;
 {
        register struct inode *ip;
+       register struct inode *pdir = ndp->ni_pdir;
        ino_t ipref;
 
        if ((mode & IFMT) == IFDIR)
        ino_t ipref;
 
        if ((mode & IFMT) == IFDIR)
-               ipref = dirpref(u.u_pdir->i_fs);
+               ipref = dirpref(pdir->i_fs);
        else
        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) {
        if (ip == NULL) {
-               iput(u.u_pdir);
+               iput(pdir);
                return (NULL);
        }
 #ifdef QUOTA
                return (NULL);
        }
 #ifdef QUOTA
@@ -1053,8 +1112,8 @@ maknode(mode)
        ip->i_mode = mode & ~u.u_cmask;
        ip->i_nlink = 1;
        ip->i_uid = u.u_uid;
        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;
-       if (ip->i_mode & ISGID && !groupmember(ip->i_gid))
+       ip->i_gid = pdir->i_gid;
+       if (ip->i_mode & ISGID && !groupmember(ip->i_gid) && !suser())
                ip->i_mode &= ~ISGID;
 #ifdef QUOTA
        ip->i_dquot = inoquota(ip);
                ip->i_mode &= ~ISGID;
 #ifdef QUOTA
        ip->i_dquot = inoquota(ip);
@@ -1064,7 +1123,7 @@ maknode(mode)
         * Make sure inode goes to disk before directory entry.
         */
        iupdat(ip, &time, &time, 1);
         * Make sure inode goes to disk before directory entry.
         */
        iupdat(ip, &time, &time, 1);
-       u.u_error = direnter(ip);
+       u.u_error = direnter(ip, ndp);
        if (u.u_error) {
                /*
                 * Write error occurred trying to update directory
        if (u.u_error) {
                /*
                 * Write error occurred trying to update directory
@@ -1094,12 +1153,15 @@ mkdir()
        struct a {
                char    *name;
                int     dmode;
        struct a {
                char    *name;
                int     dmode;
-       } *uap;
+       } *uap = (struct a *)u.u_ap;
        register struct inode *ip, *dp;
        struct dirtemplate dirtemplate;
        register struct inode *ip, *dp;
        struct dirtemplate dirtemplate;
+       register struct nameidata *ndp = &u.u_nd;
 
 
-       uap = (struct a *)u.u_ap;
-       ip = namei(uchar, CREATE, 0);
+       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) {
        if (u.u_error)
                return;
        if (ip != NULL) {
@@ -1107,7 +1169,7 @@ mkdir()
                u.u_error = EEXIST;
                return;
        }
                u.u_error = EEXIST;
                return;
        }
-       dp = u.u_pdir;
+       dp = ndp->ni_pdir;
        uap->dmode &= 0777;
        uap->dmode |= IFDIR;
        /*
        uap->dmode &= 0777;
        uap->dmode |= IFDIR;
        /*
@@ -1160,16 +1222,22 @@ mkdir()
                dp->i_flag |= ICHG;
                goto bad;
        }
                dp->i_flag |= ICHG;
                goto bad;
        }
+       if (DIRBLKSIZ > ip->i_fs->fs_fsize)
+               panic("mkdir: blksize");     /* XXX - should grow with bmap() */
+       else
+               ip->i_size = DIRBLKSIZ;
        /*
         * Directory all set up, now
         * install the entry for it in
         * the parent directory.
         */
        /*
         * Directory all set up, now
         * install the entry for it in
         * the parent directory.
         */
-       u.u_error = direnter(ip);
+       u.u_error = direnter(ip, ndp);
        dp = NULL;
        if (u.u_error) {
        dp = NULL;
        if (u.u_error) {
-               u.u_dirp = uap->name;
-               dp = namei(uchar, LOOKUP | NOCACHE, 0);
+               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;
                if (dp) {
                        dp->i_nlink--;
                        dp->i_flag |= ICHG;
@@ -1197,13 +1265,17 @@ rmdir()
 {
        struct a {
                char    *name;
 {
        struct a {
                char    *name;
-       };
+       } *uap = (struct a *)u.u_ap;
        register struct inode *ip, *dp;
        register struct inode *ip, *dp;
+       register struct nameidata *ndp = &u.u_nd;
 
 
-       ip = namei(uchar, DELETE | LOCKPARENT, 0);
+       ndp->ni_nameiop = DELETE | LOCKPARENT;
+       ndp->ni_segflg = UIO_USERSPACE;
+       ndp->ni_dirp = uap->name;
+       ip = namei(ndp);
        if (ip == NULL)
                return;
        if (ip == NULL)
                return;
-       dp = u.u_pdir;
+       dp = ndp->ni_pdir;
        /*
         * No rmdir "." please.
         */
        /*
         * No rmdir "." please.
         */
@@ -1231,7 +1303,7 @@ rmdir()
         *  the current directory and thus be
         *  non-empty.)
         */
         *  the current directory and thus be
         *  non-empty.)
         */
-       if (ip->i_nlink != 2 || !dirempty(ip)) {
+       if (ip->i_nlink != 2 || !dirempty(ip, dp->i_number)) {
                u.u_error = ENOTEMPTY;
                goto out;
        }
                u.u_error = ENOTEMPTY;
                goto out;
        }
@@ -1240,10 +1312,11 @@ rmdir()
         * inode.  If we crash in between, the directory
         * will be reattached to lost+found,
         */
         * inode.  If we crash in between, the directory
         * will be reattached to lost+found,
         */
-       if (dirremove() == 0)
+       if (dirremove(ndp) == 0)
                goto out;
        dp->i_nlink--;
        dp->i_flag |= ICHG;
                goto out;
        dp->i_nlink--;
        dp->i_flag |= ICHG;
+       cacheinval(dp);
        iput(dp);
        dp = NULL;
        /*
        iput(dp);
        dp = NULL;
        /*
@@ -1259,6 +1332,7 @@ rmdir()
         */
        ip->i_nlink -= 2;
        itrunc(ip, (u_long)0);
         */
        ip->i_nlink -= 2;
        itrunc(ip, (u_long)0);
+       cacheinval(ip);
 out:
        if (dp)
                iput(dp);
 out:
        if (dp)
                iput(dp);
@@ -1289,9 +1363,8 @@ umask()
 {
        register struct a {
                int     mask;
 {
        register struct a {
                int     mask;
-       } *uap;
+       } *uap = (struct a *)u.u_ap;
 
 
-       uap = (struct a *)u.u_ap;
        u.u_r.r_val1 = u.u_cmask;
        u.u_cmask = uap->mask & 07777;
 }
        u.u_r.r_val1 = u.u_cmask;
        u.u_cmask = uap->mask & 07777;
 }