fix chown; don't steal groups or give away files
[unix-history] / usr / src / sys / ufs / ffs / ufs_vnops.c
index d382459..016a175 100644 (file)
@@ -1,21 +1,27 @@
-/*     ufs_vnops.c     6.12    84/07/20        */
-
-#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/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.
+ *
+ *     @(#)ufs_vnops.c 7.4 (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();
@@ -113,31 +119,31 @@ copen(mode, arg, fname)
        register struct inode *ip;
        register struct file *fp;
        register struct nameidata *ndp = &u.u_nd;
        register struct inode *ip;
        register struct file *fp;
        register struct nameidata *ndp = &u.u_nd;
-       int i;
+       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) {
        ndp->ni_segflg = UIO_USERSPACE;
        ndp->ni_dirp = fname;
        if (mode&FCREAT) {
-               ndp->ni_nameiop = CREATE | FOLLOW;
+               if (mode & FEXCL)
+                       ndp->ni_nameiop = CREATE;
+               else
+                       ndp->ni_nameiop = CREATE | FOLLOW;
                ip = namei(ndp);
                if (ip == NULL) {
                        if (u.u_error)
                ip = namei(ndp);
                if (ip == NULL) {
                        if (u.u_error)
-                               return;
+                               goto bad1;
                        ip = maknode(arg&07777&(~ISVTX), ndp);
                        if (ip == NULL)
                        ip = maknode(arg&07777&(~ISVTX), ndp);
                        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;
                }
                        }
                        mode &= ~FCREAT;
                }
@@ -145,7 +151,7 @@ copen(mode, arg, fname)
                ndp->ni_nameiop = LOOKUP | FOLLOW;
                ip = namei(ndp);
                if (ip == NULL)
                ndp->ni_nameiop = LOOKUP | FOLLOW;
                ip = namei(ndp);
                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;
@@ -164,9 +170,6 @@ copen(mode, arg, fname)
                        }
                }
        }
                        }
                }
        }
-       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);
@@ -174,23 +177,22 @@ copen(mode, arg, fname)
        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--;
 }
 
 /*
 }
 
 /*
@@ -329,7 +331,8 @@ symlink()
        ip = maknode(IFLNK | 0777, ndp);
        if (ip == NULL)
                return;
        ip = maknode(IFLNK | 0777, ndp);
        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);
 }
@@ -511,10 +514,11 @@ readlink()
        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;
@@ -533,7 +537,7 @@ chmod()
 
        if ((ip = owner(uap->fname, FOLLOW)) == NULL)
                return;
 
        if ((ip = owner(uap->fname, FOLLOW)) == NULL)
                return;
-       chmod1(ip, uap->fmode);
+       u.u_error = chmod1(ip, uap->fmode);
        iput(ip);
 }
 
        iput(ip);
 }
 
@@ -556,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);
 }
 
@@ -569,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;
        }
@@ -579,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);
 }
 
 /*
 }
 
 /*
@@ -592,8 +600,13 @@ chown()
                int     uid;
                int     gid;
        } *uap = (struct a *)u.u_ap;
                int     uid;
                int     gid;
        } *uap = (struct a *)u.u_ap;
+       register struct nameidata *ndp = &u.u_nd;
 
 
-       if (!suser() || (ip = owner(uap->fname, NOFOLLOW)) == NULL)
+       ndp->ni_nameiop = LOOKUP | NOFOLLOW;
+       ndp->ni_segflg = UIO_USERSPACE;
+       ndp->ni_dirp = uap->fname;
+       ip = namei(ndp);
+       if (ip == 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);
@@ -616,8 +629,6 @@ fchown()
        if (fp == NULL)
                return;
        ip = (struct inode *)fp->f_data;
        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);
@@ -635,10 +646,20 @@ 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 we don't own the file, are trying to change the owner
+        * of the file, or are not a member of the target group,
+        * the caller must be superuser or the call fails.
+        */
+       if ((u.u_uid != ip->i_uid || uid != ip->i_uid ||
+           !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;
@@ -656,7 +677,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);
@@ -674,6 +695,11 @@ utimes()
 
        if ((ip = owner(uap->fname, FOLLOW)) == NULL)
                return;
 
        if ((ip = owner(uap->fname, FOLLOW)) == NULL)
                return;
+       if (ip->i_fs->fs_ronly) {
+               u.u_error = EROFS;
+               iput(ip);
+               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;
@@ -698,7 +724,7 @@ truncate()
 {
        struct a {
                char    *fname;
 {
        struct a {
                char    *fname;
-               u_long  length;
+               off_t   length;
        } *uap = (struct a *)u.u_ap;
        struct inode *ip;
        register struct nameidata *ndp = &u.u_nd;
        } *uap = (struct a *)u.u_ap;
        struct inode *ip;
        register struct nameidata *ndp = &u.u_nd;
@@ -715,7 +741,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);
 }
@@ -727,7 +753,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;
@@ -741,7 +767,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);
 }
 
