Exemple #1
0
/*
 * Perform chown operation on inode ip;
 * inode must be locked prior to call.
 */
static int
ext2fs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, struct proc *p)
{
	struct inode *ip = VTOI(vp);
	uid_t ouid;
	gid_t ogid;
	int error = 0;

	if (uid == (uid_t)VNOVAL)
		uid = ip->i_e2fs_uid;
	if (gid == (gid_t)VNOVAL)
		gid = ip->i_e2fs_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_e2fs_uid || uid != ip->i_e2fs_uid ||
		(gid != ip->i_e2fs_gid && !groupmember((gid_t)gid, cred))) &&
		(error = suser_ucred(cred)))
		return (error);
	ogid = ip->i_e2fs_gid;
	ouid = ip->i_e2fs_uid;

	ip->i_e2fs_gid = gid;
	ip->i_e2fs_uid = uid;
	if (ouid != uid || ogid != gid)
		ip->i_flag |= IN_CHANGE;
	if (ouid != uid && cred->cr_uid != 0)
		ip->i_e2fs_mode &= ~ISUID;
	if (ogid != gid && cred->cr_uid != 0)
		ip->i_e2fs_mode &= ~ISGID;
	return (0);
}
Exemple #2
0
/*
 * Change the mode on a file.
 * Inode must be locked before calling.
 */
static int
ext2fs_chmod(struct vnode *vp, mode_t mode, struct ucred *cred, struct proc *p)
{
	struct inode *ip = VTOI(vp);
	int error;

	if (cred->cr_uid != ip->i_e2fs_uid && (error = suser_ucred(cred)))
		return (error);
	if (cred->cr_uid) {
		if (vp->v_type != VDIR && (mode & S_ISTXT))
			return (EFTYPE);
		if (!groupmember(ip->i_e2fs_gid, cred) && (mode & ISGID))
			return (EPERM);
	}
	ip->i_e2fs_mode &= ~ALLPERMS;
	ip->i_e2fs_mode |= (mode & ALLPERMS);
	ip->i_flag |= IN_CHANGE;
	if ((vp->v_flag & VTEXT) && (ip->i_e2fs_mode & S_ISTXT) == 0)
		(void) uvm_vnp_uncache(vp);
	return (0);
}
Exemple #3
0
/*
 * Change the mode on a file.
 * Inode must be locked before calling.
 */
int
ufs_chmod(struct vnode *vp, int mode, struct ucred *cred, struct proc *p)
{
	struct inode *ip = VTOI(vp);
	int error;

	if (cred->cr_uid != DIP(ip, uid) &&
	    (error = suser_ucred(cred)))
		return (error);
	if (cred->cr_uid) {
		if (vp->v_type != VDIR && (mode & S_ISTXT))
			return (EFTYPE);
		if (!groupmember(DIP(ip, gid), cred) && (mode & ISGID))
			return (EPERM);
	}
	DIP_AND(ip, mode, ~ALLPERMS);
	DIP_OR(ip, mode, mode & ALLPERMS);
	ip->i_flag |= IN_CHANGE;
	if ((vp->v_flag & VTEXT) && (DIP(ip, mode) & S_ISTXT) == 0)
		(void) uvm_vnp_uncache(vp);
	return (0);
}
Exemple #4
0
/*
 * Perform chown operation on inode ip;
 * inode must be locked prior to call.
 */
int
ufs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred,
    struct proc *p)
{
	struct inode *ip = VTOI(vp);
	uid_t ouid;
	gid_t ogid;
	int error = 0;
	daddr64_t change;
	enum ufs_quota_flags quota_flags = 0;

