1 | /* $NetBSD: genfs_vnops.c,v 1.192 2014/03/24 13:42:40 hannken Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
17 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
19 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
20 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 | * POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | /* |
30 | * Copyright (c) 1982, 1986, 1989, 1993 |
31 | * The Regents of the University of California. All rights reserved. |
32 | * |
33 | * Redistribution and use in source and binary forms, with or without |
34 | * modification, are permitted provided that the following conditions |
35 | * are met: |
36 | * 1. Redistributions of source code must retain the above copyright |
37 | * notice, this list of conditions and the following disclaimer. |
38 | * 2. Redistributions in binary form must reproduce the above copyright |
39 | * notice, this list of conditions and the following disclaimer in the |
40 | * documentation and/or other materials provided with the distribution. |
41 | * 3. Neither the name of the University nor the names of its contributors |
42 | * may be used to endorse or promote products derived from this software |
43 | * without specific prior written permission. |
44 | * |
45 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
46 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
47 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
48 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
49 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
50 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
51 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
52 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
53 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
54 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
55 | * SUCH DAMAGE. |
56 | * |
57 | */ |
58 | |
59 | #include <sys/cdefs.h> |
60 | __KERNEL_RCSID(0, "$NetBSD: genfs_vnops.c,v 1.192 2014/03/24 13:42:40 hannken Exp $" ); |
61 | |
62 | #include <sys/param.h> |
63 | #include <sys/systm.h> |
64 | #include <sys/proc.h> |
65 | #include <sys/kernel.h> |
66 | #include <sys/mount.h> |
67 | #include <sys/fstrans.h> |
68 | #include <sys/namei.h> |
69 | #include <sys/vnode.h> |
70 | #include <sys/fcntl.h> |
71 | #include <sys/kmem.h> |
72 | #include <sys/poll.h> |
73 | #include <sys/mman.h> |
74 | #include <sys/file.h> |
75 | #include <sys/kauth.h> |
76 | #include <sys/stat.h> |
77 | |
78 | #include <miscfs/genfs/genfs.h> |
79 | #include <miscfs/genfs/genfs_node.h> |
80 | #include <miscfs/specfs/specdev.h> |
81 | |
82 | #include <uvm/uvm.h> |
83 | #include <uvm/uvm_pager.h> |
84 | |
85 | static void filt_genfsdetach(struct knote *); |
86 | static int filt_genfsread(struct knote *, long); |
87 | static int filt_genfsvnode(struct knote *, long); |
88 | |
89 | int |
90 | genfs_poll(void *v) |
91 | { |
92 | struct vop_poll_args /* { |
93 | struct vnode *a_vp; |
94 | int a_events; |
95 | struct lwp *a_l; |
96 | } */ *ap = v; |
97 | |
98 | return (ap->a_events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)); |
99 | } |
100 | |
101 | int |
102 | genfs_seek(void *v) |
103 | { |
104 | struct vop_seek_args /* { |
105 | struct vnode *a_vp; |
106 | off_t a_oldoff; |
107 | off_t a_newoff; |
108 | kauth_cred_t cred; |
109 | } */ *ap = v; |
110 | |
111 | if (ap->a_newoff < 0) |
112 | return (EINVAL); |
113 | |
114 | return (0); |
115 | } |
116 | |
117 | int |
118 | genfs_abortop(void *v) |
119 | { |
120 | struct vop_abortop_args /* { |
121 | struct vnode *a_dvp; |
122 | struct componentname *a_cnp; |
123 | } */ *ap = v; |
124 | |
125 | (void)ap; |
126 | |
127 | return (0); |
128 | } |
129 | |
130 | int |
131 | genfs_fcntl(void *v) |
132 | { |
133 | struct vop_fcntl_args /* { |
134 | struct vnode *a_vp; |
135 | u_int a_command; |
136 | void *a_data; |
137 | int a_fflag; |
138 | kauth_cred_t a_cred; |
139 | struct lwp *a_l; |
140 | } */ *ap = v; |
141 | |
142 | if (ap->a_command == F_SETFL) |
143 | return (0); |
144 | else |
145 | return (EOPNOTSUPP); |
146 | } |
147 | |
148 | /*ARGSUSED*/ |
149 | int |
150 | genfs_badop(void *v) |
151 | { |
152 | |
153 | panic("genfs: bad op" ); |
154 | } |
155 | |
156 | /*ARGSUSED*/ |
157 | int |
158 | genfs_nullop(void *v) |
159 | { |
160 | |
161 | return (0); |
162 | } |
163 | |
164 | /*ARGSUSED*/ |
165 | int |
166 | genfs_einval(void *v) |
167 | { |
168 | |
169 | return (EINVAL); |
170 | } |
171 | |
172 | /* |
173 | * Called when an fs doesn't support a particular vop. |
174 | * This takes care to vrele, vput, or vunlock passed in vnodes |
175 | * and calls VOP_ABORTOP for a componentname (in non-rename VOP). |
176 | */ |
177 | int |
178 | genfs_eopnotsupp(void *v) |
179 | { |
180 | struct vop_generic_args /* |
181 | struct vnodeop_desc *a_desc; |
182 | / * other random data follows, presumably * / |
183 | } */ *ap = v; |
184 | struct vnodeop_desc *desc = ap->a_desc; |
185 | struct vnode *vp, *vp_last = NULL; |
186 | int flags, i, j, offset_cnp, offset_vp; |
187 | |
188 | KASSERT(desc->vdesc_offset != VOP_LOOKUP_DESCOFFSET); |
189 | KASSERT(desc->vdesc_offset != VOP_ABORTOP_DESCOFFSET); |
190 | |
191 | /* |
192 | * Abort any componentname that lookup potentially left state in. |
193 | * |
194 | * As is logical, componentnames for VOP_RENAME are handled by |
195 | * the caller of VOP_RENAME. Yay, rename! |
196 | */ |
197 | if (desc->vdesc_offset != VOP_RENAME_DESCOFFSET && |
198 | (offset_vp = desc->vdesc_vp_offsets[0]) != VDESC_NO_OFFSET && |
199 | (offset_cnp = desc->vdesc_componentname_offset) != VDESC_NO_OFFSET){ |
200 | struct componentname *cnp; |
201 | struct vnode *dvp; |
202 | |
203 | dvp = *VOPARG_OFFSETTO(struct vnode **, offset_vp, ap); |
204 | cnp = *VOPARG_OFFSETTO(struct componentname **, offset_cnp, ap); |
205 | |
206 | VOP_ABORTOP(dvp, cnp); |
207 | } |
208 | |
209 | flags = desc->vdesc_flags; |
210 | for (i = 0; i < VDESC_MAX_VPS; flags >>=1, i++) { |
211 | if ((offset_vp = desc->vdesc_vp_offsets[i]) == VDESC_NO_OFFSET) |
212 | break; /* stop at end of list */ |
213 | if ((j = flags & VDESC_VP0_WILLPUT)) { |
214 | vp = *VOPARG_OFFSETTO(struct vnode **, offset_vp, ap); |
215 | |
216 | /* Skip if NULL */ |
217 | if (!vp) |
218 | continue; |
219 | |
220 | switch (j) { |
221 | case VDESC_VP0_WILLPUT: |
222 | /* Check for dvp == vp cases */ |
223 | if (vp == vp_last) |
224 | vrele(vp); |
225 | else { |
226 | vput(vp); |
227 | vp_last = vp; |
228 | } |
229 | break; |
230 | case VDESC_VP0_WILLUNLOCK: |
231 | VOP_UNLOCK(vp); |
232 | break; |
233 | case VDESC_VP0_WILLRELE: |
234 | vrele(vp); |
235 | break; |
236 | } |
237 | } |
238 | } |
239 | |
240 | return (EOPNOTSUPP); |
241 | } |
242 | |
243 | /*ARGSUSED*/ |
244 | int |
245 | genfs_ebadf(void *v) |
246 | { |
247 | |
248 | return (EBADF); |
249 | } |
250 | |
251 | /* ARGSUSED */ |
252 | int |
253 | genfs_enoioctl(void *v) |
254 | { |
255 | |
256 | return (EPASSTHROUGH); |
257 | } |
258 | |
259 | |
260 | /* |
261 | * Eliminate all activity associated with the requested vnode |
262 | * and with all vnodes aliased to the requested vnode. |
263 | */ |
264 | int |
265 | genfs_revoke(void *v) |
266 | { |
267 | struct vop_revoke_args /* { |
268 | struct vnode *a_vp; |
269 | int a_flags; |
270 | } */ *ap = v; |
271 | |
272 | #ifdef DIAGNOSTIC |
273 | if ((ap->a_flags & REVOKEALL) == 0) |
274 | panic("genfs_revoke: not revokeall" ); |
275 | #endif |
276 | vrevoke(ap->a_vp); |
277 | return (0); |
278 | } |
279 | |
280 | /* |
281 | * Lock the node (for deadfs). |
282 | */ |
283 | int |
284 | genfs_deadlock(void *v) |
285 | { |
286 | struct vop_lock_args /* { |
287 | struct vnode *a_vp; |
288 | int a_flags; |
289 | } */ *ap = v; |
290 | struct vnode *vp = ap->a_vp; |
291 | int flags = ap->a_flags; |
292 | krw_t op; |
293 | int error; |
294 | |
295 | op = (ISSET(flags, LK_EXCLUSIVE) ? RW_WRITER : RW_READER); |
296 | if (ISSET(flags, LK_NOWAIT)) { |
297 | if (! rw_tryenter(&vp->v_lock, op)) |
298 | return EBUSY; |
299 | if (mutex_tryenter(vp->v_interlock)) { |
300 | error = vdead_check(vp, VDEAD_NOWAIT); |
301 | if (error == ENOENT && ISSET(flags, LK_RETRY)) |
302 | error = 0; |
303 | mutex_exit(vp->v_interlock); |
304 | } else |
305 | error = EBUSY; |
306 | if (error) |
307 | rw_exit(&vp->v_lock); |
308 | return error; |
309 | } |
310 | |
311 | rw_enter(&vp->v_lock, op); |
312 | mutex_enter(vp->v_interlock); |
313 | error = vdead_check(vp, VDEAD_NOWAIT); |
314 | if (error == EBUSY) { |
315 | rw_exit(&vp->v_lock); |
316 | error = vdead_check(vp, 0); |
317 | KASSERT(error == ENOENT); |
318 | mutex_exit(vp->v_interlock); |
319 | rw_enter(&vp->v_lock, op); |
320 | mutex_enter(vp->v_interlock); |
321 | } |
322 | KASSERT(error == ENOENT); |
323 | mutex_exit(vp->v_interlock); |
324 | if (! ISSET(flags, LK_RETRY)) { |
325 | rw_exit(&vp->v_lock); |
326 | return ENOENT; |
327 | } |
328 | return 0; |
329 | } |
330 | |
331 | /* |
332 | * Unlock the node (for deadfs). |
333 | */ |
334 | int |
335 | genfs_deadunlock(void *v) |
336 | { |
337 | struct vop_unlock_args /* { |
338 | struct vnode *a_vp; |
339 | } */ *ap = v; |
340 | struct vnode *vp = ap->a_vp; |
341 | |
342 | rw_exit(&vp->v_lock); |
343 | |
344 | return 0; |
345 | } |
346 | |
347 | /* |
348 | * Lock the node. |
349 | */ |
350 | int |
351 | genfs_lock(void *v) |
352 | { |
353 | struct vop_lock_args /* { |
354 | struct vnode *a_vp; |
355 | int a_flags; |
356 | } */ *ap = v; |
357 | struct vnode *vp = ap->a_vp; |
358 | struct mount *mp = vp->v_mount; |
359 | int flags = ap->a_flags; |
360 | krw_t op; |
361 | int error; |
362 | |
363 | op = (ISSET(flags, LK_EXCLUSIVE) ? RW_WRITER : RW_READER); |
364 | if (ISSET(flags, LK_NOWAIT)) { |
365 | if (fstrans_start_nowait(mp, FSTRANS_SHARED)) |
366 | return EBUSY; |
367 | if (! rw_tryenter(&vp->v_lock, op)) { |
368 | fstrans_done(mp); |
369 | return EBUSY; |
370 | } |
371 | if (mutex_tryenter(vp->v_interlock)) { |
372 | error = vdead_check(vp, VDEAD_NOWAIT); |
373 | mutex_exit(vp->v_interlock); |
374 | } else |
375 | error = EBUSY; |
376 | if (error) { |
377 | rw_exit(&vp->v_lock); |
378 | fstrans_done(mp); |
379 | } |
380 | return error; |
381 | } |
382 | |
383 | fstrans_start(mp, FSTRANS_SHARED); |
384 | rw_enter(&vp->v_lock, op); |
385 | mutex_enter(vp->v_interlock); |
386 | error = vdead_check(vp, VDEAD_NOWAIT); |
387 | if (error) { |
388 | rw_exit(&vp->v_lock); |
389 | fstrans_done(mp); |
390 | error = vdead_check(vp, 0); |
391 | KASSERT(error == ENOENT); |
392 | } |
393 | mutex_exit(vp->v_interlock); |
394 | return error; |
395 | } |
396 | |
397 | /* |
398 | * Unlock the node. |
399 | */ |
400 | int |
401 | genfs_unlock(void *v) |
402 | { |
403 | struct vop_unlock_args /* { |
404 | struct vnode *a_vp; |
405 | } */ *ap = v; |
406 | struct vnode *vp = ap->a_vp; |
407 | struct mount *mp = vp->v_mount; |
408 | |
409 | rw_exit(&vp->v_lock); |
410 | fstrans_done(mp); |
411 | |
412 | return 0; |
413 | } |
414 | |
415 | /* |
416 | * Return whether or not the node is locked. |
417 | */ |
418 | int |
419 | genfs_islocked(void *v) |
420 | { |
421 | struct vop_islocked_args /* { |
422 | struct vnode *a_vp; |
423 | } */ *ap = v; |
424 | struct vnode *vp = ap->a_vp; |
425 | |
426 | if (rw_write_held(&vp->v_lock)) |
427 | return LK_EXCLUSIVE; |
428 | |
429 | if (rw_read_held(&vp->v_lock)) |
430 | return LK_SHARED; |
431 | |
432 | return 0; |
433 | } |
434 | |
435 | /* |
436 | * Stubs to use when there is no locking to be done on the underlying object. |
437 | */ |
438 | int |
439 | genfs_nolock(void *v) |
440 | { |
441 | |
442 | return (0); |
443 | } |
444 | |
445 | int |
446 | genfs_nounlock(void *v) |
447 | { |
448 | |
449 | return (0); |
450 | } |
451 | |
452 | int |
453 | genfs_noislocked(void *v) |
454 | { |
455 | |
456 | return (0); |
457 | } |
458 | |
459 | int |
460 | genfs_mmap(void *v) |
461 | { |
462 | |
463 | return (0); |
464 | } |
465 | |
466 | /* |
467 | * VOP_PUTPAGES() for vnodes which never have pages. |
468 | */ |
469 | |
470 | int |
471 | genfs_null_putpages(void *v) |
472 | { |
473 | struct vop_putpages_args /* { |
474 | struct vnode *a_vp; |
475 | voff_t a_offlo; |
476 | voff_t a_offhi; |
477 | int a_flags; |
478 | } */ *ap = v; |
479 | struct vnode *vp = ap->a_vp; |
480 | |
481 | KASSERT(vp->v_uobj.uo_npages == 0); |
482 | mutex_exit(vp->v_interlock); |
483 | return (0); |
484 | } |
485 | |
486 | void |
487 | genfs_node_init(struct vnode *vp, const struct genfs_ops *ops) |
488 | { |
489 | struct genfs_node *gp = VTOG(vp); |
490 | |
491 | rw_init(&gp->g_glock); |
492 | gp->g_op = ops; |
493 | } |
494 | |
495 | void |
496 | genfs_node_destroy(struct vnode *vp) |
497 | { |
498 | struct genfs_node *gp = VTOG(vp); |
499 | |
500 | rw_destroy(&gp->g_glock); |
501 | } |
502 | |
503 | void |
504 | genfs_size(struct vnode *vp, off_t size, off_t *eobp, int flags) |
505 | { |
506 | int bsize; |
507 | |
508 | bsize = 1 << vp->v_mount->mnt_fs_bshift; |
509 | *eobp = (size + bsize - 1) & ~(bsize - 1); |
510 | } |
511 | |
512 | static void |
513 | filt_genfsdetach(struct knote *kn) |
514 | { |
515 | struct vnode *vp = (struct vnode *)kn->kn_hook; |
516 | |
517 | mutex_enter(vp->v_interlock); |
518 | SLIST_REMOVE(&vp->v_klist, kn, knote, kn_selnext); |
519 | mutex_exit(vp->v_interlock); |
520 | } |
521 | |
522 | static int |
523 | filt_genfsread(struct knote *kn, long hint) |
524 | { |
525 | struct vnode *vp = (struct vnode *)kn->kn_hook; |
526 | int rv; |
527 | |
528 | /* |
529 | * filesystem is gone, so set the EOF flag and schedule |
530 | * the knote for deletion. |
531 | */ |
532 | switch (hint) { |
533 | case NOTE_REVOKE: |
534 | KASSERT(mutex_owned(vp->v_interlock)); |
535 | kn->kn_flags |= (EV_EOF | EV_ONESHOT); |
536 | return (1); |
537 | case 0: |
538 | mutex_enter(vp->v_interlock); |
539 | kn->kn_data = vp->v_size - ((file_t *)kn->kn_obj)->f_offset; |
540 | rv = (kn->kn_data != 0); |
541 | mutex_exit(vp->v_interlock); |
542 | return rv; |
543 | default: |
544 | KASSERT(mutex_owned(vp->v_interlock)); |
545 | kn->kn_data = vp->v_size - ((file_t *)kn->kn_obj)->f_offset; |
546 | return (kn->kn_data != 0); |
547 | } |
548 | } |
549 | |
550 | static int |
551 | filt_genfsvnode(struct knote *kn, long hint) |
552 | { |
553 | struct vnode *vp = (struct vnode *)kn->kn_hook; |
554 | int fflags; |
555 | |
556 | switch (hint) { |
557 | case NOTE_REVOKE: |
558 | KASSERT(mutex_owned(vp->v_interlock)); |
559 | kn->kn_flags |= EV_EOF; |
560 | if ((kn->kn_sfflags & hint) != 0) |
561 | kn->kn_fflags |= hint; |
562 | return (1); |
563 | case 0: |
564 | mutex_enter(vp->v_interlock); |
565 | fflags = kn->kn_fflags; |
566 | mutex_exit(vp->v_interlock); |
567 | break; |
568 | default: |
569 | KASSERT(mutex_owned(vp->v_interlock)); |
570 | if ((kn->kn_sfflags & hint) != 0) |
571 | kn->kn_fflags |= hint; |
572 | fflags = kn->kn_fflags; |
573 | break; |
574 | } |
575 | |
576 | return (fflags != 0); |
577 | } |
578 | |
579 | static const struct filterops genfsread_filtops = |
580 | { 1, NULL, filt_genfsdetach, filt_genfsread }; |
581 | static const struct filterops genfsvnode_filtops = |
582 | { 1, NULL, filt_genfsdetach, filt_genfsvnode }; |
583 | |
584 | int |
585 | genfs_kqfilter(void *v) |
586 | { |
587 | struct vop_kqfilter_args /* { |
588 | struct vnode *a_vp; |
589 | struct knote *a_kn; |
590 | } */ *ap = v; |
591 | struct vnode *vp; |
592 | struct knote *kn; |
593 | |
594 | vp = ap->a_vp; |
595 | kn = ap->a_kn; |
596 | switch (kn->kn_filter) { |
597 | case EVFILT_READ: |
598 | kn->kn_fop = &genfsread_filtops; |
599 | break; |
600 | case EVFILT_VNODE: |
601 | kn->kn_fop = &genfsvnode_filtops; |
602 | break; |
603 | default: |
604 | return (EINVAL); |
605 | } |
606 | |
607 | kn->kn_hook = vp; |
608 | |
609 | mutex_enter(vp->v_interlock); |
610 | SLIST_INSERT_HEAD(&vp->v_klist, kn, kn_selnext); |
611 | mutex_exit(vp->v_interlock); |
612 | |
613 | return (0); |
614 | } |
615 | |
616 | void |
617 | genfs_node_wrlock(struct vnode *vp) |
618 | { |
619 | struct genfs_node *gp = VTOG(vp); |
620 | |
621 | rw_enter(&gp->g_glock, RW_WRITER); |
622 | } |
623 | |
624 | void |
625 | genfs_node_rdlock(struct vnode *vp) |
626 | { |
627 | struct genfs_node *gp = VTOG(vp); |
628 | |
629 | rw_enter(&gp->g_glock, RW_READER); |
630 | } |
631 | |
632 | int |
633 | genfs_node_rdtrylock(struct vnode *vp) |
634 | { |
635 | struct genfs_node *gp = VTOG(vp); |
636 | |
637 | return rw_tryenter(&gp->g_glock, RW_READER); |
638 | } |
639 | |
640 | void |
641 | genfs_node_unlock(struct vnode *vp) |
642 | { |
643 | struct genfs_node *gp = VTOG(vp); |
644 | |
645 | rw_exit(&gp->g_glock); |
646 | } |
647 | |
648 | int |
649 | genfs_node_wrlocked(struct vnode *vp) |
650 | { |
651 | struct genfs_node *gp = VTOG(vp); |
652 | |
653 | return rw_write_held(&gp->g_glock); |
654 | } |
655 | |
656 | /* |
657 | * Do the usual access checking. |
658 | * file_mode, uid and gid are from the vnode in question, |
659 | * while acc_mode and cred are from the VOP_ACCESS parameter list |
660 | */ |
661 | int |
662 | genfs_can_access(enum vtype type, mode_t file_mode, uid_t uid, gid_t gid, |
663 | mode_t acc_mode, kauth_cred_t cred) |
664 | { |
665 | mode_t mask; |
666 | int error, ismember; |
667 | |
668 | mask = 0; |
669 | |
670 | /* Otherwise, check the owner. */ |
671 | if (kauth_cred_geteuid(cred) == uid) { |
672 | if (acc_mode & VEXEC) |
673 | mask |= S_IXUSR; |
674 | if (acc_mode & VREAD) |
675 | mask |= S_IRUSR; |
676 | if (acc_mode & VWRITE) |
677 | mask |= S_IWUSR; |
678 | return ((file_mode & mask) == mask ? 0 : EACCES); |
679 | } |
680 | |
681 | /* Otherwise, check the groups. */ |
682 | error = kauth_cred_ismember_gid(cred, gid, &ismember); |
683 | if (error) |
684 | return (error); |
685 | if (kauth_cred_getegid(cred) == gid || ismember) { |
686 | if (acc_mode & VEXEC) |
687 | mask |= S_IXGRP; |
688 | if (acc_mode & VREAD) |
689 | mask |= S_IRGRP; |
690 | if (acc_mode & VWRITE) |
691 | mask |= S_IWGRP; |
692 | return ((file_mode & mask) == mask ? 0 : EACCES); |
693 | } |
694 | |
695 | /* Otherwise, check everyone else. */ |
696 | if (acc_mode & VEXEC) |
697 | mask |= S_IXOTH; |
698 | if (acc_mode & VREAD) |
699 | mask |= S_IROTH; |
700 | if (acc_mode & VWRITE) |
701 | mask |= S_IWOTH; |
702 | return ((file_mode & mask) == mask ? 0 : EACCES); |
703 | } |
704 | |
705 | /* |
706 | * Common routine to check if chmod() is allowed. |
707 | * |
708 | * Policy: |
709 | * - You must own the file, and |
710 | * - You must not set the "sticky" bit (meaningless, see chmod(2)) |
711 | * - You must be a member of the group if you're trying to set the |
712 | * SGIDf bit |
713 | * |
714 | * cred - credentials of the invoker |
715 | * vp - vnode of the file-system object |
716 | * cur_uid, cur_gid - current uid/gid of the file-system object |
717 | * new_mode - new mode for the file-system object |
718 | * |
719 | * Returns 0 if the change is allowed, or an error value otherwise. |
720 | */ |
721 | int |
722 | genfs_can_chmod(enum vtype type, kauth_cred_t cred, uid_t cur_uid, |
723 | gid_t cur_gid, mode_t new_mode) |
724 | { |
725 | int error; |
726 | |
727 | /* The user must own the file. */ |
728 | if (kauth_cred_geteuid(cred) != cur_uid) |
729 | return (EPERM); |
730 | |
731 | /* |
732 | * Unprivileged users can't set the sticky bit on files. |
733 | */ |
734 | if ((type != VDIR) && (new_mode & S_ISTXT)) |
735 | return (EFTYPE); |
736 | |
737 | /* |
738 | * If the invoker is trying to set the SGID bit on the file, |
739 | * check group membership. |
740 | */ |
741 | if (new_mode & S_ISGID) { |
742 | int ismember; |
743 | |
744 | error = kauth_cred_ismember_gid(cred, cur_gid, |
745 | &ismember); |
746 | if (error || !ismember) |
747 | return (EPERM); |
748 | } |
749 | |
750 | return (0); |
751 | } |
752 | |
753 | /* |
754 | * Common routine to check if chown() is allowed. |
755 | * |
756 | * Policy: |
757 | * - You must own the file, and |
758 | * - You must not try to change ownership, and |
759 | * - You must be member of the new group |
760 | * |
761 | * cred - credentials of the invoker |
762 | * cur_uid, cur_gid - current uid/gid of the file-system object |
763 | * new_uid, new_gid - target uid/gid of the file-system object |
764 | * |
765 | * Returns 0 if the change is allowed, or an error value otherwise. |
766 | */ |
767 | int |
768 | genfs_can_chown(kauth_cred_t cred, uid_t cur_uid, |
769 | gid_t cur_gid, uid_t new_uid, gid_t new_gid) |
770 | { |
771 | int error, ismember; |
772 | |
773 | /* |
774 | * You can only change ownership of a file if: |
775 | * You own the file and... |
776 | */ |
777 | if (kauth_cred_geteuid(cred) == cur_uid) { |
778 | /* |
779 | * You don't try to change ownership, and... |
780 | */ |
781 | if (new_uid != cur_uid) |
782 | return (EPERM); |
783 | |
784 | /* |
785 | * You don't try to change group (no-op), or... |
786 | */ |
787 | if (new_gid == cur_gid) |
788 | return (0); |
789 | |
790 | /* |
791 | * Your effective gid is the new gid, or... |
792 | */ |
793 | if (kauth_cred_getegid(cred) == new_gid) |
794 | return (0); |
795 | |
796 | /* |
797 | * The new gid is one you're a member of. |
798 | */ |
799 | ismember = 0; |
800 | error = kauth_cred_ismember_gid(cred, new_gid, |
801 | &ismember); |
802 | if (!error && ismember) |
803 | return (0); |
804 | } |
805 | |
806 | return (EPERM); |
807 | } |
808 | |
809 | int |
810 | genfs_can_chtimes(vnode_t *vp, u_int vaflags, uid_t owner_uid, |
811 | kauth_cred_t cred) |
812 | { |
813 | int error; |
814 | |
815 | /* Must be owner, or... */ |
816 | if (kauth_cred_geteuid(cred) == owner_uid) |
817 | return (0); |
818 | |
819 | /* set the times to the current time, and... */ |
820 | if ((vaflags & VA_UTIMES_NULL) == 0) |
821 | return (EPERM); |
822 | |
823 | /* have write access. */ |
824 | error = VOP_ACCESS(vp, VWRITE, cred); |
825 | if (error) |
826 | return (error); |
827 | |
828 | return (0); |
829 | } |
830 | |
831 | /* |
832 | * Common routine to check if chflags() is allowed. |
833 | * |
834 | * Policy: |
835 | * - You must own the file, and |
836 | * - You must not change system flags, and |
837 | * - You must not change flags on character/block devices. |
838 | * |
839 | * cred - credentials of the invoker |
840 | * owner_uid - uid of the file-system object |
841 | * changing_sysflags - true if the invoker wants to change system flags |
842 | */ |
843 | int |
844 | genfs_can_chflags(kauth_cred_t cred, enum vtype type, uid_t owner_uid, |
845 | bool changing_sysflags) |
846 | { |
847 | |
848 | /* The user must own the file. */ |
849 | if (kauth_cred_geteuid(cred) != owner_uid) { |
850 | return EPERM; |
851 | } |
852 | |
853 | if (changing_sysflags) { |
854 | return EPERM; |
855 | } |
856 | |
857 | /* |
858 | * Unprivileged users cannot change the flags on devices, even if they |
859 | * own them. |
860 | */ |
861 | if (type == VCHR || type == VBLK) { |
862 | return EPERM; |
863 | } |
864 | |
865 | return 0; |
866 | } |
867 | |
868 | /* |
869 | * Common "sticky" policy. |
870 | * |
871 | * When a directory is "sticky" (as determined by the caller), this |
872 | * function may help implementing the following policy: |
873 | * - Renaming a file in it is only possible if the user owns the directory |
874 | * or the file being renamed. |
875 | * - Deleting a file from it is only possible if the user owns the |
876 | * directory or the file being deleted. |
877 | */ |
878 | int |
879 | genfs_can_sticky(kauth_cred_t cred, uid_t dir_uid, uid_t file_uid) |
880 | { |
881 | if (kauth_cred_geteuid(cred) != dir_uid && |
882 | kauth_cred_geteuid(cred) != file_uid) |
883 | return EPERM; |
884 | |
885 | return 0; |
886 | } |
887 | |
888 | int |
889 | genfs_can_extattr(kauth_cred_t cred, int access_mode, vnode_t *vp, |
890 | const char *attr) |
891 | { |
892 | /* We can't allow privileged namespaces. */ |
893 | if (strncasecmp(attr, "system" , 6) == 0) |
894 | return EPERM; |
895 | |
896 | return VOP_ACCESS(vp, access_mode, cred); |
897 | } |
898 | |