xref: /titanic_50/usr/src/uts/common/fs/dev/sdev_vtops.c (revision 9b5097ee22b7d249db813b466eda136ffc2c21fa)
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 2009 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 vattr;
218 	struct vattr *vap = &vattr;
219 	major_t maj;
220 	minor_t min;
221 
222 	if ((maj = vt_wc_attached()) == (major_t)-1)
223 		return;
224 
225 	if (strcmp(nm, DEVVT_ACTIVE_NAME) != 0 &&
226 	    devvt_str2minor(nm, &min) != 0)
227 		return;
228 
229 	error = sdev_mknode(ddv, nm, &sdv, NULL, NULL, NULL, cred, SDEV_INIT);
230 	if (error || !sdv) {
231 		return;
232 	}
233 
234 	mutex_enter(&sdv->sdev_lookup_lock);
235 	SDEV_BLOCK_OTHERS(sdv, SDEV_LOOKUP);
236 	mutex_exit(&sdv->sdev_lookup_lock);
237 
238 	if (type & SDEV_VATTR) {
239 		*vap = devvt_vattr;
240 		vap->va_rdev = makedevice(maj, min);
241 		error = sdev_mknode(ddv, nm, &sdv, vap, NULL,
242 		    NULL, cred, SDEV_READY);
243 	} else if (type & SDEV_VLINK) {
244 		char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
245 
246 		(void) vt_getactive(link, MAXPATHLEN);
247 		*vap = sdev_vattr_lnk;
248 		vap->va_size = strlen(link);
249 		error = sdev_mknode(ddv, nm, &sdv, vap, NULL,
250 		    (void *)link, cred, SDEV_READY);
251 
252 		kmem_free(link, MAXPATHLEN);
253 	}
254 
255 	mutex_enter(&sdv->sdev_lookup_lock);
256 	SDEV_UNBLOCK_OTHERS(sdv, SDEV_LOOKUP);
257 	mutex_exit(&sdv->sdev_lookup_lock);
258 
259 }
260 
261 static void
262 devvt_prunedir(struct sdev_node *ddv)
263 {
264 	struct vnode *vp;
265 	struct sdev_node *dv, *next = NULL;
266 	int (*vtor)(struct sdev_node *) = NULL;
267 
268 	ASSERT(ddv->sdev_flags & SDEV_VTOR);
269 
270 	vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv);
271 	ASSERT(vtor);
272 
273 	for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
274 		next = SDEV_NEXT_ENTRY(ddv, dv);
275 
276 		/* skip stale nodes */
277 		if (dv->sdev_flags & SDEV_STALE)
278 			continue;
279 
280 		/* validate and prune only ready nodes */
281 		if (dv->sdev_state != SDEV_READY)
282 			continue;
283 
284 		switch (vtor(dv)) {
285 		case SDEV_VTOR_VALID:
286 		case SDEV_VTOR_SKIP:
287 			continue;
288 		case SDEV_VTOR_INVALID:
289 		case SDEV_VTOR_STALE:
290 			sdcmn_err7(("destroy invalid "
291 			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
292 			break;
293 		}
294 		vp = SDEVTOV(dv);
295 		if (vp->v_count > 0)
296 			continue;
297 		SDEV_HOLD(dv);
298 		/* remove the cache node */
299 		(void) sdev_cache_update(ddv, &dv, dv->sdev_name,
300 		    SDEV_CACHE_DELETE);
301 	}
302 }
303 
304 static void
305 devvt_cleandir(struct vnode *dvp, struct cred *cred)
306 {
307 	struct sdev_node *sdvp = VTOSDEV(dvp);
308 	struct sdev_node *dv, *next = NULL;
309 	int min, cnt;
310 	int found = 0;
311 
312 	mutex_enter(&vc_lock);
313 	cnt = VC_INSTANCES_COUNT;
314 	mutex_exit(&vc_lock);
315 
316 /* We have to fool warlock this way, otherwise it will complain */
317 #ifndef	__lock_lint
318 	if (rw_tryupgrade(&sdvp->sdev_contents) == NULL) {
319 		rw_exit(&sdvp->sdev_contents);
320 		rw_enter(&sdvp->sdev_contents, RW_WRITER);
321 	}
322 #else
323 	rw_enter(&sdvp->sdev_contents, RW_WRITER);
324 #endif
325 
326 	/* 1. create missed nodes */
327 	for (min = 0; min < cnt; min++) {
328 		char nm[16];
329 
330 		if (vt_minor_valid(min) == B_FALSE)
331 			continue;
332 
333 		(void) snprintf(nm, sizeof (nm), "%d", min);
334 		found = 0;
335 		for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) {
336 			next = SDEV_NEXT_ENTRY(sdvp, dv);
337 
338 			/* skip stale nodes */
339 			if (dv->sdev_flags & SDEV_STALE)
340 				continue;
341 			/* validate and prune only ready nodes */
342 			if (dv->sdev_state != SDEV_READY)
343 				continue;
344 			if (strcmp(nm, dv->sdev_name) == 0) {
345 				found = 1;
346 				break;
347 			}
348 		}
349 		if (!found) {
350 			devvt_create_snode(sdvp, nm, cred, SDEV_VATTR);
351 		}
352 	}
353 
354 	/* 2. create active link node */
355 	found = 0;
356 	for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) {
357 		next = SDEV_NEXT_ENTRY(sdvp, dv);
358 
359 		/* skip stale nodes */
360 		if (dv->sdev_flags & SDEV_STALE)
361 			continue;
362 		/* validate and prune only ready nodes */
363 		if (dv->sdev_state != SDEV_READY)
364 			continue;
365 		if ((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == NULL)) {
366 			found = 1;
367 			break;
368 		}
369 	}
370 	if (!found)
371 		devvt_create_snode(sdvp, DEVVT_ACTIVE_NAME, cred, SDEV_VLINK);
372 
373 	/* 3. cleanup invalid nodes */
374 	devvt_prunedir(sdvp);
375 
376 #ifndef	__lock_lint
377 	rw_downgrade(&sdvp->sdev_contents);
378 #else
379 	rw_exit(&sdvp->sdev_contents);
380 #endif
381 }
382 
383 /*ARGSUSED4*/
384 static int
385 devvt_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
386     int *eofp, caller_context_t *ct, int flags)
387 {
388 	if (uiop->uio_offset == 0) {
389 		devvt_cleandir(dvp, cred);
390 	}
391 
392 	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
393 }
394 
395 /*
396  * We allow create to find existing nodes
397  *	- if the node doesn't exist - EROFS
398  *	- creating an existing dir read-only succeeds, otherwise EISDIR
399  *	- exclusive creates fail - EEXIST
400  */
401 /*ARGSUSED2*/
402 static int
403 devvt_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
404     int mode, struct vnode **vpp, struct cred *cred, int flag,
405     caller_context_t *ct, vsecattr_t *vsecp)
406 {
407 	int error;
408 	struct vnode *vp;
409 
410 	*vpp = NULL;
411 
412 	if ((error = devvt_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL,
413 	    NULL)) != 0) {
414 		if (error == ENOENT)
415 			error = EROFS;
416 		return (error);
417 	}
418 
419 	if (excl == EXCL)
420 		error = EEXIST;
421 	else if (vp->v_type == VDIR && (mode & VWRITE))
422 		error = EISDIR;
423 	else
424 		error = VOP_ACCESS(vp, mode, 0, cred, ct);
425 
426 	if (error) {
427 		VN_RELE(vp);
428 	} else
429 		*vpp = vp;
430 
431 	return (error);
432 }
433 
434 const fs_operation_def_t devvt_vnodeops_tbl[] = {
435 	VOPNAME_READDIR,	{ .vop_readdir = devvt_readdir },
436 	VOPNAME_LOOKUP,		{ .vop_lookup = devvt_lookup },
437 	VOPNAME_CREATE,		{ .vop_create = devvt_create },
438 	VOPNAME_REMOVE,		{ .error = fs_nosys },
439 	VOPNAME_MKDIR,		{ .error = fs_nosys },
440 	VOPNAME_RMDIR,		{ .error = fs_nosys },
441 	VOPNAME_SYMLINK,	{ .error = fs_nosys },
442 	VOPNAME_SETSECATTR,	{ .error = fs_nosys },
443 	NULL,			NULL
444 };
445