Hello.

I think I got patch for setattr() syscall (attached as setattr.patch)
and it works fine, but there is a problem in ufs_setattr().
It looks like ufs_setattr() will don't handle property changes of many
file attibutes, because there is many places where it can modify some
attributes, but return with error, because next change will fail.
For now there is also no chance to change flags to '[su]chg'/'[su]appnd'
and other attributes in one call of ufs_setattr(), because flags are
changed first and other modifications aren't permitted because of that.
If we put flags changing code at the end, there will be no chance to
change other attributes when flags are '[su]chg' or '[su]appnd'.
So I'm sending also patch for UFS that allow other attributes changes
when before or after setattr() there is/will be no [su](chg|appnd)
flags (patch attached: ufs_setattr.patch). It isn't the best way.
The best why will be made a copy of inode struct go through changes
and copy it back only if everything succeeded. This cost performance
of course, so...

PS. This syscall will be quite usefull in tar(1) I think.

-- 
Pawel Jakub Dawidek                       [EMAIL PROTECTED]
UNIX Systems Programmer/Administrator     http://garage.freebsd.pl
Am I Evil? Yes, I Am!                     http://cerber.sourceforge.net
diff -ur /usr/src/sys/kern/syscalls.master src/sys/kern/syscalls.master
--- /usr/src/sys/kern/syscalls.master   Sat Jul  5 16:54:11 2003
+++ src/sys/kern/syscalls.master        Sat Jul  5 16:54:05 2003
@@ -637,6 +637,9 @@
                            int attrnamespace, void *data, size_t nbytes); }
 439    STD     BSD     { ssize_t extattr_list_link(const char *path, \
                            int attrnamespace, void *data, size_t nbytes); }
+440    STD     BSD     { int setattr(char *path, struct stat *sb, int op); }
+441    STD     BSD     { int lsetattr(char *path, struct stat *sb, int op); }
+442    STD     BSD     { int fsetattr(int fd, struct stat *sb, int op); }
 
 ; Please copy any additions and changes to the following compatability tables:
 ; sys/ia64/ia32/syscalls.master  (take a best guess)
diff -ur /usr/src/sys/kern/vfs_syscalls.c src/sys/kern/vfs_syscalls.c
--- /usr/src/sys/kern/vfs_syscalls.c    Tue Jul  1 11:29:38 2003
+++ src/sys/kern/vfs_syscalls.c Sat Jul  5 21:12:23 2003
@@ -1940,48 +1940,254 @@
        return (error);
 }
 
-/*
- * Common implementation code for chflags() and fchflags().
- */
 static int
-setfflags(td, vp, flags)
-       struct thread *td;
-       struct vnode *vp;
-       int flags;
+setfattr(struct thread *td, struct vnode *vp, struct stat *sb, u_int op,
+    int locked)
 {
-       int error;
        struct mount *mp;
        struct vattr vattr;
+       int error;
 
-       /*
-        * Prevent non-root users from setting flags on devices.  When
-        * a device is reused, users can retain ownership of the device
-        * if they are allowed to set flags and programs assume that
-        * chown can't fail when done as root.
-        */
-       if (vp->v_type == VCHR || vp->v_type == VBLK) {
-               error = suser_cred(td->td_ucred, PRISON_ROOT);
-               if (error)
+       if ((op & SA_OP_ALL) == 0)
+               return (0);
+
+       if ((op & SA_OP_FLAGS) != 0) {
+               /*
+                * Prevent non-root users from setting flags on devices.  When
+                * a device is reused, users can retain ownership of the device
+                * if they are allowed to set flags and programs assume that
+                * chown can't fail when done as root.
+                */
+               if (vp->v_type == VCHR || vp->v_type == VBLK) {
+                       error = suser_cred(td->td_ucred, PRISON_ROOT);
+                       if (error)
+                               return (error);
+               }
+       }
+
+       if (!locked) {
+               if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0)
                        return (error);
+               VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE);
+               vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
        }
 
