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.
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.
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]
22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
26 * vnode ops for the /dev/vt directory
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>
38 #include <sys/vfs_opreg.h>
40 #include <sys/vt_impl.h>
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
))
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"
52 #define isdigit(ch) ((ch) >= '0' && (ch) <= '9')
54 /* attributes for VT nodes */
55 static vattr_t devvt_vattr
= {
56 AT_TYPE
|AT_MODE
|AT_UID
|AT_GID
, /* va_mask */
58 S_IFCHR
| DEVVT_DEVMODE_DEFAULT
, /* va_mode */
59 DEVVT_UID_DEFAULT
, /* va_uid */
60 DEVVT_GID_DEFAULT
, /* va_gid */
64 struct vnodeops
*devvt_vnodeops
;
67 devvt_getvnodeops(void)
69 return (devvt_vnodeops
);
73 devvt_str2minor(const char *nm
, minor_t
*mp
)
78 if (nm
== NULL
|| !isdigit(*nm
))
82 if (ddi_strtol(nm
, &endptr
, 10, &uminor
) != 0 ||
83 *endptr
!= '\0' || uminor
< 0) {
87 *mp
= (minor_t
)uminor
;
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
98 devvt_validate(struct sdev_node
*dv
)
101 char *nm
= dv
->sdev_name
;
104 ASSERT(dv
->sdev_state
== SDEV_READY
);
105 ASSERT(RW_LOCK_HELD(&(dv
->sdev_dotdot
)->sdev_contents
));
107 /* validate only READY nodes */
108 if (dv
->sdev_state
!= SDEV_READY
) {
109 sdcmn_err(("dev fs: skipping: node not ready %s(%p)",
111 return (SDEV_VTOR_SKIP
);
114 if (vt_wc_attached() == (major_t
)-1)
115 return (SDEV_VTOR_INVALID
);
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
);
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
);
135 if (devvt_str2minor(nm
, &min
) != 0) {
136 return (SDEV_VTOR_INVALID
);
139 if (vt_minor_valid(min
) == B_FALSE
)
140 return (SDEV_VTOR_INVALID
);
142 return (SDEV_VTOR_VALID
);
146 * This callback is invoked from devname_lookup_func() to create
147 * a entry when the node is not found in the cache.
151 devvt_create_rvp(struct sdev_node
*ddv
, char *nm
,
152 void **arg
, cred_t
*cred
, void *whatever
, char *whichever
)
156 struct vattr
*vap
= (struct vattr
*)arg
;
158 if ((maj
= vt_wc_attached()) == (major_t
)-1)
159 return (SDEV_VTOR_INVALID
);
161 if (strcmp(nm
, DEVVT_ACTIVE_NAME
) == 0) {
162 (void) vt_getactive((char *)*arg
, MAXPATHLEN
);
166 if (strcmp(nm
, DEVVT_CONSUSER_NAME
) == 0) {
167 (void) vt_getconsuser((char *)*arg
, MAXPATHLEN
);
170 if (devvt_str2minor(nm
, &min
) != 0)
173 if (vt_minor_valid(min
) == B_FALSE
)
177 vap
->va_rdev
= makedevice(maj
, min
);
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
)
188 struct sdev_node
*sdvp
= VTOSDEV(dvp
);
189 struct sdev_node
*dv
;
190 struct vnode
*rvp
= NULL
;
193 if ((strcmp(nm
, DEVVT_ACTIVE_NAME
) == 0) ||
194 (strcmp(nm
, DEVVT_CONSUSER_NAME
) == 0)) {
200 /* Give warlock a more clear call graph */
202 error
= devname_lookup_func(sdvp
, nm
, vpp
, cred
,
203 devvt_create_rvp
, type
);
205 devvt_create_rvp(0, 0, 0, 0, 0, 0);
209 switch ((*vpp
)->v_type
) {
211 dv
= VTOSDEV(VTOS(*vpp
)->s_realvp
);
212 ASSERT(VOP_REALVP(SDEVTOV(dv
), &rvp
, NULL
) == ENOSYS
);
219 cmn_err(CE_PANIC
, "devvt_lookup: Unsupported node "
220 "type: %p: %d", (void *)(*vpp
), (*vpp
)->v_type
);
223 ASSERT(SDEV_HELD(dv
));
230 devvt_create_snode(struct sdev_node
*ddv
, char *nm
, struct cred
*cred
, int type
)
233 struct sdev_node
*sdv
= NULL
;
235 struct vattr
*vap
= &vattr
;
239 ASSERT(RW_WRITE_HELD(&ddv
->sdev_contents
));
241 if ((maj
= vt_wc_attached()) == (major_t
)-1)
244 if (strcmp(nm
, DEVVT_ACTIVE_NAME
) != 0 &&
245 strcmp(nm
, DEVVT_CONSUSER_NAME
) != 0 &&
246 devvt_str2minor(nm
, &min
) != 0)
249 error
= sdev_mknode(ddv
, nm
, &sdv
, NULL
, NULL
, NULL
, cred
, SDEV_INIT
);
254 mutex_enter(&sdv
->sdev_lookup_lock
);
255 SDEV_BLOCK_OTHERS(sdv
, SDEV_LOOKUP
);
256 mutex_exit(&sdv
->sdev_lookup_lock
);
258 if (type
& SDEV_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
);
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
);
272 kmem_free(link
, MAXPATHLEN
);
280 mutex_enter(&sdv
->sdev_lookup_lock
);
281 SDEV_UNBLOCK_OTHERS(sdv
, SDEV_LOOKUP
);
282 mutex_exit(&sdv
->sdev_lookup_lock
);
287 devvt_rebuild_stale_link(struct sdev_node
*ddv
, struct sdev_node
*dv
)
291 ASSERT(RW_WRITE_HELD(&ddv
->sdev_contents
));
293 ASSERT((strcmp(dv
->sdev_name
, DEVVT_ACTIVE_NAME
) == 0) ||
294 (strcmp(dv
->sdev_name
, DEVVT_CONSUSER_NAME
) == 0));
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
);
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
);
308 kmem_free(link
, MAXPATHLEN
);
312 * First step in refreshing directory contents.
313 * Remove each invalid entry and rebuild the link
314 * reference for each stale entry.
317 devvt_prunedir(struct sdev_node
*ddv
)
320 struct sdev_node
*dv
, *next
= NULL
;
321 int (*vtor
)(struct sdev_node
*) = NULL
;
323 ASSERT(ddv
->sdev_flags
& SDEV_VTOR
);
325 vtor
= (int (*)(struct sdev_node
*))sdev_get_vtor(ddv
);
328 for (dv
= SDEV_FIRST_ENTRY(ddv
); dv
; dv
= next
) {
329 next
= SDEV_NEXT_ENTRY(ddv
, dv
);
332 case SDEV_VTOR_VALID
:
336 case SDEV_VTOR_INVALID
:
338 if (vp
->v_count
!= 0)
340 /* remove the cached node */
342 (void) sdev_cache_update(ddv
, &dv
,
343 dv
->sdev_name
, SDEV_CACHE_DELETE
);
346 case SDEV_VTOR_STALE
:
347 devvt_rebuild_stale_link(ddv
, dv
);
354 devvt_cleandir(struct vnode
*dvp
, struct cred
*cred
)
356 struct sdev_node
*sdvp
= VTOSDEV(dvp
);
357 struct sdev_node
*dv
, *next
= NULL
;
361 mutex_enter(&vc_lock
);
362 cnt
= VC_INSTANCES_COUNT
;
363 mutex_exit(&vc_lock
);
365 /* We have to fool warlock this way, otherwise it will complain */
367 if (rw_tryupgrade(&sdvp
->sdev_contents
) == NULL
) {
368 rw_exit(&sdvp
->sdev_contents
);
369 rw_enter(&sdvp
->sdev_contents
, RW_WRITER
);
372 rw_enter(&sdvp
->sdev_contents
, RW_WRITER
);
375 /* 1. prune invalid nodes and rebuild stale symlinks */
376 devvt_prunedir(sdvp
);
378 /* 2. create missing nodes */
379 for (min
= 0; min
< cnt
; min
++) {
382 if (vt_minor_valid(min
) == B_FALSE
)
385 (void) snprintf(nm
, sizeof (nm
), "%d", min
);
387 for (dv
= SDEV_FIRST_ENTRY(sdvp
); dv
; dv
= next
) {
388 next
= SDEV_NEXT_ENTRY(sdvp
, dv
);
390 /* validate only ready nodes */
391 if (dv
->sdev_state
!= SDEV_READY
)
393 if (strcmp(nm
, dv
->sdev_name
) == 0) {
399 devvt_create_snode(sdvp
, nm
, cred
, SDEV_VATTR
);
403 /* 3. create active link node and console user link node */
405 for (dv
= SDEV_FIRST_ENTRY(sdvp
); dv
; dv
= next
) {
406 next
= SDEV_NEXT_ENTRY(sdvp
, dv
);
408 /* validate only ready nodes */
409 if (dv
->sdev_state
!= SDEV_READY
)
411 if ((strcmp(dv
->sdev_name
, DEVVT_ACTIVE_NAME
) == NULL
))
413 if ((strcmp(dv
->sdev_name
, DEVVT_CONSUSER_NAME
) == NULL
))
416 if ((found
& 0x01) && (found
& 0x02))
420 devvt_create_snode(sdvp
, DEVVT_ACTIVE_NAME
, cred
, SDEV_VLINK
);
422 devvt_create_snode(sdvp
, DEVVT_CONSUSER_NAME
, cred
, SDEV_VLINK
);
425 rw_downgrade(&sdvp
->sdev_contents
);
427 rw_exit(&sdvp
->sdev_contents
);
433 devvt_readdir(struct vnode
*dvp
, struct uio
*uiop
, struct cred
*cred
,
434 int *eofp
, caller_context_t
*ct
, int flags
)
436 if (uiop
->uio_offset
== 0) {
437 devvt_cleandir(dvp
, cred
);
440 return (devname_readdir_func(dvp
, uiop
, cred
, eofp
, 0));
444 * We allow create to find existing nodes
445 * - if the node doesn't exist - EROFS
446 * - creating an existing dir read-only succeeds, otherwise EISDIR
447 * - exclusive creates fail - EEXIST
451 devvt_create(struct vnode
*dvp
, char *nm
, struct vattr
*vap
, vcexcl_t excl
,
452 int mode
, struct vnode
**vpp
, struct cred
*cred
, int flag
,
453 caller_context_t
*ct
, vsecattr_t
*vsecp
)
460 if ((error
= devvt_lookup(dvp
, nm
, &vp
, NULL
, 0, NULL
, cred
, ct
, NULL
,
469 else if (vp
->v_type
== VDIR
&& (mode
& VWRITE
))
472 error
= VOP_ACCESS(vp
, mode
, 0, cred
, ct
);
482 const fs_operation_def_t devvt_vnodeops_tbl
[] = {
483 VOPNAME_READDIR
, { .vop_readdir
= devvt_readdir
},
484 VOPNAME_LOOKUP
, { .vop_lookup
= devvt_lookup
},
485 VOPNAME_CREATE
, { .vop_create
= devvt_create
},
486 VOPNAME_REMOVE
, { .error
= fs_nosys
},
487 VOPNAME_MKDIR
, { .error
= fs_nosys
},
488 VOPNAME_RMDIR
, { .error
= fs_nosys
},
489 VOPNAME_SYMLINK
, { .error
= fs_nosys
},
490 VOPNAME_SETSECATTR
, { .error
= fs_nosys
},