80 return (EINVAL);
81
82 *mp = 0;
83 if (ddi_strtol(nm, &endptr, 10, &uminor) != 0 ||
84 *endptr != '\0' || uminor < 0) {
85 return (EINVAL);
86 }
87
88 *mp = (minor_t)uminor;
89 return (0);
90 }
91
92 /*
93 * Check if a pts sdev_node is still valid - i.e. it represents a current pty.
94 * This serves two purposes
95 * - only valid pts nodes are returned during lookup() and readdir().
96 * - since pts sdev_nodes are not actively destroyed when a pty goes
97 * away, we use the validator to do deferred cleanup i.e. when such
98 * nodes are encountered during subsequent lookup() and readdir().
99 */
100 /*ARGSUSED*/
101 int
102 devpts_validate(struct sdev_node *dv)
103 {
104 minor_t min;
105 uid_t uid;
106 gid_t gid;
107 timestruc_t now;
108 char *nm = dv->sdev_name;
109
110 ASSERT(dv->sdev_state == SDEV_READY);
111
112 /* validate only READY nodes */
113 if (dv->sdev_state != SDEV_READY) {
114 sdcmn_err(("dev fs: skipping: node not ready %s(%p)",
115 nm, (void *)dv));
116 return (SDEV_VTOR_SKIP);
117 }
118
119 if (devpts_strtol(nm, &min) != 0) {
120 sdcmn_err7(("devpts_validate: not a valid minor: %s\n", nm));
121 return (SDEV_VTOR_INVALID);
122 }
123
124 /*
125 * Check if pts driver is attached
126 */
127 if (ptms_slave_attached() == (major_t)-1) {
128 sdcmn_err7(("devpts_validate: slave not attached\n"));
129 return (SDEV_VTOR_INVALID);
130 }
131
132 if (ptms_minor_valid(min, &uid, &gid) == 0) {
133 if (ptms_minor_exists(min)) {
134 sdcmn_err7(("devpts_validate: valid in different zone "
135 "%s\n", nm));
136 return (SDEV_VTOR_SKIP);
137 } else {
138 sdcmn_err7(("devpts_validate: %s not valid pty\n",
139 nm));
140 return (SDEV_VTOR_INVALID);
141 }
142 }
143
144 ASSERT(dv->sdev_attr);
145 if (dv->sdev_attr->va_uid != uid || dv->sdev_attr->va_gid != gid) {
146 dv->sdev_attr->va_uid = uid;
147 dv->sdev_attr->va_gid = gid;
148 gethrestime(&now);
149 dv->sdev_attr->va_atime = now;
150 dv->sdev_attr->va_mtime = now;
151 dv->sdev_attr->va_ctime = now;
152 sdcmn_err7(("devpts_validate: update uid/gid/times%s\n", nm));
153 }
154
155 return (SDEV_VTOR_VALID);
156 }
157
158 /*
159 * This callback is invoked from devname_lookup_func() to create
160 * a pts entry when the node is not found in the cache.
161 */
162 /*ARGSUSED*/
163 static int
164 devpts_create_rvp(struct sdev_node *ddv, char *nm,
165 void **arg, cred_t *cred, void *whatever, char *whichever)
166 {
167 minor_t min;
168 major_t maj;
169 uid_t uid;
170 gid_t gid;
171 timestruc_t now;
172 struct vattr *vap = (struct vattr *)arg;
173
174 if (devpts_strtol(nm, &min) != 0) {
175 sdcmn_err7(("devpts_create_rvp: not a valid minor: %s\n", nm));
176 return (-1);
177 }
178
179 /*
180 * Check if pts driver is attached and if it is
181 * get the major number.
182 */
183 maj = ptms_slave_attached();
184 if (maj == (major_t)-1) {
185 sdcmn_err7(("devpts_create_rvp: slave not attached\n"));
186 return (-1);
187 }
188
189 /*
190 * Only allow creation of ptys allocated to our zone
191 */
192 if (!ptms_minor_valid(min, &uid, &gid)) {
193 sdcmn_err7(("devpts_create_rvp: %s not valid pty"
194 "or not valid in this zone\n", nm));
195 return (-1);
196 }
197
198
199 /*
200 * This is a valid pty (at least at this point in time).
201 * Create the node by setting the attribute. The rest
202 * is taken care of by devname_lookup_func().
203 */
204 *vap = devpts_vattr;
205 vap->va_rdev = makedevice(maj, min);
269 *
270 * There is a potential denial of service here via
271 * fattach on top of a /dev/pts node - any permission changes
272 * applied to the node, apply to the fattached file and not
273 * to the underlying pts node. As a result when the previous
274 * user fdetaches, the pts node is still owned by the previous
275 * owner. To prevent this we don't allow fattach() on top of a pts
276 * node. This is done by a modification in the namefs filesystem
277 * where we check if the underlying node has the /dev/pts vnodeops.
278 * We do this via VOP_REALVP() on the underlying specfs node.
279 * sdev_nodes currently don't have a realvp. If a realvp is ever
280 * created for sdev_nodes, then VOP_REALVP() will return the
281 * actual realvp (possibly a ufs vnode). This will defeat the check
282 * in namefs code which checks if VOP_REALVP() returns a devpts
283 * node. We add an ASSERT here in /dev/pts lookup() to check for
284 * this condition. If sdev_nodes ever get a VOP_REALVP() entry point,
285 * change the code in the namefs filesystem code (in nm_mount()) to
286 * access the realvp of the specfs node directly instead of using
287 * VOP_REALVP().
288 */
289 /*ARGSUSED3*/
290 static int
291 devpts_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
292 struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
293 caller_context_t *ct, int *direntflags, pathname_t *realpnp)
294 {
295 struct sdev_node *sdvp = VTOSDEV(dvp);
296 struct sdev_node *dv;
297 struct vnode *rvp = NULL;
298 int error;
299
300 error = devname_lookup_func(sdvp, nm, vpp, cred, devpts_create_rvp,
301 SDEV_VATTR);
302
303 if (error == 0) {
304 switch ((*vpp)->v_type) {
305 case VCHR:
306 dv = VTOSDEV(VTOS(*vpp)->s_realvp);
307 ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS);
308 break;
309 case VDIR:
310 dv = VTOSDEV(*vpp);
311 break;
312 default:
313 cmn_err(CE_PANIC, "devpts_lookup: Unsupported node "
314 "type: %p: %d", (void *)(*vpp), (*vpp)->v_type);
315 break;
316 }
317 ASSERT(SDEV_HELD(dv));
318 }
319
320 return (error);
321 }
322
323 /*
324 * We allow create to find existing nodes
325 * - if the node doesn't exist - EROFS
326 * - creating an existing dir read-only succeeds, otherwise EISDIR
327 * - exclusive creates fail - EEXIST
328 */
329 /*ARGSUSED2*/
330 static int
331 devpts_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
332 int mode, struct vnode **vpp, struct cred *cred, int flag,
333 caller_context_t *ct, vsecattr_t *vsecp)
334 {
335 int error;
336 struct vnode *vp;
337
338 *vpp = NULL;
339
340 error = devpts_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL,
341 NULL);
342 if (error == 0) {
343 if (excl == EXCL)
344 error = EEXIST;
345 else if (vp->v_type == VDIR && (mode & VWRITE))
346 error = EISDIR;
347 else
348 error = VOP_ACCESS(vp, mode, 0, cred, ct);
349
350 if (error) {
351 VN_RELE(vp);
352 } else
353 *vpp = vp;
354 } else if (error == ENOENT) {
355 error = EROFS;
356 }
357
358 return (error);
359 }
360
361 /*
362 * Display all instantiated pts (slave) device nodes.
363 * A /dev/pts entry will be created only after the first lookup of the slave
364 * device succeeds.
365 */
366 /*ARGSUSED4*/
367 static int
368 devpts_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
369 int *eofp, caller_context_t *ct, int flags)
370 {
371 struct sdev_node *sdvp = VTOSDEV(dvp);
372 if (uiop->uio_offset == 0) {
373 devpts_prunedir(sdvp);
374 }
375
376 return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
377 }
378
379
380 static int
381 devpts_set_id(struct sdev_node *dv, struct vattr *vap, int protocol)
382 {
383 ASSERT((protocol & AT_UID) || (protocol & AT_GID));
384 ptms_set_owner(getminor(SDEVTOV(dv)->v_rdev),
385 vap->va_uid, vap->va_gid);
386 return (0);
387
388 }
389
390 /*ARGSUSED4*/
391 static int
392 devpts_setattr(struct vnode *vp, struct vattr *vap, int flags,
393 struct cred *cred, caller_context_t *ctp)
394 {
395 ASSERT((vp->v_type == VCHR) || (vp->v_type == VDIR));
396 return (devname_setattr_func(vp, vap, flags, cred,
397 devpts_set_id, AT_UID|AT_GID));
398 }
399
400
401 /*
402 * We override lookup and readdir to build entries based on the
403 * in kernel pty table. Also override setattr/setsecattr to
404 * avoid persisting permissions.
405 */
406 const fs_operation_def_t devpts_vnodeops_tbl[] = {
407 VOPNAME_READDIR, { .vop_readdir = devpts_readdir },
408 VOPNAME_LOOKUP, { .vop_lookup = devpts_lookup },
409 VOPNAME_CREATE, { .vop_create = devpts_create },
410 VOPNAME_SETATTR, { .vop_setattr = devpts_setattr },
|
80 return (EINVAL);
81
82 *mp = 0;
83 if (ddi_strtol(nm, &endptr, 10, &uminor) != 0 ||
84 *endptr != '\0' || uminor < 0) {
85 return (EINVAL);
86 }
87
88 *mp = (minor_t)uminor;
89 return (0);
90 }
91
92 /*
93 * Check if a pts sdev_node is still valid - i.e. it represents a current pty.
94 * This serves two purposes
95 * - only valid pts nodes are returned during lookup() and readdir().
96 * - since pts sdev_nodes are not actively destroyed when a pty goes
97 * away, we use the validator to do deferred cleanup i.e. when such
98 * nodes are encountered during subsequent lookup() and readdir().
99 */
100 int
101 devpts_validate(struct sdev_node *dv)
102 {
103 minor_t min;
104 uid_t uid;
105 gid_t gid;
106 timestruc_t now;
107 char *nm = dv->sdev_name;
108
109 ASSERT(dv->sdev_state == SDEV_READY);
110
111 /* validate only READY nodes */
112 if (dv->sdev_state != SDEV_READY) {
113 sdcmn_err(("dev fs: skipping: node not ready %s(%p)",
114 nm, (void *)dv));
115 return (SDEV_VTOR_SKIP);
116 }
117
118 if (devpts_strtol(nm, &min) != 0) {
119 sdcmn_err7(("devpts_validate: not a valid minor: %s\n", nm));
120 return (SDEV_VTOR_INVALID);
121 }
122
123 /*
124 * Check if pts driver is attached
125 */
126 if (ptms_subsidiary_attached() == (major_t)-1) {
127 sdcmn_err7(("devpts_validate: subsidiary not attached\n"));
128 return (SDEV_VTOR_INVALID);
129 }
130
131 if (ptms_minor_valid(min, &uid, &gid) == 0) {
132 if (ptms_minor_exists(min)) {
133 sdcmn_err7(("devpts_validate: valid in different zone "
134 "%s\n", nm));
135 return (SDEV_VTOR_SKIP);
136 } else {
137 sdcmn_err7(("devpts_validate: %s not valid pty\n",
138 nm));
139 return (SDEV_VTOR_INVALID);
140 }
141 }
142
143 ASSERT(dv->sdev_attr);
144 if (dv->sdev_attr->va_uid != uid || dv->sdev_attr->va_gid != gid) {
145 dv->sdev_attr->va_uid = uid;
146 dv->sdev_attr->va_gid = gid;
147 gethrestime(&now);
148 dv->sdev_attr->va_atime = now;
149 dv->sdev_attr->va_mtime = now;
150 dv->sdev_attr->va_ctime = now;
151 sdcmn_err7(("devpts_validate: update uid/gid/times%s\n", nm));
152 }
153
154 return (SDEV_VTOR_VALID);
155 }
156
157 /*
158 * This callback is invoked from devname_lookup_func() to create
159 * a pts entry when the node is not found in the cache.
160 */
161 static int
162 devpts_create_rvp(struct sdev_node *ddv, char *nm,
163 void **arg, cred_t *cred, void *whatever, char *whichever)
164 {
165 minor_t min;
166 major_t maj;
167 uid_t uid;
168 gid_t gid;
169 timestruc_t now;
170 struct vattr *vap = (struct vattr *)arg;
171
172 if (devpts_strtol(nm, &min) != 0) {
173 sdcmn_err7(("devpts_create_rvp: not a valid minor: %s\n", nm));
174 return (-1);
175 }
176
177 /*
178 * Check if pts driver is attached and if it is get the major number.
179 */
180 maj = ptms_subsidiary_attached();
181 if (maj == (major_t)-1) {
182 sdcmn_err7(("devpts_create_rvp: subsidiary not attached\n"));
183 return (-1);
184 }
185
186 /*
187 * Only allow creation of ptys allocated to our zone
188 */
189 if (!ptms_minor_valid(min, &uid, &gid)) {
190 sdcmn_err7(("devpts_create_rvp: %s not valid pty"
191 "or not valid in this zone\n", nm));
192 return (-1);
193 }
194
195
196 /*
197 * This is a valid pty (at least at this point in time).
198 * Create the node by setting the attribute. The rest
199 * is taken care of by devname_lookup_func().
200 */
201 *vap = devpts_vattr;
202 vap->va_rdev = makedevice(maj, min);
266 *
267 * There is a potential denial of service here via
268 * fattach on top of a /dev/pts node - any permission changes
269 * applied to the node, apply to the fattached file and not
270 * to the underlying pts node. As a result when the previous
271 * user fdetaches, the pts node is still owned by the previous
272 * owner. To prevent this we don't allow fattach() on top of a pts
273 * node. This is done by a modification in the namefs filesystem
274 * where we check if the underlying node has the /dev/pts vnodeops.
275 * We do this via VOP_REALVP() on the underlying specfs node.
276 * sdev_nodes currently don't have a realvp. If a realvp is ever
277 * created for sdev_nodes, then VOP_REALVP() will return the
278 * actual realvp (possibly a ufs vnode). This will defeat the check
279 * in namefs code which checks if VOP_REALVP() returns a devpts
280 * node. We add an ASSERT here in /dev/pts lookup() to check for
281 * this condition. If sdev_nodes ever get a VOP_REALVP() entry point,
282 * change the code in the namefs filesystem code (in nm_mount()) to
283 * access the realvp of the specfs node directly instead of using
284 * VOP_REALVP().
285 */
286 static int
287 devpts_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
288 struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
289 caller_context_t *ct, int *direntflags, pathname_t *realpnp)
290 {
291 struct sdev_node *sdvp = VTOSDEV(dvp);
292 struct sdev_node *dv;
293 struct vnode *rvp = NULL;
294 int error;
295
296 error = devname_lookup_func(sdvp, nm, vpp, cred, devpts_create_rvp,
297 SDEV_VATTR);
298
299 if (error == 0) {
300 switch ((*vpp)->v_type) {
301 case VCHR:
302 dv = VTOSDEV(VTOS(*vpp)->s_realvp);
303 ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS);
304 break;
305 case VDIR:
306 dv = VTOSDEV(*vpp);
307 break;
308 default:
309 cmn_err(CE_PANIC, "devpts_lookup: Unsupported node "
310 "type: %p: %d", (void *)(*vpp), (*vpp)->v_type);
311 break;
312 }
313 ASSERT(SDEV_HELD(dv));
314 }
315
316 return (error);
317 }
318
319 /*
320 * We allow create to find existing nodes
321 * - if the node doesn't exist - EROFS
322 * - creating an existing dir read-only succeeds, otherwise EISDIR
323 * - exclusive creates fail - EEXIST
324 */
325 static int
326 devpts_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
327 int mode, struct vnode **vpp, struct cred *cred, int flag,
328 caller_context_t *ct, vsecattr_t *vsecp)
329 {
330 int error;
331 struct vnode *vp;
332
333 *vpp = NULL;
334
335 error = devpts_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL,
336 NULL);
337 if (error == 0) {
338 if (excl == EXCL)
339 error = EEXIST;
340 else if (vp->v_type == VDIR && (mode & VWRITE))
341 error = EISDIR;
342 else
343 error = VOP_ACCESS(vp, mode, 0, cred, ct);
344
345 if (error) {
346 VN_RELE(vp);
347 } else
348 *vpp = vp;
349 } else if (error == ENOENT) {
350 error = EROFS;
351 }
352
353 return (error);
354 }
355
356 /*
357 * Display all instantiated pts (subsidiary) device nodes.
358 * A /dev/pts entry will be created only after the first lookup of the
359 * subsidiary device succeeds.
360 */
361 static int
362 devpts_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
363 int *eofp, caller_context_t *ct, int flags)
364 {
365 struct sdev_node *sdvp = VTOSDEV(dvp);
366 if (uiop->uio_offset == 0) {
367 devpts_prunedir(sdvp);
368 }
369
370 return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
371 }
372
373
374 static int
375 devpts_set_id(struct sdev_node *dv, struct vattr *vap, int protocol)
376 {
377 ASSERT((protocol & AT_UID) || (protocol & AT_GID));
378 ptms_set_owner(getminor(SDEVTOV(dv)->v_rdev),
379 vap->va_uid, vap->va_gid);
380 return (0);
381
382 }
383
384 static int
385 devpts_setattr(struct vnode *vp, struct vattr *vap, int flags,
386 struct cred *cred, caller_context_t *ctp)
387 {
388 ASSERT((vp->v_type == VCHR) || (vp->v_type == VDIR));
389 return (devname_setattr_func(vp, vap, flags, cred,
390 devpts_set_id, AT_UID|AT_GID));
391 }
392
393
394 /*
395 * We override lookup and readdir to build entries based on the
396 * in kernel pty table. Also override setattr/setsecattr to
397 * avoid persisting permissions.
398 */
399 const fs_operation_def_t devpts_vnodeops_tbl[] = {
400 VOPNAME_READDIR, { .vop_readdir = devpts_readdir },
401 VOPNAME_LOOKUP, { .vop_lookup = devpts_lookup },
402 VOPNAME_CREATE, { .vop_create = devpts_create },
403 VOPNAME_SETATTR, { .vop_setattr = devpts_setattr },
|