1 /*	$NetBSD: genfs_rename.c,v 1.2 2014/02/06 10:57:12 hannken 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  * Generic rename abstraction.
34  *
35  * Rename is unbelievably hairy.  Try to use this if you can --
36  * otherwise you are practically guaranteed to get it wrong.
37  */
38 
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: genfs_rename.c,v 1.2 2014/02/06 10:57:12 hannken Exp $");
41 
42 #include <sys/param.h>
43 #include <sys/kauth.h>
44 #include <sys/mount.h>
45 #include <sys/namei.h>
46 #include <sys/stat.h>
47 #include <sys/vnode.h>
48 #include <sys/fstrans.h>
49 #include <sys/types.h>
50 
51 #include <miscfs/genfs/genfs.h>
52 
53 /*
54  * Sample copypasta for implementing VOP_RENAME via genfs_rename.
55  * Don't change this template without carefully considering whether
56  * every other file system that already uses it needs to change too.
57  * That way, once we have changed all the file systems to use it, we
58  * can easily replace mumblefs_rename by mumblefs_sane_rename and
59  * eliminate the insane API altogether.
60  */
61 
62 /* begin sample copypasta */
63 #if 0
64 
65 static const struct genfs_rename_ops mumblefs_genfs_rename_ops;
66 
67 /*
68  * mumblefs_sane_rename: The hairiest vop, with the saner API.
69  *
70  * Arguments:
71  *
72  * . fdvp (from directory vnode),
73  * . fcnp (from component name),
74  * . tdvp (to directory vnode),
75  * . tcnp (to component name),
76  * . cred (credentials structure), and
77  * . posixly_correct (flag for behaviour if target & source link same file).
78  *
79  * fdvp and tdvp may be the same, and must be referenced and unlocked.
80  */
81 static int
82 mumblefs_sane_rename(
83     struct vnode *fdvp, struct componentname *fcnp,
84     struct vnode *tdvp, struct componentname *tcnp,
85     kauth_cred_t cred, bool posixly_correct)
86 {
87 	struct mumblefs_lookup_results fulr, tulr;
88 
89 	return genfs_sane_rename(&mumblefs_genfs_rename_ops,
90 	    fdvp, fcnp, &fulr, tdvp, tcnp, &tulr,
91 	    cred, posixly_correct);
92 }
93 
94 /*
95  * mumblefs_rename: The hairiest vop, with the insanest API.  Defer to
96  * genfs_insane_rename immediately.
97  */
98 int
99 mumblefs_rename(void *v)
100 {
101 
102 	return genfs_insane_rename(v, &mumblefs_sane_rename);
103 }
104 
105 #endif
106 /* end sample copypasta */
107 
108 /*
109  * Forward declarations
110  */
111 
112 static int genfs_rename_enter(const struct genfs_rename_ops *, struct mount *,
113     kauth_cred_t,
114     struct vnode *, struct componentname *, void *, struct vnode **,
115     struct vnode *, struct componentname *, void *, struct vnode **);
116 static int genfs_rename_enter_common(const struct genfs_rename_ops *,
117     struct mount *, kauth_cred_t, struct vnode *,
118     struct componentname *, void *, struct vnode **,
119     struct componentname *, void *, struct vnode **);
120 static int genfs_rename_enter_separate(const struct genfs_rename_ops *,
121     struct mount *, kauth_cred_t,
122     struct vnode *, struct componentname *, void *, struct vnode **,
123     struct vnode *, struct componentname *, void *, struct vnode **);
124 static int genfs_rename_lock(const struct genfs_rename_ops *, struct mount *,
125     kauth_cred_t, int, int, int,
126     struct vnode *, struct componentname *, bool, void *, struct vnode **,
127     struct vnode *, struct componentname *, bool, void *, struct vnode **);
128 static void genfs_rename_exit(const struct genfs_rename_ops *, struct mount *,
129     struct vnode *, struct vnode *,
130     struct vnode *, struct vnode *);
131 static int genfs_rename_remove(const struct genfs_rename_ops *, struct mount *,
132     kauth_cred_t,
133     struct vnode *, struct componentname *, void *, struct vnode *);
134 
135 /*
136  * genfs_insane_rename: Generic implementation of the insane API for
137  * the rename vop.
138  *
139  * Arguments:
140  *
141  * . fdvp (from directory vnode),
142  * . fvp (from vnode),
143  * . fcnp (from component name),
144  * . tdvp (to directory vnode),
145  * . tvp (to vnode, or NULL), and
146  * . tcnp (to component name).
147  *
148  * Any pair of vnode parameters may have the same vnode.
149  *
150  * On entry,
151  *
152  * . fdvp, fvp, tdvp, and tvp are referenced,
153  * . fdvp and fvp are unlocked, and
154  * . tdvp and tvp (if nonnull) are locked.
155  *
156  * On exit,
157  *
158  * . fdvp, fvp, tdvp, and tvp (if nonnull) are unreferenced, and
159  * . tdvp and tvp (if nonnull) are unlocked.
160  */
161 int
genfs_insane_rename(void * v,int (* sane_rename)(struct vnode * fdvp,struct componentname * fcnp,struct vnode * tdvp,struct componentname * tcnp,kauth_cred_t cred,bool posixly_correct))162 genfs_insane_rename(void *v,
163     int (*sane_rename)(struct vnode *fdvp, struct componentname *fcnp,
164 	struct vnode *tdvp, struct componentname *tcnp,
165 	kauth_cred_t cred, bool posixly_correct))
166 {
167 	struct vop_rename_args	/* {
168 		struct vnode *a_fdvp;
169 		struct vnode *a_fvp;
170 		struct componentname *a_fcnp;
171 		struct vnode *a_tdvp;
172 		struct vnode *a_tvp;
173 		struct componentname *a_tcnp;
174 	} */ *ap = v;
175 	struct vnode *fdvp = ap->a_fdvp;
176 	struct vnode *fvp = ap->a_fvp;
177 	struct componentname *fcnp = ap->a_fcnp;
178 	struct vnode *tdvp = ap->a_tdvp;
179 	struct vnode *tvp = ap->a_tvp;
180 	struct mount *mp = fdvp->v_mount;
181 	struct componentname *tcnp = ap->a_tcnp;
182 	kauth_cred_t cred;
183 	int error;
184 
185 	KASSERT(fdvp != NULL);
186 	KASSERT(fvp != NULL);
187 	KASSERT(fcnp != NULL);
188 	KASSERT(fcnp->cn_nameptr != NULL);
189 	KASSERT(tdvp != NULL);
190 	KASSERT(tcnp != NULL);
191 	KASSERT(fcnp->cn_nameptr != NULL);
192 	/* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */
193 	/* KASSERT(VOP_ISLOCKED(fvp) != LK_EXCLUSIVE); */
194 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
195 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
196 	KASSERT(fdvp->v_type == VDIR);
197 	KASSERT(tdvp->v_type == VDIR);
198 
199 	fstrans_start(mp, FSTRANS_SHARED);
200 
201 	cred = fcnp->cn_cred;
202 
203 	/*
204 	 * XXX Want a better equality test.  `tcnp->cn_cred == cred'
205 	 * hoses p2k because puffs transmits the creds separately and
206 	 * allocates distinct but equivalent structures for them.
207 	 */
208 	KASSERT(kauth_cred_uidmatch(cred, tcnp->cn_cred));
209 
210 	/*
211 	 * Sanitize our world from the VFS insanity.  Unlock the target
212 	 * directory and node, which are locked.  Release the children,
213 	 * which are referenced, since we'll be looking them up again
214 	 * later.
215 	 */
216 
217 	VOP_UNLOCK(tdvp);
218 	if ((tvp != NULL) && (tvp != tdvp))
219 		VOP_UNLOCK(tvp);
220 
221 	vrele(fvp);
222 	if (tvp != NULL)
223 		vrele(tvp);
224 
225 	error = (*sane_rename)(fdvp, fcnp, tdvp, tcnp, cred, false);
226 
227 	/*
228 	 * All done, whether with success or failure.  Release the
229 	 * directory nodes now, as the caller expects from the VFS
230 	 * protocol.
231 	 */
232 	vrele(fdvp);
233 	vrele(tdvp);
234 
235 	fstrans_done(mp);
236 
237 	return error;
238 }
239 
240 /*
241  * genfs_sane_rename: Generic implementation of the saner API for the
242  * rename vop.  Handles ancestry checks, locking, and permissions
243  * checks.  Caller is responsible for implementing the genfs rename
244  * operations.
245  *
246  * fdvp and tdvp must be referenced and unlocked.
247  */
248 int
genfs_sane_rename(const struct genfs_rename_ops * ops,struct vnode * fdvp,struct componentname * fcnp,void * fde,struct vnode * tdvp,struct componentname * tcnp,void * tde,kauth_cred_t cred,bool posixly_correct)249 genfs_sane_rename(const struct genfs_rename_ops *ops,
250     struct vnode *fdvp, struct componentname *fcnp, void *fde,
251     struct vnode *tdvp, struct componentname *tcnp, void *tde,
252     kauth_cred_t cred, bool posixly_correct)
253 {
254 	struct mount *mp;
255 	struct vnode *fvp = NULL, *tvp = NULL;
256 	int error;
257 
258 	KASSERT(ops != NULL);
259 	KASSERT(fdvp != NULL);
260 	KASSERT(fcnp != NULL);
261 	KASSERT(tdvp != NULL);
262 	KASSERT(tcnp != NULL);
263 	/* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */
264 	/* KASSERT(VOP_ISLOCKED(tdvp) != LK_EXCLUSIVE); */
265 	KASSERT(fdvp->v_type == VDIR);
266 	KASSERT(tdvp->v_type == VDIR);
267 	KASSERT(fdvp->v_mount == tdvp->v_mount);
268 	KASSERT(fcnp != tcnp);
269 	KASSERT(fcnp->cn_nameiop == DELETE);
270 	KASSERT(tcnp->cn_nameiop == RENAME);
271 
272         /* XXX Want a better equality test.  */
273 	KASSERT(kauth_cred_uidmatch(cred, fcnp->cn_cred));
274 	KASSERT(kauth_cred_uidmatch(cred, tcnp->cn_cred));
275 
276 	mp = fdvp->v_mount;
277 	KASSERT(mp != NULL);
278 	KASSERT(mp == tdvp->v_mount);
279 	/* XXX How can we be sure this stays true?  */
280 	KASSERT((mp->mnt_flag & MNT_RDONLY) == 0);
281 
282 	/* Reject rename("x/..", ...) and rename(..., "x/..") early.  */
283 	if ((fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT)
284 		return EINVAL;	/* XXX EISDIR?  */
285 
286 	error = genfs_rename_enter(ops, mp, cred,
287 	    fdvp, fcnp, fde, &fvp,
288 	    tdvp, tcnp, tde, &tvp);
289 	if (error)
290 		return error;
291 
292 	/*
293 	 * Check that everything is locked and looks right.
294 	 */
295 	KASSERT(fvp != NULL);
296 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
297 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
298 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
299 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
300 
301 	/*
302 	 * If the source and destination are the same object, we need
303 	 * only at most delete the source entry.  We are guaranteed at
304 	 * this point that the entries are distinct.
305 	 */
306 	if (fvp == tvp) {
307 		KASSERT(tvp != NULL);
308 		if (fvp->v_type == VDIR)
309 			/* XXX This shouldn't be possible.  */
310 			error = EINVAL;
311 		else if (posixly_correct)
312 			/* POSIX sez to leave them alone.  */
313 			error = 0;
314 		else if ((fdvp == tdvp) &&
315 		    (fcnp->cn_namelen == tcnp->cn_namelen) &&
316 		    (memcmp(fcnp->cn_nameptr, tcnp->cn_nameptr,
317 			fcnp->cn_namelen) == 0))
318 			/* Renaming an entry over itself does nothing.  */
319 			error = 0;
320 		else
321 			/* XXX Can't use VOP_REMOVE because of locking.  */
322 			error = genfs_rename_remove(ops, mp, cred,
323 			    fdvp, fcnp, fde, fvp);
324 		goto out;
325 	}
326 	KASSERT(fvp != tvp);
327 	KASSERT((fdvp != tdvp) ||
328 	    (fcnp->cn_namelen != tcnp->cn_namelen) ||
329 	    (memcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen)
330 		!= 0));
331 
332 	/*
333 	 * If the target exists, refuse to rename a directory over a
334 	 * non-directory or vice versa, or to clobber a non-empty
335 	 * directory.
336 	 */
337 	if (tvp != NULL) {
338 		if (fvp->v_type == VDIR && tvp->v_type == VDIR)
339 			error =
340 			    (ops->gro_directory_empty_p(mp, cred, tvp, tdvp)?
341 				0 : ENOTEMPTY);
342 		else if (fvp->v_type == VDIR && tvp->v_type != VDIR)
343 			error = ENOTDIR;
344 		else if (fvp->v_type != VDIR && tvp->v_type == VDIR)
345 			error = EISDIR;
346 		else
347 			error = 0;
348 		if (error)
349 			goto out;
350 		KASSERT((fvp->v_type == VDIR) == (tvp->v_type == VDIR));
351 	}
352 
353 	/*
354 	 * Authorize the rename.
355 	 */
356 	error = ops->gro_rename_check_possible(mp, fdvp, fvp, tdvp, tvp);
357 	if (error)
358 		goto out;
359 	error = ops->gro_rename_check_permitted(mp, cred, fdvp, fvp, tdvp, tvp);
360 	error = kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE, fvp, fdvp,
361 	    error);
362 	error = kauth_authorize_vnode(cred, KAUTH_VNODE_RENAME, tvp, tdvp,
363 	    error);
364 	if (error)
365 		goto out;
366 
367 	/*
368 	 * Everything is hunky-dory.  Shuffle the directory entries.
369 	 */
370 	error = ops->gro_rename(mp, cred,
371 	    fdvp, fcnp, fde, fvp,
372 	    tdvp, tcnp, tde, tvp);
373 	if (error)
374 		goto out;
375 
376 	/* Success!  */
377 
378 out:	genfs_rename_exit(ops, mp, fdvp, fvp, tdvp, tvp);
379 	return error;
380 }
381 
382 /*
383  * genfs_rename_knote: Note events about the various vnodes in a
384  * rename.  To be called by gro_rename on success.  The only pair of
385  * vnodes that may be identical is {fdvp, tdvp}.  deleted_p is true iff
386  * the rename overwrote the last link to tvp.
387  */
388 void
genfs_rename_knote(struct vnode * fdvp,struct vnode * fvp,struct vnode * tdvp,struct vnode * tvp,bool deleted_p)389 genfs_rename_knote(struct vnode *fdvp, struct vnode *fvp,
390     struct vnode *tdvp, struct vnode *tvp, bool deleted_p)
391 {
392 	long fdvp_events, tdvp_events;
393 	bool directory_p, reparent_p, replaced_p;
394 
395 	KASSERT(fdvp != NULL);
396 	KASSERT(fvp != NULL);
397 	KASSERT(tdvp != NULL);
398 	KASSERT(fdvp != fvp);
399 	KASSERT(fdvp != tvp);
400 	KASSERT(tdvp != fvp);
401 	KASSERT(tdvp != tvp);
402 	KASSERT(fvp != tvp);
403 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
404 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
405 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
406 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
407 
408 	directory_p = (fvp->v_type == VDIR);
409 	reparent_p = (fdvp != tdvp);
410 	replaced_p = (tvp != NULL);
411 
412 	KASSERT((tvp == NULL) || (directory_p == (tvp->v_type == VDIR)));
413 	KASSERT(!deleted_p || replaced_p);
414 
415 	fdvp_events = NOTE_WRITE;
416 	if (directory_p && reparent_p)
417 		fdvp_events |= NOTE_LINK;
418 	VN_KNOTE(fdvp, fdvp_events);
419 
420 	VN_KNOTE(fvp, NOTE_RENAME);
421 
422 	if (reparent_p) {
423 		tdvp_events = NOTE_WRITE;
424 		if (!replaced_p) {
425 			tdvp_events |= NOTE_EXTEND;
426 			if (directory_p)
427 				tdvp_events |= NOTE_LINK;
428 		}
429 		VN_KNOTE(tdvp, tdvp_events);
430 	}
431 
432 	if (replaced_p)
433 		VN_KNOTE(tvp, (deleted_p? NOTE_DELETE : NOTE_LINK));
434 }
435 
436 /*
437  * genfs_rename_cache_purge: Purge the name cache.  To be called by
438  * gro_rename on success.  The only pair of vnodes that may be
439  * identical is {fdvp, tdvp}.
440  */
441 void
genfs_rename_cache_purge(struct vnode * fdvp,struct vnode * fvp,struct vnode * tdvp,struct vnode * tvp)442 genfs_rename_cache_purge(struct vnode *fdvp, struct vnode *fvp,
443     struct vnode *tdvp, struct vnode *tvp)
444 {
445 
446 	KASSERT(fdvp != NULL);
447 	KASSERT(fvp != NULL);
448 	KASSERT(tdvp != NULL);
449 	KASSERT(fdvp != fvp);
450 	KASSERT(fdvp != tvp);
451 	KASSERT(tdvp != fvp);
452 	KASSERT(tdvp != tvp);
453 	KASSERT(fvp != tvp);
454 	KASSERT(fdvp->v_type == VDIR);
455 	KASSERT(tdvp->v_type == VDIR);
456 
457 	/*
458 	 * XXX What actually needs to be purged?
459 	 */
460 
461 	cache_purge(fdvp);
462 
463 	if (fvp->v_type == VDIR)
464 		cache_purge(fvp);
465 
466 	if (tdvp != fdvp)
467 		cache_purge(tdvp);
468 
469 	if ((tvp != NULL) && (tvp->v_type == VDIR))
470 		cache_purge(tvp);
471 }
472 
473 /*
474  * genfs_rename_enter: Look up fcnp in fdvp, and store the lookup
475  * results in *fde_ret and the associated vnode in *fvp_ret; fail if
476  * not found.  Look up tcnp in tdvp, and store the lookup results in
477  * *tde_ret and the associated vnode in *tvp_ret; store null instead if
478  * not found.  Fail if anything has been mounted on any of the nodes
479  * involved.
480  *
481  * fdvp and tdvp must be referenced.
482  *
483  * On entry, nothing is locked.
484  *
485  * On success, everything is locked, and *fvp_ret, and *tvp_ret if
486  * nonnull, are referenced.  The only pairs of vnodes that may be
487  * identical are {fdvp, tdvp} and {fvp, tvp}.
488  *
489  * On failure, everything remains as was.
490  *
491  * Locking everything including the source and target nodes is
492  * necessary to make sure that, e.g., link count updates are OK.  The
493  * locking order is, in general, ancestor-first, matching the order you
494  * need to use to look up a descendant anyway.
495  */
496 static int
genfs_rename_enter(const struct genfs_rename_ops * ops,struct mount * mp,kauth_cred_t cred,struct vnode * fdvp,struct componentname * fcnp,void * fde_ret,struct vnode ** fvp_ret,struct vnode * tdvp,struct componentname * tcnp,void * tde_ret,struct vnode ** tvp_ret)497 genfs_rename_enter(const struct genfs_rename_ops *ops,
498     struct mount *mp, kauth_cred_t cred,
499     struct vnode *fdvp, struct componentname *fcnp,
500     void *fde_ret, struct vnode **fvp_ret,
501     struct vnode *tdvp, struct componentname *tcnp,
502     void *tde_ret, struct vnode **tvp_ret)
503 {
504 	int error;
505 
506 	KASSERT(mp != NULL);
507 	KASSERT(fdvp != NULL);
508 	KASSERT(fcnp != NULL);
509 	KASSERT(fvp_ret != NULL);
510 	KASSERT(tdvp != NULL);
511 	KASSERT(tcnp != NULL);
512 	KASSERT(tvp_ret != NULL);
513 	KASSERT(fvp_ret != tvp_ret);
514 	KASSERT(fdvp->v_type == VDIR);
515 	KASSERT(tdvp->v_type == VDIR);
516 	KASSERT(fdvp->v_mount == mp);
517 	KASSERT(tdvp->v_mount == mp);
518 
519 	if (fdvp == tdvp)
520 		error = genfs_rename_enter_common(ops, mp, cred, fdvp,
521 		    fcnp, fde_ret, fvp_ret,
522 		    tcnp, tde_ret, tvp_ret);
523 	else
524 		error = genfs_rename_enter_separate(ops, mp, cred,
525 		    fdvp, fcnp, fde_ret, fvp_ret,
526 		    tdvp, tcnp, tde_ret, tvp_ret);
527 
528 	if (error)
529 		return error;
530 
531 	KASSERT(*fvp_ret != NULL);
532 	KASSERT(VOP_ISLOCKED(*fvp_ret) == LK_EXCLUSIVE);
533 	KASSERT((*tvp_ret == NULL) || (VOP_ISLOCKED(*tvp_ret) == LK_EXCLUSIVE));
534 	KASSERT(*fvp_ret != fdvp);
535 	KASSERT(*fvp_ret != tdvp);
536 	KASSERT(*tvp_ret != fdvp);
537 	KASSERT(*tvp_ret != tdvp);
538 	return 0;
539 }
540 
541 /*
542  * genfs_rename_enter_common: Lock and look up with a common
543  * source/target directory.
544  */
545 static int
genfs_rename_enter_common(const struct genfs_rename_ops * ops,struct mount * mp,kauth_cred_t cred,struct vnode * dvp,struct componentname * fcnp,void * fde_ret,struct vnode ** fvp_ret,struct componentname * tcnp,void * tde_ret,struct vnode ** tvp_ret)546 genfs_rename_enter_common(const struct genfs_rename_ops *ops,
547     struct mount *mp, kauth_cred_t cred, struct vnode *dvp,
548     struct componentname *fcnp,
549     void *fde_ret, struct vnode **fvp_ret,
550     struct componentname *tcnp,
551     void *tde_ret, struct vnode **tvp_ret)
552 {
553 	struct vnode *fvp, *tvp;
554 	int error;
555 
556 	KASSERT(ops != NULL);
557 	KASSERT(mp != NULL);
558 	KASSERT(dvp != NULL);
559 	KASSERT(fcnp != NULL);
560 	KASSERT(fvp_ret != NULL);
561 	KASSERT(tcnp != NULL);
562 	KASSERT(tvp_ret != NULL);
563 	KASSERT(dvp->v_type == VDIR);
564 	KASSERT(dvp->v_mount == mp);
565 
566 	error = ops->gro_lock_directory(mp, dvp);
567 	if (error)
568 		goto fail0;
569 
570 	/* Did we lose a race with mount?  */
571 	if (dvp->v_mountedhere != NULL) {
572 		error = EBUSY;
573 		goto fail1;
574 	}
575 
576 	KASSERT(fcnp->cn_nameiop == DELETE);
577 	error = ops->gro_lookup(mp, dvp, fcnp, fde_ret, &fvp);
578 	if (error)
579 		goto fail1;
580 
581 	KASSERT(fvp != NULL);
582 
583 	/* Refuse to rename `.'.  */
584 	if (fvp == dvp) {
585 		error = EINVAL;
586 		goto fail2;
587 	}
588 	KASSERT(fvp != dvp);
589 
590 	KASSERT(tcnp->cn_nameiop == RENAME);
591 	error = ops->gro_lookup(mp, dvp, tcnp, tde_ret, &tvp);
592 	if (error == ENOENT) {
593 		tvp = NULL;
594 	} else if (error) {
595 		goto fail2;
596 	} else {
597 		KASSERT(tvp != NULL);
598 
599 		/* Refuse to rename over `.'.  */
600 		if (tvp == dvp) {
601 			error = EISDIR; /* XXX EINVAL?  */
602 			goto fail2;
603 		}
604 	}
605 	KASSERT(tvp != dvp);
606 
607 	/*
608 	 * We've looked up both nodes.  Now lock them and check them.
609 	 */
610 
611 	vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY);
612 	KASSERT(fvp->v_mount == mp);
613 	/* Refuse to rename a mount point.  */
614 	if ((fvp->v_type == VDIR) && (fvp->v_mountedhere != NULL)) {
615 		error = EBUSY;
616 		goto fail3;
617 	}
618 
619 	if ((tvp != NULL) && (tvp != fvp)) {
620 		vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
621 		KASSERT(tvp->v_mount == mp);
622 		/* Refuse to rename over a mount point.  */
623 		if ((tvp->v_type == VDIR) && (tvp->v_mountedhere != NULL)) {
624 			error = EBUSY;
625 			goto fail4;
626 		}
627 	}
628 
629 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
630 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
631 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
632 
633 	*fvp_ret = fvp;
634 	*tvp_ret = tvp;
635 	return 0;
636 
637 fail4:	if ((tvp != NULL) && (tvp != fvp))
638 		VOP_UNLOCK(tvp);
639 fail3:	VOP_UNLOCK(fvp);
640 	if (tvp != NULL)
641 		vrele(tvp);
642 fail2:	vrele(fvp);
643 fail1:	VOP_UNLOCK(dvp);
644 fail0:	return error;
645 }
646 
647 /*
648  * genfs_rename_enter_separate: Lock and look up with separate source
649  * and target directories.
650  */
651 static int
genfs_rename_enter_separate(const struct genfs_rename_ops * ops,struct mount * mp,kauth_cred_t cred,struct vnode * fdvp,struct componentname * fcnp,void * fde_ret,struct vnode ** fvp_ret,struct vnode * tdvp,struct componentname * tcnp,void * tde_ret,struct vnode ** tvp_ret)652 genfs_rename_enter_separate(const struct genfs_rename_ops *ops,
653     struct mount *mp, kauth_cred_t cred,
654     struct vnode *fdvp, struct componentname *fcnp,
655     void *fde_ret, struct vnode **fvp_ret,
656     struct vnode *tdvp, struct componentname *tcnp,
657     void *tde_ret, struct vnode **tvp_ret)
658 {
659 	struct vnode *intermediate_node;
660 	struct vnode *fvp, *tvp;
661 	int error;
662 
663 	KASSERT(ops != NULL);
664 	KASSERT(mp != NULL);
665 	KASSERT(fdvp != NULL);
666 	KASSERT(fcnp != NULL);
667 	KASSERT(fvp_ret != NULL);
668 	KASSERT(tdvp != NULL);
669 	KASSERT(tcnp != NULL);
670 	KASSERT(tvp_ret != NULL);
671 	KASSERT(fdvp != tdvp);
672 	KASSERT(fcnp != tcnp);
673 	KASSERT(fcnp->cn_nameiop == DELETE);
674 	KASSERT(tcnp->cn_nameiop == RENAME);
675 	KASSERT(fvp_ret != tvp_ret);
676 	KASSERT(fdvp->v_type == VDIR);
677 	KASSERT(tdvp->v_type == VDIR);
678 	KASSERT(fdvp->v_mount == mp);
679 	KASSERT(tdvp->v_mount == mp);
680 
681 	error = ops->gro_genealogy(mp, cred, fdvp, tdvp, &intermediate_node);
682 	if (error)
683 		return error;
684 
685 	/*
686 	 * intermediate_node == NULL means fdvp is not an ancestor of tdvp.
687 	 */
688 	if (intermediate_node == NULL)
689 		error = genfs_rename_lock(ops, mp, cred,
690 		    ENOTEMPTY, EISDIR, EINVAL,
691 		    tdvp, tcnp, true, tde_ret, &tvp,
692 		    fdvp, fcnp, false, fde_ret, &fvp);
693 	else
694 		error = genfs_rename_lock(ops, mp, cred,
695 		    EINVAL, EISDIR, EINVAL,
696 		    fdvp, fcnp, false, fde_ret, &fvp,
697 		    tdvp, tcnp, true, tde_ret, &tvp);
698 	if (error)
699 		goto out;
700 
701 	KASSERT(fvp != NULL);
702 
703 	/*
704 	 * Reject rename("foo/bar", "foo/bar/baz/quux/zot").
705 	 */
706 	if (fvp == intermediate_node) {
707 		genfs_rename_exit(ops, mp, fdvp, fvp, tdvp, tvp);
708 		error = EINVAL;
709 		goto out;
710 	}
711 
712 	*fvp_ret = fvp;
713 	*tvp_ret = tvp;
714 	error = 0;
715 
716 out:	if (intermediate_node != NULL)
717 		vrele(intermediate_node);
718 	return error;
719 }
720 
721 /*
722  * genfs_rename_lock: Lock directories a and b, which must be distinct,
723  * and look up and lock nodes a and b.  Do a first and then b.
724  * Directory b may not be an ancestor of directory a, although
725  * directory a may be an ancestor of directory b.  Fail with
726  * overlap_error if node a is directory b.  Neither componentname may
727  * be `.' or `..'.
728  *
729  * a_dvp and b_dvp must be referenced.
730  *
731  * On entry, a_dvp and b_dvp are unlocked.
732  *
733  * On success,
734  * . a_dvp and b_dvp are locked,
735  * . *a_dirent_ret is filled with a directory entry whose node is
736  *     locked and referenced,
737  * . *b_vp_ret is filled with the corresponding vnode,
738  * . *b_dirent_ret is filled either with null or with a directory entry
739  *     whose node is locked and referenced,
740  * . *b_vp is filled either with null or with the corresponding vnode,
741  *     and
742  * . the only pair of vnodes that may be identical is a_vp and b_vp.
743  *
744  * On failure, a_dvp and b_dvp are left unlocked, and *a_dirent_ret,
745  * *a_vp, *b_dirent_ret, and *b_vp are left alone.
746  */
747 static int
genfs_rename_lock(const struct genfs_rename_ops * ops,struct mount * mp,kauth_cred_t cred,int overlap_error,int a_dot_error,int b_dot_error,struct vnode * a_dvp,struct componentname * a_cnp,bool a_missing_ok,void * a_de_ret,struct vnode ** a_vp_ret,struct vnode * b_dvp,struct componentname * b_cnp,bool b_missing_ok,void * b_de_ret,struct vnode ** b_vp_ret)748 genfs_rename_lock(const struct genfs_rename_ops *ops,
749     struct mount *mp, kauth_cred_t cred,
750     int overlap_error, int a_dot_error, int b_dot_error,
751     struct vnode *a_dvp, struct componentname *a_cnp, bool a_missing_ok,
752     void *a_de_ret, struct vnode **a_vp_ret,
753     struct vnode *b_dvp, struct componentname *b_cnp, bool b_missing_ok,
754     void *b_de_ret, struct vnode **b_vp_ret)
755 {
756 	struct vnode *a_vp, *b_vp;
757 	int error;
758 
759 	KASSERT(ops != NULL);
760 	KASSERT(mp != NULL);
761 	KASSERT(a_dvp != NULL);
762 	KASSERT(a_cnp != NULL);
763 	KASSERT(a_vp_ret != NULL);
764 	KASSERT(b_dvp != NULL);
765 	KASSERT(b_cnp != NULL);
766 	KASSERT(b_vp_ret != NULL);
767 	KASSERT(a_dvp != b_dvp);
768 	KASSERT(a_vp_ret != b_vp_ret);
769 	KASSERT(a_dvp->v_type == VDIR);
770 	KASSERT(b_dvp->v_type == VDIR);
771 	KASSERT(a_dvp->v_mount == mp);
772 	KASSERT(b_dvp->v_mount == mp);
773 	KASSERT(a_missing_ok != b_missing_ok);
774 
775 	error = ops->gro_lock_directory(mp, a_dvp);
776 	if (error)
777 		goto fail0;
778 
779 	/* Did we lose a race with mount?  */
780 	if (a_dvp->v_mountedhere != NULL) {
781 		error = EBUSY;
782 		goto fail1;
783 	}
784 
785 	error = ops->gro_lookup(mp, a_dvp, a_cnp, a_de_ret, &a_vp);
786 	if (error) {
787 		if (a_missing_ok && (error == ENOENT))
788 			a_vp = NULL;
789 		else
790 			goto fail1;
791 	} else {
792 		KASSERT(a_vp != NULL);
793 
794 		/* Refuse to rename (over) `.'.  */
795 		if (a_vp == a_dvp) {
796 			error = a_dot_error;
797 			goto fail2;
798 		}
799 
800 		if (a_vp == b_dvp) {
801 			error = overlap_error;
802 			goto fail2;
803 		}
804 	}
805 
806 	KASSERT(a_vp != a_dvp);
807 	KASSERT(a_vp != b_dvp);
808 
809 	error = ops->gro_lock_directory(mp, b_dvp);
810 	if (error)
811 		goto fail2;
812 
813 	/* Did we lose a race with mount?  */
814 	if (b_dvp->v_mountedhere != NULL) {
815 		error = EBUSY;
816 		goto fail3;
817 	}
818 
819 	error = ops->gro_lookup(mp, b_dvp, b_cnp, b_de_ret, &b_vp);
820 	if (error) {
821 		if (b_missing_ok && (error == ENOENT))
822 			b_vp = NULL;
823 		else
824 			goto fail3;
825 	} else {
826 		KASSERT(b_vp != NULL);
827 
828 		/* Refuse to rename (over) `.'.  */
829 		if (b_vp == b_dvp) {
830 			error = b_dot_error;
831 			goto fail4;
832 		}
833 
834 		/* b is not an ancestor of a.  */
835 		if (b_vp == a_dvp) {
836 			/*
837 			 * We have a directory hard link before us.
838 			 * XXX What error should this return?  EDEADLK?
839 			 * Panic?
840 			 */
841 			error = EIO;
842 			goto fail4;
843 		}
844 	}
845 	KASSERT(b_vp != b_dvp);
846 	KASSERT(b_vp != a_dvp);
847 
848 	/*
849 	 * We've looked up both nodes.  Now lock them and check them.
850 	 */
851 
852 	if (a_vp != NULL) {
853 		vn_lock(a_vp, LK_EXCLUSIVE | LK_RETRY);
854 		KASSERT(a_vp->v_mount == mp);
855 		/* Refuse to rename (over) a mount point.  */
856 		if ((a_vp->v_type == VDIR) && (a_vp->v_mountedhere != NULL)) {
857 			error = EBUSY;
858 			goto fail5;
859 		}
860 	}
861 
862 	if ((b_vp != NULL) && (b_vp != a_vp)) {
863 		vn_lock(b_vp, LK_EXCLUSIVE | LK_RETRY);
864 		KASSERT(b_vp->v_mount == mp);
865 		/* Refuse to rename (over) a mount point.  */
866 		if ((b_vp->v_type == VDIR) && (b_vp->v_mountedhere != NULL)) {
867 			error = EBUSY;
868 			goto fail6;
869 		}
870 	}
871 
872 	KASSERT(VOP_ISLOCKED(a_dvp) == LK_EXCLUSIVE);
873 	KASSERT(VOP_ISLOCKED(b_dvp) == LK_EXCLUSIVE);
874 	KASSERT(a_missing_ok || (a_vp != NULL));
875 	KASSERT(b_missing_ok || (b_vp != NULL));
876 	KASSERT((a_vp == NULL) || (VOP_ISLOCKED(a_vp) == LK_EXCLUSIVE));
877 	KASSERT((b_vp == NULL) || (VOP_ISLOCKED(b_vp) == LK_EXCLUSIVE));
878 
879 	*a_vp_ret = a_vp;
880 	*b_vp_ret = b_vp;
881 	return 0;
882 
883 fail6:	if ((b_vp != NULL) && (b_vp != a_vp))
884 		VOP_UNLOCK(b_vp);
885 fail5:	if (a_vp != NULL)
886 		VOP_UNLOCK(a_vp);
887 fail4:	if (b_vp != NULL)
888 		vrele(b_vp);
889 fail3:	VOP_UNLOCK(b_dvp);
890 fail2:	if (a_vp != NULL)
891 		vrele(a_vp);
892 fail1:	VOP_UNLOCK(a_dvp);
893 fail0:	return error;
894 }
895 
896 /*
897  * genfs_rename_exit: Unlock everything we locked for rename.
898  *
899  * fdvp and tdvp must be referenced.
900  *
901  * On entry, everything is locked, and fvp and tvp referenced.
902  *
903  * On exit, everything is unlocked, and fvp and tvp are released.
904  */
905 static void
genfs_rename_exit(const struct genfs_rename_ops * ops,struct mount * mp,struct vnode * fdvp,struct vnode * fvp,struct vnode * tdvp,struct vnode * tvp)906 genfs_rename_exit(const struct genfs_rename_ops *ops,
907     struct mount *mp,
908     struct vnode *fdvp, struct vnode *fvp,
909     struct vnode *tdvp, struct vnode *tvp)
910 {
911 
912 	(void)ops;
913 	KASSERT(ops != NULL);
914 	KASSERT(mp != NULL);
915 	KASSERT(fdvp != NULL);
916 	KASSERT(fvp != NULL);
917 	KASSERT(fdvp != fvp);
918 	KASSERT(fdvp != tvp);
919 	KASSERT(tdvp != tvp);
920 	KASSERT(tdvp != fvp);
921 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
922 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
923 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
924 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
925 
926 	if ((tvp != NULL) && (tvp != fvp))
927 		VOP_UNLOCK(tvp);
928 	VOP_UNLOCK(fvp);
929 	if (tvp != NULL)
930 		vrele(tvp);
931 	if (tdvp != fdvp)
932 		VOP_UNLOCK(tdvp);
933 	vrele(fvp);
934 	VOP_UNLOCK(fdvp);
935 }
936 
937 /*
938  * genfs_rename_remove: Remove the entry for the non-directory vp with
939  * componentname cnp from the directory dvp, using the lookup results
940  * de.  It is the responsibility of gro_remove to purge the name cache
941  * and note kevents.
942  *
943  * Everything must be locked and referenced.
944  */
945 static int
genfs_rename_remove(const struct genfs_rename_ops * ops,struct mount * mp,kauth_cred_t cred,struct vnode * dvp,struct componentname * cnp,void * de,struct vnode * vp)946 genfs_rename_remove(const struct genfs_rename_ops *ops,
947     struct mount *mp, kauth_cred_t cred,
948     struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp)
949 {
950 	int error;
951 
952 	KASSERT(ops != NULL);
953 	KASSERT(mp != NULL);
954 	KASSERT(dvp != NULL);
955 	KASSERT(cnp != NULL);
956 	KASSERT(vp != NULL);
957 	KASSERT(dvp != vp);
958 	KASSERT(dvp->v_type == VDIR);
959 	KASSERT(vp->v_type != VDIR);
960 	KASSERT(dvp->v_mount == mp);
961 	KASSERT(vp->v_mount == mp);
962 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
963 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
964 
965 	error = ops->gro_remove_check_possible(mp, dvp, vp);
966 	if (error)
967 		return error;
968 
969 	error = ops->gro_remove_check_permitted(mp, cred, dvp, vp);
970 	error = kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE, vp, dvp,
971 	    error);
972 	if (error)
973 		return error;
974 
975 	error = ops->gro_remove(mp, cred, dvp, cnp, de, vp);
976 	if (error)
977 		return error;
978 
979 	return 0;
980 }
981 
982 static int
983 genfs_ufslike_check_sticky(kauth_cred_t, mode_t, uid_t, struct vnode *, uid_t);
984 
985 /*
986  * genfs_ufslike_rename_check_possible: Check whether a rename is
987  * possible independent of credentials, assuming UFS-like inode flag
988  * semantics.  clobber_p is true iff the target node already exists.
989  */
990 int
genfs_ufslike_rename_check_possible(unsigned long fdflags,unsigned long fflags,unsigned long tdflags,unsigned long tflags,bool clobber_p,unsigned long immutable,unsigned long append)991 genfs_ufslike_rename_check_possible(
992     unsigned long fdflags, unsigned long fflags,
993     unsigned long tdflags, unsigned long tflags, bool clobber_p,
994     unsigned long immutable, unsigned long append)
995 {
996 
997 	if ((fdflags | fflags) & (immutable | append))
998 		return EPERM;
999 
1000 	if (tdflags & (immutable | (clobber_p? append : 0)))
1001 		return EPERM;
1002 
1003 	if (clobber_p && (tflags & (immutable | append)))
1004 		return EPERM;
1005 
1006 	return 0;
1007 }
1008 
1009 /*
1010  * genfs_ufslike_rename_check_permitted: Check whether a rename is
1011  * permitted given our credentials, assuming UFS-like permission and
1012  * ownership semantics.
1013  *
1014  * The only pair of vnodes that may be identical is {fdvp, tdvp}.
1015  *
1016  * Everything must be locked and referenced.
1017  */
1018 int
genfs_ufslike_rename_check_permitted(kauth_cred_t cred,struct vnode * fdvp,mode_t fdmode,uid_t fduid,struct vnode * fvp,uid_t fuid,struct vnode * tdvp,mode_t tdmode,uid_t tduid,struct vnode * tvp,uid_t tuid)1019 genfs_ufslike_rename_check_permitted(kauth_cred_t cred,
1020     struct vnode *fdvp, mode_t fdmode, uid_t fduid,
1021     struct vnode *fvp, uid_t fuid,
1022     struct vnode *tdvp, mode_t tdmode, uid_t tduid,
1023     struct vnode *tvp, uid_t tuid)
1024 {
1025 	int error;
1026 
1027 	KASSERT(fdvp != NULL);
1028 	KASSERT(fvp != NULL);
1029 	KASSERT(tdvp != NULL);
1030 	KASSERT(fdvp != fvp);
1031 	KASSERT(fdvp != tvp);
1032 	KASSERT(tdvp != fvp);
1033 	KASSERT(tdvp != tvp);
1034 	KASSERT(fvp != tvp);
1035 	KASSERT(fdvp->v_type == VDIR);
1036 	KASSERT(tdvp->v_type == VDIR);
1037 	KASSERT(fdvp->v_mount == fvp->v_mount);
1038 	KASSERT(fdvp->v_mount == tdvp->v_mount);
1039 	KASSERT((tvp == NULL) || (fdvp->v_mount == tvp->v_mount));
1040 	KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
1041 	KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
1042 	KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
1043 	KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
1044 
1045 	/*
1046 	 * We need to remove or change an entry in the source directory.
1047 	 */
1048 	error = VOP_ACCESS(fdvp, VWRITE, cred);
1049 	if (error)
1050 		return error;
1051 
1052 	/*
1053 	 * If we are changing directories, then we need to write to the
1054 	 * target directory to add or change an entry.  Also, if fvp is
1055 	 * a directory, we need to write to it to change its `..'
1056 	 * entry.
1057 	 */
1058 	if (fdvp != tdvp) {
1059 		error = VOP_ACCESS(tdvp, VWRITE, cred);
1060 		if (error)
1061 			return error;
1062 		if (fvp->v_type == VDIR) {
1063 			error = VOP_ACCESS(fvp, VWRITE, cred);
1064 			if (error)
1065 				return error;
1066 		}
1067 	}
1068 
1069 	error = genfs_ufslike_check_sticky(cred, fdmode, fduid, fvp, fuid);
1070 	if (error)
1071 		return error;
1072 
1073 	error = genfs_ufslike_check_sticky(cred, tdmode, tduid, tvp, tuid);
1074 	if (error)
1075 		return error;
1076 
1077 	return 0;
1078 }
1079 
1080 /*
1081  * genfs_ufslike_remove_check_possible: Check whether a remove is
1082  * possible independent of credentials, assuming UFS-like inode flag
1083  * semantics.
1084  */
1085 int
genfs_ufslike_remove_check_possible(unsigned long dflags,unsigned long flags,unsigned long immutable,unsigned long append)1086 genfs_ufslike_remove_check_possible(unsigned long dflags, unsigned long flags,
1087     unsigned long immutable, unsigned long append)
1088 {
1089 
1090 	/*
1091 	 * We want to delete the entry.  If the directory is immutable,
1092 	 * we can't write to it to delete the entry.  If the directory
1093 	 * is append-only, the only change we can make is to add
1094 	 * entries, so we can't delete entries.  If the node is
1095 	 * immutable, we can't change the links to it, so we can't
1096 	 * delete the entry.  If the node is append-only...well, this
1097 	 * is what UFS does.
1098 	 */
1099 	if ((dflags | flags) & (immutable | append))
1100 		return EPERM;
1101 
1102 	return 0;
1103 }
1104 
1105 /*
1106  * genfs_ufslike_remove_check_permitted: Check whether a remove is
1107  * permitted given our credentials, assuming UFS-like permission and
1108  * ownership semantics.
1109  *
1110  * Everything must be locked and referenced.
1111  */
1112 int
genfs_ufslike_remove_check_permitted(kauth_cred_t cred,struct vnode * dvp,mode_t dmode,uid_t duid,struct vnode * vp,uid_t uid)1113 genfs_ufslike_remove_check_permitted(kauth_cred_t cred,
1114     struct vnode *dvp, mode_t dmode, uid_t duid,
1115     struct vnode *vp, uid_t uid)
1116 {
1117 	int error;
1118 
1119 	KASSERT(dvp != NULL);
1120 	KASSERT(vp != NULL);
1121 	KASSERT(dvp != vp);
1122 	KASSERT(dvp->v_type == VDIR);
1123 	KASSERT(vp->v_type != VDIR);
1124 	KASSERT(dvp->v_mount == vp->v_mount);
1125 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
1126 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
1127 
1128 	/*
1129 	 * We need to write to the directory to remove from it.
1130 	 */
1131 	error = VOP_ACCESS(dvp, VWRITE, cred);
1132 	if (error)
1133 		return error;
1134 
1135 	error = genfs_ufslike_check_sticky(cred, dmode, duid, vp, uid);
1136 	if (error)
1137 		return error;
1138 
1139 	return 0;
1140 }
1141 
1142 /*
1143  * genfs_ufslike_check_sticky: Check whether a party with credentials
1144  * cred may change an entry in a sticky directory, assuming UFS-like
1145  * permission, ownership, and stickiness semantics: If the directory is
1146  * sticky and the entry exists, the user must own either the directory
1147  * or the entry's node in order to change the entry.
1148  *
1149  * Everything must be locked and referenced.
1150  */
1151 int
genfs_ufslike_check_sticky(kauth_cred_t cred,mode_t dmode,uid_t duid,struct vnode * vp,uid_t uid)1152 genfs_ufslike_check_sticky(kauth_cred_t cred, mode_t dmode, uid_t duid,
1153     struct vnode *vp, uid_t uid)
1154 {
1155 
1156 	if ((dmode & S_ISTXT) && (vp != NULL))
1157 		return genfs_can_sticky(cred, duid, uid);
1158 
1159 	return 0;
1160 }
1161