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