sdev_vtops.c revision 11279:b69054bce66e
1252867Sdelphij/*
2252867Sdelphij * CDDL HEADER START
3252867Sdelphij *
4252867Sdelphij * The contents of this file are subject to the terms of the
5252867Sdelphij * Common Development and Distribution License (the "License").
6252867Sdelphij * You may not use this file except in compliance with the License.
7252867Sdelphij *
8252867Sdelphij * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9252867Sdelphij * or http://www.opensolaris.org/os/licensing.
10252867Sdelphij * See the License for the specific language governing permissions
11252867Sdelphij * and limitations under the License.
12252867Sdelphij *
13252867Sdelphij * When distributing Covered Code, include this CDDL HEADER in each
14252867Sdelphij * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15252867Sdelphij * If applicable, add the following below this CDDL HEADER, with the
16252867Sdelphij * fields enclosed by brackets "[]" replaced with your own identifying
17252867Sdelphij * information: Portions Copyright [yyyy] [name of copyright owner]
18252867Sdelphij *
19252867Sdelphij * CDDL HEADER END
20252867Sdelphij */
21252867Sdelphij/*
22252867Sdelphij * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23252867Sdelphij * Use is subject to license terms.
24252867Sdelphij */
25252867Sdelphij
26252867Sdelphij/*
27252867Sdelphij * vnode ops for the /dev/vt directory
28252867Sdelphij */
29252867Sdelphij
30252867Sdelphij#include <sys/types.h>
31252867Sdelphij#include <sys/param.h>
32252867Sdelphij#include <sys/sysmacros.h>
33252867Sdelphij#include <sys/sunndi.h>
34252867Sdelphij#include <fs/fs_subr.h>
35252867Sdelphij#include <sys/fs/dv_node.h>
36252867Sdelphij#include <sys/fs/sdev_impl.h>
37252867Sdelphij#include <sys/policy.h>
38252867Sdelphij#include <sys/stat.h>
39252867Sdelphij#include <sys/vfs_opreg.h>
40252867Sdelphij#include <sys/tty.h>
41252867Sdelphij#include <sys/vt_impl.h>
42252867Sdelphij#include <sys/note.h>
43252867Sdelphij
44252867Sdelphij/* warlock in this file only cares about variables shared by vt and devfs */
45252867Sdelphij_NOTE(SCHEME_PROTECTS_DATA("Do not care", sdev_node vattr vnode))
46252867Sdelphij
47252867Sdelphij#define	DEVVT_UID_DEFAULT	SDEV_UID_DEFAULT
48252867Sdelphij#define	DEVVT_GID_DEFAULT	(0)
49252867Sdelphij#define	DEVVT_DEVMODE_DEFAULT	(0600)
50252867Sdelphij#define	DEVVT_ACTIVE_NAME	"active"
51252867Sdelphij
52252867Sdelphij#define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
53252867Sdelphij
54252867Sdelphij/* attributes for VT nodes */
55252867Sdelphijstatic vattr_t devvt_vattr = {
56252867Sdelphij	AT_TYPE|AT_MODE|AT_UID|AT_GID,		/* va_mask */
57252867Sdelphij	VCHR,					/* va_type */
58252867Sdelphij	S_IFCHR | DEVVT_DEVMODE_DEFAULT,	/* va_mode */
59252867Sdelphij	DEVVT_UID_DEFAULT,			/* va_uid */
60252867Sdelphij	DEVVT_GID_DEFAULT,			/* va_gid */
61252867Sdelphij	0					/* 0 hereafter */
62252867Sdelphij};
63252867Sdelphij
64252867Sdelphijstruct vnodeops		*devvt_vnodeops;
65252867Sdelphij
66252867Sdelphijstruct vnodeops *
67252867Sdelphijdevvt_getvnodeops(void)
68252867Sdelphij{
69252867Sdelphij	return (devvt_vnodeops);
70252867Sdelphij}
71252867Sdelphij
72252867Sdelphijstatic int
73252867Sdelphijdevvt_str2minor(const char *nm, minor_t *mp)
74252867Sdelphij{
75252867Sdelphij	long uminor = 0;
76252867Sdelphij	char *endptr = NULL;
77252867Sdelphij
78252867Sdelphij	if (nm == NULL || !isdigit(*nm))
79252867Sdelphij		return (EINVAL);
80252867Sdelphij
81252867Sdelphij	*mp = 0;
82252867Sdelphij	if (ddi_strtol(nm, &endptr, 10, &uminor) != 0 ||
83252867Sdelphij	    *endptr != '\0' || uminor < 0) {
84252867Sdelphij		return (EINVAL);
85252867Sdelphij	}
86252867Sdelphij
87252867Sdelphij	*mp = (minor_t)uminor;
88252867Sdelphij	return (0);
89252867Sdelphij}
90252867Sdelphij
91252867Sdelphij/*ARGSUSED*/
92252867Sdelphijint
93252867Sdelphijdevvt_validate(struct sdev_node *dv)
94252867Sdelphij{
95252867Sdelphij	minor_t min;
96252867Sdelphij	char *nm = dv->sdev_name;
97252867Sdelphij
98252867Sdelphij	ASSERT(!(dv->sdev_flags & SDEV_STALE));
99252867Sdelphij	ASSERT(dv->sdev_state == SDEV_READY);
100252867Sdelphij
101252867Sdelphij	/* validate only READY nodes */
102252867Sdelphij	if (dv->sdev_state != SDEV_READY) {
103252867Sdelphij		sdcmn_err(("dev fs: skipping: node not ready %s(%p)",
104252867Sdelphij		    nm, (void *)dv));
105252867Sdelphij		return (SDEV_VTOR_SKIP);
106252867Sdelphij	}
107252867Sdelphij
108252867Sdelphij	if (vt_wc_attached() == (major_t)-1)
109252867Sdelphij		return (SDEV_VTOR_INVALID);
110252867Sdelphij
111252867Sdelphij	if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) {
112252867Sdelphij		char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
113252867Sdelphij
114252867Sdelphij		(void) vt_getactive(link, MAXPATHLEN);
115252867Sdelphij		if (strcmp(link, dv->sdev_symlink) != 0) {
116252867Sdelphij			kmem_free(dv->sdev_symlink,
117252867Sdelphij			    strlen(dv->sdev_symlink) + 1);
118252867Sdelphij			dv->sdev_symlink = i_ddi_strdup(link, KM_SLEEP);
119252867Sdelphij			dv->sdev_attr->va_size = strlen(link);
120252867Sdelphij		}
121252867Sdelphij		kmem_free(link, MAXPATHLEN);
122252867Sdelphij		return (SDEV_VTOR_VALID);
123252867Sdelphij	} else if (devvt_str2minor(nm, &min) != 0) {
124252867Sdelphij		return (SDEV_VTOR_INVALID);
125252867Sdelphij	}
126252867Sdelphij
127252867Sdelphij	if (vt_minor_valid(min) == B_FALSE)
128252867Sdelphij		return (SDEV_VTOR_INVALID);
129252867Sdelphij
130252867Sdelphij	return (SDEV_VTOR_VALID);
131252867Sdelphij}
132252867Sdelphij
133252867Sdelphij/*
134252867Sdelphij * This callback is invoked from devname_lookup_func() to create
135252867Sdelphij * a entry when the node is not found in the cache.
136252867Sdelphij */
137252867Sdelphij/*ARGSUSED*/
138252867Sdelphijstatic int
139252867Sdelphijdevvt_create_rvp(struct sdev_node *ddv, char *nm,
140252867Sdelphij    void **arg, cred_t *cred, void *whatever, char *whichever)
141252867Sdelphij{
142252867Sdelphij	minor_t min;
143252867Sdelphij	major_t maj;
144252867Sdelphij	struct vattr *vap = (struct vattr *)arg;
145252867Sdelphij
146252867Sdelphij	if ((maj = vt_wc_attached()) == (major_t)-1)
147252867Sdelphij		return (SDEV_VTOR_INVALID);
148252867Sdelphij
149252867Sdelphij	if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) {
150252867Sdelphij		(void) vt_getactive((char *)*arg, MAXPATHLEN);
151252867Sdelphij		return (0);
152252867Sdelphij	}
153252867Sdelphij
154252867Sdelphij	if (devvt_str2minor(nm, &min) != 0)
155252867Sdelphij		return (-1);
156252867Sdelphij
157252867Sdelphij	if (vt_minor_valid(min) == B_FALSE)
158252867Sdelphij		return (-1);
159252867Sdelphij
160252867Sdelphij	*vap = devvt_vattr;
161252867Sdelphij	vap->va_rdev = makedevice(maj, min);
162252867Sdelphij
163252867Sdelphij	return (0);
164252867Sdelphij}
165252867Sdelphij
166252867Sdelphij/*ARGSUSED3*/
167252867Sdelphijstatic int
168252867Sdelphijdevvt_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
169252867Sdelphij    struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
170252867Sdelphij    caller_context_t *ct, int *direntflags, pathname_t *realpnp)
171252867Sdelphij{
172252867Sdelphij	struct sdev_node *sdvp = VTOSDEV(dvp);
173252867Sdelphij	struct sdev_node *dv;
174252867Sdelphij	struct vnode *rvp = NULL;
175252867Sdelphij	int type, error;
176252867Sdelphij
177252867Sdelphij	if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) {
178252867Sdelphij		type = SDEV_VLINK;
179252867Sdelphij	} else {
180252867Sdelphij		type = SDEV_VATTR;
181252867Sdelphij	}
182252867Sdelphij
183252867Sdelphij/* Give warlock a more clear call graph */
184252867Sdelphij#ifndef __lock_lint
185252867Sdelphij	error = devname_lookup_func(sdvp, nm, vpp, cred,
186252867Sdelphij	    devvt_create_rvp, type);
187252867Sdelphij#else
188252867Sdelphij	devvt_create_rvp(0, 0, 0, 0, 0, 0);
189252867Sdelphij#endif
190252867Sdelphij
191252867Sdelphij	if (error == 0) {
192252867Sdelphij		switch ((*vpp)->v_type) {
193252867Sdelphij		case VCHR:
194252867Sdelphij			dv = VTOSDEV(VTOS(*vpp)->s_realvp);
195252867Sdelphij			ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS);
196252867Sdelphij			break;
197252867Sdelphij		case VDIR:
198252867Sdelphij		case VLNK:
199252867Sdelphij			dv = VTOSDEV(*vpp);
200252867Sdelphij			break;
201252867Sdelphij		default:
202252867Sdelphij			cmn_err(CE_PANIC, "devvt_lookup: Unsupported node "
203252867Sdelphij			    "type: %p: %d", (void *)(*vpp), (*vpp)->v_type);
204252867Sdelphij			break;
205252867Sdelphij		}
206252867Sdelphij		ASSERT(SDEV_HELD(dv));
207252867Sdelphij	}
208252867Sdelphij
209252867Sdelphij	return (error);
210252867Sdelphij}
211252867Sdelphij
212252867Sdelphijstatic void
213252867Sdelphijdevvt_create_snode(struct sdev_node *ddv, char *nm, struct cred *cred, int type)
214252867Sdelphij{
215252867Sdelphij	int error;
216252867Sdelphij	struct sdev_node *sdv = NULL;
217252867Sdelphij	struct vattr vattr;
218252867Sdelphij	struct vattr *vap = &vattr;
219252867Sdelphij	major_t maj;
220252867Sdelphij	minor_t min;
221252867Sdelphij
222252867Sdelphij	if ((maj = vt_wc_attached()) == (major_t)-1)
223252867Sdelphij		return;
224252867Sdelphij
225252867Sdelphij	if (strcmp(nm, DEVVT_ACTIVE_NAME) != 0 &&
226252867Sdelphij	    devvt_str2minor(nm, &min) != 0)
227252867Sdelphij		return;
228252867Sdelphij
229252867Sdelphij	error = sdev_mknode(ddv, nm, &sdv, NULL, NULL, NULL, cred, SDEV_INIT);
230252867Sdelphij	if (error || !sdv) {
231252867Sdelphij		return;
232252867Sdelphij	}
233252867Sdelphij
234252867Sdelphij	mutex_enter(&sdv->sdev_lookup_lock);
235252867Sdelphij	SDEV_BLOCK_OTHERS(sdv, SDEV_LOOKUP);
236252867Sdelphij	mutex_exit(&sdv->sdev_lookup_lock);
237269613Sjhb
238269613Sjhb	if (type & SDEV_VATTR) {
239269613Sjhb		*vap = devvt_vattr;
240252867Sdelphij		vap->va_rdev = makedevice(maj, min);
241252867Sdelphij		error = sdev_mknode(ddv, nm, &sdv, vap, NULL,
242252867Sdelphij		    NULL, cred, SDEV_READY);
243252867Sdelphij	} else if (type & SDEV_VLINK) {
244252867Sdelphij		char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
245252867Sdelphij
246252867Sdelphij		(void) vt_getactive(link, MAXPATHLEN);
247252867Sdelphij		*vap = sdev_vattr_lnk;
248252867Sdelphij		vap->va_size = strlen(link);
249252867Sdelphij		error = sdev_mknode(ddv, nm, &sdv, vap, NULL,
250252867Sdelphij		    (void *)link, cred, SDEV_READY);
251252867Sdelphij
252252867Sdelphij		kmem_free(link, MAXPATHLEN);
253252867Sdelphij	}
254252867Sdelphij
255252867Sdelphij	mutex_enter(&sdv->sdev_lookup_lock);
256252867Sdelphij	SDEV_UNBLOCK_OTHERS(sdv, SDEV_LOOKUP);
257252867Sdelphij	mutex_exit(&sdv->sdev_lookup_lock);
258252867Sdelphij
259252867Sdelphij}
260252867Sdelphij
261252867Sdelphijstatic void
262252867Sdelphijdevvt_prunedir(struct sdev_node *ddv)
263252867Sdelphij{
264252867Sdelphij	struct vnode *vp;
265252867Sdelphij	struct sdev_node *dv, *next = NULL;
266252867Sdelphij	int (*vtor)(struct sdev_node *) = NULL;
267252867Sdelphij
268252867Sdelphij	ASSERT(ddv->sdev_flags & SDEV_VTOR);
269252867Sdelphij
270252867Sdelphij	vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
271252867Sdelphij	ASSERT(vtor);
272252867Sdelphij
273270384Sdelphij	for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
274252867Sdelphij		next = SDEV_NEXT_ENTRY(ddv, dv);
275252867Sdelphij
276252867Sdelphij		/* skip stale nodes */
277252867Sdelphij		if (dv->sdev_flags & SDEV_STALE)
278252867Sdelphij			continue;
279252867Sdelphij
280252867Sdelphij		/* validate and prune only ready nodes */
281252867Sdelphij		if (dv->sdev_state != SDEV_READY)
282252867Sdelphij			continue;
283252867Sdelphij
284252867Sdelphij		switch (vtor(dv)) {
285252867Sdelphij		case SDEV_VTOR_VALID:
286252867Sdelphij		case SDEV_VTOR_SKIP:
287252867Sdelphij			continue;
288252867Sdelphij		case SDEV_VTOR_INVALID:
289252867Sdelphij		case SDEV_VTOR_STALE:
290252867Sdelphij			sdcmn_err7(("destroy invalid "
291252867Sdelphij			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
292252867Sdelphij			break;
293252867Sdelphij		}
294252867Sdelphij		vp = SDEVTOV(dv);
295252867Sdelphij		if (vp->v_count > 0)
296252867Sdelphij			continue;
297252867Sdelphij		SDEV_HOLD(dv);
298252867Sdelphij		/* remove the cache node */
299252867Sdelphij		(void) sdev_cache_update(ddv, &dv, dv->sdev_name,
300252867Sdelphij		    SDEV_CACHE_DELETE);
301252867Sdelphij	}
302252867Sdelphij}
303
304static void
305devvt_cleandir(struct vnode *dvp, struct cred *cred)
306{
307	struct sdev_node *sdvp = VTOSDEV(dvp);
308	struct sdev_node *dv, *next = NULL;
309	int min, cnt;
310	int found = 0;
311
312	mutex_enter(&vc_lock);
313	cnt = VC_INSTANCES_COUNT;
314	mutex_exit(&vc_lock);
315
316/* We have to fool warlock this way, otherwise it will complain */
317#ifndef	__lock_lint
318	if (rw_tryupgrade(&sdvp->sdev_contents) == NULL) {
319		rw_exit(&sdvp->sdev_contents);
320		rw_enter(&sdvp->sdev_contents, RW_WRITER);
321	}
322#else
323	rw_enter(&sdvp->sdev_contents, RW_WRITER);
324#endif
325
326	/* 1. create missed nodes */
327	for (min = 0; min < cnt; min++) {
328		char nm[16];
329
330		if (vt_minor_valid(min) == B_FALSE)
331			continue;
332
333		(void) snprintf(nm, sizeof (nm), "%d", min);
334		found = 0;
335		for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) {
336			next = SDEV_NEXT_ENTRY(sdvp, dv);
337
338			/* skip stale nodes */
339			if (dv->sdev_flags & SDEV_STALE)
340				continue;
341			/* validate and prune only ready nodes */
342			if (dv->sdev_state != SDEV_READY)
343				continue;
344			if (strcmp(nm, dv->sdev_name) == 0) {
345				found = 1;
346				break;
347			}
348		}
349		if (!found) {
350			devvt_create_snode(sdvp, nm, cred, SDEV_VATTR);
351		}
352	}
353
354	/* 2. create active link node */
355	found = 0;
356	for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) {
357		next = SDEV_NEXT_ENTRY(sdvp, dv);
358
359		/* skip stale nodes */
360		if (dv->sdev_flags & SDEV_STALE)
361			continue;
362		/* validate and prune only ready nodes */
363		if (dv->sdev_state != SDEV_READY)
364			continue;
365		if ((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == NULL)) {
366			found = 1;
367			break;
368		}
369	}
370	if (!found)
371		devvt_create_snode(sdvp, DEVVT_ACTIVE_NAME, cred, SDEV_VLINK);
372
373	/* 3. cleanup invalid nodes */
374	devvt_prunedir(sdvp);
375
376#ifndef	__lock_lint
377	rw_downgrade(&sdvp->sdev_contents);
378#else
379	rw_exit(&sdvp->sdev_contents);
380#endif
381}
382
383/*ARGSUSED4*/
384static int
385devvt_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
386    int *eofp, caller_context_t *ct, int flags)
387{
388	if (uiop->uio_offset == 0) {
389		devvt_cleandir(dvp, cred);
390	}
391
392	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
393}
394
395/*
396 * We allow create to find existing nodes
397 *	- if the node doesn't exist - EROFS
398 *	- creating an existing dir read-only succeeds, otherwise EISDIR
399 *	- exclusive creates fail - EEXIST
400 */
401/*ARGSUSED2*/
402static int
403devvt_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
404    int mode, struct vnode **vpp, struct cred *cred, int flag,
405    caller_context_t *ct, vsecattr_t *vsecp)
406{
407	int error;
408	struct vnode *vp;
409
410	*vpp = NULL;
411
412	if ((error = devvt_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL,
413	    NULL)) != 0) {
414		if (error == ENOENT)
415			error = EROFS;
416		return (error);
417	}
418
419	if (excl == EXCL)
420		error = EEXIST;
421	else if (vp->v_type == VDIR && (mode & VWRITE))
422		error = EISDIR;
423	else
424		error = VOP_ACCESS(vp, mode, 0, cred, ct);
425
426	if (error) {
427		VN_RELE(vp);
428	} else
429		*vpp = vp;
430
431	return (error);
432}
433
434const fs_operation_def_t devvt_vnodeops_tbl[] = {
435	VOPNAME_READDIR,	{ .vop_readdir = devvt_readdir },
436	VOPNAME_LOOKUP,		{ .vop_lookup = devvt_lookup },
437	VOPNAME_CREATE,		{ .vop_create = devvt_create },
438	VOPNAME_REMOVE,		{ .error = fs_nosys },
439	VOPNAME_MKDIR,		{ .error = fs_nosys },
440	VOPNAME_RMDIR,		{ .error = fs_nosys },
441	VOPNAME_SYMLINK,	{ .error = fs_nosys },
442	VOPNAME_SETSECATTR,	{ .error = fs_nosys },
443	NULL,			NULL
444};
445