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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * vnode ops for the /dev/vt directory 27 */ 28 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> 42 43 #define DEVVT_UID_DEFAULT SDEV_UID_DEFAULT 44 #define DEVVT_GID_DEFAULT (0) 45 #define DEVVT_DEVMODE_DEFAULT (0600) 46 #define DEVVT_ACTIVE_NAME "active" 47 #define DEVVT_CONSUSER_NAME "console_user" 48 49 #define isdigit(ch) ((ch) >= '0' && (ch) <= '9') 50 51 /* attributes for VT nodes */ 52 static vattr_t devvt_vattr = { 53 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 54 VCHR, /* va_type */ 55 S_IFCHR | DEVVT_DEVMODE_DEFAULT, /* va_mode */ 56 DEVVT_UID_DEFAULT, /* va_uid */ 57 DEVVT_GID_DEFAULT, /* va_gid */ 58 0 /* 0 hereafter */ 59 }; 60 61 struct vnodeops *devvt_vnodeops; 62 63 struct vnodeops * 64 devvt_getvnodeops(void) 65 { 66 return (devvt_vnodeops); 67 } 68 69 static int 70 devvt_str2minor(const char *nm, minor_t *mp) 71 { 72 long uminor = 0; 73 char *endptr = NULL; 74 75 if (nm == NULL || !isdigit(*nm)) 76 return (EINVAL); 77 78 *mp = 0; 79 if (ddi_strtol(nm, &endptr, 10, &uminor) != 0 || 80 *endptr != '\0' || uminor < 0) { 81 return (EINVAL); 82 } 83 84 *mp = (minor_t)uminor; 85 return (0); 86 } 87 88 /* 89 * Validate that a node is up-to-date and correct. 90 * A validator may not update the node state or 91 * contents as a read lock permits entry by 92 * multiple threads. 93 */ 94 int 95 devvt_validate(struct sdev_node *dv) 96 { 97 minor_t min; 98 char *nm = dv->sdev_name; 99 int rval; 100 101 ASSERT(dv->sdev_state == SDEV_READY); 102 ASSERT(RW_LOCK_HELD(&(dv->sdev_dotdot)->sdev_contents)); 103 104 /* validate only READY nodes */ 105 if (dv->sdev_state != SDEV_READY) { 106 sdcmn_err(("dev fs: skipping: node not ready %s(%p)", 107 nm, (void *)dv)); 108 return (SDEV_VTOR_SKIP); 109 } 110 111 if (vt_wc_attached() == (major_t)-1) 112 return (SDEV_VTOR_INVALID); 113 114 if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) { 115 char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 116 (void) vt_getactive(link, MAXPATHLEN); 117 rval = (strcmp(link, dv->sdev_symlink) == 0) ? 118 SDEV_VTOR_VALID : SDEV_VTOR_STALE; 119 kmem_free(link, MAXPATHLEN); 120 return (rval); 121 } 122 123 if (strcmp(nm, DEVVT_CONSUSER_NAME) == 0) { 124 char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 125 (void) vt_getconsuser(link, MAXPATHLEN); 126 rval = (strcmp(link, dv->sdev_symlink) == 0) ? 127 SDEV_VTOR_VALID : SDEV_VTOR_STALE; 128 kmem_free(link, MAXPATHLEN); 129 return (rval); 130 } 131 132 if (devvt_str2minor(nm, &min) != 0) { 133 return (SDEV_VTOR_INVALID); 134 } 135 136 if (vt_minor_valid(min) == B_FALSE) 137 return (SDEV_VTOR_INVALID); 138 139 return (SDEV_VTOR_VALID); 140 } 141 142 /* 143 * This callback is invoked from devname_lookup_func() to create 144 * a entry when the node is not found in the cache. 145 */ 146 /*ARGSUSED*/ 147 static int 148 devvt_create_rvp(struct sdev_node *ddv, char *nm, 149 void **arg, cred_t *cred, void *whatever, char *whichever) 150 { 151 minor_t min; 152 major_t maj; 153 struct vattr *vap = (struct vattr *)arg; 154 155 if ((maj = vt_wc_attached()) == (major_t)-1) 156 return (SDEV_VTOR_INVALID); 157 158 if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) { 159 (void) vt_getactive((char *)*arg, MAXPATHLEN); 160 return (0); 161 } 162 163 if (strcmp(nm, DEVVT_CONSUSER_NAME) == 0) { 164 (void) vt_getconsuser((char *)*arg, MAXPATHLEN); 165 return (0); 166 } 167 if (devvt_str2minor(nm, &min) != 0) 168 return (-1); 169 170 if (vt_minor_valid(min) == B_FALSE) 171 return (-1); 172 173 *vap = devvt_vattr; 174 vap->va_rdev = makedevice(maj, min); 175 176 return (0); 177 } 178 179 /*ARGSUSED3*/ 180 static int 181 devvt_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, 182 struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, 183 caller_context_t *ct, int *direntflags, pathname_t *realpnp) 184 { 185 struct sdev_node *sdvp = VTOSDEV(dvp); 186 struct sdev_node *dv; 187 struct vnode *rvp = NULL; 188 int type, error; 189 190 if ((strcmp(nm, DEVVT_ACTIVE_NAME) == 0) || 191 (strcmp(nm, DEVVT_CONSUSER_NAME) == 0)) { 192 type = SDEV_VLINK; 193 } else { 194 type = SDEV_VATTR; 195 } 196 197 error = devname_lookup_func(sdvp, nm, vpp, cred, 198 devvt_create_rvp, type); 199 200 if (error == 0) { 201 switch ((*vpp)->v_type) { 202 case VCHR: 203 dv = VTOSDEV(VTOS(*vpp)->s_realvp); 204 ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS); 205 break; 206 case VDIR: 207 case VLNK: 208 dv = VTOSDEV(*vpp); 209 break; 210 default: 211 cmn_err(CE_PANIC, "devvt_lookup: Unsupported node " 212 "type: %p: %d", (void *)(*vpp), (*vpp)->v_type); 213 break; 214 } 215 ASSERT(SDEV_HELD(dv)); 216 } 217 218 return (error); 219 } 220 221 static void 222 devvt_create_snode(struct sdev_node *ddv, char *nm, struct cred *cred, int type) 223 { 224 int error; 225 struct sdev_node *sdv = NULL; 226 struct vattr vattr; 227 struct vattr *vap = &vattr; 228 major_t maj; 229 minor_t min; 230 231 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 232 233 if ((maj = vt_wc_attached()) == (major_t)-1) 234 return; 235 236 if (strcmp(nm, DEVVT_ACTIVE_NAME) != 0 && 237 strcmp(nm, DEVVT_CONSUSER_NAME) != 0 && 238 devvt_str2minor(nm, &min) != 0) 239 return; 240 241 error = sdev_mknode(ddv, nm, &sdv, NULL, NULL, NULL, cred, SDEV_INIT); 242 if (error || !sdv) { 243 return; 244 } 245 246 mutex_enter(&sdv->sdev_lookup_lock); 247 SDEV_BLOCK_OTHERS(sdv, SDEV_LOOKUP); 248 mutex_exit(&sdv->sdev_lookup_lock); 249 250 if (type & SDEV_VATTR) { 251 *vap = devvt_vattr; 252 vap->va_rdev = makedevice(maj, min); 253 error = sdev_mknode(ddv, nm, &sdv, vap, NULL, 254 NULL, cred, SDEV_READY); 255 } else if (type & SDEV_VLINK) { 256 char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 257 258 (void) vt_getactive(link, MAXPATHLEN); 259 *vap = sdev_vattr_lnk; 260 vap->va_size = strlen(link); 261 error = sdev_mknode(ddv, nm, &sdv, vap, NULL, 262 (void *)link, cred, SDEV_READY); 263 264 kmem_free(link, MAXPATHLEN); 265 } 266 267 if (error != 0) { 268 SDEV_RELE(sdv); 269 return; 270 } 271 272 mutex_enter(&sdv->sdev_lookup_lock); 273 SDEV_UNBLOCK_OTHERS(sdv, SDEV_LOOKUP); 274 mutex_exit(&sdv->sdev_lookup_lock); 275 276 } 277 278 static void 279 devvt_rebuild_stale_link(struct sdev_node *ddv, struct sdev_node *dv) 280 { 281 char *link; 282 283 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 284 285 ASSERT((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == 0) || 286 (strcmp(dv->sdev_name, DEVVT_CONSUSER_NAME) == 0)); 287 288 link = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 289 if (strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == 0) { 290 (void) vt_getactive(link, MAXPATHLEN); 291 } else if (strcmp(dv->sdev_name, DEVVT_CONSUSER_NAME) == 0) { 292 (void) vt_getconsuser(link, MAXPATHLEN); 293 } 294 295 if (strcmp(link, dv->sdev_symlink) != 0) { 296 strfree(dv->sdev_symlink); 297 dv->sdev_symlink = strdup(link); 298 dv->sdev_attr->va_size = strlen(link); 299 } 300 kmem_free(link, MAXPATHLEN); 301 } 302 303 /* 304 * First step in refreshing directory contents. 305 * Remove each invalid entry and rebuild the link 306 * reference for each stale entry. 307 */ 308 static void 309 devvt_prunedir(struct sdev_node *ddv) 310 { 311 struct vnode *vp; 312 struct sdev_node *dv, *next = NULL; 313 int (*vtor)(struct sdev_node *) = NULL; 314 315 ASSERT(ddv->sdev_flags & SDEV_VTOR); 316 317 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 318 ASSERT(vtor); 319 320 for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) { 321 next = SDEV_NEXT_ENTRY(ddv, dv); 322 323 switch (vtor(dv)) { 324 case SDEV_VTOR_VALID: 325 break; 326 case SDEV_VTOR_SKIP: 327 break; 328 case SDEV_VTOR_INVALID: 329 vp = SDEVTOV(dv); 330 if (vp->v_count != 0) 331 break; 332 /* remove the cached node */ 333 SDEV_HOLD(dv); 334 (void) sdev_cache_update(ddv, &dv, 335 dv->sdev_name, SDEV_CACHE_DELETE); 336 SDEV_RELE(dv); 337 break; 338 case SDEV_VTOR_STALE: 339 devvt_rebuild_stale_link(ddv, dv); 340 break; 341 } 342 } 343 } 344 345 static void 346 devvt_cleandir(struct vnode *dvp, struct cred *cred) 347 { 348 struct sdev_node *sdvp = VTOSDEV(dvp); 349 struct sdev_node *dv, *next = NULL; 350 int min, cnt; 351 char found = 0; 352 353 mutex_enter(&vc_lock); 354 cnt = VC_INSTANCES_COUNT; 355 mutex_exit(&vc_lock); 356 357 if (rw_tryupgrade(&sdvp->sdev_contents) == NULL) { 358 rw_exit(&sdvp->sdev_contents); 359 rw_enter(&sdvp->sdev_contents, RW_WRITER); 360 } 361 362 /* 1. prune invalid nodes and rebuild stale symlinks */ 363 devvt_prunedir(sdvp); 364 365 /* 2. create missing nodes */ 366 for (min = 0; min < cnt; min++) { 367 char nm[16]; 368 369 if (vt_minor_valid(min) == B_FALSE) 370 continue; 371 372 (void) snprintf(nm, sizeof (nm), "%d", min); 373 found = 0; 374 for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) { 375 next = SDEV_NEXT_ENTRY(sdvp, dv); 376 377 /* validate only ready nodes */ 378 if (dv->sdev_state != SDEV_READY) 379 continue; 380 if (strcmp(nm, dv->sdev_name) == 0) { 381 found = 1; 382 break; 383 } 384 } 385 if (!found) { 386 devvt_create_snode(sdvp, nm, cred, SDEV_VATTR); 387 } 388 } 389 390 /* 3. create active link node and console user link node */ 391 found = 0; 392 for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) { 393 next = SDEV_NEXT_ENTRY(sdvp, dv); 394 395 /* validate only ready nodes */ 396 if (dv->sdev_state != SDEV_READY) 397 continue; 398 if ((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == NULL)) 399 found |= 0x01; 400 if ((strcmp(dv->sdev_name, DEVVT_CONSUSER_NAME) == NULL)) 401 found |= 0x02; 402 403 if ((found & 0x01) && (found & 0x02)) 404 break; 405 } 406 if (!(found & 0x01)) 407 devvt_create_snode(sdvp, DEVVT_ACTIVE_NAME, cred, SDEV_VLINK); 408 if (!(found & 0x02)) 409 devvt_create_snode(sdvp, DEVVT_CONSUSER_NAME, cred, SDEV_VLINK); 410 411 rw_downgrade(&sdvp->sdev_contents); 412 } 413 414 /*ARGSUSED4*/ 415 static int 416 devvt_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, 417 int *eofp, caller_context_t *ct, int flags) 418 { 419 if (uiop->uio_offset == 0) { 420 devvt_cleandir(dvp, cred); 421 } 422 423 return (devname_readdir_func(dvp, uiop, cred, eofp, 0)); 424 } 425 426 /* 427 * We allow create to find existing nodes 428 * - if the node doesn't exist - EROFS 429 * - creating an existing dir read-only succeeds, otherwise EISDIR 430 * - exclusive creates fail - EEXIST 431 */ 432 /*ARGSUSED2*/ 433 static int 434 devvt_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, 435 int mode, struct vnode **vpp, struct cred *cred, int flag, 436 caller_context_t *ct, vsecattr_t *vsecp) 437 { 438 int error; 439 struct vnode *vp; 440 441 *vpp = NULL; 442 443 if ((error = devvt_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, 444 NULL)) != 0) { 445 if (error == ENOENT) 446 error = EROFS; 447 return (error); 448 } 449 450 if (excl == EXCL) 451 error = EEXIST; 452 else if (vp->v_type == VDIR && (mode & VWRITE)) 453 error = EISDIR; 454 else 455 error = VOP_ACCESS(vp, mode, 0, cred, ct); 456 457 if (error) { 458 VN_RELE(vp); 459 } else 460 *vpp = vp; 461 462 return (error); 463 } 464 465 const fs_operation_def_t devvt_vnodeops_tbl[] = { 466 VOPNAME_READDIR, { .vop_readdir = devvt_readdir }, 467 VOPNAME_LOOKUP, { .vop_lookup = devvt_lookup }, 468 VOPNAME_CREATE, { .vop_create = devvt_create }, 469 VOPNAME_REMOVE, { .error = fs_nosys }, 470 VOPNAME_MKDIR, { .error = fs_nosys }, 471 VOPNAME_RMDIR, { .error = fs_nosys }, 472 VOPNAME_SYMLINK, { .error = fs_nosys }, 473 VOPNAME_SETSECATTR, { .error = fs_nosys }, 474 NULL, NULL 475 };