door_vnops.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/vnode.h> 30#include <sys/vfs_opreg.h> 31#include <sys/door.h> 32#include <sys/proc.h> 33#include <sys/kmem.h> 34#include <sys/debug.h> 35#include <sys/cmn_err.h> 36#include <fs/fs_subr.h> 37#include <sys/zone.h> 38#include <sys/tsol/label.h> 39 40kmutex_t door_knob; 41static int door_open(struct vnode **vpp, int flag, struct cred *cr); 42static int door_close(struct vnode *vp, int flag, int count, 43 offset_t offset, struct cred *cr); 44static int door_getattr(struct vnode *vp, struct vattr *vap, 45 int flags, struct cred *cr); 46static void door_inactive(struct vnode *vp, struct cred *cr); 47static int door_access(struct vnode *vp, int mode, int flags, 48 struct cred *cr); 49static int door_realvp(vnode_t *vp, vnode_t **vpp); 50 51struct vfs door_vfs; 52 53struct vnodeops *door_vnodeops; 54 55const fs_operation_def_t door_vnodeops_template[] = { 56 VOPNAME_OPEN, { .vop_open = door_open }, 57 VOPNAME_CLOSE, { .vop_close = door_close }, 58 VOPNAME_GETATTR, { .vop_getattr = door_getattr }, 59 VOPNAME_ACCESS, { .vop_access = door_access }, 60 VOPNAME_INACTIVE, { .vop_inactive = door_inactive }, 61 VOPNAME_FRLOCK, { .error = fs_error }, 62 VOPNAME_REALVP, { .vop_realvp = door_realvp }, 63 VOPNAME_POLL, { .error = fs_error }, 64 VOPNAME_PATHCONF, { .error = fs_error }, 65 VOPNAME_DISPOSE, { .error = fs_error }, 66 VOPNAME_GETSECATTR, { .error = fs_error }, 67 VOPNAME_SHRLOCK, { .error = fs_error }, 68 NULL, NULL 69}; 70 71/* ARGSUSED */ 72static int 73door_open(struct vnode **vpp, int flag, struct cred *cr) 74{ 75 /* 76 * MAC policy for doors. Restrict cross-zone open()s so that only 77 * door servers in the global zone can have clients from other zones. 78 * For other zones, client must be within the same zone as server. 79 */ 80 if (is_system_labeled()) { 81 zone_t *server_zone, *client_zone; 82 door_node_t *dp = VTOD((*vpp)); 83 84 mutex_enter(&door_knob); 85 if (DOOR_INVALID(dp)) { 86 mutex_exit(&door_knob); 87 return (0); 88 } 89 client_zone = curproc->p_zone; 90 server_zone = dp->door_target->p_zone; 91 mutex_exit(&door_knob); 92 if (server_zone != global_zone && 93 server_zone != client_zone) 94 return (EACCES); 95 } 96 return (0); 97} 98 99/* ARGSUSED */ 100static int 101door_close( 102 struct vnode *vp, 103 int flag, 104 int count, 105 offset_t offset, 106 struct cred *cr 107) 108{ 109 door_node_t *dp = VTOD(vp); 110 111 /* 112 * If this is being called from closeall on exit, any doors created 113 * by this process should have been revoked already in door_exit. 114 */ 115 ASSERT(dp->door_target != curproc || 116 ((curthread->t_proc_flag & TP_LWPEXIT) == 0)); 117 118 /* 119 * Deliver an unref if needed. 120 * 121 * If the count is equal to 2, it means that I'm doing a VOP_CLOSE 122 * on the next to last reference for *this* file struct. There may 123 * be multiple files pointing to this vnode in which case the v_count 124 * will be > 1. 125 * 126 * The door_active count is bumped during each invocation. 127 */ 128 if (count == 2 && vp->v_count == 1 && 129 (dp->door_flags & (DOOR_UNREF | DOOR_UNREF_MULTI))) { 130 mutex_enter(&door_knob); 131 if (dp->door_active == 0) { 132 /* o.k. to deliver unref now */ 133 door_deliver_unref(dp); 134 } else { 135 /* do the unref later */ 136 dp->door_flags |= DOOR_DELAY; 137 } 138 mutex_exit(&door_knob); 139 } 140 return (0); 141} 142 143/* ARGSUSED */ 144static int 145door_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr) 146{ 147 static timestruc_t tzero = {0, 0}; 148 extern dev_t doordev; 149 150 vap->va_mask = 0; /* bit-mask of attributes */ 151 vap->va_type = vp->v_type; /* vnode type (for create) */ 152 vap->va_mode = 0777; /* file access mode */ 153 vap->va_uid = 0; /* owner user id */ 154 vap->va_gid = 0; /* owner group id */ 155 vap->va_fsid = doordev; /* file system id (dev for now) */ 156 vap->va_nodeid = (ino64_t)0; /* node id */ 157 vap->va_nlink = vp->v_count; /* number of references to file */ 158 vap->va_size = (u_offset_t)0; /* file size in bytes */ 159 vap->va_atime = tzero; /* time of last access */ 160 vap->va_mtime = tzero; /* time of last modification */ 161 vap->va_ctime = tzero; /* time file ``created'' */ 162 vap->va_rdev = doordev; /* device the file represents */ 163 vap->va_blksize = 0; /* fundamental block size */ 164 vap->va_nblocks = (fsblkcnt64_t)0; /* # of blocks allocated */ 165 vap->va_seq = 0; /* sequence number */ 166 167 return (0); 168} 169 170/* ARGSUSED */ 171static void 172door_inactive(struct vnode *vp, struct cred *cr) 173{ 174 door_node_t *dp = VTOD(vp); 175 176 mutex_enter(&vp->v_lock); 177 /* 178 * Once the door_node is unreferenced, it stays unreferenced, 179 * so we can simply return if there are active thread bindings; 180 * the final door_unbind_thread() will re-invoke us. 181 */ 182 ASSERT(vp->v_count == 1); 183 if (dp->door_bound_threads > 0) { 184 vp->v_count--; 185 mutex_exit(&vp->v_lock); 186 return; 187 } 188 mutex_exit(&vp->v_lock); 189 190 /* if not revoked, remove door from per-process list */ 191 if (dp->door_target) { 192 mutex_enter(&door_knob); 193 if (dp->door_target) /* recheck door_target under lock */ 194 door_list_delete(dp); 195 mutex_exit(&door_knob); 196 } 197 vn_invalid(vp); 198 vn_free(vp); 199 kmem_free(dp, sizeof (door_node_t)); 200} 201 202/* 203 * To avoid having bound threads interfere with unref processing, we 204 * don't use VN_HOLD/VN_RELE to track threads bound to our private 205 * pool. Instead, we keep a separate counter, also under v_lock. 206 */ 207void 208door_bind_thread(door_node_t *dp) 209{ 210 vnode_t *vp = DTOV(dp); 211 212 mutex_enter(&vp->v_lock); 213 dp->door_bound_threads++; 214 ASSERT(dp->door_bound_threads > 0 && vp->v_count > 0); 215 mutex_exit(&vp->v_lock); 216} 217 218void 219door_unbind_thread(door_node_t *dp) 220{ 221 vnode_t *vp = DTOV(dp); 222 int do_inactive = 0; 223 224 mutex_enter(&vp->v_lock); 225 ASSERT(dp->door_bound_threads > 0); 226 if (--dp->door_bound_threads == 0 && vp->v_count == 0) { 227 /* set up for inactive handling */ 228 vp->v_count++; 229 do_inactive = 1; 230 } 231 mutex_exit(&vp->v_lock); 232 233 if (do_inactive) 234 door_inactive(vp, NULL); 235} 236 237/* ARGSUSED */ 238static int 239door_access(struct vnode *vp, int mode, int flags, struct cred *cr) 240{ 241 return (0); 242} 243 244/* ARGSUSED */ 245static int 246door_realvp(vnode_t *vp, vnode_t **vpp) 247{ 248 *vpp = vp; 249 return (0); 250} 251