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