/* * Perform chown operation on inode ip; * inode must be locked prior to call. */ static int ulfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred, struct lwp *l) { struct inode *ip; int error = 0; #if defined(LFS_QUOTA) || defined(LFS_QUOTA2) uid_t ouid; gid_t ogid; int64_t change; #endif ip = VTOI(vp); error = 0; if (uid == (uid_t)VNOVAL) uid = ip->i_uid; if (gid == (gid_t)VNOVAL) gid = ip->i_gid; error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP, vp, NULL, genfs_can_chown(cred, ip->i_uid, ip->i_gid, uid, gid)); if (error) return (error); fstrans_start(vp->v_mount, FSTRANS_SHARED); #if defined(LFS_QUOTA) || defined(LFS_QUOTA2) ogid = ip->i_gid; ouid = ip->i_uid; change = DIP(ip, blocks); (void) lfs_chkdq(ip, -change, cred, 0); (void) lfs_chkiq(ip, -1, cred, 0); #endif ip->i_gid = gid; DIP_ASSIGN(ip, gid, gid); ip->i_uid = uid; DIP_ASSIGN(ip, uid, uid); #if defined(LFS_QUOTA) || defined(LFS_QUOTA2) if ((error = lfs_chkdq(ip, change, cred, 0)) == 0) { if ((error = lfs_chkiq(ip, 1, cred, 0)) == 0) goto good; else (void) lfs_chkdq(ip, -change, cred, FORCE); } ip->i_gid = ogid; DIP_ASSIGN(ip, gid, ogid); ip->i_uid = ouid; DIP_ASSIGN(ip, uid, ouid); (void) lfs_chkdq(ip, change, cred, FORCE); (void) lfs_chkiq(ip, 1, cred, FORCE); fstrans_done(vp->v_mount); return (error); good: #endif /* LFS_QUOTA || LFS_QUOTA2 */ ip->i_flag |= IN_CHANGE; fstrans_done(vp->v_mount); return (0); }
/* * Last reference to an inode. If necessary, write or delete it. */ int ulfs_inactive(void *v) { struct vop_inactive_args /* { struct vnode *a_vp; struct bool *a_recycle; } */ *ap = v; struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); struct mount *transmp; mode_t mode; int error = 0; transmp = vp->v_mount; fstrans_start(transmp, FSTRANS_LAZY); /* * Ignore inodes related to stale file handles. */ if (ip->i_mode == 0) goto out; if (ip->i_nlink <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { #ifdef LFS_EXTATTR ulfs_extattr_vnode_inactive(vp, curlwp); #endif if (ip->i_size != 0) { error = lfs_truncate(vp, (off_t)0, 0, NOCRED); } #if defined(LFS_QUOTA) || defined(LFS_QUOTA2) (void)lfs_chkiq(ip, -1, NOCRED, 0); #endif DIP_ASSIGN(ip, rdev, 0); mode = ip->i_mode; ip->i_mode = 0; ip->i_omode = mode; DIP_ASSIGN(ip, mode, 0); ip->i_flag |= IN_CHANGE | IN_UPDATE; /* * Defer final inode free and update to ulfs_reclaim(). */ } if (ip->i_flag & (IN_CHANGE | IN_UPDATE | IN_MODIFIED)) { lfs_update(vp, NULL, NULL, 0); } out: /* * If we are done with the inode, reclaim it * so that it can be reused immediately. */ *ap->a_recycle = (ip->i_mode == 0); VOP_UNLOCK(vp); fstrans_done(transmp); return (error); }
int ulfs_mkdir(void *v) { struct vop_mkdir_v3_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap = v; struct vnode *dvp = ap->a_dvp, *tvp; struct vattr *vap = ap->a_vap; struct componentname *cnp = ap->a_cnp; struct inode *ip, *dp = VTOI(dvp); struct buf *bp; struct lfs_dirtemplate dirtemplate; struct lfs_direct *newdir; int error, dmode; struct ulfsmount *ump = dp->i_ump; struct lfs *fs = ump->um_lfs; int dirblksiz = fs->um_dirblksiz; struct ulfs_lookup_results *ulr; fstrans_start(dvp->v_mount, FSTRANS_SHARED); /* XXX should handle this material another way */ ulr = &dp->i_crap; ULFS_CHECK_CRAPCOUNTER(dp); if ((nlink_t)dp->i_nlink >= LINK_MAX) { error = EMLINK; goto out; } dmode = vap->va_mode & ACCESSPERMS; dmode |= LFS_IFDIR; /* * Must simulate part of ulfs_makeinode here to acquire the inode, * but not have it entered in the parent directory. The entry is * made later after writing "." and ".." entries. */ if ((error = lfs_valloc(dvp, dmode, cnp->cn_cred, ap->a_vpp)) != 0) goto out; tvp = *ap->a_vpp; ip = VTOI(tvp); ip->i_uid = kauth_cred_geteuid(cnp->cn_cred); DIP_ASSIGN(ip, uid, ip->i_uid); ip->i_gid = dp->i_gid; DIP_ASSIGN(ip, gid, ip->i_gid); #if defined(LFS_QUOTA) || defined(LFS_QUOTA2) if ((error = lfs_chkiq(ip, 1, cnp->cn_cred, 0))) { lfs_vfree(tvp, ip->i_number, dmode); fstrans_done(dvp->v_mount); vput(tvp); return (error); } #endif ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; ip->i_mode = dmode; DIP_ASSIGN(ip, mode, dmode); tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */ ip->i_nlink = 2; DIP_ASSIGN(ip, nlink, 2); if (cnp->cn_flags & ISWHITEOUT) { ip->i_flags |= UF_OPAQUE; DIP_ASSIGN(ip, flags, ip->i_flags); } /* * Bump link count in parent directory to reflect work done below. * Should be done before reference is created so cleanup is * possible if we crash. */ dp->i_nlink++; DIP_ASSIGN(dp, nlink, dp->i_nlink); dp->i_flag |= IN_CHANGE; if ((error = lfs_update(dvp, NULL, NULL, UPDATE_DIROP)) != 0) goto bad; /* * Initialize directory with "." and ".." from static template. */ dirtemplate = mastertemplate; dirtemplate.dotdot_reclen = dirblksiz - dirtemplate.dot_reclen; dirtemplate.dot_ino = ulfs_rw32(ip->i_number, ULFS_MPNEEDSWAP(fs)); dirtemplate.dotdot_ino = ulfs_rw32(dp->i_number, ULFS_MPNEEDSWAP(fs)); dirtemplate.dot_reclen = ulfs_rw16(dirtemplate.dot_reclen, ULFS_MPNEEDSWAP(fs)); dirtemplate.dotdot_reclen = ulfs_rw16(dirtemplate.dotdot_reclen, ULFS_MPNEEDSWAP(fs)); if (fs->um_maxsymlinklen <= 0) { #if BYTE_ORDER == LITTLE_ENDIAN if (ULFS_MPNEEDSWAP(fs) == 0) #else if (ULFS_MPNEEDSWAP(fs) != 0) #endif { dirtemplate.dot_type = dirtemplate.dot_namlen; dirtemplate.dotdot_type = dirtemplate.dotdot_namlen; dirtemplate.dot_namlen = dirtemplate.dotdot_namlen = 0; } else dirtemplate.dot_type = dirtemplate.dotdot_type = 0; } if ((error = lfs_balloc(tvp, (off_t)0, dirblksiz, cnp->cn_cred, B_CLRBUF, &bp)) != 0) goto bad; ip->i_size = dirblksiz; DIP_ASSIGN(ip, size, dirblksiz); ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; uvm_vnp_setsize(tvp, ip->i_size); memcpy((void *)bp->b_data, (void *)&dirtemplate, sizeof dirtemplate); /* * Directory set up, now install it's entry in the parent directory. * We must write out the buffer containing the new directory body * before entering the new name in the parent. */ if ((error = VOP_BWRITE(bp->b_vp, bp)) != 0) goto bad; if ((error = lfs_update(tvp, NULL, NULL, UPDATE_DIROP)) != 0) { goto bad; } newdir = pool_cache_get(ulfs_direct_cache, PR_WAITOK); ulfs_makedirentry(ip, cnp, newdir); error = ulfs_direnter(dvp, ulr, tvp, newdir, cnp, bp); pool_cache_put(ulfs_direct_cache, newdir); bad: if (error == 0) { VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); VOP_UNLOCK(tvp); } else { dp->i_nlink--; DIP_ASSIGN(dp, nlink, dp->i_nlink); dp->i_flag |= IN_CHANGE; /* * No need to do an explicit lfs_truncate here, vrele will * do this for us because we set the link count to 0. */ ip->i_nlink = 0; DIP_ASSIGN(ip, nlink, 0); ip->i_flag |= IN_CHANGE; /* If IN_ADIROP, account for it */ lfs_unmark_vnode(tvp); vput(tvp); } out: fstrans_done(dvp->v_mount); return (error); }
/* * Allocate a new inode. */ int ulfs_makeinode(int mode, struct vnode *dvp, const struct ulfs_lookup_results *ulr, struct vnode **vpp, struct componentname *cnp) { struct inode *ip, *pdir; struct lfs_direct *newdir; struct vnode *tvp; int error; pdir = VTOI(dvp); if ((mode & LFS_IFMT) == 0) mode |= LFS_IFREG; if ((error = lfs_valloc(dvp, mode, cnp->cn_cred, vpp)) != 0) { return (error); } tvp = *vpp; ip = VTOI(tvp); ip->i_gid = pdir->i_gid; DIP_ASSIGN(ip, gid, ip->i_gid); ip->i_uid = kauth_cred_geteuid(cnp->cn_cred); DIP_ASSIGN(ip, uid, ip->i_uid); #if defined(LFS_QUOTA) || defined(LFS_QUOTA2) if ((error = lfs_chkiq(ip, 1, cnp->cn_cred, 0))) { lfs_vfree(tvp, ip->i_number, mode); vput(tvp); return (error); } #endif ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; ip->i_mode = mode; DIP_ASSIGN(ip, mode, mode); tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */ ip->i_nlink = 1; DIP_ASSIGN(ip, nlink, 1); /* Authorize setting SGID if needed. */ if (ip->i_mode & ISGID) { error = kauth_authorize_vnode(cnp->cn_cred, KAUTH_VNODE_WRITE_SECURITY, tvp, NULL, genfs_can_chmod(tvp->v_type, cnp->cn_cred, ip->i_uid, ip->i_gid, mode)); if (error) { ip->i_mode &= ~ISGID; DIP_ASSIGN(ip, mode, ip->i_mode); } } if (cnp->cn_flags & ISWHITEOUT) { ip->i_flags |= UF_OPAQUE; DIP_ASSIGN(ip, flags, ip->i_flags); } /* * Make sure inode goes to disk before directory entry. */ if ((error = lfs_update(tvp, NULL, NULL, UPDATE_DIROP)) != 0) goto bad; newdir = pool_cache_get(ulfs_direct_cache, PR_WAITOK); ulfs_makedirentry(ip, cnp, newdir); error = ulfs_direnter(dvp, ulr, tvp, newdir, cnp, NULL); pool_cache_put(ulfs_direct_cache, newdir); if (error) goto bad; *vpp = tvp; return (0); bad: /* * Write error occurred trying to update the inode * or the directory so must deallocate the inode. */ ip->i_nlink = 0; DIP_ASSIGN(ip, nlink, 0); ip->i_flag |= IN_CHANGE; /* If IN_ADIROP, account for it */ lfs_unmark_vnode(tvp); tvp->v_type = VNON; /* explodes later if VBLK */ vput(tvp); return (error); }