Home | History | Annotate | Line # | Download | only in udf
      1  1.16  thorpej /* $NetBSD: udf_rename.c,v 1.16 2024/05/18 00:04:01 thorpej Exp $ */
      2   1.1  reinoud 
      3   1.1  reinoud /*
      4   1.1  reinoud  * Copyright (c) 2013 Reinoud Zandijk
      5   1.1  reinoud  * All rights reserved.
      6   1.1  reinoud  *
      7   1.1  reinoud  * Redistribution and use in source and binary forms, with or without
      8   1.1  reinoud  * modification, are permitted provided that the following conditions
      9   1.1  reinoud  * are met:
     10   1.1  reinoud  * 1. Redistributions of source code must retain the above copyright
     11   1.1  reinoud  *    notice, this list of conditions and the following disclaimer.
     12   1.1  reinoud  * 2. Redistributions in binary form must reproduce the above copyright
     13   1.1  reinoud  *    notice, this list of conditions and the following disclaimer in the
     14   1.1  reinoud  *    documentation and/or other materials provided with the distribution.
     15   1.1  reinoud  *
     16   1.1  reinoud  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17   1.1  reinoud  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18   1.1  reinoud  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19   1.1  reinoud  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20   1.1  reinoud  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21   1.1  reinoud  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22   1.1  reinoud  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23   1.1  reinoud  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24   1.1  reinoud  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25   1.1  reinoud  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26   1.1  reinoud  *
     27   1.1  reinoud  * Comments and trivial code from the reference implementation in tmpfs.
     28   1.1  reinoud  */
     29   1.1  reinoud 
     30   1.1  reinoud #include <sys/cdefs.h>
     31  1.16  thorpej __KERNEL_RCSID(0, "$NetBSD: udf_rename.c,v 1.16 2024/05/18 00:04:01 thorpej Exp $");
     32   1.1  reinoud 
     33   1.1  reinoud #include <sys/param.h>
     34   1.1  reinoud #include <sys/errno.h>
     35   1.1  reinoud #include <sys/kauth.h>
     36   1.1  reinoud #include <sys/mount.h>
     37   1.1  reinoud #include <sys/namei.h>
     38   1.1  reinoud #include <sys/stat.h>
     39   1.1  reinoud #include <sys/dirent.h>
     40   1.1  reinoud #include <sys/vnode.h>
     41   1.1  reinoud #include <sys/vnode_if.h>
     42   1.1  reinoud 
     43   1.1  reinoud #include <miscfs/genfs/genfs.h>
     44   1.1  reinoud 
     45   1.1  reinoud #include <fs/udf/ecma167-udf.h>
     46   1.1  reinoud #include <fs/udf/udf_mount.h>
     47   1.1  reinoud #include <sys/dirhash.h>
     48   1.1  reinoud 
     49   1.1  reinoud #include "udf.h"
     50   1.1  reinoud #include "udf_subr.h"
     51   1.1  reinoud #include "udf_bswap.h"
     52   1.1  reinoud 
     53   1.1  reinoud 
     54   1.1  reinoud /* forwards */
     55   1.1  reinoud static int udf_sane_rename( struct vnode *, struct componentname *,
     56   1.1  reinoud     struct vnode *, struct componentname *,
     57   1.1  reinoud     kauth_cred_t, bool);
     58   1.1  reinoud static bool udf_rmdired_p(struct vnode *);
     59   1.1  reinoud static int udf_gro_lock_directory(struct mount *, struct vnode *);
     60   1.1  reinoud 
     61   1.1  reinoud static const struct genfs_rename_ops udf_genfs_rename_ops;
     62   1.1  reinoud 
     63   1.1  reinoud 
     64   1.1  reinoud #define VTOI(vnode) ((struct udf_node *) (vnode)->v_data)
     65   1.1  reinoud 
     66   1.1  reinoud 
     67   1.1  reinoud /*
     68   1.1  reinoud  * udf_sane_rename: The hairiest vop, with the saner API.
     69   1.1  reinoud  *
     70   1.1  reinoud  * Arguments:
     71   1.1  reinoud  *
     72   1.1  reinoud  * . fdvp (from directory vnode),
     73   1.1  reinoud  * . fcnp (from component name),
     74   1.1  reinoud  * . tdvp (to directory vnode),
     75   1.1  reinoud  * . tcnp (to component name),
     76   1.1  reinoud  * . cred (credentials structure), and
     77   1.1  reinoud  * . posixly_correct (flag for behaviour if target & source link same file).
     78   1.1  reinoud  *
     79   1.1  reinoud  * fdvp and tdvp may be the same, and must be referenced and unlocked.
     80   1.1  reinoud  */
     81   1.1  reinoud static int
     82   1.1  reinoud udf_sane_rename( struct vnode *fdvp, struct componentname *fcnp,
     83   1.1  reinoud     struct vnode *tdvp, struct componentname *tcnp,
     84   1.1  reinoud     kauth_cred_t cred, bool posixly_correct)
     85   1.1  reinoud {
     86   1.1  reinoud 	DPRINTF(CALL, ("udf_sane_rename '%s' -> '%s'\n",
     87   1.1  reinoud 		fcnp->cn_nameptr, tcnp->cn_nameptr));
     88   1.1  reinoud 	return genfs_sane_rename(&udf_genfs_rename_ops,
     89   1.3  reinoud 	    fdvp, fcnp, NULL, tdvp, tcnp, NULL,
     90   1.1  reinoud 	    cred, posixly_correct);
     91   1.1  reinoud }
     92   1.1  reinoud 
     93   1.1  reinoud 
     94   1.1  reinoud /*
     95   1.1  reinoud  * udf_rename: the hairiest vop, with the insanest API. Pass to
     96   1.1  reinoud  * genfs_insane_rename immediately.
     97   1.1  reinoud  */
     98   1.1  reinoud int
     99   1.1  reinoud udf_rename(void *v)
    100   1.1  reinoud {
    101   1.1  reinoud 	struct vop_rename_args /* {
    102   1.1  reinoud 		struct vnode *a_fdvp;
    103   1.1  reinoud 		struct vnode *a_fvp;
    104   1.1  reinoud 		struct componentname *a_fcnp;
    105   1.1  reinoud 		struct vnode *a_tdvp;
    106   1.1  reinoud 		struct vnode *a_tvp;
    107   1.1  reinoud 		struct componentname *a_tcnp;
    108   1.1  reinoud 	} */ *ap = v;
    109   1.1  reinoud 	DPRINTF(CALL, ("udf_rename called\n"));
    110   1.1  reinoud 	return genfs_insane_rename(ap, &udf_sane_rename);
    111   1.1  reinoud }
    112   1.1  reinoud 
    113   1.1  reinoud 
    114   1.1  reinoud /*
    115   1.1  reinoud  * udf_gro_directory_empty_p: return true if the directory vp is empty. dvp is
    116   1.1  reinoud  * its parent.
    117   1.1  reinoud  *
    118   1.1  reinoud  * vp and dvp must be locked and referenced.
    119   1.1  reinoud  */
    120   1.1  reinoud static bool
    121   1.1  reinoud udf_gro_directory_empty_p(struct mount *mp, kauth_cred_t cred,
    122   1.1  reinoud     struct vnode *vp, struct vnode *dvp)
    123   1.1  reinoud {
    124   1.1  reinoud 	struct udf_node *udf_node = VTOI(vp);
    125   1.1  reinoud 	int error, isempty;
    126   1.1  reinoud 
    127   1.1  reinoud 	KASSERT(mp != NULL);
    128   1.1  reinoud 	KASSERT(vp != NULL);
    129   1.1  reinoud 	KASSERT(dvp != NULL);
    130   1.1  reinoud 	KASSERT(vp != dvp);
    131   1.1  reinoud 	KASSERT(vp->v_mount == mp);
    132   1.1  reinoud 	KASSERT(dvp->v_mount == mp);
    133   1.1  reinoud 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    134   1.1  reinoud 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
    135   1.1  reinoud 
    136   1.1  reinoud 	DPRINTF(CALL, ("udf_gro_directory_empty_p called\n"));
    137   1.1  reinoud 	/* make sure our `leaf' node's hash is populated */
    138   1.1  reinoud 	dirhash_get(&udf_node->dir_hash);
    139   1.1  reinoud 	error = udf_dirhash_fill(udf_node);
    140   1.1  reinoud 	if (error) {
    141   1.1  reinoud 		dirhash_put(udf_node->dir_hash);
    142   1.5  reinoud 		/* VERY unlikely, answer its not empty */
    143   1.5  reinoud 		return 0;
    144   1.1  reinoud 	}
    145   1.1  reinoud 
    146   1.1  reinoud 	/* check to see if the directory is empty */
    147   1.1  reinoud 	isempty = dirhash_dir_isempty(udf_node->dir_hash);
    148   1.1  reinoud 	dirhash_put(udf_node->dir_hash);
    149   1.1  reinoud 
    150   1.1  reinoud 	return isempty;
    151   1.1  reinoud }
    152   1.1  reinoud 
    153   1.1  reinoud 
    154   1.1  reinoud /*
    155   1.1  reinoud  * udf_gro_rename_check_possible: check whether a rename is possible
    156   1.1  reinoud  * independent of credentials.
    157   1.1  reinoud  */
    158   1.1  reinoud static int
    159   1.1  reinoud udf_gro_rename_check_possible(struct mount *mp,
    160   1.1  reinoud     struct vnode *fdvp, struct vnode *fvp,
    161   1.1  reinoud     struct vnode *tdvp, struct vnode *tvp)
    162   1.1  reinoud {
    163   1.1  reinoud 	(void)mp;
    164   1.1  reinoud 	KASSERT(mp != NULL);
    165   1.1  reinoud 	KASSERT(fdvp != NULL);
    166   1.1  reinoud 	KASSERT(fvp != NULL);
    167   1.1  reinoud 	KASSERT(tdvp != NULL);
    168   1.1  reinoud 	KASSERT(fdvp != fvp);
    169   1.1  reinoud 	KASSERT(fdvp != tvp);
    170   1.1  reinoud 	KASSERT(tdvp != fvp);
    171   1.1  reinoud 	KASSERT(tdvp != tvp);
    172   1.1  reinoud 	KASSERT(fvp != tvp);
    173   1.1  reinoud 	KASSERT(fdvp->v_type == VDIR);
    174   1.1  reinoud 	KASSERT(tdvp->v_type == VDIR);
    175   1.1  reinoud 	KASSERT(fdvp->v_mount == mp);
    176   1.1  reinoud 	KASSERT(fvp->v_mount == mp);
    177   1.1  reinoud 	KASSERT(tdvp->v_mount == mp);
    178   1.1  reinoud 	KASSERT((tvp == NULL) || (tvp->v_mount == mp));
    179   1.1  reinoud 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
    180   1.1  reinoud 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
    181   1.1  reinoud 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
    182   1.1  reinoud 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
    183   1.1  reinoud 
    184   1.1  reinoud 	DPRINTF(CALL, ("udf_gro_rename_check_possible called\n"));
    185   1.1  reinoud 
    186   1.1  reinoud 	/* flags not implemented since they are not defined (yet) in UDF */
    187   1.1  reinoud 	return 0;
    188   1.1  reinoud }
    189   1.1  reinoud 
    190   1.1  reinoud 
    191   1.1  reinoud /*
    192   1.1  reinoud  * udf_gro_rename_check_permitted: check whether a rename is permitted given
    193   1.1  reinoud  * our credentials.
    194   1.1  reinoud  */
    195   1.1  reinoud static int
    196   1.1  reinoud udf_gro_rename_check_permitted(struct mount *mp, kauth_cred_t cred,
    197   1.1  reinoud     struct vnode *fdvp, struct vnode *fvp,
    198   1.1  reinoud     struct vnode *tdvp, struct vnode *tvp)
    199   1.1  reinoud {
    200   1.1  reinoud 	struct udf_node *fdir_node = VTOI(fdvp);
    201   1.1  reinoud 	struct udf_node *tdir_node = VTOI(tdvp);
    202   1.1  reinoud 	struct udf_node *f_node = VTOI(fvp);
    203   1.1  reinoud 	struct udf_node *t_node = (tvp? VTOI(tvp): NULL);
    204   1.1  reinoud 	mode_t fdmode, tdmode;
    205   1.1  reinoud 	uid_t fduid, tduid, fuid, tuid;
    206   1.1  reinoud 	gid_t gdummy;
    207   1.1  reinoud 
    208   1.1  reinoud 	(void)mp;
    209   1.1  reinoud 	KASSERT(mp != NULL);
    210   1.1  reinoud 	KASSERT(fdvp != NULL);
    211   1.1  reinoud 	KASSERT(fvp != NULL);
    212   1.1  reinoud 	KASSERT(tdvp != NULL);
    213   1.1  reinoud 	KASSERT(fdvp != fvp);
    214   1.1  reinoud 	KASSERT(fdvp != tvp);
    215   1.1  reinoud 	KASSERT(tdvp != fvp);
    216   1.1  reinoud 	KASSERT(tdvp != tvp);
    217   1.1  reinoud 	KASSERT(fvp != tvp);
    218   1.1  reinoud 	KASSERT(fdvp->v_type == VDIR);
    219   1.1  reinoud 	KASSERT(tdvp->v_type == VDIR);
    220   1.1  reinoud 	KASSERT(fdvp->v_mount == mp);
    221   1.1  reinoud 	KASSERT(fvp->v_mount == mp);
    222   1.1  reinoud 	KASSERT(tdvp->v_mount == mp);
    223   1.1  reinoud 	KASSERT((tvp == NULL) || (tvp->v_mount == mp));
    224   1.1  reinoud 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
    225   1.1  reinoud 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
    226   1.1  reinoud 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
    227   1.1  reinoud 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
    228   1.1  reinoud 
    229   1.1  reinoud 	DPRINTF(CALL, ("udf_gro_rename_check_permitted called\n"));
    230   1.1  reinoud 	fdmode = udf_getaccessmode(fdir_node);
    231   1.1  reinoud 	tdmode = udf_getaccessmode(tdir_node);
    232   1.1  reinoud 
    233   1.1  reinoud 	udf_getownership(fdir_node, &fduid, &gdummy);
    234   1.1  reinoud 	udf_getownership(tdir_node, &tduid, &gdummy);
    235   1.1  reinoud 	udf_getownership(f_node,    &fuid, &gdummy);
    236   1.1  reinoud 
    237   1.1  reinoud 	tuid = 0;
    238   1.1  reinoud 	if (t_node)
    239   1.1  reinoud 		udf_getownership(t_node, &tuid, &gdummy);
    240   1.1  reinoud 
    241   1.1  reinoud 	return genfs_ufslike_rename_check_permitted(cred,
    242   1.1  reinoud 	    fdvp, fdmode, fduid,
    243   1.1  reinoud 	    fvp,  fuid,
    244   1.1  reinoud 	    tdvp, tdmode, tduid,
    245   1.1  reinoud 	    tvp,  tuid);
    246   1.1  reinoud }
    247   1.1  reinoud 
    248   1.1  reinoud 
    249   1.1  reinoud /*
    250   1.1  reinoud  * udf_gro_remove_check_possible: check whether a remove is possible
    251   1.1  reinoud  * independent of credentials.
    252   1.1  reinoud  *
    253   1.1  reinoud  * XXX could check for special attributes?
    254   1.1  reinoud  */
    255   1.1  reinoud static int
    256   1.1  reinoud udf_gro_remove_check_possible(struct mount *mp,
    257   1.1  reinoud     struct vnode *dvp, struct vnode *vp)
    258   1.1  reinoud {
    259   1.1  reinoud 	(void)mp;
    260   1.1  reinoud 	KASSERT(mp != NULL);
    261   1.1  reinoud 	KASSERT(dvp != NULL);
    262   1.1  reinoud 	KASSERT(vp != NULL);
    263   1.1  reinoud 	KASSERT(dvp != vp);
    264   1.1  reinoud 	KASSERT(dvp->v_type == VDIR);
    265   1.1  reinoud 	KASSERT(vp->v_type != VDIR);
    266   1.1  reinoud 	KASSERT(dvp->v_mount == mp);
    267   1.1  reinoud 	KASSERT(vp->v_mount == mp);
    268   1.1  reinoud 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
    269   1.1  reinoud 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    270   1.1  reinoud 
    271   1.1  reinoud 	DPRINTF(CALL, ("udf_gro_remove_check_possible called\n"));
    272   1.1  reinoud 
    273   1.1  reinoud 	/* flags not implemented since they are not defined (yet) in UDF */
    274   1.1  reinoud 	return 0;
    275   1.1  reinoud }
    276   1.1  reinoud 
    277   1.1  reinoud 
    278   1.1  reinoud /*
    279   1.1  reinoud  * udf_gro_remove_check_permitted: check whether a remove is permitted given
    280   1.1  reinoud  * our credentials.
    281   1.1  reinoud  */
    282   1.1  reinoud static int
    283   1.1  reinoud udf_gro_remove_check_permitted(struct mount *mp, kauth_cred_t cred,
    284   1.1  reinoud     struct vnode *dvp, struct vnode *vp)
    285   1.1  reinoud {
    286   1.1  reinoud 	struct udf_node *dir_node = VTOI(dvp);
    287   1.1  reinoud 	struct udf_node *udf_node = VTOI(vp);
    288   1.1  reinoud 	mode_t dmode;
    289   1.1  reinoud 	uid_t duid, uid;
    290   1.1  reinoud 	gid_t gdummy;
    291   1.1  reinoud 
    292   1.1  reinoud 	(void)mp;
    293   1.1  reinoud 	KASSERT(mp != NULL);
    294   1.1  reinoud 	KASSERT(dvp != NULL);
    295   1.1  reinoud 	KASSERT(vp != NULL);
    296   1.1  reinoud 	KASSERT(dvp != vp);
    297   1.1  reinoud 	KASSERT(dvp->v_type == VDIR);
    298   1.1  reinoud 	KASSERT(vp->v_type != VDIR);
    299   1.1  reinoud 	KASSERT(dvp->v_mount == mp);
    300   1.1  reinoud 	KASSERT(vp->v_mount == mp);
    301   1.1  reinoud 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
    302   1.1  reinoud 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    303   1.1  reinoud 
    304   1.1  reinoud 	DPRINTF(CALL, ("udf_gro_remove_check_permitted called\n"));
    305   1.1  reinoud 	dmode = udf_getaccessmode(dir_node);
    306   1.1  reinoud 
    307   1.1  reinoud 	udf_getownership(dir_node, &duid, &gdummy);
    308   1.1  reinoud 	udf_getownership(udf_node, &uid,  &gdummy);
    309   1.1  reinoud 
    310   1.1  reinoud 	return genfs_ufslike_remove_check_permitted(cred,
    311   1.1  reinoud 	    dvp, dmode, duid,
    312   1.1  reinoud 	    vp, uid);
    313   1.1  reinoud }
    314   1.1  reinoud 
    315   1.1  reinoud 
    316   1.1  reinoud /*
    317   1.1  reinoud  * udf_gro_rename: actually perform the rename operation.
    318   1.1  reinoud  */
    319   1.1  reinoud static int
    320   1.1  reinoud udf_gro_rename(struct mount *mp, kauth_cred_t cred,
    321   1.1  reinoud     struct vnode *fdvp, struct componentname *fcnp,
    322   1.1  reinoud     void *fde, struct vnode *fvp,
    323   1.1  reinoud     struct vnode *tdvp, struct componentname *tcnp,
    324  1.14  thorpej     void *tde, struct vnode *tvp, nlink_t *tvp_nlinkp)
    325   1.1  reinoud {
    326   1.1  reinoud 	struct udf_node *fnode, *fdnode, *tnode, *tdnode;
    327   1.1  reinoud 	struct vattr fvap;
    328   1.1  reinoud 	int error;
    329   1.1  reinoud 
    330   1.1  reinoud 	(void)cred;
    331   1.1  reinoud 	KASSERT(mp != NULL);
    332   1.1  reinoud 	KASSERT(fdvp != NULL);
    333   1.1  reinoud 	KASSERT(fcnp != NULL);
    334   1.1  reinoud 	KASSERT(fvp != NULL);
    335   1.1  reinoud 	KASSERT(tdvp != NULL);
    336   1.1  reinoud 	KASSERT(tcnp != NULL);
    337   1.1  reinoud 	KASSERT(fdvp != fvp);
    338   1.1  reinoud 	KASSERT(fdvp != tvp);
    339   1.1  reinoud 	KASSERT(tdvp != fvp);
    340   1.1  reinoud 	KASSERT(tdvp != tvp);
    341   1.1  reinoud 	KASSERT(fvp != tvp);
    342   1.1  reinoud 	KASSERT(fdvp->v_mount == mp);
    343   1.1  reinoud 	KASSERT(fvp->v_mount == mp);
    344   1.1  reinoud 	KASSERT(tdvp->v_mount == mp);
    345   1.1  reinoud 	KASSERT((tvp == NULL) || (tvp->v_mount == mp));
    346   1.1  reinoud 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
    347   1.1  reinoud 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
    348   1.1  reinoud 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
    349   1.1  reinoud 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
    350   1.1  reinoud 
    351   1.1  reinoud 	DPRINTF(CALL, ("udf_gro_rename called\n"));
    352   1.1  reinoud 	DPRINTF(NODE, ("udf_gro_rename called : %s -> %s\n",
    353   1.1  reinoud 		fcnp->cn_nameptr, tcnp->cn_nameptr));
    354   1.1  reinoud 
    355   1.1  reinoud 	fnode  = VTOI(fvp);
    356   1.1  reinoud 	fdnode = VTOI(fdvp);
    357   1.1  reinoud 	tnode  = (tvp == NULL) ? NULL : VTOI(tvp);
    358   1.1  reinoud 	tdnode = VTOI(tdvp);
    359   1.1  reinoud 
    360   1.1  reinoud 	/* get attribute information */
    361   1.1  reinoud 	error = VOP_GETATTR(fvp, &fvap, NULL);
    362   1.1  reinoud 	if (error)
    363   1.1  reinoud 		return error;
    364   1.1  reinoud 
    365   1.1  reinoud 	/* remove existing entry if present */
    366  1.14  thorpej 	if (tvp) {
    367   1.1  reinoud 		udf_dir_detach(tdnode->ump, tdnode, tnode, tcnp);
    368  1.14  thorpej 		if (tnode->fe) {
    369  1.14  thorpej 			*tvp_nlinkp = udf_rw16(tnode->fe->link_cnt);
    370  1.14  thorpej 		} else {
    371  1.14  thorpej 			KASSERT(tnode->efe != NULL);
    372  1.14  thorpej 			*tvp_nlinkp = udf_rw16(tnode->efe->link_cnt);
    373  1.14  thorpej 		}
    374  1.14  thorpej 	}
    375   1.1  reinoud 
    376   1.1  reinoud 	/* create new directory entry for the node */
    377   1.1  reinoud 	error = udf_dir_attach(tdnode->ump, tdnode, fnode, &fvap, tcnp);
    378   1.1  reinoud 	if (error)
    379   1.1  reinoud 		return error;
    380   1.1  reinoud 
    381   1.1  reinoud 	/* unlink old directory entry for the node, if failing, unattach new */
    382   1.1  reinoud 	error = udf_dir_detach(tdnode->ump, fdnode, fnode, fcnp);
    383   1.6  reinoud 	if (error)
    384   1.6  reinoud 		goto rollback_attach;
    385   1.6  reinoud 
    386   1.6  reinoud 	if ((fdnode != tdnode) && (fvp->v_type == VDIR)) {
    387   1.1  reinoud 		/* update fnode's '..' entry */
    388   1.1  reinoud 		error = udf_dir_update_rootentry(fnode->ump, fnode, tdnode);
    389   1.6  reinoud 		if (error)
    390   1.6  reinoud 			goto rollback;
    391   1.1  reinoud 	}
    392   1.1  reinoud 
    393   1.6  reinoud 	genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp);
    394   1.6  reinoud 	return 0;
    395   1.6  reinoud 
    396   1.6  reinoud rollback:
    397   1.6  reinoud 	/* 'try' to recover from this situation */
    398   1.6  reinoud 	udf_dir_attach(tdnode->ump, fdnode, fnode, &fvap, fcnp);
    399   1.6  reinoud rollback_attach:
    400   1.6  reinoud 	udf_dir_detach(tdnode->ump, tdnode, fnode, tcnp);
    401   1.1  reinoud 
    402   1.1  reinoud 	return error;
    403   1.1  reinoud }
    404   1.1  reinoud 
    405   1.1  reinoud 
    406   1.1  reinoud /*
    407   1.1  reinoud  * udf_gro_remove: rename an object over another link to itself, effectively
    408   1.1  reinoud  * removing just the original link.
    409   1.1  reinoud  */
    410   1.1  reinoud static int
    411   1.1  reinoud udf_gro_remove(struct mount *mp, kauth_cred_t cred,
    412  1.14  thorpej     struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp,
    413  1.14  thorpej     nlink_t *tvp_nlinkp)
    414   1.1  reinoud {
    415   1.1  reinoud 	struct udf_node *dir_node, *udf_node;
    416   1.1  reinoud 
    417   1.1  reinoud 	KASSERT(mp != NULL);
    418   1.1  reinoud 	KASSERT(dvp != NULL);
    419   1.1  reinoud 	KASSERT(cnp != NULL);
    420   1.1  reinoud 	KASSERT(vp != NULL);
    421   1.1  reinoud 	KASSERT(dvp != vp);
    422   1.1  reinoud 	KASSERT(dvp->v_mount == mp);
    423   1.1  reinoud 	KASSERT(vp->v_mount == mp);
    424   1.1  reinoud 	KASSERT(dvp->v_type == VDIR);
    425   1.1  reinoud 	KASSERT(vp->v_type != VDIR);
    426   1.1  reinoud 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
    427   1.1  reinoud 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    428   1.1  reinoud 
    429   1.1  reinoud 	DPRINTF(CALL, ("udf_gro_remove called\n"));
    430   1.1  reinoud 
    431   1.1  reinoud 	dir_node = VTOI(dvp);
    432   1.1  reinoud 	udf_node = VTOI(vp);
    433   1.1  reinoud 	udf_dir_detach(dir_node->ump, dir_node, udf_node, cnp);
    434   1.1  reinoud 
    435  1.14  thorpej 	if (udf_node->fe) {
    436  1.14  thorpej 		*tvp_nlinkp = udf_rw16(udf_node->fe->link_cnt);
    437  1.14  thorpej 	} else {
    438  1.14  thorpej 		KASSERT(udf_node->efe != NULL);
    439  1.14  thorpej 		*tvp_nlinkp = udf_rw16(udf_node->efe->link_cnt);
    440  1.14  thorpej 	}
    441  1.14  thorpej 
    442   1.1  reinoud 	return 0;
    443   1.1  reinoud }
    444   1.1  reinoud 
    445   1.1  reinoud 
    446   1.1  reinoud /*
    447   1.1  reinoud  * udf_gro_lookup: look up and save the lookup results.
    448   1.1  reinoud  */
    449   1.1  reinoud static int
    450   1.1  reinoud udf_gro_lookup(struct mount *mp, struct vnode *dvp,
    451   1.1  reinoud     struct componentname *cnp, void *de_ret, struct vnode **vp_ret)
    452   1.1  reinoud {
    453   1.1  reinoud 	struct udf_node *dir_node, *res_node;
    454   1.1  reinoud 	struct long_ad   icb_loc;
    455   1.1  reinoud 	const char *name;
    456   1.1  reinoud 	int namelen, error;
    457   1.1  reinoud 	int found;
    458   1.1  reinoud 
    459   1.1  reinoud 	(void)mp;
    460   1.1  reinoud 	KASSERT(mp != NULL);
    461   1.1  reinoud 	KASSERT(dvp != NULL);
    462   1.1  reinoud 	KASSERT(cnp != NULL);
    463   1.1  reinoud 	KASSERT(vp_ret != NULL);
    464   1.1  reinoud 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
    465   1.1  reinoud 
    466   1.1  reinoud 	dir_node = VTOI(dvp);
    467   1.1  reinoud 
    468   1.1  reinoud 	DPRINTF(CALL, ("udf_gro_lookup called\n"));
    469   1.1  reinoud 
    470   1.1  reinoud 	/* lookup filename in the directory; location icb_loc */
    471   1.1  reinoud 	name    = cnp->cn_nameptr;
    472   1.1  reinoud 	namelen = cnp->cn_namelen;
    473   1.1  reinoud 	error = udf_lookup_name_in_dir(dvp, name, namelen,
    474   1.1  reinoud 			&icb_loc, &found);
    475   1.1  reinoud 	if (error)
    476   1.1  reinoud 		return error;
    477   1.1  reinoud 	if (!found)
    478   1.1  reinoud 		return ENOENT;
    479   1.1  reinoud 
    480   1.1  reinoud 	DPRINTF(LOOKUP, ("udf_gro_lookup found '%s'\n", name));
    481  1.13       ad 	error = udf_get_node(dir_node->ump, &icb_loc, &res_node, LK_EXCLUSIVE);
    482   1.1  reinoud 	if (error)
    483   1.1  reinoud 		return error;
    484   1.1  reinoud 	*vp_ret = res_node->vnode;
    485   1.1  reinoud 	VOP_UNLOCK(res_node->vnode);
    486   1.1  reinoud 
    487   1.1  reinoud 	return 0;
    488   1.1  reinoud }
    489   1.1  reinoud 
    490   1.1  reinoud 
    491   1.1  reinoud /*
    492   1.1  reinoud  * udf_rmdired_p: check whether the directory vp has been rmdired.
    493   1.1  reinoud  *
    494   1.1  reinoud  * vp must be locked and referenced.
    495   1.1  reinoud  */
    496   1.1  reinoud static bool
    497   1.1  reinoud udf_rmdired_p(struct vnode *vp)
    498   1.1  reinoud {
    499   1.1  reinoud 	DPRINTF(CALL, ("udf_rmdired_p called\n"));
    500   1.1  reinoud 
    501   1.1  reinoud 	KASSERT(vp != NULL);
    502   1.1  reinoud 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    503   1.1  reinoud 	KASSERT(vp->v_type == VDIR);
    504   1.1  reinoud 
    505   1.4  reinoud 	return (VTOI(vp)->i_flags & IN_DELETED);
    506   1.1  reinoud }
    507   1.1  reinoud 
    508   1.1  reinoud 
    509   1.1  reinoud /*
    510   1.1  reinoud  * udf_gro_genealogy: analyze the genealogy of the source and target
    511   1.1  reinoud  * directories.
    512   1.1  reinoud  */
    513   1.1  reinoud static int
    514   1.1  reinoud udf_gro_genealogy(struct mount *mp, kauth_cred_t cred,
    515   1.1  reinoud     struct vnode *fdvp, struct vnode *tdvp,
    516   1.1  reinoud     struct vnode **intermediate_node_ret)
    517   1.1  reinoud {
    518   1.1  reinoud 	struct udf_mount *ump;
    519  1.10  reinoud 	struct udf_node *parent_node;
    520  1.10  reinoud 	struct vnode *vp, *dvp;
    521   1.1  reinoud 	struct long_ad parent_loc;
    522   1.1  reinoud 	const char *name;
    523   1.1  reinoud 	int namelen;
    524   1.1  reinoud 	int error, found;
    525   1.1  reinoud 
    526   1.1  reinoud 	(void)cred;
    527   1.1  reinoud 	KASSERT(mp != NULL);
    528   1.1  reinoud 	KASSERT(fdvp != NULL);
    529   1.1  reinoud 	KASSERT(tdvp != NULL);
    530   1.1  reinoud 	KASSERT(fdvp != tdvp);
    531   1.1  reinoud 	KASSERT(intermediate_node_ret != NULL);
    532   1.1  reinoud 	KASSERT(fdvp->v_mount == mp);
    533   1.1  reinoud 	KASSERT(tdvp->v_mount == mp);
    534   1.1  reinoud 	KASSERT(fdvp->v_type == VDIR);
    535   1.1  reinoud 	KASSERT(tdvp->v_type == VDIR);
    536   1.1  reinoud 
    537   1.1  reinoud 	DPRINTF(CALL, ("udf_gro_genealogy called\n"));
    538   1.1  reinoud 
    539   1.1  reinoud 	/*
    540   1.1  reinoud 	 * We need to provisionally lock tdvp to keep rmdir from deleting it
    541   1.1  reinoud 	 * -- or any ancestor -- at an inopportune moment.
    542   1.1  reinoud 	 *
    543   1.1  reinoud 	 * XXX WHY is this not in genfs's rename? XXX
    544   1.1  reinoud 	 */
    545   1.1  reinoud 	error = udf_gro_lock_directory(mp, tdvp);
    546   1.1  reinoud 	if (error)
    547   1.1  reinoud 		return error;
    548   1.1  reinoud 
    549   1.1  reinoud 	name     = "..";
    550   1.1  reinoud 	namelen  = 2;
    551   1.1  reinoud 	error    = 0;
    552   1.1  reinoud 
    553  1.10  reinoud 	ump = VTOI(tdvp)->ump;
    554   1.1  reinoud 
    555   1.1  reinoud 	/* if nodes are equal, it is no use looking */
    556  1.10  reinoud 	KASSERT(udf_compare_icb(&VTOI(fdvp)->loc, &VTOI(tdvp)->loc) != 0);
    557   1.1  reinoud 
    558  1.10  reinoud 	/* start at destination vnode and walk up the tree */
    559  1.10  reinoud 	vp = tdvp;
    560  1.10  reinoud 	vref(vp);
    561   1.1  reinoud 
    562   1.1  reinoud 	for (;;) {
    563  1.10  reinoud 		KASSERT(vp != NULL);
    564  1.10  reinoud 		KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
    565  1.10  reinoud 		KASSERT(vp->v_mount == mp);
    566  1.10  reinoud 		KASSERT(vp->v_type == VDIR);
    567  1.10  reinoud 		KASSERT(!udf_rmdired_p(vp));
    568  1.10  reinoud 
    569   1.1  reinoud 		DPRINTF(NODE, ("udf_gro_genealogy : "
    570  1.10  reinoud 			"fdvp %p, looking at vp %p\n",
    571  1.10  reinoud 			fdvp, vp));
    572   1.1  reinoud 
    573   1.1  reinoud 		/* sanity check */
    574  1.10  reinoud 		if (vp->v_type != VDIR) {
    575  1.10  reinoud 			vput(vp);
    576   1.1  reinoud 			return ENOTDIR;
    577   1.1  reinoud 		}
    578   1.1  reinoud 
    579   1.1  reinoud 		/* go down one level */
    580  1.10  reinoud 		error = udf_lookup_name_in_dir(vp, name, namelen,
    581   1.1  reinoud 			&parent_loc, &found);
    582   1.1  reinoud 		DPRINTF(NODE, ("\tlookup of parent '..' resulted in error %d, "
    583   1.1  reinoud 			"found %d\n", error, found));
    584   1.1  reinoud 		if (!found)
    585   1.1  reinoud 			error = ENOENT;
    586   1.1  reinoud 		if (error) {
    587  1.10  reinoud 			vput(vp);
    588   1.1  reinoud 			return error;
    589   1.1  reinoud 		}
    590   1.1  reinoud 
    591   1.1  reinoud 		/* did we encounter the root node? i.e. loop back */
    592  1.10  reinoud 		if (udf_compare_icb(&parent_loc, &VTOI(vp)->loc) == 0) {
    593   1.1  reinoud 			DPRINTF(NODE, ("ROOT found!\n"));
    594  1.10  reinoud 			vput(vp);
    595   1.1  reinoud 			*intermediate_node_ret = NULL;
    596   1.1  reinoud 			return 0;
    597   1.1  reinoud 		}
    598   1.1  reinoud 
    599  1.10  reinoud 		/* Did we find that fdvp is an ancestor of tdvp? */
    600  1.10  reinoud 		if (udf_compare_icb(&parent_loc, &VTOI(fdvp)->loc) == 0) {
    601  1.10  reinoud 			DPRINTF(NODE, ("fdvp is ancestor of tdvp\n"));
    602  1.10  reinoud 			*intermediate_node_ret = vp;
    603  1.10  reinoud 			VOP_UNLOCK(vp);
    604   1.1  reinoud 			return 0;
    605   1.1  reinoud 		}
    606   1.1  reinoud 
    607   1.9  reinoud 		/*
    608   1.9  reinoud 		 * Unlock vp so that we can lock the parent, but keep child vp
    609   1.9  reinoud 		 * referenced until after we have found the parent, so that
    610  1.10  reinoud 		 * parent_node will not be recycled.
    611   1.9  reinoud 		 */
    612   1.1  reinoud 		DPRINTF(NODE, ("\tgetting the parent node\n"));
    613  1.10  reinoud 		VOP_UNLOCK(vp);
    614  1.13       ad 		error = udf_get_node(ump, &parent_loc, &parent_node,
    615  1.13       ad 		    LK_EXCLUSIVE);
    616  1.10  reinoud 		vrele(vp);
    617   1.9  reinoud 		if (error)
    618   1.9  reinoud 			return error;
    619   1.9  reinoud 
    620  1.10  reinoud 		dvp = parent_node->vnode;
    621  1.10  reinoud 
    622  1.10  reinoud 		/* switch */
    623  1.10  reinoud 		KASSERT(dvp != NULL);
    624  1.10  reinoud 		KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
    625  1.10  reinoud 		vp  = dvp;
    626  1.10  reinoud 
    627   1.9  reinoud 		/* sanity check */
    628  1.10  reinoud 		if (vp->v_type != VDIR) {
    629   1.9  reinoud 			/*
    630  1.15   andvar 			 * Odd, but can happen if we lose the race and the
    631   1.9  reinoud 			 * '..' node has been recycled.
    632   1.9  reinoud 			 */
    633  1.10  reinoud 			vput(vp);
    634   1.9  reinoud 			return ENOTDIR;
    635   1.1  reinoud 		}
    636   1.1  reinoud 
    637  1.10  reinoud 		if (udf_rmdired_p(vp)) {
    638  1.10  reinoud 			vput(vp);
    639   1.1  reinoud 			return ENOENT;
    640   1.1  reinoud 		}
    641   1.1  reinoud 	}
    642   1.1  reinoud }
    643   1.1  reinoud 
    644   1.1  reinoud 
    645   1.1  reinoud /*
    646   1.1  reinoud  * udf_gro_lock_directory: lock the directory vp, but fail if it has been
    647   1.1  reinoud  * rmdir'd.
    648   1.1  reinoud  */
    649   1.1  reinoud static int
    650   1.1  reinoud udf_gro_lock_directory(struct mount *mp, struct vnode *vp)
    651   1.1  reinoud {
    652   1.1  reinoud 
    653   1.1  reinoud 	(void)mp;
    654   1.1  reinoud 	KASSERT(mp != NULL);
    655   1.1  reinoud 	KASSERT(vp != NULL);
    656   1.1  reinoud 	KASSERT(vp->v_mount == mp);
    657   1.1  reinoud 
    658   1.1  reinoud 	DPRINTF(CALL, ("udf_gro_lock_directory called\n"));
    659   1.1  reinoud 	DPRINTF(LOCKING, ("udf_gro_lock_directory called\n"));
    660   1.1  reinoud 
    661   1.1  reinoud 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
    662   1.1  reinoud 
    663   1.1  reinoud 	if (udf_rmdired_p(vp)) {
    664   1.1  reinoud 		VOP_UNLOCK(vp);
    665   1.1  reinoud 		return ENOENT;
    666   1.1  reinoud 	}
    667   1.1  reinoud 
    668   1.1  reinoud 	return 0;
    669   1.1  reinoud }
    670   1.1  reinoud 
    671   1.1  reinoud 
    672   1.1  reinoud static const struct genfs_rename_ops udf_genfs_rename_ops = {
    673   1.1  reinoud 	.gro_directory_empty_p		= udf_gro_directory_empty_p,
    674   1.1  reinoud 	.gro_rename_check_possible	= udf_gro_rename_check_possible,
    675   1.1  reinoud 	.gro_rename_check_permitted	= udf_gro_rename_check_permitted,
    676   1.1  reinoud 	.gro_remove_check_possible	= udf_gro_remove_check_possible,
    677   1.1  reinoud 	.gro_remove_check_permitted	= udf_gro_remove_check_permitted,
    678   1.1  reinoud 	.gro_rename			= udf_gro_rename,
    679   1.1  reinoud 	.gro_remove			= udf_gro_remove,
    680   1.1  reinoud 	.gro_lookup			= udf_gro_lookup,
    681   1.1  reinoud 	.gro_genealogy			= udf_gro_genealogy,
    682   1.1  reinoud 	.gro_lock_directory		= udf_gro_lock_directory,
    683   1.1  reinoud };
    684