sdev_vtops.c revision 11279:b69054bce66e
1252867Sdelphij/* 2252867Sdelphij * CDDL HEADER START 3252867Sdelphij * 4252867Sdelphij * The contents of this file are subject to the terms of the 5252867Sdelphij * Common Development and Distribution License (the "License"). 6252867Sdelphij * You may not use this file except in compliance with the License. 7252867Sdelphij * 8252867Sdelphij * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9252867Sdelphij * or http://www.opensolaris.org/os/licensing. 10252867Sdelphij * See the License for the specific language governing permissions 11252867Sdelphij * and limitations under the License. 12252867Sdelphij * 13252867Sdelphij * When distributing Covered Code, include this CDDL HEADER in each 14252867Sdelphij * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15252867Sdelphij * If applicable, add the following below this CDDL HEADER, with the 16252867Sdelphij * fields enclosed by brackets "[]" replaced with your own identifying 17252867Sdelphij * information: Portions Copyright [yyyy] [name of copyright owner] 18252867Sdelphij * 19252867Sdelphij * CDDL HEADER END 20252867Sdelphij */ 21252867Sdelphij/* 22252867Sdelphij * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23252867Sdelphij * Use is subject to license terms. 24252867Sdelphij */ 25252867Sdelphij 26252867Sdelphij/* 27252867Sdelphij * vnode ops for the /dev/vt directory 28252867Sdelphij */ 29252867Sdelphij 30252867Sdelphij#include <sys/types.h> 31252867Sdelphij#include <sys/param.h> 32252867Sdelphij#include <sys/sysmacros.h> 33252867Sdelphij#include <sys/sunndi.h> 34252867Sdelphij#include <fs/fs_subr.h> 35252867Sdelphij#include <sys/fs/dv_node.h> 36252867Sdelphij#include <sys/fs/sdev_impl.h> 37252867Sdelphij#include <sys/policy.h> 38252867Sdelphij#include <sys/stat.h> 39252867Sdelphij#include <sys/vfs_opreg.h> 40252867Sdelphij#include <sys/tty.h> 41252867Sdelphij#include <sys/vt_impl.h> 42252867Sdelphij#include <sys/note.h> 43252867Sdelphij 44252867Sdelphij/* warlock in this file only cares about variables shared by vt and devfs */ 45252867Sdelphij_NOTE(SCHEME_PROTECTS_DATA("Do not care", sdev_node vattr vnode)) 46252867Sdelphij 47252867Sdelphij#define DEVVT_UID_DEFAULT SDEV_UID_DEFAULT 48252867Sdelphij#define DEVVT_GID_DEFAULT (0) 49252867Sdelphij#define DEVVT_DEVMODE_DEFAULT (0600) 50252867Sdelphij#define DEVVT_ACTIVE_NAME "active" 51252867Sdelphij 52252867Sdelphij#define isdigit(ch) ((ch) >= '0' && (ch) <= '9') 53252867Sdelphij 54252867Sdelphij/* attributes for VT nodes */ 55252867Sdelphijstatic vattr_t devvt_vattr = { 56252867Sdelphij AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 57252867Sdelphij VCHR, /* va_type */ 58252867Sdelphij S_IFCHR | DEVVT_DEVMODE_DEFAULT, /* va_mode */ 59252867Sdelphij DEVVT_UID_DEFAULT, /* va_uid */ 60252867Sdelphij DEVVT_GID_DEFAULT, /* va_gid */ 61252867Sdelphij 0 /* 0 hereafter */ 62252867Sdelphij}; 63252867Sdelphij 64252867Sdelphijstruct vnodeops *devvt_vnodeops; 65252867Sdelphij 66252867Sdelphijstruct vnodeops * 67252867Sdelphijdevvt_getvnodeops(void) 68252867Sdelphij{ 69252867Sdelphij return (devvt_vnodeops); 70252867Sdelphij} 71252867Sdelphij 72252867Sdelphijstatic int 73252867Sdelphijdevvt_str2minor(const char *nm, minor_t *mp) 74252867Sdelphij{ 75252867Sdelphij long uminor = 0; 76252867Sdelphij char *endptr = NULL; 77252867Sdelphij 78252867Sdelphij if (nm == NULL || !isdigit(*nm)) 79252867Sdelphij return (EINVAL); 80252867Sdelphij 81252867Sdelphij *mp = 0; 82252867Sdelphij if (ddi_strtol(nm, &endptr, 10, &uminor) != 0 || 83252867Sdelphij *endptr != '\0' || uminor < 0) { 84252867Sdelphij return (EINVAL); 85252867Sdelphij } 86252867Sdelphij 87252867Sdelphij *mp = (minor_t)uminor; 88252867Sdelphij return (0); 89252867Sdelphij} 90252867Sdelphij 91252867Sdelphij/*ARGSUSED*/ 92252867Sdelphijint 93252867Sdelphijdevvt_validate(struct sdev_node *dv) 94252867Sdelphij{ 95252867Sdelphij minor_t min; 96252867Sdelphij char *nm = dv->sdev_name; 97252867Sdelphij 98252867Sdelphij ASSERT(!(dv->sdev_flags & SDEV_STALE)); 99252867Sdelphij ASSERT(dv->sdev_state == SDEV_READY); 100252867Sdelphij 101252867Sdelphij /* validate only READY nodes */ 102252867Sdelphij if (dv->sdev_state != SDEV_READY) { 103252867Sdelphij sdcmn_err(("dev fs: skipping: node not ready %s(%p)", 104252867Sdelphij nm, (void *)dv)); 105252867Sdelphij return (SDEV_VTOR_SKIP); 106252867Sdelphij } 107252867Sdelphij 108252867Sdelphij if (vt_wc_attached() == (major_t)-1) 109252867Sdelphij return (SDEV_VTOR_INVALID); 110252867Sdelphij 111252867Sdelphij if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) { 112252867Sdelphij char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 113252867Sdelphij 114252867Sdelphij (void) vt_getactive(link, MAXPATHLEN); 115252867Sdelphij if (strcmp(link, dv->sdev_symlink) != 0) { 116252867Sdelphij kmem_free(dv->sdev_symlink, 117252867Sdelphij strlen(dv->sdev_symlink) + 1); 118252867Sdelphij dv->sdev_symlink = i_ddi_strdup(link, KM_SLEEP); 119252867Sdelphij dv->sdev_attr->va_size = strlen(link); 120252867Sdelphij } 121252867Sdelphij kmem_free(link, MAXPATHLEN); 122252867Sdelphij return (SDEV_VTOR_VALID); 123252867Sdelphij } else if (devvt_str2minor(nm, &min) != 0) { 124252867Sdelphij return (SDEV_VTOR_INVALID); 125252867Sdelphij } 126252867Sdelphij 127252867Sdelphij if (vt_minor_valid(min) == B_FALSE) 128252867Sdelphij return (SDEV_VTOR_INVALID); 129252867Sdelphij 130252867Sdelphij return (SDEV_VTOR_VALID); 131252867Sdelphij} 132252867Sdelphij 133252867Sdelphij/* 134252867Sdelphij * This callback is invoked from devname_lookup_func() to create 135252867Sdelphij * a entry when the node is not found in the cache. 136252867Sdelphij */ 137252867Sdelphij/*ARGSUSED*/ 138252867Sdelphijstatic int 139252867Sdelphijdevvt_create_rvp(struct sdev_node *ddv, char *nm, 140252867Sdelphij void **arg, cred_t *cred, void *whatever, char *whichever) 141252867Sdelphij{ 142252867Sdelphij minor_t min; 143252867Sdelphij major_t maj; 144252867Sdelphij struct vattr *vap = (struct vattr *)arg; 145252867Sdelphij 146252867Sdelphij if ((maj = vt_wc_attached()) == (major_t)-1) 147252867Sdelphij return (SDEV_VTOR_INVALID); 148252867Sdelphij 149252867Sdelphij if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) { 150252867Sdelphij (void) vt_getactive((char *)*arg, MAXPATHLEN); 151252867Sdelphij return (0); 152252867Sdelphij } 153252867Sdelphij 154252867Sdelphij if (devvt_str2minor(nm, &min) != 0) 155252867Sdelphij return (-1); 156252867Sdelphij 157252867Sdelphij if (vt_minor_valid(min) == B_FALSE) 158252867Sdelphij return (-1); 159252867Sdelphij 160252867Sdelphij *vap = devvt_vattr; 161252867Sdelphij vap->va_rdev = makedevice(maj, min); 162252867Sdelphij 163252867Sdelphij return (0); 164252867Sdelphij} 165252867Sdelphij 166252867Sdelphij/*ARGSUSED3*/ 167252867Sdelphijstatic int 168252867Sdelphijdevvt_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, 169252867Sdelphij struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, 170252867Sdelphij caller_context_t *ct, int *direntflags, pathname_t *realpnp) 171252867Sdelphij{ 172252867Sdelphij struct sdev_node *sdvp = VTOSDEV(dvp); 173252867Sdelphij struct sdev_node *dv; 174252867Sdelphij struct vnode *rvp = NULL; 175252867Sdelphij int type, error; 176252867Sdelphij 177252867Sdelphij if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) { 178252867Sdelphij type = SDEV_VLINK; 179252867Sdelphij } else { 180252867Sdelphij type = SDEV_VATTR; 181252867Sdelphij } 182252867Sdelphij 183252867Sdelphij/* Give warlock a more clear call graph */ 184252867Sdelphij#ifndef __lock_lint 185252867Sdelphij error = devname_lookup_func(sdvp, nm, vpp, cred, 186252867Sdelphij devvt_create_rvp, type); 187252867Sdelphij#else 188252867Sdelphij devvt_create_rvp(0, 0, 0, 0, 0, 0); 189252867Sdelphij#endif 190252867Sdelphij 191252867Sdelphij if (error == 0) { 192252867Sdelphij switch ((*vpp)->v_type) { 193252867Sdelphij case VCHR: 194252867Sdelphij dv = VTOSDEV(VTOS(*vpp)->s_realvp); 195252867Sdelphij ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS); 196252867Sdelphij break; 197252867Sdelphij case VDIR: 198252867Sdelphij case VLNK: 199252867Sdelphij dv = VTOSDEV(*vpp); 200252867Sdelphij break; 201252867Sdelphij default: 202252867Sdelphij cmn_err(CE_PANIC, "devvt_lookup: Unsupported node " 203252867Sdelphij "type: %p: %d", (void *)(*vpp), (*vpp)->v_type); 204252867Sdelphij break; 205252867Sdelphij } 206252867Sdelphij ASSERT(SDEV_HELD(dv)); 207252867Sdelphij } 208252867Sdelphij 209252867Sdelphij return (error); 210252867Sdelphij} 211252867Sdelphij 212252867Sdelphijstatic void 213252867Sdelphijdevvt_create_snode(struct sdev_node *ddv, char *nm, struct cred *cred, int type) 214252867Sdelphij{ 215252867Sdelphij int error; 216252867Sdelphij struct sdev_node *sdv = NULL; 217252867Sdelphij struct vattr vattr; 218252867Sdelphij struct vattr *vap = &vattr; 219252867Sdelphij major_t maj; 220252867Sdelphij minor_t min; 221252867Sdelphij 222252867Sdelphij if ((maj = vt_wc_attached()) == (major_t)-1) 223252867Sdelphij return; 224252867Sdelphij 225252867Sdelphij if (strcmp(nm, DEVVT_ACTIVE_NAME) != 0 && 226252867Sdelphij devvt_str2minor(nm, &min) != 0) 227252867Sdelphij return; 228252867Sdelphij 229252867Sdelphij error = sdev_mknode(ddv, nm, &sdv, NULL, NULL, NULL, cred, SDEV_INIT); 230252867Sdelphij if (error || !sdv) { 231252867Sdelphij return; 232252867Sdelphij } 233252867Sdelphij 234252867Sdelphij mutex_enter(&sdv->sdev_lookup_lock); 235252867Sdelphij SDEV_BLOCK_OTHERS(sdv, SDEV_LOOKUP); 236252867Sdelphij mutex_exit(&sdv->sdev_lookup_lock); 237269613Sjhb 238269613Sjhb if (type & SDEV_VATTR) { 239269613Sjhb *vap = devvt_vattr; 240252867Sdelphij vap->va_rdev = makedevice(maj, min); 241252867Sdelphij error = sdev_mknode(ddv, nm, &sdv, vap, NULL, 242252867Sdelphij NULL, cred, SDEV_READY); 243252867Sdelphij } else if (type & SDEV_VLINK) { 244252867Sdelphij char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 245252867Sdelphij 246252867Sdelphij (void) vt_getactive(link, MAXPATHLEN); 247252867Sdelphij *vap = sdev_vattr_lnk; 248252867Sdelphij vap->va_size = strlen(link); 249252867Sdelphij error = sdev_mknode(ddv, nm, &sdv, vap, NULL, 250252867Sdelphij (void *)link, cred, SDEV_READY); 251252867Sdelphij 252252867Sdelphij kmem_free(link, MAXPATHLEN); 253252867Sdelphij } 254252867Sdelphij 255252867Sdelphij mutex_enter(&sdv->sdev_lookup_lock); 256252867Sdelphij SDEV_UNBLOCK_OTHERS(sdv, SDEV_LOOKUP); 257252867Sdelphij mutex_exit(&sdv->sdev_lookup_lock); 258252867Sdelphij 259252867Sdelphij} 260252867Sdelphij 261252867Sdelphijstatic void 262252867Sdelphijdevvt_prunedir(struct sdev_node *ddv) 263252867Sdelphij{ 264252867Sdelphij struct vnode *vp; 265252867Sdelphij struct sdev_node *dv, *next = NULL; 266252867Sdelphij int (*vtor)(struct sdev_node *) = NULL; 267252867Sdelphij 268252867Sdelphij ASSERT(ddv->sdev_flags & SDEV_VTOR); 269252867Sdelphij 270252867Sdelphij vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 271252867Sdelphij ASSERT(vtor); 272252867Sdelphij 273270384Sdelphij for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) { 274252867Sdelphij next = SDEV_NEXT_ENTRY(ddv, dv); 275252867Sdelphij 276252867Sdelphij /* skip stale nodes */ 277252867Sdelphij if (dv->sdev_flags & SDEV_STALE) 278252867Sdelphij continue; 279252867Sdelphij 280252867Sdelphij /* validate and prune only ready nodes */ 281252867Sdelphij if (dv->sdev_state != SDEV_READY) 282252867Sdelphij continue; 283252867Sdelphij 284252867Sdelphij switch (vtor(dv)) { 285252867Sdelphij case SDEV_VTOR_VALID: 286252867Sdelphij case SDEV_VTOR_SKIP: 287252867Sdelphij continue; 288252867Sdelphij case SDEV_VTOR_INVALID: 289252867Sdelphij case SDEV_VTOR_STALE: 290252867Sdelphij sdcmn_err7(("destroy invalid " 291252867Sdelphij "node: %s(%p)\n", dv->sdev_name, (void *)dv)); 292252867Sdelphij break; 293252867Sdelphij } 294252867Sdelphij vp = SDEVTOV(dv); 295252867Sdelphij if (vp->v_count > 0) 296252867Sdelphij continue; 297252867Sdelphij SDEV_HOLD(dv); 298252867Sdelphij /* remove the cache node */ 299252867Sdelphij (void) sdev_cache_update(ddv, &dv, dv->sdev_name, 300252867Sdelphij SDEV_CACHE_DELETE); 301252867Sdelphij } 302252867Sdelphij} 303 304static void 305devvt_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*/ 384static int 385devvt_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*/ 402static int 403devvt_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 434const 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