	if (uid == (uid_t)VNOVAL)
		uid = DIP(ip, uid);
	if (gid == (gid_t)VNOVAL)
		gid = DIP(ip, 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 != DIP(ip, uid) || uid != DIP(ip, uid) ||
	    (gid != DIP(ip, gid) && !groupmember((gid_t)gid, cred))) &&
	    (error = suser_ucred(cred)))
		return (error);
	ogid = DIP(ip, gid);
	ouid = DIP(ip, uid);
	change = DIP(ip, blocks);

	if (ouid == uid)
		quota_flags |= UFS_QUOTA_NOUID;
	
	if (ogid == gid)
		quota_flags |= UFS_QUOTA_NOGID;

	if ((error = getinoquota(ip)) != 0)
		return (error);
	(void) ufs_quota_free_blocks2(ip, change, cred, quota_flags);
	(void) ufs_quota_free_inode2(ip, cred, quota_flags);
	(void) ufs_quota_delete(ip);

	DIP_ASSIGN(ip, gid, gid);
	DIP_ASSIGN(ip, uid, uid);

	if ((error = getinoquota(ip)) != 0)
		goto error;

	if ((error = ufs_quota_alloc_blocks2(ip, change, cred, 
		 quota_flags)) != 0) 
		goto error;

	if ((error = ufs_quota_alloc_inode2(ip, cred ,
		 quota_flags)) != 0) {
		(void)ufs_quota_free_blocks2(ip, change, cred, 
		    quota_flags);		
		goto error;
	}

	if (getinoquota(ip))
		panic("chown: lost quota");

	if (ouid != uid || ogid != gid)
		ip->i_flag |= IN_CHANGE;
	if (ouid != uid && cred->cr_uid != 0)
		DIP_AND(ip, mode, ~ISUID);
	if (ogid != gid && cred->cr_uid != 0)
		DIP_AND(ip, mode, ~ISGID);
	return (0);

error:
	(void) ufs_quota_delete(ip);

	DIP_ASSIGN(ip, gid, ogid);
	DIP_ASSIGN(ip, uid, ouid);

	if (getinoquota(ip) == 0) {
		(void) ufs_quota_alloc_blocks2(ip, change, cred, 
		    quota_flags | UFS_QUOTA_FORCE);
		(void) ufs_quota_alloc_inode2(ip, cred,
		    quota_flags | UFS_QUOTA_FORCE);
		(void) getinoquota(ip);
	}
	return (error);

}
Exemple #5
0
/*
 * Set attribute vnode op. called from several syscalls
 */
int
ufs_setattr(void *v)
{
	struct vop_setattr_args *ap = v;
	struct vattr *vap = ap->a_vap;
	struct vnode *vp = ap->a_vp;
	struct inode *ip = VTOI(vp);
	struct ucred *cred = ap->a_cred;
	struct proc *p = curproc;
	int error;
	long hint = NOTE_ATTRIB;
	u_quad_t oldsize;

	/*
	 * 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);
	}
	if (vap->va_flags != VNOVAL) {
		if (vp->v_mount->mnt_flag & MNT_RDONLY)
			return (EROFS);
		if (cred->cr_uid != DIP(ip, uid) &&
		    (error = suser_ucred(cred)))
			return (error);
		if (cred->cr_uid == 0) {
			if ((DIP(ip, flags) & (SF_IMMUTABLE | SF_APPEND)) &&
			    securelevel > 0)
				return (EPERM);
			DIP_ASSIGN(ip, flags, vap->va_flags);
		} else {
			if (DIP(ip, flags) & (SF_IMMUTABLE | SF_APPEND) ||
			    (vap->va_flags & UF_SETTABLE) != vap->va_flags)
				return (EPERM);
			DIP_AND(ip, flags, SF_SETTABLE);
			DIP_OR(ip, flags, vap->va_flags & UF_SETTABLE);
		}
		ip->i_flag |= IN_CHANGE;
		if (vap->va_flags & (IMMUTABLE | APPEND))
			return (0);
	}
	if (DIP(ip, flags) & (IMMUTABLE | APPEND))
		return (EPERM);
	/*
	 * Go through the fields and update if not VNOVAL.
	 */
	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
		if (vp->v_mount->mnt_flag & MNT_RDONLY)
			return (EROFS);
		error = ufs_chown(vp, vap->va_uid, vap->va_gid, cred, p);
		if (error)
			return (error);
	}
	if (vap->va_size != VNOVAL) {
		oldsize = DIP(ip, size);
		/*
		 * Disallow write attempts on read-only file systems;
		 * unless the file is a socket, fifo, or a block or
		 * character device resident on the file system.
		 */
		switch (vp->v_type) {
		case VDIR:
 			return (EISDIR);
		case VLNK:
		case VREG:
			if (vp->v_mount->mnt_flag & MNT_RDONLY)
				return (EROFS);
			break;
		default:
			break;
		}
 		if ((error = UFS_TRUNCATE(ip, vap->va_size, 0, cred)) != 0)
 			return (error);
		if (vap->va_size < oldsize)
			hint |= NOTE_TRUNCATE;
	}
	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
		if (vp->v_mount->mnt_flag & MNT_RDONLY)
			return (EROFS);
		if (cred->cr_uid != DIP(ip, uid) &&
		    (error = suser_ucred(cred)) &&
		    ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || 
		    (error = VOP_ACCESS(vp, VWRITE, cred))))
			return (error);
		if (vap->va_mtime.tv_sec != VNOVAL)
			ip->i_flag |= IN_CHANGE | IN_UPDATE;
		if (vap->va_atime.tv_sec != VNOVAL) {
			if (!(vp->v_mount->mnt_flag & MNT_NOATIME) ||
			    (ip->i_flag & (IN_CHANGE | IN_UPDATE)))
				ip->i_flag |= IN_ACCESS;
		}
		error = UFS_UPDATE2(ip, &vap->va_atime, &vap->va_mtime, 0);
		if (error)
			return (error);
	}
	error = 0;
	if (vap->va_mode != (mode_t)VNOVAL) {
		if (vp->v_mount->mnt_flag & MNT_RDONLY)
			return (EROFS);
		error = ufs_chmod(vp, (int)vap->va_mode, cred, p);
	}
	VN_KNOTE(vp, hint);
	return (error);
}
Exemple #6
0
/*
 * Allocate a new inode.
 */