-       if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0)
-               return (error);
-       VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE);
-       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
        VATTR_NULL(&vattr);
-       vattr.va_flags = flags;
+
+       if ((op & SA_OP_FLAGS) != 0) {
+               vattr.va_flags = sb->st_flags;
 #ifdef MAC
-       error = mac_check_vnode_setflags(td->td_ucred, vp, vattr.va_flags);
-       if (error == 0)
+               error = mac_check_vnode_setflags(td->td_ucred, vp,
+                   vattr.va_flags);
+               if (error != 0)
+                       goto fail;
 #endif
-               error = VOP_SETATTR(vp, &vattr, td->td_ucred, td);
-       VOP_UNLOCK(vp, 0, td);
-       vn_finished_write(mp);
+       }
+       if ((op & SA_OP_MODE) != 0) {
+               vattr.va_mode = sb->st_mode & ALLPERMS;
+#ifdef MAC
+               error = mac_check_vnode_setmode(td->td_ucred, vp,
+                   vattr.va_mode);
+               if (error != 0)
+                       goto fail;
+#endif
+       }
+       if ((op & SA_OP_OWNER) != 0) {
+               vattr.va_uid = sb->st_uid;
+               vattr.va_gid = sb->st_gid;
+#ifdef MAC
+               error = mac_check_vnode_setowner(td->td_ucred, vp, vattr.va_uid,
+                   vattr.va_gid);
+               if (error != 0)
+                       goto fail;
+#endif
+       }
+       if ((op & SA_OP_UTIMES) != 0) {
+               if ((op & SA_OP_ATIME) != 0)
+                       vattr.va_atime = sb->st_atimespec;
+               if ((op & SA_OP_MTIME) != 0)
+                       vattr.va_mtime = sb->st_mtimespec;
+               if ((op & SA_OP_BIRTHTIME) != 0)
+                       vattr.va_birthtime = sb->st_birthtimespec;
+#ifdef MAC
+               error = mac_check_vnode_setutimes(td->td_ucred, vp,
+                   vattr.va_atime, vattr.va_mtime);
+               if (error != 0)
+                       goto fail;
+#endif
+               if ((op & SA_OP_UTIMES_NULL) != 0)
+                       vattr.va_vaflags |= VA_UTIMES_NULL;
+       }
+
+       error = VOP_SETATTR(vp, &vattr, td->td_ucred, td);
+fail:
+       if (!locked) {
+               VOP_UNLOCK(vp, 0, td);
+               vn_finished_write(mp);
+       }
+
+       return (error);
+}
+
+/*
+ * Change attributes of a file given path name.
+ */
+#ifndef _SYS_SYSPROTO_H_
+struct setattr_args {
+       char    *path;
+       struct stat *sb;
+       u_int   op;
+};
+#endif
+/* ARGSUSED */
+int
+setattr(td, uap)
+       struct thread *td;
+       register struct setattr_args /* {
+               char *path;
+               struct stat *sb;
+               u_int op;
+       } */ *uap;
+{
+
+       return (kern_setattr(td, uap->path, UIO_USERSPACE, uap->sb,
+           UIO_USERSPACE, uap->op));
+}
+
+int
+kern_setattr(struct thread *td, char *path, enum uio_seg pathseg,
+    struct stat *sb, enum uio_seg sbseg, u_int op)
+{
+       int error;
+       struct nameidata nd;
+       struct stat stb;
+
+       if (sbseg == UIO_USERSPACE) {
+               if ((error = copyin(sb, &stb, sizeof(stb))) != 0)
+                       return (error);
+       } else {
+               stb = *sb;
+       }
+       NDINIT(&nd, LOOKUP, FOLLOW, pathseg, path, td);
+       if ((error = namei(&nd)) != 0)
+               return (error);
+       NDFREE(&nd, NDF_ONLY_PNBUF);
+       error = setfattr(td, nd.ni_vp, &stb, op, 0);
+       vrele(nd.ni_vp);
+       return error;
+}
+
+/*
+ * Change attributes of a file given path name (don't follow links.)
+ */
+#ifndef _SYS_SYSPROTO_H_
+struct lsetattr_args {
+       char    *path;
+       struct stat *sb;
+       u_int   op;
+};
+#endif
+/* ARGSUSED */
+int
+lsetattr(td, uap)
+       struct thread *td;
+       register struct lsetattr_args /* {
+               char *path;
+               struct stat *sb;
+               u_int op;
+       } */ *uap;
+{
+
+       return (kern_lsetattr(td, uap->path, UIO_USERSPACE, uap->sb,
+           UIO_USERSPACE, uap->op));
+}
+
+int
+kern_lsetattr(struct thread *td, char *path, enum uio_seg pathseg,
+    struct stat *sb, enum uio_seg sbseg, u_int op)
+{
+       int error;
+       struct nameidata nd;
+       struct stat stb;
+
+       if (sbseg == UIO_USERSPACE) {
+               if ((error = copyin(sb, &stb, sizeof(stb))) != 0)
+                       return (error);
+       } else {
+               stb = *sb;
+       }
+       NDINIT(&nd, LOOKUP, NOFOLLOW, pathseg, path, td);
+       if ((error = namei(&nd)) != 0)
+               return (error);
+       NDFREE(&nd, NDF_ONLY_PNBUF);
+       error = setfattr(td, nd.ni_vp, &stb, op, 0);
+       vrele(nd.ni_vp);
+       return error;
+}
+
+/*
+ * Change attributes of a file given a file descriptor.
+ */
+#ifndef _SYS_SYSPROTO_H_
+struct fsetattr_args {
+       int     fd;
+       struct stat *sb;
+       u_int   op;
+};
+#endif
+/* ARGSUSED */
+int
+fsetattr(td, uap)
+       struct thread *td;
+       register struct fsetattr_args /* {
+               int fd;
+               struct stat *sb;
+               u_int op;
+       } */ *uap;
+{
+
+       return (kern_fsetattr(td, uap->fd, uap->sb, UIO_USERSPACE, uap->op));
+}
+
+int
+kern_fsetattr(struct thread *td, int fd, struct stat *sb, enum uio_seg sbseg,
+    u_int op)
+{
+       int error;
+       struct file *fp;
+       struct stat stb;
+
+       if (sbseg == UIO_USERSPACE) {
+               if ((error = copyin(sb, &stb, sizeof(stb))) != 0)
+                       return (error);
+       } else {
+               stb = *sb;
+       }
+       if ((error = getvnode(td->td_proc->p_fd, fd, &fp)) != 0)
+               return (error);
+       error = setfattr(td, fp->f_vnode, &stb, op, 0);
+       fdrop(fp, td);
+
        return (error);
 }
 
 /*
+ * Common implementation code for chflags() and fchflags().
+ */
+static int __inline
+setfflags(td, vp, flags)
+       struct thread *td;
+       struct vnode *vp;
+       int flags;
+{
+       struct stat sb;
+
+       sb.st_flags = flags;
+
+       return (setfattr(td, vp, &sb, SA_OP_FLAGS, 0));
+}
+
+/*
  * Change flags of a file given a path name.
  */
 #ifndef _SYS_SYSPROTO_H_
