3978 renaming dynamic sdev nodes is problematic
[unleashed.git] / usr / src / uts / common / fs / dev / sdev_vtops.c
blobe29262d257346c5563cbd13cfc9f59ac902cd6c2
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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>
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>
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 */
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 */
64 struct vnodeops *devvt_vnodeops;
66 struct vnodeops *
67 devvt_getvnodeops(void)
69 return (devvt_vnodeops);
72 static int
73 devvt_str2minor(const char *nm, minor_t *mp)
75 long uminor = 0;
76 char *endptr = NULL;
78 if (nm == NULL || !isdigit(*nm))
79 return (EINVAL);
81 *mp = 0;
82 if (ddi_strtol(nm, &endptr, 10, &uminor) != 0 ||
83 *endptr != '\0' || uminor < 0) {
84 return (EINVAL);
87 *mp = (minor_t)uminor;
88 return (0);
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.
97 int
98 devvt_validate(struct sdev_node *dv)
100 minor_t min;
101 char *nm = dv->sdev_name;
102 int rval;
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)",
110 nm, (void *)dv));
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);
123 return (rval);
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);
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.
149 /*ARGSUSED*/
150 static int
151 devvt_create_rvp(struct sdev_node *ddv, char *nm,
152 void **arg, cred_t *cred, void *whatever, char *whichever)
154 minor_t min;
155 major_t maj;
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);
163 return (0);
166 if (strcmp(nm, DEVVT_CONSUSER_NAME) == 0) {
167 (void) vt_getconsuser((char *)*arg, MAXPATHLEN);
168 return (0);
170 if (devvt_str2minor(nm, &min) != 0)
171 return (-1);
173 if (vt_minor_valid(min) == B_FALSE)
174 return (-1);
176 *vap = devvt_vattr;
177 vap->va_rdev = makedevice(maj, min);
179 return (0);
182 /*ARGSUSED3*/
183 static int
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;
191 int type, error;
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;
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
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;
223 ASSERT(SDEV_HELD(dv));
226 return (error);
229 static void
230 devvt_create_snode(struct sdev_node *ddv, char *nm, struct cred *cred, int type)
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;
239 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
241 if ((maj = vt_wc_attached()) == (major_t)-1)
242 return;
244 if (strcmp(nm, DEVVT_ACTIVE_NAME) != 0 &&
245 strcmp(nm, DEVVT_CONSUSER_NAME) != 0 &&
246 devvt_str2minor(nm, &min) != 0)
247 return;
249 error = sdev_mknode(ddv, nm, &sdv, NULL, NULL, NULL, cred, SDEV_INIT);
250 if (error || !sdv) {
251 return;
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) {
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);
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);
275 if (error != 0) {
276 SDEV_RELE(sdv);
277 return;
280 mutex_enter(&sdv->sdev_lookup_lock);
281 SDEV_UNBLOCK_OTHERS(sdv, SDEV_LOOKUP);
282 mutex_exit(&sdv->sdev_lookup_lock);
286 static void
287 devvt_rebuild_stale_link(struct sdev_node *ddv, struct sdev_node *dv)
289 char *link;
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.
316 static void
317 devvt_prunedir(struct sdev_node *ddv)
319 struct vnode *vp;
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);
326 ASSERT(vtor);
328 for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
329 next = SDEV_NEXT_ENTRY(ddv, dv);
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;
353 static void
354 devvt_cleandir(struct vnode *dvp, struct cred *cred)
356 struct sdev_node *sdvp = VTOSDEV(dvp);
357 struct sdev_node *dv, *next = NULL;
358 int min, cnt;
359 char found = 0;
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 */
366 #ifndef __lock_lint
367 if (rw_tryupgrade(&sdvp->sdev_contents) == NULL) {
368 rw_exit(&sdvp->sdev_contents);
369 rw_enter(&sdvp->sdev_contents, RW_WRITER);
371 #else
372 rw_enter(&sdvp->sdev_contents, RW_WRITER);
373 #endif
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++) {
380 char nm[16];
382 if (vt_minor_valid(min) == B_FALSE)
383 continue;
385 (void) snprintf(nm, sizeof (nm), "%d", min);
386 found = 0;
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)
392 continue;
393 if (strcmp(nm, dv->sdev_name) == 0) {
394 found = 1;
395 break;
398 if (!found) {
399 devvt_create_snode(sdvp, nm, cred, SDEV_VATTR);
403 /* 3. create active link node and console user link node */
404 found = 0;
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)
410 continue;
411 if ((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == NULL))
412 found |= 0x01;
413 if ((strcmp(dv->sdev_name, DEVVT_CONSUSER_NAME) == NULL))
414 found |= 0x02;
416 if ((found & 0x01) && (found & 0x02))
417 break;
419 if (!(found & 0x01))
420 devvt_create_snode(sdvp, DEVVT_ACTIVE_NAME, cred, SDEV_VLINK);
421 if (!(found & 0x02))
422 devvt_create_snode(sdvp, DEVVT_CONSUSER_NAME, cred, SDEV_VLINK);
424 #ifndef __lock_lint
425 rw_downgrade(&sdvp->sdev_contents);
426 #else
427 rw_exit(&sdvp->sdev_contents);
428 #endif
431 /*ARGSUSED4*/
432 static int
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
449 /*ARGSUSED2*/
450 static int
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)
455 int error;
456 struct vnode *vp;
458 *vpp = NULL;
460 if ((error = devvt_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL,
461 NULL)) != 0) {
462 if (error == ENOENT)
463 error = EROFS;
464 return (error);
467 if (excl == EXCL)
468 error = EEXIST;
469 else if (vp->v_type == VDIR && (mode & VWRITE))
470 error = EISDIR;
471 else
472 error = VOP_ACCESS(vp, mode, 0, cred, ct);
474 if (error) {
475 VN_RELE(vp);
476 } else
477 *vpp = vp;
479 return (error);
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 },
491 NULL, NULL