Home | History | Annotate | Line # | Download | only in ext2fs
ext2fs_rename.c revision 1.5.2.1
      1  1.5.2.1     rmind /*	$NetBSD: ext2fs_rename.c,v 1.5.2.1 2014/05/18 17:46:21 rmind Exp $	*/
      2      1.1  riastrad 
      3      1.1  riastrad /*-
      4      1.1  riastrad  * Copyright (c) 2012 The NetBSD Foundation, Inc.
      5      1.1  riastrad  * All rights reserved.
      6      1.1  riastrad  *
      7      1.1  riastrad  * This code is derived from software contributed to The NetBSD Foundation
      8      1.1  riastrad  * by Taylor R Campbell.
      9      1.1  riastrad  *
     10      1.1  riastrad  * Redistribution and use in source and binary forms, with or without
     11      1.1  riastrad  * modification, are permitted provided that the following conditions
     12      1.1  riastrad  * are met:
     13      1.1  riastrad  * 1. Redistributions of source code must retain the above copyright
     14      1.1  riastrad  *    notice, this list of conditions and the following disclaimer.
     15      1.1  riastrad  * 2. Redistributions in binary form must reproduce the above copyright
     16      1.1  riastrad  *    notice, this list of conditions and the following disclaimer in the
     17      1.1  riastrad  *    documentation and/or other materials provided with the distribution.
     18      1.1  riastrad  *
     19      1.1  riastrad  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20      1.1  riastrad  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21      1.1  riastrad  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22      1.1  riastrad  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23      1.1  riastrad  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24      1.1  riastrad  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25      1.1  riastrad  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26      1.1  riastrad  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27      1.1  riastrad  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28      1.1  riastrad  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29      1.1  riastrad  * POSSIBILITY OF SUCH DAMAGE.
     30      1.1  riastrad  */
     31      1.1  riastrad 
     32      1.1  riastrad /*
     33      1.1  riastrad  * Ext2fs Rename
     34      1.1  riastrad  */
     35      1.1  riastrad 
     36      1.1  riastrad #include <sys/cdefs.h>
     37  1.5.2.1     rmind __KERNEL_RCSID(0, "$NetBSD: ext2fs_rename.c,v 1.5.2.1 2014/05/18 17:46:21 rmind Exp $");
     38      1.1  riastrad 
     39      1.1  riastrad #include <sys/param.h>
     40      1.1  riastrad #include <sys/buf.h>
     41      1.1  riastrad #include <sys/errno.h>
     42      1.1  riastrad #include <sys/kauth.h>
     43      1.1  riastrad #include <sys/mount.h>
     44      1.1  riastrad #include <sys/namei.h>
     45      1.1  riastrad #include <sys/vnode.h>
     46      1.1  riastrad #include <sys/vnode_if.h>
     47      1.1  riastrad 
     48      1.1  riastrad #include <miscfs/genfs/genfs.h>
     49      1.1  riastrad 
     50      1.1  riastrad #include <ufs/ext2fs/ext2fs.h>
     51      1.1  riastrad #include <ufs/ext2fs/ext2fs_dir.h>
     52      1.1  riastrad #include <ufs/ext2fs/ext2fs_extern.h>
     53      1.1  riastrad #include <ufs/ufs/inode.h>
     54      1.1  riastrad #include <ufs/ufs/ufs_extern.h>
     55      1.1  riastrad #include <ufs/ufs/ufsmount.h>
     56      1.1  riastrad 
     57      1.1  riastrad /*
     58      1.1  riastrad  * Forward declarations
     59      1.1  riastrad  */
     60      1.1  riastrad static int ext2fs_sane_rename(struct vnode *, struct componentname *,
     61      1.1  riastrad     struct vnode *, struct componentname *,
     62      1.1  riastrad     kauth_cred_t, bool);
     63      1.1  riastrad static bool ext2fs_rename_ulr_overlap_p(const struct ufs_lookup_results *,
     64      1.1  riastrad     const struct ufs_lookup_results *);
     65      1.1  riastrad static int ext2fs_rename_recalculate_fulr(struct vnode *,
     66      1.1  riastrad     struct ufs_lookup_results *, const struct ufs_lookup_results *,
     67      1.1  riastrad     const struct componentname *);
     68      1.1  riastrad static bool ext2fs_rmdired_p(struct vnode *);
     69      1.1  riastrad static int ext2fs_read_dotdot(struct vnode *, kauth_cred_t, ino_t *);
     70      1.1  riastrad static int ext2fs_rename_replace_dotdot(struct vnode *,
     71      1.1  riastrad     struct vnode *, struct vnode *, kauth_cred_t);
     72      1.1  riastrad static int ext2fs_gro_lock_directory(struct mount *, struct vnode *);
     73      1.1  riastrad 
     74      1.1  riastrad static const struct genfs_rename_ops ext2fs_genfs_rename_ops;
     75      1.1  riastrad 
     76      1.1  riastrad /*
     77      1.1  riastrad  * ext2fs_sane_rename: The hairiest vop, with the saner API.
     78      1.1  riastrad  *
     79      1.1  riastrad  * Arguments:
     80      1.1  riastrad  *
     81      1.1  riastrad  * . fdvp (from directory vnode),
     82      1.1  riastrad  * . fcnp (from component name),
     83      1.1  riastrad  * . tdvp (to directory vnode),
     84      1.1  riastrad  * . tcnp (to component name),
     85      1.1  riastrad  * . cred (credentials structure), and
     86      1.1  riastrad  * . posixly_correct (flag for behaviour if target & source link same file).
     87      1.1  riastrad  *
     88      1.1  riastrad  * fdvp and tdvp may be the same, and must be referenced and unlocked.
     89      1.1  riastrad  */
     90      1.1  riastrad static int
     91      1.1  riastrad ext2fs_sane_rename(
     92      1.1  riastrad     struct vnode *fdvp, struct componentname *fcnp,
     93      1.1  riastrad     struct vnode *tdvp, struct componentname *tcnp,
     94      1.1  riastrad     kauth_cred_t cred, bool posixly_correct)
     95      1.1  riastrad {
     96      1.1  riastrad 	struct ufs_lookup_results fulr, tulr;
     97      1.1  riastrad 
     98      1.1  riastrad 	return genfs_sane_rename(&ext2fs_genfs_rename_ops,
     99      1.1  riastrad 	    fdvp, fcnp, &fulr, tdvp, tcnp, &tulr,
    100      1.1  riastrad 	    cred, posixly_correct);
    101      1.1  riastrad }
    102      1.1  riastrad 
    103      1.1  riastrad /*
    104      1.1  riastrad  * ext2fs_rename: The hairiest vop, with the insanest API.  Defer to
    105      1.1  riastrad  * genfs_insane_rename immediately.
    106      1.1  riastrad  */
    107      1.1  riastrad int
    108      1.1  riastrad ext2fs_rename(void *v)
    109      1.1  riastrad {
    110      1.1  riastrad 
    111      1.1  riastrad 	return genfs_insane_rename(v, &ext2fs_sane_rename);
    112      1.1  riastrad }
    113      1.1  riastrad 
    114      1.1  riastrad /*
    115      1.1  riastrad  * ext2fs_gro_directory_empty_p: Return true if the directory vp is
    116      1.1  riastrad  * empty.  dvp is its parent.
    117      1.1  riastrad  *
    118      1.1  riastrad  * vp and dvp must be locked and referenced.
    119      1.1  riastrad  */
    120      1.1  riastrad static bool
    121      1.1  riastrad ext2fs_gro_directory_empty_p(struct mount *mp, kauth_cred_t cred,
    122      1.1  riastrad     struct vnode *vp, struct vnode *dvp)
    123      1.1  riastrad {
    124      1.1  riastrad 
    125      1.1  riastrad 	(void)mp;
    126      1.1  riastrad 	KASSERT(mp != NULL);
    127      1.1  riastrad 	KASSERT(vp != NULL);
    128      1.1  riastrad 	KASSERT(dvp != NULL);
    129      1.1  riastrad 	KASSERT(vp != dvp);
    130      1.1  riastrad 	KASSERT(vp->v_mount == mp);
    131      1.1  riastrad 	KASSERT(dvp->v_mount == mp);
    132      1.1  riastrad 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    133      1.1  riastrad 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
    134      1.1  riastrad 
    135      1.1  riastrad 	return ext2fs_dirempty(VTOI(vp), VTOI(dvp)->i_number, cred);
    136      1.1  riastrad }
    137      1.1  riastrad 
    138      1.1  riastrad /*
    139      1.1  riastrad  * ext2fs_gro_rename_check_possible: Check whether a rename is possible
    140      1.1  riastrad  * independent of credentials.
    141      1.1  riastrad  */
    142      1.1  riastrad static int
    143      1.1  riastrad ext2fs_gro_rename_check_possible(struct mount *mp,
    144      1.1  riastrad     struct vnode *fdvp, struct vnode *fvp,
    145      1.1  riastrad     struct vnode *tdvp, struct vnode *tvp)
    146      1.1  riastrad {
    147      1.1  riastrad 
    148      1.1  riastrad 	(void)mp;
    149      1.1  riastrad 	KASSERT(mp != NULL);
    150      1.1  riastrad 	KASSERT(fdvp != NULL);
    151      1.1  riastrad 	KASSERT(fvp != NULL);
    152      1.1  riastrad 	KASSERT(tdvp != NULL);
    153      1.1  riastrad 	KASSERT(fdvp != fvp);
    154      1.1  riastrad 	KASSERT(fdvp != tvp);
    155      1.1  riastrad 	KASSERT(tdvp != fvp);
    156      1.1  riastrad 	KASSERT(tdvp != tvp);
    157      1.1  riastrad 	KASSERT(fvp != tvp);
    158      1.1  riastrad 	KASSERT(fdvp->v_type == VDIR);
    159      1.1  riastrad 	KASSERT(tdvp->v_type == VDIR);
    160      1.1  riastrad 	KASSERT(fdvp->v_mount == mp);
    161      1.1  riastrad 	KASSERT(fvp->v_mount == mp);
    162      1.1  riastrad 	KASSERT(tdvp->v_mount == mp);
    163      1.1  riastrad 	KASSERT((tvp == NULL) || (tvp->v_mount == mp));
    164      1.1  riastrad 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
    165      1.1  riastrad 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
    166      1.1  riastrad 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
    167      1.1  riastrad 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
    168      1.1  riastrad 
    169      1.1  riastrad 	return genfs_ufslike_rename_check_possible(
    170      1.1  riastrad 	    VTOI(fdvp)->i_e2fs_flags, VTOI(fvp)->i_e2fs_flags,
    171      1.1  riastrad 	    VTOI(tdvp)->i_e2fs_flags, (tvp? VTOI(tvp)->i_e2fs_flags : 0),
    172      1.1  riastrad 	    (tvp != NULL),
    173      1.1  riastrad 	    EXT2_IMMUTABLE, EXT2_APPEND);
    174      1.1  riastrad }
    175      1.1  riastrad 
    176      1.1  riastrad /*
    177      1.1  riastrad  * ext2fs_gro_rename_check_permitted: Check whether a rename is
    178      1.1  riastrad  * permitted given our credentials.
    179      1.1  riastrad  */
    180      1.1  riastrad static int
    181      1.1  riastrad ext2fs_gro_rename_check_permitted(struct mount *mp, kauth_cred_t cred,
    182      1.1  riastrad     struct vnode *fdvp, struct vnode *fvp,
    183      1.1  riastrad     struct vnode *tdvp, struct vnode *tvp)
    184      1.1  riastrad {
    185      1.1  riastrad 
    186      1.1  riastrad 	(void)mp;
    187      1.1  riastrad 	KASSERT(mp != NULL);
    188      1.1  riastrad 	KASSERT(fdvp != NULL);
    189      1.1  riastrad 	KASSERT(fvp != NULL);
    190      1.1  riastrad 	KASSERT(tdvp != NULL);
    191      1.1  riastrad 	KASSERT(fdvp != fvp);
    192      1.1  riastrad 	KASSERT(fdvp != tvp);
    193      1.1  riastrad 	KASSERT(tdvp != fvp);
    194      1.1  riastrad 	KASSERT(tdvp != tvp);
    195      1.1  riastrad 	KASSERT(fvp != tvp);
    196      1.1  riastrad 	KASSERT(fdvp->v_type == VDIR);
    197      1.1  riastrad 	KASSERT(tdvp->v_type == VDIR);
    198      1.1  riastrad 	KASSERT(fdvp->v_mount == mp);
    199      1.1  riastrad 	KASSERT(fvp->v_mount == mp);
    200      1.1  riastrad 	KASSERT(tdvp->v_mount == mp);
    201      1.1  riastrad 	KASSERT((tvp == NULL) || (tvp->v_mount == mp));
    202      1.1  riastrad 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
    203      1.1  riastrad 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
    204      1.1  riastrad 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
    205      1.1  riastrad 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
    206      1.1  riastrad 
    207      1.1  riastrad 	return genfs_ufslike_rename_check_permitted(cred,
    208      1.1  riastrad 	    fdvp, VTOI(fdvp)->i_e2fs_mode, VTOI(fdvp)->i_uid,
    209      1.1  riastrad 	    fvp, VTOI(fvp)->i_uid,
    210      1.1  riastrad 	    tdvp, VTOI(tdvp)->i_e2fs_mode, VTOI(tdvp)->i_uid,
    211      1.1  riastrad 	    tvp, (tvp? VTOI(tvp)->i_uid : 0));
    212      1.1  riastrad }
    213      1.1  riastrad 
    214      1.1  riastrad /*
    215      1.1  riastrad  * ext2fs_gro_remove_check_possible: Check whether a remove is possible
    216      1.1  riastrad  * independent of credentials.
    217      1.1  riastrad  */
    218      1.1  riastrad static int
    219      1.1  riastrad ext2fs_gro_remove_check_possible(struct mount *mp,
    220      1.1  riastrad     struct vnode *dvp, struct vnode *vp)
    221      1.1  riastrad {
    222      1.1  riastrad 
    223      1.1  riastrad 	(void)mp;
    224      1.1  riastrad 	KASSERT(mp != NULL);
    225      1.1  riastrad 	KASSERT(dvp != NULL);
    226      1.1  riastrad 	KASSERT(vp != NULL);
    227      1.1  riastrad 	KASSERT(dvp != vp);
    228      1.1  riastrad 	KASSERT(dvp->v_type == VDIR);
    229      1.1  riastrad 	KASSERT(vp->v_type != VDIR);
    230      1.1  riastrad 	KASSERT(dvp->v_mount == mp);
    231      1.1  riastrad 	KASSERT(vp->v_mount == mp);
    232      1.1  riastrad 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
    233      1.1  riastrad 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    234      1.1  riastrad 
    235      1.1  riastrad 	return genfs_ufslike_remove_check_possible(
    236      1.1  riastrad 	    VTOI(dvp)->i_e2fs_flags, VTOI(vp)->i_e2fs_flags,
    237      1.1  riastrad 	    EXT2_IMMUTABLE, EXT2_APPEND);
    238      1.1  riastrad }
    239      1.1  riastrad 
    240      1.1  riastrad /*
    241      1.1  riastrad  * ext2fs_gro_remove_check_permitted: Check whether a remove is
    242      1.1  riastrad  * permitted given our credentials.
    243      1.1  riastrad  */
    244      1.1  riastrad static int
    245      1.1  riastrad ext2fs_gro_remove_check_permitted(struct mount *mp, kauth_cred_t cred,
    246      1.1  riastrad     struct vnode *dvp, struct vnode *vp)
    247      1.1  riastrad {
    248      1.1  riastrad 
    249      1.1  riastrad 	(void)mp;
    250      1.1  riastrad 	KASSERT(mp != NULL);
    251      1.1  riastrad 	KASSERT(dvp != NULL);
    252      1.1  riastrad 	KASSERT(vp != NULL);
    253      1.1  riastrad 	KASSERT(dvp != vp);
    254      1.1  riastrad 	KASSERT(dvp->v_type == VDIR);
    255      1.1  riastrad 	KASSERT(vp->v_type != VDIR);
    256      1.1  riastrad 	KASSERT(dvp->v_mount == mp);
    257      1.1  riastrad 	KASSERT(vp->v_mount == mp);
    258      1.1  riastrad 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
    259      1.1  riastrad 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    260      1.1  riastrad 
    261      1.1  riastrad 	return genfs_ufslike_remove_check_permitted(cred,
    262      1.1  riastrad 	    dvp, VTOI(dvp)->i_e2fs_mode, VTOI(dvp)->i_uid,
    263      1.1  riastrad 	    vp, VTOI(vp)->i_uid);
    264      1.1  riastrad }
    265      1.1  riastrad 
    266      1.1  riastrad /*
    267      1.1  riastrad  * ext2fs_gro_rename: Actually perform the rename operation.
    268      1.1  riastrad  */
    269      1.1  riastrad static int
    270      1.1  riastrad ext2fs_gro_rename(struct mount *mp, kauth_cred_t cred,
    271      1.1  riastrad     struct vnode *fdvp, struct componentname *fcnp,
    272      1.1  riastrad     void *fde, struct vnode *fvp,
    273      1.1  riastrad     struct vnode *tdvp, struct componentname *tcnp,
    274      1.1  riastrad     void *tde, struct vnode *tvp)
    275      1.1  riastrad {
    276      1.1  riastrad 	struct ufs_lookup_results *fulr = fde;
    277      1.1  riastrad 	struct ufs_lookup_results *tulr = tde;
    278      1.1  riastrad 	bool directory_p, reparent_p;
    279      1.1  riastrad 	int error;
    280      1.1  riastrad 
    281      1.1  riastrad 	(void)mp;
    282      1.1  riastrad 	KASSERT(mp != NULL);
    283      1.1  riastrad 	KASSERT(fdvp != NULL);
    284      1.1  riastrad 	KASSERT(fcnp != NULL);
    285      1.1  riastrad 	KASSERT(fulr != NULL);
    286      1.1  riastrad 	KASSERT(fvp != NULL);
    287      1.1  riastrad 	KASSERT(tdvp != NULL);
    288      1.1  riastrad 	KASSERT(tcnp != NULL);
    289      1.1  riastrad 	KASSERT(tulr != NULL);
    290      1.1  riastrad 	KASSERT(fulr != tulr);
    291      1.1  riastrad 	KASSERT(fdvp != fvp);
    292      1.1  riastrad 	KASSERT(fdvp != tvp);
    293      1.1  riastrad 	KASSERT(tdvp != fvp);
    294      1.1  riastrad 	KASSERT(tdvp != tvp);
    295      1.1  riastrad 	KASSERT(fvp != tvp);
    296      1.1  riastrad 	KASSERT(fdvp->v_mount == mp);
    297      1.1  riastrad 	KASSERT(fvp->v_mount == mp);
    298      1.1  riastrad 	KASSERT(tdvp->v_mount == mp);
    299      1.1  riastrad 	KASSERT((tvp == NULL) || (tvp->v_mount == mp));
    300      1.1  riastrad 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
    301      1.1  riastrad 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
    302      1.1  riastrad 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
    303      1.1  riastrad 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
    304      1.1  riastrad 
    305      1.1  riastrad 	/*
    306      1.1  riastrad 	 * We shall need to temporarily bump the link count, so make
    307      1.1  riastrad 	 * sure there is room to do so.
    308      1.1  riastrad 	 */
    309      1.1  riastrad 	if ((nlink_t)VTOI(fvp)->i_e2fs_nlink >= LINK_MAX)
    310      1.1  riastrad 		return EMLINK;
    311      1.1  riastrad 
    312      1.1  riastrad 	directory_p = (fvp->v_type == VDIR);
    313      1.1  riastrad 	KASSERT(directory_p == ((VTOI(fvp)->i_e2fs_mode & IFMT) == IFDIR));
    314      1.1  riastrad 	KASSERT((tvp == NULL) || (directory_p == (tvp->v_type == VDIR)));
    315      1.1  riastrad 	KASSERT((tvp == NULL) || (directory_p ==
    316      1.1  riastrad 		((VTOI(tvp)->i_e2fs_mode & IFMT) == IFDIR)));
    317      1.1  riastrad 
    318      1.1  riastrad 	reparent_p = (fdvp != tdvp);
    319      1.1  riastrad 	KASSERT(reparent_p == (VTOI(fdvp)->i_number != VTOI(tdvp)->i_number));
    320      1.1  riastrad 
    321      1.1  riastrad 	/*
    322      1.1  riastrad 	 * Commence hacking of the data on disk.
    323      1.1  riastrad 	 */
    324      1.1  riastrad 
    325      1.1  riastrad 	/*
    326      1.1  riastrad 	 * 1) Bump link count while we're moving stuff
    327      1.1  riastrad 	 *    around.  If we crash somewhere before
    328      1.1  riastrad 	 *    completing our work, the link count
    329      1.1  riastrad 	 *    may be wrong, but correctable.
    330      1.1  riastrad 	 */
    331      1.1  riastrad 
    332      1.1  riastrad 	KASSERT((nlink_t)VTOI(fvp)->i_e2fs_nlink < LINK_MAX);
    333      1.1  riastrad 	VTOI(fvp)->i_e2fs_nlink++;
    334      1.1  riastrad 	VTOI(fvp)->i_flag |= IN_CHANGE;
    335      1.1  riastrad 	error = ext2fs_update(fvp, NULL, NULL, UPDATE_WAIT);
    336      1.1  riastrad 	if (error)
    337      1.1  riastrad 		goto whymustithurtsomuch;
    338      1.1  riastrad 
    339      1.1  riastrad 	/*
    340      1.1  riastrad 	 * 2) If target doesn't exist, link the target
    341      1.1  riastrad 	 *    to the source and unlink the source.
    342      1.1  riastrad 	 *    Otherwise, rewrite the target directory
    343      1.1  riastrad 	 *    entry to reference the source inode and
    344      1.1  riastrad 	 *    expunge the original entry's existence.
    345      1.1  riastrad 	 */
    346      1.1  riastrad 
    347      1.1  riastrad 	if (tvp == NULL) {
    348      1.1  riastrad 		/*
    349      1.1  riastrad 		 * Account for ".." in new directory.
    350      1.1  riastrad 		 * When source and destination have the same
    351      1.1  riastrad 		 * parent we don't fool with the link count.
    352      1.1  riastrad 		 */
    353      1.1  riastrad 		if (directory_p && reparent_p) {
    354      1.1  riastrad 			if ((nlink_t)VTOI(tdvp)->i_e2fs_nlink >= LINK_MAX) {
    355      1.1  riastrad 				error = EMLINK;
    356      1.1  riastrad 				goto whymustithurtsomuch;
    357      1.1  riastrad 			}
    358      1.1  riastrad 			KASSERT((nlink_t)VTOI(tdvp)->i_e2fs_nlink < LINK_MAX);
    359      1.1  riastrad 			VTOI(tdvp)->i_e2fs_nlink++;
    360      1.1  riastrad 			VTOI(tdvp)->i_flag |= IN_CHANGE;
    361      1.1  riastrad 			error = ext2fs_update(tdvp, NULL, NULL, UPDATE_WAIT);
    362      1.1  riastrad 			if (error) {
    363      1.1  riastrad 				/*
    364      1.1  riastrad 				 * Link count update didn't take --
    365      1.1  riastrad 				 * back out the in-memory link count.
    366      1.1  riastrad 				 */
    367      1.1  riastrad 				KASSERT(0 < VTOI(tdvp)->i_e2fs_nlink);
    368      1.1  riastrad 				VTOI(tdvp)->i_e2fs_nlink--;
    369      1.1  riastrad 				VTOI(tdvp)->i_flag |= IN_CHANGE;
    370      1.1  riastrad 				goto whymustithurtsomuch;
    371      1.1  riastrad 			}
    372      1.1  riastrad 		}
    373      1.1  riastrad 
    374      1.1  riastrad 		error = ext2fs_direnter(VTOI(fvp), tdvp, tulr, tcnp);
    375      1.1  riastrad 		if (error) {
    376      1.1  riastrad 			if (directory_p && reparent_p) {
    377      1.1  riastrad 				/*
    378      1.1  riastrad 				 * Directory update didn't take, but
    379      1.1  riastrad 				 * the link count update did -- back
    380      1.1  riastrad 				 * out the in-memory link count and the
    381      1.1  riastrad 				 * on-disk link count.
    382      1.1  riastrad 				 */
    383      1.1  riastrad 				KASSERT(0 < VTOI(tdvp)->i_e2fs_nlink);
    384      1.1  riastrad 				VTOI(tdvp)->i_e2fs_nlink--;
    385      1.1  riastrad 				VTOI(tdvp)->i_flag |= IN_CHANGE;
    386      1.1  riastrad 				(void)ext2fs_update(tdvp, NULL, NULL,
    387      1.1  riastrad 				    UPDATE_WAIT);
    388      1.1  riastrad 			}
    389      1.1  riastrad 			goto whymustithurtsomuch;
    390      1.1  riastrad 		}
    391      1.1  riastrad 	} else {
    392      1.1  riastrad 		if (directory_p)
    393      1.1  riastrad 			/* XXX WTF?  Why purge here?  Why not purge others?  */
    394      1.1  riastrad 			cache_purge(tdvp);
    395      1.1  riastrad 
    396      1.1  riastrad 		/*
    397      1.1  riastrad 		 * Make the target directory's entry for tcnp point at
    398      1.1  riastrad 		 * the source node.
    399      1.1  riastrad 		 */
    400      1.1  riastrad 		error = ext2fs_dirrewrite(VTOI(tdvp), tulr, VTOI(fvp), tcnp);
    401      1.1  riastrad 		if (error)
    402      1.1  riastrad 			goto whymustithurtsomuch;
    403      1.1  riastrad 
    404      1.1  riastrad 		/*
    405      1.1  riastrad 		 * If the source and target are directories, and the
    406      1.1  riastrad 		 * target is in the same directory as the source,
    407      1.1  riastrad 		 * decrement the link count of the common parent
    408      1.1  riastrad 		 * directory, since we are removing the target from
    409      1.1  riastrad 		 * that directory.
    410      1.1  riastrad 		 */
    411      1.1  riastrad 		if (directory_p && !reparent_p) {
    412      1.1  riastrad 			KASSERT(fdvp == tdvp);
    413      1.1  riastrad 			/* XXX check, don't kassert */
    414      1.1  riastrad 			KASSERT(0 < VTOI(tdvp)->i_e2fs_nlink);
    415      1.1  riastrad 			VTOI(tdvp)->i_e2fs_nlink--;
    416      1.1  riastrad 			VTOI(tdvp)->i_flag |= IN_CHANGE;
    417      1.1  riastrad 		}
    418      1.1  riastrad 
    419      1.1  riastrad 		/*
    420      1.1  riastrad 		 * Adjust the link count of the target to
    421      1.1  riastrad 		 * reflect the dirrewrite above.  If this is
    422      1.1  riastrad 		 * a directory it is empty and there are
    423      1.1  riastrad 		 * no links to it, so we can squash the inode and
    424      1.1  riastrad 		 * any space associated with it.  We disallowed
    425      1.1  riastrad 		 * renaming over top of a directory with links to
    426      1.1  riastrad 		 * it above, as the remaining link would point to
    427      1.1  riastrad 		 * a directory without "." or ".." entries.
    428      1.1  riastrad 		 */
    429      1.1  riastrad 		/* XXX check, don't kassert */
    430      1.1  riastrad 		KASSERT(0 < VTOI(tvp)->i_e2fs_nlink);
    431      1.1  riastrad 		VTOI(tvp)->i_e2fs_nlink--;
    432      1.1  riastrad 		if (directory_p) {
    433      1.1  riastrad 			/*
    434      1.1  riastrad 			 * XXX The ext2fs_dirempty call earlier does
    435      1.1  riastrad 			 * not guarantee anything about nlink.
    436      1.1  riastrad 			 */
    437      1.1  riastrad 			if (VTOI(tvp)->i_e2fs_nlink != 1)
    438      1.1  riastrad 				ufs_dirbad(VTOI(tvp), (doff_t)0,
    439      1.1  riastrad 				    "hard-linked directory");
    440      1.1  riastrad 			VTOI(tvp)->i_e2fs_nlink = 0;
    441      1.1  riastrad 			error = ext2fs_truncate(tvp, (off_t)0, IO_SYNC, cred);
    442      1.1  riastrad #if 0			/* XXX This branch was not in ext2fs_rename!  */
    443      1.1  riastrad 			if (error)
    444      1.1  riastrad 				goto whymustithurtsomuch;
    445      1.1  riastrad #endif
    446      1.1  riastrad 		}
    447      1.1  riastrad 		/*
    448      1.1  riastrad 		 * XXX Why is this here, and not above the preceding
    449      1.1  riastrad 		 * conditional?
    450      1.1  riastrad 		 */
    451      1.1  riastrad 		VTOI(tvp)->i_flag |= IN_CHANGE;
    452      1.1  riastrad 	}
    453      1.1  riastrad 
    454      1.1  riastrad 	/*
    455      1.1  riastrad 	 * If the source is a directory with a new parent, the link
    456      1.1  riastrad 	 * count of the old parent directory must be decremented and
    457      1.1  riastrad 	 * ".." set to point to the new parent.
    458      1.1  riastrad 	 */
    459      1.1  riastrad 	if (directory_p && reparent_p) {
    460      1.1  riastrad 		error = ext2fs_rename_replace_dotdot(fvp, fdvp, tdvp, cred);
    461      1.1  riastrad 		if (error)
    462      1.1  riastrad 			goto whymustithurtsomuch;
    463      1.1  riastrad 
    464      1.1  riastrad 		/* XXX WTF?  Why purge here?  Why not purge others?  */
    465      1.1  riastrad 		cache_purge(fdvp);
    466      1.1  riastrad 	}
    467      1.1  riastrad 
    468      1.1  riastrad 	/*
    469      1.1  riastrad 	 * 3) Unlink the source.
    470      1.1  riastrad 	 */
    471      1.1  riastrad 
    472      1.1  riastrad 	/*
    473      1.1  riastrad 	 * ext2fs_direnter may compact the directory in the process of
    474      1.1  riastrad 	 * inserting a new entry.  That may invalidate fulr, which we
    475      1.1  riastrad 	 * need in order to remove the old entry.  In that case, we
    476      1.1  riastrad 	 * need to recalculate what fulr should be.
    477      1.1  riastrad 	 */
    478      1.3  riastrad 	if (!reparent_p && (tvp == NULL) &&
    479      1.3  riastrad 	    ext2fs_rename_ulr_overlap_p(fulr, tulr)) {
    480      1.1  riastrad 		error = ext2fs_rename_recalculate_fulr(fdvp, fulr, tulr, fcnp);
    481      1.1  riastrad #if 0				/* XXX */
    482      1.1  riastrad 		if (error)	/* XXX Try to back out changes?  */
    483      1.1  riastrad 			goto whymustithurtsomuch;
    484      1.1  riastrad #endif
    485      1.1  riastrad 	}
    486      1.1  riastrad 
    487      1.1  riastrad 	error = ext2fs_dirremove(fdvp, fulr, fcnp);
    488      1.1  riastrad 	if (error)
    489      1.1  riastrad 		goto whymustithurtsomuch;
    490      1.1  riastrad 
    491      1.1  riastrad 	/*
    492      1.1  riastrad 	 * XXX Perhaps this should go at the top, in case the file
    493      1.1  riastrad 	 * system is modified but incompletely so because of an
    494      1.1  riastrad 	 * intermediate error.
    495      1.1  riastrad 	 */
    496      1.1  riastrad 	genfs_rename_knote(fdvp, fvp, tdvp, tvp,
    497      1.1  riastrad 	    ((tvp != NULL) && (VTOI(tvp)->i_e2fs_nlink == 0)));
    498      1.1  riastrad #if 0				/* XXX */
    499      1.1  riastrad 	genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp);
    500      1.1  riastrad #endif
    501      1.1  riastrad 
    502      1.1  riastrad whymustithurtsomuch:
    503      1.1  riastrad 	KASSERT(0 < VTOI(fvp)->i_e2fs_nlink);
    504      1.1  riastrad 	VTOI(fvp)->i_e2fs_nlink--;
    505      1.1  riastrad 	VTOI(fvp)->i_flag |= IN_CHANGE;
    506      1.1  riastrad 	return error;
    507      1.1  riastrad }
    508      1.1  riastrad 
    509      1.1  riastrad /*
    510      1.1  riastrad  * ext2fs_rename_ulr_overlap_p: True iff tulr overlaps with fulr so
    511      1.1  riastrad  * that entering a directory entry at tulr may move fulr.
    512      1.1  riastrad  */
    513      1.1  riastrad static bool
    514      1.1  riastrad ext2fs_rename_ulr_overlap_p(const struct ufs_lookup_results *fulr,
    515      1.1  riastrad     const struct ufs_lookup_results *tulr)
    516      1.1  riastrad {
    517      1.1  riastrad 	doff_t from_prev_start, from_prev_end, to_start, to_end;
    518      1.1  riastrad 
    519      1.1  riastrad 	KASSERT(fulr != NULL);
    520      1.1  riastrad 	KASSERT(tulr != NULL);
    521      1.1  riastrad 	KASSERT(fulr != tulr);
    522      1.1  riastrad 
    523      1.1  riastrad 	/*
    524      1.1  riastrad 	 * fulr is from a DELETE lookup, so fulr->ulr_count is the size
    525      1.1  riastrad 	 * of the preceding entry (d_reclen).
    526      1.1  riastrad 	 */
    527      1.1  riastrad 	from_prev_end = fulr->ulr_offset;
    528      1.1  riastrad 	KASSERT(fulr->ulr_count <= from_prev_end);
    529      1.1  riastrad 	from_prev_start = (from_prev_end - fulr->ulr_count);
    530      1.1  riastrad 
    531      1.1  riastrad 	/*
    532      1.1  riastrad 	 * tulr is from a RENAME lookup, so tulr->ulr_count is the size
    533      1.1  riastrad 	 * of the free space for an entry that we are about to fill.
    534      1.1  riastrad 	 */
    535      1.1  riastrad 	to_start = tulr->ulr_offset;
    536      1.1  riastrad 	KASSERT(tulr->ulr_count < (EXT2FS_MAXDIRSIZE - to_start));
    537      1.1  riastrad 	to_end = (to_start + tulr->ulr_count);
    538      1.1  riastrad 
    539      1.1  riastrad 	return
    540      1.1  riastrad 	    (((to_start <= from_prev_start) && (from_prev_start < to_end)) ||
    541      1.1  riastrad 		((to_start <= from_prev_end) && (from_prev_end < to_end)));
    542      1.1  riastrad }
    543      1.1  riastrad 
    544      1.1  riastrad /*
    545      1.1  riastrad  * ext2fs_rename_recalculate_fulr: If we have just entered a directory
    546      1.1  riastrad  * into dvp at tulr, and we were about to remove one at fulr for an
    547      1.1  riastrad  * entry named fcnp, fulr may be invalid.  So, if necessary,
    548      1.1  riastrad  * recalculate it.
    549      1.1  riastrad  */
    550      1.1  riastrad static int
    551      1.1  riastrad ext2fs_rename_recalculate_fulr(struct vnode *dvp,
    552      1.1  riastrad     struct ufs_lookup_results *fulr, const struct ufs_lookup_results *tulr,
    553      1.1  riastrad     const struct componentname *fcnp)
    554      1.1  riastrad {
    555      1.1  riastrad 	struct mount *mp;
    556      1.1  riastrad 	struct ufsmount *ump;
    557      1.1  riastrad 	/* XXX int is a silly type for this; blame ufsmount::um_dirblksiz.  */
    558      1.3  riastrad 	int dirblksiz;
    559      1.3  riastrad 	doff_t search_start, search_end;
    560      1.1  riastrad 	doff_t offset;		/* Offset of entry we're examining.  */
    561      1.1  riastrad 	struct buf *bp;		/* I/O block we're examining.  */
    562      1.3  riastrad 	char *dirbuf;		/* Pointer into directory at search_start.  */
    563      1.1  riastrad 	struct ext2fs_direct *ep; /* Pointer to the entry we're examining.  */
    564      1.1  riastrad 	/* XXX direct::d_reclen is 16-bit;
    565      1.1  riastrad 	 * ufs_lookup_results::ulr_reclen is 32-bit.  Blah.  */
    566      1.1  riastrad 	uint32_t reclen;	/* Length of the entry we're examining.  */
    567      1.1  riastrad 	uint32_t prev_reclen;	/* Length of the preceding entry.  */
    568      1.1  riastrad 	int error;
    569      1.1  riastrad 
    570      1.1  riastrad 	KASSERT(dvp != NULL);
    571      1.1  riastrad 	KASSERT(dvp->v_mount != NULL);
    572      1.1  riastrad 	KASSERT(VTOI(dvp) != NULL);
    573      1.1  riastrad 	KASSERT(fulr != NULL);
    574      1.1  riastrad 	KASSERT(tulr != NULL);
    575      1.1  riastrad 	KASSERT(fulr != tulr);
    576      1.1  riastrad 	KASSERT(ext2fs_rename_ulr_overlap_p(fulr, tulr));
    577      1.1  riastrad 
    578      1.1  riastrad 	mp = dvp->v_mount;
    579      1.1  riastrad 	ump = VFSTOUFS(mp);
    580      1.1  riastrad 	KASSERT(ump != NULL);
    581      1.1  riastrad 	KASSERT(ump == VTOI(dvp)->i_ump);
    582      1.1  riastrad 
    583      1.3  riastrad 	dirblksiz = ump->um_dirblksiz;
    584      1.3  riastrad 	KASSERT(0 < dirblksiz);
    585      1.3  riastrad 	KASSERT((dirblksiz & (dirblksiz - 1)) == 0);
    586      1.1  riastrad 
    587      1.3  riastrad 	/* A directory block may not span across multiple I/O blocks.  */
    588      1.3  riastrad 	KASSERT(dirblksiz <= mp->mnt_stat.f_iosize);
    589      1.3  riastrad 
    590      1.3  riastrad 	/* Find the bounds of the search.  */
    591      1.3  riastrad 	search_start = tulr->ulr_offset;
    592      1.1  riastrad 	KASSERT(fulr->ulr_reclen < (EXT2FS_MAXDIRSIZE - fulr->ulr_offset));
    593      1.1  riastrad 	search_end = (fulr->ulr_offset + fulr->ulr_reclen);
    594      1.1  riastrad 
    595      1.3  riastrad 	/* Compaction must happen only within a directory block. (*)  */
    596      1.3  riastrad 	KASSERT(search_start <= search_end);
    597      1.3  riastrad 	KASSERT((search_end - (search_start &~ (dirblksiz - 1))) <= dirblksiz);
    598      1.3  riastrad 
    599      1.1  riastrad 	dirbuf = NULL;
    600      1.1  riastrad 	bp = NULL;
    601      1.3  riastrad 	error = ext2fs_blkatoff(dvp, (off_t)search_start, &dirbuf, &bp);
    602      1.1  riastrad 	if (error)
    603      1.1  riastrad 		return error;
    604      1.1  riastrad 	KASSERT(dirbuf != NULL);
    605      1.1  riastrad 	KASSERT(bp != NULL);
    606      1.1  riastrad 
    607      1.3  riastrad 	/*
    608      1.3  riastrad 	 * Guarantee we sha'n't go past the end of the buffer we got.
    609      1.3  riastrad 	 * dirbuf is bp->b_data + (search_start & (iosize - 1)), and
    610      1.3  riastrad 	 * the valid range is [bp->b_data, bp->b_data + bp->b_bcount).
    611      1.3  riastrad 	 */
    612      1.3  riastrad 	KASSERT((search_end - search_start) <=
    613      1.3  riastrad 	    (bp->b_bcount - (search_start & (mp->mnt_stat.f_iosize - 1))));
    614      1.3  riastrad 
    615      1.1  riastrad 	prev_reclen = fulr->ulr_count;
    616      1.3  riastrad 	offset = search_start;
    617      1.1  riastrad 
    618      1.1  riastrad 	/*
    619      1.3  riastrad 	 * Search from search_start to search_end for the entry matching
    620      1.1  riastrad 	 * fcnp, which must be there because we found it before and it
    621      1.1  riastrad 	 * should only at most have moved earlier.
    622      1.1  riastrad 	 */
    623      1.1  riastrad 	for (;;) {
    624      1.3  riastrad 		KASSERT(search_start <= offset);
    625      1.1  riastrad 		KASSERT(offset < search_end);
    626      1.1  riastrad 
    627      1.1  riastrad 		/*
    628      1.1  riastrad 		 * Examine the directory entry at offset.
    629      1.1  riastrad 		 */
    630      1.1  riastrad 		ep = (struct ext2fs_direct *)
    631      1.3  riastrad 		    (dirbuf + (offset - search_start));
    632      1.2  riastrad 		reclen = fs2h16(ep->e2d_reclen);
    633      1.1  riastrad 
    634      1.1  riastrad 		if (ep->e2d_ino == 0)
    635      1.1  riastrad 			goto next;	/* Entry is unused.  */
    636      1.1  riastrad 
    637      1.5  dholland 		if (fs2h32(ep->e2d_ino) == UFS_WINO)
    638      1.1  riastrad 			goto next;	/* Entry is whiteout.  */
    639      1.1  riastrad 
    640      1.1  riastrad 		if (fcnp->cn_namelen != ep->e2d_namlen)
    641      1.1  riastrad 			goto next;	/* Wrong name length.  */
    642      1.1  riastrad 
    643      1.1  riastrad 		if (memcmp(ep->e2d_name, fcnp->cn_nameptr, fcnp->cn_namelen))
    644      1.1  riastrad 			goto next;	/* Wrong name.  */
    645      1.1  riastrad 
    646      1.1  riastrad 		/* Got it!  */
    647      1.1  riastrad 		break;
    648      1.1  riastrad 
    649      1.1  riastrad next:
    650      1.1  riastrad 		if (! ((reclen < search_end) &&
    651      1.1  riastrad 			(offset < (search_end - reclen)))) {
    652      1.1  riastrad 			brelse(bp, 0);
    653      1.1  riastrad 			return EIO;	/* XXX Panic?  What?  */
    654      1.1  riastrad 		}
    655      1.1  riastrad 
    656      1.3  riastrad 		/* We may not move past the search end.  */
    657      1.1  riastrad 		KASSERT(reclen < search_end);
    658      1.1  riastrad 		KASSERT(offset < (search_end - reclen));
    659      1.3  riastrad 
    660      1.3  riastrad 		/*
    661      1.3  riastrad 		 * We may not move across a directory block boundary;
    662      1.3  riastrad 		 * see (*) above.
    663      1.3  riastrad 		 */
    664      1.3  riastrad 		KASSERT((offset &~ (dirblksiz - 1)) ==
    665      1.3  riastrad 		    ((offset + reclen) &~ (dirblksiz - 1)));
    666      1.3  riastrad 
    667      1.1  riastrad 		prev_reclen = reclen;
    668      1.1  riastrad 		offset += reclen;
    669      1.1  riastrad 	}
    670      1.1  riastrad 
    671      1.1  riastrad 	/*
    672      1.1  riastrad 	 * Found the entry.  Record where.
    673      1.1  riastrad 	 */
    674      1.1  riastrad 	fulr->ulr_offset = offset;
    675      1.1  riastrad 	fulr->ulr_reclen = reclen;
    676      1.1  riastrad 
    677      1.1  riastrad 	/*
    678      1.1  riastrad 	 * Record the preceding record length, but not if we're at the
    679      1.1  riastrad 	 * start of a directory block.
    680      1.1  riastrad 	 */
    681      1.3  riastrad 	fulr->ulr_count = ((offset & (dirblksiz - 1))? prev_reclen : 0);
    682      1.1  riastrad 
    683      1.1  riastrad 	brelse(bp, 0);
    684      1.1  riastrad 	return 0;
    685      1.1  riastrad }
    686      1.1  riastrad 
    687      1.1  riastrad /*
    688      1.1  riastrad  * ext2fs_gro_remove: Rename an object over another link to itself,
    689      1.1  riastrad  * effectively removing just the original link.
    690      1.1  riastrad  */
    691      1.1  riastrad static int
    692      1.1  riastrad ext2fs_gro_remove(struct mount *mp, kauth_cred_t cred,
    693      1.1  riastrad     struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp)
    694      1.1  riastrad {
    695      1.1  riastrad 	struct ufs_lookup_results *ulr = de;
    696      1.1  riastrad 	int error;
    697      1.1  riastrad 
    698      1.1  riastrad 	(void)mp;
    699      1.1  riastrad 	KASSERT(mp != NULL);
    700      1.1  riastrad 	KASSERT(dvp != NULL);
    701      1.1  riastrad 	KASSERT(cnp != NULL);
    702      1.1  riastrad 	KASSERT(ulr != NULL);
    703      1.1  riastrad 	KASSERT(vp != NULL);
    704      1.1  riastrad 	KASSERT(dvp != vp);
    705      1.1  riastrad 	KASSERT(dvp->v_mount == mp);
    706      1.1  riastrad 	KASSERT(vp->v_mount == mp);
    707      1.1  riastrad 	KASSERT(dvp->v_type == VDIR);
    708      1.1  riastrad 	KASSERT(vp->v_type != VDIR);
    709      1.1  riastrad 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
    710      1.1  riastrad 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    711      1.1  riastrad 
    712      1.1  riastrad 	error = ext2fs_dirremove(dvp, ulr, cnp);
    713      1.1  riastrad 	if (error)
    714      1.1  riastrad 		return error;
    715      1.1  riastrad 
    716      1.1  riastrad 	KASSERT(0 < VTOI(vp)->i_e2fs_nlink);
    717      1.1  riastrad 	VTOI(vp)->i_e2fs_nlink--;
    718      1.1  riastrad 	VTOI(vp)->i_flag |= IN_CHANGE;
    719      1.1  riastrad 
    720      1.1  riastrad 	VN_KNOTE(dvp, NOTE_WRITE);
    721      1.1  riastrad 	VN_KNOTE(vp, (VTOI(vp)->i_e2fs_nlink? NOTE_LINK : NOTE_DELETE));
    722      1.1  riastrad 
    723      1.1  riastrad 	return 0;
    724      1.1  riastrad }
    725      1.1  riastrad 
    726      1.1  riastrad /*
    727      1.1  riastrad  * ext2fs_gro_lookup: Look up and save the lookup results.
    728      1.1  riastrad  */
    729      1.1  riastrad static int
    730      1.1  riastrad ext2fs_gro_lookup(struct mount *mp, struct vnode *dvp,
    731      1.1  riastrad     struct componentname *cnp, void *de_ret, struct vnode **vp_ret)
    732      1.1  riastrad {
    733      1.1  riastrad 	struct ufs_lookup_results *ulr_ret = de_ret;
    734      1.1  riastrad 	struct vnode *vp;
    735      1.1  riastrad 	int error;
    736      1.1  riastrad 
    737      1.1  riastrad 	(void)mp;
    738      1.1  riastrad 	KASSERT(mp != NULL);
    739      1.1  riastrad 	KASSERT(dvp != NULL);
    740      1.1  riastrad 	KASSERT(cnp != NULL);
    741      1.1  riastrad 	KASSERT(ulr_ret != NULL);
    742      1.1  riastrad 	KASSERT(vp_ret != NULL);
    743      1.1  riastrad 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
    744      1.1  riastrad 
    745      1.1  riastrad 	/* Kludge cargo-culted from dholland's ufs_rename.  */
    746      1.1  riastrad 	cnp->cn_flags &=~ MODMASK;
    747      1.1  riastrad 	cnp->cn_flags |= (LOCKPARENT | LOCKLEAF);
    748      1.1  riastrad 
    749      1.1  riastrad 	error = relookup(dvp, &vp, cnp, 0 /* dummy */);
    750      1.1  riastrad 	if ((error == 0) && (vp == NULL)) {
    751      1.1  riastrad 		error = ENOENT;
    752      1.1  riastrad 		goto out;
    753      1.1  riastrad 	} else if (error) {
    754      1.1  riastrad 		return error;
    755      1.1  riastrad 	}
    756      1.1  riastrad 
    757      1.1  riastrad 	/*
    758      1.1  riastrad 	 * Thanks to VFS insanity, relookup locks vp, which screws us
    759      1.1  riastrad 	 * in various ways.
    760      1.1  riastrad 	 */
    761      1.1  riastrad 	KASSERT(vp != NULL);
    762      1.1  riastrad 	VOP_UNLOCK(vp);
    763      1.1  riastrad 
    764      1.1  riastrad out:	*ulr_ret = VTOI(dvp)->i_crap;
    765      1.1  riastrad 	*vp_ret = vp;
    766      1.1  riastrad 	return error;
    767      1.1  riastrad }
    768      1.1  riastrad 
    769      1.1  riastrad /*
    770      1.1  riastrad  * ext2fs_rmdired_p: Check whether the directory vp has been rmdired.
    771      1.1  riastrad  *
    772      1.1  riastrad  * vp must be locked and referenced.
    773      1.1  riastrad  */
    774      1.1  riastrad static bool
    775      1.1  riastrad ext2fs_rmdired_p(struct vnode *vp)
    776      1.1  riastrad {
    777      1.1  riastrad 
    778      1.1  riastrad 	KASSERT(vp != NULL);
    779      1.1  riastrad 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    780      1.1  riastrad 	KASSERT(vp->v_type == VDIR);
    781      1.1  riastrad 
    782      1.1  riastrad 	/* XXX Is this correct?  */
    783      1.1  riastrad 	return (ext2fs_size(VTOI(vp)) == 0);
    784      1.1  riastrad }
    785      1.1  riastrad 
    786      1.1  riastrad /*
    787      1.1  riastrad  * ext2fs_gro_genealogy: Analyze the genealogy of the source and target
    788      1.1  riastrad  * directories.
    789      1.1  riastrad  */
    790      1.1  riastrad static int
    791      1.1  riastrad ext2fs_gro_genealogy(struct mount *mp, kauth_cred_t cred,
    792      1.1  riastrad     struct vnode *fdvp, struct vnode *tdvp,
    793      1.1  riastrad     struct vnode **intermediate_node_ret)
    794      1.1  riastrad {
    795      1.1  riastrad 	struct vnode *vp, *dvp;
    796  1.5.2.1     rmind 	ino_t dotdot_ino = -1;	/* XXX gcc 4.8.3: maybe-uninitialized */
    797      1.1  riastrad 	int error;
    798      1.1  riastrad 
    799      1.1  riastrad 	KASSERT(mp != NULL);
    800      1.1  riastrad 	KASSERT(fdvp != NULL);
    801      1.1  riastrad 	KASSERT(tdvp != NULL);
    802      1.1  riastrad 	KASSERT(fdvp != tdvp);
    803      1.1  riastrad 	KASSERT(intermediate_node_ret != NULL);
    804      1.1  riastrad 	KASSERT(fdvp->v_mount == mp);
    805      1.1  riastrad 	KASSERT(tdvp->v_mount == mp);
    806      1.1  riastrad 	KASSERT(fdvp->v_type == VDIR);
    807      1.1  riastrad 	KASSERT(tdvp->v_type == VDIR);
    808      1.1  riastrad 
    809      1.1  riastrad 	/*
    810      1.1  riastrad 	 * We need to provisionally lock tdvp to keep rmdir from
    811      1.1  riastrad 	 * deleting it -- or any ancestor -- at an inopportune moment.
    812      1.1  riastrad 	 */
    813      1.1  riastrad 	error = ext2fs_gro_lock_directory(mp, tdvp);
    814      1.1  riastrad 	if (error)
    815      1.1  riastrad 		return error;
    816      1.1  riastrad 
    817      1.1  riastrad 	vp = tdvp;
    818      1.1  riastrad 	vref(vp);
    819      1.1  riastrad 
    820      1.1  riastrad 	for (;;) {
    821      1.1  riastrad 		KASSERT(vp != NULL);
    822      1.1  riastrad 		KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    823      1.1  riastrad 		KASSERT(vp->v_mount == mp);
    824      1.1  riastrad 		KASSERT(vp->v_type == VDIR);
    825      1.1  riastrad 		KASSERT(!ext2fs_rmdired_p(vp));
    826      1.1  riastrad 
    827      1.1  riastrad 		/* Did we hit the root without finding fdvp?  */
    828      1.5  dholland 		if (VTOI(vp)->i_number == UFS_ROOTINO) {
    829      1.1  riastrad 			vput(vp);
    830      1.1  riastrad 			*intermediate_node_ret = NULL;
    831      1.1  riastrad 			return 0;
    832      1.1  riastrad 		}
    833      1.1  riastrad 
    834      1.1  riastrad 		error = ext2fs_read_dotdot(vp, cred, &dotdot_ino);
    835      1.1  riastrad 		if (error) {
    836      1.1  riastrad 			vput(vp);
    837      1.1  riastrad 			return error;
    838      1.1  riastrad 		}
    839      1.1  riastrad 
    840      1.1  riastrad 		/* Did we find that fdvp is an ancestor of tdvp?  */
    841      1.1  riastrad 		if (VTOI(fdvp)->i_number == dotdot_ino) {
    842      1.1  riastrad 			/* Unlock vp, but keep it referenced.  */
    843      1.1  riastrad 			VOP_UNLOCK(vp);
    844      1.1  riastrad 			*intermediate_node_ret = vp;
    845      1.1  riastrad 			return 0;
    846      1.1  riastrad 		}
    847      1.1  riastrad 
    848      1.1  riastrad 		/* Neither -- keep ascending the family tree.  */
    849      1.1  riastrad 
    850      1.1  riastrad 		/*
    851      1.1  riastrad 		 * Unlock vp so that we can lock the parent, but keep
    852      1.1  riastrad 		 * vp referenced until after we have found the parent,
    853      1.1  riastrad 		 * so that dotdot_ino will not be recycled.
    854      1.1  riastrad 		 *
    855      1.1  riastrad 		 * XXX This guarantees that vp's inode number will not
    856      1.1  riastrad 		 * be recycled, but why can't dotdot_ino be recycled?
    857      1.1  riastrad 		 */
    858      1.1  riastrad 		VOP_UNLOCK(vp);
    859      1.1  riastrad 		error = VFS_VGET(mp, dotdot_ino, &dvp);
    860      1.1  riastrad 		vrele(vp);
    861      1.1  riastrad 		if (error)
    862      1.1  riastrad 			return error;
    863      1.1  riastrad 
    864      1.1  riastrad 		KASSERT(dvp != NULL);
    865      1.1  riastrad 		KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
    866      1.1  riastrad 		vp = dvp;
    867      1.1  riastrad 
    868      1.1  riastrad 		if (vp->v_type != VDIR) {
    869      1.1  riastrad 			/*
    870      1.1  riastrad 			 * XXX Panic?  Print a warning?  Can this
    871      1.1  riastrad 			 * happen if we lose the race I suspect to
    872      1.1  riastrad 			 * exist above, and the `..' inode number has
    873      1.1  riastrad 			 * been recycled?
    874      1.1  riastrad 			 */
    875      1.1  riastrad 			vput(vp);
    876      1.1  riastrad 			return ENOTDIR;
    877      1.1  riastrad 		}
    878      1.1  riastrad 
    879      1.1  riastrad 		if (ext2fs_rmdired_p(vp)) {
    880      1.1  riastrad 			vput(vp);
    881      1.1  riastrad 			return ENOENT;
    882      1.1  riastrad 		}
    883      1.1  riastrad 	}
    884      1.1  riastrad }
    885      1.1  riastrad 
    886      1.1  riastrad /*
    887      1.1  riastrad  * ext2fs_read_dotdot: Store in *ino_ret the inode number of the parent
    888      1.1  riastrad  * of the directory vp.
    889      1.1  riastrad  */
    890      1.1  riastrad static int
    891      1.1  riastrad ext2fs_read_dotdot(struct vnode *vp, kauth_cred_t cred, ino_t *ino_ret)
    892      1.1  riastrad {
    893      1.1  riastrad 	struct ext2fs_dirtemplate dirbuf;
    894      1.1  riastrad 	int error;
    895      1.1  riastrad 
    896      1.1  riastrad 	KASSERT(vp != NULL);
    897      1.1  riastrad 	KASSERT(ino_ret != NULL);
    898      1.1  riastrad 	KASSERT(vp->v_type == VDIR);
    899      1.1  riastrad 
    900      1.1  riastrad 	error = vn_rdwr(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0,
    901      1.1  riastrad 	    UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, NULL);
    902      1.1  riastrad 	if (error)
    903      1.1  riastrad 		return error;
    904      1.1  riastrad 
    905      1.1  riastrad 	if (dirbuf.dotdot_namlen != 2 ||
    906      1.1  riastrad 	    dirbuf.dotdot_name[0] != '.' ||
    907      1.1  riastrad 	    dirbuf.dotdot_name[1] != '.')
    908      1.1  riastrad 		/* XXX Panic?  Print warning?  */
    909      1.1  riastrad 		return ENOTDIR;
    910      1.1  riastrad 
    911      1.1  riastrad 	*ino_ret = fs2h32(dirbuf.dotdot_ino);
    912      1.1  riastrad 	return 0;
    913      1.1  riastrad }
    914      1.1  riastrad 
    915      1.1  riastrad /*
    916      1.1  riastrad  * ext2fs_rename_replace_dotdot: Change the target of the `..' entry of
    917      1.1  riastrad  * the directory vp from fdvp to tdvp.
    918      1.1  riastrad  */
    919      1.1  riastrad static int
    920      1.1  riastrad ext2fs_rename_replace_dotdot(struct vnode *vp,
    921      1.1  riastrad     struct vnode *fdvp, struct vnode *tdvp,
    922      1.1  riastrad     kauth_cred_t cred)
    923      1.1  riastrad {
    924      1.1  riastrad 	struct ext2fs_dirtemplate dirbuf;
    925      1.1  riastrad 	int error;
    926      1.1  riastrad 
    927      1.1  riastrad 	/* XXX Does it make sense to do this before the sanity checks below?  */
    928      1.1  riastrad 	KASSERT(0 < VTOI(fdvp)->i_e2fs_nlink);
    929      1.1  riastrad 	VTOI(fdvp)->i_e2fs_nlink--;
    930      1.1  riastrad 	VTOI(fdvp)->i_flag |= IN_CHANGE;
    931      1.1  riastrad 
    932      1.1  riastrad 	error = vn_rdwr(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0,
    933      1.1  riastrad 	    UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, NULL);
    934      1.1  riastrad 	if (error)
    935      1.1  riastrad 		return error;
    936      1.1  riastrad 
    937      1.1  riastrad 	if (dirbuf.dotdot_namlen != 2 ||
    938      1.1  riastrad 	    dirbuf.dotdot_name[0] != '.' ||
    939      1.1  riastrad 	    dirbuf.dotdot_name[1] != '.') {
    940      1.1  riastrad 		ufs_dirbad(VTOI(vp), (doff_t)12, "bad `..' entry");
    941      1.1  riastrad 		return 0;
    942      1.1  riastrad 	}
    943      1.1  riastrad 
    944      1.1  riastrad 	if (fs2h32(dirbuf.dotdot_ino) != VTOI(fdvp)->i_number) {
    945      1.1  riastrad 		ufs_dirbad(VTOI(vp), (doff_t)12,
    946      1.1  riastrad 		    "`..' does not point at parent");
    947      1.1  riastrad 		return 0;
    948      1.1  riastrad 	}
    949      1.1  riastrad 
    950      1.1  riastrad 	dirbuf.dotdot_ino = h2fs32(VTOI(tdvp)->i_number);
    951      1.1  riastrad 	/* XXX WTF?  Why not check error?  */
    952      1.1  riastrad 	(void)vn_rdwr(UIO_WRITE, vp, &dirbuf, sizeof dirbuf, (off_t)0,
    953      1.1  riastrad 	    UIO_SYSSPACE, (IO_NODELOCKED | IO_SYNC), cred, NULL, NULL);
    954      1.1  riastrad 
    955      1.1  riastrad 	return 0;
    956      1.1  riastrad }
    957      1.1  riastrad 
    958      1.1  riastrad /*
    959      1.1  riastrad  * ext2fs_gro_lock_directory: Lock the directory vp, but fail if it has
    960      1.1  riastrad  * been rmdir'd.
    961      1.1  riastrad  */
    962      1.1  riastrad static int
    963      1.1  riastrad ext2fs_gro_lock_directory(struct mount *mp, struct vnode *vp)
    964      1.1  riastrad {
    965      1.1  riastrad 
    966      1.1  riastrad 	(void)mp;
    967      1.1  riastrad 	KASSERT(mp != NULL);
    968      1.1  riastrad 	KASSERT(vp != NULL);
    969      1.1  riastrad 	KASSERT(vp->v_mount == mp);
    970      1.1  riastrad 
    971      1.1  riastrad 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
    972      1.1  riastrad 
    973      1.1  riastrad 	if (ext2fs_rmdired_p(vp)) {
    974      1.1  riastrad 		VOP_UNLOCK(vp);
    975      1.1  riastrad 		return ENOENT;
    976      1.1  riastrad 	}
    977      1.1  riastrad 
    978      1.1  riastrad 	return 0;
    979      1.1  riastrad }
    980      1.1  riastrad 
    981      1.1  riastrad static const struct genfs_rename_ops ext2fs_genfs_rename_ops = {
    982      1.1  riastrad 	.gro_directory_empty_p		= ext2fs_gro_directory_empty_p,
    983      1.1  riastrad 	.gro_rename_check_possible	= ext2fs_gro_rename_check_possible,
    984      1.1  riastrad 	.gro_rename_check_permitted	= ext2fs_gro_rename_check_permitted,
    985      1.1  riastrad 	.gro_remove_check_possible	= ext2fs_gro_remove_check_possible,
    986      1.1  riastrad 	.gro_remove_check_permitted	= ext2fs_gro_remove_check_permitted,
    987      1.1  riastrad 	.gro_rename			= ext2fs_gro_rename,
    988      1.1  riastrad 	.gro_remove			= ext2fs_gro_remove,
    989      1.1  riastrad 	.gro_lookup			= ext2fs_gro_lookup,
    990      1.1  riastrad 	.gro_genealogy			= ext2fs_gro_genealogy,
    991      1.1  riastrad 	.gro_lock_directory		= ext2fs_gro_lock_directory,
    992      1.1  riastrad };
    993