@@ -2065,30 +2271,17 @@
 /*
  * Common implementation code for chmod(), lchmod() and fchmod().
  */
-static int
+static int __inline
 setfmode(td, vp, mode)
        struct thread *td;
        struct vnode *vp;
        int mode;
 {
-       int error;
-       struct mount *mp;
-       struct vattr vattr;
+       struct stat sb;
 
-       if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0)
-               return (error);
-       VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE);
-       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
-       VATTR_NULL(&vattr);
-       vattr.va_mode = mode & ALLPERMS;
-#ifdef MAC
-       error = mac_check_vnode_setmode(td->td_ucred, vp, vattr.va_mode);
-       if (error == 0)
-#endif
-               error = VOP_SETATTR(vp, &vattr, td->td_ucred, td);
-       VOP_UNLOCK(vp, 0, td);
-       vn_finished_write(mp);
-       return error;
+       sb.st_mode = mode;
+
+       return (setfattr(td, vp, &sb, SA_OP_MODE, 0));
 }
 
 /*
@@ -2189,33 +2382,19 @@
 /*
  * Common implementation for chown(), lchown(), and fchown()
  */
-static int
+static int __inline
 setfown(td, vp, uid, gid)
        struct thread *td;
        struct vnode *vp;
        uid_t uid;
        gid_t gid;
 {
-       int error;
-       struct mount *mp;
-       struct vattr vattr;
+       struct stat sb;
 
-       if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0)
-               return (error);
-       VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE);
-       vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
-       VATTR_NULL(&vattr);
-       vattr.va_uid = uid;
-       vattr.va_gid = gid;
-#ifdef MAC
-       error = mac_check_vnode_setowner(td->td_ucred, vp, vattr.va_uid,
-           vattr.va_gid);
-       if (error == 0)
-#endif
-               error = VOP_SETATTR(vp, &vattr, td->td_ucred, td);
-       VOP_UNLOCK(vp, 0, td);
-       vn_finished_write(mp);
-       return error;
+       sb.st_uid = uid;
+       sb.st_gid = gid;
+
+       return (setfattr(td, vp, &sb, SA_OP_OWNER, 0));
 }
 
 /*
@@ -2363,7 +2542,7 @@
 /*
  * Common implementation code for utimes(), lutimes(), and futimes().
  */
