1 1.12 thorpej /* $NetBSD: tmpfs_rename.c,v 1.12 2021/10/20 14:28:21 thorpej 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 * tmpfs rename 34 1.1 riastrad */ 35 1.1 riastrad 36 1.1 riastrad #include <sys/cdefs.h> 37 1.12 thorpej __KERNEL_RCSID(0, "$NetBSD: tmpfs_rename.c,v 1.12 2021/10/20 14:28:21 thorpej Exp $"); 38 1.1 riastrad 39 1.1 riastrad #include <sys/param.h> 40 1.1 riastrad #include <sys/errno.h> 41 1.1 riastrad #include <sys/kauth.h> 42 1.1 riastrad #include <sys/mount.h> 43 1.1 riastrad #include <sys/namei.h> 44 1.1 riastrad #include <sys/stat.h> 45 1.1 riastrad #include <sys/vnode.h> 46 1.1 riastrad #include <sys/vnode_if.h> 47 1.1 riastrad 48 1.1 riastrad #include <miscfs/genfs/genfs.h> 49 1.1 riastrad 50 1.1 riastrad #include <fs/tmpfs/tmpfs_vnops.h> 51 1.1 riastrad #include <fs/tmpfs/tmpfs.h> 52 1.1 riastrad 53 1.1 riastrad /* 54 1.1 riastrad * Forward declarations 55 1.1 riastrad */ 56 1.1 riastrad 57 1.1 riastrad static int tmpfs_sane_rename(struct vnode *, struct componentname *, 58 1.1 riastrad struct vnode *, struct componentname *, 59 1.1 riastrad kauth_cred_t, bool); 60 1.1 riastrad static bool tmpfs_rmdired_p(struct vnode *); 61 1.1 riastrad static int tmpfs_gro_lock_directory(struct mount *, struct vnode *); 62 1.1 riastrad 63 1.1 riastrad static const struct genfs_rename_ops tmpfs_genfs_rename_ops; 64 1.1 riastrad 65 1.1 riastrad /* 66 1.1 riastrad * tmpfs_sane_rename: The hairiest vop, with the saner API. 67 1.1 riastrad * 68 1.1 riastrad * Arguments: 69 1.1 riastrad * 70 1.1 riastrad * . fdvp (from directory vnode), 71 1.1 riastrad * . fcnp (from component name), 72 1.1 riastrad * . tdvp (to directory vnode), 73 1.1 riastrad * . tcnp (to component name), 74 1.1 riastrad * . cred (credentials structure), and 75 1.1 riastrad * . posixly_correct (flag for behaviour if target & source link same file). 76 1.1 riastrad * 77 1.1 riastrad * fdvp and tdvp may be the same, and must be referenced and unlocked. 78 1.1 riastrad */ 79 1.1 riastrad static int 80 1.1 riastrad tmpfs_sane_rename( 81 1.1 riastrad struct vnode *fdvp, struct componentname *fcnp, 82 1.1 riastrad struct vnode *tdvp, struct componentname *tcnp, 83 1.1 riastrad kauth_cred_t cred, bool posixly_correct) 84 1.1 riastrad { 85 1.1 riastrad struct tmpfs_dirent *fdirent, *tdirent; 86 1.1 riastrad 87 1.1 riastrad return genfs_sane_rename(&tmpfs_genfs_rename_ops, 88 1.1 riastrad fdvp, fcnp, &fdirent, tdvp, tcnp, &tdirent, 89 1.1 riastrad cred, posixly_correct); 90 1.1 riastrad } 91 1.1 riastrad 92 1.1 riastrad /* 93 1.1 riastrad * tmpfs_rename: The hairiest vop, with the insanest API. Defer to 94 1.1 riastrad * genfs_insane_rename immediately. 95 1.1 riastrad */ 96 1.1 riastrad int 97 1.1 riastrad tmpfs_rename(void *v) 98 1.1 riastrad { 99 1.1 riastrad 100 1.1 riastrad return genfs_insane_rename(v, &tmpfs_sane_rename); 101 1.1 riastrad } 102 1.1 riastrad 103 1.1 riastrad /* 104 1.1 riastrad * tmpfs_gro_directory_empty_p: Return true if the directory vp is 105 1.1 riastrad * empty. dvp is its parent. 106 1.1 riastrad * 107 1.1 riastrad * vp and dvp must be locked and referenced. 108 1.1 riastrad */ 109 1.1 riastrad static bool 110 1.1 riastrad tmpfs_gro_directory_empty_p(struct mount *mp, kauth_cred_t cred, 111 1.1 riastrad struct vnode *vp, struct vnode *dvp) 112 1.1 riastrad { 113 1.1 riastrad 114 1.1 riastrad (void)mp; 115 1.1 riastrad (void)cred; 116 1.1 riastrad (void)dvp; 117 1.1 riastrad KASSERT(mp != NULL); 118 1.1 riastrad KASSERT(vp != NULL); 119 1.1 riastrad KASSERT(dvp != NULL); 120 1.1 riastrad KASSERT(vp != dvp); 121 1.1 riastrad KASSERT(vp->v_mount == mp); 122 1.1 riastrad KASSERT(dvp->v_mount == mp); 123 1.1 riastrad KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 124 1.1 riastrad KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 125 1.1 riastrad 126 1.1 riastrad return (VP_TO_TMPFS_NODE(vp)->tn_size == 0); 127 1.1 riastrad } 128 1.1 riastrad 129 1.1 riastrad /* 130 1.1 riastrad * tmpfs_gro_rename_check_possible: Check whether a rename is possible 131 1.1 riastrad * independent of credentials. 132 1.1 riastrad */ 133 1.1 riastrad static int 134 1.1 riastrad tmpfs_gro_rename_check_possible(struct mount *mp, 135 1.1 riastrad struct vnode *fdvp, struct vnode *fvp, 136 1.1 riastrad struct vnode *tdvp, struct vnode *tvp) 137 1.1 riastrad { 138 1.1 riastrad 139 1.1 riastrad (void)mp; 140 1.1 riastrad KASSERT(mp != NULL); 141 1.1 riastrad KASSERT(fdvp != NULL); 142 1.1 riastrad KASSERT(fvp != NULL); 143 1.1 riastrad KASSERT(tdvp != NULL); 144 1.1 riastrad KASSERT(fdvp != fvp); 145 1.1 riastrad KASSERT(fdvp != tvp); 146 1.1 riastrad KASSERT(tdvp != fvp); 147 1.1 riastrad KASSERT(tdvp != tvp); 148 1.1 riastrad KASSERT(fvp != tvp); 149 1.1 riastrad KASSERT(fdvp->v_type == VDIR); 150 1.1 riastrad KASSERT(tdvp->v_type == VDIR); 151 1.1 riastrad KASSERT(fdvp->v_mount == mp); 152 1.1 riastrad KASSERT(fvp->v_mount == mp); 153 1.1 riastrad KASSERT(tdvp->v_mount == mp); 154 1.1 riastrad KASSERT((tvp == NULL) || (tvp->v_mount == mp)); 155 1.1 riastrad KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); 156 1.1 riastrad KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); 157 1.1 riastrad KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 158 1.1 riastrad KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 159 1.1 riastrad 160 1.1 riastrad return genfs_ufslike_rename_check_possible( 161 1.1 riastrad VP_TO_TMPFS_NODE(fdvp)->tn_flags, VP_TO_TMPFS_NODE(fvp)->tn_flags, 162 1.1 riastrad VP_TO_TMPFS_NODE(tdvp)->tn_flags, 163 1.1 riastrad (tvp? VP_TO_TMPFS_NODE(tvp)->tn_flags : 0), (tvp != NULL), 164 1.1 riastrad IMMUTABLE, APPEND); 165 1.1 riastrad } 166 1.1 riastrad 167 1.1 riastrad /* 168 1.1 riastrad * tmpfs_gro_rename_check_permitted: Check whether a rename is 169 1.1 riastrad * permitted given our credentials. 170 1.1 riastrad */ 171 1.1 riastrad static int 172 1.1 riastrad tmpfs_gro_rename_check_permitted(struct mount *mp, kauth_cred_t cred, 173 1.1 riastrad struct vnode *fdvp, struct vnode *fvp, 174 1.1 riastrad struct vnode *tdvp, struct vnode *tvp) 175 1.1 riastrad { 176 1.1 riastrad 177 1.1 riastrad (void)mp; 178 1.1 riastrad KASSERT(mp != NULL); 179 1.1 riastrad KASSERT(fdvp != NULL); 180 1.1 riastrad KASSERT(fvp != NULL); 181 1.1 riastrad KASSERT(tdvp != NULL); 182 1.1 riastrad KASSERT(fdvp != fvp); 183 1.1 riastrad KASSERT(fdvp != tvp); 184 1.1 riastrad KASSERT(tdvp != fvp); 185 1.1 riastrad KASSERT(tdvp != tvp); 186 1.1 riastrad KASSERT(fvp != tvp); 187 1.1 riastrad KASSERT(fdvp->v_type == VDIR); 188 1.1 riastrad KASSERT(tdvp->v_type == VDIR); 189 1.1 riastrad KASSERT(fdvp->v_mount == mp); 190 1.1 riastrad KASSERT(fvp->v_mount == mp); 191 1.1 riastrad KASSERT(tdvp->v_mount == mp); 192 1.1 riastrad KASSERT((tvp == NULL) || (tvp->v_mount == mp)); 193 1.1 riastrad KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); 194 1.1 riastrad KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); 195 1.1 riastrad KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 196 1.1 riastrad KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 197 1.1 riastrad 198 1.1 riastrad return genfs_ufslike_rename_check_permitted(cred, 199 1.1 riastrad fdvp, VP_TO_TMPFS_NODE(fdvp)->tn_mode, 200 1.1 riastrad VP_TO_TMPFS_NODE(fdvp)->tn_uid, 201 1.1 riastrad fvp, VP_TO_TMPFS_NODE(fvp)->tn_uid, 202 1.1 riastrad tdvp, VP_TO_TMPFS_NODE(tdvp)->tn_mode, 203 1.1 riastrad VP_TO_TMPFS_NODE(tdvp)->tn_uid, 204 1.1 riastrad tvp, (tvp? VP_TO_TMPFS_NODE(tvp)->tn_uid : 0)); 205 1.1 riastrad } 206 1.1 riastrad 207 1.1 riastrad /* 208 1.1 riastrad * tmpfs_gro_remove_check_possible: Check whether a remove is possible 209 1.1 riastrad * independent of credentials. 210 1.1 riastrad */ 211 1.1 riastrad static int 212 1.1 riastrad tmpfs_gro_remove_check_possible(struct mount *mp, 213 1.1 riastrad struct vnode *dvp, struct vnode *vp) 214 1.1 riastrad { 215 1.1 riastrad 216 1.1 riastrad (void)mp; 217 1.1 riastrad KASSERT(mp != NULL); 218 1.1 riastrad KASSERT(dvp != NULL); 219 1.1 riastrad KASSERT(vp != NULL); 220 1.1 riastrad KASSERT(dvp != vp); 221 1.1 riastrad KASSERT(dvp->v_type == VDIR); 222 1.1 riastrad KASSERT(vp->v_type != VDIR); 223 1.1 riastrad KASSERT(dvp->v_mount == mp); 224 1.1 riastrad KASSERT(vp->v_mount == mp); 225 1.1 riastrad KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 226 1.1 riastrad KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 227 1.1 riastrad 228 1.1 riastrad return genfs_ufslike_remove_check_possible( 229 1.1 riastrad VP_TO_TMPFS_NODE(dvp)->tn_flags, VP_TO_TMPFS_NODE(vp)->tn_flags, 230 1.1 riastrad IMMUTABLE, APPEND); 231 1.1 riastrad } 232 1.1 riastrad 233 1.1 riastrad /* 234 1.1 riastrad * tmpfs_gro_remove_check_permitted: Check whether a remove is 235 1.1 riastrad * permitted given our credentials. 236 1.1 riastrad */ 237 1.1 riastrad static int 238 1.1 riastrad tmpfs_gro_remove_check_permitted(struct mount *mp, kauth_cred_t cred, 239 1.1 riastrad struct vnode *dvp, struct vnode *vp) 240 1.1 riastrad { 241 1.1 riastrad 242 1.1 riastrad (void)mp; 243 1.1 riastrad KASSERT(mp != NULL); 244 1.1 riastrad KASSERT(dvp != NULL); 245 1.1 riastrad KASSERT(vp != NULL); 246 1.1 riastrad KASSERT(dvp != vp); 247 1.1 riastrad KASSERT(dvp->v_type == VDIR); 248 1.1 riastrad KASSERT(vp->v_type != VDIR); 249 1.1 riastrad KASSERT(dvp->v_mount == mp); 250 1.1 riastrad KASSERT(vp->v_mount == mp); 251 1.1 riastrad KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 252 1.1 riastrad KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 253 1.1 riastrad 254 1.1 riastrad return genfs_ufslike_remove_check_permitted(cred, 255 1.1 riastrad dvp, VP_TO_TMPFS_NODE(dvp)->tn_mode, VP_TO_TMPFS_NODE(dvp)->tn_uid, 256 1.1 riastrad vp, VP_TO_TMPFS_NODE(vp)->tn_uid); 257 1.1 riastrad } 258 1.1 riastrad 259 1.1 riastrad /* 260 1.1 riastrad * tmpfs_gro_rename: Actually perform the rename operation. 261 1.1 riastrad */ 262 1.1 riastrad static int 263 1.1 riastrad tmpfs_gro_rename(struct mount *mp, kauth_cred_t cred, 264 1.1 riastrad struct vnode *fdvp, struct componentname *fcnp, 265 1.1 riastrad void *fde, struct vnode *fvp, 266 1.1 riastrad struct vnode *tdvp, struct componentname *tcnp, 267 1.11 thorpej void *tde, struct vnode *tvp, nlink_t *tvp_nlinkp) 268 1.1 riastrad { 269 1.6 rmind tmpfs_node_t *fdnode = VP_TO_TMPFS_DIR(fdvp); 270 1.6 rmind tmpfs_node_t *tdnode = VP_TO_TMPFS_DIR(tdvp); 271 1.1 riastrad struct tmpfs_dirent **fdep = fde; 272 1.1 riastrad struct tmpfs_dirent **tdep = tde; 273 1.1 riastrad char *newname; 274 1.1 riastrad 275 1.1 riastrad (void)cred; 276 1.1 riastrad KASSERT(mp != NULL); 277 1.1 riastrad KASSERT(fdvp != NULL); 278 1.1 riastrad KASSERT(fcnp != NULL); 279 1.1 riastrad KASSERT(fdep != NULL); 280 1.1 riastrad KASSERT(fvp != NULL); 281 1.1 riastrad KASSERT(tdvp != NULL); 282 1.1 riastrad KASSERT(tcnp != NULL); 283 1.1 riastrad KASSERT(tdep != NULL); 284 1.1 riastrad KASSERT(fdep != tdep); 285 1.9 maxv KASSERT((tvp == NULL) || (*fdep) != (*tdep)); 286 1.1 riastrad KASSERT((*fdep) != NULL); 287 1.1 riastrad KASSERT((*fdep)->td_node == VP_TO_TMPFS_NODE(fvp)); 288 1.1 riastrad KASSERT((tvp == NULL) || ((*tdep) != NULL)); 289 1.1 riastrad KASSERT((tvp == NULL) || ((*tdep)->td_node == VP_TO_TMPFS_NODE(tvp))); 290 1.1 riastrad KASSERT(fdvp != fvp); 291 1.1 riastrad KASSERT(fdvp != tvp); 292 1.1 riastrad KASSERT(tdvp != fvp); 293 1.1 riastrad KASSERT(tdvp != tvp); 294 1.1 riastrad KASSERT(fvp != tvp); 295 1.1 riastrad KASSERT(fdvp->v_mount == mp); 296 1.1 riastrad KASSERT(fvp->v_mount == mp); 297 1.1 riastrad KASSERT(tdvp->v_mount == mp); 298 1.1 riastrad KASSERT((tvp == NULL) || (tvp->v_mount == mp)); 299 1.1 riastrad KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); 300 1.1 riastrad KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); 301 1.1 riastrad KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 302 1.1 riastrad KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 303 1.1 riastrad 304 1.1 riastrad if (tmpfs_strname_neqlen(fcnp, tcnp)) { 305 1.1 riastrad newname = tmpfs_strname_alloc(VFS_TO_TMPFS(mp), 306 1.1 riastrad tcnp->cn_namelen); 307 1.1 riastrad if (newname == NULL) 308 1.1 riastrad return ENOSPC; 309 1.1 riastrad } else { 310 1.1 riastrad newname = NULL; 311 1.1 riastrad } 312 1.1 riastrad 313 1.1 riastrad /* 314 1.1 riastrad * If we are moving from one directory to another, detach the 315 1.1 riastrad * source entry and reattach it to the target directory. 316 1.1 riastrad */ 317 1.1 riastrad if (fdvp != tdvp) { 318 1.5 rmind tmpfs_dir_detach(fdnode, *fdep); 319 1.5 rmind tmpfs_dir_attach(tdnode, *fdep, VP_TO_TMPFS_NODE(fvp)); 320 1.1 riastrad } 321 1.1 riastrad 322 1.1 riastrad /* 323 1.1 riastrad * If we are replacing an existing target entry, delete it. 324 1.1 riastrad * 325 1.1 riastrad * XXX What if the target is a directory with whiteout entries? 326 1.1 riastrad */ 327 1.1 riastrad if (tvp != NULL) { 328 1.6 rmind tdnode = VP_TO_TMPFS_DIR(tdvp); 329 1.5 rmind 330 1.1 riastrad KASSERT((*tdep) != NULL); 331 1.1 riastrad KASSERT((*tdep)->td_node == VP_TO_TMPFS_NODE(tvp)); 332 1.1 riastrad KASSERT((fvp->v_type == VDIR) == (tvp->v_type == VDIR)); 333 1.1 riastrad if (tvp->v_type == VDIR) { 334 1.1 riastrad KASSERT(VP_TO_TMPFS_NODE(tvp)->tn_size == 0); 335 1.1 riastrad KASSERT(VP_TO_TMPFS_NODE(tvp)->tn_links == 2); 336 1.1 riastrad 337 1.1 riastrad /* 338 1.1 riastrad * Decrement the extra link count for `.' so 339 1.1 riastrad * the vnode will be recycled when released. 340 1.1 riastrad */ 341 1.1 riastrad VP_TO_TMPFS_NODE(tvp)->tn_links--; 342 1.1 riastrad } 343 1.5 rmind tmpfs_dir_detach(tdnode, *tdep); 344 1.1 riastrad tmpfs_free_dirent(VFS_TO_TMPFS(mp), *tdep); 345 1.11 thorpej 346 1.11 thorpej *tvp_nlinkp = VP_TO_TMPFS_NODE(tvp)->tn_links; 347 1.1 riastrad } 348 1.1 riastrad 349 1.1 riastrad /* 350 1.1 riastrad * Update the directory entry's name if necessary, and flag 351 1.1 riastrad * metadata updates. A memory allocation failure here is not 352 1.1 riastrad * OK because we've already committed some changes that we 353 1.1 riastrad * can't back out at this point, hence the early allocation 354 1.1 riastrad * above. 355 1.1 riastrad */ 356 1.1 riastrad if (newname != NULL) { 357 1.1 riastrad KASSERT(tcnp->cn_namelen <= TMPFS_MAXNAMLEN); 358 1.1 riastrad 359 1.1 riastrad tmpfs_strname_free(VFS_TO_TMPFS(mp), (*fdep)->td_name, 360 1.1 riastrad (*fdep)->td_namelen); 361 1.1 riastrad (*fdep)->td_namelen = (uint16_t)tcnp->cn_namelen; 362 1.1 riastrad (void)memcpy(newname, tcnp->cn_nameptr, tcnp->cn_namelen); 363 1.1 riastrad (*fdep)->td_name = newname; 364 1.6 rmind } 365 1.1 riastrad 366 1.6 rmind /* 367 1.6 rmind * Update the timestamps of both parent directories and 368 1.6 rmind * the renamed file itself. 369 1.6 rmind */ 370 1.6 rmind tmpfs_update(fdvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); 371 1.6 rmind tmpfs_update(tdvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); 372 1.6 rmind tmpfs_update(fvp, TMPFS_UPDATE_CTIME); 373 1.1 riastrad 374 1.1 riastrad genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp); 375 1.1 riastrad 376 1.1 riastrad return 0; 377 1.1 riastrad } 378 1.1 riastrad 379 1.1 riastrad /* 380 1.1 riastrad * tmpfs_gro_remove: Rename an object over another link to itself, 381 1.1 riastrad * effectively removing just the original link. 382 1.1 riastrad */ 383 1.1 riastrad static int 384 1.1 riastrad tmpfs_gro_remove(struct mount *mp, kauth_cred_t cred, 385 1.11 thorpej struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp, 386 1.11 thorpej nlink_t *tvp_nlinkp) 387 1.1 riastrad { 388 1.5 rmind tmpfs_node_t *dnode = VP_TO_TMPFS_DIR(dvp); 389 1.1 riastrad struct tmpfs_dirent **dep = de; 390 1.1 riastrad 391 1.1 riastrad (void)vp; 392 1.1 riastrad KASSERT(mp != NULL); 393 1.1 riastrad KASSERT(dvp != NULL); 394 1.1 riastrad KASSERT(cnp != NULL); 395 1.1 riastrad KASSERT(dep != NULL); 396 1.1 riastrad KASSERT(vp != NULL); 397 1.1 riastrad KASSERT(dvp != vp); 398 1.1 riastrad KASSERT(dvp->v_mount == mp); 399 1.1 riastrad KASSERT(vp->v_mount == mp); 400 1.1 riastrad KASSERT(dvp->v_type == VDIR); 401 1.1 riastrad KASSERT(vp->v_type != VDIR); 402 1.1 riastrad KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 403 1.1 riastrad KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 404 1.1 riastrad 405 1.12 thorpej KASSERT((*dep)->td_node == VP_TO_TMPFS_NODE(vp)); 406 1.12 thorpej 407 1.5 rmind tmpfs_dir_detach(dnode, *dep); 408 1.1 riastrad tmpfs_free_dirent(VFS_TO_TMPFS(mp), *dep); 409 1.6 rmind tmpfs_update(dvp, TMPFS_UPDATE_MTIME | TMPFS_UPDATE_CTIME); 410 1.1 riastrad 411 1.11 thorpej *tvp_nlinkp = VP_TO_TMPFS_NODE(vp)->tn_links; 412 1.11 thorpej 413 1.1 riastrad return 0; 414 1.1 riastrad } 415 1.1 riastrad 416 1.1 riastrad /* 417 1.1 riastrad * tmpfs_gro_lookup: Look up and save the lookup results. 418 1.1 riastrad */ 419 1.1 riastrad static int 420 1.1 riastrad tmpfs_gro_lookup(struct mount *mp, struct vnode *dvp, 421 1.1 riastrad struct componentname *cnp, void *de_ret, struct vnode **vp_ret) 422 1.1 riastrad { 423 1.1 riastrad struct tmpfs_dirent *dirent, **dep_ret = de_ret; 424 1.1 riastrad struct vnode *vp; 425 1.10 riastrad int error; 426 1.1 riastrad 427 1.1 riastrad (void)mp; 428 1.1 riastrad KASSERT(mp != NULL); 429 1.1 riastrad KASSERT(dvp != NULL); 430 1.1 riastrad KASSERT(cnp != NULL); 431 1.1 riastrad KASSERT(dep_ret != NULL); 432 1.1 riastrad KASSERT(vp_ret != NULL); 433 1.1 riastrad KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 434 1.1 riastrad 435 1.1 riastrad dirent = tmpfs_dir_lookup(VP_TO_TMPFS_NODE(dvp), cnp); 436 1.1 riastrad if (dirent == NULL) 437 1.1 riastrad return ENOENT; 438 1.1 riastrad 439 1.7 hannken error = vcache_get(mp, &dirent->td_node, sizeof(dirent->td_node), &vp); 440 1.1 riastrad if (error) 441 1.1 riastrad return error; 442 1.1 riastrad KASSERT(vp != NULL); 443 1.1 riastrad 444 1.1 riastrad *dep_ret = dirent; 445 1.1 riastrad *vp_ret = vp; 446 1.1 riastrad return 0; 447 1.1 riastrad } 448 1.1 riastrad 449 1.1 riastrad /* 450 1.1 riastrad * tmpfs_rmdired_p: Check whether the directory vp has been rmdired. 451 1.1 riastrad * 452 1.1 riastrad * vp must be locked and referenced. 453 1.1 riastrad */ 454 1.1 riastrad static bool 455 1.1 riastrad tmpfs_rmdired_p(struct vnode *vp) 456 1.1 riastrad { 457 1.1 riastrad 458 1.1 riastrad KASSERT(vp != NULL); 459 1.1 riastrad KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 460 1.1 riastrad KASSERT(vp->v_type == VDIR); 461 1.1 riastrad 462 1.1 riastrad return (VP_TO_TMPFS_NODE(vp)->tn_spec.tn_dir.tn_parent == NULL); 463 1.1 riastrad } 464 1.1 riastrad 465 1.1 riastrad /* 466 1.1 riastrad * tmpfs_gro_genealogy: Analyze the genealogy of the source and target 467 1.1 riastrad * directories. 468 1.1 riastrad */ 469 1.1 riastrad static int 470 1.1 riastrad tmpfs_gro_genealogy(struct mount *mp, kauth_cred_t cred, 471 1.1 riastrad struct vnode *fdvp, struct vnode *tdvp, 472 1.1 riastrad struct vnode **intermediate_node_ret) 473 1.1 riastrad { 474 1.7 hannken struct vnode *vp, *ovp; 475 1.1 riastrad struct tmpfs_node *dnode; 476 1.1 riastrad int error; 477 1.1 riastrad 478 1.1 riastrad (void)cred; 479 1.1 riastrad KASSERT(mp != NULL); 480 1.1 riastrad KASSERT(fdvp != NULL); 481 1.1 riastrad KASSERT(tdvp != NULL); 482 1.1 riastrad KASSERT(fdvp != tdvp); 483 1.1 riastrad KASSERT(intermediate_node_ret != NULL); 484 1.1 riastrad KASSERT(fdvp->v_mount == mp); 485 1.1 riastrad KASSERT(tdvp->v_mount == mp); 486 1.1 riastrad KASSERT(fdvp->v_type == VDIR); 487 1.1 riastrad KASSERT(tdvp->v_type == VDIR); 488 1.1 riastrad 489 1.1 riastrad /* 490 1.1 riastrad * We need to provisionally lock tdvp to keep rmdir from 491 1.1 riastrad * deleting it -- or any ancestor -- at an inopportune moment. 492 1.1 riastrad */ 493 1.1 riastrad error = tmpfs_gro_lock_directory(mp, tdvp); 494 1.1 riastrad if (error) 495 1.1 riastrad return error; 496 1.1 riastrad 497 1.1 riastrad vp = tdvp; 498 1.1 riastrad vref(vp); 499 1.1 riastrad 500 1.1 riastrad for (;;) { 501 1.1 riastrad KASSERT(vp != NULL); 502 1.1 riastrad KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 503 1.1 riastrad KASSERT(vp->v_mount == mp); 504 1.1 riastrad KASSERT(vp->v_type == VDIR); 505 1.1 riastrad KASSERT(!tmpfs_rmdired_p(vp)); 506 1.1 riastrad 507 1.1 riastrad dnode = VP_TO_TMPFS_NODE(vp)->tn_spec.tn_dir.tn_parent; 508 1.1 riastrad 509 1.1 riastrad /* 510 1.1 riastrad * If dnode is null then vp has been rmdir'd, which is 511 1.1 riastrad * not supposed to happen because we have it locked. 512 1.1 riastrad */ 513 1.1 riastrad KASSERT(dnode != NULL); 514 1.1 riastrad 515 1.1 riastrad /* Did we hit the root without finding fdvp? */ 516 1.1 riastrad if (dnode == VP_TO_TMPFS_NODE(vp)) { 517 1.1 riastrad vput(vp); 518 1.1 riastrad *intermediate_node_ret = NULL; 519 1.1 riastrad return 0; 520 1.1 riastrad } 521 1.1 riastrad 522 1.1 riastrad /* Did we find that fdvp is an ancestor of tdvp? */ 523 1.1 riastrad if (dnode == VP_TO_TMPFS_NODE(fdvp)) { 524 1.1 riastrad KASSERT(dnode->tn_vnode == fdvp); 525 1.1 riastrad /* Unlock vp, but keep it referenced. */ 526 1.1 riastrad VOP_UNLOCK(vp); 527 1.1 riastrad *intermediate_node_ret = vp; 528 1.1 riastrad return 0; 529 1.1 riastrad } 530 1.1 riastrad 531 1.1 riastrad /* Neither -- keep ascending the family tree. */ 532 1.7 hannken ovp = vp; 533 1.7 hannken vp = NULL; 534 1.7 hannken error = vcache_get(mp, &dnode, sizeof(dnode), &vp); 535 1.7 hannken vput(ovp); 536 1.1 riastrad if (error) 537 1.1 riastrad return error; 538 1.7 hannken error = vn_lock(vp, LK_EXCLUSIVE); 539 1.7 hannken if (error) { 540 1.7 hannken vrele(vp); 541 1.7 hannken return error; 542 1.7 hannken } 543 1.3 riastrad 544 1.3 riastrad /* 545 1.7 hannken * vcache_get only guarantees that dnode will not 546 1.3 riastrad * be freed while we get a vnode for it. It does not 547 1.3 riastrad * preserve any other invariants, so we must check 548 1.3 riastrad * whether the parent has been removed in the meantime. 549 1.3 riastrad */ 550 1.3 riastrad if (tmpfs_rmdired_p(vp)) { 551 1.3 riastrad vput(vp); 552 1.3 riastrad return ENOENT; 553 1.3 riastrad } 554 1.1 riastrad } 555 1.1 riastrad } 556 1.1 riastrad 557 1.1 riastrad /* 558 1.1 riastrad * tmpfs_gro_lock_directory: Lock the directory vp, but fail if it has 559 1.1 riastrad * been rmdir'd. 560 1.1 riastrad */ 561 1.1 riastrad static int 562 1.1 riastrad tmpfs_gro_lock_directory(struct mount *mp, struct vnode *vp) 563 1.1 riastrad { 564 1.1 riastrad 565 1.1 riastrad (void)mp; 566 1.1 riastrad KASSERT(mp != NULL); 567 1.1 riastrad KASSERT(vp != NULL); 568 1.1 riastrad KASSERT(vp->v_mount == mp); 569 1.1 riastrad 570 1.1 riastrad vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 571 1.1 riastrad 572 1.1 riastrad if (tmpfs_rmdired_p(vp)) { 573 1.1 riastrad VOP_UNLOCK(vp); 574 1.1 riastrad return ENOENT; 575 1.1 riastrad } 576 1.1 riastrad 577 1.1 riastrad return 0; 578 1.1 riastrad } 579 1.1 riastrad 580 1.1 riastrad static const struct genfs_rename_ops tmpfs_genfs_rename_ops = { 581 1.1 riastrad .gro_directory_empty_p = tmpfs_gro_directory_empty_p, 582 1.1 riastrad .gro_rename_check_possible = tmpfs_gro_rename_check_possible, 583 1.1 riastrad .gro_rename_check_permitted = tmpfs_gro_rename_check_permitted, 584 1.1 riastrad .gro_remove_check_possible = tmpfs_gro_remove_check_possible, 585 1.1 riastrad .gro_remove_check_permitted = tmpfs_gro_remove_check_permitted, 586 1.1 riastrad .gro_rename = tmpfs_gro_rename, 587 1.1 riastrad .gro_remove = tmpfs_gro_remove, 588 1.1 riastrad .gro_lookup = tmpfs_gro_lookup, 589 1.1 riastrad .gro_genealogy = tmpfs_gro_genealogy, 590 1.1 riastrad .gro_lock_directory = tmpfs_gro_lock_directory, 591 1.1 riastrad }; 592