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