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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 | |