175295Sdes/*- 2230132Suqs * Copyright (c) 2001 Dag-Erling Co��dan Sm��rgrav 375295Sdes * All rights reserved. 475295Sdes * 575295Sdes * Redistribution and use in source and binary forms, with or without 675295Sdes * modification, are permitted provided that the following conditions 775295Sdes * are met: 875295Sdes * 1. Redistributions of source code must retain the above copyright 975295Sdes * notice, this list of conditions and the following disclaimer 1075295Sdes * in this position and unchanged. 1175295Sdes * 2. Redistributions in binary form must reproduce the above copyright 1275295Sdes * notice, this list of conditions and the following disclaimer in the 1375295Sdes * documentation and/or other materials provided with the distribution. 1475295Sdes * 3. The name of the author may not be used to endorse or promote products 1575295Sdes * derived from this software without specific prior written permission. 1675295Sdes * 1775295Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1875295Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1975295Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2075295Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2175295Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2275295Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2375295Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2475295Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2575295Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2675295Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2775295Sdes */ 2875295Sdes 29143592Sdes#include <sys/cdefs.h> 30143592Sdes__FBSDID("$FreeBSD$"); 31143592Sdes 32143592Sdes#include "opt_pseudofs.h" 33143592Sdes 3475295Sdes#include <sys/param.h> 3575295Sdes#include <sys/kernel.h> 3675295Sdes#include <sys/systm.h> 3775295Sdes#include <sys/ctype.h> 3875295Sdes#include <sys/dirent.h> 3984187Sdes#include <sys/fcntl.h> 40120665Snectar#include <sys/limits.h> 4178073Sdes#include <sys/lock.h> 42143596Sdes#include <sys/malloc.h> 4375295Sdes#include <sys/mount.h> 4477967Sdes#include <sys/mutex.h> 4575295Sdes#include <sys/namei.h> 4675295Sdes#include <sys/proc.h> 4775295Sdes#include <sys/sbuf.h> 4875295Sdes#include <sys/sx.h> 4975295Sdes#include <sys/sysctl.h> 5075295Sdes#include <sys/vnode.h> 5175295Sdes 5275295Sdes#include <fs/pseudofs/pseudofs.h> 5375295Sdes#include <fs/pseudofs/pseudofs_internal.h> 5475295Sdes 55190806Sdes#define KASSERT_PN_IS_DIR(pn) \ 56190806Sdes KASSERT((pn)->pn_type == pfstype_root || \ 57190806Sdes (pn)->pn_type == pfstype_dir || \ 58190806Sdes (pn)->pn_type == pfstype_procdir, \ 59190806Sdes ("%s(): VDIR vnode refers to non-directory pfs_node", __func__)) 60190806Sdes 61190806Sdes#define KASSERT_PN_IS_FILE(pn) \ 62190806Sdes KASSERT((pn)->pn_type == pfstype_file, \ 63190806Sdes ("%s(): VREG vnode refers to non-file pfs_node", __func__)) 64190806Sdes 65190806Sdes#define KASSERT_PN_IS_LINK(pn) \ 66190806Sdes KASSERT((pn)->pn_type == pfstype_symlink, \ 67190806Sdes ("%s(): VLNK vnode refers to non-link pfs_node", __func__)) 68190806Sdes 6975295Sdes/* 70168764Sdes * Returns the fileno, adjusted for target pid 71168720Sdes */ 72168720Sdesstatic uint32_t 73168764Sdespn_fileno(struct pfs_node *pn, pid_t pid) 74168720Sdes{ 75168764Sdes 76168764Sdes KASSERT(pn->pn_fileno > 0, 77168764Sdes ("%s(): no fileno allocated", __func__)); 78168720Sdes if (pid != NO_PID) 79168720Sdes return (pn->pn_fileno * NO_PID + pid); 80168720Sdes return (pn->pn_fileno); 81168720Sdes} 82168720Sdes 83168720Sdes/* 84168764Sdes * Returns non-zero if given file is visible to given thread. 8584246Sdes */ 8684246Sdesstatic int 87168764Sdespfs_visible_proc(struct thread *td, struct pfs_node *pn, struct proc *proc) 88168764Sdes{ 89168764Sdes int visible; 90168764Sdes 91168764Sdes if (proc == NULL) 92168764Sdes return (0); 93168764Sdes 94168764Sdes PROC_LOCK_ASSERT(proc, MA_OWNED); 95168764Sdes 96168764Sdes visible = ((proc->p_flag & P_WEXIT) == 0); 97168764Sdes if (visible) 98168764Sdes visible = (p_cansee(td, proc) == 0); 99168764Sdes if (visible && pn->pn_vis != NULL) 100168764Sdes visible = pn_vis(td, proc, pn); 101168764Sdes if (!visible) 102168764Sdes return (0); 103168764Sdes return (1); 104168764Sdes} 105168764Sdes 106168764Sdesstatic int 107297267Skibpfs_visible(struct thread *td, struct pfs_node *pn, pid_t pid, 108297267Skib bool allproc_locked, struct proc **p) 10984246Sdes{ 11084246Sdes struct proc *proc; 11197940Sdes 11284246Sdes PFS_TRACE(("%s (pid: %d, req: %d)", 11384246Sdes pn->pn_name, pid, td->td_proc->p_pid)); 11484386Sdes 115168764Sdes if (p) 116168764Sdes *p = NULL; 117168764Sdes if (pid == NO_PID) 118168764Sdes PFS_RETURN (1); 119297267Skib proc = allproc_locked ? pfind_locked(pid) : pfind(pid); 120297267Skib if (proc == NULL) 12184386Sdes PFS_RETURN (0); 122168764Sdes if (pfs_visible_proc(td, pn, proc)) { 123168764Sdes if (p) 124155920Sjhb *p = proc; 125168764Sdes else 126155920Sjhb PROC_UNLOCK(proc); 127168764Sdes PFS_RETURN (1); 128168764Sdes } 129168764Sdes PROC_UNLOCK(proc); 130168764Sdes PFS_RETURN (0); 13184246Sdes} 13284246Sdes 13384246Sdes/* 13475295Sdes * Verify permissions 13575295Sdes */ 13675295Sdesstatic int 13775295Sdespfs_access(struct vop_access_args *va) 13875295Sdes{ 13975295Sdes struct vnode *vn = va->a_vp; 140168764Sdes struct pfs_vdata *pvd = vn->v_data; 14175295Sdes struct vattr vattr; 14275295Sdes int error; 14397940Sdes 144168764Sdes PFS_TRACE(("%s", pvd->pvd_pn->pn_name)); 145168768Sdes (void)pvd; 14697940Sdes 147182371Sattilio error = VOP_GETATTR(vn, &vattr, va->a_cred); 14875295Sdes if (error) 14984246Sdes PFS_RETURN (error); 15075295Sdes error = vaccess(vn->v_type, vattr.va_mode, vattr.va_uid, 151184413Strasz vattr.va_gid, va->a_accmode, va->a_cred, NULL); 15284246Sdes PFS_RETURN (error); 15375295Sdes} 15475295Sdes 15575295Sdes/* 15675295Sdes * Close a file or directory 15775295Sdes */ 15875295Sdesstatic int 15975295Sdespfs_close(struct vop_close_args *va) 16075295Sdes{ 16186969Sdes struct vnode *vn = va->a_vp; 162168764Sdes struct pfs_vdata *pvd = vn->v_data; 16386969Sdes struct pfs_node *pn = pvd->pvd_pn; 16486969Sdes struct proc *proc; 16586969Sdes int error; 16686969Sdes 167168764Sdes PFS_TRACE(("%s", pn->pn_name)); 168168764Sdes pfs_assert_not_owned(pn); 16986969Sdes 17086969Sdes /* 17186969Sdes * Do nothing unless this is the last close and the node has a 17286969Sdes * last-close handler. 17386969Sdes */ 174103936Sjeff if (vrefcnt(vn) > 1 || pn->pn_close == NULL) 17586969Sdes PFS_RETURN (0); 17686969Sdes 177168764Sdes if (pvd->pvd_pid != NO_PID) { 17886969Sdes proc = pfind(pvd->pvd_pid); 179168764Sdes } else { 18086969Sdes proc = NULL; 181168764Sdes } 18297940Sdes 183168764Sdes error = pn_close(va->a_td, proc, pn); 18497940Sdes 18586969Sdes if (proc != NULL) 18686969Sdes PROC_UNLOCK(proc); 18797940Sdes 18886969Sdes PFS_RETURN (error); 18975295Sdes} 19075295Sdes 19175295Sdes/* 19275295Sdes * Get file attributes 19375295Sdes */ 19475295Sdesstatic int 19575295Sdespfs_getattr(struct vop_getattr_args *va) 19675295Sdes{ 19775295Sdes struct vnode *vn = va->a_vp; 198168764Sdes struct pfs_vdata *pvd = vn->v_data; 19977998Sdes struct pfs_node *pn = pvd->pvd_pn; 20075295Sdes struct vattr *vap = va->a_vap; 20184098Sdes struct proc *proc; 20284098Sdes int error = 0; 20375295Sdes 204168764Sdes PFS_TRACE(("%s", pn->pn_name)); 205168764Sdes pfs_assert_not_owned(pn); 20697940Sdes 207297267Skib if (!pfs_visible(curthread, pn, pvd->pvd_pid, false, &proc)) 208119122Sdes PFS_RETURN (ENOENT); 209119122Sdes 21075295Sdes vap->va_type = vn->v_type; 211168764Sdes vap->va_fileid = pn_fileno(pn, pvd->pvd_pid); 21275295Sdes vap->va_flags = 0; 21375295Sdes vap->va_blocksize = PAGE_SIZE; 21475295Sdes vap->va_bytes = vap->va_size = 0; 215183215Skib vap->va_filerev = 0; 21675295Sdes vap->va_fsid = vn->v_mount->mnt_stat.f_fsid.val[0]; 21775295Sdes vap->va_nlink = 1; 21875295Sdes nanotime(&vap->va_ctime); 21975295Sdes vap->va_atime = vap->va_mtime = vap->va_ctime; 22075295Sdes 22184098Sdes switch (pn->pn_type) { 22284098Sdes case pfstype_procdir: 22384098Sdes case pfstype_root: 22484098Sdes case pfstype_dir: 225168764Sdes#if 0 226168764Sdes pfs_lock(pn); 227168764Sdes /* compute link count */ 228168764Sdes pfs_unlock(pn); 229168764Sdes#endif 23084098Sdes vap->va_mode = 0555; 23184098Sdes break; 23284098Sdes case pfstype_file: 23384187Sdes case pfstype_symlink: 23484098Sdes vap->va_mode = 0444; 23584098Sdes break; 23684098Sdes default: 23784098Sdes printf("shouldn't be here!\n"); 23884098Sdes vap->va_mode = 0; 23984098Sdes break; 24084098Sdes } 24184098Sdes 242155920Sjhb if (proc != NULL) { 24384098Sdes vap->va_uid = proc->p_ucred->cr_ruid; 24484098Sdes vap->va_gid = proc->p_ucred->cr_rgid; 24584098Sdes } else { 24684098Sdes vap->va_uid = 0; 24784098Sdes vap->va_gid = 0; 24884098Sdes } 24997940Sdes 250188677Sdes if (pn->pn_attr != NULL) 251188677Sdes error = pn_attr(curthread, proc, pn, vap); 252188677Sdes 253188677Sdes if(proc != NULL) 254188677Sdes PROC_UNLOCK(proc); 255188677Sdes 25684098Sdes PFS_RETURN (error); 25775295Sdes} 25875295Sdes 25975295Sdes/* 26085561Sdes * Perform an ioctl 26185561Sdes */ 26285561Sdesstatic int 26385561Sdespfs_ioctl(struct vop_ioctl_args *va) 26485561Sdes{ 265193919Skib struct vnode *vn; 266193919Skib struct pfs_vdata *pvd; 267193919Skib struct pfs_node *pn; 268155920Sjhb struct proc *proc; 26985561Sdes int error; 27085561Sdes 271193919Skib vn = va->a_vp; 272193919Skib vn_lock(vn, LK_SHARED | LK_RETRY); 273193919Skib if (vn->v_iflag & VI_DOOMED) { 274193919Skib VOP_UNLOCK(vn, 0); 275193919Skib return (EBADF); 276193919Skib } 277193919Skib pvd = vn->v_data; 278193919Skib pn = pvd->pvd_pn; 279193919Skib 28087541Sdes PFS_TRACE(("%s: %lx", pn->pn_name, va->a_command)); 281168764Sdes pfs_assert_not_owned(pn); 28297940Sdes 283193919Skib if (vn->v_type != VREG) { 284193919Skib VOP_UNLOCK(vn, 0); 28585561Sdes PFS_RETURN (EINVAL); 286193919Skib } 287190806Sdes KASSERT_PN_IS_FILE(pn); 28885561Sdes 289193919Skib if (pn->pn_ioctl == NULL) { 290193919Skib VOP_UNLOCK(vn, 0); 29185561Sdes PFS_RETURN (ENOTTY); 292193919Skib } 29397940Sdes 29485561Sdes /* 295116639Sjmg * This is necessary because process' privileges may 29685561Sdes * have changed since the open() call. 29785561Sdes */ 298297267Skib if (!pfs_visible(curthread, pn, pvd->pvd_pid, false, &proc)) { 299193919Skib VOP_UNLOCK(vn, 0); 30085561Sdes PFS_RETURN (EIO); 301193919Skib } 30297940Sdes 303168764Sdes error = pn_ioctl(curthread, proc, pn, va->a_command, va->a_data); 30497940Sdes 30585561Sdes if (proc != NULL) 306168764Sdes PROC_UNLOCK(proc); 30797940Sdes 308193919Skib VOP_UNLOCK(vn, 0); 30985561Sdes PFS_RETURN (error); 31085561Sdes} 31185561Sdes 31285561Sdes/* 31387670Sgreen * Perform getextattr 31487670Sgreen */ 31587670Sgreenstatic int 31687670Sgreenpfs_getextattr(struct vop_getextattr_args *va) 31787670Sgreen{ 31887670Sgreen struct vnode *vn = va->a_vp; 319168764Sdes struct pfs_vdata *pvd = vn->v_data; 32087670Sgreen struct pfs_node *pn = pvd->pvd_pn; 321155920Sjhb struct proc *proc; 32287670Sgreen int error; 32387670Sgreen 324168764Sdes PFS_TRACE(("%s", pn->pn_name)); 325168764Sdes pfs_assert_not_owned(pn); 32687670Sgreen 32787670Sgreen /* 32887670Sgreen * This is necessary because either process' privileges may 32987670Sgreen * have changed since the open() call. 33087670Sgreen */ 331297267Skib if (!pfs_visible(curthread, pn, pvd->pvd_pid, false, &proc)) 33287670Sgreen PFS_RETURN (EIO); 33397940Sdes 334168764Sdes if (pn->pn_getextattr == NULL) 335168764Sdes error = EOPNOTSUPP; 336168764Sdes else 337168764Sdes error = pn_getextattr(curthread, proc, pn, 338168764Sdes va->a_attrnamespace, va->a_name, va->a_uio, 339168764Sdes va->a_size, va->a_cred); 340155920Sjhb 341168764Sdes if (proc != NULL) 34287670Sgreen PROC_UNLOCK(proc); 34397940Sdes 34487670Sgreen PFS_RETURN (error); 34587670Sgreen} 34687670Sgreen 34787670Sgreen/* 348186617Smarcus * Convert a vnode to its component name 349186617Smarcus */ 350186617Smarcusstatic int 351186617Smarcuspfs_vptocnp(struct vop_vptocnp_args *ap) 352186617Smarcus{ 353186617Smarcus struct vnode *vp = ap->a_vp; 354186617Smarcus struct vnode **dvp = ap->a_vpp; 355186617Smarcus struct pfs_vdata *pvd = vp->v_data; 356186617Smarcus struct pfs_node *pd = pvd->pvd_pn; 357186617Smarcus struct pfs_node *pn; 358186617Smarcus struct mount *mp; 359186617Smarcus char *buf = ap->a_buf; 360186617Smarcus int *buflen = ap->a_buflen; 361186617Smarcus char pidbuf[PFS_NAMELEN]; 362186617Smarcus pid_t pid = pvd->pvd_pid; 363186617Smarcus int len, i, error, locked; 364186617Smarcus 365186617Smarcus i = *buflen; 366186617Smarcus error = 0; 367186617Smarcus 368186617Smarcus pfs_lock(pd); 369186617Smarcus 370186617Smarcus if (vp->v_type == VDIR && pd->pn_type == pfstype_root) { 371186617Smarcus *dvp = vp; 372186617Smarcus vhold(*dvp); 373186617Smarcus pfs_unlock(pd); 374186617Smarcus PFS_RETURN (0); 375186617Smarcus } else if (vp->v_type == VDIR && pd->pn_type == pfstype_procdir) { 376186617Smarcus len = snprintf(pidbuf, sizeof(pidbuf), "%d", pid); 377186617Smarcus i -= len; 378186617Smarcus if (i < 0) { 379186617Smarcus error = ENOMEM; 380186617Smarcus goto failed; 381186617Smarcus } 382186617Smarcus bcopy(pidbuf, buf + i, len); 383186617Smarcus } else { 384192973Sdes len = strlen(pd->pn_name); 385192973Sdes i -= len; 386186617Smarcus if (i < 0) { 387186617Smarcus error = ENOMEM; 388186617Smarcus goto failed; 389186617Smarcus } 390192973Sdes bcopy(pd->pn_name, buf + i, len); 391186617Smarcus } 392186617Smarcus 393186617Smarcus pn = pd->pn_parent; 394186617Smarcus pfs_unlock(pd); 395186617Smarcus 396186617Smarcus mp = vp->v_mount; 397186617Smarcus error = vfs_busy(mp, 0); 398186617Smarcus if (error) 399186617Smarcus return (error); 400186617Smarcus 401186617Smarcus /* 402186617Smarcus * vp is held by caller. 403186617Smarcus */ 404186617Smarcus locked = VOP_ISLOCKED(vp); 405186617Smarcus VOP_UNLOCK(vp, 0); 406186617Smarcus 407186617Smarcus error = pfs_vncache_alloc(mp, dvp, pn, pid); 408186617Smarcus if (error) { 409186617Smarcus vn_lock(vp, locked | LK_RETRY); 410186617Smarcus vfs_unbusy(mp); 411186617Smarcus PFS_RETURN(error); 412186617Smarcus } 413186617Smarcus 414186617Smarcus *buflen = i; 415227697Skib VOP_UNLOCK(*dvp, 0); 416186617Smarcus vn_lock(vp, locked | LK_RETRY); 417186617Smarcus vfs_unbusy(mp); 418186617Smarcus 419186617Smarcus PFS_RETURN (0); 420186617Smarcusfailed: 421186617Smarcus pfs_unlock(pd); 422186617Smarcus PFS_RETURN(error); 423186617Smarcus} 424186617Smarcus 425186617Smarcus/* 42675295Sdes * Look up a file or directory 42775295Sdes */ 42875295Sdesstatic int 429143597Sdespfs_lookup(struct vop_cachedlookup_args *va) 43075295Sdes{ 43177998Sdes struct vnode *vn = va->a_dvp; 43275295Sdes struct vnode **vpp = va->a_vpp; 43375295Sdes struct componentname *cnp = va->a_cnp; 434168764Sdes struct pfs_vdata *pvd = vn->v_data; 43577998Sdes struct pfs_node *pd = pvd->pvd_pn; 43677998Sdes struct pfs_node *pn, *pdn = NULL; 437232541Skib struct mount *mp; 43877998Sdes pid_t pid = pvd->pvd_pid; 43975295Sdes char *pname; 440168764Sdes int error, i, namelen, visible; 44175295Sdes 44284098Sdes PFS_TRACE(("%.*s", (int)cnp->cn_namelen, cnp->cn_nameptr)); 443168764Sdes pfs_assert_not_owned(pd); 44497940Sdes 44577998Sdes if (vn->v_type != VDIR) 44684098Sdes PFS_RETURN (ENOTDIR); 447190806Sdes KASSERT_PN_IS_DIR(pd); 44897940Sdes 449143597Sdes error = VOP_ACCESS(vn, VEXEC, cnp->cn_cred, cnp->cn_thread); 450143597Sdes if (error) 451143597Sdes PFS_RETURN (error); 452143597Sdes 45384098Sdes /* 45484098Sdes * Don't support DELETE or RENAME. CREATE is supported so 45584098Sdes * that O_CREAT will work, but the lookup will still fail if 45684098Sdes * the file does not exist. 45784098Sdes */ 458143597Sdes if ((cnp->cn_flags & ISLASTCN) && 459143597Sdes (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 46084187Sdes PFS_RETURN (EOPNOTSUPP); 46175295Sdes 46284098Sdes /* shortcut: check if the name is too long */ 46375295Sdes if (cnp->cn_namelen >= PFS_NAMELEN) 46484098Sdes PFS_RETURN (ENOENT); 46584098Sdes 466155920Sjhb /* check that parent directory is visible... */ 467297267Skib if (!pfs_visible(curthread, pd, pvd->pvd_pid, false, NULL)) 46884246Sdes PFS_RETURN (ENOENT); 46997940Sdes 47075295Sdes /* self */ 47177998Sdes namelen = cnp->cn_namelen; 47275295Sdes pname = cnp->cn_nameptr; 473143597Sdes if (namelen == 1 && pname[0] == '.') { 47475295Sdes pn = pd; 47577998Sdes *vpp = vn; 47677998Sdes VREF(vn); 47784246Sdes PFS_RETURN (0); 47875295Sdes } 47975295Sdes 480232541Skib mp = vn->v_mount; 481232541Skib 48275295Sdes /* parent */ 48375295Sdes if (cnp->cn_flags & ISDOTDOT) { 48475295Sdes if (pd->pn_type == pfstype_root) 48584098Sdes PFS_RETURN (EIO); 486232541Skib error = vfs_busy(mp, MBF_NOWAIT); 487232541Skib if (error != 0) { 488232541Skib vfs_ref(mp); 489232541Skib VOP_UNLOCK(vn, 0); 490232541Skib error = vfs_busy(mp, 0); 491232541Skib vn_lock(vn, LK_EXCLUSIVE | LK_RETRY); 492232541Skib vfs_rel(mp); 493232541Skib if (error != 0) 494232541Skib PFS_RETURN(ENOENT); 495232541Skib if (vn->v_iflag & VI_DOOMED) { 496232541Skib vfs_unbusy(mp); 497232541Skib PFS_RETURN(ENOENT); 498232541Skib } 499232541Skib } 500175294Sattilio VOP_UNLOCK(vn, 0); 501168764Sdes KASSERT(pd->pn_parent != NULL, 502168764Sdes ("%s(): non-root directory has no parent", __func__)); 50377998Sdes /* 50477998Sdes * This one is tricky. Descendents of procdir nodes 50577998Sdes * inherit their parent's process affinity, but 50677998Sdes * there's no easy reverse mapping. For simplicity, 50777998Sdes * we assume that if this node is a procdir, its 50877998Sdes * parent isn't (which is correct as long as 50977998Sdes * descendents of procdir nodes are never procdir 51077998Sdes * nodes themselves) 51177998Sdes */ 51277998Sdes if (pd->pn_type == pfstype_procdir) 51377998Sdes pid = NO_PID; 514168764Sdes pfs_lock(pd); 51584246Sdes pn = pd->pn_parent; 516168764Sdes pfs_unlock(pd); 51784246Sdes goto got_pnode; 51875295Sdes } 51975295Sdes 520168764Sdes pfs_lock(pd); 521168764Sdes 52277998Sdes /* named node */ 52385128Sdes for (pn = pd->pn_nodes; pn != NULL; pn = pn->pn_next) 52477998Sdes if (pn->pn_type == pfstype_procdir) 52577998Sdes pdn = pn; 526143597Sdes else if (pn->pn_name[namelen] == '\0' && 527168764Sdes bcmp(pname, pn->pn_name, namelen) == 0) { 528168764Sdes pfs_unlock(pd); 52975295Sdes goto got_pnode; 530168764Sdes } 53177998Sdes 53277998Sdes /* process dependent node */ 53377998Sdes if ((pn = pdn) != NULL) { 53477998Sdes pid = 0; 53577998Sdes for (pid = 0, i = 0; i < namelen && isdigit(pname[i]); ++i) 53677998Sdes if ((pid = pid * 10 + pname[i] - '0') > PID_MAX) 53775295Sdes break; 538168764Sdes if (i == cnp->cn_namelen) { 539168764Sdes pfs_unlock(pd); 54075295Sdes goto got_pnode; 541168764Sdes } 54275295Sdes } 54397940Sdes 544168764Sdes pfs_unlock(pd); 545168764Sdes 54684187Sdes PFS_RETURN (ENOENT); 547168764Sdes 54875295Sdes got_pnode: 549168764Sdes pfs_assert_not_owned(pd); 550168764Sdes pfs_assert_not_owned(pn); 551297267Skib visible = pfs_visible(curthread, pn, pid, false, NULL); 552168764Sdes if (!visible) { 553144208Sjeff error = ENOENT; 554144208Sjeff goto failed; 555144208Sjeff } 55699566Sjeff 557232541Skib error = pfs_vncache_alloc(mp, vpp, pn, pid); 55875295Sdes if (error) 559144208Sjeff goto failed; 56099566Sjeff 561232541Skib if (cnp->cn_flags & ISDOTDOT) { 562232541Skib vfs_unbusy(mp); 563232541Skib vn_lock(vn, LK_EXCLUSIVE | LK_RETRY); 564232541Skib if (vn->v_iflag & VI_DOOMED) { 565232541Skib vput(*vpp); 566232541Skib *vpp = NULL; 567232541Skib PFS_RETURN(ENOENT); 568232541Skib } 569232541Skib } 570206894Skib if (cnp->cn_flags & MAKEENTRY && !(vn->v_iflag & VI_DOOMED)) 57177998Sdes cache_enter(vn, *vpp, cnp); 57284098Sdes PFS_RETURN (0); 573144208Sjeff failed: 574232541Skib if (cnp->cn_flags & ISDOTDOT) { 575232541Skib vfs_unbusy(mp); 576232541Skib vn_lock(vn, LK_EXCLUSIVE | LK_RETRY); 577232541Skib *vpp = NULL; 578232541Skib } 579144208Sjeff PFS_RETURN(error); 58075295Sdes} 58175295Sdes 58275295Sdes/* 58375295Sdes * Open a file or directory. 58475295Sdes */ 58575295Sdesstatic int 58675295Sdespfs_open(struct vop_open_args *va) 58775295Sdes{ 58884098Sdes struct vnode *vn = va->a_vp; 589168764Sdes struct pfs_vdata *pvd = vn->v_data; 59084098Sdes struct pfs_node *pn = pvd->pvd_pn; 59184187Sdes int mode = va->a_mode; 59284098Sdes 59384187Sdes PFS_TRACE(("%s (mode 0x%x)", pn->pn_name, mode)); 594168764Sdes pfs_assert_not_owned(pn); 59584187Sdes 59684187Sdes /* check if the requested mode is permitted */ 59784187Sdes if (((mode & FREAD) && !(mode & PFS_RD)) || 59884187Sdes ((mode & FWRITE) && !(mode & PFS_WR))) 59984187Sdes PFS_RETURN (EPERM); 60084187Sdes 60184187Sdes /* we don't support locking */ 60284187Sdes if ((mode & O_SHLOCK) || (mode & O_EXLOCK)) 60384187Sdes PFS_RETURN (EOPNOTSUPP); 60497940Sdes 60584246Sdes PFS_RETURN (0); 60675295Sdes} 60775295Sdes 60875295Sdes/* 60975295Sdes * Read from a file 61075295Sdes */ 61175295Sdesstatic int 61275295Sdespfs_read(struct vop_read_args *va) 61375295Sdes{ 61475295Sdes struct vnode *vn = va->a_vp; 615168764Sdes struct pfs_vdata *pvd = vn->v_data; 61677998Sdes struct pfs_node *pn = pvd->pvd_pn; 61775295Sdes struct uio *uio = va->a_uio; 618155920Sjhb struct proc *proc; 61977964Sdes struct sbuf *sb = NULL; 620186561Skib int error, locked; 621259506Skib off_t buflen; 62275295Sdes 623168764Sdes PFS_TRACE(("%s", pn->pn_name)); 624168764Sdes pfs_assert_not_owned(pn); 62597940Sdes 62675295Sdes if (vn->v_type != VREG) 62784187Sdes PFS_RETURN (EINVAL); 628190806Sdes KASSERT_PN_IS_FILE(pn); 62975295Sdes 63084187Sdes if (!(pn->pn_flags & PFS_RD)) 63184187Sdes PFS_RETURN (EBADF); 63283927Sdes 633168764Sdes if (pn->pn_fill == NULL) 63485561Sdes PFS_RETURN (EIO); 63597940Sdes 63684246Sdes /* 63784246Sdes * This is necessary because either process' privileges may 63884246Sdes * have changed since the open() call. 63984246Sdes */ 640297267Skib if (!pfs_visible(curthread, pn, pvd->pvd_pid, false, &proc)) 64184246Sdes PFS_RETURN (EIO); 642155920Sjhb if (proc != NULL) { 64377998Sdes _PHOLD(proc); 64477998Sdes PROC_UNLOCK(proc); 64577998Sdes } 64683927Sdes 647186561Skib vhold(vn); 648186561Skib locked = VOP_ISLOCKED(vn); 649186561Skib VOP_UNLOCK(vn, 0); 650186561Skib 65183927Sdes if (pn->pn_flags & PFS_RAWRD) { 652194990Skib PFS_TRACE(("%zd resid", uio->uio_resid)); 653168764Sdes error = pn_fill(curthread, proc, pn, NULL, uio); 654194990Skib PFS_TRACE(("%zd resid", uio->uio_resid)); 655186561Skib goto ret; 65683927Sdes } 65797940Sdes 658259506Skib if (uio->uio_resid < 0 || uio->uio_offset < 0 || 659259506Skib uio->uio_resid > OFF_MAX - uio->uio_offset) { 660186561Skib error = EINVAL; 661186561Skib goto ret; 662120665Snectar } 663259506Skib buflen = uio->uio_offset + uio->uio_resid; 664229694Sjh if (buflen > MAXPHYS) 665229694Sjh buflen = MAXPHYS; 666168764Sdes 667229694Sjh sb = sbuf_new(sb, NULL, buflen + 1, 0); 66877998Sdes if (sb == NULL) { 669186561Skib error = EIO; 670186561Skib goto ret; 67177998Sdes } 67275295Sdes 673168764Sdes error = pn_fill(curthread, proc, pn, sb, uio); 67477998Sdes 67578018Sdes if (error) { 67678018Sdes sbuf_delete(sb); 677186561Skib goto ret; 67878018Sdes } 67997940Sdes 680229694Sjh /* 681229694Sjh * XXX: If the buffer overflowed, sbuf_len() will not return 682229694Sjh * the data length. Then just use the full length because an 683229694Sjh * overflowed sbuf must be full. 684229694Sjh */ 685229694Sjh if (sbuf_finish(sb) == 0) 686229694Sjh buflen = sbuf_len(sb); 687229694Sjh error = uiomove_frombuf(sbuf_data(sb), buflen, uio); 68877964Sdes sbuf_delete(sb); 689186561Skibret: 690186561Skib vn_lock(vn, locked | LK_RETRY); 691186561Skib vdrop(vn); 692186561Skib if (proc != NULL) 693186561Skib PRELE(proc); 69484187Sdes PFS_RETURN (error); 69575295Sdes} 69675295Sdes 69775295Sdes/* 69877998Sdes * Iterate through directory entries 69977998Sdes */ 70077998Sdesstatic int 701168764Sdespfs_iterate(struct thread *td, struct proc *proc, struct pfs_node *pd, 70297940Sdes struct pfs_node **pn, struct proc **p) 70377998Sdes{ 704168764Sdes int visible; 705168764Sdes 706155920Sjhb sx_assert(&allproc_lock, SX_SLOCKED); 707168764Sdes pfs_assert_owned(pd); 708119069Sdes again: 709119069Sdes if (*pn == NULL) { 710119069Sdes /* first node */ 71185128Sdes *pn = pd->pn_nodes; 712119069Sdes } else if ((*pn)->pn_type != pfstype_procdir) { 713119069Sdes /* next node */ 71485128Sdes *pn = (*pn)->pn_next; 715119069Sdes } 716119069Sdes if (*pn != NULL && (*pn)->pn_type == pfstype_procdir) { 717119069Sdes /* next process */ 71877998Sdes if (*p == NULL) 71977998Sdes *p = LIST_FIRST(&allproc); 72077998Sdes else 72177998Sdes *p = LIST_NEXT(*p, p_list); 722119069Sdes /* out of processes: next node */ 723119069Sdes if (*p == NULL) 724119069Sdes *pn = (*pn)->pn_next; 725168764Sdes else 726168764Sdes PROC_LOCK(*p); 72777998Sdes } 72897940Sdes 72985128Sdes if ((*pn) == NULL) 73077998Sdes return (-1); 73184246Sdes 732168764Sdes if (*p != NULL) { 733168764Sdes visible = pfs_visible_proc(td, *pn, *p); 734168764Sdes PROC_UNLOCK(*p); 735168764Sdes } else if (proc != NULL) { 736168764Sdes visible = pfs_visible_proc(td, *pn, proc); 737168764Sdes } else { 738168764Sdes visible = 1; 739168764Sdes } 740168764Sdes if (!visible) 74184246Sdes goto again; 74297940Sdes 74377998Sdes return (0); 74477998Sdes} 74577998Sdes 746227550Spho/* Directory entry list */ 747227550Sphostruct pfsentry { 748227550Spho STAILQ_ENTRY(pfsentry) link; 749227550Spho struct dirent entry; 750227550Spho}; 751227550SphoSTAILQ_HEAD(pfsdirentlist, pfsentry); 752227550Spho 75377998Sdes/* 75475295Sdes * Return directory entries. 75575295Sdes */ 75675295Sdesstatic int 75775295Sdespfs_readdir(struct vop_readdir_args *va) 75875295Sdes{ 75975295Sdes struct vnode *vn = va->a_vp; 760168764Sdes struct pfs_vdata *pvd = vn->v_data; 76177998Sdes struct pfs_node *pd = pvd->pvd_pn; 76284246Sdes pid_t pid = pvd->pvd_pid; 763168764Sdes struct proc *p, *proc; 76477998Sdes struct pfs_node *pn; 76575295Sdes struct uio *uio; 766227550Spho struct pfsentry *pfsent, *pfsent2; 767227550Spho struct pfsdirentlist lst; 76875295Sdes off_t offset; 76975295Sdes int error, i, resid; 77075295Sdes 771227550Spho STAILQ_INIT(&lst); 772227550Spho error = 0; 773168720Sdes KASSERT(pd->pn_info == vn->v_mount->mnt_data, 774168764Sdes ("%s(): pn_info does not match mountpoint", __func__)); 775168764Sdes PFS_TRACE(("%s pid %lu", pd->pn_name, (unsigned long)pid)); 776168764Sdes pfs_assert_not_owned(pd); 77797940Sdes 77875295Sdes if (vn->v_type != VDIR) 77984187Sdes PFS_RETURN (ENOTDIR); 780190806Sdes KASSERT_PN_IS_DIR(pd); 78175295Sdes uio = va->a_uio; 78275295Sdes 78375295Sdes /* only allow reading entire entries */ 78475295Sdes offset = uio->uio_offset; 78575295Sdes resid = uio->uio_resid; 786143596Sdes if (offset < 0 || offset % PFS_DELEN != 0 || 787143596Sdes (resid && resid < PFS_DELEN)) 78884187Sdes PFS_RETURN (EINVAL); 789143596Sdes if (resid == 0) 790143596Sdes PFS_RETURN (0); 79175295Sdes 792168764Sdes sx_slock(&allproc_lock); 793168764Sdes pfs_lock(pd); 794168764Sdes 795168764Sdes /* check if the directory is visible to the caller */ 796297267Skib if (!pfs_visible(curthread, pd, pid, true, &proc)) { 797168764Sdes sx_sunlock(&allproc_lock); 798168764Sdes pfs_unlock(pd); 799168764Sdes PFS_RETURN (ENOENT); 800168764Sdes } 801168764Sdes KASSERT(pid == NO_PID || proc != NULL, 802168764Sdes ("%s(): no process for pid %lu", __func__, (unsigned long)pid)); 803168764Sdes 80475295Sdes /* skip unwanted entries */ 805168764Sdes for (pn = NULL, p = NULL; offset > 0; offset -= PFS_DELEN) { 806168764Sdes if (pfs_iterate(curthread, proc, pd, &pn, &p) == -1) { 80785128Sdes /* nothing left... */ 808168764Sdes if (proc != NULL) 809168764Sdes PROC_UNLOCK(proc); 810168764Sdes pfs_unlock(pd); 81185128Sdes sx_sunlock(&allproc_lock); 81285128Sdes PFS_RETURN (0); 81385128Sdes } 814168764Sdes } 81597940Sdes 81675295Sdes /* fill in entries */ 817168764Sdes while (pfs_iterate(curthread, proc, pd, &pn, &p) != -1 && 818143596Sdes resid >= PFS_DELEN) { 819227550Spho if ((pfsent = malloc(sizeof(struct pfsentry), M_IOV, 820227550Spho M_NOWAIT | M_ZERO)) == NULL) { 821227550Spho error = ENOMEM; 822227550Spho break; 823227550Spho } 824227550Spho pfsent->entry.d_reclen = PFS_DELEN; 825227550Spho pfsent->entry.d_fileno = pn_fileno(pn, pid); 82675295Sdes /* PFS_DELEN was picked to fit PFS_NAMLEN */ 82775295Sdes for (i = 0; i < PFS_NAMELEN - 1 && pn->pn_name[i] != '\0'; ++i) 828227550Spho pfsent->entry.d_name[i] = pn->pn_name[i]; 829227550Spho pfsent->entry.d_name[i] = 0; 830227550Spho pfsent->entry.d_namlen = i; 83175295Sdes switch (pn->pn_type) { 83277998Sdes case pfstype_procdir: 83377998Sdes KASSERT(p != NULL, 83477998Sdes ("reached procdir node with p == NULL")); 835227550Spho pfsent->entry.d_namlen = snprintf(pfsent->entry.d_name, 83677998Sdes PFS_NAMELEN, "%d", p->p_pid); 83777998Sdes /* fall through */ 83875295Sdes case pfstype_root: 83975295Sdes case pfstype_dir: 84075295Sdes case pfstype_this: 84175295Sdes case pfstype_parent: 842227550Spho pfsent->entry.d_type = DT_DIR; 84375295Sdes break; 84475295Sdes case pfstype_file: 845227550Spho pfsent->entry.d_type = DT_REG; 84675295Sdes break; 84775295Sdes case pfstype_symlink: 848227550Spho pfsent->entry.d_type = DT_LNK; 84975295Sdes break; 85075295Sdes default: 85175295Sdes panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type); 85275295Sdes } 853227550Spho PFS_TRACE(("%s", pfsent->entry.d_name)); 854227550Spho STAILQ_INSERT_TAIL(&lst, pfsent, link); 85575295Sdes offset += PFS_DELEN; 85675295Sdes resid -= PFS_DELEN; 85775295Sdes } 858168764Sdes if (proc != NULL) 859168764Sdes PROC_UNLOCK(proc); 860168764Sdes pfs_unlock(pd); 86177998Sdes sx_sunlock(&allproc_lock); 862227550Spho i = 0; 863227550Spho STAILQ_FOREACH_SAFE(pfsent, &lst, link, pfsent2) { 864227550Spho if (error == 0) 865227550Spho error = uiomove(&pfsent->entry, PFS_DELEN, uio); 866227550Spho free(pfsent, M_IOV); 867227550Spho i++; 868227550Spho } 869227576Skib PFS_TRACE(("%d bytes", i * PFS_DELEN)); 870131871Sdes PFS_RETURN (error); 87175295Sdes} 87275295Sdes 87375295Sdes/* 87475295Sdes * Read a symbolic link 87575295Sdes */ 87675295Sdesstatic int 87775295Sdespfs_readlink(struct vop_readlink_args *va) 87875295Sdes{ 87975295Sdes struct vnode *vn = va->a_vp; 880168764Sdes struct pfs_vdata *pvd = vn->v_data; 88177998Sdes struct pfs_node *pn = pvd->pvd_pn; 88275295Sdes struct uio *uio = va->a_uio; 88377998Sdes struct proc *proc = NULL; 884168764Sdes char buf[PATH_MAX]; 88575295Sdes struct sbuf sb; 886193176Skib int error, locked; 88775295Sdes 888168764Sdes PFS_TRACE(("%s", pn->pn_name)); 889168764Sdes pfs_assert_not_owned(pn); 89097940Sdes 89175295Sdes if (vn->v_type != VLNK) 89284187Sdes PFS_RETURN (EINVAL); 893190806Sdes KASSERT_PN_IS_LINK(pn); 89475295Sdes 895168764Sdes if (pn->pn_fill == NULL) 89685561Sdes PFS_RETURN (EIO); 89797940Sdes 89877998Sdes if (pvd->pvd_pid != NO_PID) { 89977998Sdes if ((proc = pfind(pvd->pvd_pid)) == NULL) 90084187Sdes PFS_RETURN (EIO); 901155922Sjhb if (proc->p_flag & P_WEXIT) { 902155922Sjhb PROC_UNLOCK(proc); 903155922Sjhb PFS_RETURN (EIO); 904155922Sjhb } 90577998Sdes _PHOLD(proc); 90677998Sdes PROC_UNLOCK(proc); 90777998Sdes } 908193176Skib vhold(vn); 909193176Skib locked = VOP_ISLOCKED(vn); 910193176Skib VOP_UNLOCK(vn, 0); 91197940Sdes 91275295Sdes /* sbuf_new() can't fail with a static buffer */ 91375295Sdes sbuf_new(&sb, buf, sizeof buf, 0); 91475295Sdes 915168764Sdes error = pn_fill(curthread, proc, pn, &sb, NULL); 91677998Sdes 91777998Sdes if (proc != NULL) 91877998Sdes PRELE(proc); 919193176Skib vn_lock(vn, locked | LK_RETRY); 920193176Skib vdrop(vn); 92197940Sdes 92278018Sdes if (error) { 92378018Sdes sbuf_delete(&sb); 92484187Sdes PFS_RETURN (error); 92578018Sdes } 92697940Sdes 927229692Sjh if (sbuf_finish(&sb) != 0) { 928229692Sjh sbuf_delete(&sb); 929229692Sjh PFS_RETURN (ENAMETOOLONG); 930229692Sjh } 931229692Sjh 932120665Snectar error = uiomove_frombuf(sbuf_data(&sb), sbuf_len(&sb), uio); 93375295Sdes sbuf_delete(&sb); 93484187Sdes PFS_RETURN (error); 93575295Sdes} 93675295Sdes 93775295Sdes/* 93875295Sdes * Reclaim a vnode 93975295Sdes */ 94075295Sdesstatic int 94175295Sdespfs_reclaim(struct vop_reclaim_args *va) 94275295Sdes{ 943168764Sdes struct vnode *vn = va->a_vp; 944168764Sdes struct pfs_vdata *pvd = vn->v_data; 945168764Sdes struct pfs_node *pn = pvd->pvd_pn; 94688234Sdillon 947168764Sdes PFS_TRACE(("%s", pn->pn_name)); 948168764Sdes pfs_assert_not_owned(pn); 949168764Sdes 95075295Sdes return (pfs_vncache_free(va->a_vp)); 95175295Sdes} 95275295Sdes 95375295Sdes/* 95475295Sdes * Set attributes 95575295Sdes */ 95675295Sdesstatic int 95775295Sdespfs_setattr(struct vop_setattr_args *va) 95875295Sdes{ 959168764Sdes struct vnode *vn = va->a_vp; 960168764Sdes struct pfs_vdata *pvd = vn->v_data; 961168764Sdes struct pfs_node *pn = pvd->pvd_pn; 96290206Srwatson 963168764Sdes PFS_TRACE(("%s", pn->pn_name)); 964168764Sdes pfs_assert_not_owned(pn); 965168764Sdes 96690206Srwatson PFS_RETURN (EOPNOTSUPP); 96775295Sdes} 96875295Sdes 96975295Sdes/* 970145714Sdes * Write to a file 97175295Sdes */ 97283927Sdesstatic int 973138290Sphkpfs_write(struct vop_write_args *va) 97483927Sdes{ 97583927Sdes struct vnode *vn = va->a_vp; 976168764Sdes struct pfs_vdata *pvd = vn->v_data; 97783927Sdes struct pfs_node *pn = pvd->pvd_pn; 97883927Sdes struct uio *uio = va->a_uio; 979155920Sjhb struct proc *proc; 98084098Sdes struct sbuf sb; 98183927Sdes int error; 98283927Sdes 983168764Sdes PFS_TRACE(("%s", pn->pn_name)); 984168764Sdes pfs_assert_not_owned(pn); 98597940Sdes 98683927Sdes if (vn->v_type != VREG) 98784187Sdes PFS_RETURN (EINVAL); 988190806Sdes KASSERT_PN_IS_FILE(pn); 98983927Sdes 99084187Sdes if (!(pn->pn_flags & PFS_WR)) 99184187Sdes PFS_RETURN (EBADF); 99283927Sdes 993168764Sdes if (pn->pn_fill == NULL) 99485561Sdes PFS_RETURN (EIO); 99597940Sdes 99684246Sdes /* 99784246Sdes * This is necessary because either process' privileges may 99884246Sdes * have changed since the open() call. 99984246Sdes */ 1000297267Skib if (!pfs_visible(curthread, pn, pvd->pvd_pid, false, &proc)) 100184246Sdes PFS_RETURN (EIO); 1002155920Sjhb if (proc != NULL) { 100383927Sdes _PHOLD(proc); 100483927Sdes PROC_UNLOCK(proc); 100583927Sdes } 100683927Sdes 100783927Sdes if (pn->pn_flags & PFS_RAWWR) { 1008168764Sdes error = pn_fill(curthread, proc, pn, NULL, uio); 100983927Sdes if (proc != NULL) 101083927Sdes PRELE(proc); 101184187Sdes PFS_RETURN (error); 101283927Sdes } 101384098Sdes 101484098Sdes sbuf_uionew(&sb, uio, &error); 1015168764Sdes if (error) { 1016168764Sdes if (proc != NULL) 1017168764Sdes PRELE(proc); 101884187Sdes PFS_RETURN (error); 1019168764Sdes } 102097940Sdes 1021168764Sdes error = pn_fill(curthread, proc, pn, &sb, uio); 102284098Sdes 1023168764Sdes sbuf_delete(&sb); 102484098Sdes if (proc != NULL) 102584098Sdes PRELE(proc); 102684187Sdes PFS_RETURN (error); 102783927Sdes} 102883927Sdes 102983927Sdes/* 103075295Sdes * Vnode operations 103175295Sdes */ 1032138290Sphkstruct vop_vector pfs_vnodeops = { 1033138290Sphk .vop_default = &default_vnodeops, 1034140196Sphk 1035138290Sphk .vop_access = pfs_access, 1036143597Sdes .vop_cachedlookup = pfs_lookup, 1037138290Sphk .vop_close = pfs_close, 1038138290Sphk .vop_create = VOP_EOPNOTSUPP, 1039138290Sphk .vop_getattr = pfs_getattr, 1040138290Sphk .vop_getextattr = pfs_getextattr, 1041138290Sphk .vop_ioctl = pfs_ioctl, 1042138290Sphk .vop_link = VOP_EOPNOTSUPP, 1043143597Sdes .vop_lookup = vfs_cache_lookup, 1044138290Sphk .vop_mkdir = VOP_EOPNOTSUPP, 1045138290Sphk .vop_mknod = VOP_EOPNOTSUPP, 1046138290Sphk .vop_open = pfs_open, 1047138290Sphk .vop_read = pfs_read, 1048138290Sphk .vop_readdir = pfs_readdir, 1049138290Sphk .vop_readlink = pfs_readlink, 1050138290Sphk .vop_reclaim = pfs_reclaim, 1051138290Sphk .vop_remove = VOP_EOPNOTSUPP, 1052138290Sphk .vop_rename = VOP_EOPNOTSUPP, 1053138290Sphk .vop_rmdir = VOP_EOPNOTSUPP, 1054138290Sphk .vop_setattr = pfs_setattr, 1055138290Sphk .vop_symlink = VOP_EOPNOTSUPP, 1056186617Smarcus .vop_vptocnp = pfs_vptocnp, 1057138290Sphk .vop_write = pfs_write, 1058138290Sphk /* XXX I've probably forgotten a few that need VOP_EOPNOTSUPP */ 105975295Sdes}; 1060