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