+ int error;
+
+ /*
+ * Check for unsettable attributes.
+ */
+ if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
+ (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
+ (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
+ ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
+ return (EINVAL);
+ }
+ /*
+ * Go through the fields and update iff not VNOVAL.
+ */
+ if (vap->va_uid != (u_short)VNOVAL || vap->va_gid != (u_short)VNOVAL)
+ if (error = ufs_chown(vp, vap->va_uid, vap->va_gid, p))
+ return (error);
+ if (vap->va_size != VNOVAL) {
+ if (vp->v_type == VDIR)
+ return (EISDIR);
+ if (error = VOP_TRUNCATE(vp, vap->va_size, 0)) /* IO_SYNC? */
+ return (error);
+ }
+ ip = VTOI(vp);
+ if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
+ if (cred->cr_uid != ip->i_uid &&
+ (error = suser(cred, &p->p_acflag)))
+ return (error);
+ if (vap->va_atime.tv_sec != VNOVAL)
+ ip->i_flag |= IACC;
+ if (vap->va_mtime.tv_sec != VNOVAL)
+ ip->i_flag |= IUPD;
+ ip->i_flag |= ICHG;
+ if (error = VOP_UPDATE(vp, &vap->va_atime, &vap->va_mtime, 1))
+ return (error);
+ }
+ error = 0;
+ if (vap->va_mode != (u_short)VNOVAL)
+ error = ufs_chmod(vp, (int)vap->va_mode, p);
+ if (vap->va_flags != VNOVAL) {
+ if (cred->cr_uid != ip->i_uid &&
+ (error = suser(cred, &p->p_acflag)))
+ return (error);
+ if (cred->cr_uid == 0) {
+ ip->i_flags = vap->va_flags;
+ } else {
+ ip->i_flags &= 0xffff0000;
+ ip->i_flags |= (vap->va_flags & 0xffff);
+ }
+ ip->i_flag |= ICHG;
+ }
+ return (error);
+}
+
+/*
+ * Change the mode on a file.
+ * Inode must be locked before calling.
+ */
+static int
+ufs_chmod(vp, mode, p)
+ register struct vnode *vp;
+ register int mode;
+ struct proc *p;
+{
+ register struct ucred *cred = p->p_ucred;
+ register struct inode *ip = VTOI(vp);
+ int error;
+
+ if (cred->cr_uid != ip->i_uid &&
+ (error = suser(cred, &p->p_acflag)))
+ return (error);
+ if (cred->cr_uid) {
+ if (vp->v_type != VDIR && (mode & ISVTX))
+ return (EFTYPE);
+ if (!groupmember(ip->i_gid, cred) && (mode & ISGID))
+ return (EPERM);
+ }
+ ip->i_mode &= ~07777;
+ ip->i_mode |= mode & 07777;
+ ip->i_flag |= ICHG;
+ if ((vp->v_flag & VTEXT) && (ip->i_mode & ISVTX) == 0)
+ (void) vnode_pager_uncache(vp);
+ return (0);
+}
+
+/*
+ * Perform chown operation on inode ip;
+ * inode must be locked prior to call.
+ */
+static int
+ufs_chown(vp, uid, gid, p)
+ register struct vnode *vp;
+ u_int uid;
+ u_int gid;
+ struct proc *p;
+{
+ register struct inode *ip = VTOI(vp);
+ register struct ucred *cred = p->p_ucred;
+ uid_t ouid;
+ gid_t ogid;
+ int error = 0;
+#ifdef QUOTA
+ register int i;
+ long change;
+#endif
+
+ if (uid == (u_short)VNOVAL)
+ uid = ip->i_uid;
+ if (gid == (u_short)VNOVAL)
+ 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 ((cred->cr_uid != ip->i_uid || uid != ip->i_uid ||
+ !groupmember((gid_t)gid, cred)) &&
+ (error = suser(cred, &p->p_acflag)))
+ return (error);
+ ouid = ip->i_uid;
+ ogid = ip->i_gid;
+#ifdef QUOTA
+ if (error = getinoquota(ip))
+ return (error);
+ if (ouid == uid) {
+ dqrele(vp, ip->i_dquot[USRQUOTA]);
+ ip->i_dquot[USRQUOTA] = NODQUOT;
+ }
+ if (ogid == gid) {
+ dqrele(vp, ip->i_dquot[GRPQUOTA]);
+ ip->i_dquot[GRPQUOTA] = NODQUOT;
+ }
+ change = ip->i_blocks;
+ (void) chkdq(ip, -change, cred, CHOWN);
+ (void) chkiq(ip, -1, cred, CHOWN);
+ for (i = 0; i < MAXQUOTAS; i++) {
+ dqrele(vp, ip->i_dquot[i]);
+ ip->i_dquot[i] = NODQUOT;
+ }
+#endif
+ ip->i_uid = uid;
+ ip->i_gid = gid;
+#ifdef QUOTA
+ if ((error = getinoquota(ip)) == 0) {
+ if (ouid == uid) {
+ dqrele(vp, ip->i_dquot[USRQUOTA]);
+ ip->i_dquot[USRQUOTA] = NODQUOT;
+ }
+ if (ogid == gid) {
+ dqrele(vp, ip->i_dquot[GRPQUOTA]);
+ ip->i_dquot[GRPQUOTA] = NODQUOT;
+ }
+ if ((error = chkdq(ip, change, cred, CHOWN)) == 0) {
+ if ((error = chkiq(ip, 1, cred, CHOWN)) == 0)
+ goto good;
+ else
+ (void) chkdq(ip, -change, cred, CHOWN|FORCE);
+ }
+ for (i = 0; i < MAXQUOTAS; i++) {
+ dqrele(vp, ip->i_dquot[i]);
+ ip->i_dquot[i] = NODQUOT;
+ }
+ }
+ ip->i_uid = ouid;
+ ip->i_gid = ogid;
+ if (getinoquota(ip) == 0) {
+ if (ouid == uid) {
+ dqrele(vp, ip->i_dquot[USRQUOTA]);
+ ip->i_dquot[USRQUOTA] = NODQUOT;
+ }
+ if (ogid == gid) {
+ dqrele(vp, ip->i_dquot[GRPQUOTA]);
+ ip->i_dquot[GRPQUOTA] = NODQUOT;
+ }
+ (void) chkdq(ip, change, cred, FORCE|CHOWN);
+ (void) chkiq(ip, 1, cred, FORCE|CHOWN);
+ (void) getinoquota(ip);
+ }
+ return (error);
+good:
+ if (getinoquota(ip))
+ panic("chown: lost quota");
+#endif /* QUOTA */
+ if (ouid != uid || ogid != gid)
+ ip->i_flag |= ICHG;
+ if (ouid != uid && cred->cr_uid != 0)
+ ip->i_mode &= ~ISUID;
+ if (ogid != gid && cred->cr_uid != 0)
+ ip->i_mode &= ~ISGID;
+ return (0);
+}
+
+/* ARGSUSED */
+int
+ufs_ioctl(vp, com, data, fflag, cred, p)
+ struct vnode *vp;
+ int com;
+ caddr_t data;
+ int fflag;
+ struct ucred *cred;
+ struct proc *p;
+{
+
+ return (ENOTTY);
+}
+
+/* ARGSUSED */
+int
+ufs_select(vp, which, fflags, cred, p)
+ struct vnode *vp;
+ int which, fflags;
+ struct ucred *cred;
+ struct proc *p;
+{
+
+ /*
+ * We should really check to see if I/O is possible.
+ */
+ return (1);
+}
+
+/*
+ * Mmap a file
+ *
+ * NB Currently unsupported.
+ */
+/* ARGSUSED */
+int
+ufs_mmap(vp, fflags, cred, p)
+ struct vnode *vp;
+ int fflags;
+ struct ucred *cred;
+ struct proc *p;
+{
+
+ return (EINVAL);
+}