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 2006 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 /* 29 * vnode ops for the /dev/pts directory 30 * The lookup is based on the internal pty table. We also 31 * override readdir in order to delete pts nodes no longer 32 * in use. 33 */ 34 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/sysmacros.h> 38 #include <sys/sunndi.h> 39 #include <fs/fs_subr.h> 40 #include <sys/fs/dv_node.h> 41 #include <sys/fs/sdev_impl.h> 42 #include <sys/policy.h> 43 #include <sys/ptms.h> 44 #include <sys/stat.h> 45 46 #define DEVPTS_UID_DEFAULT 0 47 #define DEVPTS_GID_DEFAULT 3 48 #define DEVPTS_DEVMODE_DEFAULT (0620) 49 50 #define isdigit(ch) ((ch) >= '0' && (ch) <= '9') 51 52 static vattr_t devpts_vattr = { 53 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 54 VCHR, /* va_type */ 55 S_IFCHR | DEVPTS_DEVMODE_DEFAULT, /* va_mode */ 56 DEVPTS_UID_DEFAULT, /* va_uid */ 57 DEVPTS_GID_DEFAULT, /* va_gid */ 58 0 /* 0 hereafter */ 59 }; 60 61 struct vnodeops *devpts_vnodeops; 62 63 struct vnodeops * 64 devpts_getvnodeops(void) 65 { 66 return (devpts_vnodeops); 67 } 68 69 /* 70 * Convert string to minor number. Some care must be taken 71 * as we are processing user input. Catch cases like 72 * /dev/pts/4foo and /dev/pts/-1 73 */ 74 static int 75 devpts_strtol(const char *nm, minor_t *mp) 76 { 77 long uminor = 0; 78 char *endptr = NULL; 79 80 if (nm == NULL || !isdigit(*nm)) 81 return (EINVAL); 82 83 *mp = 0; 84 if (ddi_strtol(nm, &endptr, 10, &uminor) != 0 || 85 *endptr != '\0' || uminor < 0) { 86 return (EINVAL); 87 } 88 89 *mp = (minor_t)uminor; 90 return (0); 91 } 92 93 /* 94 * Check if a pts sdev_node is still valid - i.e. it represents a current pty. 95 * This serves two purposes 96 * - only valid pts nodes are returned during lookup() and readdir(). 97 * - since pts sdev_nodes are not actively destroyed when a pty goes 98 * away, we use the validator to do deferred cleanup i.e. when such 99 * nodes are encountered during subsequent lookup() and readdir(). 100 */ 101 /*ARGSUSED*/ 102 int 103 devpts_validate(struct sdev_node *dv) 104 { 105 minor_t min; 106 uid_t uid; 107 gid_t gid; 108 timestruc_t now; 109 char *nm = dv->sdev_name; 110 111 ASSERT(!(dv->sdev_flags & SDEV_STALE)); 112 ASSERT(dv->sdev_state == SDEV_READY); 113 114 /* validate only READY nodes */ 115 if (dv->sdev_state != SDEV_READY) { 116 sdcmn_err(("dev fs: skipping: node not ready %s(%p)", 117 nm, (void *)dv)); 118 return (SDEV_VTOR_SKIP); 119 } 120 121 if (devpts_strtol(nm, &min) != 0) { 122 sdcmn_err7(("devpts_validate: not a valid minor: %s\n", nm)); 123 return (SDEV_VTOR_INVALID); 124 } 125 126 /* 127 * Check if pts driver is attached 128 */ 129 if (ptms_slave_attached() == (major_t)-1) { 130 sdcmn_err7(("devpts_validate: slave not attached\n")); 131 return (SDEV_VTOR_INVALID); 132 } 133 134 if (ptms_minor_valid(min, &uid, &gid) == 0) { 135 if (ptms_minor_exists(min)) { 136 sdcmn_err7(("devpts_validate: valid in different zone " 137 "%s\n", nm)); 138 return (SDEV_VTOR_SKIP); 139 } else { 140 sdcmn_err7(("devpts_validate: %s not valid pty\n", 141 nm)); 142 return (SDEV_VTOR_INVALID); 143 } 144 } 145 146 ASSERT(dv->sdev_attr); 147 if (dv->sdev_attr->va_uid != uid || dv->sdev_attr->va_gid != gid) { 148 ASSERT(uid >= 0); 149 ASSERT(gid >= 0); 150 dv->sdev_attr->va_uid = uid; 151 dv->sdev_attr->va_gid = gid; 152 gethrestime(&now); 153 dv->sdev_attr->va_atime = now; 154 dv->sdev_attr->va_mtime = now; 155 dv->sdev_attr->va_ctime = now; 156 sdcmn_err7(("devpts_validate: update uid/gid/times%s\n", nm)); 157 } 158 159 return (SDEV_VTOR_VALID); 160 } 161 162 /* 163 * This callback is invoked from devname_lookup_func() to create 164 * a pts entry when the node is not found in the cache. 165 */ 166 /*ARGSUSED*/ 167 static int 168 devpts_create_rvp(struct sdev_node *ddv, char *nm, 169 void **arg, cred_t *cred, void *whatever, char *whichever) 170 { 171 minor_t min; 172 major_t maj; 173 uid_t uid; 174 gid_t gid; 175 timestruc_t now; 176 struct vattr *vap = (struct vattr *)arg; 177 178 if (devpts_strtol(nm, &min) != 0) { 179 sdcmn_err7(("devpts_create_rvp: not a valid minor: %s\n", nm)); 180 return (-1); 181 } 182 183 /* 184 * Check if pts driver is attached and if it is 185 * get the major number. 186 */ 187 maj = ptms_slave_attached(); 188 if (maj == (major_t)-1) { 189 sdcmn_err7(("devpts_create_rvp: slave not attached\n")); 190 return (-1); 191 } 192 193 /* 194 * Only allow creation of ptys allocated to our zone 195 */ 196 if (!ptms_minor_valid(min, &uid, &gid)) { 197 sdcmn_err7(("devpts_create_rvp: %s not valid pty" 198 "or not valid in this zone\n", nm)); 199 return (-1); 200 } 201 202 203 /* 204 * This is a valid pty (at least at this point in time). 205 * Create the node by setting the attribute. The rest 206 * is taken care of by devname_lookup_func(). 207 */ 208 *vap = devpts_vattr; 209 vap->va_rdev = makedevice(maj, min); 210 ASSERT(uid >= 0); 211 ASSERT(gid >= 0); 212 vap->va_uid = uid; 213 vap->va_gid = gid; 214 gethrestime(&now); 215 vap->va_atime = now; 216 vap->va_mtime = now; 217 vap->va_ctime = now; 218 219 return (0); 220 } 221 222 /* 223 * Clean pts sdev_nodes that are no longer valid. 224 */ 225 static void 226 devpts_prunedir(struct sdev_node *ddv) 227 { 228 struct vnode *vp; 229 struct sdev_node *dv, *next = NULL; 230 int (*vtor)(struct sdev_node *) = NULL; 231 232 ASSERT(ddv->sdev_flags & SDEV_VTOR); 233 234 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 235 ASSERT(vtor); 236 237 if (rw_tryupgrade(&ddv->sdev_contents) == NULL) { 238 rw_exit(&ddv->sdev_contents); 239 rw_enter(&ddv->sdev_contents, RW_WRITER); 240 } 241 242 for (dv = ddv->sdev_dot; dv; dv = next) { 243 next = dv->sdev_next; 244 245 /* skip stale nodes */ 246 if (dv->sdev_flags & SDEV_STALE) 247 continue; 248 249 /* validate and prune only ready nodes */ 250 if (dv->sdev_state != SDEV_READY) 251 continue; 252 253 switch (vtor(dv)) { 254 case SDEV_VTOR_VALID: 255 case SDEV_VTOR_SKIP: 256 continue; 257 case SDEV_VTOR_INVALID: 258 sdcmn_err7(("prunedir: destroy invalid " 259 "node: %s(%p)\n", dv->sdev_name, (void *)dv)); 260 break; 261 } 262 vp = SDEVTOV(dv); 263 if (vp->v_count > 0) 264 continue; 265 SDEV_HOLD(dv); 266 /* remove the cache node */ 267 (void) sdev_cache_update(ddv, &dv, dv->sdev_name, 268 SDEV_CACHE_DELETE); 269 } 270 rw_downgrade(&ddv->sdev_contents); 271 } 272 273 /* 274 * Lookup for /dev/pts directory 275 * If the entry does not exist, the devpts_create_rvp() callback 276 * is invoked to create it. Nodes do not persist across reboot. 277 */ 278 /*ARGSUSED3*/ 279 static int 280 devpts_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, 281 struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred) 282 { 283 struct sdev_node *sdvp = VTOSDEV(dvp); 284 struct sdev_node *dv; 285 int error; 286 287 error = devname_lookup_func(sdvp, nm, vpp, cred, devpts_create_rvp, 288 SDEV_VATTR); 289 290 if (error == 0) { 291 switch ((*vpp)->v_type) { 292 case VCHR: 293 dv = VTOSDEV(VTOS(*vpp)->s_realvp); 294 break; 295 case VDIR: 296 dv = VTOSDEV(*vpp); 297 break; 298 default: 299 cmn_err(CE_PANIC, "devpts_lookup: Unsupported node " 300 "type: %p: %d", (void *)(*vpp), (*vpp)->v_type); 301 break; 302 } 303 ASSERT(SDEV_HELD(dv)); 304 } 305 306 return (error); 307 } 308 309 /* 310 * We allow create to find existing nodes 311 * - if the node doesn't exist - EROFS 312 * - creating an existing dir read-only succeeds, otherwise EISDIR 313 * - exclusive creates fail - EEXIST 314 */ 315 /*ARGSUSED2*/ 316 static int 317 devpts_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, 318 int mode, struct vnode **vpp, struct cred *cred, int flag) 319 { 320 int error; 321 struct vnode *vp; 322 323 *vpp = NULL; 324 325 error = devpts_lookup(dvp, nm, &vp, NULL, 0, NULL, cred); 326 if (error == 0) { 327 if (excl == EXCL) 328 error = EEXIST; 329 else if (vp->v_type == VDIR && (mode & VWRITE)) 330 error = EISDIR; 331 else 332 error = VOP_ACCESS(vp, mode, 0, cred); 333 334 if (error) { 335 VN_RELE(vp); 336 } else 337 *vpp = vp; 338 } else if (error == ENOENT) { 339 error = EROFS; 340 } 341 342 return (error); 343 } 344 345 /* 346 * Display all instantiated pts (slave) device nodes. 347 * A /dev/pts entry will be created only after the first lookup of the slave 348 * device succeeds. 349 */ 350 static int 351 devpts_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, 352 int *eofp) 353 { 354 struct sdev_node *sdvp = VTOSDEV(dvp); 355 if (uiop->uio_offset == 0) { 356 devpts_prunedir(sdvp); 357 } 358 359 return (devname_readdir_func(dvp, uiop, cred, eofp, 0)); 360 } 361 362 363 static int 364 devpts_set_id(struct sdev_node *dv, struct vattr *vap, int protocol) 365 { 366 ASSERT((protocol & AT_UID) || (protocol & AT_GID)); 367 ptms_set_owner(getminor(SDEVTOV(dv)->v_rdev), 368 vap->va_uid, vap->va_gid); 369 return (0); 370 371 } 372 373 static int 374 devpts_setattr(struct vnode *vp, struct vattr *vap, int flags, 375 struct cred *cred) 376 { 377 ASSERT((vp->v_type == VCHR) || (vp->v_type == VDIR)); 378 return (devname_setattr_func(vp, vap, flags, cred, 379 devpts_set_id, AT_UID|AT_GID)); 380 } 381 382 /* 383 * We override lookup and readdir to build entries based on the 384 * in kernel pty table. Also override setattr/setsecattr to 385 * avoid persisting permissions. 386 */ 387 const fs_operation_def_t devpts_vnodeops_tbl[] = { 388 VOPNAME_READDIR, devpts_readdir, 389 VOPNAME_LOOKUP, devpts_lookup, 390 VOPNAME_CREATE, devpts_create, 391 VOPNAME_SETATTR, devpts_setattr, 392 VOPNAME_REMOVE, fs_nosys, 393 VOPNAME_MKDIR, fs_nosys, 394 VOPNAME_RMDIR, fs_nosys, 395 VOPNAME_SYMLINK, fs_nosys, 396 VOPNAME_SETSECATTR, fs_nosys, 397 NULL, NULL 398 }; 399