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