/* * Allocate a particular inode with a particular version number, freeing * any previous versions of this inode that may have gone before. * Used by the roll-forward code. * * XXX this function does not have appropriate locking to be used on a live fs; * XXX but something similar could probably be used for an "undelete" call. * * Called with the Ifile inode locked. */ int lfs_rf_valloc(struct lfs *fs, ino_t ino, int vers, struct lwp *l, struct vnode **vpp) { IFILE *ifp; struct buf *bp, *cbp; struct vnode *vp; struct inode *ip; ino_t tino, oldnext; int error; CLEANERINFO *cip; ASSERT_SEGLOCK(fs); /* XXX it doesn't, really */ /* * First, just try a vget. If the version number is the one we want, * we don't have to do anything else. If the version number is wrong, * take appropriate action. */ error = VFS_VGET(fs->lfs_ivnode->v_mount, ino, &vp); if (error == 0) { DLOG((DLOG_RF, "lfs_rf_valloc[1]: ino %d vp %p\n", ino, vp)); *vpp = vp; ip = VTOI(vp); if (ip->i_gen == vers) return 0; else if (ip->i_gen < vers) { lfs_truncate(vp, (off_t)0, 0, NOCRED); ip->i_gen = ip->i_ffs1_gen = vers; LFS_SET_UINO(ip, IN_CHANGE | IN_UPDATE); return 0; } else { DLOG((DLOG_RF, "ino %d: sought version %d, got %d\n", ino, vers, ip->i_ffs1_gen)); vput(vp); *vpp = NULLVP; return EEXIST; } } /* * The inode is not in use. Find it on the free list. */ /* If the Ifile is too short to contain this inum, extend it */ while (VTOI(fs->lfs_ivnode)->i_size <= (ino / fs->lfs_ifpb + fs->lfs_cleansz + fs->lfs_segtabsz) << fs->lfs_bshift) { lfs_extend_ifile(fs, NOCRED); } LFS_IENTRY(ifp, fs, ino, bp); oldnext = ifp->if_nextfree; ifp->if_version = vers; brelse(bp, 0); LFS_GET_HEADFREE(fs, cip, cbp, &ino); if (ino) { LFS_PUT_HEADFREE(fs, cip, cbp, oldnext); } else { tino = ino; while (1) { LFS_IENTRY(ifp, fs, tino, bp); if (ifp->if_nextfree == ino || ifp->if_nextfree == LFS_UNUSED_INUM) break; tino = ifp->if_nextfree; brelse(bp, 0); } if (ifp->if_nextfree == LFS_UNUSED_INUM) { brelse(bp, 0); return ENOENT; } ifp->if_nextfree = oldnext; LFS_BWRITE_LOG(bp); } error = lfs_ialloc(fs, fs->lfs_ivnode, ino, vers, &vp); if (error == 0) { /* * Make it VREG so we can put blocks on it. We will change * this later if it turns out to be some other kind of file. */ ip = VTOI(vp); ip->i_mode = ip->i_ffs1_mode = IFREG; ip->i_nlink = ip->i_ffs1_nlink = 1; ufs_vinit(vp->v_mount, lfs_specop_p, lfs_fifoop_p, &vp); ip = VTOI(vp); DLOG((DLOG_RF, "lfs_rf_valloc: ino %d vp %p\n", ino, vp)); /* The dirop-nature of this vnode is past */ lfs_unmark_vnode(vp); (void)lfs_vunref(vp); vp->v_uflag &= ~VU_DIROP; mutex_enter(&lfs_lock); --lfs_dirvcount; --fs->lfs_dirvcount; TAILQ_REMOVE(&fs->lfs_dchainhd, ip, i_lfs_dchain); wakeup(&lfs_dirvcount); wakeup(&fs->lfs_dirvcount); mutex_exit(&lfs_lock); } *vpp = vp; 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); }
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(struct vattr *vap, struct vnode *dvp, const struct ulfs_lookup_results *ulr, struct vnode **vpp, struct componentname *cnp) { struct inode *ip; struct vnode *tvp; int error; error = vcache_new(dvp->v_mount, dvp, vap, cnp->cn_cred, &tvp); if (error) return error; error = vn_lock(tvp, LK_EXCLUSIVE); if (error) { vrele(tvp); return error; } lfs_mark_vnode(tvp); *vpp = tvp; ip = VTOI(tvp); ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; 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, MAKEIMODE(vap->va_type, vap->va_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; error = ulfs_direnter(dvp, ulr, tvp, cnp, ip->i_number, LFS_IFTODT(ip->i_mode), NULL); 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); vput(tvp); return (error); }