1 /* $NetBSD: genfs_rename.c,v 1.7 2021/10/20 13:29:06 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 * 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.7 2021/10/20 13:29:06 thorpej 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 *, nlink_t *);
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
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))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
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)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 nlink_t tvp_new_nlink = 0;
251 int error;
252
253 KASSERT(ops != NULL);
254 KASSERT(fdvp != NULL);
255 KASSERT(fcnp != NULL);
256 KASSERT(tdvp != NULL);
257 KASSERT(tcnp != NULL);
258 /* KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */
259 /* KASSERT(VOP_ISLOCKED(tdvp) != LK_EXCLUSIVE); */
260 KASSERT(fdvp->v_type == VDIR);
261 KASSERT(tdvp->v_type == VDIR);
262 KASSERT(fdvp->v_mount == tdvp->v_mount);
263 KASSERT(fcnp != tcnp);
264 KASSERT(fcnp->cn_nameiop == DELETE);
265 KASSERT(tcnp->cn_nameiop == RENAME);
266
267 /* XXX Want a better equality test. */
268 KASSERT(kauth_cred_uidmatch(cred, fcnp->cn_cred));
269 KASSERT(kauth_cred_uidmatch(cred, tcnp->cn_cred));
270
271 mp = fdvp->v_mount;
272 KASSERT(mp != NULL);
273 KASSERT(mp == tdvp->v_mount);
274 /* XXX How can we be sure this stays true? */
275 KASSERT((mp->mnt_flag & MNT_RDONLY) == 0);
276
277 /* Reject rename("x/..", ...) and rename(..., "x/..") early. */
278 if ((fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT)
279 return EINVAL; /* XXX EISDIR? */
280
281 error = genfs_rename_enter(ops, mp, cred,
282 fdvp, fcnp, fde, &fvp,
283 tdvp, tcnp, tde, &tvp);
284 if (error)
285 return error;
286
287 /*
288 * Check that everything is locked and looks right.
289 */
290 KASSERT(fvp != NULL);
291 KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
292 KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
293 KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
294 KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
295
296 /*
297 * If the source and destination are the same object, we need
298 * only at most delete the source entry. We are guaranteed at
299 * this point that the entries are distinct.
300 */
301 if (fvp == tvp) {
302 KASSERT(tvp != NULL);
303 if (fvp->v_type == VDIR)
304 /* XXX This shouldn't be possible. */
305 error = EINVAL;
306 else if (posixly_correct)
307 /* POSIX sez to leave them alone. */
308 error = 0;
309 else if ((fdvp == tdvp) &&
310 (fcnp->cn_namelen == tcnp->cn_namelen) &&
311 (memcmp(fcnp->cn_nameptr, tcnp->cn_nameptr,
312 fcnp->cn_namelen) == 0))
313 /* Renaming an entry over itself does nothing. */
314 error = 0;
315 else {
316 /* XXX Can't use VOP_REMOVE because of locking. */
317 error = genfs_rename_remove(ops, mp, cred,
318 fdvp, fcnp, fde, fvp, &tvp_new_nlink);
319 VN_KNOTE(fdvp, NOTE_WRITE);
320 VN_KNOTE(fvp,
321 tvp_new_nlink == 0 ? NOTE_DELETE : NOTE_LINK);
322 }
323 goto out;
324 }
325 KASSERT(fvp != tvp);
326 KASSERT((fdvp != tdvp) ||
327 (fcnp->cn_namelen != tcnp->cn_namelen) ||
328 (memcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen)
329 != 0));
330
331 /*
332 * If the target exists, refuse to rename a directory over a
333 * non-directory or vice versa, or to clobber a non-empty
334 * directory.
335 */
336 if (tvp != NULL) {
337 if (fvp->v_type == VDIR && tvp->v_type == VDIR)
338 error =
339 (ops->gro_directory_empty_p(mp, cred, tvp, tdvp)?
340 0 : ENOTEMPTY);
341 else if (fvp->v_type == VDIR && tvp->v_type != VDIR)
342 error = ENOTDIR;
343 else if (fvp->v_type != VDIR && tvp->v_type == VDIR)
344 error = EISDIR;
345 else
346 error = 0;
347 if (error)
348 goto out;
349 KASSERT((fvp->v_type == VDIR) == (tvp->v_type == VDIR));
350 }
351
352 /*
353 * Authorize the rename.
354 */
355 error = ops->gro_rename_check_possible(mp, fdvp, fvp, tdvp, tvp);
356 if (error)
357 goto out;
358 error = ops->gro_rename_check_permitted(mp, cred, fdvp, fvp, tdvp, tvp);
359 error = kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE, fvp, fdvp,
360 error);
361 error = kauth_authorize_vnode(cred, KAUTH_VNODE_RENAME, tvp, tdvp,
362 error);
363 if (error)
364 goto out;
365
366 /*
367 * Everything is hunky-dory. Shuffle the directory entries.
368 */
369 error = ops->gro_rename(mp, cred,
370 fdvp, fcnp, fde, fvp,
371 tdvp, tcnp, tde, tvp,
372 &tvp_new_nlink);
373 if (error)
374 goto out;
375
376 /* Success! */
377 genfs_rename_knote(fdvp, fvp, tdvp, tvp, tvp_new_nlink);
378
379 out:
380 genfs_rename_exit(ops, mp, fdvp, fvp, tdvp, tvp);
381 return error;
382 }
383
384 /*
385 * genfs_rename_knote: Note events about the various vnodes in a
386 * rename. To be called by gro_rename on success. The only pair of
387 * vnodes that may be identical is {fdvp, tdvp}. tvp_new_nlink is
388 * the resulting link count of tvp.
389 */
390 void
genfs_rename_knote(struct vnode * fdvp,struct vnode * fvp,struct vnode * tdvp,struct vnode * tvp,nlink_t tvp_new_nlink)391 genfs_rename_knote(struct vnode *fdvp, struct vnode *fvp,
392 struct vnode *tdvp, struct vnode *tvp, nlink_t tvp_new_nlink)
393 {
394 long fdvp_events, tdvp_events;
395 bool directory_p, reparent_p, replaced_p;
396
397 KASSERT(fdvp != NULL);
398 KASSERT(fvp != NULL);
399 KASSERT(tdvp != NULL);
400 KASSERT(fdvp != fvp);
401 KASSERT(fdvp != tvp);
402 KASSERT(tdvp != fvp);
403 KASSERT(tdvp != tvp);
404 KASSERT(fvp != tvp);
405 KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
406 KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
407 KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
408 KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
409
410 directory_p = (fvp->v_type == VDIR);
411 reparent_p = (fdvp != tdvp);
412 replaced_p = (tvp != NULL);
413
414 KASSERT((tvp == NULL) || (directory_p == (tvp->v_type == VDIR)));
415
416 fdvp_events = NOTE_WRITE;
417 if (directory_p && reparent_p)
418 fdvp_events |= NOTE_LINK;
419 VN_KNOTE(fdvp, fdvp_events);
420
421 VN_KNOTE(fvp, NOTE_RENAME);
422
423 if (reparent_p) {
424 tdvp_events = NOTE_WRITE;
425 if (!replaced_p) {
426 tdvp_events |= NOTE_EXTEND;
427 if (directory_p)
428 tdvp_events |= NOTE_LINK;
429 }
430 VN_KNOTE(tdvp, tdvp_events);
431 }
432
433 if (replaced_p)
434 VN_KNOTE(tvp, (tvp_new_nlink == 0 ? NOTE_DELETE : NOTE_LINK));
435 }
436
437 /*
438 * genfs_rename_cache_purge: Purge the name cache. To be called by
439 * gro_rename on success. The only pair of vnodes that may be
440 * identical is {fdvp, tdvp}.
441 */
442 void
genfs_rename_cache_purge(struct vnode * fdvp,struct vnode * fvp,struct vnode * tdvp,struct vnode * tvp)443 genfs_rename_cache_purge(struct vnode *fdvp, struct vnode *fvp,
444 struct vnode *tdvp, struct vnode *tvp)
445 {
446
447 KASSERT(fdvp != NULL);
448 KASSERT(fvp != NULL);
449 KASSERT(tdvp != NULL);
450 KASSERT(fdvp != fvp);
451 KASSERT(fdvp != tvp);
452 KASSERT(tdvp != fvp);
453 KASSERT(tdvp != tvp);
454 KASSERT(fvp != tvp);
455 KASSERT(fdvp->v_type == VDIR);
456 KASSERT(tdvp->v_type == VDIR);
457
458 /*
459 * XXX What actually needs to be purged?
460 */
461
462 cache_purge(fdvp);
463
464 if (fvp->v_type == VDIR)
465 cache_purge(fvp);
466
467 if (tdvp != fdvp)
468 cache_purge(tdvp);
469
470 if ((tvp != NULL) && (tvp->v_type == VDIR))
471 cache_purge(tvp);
472 }
473
474 /*
475 * genfs_rename_enter: Look up fcnp in fdvp, and store the lookup
476 * results in *fde_ret and the associated vnode in *fvp_ret; fail if
477 * not found. Look up tcnp in tdvp, and store the lookup results in
478 * *tde_ret and the associated vnode in *tvp_ret; store null instead if
479 * not found. Fail if anything has been mounted on any of the nodes
480 * involved.
481 *
482 * fdvp and tdvp must be referenced.
483 *
484 * On entry, nothing is locked.
485 *
486 * On success, everything is locked, and *fvp_ret, and *tvp_ret if
487 * nonnull, are referenced. The only pairs of vnodes that may be
488 * identical are {fdvp, tdvp} and {fvp, tvp}.
489 *
490 * On failure, everything remains as was.
491 *
492 * Locking everything including the source and target nodes is
493 * necessary to make sure that, e.g., link count updates are OK. The
494 * locking order is, in general, ancestor-first, matching the order you
495 * need to use to look up a descendant anyway.
496 */
497 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)498 genfs_rename_enter(const struct genfs_rename_ops *ops,
499 struct mount *mp, kauth_cred_t cred,
500 struct vnode *fdvp, struct componentname *fcnp,
501 void *fde_ret, struct vnode **fvp_ret,
502 struct vnode *tdvp, struct componentname *tcnp,
503 void *tde_ret, struct vnode **tvp_ret)
504 {
505 int error;
506
507 KASSERT(mp != NULL);
508 KASSERT(fdvp != NULL);
509 KASSERT(fcnp != NULL);
510 KASSERT(fvp_ret != NULL);
511 KASSERT(tdvp != NULL);
512 KASSERT(tcnp != NULL);
513 KASSERT(tvp_ret != NULL);
514 KASSERT(fvp_ret != tvp_ret);
515 KASSERT(fdvp->v_type == VDIR);
516 KASSERT(tdvp->v_type == VDIR);
517 KASSERT(fdvp->v_mount == mp);
518 KASSERT(tdvp->v_mount == mp);
519
520 if (fdvp == tdvp)
521 error = genfs_rename_enter_common(ops, mp, cred, fdvp,
522 fcnp, fde_ret, fvp_ret,
523 tcnp, tde_ret, tvp_ret);
524 else
525 error = genfs_rename_enter_separate(ops, mp, cred,
526 fdvp, fcnp, fde_ret, fvp_ret,
527 tdvp, tcnp, tde_ret, tvp_ret);
528
529 if (error)
530 return error;
531
532 KASSERT(*fvp_ret != NULL);
533 KASSERT(VOP_ISLOCKED(*fvp_ret) == LK_EXCLUSIVE);
534 KASSERT((*tvp_ret == NULL) || (VOP_ISLOCKED(*tvp_ret) == LK_EXCLUSIVE));
535 KASSERT(*fvp_ret != fdvp);
536 KASSERT(*fvp_ret != tdvp);
537 KASSERT(*tvp_ret != fdvp);
538 KASSERT(*tvp_ret != tdvp);
539 return 0;
540 }
541
542 /*
543 * genfs_rename_enter_common: Lock and look up with a common
544 * source/target directory.
545 */
546 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)547 genfs_rename_enter_common(const struct genfs_rename_ops *ops,
548 struct mount *mp, kauth_cred_t cred, struct vnode *dvp,
549 struct componentname *fcnp,
550 void *fde_ret, struct vnode **fvp_ret,
551 struct componentname *tcnp,
552 void *tde_ret, struct vnode **tvp_ret)
553 {
554 struct vnode *fvp, *tvp;
555 int error;
556
557 KASSERT(ops != NULL);
558 KASSERT(mp != NULL);
559 KASSERT(dvp != NULL);
560 KASSERT(fcnp != NULL);
561 KASSERT(fvp_ret != NULL);
562 KASSERT(tcnp != NULL);
563 KASSERT(tvp_ret != NULL);
564 KASSERT(dvp->v_type == VDIR);
565 KASSERT(dvp->v_mount == mp);
566
567 error = ops->gro_lock_directory(mp, dvp);
568 if (error)
569 goto fail0;
570
571 /* Did we lose a race with mount? */
572 if (dvp->v_mountedhere != NULL) {
573 error = EBUSY;
574 goto fail1;
575 }
576
577 KASSERT(fcnp->cn_nameiop == DELETE);
578 error = ops->gro_lookup(mp, dvp, fcnp, fde_ret, &fvp);
579 if (error)
580 goto fail1;
581
582 KASSERT(fvp != NULL);
583
584 /* Refuse to rename `.'. */
585 if (fvp == dvp) {
586 error = EINVAL;
587 goto fail2;
588 }
589 KASSERT(fvp != dvp);
590
591 KASSERT(tcnp->cn_nameiop == RENAME);
592 error = ops->gro_lookup(mp, dvp, tcnp, tde_ret, &tvp);
593 if (error == ENOENT) {
594 tvp = NULL;
595 } else if (error) {
596 goto fail2;
597 } else {
598 KASSERT(tvp != NULL);
599
600 /* Refuse to rename over `.'. */
601 if (tvp == dvp) {
602 error = EISDIR; /* XXX EINVAL? */
603 goto fail2;
604 }
605 }
606 KASSERT(tvp != dvp);
607
608 /*
609 * We've looked up both nodes. Now lock them and check them.
610 */
611
612 vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY);
613 KASSERT(fvp->v_mount == mp);
614 /* Refuse to rename a mount point. */
615 if ((fvp->v_type == VDIR) && (fvp->v_mountedhere != NULL)) {
616 error = EBUSY;
617 goto fail3;
618 }
619
620 if ((tvp != NULL) && (tvp != fvp)) {
621 vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
622 KASSERT(tvp->v_mount == mp);
623 /* Refuse to rename over a mount point. */
624 if ((tvp->v_type == VDIR) && (tvp->v_mountedhere != NULL)) {
625 error = EBUSY;
626 goto fail4;
627 }
628 }
629
630 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
631 KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
632 KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
633
634 *fvp_ret = fvp;
635 *tvp_ret = tvp;
636 return 0;
637
638 fail4: if ((tvp != NULL) && (tvp != fvp))
639 VOP_UNLOCK(tvp);
640 fail3: VOP_UNLOCK(fvp);
641 if (tvp != NULL)
642 vrele(tvp);
643 fail2: vrele(fvp);
644 fail1: VOP_UNLOCK(dvp);
645 fail0: return error;
646 }
647
648 /*
649 * genfs_rename_enter_separate: Lock and look up with separate source
650 * and target directories.
651 */
652 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)653 genfs_rename_enter_separate(const struct genfs_rename_ops *ops,
654 struct mount *mp, kauth_cred_t cred,
655 struct vnode *fdvp, struct componentname *fcnp,
656 void *fde_ret, struct vnode **fvp_ret,
657 struct vnode *tdvp, struct componentname *tcnp,
658 void *tde_ret, struct vnode **tvp_ret)
659 {
660 struct vnode *intermediate_node;
661 struct vnode *fvp, *tvp;
662 int error;
663
664 KASSERT(ops != NULL);
665 KASSERT(mp != NULL);
666 KASSERT(fdvp != NULL);
667 KASSERT(fcnp != NULL);
668 KASSERT(fvp_ret != NULL);
669 KASSERT(tdvp != NULL);
670 KASSERT(tcnp != NULL);
671 KASSERT(tvp_ret != NULL);
672 KASSERT(fdvp != tdvp);
673 KASSERT(fcnp != tcnp);
674 KASSERT(fcnp->cn_nameiop == DELETE);
675 KASSERT(tcnp->cn_nameiop == RENAME);
676 KASSERT(fvp_ret != tvp_ret);
677 KASSERT(fdvp->v_type == VDIR);
678 KASSERT(tdvp->v_type == VDIR);
679 KASSERT(fdvp->v_mount == mp);
680 KASSERT(tdvp->v_mount == mp);
681
682 error = ops->gro_genealogy(mp, cred, fdvp, tdvp, &intermediate_node);
683 if (error)
684 return error;
685
686 /*
687 * intermediate_node == NULL means fdvp is not an ancestor of tdvp.
688 */
689 if (intermediate_node == NULL)
690 error = genfs_rename_lock(ops, mp, cred,
691 ENOTEMPTY, EISDIR, EINVAL,
692 tdvp, tcnp, true, tde_ret, &tvp,
693 fdvp, fcnp, false, fde_ret, &fvp);
694 else
695 error = genfs_rename_lock(ops, mp, cred,
696 EINVAL, EISDIR, EINVAL,
697 fdvp, fcnp, false, fde_ret, &fvp,
698 tdvp, tcnp, true, tde_ret, &tvp);
699 if (error)
700 goto out;
701
702 KASSERT(fvp != NULL);
703
704 /*
705 * Reject rename("foo/bar", "foo/bar/baz/quux/zot").
706 */
707 if (fvp == intermediate_node) {
708 genfs_rename_exit(ops, mp, fdvp, fvp, tdvp, tvp);
709 error = EINVAL;
710 goto out;
711 }
712
713 *fvp_ret = fvp;
714 *tvp_ret = tvp;
715 error = 0;
716
717 out: if (intermediate_node != NULL)
718 vrele(intermediate_node);
719 return error;
720 }
721
722 /*
723 * genfs_rename_lock: Lookup and lock it all. The lock order is:
724 *
725 * a_dvp -> a_vp -> b_dvp -> b_vp,
726 *
727 * except if a_vp is a nondirectory in which case the lock order is:
728 *
729 * a_dvp -> b_dvp -> b_vp -> a_vp,
730 *
731 * which can't violate ancestor->descendant because a_vp has no
732 * descendants in this case. This edge case is necessary because some
733 * file systems can only lookup/lock/unlock, and we can't hold a_vp
734 * locked when we lookup/lock/unlock b_vp if they turn out to be the
735 * same, and we can't find out that they're the same until after the
736 * lookup.
737 *
738 * b_dvp must not be an ancestor of a_dvp, although a_dvp may be an
739 * ancestor of b_dvp.
740 *
741 * Fail with overlap_error if node a is directory b. Neither
742 * componentname may be `.' or `..'.
743 *
744 * a_dvp and b_dvp must be referenced.
745 *
746 * On entry, a_dvp and b_dvp are unlocked.
747 *
748 * On success,
749 * . a_dvp and b_dvp are locked,
750 * . *a_dirent_ret is filled with a directory entry whose node is
751 * locked and referenced,
752 * . *b_vp_ret is filled with the corresponding vnode,
753 * . *b_dirent_ret is filled either with null or with a directory entry
754 * whose node is locked and referenced,
755 * . *b_vp is filled either with null or with the corresponding vnode,
756 * and
757 * . the only pair of vnodes that may be identical is a_vp and b_vp.
758 *
759 * On failure, a_dvp and b_dvp are left unlocked, and *a_dirent_ret,
760 * *a_vp, *b_dirent_ret, and *b_vp are left alone.
761 */
762 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)763 genfs_rename_lock(const struct genfs_rename_ops *ops,
764 struct mount *mp, kauth_cred_t cred,
765 int overlap_error, int a_dot_error, int b_dot_error,
766 struct vnode *a_dvp, struct componentname *a_cnp, bool a_missing_ok,
767 void *a_de_ret, struct vnode **a_vp_ret,
768 struct vnode *b_dvp, struct componentname *b_cnp, bool b_missing_ok,
769 void *b_de_ret, struct vnode **b_vp_ret)
770 {
771 struct vnode *a_vp, *b_vp;
772 int error;
773
774 KASSERT(ops != NULL);
775 KASSERT(mp != NULL);
776 KASSERT(a_dvp != NULL);
777 KASSERT(a_cnp != NULL);
778 KASSERT(a_vp_ret != NULL);
779 KASSERT(b_dvp != NULL);
780 KASSERT(b_cnp != NULL);
781 KASSERT(b_vp_ret != NULL);
782 KASSERT(a_dvp != b_dvp);
783 KASSERT(a_vp_ret != b_vp_ret);
784 KASSERT(a_dvp->v_type == VDIR);
785 KASSERT(b_dvp->v_type == VDIR);
786 KASSERT(a_dvp->v_mount == mp);
787 KASSERT(b_dvp->v_mount == mp);
788 KASSERT(a_missing_ok != b_missing_ok);
789
790 /*
791 * 1. Lock a_dvp.
792 */
793 error = ops->gro_lock_directory(mp, a_dvp);
794 if (error)
795 goto fail0;
796
797 /* Did we lose a race with mount? */
798 if (a_dvp->v_mountedhere != NULL) {
799 error = EBUSY;
800 goto fail1;
801 }
802
803 /*
804 * 2. Lookup a_vp. May lock/unlock a_vp.
805 */
806 error = ops->gro_lookup(mp, a_dvp, a_cnp, a_de_ret, &a_vp);
807 if (error) {
808 if (a_missing_ok && (error == ENOENT))
809 a_vp = NULL;
810 else
811 goto fail1;
812 } else {
813 KASSERT(a_vp != NULL);
814
815 /* Refuse to rename (over) `.'. */
816 if (a_vp == a_dvp) {
817 error = a_dot_error;
818 goto fail2;
819 }
820
821 /* Reject rename("x", "x/y") or rename("x/y", "x"). */
822 if (a_vp == b_dvp) {
823 error = overlap_error;
824 goto fail2;
825 }
826 }
827
828 KASSERT(a_vp != a_dvp);
829 KASSERT(a_vp != b_dvp);
830
831 /*
832 * 3. Lock a_vp, if it is a directory.
833 *
834 * We already ruled out a_vp == a_dvp (i.e., a_cnp is `.'), so
835 * this is not locking against self, and we already ruled out
836 * a_vp == b_dvp, so this won't cause subsequent locking of
837 * b_dvp to lock against self.
838 *
839 * If a_vp is a nondirectory, we can't hold it when we lookup
840 * b_vp in case (a) the file system can only lookup/lock/unlock
841 * and (b) b_vp turns out to be the same file as a_vp due to
842 * hard links -- and we can't even detect that case until after
843 * we've looked up b_vp. Fortunately, if a_vp is a
844 * nondirectory, then it is a leaf, so we can safely lock it
845 * last.
846 */
847 if (a_vp != NULL && a_vp->v_type == VDIR) {
848 vn_lock(a_vp, LK_EXCLUSIVE | LK_RETRY);
849 KASSERT(a_vp->v_mount == mp);
850 /* Refuse to rename (over) a mount point. */
851 if (a_vp->v_mountedhere != NULL) {
852 error = EBUSY;
853 goto fail3;
854 }
855 }
856
857 /*
858 * 4. Lock b_dvp.
859 */
860 error = ops->gro_lock_directory(mp, b_dvp);
861 if (error)
862 goto fail3;
863
864 /* Did we lose a race with mount? */
865 if (b_dvp->v_mountedhere != NULL) {
866 error = EBUSY;
867 goto fail4;
868 }
869
870 /*
871 * 5. Lookup b_vp. May lock/unlock b_vp.
872 */
873 error = ops->gro_lookup(mp, b_dvp, b_cnp, b_de_ret, &b_vp);
874 if (error) {
875 if (b_missing_ok && (error == ENOENT))
876 b_vp = NULL;
877 else
878 goto fail4;
879 } else {
880 KASSERT(b_vp != NULL);
881
882 /* Refuse to rename (over) `.'. */
883 if (b_vp == b_dvp) {
884 error = b_dot_error;
885 goto fail5;
886 }
887
888 /*
889 * b_dvp must not be an ancestor of a_dvp, so if we
890 * find b_dvp/b_vp=a_dvp/a_vp something is wrong.
891 */
892 if (b_vp == a_dvp) {
893 /*
894 * We have a directory hard link before us.
895 * XXX What error should this return? EDEADLK?
896 * Panic?
897 */
898 error = EIO;
899 goto fail5;
900 }
901 }
902 KASSERT(b_vp != b_dvp);
903 KASSERT(b_vp != a_dvp);
904
905 /*
906 * 6. Lock a_vp, if it is a nondirectory.
907 *
908 * In this case a_vp is a leaf, so it is either equal to or
909 * incommensurate with b_vp, and so we can safely lock it at
910 * any point now.
911 */
912 if (a_vp != NULL && a_vp->v_type != VDIR) {
913 vn_lock(a_vp, LK_EXCLUSIVE | LK_RETRY);
914 KASSERT(a_vp->v_mount == mp);
915 /* (not a directory so can't have anything mounted here) */
916 }
917
918 /*
919 * 7. Lock b_vp, if it is not a_vp.
920 *
921 * b_vp and a_vp may the same inode if they are hard links to
922 * one another.
923 */
924 if ((b_vp != NULL) && (b_vp != a_vp)) {
925 vn_lock(b_vp, LK_EXCLUSIVE | LK_RETRY);
926 KASSERT(b_vp->v_mount == mp);
927 /* Refuse to rename (over) a mount point. */
928 if ((b_vp->v_type == VDIR) && (b_vp->v_mountedhere != NULL)) {
929 error = EBUSY;
930 goto fail6;
931 }
932 }
933
934 KASSERT(VOP_ISLOCKED(a_dvp) == LK_EXCLUSIVE);
935 KASSERT(VOP_ISLOCKED(b_dvp) == LK_EXCLUSIVE);
936 KASSERT(a_missing_ok || (a_vp != NULL));
937 KASSERT(b_missing_ok || (b_vp != NULL));
938 KASSERT((a_vp == NULL) || (VOP_ISLOCKED(a_vp) == LK_EXCLUSIVE));
939 KASSERT((b_vp == NULL) || (VOP_ISLOCKED(b_vp) == LK_EXCLUSIVE));
940
941 *a_vp_ret = a_vp;
942 *b_vp_ret = b_vp;
943 return 0;
944
945 fail6: if ((b_vp != NULL) && (b_vp != a_vp))
946 VOP_UNLOCK(b_vp);
947 if (a_vp != NULL && a_vp->v_type != VDIR)
948 VOP_UNLOCK(a_vp);
949 fail5: if (b_vp != NULL)
950 vrele(b_vp);
951 fail4: VOP_UNLOCK(b_dvp);
952 fail3: if (a_vp != NULL && a_vp->v_type == VDIR)
953 VOP_UNLOCK(a_vp);
954 fail2: if (a_vp != NULL)
955 vrele(a_vp);
956 fail1: VOP_UNLOCK(a_dvp);
957 fail0: return error;
958 }
959
960 /*
961 * genfs_rename_exit: Unlock everything we locked for rename.
962 *
963 * fdvp and tdvp must be referenced.
964 *
965 * On entry, everything is locked, and fvp and tvp referenced.
966 *
967 * On exit, everything is unlocked, and fvp and tvp are released.
968 */
969 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)970 genfs_rename_exit(const struct genfs_rename_ops *ops,
971 struct mount *mp,
972 struct vnode *fdvp, struct vnode *fvp,
973 struct vnode *tdvp, struct vnode *tvp)
974 {
975
976 (void)ops;
977 KASSERT(ops != NULL);
978 KASSERT(mp != NULL);
979 KASSERT(fdvp != NULL);
980 KASSERT(fvp != NULL);
981 KASSERT(fdvp != fvp);
982 KASSERT(fdvp != tvp);
983 KASSERT(tdvp != tvp);
984 KASSERT(tdvp != fvp);
985 KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
986 KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
987 KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
988 KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
989
990 if ((tvp != NULL) && (tvp != fvp))
991 VOP_UNLOCK(tvp);
992 VOP_UNLOCK(fvp);
993 if (tvp != NULL)
994 vrele(tvp);
995 if (tdvp != fdvp)
996 VOP_UNLOCK(tdvp);
997 vrele(fvp);
998 VOP_UNLOCK(fdvp);
999 }
1000
1001 /*
1002 * genfs_rename_remove: Remove the entry for the non-directory vp with
1003 * componentname cnp from the directory dvp, using the lookup results
1004 * de. It is the responsibility of gro_remove to purge the name cache.
1005 *
1006 * Everything must be locked and referenced.
1007 */
1008 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,nlink_t * tvp_nlinkp)1009 genfs_rename_remove(const struct genfs_rename_ops *ops,
1010 struct mount *mp, kauth_cred_t cred,
1011 struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp,
1012 nlink_t *tvp_nlinkp)
1013 {
1014 int error;
1015
1016 KASSERT(ops != NULL);
1017 KASSERT(mp != NULL);
1018 KASSERT(dvp != NULL);
1019 KASSERT(cnp != NULL);
1020 KASSERT(vp != NULL);
1021 KASSERT(dvp != vp);
1022 KASSERT(dvp->v_type == VDIR);
1023 KASSERT(vp->v_type != VDIR);
1024 KASSERT(dvp->v_mount == mp);
1025 KASSERT(vp->v_mount == mp);
1026 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
1027 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
1028
1029 error = ops->gro_remove_check_possible(mp, dvp, vp);
1030 if (error)
1031 return error;
1032
1033 error = ops->gro_remove_check_permitted(mp, cred, dvp, vp);
1034 error = kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE, vp, dvp,
1035 error);
1036 if (error)
1037 return error;
1038
1039 error = ops->gro_remove(mp, cred, dvp, cnp, de, vp, tvp_nlinkp);
1040 if (error)
1041 return error;
1042
1043 return 0;
1044 }
1045
1046 static int
1047 genfs_ufslike_check_sticky(kauth_cred_t, mode_t, uid_t, struct vnode *, uid_t);
1048
1049 /*
1050 * genfs_ufslike_rename_check_possible: Check whether a rename is
1051 * possible independent of credentials, assuming UFS-like inode flag
1052 * semantics. clobber_p is true iff the target node already exists.
1053 */
1054 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)1055 genfs_ufslike_rename_check_possible(
1056 unsigned long fdflags, unsigned long fflags,
1057 unsigned long tdflags, unsigned long tflags, bool clobber_p,
1058 unsigned long immutable, unsigned long append)
1059 {
1060
1061 if ((fdflags | fflags) & (immutable | append))
1062 return EPERM;
1063
1064 if (tdflags & (immutable | (clobber_p? append : 0)))
1065 return EPERM;
1066
1067 if (clobber_p && (tflags & (immutable | append)))
1068 return EPERM;
1069
1070 return 0;
1071 }
1072
1073 /*
1074 * genfs_ufslike_rename_check_permitted: Check whether a rename is
1075 * permitted given our credentials, assuming UFS-like permission and
1076 * ownership semantics.
1077 *
1078 * The only pair of vnodes that may be identical is {fdvp, tdvp}.
1079 *
1080 * Everything must be locked and referenced.
1081 */
1082 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)1083 genfs_ufslike_rename_check_permitted(kauth_cred_t cred,
1084 struct vnode *fdvp, mode_t fdmode, uid_t fduid,
1085 struct vnode *fvp, uid_t fuid,
1086 struct vnode *tdvp, mode_t tdmode, uid_t tduid,
1087 struct vnode *tvp, uid_t tuid)
1088 {
1089 int error;
1090
1091 KASSERT(fdvp != NULL);
1092 KASSERT(fvp != NULL);
1093 KASSERT(tdvp != NULL);
1094 KASSERT(fdvp != fvp);
1095 KASSERT(fdvp != tvp);
1096 KASSERT(tdvp != fvp);
1097 KASSERT(tdvp != tvp);
1098 KASSERT(fvp != tvp);
1099 KASSERT(fdvp->v_type == VDIR);
1100 KASSERT(tdvp->v_type == VDIR);
1101 KASSERT(fdvp->v_mount == fvp->v_mount);
1102 KASSERT(fdvp->v_mount == tdvp->v_mount);
1103 KASSERT((tvp == NULL) || (fdvp->v_mount == tvp->v_mount));
1104 KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
1105 KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
1106 KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
1107 KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
1108
1109 /*
1110 * We need to remove or change an entry in the source directory.
1111 */
1112 error = VOP_ACCESS(fdvp, VWRITE, cred);
1113 if (error)
1114 return error;
1115
1116 /*
1117 * If we are changing directories, then we need to write to the
1118 * target directory to add or change an entry. Also, if fvp is
1119 * a directory, we need to write to it to change its `..'
1120 * entry.
1121 */
1122 if (fdvp != tdvp) {
1123 error = VOP_ACCESS(tdvp, VWRITE, cred);
1124 if (error)
1125 return error;
1126 if (fvp->v_type == VDIR) {
1127 error = VOP_ACCESS(fvp, VWRITE, cred);
1128 if (error)
1129 return error;
1130 }
1131 }
1132
1133 error = genfs_ufslike_check_sticky(cred, fdmode, fduid, fvp, fuid);
1134 if (error)
1135 return error;
1136
1137 error = genfs_ufslike_check_sticky(cred, tdmode, tduid, tvp, tuid);
1138 if (error)
1139 return error;
1140
1141 return 0;
1142 }
1143
1144 /*
1145 * genfs_ufslike_remove_check_possible: Check whether a remove is
1146 * possible independent of credentials, assuming UFS-like inode flag
1147 * semantics.
1148 */
1149 int
genfs_ufslike_remove_check_possible(unsigned long dflags,unsigned long flags,unsigned long immutable,unsigned long append)1150 genfs_ufslike_remove_check_possible(unsigned long dflags, unsigned long flags,
1151 unsigned long immutable, unsigned long append)
1152 {
1153
1154 /*
1155 * We want to delete the entry. If the directory is immutable,
1156 * we can't write to it to delete the entry. If the directory
1157 * is append-only, the only change we can make is to add
1158 * entries, so we can't delete entries. If the node is
1159 * immutable, we can't change the links to it, so we can't
1160 * delete the entry. If the node is append-only...well, this
1161 * is what UFS does.
1162 */
1163 if ((dflags | flags) & (immutable | append))
1164 return EPERM;
1165
1166 return 0;
1167 }
1168
1169 /*
1170 * genfs_ufslike_remove_check_permitted: Check whether a remove is
1171 * permitted given our credentials, assuming UFS-like permission and
1172 * ownership semantics.
1173 *
1174 * Everything must be locked and referenced.
1175 */
1176 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)1177 genfs_ufslike_remove_check_permitted(kauth_cred_t cred,
1178 struct vnode *dvp, mode_t dmode, uid_t duid,
1179 struct vnode *vp, uid_t uid)
1180 {
1181 int error;
1182
1183 KASSERT(dvp != NULL);
1184 KASSERT(vp != NULL);
1185 KASSERT(dvp != vp);
1186 KASSERT(dvp->v_type == VDIR);
1187 KASSERT(vp->v_type != VDIR);
1188 KASSERT(dvp->v_mount == vp->v_mount);
1189 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
1190 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
1191
1192 /*
1193 * We need to write to the directory to remove from it.
1194 */
1195 error = VOP_ACCESS(dvp, VWRITE, cred);
1196 if (error)
1197 return error;
1198
1199 error = genfs_ufslike_check_sticky(cred, dmode, duid, vp, uid);
1200 if (error)
1201 return error;
1202
1203 return 0;
1204 }
1205
1206 /*
1207 * genfs_ufslike_check_sticky: Check whether a party with credentials
1208 * cred may change an entry in a sticky directory, assuming UFS-like
1209 * permission, ownership, and stickiness semantics: If the directory is
1210 * sticky and the entry exists, the user must own either the directory
1211 * or the entry's node in order to change the entry.
1212 *
1213 * Everything must be locked and referenced.
1214 */
1215 int
genfs_ufslike_check_sticky(kauth_cred_t cred,mode_t dmode,uid_t duid,struct vnode * vp,uid_t uid)1216 genfs_ufslike_check_sticky(kauth_cred_t cred, mode_t dmode, uid_t duid,
1217 struct vnode *vp, uid_t uid)
1218 {
1219
1220 if ((dmode & S_ISTXT) && (vp != NULL))
1221 return genfs_can_sticky(vp, cred, duid, uid);
1222
1223 return 0;
1224 }
1225