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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * vnode ops for the /dev/vt directory
27 */
28
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/sysmacros.h>
32 #include <sys/sunndi.h>
33 #include <fs/fs_subr.h>
34 #include <sys/fs/dv_node.h>
35 #include <sys/fs/sdev_impl.h>
36 #include <sys/policy.h>
37 #include <sys/stat.h>
38 #include <sys/vfs_opreg.h>
39 #include <sys/tty.h>
40 #include <sys/vt_impl.h>
41 #include <sys/note.h>
42
43 #define DEVVT_UID_DEFAULT SDEV_UID_DEFAULT
44 #define DEVVT_GID_DEFAULT (0)
45 #define DEVVT_DEVMODE_DEFAULT (0600)
46 #define DEVVT_ACTIVE_NAME "active"
47 #define DEVVT_CONSUSER_NAME "console_user"
48
49 #define isdigit(ch) ((ch) >= '0' && (ch) <= '9')
50
51 /* attributes for VT nodes */
52 static vattr_t devvt_vattr = {
53 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */
54 VCHR, /* va_type */
55 S_IFCHR | DEVVT_DEVMODE_DEFAULT, /* va_mode */
56 DEVVT_UID_DEFAULT, /* va_uid */
57 DEVVT_GID_DEFAULT, /* va_gid */
58 0 /* 0 hereafter */
59 };
60
61 struct vnodeops *devvt_vnodeops;
62
63 struct vnodeops *
64 devvt_getvnodeops(void)
65 {
66 return (devvt_vnodeops);
67 }
68
69 static int
70 devvt_str2minor(const char *nm, minor_t *mp)
71 {
72 long uminor = 0;
73 char *endptr = NULL;
74
75 if (nm == NULL || !isdigit(*nm))
76 return (EINVAL);
77
78 *mp = 0;
79 if (ddi_strtol(nm, &endptr, 10, &uminor) != 0 ||
80 *endptr != '\0' || uminor < 0) {
81 return (EINVAL);
82 }
83
84 *mp = (minor_t)uminor;
85 return (0);
86 }
87
88 /*
89 * Validate that a node is up-to-date and correct.
90 * A validator may not update the node state or
91 * contents as a read lock permits entry by
92 * multiple threads.
93 */
94 int
95 devvt_validate(struct sdev_node *dv)
96 {
97 minor_t min;
98 char *nm = dv->sdev_name;
99 int rval;
100
101 ASSERT(dv->sdev_state == SDEV_READY);
102 ASSERT(RW_LOCK_HELD(&(dv->sdev_dotdot)->sdev_contents));
103
104 /* validate only READY nodes */
105 if (dv->sdev_state != SDEV_READY) {
106 sdcmn_err(("dev fs: skipping: node not ready %s(%p)",
107 nm, (void *)dv));
108 return (SDEV_VTOR_SKIP);
109 }
110
111 if (vt_wc_attached() == (major_t)-1)
112 return (SDEV_VTOR_INVALID);
113
114 if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) {
115 char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
116 (void) vt_getactive(link, MAXPATHLEN);
117 rval = (strcmp(link, dv->sdev_symlink) == 0) ?
118 SDEV_VTOR_VALID : SDEV_VTOR_STALE;
119 kmem_free(link, MAXPATHLEN);
120 return (rval);
121 }
122
123 if (strcmp(nm, DEVVT_CONSUSER_NAME) == 0) {
124 char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
125 (void) vt_getconsuser(link, MAXPATHLEN);
126 rval = (strcmp(link, dv->sdev_symlink) == 0) ?
127 SDEV_VTOR_VALID : SDEV_VTOR_STALE;
128 kmem_free(link, MAXPATHLEN);
129 return (rval);
130 }
131
132 if (devvt_str2minor(nm, &min) != 0) {
133 return (SDEV_VTOR_INVALID);
134 }
135
136 if (vt_minor_valid(min) == B_FALSE)
137 return (SDEV_VTOR_INVALID);
138
139 return (SDEV_VTOR_VALID);
140 }
141
142 /*
143 * This callback is invoked from devname_lookup_func() to create
144 * a entry when the node is not found in the cache.
145 */
146 /*ARGSUSED*/
147 static int
148 devvt_create_rvp(struct sdev_node *ddv, char *nm,
149 void **arg, cred_t *cred, void *whatever, char *whichever)
150 {
151 minor_t min;
152 major_t maj;
153 struct vattr *vap = (struct vattr *)arg;
154
155 if ((maj = vt_wc_attached()) == (major_t)-1)
156 return (SDEV_VTOR_INVALID);
157
158 if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) {
159 (void) vt_getactive((char *)*arg, MAXPATHLEN);
160 return (0);
161 }
162
163 if (strcmp(nm, DEVVT_CONSUSER_NAME) == 0) {
164 (void) vt_getconsuser((char *)*arg, MAXPATHLEN);
165 return (0);
166 }
167 if (devvt_str2minor(nm, &min) != 0)
168 return (-1);
169
170 if (vt_minor_valid(min) == B_FALSE)
171 return (-1);
172
173 *vap = devvt_vattr;
174 vap->va_rdev = makedevice(maj, min);
175
176 return (0);
177 }
178
179 /*ARGSUSED3*/
180 static int
181 devvt_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
182 struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
183 caller_context_t *ct, int *direntflags, pathname_t *realpnp)
184 {
185 struct sdev_node *sdvp = VTOSDEV(dvp);
186 struct sdev_node *dv;
187 struct vnode *rvp = NULL;
188 int type, error;
189
190 if ((strcmp(nm, DEVVT_ACTIVE_NAME) == 0) ||
191 (strcmp(nm, DEVVT_CONSUSER_NAME) == 0)) {
192 type = SDEV_VLINK;
193 } else {
194 type = SDEV_VATTR;
195 }
196
197 error = devname_lookup_func(sdvp, nm, vpp, cred,
198 devvt_create_rvp, type);
199
200 if (error == 0) {
201 switch ((*vpp)->v_type) {
202 case VCHR:
203 dv = VTOSDEV(VTOS(*vpp)->s_realvp);
204 ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS);
205 break;
206 case VDIR:
207 case VLNK:
208 dv = VTOSDEV(*vpp);
209 break;
210 default:
211 cmn_err(CE_PANIC, "devvt_lookup: Unsupported node "
212 "type: %p: %d", (void *)(*vpp), (*vpp)->v_type);
213 break;
214 }
215 ASSERT(SDEV_HELD(dv));
216 }
217
218 return (error);
219 }
220
221 static void
222 devvt_create_snode(struct sdev_node *ddv, char *nm, struct cred *cred, int type)
223 {
224 int error;
225 struct sdev_node *sdv = NULL;
226 struct vattr vattr;
227 struct vattr *vap = &vattr;
228 major_t maj;
229 minor_t min;
230
231 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
232
233 if ((maj = vt_wc_attached()) == (major_t)-1)
234 return;
235
236 if (strcmp(nm, DEVVT_ACTIVE_NAME) != 0 &&
237 strcmp(nm, DEVVT_CONSUSER_NAME) != 0 &&
238 devvt_str2minor(nm, &min) != 0)
239 return;
240
241 error = sdev_mknode(ddv, nm, &sdv, NULL, NULL, NULL, cred, SDEV_INIT);
242 if (error || !sdv) {
243 return;
244 }
245
246 mutex_enter(&sdv->sdev_lookup_lock);
247 SDEV_BLOCK_OTHERS(sdv, SDEV_LOOKUP);
248 mutex_exit(&sdv->sdev_lookup_lock);
249
250 if (type & SDEV_VATTR) {
251 *vap = devvt_vattr;
252 vap->va_rdev = makedevice(maj, min);
253 error = sdev_mknode(ddv, nm, &sdv, vap, NULL,
254 NULL, cred, SDEV_READY);
255 } else if (type & SDEV_VLINK) {
256 char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
257
258 (void) vt_getactive(link, MAXPATHLEN);
259 *vap = sdev_vattr_lnk;
260 vap->va_size = strlen(link);
261 error = sdev_mknode(ddv, nm, &sdv, vap, NULL,
262 (void *)link, cred, SDEV_READY);
263
264 kmem_free(link, MAXPATHLEN);
265 }
266
267 if (error != 0) {
268 SDEV_RELE(sdv);
269 return;
270 }
271
272 mutex_enter(&sdv->sdev_lookup_lock);
273 SDEV_UNBLOCK_OTHERS(sdv, SDEV_LOOKUP);
274 mutex_exit(&sdv->sdev_lookup_lock);
275
276 }
277
278 static void
279 devvt_rebuild_stale_link(struct sdev_node *ddv, struct sdev_node *dv)
280 {
281 char *link;
282
283 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
284
285 ASSERT((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == 0) ||
286 (strcmp(dv->sdev_name, DEVVT_CONSUSER_NAME) == 0));
287
288 link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
289 if (strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == 0) {
290 (void) vt_getactive(link, MAXPATHLEN);
291 } else if (strcmp(dv->sdev_name, DEVVT_CONSUSER_NAME) == 0) {
292 (void) vt_getconsuser(link, MAXPATHLEN);
293 }
294
295 if (strcmp(link, dv->sdev_symlink) != 0) {
296 strfree(dv->sdev_symlink);
297 dv->sdev_symlink = strdup(link);
298 dv->sdev_attr->va_size = strlen(link);
299 }
300 kmem_free(link, MAXPATHLEN);
301 }
302
303 /*
304 * First step in refreshing directory contents.
305 * Remove each invalid entry and rebuild the link
306 * reference for each stale entry.
307 */
308 static void
309 devvt_prunedir(struct sdev_node *ddv)
310 {
311 struct vnode *vp;
312 struct sdev_node *dv, *next = NULL;
313 int (*vtor)(struct sdev_node *) = NULL;
314
315 ASSERT(ddv->sdev_flags & SDEV_VTOR);
316
317 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
318 ASSERT(vtor);
319
320 for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
321 next = SDEV_NEXT_ENTRY(ddv, dv);
322
323 switch (vtor(dv)) {
324 case SDEV_VTOR_VALID:
325 break;
326 case SDEV_VTOR_SKIP:
327 break;
328 case SDEV_VTOR_INVALID:
329 vp = SDEVTOV(dv);
330 if (vp->v_count != 0)
331 break;
332 /* remove the cached node */
333 SDEV_HOLD(dv);
334 (void) sdev_cache_update(ddv, &dv,
335 dv->sdev_name, SDEV_CACHE_DELETE);
336 SDEV_RELE(dv);
337 break;
338 case SDEV_VTOR_STALE:
339 devvt_rebuild_stale_link(ddv, dv);
340 break;
341 }
342 }
343 }
344
345 static void
346 devvt_cleandir(struct vnode *dvp, struct cred *cred)
347 {
348 struct sdev_node *sdvp = VTOSDEV(dvp);
349 struct sdev_node *dv, *next = NULL;
350 int min, cnt;
351 char found = 0;
352
353 mutex_enter(&vc_lock);
354 cnt = VC_INSTANCES_COUNT;
355 mutex_exit(&vc_lock);
356
357 if (rw_tryupgrade(&sdvp->sdev_contents) == NULL) {
358 rw_exit(&sdvp->sdev_contents);
359 rw_enter(&sdvp->sdev_contents, RW_WRITER);
360 }
361
362 /* 1. prune invalid nodes and rebuild stale symlinks */
363 devvt_prunedir(sdvp);
364
365 /* 2. create missing nodes */
366 for (min = 0; min < cnt; min++) {
367 char nm[16];
368
369 if (vt_minor_valid(min) == B_FALSE)
370 continue;
371
372 (void) snprintf(nm, sizeof (nm), "%d", min);
373 found = 0;
374 for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) {
375 next = SDEV_NEXT_ENTRY(sdvp, dv);
376
377 /* validate only ready nodes */
378 if (dv->sdev_state != SDEV_READY)
379 continue;
380 if (strcmp(nm, dv->sdev_name) == 0) {
381 found = 1;
382 break;
383 }
384 }
385 if (!found) {
386 devvt_create_snode(sdvp, nm, cred, SDEV_VATTR);
387 }
388 }
389
390 /* 3. create active link node and console user link node */
391 found = 0;
392 for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) {
393 next = SDEV_NEXT_ENTRY(sdvp, dv);
394
395 /* validate only ready nodes */
396 if (dv->sdev_state != SDEV_READY)
397 continue;
398 if ((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == NULL))
399 found |= 0x01;
400 if ((strcmp(dv->sdev_name, DEVVT_CONSUSER_NAME) == NULL))
401 found |= 0x02;
402
403 if ((found & 0x01) && (found & 0x02))
404 break;
405 }
406 if (!(found & 0x01))
407 devvt_create_snode(sdvp, DEVVT_ACTIVE_NAME, cred, SDEV_VLINK);
408 if (!(found & 0x02))
409 devvt_create_snode(sdvp, DEVVT_CONSUSER_NAME, cred, SDEV_VLINK);
410
411 rw_downgrade(&sdvp->sdev_contents);
412 }
413
414 /*ARGSUSED4*/
415 static int
416 devvt_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
417 int *eofp, caller_context_t *ct, int flags)
418 {
419 if (uiop->uio_offset == 0) {
420 devvt_cleandir(dvp, cred);
421 }
422
423 return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
424 }
425
426 /*
427 * We allow create to find existing nodes
428 * - if the node doesn't exist - EROFS
429 * - creating an existing dir read-only succeeds, otherwise EISDIR
430 * - exclusive creates fail - EEXIST
431 */
432 /*ARGSUSED2*/
433 static int
434 devvt_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
435 int mode, struct vnode **vpp, struct cred *cred, int flag,
436 caller_context_t *ct, vsecattr_t *vsecp)
437 {
438 int error;
439 struct vnode *vp;
440
441 *vpp = NULL;
442
443 if ((error = devvt_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL,
444 NULL)) != 0) {
445 if (error == ENOENT)
446 error = EROFS;
447 return (error);
448 }
449
450 if (excl == EXCL)
451 error = EEXIST;
452 else if (vp->v_type == VDIR && (mode & VWRITE))
453 error = EISDIR;
454 else
455 error = VOP_ACCESS(vp, mode, 0, cred, ct);
456
457 if (error) {
458 VN_RELE(vp);
459 } else
460 *vpp = vp;
461
462 return (error);
463 }
464
465 const fs_operation_def_t devvt_vnodeops_tbl[] = {
466 VOPNAME_READDIR, { .vop_readdir = devvt_readdir },
467 VOPNAME_LOOKUP, { .vop_lookup = devvt_lookup },
468 VOPNAME_CREATE, { .vop_create = devvt_create },
469 VOPNAME_REMOVE, { .error = fs_nosys },
470 VOPNAME_MKDIR, { .error = fs_nosys },
471 VOPNAME_RMDIR, { .error = fs_nosys },
472 VOPNAME_SYMLINK, { .error = fs_nosys },
473 VOPNAME_SETSECATTR, { .error = fs_nosys },
474 NULL, NULL
475 };