1 1.4 mlelstv /* $NetBSD: msdosfs_rename.c,v 1.4 2024/05/04 05:49:39 mlelstv Exp $ */ 2 1.1 hannken 3 1.1 hannken /*- 4 1.2 hannken * Copyright (c) 2011 The NetBSD Foundation, Inc. 5 1.1 hannken * All rights reserved. 6 1.2 hannken * 7 1.2 hannken * This code is derived from software contributed to The NetBSD Foundation 8 1.2 hannken * by Taylor R Campbell. 9 1.1 hannken * 10 1.1 hannken * Redistribution and use in source and binary forms, with or without 11 1.1 hannken * modification, are permitted provided that the following conditions 12 1.1 hannken * are met: 13 1.1 hannken * 1. Redistributions of source code must retain the above copyright 14 1.1 hannken * notice, this list of conditions and the following disclaimer. 15 1.1 hannken * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 hannken * notice, this list of conditions and the following disclaimer in the 17 1.1 hannken * documentation and/or other materials provided with the distribution. 18 1.1 hannken * 19 1.2 hannken * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.2 hannken * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.2 hannken * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.2 hannken * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.2 hannken * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.2 hannken * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.2 hannken * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.2 hannken * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.2 hannken * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.2 hannken * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.2 hannken * POSSIBILITY OF SUCH DAMAGE. 30 1.1 hannken */ 31 1.1 hannken 32 1.2 hannken /* 33 1.2 hannken * MS-DOS FS Rename 34 1.2 hannken */ 35 1.2 hannken 36 1.2 hannken #include <sys/cdefs.h> 37 1.4 mlelstv __KERNEL_RCSID(0, "$NetBSD: msdosfs_rename.c,v 1.4 2024/05/04 05:49:39 mlelstv Exp $"); 38 1.2 hannken 39 1.1 hannken #include <sys/param.h> 40 1.2 hannken #include <sys/buf.h> 41 1.2 hannken #include <sys/errno.h> 42 1.2 hannken #include <sys/kauth.h> 43 1.1 hannken #include <sys/namei.h> 44 1.1 hannken #include <sys/vnode.h> 45 1.2 hannken #include <sys/vnode_if.h> 46 1.1 hannken 47 1.1 hannken #include <miscfs/genfs/genfs.h> 48 1.1 hannken 49 1.1 hannken #include <fs/msdosfs/bpb.h> 50 1.1 hannken #include <fs/msdosfs/direntry.h> 51 1.1 hannken #include <fs/msdosfs/denode.h> 52 1.1 hannken #include <fs/msdosfs/msdosfsmount.h> 53 1.1 hannken #include <fs/msdosfs/fat.h> 54 1.1 hannken 55 1.2 hannken /* 56 1.2 hannken * Forward declarations 57 1.2 hannken */ 58 1.2 hannken 59 1.2 hannken static int msdosfs_sane_rename(struct vnode *, struct componentname *, 60 1.2 hannken struct vnode *, struct componentname *, 61 1.2 hannken kauth_cred_t, bool); 62 1.2 hannken static bool msdosfs_rmdired_p(struct vnode *); 63 1.2 hannken static int msdosfs_read_dotdot(struct vnode *, kauth_cred_t, unsigned long *); 64 1.2 hannken static int msdosfs_rename_replace_dotdot(struct vnode *, 65 1.2 hannken struct vnode *, struct vnode *, kauth_cred_t); 66 1.2 hannken static int msdosfs_gro_lock_directory(struct mount *, struct vnode *); 67 1.2 hannken 68 1.2 hannken static const struct genfs_rename_ops msdosfs_genfs_rename_ops; 69 1.2 hannken 70 1.2 hannken /* 71 1.2 hannken * msdosfs_rename: The hairiest vop, with the insanest API. 72 1.2 hannken * 73 1.2 hannken * Arguments: 74 1.2 hannken * 75 1.2 hannken * . fdvp (from directory vnode), 76 1.2 hannken * . fvp (from vnode), 77 1.2 hannken * . fcnp (from component name), 78 1.2 hannken * . tdvp (to directory vnode), 79 1.2 hannken * . tvp (to vnode, or NULL), and 80 1.2 hannken * . tcnp (to component name). 81 1.2 hannken * 82 1.2 hannken * Any pair of vnode parameters may have the same vnode. 83 1.2 hannken * 84 1.2 hannken * On entry, 85 1.2 hannken * 86 1.2 hannken * . fdvp, fvp, tdvp, and tvp are referenced, 87 1.2 hannken * . fdvp and fvp are unlocked, and 88 1.2 hannken * . tdvp and tvp (if nonnull) are locked. 89 1.2 hannken * 90 1.2 hannken * On exit, 91 1.2 hannken * 92 1.2 hannken * . fdvp, fvp, tdvp, and tvp (if nonnull) are unreferenced, and 93 1.2 hannken * . tdvp and tvp are unlocked. 94 1.2 hannken */ 95 1.1 hannken int 96 1.1 hannken msdosfs_rename(void *v) 97 1.1 hannken { 98 1.2 hannken struct vop_rename_args /* { 99 1.1 hannken struct vnode *a_fdvp; 100 1.1 hannken struct vnode *a_fvp; 101 1.1 hannken struct componentname *a_fcnp; 102 1.1 hannken struct vnode *a_tdvp; 103 1.1 hannken struct vnode *a_tvp; 104 1.1 hannken struct componentname *a_tcnp; 105 1.1 hannken } */ *ap = v; 106 1.2 hannken struct vnode *fdvp = ap->a_fdvp; 107 1.2 hannken struct vnode *fvp = ap->a_fvp; 108 1.2 hannken struct componentname *fcnp = ap->a_fcnp; 109 1.2 hannken struct vnode *tdvp = ap->a_tdvp; 110 1.1 hannken struct vnode *tvp = ap->a_tvp; 111 1.1 hannken struct componentname *tcnp = ap->a_tcnp; 112 1.2 hannken kauth_cred_t cred; 113 1.1 hannken int error; 114 1.2 hannken 115 1.2 hannken KASSERT(fdvp != NULL); 116 1.2 hannken KASSERT(fvp != NULL); 117 1.2 hannken KASSERT(fcnp != NULL); 118 1.2 hannken KASSERT(fcnp->cn_nameptr != NULL); 119 1.2 hannken KASSERT(tdvp != NULL); 120 1.2 hannken KASSERT(tcnp != NULL); 121 1.2 hannken KASSERT(fcnp->cn_nameptr != NULL); 122 1.2 hannken /* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */ 123 1.2 hannken /* KASSERT(VOP_ISLOCKED(fvp) != LK_EXCLUSIVE); */ 124 1.2 hannken KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 125 1.2 hannken KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 126 1.2 hannken KASSERT(fdvp->v_type == VDIR); 127 1.2 hannken KASSERT(tdvp->v_type == VDIR); 128 1.2 hannken 129 1.2 hannken cred = fcnp->cn_cred; 130 1.4 mlelstv KASSERT(kauth_cred_uidmatch(cred, tcnp->cn_cred)); 131 1.2 hannken 132 1.2 hannken /* 133 1.2 hannken * Sanitize our world from the VFS insanity. Unlock the target 134 1.2 hannken * directory and node, which are locked. Release the children, 135 1.2 hannken * which are referenced. Check for rename("x", "y/."), which 136 1.2 hannken * it is our responsibility to reject, not the caller's. (But 137 1.2 hannken * the caller does reject rename("x/.", "y"). Go figure.) 138 1.2 hannken */ 139 1.2 hannken 140 1.2 hannken VOP_UNLOCK(tdvp); 141 1.2 hannken if ((tvp != NULL) && (tvp != tdvp)) 142 1.2 hannken VOP_UNLOCK(tvp); 143 1.2 hannken 144 1.2 hannken vrele(fvp); 145 1.2 hannken if (tvp != NULL) 146 1.2 hannken vrele(tvp); 147 1.2 hannken 148 1.2 hannken if (tvp == tdvp) { 149 1.2 hannken error = EINVAL; 150 1.2 hannken goto out; 151 1.2 hannken } 152 1.2 hannken 153 1.2 hannken error = msdosfs_sane_rename(fdvp, fcnp, tdvp, tcnp, cred, false); 154 1.2 hannken 155 1.2 hannken out: /* 156 1.2 hannken * All done, whether with success or failure. Release the 157 1.2 hannken * directory nodes now, as the caller expects from the VFS 158 1.2 hannken * protocol. 159 1.2 hannken */ 160 1.2 hannken vrele(fdvp); 161 1.2 hannken vrele(tdvp); 162 1.2 hannken 163 1.2 hannken return error; 164 1.2 hannken } 165 1.2 hannken 166 1.2 hannken /* 167 1.2 hannken * msdosfs_sane_rename: The hairiest vop, with the saner API. 168 1.2 hannken * 169 1.2 hannken * Arguments: 170 1.2 hannken * 171 1.2 hannken * . fdvp (from directory vnode), 172 1.2 hannken * . fcnp (from component name), 173 1.2 hannken * . tdvp (to directory vnode), and 174 1.2 hannken * . tcnp (to component name). 175 1.2 hannken * 176 1.2 hannken * fdvp and tdvp must be referenced and unlocked. 177 1.2 hannken */ 178 1.2 hannken static int 179 1.2 hannken msdosfs_sane_rename( 180 1.2 hannken struct vnode *fdvp, struct componentname *fcnp, 181 1.2 hannken struct vnode *tdvp, struct componentname *tcnp, 182 1.2 hannken kauth_cred_t cred, bool posixly_correct) 183 1.2 hannken { 184 1.2 hannken struct msdosfs_lookup_results fmlr, tmlr; 185 1.2 hannken 186 1.2 hannken return genfs_sane_rename(&msdosfs_genfs_rename_ops, 187 1.2 hannken fdvp, fcnp, &fmlr, tdvp, tcnp, &tmlr, 188 1.2 hannken cred, posixly_correct); 189 1.2 hannken } 190 1.2 hannken 191 1.2 hannken /* 192 1.2 hannken * msdosfs_gro_directory_empty_p: Return true if the directory vp is 193 1.2 hannken * empty. dvp is its parent. 194 1.2 hannken * 195 1.2 hannken * vp and dvp must be locked and referenced. 196 1.2 hannken */ 197 1.2 hannken static bool 198 1.2 hannken msdosfs_gro_directory_empty_p(struct mount *mp, kauth_cred_t cred, 199 1.2 hannken struct vnode *vp, struct vnode *dvp) 200 1.2 hannken { 201 1.2 hannken 202 1.2 hannken (void)mp; 203 1.2 hannken (void)cred; 204 1.2 hannken (void)dvp; 205 1.2 hannken KASSERT(mp != NULL); 206 1.2 hannken KASSERT(vp != NULL); 207 1.2 hannken KASSERT(dvp != NULL); 208 1.2 hannken KASSERT(vp != dvp); 209 1.2 hannken KASSERT(vp->v_mount == mp); 210 1.2 hannken KASSERT(dvp->v_mount == mp); 211 1.2 hannken KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 212 1.2 hannken KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 213 1.2 hannken 214 1.3 thorpej return msdosfs_dosdirempty(VTODE(vp)); 215 1.2 hannken } 216 1.2 hannken 217 1.2 hannken /* 218 1.2 hannken * Return a UFS-like mode for vp. 219 1.2 hannken */ 220 1.2 hannken static mode_t 221 1.2 hannken msdosfs_vnode_mode(struct vnode *vp) 222 1.2 hannken { 223 1.2 hannken struct msdosfsmount *pmp; 224 1.2 hannken mode_t mode, mask; 225 1.2 hannken 226 1.2 hannken KASSERT(vp != NULL); 227 1.2 hannken 228 1.2 hannken pmp = VTODE(vp)->de_pmp; 229 1.2 hannken KASSERT(pmp != NULL); 230 1.2 hannken 231 1.2 hannken if (VTODE(vp)->de_Attributes & ATTR_READONLY) 232 1.2 hannken mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 233 1.2 hannken else 234 1.2 hannken mode = S_IRWXU|S_IRWXG|S_IRWXO; 235 1.2 hannken 236 1.2 hannken if (vp->v_type == VDIR) 237 1.2 hannken mask = pmp->pm_dirmask; 238 1.2 hannken else 239 1.2 hannken mask = pmp->pm_mask; 240 1.2 hannken 241 1.2 hannken return (mode & mask); 242 1.2 hannken } 243 1.2 hannken 244 1.2 hannken /* 245 1.2 hannken * msdosfs_gro_rename_check_possible: Check whether renaming fvp in fdvp 246 1.2 hannken * to tvp in tdvp is possible independent of credentials. 247 1.2 hannken */ 248 1.2 hannken static int 249 1.2 hannken msdosfs_gro_rename_check_possible(struct mount *mp, 250 1.2 hannken struct vnode *fdvp, struct vnode *fvp, 251 1.2 hannken struct vnode *tdvp, struct vnode *tvp) 252 1.2 hannken { 253 1.2 hannken 254 1.2 hannken (void)mp; 255 1.2 hannken (void)fdvp; 256 1.2 hannken (void)fvp; 257 1.2 hannken (void)tdvp; 258 1.2 hannken (void)tvp; 259 1.2 hannken KASSERT(mp != NULL); 260 1.2 hannken KASSERT(fdvp != NULL); 261 1.2 hannken KASSERT(fvp != NULL); 262 1.2 hannken KASSERT(tdvp != NULL); 263 1.2 hannken KASSERT(fdvp != fvp); 264 1.2 hannken KASSERT(fdvp != tvp); 265 1.2 hannken KASSERT(tdvp != fvp); 266 1.2 hannken KASSERT(tdvp != tvp); 267 1.2 hannken KASSERT(fvp != tvp); 268 1.2 hannken KASSERT(fdvp->v_mount == mp); 269 1.2 hannken KASSERT(fvp->v_mount == mp); 270 1.2 hannken KASSERT(tdvp->v_mount == mp); 271 1.2 hannken KASSERT((tvp == NULL) || (tvp->v_mount == mp)); 272 1.2 hannken KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); 273 1.2 hannken KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); 274 1.2 hannken KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 275 1.2 hannken KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 276 1.2 hannken 277 1.2 hannken /* It's always possible: no error. */ 278 1.2 hannken return 0; 279 1.2 hannken } 280 1.2 hannken 281 1.2 hannken /* 282 1.2 hannken * msdosfs_gro_rename_check_permitted: ... 283 1.2 hannken */ 284 1.2 hannken static int 285 1.2 hannken msdosfs_gro_rename_check_permitted(struct mount *mp, kauth_cred_t cred, 286 1.2 hannken struct vnode *fdvp, struct vnode *fvp, 287 1.2 hannken struct vnode *tdvp, struct vnode *tvp) 288 1.2 hannken { 289 1.2 hannken struct msdosfsmount *pmp; 290 1.2 hannken 291 1.2 hannken KASSERT(mp != NULL); 292 1.2 hannken KASSERT(fdvp != NULL); 293 1.2 hannken KASSERT(fvp != NULL); 294 1.2 hannken KASSERT(tdvp != NULL); 295 1.2 hannken KASSERT(fdvp != fvp); 296 1.2 hannken KASSERT(fdvp != tvp); 297 1.2 hannken KASSERT(tdvp != fvp); 298 1.2 hannken KASSERT(tdvp != tvp); 299 1.2 hannken KASSERT(fvp != tvp); 300 1.2 hannken KASSERT(fdvp->v_mount == mp); 301 1.2 hannken KASSERT(fvp->v_mount == mp); 302 1.2 hannken KASSERT(tdvp->v_mount == mp); 303 1.2 hannken KASSERT((tvp == NULL) || (tvp->v_mount == mp)); 304 1.2 hannken KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); 305 1.2 hannken KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); 306 1.2 hannken KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 307 1.2 hannken KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 308 1.2 hannken 309 1.2 hannken pmp = VFSTOMSDOSFS(mp); 310 1.2 hannken KASSERT(pmp != NULL); 311 1.2 hannken 312 1.2 hannken return genfs_ufslike_rename_check_permitted(cred, 313 1.2 hannken fdvp, msdosfs_vnode_mode(fdvp), pmp->pm_uid, 314 1.2 hannken fvp, pmp->pm_uid, 315 1.2 hannken tdvp, msdosfs_vnode_mode(tdvp), pmp->pm_uid, 316 1.2 hannken tvp, (tvp? pmp->pm_uid : 0)); 317 1.2 hannken } 318 1.2 hannken 319 1.2 hannken /* 320 1.2 hannken * msdosfs_gro_remove_check_possible: ... 321 1.2 hannken */ 322 1.2 hannken static int 323 1.2 hannken msdosfs_gro_remove_check_possible(struct mount *mp, 324 1.2 hannken struct vnode *dvp, struct vnode *vp) 325 1.2 hannken { 326 1.2 hannken 327 1.2 hannken KASSERT(mp != NULL); 328 1.2 hannken KASSERT(dvp != NULL); 329 1.2 hannken KASSERT(vp != NULL); 330 1.2 hannken KASSERT(dvp != vp); 331 1.2 hannken KASSERT(dvp->v_mount == mp); 332 1.2 hannken KASSERT(vp->v_mount == mp); 333 1.2 hannken KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 334 1.2 hannken KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 335 1.2 hannken 336 1.2 hannken /* It's always possible: no error. */ 337 1.2 hannken return 0; 338 1.2 hannken } 339 1.2 hannken 340 1.2 hannken /* 341 1.2 hannken * msdosfs_gro_remove_check_permitted: ... 342 1.2 hannken */ 343 1.2 hannken static int 344 1.2 hannken msdosfs_gro_remove_check_permitted(struct mount *mp, kauth_cred_t cred, 345 1.2 hannken struct vnode *dvp, struct vnode *vp) 346 1.2 hannken { 347 1.1 hannken struct msdosfsmount *pmp; 348 1.1 hannken 349 1.2 hannken KASSERT(mp != NULL); 350 1.2 hannken KASSERT(dvp != NULL); 351 1.2 hannken KASSERT(vp != NULL); 352 1.2 hannken KASSERT(dvp != vp); 353 1.2 hannken KASSERT(dvp->v_mount == mp); 354 1.2 hannken KASSERT(vp->v_mount == mp); 355 1.2 hannken KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 356 1.2 hannken KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 357 1.2 hannken 358 1.2 hannken pmp = VFSTOMSDOSFS(mp); 359 1.2 hannken KASSERT(pmp != NULL); 360 1.2 hannken 361 1.2 hannken return genfs_ufslike_remove_check_permitted(cred, 362 1.2 hannken dvp, msdosfs_vnode_mode(dvp), pmp->pm_uid, vp, pmp->pm_uid); 363 1.2 hannken } 364 1.2 hannken 365 1.2 hannken /* 366 1.2 hannken * msdosfs_gro_rename: Actually perform the rename operation. 367 1.2 hannken */ 368 1.2 hannken static int 369 1.2 hannken msdosfs_gro_rename(struct mount *mp, kauth_cred_t cred, 370 1.2 hannken struct vnode *fdvp, struct componentname *fcnp, 371 1.2 hannken void *fde, struct vnode *fvp, 372 1.2 hannken struct vnode *tdvp, struct componentname *tcnp, 373 1.2 hannken void *tde, struct vnode *tvp, nlink_t *tvp_nlinkp) 374 1.2 hannken { 375 1.2 hannken struct msdosfs_lookup_results *fmlr = fde; 376 1.2 hannken struct msdosfs_lookup_results *tmlr = tde; 377 1.2 hannken struct msdosfsmount *pmp; 378 1.2 hannken bool directory_p, reparent_p; 379 1.2 hannken unsigned char toname[12], oldname[12]; 380 1.2 hannken int error; 381 1.1 hannken 382 1.2 hannken KASSERT(mp != NULL); 383 1.2 hannken KASSERT(fdvp != NULL); 384 1.2 hannken KASSERT(fcnp != NULL); 385 1.2 hannken KASSERT(fmlr != NULL); 386 1.2 hannken KASSERT(fvp != NULL); 387 1.2 hannken KASSERT(tdvp != NULL); 388 1.2 hannken KASSERT(tcnp != NULL); 389 1.2 hannken KASSERT(tmlr != NULL); 390 1.2 hannken KASSERT(fmlr != tmlr); 391 1.2 hannken KASSERT(fdvp != fvp); 392 1.2 hannken KASSERT(fdvp != tvp); 393 1.2 hannken KASSERT(tdvp != fvp); 394 1.2 hannken KASSERT(tdvp != tvp); 395 1.2 hannken KASSERT(fvp != tvp); 396 1.2 hannken KASSERT(fdvp->v_mount == mp); 397 1.2 hannken KASSERT(fvp->v_mount == mp); 398 1.2 hannken KASSERT(tdvp->v_mount == mp); 399 1.2 hannken KASSERT((tvp == NULL) || (tvp->v_mount == mp)); 400 1.2 hannken KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); 401 1.2 hannken KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); 402 1.2 hannken KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 403 1.2 hannken KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 404 1.2 hannken 405 1.2 hannken /* 406 1.2 hannken * We shall need to temporarily bump the reference count, so 407 1.2 hannken * make sure there is room to do so. 408 1.2 hannken */ 409 1.2 hannken if (VTODE(fvp)->de_refcnt >= LONG_MAX) 410 1.2 hannken return EMLINK; 411 1.2 hannken 412 1.2 hannken /* 413 1.2 hannken * XXX There is a pile of logic here to handle a voodoo flag 414 1.2 hannken * DE_RENAME. I think this is a vestige of days when the file 415 1.2 hannken * system hackers didn't understand concurrency or race 416 1.2 hannken * conditions; I believe it serves no useful function 417 1.2 hannken * whatsoever. 418 1.2 hannken */ 419 1.2 hannken 420 1.2 hannken directory_p = (fvp->v_type == VDIR); 421 1.2 hannken KASSERT(directory_p == 422 1.2 hannken ((VTODE(fvp)->de_Attributes & ATTR_DIRECTORY) != 0)); 423 1.2 hannken KASSERT((tvp == NULL) || (directory_p == (tvp->v_type == VDIR))); 424 1.2 hannken KASSERT((tvp == NULL) || (directory_p == 425 1.2 hannken ((VTODE(fvp)->de_Attributes & ATTR_DIRECTORY) != 0))); 426 1.2 hannken if (directory_p) { 427 1.2 hannken if (VTODE(fvp)->de_flag & DE_RENAME) 428 1.2 hannken return EINVAL; 429 1.2 hannken VTODE(fvp)->de_flag |= DE_RENAME; 430 1.2 hannken } 431 1.2 hannken 432 1.2 hannken reparent_p = (fdvp != tdvp); 433 1.2 hannken KASSERT(reparent_p == (VTODE(fdvp)->de_StartCluster != 434 1.2 hannken VTODE(tdvp)->de_StartCluster)); 435 1.2 hannken 436 1.2 hannken /* 437 1.2 hannken * XXX Hold it right there -- surely if we crash after 438 1.2 hannken * removede, we'll fail to provide rename's guarantee that 439 1.2 hannken * there will be something at the target pathname? 440 1.1 hannken */ 441 1.2 hannken if (tvp != NULL) { 442 1.3 thorpej error = msdosfs_removede(VTODE(tdvp), VTODE(tvp), tmlr); 443 1.2 hannken if (error) 444 1.2 hannken goto out; 445 1.1 hannken } 446 1.1 hannken 447 1.1 hannken /* 448 1.2 hannken * Convert the filename in tcnp into a dos filename. We copy this 449 1.2 hannken * into the denode and directory entry for the destination 450 1.2 hannken * file/directory. 451 1.1 hannken */ 452 1.3 thorpej error = msdosfs_uniqdosname(VTODE(tdvp), tcnp, toname); 453 1.2 hannken if (error) 454 1.2 hannken goto out; 455 1.2 hannken 456 1.2 hannken /* 457 1.2 hannken * First write a new entry in the destination directory and 458 1.2 hannken * mark the entry in the source directory as deleted. Then 459 1.2 hannken * move the denode to the correct hash chain for its new 460 1.2 hannken * location in the filesystem. And, if we moved a directory, 461 1.2 hannken * then update its .. entry to point to the new parent 462 1.2 hannken * directory. 463 1.2 hannken */ 464 1.2 hannken 465 1.2 hannken /* Save the old name in case we need to back out. */ 466 1.2 hannken memcpy(oldname, VTODE(fvp)->de_Name, 11); 467 1.2 hannken memcpy(VTODE(fvp)->de_Name, toname, 11); 468 1.2 hannken 469 1.3 thorpej error = msdosfs_createde(VTODE(fvp), VTODE(tdvp), tmlr, 0, tcnp); 470 1.2 hannken if (error) { 471 1.2 hannken /* Directory entry didn't take -- back out the name change. */ 472 1.2 hannken memcpy(VTODE(fvp)->de_Name, oldname, 11); 473 1.2 hannken goto out; 474 1.1 hannken } 475 1.1 hannken 476 1.1 hannken /* 477 1.2 hannken * createde doesn't increment de_refcnt, but removede 478 1.2 hannken * decrements it. Go figure. 479 1.1 hannken */ 480 1.2 hannken KASSERT(VTODE(fvp)->de_refcnt < LONG_MAX); 481 1.2 hannken VTODE(fvp)->de_refcnt++; 482 1.1 hannken 483 1.1 hannken /* 484 1.2 hannken * XXX Yes, createde and removede have arguments swapped. Go figure. 485 1.1 hannken */ 486 1.3 thorpej error = msdosfs_removede(VTODE(fdvp), VTODE(fvp), fmlr); 487 1.2 hannken if (error) { 488 1.2 hannken #if 0 /* XXX Back out the new directory entry? Panic? */ 489 1.3 thorpej (void)msdosfs_removede(VTODE(tdvp), VTODE(fvp), tmlr); 490 1.2 hannken memcpy(VTODE(fvp)->de_Name, oldname, 11); 491 1.2 hannken #endif 492 1.2 hannken goto out; 493 1.1 hannken } 494 1.1 hannken 495 1.2 hannken pmp = VFSTOMSDOSFS(mp); 496 1.2 hannken 497 1.2 hannken if (!directory_p) { 498 1.2 hannken struct denode_key old_key = VTODE(fvp)->de_key; 499 1.2 hannken struct denode_key new_key = VTODE(fvp)->de_key; 500 1.2 hannken 501 1.3 thorpej error = msdosfs_pcbmap(VTODE(tdvp), 502 1.2 hannken de_cluster(pmp, tmlr->mlr_fndoffset), NULL, 503 1.2 hannken &new_key.dk_dirclust, NULL); 504 1.2 hannken if (error) /* XXX Back everything out? Panic? */ 505 1.2 hannken goto out; 506 1.2 hannken new_key.dk_diroffset = tmlr->mlr_fndoffset; 507 1.2 hannken if (new_key.dk_dirclust != MSDOSFSROOT) 508 1.2 hannken new_key.dk_diroffset &= pmp->pm_crbomask; 509 1.2 hannken vcache_rekey_enter(pmp->pm_mountp, fvp, &old_key, 510 1.2 hannken sizeof(old_key), &new_key, sizeof(new_key)); 511 1.2 hannken VTODE(fvp)->de_key = new_key; 512 1.2 hannken vcache_rekey_exit(pmp->pm_mountp, fvp, &old_key, 513 1.2 hannken sizeof(old_key), &VTODE(fvp)->de_key, 514 1.2 hannken sizeof(VTODE(fvp)->de_key)); 515 1.1 hannken } 516 1.1 hannken 517 1.1 hannken /* 518 1.2 hannken * If we moved a directory to a new parent directory, then we must 519 1.2 hannken * fixup the ".." entry in the moved directory. 520 1.1 hannken */ 521 1.2 hannken if (directory_p && reparent_p) { 522 1.2 hannken error = msdosfs_rename_replace_dotdot(fvp, fdvp, tdvp, cred); 523 1.2 hannken if (error) 524 1.2 hannken goto out; 525 1.2 hannken } 526 1.2 hannken 527 1.2 hannken out:; 528 1.2 hannken if (tvp != NULL) 529 1.2 hannken *tvp_nlinkp = (error ? 1 : 0); 530 1.2 hannken 531 1.2 hannken genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp); 532 1.2 hannken 533 1.2 hannken if (directory_p) 534 1.2 hannken VTODE(fvp)->de_flag &=~ DE_RENAME; 535 1.2 hannken 536 1.2 hannken return error; 537 1.2 hannken } 538 1.2 hannken 539 1.2 hannken /* 540 1.2 hannken * msdosfs_gro_remove: Rename an object over another link to itself, 541 1.2 hannken * effectively removing just the original link. 542 1.2 hannken */ 543 1.2 hannken static int 544 1.2 hannken msdosfs_gro_remove(struct mount *mp, kauth_cred_t cred, 545 1.2 hannken struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp, 546 1.2 hannken nlink_t *tvp_nlinkp) 547 1.2 hannken { 548 1.2 hannken struct msdosfs_lookup_results *mlr = de; 549 1.2 hannken int error; 550 1.2 hannken 551 1.2 hannken KASSERT(mp != NULL); 552 1.2 hannken KASSERT(dvp != NULL); 553 1.2 hannken KASSERT(cnp != NULL); 554 1.2 hannken KASSERT(mlr != NULL); 555 1.2 hannken KASSERT(vp != NULL); 556 1.2 hannken KASSERT(dvp != vp); 557 1.2 hannken KASSERT(dvp->v_mount == mp); 558 1.2 hannken KASSERT(vp->v_mount == mp); 559 1.2 hannken KASSERT(dvp->v_type == VDIR); 560 1.2 hannken KASSERT(vp->v_type != VDIR); 561 1.2 hannken KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 562 1.2 hannken KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 563 1.2 hannken 564 1.3 thorpej error = msdosfs_removede(VTODE(dvp), VTODE(vp), mlr); 565 1.2 hannken 566 1.2 hannken *tvp_nlinkp = (error ? 1 : 0); 567 1.2 hannken 568 1.2 hannken return error; 569 1.2 hannken } 570 1.2 hannken 571 1.2 hannken /* 572 1.2 hannken * msdosfs_gro_lookup: Look up and save the lookup results. 573 1.2 hannken */ 574 1.2 hannken static int 575 1.2 hannken msdosfs_gro_lookup(struct mount *mp, struct vnode *dvp, 576 1.2 hannken struct componentname *cnp, void *de_ret, struct vnode **vp_ret) 577 1.2 hannken { 578 1.2 hannken struct msdosfs_lookup_results *mlr_ret = de_ret; 579 1.2 hannken struct vnode *vp; 580 1.2 hannken int error; 581 1.2 hannken 582 1.2 hannken (void)mp; 583 1.2 hannken KASSERT(mp != NULL); 584 1.2 hannken KASSERT(dvp != NULL); 585 1.2 hannken KASSERT(cnp != NULL); 586 1.2 hannken KASSERT(mlr_ret != NULL); 587 1.2 hannken KASSERT(vp_ret != NULL); 588 1.2 hannken KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 589 1.2 hannken 590 1.2 hannken /* Kludge cargo-culted from dholland's ufs_rename. */ 591 1.2 hannken cnp->cn_flags &=~ MODMASK; 592 1.2 hannken cnp->cn_flags |= (LOCKPARENT | LOCKLEAF); 593 1.2 hannken 594 1.2 hannken error = relookup(dvp, &vp, cnp, 0); 595 1.2 hannken if ((error == 0) && (vp == NULL)) { 596 1.2 hannken error = ENOENT; 597 1.2 hannken goto out; 598 1.1 hannken } 599 1.2 hannken if (error) 600 1.2 hannken return error; 601 1.1 hannken 602 1.1 hannken /* 603 1.2 hannken * Thanks to VFS insanity, relookup locks vp, which screws us 604 1.2 hannken * in various ways. 605 1.1 hannken */ 606 1.2 hannken VOP_UNLOCK(vp); 607 1.2 hannken 608 1.2 hannken out: 609 1.2 hannken *mlr_ret = VTODE(dvp)->de_crap; 610 1.2 hannken *vp_ret = vp; 611 1.2 hannken return error; 612 1.2 hannken } 613 1.2 hannken 614 1.2 hannken /* 615 1.2 hannken * msdosfs_rmdired_p: Check whether the directory vp has been rmdired. 616 1.2 hannken * 617 1.2 hannken * vp must be locked and referenced. 618 1.2 hannken */ 619 1.2 hannken static bool 620 1.2 hannken msdosfs_rmdired_p(struct vnode *vp) 621 1.2 hannken { 622 1.2 hannken 623 1.2 hannken KASSERT(vp != NULL); 624 1.2 hannken KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 625 1.2 hannken KASSERT(vp->v_type == VDIR); 626 1.2 hannken 627 1.2 hannken return (VTODE(vp)->de_FileSize == 0); 628 1.2 hannken } 629 1.2 hannken 630 1.2 hannken /* 631 1.2 hannken * msdosfs_gro_genealogy: Analyze the genealogy of the source and target 632 1.2 hannken * directories. 633 1.2 hannken */ 634 1.2 hannken static int 635 1.2 hannken msdosfs_gro_genealogy(struct mount *mp, kauth_cred_t cred, 636 1.2 hannken struct vnode *fdvp, struct vnode *tdvp, 637 1.2 hannken struct vnode **intermediate_node_ret) 638 1.2 hannken { 639 1.2 hannken struct msdosfsmount *pmp; 640 1.2 hannken struct vnode *vp, *dvp; 641 1.2 hannken unsigned long dotdot_cn; 642 1.2 hannken int error; 643 1.2 hannken 644 1.2 hannken KASSERT(mp != NULL); 645 1.2 hannken KASSERT(fdvp != NULL); 646 1.2 hannken KASSERT(tdvp != NULL); 647 1.2 hannken KASSERT(fdvp != tdvp); 648 1.2 hannken KASSERT(intermediate_node_ret != NULL); 649 1.2 hannken KASSERT(fdvp->v_mount == mp); 650 1.2 hannken KASSERT(tdvp->v_mount == mp); 651 1.2 hannken KASSERT(fdvp->v_type == VDIR); 652 1.2 hannken KASSERT(tdvp->v_type == VDIR); 653 1.2 hannken 654 1.2 hannken pmp = VFSTOMSDOSFS(mp); 655 1.2 hannken KASSERT(pmp != NULL); 656 1.2 hannken 657 1.2 hannken /* 658 1.2 hannken * We need to provisionally lock tdvp to keep rmdir from 659 1.2 hannken * deleting it -- or any ancestor -- at an inopportune moment. 660 1.2 hannken */ 661 1.2 hannken error = msdosfs_gro_lock_directory(mp, tdvp); 662 1.2 hannken if (error) 663 1.2 hannken return error; 664 1.2 hannken 665 1.2 hannken vp = tdvp; 666 1.2 hannken vref(vp); 667 1.2 hannken 668 1.2 hannken for (;;) { 669 1.2 hannken KASSERT(vp->v_type == VDIR); 670 1.2 hannken 671 1.2 hannken /* Did we hit the root without finding fdvp? */ 672 1.2 hannken if ((vp->v_vflag & VV_ROOT) != 0) { 673 1.2 hannken vput(vp); 674 1.2 hannken *intermediate_node_ret = NULL; 675 1.2 hannken return 0; 676 1.2 hannken } 677 1.1 hannken 678 1.2 hannken error = msdosfs_read_dotdot(vp, cred, &dotdot_cn); 679 1.1 hannken if (error) { 680 1.2 hannken vput(vp); 681 1.2 hannken return error; 682 1.1 hannken } 683 1.2 hannken 684 1.2 hannken /* Did we find that fdvp is an ancestor? */ 685 1.2 hannken if (VTODE(fdvp)->de_StartCluster == dotdot_cn) { 686 1.2 hannken /* Unlock vp, but keep it referenced. */ 687 1.2 hannken VOP_UNLOCK(vp); 688 1.2 hannken *intermediate_node_ret = vp; 689 1.2 hannken return 0; 690 1.1 hannken } 691 1.1 hannken 692 1.2 hannken /* Neither -- keep ascending. */ 693 1.2 hannken 694 1.3 thorpej error = msdosfs_deget(pmp, dotdot_cn, 695 1.3 thorpej (dotdot_cn ? 0 : MSDOSFSROOT_OFS), &dvp); 696 1.2 hannken vput(vp); 697 1.2 hannken if (error) 698 1.2 hannken return error; 699 1.2 hannken error = vn_lock(dvp, LK_EXCLUSIVE); 700 1.1 hannken if (error) { 701 1.2 hannken vrele(dvp); 702 1.2 hannken return error; 703 1.1 hannken } 704 1.2 hannken 705 1.2 hannken KASSERT(dvp != NULL); 706 1.2 hannken KASSERT(dvp->v_type == VDIR); 707 1.2 hannken 708 1.2 hannken vp = dvp; 709 1.2 hannken 710 1.2 hannken if (msdosfs_rmdired_p(vp)) { 711 1.2 hannken vput(vp); 712 1.2 hannken return ENOENT; 713 1.1 hannken } 714 1.1 hannken } 715 1.1 hannken } 716 1.1 hannken 717 1.1 hannken /* 718 1.2 hannken * msdosfs_read_dotdot: Store in *cn_ret the cluster number of the 719 1.2 hannken * parent of the directory vp. 720 1.1 hannken */ 721 1.2 hannken static int 722 1.2 hannken msdosfs_read_dotdot(struct vnode *vp, kauth_cred_t cred, unsigned long *cn_ret) 723 1.1 hannken { 724 1.1 hannken struct msdosfsmount *pmp; 725 1.2 hannken unsigned long start_cn, cn; 726 1.2 hannken struct buf *bp; 727 1.1 hannken struct direntry *ep; 728 1.2 hannken int error; 729 1.2 hannken 730 1.2 hannken KASSERT(vp != NULL); 731 1.2 hannken KASSERT(cn_ret != NULL); 732 1.2 hannken KASSERT(vp->v_type == VDIR); 733 1.2 hannken KASSERT(VTODE(vp) != NULL); 734 1.2 hannken 735 1.2 hannken pmp = VTODE(vp)->de_pmp; 736 1.2 hannken KASSERT(pmp != NULL); 737 1.2 hannken 738 1.2 hannken start_cn = VTODE(vp)->de_StartCluster; 739 1.2 hannken error = bread(pmp->pm_devvp, de_bn2kb(pmp, cntobn(pmp, start_cn)), 740 1.2 hannken pmp->pm_bpcluster, 0, &bp); 741 1.2 hannken if (error) 742 1.2 hannken return error; 743 1.2 hannken 744 1.2 hannken ep = (struct direntry *)bp->b_data + 1; 745 1.2 hannken if (((ep->deAttributes & ATTR_DIRECTORY) == ATTR_DIRECTORY) && 746 1.2 hannken (memcmp(ep->deName, ".. ", 11) == 0)) { 747 1.2 hannken cn = getushort(ep->deStartCluster); 748 1.2 hannken if (FAT32(pmp)) 749 1.2 hannken cn |= getushort(ep->deHighClust) << 16; 750 1.2 hannken *cn_ret = cn; 751 1.2 hannken error = 0; 752 1.2 hannken } else { 753 1.1 hannken error = ENOTDIR; 754 1.1 hannken } 755 1.2 hannken 756 1.2 hannken brelse(bp, 0); 757 1.2 hannken 758 1.2 hannken return error; 759 1.2 hannken } 760 1.2 hannken 761 1.2 hannken /* 762 1.2 hannken * msdosfs_rename_replace_dotdot: Change the target of the `..' entry of 763 1.2 hannken * the directory vp from fdvp to tdvp. 764 1.2 hannken */ 765 1.2 hannken static int 766 1.2 hannken msdosfs_rename_replace_dotdot(struct vnode *vp, 767 1.2 hannken struct vnode *fdvp, struct vnode *tdvp, 768 1.2 hannken kauth_cred_t cred) 769 1.2 hannken { 770 1.2 hannken struct msdosfsmount *pmp; 771 1.2 hannken struct direntry *dotdotp; 772 1.2 hannken struct buf *bp; 773 1.2 hannken daddr_t bn; 774 1.2 hannken u_long cn; 775 1.2 hannken int error; 776 1.2 hannken 777 1.2 hannken pmp = VFSTOMSDOSFS(fdvp->v_mount); 778 1.2 hannken 779 1.2 hannken cn = VTODE(vp)->de_StartCluster; 780 1.2 hannken if (cn == MSDOSFSROOT) { 781 1.2 hannken /* this should never happen */ 782 1.2 hannken panic("msdosfs_rename: updating .. in root directory?"); 783 1.2 hannken } else 784 1.2 hannken bn = cntobn(pmp, cn); 785 1.2 hannken 786 1.2 hannken error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), 787 1.2 hannken pmp->pm_bpcluster, B_MODIFY, &bp); 788 1.2 hannken if (error) 789 1.2 hannken return error; 790 1.2 hannken 791 1.2 hannken dotdotp = (struct direntry *)bp->b_data + 1; 792 1.2 hannken putushort(dotdotp->deStartCluster, VTODE(tdvp)->de_StartCluster); 793 1.2 hannken if (FAT32(pmp)) { 794 1.2 hannken putushort(dotdotp->deHighClust, 795 1.2 hannken VTODE(tdvp)->de_StartCluster >> 16); 796 1.2 hannken } else { 797 1.2 hannken putushort(dotdotp->deHighClust, 0); 798 1.1 hannken } 799 1.1 hannken 800 1.2 hannken error = bwrite(bp); 801 1.2 hannken 802 1.2 hannken return error; 803 1.2 hannken } 804 1.2 hannken 805 1.2 hannken /* 806 1.2 hannken * msdosfs_gro_lock_directory: Lock the directory vp, but fail if it has 807 1.2 hannken * been rmdir'd. 808 1.2 hannken */ 809 1.2 hannken static int 810 1.2 hannken msdosfs_gro_lock_directory(struct mount *mp, struct vnode *vp) 811 1.2 hannken { 812 1.2 hannken int error; 813 1.1 hannken 814 1.2 hannken (void)mp; 815 1.2 hannken KASSERT(vp != NULL); 816 1.1 hannken 817 1.2 hannken error = vn_lock(vp, LK_EXCLUSIVE); 818 1.2 hannken if (error) 819 1.2 hannken return error; 820 1.1 hannken 821 1.2 hannken KASSERT(mp != NULL); 822 1.2 hannken KASSERT(vp->v_mount == mp); 823 1.1 hannken 824 1.2 hannken if (msdosfs_rmdired_p(vp)) { 825 1.2 hannken VOP_UNLOCK(vp); 826 1.2 hannken return ENOENT; 827 1.1 hannken } 828 1.2 hannken 829 1.2 hannken return 0; 830 1.1 hannken } 831 1.2 hannken 832 1.2 hannken static const struct genfs_rename_ops msdosfs_genfs_rename_ops = { 833 1.2 hannken .gro_directory_empty_p = msdosfs_gro_directory_empty_p, 834 1.2 hannken .gro_rename_check_possible = msdosfs_gro_rename_check_possible, 835 1.2 hannken .gro_rename_check_permitted = msdosfs_gro_rename_check_permitted, 836 1.2 hannken .gro_remove_check_possible = msdosfs_gro_remove_check_possible, 837 1.2 hannken .gro_remove_check_permitted = msdosfs_gro_remove_check_permitted, 838 1.2 hannken .gro_rename = msdosfs_gro_rename, 839 1.2 hannken .gro_remove = msdosfs_gro_remove, 840 1.2 hannken .gro_lookup = msdosfs_gro_lookup, 841 1.2 hannken .gro_genealogy = msdosfs_gro_genealogy, 842 1.2 hannken .gro_lock_directory = msdosfs_gro_lock_directory, 843 1.2 hannken }; 844