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