int
ufs_makeinode(int mode, struct vnode *dvp, struct vnode **vpp,
    struct componentname *cnp)
{
	struct inode *ip, *pdir;
	struct direct newdir;
	struct vnode *tvp;
	int error;

	pdir = VTOI(dvp);
#ifdef DIAGNOSTIC
	if ((cnp->cn_flags & HASBUF) == 0)
		panic("ufs_makeinode: no name");
#endif
	*vpp = NULL;
	if ((mode & IFMT) == 0)
		mode |= IFREG;

	if ((error = UFS_INODE_ALLOC(pdir, mode, cnp->cn_cred, &tvp)) != 0) {
		pool_put(&namei_pool, cnp->cn_pnbuf);
		vput(dvp);
		return (error);
	}

	ip = VTOI(tvp);

	DIP_ASSIGN(ip, gid, DIP(pdir, gid));
	DIP_ASSIGN(ip, uid, cnp->cn_cred->cr_uid);

	if ((error = getinoquota(ip)) ||
	    (error = ufs_quota_alloc_inode(ip, cnp->cn_cred))) {
		pool_put(&namei_pool, cnp->cn_pnbuf);
		UFS_INODE_FREE(ip, ip->i_number, mode);
		vput(tvp);
		vput(dvp);
		return (error);
	}

	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
	DIP_ASSIGN(ip, mode, mode);
	tvp->v_type = IFTOVT(mode);	/* Rest init'd in getnewvnode(). */
	ip->i_effnlink = 1;
	DIP_ASSIGN(ip, nlink, 1);
	if (DOINGSOFTDEP(tvp))
		softdep_change_linkcnt(ip, 0);
	if ((DIP(ip, mode) & ISGID) &&
		!groupmember(DIP(ip, gid), cnp->cn_cred) &&
	    suser_ucred(cnp->cn_cred))
		DIP_AND(ip, mode, ~ISGID);

	/*
	 * Make sure inode goes to disk before directory entry.
	 */
	if ((error = UFS_UPDATE(ip, !DOINGSOFTDEP(tvp))) != 0)
		goto bad;

	ufs_makedirentry(ip, cnp, &newdir);
	if ((error = ufs_direnter(dvp, tvp, &newdir, cnp, NULL)) != 0)
		goto bad;

	if ((cnp->cn_flags & SAVESTART) == 0)
		pool_put(&namei_pool, cnp->cn_pnbuf);
	vput(dvp);
	*vpp = tvp;
	return (0);

bad:
	/*
	 * Write error occurred trying to update the inode
	 * or the directory so must deallocate the inode.
	 */
	pool_put(&namei_pool, cnp->cn_pnbuf);
	vput(dvp);
	ip->i_effnlink = 0;
	DIP_ASSIGN(ip, nlink, 0);
	ip->i_flag |= IN_CHANGE;
	if (DOINGSOFTDEP(tvp))
		softdep_change_linkcnt(ip, 0);
	tvp->v_type = VNON;
	vput(tvp);