-static int
+static int __inline
 setutimes(td, vp, ts, numtimes, nullflag)
        struct thread *td;
        struct vnode *vp;
@@ -2374,6 +2553,8 @@
        int error, setbirthtime;
        struct mount *mp;
        struct vattr vattr;
+       struct stat sb;
+       u_int op;
 
        if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0)
                return (error);
@@ -2383,24 +2564,26 @@
        if (numtimes < 3 && VOP_GETATTR(vp, &vattr, td->td_ucred, td) == 0 &&
            timespeccmp(&ts[1], &vattr.va_birthtime, < ))
                setbirthtime = 1;
-       VATTR_NULL(&vattr);
-       vattr.va_atime = ts[0];
-       vattr.va_mtime = ts[1];
-       if (setbirthtime)
-               vattr.va_birthtime = ts[1];
-       if (numtimes > 2)
-               vattr.va_birthtime = ts[2];
+       sb.st_atimespec = ts[0];
+       sb.st_mtimespec = ts[1];
+       op = SA_OP_ATIME | SA_OP_MTIME;
+       if (setbirthtime) {
+               sb.st_birthtimespec = ts[1];
+               op |= SA_OP_BIRTHTIME;
+       }
+       if (numtimes > 2) {
+               sb.st_birthtimespec = ts[2];
+               op |= SA_OP_BIRTHTIME;
+       }
        if (nullflag)
-               vattr.va_vaflags |= VA_UTIMES_NULL;
-#ifdef MAC
-       error = mac_check_vnode_setutimes(td->td_ucred, vp, vattr.va_atime,
-           vattr.va_mtime);
-#endif
-       if (error == 0)
-               error = VOP_SETATTR(vp, &vattr, td->td_ucred, td);
+               op |= SA_OP_UTIMES_NULL;
+
+       error = setfattr(td, vp, &sb, op, 1);
+
        VOP_UNLOCK(vp, 0, td);
        vn_finished_write(mp);
