xref: /illumos-gate/usr/src/uts/common/fs/dev/sdev_vtops.c (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
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 		case SDEV_VTOR_STALE:
289 			sdcmn_err7(("destroy invalid "
290 			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
291 			break;
292 		}
293 		vp = SDEVTOV(dv);
294 		if (vp->v_count > 0)
295 			continue;
296 		SDEV_HOLD(dv);
297 		/* remove the cache node */
298 		(void) sdev_cache_update(ddv, &dv, dv->sdev_name,
299 		    SDEV_CACHE_DELETE);
300 	}
301 }
302 
303 static void
304 devvt_cleandir(struct vnode *dvp, struct cred *cred)
305 {
306 	struct sdev_node *sdvp = VTOSDEV(dvp);
307 	struct sdev_node *dv, *next = NULL;
308 	int min, cnt;
309 	int found = 0;
310 
311 	mutex_enter(&vc_lock);
312 	cnt = VC_INSTANCES_COUNT;
313 	mutex_exit(&vc_lock);
314 
315 /* We have to fool warlock this way, otherwise it will complain */
316 #ifndef	__lock_lint
317 	if (rw_tryupgrade(&sdvp->sdev_contents) == NULL) {
318 		rw_exit(&sdvp->sdev_contents);
319 		rw_enter(&sdvp->sdev_contents, RW_WRITER);
320 	}
321 #else
322 	rw_enter(&sdvp->sdev_contents, RW_WRITER);
323 #endif
324 
325 	/* 1. create missed nodes */
326 	for (min = 0; min < cnt; min++) {
327 		char nm[16];
328 
329 		if (vt_minor_valid(min) == B_FALSE)
330 			continue;
331 
332 		(void) snprintf(nm, sizeof (nm), "%d", min);
333 		found = 0;
334 		for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) {
335 			next = SDEV_NEXT_ENTRY(sdvp, dv);
336 
337 			/* skip stale nodes */
338 			if (dv->sdev_flags & SDEV_STALE)
339 				continue;
340 			/* validate and prune only ready nodes */
341 			if (dv->sdev_state != SDEV_READY)
342 				continue;
343 			if (strcmp(nm, dv->sdev_name) == 0) {
344 				found = 1;
345 				break;
346 			}
347 		}
348 		if (!found) {
349 			devvt_create_snode(sdvp, nm, cred, SDEV_VATTR);
350 		}
351 	}
352 
353 	/* 2. create active link node */
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(dv->sdev_name, DEVVT_ACTIVE_NAME) == NULL)) {
365 			found = 1;
366 			break;
367 		}
368 	}
369 	if (!found)
370 		devvt_create_snode(sdvp, DEVVT_ACTIVE_NAME, cred, SDEV_VLINK);
371 
372 	/* 3. cleanup invalid nodes */
373 	devvt_prunedir(sdvp);
374 
375 #ifndef	__lock_lint
376 	rw_downgrade(&sdvp->sdev_contents);
377 #else
378 	rw_exit(&sdvp->sdev_contents);
379 #endif
380 }
381 
382 /*ARGSUSED4*/
383 static int
384 devvt_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
385     int *eofp, caller_context_t *ct, int flags)
386 {
387 	if (uiop->uio_offset == 0) {
388 		devvt_cleandir(dvp, cred);
389 	}
390 
391 	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
392 }
393 
394 /*
395  * We allow create to find existing nodes
396  *	- if the node doesn't exist - EROFS
397  *	- creating an existing dir read-only succeeds, otherwise EISDIR
398  *	- exclusive creates fail - EEXIST
399  */
400 /*ARGSUSED2*/
401 static int
402 devvt_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
403     int mode, struct vnode **vpp, struct cred *cred, int flag,
404     caller_context_t *ct, vsecattr_t *vsecp)
405 {
406 	int error;
407 	struct vnode *vp;
408 
409 	*vpp = NULL;
410 
411 	if ((error = devvt_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL,
412 	    NULL)) != 0) {
413 		if (error == ENOENT)
414 			error = EROFS;
415 		return (error);
416 	}
417 
418 	if (excl == EXCL)
419 		error = EEXIST;
420 	else if (vp->v_type == VDIR && (mode & VWRITE))
421 		error = EISDIR;
422 	else
423 		error = VOP_ACCESS(vp, mode, 0, cred, ct);
424 
425 	if (error) {
426 		VN_RELE(vp);
427 	} else
428 		*vpp = vp;
429 
430 	return (error);
431 }
432 
433 const fs_operation_def_t devvt_vnodeops_tbl[] = {
434 	VOPNAME_READDIR,	{ .vop_readdir = devvt_readdir },
435 	VOPNAME_LOOKUP,		{ .vop_lookup = devvt_lookup },
436 	VOPNAME_CREATE,		{ .vop_create = devvt_create },
437 	VOPNAME_REMOVE,		{ .error = fs_nosys },
438 	VOPNAME_MKDIR,		{ .error = fs_nosys },
439 	VOPNAME_RMDIR,		{ .error = fs_nosys },
440 	VOPNAME_SYMLINK,	{ .error = fs_nosys },
441 	VOPNAME_SETSECATTR,	{ .error = fs_nosys },
442 	NULL,			NULL
443 };
444