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