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