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 /* 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)) 45 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" 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 strfree(dv->sdev_symlink); 117 dv->sdev_symlink = strdup(link); 118 dv->sdev_attr->va_size = strlen(link); 119 } 120 kmem_free(link, MAXPATHLEN); 121 return (SDEV_VTOR_VALID); 122 } 123 124 if (strcmp(nm, DEVVT_CONSUSER_NAME) == 0) { 125 char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 126 127 (void) vt_getconsuser(link, MAXPATHLEN); 128 if (strcmp(link, dv->sdev_symlink) != 0) { 129 strfree(dv->sdev_symlink); 130 dv->sdev_symlink = strdup(link); 131 dv->sdev_attr->va_size = strlen(link); 132 } 133 kmem_free(link, MAXPATHLEN); 134 return (SDEV_VTOR_VALID); 135 } 136 137 if (devvt_str2minor(nm, &min) != 0) { 138 return (SDEV_VTOR_INVALID); 139 } 140 141 if (vt_minor_valid(min) == B_FALSE) 142 return (SDEV_VTOR_INVALID); 143 144 return (SDEV_VTOR_VALID); 145 } 146 147 /* 148 * This callback is invoked from devname_lookup_func() to create 149 * a entry when the node is not found in the cache. 150 */ 151 /*ARGSUSED*/ 152 static int 153 devvt_create_rvp(struct sdev_node *ddv, char *nm, 154 void **arg, cred_t *cred, void *whatever, char *whichever) 155 { 156 minor_t min; 157 major_t maj; 158 struct vattr *vap = (struct vattr *)arg; 159 160 if ((maj = vt_wc_attached()) == (major_t)-1) 161 return (SDEV_VTOR_INVALID); 162 163 if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) { 164 (void) vt_getactive((char *)*arg, MAXPATHLEN); 165 return (0); 166 } 167 168 if (strcmp(nm, DEVVT_CONSUSER_NAME) == 0) { 169 (void) vt_getconsuser((char *)*arg, MAXPATHLEN); 170 return (0); 171 } 172 if (devvt_str2minor(nm, &min) != 0) 173 return (-1); 174 175 if (vt_minor_valid(min) == B_FALSE) 176 return (-1); 177 178 *vap = devvt_vattr; 179 vap->va_rdev = makedevice(maj, min); 180 181 return (0); 182 } 183 184 /*ARGSUSED3*/ 185 static int 186 devvt_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, 187 struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, 188 caller_context_t *ct, int *direntflags, pathname_t *realpnp) 189 { 190 struct sdev_node *sdvp = VTOSDEV(dvp); 191 struct sdev_node *dv; 192 struct vnode *rvp = NULL; 193 int type, error; 194 195 if ((strcmp(nm, DEVVT_ACTIVE_NAME) == 0) || 196 (strcmp(nm, DEVVT_CONSUSER_NAME) == 0)) { 197 type = SDEV_VLINK; 198 } else { 199 type = SDEV_VATTR; 200 } 201 202 /* Give warlock a more clear call graph */ 203 #ifndef __lock_lint 204 error = devname_lookup_func(sdvp, nm, vpp, cred, 205 devvt_create_rvp, type); 206 #else 207 devvt_create_rvp(0, 0, 0, 0, 0, 0); 208 #endif 209 210 if (error == 0) { 211 switch ((*vpp)->v_type) { 212 case VCHR: 213 dv = VTOSDEV(VTOS(*vpp)->s_realvp); 214 ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS); 215 break; 216 case VDIR: 217 case VLNK: 218 dv = VTOSDEV(*vpp); 219 break; 220 default: 221 cmn_err(CE_PANIC, "devvt_lookup: Unsupported node " 222 "type: %p: %d", (void *)(*vpp), (*vpp)->v_type); 223 break; 224 } 225 ASSERT(SDEV_HELD(dv)); 226 } 227 228 return (error); 229 } 230 231 static void 232 devvt_create_snode(struct sdev_node *ddv, char *nm, struct cred *cred, int type) 233 { 234 int error; 235 struct sdev_node *sdv = NULL; 236 struct vattr vattr; 237 struct vattr *vap = &vattr; 238 major_t maj; 239 minor_t min; 240 241 if ((maj = vt_wc_attached()) == (major_t)-1) 242 return; 243 244 if (strcmp(nm, DEVVT_ACTIVE_NAME) != 0 && 245 strcmp(nm, DEVVT_CONSUSER_NAME) != 0 && 246 devvt_str2minor(nm, &min) != 0) 247 return; 248 249 error = sdev_mknode(ddv, nm, &sdv, NULL, NULL, NULL, cred, SDEV_INIT); 250 if (error || !sdv) { 251 return; 252 } 253 254 mutex_enter(&sdv->sdev_lookup_lock); 255 SDEV_BLOCK_OTHERS(sdv, SDEV_LOOKUP); 256 mutex_exit(&sdv->sdev_lookup_lock); 257 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); 265 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); 271 272 kmem_free(link, MAXPATHLEN); 273 } 274 275 mutex_enter(&sdv->sdev_lookup_lock); 276 SDEV_UNBLOCK_OTHERS(sdv, SDEV_LOOKUP); 277 mutex_exit(&sdv->sdev_lookup_lock); 278 279 } 280 281 static void 282 devvt_prunedir(struct sdev_node *ddv) 283 { 284 struct vnode *vp; 285 struct sdev_node *dv, *next = NULL; 286 int (*vtor)(struct sdev_node *) = NULL; 287 288 ASSERT(ddv->sdev_flags & SDEV_VTOR); 289 290 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 291 ASSERT(vtor); 292 293 for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) { 294 next = SDEV_NEXT_ENTRY(ddv, dv); 295 296 /* skip stale nodes */ 297 if (dv->sdev_flags & SDEV_STALE) 298 continue; 299 300 /* validate and prune only ready nodes */ 301 if (dv->sdev_state != SDEV_READY) 302 continue; 303 304 switch (vtor(dv)) { 305 case SDEV_VTOR_VALID: 306 case SDEV_VTOR_SKIP: 307 continue; 308 case SDEV_VTOR_INVALID: 309 case SDEV_VTOR_STALE: 310 sdcmn_err7(("destroy invalid " 311 "node: %s(%p)\n", dv->sdev_name, (void *)dv)); 312 break; 313 } 314 vp = SDEVTOV(dv); 315 if (vp->v_count > 0) 316 continue; 317 SDEV_HOLD(dv); 318 /* remove the cache node */ 319 (void) sdev_cache_update(ddv, &dv, dv->sdev_name, 320 SDEV_CACHE_DELETE); 321 } 322 } 323 324 static void 325 devvt_cleandir(struct vnode *dvp, struct cred *cred) 326 { 327 struct sdev_node *sdvp = VTOSDEV(dvp); 328 struct sdev_node *dv, *next = NULL; 329 int min, cnt; 330 char found = 0; 331 332 mutex_enter(&vc_lock); 333 cnt = VC_INSTANCES_COUNT; 334 mutex_exit(&vc_lock); 335 336 /* We have to fool warlock this way, otherwise it will complain */ 337 #ifndef __lock_lint 338 if (rw_tryupgrade(&sdvp->sdev_contents) == NULL) { 339 rw_exit(&sdvp->sdev_contents); 340 rw_enter(&sdvp->sdev_contents, RW_WRITER); 341 } 342 #else 343 rw_enter(&sdvp->sdev_contents, RW_WRITER); 344 #endif 345 346 /* 1. create missed nodes */ 347 for (min = 0; min < cnt; min++) { 348 char nm[16]; 349 350 if (vt_minor_valid(min) == B_FALSE) 351 continue; 352 353 (void) snprintf(nm, sizeof (nm), "%d", min); 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(nm, dv->sdev_name) == 0) { 365 found = 1; 366 break; 367 } 368 } 369 if (!found) { 370 devvt_create_snode(sdvp, nm, cred, SDEV_VATTR); 371 } 372 } 373 374 /* 2. create active link node and console user link node */ 375 found = 0; 376 for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) { 377 next = SDEV_NEXT_ENTRY(sdvp, dv); 378 379 /* skip stale nodes */ 380 if (dv->sdev_flags & SDEV_STALE) 381 continue; 382 /* validate and prune only ready nodes */ 383 if (dv->sdev_state != SDEV_READY) 384 continue; 385 if ((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == NULL)) 386 found |= 0x01; 387 if ((strcmp(dv->sdev_name, DEVVT_CONSUSER_NAME) == NULL)) 388 found |= 0x02; 389 390 if ((found & 0x01) && (found & 0x02)) 391 break; 392 } 393 if (!(found & 0x01)) 394 devvt_create_snode(sdvp, DEVVT_ACTIVE_NAME, cred, SDEV_VLINK); 395 396 if (!(found & 0x02)) 397 devvt_create_snode(sdvp, DEVVT_CONSUSER_NAME, cred, SDEV_VLINK); 398 399 /* 3. cleanup invalid nodes */ 400 devvt_prunedir(sdvp); 401 402 #ifndef __lock_lint 403 rw_downgrade(&sdvp->sdev_contents); 404 #else 405 rw_exit(&sdvp->sdev_contents); 406 #endif 407 } 408 409 /*ARGSUSED4*/ 410 static int 411 devvt_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, 412 int *eofp, caller_context_t *ct, int flags) 413 { 414 if (uiop->uio_offset == 0) { 415 devvt_cleandir(dvp, cred); 416 } 417 418 return (devname_readdir_func(dvp, uiop, cred, eofp, 0)); 419 } 420 421 /* 422 * We allow create to find existing nodes 423 * - if the node doesn't exist - EROFS 424 * - creating an existing dir read-only succeeds, otherwise EISDIR 425 * - exclusive creates fail - EEXIST 426 */ 427 /*ARGSUSED2*/ 428 static int 429 devvt_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, 430 int mode, struct vnode **vpp, struct cred *cred, int flag, 431 caller_context_t *ct, vsecattr_t *vsecp) 432 { 433 int error; 434 struct vnode *vp; 435 436 *vpp = NULL; 437 438 if ((error = devvt_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, 439 NULL)) != 0) { 440 if (error == ENOENT) 441 error = EROFS; 442 return (error); 443 } 444 445 if (excl == EXCL) 446 error = EEXIST; 447 else if (vp->v_type == VDIR && (mode & VWRITE)) 448 error = EISDIR; 449 else 450 error = VOP_ACCESS(vp, mode, 0, cred, ct); 451 452 if (error) { 453 VN_RELE(vp); 454 } else 455 *vpp = vp; 456 457 return (error); 458 } 459 460 const fs_operation_def_t devvt_vnodeops_tbl[] = { 461 VOPNAME_READDIR, { .vop_readdir = devvt_readdir }, 462 VOPNAME_LOOKUP, { .vop_lookup = devvt_lookup }, 463 VOPNAME_CREATE, { .vop_create = devvt_create }, 464 VOPNAME_REMOVE, { .error = fs_nosys }, 465 VOPNAME_MKDIR, { .error = fs_nosys }, 466 VOPNAME_RMDIR, { .error = fs_nosys }, 467 VOPNAME_SYMLINK, { .error = fs_nosys }, 468 VOPNAME_SETSECATTR, { .error = fs_nosys }, 469 NULL, NULL 470 }; 471