-       return error;
+
+       return (error);
 }
 
 /*
diff -ur /usr/src/sys/sys/stat.h src/sys/sys/stat.h
--- /usr/src/sys/sys/stat.h     Thu May 22 19:07:57 2003
+++ src/sys/sys/stat.h  Mon Jul  7 11:34:36 2003
@@ -288,6 +288,22 @@
 #define        SF_NOUNLINK     0x00100000      /* file may not be removed or renamed 
*/
 #define        SF_SNAPSHOT     0x00200000      /* snapshot inode */
 
+#if __BSD_VISIBLE
+/*
+ * Avaliable options for setattr() syscall.
+ */
+#define        SA_OP_FLAGS     0x01
+#define        SA_OP_MODE      0x02
+#define        SA_OP_OWNER     0x04
+#define        SA_OP_ATIME     0x08
+#define        SA_OP_MTIME     0x10
+#define        SA_OP_BIRTHTIME 0x20
+#define        SA_OP_UTIMES    (SA_OP_ATIME | SA_OP_MTIME | SA_OP_BIRTHTIME)
+#define        SA_OP_ALL       (SA_OP_FLAGS | SA_OP_MODE | SA_OP_OWNER | SA_OP_UTIMES)
+/* Additional options: */
+#define        SA_OP_UTIMES_NULL       0x0100
+#endif
+
 #ifdef _KERNEL
 /*
  * Shorthand abbreviations of above.
@@ -303,6 +319,9 @@
 #ifndef _KERNEL
 __BEGIN_DECLS
 #if __BSD_VISIBLE
+int    setattr(const char *, struct stat *, unsigned op);
+int    lsetattr(const char *, struct stat *, unsigned op);
+int    fsetattr(int, struct stat *, unsigned op);
 int    chflags(const char *, unsigned long);
 #endif
 int    chmod(const char *, mode_t);
diff -ur /usr/src/sys/sys/syscall.h src/sys/sys/syscall.h
--- /usr/src/sys/sys/syscall.h  Tue Jul  1 11:30:18 2003
+++ src/sys/sys/syscall.h       Sat Jul  5 21:06:39 2003
@@ -2,8 +2,8 @@
  * System call numbers.
  *
  * DO NOT EDIT-- this file is automatically generated.
- * $FreeBSD: src/sys/sys/syscall.h,v 1.138 2003/06/28 08:29:04 davidxu Exp $
- * created from FreeBSD: src/sys/kern/syscalls.master,v 1.150 2003/06/04 03:49:31 
rwatson Exp 
+ * $FreeBSD$
+ * created from FreeBSD: src/sys/kern/syscalls.master,v 1.151 2003/06/28 08:29:05 
davidxu Exp 
  */
 
 #define        SYS_syscall     0
@@ -347,4 +347,7 @@
 #define        SYS_extattr_list_fd     437
 #define        SYS_extattr_list_file   438
 #define        SYS_extattr_list_link   439
-#define        SYS_MAXSYSCALL  440
+#define        SYS_setattr     440
+#define        SYS_lsetattr    441
+#define        SYS_fsetattr    442
+#define        SYS_MAXSYSCALL  443
diff -ur /usr/src/sys/sys/syscall.mk src/sys/sys/syscall.mk
--- /usr/src/sys/sys/syscall.mk Tue Jul  1 11:30:18 2003
+++ src/sys/sys/syscall.mk      Sat Jul  5 21:06:39 2003
@@ -1,7 +1,7 @@
 # FreeBSD system call names.
 # DO NOT EDIT-- this file is automatically generated.
-# $FreeBSD: src/sys/sys/syscall.mk,v 1.93 2003/06/28 08:29:04 davidxu Exp $
-# created from FreeBSD: src/sys/kern/syscalls.master,v 1.150 2003/06/04 03:49:31 
rwatson Exp 
+# $FreeBSD$
+# created from FreeBSD: src/sys/kern/syscalls.master,v 1.151 2003/06/28 08:29:05 
davidxu Exp 
 MIASM =  \
        syscall.o \
        exit.o \
@@ -292,4 +292,7 @@
        jail_attach.o \
        extattr_list_fd.o \
        extattr_list_file.o \
