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