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