diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index 0c4707b95d6d..3f9a8b1734ae 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -1,875 +1,949 @@ /* * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vfs_vnops.c 8.2 (Berkeley) 1/21/94 * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int vn_closefile __P((struct file *fp, struct proc *p)); static int vn_ioctl __P((struct file *fp, u_long com, caddr_t data, struct proc *p)); static int vn_read __P((struct file *fp, struct uio *uio, struct ucred *cred, int flags, struct proc *p)); static int vn_poll __P((struct file *fp, int events, struct ucred *cred, struct proc *p)); static int vn_statfile __P((struct file *fp, struct stat *sb, struct proc *p)); static int vn_write __P((struct file *fp, struct uio *uio, struct ucred *cred, int flags, struct proc *p)); struct fileops vnops = { vn_read, vn_write, vn_ioctl, vn_poll, vn_statfile, vn_closefile }; static int filt_nullattach(struct knote *kn); static int filt_vnattach(struct knote *kn); static void filt_vndetach(struct knote *kn); static int filt_vnode(struct knote *kn, long hint); static int filt_vnread(struct knote *kn, long hint); struct filterops vn_filtops = { 1, filt_vnattach, filt_vndetach, filt_vnode }; /* * XXX * filt_vnread is ufs-specific, so the attach routine should really * switch out to different filterops based on the vn filetype */ struct filterops vn_rwfiltops[] = { { 1, filt_vnattach, filt_vndetach, filt_vnread }, { 1, filt_nullattach, NULL, NULL }, }; /* * Common code for vnode open operations. * Check permissions, and call the VOP_OPEN or VOP_CREATE routine. * * Note that this does NOT free nameidata for the successful case, * due to the NDINIT being done elsewhere. */ int vn_open(ndp, flagp, cmode) register struct nameidata *ndp; int *flagp, cmode; { struct vnode *vp; struct mount *mp; struct proc *p = ndp->ni_cnd.cn_proc; struct ucred *cred = p->p_ucred; struct vattr vat; struct vattr *vap = &vat; int mode, fmode, error; restart: fmode = *flagp; if (fmode & O_CREAT) { ndp->ni_cnd.cn_nameiop = CREATE; ndp->ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; if ((fmode & O_EXCL) == 0 && (fmode & O_NOFOLLOW) == 0) ndp->ni_cnd.cn_flags |= FOLLOW; bwillwrite(); if ((error = namei(ndp)) != 0) return (error); if (ndp->ni_vp == NULL) { VATTR_NULL(vap); vap->va_type = VREG; vap->va_mode = cmode; if (fmode & O_EXCL) vap->va_vaflags |= VA_EXCLUSIVE; if (vn_start_write(ndp->ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(ndp, NDF_ONLY_PNBUF); vput(ndp->ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VOP_LEASE(ndp->ni_dvp, p, cred, LEASE_WRITE); error = VOP_CREATE(ndp->ni_dvp, &ndp->ni_vp, &ndp->ni_cnd, vap); vput(ndp->ni_dvp); vn_finished_write(mp); if (error) { NDFREE(ndp, NDF_ONLY_PNBUF); return (error); } ASSERT_VOP_UNLOCKED(ndp->ni_dvp, "create"); ASSERT_VOP_LOCKED(ndp->ni_vp, "create"); fmode &= ~O_TRUNC; vp = ndp->ni_vp; } else { if (ndp->ni_dvp == ndp->ni_vp) vrele(ndp->ni_dvp); else vput(ndp->ni_dvp); ndp->ni_dvp = NULL; vp = ndp->ni_vp; if (fmode & O_EXCL) { error = EEXIST; goto bad; } fmode &= ~O_CREAT; } } else { ndp->ni_cnd.cn_nameiop = LOOKUP; ndp->ni_cnd.cn_flags = ((fmode & O_NOFOLLOW) ? NOFOLLOW : FOLLOW) | LOCKLEAF; if ((error = namei(ndp)) != 0) return (error); vp = ndp->ni_vp; } if (vp->v_type == VLNK) { error = EMLINK; goto bad; } if (vp->v_type == VSOCK) { error = EOPNOTSUPP; goto bad; } if ((fmode & O_CREAT) == 0) { mode = 0; if (fmode & (FWRITE | O_TRUNC)) { if (vp->v_type == VDIR) { error = EISDIR; goto bad; } error = vn_writechk(vp); if (error) goto bad; mode |= VWRITE; } if (fmode & FREAD) mode |= VREAD; if (mode) { error = VOP_ACCESS(vp, mode, cred, p); if (error) goto bad; } } if ((error = VOP_OPEN(vp, fmode, cred, p)) != 0) goto bad; /* * Make sure that a VM object is created for VMIO support. */ if (vn_canvmio(vp) == TRUE) { if ((error = vfs_object_create(vp, p, cred)) != 0) goto bad; } if (fmode & FWRITE) vp->v_writecount++; *flagp = fmode; return (0); bad: NDFREE(ndp, NDF_ONLY_PNBUF); vput(vp); *flagp = fmode; return (error); } /* * Check for write permissions on the specified vnode. * Prototype text segments cannot be written. */ int vn_writechk(vp) register struct vnode *vp; { /* * If there's shared text associated with * the vnode, try to free it up once. If * we fail, we can't allow writing. */ if (vp->v_flag & VTEXT) return (ETXTBSY); return (0); } /* * Vnode close call */ int vn_close(vp, flags, cred, p) register struct vnode *vp; int flags; struct ucred *cred; struct proc *p; { int error; if (flags & FWRITE) vp->v_writecount--; error = VOP_CLOSE(vp, flags, cred, p); vrele(vp); return (error); } static __inline int sequential_heuristic(struct uio *uio, struct file *fp) { /* * Sequential heuristic - detect sequential operation */ if ((uio->uio_offset == 0 && fp->f_seqcount > 0) || uio->uio_offset == fp->f_nextoff) { /* * XXX we assume that the filesystem block size is * the default. Not true, but still gives us a pretty * good indicator of how sequential the read operations * are. */ fp->f_seqcount += (uio->uio_resid + BKVASIZE - 1) / BKVASIZE; if (fp->f_seqcount >= 127) fp->f_seqcount = 127; return(fp->f_seqcount << 16); } /* * Not sequential, quick draw-down of seqcount */ if (fp->f_seqcount > 1) fp->f_seqcount = 1; else fp->f_seqcount = 0; return(0); } /* * Package up an I/O request on a vnode into a uio and do it. */ int vn_rdwr(rw, vp, base, len, offset, segflg, ioflg, cred, aresid, p) enum uio_rw rw; struct vnode *vp; caddr_t base; int len; off_t offset; enum uio_seg segflg; int ioflg; struct ucred *cred; int *aresid; struct proc *p; { struct uio auio; struct iovec aiov; struct mount *mp; int error; if ((ioflg & IO_NODELOCKED) == 0) { mp = NULL; if (rw == UIO_WRITE && vp->v_type != VCHR && vp->v_type != VBLK && (error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); } auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = base; aiov.iov_len = len; auio.uio_resid = len; auio.uio_offset = offset; auio.uio_segflg = segflg; auio.uio_rw = rw; auio.uio_procp = p; if (rw == UIO_READ) { error = VOP_READ(vp, &auio, ioflg, cred); } else { error = VOP_WRITE(vp, &auio, ioflg, cred); } if (aresid) *aresid = auio.uio_resid; else if (auio.uio_resid && error == 0) error = EIO; if ((ioflg & IO_NODELOCKED) == 0) { vn_finished_write(mp); VOP_UNLOCK(vp, 0, p); } return (error); } /* * File table vnode read routine. */ static int vn_read(fp, uio, cred, flags, p) struct file *fp; struct uio *uio; struct ucred *cred; struct proc *p; int flags; { struct vnode *vp; int error, ioflag; KASSERT(uio->uio_procp == p, ("uio_procp %p is not p %p", uio->uio_procp, p)); vp = (struct vnode *)fp->f_data; ioflag = 0; if (fp->f_flag & FNONBLOCK) ioflag |= IO_NDELAY; VOP_LEASE(vp, p, cred, LEASE_READ); vn_lock(vp, LK_SHARED | LK_NOPAUSE | LK_RETRY, p); if ((flags & FOF_OFFSET) == 0) uio->uio_offset = fp->f_offset; ioflag |= sequential_heuristic(uio, fp); error = VOP_READ(vp, uio, ioflag, cred); if ((flags & FOF_OFFSET) == 0) fp->f_offset = uio->uio_offset; fp->f_nextoff = uio->uio_offset; VOP_UNLOCK(vp, 0, p); return (error); } /* * File table vnode write routine. */ static int vn_write(fp, uio, cred, flags, p) struct file *fp; struct uio *uio; struct ucred *cred; struct proc *p; int flags; { struct vnode *vp; struct mount *mp; int error, ioflag; KASSERT(uio->uio_procp == p, ("uio_procp %p is not p %p", uio->uio_procp, p)); vp = (struct vnode *)fp->f_data; if (vp->v_type == VREG) bwillwrite(); vp = (struct vnode *)fp->f_data; /* XXX needed? */ ioflag = IO_UNIT; if (vp->v_type == VREG && (fp->f_flag & O_APPEND)) ioflag |= IO_APPEND; if (fp->f_flag & FNONBLOCK) ioflag |= IO_NDELAY; if ((fp->f_flag & O_FSYNC) || (vp->v_mount && (vp->v_mount->mnt_flag & MNT_SYNCHRONOUS))) ioflag |= IO_SYNC; mp = NULL; if (vp->v_type != VCHR && vp->v_type != VBLK && (error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, p, cred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if ((flags & FOF_OFFSET) == 0) uio->uio_offset = fp->f_offset; ioflag |= sequential_heuristic(uio, fp); error = VOP_WRITE(vp, uio, ioflag, cred); if ((flags & FOF_OFFSET) == 0) fp->f_offset = uio->uio_offset; fp->f_nextoff = uio->uio_offset; VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return (error); } /* * File table vnode stat routine. */ static int vn_statfile(fp, sb, p) struct file *fp; struct stat *sb; struct proc *p; { struct vnode *vp = (struct vnode *)fp->f_data; return vn_stat(vp, sb, p); } int vn_stat(vp, sb, p) struct vnode *vp; register struct stat *sb; struct proc *p; { struct vattr vattr; register struct vattr *vap; int error; u_short mode; vap = &vattr; error = VOP_GETATTR(vp, vap, p->p_ucred, p); if (error) return (error); /* * Zero the spare stat fields */ sb->st_lspare = 0; sb->st_qspare[0] = 0; sb->st_qspare[1] = 0; /* * Copy from vattr table */ if (vap->va_fsid != VNOVAL) sb->st_dev = vap->va_fsid; else sb->st_dev = vp->v_mount->mnt_stat.f_fsid.val[0]; sb->st_ino = vap->va_fileid; mode = vap->va_mode; switch (vap->va_type) { case VREG: mode |= S_IFREG; break; case VDIR: mode |= S_IFDIR; break; case VBLK: mode |= S_IFBLK; break; case VCHR: mode |= S_IFCHR; break; case VLNK: mode |= S_IFLNK; /* This is a cosmetic change, symlinks do not have a mode. */ if (vp->v_mount->mnt_flag & MNT_NOSYMFOLLOW) sb->st_mode &= ~ACCESSPERMS; /* 0000 */ else sb->st_mode |= ACCESSPERMS; /* 0777 */ break; case VSOCK: mode |= S_IFSOCK; break; case VFIFO: mode |= S_IFIFO; break; default: return (EBADF); }; sb->st_mode = mode; sb->st_nlink = vap->va_nlink; sb->st_uid = vap->va_uid; sb->st_gid = vap->va_gid; sb->st_rdev = vap->va_rdev; sb->st_size = vap->va_size; sb->st_atimespec = vap->va_atime; sb->st_mtimespec = vap->va_mtime; sb->st_ctimespec = vap->va_ctime; /* * According to www.opengroup.org, the meaning of st_blksize is * "a filesystem-specific preferred I/O block size for this * object. In some filesystem types, this may vary from file * to file" * Default to zero to catch bogus uses of this field. */ if (vap->va_type == VREG) { sb->st_blksize = vap->va_blocksize; } else if (vn_isdisk(vp, NULL)) { sb->st_blksize = vp->v_rdev->si_bsize_best; if (sb->st_blksize < vp->v_rdev->si_bsize_phys) sb->st_blksize = vp->v_rdev->si_bsize_phys; if (sb->st_blksize < BLKDEV_IOSIZE) sb->st_blksize = BLKDEV_IOSIZE; } else { sb->st_blksize = 0; } sb->st_flags = vap->va_flags; if (suser_xxx(p->p_ucred, 0, 0)) sb->st_gen = 0; else sb->st_gen = vap->va_gen; #if (S_BLKSIZE == 512) /* Optimize this case */ sb->st_blocks = vap->va_bytes >> 9; #else sb->st_blocks = vap->va_bytes / S_BLKSIZE; #endif return (0); } /* * File table vnode ioctl routine. */ static int vn_ioctl(fp, com, data, p) struct file *fp; u_long com; caddr_t data; struct proc *p; { register struct vnode *vp = ((struct vnode *)fp->f_data); struct vattr vattr; int error; switch (vp->v_type) { case VREG: case VDIR: if (com == FIONREAD) { error = VOP_GETATTR(vp, &vattr, p->p_ucred, p); if (error) return (error); *(int *)data = vattr.va_size - fp->f_offset; return (0); } if (com == FIONBIO || com == FIOASYNC) /* XXX */ return (0); /* XXX */ /* fall into ... */ default: #if 0 return (ENOTTY); #endif case VFIFO: case VCHR: case VBLK: if (com == FIODTYPE) { if (vp->v_type != VCHR && vp->v_type != VBLK) return (ENOTTY); *(int *)data = devsw(vp->v_rdev)->d_flags & D_TYPEMASK; return (0); } error = VOP_IOCTL(vp, com, data, fp->f_flag, p->p_ucred, p); if (error == 0 && com == TIOCSCTTY) { /* Do nothing if reassigning same control tty */ if (p->p_session->s_ttyvp == vp) return (0); /* Get rid of reference to old control tty */ if (p->p_session->s_ttyvp) vrele(p->p_session->s_ttyvp); p->p_session->s_ttyvp = vp; VREF(vp); } return (error); } } /* * File table vnode poll routine. */ static int vn_poll(fp, events, cred, p) struct file *fp; int events; struct ucred *cred; struct proc *p; { return (VOP_POLL(((struct vnode *)fp->f_data), events, cred, p)); } /* * Check that the vnode is still valid, and if so * acquire requested lock. */ int #ifndef DEBUG_LOCKS vn_lock(vp, flags, p) #else debug_vn_lock(vp, flags, p, filename, line) #endif struct vnode *vp; int flags; struct proc *p; #ifdef DEBUG_LOCKS const char *filename; int line; #endif { int error; do { if ((flags & LK_INTERLOCK) == 0) simple_lock(&vp->v_interlock); if (vp->v_flag & VXLOCK) { vp->v_flag |= VXWANT; simple_unlock(&vp->v_interlock); tsleep((caddr_t)vp, PINOD, "vn_lock", 0); error = ENOENT; } else { #ifdef DEBUG_LOCKS vp->filename = filename; vp->line = line; #endif error = VOP_LOCK(vp, flags | LK_NOPAUSE | LK_INTERLOCK, p); if (error == 0) return (error); } flags &= ~LK_INTERLOCK; } while (flags & LK_RETRY); return (error); } /* * File table vnode close routine. */ static int vn_closefile(fp, p) struct file *fp; struct proc *p; { fp->f_ops = &badfileops; return (vn_close(((struct vnode *)fp->f_data), fp->f_flag, fp->f_cred, p)); } /* * Preparing to start a filesystem write operation. If the operation is * permitted, then we bump the count of operations in progress and * proceed. If a suspend request is in progress, we wait until the * suspension is over, and then proceed. */ int vn_start_write(vp, mpp, flags) struct vnode *vp; struct mount **mpp; int flags; { struct mount *mp; int error; /* * If a vnode is provided, get and return the mount point that * to which it will write. */ if (vp != NULL) { if ((error = VOP_GETWRITEMOUNT(vp, mpp)) != 0) { *mpp = NULL; if (error != EOPNOTSUPP) return (error); return (0); } } if ((mp = *mpp) == NULL) return (0); /* * Check on status of suspension. */ while ((mp->mnt_kern_flag & MNTK_SUSPEND) != 0) { if (flags & V_NOWAIT) return (EWOULDBLOCK); error = tsleep(&mp->mnt_flag, (PUSER - 1) | (flags & PCATCH), "suspfs", 0); if (error) return (error); } if (flags & V_XSLEEP) return (0); mp->mnt_writeopcount++; return (0); } /* * Secondary suspension. Used by operations such as vop_inactive * routines that are needed by the higher level functions. These * are allowed to proceed until all the higher level functions have * completed (indicated by mnt_writeopcount dropping to zero). At that * time, these operations are halted until the suspension is over. */ int vn_write_suspend_wait(vp, mp, flags) struct vnode *vp; struct mount *mp; int flags; { int error; if (vp != NULL) { if ((error = VOP_GETWRITEMOUNT(vp, &mp)) != 0) { if (error != EOPNOTSUPP) return (error); return (0); } } /* * If we are not suspended or have not yet reached suspended * mode, then let the operation proceed. */ if (mp == NULL || (mp->mnt_kern_flag & MNTK_SUSPENDED) == 0) return (0); if (flags & V_NOWAIT) return (EWOULDBLOCK); /* * Wait for the suspension to finish. */ return (tsleep(&mp->mnt_flag, (PUSER - 1) | (flags & PCATCH), "suspfs", 0)); } /* * Filesystem write operation has completed. If we are suspending and this * operation is the last one, notify the suspender that the suspension is * now in effect. */ void vn_finished_write(mp) struct mount *mp; { if (mp == NULL) return; mp->mnt_writeopcount--; if (mp->mnt_writeopcount < 0) panic("vn_finished_write: neg cnt"); if ((mp->mnt_kern_flag & MNTK_SUSPEND) != 0 && mp->mnt_writeopcount <= 0) wakeup(&mp->mnt_writeopcount); } /* * Request a filesystem to suspend write operations. */ void vfs_write_suspend(mp) struct mount *mp; { struct proc *p = curproc; if (mp->mnt_kern_flag & MNTK_SUSPEND) return; mp->mnt_kern_flag |= MNTK_SUSPEND; if (mp->mnt_writeopcount > 0) (void) tsleep(&mp->mnt_writeopcount, PUSER - 1, "suspwt", 0); VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p); mp->mnt_kern_flag |= MNTK_SUSPENDED; } /* * Request a filesystem to resume write operations. */ void vfs_write_resume(mp) struct mount *mp; { if ((mp->mnt_kern_flag & MNTK_SUSPEND) == 0) return; mp->mnt_kern_flag &= ~(MNTK_SUSPEND | MNTK_SUSPENDED); wakeup(&mp->mnt_writeopcount); wakeup(&mp->mnt_flag); } static int filt_vnattach(struct knote *kn) { struct vnode *vp; if (kn->kn_fp->f_type != DTYPE_VNODE && kn->kn_fp->f_type != DTYPE_FIFO) return (EBADF); vp = (struct vnode *)kn->kn_fp->f_data; /* * XXX * this is a hack simply to cause the filter attach to fail * for non-ufs filesystems, until the support for them is done. */ if ((vp)->v_tag != VT_UFS) return (EOPNOTSUPP); simple_lock(&vp->v_pollinfo.vpi_lock); SLIST_INSERT_HEAD(&vp->v_pollinfo.vpi_selinfo.si_note, kn, kn_selnext); simple_unlock(&vp->v_pollinfo.vpi_lock); return (0); } static void filt_vndetach(struct knote *kn) { struct vnode *vp = (struct vnode *)kn->kn_fp->f_data; simple_lock(&vp->v_pollinfo.vpi_lock); SLIST_REMOVE(&vp->v_pollinfo.vpi_selinfo.si_note, kn, knote, kn_selnext); simple_unlock(&vp->v_pollinfo.vpi_lock); } static int filt_vnode(struct knote *kn, long hint) { if (kn->kn_sfflags & hint) kn->kn_fflags |= hint; return (kn->kn_fflags != 0); } static int filt_nullattach(struct knote *kn) { return (ENXIO); } /*ARGSUSED*/ static int filt_vnread(struct knote *kn, long hint) { struct vnode *vp = (struct vnode *)kn->kn_fp->f_data; struct inode *ip = VTOI(vp); kn->kn_data = ip->i_size - kn->kn_fp->f_offset; return (kn->kn_data != 0); } + +/* + * Simplified in-kernel wrapper calls for extended attribute access. + * Both calls pass in a NULL credential, authorizing as "kernel" access. + * Set IO_NODELOCKED in ioflg if the vnode is already locked. + */ +int +vn_extattr_get(struct vnode *vp, int ioflg, const char *attrname, int *buflen, + char *buf, struct proc *p) +{ + struct uio auio; + struct iovec iov; + int error; + + iov.iov_len = *buflen; + iov.iov_base = buf; + + auio.uio_iov = &iov; + auio.uio_iovcnt = 1; + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_procp = p; + auio.uio_offset = 0; + auio.uio_resid = *buflen; + + if ((ioflg & IO_NODELOCKED) == 0) + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + /* authorize attribute retrieval as kernel */ + error = VOP_GETEXTATTR(vp, attrname, &auio, NULL, p); + + if ((ioflg & IO_NODELOCKED) == 0) + VOP_UNLOCK(vp, 0, p); + + if (error == 0) { + *buflen = *buflen - auio.uio_resid; + } + + return (error); +} + +/* + * XXX failure mode if partially written? + */ +int +vn_extattr_set(struct vnode *vp, int ioflg, const char *attrname, int buflen, + char *buf, struct proc *p) +{ + struct uio auio; + struct iovec iov; + int error; + + iov.iov_len = buflen; + iov.iov_base = buf; + + auio.uio_iov = &iov; + auio.uio_iovcnt = 1; + auio.uio_rw = UIO_WRITE; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_procp = p; + auio.uio_offset = 0; + auio.uio_resid = buflen; + + if ((ioflg & IO_NODELOCKED) == 0) + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + /* authorize attribute setting as kernel */ + error = VOP_SETEXTATTR(vp, attrname, &auio, NULL, p); + + if ((ioflg & IO_NODELOCKED) == 0) + VOP_UNLOCK(vp, 0, p); + + return (error); +} diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 819681c2f1d5..ccd845e78d8c 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -1,637 +1,641 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vnode.h 8.7 (Berkeley) 2/4/94 * $FreeBSD$ */ #ifndef _SYS_VNODE_H_ #define _SYS_VNODE_H_ #include #include #include #include #include /* * The vnode is the focus of all file activity in UNIX. There is a * unique vnode allocated for each active file, each current directory, * each mounted-on file, text file, and the root. */ /* * Vnode types. VNON means no type. */ enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD }; /* * Vnode tag types. * These are for the benefit of external programs only (e.g., pstat) * and should NEVER be inspected by the kernel. */ enum vtagtype { VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_PC, VT_LFS, VT_LOFS, VT_FDESC, VT_PORTAL, VT_NULL, VT_UMAP, VT_KERNFS, VT_PROCFS, VT_AFS, VT_ISOFS, VT_UNION, VT_MSDOSFS, VT_DEVFS, VT_TFS, VT_VFS, VT_CODA, VT_NTFS, VT_HPFS, VT_NWFS }; /* * Each underlying filesystem allocates its own private area and hangs * it from v_data. If non-null, this area is freed in getnewvnode(). */ TAILQ_HEAD(buflists, buf); typedef int vop_t __P((void *)); struct namecache; /* * Reading or writing any of these items requires holding the appropriate lock. * v_freelist is locked by the global vnode_free_list simple lock. * v_mntvnodes is locked by the global mntvnodes simple lock. * v_flag, v_usecount, v_holdcount and v_writecount are * locked by the v_interlock simple lock. * v_pollinfo is locked by the lock contained inside it. */ struct vnode { u_long v_flag; /* vnode flags (see below) */ int v_usecount; /* reference count of users */ int v_writecount; /* reference count of writers */ int v_holdcnt; /* page & buffer references */ u_long v_id; /* capability identifier */ struct mount *v_mount; /* ptr to vfs we are in */ vop_t **v_op; /* vnode operations vector */ TAILQ_ENTRY(vnode) v_freelist; /* vnode freelist */ LIST_ENTRY(vnode) v_mntvnodes; /* vnodes for mount point */ struct buflists v_cleanblkhd; /* clean blocklist head */ struct buflists v_dirtyblkhd; /* dirty blocklist head */ LIST_ENTRY(vnode) v_synclist; /* vnodes with dirty buffers */ long v_numoutput; /* num of writes in progress */ enum vtype v_type; /* vnode type */ union { struct mount *vu_mountedhere;/* ptr to mounted vfs (VDIR) */ struct socket *vu_socket; /* unix ipc (VSOCK) */ struct { struct specinfo *vu_specinfo; /* device (VCHR, VBLK) */ SLIST_ENTRY(vnode) vu_specnext; } vu_spec; struct fifoinfo *vu_fifoinfo; /* fifo (VFIFO) */ } v_un; struct nqlease *v_lease; /* Soft reference to lease */ daddr_t v_lastw; /* last write (write cluster) */ daddr_t v_cstart; /* start block of cluster */ daddr_t v_lasta; /* last allocation */ int v_clen; /* length of current cluster */ struct vm_object *v_object; /* Place to store VM object */ struct simplelock v_interlock; /* lock on usecount and flag */ struct lock *v_vnlock; /* used for non-locking fs's */ enum vtagtype v_tag; /* type of underlying data */ void *v_data; /* private data for fs */ LIST_HEAD(, namecache) v_cache_src; /* Cache entries from us */ TAILQ_HEAD(, namecache) v_cache_dst; /* Cache entries to us */ struct vnode *v_dd; /* .. vnode */ u_long v_ddid; /* .. capability identifier */ struct { struct simplelock vpi_lock; /* lock to protect below */ struct selinfo vpi_selinfo; /* identity of poller(s) */ short vpi_events; /* what they are looking for */ short vpi_revents; /* what has happened */ } v_pollinfo; #ifdef DEBUG_LOCKS const char *filename; /* Source file doing locking */ int line; /* Line number doing locking */ #endif }; #define v_mountedhere v_un.vu_mountedhere #define v_socket v_un.vu_socket #define v_rdev v_un.vu_spec.vu_specinfo #define v_specnext v_un.vu_spec.vu_specnext #define v_fifoinfo v_un.vu_fifoinfo #define VN_POLLEVENT(vp, events) \ do { \ if ((vp)->v_pollinfo.vpi_events & (events)) \ vn_pollevent((vp), (events)); \ } while (0) /* * Vnode flags. */ #define VROOT 0x00001 /* root of its file system */ #define VTEXT 0x00002 /* vnode is a pure text prototype */ #define VSYSTEM 0x00004 /* vnode being used by kernel */ #define VISTTY 0x00008 /* vnode represents a tty */ #define VXLOCK 0x00100 /* vnode is locked to change underlying type */ #define VXWANT 0x00200 /* process is waiting for vnode */ #define VBWAIT 0x00400 /* waiting for output to complete */ /* open for business 0x00800 */ /* open for business 0x01000 */ #define VOBJBUF 0x02000 /* Allocate buffers in VM object */ #define VCOPYONWRITE 0x04000 /* vnode is doing copy-on-write */ #define VAGE 0x08000 /* Insert vnode at head of free list */ #define VOLOCK 0x10000 /* vnode is locked waiting for an object */ #define VOWANT 0x20000 /* a process is waiting for VOLOCK */ #define VDOOMED 0x40000 /* This vnode is being recycled */ #define VFREE 0x80000 /* This vnode is on the freelist */ /* open for business 0x100000 */ #define VONWORKLST 0x200000 /* On syncer work-list */ #define VMOUNT 0x400000 /* Mount in progress */ /* * Vnode attributes. A field value of VNOVAL represents a field whose value * is unavailable (getattr) or which is not to be changed (setattr). */ struct vattr { enum vtype va_type; /* vnode type (for create) */ u_short va_mode; /* files access mode and type */ short va_nlink; /* number of references to file */ uid_t va_uid; /* owner user id */ gid_t va_gid; /* owner group id */ udev_t va_fsid; /* file system id */ long va_fileid; /* file id */ u_quad_t va_size; /* file size in bytes */ long va_blocksize; /* blocksize preferred for i/o */ struct timespec va_atime; /* time of last access */ struct timespec va_mtime; /* time of last modification */ struct timespec va_ctime; /* time file changed */ u_long va_gen; /* generation number of file */ u_long va_flags; /* flags defined for file */ udev_t va_rdev; /* device the special file represents */ u_quad_t va_bytes; /* bytes of disk space held by file */ u_quad_t va_filerev; /* file modification number */ u_int va_vaflags; /* operations flags, see below */ long va_spare; /* remain quad aligned */ }; /* * Flags for va_vaflags. */ #define VA_UTIMES_NULL 0x01 /* utimes argument was NULL */ #define VA_EXCLUSIVE 0x02 /* exclusive create request */ /* * Flags for ioflag. (high 16 bits used to ask for read-ahead and * help with write clustering) */ #define IO_UNIT 0x01 /* do I/O as atomic unit */ #define IO_APPEND 0x02 /* append write to end */ #define IO_SYNC 0x04 /* do I/O synchronously */ #define IO_NODELOCKED 0x08 /* underlying node already locked */ #define IO_NDELAY 0x10 /* FNDELAY flag set in file table */ #define IO_VMIO 0x20 /* data already in VMIO space */ #define IO_INVAL 0x40 /* invalidate after I/O */ /* * Modes. Some values same as Ixxx entries from inode.h for now. */ #define VSUID 04000 /* set user id on execution */ #define VSGID 02000 /* set group id on execution */ #define VSVTX 01000 /* save swapped text even after use */ #define VREAD 00400 /* read, write, execute permissions */ #define VWRITE 00200 #define VEXEC 00100 /* * Token indicating no attribute value yet assigned. */ #define VNOVAL (-1) #ifdef _KERNEL #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_VNODE); #endif /* * Convert between vnode types and inode formats (since POSIX.1 * defines mode word of stat structure in terms of inode formats). */ extern enum vtype iftovt_tab[]; extern int vttoif_tab[]; #define IFTOVT(mode) (iftovt_tab[((mode) & S_IFMT) >> 12]) #define VTTOIF(indx) (vttoif_tab[(int)(indx)]) #define MAKEIMODE(indx, mode) (int)(VTTOIF(indx) | (mode)) /* * Flags to various vnode functions. */ #define SKIPSYSTEM 0x0001 /* vflush: skip vnodes marked VSYSTEM */ #define FORCECLOSE 0x0002 /* vflush: force file closure */ #define WRITECLOSE 0x0004 /* vflush: only close writable files */ #define DOCLOSE 0x0008 /* vclean: close active files */ #define V_SAVE 0x0001 /* vinvalbuf: sync file first */ #define REVOKEALL 0x0001 /* vop_revoke: revoke all aliases */ #define V_WAIT 0x0001 /* vn_start_write: sleep for suspend */ #define V_NOWAIT 0x0002 /* vn_start_write: don't sleep for suspend */ #define V_XSLEEP 0x0004 /* vn_start_write: just return after sleep */ #define VREF(vp) vref(vp) #ifdef DIAGNOSTIC #define VATTR_NULL(vap) vattr_null(vap) #else #define VATTR_NULL(vap) (*(vap) = va_null) /* initialize a vattr */ #endif /* DIAGNOSTIC */ #define NULLVP ((struct vnode *)NULL) #define VNODEOP_SET(f) \ C_SYSINIT(f##init, SI_SUB_VFS, SI_ORDER_SECOND, vfs_add_vnodeops, &f); \ C_SYSUNINIT(f##uninit, SI_SUB_VFS, SI_ORDER_SECOND, vfs_rm_vnodeops, &f); /* * Global vnode data. */ extern struct vnode *rootvnode; /* root (i.e. "/") vnode */ extern int desiredvnodes; /* number of vnodes desired */ extern time_t syncdelay; /* max time to delay syncing data */ extern time_t filedelay; /* time to delay syncing files */ extern time_t dirdelay; /* time to delay syncing directories */ extern time_t metadelay; /* time to delay syncing metadata */ extern struct vm_zone *namei_zone; extern int prtactive; /* nonzero to call vprint() */ extern struct vattr va_null; /* predefined null vattr structure */ extern int vfs_ioopt; /* * Macro/function to check for client cache inconsistency w.r.t. leasing. */ #define LEASE_READ 0x1 /* Check lease for readers */ #define LEASE_WRITE 0x2 /* Check lease for modifiers */ extern void (*lease_updatetime) __P((int deltat)); #define VSHOULDFREE(vp) \ (!((vp)->v_flag & (VFREE|VDOOMED)) && \ !(vp)->v_holdcnt && !(vp)->v_usecount && \ (!(vp)->v_object || \ !((vp)->v_object->ref_count || (vp)->v_object->resident_page_count))) #define VSHOULDBUSY(vp) \ (((vp)->v_flag & VFREE) && \ ((vp)->v_holdcnt || (vp)->v_usecount)) #endif /* _KERNEL */ /* * Mods for extensibility. */ /* * Flags for vdesc_flags: */ #define VDESC_MAX_VPS 16 /* Low order 16 flag bits are reserved for willrele flags for vp arguments. */ #define VDESC_VP0_WILLRELE 0x0001 #define VDESC_VP1_WILLRELE 0x0002 #define VDESC_VP2_WILLRELE 0x0004 #define VDESC_VP3_WILLRELE 0x0008 #define VDESC_NOMAP_VPP 0x0100 #define VDESC_VPP_WILLRELE 0x0200 /* * VDESC_NO_OFFSET is used to identify the end of the offset list * and in places where no such field exists. */ #define VDESC_NO_OFFSET -1 /* * This structure describes the vnode operation taking place. */ struct vnodeop_desc { int vdesc_offset; /* offset in vector--first for speed */ char *vdesc_name; /* a readable name for debugging */ int vdesc_flags; /* VDESC_* flags */ /* * These ops are used by bypass routines to map and locate arguments. * Creds and procs are not needed in bypass routines, but sometimes * they are useful to (for example) transport layers. * Nameidata is useful because it has a cred in it. */ int *vdesc_vp_offsets; /* list ended by VDESC_NO_OFFSET */ int vdesc_vpp_offset; /* return vpp location */ int vdesc_cred_offset; /* cred location, if any */ int vdesc_proc_offset; /* proc location, if any */ int vdesc_componentname_offset; /* if any */ /* * Finally, we've got a list of private data (about each operation) * for each transport layer. (Support to manage this list is not * yet part of BSD.) */ caddr_t *vdesc_transports; }; #ifdef _KERNEL /* * A list of all the operation descs. */ extern struct vnodeop_desc *vnodeop_descs[]; /* * Interlock for scanning list of vnodes attached to a mountpoint */ extern struct simplelock mntvnode_slock; /* * This macro is very helpful in defining those offsets in the vdesc struct. * * This is stolen from X11R4. I ignored all the fancy stuff for * Crays, so if you decide to port this to such a serious machine, * you might want to consult Intrinsic.h's XtOffset{,Of,To}. */ #define VOPARG_OFFSET(p_type,field) \ ((int) (((char *) (&(((p_type)NULL)->field))) - ((char *) NULL))) #define VOPARG_OFFSETOF(s_type,field) \ VOPARG_OFFSET(s_type*,field) #define VOPARG_OFFSETTO(S_TYPE,S_OFFSET,STRUCT_P) \ ((S_TYPE)(((char*)(STRUCT_P))+(S_OFFSET))) /* * This structure is used to configure the new vnodeops vector. */ struct vnodeopv_entry_desc { struct vnodeop_desc *opve_op; /* which operation this is */ vop_t *opve_impl; /* code implementing this operation */ }; struct vnodeopv_desc { /* ptr to the ptr to the vector where op should go */ vop_t ***opv_desc_vector_p; struct vnodeopv_entry_desc *opv_desc_ops; /* null terminated list */ }; /* * A generic structure. * This can be used by bypass routines to identify generic arguments. */ struct vop_generic_args { struct vnodeop_desc *a_desc; /* other random data follows, presumably */ }; #ifdef DEBUG_VFS_LOCKS /* * Macros to aid in tracing VFS locking problems. Not totally * reliable since if the process sleeps between changing the lock * state and checking it with the assert, some other process could * change the state. They are good enough for debugging a single * filesystem using a single-threaded test. I find that 'cvs co src' * is a pretty good test. */ /* * [dfr] Kludge until I get around to fixing all the vfs locking. */ #define IS_LOCKING_VFS(vp) ((vp)->v_tag == VT_UFS \ || (vp)->v_tag == VT_MFS \ || (vp)->v_tag == VT_NFS \ || (vp)->v_tag == VT_LFS \ || (vp)->v_tag == VT_ISOFS \ || (vp)->v_tag == VT_MSDOSFS \ || (vp)->v_tag == VT_DEVFS) #define ASSERT_VOP_LOCKED(vp, str) \ do { \ struct vnode *_vp = (vp); \ \ if (_vp && IS_LOCKING_VFS(_vp) && !VOP_ISLOCKED(_vp, NULL)) \ panic("%s: %p is not locked but should be", str, _vp); \ } while (0) #define ASSERT_VOP_UNLOCKED(vp, str) \ do { \ struct vnode *_vp = (vp); \ int lockstate; \ \ if (_vp && IS_LOCKING_VFS(_vp)) { \ lockstate = VOP_ISLOCKED(_vp, curproc); \ if (lockstate == LK_EXCLUSIVE) \ panic("%s: %p is locked but should not be", \ str, _vp); \ } \ } while (0) #define ASSERT_VOP_ELOCKED(vp, str) \ do { \ struct vnode *_vp = (vp); \ \ if (_vp && IS_LOCKING_VFS(_vp) && \ VOP_ISLOCKED(_vp, curproc) != LK_EXCLUSIVE) \ panic("%s: %p is not exclusive locked but should be", \ str, _vp); \ } while (0) #define ASSERT_VOP_ELOCKED_OTHER(vp, str) \ do { \ struct vnode *_vp = (vp); \ \ if (_vp && IS_LOCKING_VFS(_vp) && \ VOP_ISLOCKED(_vp, curproc) != LK_EXCLOTHER) \ panic("%s: %p is not exclusive locked by another proc", \ str, _vp); \ } while (0) #define ASSERT_VOP_SLOCKED(vp, str) \ do { \ struct vnode *_vp = (vp); \ \ if (_vp && IS_LOCKING_VFS(_vp) && \ VOP_ISLOCKED(_vp, NULL) != LK_SHARED) \ panic("%s: %p is not locked shared but should be", \ str, _vp); \ } while (0) #else #define ASSERT_VOP_LOCKED(vp, str) #define ASSERT_VOP_UNLOCKED(vp, str) #endif /* * VOCALL calls an op given an ops vector. We break it out because BSD's * vclean changes the ops vector and then wants to call ops with the old * vector. */ #define VOCALL(OPSV,OFF,AP) (( *((OPSV)[(OFF)])) (AP)) /* * This call works for vnodes in the kernel. */ #define VCALL(VP,OFF,AP) VOCALL((VP)->v_op,(OFF),(AP)) #define VDESC(OP) (& __CONCAT(OP,_desc)) #define VOFFSET(OP) (VDESC(OP)->vdesc_offset) /* * VMIO support inline */ extern int vmiodirenable; static __inline int vn_canvmio(struct vnode *vp) { if (vp && (vp->v_type == VREG || (vmiodirenable && vp->v_type == VDIR))) return(TRUE); return(FALSE); } /* * Finally, include the default set of vnode operations. */ #include "vnode_if.h" /* * Public vnode manipulation functions. */ struct componentname; struct file; struct mount; struct nameidata; struct ostat; struct proc; struct stat; struct nstat; struct ucred; struct uio; struct vattr; struct vnode; struct vop_bwrite_args; extern int (*lease_check_hook) __P((struct vop_lease_args *)); void addalias __P((struct vnode *vp, dev_t nvp_rdev)); struct vnode *addaliasu __P((struct vnode *vp, udev_t nvp_rdev)); int bdevvp __P((dev_t dev, struct vnode **vpp)); /* cache_* may belong in namei.h. */ void cache_enter __P((struct vnode *dvp, struct vnode *vp, struct componentname *cnp)); int cache_lookup __P((struct vnode *dvp, struct vnode **vpp, struct componentname *cnp)); void cache_purge __P((struct vnode *vp)); void cache_purgevfs __P((struct mount *mp)); void cvtstat __P((struct stat *st, struct ostat *ost)); void cvtnstat __P((struct stat *sb, struct nstat *nsb)); int getnewvnode __P((enum vtagtype tag, struct mount *mp, vop_t **vops, struct vnode **vpp)); int lease_check __P((struct vop_lease_args *ap)); int spec_vnoperate __P((struct vop_generic_args *)); int speedup_syncer __P((void)); int textvp_fullpath __P((struct proc *p, char **retbuf, char **retfreebuf)); void vattr_null __P((struct vattr *vap)); int vcount __P((struct vnode *vp)); void vdrop __P((struct vnode *)); int vfinddev __P((dev_t dev, enum vtype type, struct vnode **vpp)); void vfs_add_vnodeops __P((const void *)); void vfs_rm_vnodeops __P((const void *)); int vflush __P((struct mount *mp, struct vnode *skipvp, int flags)); int vget __P((struct vnode *vp, int lockflag, struct proc *p)); void vgone __P((struct vnode *vp)); void vgonel __P((struct vnode *vp, struct proc *p)); void vhold __P((struct vnode *)); int vinvalbuf __P((struct vnode *vp, int save, struct ucred *cred, struct proc *p, int slpflag, int slptimeo)); int vtruncbuf __P((struct vnode *vp, struct ucred *cred, struct proc *p, off_t length, int blksize)); void vprint __P((char *label, struct vnode *vp)); int vrecycle __P((struct vnode *vp, struct simplelock *inter_lkp, struct proc *p)); int vn_close __P((struct vnode *vp, int flags, struct ucred *cred, struct proc *p)); void vn_finished_write __P((struct mount *mp)); int vn_isdisk __P((struct vnode *vp, int *errp)); int vn_lock __P((struct vnode *vp, int flags, struct proc *p)); #ifdef DEBUG_LOCKS int debug_vn_lock __P((struct vnode *vp, int flags, struct proc *p, const char *filename, int line)); #define vn_lock(vp,flags,p) debug_vn_lock(vp,flags,p,__FILE__,__LINE__) #endif int vn_open __P((struct nameidata *ndp, int *flagp, int cmode)); void vn_pollevent __P((struct vnode *vp, int events)); void vn_pollgone __P((struct vnode *vp)); int vn_pollrecord __P((struct vnode *vp, struct proc *p, int events)); int vn_rdwr __P((enum uio_rw rw, struct vnode *vp, caddr_t base, int len, off_t offset, enum uio_seg segflg, int ioflg, struct ucred *cred, int *aresid, struct proc *p)); int vn_stat __P((struct vnode *vp, struct stat *sb, struct proc *p)); int vn_start_write __P((struct vnode *vp, struct mount **mpp, int flags)); dev_t vn_todev __P((struct vnode *vp)); int vn_write_suspend_wait __P((struct vnode *vp, struct mount *mp, int flags)); int vn_writechk __P((struct vnode *vp)); +int vn_extattr_get __P((struct vnode *vp, int ioflg, const char *attrname, + int *buflen, char *buf, struct proc *p)); +int vn_extattr_set __P((struct vnode *vp, int ioflg, const char *attrname, + int buflen, char *buf, struct proc *p)); int vfs_cache_lookup __P((struct vop_lookup_args *ap)); int vfs_object_create __P((struct vnode *vp, struct proc *p, struct ucred *cred)); void vfs_timestamp __P((struct timespec *)); void vfs_write_resume __P((struct mount *mp)); void vfs_write_suspend __P((struct mount *mp)); int vop_stdbwrite __P((struct vop_bwrite_args *ap)); int vop_stdgetwritemount __P((struct vop_getwritemount_args *)); int vop_stdislocked __P((struct vop_islocked_args *)); int vop_stdlock __P((struct vop_lock_args *)); int vop_stdunlock __P((struct vop_unlock_args *)); int vop_noislocked __P((struct vop_islocked_args *)); int vop_nolock __P((struct vop_lock_args *)); int vop_nopoll __P((struct vop_poll_args *)); int vop_nounlock __P((struct vop_unlock_args *)); int vop_stdpathconf __P((struct vop_pathconf_args *)); int vop_stdpoll __P((struct vop_poll_args *)); int vop_revoke __P((struct vop_revoke_args *)); int vop_sharedlock __P((struct vop_lock_args *)); int vop_eopnotsupp __P((struct vop_generic_args *ap)); int vop_ebadf __P((struct vop_generic_args *ap)); int vop_einval __P((struct vop_generic_args *ap)); int vop_enotty __P((struct vop_generic_args *ap)); int vop_defaultop __P((struct vop_generic_args *ap)); int vop_null __P((struct vop_generic_args *ap)); int vop_panic __P((struct vop_generic_args *ap)); void vfree __P((struct vnode *)); void vput __P((struct vnode *vp)); void vrele __P((struct vnode *vp)); void vref __P((struct vnode *vp)); void vbusy __P((struct vnode *vp)); extern vop_t **default_vnodeop_p; extern vop_t **spec_vnodeop_p; #endif /* _KERNEL */ #endif /* !_SYS_VNODE_H_ */