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