/* * Draft of a genfs-based ext2fs_rename. Not tested, not even compiled. * * -- Riastradh, 2011-12-13 */ static const struct genfs_rename_ops ext2fs_genfs_rename_ops; /* * ext2fs_rename: The hairiest vop, with the insanest API. * * Arguments: * * . fdvp (from directory vnode), * . fvp (from vnode), * . fcnp (from component name), * . tdvp (to directory vnode), * . tvp (to vnode, or NULL), and * . tcnp (to component name). * * Any pair of vnode parameters may have the same vnode. * * On entry, * * . fdvp, fvp, tdvp, and tvp are referenced, * . fdvp and fvp are unlocked, and * . tdvp and tvp (if nonnull) are locked. * * On exit, * * . fdvp, fvp, tdvp, and tvp (if nonnull) are unreferenced, and * . tdvp and tvp are unlocked. */ int ext2fs_rename(void *v) { struct vop_rename_args /* { struct vnode *a_fdvp; struct vnode *a_fvp; struct componentname *a_fcnp; struct vnode *a_tdvp; struct vnode *a_tvp; struct componentname *a_tcnp; } */ *ap = v; struct vnode *fdvp = ap->a_fdvp; struct vnode *fvp = ap->a_fvp; struct componentname *fcnp = ap->a_fcnp; struct vnode *tdvp = ap->a_tdvp; struct vnode *tvp = ap->a_tvp; struct componentname *tcnp = ap->a_tcnp; kauth_cred_t cred; int error; KASSERT(fdvp != NULL); KASSERT(fvp != NULL); KASSERT(fcnp != NULL); KASSERT(fcnp->cn_nameptr != NULL); KASSERT(tdvp != NULL); KASSERT(tcnp != NULL); KASSERT(fcnp->cn_nameptr != NULL); /* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */ /* KASSERT(VOP_ISLOCKED(fvp) != LK_EXCLUSIVE); */ KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); KASSERT(fdvp->v_type == VDIR); KASSERT(tdvp->v_type == VDIR); cred = fcnp->cn_cred; KASSERT(tcnp->cn_cred == cred); /* * Sanitize our world from the VFS insanity. Unlock the target * directory and node, which are locked. Release the children, * which are referenced. Check for rename("x", "y/."), which * it is our responsibility to reject, not the caller's. (But * the caller does reject rename("x/.", "y"). Go figure.) */ VOP_UNLOCK(tdvp); if ((tvp != NULL) && (tvp != tdvp)) VOP_UNLOCK(tvp); vrele(fvp); if (tvp != NULL) vrele(tvp); if (tvp == tdvp) { error = EINVAL; goto out; } error = ext2fs_sane_rename(fdvp, fcnp, tdvp, tcnp, cred, false); out: /* * All done, whether with success or failure. Release the * directory nodes now, as the caller expects from the VFS * protocol. */ vrele(fdvp); vrele(tdvp); return error; } /* * ext2fs_sane_rename: The hairiest vop, with the saner API. * * Arguments: * * . fdvp (from directory vnode), * . fcnp (from component name), * . tdvp (to directory vnode), and * . tcnp (to component name). * * fdvp and tdvp must be referenced and unlocked. */ static int ext2fs_sane_rename( struct vnode *fdvp, struct componentname *fcnp, struct vnode *tdvp, struct componentname *tcnp, kauth_cred_t cred, bool posixly_correct) { struct ufs_lookup_results fulr, tulr; return genfs_sane_rename(&ext2fs_genfs_rename_ops, fdvp, fcnp, &fulr, tdvp, tcnp, &tulr, cred, posixly_correct); } /* * ext2fs_gro_de_equal_p: Return true if a_de and b_de represent the * same directory entries in a common directory. */ static int ext2fs_gro_de_equal_p(struct mount *mp, void *a_de, void *b_de) { struct ufs_lookup_results *a_ulr = a_de; struct ufs_lookup_results *b_ulr = b_de; (void)mp; KASSERT(mp != NULL); KASSERT(a_ulr != NULL); KASSERT(b_ulr != NULL); return (a_ulr->ulr_offset == b_ulr->ulr_offset); } /* * ext2fs_gro_directory_empty_p: Return true if the directory vp is * empty. dvp is its parent. * * vp and dvp must be locked and referenced. */ static int ext2fs_gro_directory_empty_p(struct mount *mp, kauth_cred_t cred, struct vnode *vp, struct vnode *dvp) { (void)mp; KASSERT(mp != NULL); KASSERT(vp != NULL); KASSERT(dvp != NULL); KASSERT(vp != dvp); KASSERT(vp->v_mount == mp); KASSERT(dvp->v_mount == mp); KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); return ext2fs_dirempty(VTOI(vp), VTOI(dvp)->i_number, cred); } /* * ext2fs_gro_rename_check_possible: Check whether renaming fvp in fdvp * to tvp in tdvp is possible independent of credentials. */ static int ext2fs_gro_rename_check_possible(struct mount *mp, struct vnode *fdvp, struct vnode *fvp, struct vnode *tdvp, struct vnode *tvp) { KASSERT(mp != NULL); KASSERT(fdvp != NULL); KASSERT(fvp != NULL); KASSERT(tdvp != NULL); KASSERT(fdvp != fvp); KASSERT(fdvp != tvp); KASSERT(tdvp != fvp); KASSERT(tdvp != tvp); KASSERT(fvp != tvp); KASSERT(fdvp->v_mount == mp); KASSERT(fvp->v_mount == mp); KASSERT(tdvp->v_mount == mp); KASSERT((tvp == NULL) || (tvp->v_mount == mp)); KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); return genfs_ufslike_rename_check_possible(mp, VTOI(fdvp)->i_e2fs_flags, VTOI(fvp)->i_e2fs_flags, VTOI(tdvp)->i_e2fs_flags, VTOI(tvp)->i_e2fs_flags, EXT2_IMMUTABLE, EXT2_APPEND); } /* * ext2fs_gro_rename_check_permitted: ... */ static int ext2fs_gro_rename_check_permitted(struct mount *mp, kauth_cred_t cred, struct vnode *fdvp, struct vnode *fvp, struct vnode *tdvp, struct vnode *tvp) { KASSERT(mp != NULL); KASSERT(fdvp != NULL); KASSERT(fvp != NULL); KASSERT(tdvp != NULL); KASSERT(fdvp != fvp); KASSERT(fdvp != tvp); KASSERT(tdvp != fvp); KASSERT(tdvp != tvp); KASSERT(fvp != tvp); KASSERT(fdvp->v_mount == mp); KASSERT(fvp->v_mount == mp); KASSERT(tdvp->v_mount == mp); KASSERT((tvp == NULL) || (tvp->v_mount == mp)); KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); return genfs_ufslike_rename_check_permitted(mp, cred, fdvp, VTOI(fdvp)->i_e2fs_mode, VTOI(fdvp)->i_uid, fvp, VTOI(fvp)->i_uid, tdvp, VTOI(tdvp)->i_e2fs_mode, VTOI(tdvp)->i_uid, tvp, (tvp? VTOI(tvp)->i_uid : 0)); } /* * ext2fs_gro_remove_check_possible: ... */ static int ext2fs_gro_remove_check_possible(struct mount *mp, struct vnode *dvp, struct vnode *vp) { KASSERT(mp != NULL); KASSERT(dvp != NULL); KASSERT(vp != NULL); KASSERT(dvp != vp); KASSERT(dvp->v_mount == mp); KASSERT(vp->v_mount == mp); KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); return genfs_ufslike_remove_check_possible(mp, VTOI(fdvp)->i_e2fs_flags, VTOI(fvp)->i_e2fs_flags, EXT2_IMMUTABLE, EXT2_APPEND); } /* * ext2fs_gro_remove_check_permitted: ... */ static int ext2fs_gro_remove_check_permitted(struct mount *mp, kauth_cred_t cred, struct vnode *dvp, struct vnode *vp) { KASSERT(mp != NULL); KASSERT(dvp != NULL); KASSERT(vp != NULL); KASSERT(dvp != vp); KASSERT(dvp->v_mount == mp); KASSERT(vp->v_mount == mp); KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); return genfs_ufslike_remove_check_permitted(mp, cred, dvp, VTOI(dvp)->i_e2fs_mode, VTOI(dvp)->i_uid, vp, VTOI(vp)->i_uid); } /* * ext2fs_gro_rename: Actually perform the rename operation. */ static int ext2fs_gro_rename(struct mount *mp, kauth_cred_t cred, struct vnode *fdvp, struct componentname *fcnp, void *fde, struct vnode *fvp, struct vnode *tdvp, struct componentname *tcnp, void *tde, struct vnode *tvp) { struct ufs_lookup_results *fulr = fde; struct ufs_lookup_results *tulr = tde; bool directory_p, reparent_p; KASSERT(mp != NULL); KASSERT(fdvp != NULL); KASSERT(fcnp != NULL); KASSERT(fulr != NULL); KASSERT(fvp != NULL); KASSERT(tdvp != NULL); KASSERT(tcnp != NULL); KASSERT(tulr != NULL); KASSERT(fdvp != fvp); KASSERT(fdvp != tvp); KASSERT(tdvp != fvp); KASSERT(tdvp != tvp); KASSERT(fvp != tvp); KASSERT(fdvp->v_mount == mp); KASSERT(fvp->v_mount == mp); KASSERT(tdvp->v_mount == mp); KASSERT((tvp == NULL) || (tvp->v_mount == mp)); KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); /* * We will need to temporarily bump the link count, so make * sure there is room to do so. */ if ((nlink_t)VTOI(fvp)->i_e2fs_nlink >= LINK_MAX) return EMLINK; /* * XXX There is a pile of logic here to handle a voodoo flag * IN_RENAME. I think this is a vestige of days when the file * system hackers didn't understand concurrency or race * conditions; I believe it serves no useful function * whatsoever. */ directory_p = (fvp->v_type == VDIR); KASSERT(directory_p == ((VTOI(fvp)->i_e2fs_mode & IFMT) == IFDIR)); KASSERT((tvp == NULL) || (directory_p == (tvp->v_type == VDIR))); KASSERT((tvp == NULL) || (directory_p == ((VTOI(fvp)->i_e2fs_mode & IFMT) == IFDIR))); if (directory_p) { if (VTOI(fvp)->i_flag & IN_RENAME) return EINVAL; VTOI(fvp)->i_flag |= IN_RENAME; } reparent_p = (fdvp != tdvp); KASSERT(reparent_p == (VTOI(fdvp)->i_number != VTOI(tdvp)->i_number)); /* * 1) Bump link count while we're moving stuff * around. If we crash somewhere before * completing our work, the link count * may be wrong, but correctable. */ KASSERT((nlink_t)VTOI(fvp)->i_e2fs_nlink < LINK_MAX); VTOI(fvp)->i_e2fs_nlink++; VTOI(fvp)->i_flag |= IN_CHANGE; error = ext2fs_update(fvp, NULL, NULL, UPDATE_WAIT); if (error) goto whymustithurtsomuch; /* * 2) If target doesn't exist, link the target * to the source and unlink the source. * Otherwise, rewrite the target directory * entry to reference the source inode and * expunge the original entry's existence. */ if (tvp == NULL) { /* * XXX WTF? Why check here and not earlier? Is this * covered by checking v_mount earlier? */ KASSERT(VTOI(tdvp)->i_dev == VTOI(fvp)->i_dev); /* * Account for ".." in new directory. * When source and destination have the same * parent we don't fool with the link count. */ if (directory_p && reparent_p) { if ((nlink_t)VTOI(tdvp)->i_e2fs_nlink >= LINK_MAX) { error = EMLINK; goto whymustithurtsomuch; } KASSERT((nlink_t)VTOI(tdvp)->i_e2fs_nlink < LINK_MAX); VTOI(tdvp)->i_e2fs_nlink++; VTOI(tdvp)->i_flag |= IN_CHANGE; error = ext2fs_update(tdvp, NULL, NULL, UPDATE_WAIT); if (error) { KASSERT(0 < VTOI(tdvp)->i_e2fs_nlink); VTOI(tdvp)->i_e2fs_nlink--; VTOI(tdvp)->i_flag |= IN_CHANGE; goto whymustithurtsomuch; } } error = ext2fs_direnter(VTOI(fvp), tdvp, tulr, tcnp); if (error) { if (directory_p && reparent_p) { KASSERT(0 < VTOI(tdvp)->i_e2fs_nlink); VTOI(tdvp)->i_e2fs_nlink--; VTOI(tdvp)->i_flag |= IN_CHANGE; (void)ext2fs_update(tdvp, NULL, NULL, UPDATE_WAIT); } goto whymustithurtsomuch; } } else { /* XXX WTF? Why check here and not earlier? Is this * covered by checking v_mount earlier? */ KASSERT(VTOI(tvp)->i_dev == VTOI(tdvp)->i_dev); KASSERT(VTOI(fvp)->i_dev == VTOI(fdvp)->i_dev); KASSERT(VTOI(tvp)->i_dev == VTOI(fvp)->i_dev); if (directory_p) /* XXX WTF? Why purge here? Why not purge others? */ cache_purge(tdvp); /* * Make the target directory's entry for tcnp point at * the source node. */ error = ext2fs_dirrewrite(VTOI(tdvp), tulr, VTOI(fvp), tcnp); if (error) goto whymustithurtsomuch; /* * If the source and target are directories, and the * target is in the same directory as the source, * decrement the link count of the common parent * directory, since we are removing the target from * that directory. */ if (directory_p && !reparent_p) { KASSERT(fdvp == tdvp); /* XXX check, don't kassert */ KASSERT(0 < VTOI(tdvp)->i_e2fs_nlink); VTOI(tdvp)->i_e2fs_nlink--; VTOI(tdvp)->i_flag |= IN_CHANGE; } /* * Adjust the link count of the target to * reflect the dirrewrite above. If this is * a directory it is empty and there are * no links to it, so we can squash the inode and * any space associated with it. We disallowed * renaming over top of a directory with links to * it above, as the remaining link would point to * a directory without "." or ".." entries. */ /* XXX check, don't kassert */ KASSERT(0 < VTOI(tvp)->i_e2fs_nlink); VTOI(tvp)->i_e2fs_nlink--; if (directory_p) { /* * XXX The ext2fs_dirempty call earlier does * not guarantee anything about nlink. */ if (VTOI(tvp)->i_e2fs_nlink != 1) ufs_dirbad(VTOI(tvp), (doff_t)0, "hard-linked directory"); VTOI(tvp)->i_e2fs_nlink = 0; error = ext2fs_truncate(tvp, (off_t)0, IO_SYNC, cred); } VTOI(tvp)->i_flag |= IN_CHANGE; } /* * ext2fs_direnter may compact the directory in the process of * inserting a new entry. That may invalidate fulr, which we * need in order to remove the old entry. In that case, we * need to recalculate what fulr should be. */ if (!reparent_p && ulr_overlap(fulr, tulr)) ext2fs_rename_unbotch_ulr_overlap(fdvp, fulr, tulr); /* * 3) Unlink the source. */ /* * If the source is a directory with a new parent, the link * count of the old parent directory must be decremented and * ".." set to point to the new parent. */ if (directory_p && reparent_p) { error = ext2fs_rename_replace_dotdot(fvp, fdvp, tdvp, cred); if (error) goto whymustithurtsomuch; /* XXX WTF? Why purge here? Why not purge others? */ cache_purge(fdvp); } error = ext2fs_dirremove(fdvp, fulr, fcnp); if (error) goto whymustithurtsomuch; genfs_rename_knote(fdvp, fvp, tdvp, tvp); whymustithurtsomuch: KASSERT(0 < VTOI(fvp)->i_e2fs_nlink); VTOI(fvp)->i_e2fs_nlink--; VTOI(fvp)->i_flag |= IN_CHANGE; if (directory_p) VTOI(fvp)->i_flag &=~ IN_RENAME; return error; } static int ext2fs_rename_unbotch_ulr_overlap(struct vnode *dvp, struct ufs_lookup_results *fulr, struct ufs_lookup_results *tulr) { XXX /* XXX */ } /* * ext2fs_gro_remove: Rename an object over another link to itself, * effectively removing just the original link. */ static int ext2fs_gro_remove(struct mount *mp, kauth_cred_t cred, struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp) { struct ufs_lookup_results *ulr = de; int error; KASSERT(mp != NULL); KASSERT(dvp != NULL); KASSERT(cnp != NULL); KASSERT(ulr != NULL); KASSERT(vp != NULL); KASSERT(dvp != vp); KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); error = ext2fs_dirremove(dvp, ulr, cnp); if (error) return error; KASSERT(0 < VTOI(vp)->i_e2fs_nlink); VTOI(vp)->i_e2fs_nlink--; VTOI(vp)->i_flag |= IN_CHANGE; return 0; } /* * ext2fs_gro_lookup: Look up and save the lookup results. */ static int ext2fs_gro_lookup(struct mount *mp, struct vnode *dvp, struct componentname *cnp, void *de_ret, struct vnode **vp_ret) { struct ufs_lookup_results *ulr_ret = de_ret; struct vnode *vp; int error; (void)mp; KASSERT(mp != NULL); KASSERT(dvp != NULL); KASSERT(cnp != NULL); KASSERT(ulr_ret != NULL); KASSERT(vp_ret != NULL); KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); /* Kludge cargo-culted from dholland's ufs_rename. */ cnp->cn_flags |= (LOCKPARENT | LOCKLEAF); error = relookup(dvp, &vp, cnp); if ((error == 0) && (vp == NULL)) error = ENOENT; if (error) return error; /* * Thanks to VFS insanity, relookup locks vp, which screws us * in various ways. */ VOP_UNLOCK(vp); *ulr_ret = VTOI(dvp)->i_crap; *vp_ret = vp; return 0; } /* * ext2fs_rmdired_p: Check whether the directory vp has been rmdired. * * vp must be locked and referenced. */ static bool ext2fs_rmdired_p(struct vnode *vp) { KASSERT(vp != NULL); KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); KASSERT(vp->v_type == VDIR); /* XXX Is this correct? */ return (VTOI(vp)->i_size == 0); } /* * ext2fs_gro_genealogy: Analyze the genealogy of the source and target * directories. */ static int ext2fs_gro_genealogy(struct mount *mp, struct vnode *fdvp, struct vnode *tdvp, struct vnode **intermediate_node_ret) { struct vnode *vp, *dvp; ino_t dotdot_ino; int error; KASSERT(mp != NULL); KASSERT(fdvp != NULL); KASSERT(tdvp != NULL); KASSERT(fdvp != tdvp); KASSERT(intermediate_node_ret != NULL); KASSERT(fdvp->v_mount == mp); KASSERT(tdvp->v_mount == mp); KASSERT(fdvp->v_type == VDIR); KASSERT(tdvp->v_type == VDIR); /* * We need to provisionally lock tdvp to keep rmdir from * deleting it -- or any ancestor -- at an inopportune moment. * XXX This doesn't do the same thing as in tmpfs. What to do? */ error = ext2fs_gro_lock_directory(mp, tdvp); if (error) return error; vp = tdvp; vref(vp); for (;;) { KASSERT(vp->v_type == VDIR); /* Did we hit the root without finding fdvp? */ if (VTOI(vp)->i_number == ROOTINO) { *intermediate_node_ret = NULL; break; } error = ext2fs_read_dotdot(vp, cred, &dotdot_ino); if (error) goto out; /* Did we find that fdvp is an ancestor? */ if (VTOI(fdvp)->i_number == dotdot_ino) { *intermediate_node_ret = vp; break; } /* Neither -- keep ascending. */ /* * vput(vp); * error = VFS_VGET(mp, dotdot_ino, &vp); */ /* * XXX Cargo-culted from ufs_parentcheck... Isn't * there a race condition here? */ VOP_UNLOCK(vp); error = VFS_VGET(mp, dotdot_ino, &dvp); vrele(vp); if (error) return error; KASSERT(dvp != NULL); KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); vp = dvp; if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } /* XXX Is this the right way to test for rmdir? */ if (ext2fs_rmdired_p(vp)) { error = ENOENT; goto out; } } error = 0; out: vput(vp); return error; } /* * ext2fs_read_dotdot: Store in *ino_ret the inode number of the parent * of the directory vp. */ static int ext2fs_read_dotdot(struct vnode *vp, kauth_cred_t cred, ino_t *ino_ret) { struct ext2fs_dirtemplate dirbuf; int error; KASSERT(vp != NULL); KASSERT(ino_ret != NULL); KASSERT(vp->v_type == VDIR); error = vn_rdwr(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, NULL); if (error) return error; if (dirbuf.dotdot_namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') return ENOTDIR; *ino_ret = fs2h32(dirbuf.dotdot_ino); return 0; } /* * ext2fs_rename_replace_dotdot: Change the target of the `..' entry of * the directory vp from fdvp to tdvp. */ static int ext2fs_rename_replace_dotdot(struct vnode *vp, struct vnode *fdvp, struct vnode *tdvp, kauth_cred_t cred) { struct ext2fs_dirtemplate dirbuf; int error; /* XXX Does it make sense to do this before the sanity checks below? */ KASSERT(0 < VTOI(fdvp)->i_e2fs_nlink); VTOI(fdvp)->i_e2fs_nlink--; VTOI(fdvp)->i_flag |= IN_CHANGE; error = vn_rdwr(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, NULL); if (error) return error; if (dirbuf.dotdot_namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') { ufs_dirbad(VTOI(vp), (doff_t)12, "bad `..' entry"); return 0; } if (fs2h32(dirbuf.dotdot_ino) != VTOI(fdvp)->i_number) { ufs_dirbad(VTOI(vp), (doff_t)12, "`..' does not point at parent"); return 0; } dirbuf.dotdot_ino = h2fs32(VTOI(tdvp)->i_number); /* XXX WTF? Why not check error? */ (void)vn_rdwr(UIO_WRITE, vp, &dirbuf, sizeof dirbuf, (off_t)0, UIO_SYSSPACE, (IO_NODELOCKED | IO_SYNC), cred, NULL, NULL); return 0; } /* * ext2fs_gro_lock_directory: Lock the directory vp, but fail if it has * been rmdir'd. */ static int ext2fs_gro_lock_directory(struct mount *mp, struct vnode *vp) { (void)mp; KASSERT(mp != NULL); KASSERT(vp != NULL); KASSERT(vp->v_mount == mp); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); if (ext2fs_rmdired_p(vp)) { VOP_UNLOCK(vp); return ENOENT; } return 0; } static const struct genfs_rename_ops ext2fs_genfs_rename_ops = { .gro_de_equal_p = ext2fs_gro_de_equal_p, .gro_directory_empty_p = ext2fs_gro_directory_empty_p, .gro_rename_check_possible = ext2fs_gro_rename_check_possible, .gro_rename_check_permitted = ext2fs_gro_rename_check_permitted, .gro_remove_check_possible = ext2fs_gro_remove_check_possible, .gro_remove_check_permitted = ext2fs_gro_remove_check_permitted, .gro_rename = ext2fs_gro_rename, .gro_remove = ext2fs_gro_remove, .gro_lookup = ext2fs_gro_lookup, .gro_genealogy = ext2fs_gro_genealogy, .gro_lock_directory = ext2fs_gro_lock_directory, };