xref: /illumos-gate/usr/src/uts/common/fs/dev/sdev_vtops.c (revision 814e7298)
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 *
devvt_getvnodeops(void)67 devvt_getvnodeops(void)
68 {
69 	return (devvt_vnodeops);
70 }
71 
72 static int
devvt_str2minor(const char * nm,minor_t * mp)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
devvt_validate(struct sdev_node * dv)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_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*/
150 static int
devvt_create_rvp(struct sdev_node * ddv,char * nm,void ** arg,cred_t * cred,void * whatever,char * whichever)151 devvt_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*/
183 static int
devvt_lookup(struct vnode * dvp,char * nm,struct vnode ** vpp,struct pathname * pnp,int flags,struct vnode * rdir,struct cred * cred,caller_context_t * ct,int * direntflags,pathname_t * realpnp)184 devvt_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 
229 static void
devvt_create_snode(struct sdev_node * ddv,char * nm,struct cred * cred,int type)230 devvt_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 
286 static void
devvt_rebuild_stale_link(struct sdev_node * ddv,struct sdev_node * dv)287 devvt_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  */
316 static void
devvt_prunedir(struct sdev_node * ddv)317 devvt_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 
353 static void
devvt_cleandir(struct vnode * dvp,struct cred * cred)354 devvt_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*/
427 static int
devvt_readdir(struct vnode * dvp,struct uio * uiop,struct cred * cred,int * eofp,caller_context_t * ct,int flags)428 devvt_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*/
445 static int
devvt_create(struct vnode * dvp,char * nm,struct vattr * vap,vcexcl_t excl,int mode,struct vnode ** vpp,struct cred * cred,int flag,caller_context_t * ct,vsecattr_t * vsecp)446 devvt_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 
477 const 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