fix chown; don't steal groups or give away files
[unix-history] / usr / src / sys / ufs / ffs / ufs_vnops.c
index 44f483c..016a175 100644 (file)
@@ -1,4 +1,10 @@
-/*     ufs_vnops.c     6.18    85/05/22        */
+/*
+ * 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 "param.h"
 #include "systm.h"
@@ -113,14 +119,12 @@ 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) {
@@ -131,16 +135,15 @@ copen(mode, arg, fname)
                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;
                }
@@ -148,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;
@@ -167,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);
@@ -177,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--;
 }
 
 /*
 }
 
 /*
@@ -332,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);
 }
@@ -517,7 +517,8 @@ readlink()
                u.u_error = EINVAL;
                goto out;
        }
                u.u_error = EINVAL;
                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;
@@ -599,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);
@@ -623,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);
@@ -648,6 +652,14 @@ chown1(ip, uid, gid)
                uid = ip->i_uid;
        if (gid == -1)
                gid = ip->i_gid;
                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;
@@ -665,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);
@@ -712,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;
@@ -729,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);
 }
@@ -741,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;
@@ -755,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);
 }
 
@@ -775,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);
 }
@@ -936,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.
@@ -1097,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);