ctfs_event.c revision 3898:c788126f2a20
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <sys/types.h>
29#include <sys/param.h>
30#include <sys/time.h>
31#include <sys/cred.h>
32#include <sys/vfs.h>
33#include <sys/vfs_opreg.h>
34#include <sys/gfs.h>
35#include <sys/vnode.h>
36#include <sys/systm.h>
37#include <sys/errno.h>
38#include <sys/sysmacros.h>
39#include <fs/fs_subr.h>
40#include <sys/contract.h>
41#include <sys/contract_impl.h>
42#include <sys/ctfs.h>
43#include <sys/ctfs_impl.h>
44#include <sys/file.h>
45#include <sys/policy.h>
46
47/*
48 * CTFS routines for the /system/contract/<type>/bundle vnode.
49 * CTFS routines for the /system/contract/<type>/pbundle vnode.
50 * CTFS routines for the /system/contract/<type>/<ctid>/events vnode.
51 */
52
53/*
54 * ctfs_endpoint_open
55 *
56 * Called by the VOP_OPEN entry points to perform some common checks
57 * and set up the endpoint listener, if not already done.
58 */
59static int
60ctfs_endpoint_open(ctfs_endpoint_t *endpt, ct_equeue_t *q, int flag)
61{
62	if ((flag & ~FNONBLOCK) != (FREAD | FOFFMAX))
63		return (EINVAL);
64
65	mutex_enter(&endpt->ctfs_endpt_lock);
66	if ((endpt->ctfs_endpt_flags & CTFS_ENDPT_SETUP) == 0) {
67		endpt->ctfs_endpt_flags |= CTFS_ENDPT_SETUP;
68		if (flag & FNONBLOCK)
69			endpt->ctfs_endpt_flags |= CTFS_ENDPT_NBLOCK;
70		cte_add_listener(q, &endpt->ctfs_endpt_listener);
71	}
72	mutex_exit(&endpt->ctfs_endpt_lock);
73
74	return (0);
75}
76
77/*
78 * ctfs_endpoint inactive
79 *
80 * Called by the VOP_INACTIVE entry points to perform common listener
81 * cleanup.
82 */
83static void
84ctfs_endpoint_inactive(ctfs_endpoint_t *endpt)
85{
86	mutex_enter(&endpt->ctfs_endpt_lock);
87	if (endpt->ctfs_endpt_flags & CTFS_ENDPT_SETUP) {
88		endpt->ctfs_endpt_flags = 0;
89		cte_remove_listener(&endpt->ctfs_endpt_listener);
90	}
91	mutex_exit(&endpt->ctfs_endpt_lock);
92}
93
94/*
95 * ctfs_endpoint_ioctl
96 *
97 * Implements the common VOP_IOCTL handling for the event endpoints.
98 * rprivchk, if true, indicates that event receive requests should
99 * check the provided credentials.  This distinction exists because
100 * contract endpoints perform their privilege checks at open-time, and
101 * process bundle queue listeners by definition may view all events
102 * their queues contain.
103 */
104static int
105ctfs_endpoint_ioctl(ctfs_endpoint_t *endpt, int cmd, intptr_t arg, cred_t *cr,
106    zone_t *zone, int rprivchk)
107{
108	uint64_t id, zuniqid;
109
110	zuniqid = zone->zone_uniqid;
111
112	switch (cmd) {
113	case CT_ERESET:
114		cte_reset_listener(&endpt->ctfs_endpt_listener);
115		break;
116	case CT_ERECV:
117		/*
118		 * We pass in NULL for the cred when reading from
119		 * process bundle queues and contract queues because
120		 * the privilege check was performed at open time.
121		 */
122		return (cte_get_event(&endpt->ctfs_endpt_listener,
123		    endpt->ctfs_endpt_flags & CTFS_ENDPT_NBLOCK,
124		    (void *)arg, rprivchk ? cr : NULL, zuniqid, 0));
125	case CT_ECRECV:
126		return (cte_get_event(&endpt->ctfs_endpt_listener,
127		    endpt->ctfs_endpt_flags & CTFS_ENDPT_NBLOCK,
128		    (void *)arg, rprivchk ? cr : NULL, zuniqid, 1));
129	case CT_ENEXT:
130		if (copyin((void *)arg, &id, sizeof (uint64_t)))
131			return (EFAULT);
132		return (cte_next_event(&endpt->ctfs_endpt_listener, id));
133	case CT_ERELIABLE:
134		return (cte_set_reliable(&endpt->ctfs_endpt_listener, cr));
135	default:
136		return (EINVAL);
137	}
138
139	return (0);
140}
141
142/*
143 * ctfs_endpoint_poll
144 *
145 * Called by the VOP_POLL entry points.
146 */
147static int
148ctfs_endpoint_poll(ctfs_endpoint_t *endpt, short events, int anyyet,
149    short *reventsp, pollhead_t **php)
150{
151	if ((events & POLLIN) && endpt->ctfs_endpt_listener.ctl_position) {
152		*reventsp = POLLIN;
153	} else {
154		*reventsp = 0;
155		if (!anyyet)
156			*php = &endpt->ctfs_endpt_listener.ctl_pollhead;
157	}
158
159	return (0);
160}
161
162/*
163 * ctfs_create_evnode
164 *
165 * Creates and returns a new evnode.
166 */
167vnode_t *
168ctfs_create_evnode(vnode_t *pvp)
169{
170	vnode_t *vp;
171	ctfs_evnode_t *evnode;
172	ctfs_cdirnode_t *cdirnode = pvp->v_data;
173
174	vp = gfs_file_create(sizeof (ctfs_evnode_t), pvp, ctfs_ops_event);
175	evnode = vp->v_data;
176
177	/*
178	 * We transitively have a hold on the contract through our
179	 * parent directory.
180	 */
181	evnode->ctfs_ev_contract = cdirnode->ctfs_cn_contract;
182
183	return (vp);
184}
185
186/*
187 * ctfs_ev_access - VOP_ACCESS entry point
188 *
189 * You only get to access event files for contracts you or your
190 * effective user id owns, unless you have a privilege.
191 */
192/*ARGSUSED*/
193static int
194ctfs_ev_access(vnode_t *vp, int mode, int flags, cred_t *cr)
195{
196	ctfs_evnode_t *evnode = vp->v_data;
197	contract_t *ct = evnode->ctfs_ev_contract;
198	int error;
199
200	if (mode & (VWRITE | VEXEC))
201		return (EACCES);
202
203	if (error = secpolicy_contract_observer(cr, ct))
204		return (error);
205
206	return (0);
207}
208
209/*
210 * ctfs_ev_open - VOP_OPEN entry point
211 *
212 * Performs the same privilege checks as ctfs_ev_access, and then calls
213 * ctfs_endpoint_open to perform the common endpoint initialization.
214 */
215/* ARGSUSED */
216static int
217ctfs_ev_open(vnode_t **vpp, int flag, cred_t *cr)
218{
219	ctfs_evnode_t *evnode = (*vpp)->v_data;
220	contract_t *ct = evnode->ctfs_ev_contract;
221	int error;
222
223	if (error = secpolicy_contract_observer(cr, ct))
224		return (error);
225
226	/*
227	 * See comment in ctfs_bu_open.
228	 */
229	return (ctfs_endpoint_open(&evnode->ctfs_ev_listener,
230	    &evnode->ctfs_ev_contract->ct_events, flag));
231}
232
233/*
234 * ctfs_ev_inactive - VOP_INACTIVE entry point
235 */
236/* ARGSUSED */
237static void
238ctfs_ev_inactive(vnode_t *vp, cred_t *cr)
239{
240	ctfs_evnode_t *evnode;
241	vnode_t *pvp = gfs_file_parent(vp);
242
243	/*
244	 * We must destroy the endpoint before releasing the parent; otherwise
245	 * we will try to destroy a contract with active listeners.  To prevent
246	 * this, we grab an extra hold on the parent.
247	 */
248	VN_HOLD(pvp);
249	if ((evnode = gfs_file_inactive(vp)) != NULL) {
250		ctfs_endpoint_inactive(&evnode->ctfs_ev_listener);
251		kmem_free(evnode, sizeof (ctfs_evnode_t));
252	}
253	VN_RELE(pvp);
254}
255
256/*
257 * ctfs_ev_getattr - VOP_GETATTR entry point
258 */
259/* ARGSUSED */
260static int
261ctfs_ev_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr)
262{
263	ctfs_evnode_t *evnode = vp->v_data;
264
265	vap->va_type = VREG;
266	vap->va_mode = 0444;
267	vap->va_nlink = 1;
268	vap->va_size = 0;
269	vap->va_ctime = evnode->ctfs_ev_contract->ct_ctime;
270	mutex_enter(&evnode->ctfs_ev_contract->ct_events.ctq_lock);
271	vap->va_atime = vap->va_mtime =
272	    evnode->ctfs_ev_contract->ct_events.ctq_atime;
273	mutex_exit(&evnode->ctfs_ev_contract->ct_events.ctq_lock);
274	ctfs_common_getattr(vp, vap);
275
276	return (0);
277}
278
279/*
280 * ctfs_ev_ioctl - VOP_IOCTL entry point
281 */
282/* ARGSUSED */
283static int
284ctfs_ev_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr,
285    int *rvalp)
286{
287	ctfs_evnode_t *evnode = vp->v_data;
288
289	return (ctfs_endpoint_ioctl(&evnode->ctfs_ev_listener, cmd, arg, cr,
290	    VTOZONE(vp), 0));
291}
292
293/*
294 * ctfs_ev_poll - VOP_POLL entry point
295 */
296static int
297ctfs_ev_poll(vnode_t *vp, short events, int anyyet, short *reventsp,
298    pollhead_t **php)
299{
300	ctfs_evnode_t *evnode = vp->v_data;
301
302	return (ctfs_endpoint_poll(&evnode->ctfs_ev_listener, events, anyyet,
303	    reventsp, php));
304}
305
306const fs_operation_def_t ctfs_tops_event[] = {
307	{ VOPNAME_OPEN,		{ .vop_open = ctfs_ev_open } },
308	{ VOPNAME_CLOSE,	{ .vop_close = ctfs_close } },
309	{ VOPNAME_IOCTL,	{ .vop_ioctl = ctfs_ev_ioctl } },
310	{ VOPNAME_GETATTR,	{ .vop_getattr = ctfs_ev_getattr } },
311	{ VOPNAME_ACCESS,	{ .vop_access = ctfs_ev_access } },
312	{ VOPNAME_READDIR,	{ .error = fs_notdir } },
313	{ VOPNAME_LOOKUP,	{ .error = fs_notdir } },
314	{ VOPNAME_INACTIVE,	{ .vop_inactive = ctfs_ev_inactive } },
315	{ VOPNAME_POLL,		{ .vop_poll = ctfs_ev_poll } },
316	{ NULL, NULL }
317};
318
319/*
320 * ctfs_create_pbundle
321 *
322 * Creates and returns a bunode for a /system/contract/<type>/pbundle
323 * file.
324 */
325vnode_t *
326ctfs_create_pbundle(vnode_t *pvp)
327{
328	vnode_t *vp;
329	ctfs_bunode_t *bundle;
330
331	vp = gfs_file_create(sizeof (ctfs_bunode_t), pvp, ctfs_ops_bundle);
332	bundle = vp->v_data;
333	bundle->ctfs_bu_queue =
334	    contract_type_pbundle(ct_types[gfs_file_index(pvp)], curproc);
335
336	return (vp);
337}
338
339/*
340 * ctfs_create_bundle
341 *
342 * Creates and returns a bunode for a /system/contract/<type>/bundle
343 * file.
344 */
345vnode_t *
346ctfs_create_bundle(vnode_t *pvp)
347{
348	vnode_t *vp;
349	ctfs_bunode_t *bundle;
350
351	vp = gfs_file_create(sizeof (ctfs_bunode_t), pvp, ctfs_ops_bundle);
352	bundle = vp->v_data;
353	bundle->ctfs_bu_queue =
354	    contract_type_bundle(ct_types[gfs_file_index(pvp)]);
355
356	return (vp);
357}
358
359/*
360 * ctfs_bu_open - VOP_OPEN entry point
361 */
362/* ARGSUSED */
363static int
364ctfs_bu_open(vnode_t **vpp, int flag, cred_t *cr)
365{
366	ctfs_bunode_t *bunode = (*vpp)->v_data;
367
368	/*
369	 * This assumes we are only ever called immediately after a
370	 * VOP_LOOKUP.  We could clone ourselves here, but doing so
371	 * would make /proc/pid/fd accesses less useful.
372	 */
373	return (ctfs_endpoint_open(&bunode->ctfs_bu_listener,
374	    bunode->ctfs_bu_queue, flag));
375}
376
377/*
378 * ctfs_bu_inactive - VOP_INACTIVE entry point
379 */
380/* ARGSUSED */
381static void
382ctfs_bu_inactive(vnode_t *vp, cred_t *cr)
383{
384	ctfs_bunode_t *bunode;
385	vnode_t *pvp = gfs_file_parent(vp);
386
387	/*
388	 * See comments in ctfs_ev_inactive() above.
389	 */
390	VN_HOLD(pvp);
391	if ((bunode = gfs_file_inactive(vp)) != NULL) {
392		ctfs_endpoint_inactive(&bunode->ctfs_bu_listener);
393		kmem_free(bunode, sizeof (ctfs_bunode_t));
394	}
395	VN_RELE(pvp);
396}
397
398/*
399 * ctfs_bu_getattr - VOP_GETATTR entry point
400 */
401/* ARGSUSED */
402static int
403ctfs_bu_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr)
404{
405	ctfs_bunode_t *bunode = vp->v_data;
406
407	vap->va_type = VREG;
408	vap->va_mode = 0444;
409	vap->va_nodeid = gfs_file_index(vp);
410	vap->va_nlink = 1;
411	vap->va_size = 0;
412	vap->va_ctime.tv_sec = vp->v_vfsp->vfs_mtime;
413	vap->va_ctime.tv_nsec = 0;
414	mutex_enter(&bunode->ctfs_bu_queue->ctq_lock);
415	vap->va_mtime = vap->va_atime = bunode->ctfs_bu_queue->ctq_atime;
416	mutex_exit(&bunode->ctfs_bu_queue->ctq_lock);
417	ctfs_common_getattr(vp, vap);
418
419	return (0);
420}
421
422/*
423 * ctfs_bu_ioctl - VOP_IOCTL entry point
424 */
425/* ARGSUSED */
426static int
427ctfs_bu_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, cred_t *cr,
428    int *rvalp)
429{
430	ctfs_bunode_t *bunode = vp->v_data;
431
432	return (ctfs_endpoint_ioctl(&bunode->ctfs_bu_listener, cmd, arg, cr,
433	    VTOZONE(vp), bunode->ctfs_bu_queue->ctq_listno == CTEL_BUNDLE));
434}
435
436/*
437 * ctfs_bu_poll - VOP_POLL entry point
438 */
439static int
440ctfs_bu_poll(vnode_t *vp, short events, int anyyet, short *reventsp,
441    pollhead_t **php)
442{
443	ctfs_bunode_t *bunode = vp->v_data;
444
445	return (ctfs_endpoint_poll(&bunode->ctfs_bu_listener, events, anyyet,
446	    reventsp, php));
447}
448
449const fs_operation_def_t ctfs_tops_bundle[] = {
450	{ VOPNAME_OPEN,		{ .vop_open = ctfs_bu_open } },
451	{ VOPNAME_CLOSE,	{ .vop_close = ctfs_close } },
452	{ VOPNAME_IOCTL,	{ .vop_ioctl = ctfs_bu_ioctl } },
453	{ VOPNAME_GETATTR,	{ .vop_getattr = ctfs_bu_getattr } },
454	{ VOPNAME_ACCESS,	{ .vop_access = ctfs_access_readonly } },
455	{ VOPNAME_READDIR,	{ .error = fs_notdir } },
456	{ VOPNAME_LOOKUP,	{ .error = fs_notdir } },
457	{ VOPNAME_INACTIVE,	{ .vop_inactive = ctfs_bu_inactive } },
458	{ VOPNAME_POLL,		{ .vop_poll = ctfs_bu_poll } },
459	{ NULL, NULL }
460};
461