Home | History | Annotate | Download | only in ctfs
      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  */
     59 static int
     60 ctfs_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  */
     83 static void
     84 ctfs_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  */
    104 static int
    105 ctfs_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  */
    147 static int
    148 ctfs_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  */
    167 vnode_t *
    168 ctfs_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*/
    193 static int
    194 ctfs_ev_access(
    195 	vnode_t *vp,
    196 	int mode,
    197 	int flags,
    198 	cred_t *cr,
    199 	caller_context_t *cct)
    200 {
    201 	ctfs_evnode_t *evnode = vp->v_data;
    202 	contract_t *ct = evnode->ctfs_ev_contract;
    203 	int error;
    204 
    205 	if (mode & (VWRITE | VEXEC))
    206 		return (EACCES);
    207 
    208 	if (error = secpolicy_contract_observer(cr, ct))
    209 		return (error);
    210 
    211 	return (0);
    212 }
    213 
    214 /*
    215  * ctfs_ev_open - VOP_OPEN entry point
    216  *
    217  * Performs the same privilege checks as ctfs_ev_access, and then calls
    218  * ctfs_endpoint_open to perform the common endpoint initialization.
    219  */
    220 /* ARGSUSED */
    221 static int
    222 ctfs_ev_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *cct)
    223 {
    224 	ctfs_evnode_t *evnode = (*vpp)->v_data;
    225 	contract_t *ct = evnode->ctfs_ev_contract;
    226 	int error;
    227 
    228 	if (error = secpolicy_contract_observer(cr, ct))
    229 		return (error);
    230 
    231 	/*
    232 	 * See comment in ctfs_bu_open.
    233 	 */
    234 	return (ctfs_endpoint_open(&evnode->ctfs_ev_listener,
    235 	    &evnode->ctfs_ev_contract->ct_events, flag));
    236 }
    237 
    238 /*
    239  * ctfs_ev_inactive - VOP_INACTIVE entry point
    240  */
    241 /* ARGSUSED */
    242 static void
    243 ctfs_ev_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
    244 {
    245 	ctfs_evnode_t *evnode;
    246 	vnode_t *pvp = gfs_file_parent(vp);
    247 
    248 	/*
    249 	 * We must destroy the endpoint before releasing the parent; otherwise
    250 	 * we will try to destroy a contract with active listeners.  To prevent
    251 	 * this, we grab an extra hold on the parent.
    252 	 */
    253 	VN_HOLD(pvp);
    254 	if ((evnode = gfs_file_inactive(vp)) != NULL) {
    255 		ctfs_endpoint_inactive(&evnode->ctfs_ev_listener);
    256 		kmem_free(evnode, sizeof (ctfs_evnode_t));
    257 	}
    258 	VN_RELE(pvp);
    259 }
    260 
    261 /*
    262  * ctfs_ev_getattr - VOP_GETATTR entry point
    263  */
    264 /* ARGSUSED */
    265 static int
    266 ctfs_ev_getattr(
    267 	vnode_t *vp,
    268 	vattr_t *vap,
    269 	int flags,
    270 	cred_t *cr,
    271 	caller_context_t *ct)
    272 {
    273 	ctfs_evnode_t *evnode = vp->v_data;
    274 
    275 	vap->va_type = VREG;
    276 	vap->va_mode = 0444;
    277 	vap->va_nlink = 1;
    278 	vap->va_size = 0;
    279 	vap->va_ctime = evnode->ctfs_ev_contract->ct_ctime;
    280 	mutex_enter(&evnode->ctfs_ev_contract->ct_events.ctq_lock);
    281 	vap->va_atime = vap->va_mtime =
    282 	    evnode->ctfs_ev_contract->ct_events.ctq_atime;
    283 	mutex_exit(&evnode->ctfs_ev_contract->ct_events.ctq_lock);
    284 	ctfs_common_getattr(vp, vap);
    285 
    286 	return (0);
    287 }
    288 
    289 /*
    290  * ctfs_ev_ioctl - VOP_IOCTL entry point
    291  */
    292 /* ARGSUSED */
    293 static int
    294 ctfs_ev_ioctl(
    295 	vnode_t *vp,
    296 	int cmd,
    297 	intptr_t arg,
    298 	int flag,
    299 	cred_t *cr,
    300 	int *rvalp,
    301 	caller_context_t *ct)
    302 {
    303 	ctfs_evnode_t *evnode = vp->v_data;
    304 
    305 	return (ctfs_endpoint_ioctl(&evnode->ctfs_ev_listener, cmd, arg, cr,
    306 	    VTOZONE(vp), 0));
    307 }
    308 
    309 /*
    310  * ctfs_ev_poll - VOP_POLL entry point
    311  */
    312 /*ARGSUSED*/
    313 static int
    314 ctfs_ev_poll(
    315 	vnode_t *vp,
    316 	short events,
    317 	int anyyet,
    318 	short *reventsp,
    319 	pollhead_t **php,
    320 	caller_context_t *ct)
    321 {
    322 	ctfs_evnode_t *evnode = vp->v_data;
    323 
    324 	return (ctfs_endpoint_poll(&evnode->ctfs_ev_listener, events, anyyet,
    325 	    reventsp, php));
    326 }
    327 
    328 const fs_operation_def_t ctfs_tops_event[] = {
    329 	{ VOPNAME_OPEN,		{ .vop_open = ctfs_ev_open } },
    330 	{ VOPNAME_CLOSE,	{ .vop_close = ctfs_close } },
    331 	{ VOPNAME_IOCTL,	{ .vop_ioctl = ctfs_ev_ioctl } },
    332 	{ VOPNAME_GETATTR,	{ .vop_getattr = ctfs_ev_getattr } },
    333 	{ VOPNAME_ACCESS,	{ .vop_access = ctfs_ev_access } },
    334 	{ VOPNAME_READDIR,	{ .error = fs_notdir } },
    335 	{ VOPNAME_LOOKUP,	{ .error = fs_notdir } },
    336 	{ VOPNAME_INACTIVE,	{ .vop_inactive = ctfs_ev_inactive } },
    337 	{ VOPNAME_POLL,		{ .vop_poll = ctfs_ev_poll } },
    338 	{ NULL, NULL }
    339 };
    340 
    341 /*
    342  * ctfs_create_pbundle
    343  *
    344  * Creates and returns a bunode for a /system/contract/<type>/pbundle
    345  * file.
    346  */
    347 vnode_t *
    348 ctfs_create_pbundle(vnode_t *pvp)
    349 {
    350 	vnode_t *vp;
    351 	ctfs_bunode_t *bundle;
    352 
    353 	vp = gfs_file_create(sizeof (ctfs_bunode_t), pvp, ctfs_ops_bundle);
    354 	bundle = vp->v_data;
    355 	bundle->ctfs_bu_queue =
    356 	    contract_type_pbundle(ct_types[gfs_file_index(pvp)], curproc);
    357 
    358 	return (vp);
    359 }
    360 
    361 /*
    362  * ctfs_create_bundle
    363  *
    364  * Creates and returns a bunode for a /system/contract/<type>/bundle
    365  * file.
    366  */
    367 vnode_t *
    368 ctfs_create_bundle(vnode_t *pvp)
    369 {
    370 	vnode_t *vp;
    371 	ctfs_bunode_t *bundle;
    372 
    373 	vp = gfs_file_create(sizeof (ctfs_bunode_t), pvp, ctfs_ops_bundle);
    374 	bundle = vp->v_data;
    375 	bundle->ctfs_bu_queue =
    376 	    contract_type_bundle(ct_types[gfs_file_index(pvp)]);
    377 
    378 	return (vp);
    379 }
    380 
    381 /*
    382  * ctfs_bu_open - VOP_OPEN entry point
    383  */
    384 /* ARGSUSED */
    385 static int
    386 ctfs_bu_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
    387 {
    388 	ctfs_bunode_t *bunode = (*vpp)->v_data;
    389 
    390 	/*
    391 	 * This assumes we are only ever called immediately after a
    392 	 * VOP_LOOKUP.  We could clone ourselves here, but doing so
    393 	 * would make /proc/pid/fd accesses less useful.
    394 	 */
    395 	return (ctfs_endpoint_open(&bunode->ctfs_bu_listener,
    396 	    bunode->ctfs_bu_queue, flag));
    397 }
    398 
    399 /*
    400  * ctfs_bu_inactive - VOP_INACTIVE entry point
    401  */
    402 /* ARGSUSED */
    403 static void
    404 ctfs_bu_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
    405 {
    406 	ctfs_bunode_t *bunode;
    407 	vnode_t *pvp = gfs_file_parent(vp);
    408 
    409 	/*
    410 	 * See comments in ctfs_ev_inactive() above.
    411 	 */
    412 	VN_HOLD(pvp);
    413 	if ((bunode = gfs_file_inactive(vp)) != NULL) {
    414 		ctfs_endpoint_inactive(&bunode->ctfs_bu_listener);
    415 		kmem_free(bunode, sizeof (ctfs_bunode_t));
    416 	}
    417 	VN_RELE(pvp);
    418 }
    419 
    420 /*
    421  * ctfs_bu_getattr - VOP_GETATTR entry point
    422  */
    423 /* ARGSUSED */
    424 static int
    425 ctfs_bu_getattr(
    426 	vnode_t *vp,
    427 	vattr_t *vap,
    428 	int flags,
    429 	cred_t *cr,
    430 	caller_context_t *ct)
    431 {
    432 	ctfs_bunode_t *bunode = vp->v_data;
    433 
    434 	vap->va_type = VREG;
    435 	vap->va_mode = 0444;
    436 	vap->va_nodeid = gfs_file_index(vp);
    437 	vap->va_nlink = 1;
    438 	vap->va_size = 0;
    439 	vap->va_ctime.tv_sec = vp->v_vfsp->vfs_mtime;
    440 	vap->va_ctime.tv_nsec = 0;
    441 	mutex_enter(&bunode->ctfs_bu_queue->ctq_lock);
    442 	vap->va_mtime = vap->va_atime = bunode->ctfs_bu_queue->ctq_atime;
    443 	mutex_exit(&bunode->ctfs_bu_queue->ctq_lock);
    444 	ctfs_common_getattr(vp, vap);
    445 
    446 	return (0);
    447 }
    448 
    449 /*
    450  * ctfs_bu_ioctl - VOP_IOCTL entry point
    451  */
    452 /* ARGSUSED */
    453 static int
    454 ctfs_bu_ioctl(
    455 	vnode_t *vp,
    456 	int cmd,
    457 	intptr_t arg,
    458 	int flag,
    459 	cred_t *cr,
    460 	int *rvalp,
    461 	caller_context_t *ct)
    462 {
    463 	ctfs_bunode_t *bunode = vp->v_data;
    464 
    465 	return (ctfs_endpoint_ioctl(&bunode->ctfs_bu_listener, cmd, arg, cr,
    466 	    VTOZONE(vp), bunode->ctfs_bu_queue->ctq_listno == CTEL_BUNDLE));
    467 }
    468 
    469 /*
    470  * ctfs_bu_poll - VOP_POLL entry point
    471  */
    472 /*ARGSUSED*/
    473 static int
    474 ctfs_bu_poll(
    475 	vnode_t *vp,
    476 	short events,
    477 	int anyyet,
    478 	short *reventsp,
    479 	pollhead_t **php,
    480 	caller_context_t *ct)
    481 {
    482 	ctfs_bunode_t *bunode = vp->v_data;
    483 
    484 	return (ctfs_endpoint_poll(&bunode->ctfs_bu_listener, events, anyyet,
    485 	    reventsp, php));
    486 }
    487 
    488 const fs_operation_def_t ctfs_tops_bundle[] = {
    489 	{ VOPNAME_OPEN,		{ .vop_open = ctfs_bu_open } },
    490 	{ VOPNAME_CLOSE,	{ .vop_close = ctfs_close } },
    491 	{ VOPNAME_IOCTL,	{ .vop_ioctl = ctfs_bu_ioctl } },
    492 	{ VOPNAME_GETATTR,	{ .vop_getattr = ctfs_bu_getattr } },
    493 	{ VOPNAME_ACCESS,	{ .vop_access = ctfs_access_readonly } },
    494 	{ VOPNAME_READDIR,	{ .error = fs_notdir } },
    495 	{ VOPNAME_LOOKUP,	{ .error = fs_notdir } },
    496 	{ VOPNAME_INACTIVE,	{ .vop_inactive = ctfs_bu_inactive } },
    497 	{ VOPNAME_POLL,		{ .vop_poll = ctfs_bu_poll } },
    498 	{ NULL, NULL }
    499 };
    500