@@ -761,6 +787,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);
 }
@@ -780,11 +808,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.
  *
@@ -798,8 +827,8 @@ rename()
                char    *to;
        } *uap = (struct a *)u.u_ap;
        register struct inode *ip, *xp, *dp;
                char    *to;
        } *uap = (struct a *)u.u_ap;
        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;
 
        register struct nameidata *ndp = &u.u_nd;
        int error = 0;
 
@@ -810,7 +839,6 @@ rename()
        if (ip == NULL)
                return;
        dp = ndp->ni_pdir;
        if (ip == NULL)
                return;
        dp = ndp->ni_pdir;
-       oldparent = 0, doingdirectory = 0;
        if ((ip->i_mode&IFMT) == IFDIR) {
                register struct direct *d;
 
        if ((ip->i_mode&IFMT) == IFDIR) {
                register struct direct *d;
 
@@ -820,7 +848,7 @@ rename()
                 */
                if ((d->d_namlen == 1 && d->d_name[0] == '.') ||
                    (d->d_namlen == 2 && bcmp(d->d_name, "..", 2) == 0) ||
                 */
                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);
@@ -829,6 +857,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++;
        }
@@ -867,8 +896,9 @@ 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 {
@@ -898,13 +928,11 @@ 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);
@@ -922,6 +950,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.
@@ -930,7 +969,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;
                        }
@@ -938,6 +977,7 @@ 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;
@@ -954,9 +994,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) {
@@ -975,79 +1014,68 @@ rename()
        ndp->ni_nameiop = DELETE | LOCKPARENT;
        ndp->ni_segflg = UIO_USERSPACE;
        ndp->ni_dirp = uap->from;
        ndp->ni_nameiop = DELETE | LOCKPARENT;
        ndp->ni_segflg = UIO_USERSPACE;
        ndp->ni_dirp = uap->from;
-       zp = namei(ndp);
-       dp = ndp->ni_pdir;
+       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(ndp)) {
                }
                if (dirremove(ndp)) {
-                       zp->i_nlink--;
-                       zp->i_flag |= ICHG;
+                       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;
-
-               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;
-                       cacheinval(dp);
-                       (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);
@@ -1058,7 +1086,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;
 }
@@ -1095,7 +1122,7 @@ maknode(mode, ndp)
        ip->i_nlink = 1;
        ip->i_uid = u.u_uid;
        ip->i_gid = pdir->i_gid;
        ip->i_nlink = 1;
        ip->i_uid = u.u_uid;
        ip->i_gid = pdir->i_gid;
-       if (ip->i_mode & ISGID && !groupmember(ip->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);
@@ -1204,6 +1231,10 @@ 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
        /*
         * Directory all set up, now
         * install the entry for it in
@@ -1281,7 +1312,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;
        }
@@ -1294,6 +1325,7 @@ rmdir()
                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;
        /*