	return (error);
}
Exemple #7
0
/*
 * Extended attribute area writing.
 */
int
ffs_ea_write(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *ucred)
{
	struct inode *ip;
	struct ufs2_dinode *dp;
	struct fs *fs;
	struct buf *bp;
	off_t osize;
	size_t resid;
	int error, flags, xfersize;

	ip = VTOI(vp);
	fs = ip->i_fs;
	dp = ip->i_din2;
	error = 0;

	/* Don't cross the limit for the extended attribute area length. */
	if ((unsigned)uio->uio_offset + uio->uio_resid > NXADDR * fs->fs_bsize)
		return (EFBIG);

	/*
	 * Save the original amount of data to be written ('uio->uio_resid') as
	 * well as the extended attribute area size ('dp->di_extsize') in case
	 * we need to truncate in the end due to an unpredicted error.
	 */

	resid = uio->uio_resid;
	osize = dp->di_extsize;

	/* Prepare flags to be passed to UFS_BUF_ALLOC(). */
	flags = IO_EXT | B_CLRBUF;
	if (!DOINGASYNC(vp))
		flags |= B_SYNC;

	/*
	 * Loop over the amount of data requested by the caller, stopping only
	 * if an error occurs. By default, we always try to write a file system
	 * block worth of bytes per iteration ('xfersize'). Check this value
	 * against what is left to be copied ('uio->uio_resid'). If we are
	 * writing less than a full block, ask the buffer to be cleaned first.
	 */

	while (uio->uio_resid > 0) {

		xfersize = fs->fs_bsize;
		if (uio->uio_resid < xfersize) {
			xfersize = uio->uio_resid;
			flags |= B_CLRBUF;
		}

		/* Ask the ffs_balloc.c code for the block. */
		error = UFS_BUF_ALLOC(ip, uio->uio_offset, xfersize, ucred,
		    flags, &bp);
		if (error)
			break;

		/* Check if we are growing the extended attributes area. */
		if (uio->uio_offset + xfersize > dp->di_extsize)
			dp->di_extsize = uio->uio_offset + xfersize;

		error = uiomove(bp->b_data, xfersize, uio);
		if (error) {
			clrbuf(bp); /* Get rid of stray contents. */
			error = EIO;
			break;
		}

		/*
		 * We use the same criteria for calling bwrite(), bawrite(), or
		 * bdwrite() as the rest of the FFS code does.
		 */

		if (ioflag & IO_SYNC)
			bwrite(bp);
		else if (xfersize == fs->fs_bsize)
			bawrite(bp);
		else
			bdwrite(bp);

		if (error || xfersize == 0)
			break;

		ip->i_flag |= IN_CHANGE | IN_UPDATE;
	}

	/*
	 * If we successfully wrote any data, and we are not the superuser we
	 * clear the setuid and setgid bits as a precaution against tampering.
	 */

	if ((DIP(ip, mode) & (ISUID | ISGID)) && resid > uio->uio_resid)
		if (ucred && suser_ucred(ucred))
			DIP(ip, mode) &= ~(ISUID | ISGID);

	if (error) {
		/* Release blocks if we failed. */
		if (ioflag & IO_UNIT) {
			ffs_truncate(ip, osize, flags, ucred);
			uio->uio_offset -= resid - uio->uio_resid;
			uio->uio_resid = resid;
		}

		return (error);
	}

	/* If needed, sync the inode now. */
	if (resid > uio->uio_resid && (ioflag & IO_SYNC))
		return (ffs_update(ip, NULL, NULL, MNT_WAIT));

	return (0);
}
Exemple #8
0
/*
 * Set attribute vnode op. called from several syscalls
 */
