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