xref: /illumos-gate/usr/src/uts/common/fs/dev/sdev_vtops.c (revision 3e95bd4ab92abca814bd28e854607d1975c7dc88)
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 */
55 static 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 
64 struct vnodeops		*devvt_vnodeops;
65 
66 struct vnodeops *
67 devvt_getvnodeops(void)
68 {
69 	return (devvt_vnodeops);
70 }
71 
72 static int
73 devvt_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 /*ARGSUSED*/
92 int
93 devvt_validate(struct sdev_node *dv)
94 {
95 	minor_t min;
96 	char *nm = dv->sdev_name;
97 
98 	ASSERT(!(dv->sdev_flags & SDEV_STALE));
99 	ASSERT(dv->sdev_state == SDEV_READY);
100 
101 	/* validate only READY nodes */
102 	if (dv->sdev_state != SDEV_READY) {
103 		sdcmn_err(("dev fs: skipping: node not ready %s(%p)",
104 		    nm, (void *)dv));
105 		return (SDEV_VTOR_SKIP);
106 	}
107 
108 	if (vt_wc_attached() == (major_t)-1)
109 		return (SDEV_VTOR_INVALID);
110 
111 	if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) {
112 		char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
113 
114 		(void) vt_getactive(link, MAXPATHLEN);
115 		if (strcmp(link, dv->sdev_symlink) != 0) {
116 			strfree(dv->sdev_symlink);
117 			dv->sdev_symlink = strdup(link);
118 			dv->sdev_attr->va_size = strlen(link);
119 		}
120 		kmem_free(link, MAXPATHLEN);
121 		return (SDEV_VTOR_VALID);
122 	}
123 
124 	if (strcmp(nm, DEVVT_CONSUSER_NAME) == 0) {
125 		char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
126 
127 		(void) vt_getconsuser(link, MAXPATHLEN);
128 		if (strcmp(link, dv->sdev_symlink) != 0) {
129 			strfree(dv->sdev_symlink);
130 			dv->sdev_symlink = strdup(link);
131 			dv->sdev_attr->va_size = strlen(link);
132 		}
133 		kmem_free(link, MAXPATHLEN);
134 		return (SDEV_VTOR_VALID);
135 	}
136 
137 	if (devvt_str2minor(nm, &min) != 0) {
138 		return (SDEV_VTOR_INVALID);
139 	}
140 
141 	if (vt_minor_valid(min) == B_FALSE)
142 		return (SDEV_VTOR_INVALID);
143 
144 	return (SDEV_VTOR_VALID);
145 }
146 
147 /*
148  * This callback is invoked from devname_lookup_func() to create
149  * a entry when the node is not found in the cache.
150  */
151 /*ARGSUSED*/
152 static int
153 devvt_create_rvp(struct sdev_node *ddv, char *nm,
154     void **arg, cred_t *cred, void *whatever, char *whichever)
155 {
156 	minor_t min;
157 	major_t maj;
158 	struct vattr *vap = (struct vattr *)arg;
159 
160 	if ((maj = vt_wc_attached()) == (major_t)-1)
161 		return (SDEV_VTOR_INVALID);
162 
163 	if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) {
164 		(void) vt_getactive((char *)*arg, MAXPATHLEN);
165 		return (0);
166 	}
167 
168 	if (strcmp(nm, DEVVT_CONSUSER_NAME) == 0) {
169 		(void) vt_getconsuser((char *)*arg, MAXPATHLEN);
170 		return (0);
171 	}
172 	if (devvt_str2minor(nm, &min) != 0)
173 		return (-1);
174 
175 	if (vt_minor_valid(min) == B_FALSE)
176 		return (-1);
177 
178 	*vap = devvt_vattr;
179 	vap->va_rdev = makedevice(maj, min);
180 
181 	return (0);
182 }
183 
184 /*ARGSUSED3*/
185 static int
186 devvt_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
187     struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
188     caller_context_t *ct, int *direntflags, pathname_t *realpnp)
189 {
190 	struct sdev_node *sdvp = VTOSDEV(dvp);
191 	struct sdev_node *dv;
192 	struct vnode *rvp = NULL;
193 	int type, error;
194 
195 	if ((strcmp(nm, DEVVT_ACTIVE_NAME) == 0) ||
196 	    (strcmp(nm, DEVVT_CONSUSER_NAME) == 0)) {
197 		type = SDEV_VLINK;
198 	} else {
199 		type = SDEV_VATTR;
200 	}
201 
202 /* Give warlock a more clear call graph */
203 #ifndef __lock_lint
204 	error = devname_lookup_func(sdvp, nm, vpp, cred,
205 	    devvt_create_rvp, type);
206 #else
207 	devvt_create_rvp(0, 0, 0, 0, 0, 0);
208 #endif
209 
210 	if (error == 0) {
211 		switch ((*vpp)->v_type) {
212 		case VCHR:
213 			dv = VTOSDEV(VTOS(*vpp)->s_realvp);
214 			ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS);
215 			break;
216 		case VDIR:
217 		case VLNK:
218 			dv = VTOSDEV(*vpp);
219 			break;
220 		default:
221 			cmn_err(CE_PANIC, "devvt_lookup: Unsupported node "
222 			    "type: %p: %d", (void *)(*vpp), (*vpp)->v_type);
223 			break;
224 		}
225 		ASSERT(SDEV_HELD(dv));
226 	}
227 
228 	return (error);
229 }
230 
231 static void
232 devvt_create_snode(struct sdev_node *ddv, char *nm, struct cred *cred, int type)
233 {
234 	int error;
235 	struct sdev_node *sdv = NULL;
236 	struct vattr vattr;
237 	struct vattr *vap = &vattr;
238 	major_t maj;
239 	minor_t min;
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 	mutex_enter(&sdv->sdev_lookup_lock);
276 	SDEV_UNBLOCK_OTHERS(sdv, SDEV_LOOKUP);
277 	mutex_exit(&sdv->sdev_lookup_lock);
278 
279 }
280 
281 static void
282 devvt_prunedir(struct sdev_node *ddv)
283 {
284 	struct vnode *vp;
285 	struct sdev_node *dv, *next = NULL;
286 	int (*vtor)(struct sdev_node *) = NULL;
287 
288 	ASSERT(ddv->sdev_flags & SDEV_VTOR);
289 
290 	vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
291 	ASSERT(vtor);
292 
293 	for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
294 		next = SDEV_NEXT_ENTRY(ddv, dv);
295 
296 		/* skip stale nodes */
297 		if (dv->sdev_flags & SDEV_STALE)
298 			continue;
299 
300 		/* validate and prune only ready nodes */
301 		if (dv->sdev_state != SDEV_READY)
302 			continue;
303 
304 		switch (vtor(dv)) {
305 		case SDEV_VTOR_VALID:
306 		case SDEV_VTOR_SKIP:
307 			continue;
308 		case SDEV_VTOR_INVALID:
309 		case SDEV_VTOR_STALE:
310 			sdcmn_err7(("destroy invalid "
311 			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
312 			break;
313 		}
314 		vp = SDEVTOV(dv);
315 		if (vp->v_count > 0)
316 			continue;
317 		SDEV_HOLD(dv);
318 		/* remove the cache node */
319 		(void) sdev_cache_update(ddv, &dv, dv->sdev_name,
320 		    SDEV_CACHE_DELETE);
321 	}
322 }
323 
324 static void
325 devvt_cleandir(struct vnode *dvp, struct cred *cred)
326 {
327 	struct sdev_node *sdvp = VTOSDEV(dvp);
328 	struct sdev_node *dv, *next = NULL;
329 	int min, cnt;
330 	char found = 0;
331 
332 	mutex_enter(&vc_lock);
333 	cnt = VC_INSTANCES_COUNT;
334 	mutex_exit(&vc_lock);
335 
336 /* We have to fool warlock this way, otherwise it will complain */
337 #ifndef	__lock_lint
338 	if (rw_tryupgrade(&sdvp->sdev_contents) == NULL) {
339 		rw_exit(&sdvp->sdev_contents);
340 		rw_enter(&sdvp->sdev_contents, RW_WRITER);
341 	}
342 #else
343 	rw_enter(&sdvp->sdev_contents, RW_WRITER);
344 #endif
345 
346 	/* 1. create missed nodes */
347 	for (min = 0; min < cnt; min++) {
348 		char nm[16];
349 
350 		if (vt_minor_valid(min) == B_FALSE)
351 			continue;
352 
353 		(void) snprintf(nm, sizeof (nm), "%d", min);
354 		found = 0;
355 		for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) {
356 			next = SDEV_NEXT_ENTRY(sdvp, dv);
357 
358 			/* skip stale nodes */
359 			if (dv->sdev_flags & SDEV_STALE)
360 				continue;
361 			/* validate and prune only ready nodes */
362 			if (dv->sdev_state != SDEV_READY)
363 				continue;
364 			if (strcmp(nm, dv->sdev_name) == 0) {
365 				found = 1;
366 				break;
367 			}
368 		}
369 		if (!found) {
370 			devvt_create_snode(sdvp, nm, cred, SDEV_VATTR);
371 		}
372 	}
373 
374 	/* 2. create active link node and console user link node */
375 	found = 0;
376 	for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) {
377 		next = SDEV_NEXT_ENTRY(sdvp, dv);
378 
379 		/* skip stale nodes */
380 		if (dv->sdev_flags & SDEV_STALE)
381 			continue;
382 		/* validate and prune only ready nodes */
383 		if (dv->sdev_state != SDEV_READY)
384 			continue;
385 		if ((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == NULL))
386 			found |= 0x01;
387 		if ((strcmp(dv->sdev_name, DEVVT_CONSUSER_NAME) == NULL))
388 			found |= 0x02;
389 
390 		if ((found & 0x01) && (found & 0x02))
391 			break;
392 	}
393 	if (!(found & 0x01))
394 		devvt_create_snode(sdvp, DEVVT_ACTIVE_NAME, cred, SDEV_VLINK);
395 
396 	if (!(found & 0x02))
397 		devvt_create_snode(sdvp, DEVVT_CONSUSER_NAME, cred, SDEV_VLINK);
398 
399 	/* 3. cleanup invalid nodes */
400 	devvt_prunedir(sdvp);
401 
402 #ifndef	__lock_lint
403 	rw_downgrade(&sdvp->sdev_contents);
404 #else
405 	rw_exit(&sdvp->sdev_contents);
406 #endif
407 }
408 
409 /*ARGSUSED4*/
410 static int
411 devvt_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
412     int *eofp, caller_context_t *ct, int flags)
413 {
414 	if (uiop->uio_offset == 0) {
415 		devvt_cleandir(dvp, cred);
416 	}
417 
418 	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
419 }
420 
421 /*
422  * We allow create to find existing nodes
423  *	- if the node doesn't exist - EROFS
424  *	- creating an existing dir read-only succeeds, otherwise EISDIR
425  *	- exclusive creates fail - EEXIST
426  */
427 /*ARGSUSED2*/
428 static int
429 devvt_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
430     int mode, struct vnode **vpp, struct cred *cred, int flag,
431     caller_context_t *ct, vsecattr_t *vsecp)
432 {
433 	int error;
434 	struct vnode *vp;
435 
436 	*vpp = NULL;
437 
438 	if ((error = devvt_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL,
439 	    NULL)) != 0) {
440 		if (error == ENOENT)
441 			error = EROFS;
442 		return (error);
443 	}
444 
445 	if (excl == EXCL)
446 		error = EEXIST;
447 	else if (vp->v_type == VDIR && (mode & VWRITE))
448 		error = EISDIR;
449 	else
450 		error = VOP_ACCESS(vp, mode, 0, cred, ct);
451 
452 	if (error) {
453 		VN_RELE(vp);
454 	} else
455 		*vpp = vp;
456 
457 	return (error);
458 }
459 
460 const fs_operation_def_t devvt_vnodeops_tbl[] = {
461 	VOPNAME_READDIR,	{ .vop_readdir = devvt_readdir },
462 	VOPNAME_LOOKUP,		{ .vop_lookup = devvt_lookup },
463 	VOPNAME_CREATE,		{ .vop_create = devvt_create },
464 	VOPNAME_REMOVE,		{ .error = fs_nosys },
465 	VOPNAME_MKDIR,		{ .error = fs_nosys },
466 	VOPNAME_RMDIR,		{ .error = fs_nosys },
467 	VOPNAME_SYMLINK,	{ .error = fs_nosys },
468 	VOPNAME_SETSECATTR,	{ .error = fs_nosys },
469 	NULL,			NULL
470 };
471