-       extattr_list_link.o
+       extattr_list_link.o \
+       setattr.o \
+       lsetattr.o \
+       fsetattr.o
diff -ur /usr/src/sys/sys/syscallsubr.h src/sys/sys/syscallsubr.h
--- /usr/src/sys/sys/syscallsubr.h      Sat Jul  5 17:01:40 2003
+++ src/sys/sys/syscallsubr.h   Sat Jul  5 19:58:10 2003
@@ -30,6 +30,7 @@
 
 #include <sys/signal.h>
 #include <sys/uio.h>
+#include <sys/stat.h>
 
 struct sockaddr;
 struct msghdr;
@@ -41,6 +42,12 @@
            int flags);
 int    kern_bind(struct thread *td, int fd, struct sockaddr *sa);
 int    kern_chdir(struct thread *td, char *path, enum uio_seg pathseg);
+int    kern_setattr(struct thread *td, char *path, enum uio_seg pathseg,
+           struct stat *sb, enum uio_seg sbseg, u_int op);
+int    kern_lsetattr(struct thread *td, char *path, enum uio_seg pathseg,
+           struct stat *sb, enum uio_seg sbseg, u_int op);
+int    kern_fsetattr(struct thread *td, int fd, struct stat *sb,
+           enum uio_seg sbseg, u_int op);
 int    kern_chmod(struct thread *td, char *path, enum uio_seg pathseg,
            int mode);
 int    kern_chown(struct thread *td, char *path, enum uio_seg pathseg, int uid,
diff -ur /usr/src/sys/ufs/ufs/ufs_vnops.c src/sys/ufs/ufs/ufs_vnops.c
--- /usr/src/sys/ufs/ufs/ufs_vnops.c    Tue Jul  1 11:30:20 2003
+++ src/sys/ufs/ufs/ufs_vnops.c Sun Jul  6 11:27:12 2003
@@ -87,6 +87,7 @@
 
 static int ufs_access(struct vop_access_args *);
 static int ufs_advlock(struct vop_advlock_args *);
+static int ufs_chflags(struct vnode *, u_int32_t, struct ucred *, struct thread *);
 static int ufs_chmod(struct vnode *, int, struct ucred *, struct thread *);
 static int ufs_chown(struct vnode *, uid_t, gid_t, struct ucred *, struct thread *);
 static int ufs_close(struct vop_close_args *);
@@ -487,51 +488,28 @@
                return (EINVAL);
        }
        if (vap->va_flags != VNOVAL) {
-               if (vp->v_mount->mnt_flag & MNT_RDONLY)
-                       return (EROFS);
                /*
-                * Callers may only modify the file flags on objects they
-                * have VADMIN rights for.
+                * If we have for example 'schg' flag on file and we're
+                * trying to remove it, but set 'sappnd' flag no other
+                * modifications are permitted.
                 */
-               if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
+               if ((vap->va_flags & (IMMUTABLE | APPEND)) &&
+                   (ip->i_flags & (IMMUTABLE | APPEND)) &&
+                   (vap->va_uid != (uid_t)VNOVAL ||
+                    vap->va_gid != (gid_t)VNOVAL ||
+                    vap->va_size != VNOVAL ||
+                    vap->va_atime.tv_sec != VNOVAL ||
+                    vap->va_mtime.tv_sec != VNOVAL ||
+                    vap->va_birthtime.tv_sec != VNOVAL ||
+                    vap->va_mode != (mode_t)VNOVAL)) {
+                       return (EPERM);
+               }
+               error = ufs_chflags(vp, (u_int32_t)vap->va_flags, cred, td);
+               if (error != 0)
                        return (error);
-               /*
-                * Unprivileged processes and privileged processes in
-                * jail() are not permitted to unset system flags, or
-                * modify flags if any system flags are set.
-                * Privileged non-jail processes may not modify system flags
-                * if securelevel > 0 and any existing system flags are set.
-                */
-               if (!suser_cred(cred, PRISON_ROOT)) {
-                       if (ip->i_flags
-                           & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {
-                               error = securelevel_gt(cred, 0);
-                               if (error)
-                                       return (error);
-                       }
-                       /* Snapshot flag cannot be set or cleared */
-                       if (((vap->va_flags & SF_SNAPSHOT) != 0 &&
-                            (ip->i_flags & SF_SNAPSHOT) == 0) ||
-                           ((vap->va_flags & SF_SNAPSHOT) == 0 &&
-                            (ip->i_flags & SF_SNAPSHOT) != 0))
-                               return (EPERM);
-                       ip->i_flags = vap->va_flags;
-                       DIP(ip, i_flags) = vap->va_flags;
-               } else {
-                       if (ip->i_flags
-                           & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) ||
-                           (vap->va_flags & UF_SETTABLE) != vap->va_flags)
-                               return (EPERM);
-                       ip->i_flags &= SF_SETTABLE;
-                       ip->i_flags |= (vap->va_flags & UF_SETTABLE);
-                       DIP(ip, i_flags) = ip->i_flags;
-               }
-               ip->i_flag |= IN_CHANGE;
-               if (vap->va_flags & (IMMUTABLE | APPEND))
-                       return (0);
-       }
-       if (ip->i_flags & (IMMUTABLE | APPEND))
+       } else if (ip->i_flags & (IMMUTABLE | APPEND)) {
                return (EPERM);
+       }
        /*
         * Go through the fields and update iff not VNOVAL.
         */