int
ext2fs_setattr(void *v)
{
	struct vop_setattr_args *ap = v;
	struct vattr *vap = ap->a_vap;
	struct vnode *vp = ap->a_vp;
	struct inode *ip = VTOI(vp);
	struct ucred *cred = ap->a_cred;
	struct proc *p = curproc;
	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);
	}
	if (vap->va_flags != VNOVAL) {
		if (vp->v_mount->mnt_flag & MNT_RDONLY)
			return (EROFS);
		if (cred->cr_uid != ip->i_e2fs_uid &&
			(error = suser_ucred(cred)))
			return (error);
#ifdef EXT2FS_SYSTEM_FLAGS
		if (cred->cr_uid == 0) {
			if ((ip->i_e2fs_flags & 
			    (EXT2_APPEND | EXT2_IMMUTABLE)) && securelevel > 0)
				return (EPERM);
			ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
			ip->i_e2fs_flags |= 
			    (vap->va_flags & SF_APPEND) ? EXT2_APPEND : 0 |
			    (vap->va_flags & SF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
		} else {
			return (EPERM);
		}
#else
		ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
		ip->i_e2fs_flags |= 
		    (vap->va_flags & UF_APPEND) ? EXT2_APPEND : 0 |
		    (vap->va_flags & UF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
#endif
		ip->i_flag |= IN_CHANGE;
		if (vap->va_flags & (IMMUTABLE | APPEND))
			return (0);
	}
	if (ip->i_e2fs_flags & (EXT2_APPEND | EXT2_IMMUTABLE))
		return (EPERM);
	/*
	 * Go through the fields and update iff not VNOVAL.
	 */
	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
		if (vp->v_mount->mnt_flag & MNT_RDONLY)
			return (EROFS);
		error = ext2fs_chown(vp, vap->va_uid, vap->va_gid, cred, p);
		if (error)
			return (error);
	}
	if (vap->va_size != VNOVAL) {
		/*
		 * Disallow write attempts on read-only file systems;
		 * unless the file is a socket, fifo, or a block or
		 * character device resident on the file system.
		 */
		switch (vp->v_type) {
		case VDIR:
			return (EISDIR);
		case VLNK:
		case VREG:
			if (vp->v_mount->mnt_flag & MNT_RDONLY)
				return (EROFS);
		default:
			break;
		}
		error = ext2fs_truncate(ip, vap->va_size, 0, cred);
		if (error)
			return (error);
	}
	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
		if (vp->v_mount->mnt_flag & MNT_RDONLY)
			return (EROFS);
		if (cred->cr_uid != ip->i_e2fs_uid &&
			(error = suser_ucred(cred)) &&
			((vap->va_vaflags & VA_UTIMES_NULL) == 0 || 
			(error = VOP_ACCESS(vp, VWRITE, cred))))
			return (error);
		if (vap->va_mtime.tv_sec != VNOVAL)
			ip->i_flag |= IN_CHANGE | IN_UPDATE;
		if (vap->va_atime.tv_sec != VNOVAL) {
			if (!(vp->v_mount->mnt_flag & MNT_NOATIME) ||
			    (ip->i_flag & (IN_CHANGE | IN_UPDATE)))
				ip->i_flag |= IN_ACCESS;
		}
		error = ext2fs_update(ip, &vap->va_atime, &vap->va_mtime, 1);
		if (error)
			return (error);
	}
	error = 0;
	if (vap->va_mode != (mode_t)VNOVAL) {
		if (vp->v_mount->mnt_flag & MNT_RDONLY)
			return (EROFS);
		error = ext2fs_chmod(vp, (int)vap->va_mode, cred, p);
	}
	return (error);
}
Exemple #9
0
/*
 * Allocate a new inode.
 */
int
ext2fs_makeinode(int mode, struct vnode *dvp, struct vnode **vpp,
    struct componentname *cnp)
{
	struct inode *ip, *pdir;
	struct vnode *tvp;
	int error;

	pdir = VTOI(dvp);
#ifdef DIAGNOSTIC
	if ((cnp->cn_flags & HASBUF) == 0)
		panic("ext2fs_makeinode: no name");
#endif
	*vpp = NULL;
	if ((mode & IFMT) == 0)
		mode |= IFREG;

	if ((error = ext2fs_inode_alloc(pdir, mode, cnp->cn_cred, &tvp)) 
	    != 0) {
		pool_put(&namei_pool, cnp->cn_pnbuf);
		vput(dvp);
		return (error);
	}
	ip = VTOI(tvp);
	ip->i_e2fs_gid = pdir->i_e2fs_gid;
	ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
	ip->i_e2fs_mode = mode;
	tvp->v_type = IFTOVT(mode);	/* Rest init'd in getnewvnode(). */
	ip->i_e2fs_nlink = 1;
	if ((ip->i_e2fs_mode & ISGID) &&
		!groupmember(ip->i_e2fs_gid, cnp->cn_cred) &&
	    suser_ucred(cnp->cn_cred))
		ip->i_e2fs_mode &= ~ISGID;

