xref: /illumos-gate/usr/src/uts/common/fs/dev/sdev_vtops.c (revision 67ce1dada345581246cd990d73516418f321a793)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * vnode ops for the /dev/vt directory
28  */
29 
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/sysmacros.h>
33 #include <sys/sunndi.h>
34 #include <fs/fs_subr.h>
35 #include <sys/fs/dv_node.h>
36 #include <sys/fs/sdev_impl.h>
37 #include <sys/policy.h>
38 #include <sys/stat.h>
39 #include <sys/vfs_opreg.h>
40 #include <sys/tty.h>
41 #include <sys/vt_impl.h>
42 #include <sys/note.h>
43 
44 /* warlock in this file only cares about variables shared by vt and devfs */
45 _NOTE(SCHEME_PROTECTS_DATA("Do not care", sdev_node vattr vnode))
46 
47 #define	DEVVT_UID_DEFAULT	SDEV_UID_DEFAULT
48 #define	DEVVT_GID_DEFAULT	(0)
49 #define	DEVVT_DEVMODE_DEFAULT	(0600)
50 #define	DEVVT_ACTIVE_NAME	"active"
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 			kmem_free(dv->sdev_symlink,
117 			    strlen(dv->sdev_symlink) + 1);
118 			dv->sdev_symlink = i_ddi_strdup(link, KM_SLEEP);
119 			dv->sdev_attr->va_size = strlen(link);
120 		}
121 		kmem_free(link, MAXPATHLEN);
122 		return (SDEV_VTOR_VALID);
123 	} else if (devvt_str2minor(nm, &min) != 0) {
124 		return (SDEV_VTOR_INVALID);
125 	}
126 
127 	if (vt_minor_valid(min) == B_FALSE)
128 		return (SDEV_VTOR_INVALID);
129 
130 	return (SDEV_VTOR_VALID);
131 }
132 
133 /*
134  * This callback is invoked from devname_lookup_func() to create
135  * a entry when the node is not found in the cache.
136  */
137 /*ARGSUSED*/
138 static int
139 devvt_create_rvp(struct sdev_node *ddv, char *nm,
140     void **arg, cred_t *cred, void *whatever, char *whichever)
141 {
142 	minor_t min;
143 	major_t maj;
144 	struct vattr *vap = (struct vattr *)arg;
145 
146 	if ((maj = vt_wc_attached()) == (major_t)-1)
147 		return (SDEV_VTOR_INVALID);
148 
149 	if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) {
150 		(void) vt_getactive((char *)*arg, MAXPATHLEN);
151 		return (0);
152 	}
153 
154 	if (devvt_str2minor(nm, &min) != 0)
155 		return (-1);
156 
157 	if (vt_minor_valid(min) == B_FALSE)
158 		return (-1);
159 
160 	*vap = devvt_vattr;
161 	vap->va_rdev = makedevice(maj, min);
162 
163 	return (0);
164 }
165 
166 /*ARGSUSED3*/
167 static int
168 devvt_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
169     struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
170     caller_context_t *ct, int *direntflags, pathname_t *realpnp)
171 {
172 	struct sdev_node *sdvp = VTOSDEV(dvp);
173 	struct sdev_node *dv;
174 	struct vnode *rvp = NULL;
175 	int type, error;
176 
177 	if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) {
178 		type = SDEV_VLINK;
179 	} else {
180 		type = SDEV_VATTR;
181 	}
182 
183 /* Give warlock a more clear call graph */
184 #ifndef __lock_lint
185 	error = devname_lookup_func(sdvp, nm, vpp, cred,
186 	    devvt_create_rvp, type);
187 #else
188 	devvt_create_rvp(0, 0, 0, 0, 0, 0);
189 #endif
190 
191 	if (error == 0) {
192 		switch ((*vpp)->v_type) {
193 		case VCHR:
194 			dv = VTOSDEV(VTOS(*vpp)->s_realvp);
195 			ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS);
196 			break;
197 		case VDIR:
198 		case VLNK:
199 			dv = VTOSDEV(*vpp);
200 			break;
201 		default:
202 			cmn_err(CE_PANIC, "devvt_lookup: Unsupported node "
203 			    "type: %p: %d", (void *)(*vpp), (*vpp)->v_type);
204 			break;
205 		}
206 		ASSERT(SDEV_HELD(dv));
207 	}
208 
209 	return (error);
210 }
211 
212 static void
213 devvt_create_snode(struct sdev_node *ddv, char *nm, struct cred *cred, int type)
214 {
215 	int error;
216 	struct sdev_node *sdv = NULL;
217 	struct vattr *vap = NULL;
218 	major_t maj;
219 	minor_t min;
220 
221 	if ((maj = vt_wc_attached()) == (major_t)-1)
222 		return;
223 
224 	if (strcmp(nm, DEVVT_ACTIVE_NAME) != 0 &&
225 	    devvt_str2minor(nm, &min) != 0)
226 		return;
227 
228 	error = sdev_mknode(ddv, nm, &sdv, NULL, NULL, NULL, cred, SDEV_INIT);
229 	if (error || !sdv) {
230 		return;
231 	}
232 
233 	mutex_enter(&sdv->sdev_lookup_lock);
234 	SDEV_BLOCK_OTHERS(sdv, SDEV_LOOKUP);
235 	mutex_exit(&sdv->sdev_lookup_lock);
236 
237 	if (type & SDEV_VATTR) {
238 		vap = &devvt_vattr;
239 		vap->va_rdev = makedevice(maj, min);
240 		error = sdev_mknode(ddv, nm, &sdv, vap, NULL,
241 		    NULL, cred, SDEV_READY);
242 	} else if (type & SDEV_VLINK) {
243 		char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
244 
245 		(void) vt_getactive(link, MAXPATHLEN);
246 		vap = &sdev_vattr_lnk;
247 		vap->va_size = strlen(link);
248 		error = sdev_mknode(ddv, nm, &sdv, vap, NULL,
249 		    (void *)link, cred, SDEV_READY);
250 
251 		kmem_free(link, MAXPATHLEN);
252 	}
253 
254 	mutex_enter(&sdv->sdev_lookup_lock);
255 	SDEV_UNBLOCK_OTHERS(sdv, SDEV_LOOKUP);
256 	mutex_exit(&sdv->sdev_lookup_lock);
257 
258 }
259 
260 static void
261 devvt_prunedir(struct sdev_node *ddv)
262 {
263 	struct vnode *vp;
264 	struct sdev_node *dv, *next = NULL;
265 	int (*vtor)(struct sdev_node *) = NULL;
266 
267 	ASSERT(ddv->sdev_flags & SDEV_VTOR);
268 
269 	vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
270 	ASSERT(vtor);
271 
272 	for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
273 		next = SDEV_NEXT_ENTRY(ddv, dv);
274 
275 		/* skip stale nodes */
276 		if (dv->sdev_flags & SDEV_STALE)
277 			continue;
278 
279 		/* validate and prune only ready nodes */
280 		if (dv->sdev_state != SDEV_READY)
281 			continue;
282 
283 		switch (vtor(dv)) {
284 		case SDEV_VTOR_VALID:
285 		case SDEV_VTOR_SKIP:
286 			continue;
287 		case SDEV_VTOR_INVALID:
288 			sdcmn_err7(("destroy invalid "
289 			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
290 			break;
291 		}
292 		vp = SDEVTOV(dv);
293 		if (vp->v_count > 0)
294 			continue;
295 		SDEV_HOLD(dv);
296 		/* remove the cache node */
297 		(void) sdev_cache_update(ddv, &dv, dv->sdev_name,
298 		    SDEV_CACHE_DELETE);
299 	}
300 }
301 
302 static void
303 devvt_cleandir(struct vnode *dvp, struct cred *cred)
304 {
305 	struct sdev_node *sdvp = VTOSDEV(dvp);
306 	struct sdev_node *dv, *next = NULL;
307 	int min, cnt;
308 	int found = 0;
309 
310 	mutex_enter(&vc_lock);
311 	cnt = VC_INSTANCES_COUNT;
312 	mutex_exit(&vc_lock);
313 
314 /* We have to fool warlock this way, otherwise it will complain */
315 #ifndef	__lock_lint
316 	if (rw_tryupgrade(&sdvp->sdev_contents) == NULL) {
317 		rw_exit(&sdvp->sdev_contents);
318 		rw_enter(&sdvp->sdev_contents, RW_WRITER);
319 	}
320 #else
321 	rw_enter(&sdvp->sdev_contents, RW_WRITER);
322 #endif
323 
324 	/* 1. create missed nodes */
325 	for (min = 0; min < cnt; min++) {
326 		char nm[16];
327 
328 		if (vt_minor_valid(min) == B_FALSE)
329 			continue;
330 
331 		(void) snprintf(nm, sizeof (nm), "%d", min);
332 		found = 0;
333 		for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) {
334 			next = SDEV_NEXT_ENTRY(sdvp, dv);
335 
336 			/* skip stale nodes */
337 			if (dv->sdev_flags & SDEV_STALE)
338 				continue;
339 			/* validate and prune only ready nodes */
340 			if (dv->sdev_state != SDEV_READY)
341 				continue;
342 			if (strcmp(nm, dv->sdev_name) == 0) {
343 				found = 1;
344 				break;
345 			}
346 		}
347 		if (!found) {
348 			devvt_create_snode(sdvp, nm, cred, SDEV_VATTR);
349 		}
350 	}
351 
352 	/* 2. create active link node */
353 	found = 0;
354 	for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) {
355 		next = SDEV_NEXT_ENTRY(sdvp, dv);
356 
357 		/* skip stale nodes */
358 		if (dv->sdev_flags & SDEV_STALE)
359 			continue;
360 		/* validate and prune only ready nodes */
361 		if (dv->sdev_state != SDEV_READY)
362 			continue;
363 		if ((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == NULL)) {
364 			found = 1;
365 			break;
366 		}
367 	}
368 	if (!found)
369 		devvt_create_snode(sdvp, DEVVT_ACTIVE_NAME, cred, SDEV_VLINK);
370 
371 	/* 3. cleanup invalid nodes */
372 	devvt_prunedir(sdvp);
373 
374 #ifndef	__lock_lint
375 	rw_downgrade(&sdvp->sdev_contents);
376 #else
377 	rw_exit(&sdvp->sdev_contents);
378 #endif
379 }
380 
381 /*ARGSUSED4*/
382 static int
383 devvt_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
384     int *eofp, caller_context_t *ct, int flags)
385 {
386 	if (uiop->uio_offset == 0) {
387 		devvt_cleandir(dvp, cred);
388 	}
389 
390 	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
391 }
392 
393 /*
394  * We allow create to find existing nodes
395  *	- if the node doesn't exist - EROFS
396  *	- creating an existing dir read-only succeeds, otherwise EISDIR
397  *	- exclusive creates fail - EEXIST
398  */
399 /*ARGSUSED2*/
400 static int
401 devvt_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
402     int mode, struct vnode **vpp, struct cred *cred, int flag,
403     caller_context_t *ct, vsecattr_t *vsecp)
404 {
405 	int error;
406 	struct vnode *vp;
407 
408 	*vpp = NULL;
409 
410 	if ((error = devvt_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL,
411 	    NULL)) != 0) {
412 		if (error == ENOENT)
413 			error = EROFS;
414 		return (error);
415 	}
416 
417 	if (excl == EXCL)
418 		error = EEXIST;
419 	else if (vp->v_type == VDIR && (mode & VWRITE))
420 		error = EISDIR;
421 	else
422 		error = VOP_ACCESS(vp, mode, 0, cred, ct);
423 
424 	if (error) {
425 		VN_RELE(vp);
426 	} else
427 		*vpp = vp;
428 
429 	return (error);
430 }
431 
432 const fs_operation_def_t devvt_vnodeops_tbl[] = {
433 	VOPNAME_READDIR,	{ .vop_readdir = devvt_readdir },
434 	VOPNAME_LOOKUP,		{ .vop_lookup = devvt_lookup },
435 	VOPNAME_CREATE,		{ .vop_create = devvt_create },
436 	VOPNAME_REMOVE,		{ .error = fs_nosys },
437 	VOPNAME_MKDIR,		{ .error = fs_nosys },
438 	VOPNAME_RMDIR,		{ .error = fs_nosys },
439 	VOPNAME_SYMLINK,	{ .error = fs_nosys },
440 	VOPNAME_SETSECATTR,	{ .error = fs_nosys },
441 	NULL,			NULL
442 };
443