@@ -618,8 +596,63 @@
                        return (EPERM);
                error = ufs_chmod(vp, (int)vap->va_mode, cred, td);
        }
+       if (error == 0 && vap->va_flags != VNOVAL && !chflags)
+               error = ufs_chflags(vp, (u_int32_t)vap->va_flags, cred, td);
        VN_KNOTE(vp, NOTE_ATTRIB);
        return (error);
+}
+
+/*
+ * Perform chflags operation on inode ip;
+ * inode must be locked prior to call.
+ */
+static int
+ufs_chflags(struct vnode *vp, u_int32_t flags, struct ucred *cred,
+    struct thread *td)
+{
+       struct inode *ip = VTOI(vp);
+       int error;
+
+       if (vp->v_mount->mnt_flag & MNT_RDONLY)
+               return (EROFS);
+       /*
+        * Callers may only modify the file flags on objects they
+        * have VADMIN rights for.
+        */
+       if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
+               return (error);
+       /*
+        * Unprivileged processes and privileged processes in
+        * jail() are not permitted to unset system flags, or
+        * modify flags if any system flags are set.
+        * Privileged non-jail processes may not modify system flags
+        * if securelevel > 0 and any existing system flags are set.
+        */
+       if (!suser_cred(cred, PRISON_ROOT)) {
+               if (ip->i_flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {
+                       error = securelevel_gt(cred, 0);
+                       if (error)
+                               return (error);
+               }
+               /* Snapshot flag cannot be set or cleared */
+               if (((flags & SF_SNAPSHOT) != 0 &&
+                    (ip->i_flags & SF_SNAPSHOT) == 0) ||
+                   ((flags & SF_SNAPSHOT) == 0 &&
+                    (ip->i_flags & SF_SNAPSHOT) != 0))
+                       return (EPERM);
+               ip->i_flags = flags;
+               DIP(ip, i_flags) = flags;
+       } else {
+               if (ip->i_flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) ||
+                   (flags & UF_SETTABLE) != flags)
+                       return (EPERM);
+               ip->i_flags &= SF_SETTABLE;
+               ip->i_flags |= (flags & UF_SETTABLE);
+               DIP(ip, i_flags) = ip->i_flags;
+       }
+       ip->i_flag |= IN_CHANGE;
+
+       return (0);
 }
 
 /*
_______________________________________________
[EMAIL PROTECTED] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-hackers
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to