	/*
	 * Make sure inode goes to disk before directory entry.
	 */
	if ((error = ext2fs_update(ip, NULL, NULL, 1)) != 0)
		goto bad;
	error = ext2fs_direnter(ip, dvp, cnp);
	if (error != 0)
		goto bad;
	if ((cnp->cn_flags & SAVESTART) == 0)
		pool_put(&namei_pool, cnp->cn_pnbuf);
	vput(dvp);
	*vpp = tvp;
	return (0);

bad:
	/*
	 * Write error occurred trying to update the inode
	 * or the directory so must deallocate the inode.
	 */
	pool_put(&namei_pool, cnp->cn_pnbuf);
	vput(dvp);
	ip->i_e2fs_nlink = 0;
	ip->i_flag |= IN_CHANGE;
	tvp->v_type = VNON;
	vput(tvp);
	return (error);
}
Exemple #10
0
int
msdosfs_setattr(void *v)
{
	struct vop_setattr_args *ap = v;
	int error = 0;
	struct denode *dep = VTODE(ap->a_vp);
	struct vattr *vap = ap->a_vap;
	struct ucred *cred = ap->a_cred;

#ifdef MSDOSFS_DEBUG
	printf("msdosfs_setattr(): vp %08x, vap %08x, cred %08x, p %08x\n",
	    ap->a_vp, vap, cred, ap->a_p);
#endif
	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) ||
	    (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL) ||
	    (vap->va_uid != VNOVAL) || (vap->va_gid != VNOVAL)) {
#ifdef MSDOSFS_DEBUG
		printf("msdosfs_setattr(): returning EINVAL\n");
		printf("    va_type %d, va_nlink %x, va_fsid %x, va_fileid %x\n",
		    vap->va_type, vap->va_nlink, vap->va_fsid, vap->va_fileid);
		printf("    va_blocksize %x, va_rdev %x, va_bytes %x, va_gen %x\n",
		    vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen);
		printf("    va_uid %x, va_gid %x\n",
		    vap->va_uid, vap->va_gid);
#endif
		return (EINVAL);
	}
	/*
	 * Directories must not ever get their attributes modified
	 */
	if (ap->a_vp->v_type == VDIR)
		return (0);

	if (vap->va_size != VNOVAL) {
		error = detrunc(dep, (uint32_t)vap->va_size, 0, cred, ap->a_p);
		if (error)
			return (error);
	}
	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
		if (cred->cr_uid != dep->de_pmp->pm_uid &&
		    (error = suser_ucred(cred)) &&
		    ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
		    (error = VOP_ACCESS(ap->a_vp, VWRITE, cred, ap->a_p))))
			return (error);
		if (!(dep->de_pmp->pm_flags & MSDOSFSMNT_NOWIN95)
		    && vap->va_atime.tv_sec != VNOVAL)
			unix2dostime(&vap->va_atime, &dep->de_ADate, NULL, NULL);
		if (vap->va_mtime.tv_sec != VNOVAL)
			unix2dostime(&vap->va_mtime, &dep->de_MDate, &dep->de_MTime, NULL);
		dep->de_Attributes |= ATTR_ARCHIVE;
		dep->de_flag |= DE_MODIFIED;
	}
	/*
	 * DOS files only have the ability to have their writability
	 * attribute set, so we use the owner write bit to set the readonly
	 * attribute.
	 */
	if (vap->va_mode != (mode_t)VNOVAL) {
		if (cred->cr_uid != dep->de_pmp->pm_uid &&
		    (error = suser_ucred(cred)))
			return (error);
		/* We ignore the read and execute bits. */
		if (vap->va_mode & VWRITE)
			dep->de_Attributes &= ~ATTR_READONLY;
		else
			dep->de_Attributes |= ATTR_READONLY;
		dep->de_flag |= DE_MODIFIED;
	}
	/*
	 * Allow the `archived' bit to be toggled.
	 */
	if (vap->va_flags != VNOVAL) {
		if (cred->cr_uid != dep->de_pmp->pm_uid &&
		    (error = suser_ucred(cred)))
			return (error);
		if (vap->va_flags & SF_ARCHIVED)
			dep->de_Attributes &= ~ATTR_ARCHIVE;
		else
			dep->de_Attributes |= ATTR_ARCHIVE;
		dep->de_flag |= DE_MODIFIED;
	}
	return (deupdat(dep, 1));
}