diff --git a/sys/fs/cd9660/cd9660_vnops.c b/sys/fs/cd9660/cd9660_vnops.c index 6a14c87db904..58d52188dc85 100644 --- a/sys/fs/cd9660/cd9660_vnops.c +++ b/sys/fs/cd9660/cd9660_vnops.c @@ -1,1187 +1,1183 @@ /*- * Copyright (c) 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension * Support code is derived from software contributed to Berkeley * by Atsushi Murai (amurai@spec.co.jp). * * 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. * * @(#)cd9660_vnops.c 8.19 (Berkeley) 5/27/95 - * $Id: cd9660_vnops.c,v 1.36 1997/08/25 10:26:18 kato Exp $ + * $Id: cd9660_vnops.c,v 1.37 1997/08/26 07:32:32 phk Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int cd9660_setattr __P((struct vop_setattr_args *)); static int cd9660_open __P((struct vop_open_args *)); static int cd9660_close __P((struct vop_close_args *)); static int cd9660_access __P((struct vop_access_args *)); static int cd9660_getattr __P((struct vop_getattr_args *)); static int cd9660_read __P((struct vop_read_args *)); static int cd9660_ioctl __P((struct vop_ioctl_args *)); -static int cd9660_select __P((struct vop_select_args *)); static int cd9660_mmap __P((struct vop_mmap_args *)); static int cd9660_seek __P((struct vop_seek_args *)); struct isoreaddir; static int iso_uiodir __P((struct isoreaddir *idp, struct dirent *dp, off_t off)); static int iso_shipdir __P((struct isoreaddir *idp)); static int cd9660_readdir __P((struct vop_readdir_args *)); static int cd9660_readlink __P((struct vop_readlink_args *ap)); static int cd9660_abortop __P((struct vop_abortop_args *)); static int cd9660_lock __P((struct vop_lock_args *)); static int cd9660_unlock __P((struct vop_unlock_args *)); static int cd9660_strategy __P((struct vop_strategy_args *)); static int cd9660_print __P((struct vop_print_args *)); static int cd9660_islocked __P((struct vop_islocked_args *)); /* * Sysctl values for the cd9660 filesystem. */ #define CD9660_CLUSTERREAD 1 /* cluster reading enabled */ #define CD9660_MAXID 2 /* number of valid cd9660 ids */ #define CD9660_NAMES { \ {0, 0}, \ { "doclusterread", CTLTYPE_INT}, \ } /* * Setattr call. Only allowed for block and character special devices. */ int cd9660_setattr(ap) struct vop_setattr_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct vattr *vap = ap->a_vap; if (vap->va_flags != (u_long)VNOVAL || vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) return (EROFS); if (vap->va_size != (u_quad_t)VNOVAL) { switch (vp->v_type) { case VDIR: return (EISDIR); case VLNK: case VREG: return (EROFS); case VCHR: case VBLK: case VSOCK: case VFIFO: return (0); } } return (0); } /* * Open called. * * Nothing to do. */ /* ARGSUSED */ static int cd9660_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (0); } /* * Close called * * Update the times on the inode on writeable file systems. */ /* ARGSUSED */ static int cd9660_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (0); } /* * Check mode permission on inode pointer. Mode is READ, WRITE or EXEC. * The mode is shifted to select the owner/group/other fields. The * super user is granted all permissions. */ /* ARGSUSED */ static int cd9660_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct iso_node *ip = VTOI(vp); struct ucred *cred = ap->a_cred; mode_t mask, mode = ap->a_mode; gid_t *gp; int i; /* * Disallow write attempts unless the file is a socket, * fifo, or a block or character device resident on the * file system. */ if (mode & VWRITE) { switch (vp->v_type) { case VDIR: case VLNK: case VREG: return (EROFS); } } /* User id 0 always gets access. */ if (cred->cr_uid == 0) return (0); mask = 0; /* Otherwise, check the owner. */ if (cred->cr_uid == ip->inode.iso_uid) { if (mode & VEXEC) mask |= S_IXUSR; if (mode & VREAD) mask |= S_IRUSR; if (mode & VWRITE) mask |= S_IWUSR; return ((ip->inode.iso_mode & mask) == mask ? 0 : EACCES); } /* Otherwise, check the groups. */ for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) if (ip->inode.iso_gid == *gp) { if (mode & VEXEC) mask |= S_IXGRP; if (mode & VREAD) mask |= S_IRGRP; if (mode & VWRITE) mask |= S_IWGRP; return ((ip->inode.iso_mode & mask) == mask ? 0 : EACCES); } /* Otherwise, check everyone else. */ if (mode & VEXEC) mask |= S_IXOTH; if (mode & VREAD) mask |= S_IROTH; if (mode & VWRITE) mask |= S_IWOTH; return ((ip->inode.iso_mode & mask) == mask ? 0 : EACCES); } static int cd9660_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; register struct vattr *vap = ap->a_vap; register struct iso_node *ip = VTOI(vp); vap->va_fsid = ip->i_dev; vap->va_fileid = ip->i_number; vap->va_mode = ip->inode.iso_mode; vap->va_nlink = ip->inode.iso_links; vap->va_uid = ip->inode.iso_uid; vap->va_gid = ip->inode.iso_gid; vap->va_atime = ip->inode.iso_atime; vap->va_mtime = ip->inode.iso_mtime; vap->va_ctime = ip->inode.iso_ctime; vap->va_rdev = ip->inode.iso_rdev; vap->va_size = (u_quad_t) ip->i_size; if (ip->i_size == 0 && (vap->va_mode & S_IFMT) == S_IFLNK) { struct vop_readlink_args rdlnk; struct iovec aiov; struct uio auio; char *cp; MALLOC(cp, char *, MAXPATHLEN, M_TEMP, M_WAITOK); aiov.iov_base = cp; aiov.iov_len = MAXPATHLEN; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_SYSSPACE; auio.uio_procp = ap->a_p; auio.uio_resid = MAXPATHLEN; rdlnk.a_uio = &auio; rdlnk.a_vp = ap->a_vp; rdlnk.a_cred = ap->a_cred; if (cd9660_readlink(&rdlnk) == 0) vap->va_size = MAXPATHLEN - auio.uio_resid; FREE(cp, M_TEMP); } vap->va_flags = 0; vap->va_gen = 1; vap->va_blocksize = ip->i_mnt->logical_block_size; vap->va_bytes = (u_quad_t) ip->i_size; vap->va_type = vp->v_type; vap->va_filerev = 0; return (0); } static int cd9660_doclusterread = 1; SYSCTL_NODE(_vfs, MOUNT_CD9660, cd9660, CTLFLAG_RW, 0, "CD9660 filesystem"); SYSCTL_INT(_vfs_cd9660, CD9660_CLUSTERREAD, doclusterread, CTLFLAG_RW, &cd9660_doclusterread, 0, ""); /* * Vnode op for reading. */ static int cd9660_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { struct vnode *vp = ap->a_vp; register struct uio *uio = ap->a_uio; register struct iso_node *ip = VTOI(vp); register struct iso_mnt *imp; struct buf *bp; daddr_t lbn, rablock; off_t diff; int rasize, error = 0; long size, n, on; if (uio->uio_resid == 0) return (0); if (uio->uio_offset < 0) return (EINVAL); ip->i_flag |= IN_ACCESS; imp = ip->i_mnt; do { lbn = lblkno(imp, uio->uio_offset); on = blkoff(imp, uio->uio_offset); n = min((u_int)(imp->logical_block_size - on), uio->uio_resid); diff = (off_t)ip->i_size - uio->uio_offset; if (diff <= 0) return (0); if (diff < n) n = diff; size = blksize(imp, ip, lbn); rablock = lbn + 1; if (cd9660_doclusterread) { if (lblktosize(imp, rablock) <= ip->i_size) error = cluster_read(vp, (off_t)ip->i_size, lbn, size, NOCRED, uio->uio_resid, (ap->a_ioflag >> 16), &bp); else error = bread(vp, lbn, size, NOCRED, &bp); } else { if (vp->v_lastr + 1 == lbn && lblktosize(imp, rablock) < ip->i_size) { rasize = blksize(imp, ip, rablock); error = breadn(vp, lbn, size, &rablock, &rasize, 1, NOCRED, &bp); } else error = bread(vp, lbn, size, NOCRED, &bp); } vp->v_lastr = lbn; n = min(n, size - bp->b_resid); if (error) { brelse(bp); return (error); } error = uiomove(bp->b_data + on, (int)n, uio); brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); return (error); } /* ARGSUSED */ static int cd9660_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; u_long a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { printf("You did ioctl for isofs !!\n"); return (ENOTTY); } -/* ARGSUSED */ -static int -cd9660_select(ap) - struct vop_select_args /* { - struct vnode *a_vp; - int a_which; - int a_fflags; - struct ucred *a_cred; - struct proc *a_p; - } */ *ap; -{ - - /* - * We should really check to see if I/O is possible. - */ - return (1); -} - /* * Mmap a file * * NB Currently unsupported. */ /* ARGSUSED */ static int cd9660_mmap(ap) struct vop_mmap_args /* { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (EINVAL); } /* * Seek on a file * * Nothing to do, so just return. */ /* ARGSUSED */ static int cd9660_seek(ap) struct vop_seek_args /* { struct vnode *a_vp; off_t a_oldoff; off_t a_newoff; struct ucred *a_cred; } */ *ap; { return (0); } /* * Structure for reading directories */ struct isoreaddir { struct dirent saveent; struct dirent assocent; struct dirent current; off_t saveoff; off_t assocoff; off_t curroff; struct uio *uio; off_t uio_off; int eofflag; u_long *cookies; int ncookies; }; int iso_uiodir(idp,dp,off) struct isoreaddir *idp; struct dirent *dp; off_t off; { int error; dp->d_name[dp->d_namlen] = 0; dp->d_reclen = GENERIC_DIRSIZ(dp); if (idp->uio->uio_resid < dp->d_reclen) { idp->eofflag = 0; return (-1); } if (idp->cookies) { if (idp->ncookies <= 0) { idp->eofflag = 0; return (-1); } *idp->cookies++ = off; --idp->ncookies; } if (error = uiomove((caddr_t) dp,dp->d_reclen,idp->uio)) return (error); idp->uio_off = off; return (0); } int iso_shipdir(idp) struct isoreaddir *idp; { struct dirent *dp; int cl, sl, assoc; int error; char *cname, *sname; cl = idp->current.d_namlen; cname = idp->current.d_name; assoc = (cl > 1) && (*cname == ASSOCCHAR); if (assoc) { cl--; cname++; } dp = &idp->saveent; sname = dp->d_name; if (!(sl = dp->d_namlen)) { dp = &idp->assocent; sname = dp->d_name + 1; sl = dp->d_namlen - 1; } if (sl > 0) { if (sl != cl || bcmp(sname,cname,sl)) { if (idp->assocent.d_namlen) { if (error = iso_uiodir(idp,&idp->assocent,idp->assocoff)) return (error); idp->assocent.d_namlen = 0; } if (idp->saveent.d_namlen) { if (error = iso_uiodir(idp,&idp->saveent,idp->saveoff)) return (error); idp->saveent.d_namlen = 0; } } } idp->current.d_reclen = GENERIC_DIRSIZ(&idp->current); if (assoc) { idp->assocoff = idp->curroff; bcopy(&idp->current,&idp->assocent,idp->current.d_reclen); } else { idp->saveoff = idp->curroff; bcopy(&idp->current,&idp->saveent,idp->current.d_reclen); } return (0); } /* * Vnode op for readdir */ static int cd9660_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; int *a_ncookies; u_long *a_cookies; } */ *ap; { register struct uio *uio = ap->a_uio; struct isoreaddir *idp; struct vnode *vdp = ap->a_vp; struct iso_node *dp; struct iso_mnt *imp; struct buf *bp = NULL; struct iso_directory_record *ep; int entryoffsetinblock; doff_t endsearch; u_long bmask; int error = 0; int reclen; u_short namelen; int ncookies = 0; u_long *cookies = NULL; dp = VTOI(vdp); imp = dp->i_mnt; bmask = imp->im_bmask; MALLOC(idp, struct isoreaddir *, sizeof(*idp), M_TEMP, M_WAITOK); idp->saveent.d_namlen = idp->assocent.d_namlen = 0; /* * XXX * Is it worth trying to figure out the type? */ idp->saveent.d_type = idp->assocent.d_type = idp->current.d_type = DT_UNKNOWN; idp->uio = uio; if (ap->a_ncookies == NULL) { idp->cookies = NULL; } else { /* * Guess the number of cookies needed. */ ncookies = uio->uio_resid / 16; MALLOC(cookies, u_long *, ncookies * sizeof(u_int), M_TEMP, M_WAITOK); idp->cookies = cookies; idp->ncookies = ncookies; } idp->eofflag = 1; idp->curroff = uio->uio_offset; if ((entryoffsetinblock = idp->curroff & bmask) && (error = VOP_BLKATOFF(vdp, (off_t)idp->curroff, NULL, &bp))) { FREE(idp, M_TEMP); return (error); } endsearch = dp->i_size; while (idp->curroff < endsearch) { /* * If offset is on a block boundary, * read the next directory block. * Release previous if it exists. */ if ((idp->curroff & bmask) == 0) { if (bp != NULL) brelse(bp); if (error = VOP_BLKATOFF(vdp, (off_t)idp->curroff, NULL, &bp)) break; entryoffsetinblock = 0; } /* * Get pointer to next entry. */ ep = (struct iso_directory_record *) ((char *)bp->b_data + entryoffsetinblock); reclen = isonum_711(ep->length); if (reclen == 0) { /* skip to next block, if any */ idp->curroff = (idp->curroff & ~bmask) + imp->logical_block_size; continue; } if (reclen < ISO_DIRECTORY_RECORD_SIZE) { error = EINVAL; /* illegal entry, stop */ break; } if (entryoffsetinblock + reclen > imp->logical_block_size) { error = EINVAL; /* illegal directory, so stop looking */ break; } idp->current.d_namlen = isonum_711(ep->name_len); if (reclen < ISO_DIRECTORY_RECORD_SIZE + idp->current.d_namlen) { error = EINVAL; /* illegal entry, stop */ break; } if (isonum_711(ep->flags)&2) idp->current.d_fileno = isodirino(ep, imp); else idp->current.d_fileno = dbtob(bp->b_blkno) + entryoffsetinblock; idp->curroff += reclen; switch (imp->iso_ftype) { case ISO_FTYPE_RRIP: cd9660_rrip_getname(ep,idp->current.d_name, &namelen, &idp->current.d_fileno,imp); idp->current.d_namlen = (u_char)namelen; if (idp->current.d_namlen) error = iso_uiodir(idp,&idp->current,idp->curroff); break; default: /* ISO_FTYPE_DEFAULT || ISO_FTYPE_9660 || ISO_FTYPE_HIGH_SIERRA*/ strcpy(idp->current.d_name,".."); switch (ep->name[0]) { case 0: idp->current.d_namlen = 1; error = iso_uiodir(idp,&idp->current,idp->curroff); break; case 1: idp->current.d_namlen = 2; error = iso_uiodir(idp,&idp->current,idp->curroff); break; default: isofntrans(ep->name,idp->current.d_namlen, idp->current.d_name, &namelen, imp->iso_ftype == ISO_FTYPE_9660, isonum_711(ep->flags)&4); idp->current.d_namlen = (u_char)namelen; if (imp->iso_ftype == ISO_FTYPE_DEFAULT) error = iso_shipdir(idp); else error = iso_uiodir(idp,&idp->current,idp->curroff); break; } } if (error) break; entryoffsetinblock += reclen; } if (!error && imp->iso_ftype == ISO_FTYPE_DEFAULT) { idp->current.d_namlen = 0; error = iso_shipdir(idp); } if (error < 0) error = 0; if (ap->a_ncookies != NULL) { if (error) free(cookies, M_TEMP); else { /* * Work out the number of cookies actually used. */ *ap->a_ncookies = ncookies - idp->ncookies; *ap->a_cookies = cookies; } } if (bp) brelse (bp); uio->uio_offset = idp->uio_off; *ap->a_eofflag = idp->eofflag; FREE(idp, M_TEMP); return (error); } /* * Return target name of a symbolic link * Shouldn't we get the parent vnode and read the data from there? * This could eventually result in deadlocks in cd9660_lookup. * But otherwise the block read here is in the block buffer two times. */ typedef struct iso_directory_record ISODIR; typedef struct iso_node ISONODE; typedef struct iso_mnt ISOMNT; static int cd9660_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { ISONODE *ip; ISODIR *dirp; ISOMNT *imp; struct buf *bp; struct uio *uio; u_short symlen; int error; char *symname; ip = VTOI(ap->a_vp); imp = ip->i_mnt; uio = ap->a_uio; if (imp->iso_ftype != ISO_FTYPE_RRIP) return (EINVAL); /* * Get parents directory record block that this inode included. */ error = bread(imp->im_devvp, (ip->i_number >> imp->im_bshift) << (imp->im_bshift - DEV_BSHIFT), imp->logical_block_size, NOCRED, &bp); if (error) { brelse(bp); return (EINVAL); } /* * Setup the directory pointer for this inode */ dirp = (ISODIR *)(bp->b_data + (ip->i_number & imp->im_bmask)); /* * Just make sure, we have a right one.... * 1: Check not cross boundary on block */ if ((ip->i_number & imp->im_bmask) + isonum_711(dirp->length) > (unsigned)imp->logical_block_size) { brelse(bp); return (EINVAL); } /* * Now get a buffer * Abuse a namei buffer for now. */ if (uio->uio_segflg == UIO_SYSSPACE) symname = uio->uio_iov->iov_base; else MALLOC(symname, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); /* * Ok, we just gathering a symbolic name in SL record. */ if (cd9660_rrip_getsymname(dirp, symname, &symlen, imp) == 0) { if (uio->uio_segflg != UIO_SYSSPACE) FREE(symname, M_NAMEI); brelse(bp); return (EINVAL); } /* * Don't forget before you leave from home ;-) */ brelse(bp); /* * return with the symbolic name to caller's. */ if (uio->uio_segflg != UIO_SYSSPACE) { error = uiomove(symname, symlen, uio); FREE(symname, M_NAMEI); return (error); } uio->uio_resid -= symlen; uio->uio_iov->iov_base += symlen; uio->uio_iov->iov_len -= symlen; return (0); } /* * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually * done. If a buffer has been saved in anticipation of a CREATE, delete it. */ static int cd9660_abortop(ap) struct vop_abortop_args /* { struct vnode *a_dvp; struct componentname *a_cnp; } */ *ap; { if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); return (0); } /* * Lock an inode. */ static int cd9660_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags, &vp->v_interlock, ap->a_p)); } /* * Unlock an inode. */ static int cd9660_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock, ap->a_p)); } /* * Calculate the logical to physical mapping if not done already, * then call the device strategy routine. */ static int cd9660_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *ap; { register struct buf *bp = ap->a_bp; register struct vnode *vp = bp->b_vp; register struct iso_node *ip; int error; ip = VTOI(vp); if (vp->v_type == VBLK || vp->v_type == VCHR) panic("cd9660_strategy: spec"); if (bp->b_blkno == bp->b_lblkno) { if ((error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL, NULL))) { bp->b_error = error; bp->b_flags |= B_ERROR; biodone(bp); return (error); } if ((long)bp->b_blkno == -1) clrbuf(bp); } if ((long)bp->b_blkno == -1) { biodone(bp); return (0); } vp = ip->i_devvp; bp->b_dev = vp->v_rdev; VOCALL (vp->v_op, VOFFSET(vop_strategy), ap); return (0); } /* * Print out the contents of an inode. */ static int cd9660_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_ISOFS, isofs vnode\n"); return (0); } /* * Check for a locked inode. */ int cd9660_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { return (lockstatus(&VTOI(ap->a_vp)->i_lock)); } /* * Return POSIX pathconf information applicable to cd9660 filesystems. */ int cd9660_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; register_t *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = 1; return (0); case _PC_NAME_MAX: if (VTOI(ap->a_vp)->i_mnt->iso_ftype == ISO_FTYPE_RRIP) *ap->a_retval = NAME_MAX; else *ap->a_retval = 37; return (0); case _PC_PATH_MAX: *ap->a_retval = PATH_MAX; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_NO_TRUNC: *ap->a_retval = 1; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Global vfs data structures for isofs */ #define cd9660_create \ ((int (*) __P((struct vop_create_args *)))eopnotsupp) #define cd9660_mknod ((int (*) __P((struct vop_mknod_args *)))eopnotsupp) #define cd9660_write ((int (*) __P((struct vop_write_args *)))eopnotsupp) #ifdef NFS #define cd9660_lease_check lease_check #else #define cd9660_lease_check ((int (*) __P((struct vop_lease_args *)))nullop) #endif +#define cd9660_poll vop_nopoll #define cd9660_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define cd9660_remove \ ((int (*) __P((struct vop_remove_args *)))eopnotsupp) #define cd9660_link ((int (*) __P((struct vop_link_args *)))eopnotsupp) #define cd9660_rename \ ((int (*) __P((struct vop_rename_args *)))eopnotsupp) #define cd9660_mkdir ((int (*) __P((struct vop_mkdir_args *)))eopnotsupp) #define cd9660_rmdir ((int (*) __P((struct vop_rmdir_args *)))eopnotsupp) #define cd9660_symlink \ ((int (*) __P((struct vop_symlink_args *)))eopnotsupp) #define cd9660_advlock \ ((int (*) __P((struct vop_advlock_args *)))eopnotsupp) #define cd9660_valloc ((int(*) __P(( \ struct vnode *pvp, \ int mode, \ struct ucred *cred, \ struct vnode **vpp))) eopnotsupp) #define cd9660_vfree ((int (*) __P((struct vop_vfree_args *)))eopnotsupp) #define cd9660_truncate \ ((int (*) __P((struct vop_truncate_args *)))eopnotsupp) #define cd9660_update \ ((int (*) __P((struct vop_update_args *)))eopnotsupp) #define cd9660_bwrite \ ((int (*) __P((struct vop_bwrite_args *)))eopnotsupp) /* * Global vfs data structures for cd9660 */ vop_t **cd9660_vnodeop_p; struct vnodeopv_entry_desc cd9660_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)vfs_cache_lookup }, /* lookup */ { &vop_cachedlookup_desc, (vop_t *)cd9660_lookup }, /* lookup */ { &vop_create_desc, (vop_t *)cd9660_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)cd9660_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)cd9660_open }, /* open */ { &vop_close_desc, (vop_t *)cd9660_close }, /* close */ { &vop_access_desc, (vop_t *)cd9660_access }, /* access */ { &vop_getattr_desc, (vop_t *)cd9660_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)cd9660_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)cd9660_read }, /* read */ { &vop_write_desc, (vop_t *)cd9660_write }, /* write */ { &vop_lease_desc, (vop_t *)cd9660_lease_check },/* lease */ { &vop_ioctl_desc, (vop_t *)cd9660_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)cd9660_select }, /* select */ + { &vop_poll_desc, (vop_t *)cd9660_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)cd9660_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)cd9660_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)cd9660_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)cd9660_seek }, /* seek */ { &vop_remove_desc, (vop_t *)cd9660_remove }, /* remove */ { &vop_link_desc, (vop_t *)cd9660_link }, /* link */ { &vop_rename_desc, (vop_t *)cd9660_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)cd9660_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)cd9660_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)cd9660_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)cd9660_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)cd9660_readlink },/* readlink */ { &vop_abortop_desc, (vop_t *)cd9660_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)cd9660_inactive },/* inactive */ { &vop_reclaim_desc, (vop_t *)cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)cd9660_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)cd9660_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)cd9660_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)cd9660_strategy },/* strategy */ { &vop_print_desc, (vop_t *)cd9660_print }, /* print */ { &vop_islocked_desc, (vop_t *)cd9660_islocked },/* islocked */ { &vop_pathconf_desc, (vop_t *)cd9660_pathconf },/* pathconf */ { &vop_advlock_desc, (vop_t *)cd9660_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)cd9660_blkatoff },/* blkatoff */ { &vop_valloc_desc, (vop_t *)cd9660_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)cd9660_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)cd9660_truncate },/* truncate */ { &vop_update_desc, (vop_t *)cd9660_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, { NULL, NULL } }; static struct vnodeopv_desc cd9660_vnodeop_opv_desc = { &cd9660_vnodeop_p, cd9660_vnodeop_entries }; VNODEOP_SET(cd9660_vnodeop_opv_desc); /* * Special device vnode ops */ vop_t **cd9660_specop_p; struct vnodeopv_entry_desc cd9660_specop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)spec_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)spec_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)spec_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)spec_open }, /* open */ { &vop_close_desc, (vop_t *)spec_close }, /* close */ { &vop_access_desc, (vop_t *)cd9660_access }, /* access */ { &vop_getattr_desc, (vop_t *)cd9660_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)cd9660_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)spec_read }, /* read */ { &vop_write_desc, (vop_t *)spec_write }, /* write */ { &vop_lease_desc, (vop_t *)spec_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)spec_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)spec_select }, /* select */ + { &vop_poll_desc, (vop_t *)spec_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)spec_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)spec_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)spec_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)spec_seek }, /* seek */ { &vop_remove_desc, (vop_t *)spec_remove }, /* remove */ { &vop_link_desc, (vop_t *)spec_link }, /* link */ { &vop_rename_desc, (vop_t *)spec_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)spec_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)spec_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)spec_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)spec_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)spec_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)cd9660_inactive },/* inactive */ { &vop_reclaim_desc, (vop_t *)cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)cd9660_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)cd9660_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)spec_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)spec_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)cd9660_print }, /* print */ { &vop_islocked_desc, (vop_t *)cd9660_islocked },/* islocked */ { &vop_pathconf_desc, (vop_t *)spec_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)spec_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)spec_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)spec_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)spec_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)cd9660_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, { NULL, NULL } }; static struct vnodeopv_desc cd9660_specop_opv_desc = { &cd9660_specop_p, cd9660_specop_entries }; VNODEOP_SET(cd9660_specop_opv_desc); vop_t **cd9660_fifoop_p; struct vnodeopv_entry_desc cd9660_fifoop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)fifo_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)fifo_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)fifo_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)fifo_open }, /* open */ { &vop_close_desc, (vop_t *)fifo_close }, /* close */ { &vop_access_desc, (vop_t *)cd9660_access }, /* access */ { &vop_getattr_desc, (vop_t *)cd9660_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)cd9660_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)fifo_read }, /* read */ { &vop_write_desc, (vop_t *)fifo_write }, /* write */ { &vop_lease_desc, (vop_t *)fifo_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)fifo_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)fifo_select }, /* select */ + { &vop_poll_desc, (vop_t *)fifo_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)fifo_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)fifo_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)fifo_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)fifo_seek }, /* seek */ { &vop_remove_desc, (vop_t *)fifo_remove }, /* remove */ { &vop_link_desc, (vop_t *)fifo_link } , /* link */ { &vop_rename_desc, (vop_t *)fifo_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)fifo_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)fifo_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)fifo_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)fifo_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)cd9660_inactive },/* inactive */ { &vop_reclaim_desc, (vop_t *)cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)cd9660_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)cd9660_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)fifo_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)fifo_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)cd9660_print }, /* print */ { &vop_islocked_desc, (vop_t *)cd9660_islocked },/* islocked */ { &vop_pathconf_desc, (vop_t *)fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)fifo_valloc }, /* valloc */ +/* XXX: vop_reallocpages */ { &vop_vfree_desc, (vop_t *)fifo_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)fifo_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)cd9660_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, { NULL, NULL } }; static struct vnodeopv_desc cd9660_fifoop_opv_desc = { &cd9660_fifoop_p, cd9660_fifoop_entries }; VNODEOP_SET(cd9660_fifoop_opv_desc); diff --git a/sys/fs/deadfs/dead_vnops.c b/sys/fs/deadfs/dead_vnops.c index b167b6fe3fb0..a8856f9b7711 100644 --- a/sys/fs/deadfs/dead_vnops.c +++ b/sys/fs/deadfs/dead_vnops.c @@ -1,381 +1,370 @@ /* * 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. * * @(#)dead_vnops.c 8.1 (Berkeley) 6/10/93 - * $Id: dead_vnops.c,v 1.13 1997/02/22 09:40:13 peter Exp $ + * $Id: dead_vnops.c,v 1.14 1997/09/02 20:06:08 bde Exp $ */ #include #include #include #include #include static int chkvnlock __P((struct vnode *)); /* * Prototypes for dead operations on vnodes. */ static int dead_badop __P((void)); static int dead_ebadf __P((void)); static int dead_lookup __P((struct vop_lookup_args *)); #define dead_create ((int (*) __P((struct vop_create_args *)))dead_badop) #define dead_mknod ((int (*) __P((struct vop_mknod_args *)))dead_badop) static int dead_open __P((struct vop_open_args *)); #define dead_close ((int (*) __P((struct vop_close_args *)))nullop) #define dead_access ((int (*) __P((struct vop_access_args *)))dead_ebadf) #define dead_getattr ((int (*) __P((struct vop_getattr_args *)))dead_ebadf) #define dead_setattr ((int (*) __P((struct vop_setattr_args *)))dead_ebadf) static int dead_read __P((struct vop_read_args *)); static int dead_write __P((struct vop_write_args *)); static int dead_ioctl __P((struct vop_ioctl_args *)); -static int dead_select __P((struct vop_select_args *)); +#define dead_poll vop_nopoll #define dead_mmap ((int (*) __P((struct vop_mmap_args *)))dead_badop) #define dead_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define dead_seek ((int (*) __P((struct vop_seek_args *)))nullop) #define dead_remove ((int (*) __P((struct vop_remove_args *)))dead_badop) #define dead_link ((int (*) __P((struct vop_link_args *)))dead_badop) #define dead_rename ((int (*) __P((struct vop_rename_args *)))dead_badop) #define dead_mkdir ((int (*) __P((struct vop_mkdir_args *)))dead_badop) #define dead_rmdir ((int (*) __P((struct vop_rmdir_args *)))dead_badop) #define dead_symlink ((int (*) __P((struct vop_symlink_args *)))dead_badop) #define dead_readdir ((int (*) __P((struct vop_readdir_args *)))dead_ebadf) #define dead_readlink ((int (*) __P((struct vop_readlink_args *)))dead_ebadf) #define dead_abortop ((int (*) __P((struct vop_abortop_args *)))dead_badop) #define dead_inactive ((int (*) __P((struct vop_inactive_args *)))nullop) #define dead_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop) static int dead_lock __P((struct vop_lock_args *)); #define dead_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) static int dead_bmap __P((struct vop_bmap_args *)); static int dead_strategy __P((struct vop_strategy_args *)); static int dead_print __P((struct vop_print_args *)); #define dead_islocked ((int(*) __P((struct vop_islocked_args *)))vop_noislocked) #define dead_pathconf ((int (*) __P((struct vop_pathconf_args *)))dead_ebadf) #define dead_advlock ((int (*) __P((struct vop_advlock_args *)))dead_ebadf) #define dead_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))dead_badop) #define dead_valloc ((int (*) __P((struct vop_valloc_args *)))dead_badop) #define dead_vfree ((int (*) __P((struct vop_vfree_args *)))dead_badop) #define dead_truncate ((int (*) __P((struct vop_truncate_args *)))nullop) #define dead_update ((int (*) __P((struct vop_update_args *)))nullop) #define dead_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop) vop_t **dead_vnodeop_p; static struct vnodeopv_entry_desc dead_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)dead_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)dead_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)dead_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)dead_open }, /* open */ { &vop_close_desc, (vop_t *)dead_close }, /* close */ { &vop_access_desc, (vop_t *)dead_access }, /* access */ { &vop_getattr_desc, (vop_t *)dead_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)dead_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)dead_read }, /* read */ { &vop_write_desc, (vop_t *)dead_write }, /* write */ +/* XXX: vop_lease */ { &vop_ioctl_desc, (vop_t *)dead_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)dead_select }, /* select */ + { &vop_poll_desc, (vop_t *)dead_poll }, /* poll */ +/* XXX: vop_revoke */ { &vop_mmap_desc, (vop_t *)dead_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)dead_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)dead_seek }, /* seek */ { &vop_remove_desc, (vop_t *)dead_remove }, /* remove */ { &vop_link_desc, (vop_t *)dead_link }, /* link */ { &vop_rename_desc, (vop_t *)dead_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)dead_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)dead_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)dead_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)dead_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)dead_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)dead_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)dead_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)dead_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)dead_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)dead_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)dead_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)dead_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)dead_print }, /* print */ { &vop_islocked_desc, (vop_t *)dead_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)dead_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)dead_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)dead_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)dead_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)dead_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)dead_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)dead_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)dead_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc dead_vnodeop_opv_desc = { &dead_vnodeop_p, dead_vnodeop_entries }; VNODEOP_SET(dead_vnodeop_opv_desc); /* * Trivial lookup routine that always fails. */ /* ARGSUSED */ static int dead_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { *ap->a_vpp = NULL; return (ENOTDIR); } /* * Open always fails as if device did not exist. */ /* ARGSUSED */ static int dead_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (ENXIO); } /* * Vnode op for read */ /* ARGSUSED */ static int dead_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { if (chkvnlock(ap->a_vp)) panic("dead_read: lock"); #if 0 /* Lite2 behaviour */ /* * Return EOF for tty devices, EIO for others */ if ((ap->a_vp->v_flag & VISTTY) == 0) return (EIO); #else /* * Return EOF for character devices, EIO for others */ if (ap->a_vp->v_type != VCHR) return (EIO); #endif return (0); } /* * Vnode op for write */ /* ARGSUSED */ static int dead_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { if (chkvnlock(ap->a_vp)) panic("dead_write: lock"); return (EIO); } /* * Device ioctl operation. */ /* ARGSUSED */ static int dead_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { if (!chkvnlock(ap->a_vp)) return (EBADF); return (VCALL(ap->a_vp, VOFFSET(vop_ioctl), ap)); } -/* ARGSUSED */ -static int -dead_select(ap) - struct vop_select_args /* { - struct vnode *a_vp; - int a_which; - int a_fflags; - struct ucred *a_cred; - struct proc *a_p; - } */ *ap; -{ - - /* - * Let the user find out that the descriptor is gone. - */ - return (1); -} - /* * Just call the device strategy routine */ static int dead_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *ap; { if (ap->a_bp->b_vp == NULL || !chkvnlock(ap->a_bp->b_vp)) { ap->a_bp->b_flags |= B_ERROR; biodone(ap->a_bp); return (EIO); } return (VOP_STRATEGY(ap->a_bp)); } /* * Wait until the vnode has finished changing state. */ static int dead_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; /* * Since we are not using the lock manager, we must clear * the interlock here. */ if (ap->a_flags & LK_INTERLOCK) { simple_unlock(&vp->v_interlock); ap->a_flags &= ~LK_INTERLOCK; } if (!chkvnlock(vp)) return (0); return (VCALL(vp, VOFFSET(vop_lock), ap)); } /* * Wait until the vnode has finished changing state. */ static int dead_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ *ap; { if (!chkvnlock(ap->a_vp)) return (EIO); return (VOP_BMAP(ap->a_vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp, ap->a_runb)); } /* * Print out the contents of a dead vnode. */ /* ARGSUSED */ static int dead_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_NON, dead vnode\n"); return (0); } /* * Empty vnode failed operation */ static int dead_ebadf() { return (EBADF); } /* * Empty vnode bad operation */ static int dead_badop() { panic("dead_badop called"); /* NOTREACHED */ } /* * We have to wait during times when the vnode is * in a state of change. */ int chkvnlock(vp) register struct vnode *vp; { int locked = 0; while (vp->v_flag & VXLOCK) { vp->v_flag |= VXWANT; (void) tsleep((caddr_t)vp, PINOD, "ckvnlk", 0); locked = 1; } return (locked); } diff --git a/sys/fs/fdescfs/fdesc_vnops.c b/sys/fs/fdescfs/fdesc_vnops.c index cb6490614c74..f63bd1b67dac 100644 --- a/sys/fs/fdescfs/fdesc_vnops.c +++ b/sys/fs/fdescfs/fdesc_vnops.c @@ -1,993 +1,998 @@ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. * * 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. * * @(#)fdesc_vnops.c 8.9 (Berkeley) 1/21/94 * - * $Id: fdesc_vnops.c,v 1.24 1997/09/02 20:06:09 bde Exp $ + * $Id: fdesc_vnops.c,v 1.25 1997/09/07 05:25:53 bde Exp $ */ /* * /dev/fd Filesystem */ #include #include #include #include /* boottime */ #include #include #include #include #include #include #include #include #include #include #include #include extern struct cdevsw ctty_cdevsw; #define cttyvp(p) ((p)->p_flag & P_CONTROLT ? (p)->p_session->s_ttyvp : NULL) #define FDL_WANT 0x01 #define FDL_LOCKED 0x02 static int fdcache_lock; static vop_t **fdesc_vnodeop_p; dev_t devctty; #if (FD_STDIN != FD_STDOUT-1) || (FD_STDOUT != FD_STDERR-1) FD_STDIN, FD_STDOUT, FD_STDERR must be a sequence n, n+1, n+2 #endif #define NFDCACHE 4 #define FD_NHASH(ix) \ (&fdhashtbl[(ix) & fdhash]) LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl; u_long fdhash; static int fdesc_attr __P((int fd, struct vattr *vap, struct ucred *cred, struct proc *p)); static int fdesc_badop __P((void)); static int fdesc_getattr __P((struct vop_getattr_args *ap)); static struct fdcache * fdesc_hash __P((int ix)); static int fdesc_inactive __P((struct vop_inactive_args *ap)); static int fdesc_ioctl __P((struct vop_ioctl_args *ap)); static int fdesc_lookup __P((struct vop_lookup_args *ap)); static int fdesc_open __P((struct vop_open_args *ap)); static int fdesc_pathconf __P((struct vop_pathconf_args *ap)); static int fdesc_print __P((struct vop_print_args *ap)); static int fdesc_read __P((struct vop_read_args *ap)); static int fdesc_readdir __P((struct vop_readdir_args *ap)); static int fdesc_readlink __P((struct vop_readlink_args *ap)); static int fdesc_reclaim __P((struct vop_reclaim_args *ap)); -static int fdesc_select __P((struct vop_select_args *ap)); +static int fdesc_poll __P((struct vop_poll_args *ap)); static int fdesc_setattr __P((struct vop_setattr_args *ap)); static int fdesc_vfree __P((struct vop_vfree_args *ap)); static int fdesc_write __P((struct vop_write_args *ap)); /* * Initialise cache headers */ int fdesc_init(vfsp) struct vfsconf *vfsp; { devctty = makedev(nchrdev, 0); fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash); return (0); } int fdesc_allocvp(ftype, ix, mp, vpp) fdntype ftype; int ix; struct mount *mp; struct vnode **vpp; { struct proc *p = curproc; /* XXX */ struct fdhashhead *fc; struct fdescnode *fd; int error = 0; fc = FD_NHASH(ix); loop: for (fd = fc->lh_first; fd != 0; fd = fd->fd_hash.le_next) { if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) { if (vget(fd->fd_vnode, 0, p)) goto loop; *vpp = fd->fd_vnode; return (error); } } /* * otherwise lock the array while we call getnewvnode * since that can block. */ if (fdcache_lock & FDL_LOCKED) { fdcache_lock |= FDL_WANT; (void) tsleep((caddr_t) &fdcache_lock, PINOD, "fdalvp", 0); goto loop; } fdcache_lock |= FDL_LOCKED; /* * Do the MALLOC before the getnewvnode since doing so afterward * might cause a bogus v_data pointer to get dereferenced * elsewhere if MALLOC should block. */ MALLOC(fd, struct fdescnode *, sizeof(struct fdescnode), M_TEMP, M_WAITOK); error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp); if (error) { FREE(fd, M_TEMP); goto out; } (*vpp)->v_data = fd; fd->fd_vnode = *vpp; fd->fd_type = ftype; fd->fd_fd = -1; fd->fd_link = 0; fd->fd_ix = ix; LIST_INSERT_HEAD(fc, fd, fd_hash); out:; fdcache_lock &= ~FDL_LOCKED; if (fdcache_lock & FDL_WANT) { fdcache_lock &= ~FDL_WANT; wakeup((caddr_t) &fdcache_lock); } return (error); } /* * vp is the current namei directory * ndp is the name to locate in that directory... */ static int fdesc_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { struct vnode **vpp = ap->a_vpp; struct vnode *dvp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; char *pname = cnp->cn_nameptr; struct proc *p = cnp->cn_proc; int nfiles = p->p_fd->fd_nfiles; unsigned fd; int error; struct vnode *fvp; char *ln; if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) { error = EROFS; goto bad; } VOP_UNLOCK(dvp, 0, p); if (cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); vn_lock(dvp, LK_SHARED | LK_RETRY, p); return (0); } switch (VTOFDESC(dvp)->fd_type) { default: case Flink: case Fdesc: case Fctty: error = ENOTDIR; goto bad; case Froot: if (cnp->cn_namelen == 2 && bcmp(pname, "fd", 2) == 0) { error = fdesc_allocvp(Fdevfd, FD_DEVFD, dvp->v_mount, &fvp); if (error) goto bad; *vpp = fvp; fvp->v_type = VDIR; vn_lock(fvp, LK_SHARED | LK_RETRY, p); return (0); } if (cnp->cn_namelen == 3 && bcmp(pname, "tty", 3) == 0) { struct vnode *ttyvp = cttyvp(p); if (ttyvp == NULL) { error = ENXIO; goto bad; } error = fdesc_allocvp(Fctty, FD_CTTY, dvp->v_mount, &fvp); if (error) goto bad; *vpp = fvp; fvp->v_type = VFIFO; vn_lock(fvp, LK_SHARED | LK_RETRY, p); return (0); } ln = 0; switch (cnp->cn_namelen) { case 5: if (bcmp(pname, "stdin", 5) == 0) { ln = "fd/0"; fd = FD_STDIN; } break; case 6: if (bcmp(pname, "stdout", 6) == 0) { ln = "fd/1"; fd = FD_STDOUT; } else if (bcmp(pname, "stderr", 6) == 0) { ln = "fd/2"; fd = FD_STDERR; } break; } if (ln) { error = fdesc_allocvp(Flink, fd, dvp->v_mount, &fvp); if (error) goto bad; VTOFDESC(fvp)->fd_link = ln; *vpp = fvp; fvp->v_type = VLNK; vn_lock(fvp, LK_SHARED | LK_RETRY, p); return (0); } else { error = ENOENT; goto bad; } /* FALL THROUGH */ case Fdevfd: if (cnp->cn_namelen == 2 && bcmp(pname, "..", 2) == 0) { if (error = fdesc_root(dvp->v_mount, vpp)) goto bad; return (0); } fd = 0; while (*pname >= '0' && *pname <= '9') { fd = 10 * fd + *pname++ - '0'; if (fd >= nfiles) break; } if (*pname != '\0') { error = ENOENT; goto bad; } if (fd >= nfiles || p->p_fd->fd_ofiles[fd] == NULL) { error = EBADF; goto bad; } error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp); if (error) goto bad; VTOFDESC(fvp)->fd_fd = fd; vn_lock(fvp, LK_SHARED | LK_RETRY, p); *vpp = fvp; return (0); } bad:; vn_lock(dvp, LK_SHARED | LK_RETRY, p); *vpp = NULL; return (error); } static int fdesc_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; int error = 0; switch (VTOFDESC(vp)->fd_type) { case Fdesc: /* * XXX Kludge: set p->p_dupfd to contain the value of the * the file descriptor being sought for duplication. The error * return ensures that the vnode for this device will be * released by vn_open. Open will detect this special error and * take the actions in dupfdopen. Other callers of vn_open or * VOP_OPEN will simply report the error. */ ap->a_p->p_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */ error = ENODEV; break; case Fctty: error = (*ctty_cdevsw.d_open)(devctty, ap->a_mode, 0, ap->a_p); break; } return (error); } static int fdesc_attr(fd, vap, cred, p) int fd; struct vattr *vap; struct ucred *cred; struct proc *p; { struct filedesc *fdp = p->p_fd; struct file *fp; struct stat stb; int error; if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) return (EBADF); switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: error = VOP_GETATTR((struct vnode *) fp->f_data, vap, cred, p); if (error == 0 && vap->va_type == VDIR) { /* * directories can cause loops in the namespace, * so turn off the 'x' bits to avoid trouble. */ vap->va_mode &= ~((VEXEC)|(VEXEC>>3)|(VEXEC>>6)); } break; case DTYPE_SOCKET: error = soo_stat((struct socket *)fp->f_data, &stb); if (error == 0) { vattr_null(vap); vap->va_type = VSOCK; vap->va_mode = stb.st_mode; vap->va_nlink = stb.st_nlink; vap->va_uid = stb.st_uid; vap->va_gid = stb.st_gid; vap->va_fsid = stb.st_dev; vap->va_fileid = stb.st_ino; vap->va_size = stb.st_size; vap->va_blocksize = stb.st_blksize; vap->va_atime = stb.st_atimespec; vap->va_mtime = stb.st_mtimespec; vap->va_ctime = stb.st_ctimespec; vap->va_gen = stb.st_gen; vap->va_flags = stb.st_flags; vap->va_rdev = stb.st_rdev; vap->va_bytes = stb.st_blocks * stb.st_blksize; } break; default: panic("fdesc attr"); break; } return (error); } static int fdesc_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct vattr *vap = ap->a_vap; unsigned fd; int error = 0; switch (VTOFDESC(vp)->fd_type) { case Froot: case Fdevfd: case Flink: case Fctty: bzero((caddr_t) vap, sizeof(*vap)); vattr_null(vap); vap->va_fileid = VTOFDESC(vp)->fd_ix; switch (VTOFDESC(vp)->fd_type) { case Flink: vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; vap->va_type = VLNK; vap->va_nlink = 1; vap->va_size = strlen(VTOFDESC(vp)->fd_link); break; case Fctty: vap->va_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; vap->va_type = VFIFO; vap->va_nlink = 1; vap->va_size = 0; break; default: vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; vap->va_type = VDIR; vap->va_nlink = 2; vap->va_size = DEV_BSIZE; break; } vap->va_uid = 0; vap->va_gid = 0; vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; vap->va_blocksize = DEV_BSIZE; vap->va_atime.tv_sec = boottime.tv_sec; vap->va_atime.tv_nsec = 0; vap->va_mtime = vap->va_atime; vap->va_ctime = vap->va_mtime; vap->va_gen = 0; vap->va_flags = 0; vap->va_rdev = 0; vap->va_bytes = 0; break; case Fdesc: fd = VTOFDESC(vp)->fd_fd; error = fdesc_attr(fd, vap, ap->a_cred, ap->a_p); break; default: panic("fdesc_getattr"); break; } if (error == 0) vp->v_type = vap->va_type; return (error); } static int fdesc_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct filedesc *fdp = ap->a_p->p_fd; struct file *fp; unsigned fd; int error; /* * Can't mess with the root vnode */ switch (VTOFDESC(ap->a_vp)->fd_type) { case Fdesc: break; case Fctty: return (0); default: return (EACCES); } fd = VTOFDESC(ap->a_vp)->fd_fd; if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) { return (EBADF); } /* * Can setattr the underlying vnode, but not sockets! */ switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: error = VOP_SETATTR((struct vnode *) fp->f_data, ap->a_vap, ap->a_cred, ap->a_p); break; case DTYPE_SOCKET: error = 0; break; default: error = EBADF; break; } return (error); } #define UIO_MX 16 static struct dirtmp { u_long d_fileno; u_short d_reclen; u_short d_namlen; char d_name[8]; } rootent[] = { { FD_DEVFD, UIO_MX, 2, "fd" }, { FD_STDIN, UIO_MX, 5, "stdin" }, { FD_STDOUT, UIO_MX, 6, "stdout" }, { FD_STDERR, UIO_MX, 6, "stderr" }, { FD_CTTY, UIO_MX, 3, "tty" }, { 0 } }; static int fdesc_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_long *a_cookies; int a_ncookies; } */ *ap; { struct uio *uio = ap->a_uio; struct filedesc *fdp; int i; int error; /* * We don't allow exporting fdesc mounts, and currently local * requests do not need cookies. */ if (ap->a_ncookies) panic("fdesc_readdir: not hungry"); switch (VTOFDESC(ap->a_vp)->fd_type) { case Fctty: return (0); case Fdesc: return (ENOTDIR); default: break; } fdp = uio->uio_procp->p_fd; if (VTOFDESC(ap->a_vp)->fd_type == Froot) { struct dirent d; struct dirent *dp = &d; struct dirtmp *dt; i = uio->uio_offset / UIO_MX; error = 0; while (uio->uio_resid > 0) { dt = &rootent[i]; if (dt->d_fileno == 0) { /**eofflagp = 1;*/ break; } i++; switch (dt->d_fileno) { case FD_CTTY: if (cttyvp(uio->uio_procp) == NULL) continue; break; case FD_STDIN: case FD_STDOUT: case FD_STDERR: if ((dt->d_fileno-FD_STDIN) >= fdp->fd_nfiles) continue; if (fdp->fd_ofiles[dt->d_fileno-FD_STDIN] == NULL) continue; break; } bzero((caddr_t) dp, UIO_MX); dp->d_fileno = dt->d_fileno; dp->d_namlen = dt->d_namlen; dp->d_type = DT_UNKNOWN; dp->d_reclen = dt->d_reclen; bcopy(dt->d_name, dp->d_name, dp->d_namlen+1); error = uiomove((caddr_t) dp, UIO_MX, uio); if (error) break; } uio->uio_offset = i * UIO_MX; return (error); } i = uio->uio_offset / UIO_MX; error = 0; while (uio->uio_resid > 0) { if (i >= fdp->fd_nfiles) break; if (fdp->fd_ofiles[i] != NULL) { struct dirent d; struct dirent *dp = &d; bzero((caddr_t) dp, UIO_MX); dp->d_namlen = sprintf(dp->d_name, "%d", i); dp->d_reclen = UIO_MX; dp->d_type = DT_UNKNOWN; dp->d_fileno = i + FD_STDIN; /* * And ship to userland */ error = uiomove((caddr_t) dp, UIO_MX, uio); if (error) break; } i++; } uio->uio_offset = i * UIO_MX; return (error); } static int fdesc_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { struct vnode *vp = ap->a_vp; int error; if (vp->v_type != VLNK) return (EPERM); if (VTOFDESC(vp)->fd_type == Flink) { char *ln = VTOFDESC(vp)->fd_link; error = uiomove(ln, strlen(ln), ap->a_uio); } else { error = EOPNOTSUPP; } return (error); } static int fdesc_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int error = EOPNOTSUPP; switch (VTOFDESC(ap->a_vp)->fd_type) { case Fctty: error = (*ctty_cdevsw.d_read)(devctty, ap->a_uio, ap->a_ioflag); break; default: error = EOPNOTSUPP; break; } return (error); } static int fdesc_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int error = EOPNOTSUPP; switch (VTOFDESC(ap->a_vp)->fd_type) { case Fctty: error = (*ctty_cdevsw.d_write)(devctty, ap->a_uio, ap->a_ioflag); break; default: error = EOPNOTSUPP; break; } return (error); } static int fdesc_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { int error = EOPNOTSUPP; switch (VTOFDESC(ap->a_vp)->fd_type) { case Fctty: error = (*ctty_cdevsw.d_ioctl)(devctty, ap->a_command, ap->a_data, ap->a_fflag, ap->a_p); break; default: error = EOPNOTSUPP; break; } return (error); } static int -fdesc_select(ap) - struct vop_select_args /* { +fdesc_poll(ap) + struct vop_poll_args /* { struct vnode *a_vp; - int a_which; - int a_fflags; + int a_events; struct ucred *a_cred; struct proc *a_p; } */ *ap; { - int error = EOPNOTSUPP; + int revents; switch (VTOFDESC(ap->a_vp)->fd_type) { case Fctty: - error = (*ctty_cdevsw.d_select)(devctty, ap->a_fflags, ap->a_p); + revents = (*ctty_cdevsw.d_poll)(devctty, ap->a_events, ap->a_p); break; default: - error = EOPNOTSUPP; + revents = seltrue(0, ap->a_events, ap->a_p); break; } - return (error); + return (revents); } static int fdesc_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; /* * Clear out the v_type field to avoid * nasty things happening in vgone(). */ VOP_UNLOCK(vp, 0, ap->a_p); vp->v_type = VNON; return (0); } static int fdesc_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; struct fdescnode *fd = VTOFDESC(vp); LIST_REMOVE(fd, fd_hash); FREE(vp->v_data, M_TEMP); vp->v_data = 0; return (0); } /* * Return POSIX pathconf information applicable to special devices. */ static int fdesc_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_MAX_CANON: *ap->a_retval = MAX_CANON; return (0); case _PC_MAX_INPUT: *ap->a_retval = MAX_INPUT; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_VDISABLE: *ap->a_retval = _POSIX_VDISABLE; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Print out the contents of a /dev/fd vnode. */ /* ARGSUSED */ static int fdesc_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_NON, fdesc vnode\n"); return (0); } /*void*/ static int fdesc_vfree(ap) struct vop_vfree_args /* { struct vnode *a_pvp; ino_t a_ino; int a_mode; } */ *ap; { return (0); } /* * /dev/fd "should never get here" operation */ static int fdesc_badop() { panic("fdesc: bad op"); /* NOTREACHED */ } #define fdesc_create ((int (*) __P((struct vop_create_args *)))eopnotsupp) #define fdesc_mknod ((int (*) __P((struct vop_mknod_args *)))eopnotsupp) #define fdesc_close ((int (*) __P((struct vop_close_args *)))nullop) #define fdesc_access ((int (*) __P((struct vop_access_args *)))nullop) #define fdesc_mmap ((int (*) __P((struct vop_mmap_args *)))eopnotsupp) #define fdesc_revoke vop_revoke #define fdesc_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define fdesc_seek ((int (*) __P((struct vop_seek_args *)))nullop) #define fdesc_remove ((int (*) __P((struct vop_remove_args *)))eopnotsupp) #define fdesc_link ((int (*) __P((struct vop_link_args *)))eopnotsupp) #define fdesc_rename ((int (*) __P((struct vop_rename_args *)))eopnotsupp) #define fdesc_mkdir ((int (*) __P((struct vop_mkdir_args *)))eopnotsupp) #define fdesc_rmdir ((int (*) __P((struct vop_rmdir_args *)))eopnotsupp) #define fdesc_symlink ((int (*) __P((struct vop_symlink_args *)))eopnotsupp) #define fdesc_abortop ((int (*) __P((struct vop_abortop_args *)))nullop) #define fdesc_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock) #define fdesc_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) #define fdesc_bmap ((int (*) __P((struct vop_bmap_args *)))fdesc_badop) #define fdesc_strategy ((int (*) __P((struct vop_strategy_args *)))fdesc_badop) #define fdesc_islocked \ ((int (*) __P((struct vop_islocked_args *)))vop_noislocked) #define fdesc_advlock ((int (*) __P((struct vop_advlock_args *)))eopnotsupp) #define fdesc_blkatoff \ ((int (*) __P((struct vop_blkatoff_args *)))eopnotsupp) #define fdesc_valloc ((int(*) __P(( \ struct vnode *pvp, \ int mode, \ struct ucred *cred, \ struct vnode **vpp))) eopnotsupp) #define fdesc_truncate \ ((int (*) __P((struct vop_truncate_args *)))eopnotsupp) #define fdesc_update ((int (*) __P((struct vop_update_args *)))eopnotsupp) #define fdesc_bwrite ((int (*) __P((struct vop_bwrite_args *)))eopnotsupp) static struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)fdesc_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)fdesc_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)fdesc_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)fdesc_open }, /* open */ { &vop_close_desc, (vop_t *)fdesc_close }, /* close */ { &vop_access_desc, (vop_t *)fdesc_access }, /* access */ { &vop_getattr_desc, (vop_t *)fdesc_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)fdesc_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)fdesc_read }, /* read */ +/* XXX: vop_lease */ { &vop_write_desc, (vop_t *)fdesc_write }, /* write */ { &vop_ioctl_desc, (vop_t *)fdesc_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)fdesc_select }, /* select */ + { &vop_poll_desc, (vop_t *)fdesc_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)fdesc_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)fdesc_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)fdesc_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)fdesc_seek }, /* seek */ { &vop_remove_desc, (vop_t *)fdesc_remove }, /* remove */ { &vop_link_desc, (vop_t *)fdesc_link }, /* link */ { &vop_rename_desc, (vop_t *)fdesc_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)fdesc_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)fdesc_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)fdesc_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)fdesc_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)fdesc_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)fdesc_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)fdesc_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)fdesc_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)fdesc_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)fdesc_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)fdesc_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)fdesc_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)fdesc_print }, /* print */ { &vop_islocked_desc, (vop_t *)fdesc_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)fdesc_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)fdesc_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)fdesc_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)fdesc_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)fdesc_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)fdesc_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)fdesc_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)fdesc_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc fdesc_vnodeop_opv_desc = { &fdesc_vnodeop_p, fdesc_vnodeop_entries }; VNODEOP_SET(fdesc_vnodeop_opv_desc); diff --git a/sys/fs/fifofs/fifo.h b/sys/fs/fifofs/fifo.h index 0a42690e3144..38314de9d1b9 100644 --- a/sys/fs/fifofs/fifo.h +++ b/sys/fs/fifofs/fifo.h @@ -1,86 +1,86 @@ /* * Copyright (c) 1991, 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. * * @(#)fifo.h 8.6 (Berkeley) 5/21/95 - * $Id$ + * $Id: fifo.h,v 1.13 1997/02/22 09:40:17 peter Exp $ */ extern vop_t **fifo_vnodeop_p; /* * Prototypes for fifo operations on vnodes. */ int fifo_badop __P((void)); int fifo_printinfo __P((struct vnode *)); int fifo_lookup __P((struct vop_lookup_args *)); #define fifo_create ((int (*) __P((struct vop_create_args *)))fifo_badop) #define fifo_mknod ((int (*) __P((struct vop_mknod_args *)))fifo_badop) int fifo_open __P((struct vop_open_args *)); int fifo_close __P((struct vop_close_args *)); #define fifo_access ((int (*) __P((struct vop_access_args *)))fifo_ebadf) #define fifo_getattr ((int (*) __P((struct vop_getattr_args *)))fifo_ebadf) #define fifo_setattr ((int (*) __P((struct vop_setattr_args *)))fifo_ebadf) int fifo_read __P((struct vop_read_args *)); int fifo_write __P((struct vop_write_args *)); #define fifo_lease_check ((int (*) __P((struct vop_lease_args *)))nullop) int fifo_ioctl __P((struct vop_ioctl_args *)); -int fifo_select __P((struct vop_select_args *)); +int fifo_poll __P((struct vop_poll_args *)); #define fifo_revoke vop_revoke #define fifo_mmap ((int (*) __P((struct vop_mmap_args *)))fifo_badop) #define fifo_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define fifo_seek ((int (*) __P((struct vop_seek_args *)))fifo_badop) #define fifo_remove ((int (*) __P((struct vop_remove_args *)))fifo_badop) #define fifo_link ((int (*) __P((struct vop_link_args *)))fifo_badop) #define fifo_rename ((int (*) __P((struct vop_rename_args *)))fifo_badop) #define fifo_mkdir ((int (*) __P((struct vop_mkdir_args *)))fifo_badop) #define fifo_rmdir ((int (*) __P((struct vop_rmdir_args *)))fifo_badop) #define fifo_symlink ((int (*) __P((struct vop_symlink_args *)))fifo_badop) #define fifo_readdir ((int (*) __P((struct vop_readdir_args *)))fifo_badop) #define fifo_readlink ((int (*) __P((struct vop_readlink_args *)))fifo_badop) #define fifo_abortop ((int (*) __P((struct vop_abortop_args *)))fifo_badop) int fifo_inactive __P((struct vop_inactive_args *)); #define fifo_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop) #define fifo_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock) #define fifo_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) int fifo_bmap __P((struct vop_bmap_args *)); #define fifo_strategy ((int (*) __P((struct vop_strategy_args *)))fifo_badop) #define fifo_islocked ((int(*) __P((struct vop_islocked_args *)))vop_noislocked) int fifo_pathconf __P((struct vop_pathconf_args *)); int fifo_advlock __P((struct vop_advlock_args *)); #define fifo_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))fifo_badop) #define fifo_valloc ((int (*) __P((struct vop_valloc_args *)))fifo_badop) #define fifo_reallocblks \ ((int (*) __P((struct vop_reallocblks_args *)))fifo_badop) #define fifo_vfree ((int (*) __P((struct vop_vfree_args *)))fifo_badop) #define fifo_truncate ((int (*) __P((struct vop_truncate_args *)))nullop) #define fifo_update ((int (*) __P((struct vop_update_args *)))nullop) #define fifo_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop) diff --git a/sys/fs/fifofs/fifo_vnops.c b/sys/fs/fifofs/fifo_vnops.c index 41b50c5d027a..bab3f53e8d78 100644 --- a/sys/fs/fifofs/fifo_vnops.c +++ b/sys/fs/fifofs/fifo_vnops.c @@ -1,543 +1,544 @@ /* * Copyright (c) 1990, 1993, 1995 * 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. * * @(#)fifo_vnops.c 8.10 (Berkeley) 5/27/95 - * $Id: fifo_vnops.c,v 1.25 1997/08/16 19:15:13 wollman Exp $ + * $Id: fifo_vnops.c,v 1.26 1997/09/02 20:06:11 bde Exp $ */ #include #include #include #include #include #include #include #include #include #include #include +#include #include #include /* * This structure is associated with the FIFO vnode and stores * the state associated with the FIFO. */ struct fifoinfo { struct socket *fi_readsock; struct socket *fi_writesock; long fi_readers; long fi_writers; }; static int fifo_ebadf __P((void)); static int fifo_print __P((struct vop_print_args *)); vop_t **fifo_vnodeop_p; static struct vnodeopv_entry_desc fifo_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)fifo_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)fifo_create }, /* create */ { &vop_mknod_desc, (vop_t *)fifo_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)fifo_open }, /* open */ { &vop_close_desc, (vop_t *)fifo_close }, /* close */ { &vop_access_desc, (vop_t *)fifo_access }, /* access */ { &vop_getattr_desc, (vop_t *)fifo_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)fifo_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)fifo_read }, /* read */ { &vop_write_desc, (vop_t *)fifo_write }, /* write */ { &vop_lease_desc, (vop_t *)fifo_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)fifo_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)fifo_select }, /* select */ + { &vop_poll_desc, (vop_t *)fifo_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)fifo_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)fifo_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)fifo_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)fifo_seek }, /* seek */ { &vop_remove_desc, (vop_t *)fifo_remove }, /* remove */ { &vop_link_desc, (vop_t *)fifo_link }, /* link */ { &vop_rename_desc, (vop_t *)fifo_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)fifo_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)fifo_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)fifo_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)fifo_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)fifo_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)fifo_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)fifo_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)fifo_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)fifo_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)fifo_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)fifo_print }, /* print */ { &vop_islocked_desc, (vop_t *)fifo_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)fifo_valloc }, /* valloc */ { &vop_vfree_desc, (vop_t *)fifo_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)fifo_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)fifo_update }, /* update */ { &vop_bwrite_desc, (vop_t *)fifo_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc fifo_vnodeop_opv_desc = { &fifo_vnodeop_p, fifo_vnodeop_entries }; VNODEOP_SET(fifo_vnodeop_opv_desc); /* * Trivial lookup routine that always fails. */ /* ARGSUSED */ int fifo_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { *ap->a_vpp = NULL; return (ENOTDIR); } /* * Open called to set up a new instance of a fifo or * to find an active instance of a fifo. */ /* ARGSUSED */ int fifo_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct fifoinfo *fip; struct proc *p = ap->a_p; struct socket *rso, *wso; int error; static char openstr[] = "fifo"; if ((fip = vp->v_fifoinfo) == NULL) { MALLOC(fip, struct fifoinfo *, sizeof(*fip), M_VNODE, M_WAITOK); vp->v_fifoinfo = fip; error = socreate(AF_LOCAL, &rso, SOCK_STREAM, 0, ap->a_p); if (error) { free(fip, M_VNODE); vp->v_fifoinfo = NULL; return (error); } fip->fi_readsock = rso; error = socreate(AF_LOCAL, &wso, SOCK_STREAM, 0, ap->a_p); if (error) { (void)soclose(rso); free(fip, M_VNODE); vp->v_fifoinfo = NULL; return (error); } fip->fi_writesock = wso; error = unp_connect2(wso, rso); if (error) { (void)soclose(wso); (void)soclose(rso); free(fip, M_VNODE); vp->v_fifoinfo = NULL; return (error); } fip->fi_readers = fip->fi_writers = 0; wso->so_state |= SS_CANTRCVMORE; rso->so_state |= SS_CANTSENDMORE; } if (ap->a_mode & FREAD) { fip->fi_readers++; if (fip->fi_readers == 1) { fip->fi_writesock->so_state &= ~SS_CANTSENDMORE; if (fip->fi_writers > 0) wakeup((caddr_t)&fip->fi_writers); } } if (ap->a_mode & FWRITE) { fip->fi_writers++; if (fip->fi_writers == 1) { fip->fi_readsock->so_state &= ~SS_CANTRCVMORE; if (fip->fi_readers > 0) wakeup((caddr_t)&fip->fi_readers); } } if ((ap->a_mode & FREAD) && (ap->a_mode & O_NONBLOCK) == 0) { while (fip->fi_writers == 0) { VOP_UNLOCK(vp, 0, p); error = tsleep((caddr_t)&fip->fi_readers, PCATCH | PSOCK, openstr, 0); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (error) goto bad; } } if (ap->a_mode & FWRITE) { if (ap->a_mode & O_NONBLOCK) { if (fip->fi_readers == 0) { error = ENXIO; goto bad; } } else { while (fip->fi_readers == 0) { VOP_UNLOCK(vp, 0, p); error = tsleep((caddr_t)&fip->fi_writers, PCATCH | PSOCK, openstr, 0); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (error) goto bad; } } } return (0); bad: VOP_CLOSE(vp, ap->a_mode, ap->a_cred, p); return (error); } /* * Vnode op for read */ /* ARGSUSED */ int fifo_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { struct uio *uio = ap->a_uio; struct socket *rso = ap->a_vp->v_fifoinfo->fi_readsock; struct proc *p = uio->uio_procp; int error, startresid; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_READ) panic("fifo_read mode"); #endif if (uio->uio_resid == 0) return (0); if (ap->a_ioflag & IO_NDELAY) rso->so_state |= SS_NBIO; startresid = uio->uio_resid; VOP_UNLOCK(ap->a_vp, 0, p); error = soreceive(rso, (struct sockaddr **)0, uio, (struct mbuf **)0, (struct mbuf **)0, (int *)0); vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p); /* * Clear EOF indication after first such return. */ if (uio->uio_resid == startresid) rso->so_state &= ~SS_CANTRCVMORE; if (ap->a_ioflag & IO_NDELAY) rso->so_state &= ~SS_NBIO; return (error); } /* * Vnode op for write */ /* ARGSUSED */ int fifo_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { struct socket *wso = ap->a_vp->v_fifoinfo->fi_writesock; struct proc *p = ap->a_uio->uio_procp; int error; #ifdef DIAGNOSTIC if (ap->a_uio->uio_rw != UIO_WRITE) panic("fifo_write mode"); #endif if (ap->a_ioflag & IO_NDELAY) wso->so_state |= SS_NBIO; VOP_UNLOCK(ap->a_vp, 0, p); error = sosend(wso, (struct sockaddr *)0, ap->a_uio, 0, (struct mbuf *)0, 0, p); vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p); if (ap->a_ioflag & IO_NDELAY) wso->so_state &= ~SS_NBIO; return (error); } /* * Device ioctl operation. */ /* ARGSUSED */ int fifo_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct file filetmp; int error; if (ap->a_command == FIONBIO) return (0); if (ap->a_fflag & FREAD) { filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock; error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p); if (error) return (error); } if (ap->a_fflag & FWRITE) { filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock; error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p); if (error) return (error); } return (0); } /* ARGSUSED */ int -fifo_select(ap) - struct vop_select_args /* { +fifo_poll(ap) + struct vop_poll_args /* { struct vnode *a_vp; - int a_which; - int a_fflags; + int a_events; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct file filetmp; - int ready; + int revents = 0; - if (ap->a_fflags & FREAD) { + if (ap->a_events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) { filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock; - ready = soo_select(&filetmp, ap->a_which, ap->a_p); - if (ready) - return (ready); + if (filetmp.f_data) + revents |= soo_poll(&filetmp, ap->a_events, ap->a_cred, + ap->a_p); } - if (ap->a_fflags & FWRITE) { + if (ap->a_events & (POLLOUT | POLLWRNORM | POLLWRBAND)) { filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock; - ready = soo_select(&filetmp, ap->a_which, ap->a_p); - if (ready) - return (ready); + if (filetmp.f_data) + revents |= soo_poll(&filetmp, ap->a_events, ap->a_cred, + ap->a_p); } - return (0); + return (revents); } int fifo_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { VOP_UNLOCK(ap->a_vp, 0, ap->a_p); return (0); } /* * This is a noop, simply returning what one has been given. */ int fifo_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; if (ap->a_runp != NULL) *ap->a_runp = 0; if (ap->a_runb != NULL) *ap->a_runb = 0; return (0); } /* * Device close routine */ /* ARGSUSED */ int fifo_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct fifoinfo *fip = vp->v_fifoinfo; int error1, error2; if (ap->a_fflag & FREAD) { fip->fi_readers--; if (fip->fi_readers == 0) socantsendmore(fip->fi_writesock); } if (ap->a_fflag & FWRITE) { fip->fi_writers--; if (fip->fi_writers == 0) socantrcvmore(fip->fi_readsock); } if (vp->v_usecount > 1) return (0); error1 = soclose(fip->fi_readsock); error2 = soclose(fip->fi_writesock); FREE(fip, M_VNODE); vp->v_fifoinfo = NULL; if (error1) return (error1); return (error2); } /* * Print out internal contents of a fifo vnode. */ int fifo_printinfo(vp) struct vnode *vp; { register struct fifoinfo *fip = vp->v_fifoinfo; printf(", fifo with %ld readers and %ld writers", fip->fi_readers, fip->fi_writers); return (0); } /* * Print out the contents of a fifo vnode. */ static int fifo_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_NON"); fifo_printinfo(ap->a_vp); printf("\n"); return (0); } /* * Return POSIX pathconf information applicable to fifo's. */ int fifo_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Fifo failed operation */ static int fifo_ebadf() { return (EBADF); } /* * Fifo advisory byte-level locks. */ /* ARGSUSED */ int fifo_advlock(ap) struct vop_advlock_args /* { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ *ap; { return (ap->a_flags & F_FLOCK ? EOPNOTSUPP : EINVAL); } /* * Fifo bad operation */ int fifo_badop() { panic("fifo_badop called"); /* NOTREACHED */ } diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c index e1884c81a5fb..f4ce26a52278 100644 --- a/sys/fs/msdosfs/msdosfs_vnops.c +++ b/sys/fs/msdosfs/msdosfs_vnops.c @@ -1,2032 +1,2043 @@ -/* $Id: msdosfs_vnops.c,v 1.42 1997/05/17 18:32:40 phk Exp $ */ +/* $Id: msdosfs_vnops.c,v 1.43 1997/08/26 07:32:39 phk Exp $ */ /* $NetBSD: msdosfs_vnops.c,v 1.20 1994/08/21 18:44:13 ws Exp $ */ /*- * Copyright (C) 1994 Wolfgang Solfrank. * Copyright (C) 1994 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * * 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 TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. */ /* * Written by Paul Popelka (paulp@uts.amdahl.com) * * You can do anything you want with this software, just don't say you wrote * it, and don't remove this notice. * * This software is provided "as is". * * The author supplies this software to be publicly redistributed on the * understanding that the author is not responsible for the correct * functioning of this software in any circumstances and is not liable for * any damages caused by this software. * * October 1992 */ #include #include #include #include /* defines plimit structure in proc struct */ #include #include #include #include #include #include #include +#include #include /* XXX */ /* defines v_rdev */ #include #include #include #include #include #include #include #include #include #include /* * Prototypes for MSDOSFS vnode operations */ static int msdosfs_create __P((struct vop_create_args *)); static int msdosfs_mknod __P((struct vop_mknod_args *)); static int msdosfs_open __P((struct vop_open_args *)); static int msdosfs_close __P((struct vop_close_args *)); static int msdosfs_access __P((struct vop_access_args *)); static int msdosfs_getattr __P((struct vop_getattr_args *)); static int msdosfs_setattr __P((struct vop_setattr_args *)); static int msdosfs_read __P((struct vop_read_args *)); static int msdosfs_write __P((struct vop_write_args *)); static int msdosfs_ioctl __P((struct vop_ioctl_args *)); -static int msdosfs_select __P((struct vop_select_args *)); +static int msdosfs_poll __P((struct vop_poll_args *)); static int msdosfs_mmap __P((struct vop_mmap_args *)); static int msdosfs_fsync __P((struct vop_fsync_args *)); static int msdosfs_seek __P((struct vop_seek_args *)); static int msdosfs_remove __P((struct vop_remove_args *)); static int msdosfs_link __P((struct vop_link_args *)); static int msdosfs_rename __P((struct vop_rename_args *)); static int msdosfs_mkdir __P((struct vop_mkdir_args *)); static int msdosfs_rmdir __P((struct vop_rmdir_args *)); static int msdosfs_symlink __P((struct vop_symlink_args *)); static int msdosfs_readdir __P((struct vop_readdir_args *)); static int msdosfs_readlink __P((struct vop_readlink_args *)); static int msdosfs_abortop __P((struct vop_abortop_args *)); static int msdosfs_lock __P((struct vop_lock_args *)); static int msdosfs_unlock __P((struct vop_unlock_args *)); static int msdosfs_bmap __P((struct vop_bmap_args *)); static int msdosfs_strategy __P((struct vop_strategy_args *)); static int msdosfs_print __P((struct vop_print_args *)); static int msdosfs_islocked __P((struct vop_islocked_args *)); static int msdosfs_advlock __P((struct vop_advlock_args *)); static int msdosfs_pathconf __P((struct vop_pathconf_args *ap)); static int msdosfs_reallocblks __P((struct vop_reallocblks_args *)); /* * Some general notes: * * In the ufs filesystem the inodes, superblocks, and indirect blocks are * read/written using the vnode for the filesystem. Blocks that represent * the contents of a file are read/written using the vnode for the file * (including directories when they are read/written as files). This * presents problems for the dos filesystem because data that should be in * an inode (if dos had them) resides in the directory itself. Since we * must update directory entries without the benefit of having the vnode * for the directory we must use the vnode for the filesystem. This means * that when a directory is actually read/written (via read, write, or * readdir, or seek) we must use the vnode for the filesystem instead of * the vnode for the directory as would happen in ufs. This is to insure we * retreive the correct block from the buffer cache since the hash value is * based upon the vnode address and the desired block number. */ /* * Create a regular file. On entry the directory to contain the file being * created is locked. We must release before we return. We must also free * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or * only if the SAVESTART bit in cn_flags is clear on success. */ static int msdosfs_create(ap) struct vop_create_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct componentname *cnp = ap->a_cnp; struct denode ndirent; struct denode *dep; struct denode *pdep = VTODE(ap->a_dvp); struct timespec ts; int error; #ifdef MSDOSFS_DEBUG printf("msdosfs_create(cnp %08x, vap %08x\n", cnp, ap->a_vap); #endif /* * Create a directory entry for the file, then call createde() to * have it installed. NOTE: DOS files are always executable. We * use the absence of the owner write bit to make the file * readonly. */ #ifdef DIAGNOSTIC if ((cnp->cn_flags & SAVENAME) == 0) panic("msdosfs_create: no name"); #endif bzero(&ndirent, sizeof(ndirent)); TIMEVAL_TO_TIMESPEC(&time, &ts); unix2dostime(&ts, &ndirent.de_Date, &ndirent.de_Time); unix2dosfn((u_char *)cnp->cn_nameptr, ndirent.de_Name, cnp->cn_namelen); ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) ? ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY; ndirent.de_StartCluster = 0; ndirent.de_FileSize = 0; ndirent.de_dev = pdep->de_dev; ndirent.de_devvp = pdep->de_devvp; if ((error = createde(&ndirent, pdep, &dep)) == 0) { *ap->a_vpp = DETOV(dep); if ((cnp->cn_flags & SAVESTART) == 0) free(cnp->cn_pnbuf, M_NAMEI); } else { free(cnp->cn_pnbuf, M_NAMEI); } vput(ap->a_dvp); /* release parent dir */ return error; } static int msdosfs_mknod(ap) struct vop_mknod_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { int error; switch (ap->a_vap->va_type) { case VDIR: error = msdosfs_mkdir((struct vop_mkdir_args *)ap); break; case VREG: error = msdosfs_create((struct vop_create_args *)ap); break; default: error = EINVAL; free(ap->a_cnp->cn_pnbuf, M_NAMEI); vput(ap->a_dvp); break; } return error; } static int msdosfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return 0; } static int msdosfs_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(vp); simple_lock(&vp->v_interlock); if (vp->v_usecount > 1) DE_TIMES(dep, &time); simple_unlock(&vp->v_interlock); return 0; } static int msdosfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; struct ucred *cred = ap->a_cred; mode_t mask, file_mode, mode = ap->a_mode; register gid_t *gp; int i; file_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); file_mode &= pmp->pm_mask; /* * Disallow write attempts on read-only file systems; * unless the file is a socket, fifo, or a block or * character device resident on the file system. */ if (mode & VWRITE) { switch (vp->v_type) { case VDIR: case VLNK: case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); break; } } /* User id 0 always gets access. */ if (cred->cr_uid == 0) return 0; mask = 0; /* Otherwise, check the owner. */ if (cred->cr_uid == pmp->pm_uid) { if (mode & VEXEC) mask |= S_IXUSR; if (mode & VREAD) mask |= S_IRUSR; if (mode & VWRITE) mask |= S_IWUSR; return (file_mode & mask) == mask ? 0 : EACCES; } /* Otherwise, check the groups. */ for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) if (pmp->pm_gid == *gp) { if (mode & VEXEC) mask |= S_IXGRP; if (mode & VREAD) mask |= S_IRGRP; if (mode & VWRITE) mask |= S_IWGRP; return (file_mode & mask) == mask ? 0 : EACCES; } /* Otherwise, check everyone else. */ if (mode & VEXEC) mask |= S_IXOTH; if (mode & VREAD) mask |= S_IROTH; if (mode & VWRITE) mask |= S_IWOTH; return (file_mode & mask) == mask ? 0 : EACCES; } static int msdosfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { u_int cn; struct denode *dep = VTODE(ap->a_vp); struct vattr *vap = ap->a_vap; DE_TIMES(dep, &time); vap->va_fsid = dep->de_dev; /* * The following computation of the fileid must be the same as that * used in msdosfs_readdir() to compute d_fileno. If not, pwd * doesn't work. */ if (dep->de_Attributes & ATTR_DIRECTORY) { if ((cn = dep->de_StartCluster) == MSDOSFSROOT) cn = 1; } else { if ((cn = dep->de_dirclust) == MSDOSFSROOT) cn = 1; cn = (cn << 16) | (dep->de_diroffset & 0xffff); } vap->va_fileid = cn; vap->va_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); vap->va_mode &= dep->de_pmp->pm_mask; if (dep->de_Attributes & ATTR_DIRECTORY) vap->va_mode |= S_IFDIR; vap->va_nlink = 1; vap->va_gid = dep->de_pmp->pm_gid; vap->va_uid = dep->de_pmp->pm_uid; vap->va_rdev = 0; vap->va_size = dep->de_FileSize; dos2unixtime(dep->de_Date, dep->de_Time, &vap->va_atime); vap->va_mtime = vap->va_atime; #if 0 #ifndef MSDOSFS_NODIRMOD if (vap->va_mode & S_IFDIR) TIMEVAL_TO_TIMESPEC(&time, &vap->va_mtime); #endif #endif vap->va_ctime = vap->va_atime; vap->va_flags = (dep->de_Attributes & ATTR_ARCHIVE) ? 0 : SF_ARCHIVED; vap->va_gen = 0; vap->va_blocksize = dep->de_pmp->pm_bpcluster; vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) & ~(dep->de_pmp->pm_crbomask); vap->va_type = ap->a_vp->v_type; vap->va_filerev = dep->de_modrev; return 0; } static int msdosfs_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(ap->a_vp); struct vattr *vap = ap->a_vap; struct ucred *cred = ap->a_cred; int error = 0; /* * Check for unsettable attributes. */ if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { return (EINVAL); } if (vap->va_flags != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != dep->de_pmp->pm_uid && (error = suser(cred, &ap->a_p->p_acflag))) return (error); /* * We are very inconsistent about handling unsupported * attributes. We ignored the the access time and the * read and execute bits. We were strict for the other * attributes. * * Here we are strict, stricter than ufs in not allowing * users to attempt to set SF_SETTABLE bits or anyone to * set unsupported bits. However, we ignore attempts to * set ATTR_ARCHIVE for directories `cp -pr' from a more * sensible file system attempts it a lot. */ if (cred->cr_uid != 0) { if (vap->va_flags & SF_SETTABLE) return EPERM; } if (vap->va_flags & ~SF_ARCHIVED) return EINVAL; if (vap->va_flags & SF_ARCHIVED) dep->de_Attributes &= ~ATTR_ARCHIVE; else if (!(dep->de_Attributes & ATTR_DIRECTORY)) dep->de_Attributes |= ATTR_ARCHIVE; dep->de_flag |= DE_MODIFIED; } if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (uid_t)VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if ((cred->cr_uid != dep->de_pmp->pm_uid || vap->va_uid != dep->de_pmp->pm_uid || (vap->va_gid != dep->de_pmp->pm_gid && !groupmember(vap->va_gid, cred))) && (error = suser(cred, &ap->a_p->p_acflag))) return error; if (vap->va_uid != dep->de_pmp->pm_uid || vap->va_gid != dep->de_pmp->pm_gid) return EINVAL; } if (vap->va_size != VNOVAL) { /* * Disallow write attempts on read-only file systems; * unless the file is a socket, fifo, or a block or * character device resident on the file system. */ switch (vp->v_type) { case VDIR: return (EISDIR); case VLNK: case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); break; } error = detrunc(dep, vap->va_size, 0, cred, ap->a_p); if (error) return error; } if (vap->va_mtime.tv_sec != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != dep->de_pmp->pm_uid && (error = suser(cred, &ap->a_p->p_acflag)) && ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || (error = VOP_ACCESS(vp, VWRITE, cred, ap->a_p)))) return error; dep->de_flag |= DE_UPDATE; error = deupdat(dep, &vap->va_mtime, 1); if (error) return error; } /* * DOS files only have the ability to have their writability * attribute set, so we use the owner write bit to set the readonly * attribute. */ error = 0; if (vap->va_mode != (u_short) VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != dep->de_pmp->pm_uid && (error = suser(cred, &ap->a_p->p_acflag))) return error; /* We ignore the read and execute bits */ if (vap->va_mode & VWRITE) dep->de_Attributes &= ~ATTR_READONLY; else dep->de_Attributes |= ATTR_READONLY; dep->de_flag |= DE_MODIFIED; } return error; } static int msdosfs_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int error = 0; int diff; int isadir; long n; long on; daddr_t lbn; daddr_t rablock; int rasize; struct buf *bp; struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(vp); struct msdosfsmount *pmp = dep->de_pmp; struct uio *uio = ap->a_uio; /* * If they didn't ask for any data, then we are done. */ if (uio->uio_resid == 0) return 0; if (uio->uio_offset < 0) return EINVAL; isadir = dep->de_Attributes & ATTR_DIRECTORY; do { lbn = uio->uio_offset >> pmp->pm_cnshift; on = uio->uio_offset & pmp->pm_crbomask; n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid); diff = dep->de_FileSize - uio->uio_offset; if (diff <= 0) return 0; /* convert cluster # to block # if a directory */ if (isadir) { error = pcbmap(dep, lbn, &lbn, 0); if (error) return error; } if (diff < n) n = diff; /* * If we are operating on a directory file then be sure to * do i/o with the vnode for the filesystem instead of the * vnode for the directory. */ if (isadir) { error = bread(pmp->pm_devvp, lbn, pmp->pm_bpcluster, NOCRED, &bp); } else { rablock = lbn + 1; #ifdef PC98 /* * 1024byte/sector support */ if (pmp->pm_BytesPerSec == 1024) vp->v_flag |= 0x10000; #endif if (vp->v_lastr + 1 == lbn && rablock * pmp->pm_bpcluster < dep->de_FileSize) { rasize = pmp->pm_bpcluster; error = breadn(vp, lbn, pmp->pm_bpcluster, &rablock, &rasize, 1, NOCRED, &bp); } else { error = bread(vp, lbn, pmp->pm_bpcluster, NOCRED, &bp); } vp->v_lastr = lbn; } n = min(n, pmp->pm_bpcluster - bp->b_resid); if (error) { brelse(bp); return error; } error = uiomove(bp->b_data + on, (int) n, uio); /* * If we have read everything from this block or have read * to end of file then we are done with this block. Mark * it to say the buffer can be reused if need be. */ #if 0 if (n + on == pmp->pm_bpcluster || uio->uio_offset == dep->de_FileSize) bp->b_flags |= B_AGE; #endif brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); return error; } /* * Write data to a file or directory. */ static int msdosfs_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int n; int isadir; int croffset; int resid; int osize; int error = 0; u_long count; daddr_t bn, lastcn; struct buf *bp; int ioflag = ap->a_ioflag; struct uio *uio = ap->a_uio; struct proc *p = uio->uio_procp; struct vnode *vp = ap->a_vp; struct vnode *thisvp; struct denode *dep = VTODE(vp); struct msdosfsmount *pmp = dep->de_pmp; struct ucred *cred = ap->a_cred; struct timespec ts; #ifdef MSDOSFS_DEBUG printf("msdosfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n", vp, uio, ioflag, cred); printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n", dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster); #endif switch (vp->v_type) { case VREG: if (ioflag & IO_APPEND) uio->uio_offset = dep->de_FileSize; isadir = 0; thisvp = vp; break; case VDIR: if ((ioflag & IO_SYNC) == 0) panic("msdosfs_write(): non-sync directory update"); isadir = 1; thisvp = pmp->pm_devvp; break; default: panic("msdosfs_write(): bad file type"); break; } if (uio->uio_offset < 0) return EINVAL; if (uio->uio_resid == 0) return 0; /* * If they've exceeded their filesize limit, tell them about it. */ if (vp->v_type == VREG && p && ((uio->uio_offset + uio->uio_resid) > p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) { psignal(p, SIGXFSZ); return EFBIG; } /* * If attempting to write beyond the end of the root directory we * stop that here because the root directory can not grow. */ if ((dep->de_Attributes & ATTR_DIRECTORY) && dep->de_StartCluster == MSDOSFSROOT && (uio->uio_offset + uio->uio_resid) > dep->de_FileSize) return ENOSPC; /* * If the offset we are starting the write at is beyond the end of * the file, then they've done a seek. Unix filesystems allow * files with holes in them, DOS doesn't so we must fill the hole * with zeroed blocks. */ if (uio->uio_offset > dep->de_FileSize) { error = deextend(dep, uio->uio_offset, cred); if (error) return error; } /* * Remember some values in case the write fails. */ resid = uio->uio_resid; osize = dep->de_FileSize; #ifdef PC98 /* * 1024byte/sector support */ if (pmp->pm_BytesPerSec == 1024) thisvp->v_flag |= 0x10000; #endif /* * If we write beyond the end of the file, extend it to its ultimate * size ahead of the time to hopefully get a contiguous area. */ if (uio->uio_offset + resid > osize) { count = de_clcount(pmp, uio->uio_offset + resid) - de_clcount(pmp, osize); if ((error = extendfile(dep, count, NULL, NULL, 0)) && (error != ENOSPC || (ioflag & IO_UNIT))) goto errexit; lastcn = dep->de_fc[FC_LASTFC].fc_frcn; } else lastcn = de_clcount(pmp, osize) - 1; do { bn = de_blk(pmp, uio->uio_offset); if (isadir) { error = pcbmap(dep, bn, &bn, 0); if (error) break; } else if (bn > lastcn) { error = ENOSPC; break; } croffset = uio->uio_offset & pmp->pm_crbomask; n = min(uio->uio_resid, pmp->pm_bpcluster - croffset); if (uio->uio_offset + n > dep->de_FileSize) { dep->de_FileSize = uio->uio_offset + n; /* The object size needs to be set before buffer is allocated */ vnode_pager_setsize(vp, dep->de_FileSize); } if ((uio->uio_offset & pmp->pm_crbomask) == 0 && (de_blk(pmp, uio->uio_offset + uio->uio_resid) > de_blk(pmp, uio->uio_offset) || uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) { /* * If either the whole cluster gets written, * or we write the cluster from its start beyond EOF, * then no need to read data from disk. */ bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0); clrbuf(bp); /* * Do the bmap now, since pcbmap needs buffers * for the fat table. (see msdosfs_strategy) */ if (!isadir) { if (bp->b_blkno == bp->b_lblkno) { error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0); if (error) bp->b_blkno = -1; } if (bp->b_blkno == -1) { brelse(bp); if (!error) error = EIO; /* XXX */ break; } } } else { /* * The block we need to write into exists, so read it in. */ error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp); if (error) break; } /* * Should these vnode_pager_* functions be done on dir * files? */ /* * Copy the data from user space into the buf header. */ error = uiomove(bp->b_data + croffset, n, uio); /* * If they want this synchronous then write it and wait for * it. Otherwise, if on a cluster boundary write it * asynchronously so we can move on to the next block * without delay. Otherwise do a delayed write because we * may want to write somemore into the block later. */ if (ioflag & IO_SYNC) (void) bwrite(bp); else if (n + croffset == pmp->pm_bpcluster) { bawrite(bp); } else bdwrite(bp); dep->de_flag |= DE_UPDATE; } while (error == 0 && uio->uio_resid > 0); /* * If the write failed and they want us to, truncate the file back * to the size it was before the write was attempted. */ errexit: if (error) { if (ioflag & IO_UNIT) { detrunc(dep, osize, ioflag & IO_SYNC, NOCRED, NULL); uio->uio_offset -= resid - uio->uio_resid; uio->uio_resid = resid; } else { detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED, NULL); if (uio->uio_resid != resid) error = 0; } } else if (ioflag & IO_SYNC) { TIMEVAL_TO_TIMESPEC(&time, &ts); error = deupdat(dep, &ts, 1); } return error; } static int msdosfs_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return ENOTTY; } static int -msdosfs_select(ap) - struct vop_select_args /* { +msdosfs_poll(ap) + struct vop_poll_args /* { struct vnode *a_vp; - int a_which; - int a_fflags; + int a_events; struct ucred *a_cred; struct proc *a_p; } */ *ap; { - return 1; /* DOS filesystems never block? */ + /* DOS filesystems never block? */ + return (ap->a_events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)); } static int msdosfs_mmap(ap) struct vop_mmap_args /* { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return EINVAL; } /* * Flush the blocks of a file to disk. * * This function is worthless for vnodes that represent directories. Maybe we * could just do a sync if they try an fsync on a directory file. */ static int msdosfs_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct buf *bp; int wait = ap->a_waitfor == MNT_WAIT; struct timespec ts; struct buf *nbp; int s; /* * Flush all dirty buffers associated with a vnode. */ loop: s = splbio(); for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if ((bp->b_flags & B_BUSY)) continue; if ((bp->b_flags & B_DELWRI) == 0) panic("msdosfs_fsync: not dirty"); bremfree(bp); bp->b_flags |= B_BUSY; splx(s); (void) bwrite(bp); goto loop; } while (vp->v_numoutput) { vp->v_flag |= VBWAIT; (void) tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "msdosfsn", 0); } #ifdef DIAGNOSTIC if (vp->v_dirtyblkhd.lh_first) { vprint("msdosfs_fsync: dirty", vp); goto loop; } #endif splx(s); TIMEVAL_TO_TIMESPEC(&time, &ts); return deupdat(VTODE(vp), &ts, wait); } /* * Now the whole work of extending a file is done in the write function. * So nothing to do here. */ static int msdosfs_seek(ap) struct vop_seek_args /* { struct vnode *a_vp; off_t a_oldoff; off_t a_newoff; struct ucred *a_cred; } */ *ap; { return 0; } static int msdosfs_remove(ap) struct vop_remove_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { int error; struct denode *dep = VTODE(ap->a_vp); struct denode *ddep = VTODE(ap->a_dvp); error = removede(ddep,dep); #ifdef MSDOSFS_DEBUG printf("msdosfs_remove(), dep %08x, v_usecount %d\n", dep, ap->a_vp->v_usecount); #endif if (ddep == dep) vrele(ap->a_vp); else vput(ap->a_vp); /* causes msdosfs_inactive() to be called * via vrele() */ vput(ap->a_dvp); return error; } /* * DOS filesystems don't know what links are. But since we already called * msdosfs_lookup() with create and lockparent, the parent is locked so we * have to free it before we return the error. */ static int msdosfs_link(ap) struct vop_link_args /* { struct vnode *a_tdvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { VOP_ABORTOP(ap->a_tdvp, ap->a_cnp); vput(ap->a_tdvp); return EOPNOTSUPP; } /* * Renames on files require moving the denode to a new hash queue since the * denode's location is used to compute which hash queue to put the file * in. Unless it is a rename in place. For example "mv a b". * * What follows is the basic algorithm: * * if (file move) { * if (dest file exists) { * remove dest file * } * if (dest and src in same directory) { * rewrite name in existing directory slot * } else { * write new entry in dest directory * update offset and dirclust in denode * move denode to new hash chain * clear old directory entry * } * } else { * directory move * if (dest directory exists) { * if (dest is not empty) { * return ENOTEMPTY * } * remove dest directory * } * if (dest and src in same directory) { * rewrite name in existing entry * } else { * be sure dest is not a child of src directory * write entry in dest directory * update "." and ".." in moved directory * clear old directory entry for moved directory * } * } * * On entry: * source's parent directory is unlocked * source file or directory is unlocked * destination's parent directory is locked * destination file or directory is locked if it exists * * On exit: * all denodes should be released * * Notes: * I'm not sure how the memory containing the pathnames pointed at by the * componentname structures is freed, there may be some memory bleeding * for each rename done. */ static int msdosfs_rename(ap) struct vop_rename_args /* { struct vnode *a_fdvp; struct vnode *a_fvp; struct componentname *a_fcnp; struct vnode *a_tdvp; struct vnode *a_tvp; struct componentname *a_tcnp; } */ *ap; { u_char toname[11]; int error; int newparent = 0; int sourceisadirectory = 0; u_long cn; daddr_t bn; struct vnode *tvp = ap->a_tvp; struct componentname *fcnp = ap->a_fcnp; struct proc *p = fcnp->cn_proc; struct denode *fddep; /* from file's parent directory */ struct denode *fdep; /* from file or directory */ struct denode *tddep; /* to file's parent directory */ struct denode *tdep; /* to file or directory */ struct msdosfsmount *pmp; struct direntry *dotdotp; struct direntry *ep; struct buf *bp; fddep = VTODE(ap->a_fdvp); fdep = VTODE(ap->a_fvp); tddep = VTODE(ap->a_tdvp); tdep = tvp ? VTODE(tvp) : NULL; pmp = fddep->de_pmp; /* Check for cross-device rename */ if ((ap->a_fvp->v_mount != ap->a_tdvp->v_mount) || (tvp && (ap->a_fvp->v_mount != tvp->v_mount))) { error = EXDEV; goto bad; } /* * Convert the filename in tcnp into a dos filename. We copy this * into the denode and directory entry for the destination * file/directory. */ unix2dosfn((u_char *) ap->a_tcnp->cn_nameptr, toname, ap->a_tcnp->cn_namelen); /* * At this point this is the lock state of the denodes: * fddep referenced * fdep referenced * tddep locked * tdep locked if it exists */ /* * Be sure we are not renaming ".", "..", or an alias of ".". This * leads to a crippled directory tree. It's pretty tough to do a * "ls" or "pwd" with the "." directory entry missing, and "cd .." * doesn't work if the ".." entry is missing. */ if (fdep->de_Attributes & ATTR_DIRECTORY) { if ((ap->a_fcnp->cn_namelen == 1 && ap->a_fcnp->cn_nameptr[0] == '.') || fddep == fdep || (ap->a_fcnp->cn_flags | ap->a_tcnp->cn_flags) & ISDOTDOT) { VOP_ABORTOP(ap->a_tdvp, ap->a_tcnp); vput(ap->a_tdvp); if (tvp) vput(tvp); VOP_ABORTOP(ap->a_fdvp, ap->a_fcnp); vrele(ap->a_fdvp); vrele(ap->a_fvp); return EINVAL; } sourceisadirectory = 1; } /* * If we are renaming a directory, and the directory is being moved * to another directory, then we must be sure the destination * directory is not in the subtree of the source directory. This * could orphan everything under the source directory. * doscheckpath() unlocks the destination's parent directory so we * must look it up again to relock it. */ if (fddep->de_StartCluster != tddep->de_StartCluster) newparent = 1; if (sourceisadirectory && newparent) { if (tdep) { vput(ap->a_tvp); tdep = NULL; } /* doscheckpath() vput()'s tddep */ error = doscheckpath(fdep, tddep); tddep = NULL; if (error) goto bad; if ((ap->a_tcnp->cn_flags & SAVESTART) == 0) panic("msdosfs_rename(): lost to startdir"); error = relookup(ap->a_tdvp, &tvp, ap->a_tcnp); if (error) goto bad; tddep = VTODE(ap->a_tdvp); tdep = tvp ? VTODE(tvp) : NULL; } /* * If the destination exists, then be sure its type (file or dir) * matches that of the source. And, if it is a directory make sure * it is empty. Then delete the destination. */ if (tdep) { if (tdep->de_Attributes & ATTR_DIRECTORY) { if (!sourceisadirectory) { error = ENOTDIR; goto bad; } if (!dosdirempty(tdep)) { error = ENOTEMPTY; goto bad; } cache_purge(DETOV(tddep)); } else { /* destination is file */ if (sourceisadirectory) { error = EISDIR; goto bad; } } error = removede(tddep,tdep); if (error) goto bad; vput(ap->a_tvp); tdep = NULL; } /* * If the source and destination are in the same directory then * just read in the directory entry, change the name in the * directory entry and write it back to disk. */ if (newparent == 0) { /* tddep and fddep point to the same denode here */ vn_lock(ap->a_fvp, LK_EXCLUSIVE, p); /* ap->a_fdvp is already locked */ error = readep(fddep->de_pmp, fdep->de_dirclust, fdep->de_diroffset, &bp, &ep); if (error) { VOP_UNLOCK(ap->a_fvp, 0, p); goto bad; } bcopy(toname, ep->deName, 11); error = bwrite(bp); if (error) { VOP_UNLOCK(ap->a_fvp, 0, p); goto bad; } bcopy(toname, fdep->de_Name, 11); /* update denode */ /* * fdep locked fddep and tddep point to the same denode * which is locked tdep is NULL */ } else { u_long dirsize = 0L; /* * If the source and destination are in different * directories, then mark the entry in the source directory * as deleted and write a new entry in the destination * directory. Then move the denode to the correct hash * chain for its new location in the filesystem. And, if * we moved a directory, then update its .. entry to point * to the new parent directory. If we moved a directory * will also insure that the directory entry on disk has a * filesize of zero. */ vn_lock(ap->a_fvp, LK_EXCLUSIVE, p); bcopy(toname, fdep->de_Name, 11); /* update denode */ if (fdep->de_Attributes & ATTR_DIRECTORY) { dirsize = fdep->de_FileSize; fdep->de_FileSize = 0; } error = createde(fdep, tddep, (struct denode **) 0); if (fdep->de_Attributes & ATTR_DIRECTORY) { fdep->de_FileSize = dirsize; } if (error) { /* should put back filename */ VOP_UNLOCK(ap->a_fvp, 0, p); goto bad; } vn_lock(ap->a_fdvp, LK_EXCLUSIVE, p); error = readep(fddep->de_pmp, fddep->de_fndclust, fddep->de_fndoffset, &bp, &ep); if (error) { VOP_UNLOCK(ap->a_fvp, 0, p); VOP_UNLOCK(ap->a_fdvp, 0, p); goto bad; } ep->deName[0] = SLOT_DELETED; error = bwrite(bp); if (error) { VOP_UNLOCK(ap->a_fvp, 0, p); VOP_UNLOCK(ap->a_fdvp, 0, p); goto bad; } if (!sourceisadirectory) { fdep->de_dirclust = tddep->de_fndclust; fdep->de_diroffset = tddep->de_fndoffset; reinsert(fdep); } VOP_UNLOCK(ap->a_fdvp, 0, p); } /* fdep is still locked here */ /* * If we moved a directory to a new parent directory, then we must * fixup the ".." entry in the moved directory. */ if (sourceisadirectory && newparent) { cn = fdep->de_StartCluster; if (cn == MSDOSFSROOT) { /* this should never happen */ panic("msdosfs_rename(): updating .. in root directory?"); } else { bn = cntobn(pmp, cn); } error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp); if (error) { /* should really panic here, fs is corrupt */ VOP_UNLOCK(ap->a_fvp, 0, p); goto bad; } dotdotp = (struct direntry *) bp->b_data + 1; putushort(dotdotp->deStartCluster, tddep->de_StartCluster); error = bwrite(bp); VOP_UNLOCK(ap->a_fvp, 0, p); if (error) { /* should really panic here, fs is corrupt */ goto bad; } } else VOP_UNLOCK(ap->a_fvp, 0, p); bad: ; vrele(DETOV(fdep)); vrele(DETOV(fddep)); if (tdep) vput(DETOV(tdep)); if (tddep) vput(DETOV(tddep)); return error; } static struct { struct direntry dot; struct direntry dotdot; } dosdirtemplate = { { ". ", " ", /* the . entry */ ATTR_DIRECTORY, /* file attribute */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* resevered */ {210, 4}, {210, 4}, /* time and date */ {0, 0}, /* startcluster */ {0, 0, 0, 0}, /* filesize */ },{ ".. ", " ", /* the .. entry */ ATTR_DIRECTORY, /* file attribute */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* resevered */ {210, 4}, {210, 4}, /* time and date */ {0, 0}, /* startcluster */ {0, 0, 0, 0}, /* filesize */ } }; static int msdosfs_mkdir(ap) struct vop_mkdir_args /* { struct vnode *a_dvp; struvt vnode **a_vpp; struvt componentname *a_cnp; struct vattr *a_vap; } */ *ap; { int bn; int error; u_long newcluster; struct denode *pdep; struct denode *ndep; struct direntry *denp; struct denode ndirent; struct msdosfsmount *pmp; struct buf *bp; struct timespec ts; u_short dDate, dTime; pdep = VTODE(ap->a_dvp); /* * If this is the root directory and there is no space left we * can't do anything. This is because the root directory can not * change size. */ if (pdep->de_StartCluster == MSDOSFSROOT && pdep->de_fndclust == (u_long)-1) { free(ap->a_cnp->cn_pnbuf, M_NAMEI); vput(ap->a_dvp); return ENOSPC; } pmp = pdep->de_pmp; /* * Allocate a cluster to hold the about to be created directory. */ error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL); if (error) { free(ap->a_cnp->cn_pnbuf, M_NAMEI); vput(ap->a_dvp); return error; } /* * Now fill the cluster with the "." and ".." entries. And write * the cluster to disk. This way it is there for the parent * directory to be pointing at if there were a crash. */ bn = cntobn(pmp, newcluster); /* always succeeds */ bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0); bzero(bp->b_data, pmp->pm_bpcluster); bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate); denp = (struct direntry *) bp->b_data; putushort(denp->deStartCluster, newcluster); TIMEVAL_TO_TIMESPEC(&time, &ts); unix2dostime(&ts, &dDate, &dTime); putushort(denp->deDate, dDate); putushort(denp->deTime, dTime); denp++; putushort(denp->deStartCluster, pdep->de_StartCluster); putushort(denp->deDate, dDate); putushort(denp->deTime, dTime); error = bwrite(bp); if (error) { clusterfree(pmp, newcluster, NULL); free(ap->a_cnp->cn_pnbuf, M_NAMEI); vput(ap->a_dvp); return error; } /* * Now build up a directory entry pointing to the newly allocated * cluster. This will be written to an empty slot in the parent * directory. */ ndep = &ndirent; bzero(ndep, sizeof(*ndep)); unix2dosfn((u_char *)ap->a_cnp->cn_nameptr, ndep->de_Name, ap->a_cnp->cn_namelen); TIMEVAL_TO_TIMESPEC(&time, &ts); unix2dostime(&ts, &ndep->de_Date, &ndep->de_Time); ndep->de_StartCluster = newcluster; ndep->de_Attributes = ATTR_DIRECTORY; error = createde(ndep, pdep, &ndep); if (error) { clusterfree(pmp, newcluster, NULL); } else { *ap->a_vpp = DETOV(ndep); } free(ap->a_cnp->cn_pnbuf, M_NAMEI); #ifdef MSDOSFS_DEBUG printf("msdosfs_mkdir(): vput(%08x)\n", ap->a_dvp); #endif vput(ap->a_dvp); return error; } static int msdosfs_rmdir(ap) struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { struct denode *ddep; struct denode *dep; int error = 0; ddep = VTODE(ap->a_dvp); /* parent dir of dir to delete */ dep = VTODE(ap->a_vp);/* directory to delete */ /* * Be sure the directory being deleted is empty. */ if (dosdirempty(dep) == 0) { error = ENOTEMPTY; goto out; } /* * Delete the entry from the directory. For dos filesystems this * gets rid of the directory entry on disk, the in memory copy * still exists but the de_refcnt is <= 0. This prevents it from * being found by deget(). When the vput() on dep is done we give * up access and eventually msdosfs_reclaim() will be called which * will remove it from the denode cache. */ error = removede(ddep,dep); if (error) goto out; /* * This is where we decrement the link count in the parent * directory. Since dos filesystems don't do this we just purge * the name cache and let go of the parent directory denode. */ cache_purge(DETOV(ddep)); vput(ap->a_dvp); ap->a_dvp = NULL; /* * Truncate the directory that is being deleted. */ error = detrunc(dep, (u_long) 0, IO_SYNC, NOCRED, NULL); cache_purge(DETOV(dep)); out: ; if (ap->a_dvp) vput(ap->a_dvp); vput(ap->a_vp); return error; } /* * DOS filesystems don't know what symlinks are. */ static int msdosfs_symlink(ap) struct vop_symlink_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; char *a_target; } */ *ap; { free(ap->a_cnp->cn_pnbuf, M_NAMEI); vput(ap->a_dvp); return EINVAL; } /* * Dummy dirents to simulate the "." and ".." entries of the root directory * in a dos filesystem. Dos doesn't provide these. Note that each entry * must be the same size as a dos directory entry (32 bytes). */ static struct dos_dirent { u_long d_fileno; u_short d_reclen; u_char d_type; u_char d_namlen; u_char d_name[24]; } rootdots[2] = { { 1, /* d_fileno */ sizeof(struct direntry), /* d_reclen */ DT_DIR, /* d_type */ 1, /* d_namlen */ "." /* d_name */ }, { 1, /* d_fileno */ sizeof(struct direntry), /* d_reclen */ DT_DIR, /* d_type */ 2, /* d_namlen */ ".." /* d_name */ } }; static int msdosfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; int *a_ncookies; u_long **a_cookies; } */ *ap; { int error = 0; int diff; char pushout; long n; long on; long lost; long count; u_long cn; u_long fileno; long bias = 0; daddr_t bn; daddr_t lbn; struct buf *bp; struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; struct direntry *dentp; struct dirent *prev; struct dirent *crnt; u_char dirbuf[512]; /* holds converted dos directories */ struct uio *uio = ap->a_uio; off_t off; int ncookies = 0; #ifdef MSDOSFS_DEBUG printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n", ap->a_vp, uio, ap->a_cred, ap->a_eofflag); #endif /* * msdosfs_readdir() won't operate properly on regular files since * it does i/o only with the the filesystem vnode, and hence can * retrieve the wrong block from the buffer cache for a plain file. * So, fail attempts to readdir() on a plain file. */ if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) return ENOTDIR; /* * If the user buffer is smaller than the size of one dos directory * entry or the file offset is not a multiple of the size of a * directory entry, then we fail the read. */ count = uio->uio_resid & ~(sizeof(struct direntry) - 1); lost = uio->uio_resid - count; if (count < sizeof(struct direntry) || (uio->uio_offset & (sizeof(struct direntry) - 1))) return EINVAL; uio->uio_resid = count; uio->uio_iov->iov_len = count; off = uio->uio_offset; /* * If they are reading from the root directory then, we simulate * the . and .. entries since these don't exist in the root * directory. We also set the offset bias to make up for having to * simulate these entries. By this I mean that at file offset 64 we * read the first entry in the root directory that lives on disk. */ if (dep->de_StartCluster == MSDOSFSROOT) { /* * printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n", * uio->uio_offset); */ bias = 2 * sizeof(struct direntry); if (uio->uio_offset < 2 * sizeof(struct direntry)) { if (uio->uio_offset && uio->uio_offset != sizeof(struct direntry)) { error = EINVAL; goto out; } n = 1; if (!uio->uio_offset) { n = 2; ncookies++; } ncookies++; error = uiomove((char *) rootdots + uio->uio_offset, n * sizeof(struct direntry), uio); } } while (!error && uio->uio_resid > 0) { lbn = (uio->uio_offset - bias) >> pmp->pm_cnshift; on = (uio->uio_offset - bias) & pmp->pm_crbomask; n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid); diff = dep->de_FileSize - (uio->uio_offset - bias); if (diff <= 0) break; if (diff < n) n = diff; error = pcbmap(dep, lbn, &bn, &cn); if (error) break; error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp); n = min(n, pmp->pm_bpcluster - bp->b_resid); if (error) { brelse(bp); return error; } /* * code to convert from dos directory entries to ufs * directory entries */ pushout = 0; dentp = (struct direntry *)(bp->b_data + on); prev = 0; crnt = (struct dirent *) dirbuf; while ((char *) dentp < bp->b_data + on + n) { /* * printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n", * dentp, prev, crnt, dentp->deName[0], dentp->deAttributes); */ /* * If we have an empty entry or a slot from a * deleted file, or a volume label entry just * concatenate its space onto the end of the * previous entry or, manufacture an empty entry if * there is no previous entry. */ if (dentp->deName[0] == SLOT_EMPTY || dentp->deName[0] == SLOT_DELETED || (dentp->deAttributes & ATTR_VOLUME)) { if (prev) { prev->d_reclen += sizeof(struct direntry); } else { prev = crnt; prev->d_fileno = 0; prev->d_reclen = sizeof(struct direntry); prev->d_type = DT_UNKNOWN; prev->d_namlen = 0; prev->d_name[0] = 0; ncookies++; } } else { /* * this computation of d_fileno must match * the computation of va_fileid in * msdosfs_getattr */ if (dentp->deAttributes & ATTR_DIRECTORY) { /* if this is the root directory */ fileno = getushort(dentp->deStartCluster); if (fileno == MSDOSFSROOT) fileno = 1; } else { /* * if the file's dirent lives in * root dir */ if ((fileno = cn) == MSDOSFSROOT) fileno = 1; fileno = (fileno << 16) | ((dentp - (struct direntry *) bp->b_data) & 0xffff); } crnt->d_fileno = fileno; crnt->d_reclen = sizeof(struct direntry); crnt->d_type = (dentp->deAttributes & ATTR_DIRECTORY) ? DT_DIR : DT_REG; crnt->d_namlen = dos2unixfn(dentp->deName, (u_char *)crnt->d_name); /* * printf("readdir: file %s, fileno %08x, attr %02x, start %08x\n", * crnt->d_name, crnt->d_fileno, dentp->deAttributes, * dentp->deStartCluster); */ prev = crnt; ncookies++; } dentp++; crnt = (struct dirent *) ((char *) crnt + sizeof(struct direntry)); pushout = 1; /* * If our intermediate buffer is full then copy its * contents to user space. I would just use the * buffer the buf header points to but, I'm afraid * that when we brelse() it someone else might find * it in the cache and think its contents are * valid. Maybe there is a way to invalidate the * buffer before brelse()'ing it. */ if ((u_char *) crnt >= &dirbuf[sizeof dirbuf]) { pushout = 0; error = uiomove(dirbuf, sizeof(dirbuf), uio); if (error) break; prev = 0; crnt = (struct dirent *) dirbuf; } } if (pushout) { pushout = 0; error = uiomove(dirbuf, (char *) crnt - (char *) dirbuf, uio); } #if 0 /* * If we have read everything from this block or have read * to end of file then we are done with this block. Mark * it to say the buffer can be reused if need be. */ if (n + on == pmp->pm_bpcluster || (uio->uio_offset - bias) == dep->de_FileSize) bp->b_flags |= B_AGE; #endif /* if 0 */ brelse(bp); if (n == 0) break; } out: ; uio->uio_resid += lost; if (!error && ap->a_ncookies != NULL) { struct dirent* dpStart; struct dirent* dpEnd; struct dirent* dp; u_long *cookies; u_long *cookiep; if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) panic("msdosfs_readdir: unexpected uio from NFS server"); dpStart = (struct dirent *) (uio->uio_iov->iov_base - (uio->uio_offset - off)); dpEnd = (struct dirent *) uio->uio_iov->iov_base; cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK); for (dp = dpStart, cookiep = cookies; dp < dpEnd; dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) { off += dp->d_reclen; *cookiep++ = (u_long) off; } *ap->a_ncookies = ncookies; *ap->a_cookies = cookies; } /* * Set the eofflag (NFS uses it) */ if (ap->a_eofflag) if (dep->de_FileSize - (uio->uio_offset - bias) <= 0) *ap->a_eofflag = 1; else *ap->a_eofflag = 0; return error; } /* * DOS filesystems don't know what symlinks are. */ static int msdosfs_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { return EINVAL; } static int msdosfs_abortop(ap) struct vop_abortop_args /* { struct vnode *a_dvp; struct componentname *a_cnp; } */ *ap; { if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); return 0; } static int msdosfs_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; return (lockmgr(&VTODE(vp)->de_lock, ap->a_flags, &vp->v_interlock, ap->a_p)); } int msdosfs_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; return (lockmgr(&VTODE(vp)->de_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock, ap->a_p)); } int msdosfs_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { return (lockstatus(&VTODE(ap->a_vp)->de_lock)); } /* * vp - address of vnode file the file * bn - which cluster we are interested in mapping to a filesystem block number. * vpp - returns the vnode for the block special file holding the filesystem * containing the file of interest * bnp - address of where to return the filesystem relative block number */ static int msdosfs_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ *ap; { struct denode *dep = VTODE(ap->a_vp); if (ap->a_vpp != NULL) *ap->a_vpp = dep->de_devvp; if (ap->a_bnp == NULL) return 0; if (ap->a_runp) { /* * Sequential clusters should be counted here. */ *ap->a_runp = 0; } if (ap->a_runb) { *ap->a_runb = 0; } return pcbmap(dep, ap->a_bn, ap->a_bnp, 0); } static int msdosfs_reallocblks(ap) struct vop_reallocblks_args /* { struct vnode *a_vp; struct cluster_save *a_buflist; } */ *ap; { /* Currently no support for clustering */ /* XXX */ return ENOSPC; } static int msdosfs_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *ap; { struct buf *bp = ap->a_bp; struct denode *dep = VTODE(bp->b_vp); struct vnode *vp; int error = 0; if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR) panic("msdosfs_strategy: spec"); /* * If we don't already know the filesystem relative block number * then get it using pcbmap(). If pcbmap() returns the block * number as -1 then we've got a hole in the file. DOS filesystems * don't allow files with holes, so we shouldn't ever see this. */ if (bp->b_blkno == bp->b_lblkno) { error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0); if (error) bp->b_blkno = -1; if (bp->b_blkno == -1) clrbuf(bp); } if (bp->b_blkno == -1) { biodone(bp); return error; } #ifdef DIAGNOSTIC #endif /* * Read/write the block from/to the disk that contains the desired * file block. */ vp = dep->de_devvp; bp->b_dev = vp->v_rdev; VOCALL(vp->v_op, VOFFSET(vop_strategy), ap); return 0; } static int msdosfs_print(ap) struct vop_print_args /* { struct vnode *vp; } */ *ap; { struct denode *dep = VTODE(ap->a_vp); printf( "tag VT_MSDOSFS, startcluster %d, dircluster %ld, diroffset %ld ", dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset); printf(" dev %d, %d", major(dep->de_dev), minor(dep->de_dev)); lockmgr_printinfo(&dep->de_lock); printf("\n"); return 0; } static int msdosfs_advlock(ap) struct vop_advlock_args /* { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ *ap; { return EINVAL; /* we don't do locking yet */ } static int msdosfs_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = 1; return 0; case _PC_NAME_MAX: *ap->a_retval = 12; return 0; case _PC_PATH_MAX: *ap->a_retval = PATH_MAX; /* 255? */ return 0; case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return 0; case _PC_NO_TRUNC: *ap->a_retval = 0; return 0; default: return EINVAL; } } /* Global vfs data structures for msdosfs */ vop_t **msdosfs_vnodeop_p; static struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)vfs_cache_lookup }, /* lookup */ { &vop_cachedlookup_desc, (vop_t *)msdosfs_lookup }, /* lookup */ { &vop_create_desc, (vop_t *)msdosfs_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)msdosfs_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)msdosfs_open }, /* open */ { &vop_close_desc, (vop_t *)msdosfs_close }, /* close */ { &vop_access_desc, (vop_t *)msdosfs_access }, /* access */ { &vop_getattr_desc, (vop_t *)msdosfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)msdosfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)msdosfs_read }, /* read */ { &vop_write_desc, (vop_t *)msdosfs_write }, /* write */ +/* XXX: vop_lease */ { &vop_ioctl_desc, (vop_t *)msdosfs_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)msdosfs_select }, /* select */ + { &vop_poll_desc, (vop_t *)msdosfs_poll }, /* poll */ +/* XXX: vop_revoke */ { &vop_mmap_desc, (vop_t *)msdosfs_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)msdosfs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)msdosfs_seek }, /* seek */ { &vop_remove_desc, (vop_t *)msdosfs_remove }, /* remove */ { &vop_link_desc, (vop_t *)msdosfs_link }, /* link */ { &vop_rename_desc, (vop_t *)msdosfs_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)msdosfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)msdosfs_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)msdosfs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)msdosfs_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)msdosfs_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)msdosfs_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)msdosfs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)msdosfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)msdosfs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)msdosfs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)msdosfs_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)msdosfs_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)msdosfs_print }, /* print */ { &vop_islocked_desc, (vop_t *)msdosfs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)msdosfs_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)msdosfs_advlock }, /* advlock */ +/* XXX: vop_blkatoff */ +/* XXX: vop_valloc */ { &vop_reallocblks_desc, (vop_t *)msdosfs_reallocblks }, /* reallocblks */ +/* XXX: vop_vfree */ +/* XXX: vop_truncate */ +/* XXX: vop_update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc msdosfs_vnodeop_opv_desc = { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries }; VNODEOP_SET(msdosfs_vnodeop_opv_desc); diff --git a/sys/fs/portalfs/portal_vnops.c b/sys/fs/portalfs/portal_vnops.c index 281d5632f88d..a8b05cd6e124 100644 --- a/sys/fs/portalfs/portal_vnops.c +++ b/sys/fs/portalfs/portal_vnops.c @@ -1,743 +1,749 @@ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. * * 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. * * @(#)portal_vnops.c 8.14 (Berkeley) 5/21/95 * - * $Id: portal_vnops.c,v 1.19 1997/08/02 14:32:08 bde Exp $ + * $Id: portal_vnops.c,v 1.20 1997/08/16 19:15:18 wollman Exp $ */ /* * Portal Filesystem */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int portal_fileid = PORTAL_ROOTFILEID+1; static int portal_badop __P((void)); static void portal_closefd __P((struct proc *p, int fd)); static int portal_connect __P((struct socket *so, struct socket *so2)); static int portal_enotsupp __P((void)); static int portal_getattr __P((struct vop_getattr_args *ap)); static int portal_inactive __P((struct vop_inactive_args *ap)); static int portal_lookup __P((struct vop_lookup_args *ap)); static int portal_open __P((struct vop_open_args *ap)); static int portal_pathconf __P((struct vop_pathconf_args *ap)); static int portal_print __P((struct vop_print_args *ap)); static int portal_readdir __P((struct vop_readdir_args *ap)); static int portal_reclaim __P((struct vop_reclaim_args *ap)); static int portal_setattr __P((struct vop_setattr_args *ap)); static int portal_vfree __P((struct vop_vfree_args *ap)); static void portal_closefd(p, fd) struct proc *p; int fd; { int error; struct close_args ua; int rc; ua.fd = fd; error = close(p, &ua, &rc); /* * We should never get an error, and there isn't anything * we could do if we got one, so just print a message. */ if (error) printf("portal_closefd: error = %d\n", error); } /* * vp is the current namei directory * cnp is the name to locate in that directory... */ static int portal_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { struct componentname *cnp = ap->a_cnp; struct vnode **vpp = ap->a_vpp; struct vnode *dvp = ap->a_dvp; char *pname = cnp->cn_nameptr; struct portalnode *pt; int error; struct vnode *fvp = 0; char *path; int size; *vpp = NULLVP; if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) return (EROFS); if (cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); /*VOP_LOCK(dvp);*/ return (0); } /* * Do the MALLOC before the getnewvnode since doing so afterward * might cause a bogus v_data pointer to get dereferenced * elsewhere if MALLOC should block. */ MALLOC(pt, struct portalnode *, sizeof(struct portalnode), M_TEMP, M_WAITOK); error = getnewvnode(VT_PORTAL, dvp->v_mount, portal_vnodeop_p, &fvp); if (error) { FREE(pt, M_TEMP); goto bad; } fvp->v_type = VREG; fvp->v_data = pt; /* * Save all of the remaining pathname and * advance the namei next pointer to the end * of the string. */ for (size = 0, path = pname; *path; path++) size++; cnp->cn_consume = size - cnp->cn_namelen; pt->pt_arg = malloc(size+1, M_TEMP, M_WAITOK); pt->pt_size = size+1; bcopy(pname, pt->pt_arg, pt->pt_size); pt->pt_fileid = portal_fileid++; *vpp = fvp; /*VOP_LOCK(fvp);*/ return (0); bad:; if (fvp) vrele(fvp); return (error); } static int portal_connect(so, so2) struct socket *so; struct socket *so2; { /* from unp_connect, bypassing the namei stuff... */ struct socket *so3; struct unpcb *unp2; struct unpcb *unp3; if (so2 == 0) return (ECONNREFUSED); if (so->so_type != so2->so_type) return (EPROTOTYPE); if ((so2->so_options & SO_ACCEPTCONN) == 0) return (ECONNREFUSED); if ((so3 = sonewconn(so2, 0)) == 0) return (ECONNREFUSED); unp2 = sotounpcb(so2); unp3 = sotounpcb(so3); if (unp2->unp_addr) unp3->unp_addr = (struct sockaddr_un *) dup_sockaddr((struct sockaddr *)unp2->unp_addr, 0); so2 = so3; return (unp_connect2(so, so2)); } static int portal_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct socket *so = 0; struct portalnode *pt; struct proc *p = ap->a_p; struct vnode *vp = ap->a_vp; int s; struct uio auio; struct iovec aiov[2]; int res; struct mbuf *cm = 0; struct cmsghdr *cmsg; int newfds; int *ip; int fd; int error; int len; struct portalmount *fmp; struct file *fp; struct portal_cred pcred; /* * Nothing to do when opening the root node. */ if (vp->v_flag & VROOT) return (0); /* * Can't be opened unless the caller is set up * to deal with the side effects. Check for this * by testing whether the p_dupfd has been set. */ if (p->p_dupfd >= 0) return (ENODEV); pt = VTOPORTAL(vp); fmp = VFSTOPORTAL(vp->v_mount); /* * Create a new socket. */ error = socreate(AF_UNIX, &so, SOCK_STREAM, 0, ap->a_p); if (error) goto bad; /* * Reserve some buffer space */ res = pt->pt_size + sizeof(pcred) + 512; /* XXX */ error = soreserve(so, res, res); if (error) goto bad; /* * Kick off connection */ error = portal_connect(so, (struct socket *)fmp->pm_server->f_data); if (error) goto bad; /* * Wait for connection to complete */ /* * XXX: Since the mount point is holding a reference on the * underlying server socket, it is not easy to find out whether * the server process is still running. To handle this problem * we loop waiting for the new socket to be connected (something * which will only happen if the server is still running) or for * the reference count on the server socket to drop to 1, which * will happen if the server dies. Sleep for 5 second intervals * and keep polling the reference count. XXX. */ s = splnet(); while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) { if (fmp->pm_server->f_count == 1) { error = ECONNREFUSED; splx(s); goto bad; } (void) tsleep((caddr_t) &so->so_timeo, PSOCK, "portalcon", 5 * hz); } splx(s); if (so->so_error) { error = so->so_error; goto bad; } /* * Set miscellaneous flags */ so->so_rcv.sb_timeo = 0; so->so_snd.sb_timeo = 0; so->so_rcv.sb_flags |= SB_NOINTR; so->so_snd.sb_flags |= SB_NOINTR; pcred.pcr_flag = ap->a_mode; pcred.pcr_uid = ap->a_cred->cr_uid; pcred.pcr_ngroups = ap->a_cred->cr_ngroups; bcopy(ap->a_cred->cr_groups, pcred.pcr_groups, NGROUPS * sizeof(gid_t)); aiov[0].iov_base = (caddr_t) &pcred; aiov[0].iov_len = sizeof(pcred); aiov[1].iov_base = pt->pt_arg; aiov[1].iov_len = pt->pt_size; auio.uio_iov = aiov; auio.uio_iovcnt = 2; auio.uio_rw = UIO_WRITE; auio.uio_segflg = UIO_SYSSPACE; auio.uio_procp = p; auio.uio_offset = 0; auio.uio_resid = aiov[0].iov_len + aiov[1].iov_len; error = sosend(so, (struct sockaddr *) 0, &auio, (struct mbuf *) 0, (struct mbuf *) 0, 0, p); if (error) goto bad; len = auio.uio_resid = sizeof(int); do { struct mbuf *m = 0; int flags = MSG_WAITALL; error = soreceive(so, (struct sockaddr **) 0, &auio, &m, &cm, &flags); if (error) goto bad; /* * Grab an error code from the mbuf. */ if (m) { m = m_pullup(m, sizeof(int)); /* Needed? */ if (m) { error = *(mtod(m, int *)); m_freem(m); } else { error = EINVAL; } } else { if (cm == 0) { error = ECONNRESET; /* XXX */ #ifdef notdef break; #endif } } } while (cm == 0 && auio.uio_resid == len && !error); if (cm == 0) goto bad; if (auio.uio_resid) { error = 0; #ifdef notdef error = EMSGSIZE; goto bad; #endif } /* * XXX: Break apart the control message, and retrieve the * received file descriptor. Note that more than one descriptor * may have been received, or that the rights chain may have more * than a single mbuf in it. What to do? */ cmsg = mtod(cm, struct cmsghdr *); newfds = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof (int); if (newfds == 0) { error = ECONNREFUSED; goto bad; } /* * At this point the rights message consists of a control message * header, followed by a data region containing a vector of * integer file descriptors. The fds were allocated by the action * of receiving the control message. */ ip = (int *) (cmsg + 1); fd = *ip++; if (newfds > 1) { /* * Close extra fds. */ int i; printf("portal_open: %d extra fds\n", newfds - 1); for (i = 1; i < newfds; i++) { portal_closefd(p, *ip); ip++; } } /* * Check that the mode the file is being opened for is a subset * of the mode of the existing descriptor. */ fp = p->p_fd->fd_ofiles[fd]; if (((ap->a_mode & (FREAD|FWRITE)) | fp->f_flag) != fp->f_flag) { portal_closefd(p, fd); error = EACCES; goto bad; } /* * Save the dup fd in the proc structure then return the * special error code (ENXIO) which causes magic things to * happen in vn_open. The whole concept is, well, hmmm. */ p->p_dupfd = fd; error = ENXIO; bad:; /* * And discard the control message. */ if (cm) { m_freem(cm); } if (so) { soshutdown(so, 2); soclose(so); } return (error); } static int portal_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct vattr *vap = ap->a_vap; struct timeval tv; bzero(vap, sizeof(*vap)); vattr_null(vap); vap->va_uid = 0; vap->va_gid = 0; vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; vap->va_size = DEV_BSIZE; vap->va_blocksize = DEV_BSIZE; microtime(&tv); TIMEVAL_TO_TIMESPEC(&tv, &vap->va_atime); vap->va_mtime = vap->va_atime; vap->va_ctime = vap->va_ctime; vap->va_gen = 0; vap->va_flags = 0; vap->va_rdev = 0; /* vap->va_qbytes = 0; */ vap->va_bytes = 0; /* vap->va_qsize = 0; */ if (vp->v_flag & VROOT) { vap->va_type = VDIR; vap->va_mode = S_IRUSR|S_IWUSR|S_IXUSR| S_IRGRP|S_IWGRP|S_IXGRP| S_IROTH|S_IWOTH|S_IXOTH; vap->va_nlink = 2; vap->va_fileid = 2; } else { vap->va_type = VREG; vap->va_mode = S_IRUSR|S_IWUSR| S_IRGRP|S_IWGRP| S_IROTH|S_IWOTH; vap->va_nlink = 1; vap->va_fileid = VTOPORTAL(vp)->pt_fileid; } return (0); } static int portal_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { /* * Can't mess with the root vnode */ if (ap->a_vp->v_flag & VROOT) return (EACCES); return (0); } /* * Fake readdir, just return empty directory. * It is hard to deal with '.' and '..' so don't bother. */ static int portal_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_long *a_cookies; int a_ncookies; } */ *ap; { /* * We don't allow exporting portal mounts, and currently local * requests do not need cookies. */ if (ap->a_ncookies) panic("portal_readdir: not hungry"); return (0); } static int portal_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { VOP_UNLOCK(ap->a_vp, 0, ap->a_p); return (0); } static int portal_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { struct portalnode *pt = VTOPORTAL(ap->a_vp); if (pt->pt_arg) { free((caddr_t) pt->pt_arg, M_TEMP); pt->pt_arg = 0; } FREE(ap->a_vp->v_data, M_TEMP); ap->a_vp->v_data = 0; return (0); } /* * Return POSIX pathconf information applicable to special devices. */ static int portal_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_MAX_CANON: *ap->a_retval = MAX_CANON; return (0); case _PC_MAX_INPUT: *ap->a_retval = MAX_INPUT; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_VDISABLE: *ap->a_retval = _POSIX_VDISABLE; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Print out the contents of a Portal vnode. */ /* ARGSUSED */ static int portal_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_PORTAL, portal vnode\n"); return (0); } /*void*/ static int portal_vfree(ap) struct vop_vfree_args /* { struct vnode *a_pvp; ino_t a_ino; int a_mode; } */ *ap; { return (0); } /* * Portal vnode unsupported operation */ static int portal_enotsupp() { return (EOPNOTSUPP); } /* * Portal "should never get here" operation */ static int portal_badop() { panic("portal: bad op"); /* NOTREACHED */ } #define portal_create ((int (*) __P((struct vop_create_args *)))portal_enotsupp) #define portal_mknod ((int (*) __P((struct vop_mknod_args *)))portal_enotsupp) #define portal_close ((int (*) __P((struct vop_close_args *)))nullop) #define portal_access ((int (*) __P((struct vop_access_args *)))nullop) #define portal_read ((int (*) __P((struct vop_read_args *)))portal_enotsupp) #define portal_write ((int (*) __P((struct vop_write_args *)))portal_enotsupp) #define portal_ioctl ((int (*) __P((struct vop_ioctl_args *)))portal_enotsupp) -#define portal_select ((int (*) __P((struct vop_select_args *)))portal_enotsupp) #define portal_mmap ((int (*) __P((struct vop_mmap_args *)))portal_enotsupp) +#define portal_poll vop_nopoll #define portal_revoke vop_revoke #define portal_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define portal_seek ((int (*) __P((struct vop_seek_args *)))nullop) #define portal_remove ((int (*) __P((struct vop_remove_args *)))portal_enotsupp) #define portal_link ((int (*) __P((struct vop_link_args *)))portal_enotsupp) #define portal_rename ((int (*) __P((struct vop_rename_args *)))portal_enotsupp) #define portal_mkdir ((int (*) __P((struct vop_mkdir_args *)))portal_enotsupp) #define portal_rmdir ((int (*) __P((struct vop_rmdir_args *)))portal_enotsupp) #define portal_symlink \ ((int (*) __P((struct vop_symlink_args *)))portal_enotsupp) #define portal_readlink \ ((int (*) __P((struct vop_readlink_args *)))portal_enotsupp) #define portal_abortop ((int (*) __P((struct vop_abortop_args *)))nullop) #define portal_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock) #define portal_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) #define portal_bmap ((int (*) __P((struct vop_bmap_args *)))portal_badop) #define portal_strategy \ ((int (*) __P((struct vop_strategy_args *)))portal_badop) #define portal_islocked \ ((int (*) __P((struct vop_islocked_args *)))vop_noislocked) #define fifo_islocked ((int(*) __P((struct vop_islocked_args *)))vop_noislocked) #define portal_advlock \ ((int (*) __P((struct vop_advlock_args *)))portal_enotsupp) #define portal_blkatoff \ ((int (*) __P((struct vop_blkatoff_args *)))portal_enotsupp) #define portal_valloc ((int(*) __P(( \ struct vnode *pvp, \ int mode, \ struct ucred *cred, \ struct vnode **vpp))) portal_enotsupp) #define portal_truncate \ ((int (*) __P((struct vop_truncate_args *)))portal_enotsupp) #define portal_update ((int (*) __P((struct vop_update_args *)))portal_enotsupp) #define portal_bwrite ((int (*) __P((struct vop_bwrite_args *)))portal_enotsupp) vop_t **portal_vnodeop_p; static struct vnodeopv_entry_desc portal_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)portal_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)portal_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)portal_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)portal_open }, /* open */ { &vop_close_desc, (vop_t *)portal_close }, /* close */ { &vop_access_desc, (vop_t *)portal_access }, /* access */ { &vop_getattr_desc, (vop_t *)portal_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)portal_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)portal_read }, /* read */ { &vop_write_desc, (vop_t *)portal_write }, /* write */ +/* XXX: vop_lease */ { &vop_ioctl_desc, (vop_t *)portal_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)portal_select }, /* select */ + { &vop_poll_desc, (vop_t *)portal_poll }, /* poll */ { &vop_mmap_desc, (vop_t *)portal_mmap }, /* mmap */ { &vop_revoke_desc, (vop_t *)portal_revoke }, /* revoke */ { &vop_fsync_desc, (vop_t *)portal_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)portal_seek }, /* seek */ { &vop_remove_desc, (vop_t *)portal_remove }, /* remove */ { &vop_link_desc, (vop_t *)portal_link }, /* link */ { &vop_rename_desc, (vop_t *)portal_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)portal_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)portal_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)portal_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)portal_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)portal_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)portal_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)portal_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)portal_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)portal_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)portal_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)portal_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)portal_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)portal_print }, /* print */ { &vop_islocked_desc, (vop_t *)portal_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)portal_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)portal_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)portal_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)portal_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)portal_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)portal_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)portal_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)portal_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc portal_vnodeop_opv_desc = { &portal_vnodeop_p, portal_vnodeop_entries }; VNODEOP_SET(portal_vnodeop_opv_desc); diff --git a/sys/fs/procfs/procfs_vnops.c b/sys/fs/procfs/procfs_vnops.c index f876318e6aec..d34fb2d53135 100644 --- a/sys/fs/procfs/procfs_vnops.c +++ b/sys/fs/procfs/procfs_vnops.c @@ -1,1017 +1,1024 @@ /* * Copyright (c) 1993, 1995 Jan-Simon Pendry * Copyright (c) 1993, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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. * * @(#)procfs_vnops.c 8.18 (Berkeley) 5/21/95 * - * $Id: procfs_vnops.c,v 1.30 1997/08/02 14:32:20 bde Exp $ + * $Id: procfs_vnops.c,v 1.31 1997/08/12 04:34:30 sef Exp $ */ /* * procfs vnode interface */ #include #include #include #include #include #include #include #include #include #include #include #include #include static int procfs_abortop __P((struct vop_abortop_args *)); static int procfs_access __P((struct vop_access_args *)); static int procfs_badop __P((void)); static int procfs_bmap __P((struct vop_bmap_args *)); static int procfs_close __P((struct vop_close_args *)); static int procfs_getattr __P((struct vop_getattr_args *)); static int procfs_inactive __P((struct vop_inactive_args *)); static int procfs_ioctl __P((struct vop_ioctl_args *)); static int procfs_lookup __P((struct vop_lookup_args *)); static int procfs_open __P((struct vop_open_args *)); static int procfs_pathconf __P((struct vop_pathconf_args *ap)); static int procfs_print __P((struct vop_print_args *)); static int procfs_readdir __P((struct vop_readdir_args *)); static int procfs_readlink __P((struct vop_readlink_args *)); static int procfs_reclaim __P((struct vop_reclaim_args *)); static int procfs_setattr __P((struct vop_setattr_args *)); /* * This is a list of the valid names in the * process-specific sub-directories. It is * used in procfs_lookup and procfs_readdir */ struct proc_target { u_char pt_type; u_char pt_namlen; char *pt_name; pfstype pt_pfstype; int (*pt_valid) __P((struct proc *p)); } proc_targets[] = { #define N(s) sizeof(s)-1, s /* name type validp */ { DT_DIR, N("."), Pproc, NULL }, { DT_DIR, N(".."), Proot, NULL }, { DT_REG, N("file"), Pfile, procfs_validfile }, { DT_REG, N("mem"), Pmem, NULL }, { DT_REG, N("regs"), Pregs, procfs_validregs }, { DT_REG, N("fpregs"), Pfpregs, procfs_validfpregs }, { DT_REG, N("ctl"), Pctl, NULL }, { DT_REG, N("status"), Pstatus, NULL }, { DT_REG, N("note"), Pnote, NULL }, { DT_REG, N("notepg"), Pnotepg, NULL }, { DT_REG, N("map"), Pmap, procfs_validmap }, { DT_REG, N("etype"), Ptype, procfs_validtype }, #undef N }; static const int nproc_targets = sizeof(proc_targets) / sizeof(proc_targets[0]); static pid_t atopid __P((const char *, u_int)); /* * set things up for doing i/o on * the pfsnode (vp). (vp) is locked * on entry, and should be left locked * on exit. * * for procfs we don't need to do anything * in particular for i/o. all that is done * is to support exclusive open on process * memory images. */ static int procfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct proc *p1 = ap->a_p, *p2 = PFIND(pfs->pfs_pid); if (p2 == NULL) return ENOENT; switch (pfs->pfs_type) { case Pmem: if ((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL) || (pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE)) return (EBUSY); if (!CHECKIO(p1, p2) && (p1->p_cred->pc_ucred->cr_gid != KMEM_GROUP)) return EPERM; if (ap->a_mode & FWRITE) pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL); return (0); default: break; } return (0); } /* * close the pfsnode (vp) after doing i/o. * (vp) is not locked on entry or exit. * * nothing to do for procfs other than undo * any exclusive open flag (see _open above). */ static int procfs_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); switch (pfs->pfs_type) { case Pmem: if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL)) pfs->pfs_flags &= ~(FWRITE|O_EXCL); break; default: break; } return (0); } /* * do an ioctl operation on pfsnode (vp). * (vp) is not locked on entry or exit. */ static int procfs_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (ENOTTY); } /* * do block mapping for pfsnode (vp). * since we don't use the buffer cache * for procfs this function should never * be called. in any case, it's not clear * what part of the kernel ever makes use * of this function. for sanity, this is the * usual no-op bmap, although returning * (EIO) would be a reasonable alternative. */ static int procfs_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; } */ *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; if (ap->a_runp != NULL) *ap->a_runp = 0; return (0); } /* * procfs_inactive is called when the pfsnode * is vrele'd and the reference count goes * to zero. (vp) will be on the vnode free * list, so to get it back vget() must be * used. * * for procfs, check if the process is still * alive and if it isn't then just throw away * the vnode by calling vgone(). this may * be overkill and a waste of time since the * chances are that the process will still be * there and PFIND is not free. * * (vp) is locked on entry, but must be unlocked on exit. */ static int procfs_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; struct pfsnode *pfs = VTOPFS(vp); VOP_UNLOCK(vp, 0, ap->a_p); if (PFIND(pfs->pfs_pid) == 0) vgone(vp); return (0); } /* * _reclaim is called when getnewvnode() * wants to make use of an entry on the vnode * free list. at this time the filesystem needs * to free any private data and remove the node * from any private lists. */ static int procfs_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { return (procfs_freevp(ap->a_vp)); } /* * Return POSIX pathconf information applicable to special devices. */ static int procfs_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_MAX_CANON: *ap->a_retval = MAX_CANON; return (0); case _PC_MAX_INPUT: *ap->a_retval = MAX_INPUT; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_VDISABLE: *ap->a_retval = _POSIX_VDISABLE; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * _print is used for debugging. * just print a readable description * of (vp). */ static int procfs_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); printf("tag VT_PROCFS, type %s, pid %d, mode %x, flags %x\n", pfs->pfs_type, pfs->pfs_pid, pfs->pfs_mode, pfs->pfs_flags); return (0); } /* * _abortop is called when operations such as * rename and create fail. this entry is responsible * for undoing any side-effects caused by the lookup. * this will always include freeing the pathname buffer. */ static int procfs_abortop(ap) struct vop_abortop_args /* { struct vnode *a_dvp; struct componentname *a_cnp; } */ *ap; { if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); return (0); } /* * generic entry point for unsupported operations */ static int procfs_badop() { return (EIO); } /* * Invent attributes for pfsnode (vp) and store * them in (vap). * Directories lengths are returned as zero since * any real length would require the genuine size * to be computed, and nothing cares anyway. * * this is relatively minimal for procfs. */ static int procfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct vattr *vap = ap->a_vap; struct proc *procp; int error; /* * First make sure that the process and its credentials * still exist. */ switch (pfs->pfs_type) { case Proot: case Pcurproc: procp = 0; break; default: procp = PFIND(pfs->pfs_pid); if (procp == 0 || procp->p_cred == NULL || procp->p_ucred == NULL) return (ENOENT); } error = 0; /* start by zeroing out the attributes */ VATTR_NULL(vap); /* next do all the common fields */ vap->va_type = ap->a_vp->v_type; vap->va_mode = pfs->pfs_mode; vap->va_fileid = pfs->pfs_fileno; vap->va_flags = 0; vap->va_blocksize = PAGE_SIZE; vap->va_bytes = vap->va_size = 0; /* * Make all times be current TOD. * It would be possible to get the process start * time from the p_stat structure, but there's * no "file creation" time stamp anyway, and the * p_stat structure is not addressible if u. gets * swapped out for that process. */ { struct timeval tv; microtime(&tv); TIMEVAL_TO_TIMESPEC(&tv, &vap->va_ctime); } vap->va_atime = vap->va_mtime = vap->va_ctime; /* * If the process has exercised some setuid or setgid * privilege, then rip away read/write permission so * that only root can gain access. */ switch (pfs->pfs_type) { case Pctl: case Pregs: case Pfpregs: if (procp->p_flag & P_SUGID) vap->va_mode &= ~((VREAD|VWRITE)| ((VREAD|VWRITE)>>3)| ((VREAD|VWRITE)>>6)); break; case Pmem: /* Retain group kmem readablity. */ if (procp->p_flag & P_SUGID) vap->va_mode &= ~(VREAD|VWRITE); break; default: break; } /* * now do the object specific fields * * The size could be set from struct reg, but it's hardly * worth the trouble, and it puts some (potentially) machine * dependent data into this machine-independent code. If it * becomes important then this function should break out into * a per-file stat function in the corresponding .c file. */ switch (pfs->pfs_type) { case Proot: /* * Set nlink to 1 to tell fts(3) we don't actually know. */ vap->va_nlink = 1; vap->va_uid = 0; vap->va_gid = 0; vap->va_size = vap->va_bytes = DEV_BSIZE; break; case Pcurproc: { char buf[16]; /* should be enough */ vap->va_nlink = 1; vap->va_uid = 0; vap->va_gid = 0; vap->va_size = vap->va_bytes = sprintf(buf, "%ld", (long)curproc->p_pid); break; } case Pproc: vap->va_nlink = nproc_targets; vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = procp->p_ucred->cr_gid; vap->va_size = vap->va_bytes = DEV_BSIZE; break; case Pfile: error = EOPNOTSUPP; break; case Pmem: vap->va_nlink = 1; /* * If we denied owner access earlier, then we have to * change the owner to root - otherwise 'ps' and friends * will break even though they are setgid kmem. *SIGH* */ if (procp->p_flag & P_SUGID) vap->va_uid = 0; else vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = KMEM_GROUP; break; case Ptype: case Pmap: case Pregs: vap->va_bytes = vap->va_size = sizeof(struct reg); vap->va_nlink = 1; vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = procp->p_ucred->cr_gid; break; case Pfpregs: vap->va_bytes = vap->va_size = sizeof(struct fpreg); case Pctl: case Pstatus: case Pnote: case Pnotepg: vap->va_nlink = 1; vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = procp->p_ucred->cr_gid; break; default: panic("procfs_getattr"); } return (error); } static int procfs_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { /* * just fake out attribute setting * it's not good to generate an error * return, otherwise things like creat() * will fail when they try to set the * file length to 0. worse, this means * that echo $note > /proc/$pid/note will fail. */ return (0); } /* * implement access checking. * * something very similar to this code is duplicated * throughout the 4bsd kernel and should be moved * into kern/vfs_subr.c sometime. * * actually, the check for super-user is slightly * broken since it will allow read access to write-only * objects. this doesn't cause any particular trouble * but does mean that the i/o entry points need to check * that the operation really does make sense. */ static int procfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vattr *vap; struct vattr vattr; int error; /* * If you're the super-user, * you always get access. */ if (ap->a_cred->cr_uid == 0) return (0); vap = &vattr; error = VOP_GETATTR(ap->a_vp, vap, ap->a_cred, ap->a_p); if (error) return (error); /* * Access check is based on only one of owner, group, public. * If not owner, then check group. If not a member of the * group, then check public access. */ if (ap->a_cred->cr_uid != vap->va_uid) { gid_t *gp; int i; ap->a_mode >>= 3; gp = ap->a_cred->cr_groups; for (i = 0; i < ap->a_cred->cr_ngroups; i++, gp++) if (vap->va_gid == *gp) goto found; ap->a_mode >>= 3; found: ; } if ((vap->va_mode & ap->a_mode) == ap->a_mode) return (0); return (EACCES); } /* * lookup. this is incredibly complicated in the * general case, however for most pseudo-filesystems * very little needs to be done. * * unless you want to get a migraine, just make sure your * filesystem doesn't do any locking of its own. otherwise * read and inwardly digest ufs_lookup(). */ static int procfs_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { struct componentname *cnp = ap->a_cnp; struct vnode **vpp = ap->a_vpp; struct vnode *dvp = ap->a_dvp; char *pname = cnp->cn_nameptr; struct proc *curp = cnp->cn_proc; int error = 0; struct proc_target *pt; struct vnode *fvp; pid_t pid; struct pfsnode *pfs; struct proc *p; int i; *vpp = NULL; if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) return (EROFS); if (cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); /* vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, curp); */ return (0); } pfs = VTOPFS(dvp); switch (pfs->pfs_type) { case Proot: if (cnp->cn_flags & ISDOTDOT) return (EIO); if (CNEQ(cnp, "curproc", 7)) return (procfs_allocvp(dvp->v_mount, vpp, 0, Pcurproc)); pid = atopid(pname, cnp->cn_namelen); if (pid == NO_PID) break; p = PFIND(pid); if (p == 0) break; return (procfs_allocvp(dvp->v_mount, vpp, pid, Pproc)); case Pproc: if (cnp->cn_flags & ISDOTDOT) return (procfs_root(dvp->v_mount, vpp)); p = PFIND(pfs->pfs_pid); if (p == 0) break; for (pt = proc_targets, i = 0; i < nproc_targets; pt++, i++) { if (cnp->cn_namelen == pt->pt_namlen && bcmp(pt->pt_name, pname, cnp->cn_namelen) == 0 && (pt->pt_valid == NULL || (*pt->pt_valid)(p))) goto found; } break; found: if (pt->pt_pfstype == Pfile) { fvp = procfs_findtextvp(p); /* We already checked that it exists. */ VREF(fvp); vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, curp); *vpp = fvp; return (0); } return (procfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid, pt->pt_pfstype)); default: return (ENOTDIR); } return (cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS); } /* * Does this process have a text file? */ int procfs_validfile(p) struct proc *p; { return (procfs_findtextvp(p) != NULLVP); } /* * readdir returns directory entries from pfsnode (vp). * * the strategy here with procfs is to generate a single * directory entry at a time (struct pfsdent) and then * copy that out to userland using uiomove. a more efficent * though more complex implementation, would try to minimize * the number of calls to uiomove(). for procfs, this is * hardly worth the added code complexity. * * this should just be done through read() */ static int procfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_long *a_cookies; int a_ncookies; } */ *ap; { struct uio *uio = ap->a_uio; struct pfsdent d; struct pfsdent *dp = &d; struct pfsnode *pfs; int error; int count; int i; /* * We don't allow exporting procfs mounts, and currently local * requests do not need cookies. */ if (ap->a_ncookies) panic("procfs_readdir: not hungry"); pfs = VTOPFS(ap->a_vp); if (uio->uio_resid < UIO_MX) return (EINVAL); if (uio->uio_offset & (UIO_MX-1)) return (EINVAL); if (uio->uio_offset < 0) return (EINVAL); error = 0; count = 0; i = uio->uio_offset / UIO_MX; switch (pfs->pfs_type) { /* * this is for the process-specific sub-directories. * all that is needed to is copy out all the entries * from the procent[] table (top of this file). */ case Pproc: { struct proc *p; struct proc_target *pt; p = PFIND(pfs->pfs_pid); if (p == NULL) break; for (pt = &proc_targets[i]; uio->uio_resid >= UIO_MX && i < nproc_targets; pt++, i++) { if (pt->pt_valid && (*pt->pt_valid)(p) == 0) continue; dp->d_reclen = UIO_MX; dp->d_fileno = PROCFS_FILENO(pfs->pfs_pid, pt->pt_pfstype); dp->d_namlen = pt->pt_namlen; bcopy(pt->pt_name, dp->d_name, pt->pt_namlen + 1); dp->d_type = pt->pt_type; if (error = uiomove((caddr_t)dp, UIO_MX, uio)) break; } break; } /* * this is for the root of the procfs filesystem * what is needed is a special entry for "curproc" * followed by an entry for each process on allproc #ifdef PROCFS_ZOMBIE * and zombproc. #endif */ case Proot: { #ifdef PROCFS_ZOMBIE int doingzomb = 0; #endif int pcnt = 0; volatile struct proc *p = allproc.lh_first; again: for (; p && uio->uio_resid >= UIO_MX; i++, pcnt++) { bzero((char *) dp, UIO_MX); dp->d_reclen = UIO_MX; switch (i) { case 0: /* `.' */ case 1: /* `..' */ dp->d_fileno = PROCFS_FILENO(0, Proot); dp->d_namlen = i + 1; bcopy("..", dp->d_name, dp->d_namlen); dp->d_name[i + 1] = '\0'; dp->d_type = DT_DIR; break; case 2: dp->d_fileno = PROCFS_FILENO(0, Pcurproc); dp->d_namlen = 7; bcopy("curproc", dp->d_name, 8); dp->d_type = DT_LNK; break; default: while (pcnt < i) { pcnt++; p = p->p_list.le_next; if (!p) goto done; } dp->d_fileno = PROCFS_FILENO(p->p_pid, Pproc); dp->d_namlen = sprintf(dp->d_name, "%ld", (long)p->p_pid); dp->d_type = DT_REG; p = p->p_list.le_next; break; } if (error = uiomove((caddr_t)dp, UIO_MX, uio)) break; } done: #ifdef PROCFS_ZOMBIE if (p == 0 && doingzomb == 0) { doingzomb = 1; p = zombproc.lh_first; goto again; } #endif break; } default: error = ENOTDIR; break; } uio->uio_offset = i * UIO_MX; return (error); } /* * readlink reads the link of `curproc' */ static int procfs_readlink(ap) struct vop_readlink_args *ap; { struct uio *uio = ap->a_uio; char buf[16]; /* should be enough */ int len; if (VTOPFS(ap->a_vp)->pfs_fileno != PROCFS_FILENO(0, Pcurproc)) return (EINVAL); len = sprintf(buf, "%ld", (long)curproc->p_pid); return (uiomove((caddr_t)buf, len, ap->a_uio)); } /* * convert decimal ascii to pid_t */ static pid_t atopid(b, len) const char *b; u_int len; { pid_t p = 0; while (len--) { char c = *b++; if (c < '0' || c > '9') return (NO_PID); p = 10 * p + (c - '0'); if (p > PID_MAX) return (NO_PID); } return (p); } #define procfs_create ((int (*) __P((struct vop_create_args *))) procfs_badop) #define procfs_mknod ((int (*) __P((struct vop_mknod_args *))) procfs_badop) #define procfs_read procfs_rw #define procfs_write procfs_rw -#define procfs_select ((int (*) __P((struct vop_select_args *))) procfs_badop) #define procfs_mmap ((int (*) __P((struct vop_mmap_args *))) procfs_badop) +#define procfs_poll vop_nopoll #define procfs_revoke vop_revoke #define procfs_fsync ((int (*) __P((struct vop_fsync_args *))) procfs_badop) #define procfs_seek ((int (*) __P((struct vop_seek_args *))) procfs_badop) #define procfs_remove ((int (*) __P((struct vop_remove_args *))) procfs_badop) #define procfs_link ((int (*) __P((struct vop_link_args *))) procfs_badop) #define procfs_rename ((int (*) __P((struct vop_rename_args *))) procfs_badop) #define procfs_mkdir ((int (*) __P((struct vop_mkdir_args *))) procfs_badop) #define procfs_rmdir ((int (*) __P((struct vop_rmdir_args *))) procfs_badop) #define procfs_symlink ((int (*) __P((struct vop_symlink_args *))) procfs_badop) #define procfs_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock) #define procfs_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) #define procfs_strategy ((int (*) __P((struct vop_strategy_args *))) procfs_badop) #define procfs_islocked \ ((int (*) __P((struct vop_islocked_args *)))vop_noislocked) #define procfs_advlock ((int (*) __P((struct vop_advlock_args *))) procfs_badop) #define procfs_blkatoff ((int (*) __P((struct vop_blkatoff_args *))) procfs_badop) #define procfs_valloc ((int (*) __P((struct vop_valloc_args *))) procfs_badop) #define procfs_vfree ((int (*) __P((struct vop_vfree_args *))) nullop) #define procfs_truncate ((int (*) __P((struct vop_truncate_args *))) procfs_badop) #define procfs_update ((int (*) __P((struct vop_update_args *))) nullop) /* * procfs vnode operations. */ vop_t **procfs_vnodeop_p; static struct vnodeopv_entry_desc procfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)procfs_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)procfs_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)procfs_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)procfs_open }, /* open */ { &vop_close_desc, (vop_t *)procfs_close }, /* close */ { &vop_access_desc, (vop_t *)procfs_access }, /* access */ { &vop_getattr_desc, (vop_t *)procfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)procfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)procfs_read }, /* read */ { &vop_write_desc, (vop_t *)procfs_write }, /* write */ +/* XXX: vop_lease */ { &vop_ioctl_desc, (vop_t *)procfs_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)procfs_select }, /* select */ + { &vop_poll_desc, (vop_t *)procfs_poll }, /* poll */ { &vop_mmap_desc, (vop_t *)procfs_mmap }, /* mmap */ { &vop_revoke_desc, (vop_t *)procfs_revoke }, /* revoke */ { &vop_fsync_desc, (vop_t *)procfs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)procfs_seek }, /* seek */ { &vop_remove_desc, (vop_t *)procfs_remove }, /* remove */ { &vop_link_desc, (vop_t *)procfs_link }, /* link */ { &vop_rename_desc, (vop_t *)procfs_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)procfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)procfs_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)procfs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)procfs_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)procfs_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)procfs_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)procfs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)procfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)procfs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)procfs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)procfs_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)procfs_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)procfs_print }, /* print */ { &vop_islocked_desc, (vop_t *)procfs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)procfs_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)procfs_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)procfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)procfs_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)procfs_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)procfs_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)procfs_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ +/* XXX: vop_bwrite */ { NULL, NULL } }; static struct vnodeopv_desc procfs_vnodeop_opv_desc = { &procfs_vnodeop_p, procfs_vnodeop_entries }; VNODEOP_SET(procfs_vnodeop_opv_desc); diff --git a/sys/fs/specfs/spec_vnops.c b/sys/fs/specfs/spec_vnops.c index 98119cb95f3a..b5407eebcd33 100644 --- a/sys/fs/specfs/spec_vnops.c +++ b/sys/fs/specfs/spec_vnops.c @@ -1,906 +1,909 @@ /* * Copyright (c) 1989, 1993, 1995 * 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. * * @(#)spec_vnops.c 8.14 (Berkeley) 5/21/95 - * $Id: spec_vnops.c,v 1.40 1997/05/29 13:29:13 tegge Exp $ + * $Id: spec_vnops.c,v 1.41 1997/09/02 20:06:12 bde Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int spec_ebadf __P((void)); static int spec_getattr __P((struct vop_getattr_args *)); struct vnode *speclisth[SPECHSZ]; vop_t **spec_vnodeop_p; static struct vnodeopv_entry_desc spec_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)spec_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)spec_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)spec_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)spec_open }, /* open */ { &vop_close_desc, (vop_t *)spec_close }, /* close */ { &vop_access_desc, (vop_t *)spec_access }, /* access */ { &vop_getattr_desc, (vop_t *)spec_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)spec_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)spec_read }, /* read */ { &vop_write_desc, (vop_t *)spec_write }, /* write */ { &vop_lease_desc, (vop_t *)spec_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)spec_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)spec_select }, /* select */ + { &vop_poll_desc, (vop_t *)spec_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)spec_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)spec_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)spec_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)spec_seek }, /* seek */ { &vop_remove_desc, (vop_t *)spec_remove }, /* remove */ { &vop_link_desc, (vop_t *)spec_link }, /* link */ { &vop_rename_desc, (vop_t *)spec_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)spec_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)spec_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)spec_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)spec_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)spec_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)spec_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)spec_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)spec_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)spec_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)spec_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)spec_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)spec_print }, /* print */ { &vop_islocked_desc, (vop_t *)spec_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)spec_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)spec_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)spec_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)spec_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)spec_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)spec_update }, /* update */ - { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { &vop_getpages_desc, (vop_t *)spec_getpages}, /* getpages */ +/* XXX: vop_putpages */ + { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc spec_vnodeop_opv_desc = { &spec_vnodeop_p, spec_vnodeop_entries }; VNODEOP_SET(spec_vnodeop_opv_desc); static void spec_getpages_iodone __P((struct buf *bp)); /* * Trivial lookup routine that always fails. */ int spec_lookup(ap) struct vop_lookup_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { *ap->a_vpp = NULL; return (ENOTDIR); } /* * Open a special file. */ /* ARGSUSED */ int spec_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct proc *p = ap->a_p; struct vnode *bvp, *vp = ap->a_vp; dev_t bdev, dev = (dev_t)vp->v_rdev; int maj = major(dev); int error; /* * Don't allow open if fs is mounted -nodev. */ if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV)) return (ENXIO); switch (vp->v_type) { case VCHR: if ((u_int)maj >= nchrdev) return (ENXIO); if ( (cdevsw[maj] == NULL) || (cdevsw[maj]->d_open == NULL)) return ENXIO; if (ap->a_cred != FSCRED && (ap->a_mode & FWRITE)) { /* * When running in very secure mode, do not allow * opens for writing of any disk character devices. */ if (securelevel >= 2 && cdevsw[maj]->d_bdev && cdevsw[maj]->d_bdev->d_flags == D_DISK) return (EPERM); /* * When running in secure mode, do not allow opens * for writing of /dev/mem, /dev/kmem, or character * devices whose corresponding block devices are * currently mounted. */ if (securelevel >= 1) { if ((bdev = chrtoblk(dev)) != NODEV && vfinddev(bdev, VBLK, &bvp) && bvp->v_usecount > 0 && (error = vfs_mountedon(bvp))) return (error); if (iskmemdev(dev)) return (EPERM); } } #if 0 /* * Lite2 stuff. We will almost certainly do this * differently with devfs. The only use of this flag * is in dead_read to make ttys return EOF instead of * EIO when they are dead. Pre-lite2 FreeBSD returns * EOF for all character devices. */ if (cdevsw[maj]->d_type == D_TTY) vp->v_flag |= VISTTY; #endif VOP_UNLOCK(vp, 0, p); error = (*cdevsw[maj]->d_open)(dev, ap->a_mode, S_IFCHR, p); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); return (error); case VBLK: if ((u_int)maj >= nblkdev) return (ENXIO); if ( (bdevsw[maj] == NULL) || (bdevsw[maj]->d_open == NULL)) return ENXIO; /* * When running in very secure mode, do not allow * opens for writing of any disk block devices. */ if (securelevel >= 2 && ap->a_cred != FSCRED && (ap->a_mode & FWRITE) && bdevsw[maj]->d_flags == D_DISK) return (EPERM); /* * Do not allow opens of block devices that are * currently mounted. */ error = vfs_mountedon(vp); if (error) return (error); return ((*bdevsw[maj]->d_open)(dev, ap->a_mode, S_IFBLK, p)); } return (0); } /* * Vnode op for read */ /* ARGSUSED */ int spec_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct uio *uio = ap->a_uio; struct proc *p = uio->uio_procp; struct buf *bp; daddr_t bn, nextbn; long bsize, bscale; struct partinfo dpart; int n, on, majordev; d_ioctl_t *ioctl; int error = 0; dev_t dev; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_READ) panic("spec_read mode"); if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc) panic("spec_read proc"); #endif if (uio->uio_resid == 0) return (0); switch (vp->v_type) { case VCHR: VOP_UNLOCK(vp, 0, p); error = (*cdevsw[major(vp->v_rdev)]->d_read) (vp->v_rdev, uio, ap->a_ioflag); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); return (error); case VBLK: if (uio->uio_offset < 0) return (EINVAL); bsize = BLKDEV_IOSIZE; dev = vp->v_rdev; if ((majordev = major(dev)) < nblkdev && (ioctl = bdevsw[majordev]->d_ioctl) != NULL && (*ioctl)(dev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0 && dpart.part->p_fstype == FS_BSDFFS && dpart.part->p_frag != 0 && dpart.part->p_fsize != 0) bsize = dpart.part->p_frag * dpart.part->p_fsize; bscale = btodb(bsize); do { bn = btodb(uio->uio_offset) & ~(bscale - 1); on = uio->uio_offset % bsize; n = min((unsigned)(bsize - on), uio->uio_resid); if (vp->v_lastr + bscale == bn) { nextbn = bn + bscale; error = breadn(vp, bn, (int)bsize, &nextbn, (int *)&bsize, 1, NOCRED, &bp); } else error = bread(vp, bn, (int)bsize, NOCRED, &bp); vp->v_lastr = bn; n = min(n, bsize - bp->b_resid); if (error) { brelse(bp); return (error); } error = uiomove((char *)bp->b_data + on, n, uio); brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); return (error); default: panic("spec_read type"); } /* NOTREACHED */ } /* * Vnode op for write */ /* ARGSUSED */ int spec_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct uio *uio = ap->a_uio; struct proc *p = uio->uio_procp; struct buf *bp; daddr_t bn; int bsize, blkmask; struct partinfo dpart; register int n, on; int error = 0; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_WRITE) panic("spec_write mode"); if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc) panic("spec_write proc"); #endif switch (vp->v_type) { case VCHR: VOP_UNLOCK(vp, 0, p); error = (*cdevsw[major(vp->v_rdev)]->d_write) (vp->v_rdev, uio, ap->a_ioflag); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); return (error); case VBLK: if (uio->uio_resid == 0) return (0); if (uio->uio_offset < 0) return (EINVAL); bsize = BLKDEV_IOSIZE; if ((*bdevsw[major(vp->v_rdev)]->d_ioctl)(vp->v_rdev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0) { if (dpart.part->p_fstype == FS_BSDFFS && dpart.part->p_frag != 0 && dpart.part->p_fsize != 0) bsize = dpart.part->p_frag * dpart.part->p_fsize; } blkmask = btodb(bsize) - 1; do { bn = btodb(uio->uio_offset) & ~blkmask; on = uio->uio_offset % bsize; n = min((unsigned)(bsize - on), uio->uio_resid); if (n == bsize) bp = getblk(vp, bn, bsize, 0, 0); else error = bread(vp, bn, bsize, NOCRED, &bp); n = min(n, bsize - bp->b_resid); if (error) { brelse(bp); return (error); } error = uiomove((char *)bp->b_data + on, n, uio); if (n + on == bsize) { /* bawrite(bp); */ cluster_write(bp, 0); } else bdwrite(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); return (error); default: panic("spec_write type"); } /* NOTREACHED */ } /* * Device ioctl operation. */ /* ARGSUSED */ int spec_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { dev_t dev = ap->a_vp->v_rdev; switch (ap->a_vp->v_type) { case VCHR: return ((*cdevsw[major(dev)]->d_ioctl)(dev, ap->a_command, ap->a_data, ap->a_fflag, ap->a_p)); case VBLK: if (ap->a_command == 0 && (int)ap->a_data == B_TAPE) if (bdevsw[major(dev)]->d_flags == D_TAPE) return (0); else return (1); return ((*bdevsw[major(dev)]->d_ioctl)(dev, ap->a_command, ap->a_data, ap->a_fflag, ap->a_p)); default: panic("spec_ioctl"); /* NOTREACHED */ } } /* ARGSUSED */ int -spec_select(ap) - struct vop_select_args /* { +spec_poll(ap) + struct vop_poll_args /* { struct vnode *a_vp; - int a_which; - int a_fflags; + int a_events; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register dev_t dev; switch (ap->a_vp->v_type) { - default: - return (1); /* XXX */ - case VCHR: dev = ap->a_vp->v_rdev; - return (*cdevsw[major(dev)]->d_select)(dev, ap->a_which, ap->a_p); + return (*cdevsw[major(dev)]->d_poll)(dev, ap->a_events, ap->a_p); + default: + return (vop_nopoll(ap)); + } } /* * Synch buffers associated with a block device */ /* ARGSUSED */ int spec_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct buf *bp; struct buf *nbp; int s; if (vp->v_type == VCHR) return (0); /* * Flush all dirty buffers associated with a block device. */ loop: s = splbio(); for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if ((bp->b_flags & B_BUSY)) continue; if ((bp->b_flags & B_DELWRI) == 0) panic("spec_fsync: not dirty"); bremfree(bp); bp->b_flags |= B_BUSY; splx(s); bawrite(bp); goto loop; } if (ap->a_waitfor == MNT_WAIT) { while (vp->v_numoutput) { vp->v_flag |= VBWAIT; (void) tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "spfsyn", 0); } #ifdef DIAGNOSTIC if (vp->v_dirtyblkhd.lh_first) { vprint("spec_fsync: dirty", vp); splx(s); goto loop; } #endif } splx(s); return (0); } int spec_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { VOP_UNLOCK(ap->a_vp, 0, ap->a_p); return (0); } /* * Just call the device strategy routine */ int spec_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *ap; { (*bdevsw[major(ap->a_bp->b_dev)]->d_strategy)(ap->a_bp); return (0); } /* * This is a noop, simply returning what one has been given. */ int spec_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; if (ap->a_runp != NULL) *ap->a_runp = 0; if (ap->a_runb != NULL) *ap->a_runb = 0; return (0); } /* * Device close routine */ /* ARGSUSED */ int spec_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; struct proc *p = ap->a_p; dev_t dev = vp->v_rdev; d_close_t *devclose; int mode, error; switch (vp->v_type) { case VCHR: /* * Hack: a tty device that is a controlling terminal * has a reference from the session structure. * We cannot easily tell that a character device is * a controlling terminal, unless it is the closing * process' controlling terminal. In that case, * if the reference count is 2 (this last descriptor * plus the session), release the reference from the session. */ if (vcount(vp) == 2 && ap->a_p && (vp->v_flag & VXLOCK) == 0 && vp == ap->a_p->p_session->s_ttyvp) { vrele(vp); ap->a_p->p_session->s_ttyvp = NULL; } /* * If the vnode is locked, then we are in the midst * of forcably closing the device, otherwise we only * close on last reference. */ if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0) return (0); devclose = cdevsw[major(dev)]->d_close; mode = S_IFCHR; break; case VBLK: /* * On last close of a block device (that isn't mounted) * we must invalidate any in core blocks, so that * we can, for instance, change floppy disks. */ error = vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 0, 0); if (error) return (error); /* * We do not want to really close the device if it * is still in use unless we are trying to close it * forcibly. Since every use (buffer, vnode, swap, cmap) * holds a reference to the vnode, and because we mark * any other vnodes that alias this device, when the * sum of the reference counts on all the aliased * vnodes descends to one, we are on last close. */ if ((vcount(vp) > (vp->v_object?2:1)) && (vp->v_flag & VXLOCK) == 0) return (0); if (vp->v_object) { vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); vnode_pager_uncache(vp, p); VOP_UNLOCK(vp, 0, p); } devclose = bdevsw[major(dev)]->d_close; mode = S_IFBLK; break; default: panic("spec_close: not special"); } return ((*devclose)(dev, ap->a_fflag, mode, ap->a_p)); } /* * Print out the contents of a special device vnode. */ int spec_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_NON, dev %d, %d\n", major(ap->a_vp->v_rdev), minor(ap->a_vp->v_rdev)); return (0); } /* * Return POSIX pathconf information applicable to special devices. */ int spec_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_MAX_CANON: *ap->a_retval = MAX_CANON; return (0); case _PC_MAX_INPUT: *ap->a_retval = MAX_INPUT; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_VDISABLE: *ap->a_retval = _POSIX_VDISABLE; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Special device advisory byte-level locks. */ /* ARGSUSED */ int spec_advlock(ap) struct vop_advlock_args /* { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ *ap; { return (ap->a_flags & F_FLOCK ? EOPNOTSUPP : EINVAL); } /* * Special device failed operation */ static int spec_ebadf() { return (EBADF); } /* * Special device bad operation */ int spec_badop() { panic("spec_badop called"); /* NOTREACHED */ } static void spec_getpages_iodone(bp) struct buf *bp; { bp->b_flags |= B_DONE; wakeup(bp); } int spec_getpages(ap) struct vop_getpages_args *ap; { vm_offset_t kva; int error; int i, pcount, size, s; daddr_t blkno; struct buf *bp; vm_ooffset_t offset; struct vnode *vp = ap->a_vp; int blksiz; error = 0; pcount = round_page(ap->a_count) / PAGE_SIZE; /* * Calculate the offset of the transfer. */ offset = IDX_TO_OFF(ap->a_m[0]->pindex) + ap->a_offset; /* XXX sanity check before we go into details. */ /* XXX limits should be defined elsewhere. */ #define DADDR_T_BIT 32 #define OFFSET_MAX ((1LL << (DADDR_T_BIT + DEV_BSHIFT)) - 1) if (offset < 0 || offset > OFFSET_MAX) { /* XXX still no %q in kernel. */ printf("spec_getpages: preposterous offset 0x%x%08x\n", (u_int)((u_quad_t)offset >> 32), (u_int)(offset & 0xffffffff)); return (VM_PAGER_ERROR); } blkno = btodb(offset); /* * Round up physical size for real devices, use the * fundamental blocksize of the fs if possible. */ if (vp && vp->v_mount) blksiz = vp->v_mount->mnt_stat.f_bsize; else blksiz = DEV_BSIZE; size = (ap->a_count + blksiz - 1) & ~(blksiz - 1); bp = getpbuf(); kva = (vm_offset_t)bp->b_data; /* * Map the pages to be read into the kva. */ pmap_qenter(kva, ap->a_m, pcount); /* Build a minimal buffer header. */ bp->b_flags = B_BUSY | B_READ | B_CALL; bp->b_iodone = spec_getpages_iodone; /* B_PHYS is not set, but it is nice to fill this in. */ bp->b_proc = curproc; bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred; if (bp->b_rcred != NOCRED) crhold(bp->b_rcred); if (bp->b_wcred != NOCRED) crhold(bp->b_wcred); bp->b_blkno = blkno; bp->b_lblkno = blkno; pbgetvp(ap->a_vp, bp); bp->b_bcount = size; bp->b_bufsize = size; cnt.v_vnodein++; cnt.v_vnodepgsin += pcount; /* Do the input. */ VOP_STRATEGY(bp); if (bp->b_flags & B_ASYNC) return (VM_PAGER_PEND); s = splbio(); /* We definitely need to be at splbio here. */ while ((bp->b_flags & B_DONE) == 0) tsleep(bp, PVM, "vnread", 0); splx(s); if ((bp->b_flags & B_ERROR) != 0) error = EIO; if (!error && ap->a_count != pcount * PAGE_SIZE) bzero((caddr_t)kva + ap->a_count, PAGE_SIZE * pcount - ap->a_count); pmap_qremove(kva, pcount); /* * Free the buffer header back to the swap buffer pool. */ relpbuf(bp); for (i = 0; i < pcount; i++) { ap->a_m[i]->dirty = 0; ap->a_m[i]->valid = VM_PAGE_BITS_ALL; ap->a_m[i]->flags &= ~PG_ZERO; if (i != ap->a_reqpage) { /* * Whether or not to leave the page activated is up in * the air, but we should put the page on a page queue * somewhere (it already is in the object). Result: * It appears that emperical results show that * deactivating pages is best. */ /* * Just in case someone was asking for this page we * now tell them that it is ok to use. */ if (!error) { vm_page_deactivate(ap->a_m[i]); PAGE_WAKEUP(ap->a_m[i]); } else vnode_pager_freepage(ap->a_m[i]); } } if (error) printf("spec_getpages: I/O read error\n"); return (error ? VM_PAGER_ERROR : VM_PAGER_OK); } /* ARGSUSED */ static int spec_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct vattr *vap = ap->a_vap; struct partinfo dpart; bzero(vap, sizeof (*vap)); if (vp->v_type == VBLK) vap->va_blocksize = BLKDEV_IOSIZE; else if (vp->v_type == VCHR) vap->va_blocksize = MAXBSIZE; if ((*bdevsw[major(vp->v_rdev)]->d_ioctl)(vp->v_rdev, DIOCGPART, (caddr_t)&dpart, FREAD, ap->a_p) == 0) { vap->va_bytes = dbtob(dpart.disklab->d_partitions [minor(vp->v_rdev)].p_size); vap->va_size = vap->va_bytes; } return (0); } diff --git a/sys/fs/unionfs/union_vnops.c b/sys/fs/unionfs/union_vnops.c index ecb8fc15a809..469ae530b541 100644 --- a/sys/fs/unionfs/union_vnops.c +++ b/sys/fs/unionfs/union_vnops.c @@ -1,1810 +1,1813 @@ /* * Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry. * Copyright (c) 1992, 1993, 1994, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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. * * @(#)union_vnops.c 8.32 (Berkeley) 6/23/95 - * $Id: union_vnops.c,v 1.40 1997/09/04 03:14:49 kato Exp $ + * $Id: union_vnops.c,v 1.41 1997/09/07 06:46:34 bde Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define FIXUP(un, p) { \ if (((un)->un_flags & UN_ULOCK) == 0) { \ union_fixup(un, p); \ } \ } #define SETKLOCK(un) (un)->un_flags |= UN_KLOCK #define CLEARKLOCK(un) (un)->un_flags &= ~UN_KLOCK static int union_abortop __P((struct vop_abortop_args *ap)); static int union_access __P((struct vop_access_args *ap)); static int union_advlock __P((struct vop_advlock_args *ap)); static int union_bmap __P((struct vop_bmap_args *ap)); static int union_close __P((struct vop_close_args *ap)); static int union_create __P((struct vop_create_args *ap)); static void union_fixup __P((struct union_node *un, struct proc *p)); static int union_fsync __P((struct vop_fsync_args *ap)); static int union_getattr __P((struct vop_getattr_args *ap)); static int union_inactive __P((struct vop_inactive_args *ap)); static int union_ioctl __P((struct vop_ioctl_args *ap)); static int union_islocked __P((struct vop_islocked_args *ap)); static int union_lease __P((struct vop_lease_args *ap)); static int union_link __P((struct vop_link_args *ap)); static int union_lock __P((struct vop_lock_args *ap)); static int union_lookup __P((struct vop_lookup_args *ap)); static int union_lookup1 __P((struct vnode *udvp, struct vnode **dvpp, struct vnode **vpp, struct componentname *cnp)); static int union_mkdir __P((struct vop_mkdir_args *ap)); static int union_mknod __P((struct vop_mknod_args *ap)); static int union_mmap __P((struct vop_mmap_args *ap)); static int union_open __P((struct vop_open_args *ap)); static int union_pathconf __P((struct vop_pathconf_args *ap)); static int union_print __P((struct vop_print_args *ap)); static int union_read __P((struct vop_read_args *ap)); static int union_readdir __P((struct vop_readdir_args *ap)); static int union_readlink __P((struct vop_readlink_args *ap)); static int union_reclaim __P((struct vop_reclaim_args *ap)); static int union_remove __P((struct vop_remove_args *ap)); static int union_rename __P((struct vop_rename_args *ap)); static int union_revoke __P((struct vop_revoke_args *ap)); static int union_rmdir __P((struct vop_rmdir_args *ap)); static int union_seek __P((struct vop_seek_args *ap)); -static int union_select __P((struct vop_select_args *ap)); +static int union_poll __P((struct vop_poll_args *ap)); static int union_setattr __P((struct vop_setattr_args *ap)); static int union_strategy __P((struct vop_strategy_args *ap)); static int union_symlink __P((struct vop_symlink_args *ap)); static int union_unlock __P((struct vop_unlock_args *ap)); static int union_whiteout __P((struct vop_whiteout_args *ap)); static int union_write __P((struct vop_read_args *ap)); static void union_fixup(un, p) struct union_node *un; struct proc *p; { vn_lock(un->un_uppervp, LK_EXCLUSIVE | LK_RETRY, p); un->un_flags |= UN_ULOCK; } static int union_lookup1(udvp, dvpp, vpp, cnp) struct vnode *udvp; struct vnode **dvpp; struct vnode **vpp; struct componentname *cnp; { int error; struct proc *p = cnp->cn_proc; struct vnode *tdvp; struct vnode *dvp; struct mount *mp; dvp = *dvpp; /* * If stepping up the directory tree, check for going * back across the mount point, in which case do what * lookup would do by stepping back down the mount * hierarchy. */ if (cnp->cn_flags & ISDOTDOT) { while ((dvp != udvp) && (dvp->v_flag & VROOT)) { /* * Don't do the NOCROSSMOUNT check * at this level. By definition, * union fs deals with namespaces, not * filesystems. */ tdvp = dvp; *dvpp = dvp = dvp->v_mount->mnt_vnodecovered; vput(tdvp); VREF(dvp); vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); } } error = VOP_LOOKUP(dvp, &tdvp, cnp); if (error) return (error); /* * The parent directory will have been unlocked, unless lookup * found the last component. In which case, re-lock the node * here to allow it to be unlocked again (phew) in union_lookup. */ if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN)) vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); dvp = tdvp; /* * Lastly check if the current node is a mount point in * which case walk up the mount hierarchy making sure not to * bump into the root of the mount tree (ie. dvp != udvp). */ while (dvp != udvp && (dvp->v_type == VDIR) && (mp = dvp->v_mountedhere)) { if (vfs_busy(mp, 0, 0, p)) continue; error = VFS_ROOT(mp, &tdvp); vfs_unbusy(mp, p); if (error) { vput(dvp); return (error); } vput(dvp); dvp = tdvp; } *vpp = dvp; return (0); } static int union_lookup(ap) struct vop_lookup_args /* { struct vnodeop_desc *a_desc; struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { int error; int uerror, lerror; struct vnode *uppervp, *lowervp; struct vnode *upperdvp, *lowerdvp; struct vnode *dvp = ap->a_dvp; struct union_node *dun = VTOUNION(dvp); struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; int lockparent = cnp->cn_flags & LOCKPARENT; struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount); struct ucred *saved_cred; int iswhiteout; struct vattr va; /* * Disallow write attemps to the filesystem mounted read-only. */ if ((cnp->cn_flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) return (EROFS); #ifdef notyet if (cnp->cn_namelen == 3 && cnp->cn_nameptr[2] == '.' && cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.') { dvp = *ap->a_vpp = LOWERVP(ap->a_dvp); if (dvp == NULLVP) return (ENOENT); VREF(dvp); vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); if (!lockparent || !(cnp->cn_flags & ISLASTCN)) VOP_UNLOCK(ap->a_dvp, 0, p); return (0); } #endif cnp->cn_flags |= LOCKPARENT; upperdvp = dun->un_uppervp; lowerdvp = dun->un_lowervp; uppervp = NULLVP; lowervp = NULLVP; iswhiteout = 0; /* * do the lookup in the upper level. * if that level comsumes additional pathnames, * then assume that something special is going * on and just return that vnode. */ if (upperdvp != NULLVP) { FIXUP(dun, p); /* * If we're doing `..' in the underlying filesystem, * we must drop our lock on the union node before * going up the tree in the lower file system--if we block * on the lowervp lock, and that's held by someone else * coming down the tree and who's waiting for our lock, * we would be hosed. */ if (cnp->cn_flags & ISDOTDOT) { /* retain lock on underlying VP: */ SETKLOCK(dun); VOP_UNLOCK(dvp, 0, p); CLEARKLOCK(dun); } uerror = union_lookup1(um->um_uppervp, &upperdvp, &uppervp, cnp); /* * Disallow write attemps to the filesystem mounted read-only. */ if (uerror == EJUSTRETURN && (cnp->cn_flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) && (cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME)) { if (!lockparent) cnp->cn_flags &= ~LOCKPARENT; return (EROFS); } if (cnp->cn_flags & ISDOTDOT) { if (dun->un_uppervp == upperdvp) { /* * We got the underlying bugger back locked... * now take back the union node lock. Since we * hold the uppervp lock, we can diddle union * locking flags at will. :) */ dun->un_flags |= UN_ULOCK; } /* * If upperdvp got swapped out, it means we did * some mount point magic, and we do not have * dun->un_uppervp locked currently--so we get it * locked here (don't set the UN_ULOCK flag). */ vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); } /*if (uppervp == upperdvp) dun->un_flags |= UN_KLOCK;*/ if (cnp->cn_consume != 0) { *ap->a_vpp = uppervp; if (!lockparent) cnp->cn_flags &= ~LOCKPARENT; return (uerror); } if (uerror == ENOENT || uerror == EJUSTRETURN) { if (cnp->cn_flags & ISWHITEOUT) { iswhiteout = 1; } else if (lowerdvp != NULLVP) { lerror = VOP_GETATTR(upperdvp, &va, cnp->cn_cred, cnp->cn_proc); if (lerror == 0 && (va.va_flags & OPAQUE)) iswhiteout = 1; } } } else { uerror = ENOENT; } /* * in a similar way to the upper layer, do the lookup * in the lower layer. this time, if there is some * component magic going on, then vput whatever we got * back from the upper layer and return the lower vnode * instead. */ if (lowerdvp != NULLVP && !iswhiteout) { int nameiop; vn_lock(lowerdvp, LK_EXCLUSIVE | LK_RETRY, p); /* * Only do a LOOKUP on the bottom node, since * we won't be making changes to it anyway. */ nameiop = cnp->cn_nameiop; cnp->cn_nameiop = LOOKUP; if (um->um_op == UNMNT_BELOW) { saved_cred = cnp->cn_cred; cnp->cn_cred = um->um_cred; } /* * We shouldn't have to worry about locking interactions * between the lower layer and our union layer (w.r.t. * `..' processing) because we don't futz with lowervp * locks in the union-node instantiation code path. */ lerror = union_lookup1(um->um_lowervp, &lowerdvp, &lowervp, cnp); if (um->um_op == UNMNT_BELOW) cnp->cn_cred = saved_cred; cnp->cn_nameiop = nameiop; if (lowervp != lowerdvp) VOP_UNLOCK(lowerdvp, 0, p); if (cnp->cn_consume != 0 || lerror == EACCES) { if (lerror == EACCES) lowervp = NULLVP; if (uppervp != NULLVP) { if (uppervp == upperdvp) vrele(uppervp); else vput(uppervp); uppervp = NULLVP; } *ap->a_vpp = lowervp; if (!lockparent) cnp->cn_flags &= ~LOCKPARENT; return (lerror); } } else { lerror = ENOENT; if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) { lowervp = LOWERVP(dun->un_pvp); if (lowervp != NULLVP) { VREF(lowervp); vn_lock(lowervp, LK_EXCLUSIVE | LK_RETRY, p); lerror = 0; } } } if (!lockparent) cnp->cn_flags &= ~LOCKPARENT; /* * at this point, we have uerror and lerror indicating * possible errors with the lookups in the upper and lower * layers. additionally, uppervp and lowervp are (locked) * references to existing vnodes in the upper and lower layers. * * there are now three cases to consider. * 1. if both layers returned an error, then return whatever * error the upper layer generated. * * 2. if the top layer failed and the bottom layer succeeded * then two subcases occur. * a. the bottom vnode is not a directory, in which * case just return a new union vnode referencing * an empty top layer and the existing bottom layer. * b. the bottom vnode is a directory, in which case * create a new directory in the top-level and * continue as in case 3. * * 3. if the top layer succeeded then return a new union * vnode referencing whatever the new top layer and * whatever the bottom layer returned. */ *ap->a_vpp = NULLVP; /* case 1. */ if ((uerror != 0) && (lerror != 0)) { return (uerror); } /* case 2. */ if (uerror != 0 /* && (lerror == 0) */ ) { if (lowervp->v_type == VDIR) { /* case 2b. */ dun->un_flags &= ~UN_ULOCK; VOP_UNLOCK(upperdvp, 0, p); uerror = union_mkshadow(um, upperdvp, cnp, &uppervp); vn_lock(upperdvp, LK_EXCLUSIVE | LK_RETRY, p); dun->un_flags |= UN_ULOCK; if (uerror) { if (lowervp != NULLVP) { vput(lowervp); lowervp = NULLVP; } return (uerror); } } } if (lowervp != NULLVP) VOP_UNLOCK(lowervp, 0, p); error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp, uppervp, lowervp, 1); if (error) { if (uppervp != NULLVP) vput(uppervp); if (lowervp != NULLVP) vrele(lowervp); } else { if (*ap->a_vpp != dvp) if (!lockparent || !(cnp->cn_flags & ISLASTCN)) VOP_UNLOCK(dvp, 0, p); #ifdef DIAGNOSTIC if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.' && *ap->a_vpp != dvp) { panic("union_lookup returning . (%p) not same as startdir (%p)", ap->a_vpp, dvp); } #endif } return (error); } static int union_create(ap) struct vop_create_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct union_node *un = VTOUNION(ap->a_dvp); struct vnode *dvp = un->un_uppervp; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (dvp != NULLVP) { int error; struct vnode *vp; struct mount *mp; FIXUP(un, p); VREF(dvp); SETKLOCK(un); mp = ap->a_dvp->v_mount; vput(ap->a_dvp); CLEARKLOCK(un); error = VOP_CREATE(dvp, &vp, cnp, ap->a_vap); if (error) return (error); error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP, cnp, vp, NULLVP, 1); if (error) vput(vp); return (error); } vput(ap->a_dvp); return (EROFS); } static int union_whiteout(ap) struct vop_whiteout_args /* { struct vnode *a_dvp; struct componentname *a_cnp; int a_flags; } */ *ap; { struct union_node *un = VTOUNION(ap->a_dvp); struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (un->un_uppervp == NULLVP) return (EOPNOTSUPP); FIXUP(un, p); return (VOP_WHITEOUT(un->un_uppervp, cnp, ap->a_flags)); } static int union_mknod(ap) struct vop_mknod_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct union_node *un = VTOUNION(ap->a_dvp); struct vnode *dvp = un->un_uppervp; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (dvp != NULLVP) { int error; struct vnode *vp; struct mount *mp; FIXUP(un, p); VREF(dvp); SETKLOCK(un); mp = ap->a_dvp->v_mount; vput(ap->a_dvp); CLEARKLOCK(un); error = VOP_MKNOD(dvp, &vp, cnp, ap->a_vap); if (error) return (error); if (vp != NULLVP) { error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP, cnp, vp, NULLVP, 1); if (error) vput(vp); } return (error); } vput(ap->a_dvp); return (EROFS); } static int union_open(ap) struct vop_open_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct union_node *un = VTOUNION(ap->a_vp); struct vnode *tvp; int mode = ap->a_mode; struct ucred *cred = ap->a_cred; struct proc *p = ap->a_p; int error; /* * If there is an existing upper vp then simply open that. */ tvp = un->un_uppervp; if (tvp == NULLVP) { /* * If the lower vnode is being opened for writing, then * copy the file contents to the upper vnode and open that, * otherwise can simply open the lower vnode. */ tvp = un->un_lowervp; if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) { error = union_copyup(un, (mode&O_TRUNC) == 0, cred, p); if (error == 0) error = VOP_OPEN(un->un_uppervp, mode, cred, p); return (error); } /* * Just open the lower vnode */ un->un_openl++; vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_OPEN(tvp, mode, cred, p); VOP_UNLOCK(tvp, 0, p); return (error); } FIXUP(un, p); error = VOP_OPEN(tvp, mode, cred, p); return (error); } static int union_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct union_node *un = VTOUNION(ap->a_vp); struct vnode *vp; if ((vp = un->un_uppervp) == NULLVP) { #ifdef UNION_DIAGNOSTIC if (un->un_openl <= 0) panic("union: un_openl cnt"); #endif --un->un_openl; vp = un->un_lowervp; } ap->a_vp = vp; return (VCALL(vp, VOFFSET(vop_close), ap)); } /* * Check access permission on the union vnode. * The access check being enforced is to check * against both the underlying vnode, and any * copied vnode. This ensures that no additional * file permissions are given away simply because * the user caused an implicit file copy. */ static int union_access(ap) struct vop_access_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct union_node *un = VTOUNION(ap->a_vp); struct proc *p = ap->a_p; int error = EACCES; struct vnode *vp; struct vnode *savedvp; /* * Disallow write attempts on filesystems mounted read-only. */ if (ap->a_mode & VWRITE && (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)) { switch (ap->a_vp->v_type) { case VREG: case VDIR: case VLNK: return (EROFS); } } if ((vp = un->un_uppervp) != NULLVP) { FIXUP(un, p); ap->a_vp = vp; return (VCALL(vp, VOFFSET(vop_access), ap)); } if ((vp = un->un_lowervp) != NULLVP) { vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); savedvp = ap->a_vp; ap->a_vp = vp; error = VCALL(vp, VOFFSET(vop_access), ap); if (error == 0) { struct union_mount *um = MOUNTTOUNIONMOUNT(savedvp->v_mount); if (um->um_op == UNMNT_BELOW) { ap->a_cred = um->um_cred; error = VCALL(vp, VOFFSET(vop_access), ap); } } VOP_UNLOCK(vp, 0, p); if (error) return (error); } return (error); } /* * We handle getattr only to change the fsid and * track object sizes */ static int union_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { int error; struct union_node *un = VTOUNION(ap->a_vp); struct vnode *vp = un->un_uppervp; struct proc *p = ap->a_p; struct vattr *vap; struct vattr va; /* * Some programs walk the filesystem hierarchy by counting * links to directories to avoid stat'ing all the time. * This means the link count on directories needs to be "correct". * The only way to do that is to call getattr on both layers * and fix up the link count. The link count will not necessarily * be accurate but will be large enough to defeat the tree walkers. */ vap = ap->a_vap; vp = un->un_uppervp; if (vp != NULLVP) { /* * It's not clear whether VOP_GETATTR is to be * called with the vnode locked or not. stat() calls * it with (vp) locked, and fstat calls it with * (vp) unlocked. * In the mean time, compensate here by checking * the union_node's lock flag. */ if (un->un_flags & UN_LOCKED) FIXUP(un, p); error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); if (error) return (error); union_newsize(ap->a_vp, vap->va_size, VNOVAL); } if (vp == NULLVP) { vp = un->un_lowervp; } else if (vp->v_type == VDIR) { vp = un->un_lowervp; vap = &va; } else { vp = NULLVP; } if (vp != NULLVP) { error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); if (error) return (error); union_newsize(ap->a_vp, VNOVAL, vap->va_size); } if ((vap != ap->a_vap) && (vap->va_type == VDIR)) ap->a_vap->va_nlink += vap->va_nlink; ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; return (0); } static int union_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct union_node *un = VTOUNION(ap->a_vp); struct proc *p = ap->a_p; struct vattr *vap = ap->a_vap; int error; /* * Disallow write attempts on filesystems mounted read-only. */ if ((ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) && (vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL)) return (EROFS); /* * Handle case of truncating lower object to zero size, * by creating a zero length upper object. This is to * handle the case of open with O_TRUNC and O_CREAT. */ if ((un->un_uppervp == NULLVP) && /* assert(un->un_lowervp != NULLVP) */ (un->un_lowervp->v_type == VREG)) { error = union_copyup(un, (ap->a_vap->va_size != 0), ap->a_cred, ap->a_p); if (error) return (error); } /* * Try to set attributes in upper layer, * otherwise return read-only filesystem error. */ if (un->un_uppervp != NULLVP) { FIXUP(un, p); error = VOP_SETATTR(un->un_uppervp, ap->a_vap, ap->a_cred, ap->a_p); if ((error == 0) && (ap->a_vap->va_size != VNOVAL)) union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL); } else { error = EROFS; } return (error); } static int union_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int error; struct proc *p = ap->a_uio->uio_procp; struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); else FIXUP(VTOUNION(ap->a_vp), p); error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred); if (dolock) VOP_UNLOCK(vp, 0, p); /* * XXX * perhaps the size of the underlying object has changed under * our feet. take advantage of the offset information present * in the uio structure. */ if (error == 0) { struct union_node *un = VTOUNION(ap->a_vp); off_t cur = ap->a_uio->uio_offset; if (vp == un->un_uppervp) { if (cur > un->un_uppersz) union_newsize(ap->a_vp, cur, VNOVAL); } else { if (cur > un->un_lowersz) union_newsize(ap->a_vp, VNOVAL, cur); } } return (error); } static int union_write(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int error; struct vnode *vp; struct union_node *un = VTOUNION(ap->a_vp); struct proc *p = ap->a_uio->uio_procp; vp = UPPERVP(ap->a_vp); if (vp == NULLVP) panic("union: missing upper layer in write"); FIXUP(un, p); error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred); /* * the size of the underlying object may be changed by the * write. */ if (error == 0) { off_t cur = ap->a_uio->uio_offset; if (cur > un->un_uppersz) union_newsize(ap->a_vp, cur, VNOVAL); } return (error); } static int union_lease(ap) struct vop_lease_args /* { struct vnode *a_vp; struct proc *a_p; struct ucred *a_cred; int a_flag; } */ *ap; { register struct vnode *ovp = OTHERVP(ap->a_vp); ap->a_vp = ovp; return (VCALL(ovp, VOFFSET(vop_lease), ap)); } static int union_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *ovp = OTHERVP(ap->a_vp); ap->a_vp = ovp; return (VCALL(ovp, VOFFSET(vop_ioctl), ap)); } static int -union_select(ap) - struct vop_select_args /* { +union_poll(ap) + struct vop_poll_args /* { struct vnode *a_vp; - int a_which; - int a_fflags; + int a_events; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *ovp = OTHERVP(ap->a_vp); ap->a_vp = ovp; - return (VCALL(ovp, VOFFSET(vop_select), ap)); + return (VCALL(ovp, VOFFSET(vop_poll), ap)); } static int union_revoke(ap) struct vop_revoke_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; if (UPPERVP(vp)) VOP_REVOKE(UPPERVP(vp), ap->a_flags); if (LOWERVP(vp)) VOP_REVOKE(LOWERVP(vp), ap->a_flags); vgone(vp); return (0); } static int union_mmap(ap) struct vop_mmap_args /* { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *ovp = OTHERVP(ap->a_vp); ap->a_vp = ovp; return (VCALL(ovp, VOFFSET(vop_mmap), ap)); } static int union_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { int error = 0; struct proc *p = ap->a_p; struct vnode *targetvp = OTHERVP(ap->a_vp); struct union_node *un; int isupperlocked = 0; if (targetvp != NULLVP) { int dolock = (targetvp == LOWERVP(ap->a_vp)); un = VTOUNION(ap->a_vp); if (dolock) vn_lock(targetvp, LK_EXCLUSIVE | LK_RETRY, p); else if ((un->un_flags & UN_ULOCK) == 0 && VOP_ISLOCKED(targetvp) == 0) { isupperlocked = 1; vn_lock(targetvp, LK_EXCLUSIVE | LK_RETRY, p); un->un_flags |= UN_ULOCK; } error = VOP_FSYNC(targetvp, ap->a_cred, ap->a_waitfor, p); if (dolock) VOP_UNLOCK(targetvp, 0, p); else if (isupperlocked) { un->un_flags &= ~UN_ULOCK; VOP_UNLOCK(targetvp, 0, p); } } return (error); } static int union_seek(ap) struct vop_seek_args /* { struct vnode *a_vp; off_t a_oldoff; off_t a_newoff; struct ucred *a_cred; } */ *ap; { register struct vnode *ovp = OTHERVP(ap->a_vp); ap->a_vp = ovp; return (VCALL(ovp, VOFFSET(vop_seek), ap)); } static int union_remove(ap) struct vop_remove_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { int error; struct union_node *dun = VTOUNION(ap->a_dvp); struct union_node *un = VTOUNION(ap->a_vp); struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (dun->un_uppervp == NULLVP) panic("union remove: null upper vnode"); if (un->un_uppervp != NULLVP) { struct vnode *dvp = dun->un_uppervp; struct vnode *vp = un->un_uppervp; FIXUP(dun, p); VREF(dvp); SETKLOCK(dun); vput(ap->a_dvp); CLEARKLOCK(dun); FIXUP(un, p); VREF(vp); SETKLOCK(un); vput(ap->a_vp); CLEARKLOCK(un); if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc)) cnp->cn_flags |= DOWHITEOUT; error = VOP_REMOVE(dvp, vp, cnp); if (!error) union_removed_upper(un); } else { FIXUP(dun, p); error = union_mkwhiteout( MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount), dun->un_uppervp, ap->a_cnp, un->un_path); vput(ap->a_dvp); vput(ap->a_vp); } return (error); } static int union_link(ap) struct vop_link_args /* { struct vnode *a_tdvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { int error = 0; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; struct union_node *un; struct vnode *vp; struct vnode *tdvp; un = VTOUNION(ap->a_tdvp); if (ap->a_tdvp->v_op != ap->a_vp->v_op) { vp = ap->a_vp; } else { struct union_node *tun = VTOUNION(ap->a_vp); if (tun->un_uppervp == NULLVP) { vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p); if (un->un_uppervp == tun->un_dirvp) { un->un_flags &= ~UN_ULOCK; VOP_UNLOCK(un->un_uppervp, 0, p); } error = union_copyup(tun, 1, cnp->cn_cred, p); if (un->un_uppervp == tun->un_dirvp) { vn_lock(un->un_uppervp, LK_EXCLUSIVE | LK_RETRY, p); un->un_flags |= UN_ULOCK; } VOP_UNLOCK(ap->a_vp, 0, p); } vp = tun->un_uppervp; } tdvp = un->un_uppervp; if (tdvp == NULLVP) error = EROFS; if (error) { vput(ap->a_tdvp); return (error); } FIXUP(un, p); VREF(tdvp); SETKLOCK(un); vput(ap->a_tdvp); CLEARKLOCK(un); return (VOP_LINK(tdvp, vp, cnp)); } static int union_rename(ap) struct vop_rename_args /* { struct vnode *a_fdvp; struct vnode *a_fvp; struct componentname *a_fcnp; struct vnode *a_tdvp; struct vnode *a_tvp; struct componentname *a_tcnp; } */ *ap; { int error; struct vnode *fdvp = ap->a_fdvp; struct vnode *fvp = ap->a_fvp; struct vnode *tdvp = ap->a_tdvp; struct vnode *tvp = ap->a_tvp; int isklockset = 0; if (fdvp->v_op == union_vnodeop_p) { /* always true */ struct union_node *un = VTOUNION(fdvp); if (un->un_uppervp == NULLVP) { /* * this should never happen in normal * operation but might if there was * a problem creating the top-level shadow * directory. */ error = EXDEV; goto bad; } fdvp = un->un_uppervp; VREF(fdvp); vrele(ap->a_fdvp); } if (fvp->v_op == union_vnodeop_p) { /* always true */ struct union_node *un = VTOUNION(fvp); if (un->un_uppervp == NULLVP) { /* XXX: should do a copyup */ error = EXDEV; goto bad; } if (un->un_lowervp != NULLVP) ap->a_fcnp->cn_flags |= DOWHITEOUT; fvp = un->un_uppervp; VREF(fvp); vrele(ap->a_fvp); } if (tdvp->v_op == union_vnodeop_p) { struct union_node *un = VTOUNION(tdvp); if (un->un_uppervp == NULLVP) { /* * this should never happen in normal * operation but might if there was * a problem creating the top-level shadow * directory. */ error = EXDEV; goto bad; } tdvp = un->un_uppervp; VREF(tdvp); SETKLOCK(un); vput(ap->a_tdvp); CLEARKLOCK(un); } if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) { struct union_node *un = VTOUNION(tvp); tvp = un->un_uppervp; if (tvp != NULLVP) { VREF(tvp); SETKLOCK(un); isklockset = 1; } vput(ap->a_tvp); if (isklockset) CLEARKLOCK(un); } return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp)); bad: vrele(fdvp); vrele(fvp); vput(tdvp); if (tvp != NULLVP) vput(tvp); return (error); } static int union_mkdir(ap) struct vop_mkdir_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct union_node *un = VTOUNION(ap->a_dvp); struct vnode *dvp = un->un_uppervp; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (dvp != NULLVP) { int error; struct vnode *vp; FIXUP(un, p); VREF(dvp); SETKLOCK(un); VOP_UNLOCK(ap->a_dvp, 0, p); CLEARKLOCK(un); error = VOP_MKDIR(dvp, &vp, cnp, ap->a_vap); if (error) { vrele(ap->a_dvp); return (error); } error = union_allocvp(ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp, NULLVP, cnp, vp, NULLVP, 1); vrele(ap->a_dvp); if (error) vput(vp); return (error); } vput(ap->a_dvp); return (EROFS); } static int union_rmdir(ap) struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { int error; struct union_node *dun = VTOUNION(ap->a_dvp); struct union_node *un = VTOUNION(ap->a_vp); struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (dun->un_uppervp == NULLVP) panic("union rmdir: null upper vnode"); if (un->un_uppervp != NULLVP) { struct vnode *dvp = dun->un_uppervp; struct vnode *vp = un->un_uppervp; FIXUP(dun, p); VREF(dvp); SETKLOCK(dun); vput(ap->a_dvp); CLEARKLOCK(dun); FIXUP(un, p); VREF(vp); SETKLOCK(un); vput(ap->a_vp); CLEARKLOCK(un); if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc)) cnp->cn_flags |= DOWHITEOUT; error = VOP_RMDIR(dvp, vp, ap->a_cnp); if (!error) union_removed_upper(un); } else { FIXUP(dun, p); error = union_mkwhiteout( MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount), dun->un_uppervp, ap->a_cnp, un->un_path); vput(ap->a_dvp); vput(ap->a_vp); } return (error); } static int union_symlink(ap) struct vop_symlink_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; char *a_target; } */ *ap; { struct union_node *un = VTOUNION(ap->a_dvp); struct vnode *dvp = un->un_uppervp; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (dvp != NULLVP) { int error; struct vnode *vp; FIXUP(un, p); VREF(dvp); SETKLOCK(un); vput(ap->a_dvp); CLEARKLOCK(un); error = VOP_SYMLINK(dvp, &vp, cnp, ap->a_vap, ap->a_target); *ap->a_vpp = NULLVP; return (error); } vput(ap->a_dvp); return (EROFS); } /* * union_readdir works in concert with getdirentries and * readdir(3) to provide a list of entries in the unioned * directories. getdirentries is responsible for walking * down the union stack. readdir(3) is responsible for * eliminating duplicate names from the returned data stream. */ static int union_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_long *a_cookies; int a_ncookies; } */ *ap; { struct union_node *un = VTOUNION(ap->a_vp); struct vnode *uvp = un->un_uppervp; struct proc *p = ap->a_uio->uio_procp; if (uvp == NULLVP) return (0); FIXUP(un, p); ap->a_vp = uvp; return (VCALL(uvp, VOFFSET(vop_readdir), ap)); } static int union_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { int error; struct uio *uio = ap->a_uio; struct proc *p = uio->uio_procp; struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); else FIXUP(VTOUNION(ap->a_vp), p); ap->a_vp = vp; error = VCALL(vp, VOFFSET(vop_readlink), ap); if (dolock) VOP_UNLOCK(vp, 0, p); return (error); } static int union_abortop(ap) struct vop_abortop_args /* { struct vnode *a_dvp; struct componentname *a_cnp; } */ *ap; { int error; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; struct vnode *vp = OTHERVP(ap->a_dvp); struct union_node *un = VTOUNION(ap->a_dvp); int islocked = un->un_flags & UN_LOCKED; int dolock = (vp == LOWERVP(ap->a_dvp)); if (islocked) { if (dolock) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); else FIXUP(VTOUNION(ap->a_dvp), p); } ap->a_dvp = vp; error = VCALL(vp, VOFFSET(vop_abortop), ap); if (islocked && dolock) VOP_UNLOCK(vp, 0, p); return (error); } static int union_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct proc *p = ap->a_p; struct union_node *un = VTOUNION(vp); struct vnode **vpp; /* * Do nothing (and _don't_ bypass). * Wait to vrele lowervp until reclaim, * so that until then our union_node is in the * cache and reusable. * * NEEDSWORK: Someday, consider inactive'ing * the lowervp and then trying to reactivate it * with capabilities (v_id) * like they do in the name lookup cache code. * That's too much work for now. */ if (un->un_dircache != 0) { for (vpp = un->un_dircache; *vpp != NULLVP; vpp++) vrele(*vpp); free(un->un_dircache, M_TEMP); un->un_dircache = 0; } VOP_UNLOCK(vp, 0, p); if ((un->un_flags & UN_CACHED) == 0) vgone(vp); return (0); } static int union_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { union_freevp(ap->a_vp); return (0); } static int union_lock(ap) struct vop_lock_args *ap; { struct vnode *vp = ap->a_vp; struct proc *p = ap->a_p; int flags = ap->a_flags; struct union_node *un; int error; vop_nolock(ap); /* * Need to do real lockmgr-style locking here. * in the mean time, draining won't work quite right, * which could lead to a few race conditions. * the following test was here, but is not quite right, we * still need to take the lock: if ((flags & LK_TYPE_MASK) == LK_DRAIN) return (0); */ flags &= ~LK_INTERLOCK; start: un = VTOUNION(vp); if (un->un_uppervp != NULLVP) { if (((un->un_flags & (UN_ULOCK | UN_KLOCK)) == 0) && (vp->v_usecount != 0)) { error = vn_lock(un->un_uppervp, flags, p); if (error) return (error); un->un_flags |= UN_ULOCK; } } if (un->un_flags & UN_LOCKED) { #ifdef DIAGNOSTIC if (curproc && un->un_pid == curproc->p_pid && un->un_pid > -1 && curproc->p_pid > -1) panic("union: locking against myself"); #endif un->un_flags |= UN_WANT; tsleep((caddr_t)&un->un_flags, PINOD, "unionlk2", 0); goto start; } #ifdef DIAGNOSTIC if (curproc) un->un_pid = curproc->p_pid; else un->un_pid = -1; #endif un->un_flags |= UN_LOCKED; return (0); } /* * When operations want to vput() a union node yet retain a lock on * the upper vnode (say, to do some further operations like link(), * mkdir(), ...), they set UN_KLOCK on the union node, then call * vput() which calls VOP_UNLOCK() and comes here. union_unlock() * unlocks the union node (leaving the upper vnode alone), clears the * KLOCK flag, and then returns to vput(). The caller then does whatever * is left to do with the upper vnode, and ensures that it gets unlocked. * * If UN_KLOCK isn't set, then the upper vnode is unlocked here. */ static int union_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct union_node *un = VTOUNION(ap->a_vp); struct proc *p = ap->a_p; #ifdef DIAGNOSTIC if ((un->un_flags & UN_LOCKED) == 0) panic("union: unlock unlocked node"); if (curproc && un->un_pid != curproc->p_pid && curproc->p_pid > -1 && un->un_pid > -1) panic("union: unlocking other process's union node"); #endif un->un_flags &= ~UN_LOCKED; if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK) VOP_UNLOCK(un->un_uppervp, 0, p); un->un_flags &= ~UN_ULOCK; if (un->un_flags & UN_WANT) { un->un_flags &= ~UN_WANT; wakeup((caddr_t) &un->un_flags); } #ifdef DIAGNOSTIC un->un_pid = 0; #endif vop_nounlock(ap); return (0); } static int union_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ *ap; { int error; struct proc *p = curproc; /* XXX */ struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); else FIXUP(VTOUNION(ap->a_vp), p); ap->a_vp = vp; error = VCALL(vp, VOFFSET(vop_bmap), ap); if (dolock) VOP_UNLOCK(vp, 0, p); return (error); } static int union_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; printf("\ttag VT_UNION, vp=%p, uppervp=%p, lowervp=%p\n", vp, UPPERVP(vp), LOWERVP(vp)); if (UPPERVP(vp) != NULLVP) vprint("union: upper", UPPERVP(vp)); if (LOWERVP(vp) != NULLVP) vprint("union: lower", LOWERVP(vp)); return (0); } static int union_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0); } static int union_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { int error; struct proc *p = curproc; /* XXX */ struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); else FIXUP(VTOUNION(ap->a_vp), p); ap->a_vp = vp; error = VCALL(vp, VOFFSET(vop_pathconf), ap); if (dolock) VOP_UNLOCK(vp, 0, p); return (error); } static int union_advlock(ap) struct vop_advlock_args /* { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ *ap; { register struct vnode *ovp = OTHERVP(ap->a_vp); ap->a_vp = ovp; return (VCALL(ovp, VOFFSET(vop_advlock), ap)); } /* * XXX - vop_strategy must be hand coded because it has no * vnode in its arguments. * This goes away with a merged VM/buffer cache. */ static int union_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *ap; { struct buf *bp = ap->a_bp; int error; struct vnode *savedvp; savedvp = bp->b_vp; bp->b_vp = OTHERVP(bp->b_vp); #ifdef DIAGNOSTIC if (bp->b_vp == NULLVP) panic("union_strategy: nil vp"); if (((bp->b_flags & B_READ) == 0) && (bp->b_vp == LOWERVP(savedvp))) panic("union_strategy: writing to lowervp"); #endif error = VOP_STRATEGY(bp); bp->b_vp = savedvp; return (error); } /* * Global vfs data structures */ vop_t **union_vnodeop_p; static struct vnodeopv_entry_desc union_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)union_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)union_create }, /* create */ { &vop_whiteout_desc, (vop_t *)union_whiteout }, /* whiteout */ { &vop_mknod_desc, (vop_t *)union_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)union_open }, /* open */ { &vop_close_desc, (vop_t *)union_close }, /* close */ { &vop_access_desc, (vop_t *)union_access }, /* access */ { &vop_getattr_desc, (vop_t *)union_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)union_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)union_read }, /* read */ { &vop_write_desc, (vop_t *)union_write }, /* write */ { &vop_lease_desc, (vop_t *)union_lease }, /* lease */ { &vop_ioctl_desc, (vop_t *)union_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)union_select }, /* select */ + { &vop_poll_desc, (vop_t *)union_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)union_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)union_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)union_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)union_seek }, /* seek */ { &vop_remove_desc, (vop_t *)union_remove }, /* remove */ { &vop_link_desc, (vop_t *)union_link }, /* link */ { &vop_rename_desc, (vop_t *)union_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)union_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)union_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)union_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)union_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)union_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)union_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)union_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)union_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)union_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)union_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)union_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)union_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)union_print }, /* print */ { &vop_islocked_desc, (vop_t *)union_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)union_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)union_advlock }, /* advlock */ #ifdef notdef { &vop_blkatoff_desc, (vop_t *)union_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)union_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)union_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)union_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)union_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)union_bwrite }, /* bwrite */ #endif { NULL, NULL } }; static struct vnodeopv_desc union_vnodeop_opv_desc = { &union_vnodeop_p, union_vnodeop_entries }; VNODEOP_SET(union_vnodeop_opv_desc); diff --git a/sys/gnu/ext2fs/ext2_vnops.c b/sys/gnu/ext2fs/ext2_vnops.c index a5b6c560452e..583164b73380 100644 --- a/sys/gnu/ext2fs/ext2_vnops.c +++ b/sys/gnu/ext2fs/ext2_vnops.c @@ -1,324 +1,326 @@ /* * modified for EXT2FS support in Lites 1.1 * * Aug 1995, Godmar Back (gback@cs.utah.edu) * University of Utah, Department of Computer Science */ /* * Copyright (c) 1982, 1986, 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. * * @(#)ext2_vnops.c 8.7 (Berkeley) 2/3/94 */ #if !defined(__FreeBSD__) #include "fifo.h" #include "diagnostic.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(__FreeBSD__) #include #else #include #endif #include #include #include #include #include #include #include static int ext2_fsync __P((struct vop_fsync_args *)); static int ext2_read __P((struct vop_read_args *)); static int ext2_write __P((struct vop_write_args *)); /* Global vfs data structures for ufs. */ vop_t **ext2_vnodeop_p; static struct vnodeopv_entry_desc ext2_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)vfs_cache_lookup }, /* lookup */ { &vop_cachedlookup_desc, (vop_t *)ext2_lookup }, /* lookup */ { &vop_create_desc, (vop_t *)ufs_create }, /* create */ { &vop_mknod_desc, (vop_t *)ufs_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)ufs_open }, /* open */ { &vop_close_desc, (vop_t *)ufs_close }, /* close */ { &vop_access_desc, (vop_t *)ufs_access }, /* access */ { &vop_getattr_desc, (vop_t *)ufs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)ufs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)ext2_read }, /* read */ { &vop_write_desc, (vop_t *)ext2_write }, /* write */ { &vop_ioctl_desc, (vop_t *)ufs_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)ufs_select }, /* select */ + { &vop_poll_desc, (vop_t *)ufs_poll }, /* poll */ { &vop_mmap_desc, (vop_t *)ufs_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)ext2_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)ufs_seek }, /* seek */ { &vop_remove_desc, (vop_t *)ufs_remove }, /* remove */ { &vop_link_desc, (vop_t *)ufs_link }, /* link */ { &vop_rename_desc, (vop_t *)ufs_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)ufs_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)ufs_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)ufs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)ext2_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)ufs_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)ufs_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)ext2_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)ffs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)ufs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)ufs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)ufs_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)ufs_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)ufs_print }, /* print */ { &vop_islocked_desc, (vop_t *)ufs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)ufs_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)ufs_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)ext2_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)ext2_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)ext2_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)ext2_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)ext2_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)ext2_update }, /* update */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc ext2fs_vnodeop_opv_desc = { &ext2_vnodeop_p, ext2_vnodeop_entries }; vop_t **ext2_specop_p; static struct vnodeopv_entry_desc ext2_specop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)spec_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)spec_create }, /* create */ { &vop_mknod_desc, (vop_t *)spec_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)spec_open }, /* open */ { &vop_close_desc, (vop_t *)ufsspec_close }, /* close */ { &vop_access_desc, (vop_t *)ufs_access }, /* access */ { &vop_getattr_desc, (vop_t *)ufs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)ufs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)ufsspec_read }, /* read */ { &vop_write_desc, (vop_t *)ufsspec_write }, /* write */ { &vop_ioctl_desc, (vop_t *)spec_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)spec_select }, /* select */ + { &vop_poll_desc, (vop_t *)spec_poll }, /* poll */ { &vop_mmap_desc, (vop_t *)spec_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)ext2_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)spec_seek }, /* seek */ { &vop_remove_desc, (vop_t *)spec_remove }, /* remove */ { &vop_link_desc, (vop_t *)spec_link }, /* link */ { &vop_rename_desc, (vop_t *)spec_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)spec_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)spec_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)spec_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)spec_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)spec_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)ext2_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)ffs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)ufs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)ufs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)spec_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)spec_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)ufs_print }, /* print */ { &vop_islocked_desc, (vop_t *)ufs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)spec_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)spec_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)spec_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)spec_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)ext2_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)spec_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)ext2_update }, /* update */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc ext2fs_specop_opv_desc = { &ext2_specop_p, ext2_specop_entries }; vop_t **ext2_fifoop_p; static struct vnodeopv_entry_desc ext2_fifoop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)fifo_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)fifo_create }, /* create */ { &vop_mknod_desc, (vop_t *)fifo_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)fifo_open }, /* open */ { &vop_close_desc, (vop_t *)ufsfifo_close }, /* close */ { &vop_access_desc, (vop_t *)ufs_access }, /* access */ { &vop_getattr_desc, (vop_t *)ufs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)ufs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)ufsfifo_read }, /* read */ { &vop_write_desc, (vop_t *)ufsfifo_write }, /* write */ { &vop_ioctl_desc, (vop_t *)fifo_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)fifo_select }, /* select */ + { &vop_poll_desc, (vop_t *)fifo_poll }, /* poll */ { &vop_mmap_desc, (vop_t *)fifo_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)ext2_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)fifo_seek }, /* seek */ { &vop_remove_desc, (vop_t *)fifo_remove }, /* remove */ { &vop_link_desc, (vop_t *)fifo_link }, /* link */ { &vop_rename_desc, (vop_t *)fifo_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)fifo_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)fifo_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)fifo_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)fifo_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)ext2_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)ffs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)ufs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)ufs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)fifo_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)fifo_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)ufs_print }, /* print */ { &vop_islocked_desc, (vop_t *)ufs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)fifo_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)fifo_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)ext2_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)fifo_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)ext2_update }, /* update */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc ext2fs_fifoop_opv_desc = { &ext2_fifoop_p, ext2_fifoop_entries }; #if defined(__FreeBSD__) VNODEOP_SET(ext2fs_vnodeop_opv_desc); VNODEOP_SET(ext2fs_specop_opv_desc); VNODEOP_SET(ext2fs_fifoop_opv_desc); #endif /* * Enabling cluster read/write operations. */ static int ext2_doclusterread = 1; static int ext2_doclusterwrite = 1; SYSCTL_NODE(_vfs, MOUNT_EXT2FS, ext2fs, CTLFLAG_RW, 0, "EXT2FS filesystem"); SYSCTL_INT(_vfs_ext2fs, EXT2FS_CLUSTERREAD, doclusterread, CTLFLAG_RW, &ext2_doclusterread, 0, ""); SYSCTL_INT(_vfs_ext2fs, EXT2FS_CLUSTERWRITE, doclusterwrite, CTLFLAG_RW, &ext2_doclusterwrite, 0, ""); #include /* * Synch an open file. */ /* ARGSUSED */ static int ext2_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct buf *bp; struct timeval tv; struct buf *nbp; int s; /* * Clean memory object. * XXX add this to all file systems. * XXX why is all this fs specific? */ #if !defined(__FreeBSD__) vn_pager_sync(vp, ap->a_waitfor); #endif /* * Flush all dirty buffers associated with a vnode. */ ext2_discard_prealloc(VTOI(vp)); loop: s = splbio(); for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if ((bp->b_flags & B_BUSY)) continue; if ((bp->b_flags & B_DELWRI) == 0) panic("ext2_fsync: not dirty"); bremfree(bp); bp->b_flags |= B_BUSY; splx(s); /* * Wait for I/O associated with indirect blocks to complete, * since there is no way to quickly wait for them below. */ if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT) (void) bawrite(bp); else (void) bwrite(bp); goto loop; } if (ap->a_waitfor == MNT_WAIT) { while (vp->v_numoutput) { vp->v_flag |= VBWAIT; #if !defined(__FreeBSD__) sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1); #else tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "extfsn", 0); #endif } #if DIAGNOSTIC if (vp->v_dirtyblkhd.lh_first) { vprint("ext2_fsync: dirty", vp); goto loop; } #endif } splx(s); gettime(&tv); return (VOP_UPDATE(ap->a_vp, &tv, &tv, ap->a_waitfor == MNT_WAIT)); } diff --git a/sys/gnu/fs/ext2fs/ext2_vnops.c b/sys/gnu/fs/ext2fs/ext2_vnops.c index a5b6c560452e..583164b73380 100644 --- a/sys/gnu/fs/ext2fs/ext2_vnops.c +++ b/sys/gnu/fs/ext2fs/ext2_vnops.c @@ -1,324 +1,326 @@ /* * modified for EXT2FS support in Lites 1.1 * * Aug 1995, Godmar Back (gback@cs.utah.edu) * University of Utah, Department of Computer Science */ /* * Copyright (c) 1982, 1986, 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. * * @(#)ext2_vnops.c 8.7 (Berkeley) 2/3/94 */ #if !defined(__FreeBSD__) #include "fifo.h" #include "diagnostic.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(__FreeBSD__) #include #else #include #endif #include #include #include #include #include #include #include static int ext2_fsync __P((struct vop_fsync_args *)); static int ext2_read __P((struct vop_read_args *)); static int ext2_write __P((struct vop_write_args *)); /* Global vfs data structures for ufs. */ vop_t **ext2_vnodeop_p; static struct vnodeopv_entry_desc ext2_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)vfs_cache_lookup }, /* lookup */ { &vop_cachedlookup_desc, (vop_t *)ext2_lookup }, /* lookup */ { &vop_create_desc, (vop_t *)ufs_create }, /* create */ { &vop_mknod_desc, (vop_t *)ufs_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)ufs_open }, /* open */ { &vop_close_desc, (vop_t *)ufs_close }, /* close */ { &vop_access_desc, (vop_t *)ufs_access }, /* access */ { &vop_getattr_desc, (vop_t *)ufs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)ufs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)ext2_read }, /* read */ { &vop_write_desc, (vop_t *)ext2_write }, /* write */ { &vop_ioctl_desc, (vop_t *)ufs_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)ufs_select }, /* select */ + { &vop_poll_desc, (vop_t *)ufs_poll }, /* poll */ { &vop_mmap_desc, (vop_t *)ufs_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)ext2_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)ufs_seek }, /* seek */ { &vop_remove_desc, (vop_t *)ufs_remove }, /* remove */ { &vop_link_desc, (vop_t *)ufs_link }, /* link */ { &vop_rename_desc, (vop_t *)ufs_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)ufs_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)ufs_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)ufs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)ext2_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)ufs_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)ufs_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)ext2_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)ffs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)ufs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)ufs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)ufs_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)ufs_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)ufs_print }, /* print */ { &vop_islocked_desc, (vop_t *)ufs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)ufs_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)ufs_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)ext2_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)ext2_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)ext2_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)ext2_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)ext2_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)ext2_update }, /* update */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc ext2fs_vnodeop_opv_desc = { &ext2_vnodeop_p, ext2_vnodeop_entries }; vop_t **ext2_specop_p; static struct vnodeopv_entry_desc ext2_specop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)spec_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)spec_create }, /* create */ { &vop_mknod_desc, (vop_t *)spec_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)spec_open }, /* open */ { &vop_close_desc, (vop_t *)ufsspec_close }, /* close */ { &vop_access_desc, (vop_t *)ufs_access }, /* access */ { &vop_getattr_desc, (vop_t *)ufs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)ufs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)ufsspec_read }, /* read */ { &vop_write_desc, (vop_t *)ufsspec_write }, /* write */ { &vop_ioctl_desc, (vop_t *)spec_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)spec_select }, /* select */ + { &vop_poll_desc, (vop_t *)spec_poll }, /* poll */ { &vop_mmap_desc, (vop_t *)spec_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)ext2_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)spec_seek }, /* seek */ { &vop_remove_desc, (vop_t *)spec_remove }, /* remove */ { &vop_link_desc, (vop_t *)spec_link }, /* link */ { &vop_rename_desc, (vop_t *)spec_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)spec_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)spec_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)spec_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)spec_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)spec_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)ext2_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)ffs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)ufs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)ufs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)spec_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)spec_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)ufs_print }, /* print */ { &vop_islocked_desc, (vop_t *)ufs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)spec_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)spec_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)spec_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)spec_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)ext2_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)spec_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)ext2_update }, /* update */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc ext2fs_specop_opv_desc = { &ext2_specop_p, ext2_specop_entries }; vop_t **ext2_fifoop_p; static struct vnodeopv_entry_desc ext2_fifoop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)fifo_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)fifo_create }, /* create */ { &vop_mknod_desc, (vop_t *)fifo_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)fifo_open }, /* open */ { &vop_close_desc, (vop_t *)ufsfifo_close }, /* close */ { &vop_access_desc, (vop_t *)ufs_access }, /* access */ { &vop_getattr_desc, (vop_t *)ufs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)ufs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)ufsfifo_read }, /* read */ { &vop_write_desc, (vop_t *)ufsfifo_write }, /* write */ { &vop_ioctl_desc, (vop_t *)fifo_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)fifo_select }, /* select */ + { &vop_poll_desc, (vop_t *)fifo_poll }, /* poll */ { &vop_mmap_desc, (vop_t *)fifo_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)ext2_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)fifo_seek }, /* seek */ { &vop_remove_desc, (vop_t *)fifo_remove }, /* remove */ { &vop_link_desc, (vop_t *)fifo_link }, /* link */ { &vop_rename_desc, (vop_t *)fifo_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)fifo_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)fifo_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)fifo_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)fifo_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)ext2_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)ffs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)ufs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)ufs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)fifo_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)fifo_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)ufs_print }, /* print */ { &vop_islocked_desc, (vop_t *)ufs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)fifo_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)fifo_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)ext2_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)fifo_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)ext2_update }, /* update */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc ext2fs_fifoop_opv_desc = { &ext2_fifoop_p, ext2_fifoop_entries }; #if defined(__FreeBSD__) VNODEOP_SET(ext2fs_vnodeop_opv_desc); VNODEOP_SET(ext2fs_specop_opv_desc); VNODEOP_SET(ext2fs_fifoop_opv_desc); #endif /* * Enabling cluster read/write operations. */ static int ext2_doclusterread = 1; static int ext2_doclusterwrite = 1; SYSCTL_NODE(_vfs, MOUNT_EXT2FS, ext2fs, CTLFLAG_RW, 0, "EXT2FS filesystem"); SYSCTL_INT(_vfs_ext2fs, EXT2FS_CLUSTERREAD, doclusterread, CTLFLAG_RW, &ext2_doclusterread, 0, ""); SYSCTL_INT(_vfs_ext2fs, EXT2FS_CLUSTERWRITE, doclusterwrite, CTLFLAG_RW, &ext2_doclusterwrite, 0, ""); #include /* * Synch an open file. */ /* ARGSUSED */ static int ext2_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct buf *bp; struct timeval tv; struct buf *nbp; int s; /* * Clean memory object. * XXX add this to all file systems. * XXX why is all this fs specific? */ #if !defined(__FreeBSD__) vn_pager_sync(vp, ap->a_waitfor); #endif /* * Flush all dirty buffers associated with a vnode. */ ext2_discard_prealloc(VTOI(vp)); loop: s = splbio(); for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if ((bp->b_flags & B_BUSY)) continue; if ((bp->b_flags & B_DELWRI) == 0) panic("ext2_fsync: not dirty"); bremfree(bp); bp->b_flags |= B_BUSY; splx(s); /* * Wait for I/O associated with indirect blocks to complete, * since there is no way to quickly wait for them below. */ if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT) (void) bawrite(bp); else (void) bwrite(bp); goto loop; } if (ap->a_waitfor == MNT_WAIT) { while (vp->v_numoutput) { vp->v_flag |= VBWAIT; #if !defined(__FreeBSD__) sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1); #else tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "extfsn", 0); #endif } #if DIAGNOSTIC if (vp->v_dirtyblkhd.lh_first) { vprint("ext2_fsync: dirty", vp); goto loop; } #endif } splx(s); gettime(&tv); return (VOP_UPDATE(ap->a_vp, &tv, &tv, ap->a_waitfor == MNT_WAIT)); } diff --git a/sys/isofs/cd9660/cd9660_vnops.c b/sys/isofs/cd9660/cd9660_vnops.c index 6a14c87db904..58d52188dc85 100644 --- a/sys/isofs/cd9660/cd9660_vnops.c +++ b/sys/isofs/cd9660/cd9660_vnops.c @@ -1,1187 +1,1183 @@ /*- * Copyright (c) 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension * Support code is derived from software contributed to Berkeley * by Atsushi Murai (amurai@spec.co.jp). * * 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. * * @(#)cd9660_vnops.c 8.19 (Berkeley) 5/27/95 - * $Id: cd9660_vnops.c,v 1.36 1997/08/25 10:26:18 kato Exp $ + * $Id: cd9660_vnops.c,v 1.37 1997/08/26 07:32:32 phk Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int cd9660_setattr __P((struct vop_setattr_args *)); static int cd9660_open __P((struct vop_open_args *)); static int cd9660_close __P((struct vop_close_args *)); static int cd9660_access __P((struct vop_access_args *)); static int cd9660_getattr __P((struct vop_getattr_args *)); static int cd9660_read __P((struct vop_read_args *)); static int cd9660_ioctl __P((struct vop_ioctl_args *)); -static int cd9660_select __P((struct vop_select_args *)); static int cd9660_mmap __P((struct vop_mmap_args *)); static int cd9660_seek __P((struct vop_seek_args *)); struct isoreaddir; static int iso_uiodir __P((struct isoreaddir *idp, struct dirent *dp, off_t off)); static int iso_shipdir __P((struct isoreaddir *idp)); static int cd9660_readdir __P((struct vop_readdir_args *)); static int cd9660_readlink __P((struct vop_readlink_args *ap)); static int cd9660_abortop __P((struct vop_abortop_args *)); static int cd9660_lock __P((struct vop_lock_args *)); static int cd9660_unlock __P((struct vop_unlock_args *)); static int cd9660_strategy __P((struct vop_strategy_args *)); static int cd9660_print __P((struct vop_print_args *)); static int cd9660_islocked __P((struct vop_islocked_args *)); /* * Sysctl values for the cd9660 filesystem. */ #define CD9660_CLUSTERREAD 1 /* cluster reading enabled */ #define CD9660_MAXID 2 /* number of valid cd9660 ids */ #define CD9660_NAMES { \ {0, 0}, \ { "doclusterread", CTLTYPE_INT}, \ } /* * Setattr call. Only allowed for block and character special devices. */ int cd9660_setattr(ap) struct vop_setattr_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct vattr *vap = ap->a_vap; if (vap->va_flags != (u_long)VNOVAL || vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) return (EROFS); if (vap->va_size != (u_quad_t)VNOVAL) { switch (vp->v_type) { case VDIR: return (EISDIR); case VLNK: case VREG: return (EROFS); case VCHR: case VBLK: case VSOCK: case VFIFO: return (0); } } return (0); } /* * Open called. * * Nothing to do. */ /* ARGSUSED */ static int cd9660_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (0); } /* * Close called * * Update the times on the inode on writeable file systems. */ /* ARGSUSED */ static int cd9660_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (0); } /* * Check mode permission on inode pointer. Mode is READ, WRITE or EXEC. * The mode is shifted to select the owner/group/other fields. The * super user is granted all permissions. */ /* ARGSUSED */ static int cd9660_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct iso_node *ip = VTOI(vp); struct ucred *cred = ap->a_cred; mode_t mask, mode = ap->a_mode; gid_t *gp; int i; /* * Disallow write attempts unless the file is a socket, * fifo, or a block or character device resident on the * file system. */ if (mode & VWRITE) { switch (vp->v_type) { case VDIR: case VLNK: case VREG: return (EROFS); } } /* User id 0 always gets access. */ if (cred->cr_uid == 0) return (0); mask = 0; /* Otherwise, check the owner. */ if (cred->cr_uid == ip->inode.iso_uid) { if (mode & VEXEC) mask |= S_IXUSR; if (mode & VREAD) mask |= S_IRUSR; if (mode & VWRITE) mask |= S_IWUSR; return ((ip->inode.iso_mode & mask) == mask ? 0 : EACCES); } /* Otherwise, check the groups. */ for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) if (ip->inode.iso_gid == *gp) { if (mode & VEXEC) mask |= S_IXGRP; if (mode & VREAD) mask |= S_IRGRP; if (mode & VWRITE) mask |= S_IWGRP; return ((ip->inode.iso_mode & mask) == mask ? 0 : EACCES); } /* Otherwise, check everyone else. */ if (mode & VEXEC) mask |= S_IXOTH; if (mode & VREAD) mask |= S_IROTH; if (mode & VWRITE) mask |= S_IWOTH; return ((ip->inode.iso_mode & mask) == mask ? 0 : EACCES); } static int cd9660_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; register struct vattr *vap = ap->a_vap; register struct iso_node *ip = VTOI(vp); vap->va_fsid = ip->i_dev; vap->va_fileid = ip->i_number; vap->va_mode = ip->inode.iso_mode; vap->va_nlink = ip->inode.iso_links; vap->va_uid = ip->inode.iso_uid; vap->va_gid = ip->inode.iso_gid; vap->va_atime = ip->inode.iso_atime; vap->va_mtime = ip->inode.iso_mtime; vap->va_ctime = ip->inode.iso_ctime; vap->va_rdev = ip->inode.iso_rdev; vap->va_size = (u_quad_t) ip->i_size; if (ip->i_size == 0 && (vap->va_mode & S_IFMT) == S_IFLNK) { struct vop_readlink_args rdlnk; struct iovec aiov; struct uio auio; char *cp; MALLOC(cp, char *, MAXPATHLEN, M_TEMP, M_WAITOK); aiov.iov_base = cp; aiov.iov_len = MAXPATHLEN; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_SYSSPACE; auio.uio_procp = ap->a_p; auio.uio_resid = MAXPATHLEN; rdlnk.a_uio = &auio; rdlnk.a_vp = ap->a_vp; rdlnk.a_cred = ap->a_cred; if (cd9660_readlink(&rdlnk) == 0) vap->va_size = MAXPATHLEN - auio.uio_resid; FREE(cp, M_TEMP); } vap->va_flags = 0; vap->va_gen = 1; vap->va_blocksize = ip->i_mnt->logical_block_size; vap->va_bytes = (u_quad_t) ip->i_size; vap->va_type = vp->v_type; vap->va_filerev = 0; return (0); } static int cd9660_doclusterread = 1; SYSCTL_NODE(_vfs, MOUNT_CD9660, cd9660, CTLFLAG_RW, 0, "CD9660 filesystem"); SYSCTL_INT(_vfs_cd9660, CD9660_CLUSTERREAD, doclusterread, CTLFLAG_RW, &cd9660_doclusterread, 0, ""); /* * Vnode op for reading. */ static int cd9660_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { struct vnode *vp = ap->a_vp; register struct uio *uio = ap->a_uio; register struct iso_node *ip = VTOI(vp); register struct iso_mnt *imp; struct buf *bp; daddr_t lbn, rablock; off_t diff; int rasize, error = 0; long size, n, on; if (uio->uio_resid == 0) return (0); if (uio->uio_offset < 0) return (EINVAL); ip->i_flag |= IN_ACCESS; imp = ip->i_mnt; do { lbn = lblkno(imp, uio->uio_offset); on = blkoff(imp, uio->uio_offset); n = min((u_int)(imp->logical_block_size - on), uio->uio_resid); diff = (off_t)ip->i_size - uio->uio_offset; if (diff <= 0) return (0); if (diff < n) n = diff; size = blksize(imp, ip, lbn); rablock = lbn + 1; if (cd9660_doclusterread) { if (lblktosize(imp, rablock) <= ip->i_size) error = cluster_read(vp, (off_t)ip->i_size, lbn, size, NOCRED, uio->uio_resid, (ap->a_ioflag >> 16), &bp); else error = bread(vp, lbn, size, NOCRED, &bp); } else { if (vp->v_lastr + 1 == lbn && lblktosize(imp, rablock) < ip->i_size) { rasize = blksize(imp, ip, rablock); error = breadn(vp, lbn, size, &rablock, &rasize, 1, NOCRED, &bp); } else error = bread(vp, lbn, size, NOCRED, &bp); } vp->v_lastr = lbn; n = min(n, size - bp->b_resid); if (error) { brelse(bp); return (error); } error = uiomove(bp->b_data + on, (int)n, uio); brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); return (error); } /* ARGSUSED */ static int cd9660_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; u_long a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { printf("You did ioctl for isofs !!\n"); return (ENOTTY); } -/* ARGSUSED */ -static int -cd9660_select(ap) - struct vop_select_args /* { - struct vnode *a_vp; - int a_which; - int a_fflags; - struct ucred *a_cred; - struct proc *a_p; - } */ *ap; -{ - - /* - * We should really check to see if I/O is possible. - */ - return (1); -} - /* * Mmap a file * * NB Currently unsupported. */ /* ARGSUSED */ static int cd9660_mmap(ap) struct vop_mmap_args /* { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (EINVAL); } /* * Seek on a file * * Nothing to do, so just return. */ /* ARGSUSED */ static int cd9660_seek(ap) struct vop_seek_args /* { struct vnode *a_vp; off_t a_oldoff; off_t a_newoff; struct ucred *a_cred; } */ *ap; { return (0); } /* * Structure for reading directories */ struct isoreaddir { struct dirent saveent; struct dirent assocent; struct dirent current; off_t saveoff; off_t assocoff; off_t curroff; struct uio *uio; off_t uio_off; int eofflag; u_long *cookies; int ncookies; }; int iso_uiodir(idp,dp,off) struct isoreaddir *idp; struct dirent *dp; off_t off; { int error; dp->d_name[dp->d_namlen] = 0; dp->d_reclen = GENERIC_DIRSIZ(dp); if (idp->uio->uio_resid < dp->d_reclen) { idp->eofflag = 0; return (-1); } if (idp->cookies) { if (idp->ncookies <= 0) { idp->eofflag = 0; return (-1); } *idp->cookies++ = off; --idp->ncookies; } if (error = uiomove((caddr_t) dp,dp->d_reclen,idp->uio)) return (error); idp->uio_off = off; return (0); } int iso_shipdir(idp) struct isoreaddir *idp; { struct dirent *dp; int cl, sl, assoc; int error; char *cname, *sname; cl = idp->current.d_namlen; cname = idp->current.d_name; assoc = (cl > 1) && (*cname == ASSOCCHAR); if (assoc) { cl--; cname++; } dp = &idp->saveent; sname = dp->d_name; if (!(sl = dp->d_namlen)) { dp = &idp->assocent; sname = dp->d_name + 1; sl = dp->d_namlen - 1; } if (sl > 0) { if (sl != cl || bcmp(sname,cname,sl)) { if (idp->assocent.d_namlen) { if (error = iso_uiodir(idp,&idp->assocent,idp->assocoff)) return (error); idp->assocent.d_namlen = 0; } if (idp->saveent.d_namlen) { if (error = iso_uiodir(idp,&idp->saveent,idp->saveoff)) return (error); idp->saveent.d_namlen = 0; } } } idp->current.d_reclen = GENERIC_DIRSIZ(&idp->current); if (assoc) { idp->assocoff = idp->curroff; bcopy(&idp->current,&idp->assocent,idp->current.d_reclen); } else { idp->saveoff = idp->curroff; bcopy(&idp->current,&idp->saveent,idp->current.d_reclen); } return (0); } /* * Vnode op for readdir */ static int cd9660_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; int *a_ncookies; u_long *a_cookies; } */ *ap; { register struct uio *uio = ap->a_uio; struct isoreaddir *idp; struct vnode *vdp = ap->a_vp; struct iso_node *dp; struct iso_mnt *imp; struct buf *bp = NULL; struct iso_directory_record *ep; int entryoffsetinblock; doff_t endsearch; u_long bmask; int error = 0; int reclen; u_short namelen; int ncookies = 0; u_long *cookies = NULL; dp = VTOI(vdp); imp = dp->i_mnt; bmask = imp->im_bmask; MALLOC(idp, struct isoreaddir *, sizeof(*idp), M_TEMP, M_WAITOK); idp->saveent.d_namlen = idp->assocent.d_namlen = 0; /* * XXX * Is it worth trying to figure out the type? */ idp->saveent.d_type = idp->assocent.d_type = idp->current.d_type = DT_UNKNOWN; idp->uio = uio; if (ap->a_ncookies == NULL) { idp->cookies = NULL; } else { /* * Guess the number of cookies needed. */ ncookies = uio->uio_resid / 16; MALLOC(cookies, u_long *, ncookies * sizeof(u_int), M_TEMP, M_WAITOK); idp->cookies = cookies; idp->ncookies = ncookies; } idp->eofflag = 1; idp->curroff = uio->uio_offset; if ((entryoffsetinblock = idp->curroff & bmask) && (error = VOP_BLKATOFF(vdp, (off_t)idp->curroff, NULL, &bp))) { FREE(idp, M_TEMP); return (error); } endsearch = dp->i_size; while (idp->curroff < endsearch) { /* * If offset is on a block boundary, * read the next directory block. * Release previous if it exists. */ if ((idp->curroff & bmask) == 0) { if (bp != NULL) brelse(bp); if (error = VOP_BLKATOFF(vdp, (off_t)idp->curroff, NULL, &bp)) break; entryoffsetinblock = 0; } /* * Get pointer to next entry. */ ep = (struct iso_directory_record *) ((char *)bp->b_data + entryoffsetinblock); reclen = isonum_711(ep->length); if (reclen == 0) { /* skip to next block, if any */ idp->curroff = (idp->curroff & ~bmask) + imp->logical_block_size; continue; } if (reclen < ISO_DIRECTORY_RECORD_SIZE) { error = EINVAL; /* illegal entry, stop */ break; } if (entryoffsetinblock + reclen > imp->logical_block_size) { error = EINVAL; /* illegal directory, so stop looking */ break; } idp->current.d_namlen = isonum_711(ep->name_len); if (reclen < ISO_DIRECTORY_RECORD_SIZE + idp->current.d_namlen) { error = EINVAL; /* illegal entry, stop */ break; } if (isonum_711(ep->flags)&2) idp->current.d_fileno = isodirino(ep, imp); else idp->current.d_fileno = dbtob(bp->b_blkno) + entryoffsetinblock; idp->curroff += reclen; switch (imp->iso_ftype) { case ISO_FTYPE_RRIP: cd9660_rrip_getname(ep,idp->current.d_name, &namelen, &idp->current.d_fileno,imp); idp->current.d_namlen = (u_char)namelen; if (idp->current.d_namlen) error = iso_uiodir(idp,&idp->current,idp->curroff); break; default: /* ISO_FTYPE_DEFAULT || ISO_FTYPE_9660 || ISO_FTYPE_HIGH_SIERRA*/ strcpy(idp->current.d_name,".."); switch (ep->name[0]) { case 0: idp->current.d_namlen = 1; error = iso_uiodir(idp,&idp->current,idp->curroff); break; case 1: idp->current.d_namlen = 2; error = iso_uiodir(idp,&idp->current,idp->curroff); break; default: isofntrans(ep->name,idp->current.d_namlen, idp->current.d_name, &namelen, imp->iso_ftype == ISO_FTYPE_9660, isonum_711(ep->flags)&4); idp->current.d_namlen = (u_char)namelen; if (imp->iso_ftype == ISO_FTYPE_DEFAULT) error = iso_shipdir(idp); else error = iso_uiodir(idp,&idp->current,idp->curroff); break; } } if (error) break; entryoffsetinblock += reclen; } if (!error && imp->iso_ftype == ISO_FTYPE_DEFAULT) { idp->current.d_namlen = 0; error = iso_shipdir(idp); } if (error < 0) error = 0; if (ap->a_ncookies != NULL) { if (error) free(cookies, M_TEMP); else { /* * Work out the number of cookies actually used. */ *ap->a_ncookies = ncookies - idp->ncookies; *ap->a_cookies = cookies; } } if (bp) brelse (bp); uio->uio_offset = idp->uio_off; *ap->a_eofflag = idp->eofflag; FREE(idp, M_TEMP); return (error); } /* * Return target name of a symbolic link * Shouldn't we get the parent vnode and read the data from there? * This could eventually result in deadlocks in cd9660_lookup. * But otherwise the block read here is in the block buffer two times. */ typedef struct iso_directory_record ISODIR; typedef struct iso_node ISONODE; typedef struct iso_mnt ISOMNT; static int cd9660_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { ISONODE *ip; ISODIR *dirp; ISOMNT *imp; struct buf *bp; struct uio *uio; u_short symlen; int error; char *symname; ip = VTOI(ap->a_vp); imp = ip->i_mnt; uio = ap->a_uio; if (imp->iso_ftype != ISO_FTYPE_RRIP) return (EINVAL); /* * Get parents directory record block that this inode included. */ error = bread(imp->im_devvp, (ip->i_number >> imp->im_bshift) << (imp->im_bshift - DEV_BSHIFT), imp->logical_block_size, NOCRED, &bp); if (error) { brelse(bp); return (EINVAL); } /* * Setup the directory pointer for this inode */ dirp = (ISODIR *)(bp->b_data + (ip->i_number & imp->im_bmask)); /* * Just make sure, we have a right one.... * 1: Check not cross boundary on block */ if ((ip->i_number & imp->im_bmask) + isonum_711(dirp->length) > (unsigned)imp->logical_block_size) { brelse(bp); return (EINVAL); } /* * Now get a buffer * Abuse a namei buffer for now. */ if (uio->uio_segflg == UIO_SYSSPACE) symname = uio->uio_iov->iov_base; else MALLOC(symname, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); /* * Ok, we just gathering a symbolic name in SL record. */ if (cd9660_rrip_getsymname(dirp, symname, &symlen, imp) == 0) { if (uio->uio_segflg != UIO_SYSSPACE) FREE(symname, M_NAMEI); brelse(bp); return (EINVAL); } /* * Don't forget before you leave from home ;-) */ brelse(bp); /* * return with the symbolic name to caller's. */ if (uio->uio_segflg != UIO_SYSSPACE) { error = uiomove(symname, symlen, uio); FREE(symname, M_NAMEI); return (error); } uio->uio_resid -= symlen; uio->uio_iov->iov_base += symlen; uio->uio_iov->iov_len -= symlen; return (0); } /* * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually * done. If a buffer has been saved in anticipation of a CREATE, delete it. */ static int cd9660_abortop(ap) struct vop_abortop_args /* { struct vnode *a_dvp; struct componentname *a_cnp; } */ *ap; { if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); return (0); } /* * Lock an inode. */ static int cd9660_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags, &vp->v_interlock, ap->a_p)); } /* * Unlock an inode. */ static int cd9660_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock, ap->a_p)); } /* * Calculate the logical to physical mapping if not done already, * then call the device strategy routine. */ static int cd9660_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *ap; { register struct buf *bp = ap->a_bp; register struct vnode *vp = bp->b_vp; register struct iso_node *ip; int error; ip = VTOI(vp); if (vp->v_type == VBLK || vp->v_type == VCHR) panic("cd9660_strategy: spec"); if (bp->b_blkno == bp->b_lblkno) { if ((error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL, NULL))) { bp->b_error = error; bp->b_flags |= B_ERROR; biodone(bp); return (error); } if ((long)bp->b_blkno == -1) clrbuf(bp); } if ((long)bp->b_blkno == -1) { biodone(bp); return (0); } vp = ip->i_devvp; bp->b_dev = vp->v_rdev; VOCALL (vp->v_op, VOFFSET(vop_strategy), ap); return (0); } /* * Print out the contents of an inode. */ static int cd9660_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_ISOFS, isofs vnode\n"); return (0); } /* * Check for a locked inode. */ int cd9660_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { return (lockstatus(&VTOI(ap->a_vp)->i_lock)); } /* * Return POSIX pathconf information applicable to cd9660 filesystems. */ int cd9660_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; register_t *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = 1; return (0); case _PC_NAME_MAX: if (VTOI(ap->a_vp)->i_mnt->iso_ftype == ISO_FTYPE_RRIP) *ap->a_retval = NAME_MAX; else *ap->a_retval = 37; return (0); case _PC_PATH_MAX: *ap->a_retval = PATH_MAX; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_NO_TRUNC: *ap->a_retval = 1; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Global vfs data structures for isofs */ #define cd9660_create \ ((int (*) __P((struct vop_create_args *)))eopnotsupp) #define cd9660_mknod ((int (*) __P((struct vop_mknod_args *)))eopnotsupp) #define cd9660_write ((int (*) __P((struct vop_write_args *)))eopnotsupp) #ifdef NFS #define cd9660_lease_check lease_check #else #define cd9660_lease_check ((int (*) __P((struct vop_lease_args *)))nullop) #endif +#define cd9660_poll vop_nopoll #define cd9660_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define cd9660_remove \ ((int (*) __P((struct vop_remove_args *)))eopnotsupp) #define cd9660_link ((int (*) __P((struct vop_link_args *)))eopnotsupp) #define cd9660_rename \ ((int (*) __P((struct vop_rename_args *)))eopnotsupp) #define cd9660_mkdir ((int (*) __P((struct vop_mkdir_args *)))eopnotsupp) #define cd9660_rmdir ((int (*) __P((struct vop_rmdir_args *)))eopnotsupp) #define cd9660_symlink \ ((int (*) __P((struct vop_symlink_args *)))eopnotsupp) #define cd9660_advlock \ ((int (*) __P((struct vop_advlock_args *)))eopnotsupp) #define cd9660_valloc ((int(*) __P(( \ struct vnode *pvp, \ int mode, \ struct ucred *cred, \ struct vnode **vpp))) eopnotsupp) #define cd9660_vfree ((int (*) __P((struct vop_vfree_args *)))eopnotsupp) #define cd9660_truncate \ ((int (*) __P((struct vop_truncate_args *)))eopnotsupp) #define cd9660_update \ ((int (*) __P((struct vop_update_args *)))eopnotsupp) #define cd9660_bwrite \ ((int (*) __P((struct vop_bwrite_args *)))eopnotsupp) /* * Global vfs data structures for cd9660 */ vop_t **cd9660_vnodeop_p; struct vnodeopv_entry_desc cd9660_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)vfs_cache_lookup }, /* lookup */ { &vop_cachedlookup_desc, (vop_t *)cd9660_lookup }, /* lookup */ { &vop_create_desc, (vop_t *)cd9660_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)cd9660_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)cd9660_open }, /* open */ { &vop_close_desc, (vop_t *)cd9660_close }, /* close */ { &vop_access_desc, (vop_t *)cd9660_access }, /* access */ { &vop_getattr_desc, (vop_t *)cd9660_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)cd9660_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)cd9660_read }, /* read */ { &vop_write_desc, (vop_t *)cd9660_write }, /* write */ { &vop_lease_desc, (vop_t *)cd9660_lease_check },/* lease */ { &vop_ioctl_desc, (vop_t *)cd9660_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)cd9660_select }, /* select */ + { &vop_poll_desc, (vop_t *)cd9660_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)cd9660_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)cd9660_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)cd9660_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)cd9660_seek }, /* seek */ { &vop_remove_desc, (vop_t *)cd9660_remove }, /* remove */ { &vop_link_desc, (vop_t *)cd9660_link }, /* link */ { &vop_rename_desc, (vop_t *)cd9660_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)cd9660_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)cd9660_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)cd9660_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)cd9660_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)cd9660_readlink },/* readlink */ { &vop_abortop_desc, (vop_t *)cd9660_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)cd9660_inactive },/* inactive */ { &vop_reclaim_desc, (vop_t *)cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)cd9660_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)cd9660_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)cd9660_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)cd9660_strategy },/* strategy */ { &vop_print_desc, (vop_t *)cd9660_print }, /* print */ { &vop_islocked_desc, (vop_t *)cd9660_islocked },/* islocked */ { &vop_pathconf_desc, (vop_t *)cd9660_pathconf },/* pathconf */ { &vop_advlock_desc, (vop_t *)cd9660_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)cd9660_blkatoff },/* blkatoff */ { &vop_valloc_desc, (vop_t *)cd9660_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)cd9660_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)cd9660_truncate },/* truncate */ { &vop_update_desc, (vop_t *)cd9660_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, { NULL, NULL } }; static struct vnodeopv_desc cd9660_vnodeop_opv_desc = { &cd9660_vnodeop_p, cd9660_vnodeop_entries }; VNODEOP_SET(cd9660_vnodeop_opv_desc); /* * Special device vnode ops */ vop_t **cd9660_specop_p; struct vnodeopv_entry_desc cd9660_specop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)spec_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)spec_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)spec_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)spec_open }, /* open */ { &vop_close_desc, (vop_t *)spec_close }, /* close */ { &vop_access_desc, (vop_t *)cd9660_access }, /* access */ { &vop_getattr_desc, (vop_t *)cd9660_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)cd9660_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)spec_read }, /* read */ { &vop_write_desc, (vop_t *)spec_write }, /* write */ { &vop_lease_desc, (vop_t *)spec_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)spec_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)spec_select }, /* select */ + { &vop_poll_desc, (vop_t *)spec_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)spec_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)spec_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)spec_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)spec_seek }, /* seek */ { &vop_remove_desc, (vop_t *)spec_remove }, /* remove */ { &vop_link_desc, (vop_t *)spec_link }, /* link */ { &vop_rename_desc, (vop_t *)spec_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)spec_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)spec_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)spec_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)spec_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)spec_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)cd9660_inactive },/* inactive */ { &vop_reclaim_desc, (vop_t *)cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)cd9660_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)cd9660_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)spec_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)spec_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)cd9660_print }, /* print */ { &vop_islocked_desc, (vop_t *)cd9660_islocked },/* islocked */ { &vop_pathconf_desc, (vop_t *)spec_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)spec_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)spec_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)spec_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)spec_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)cd9660_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, { NULL, NULL } }; static struct vnodeopv_desc cd9660_specop_opv_desc = { &cd9660_specop_p, cd9660_specop_entries }; VNODEOP_SET(cd9660_specop_opv_desc); vop_t **cd9660_fifoop_p; struct vnodeopv_entry_desc cd9660_fifoop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)fifo_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)fifo_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)fifo_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)fifo_open }, /* open */ { &vop_close_desc, (vop_t *)fifo_close }, /* close */ { &vop_access_desc, (vop_t *)cd9660_access }, /* access */ { &vop_getattr_desc, (vop_t *)cd9660_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)cd9660_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)fifo_read }, /* read */ { &vop_write_desc, (vop_t *)fifo_write }, /* write */ { &vop_lease_desc, (vop_t *)fifo_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)fifo_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)fifo_select }, /* select */ + { &vop_poll_desc, (vop_t *)fifo_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)fifo_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)fifo_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)fifo_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)fifo_seek }, /* seek */ { &vop_remove_desc, (vop_t *)fifo_remove }, /* remove */ { &vop_link_desc, (vop_t *)fifo_link } , /* link */ { &vop_rename_desc, (vop_t *)fifo_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)fifo_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)fifo_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)fifo_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)fifo_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)cd9660_inactive },/* inactive */ { &vop_reclaim_desc, (vop_t *)cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)cd9660_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)cd9660_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)fifo_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)fifo_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)cd9660_print }, /* print */ { &vop_islocked_desc, (vop_t *)cd9660_islocked },/* islocked */ { &vop_pathconf_desc, (vop_t *)fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)fifo_valloc }, /* valloc */ +/* XXX: vop_reallocpages */ { &vop_vfree_desc, (vop_t *)fifo_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)fifo_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)cd9660_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, { NULL, NULL } }; static struct vnodeopv_desc cd9660_fifoop_opv_desc = { &cd9660_fifoop_p, cd9660_fifoop_entries }; VNODEOP_SET(cd9660_fifoop_opv_desc); diff --git a/sys/miscfs/deadfs/dead_vnops.c b/sys/miscfs/deadfs/dead_vnops.c index b167b6fe3fb0..a8856f9b7711 100644 --- a/sys/miscfs/deadfs/dead_vnops.c +++ b/sys/miscfs/deadfs/dead_vnops.c @@ -1,381 +1,370 @@ /* * 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. * * @(#)dead_vnops.c 8.1 (Berkeley) 6/10/93 - * $Id: dead_vnops.c,v 1.13 1997/02/22 09:40:13 peter Exp $ + * $Id: dead_vnops.c,v 1.14 1997/09/02 20:06:08 bde Exp $ */ #include #include #include #include #include static int chkvnlock __P((struct vnode *)); /* * Prototypes for dead operations on vnodes. */ static int dead_badop __P((void)); static int dead_ebadf __P((void)); static int dead_lookup __P((struct vop_lookup_args *)); #define dead_create ((int (*) __P((struct vop_create_args *)))dead_badop) #define dead_mknod ((int (*) __P((struct vop_mknod_args *)))dead_badop) static int dead_open __P((struct vop_open_args *)); #define dead_close ((int (*) __P((struct vop_close_args *)))nullop) #define dead_access ((int (*) __P((struct vop_access_args *)))dead_ebadf) #define dead_getattr ((int (*) __P((struct vop_getattr_args *)))dead_ebadf) #define dead_setattr ((int (*) __P((struct vop_setattr_args *)))dead_ebadf) static int dead_read __P((struct vop_read_args *)); static int dead_write __P((struct vop_write_args *)); static int dead_ioctl __P((struct vop_ioctl_args *)); -static int dead_select __P((struct vop_select_args *)); +#define dead_poll vop_nopoll #define dead_mmap ((int (*) __P((struct vop_mmap_args *)))dead_badop) #define dead_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define dead_seek ((int (*) __P((struct vop_seek_args *)))nullop) #define dead_remove ((int (*) __P((struct vop_remove_args *)))dead_badop) #define dead_link ((int (*) __P((struct vop_link_args *)))dead_badop) #define dead_rename ((int (*) __P((struct vop_rename_args *)))dead_badop) #define dead_mkdir ((int (*) __P((struct vop_mkdir_args *)))dead_badop) #define dead_rmdir ((int (*) __P((struct vop_rmdir_args *)))dead_badop) #define dead_symlink ((int (*) __P((struct vop_symlink_args *)))dead_badop) #define dead_readdir ((int (*) __P((struct vop_readdir_args *)))dead_ebadf) #define dead_readlink ((int (*) __P((struct vop_readlink_args *)))dead_ebadf) #define dead_abortop ((int (*) __P((struct vop_abortop_args *)))dead_badop) #define dead_inactive ((int (*) __P((struct vop_inactive_args *)))nullop) #define dead_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop) static int dead_lock __P((struct vop_lock_args *)); #define dead_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) static int dead_bmap __P((struct vop_bmap_args *)); static int dead_strategy __P((struct vop_strategy_args *)); static int dead_print __P((struct vop_print_args *)); #define dead_islocked ((int(*) __P((struct vop_islocked_args *)))vop_noislocked) #define dead_pathconf ((int (*) __P((struct vop_pathconf_args *)))dead_ebadf) #define dead_advlock ((int (*) __P((struct vop_advlock_args *)))dead_ebadf) #define dead_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))dead_badop) #define dead_valloc ((int (*) __P((struct vop_valloc_args *)))dead_badop) #define dead_vfree ((int (*) __P((struct vop_vfree_args *)))dead_badop) #define dead_truncate ((int (*) __P((struct vop_truncate_args *)))nullop) #define dead_update ((int (*) __P((struct vop_update_args *)))nullop) #define dead_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop) vop_t **dead_vnodeop_p; static struct vnodeopv_entry_desc dead_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)dead_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)dead_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)dead_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)dead_open }, /* open */ { &vop_close_desc, (vop_t *)dead_close }, /* close */ { &vop_access_desc, (vop_t *)dead_access }, /* access */ { &vop_getattr_desc, (vop_t *)dead_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)dead_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)dead_read }, /* read */ { &vop_write_desc, (vop_t *)dead_write }, /* write */ +/* XXX: vop_lease */ { &vop_ioctl_desc, (vop_t *)dead_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)dead_select }, /* select */ + { &vop_poll_desc, (vop_t *)dead_poll }, /* poll */ +/* XXX: vop_revoke */ { &vop_mmap_desc, (vop_t *)dead_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)dead_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)dead_seek }, /* seek */ { &vop_remove_desc, (vop_t *)dead_remove }, /* remove */ { &vop_link_desc, (vop_t *)dead_link }, /* link */ { &vop_rename_desc, (vop_t *)dead_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)dead_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)dead_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)dead_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)dead_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)dead_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)dead_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)dead_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)dead_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)dead_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)dead_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)dead_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)dead_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)dead_print }, /* print */ { &vop_islocked_desc, (vop_t *)dead_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)dead_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)dead_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)dead_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)dead_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)dead_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)dead_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)dead_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)dead_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc dead_vnodeop_opv_desc = { &dead_vnodeop_p, dead_vnodeop_entries }; VNODEOP_SET(dead_vnodeop_opv_desc); /* * Trivial lookup routine that always fails. */ /* ARGSUSED */ static int dead_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { *ap->a_vpp = NULL; return (ENOTDIR); } /* * Open always fails as if device did not exist. */ /* ARGSUSED */ static int dead_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (ENXIO); } /* * Vnode op for read */ /* ARGSUSED */ static int dead_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { if (chkvnlock(ap->a_vp)) panic("dead_read: lock"); #if 0 /* Lite2 behaviour */ /* * Return EOF for tty devices, EIO for others */ if ((ap->a_vp->v_flag & VISTTY) == 0) return (EIO); #else /* * Return EOF for character devices, EIO for others */ if (ap->a_vp->v_type != VCHR) return (EIO); #endif return (0); } /* * Vnode op for write */ /* ARGSUSED */ static int dead_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { if (chkvnlock(ap->a_vp)) panic("dead_write: lock"); return (EIO); } /* * Device ioctl operation. */ /* ARGSUSED */ static int dead_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { if (!chkvnlock(ap->a_vp)) return (EBADF); return (VCALL(ap->a_vp, VOFFSET(vop_ioctl), ap)); } -/* ARGSUSED */ -static int -dead_select(ap) - struct vop_select_args /* { - struct vnode *a_vp; - int a_which; - int a_fflags; - struct ucred *a_cred; - struct proc *a_p; - } */ *ap; -{ - - /* - * Let the user find out that the descriptor is gone. - */ - return (1); -} - /* * Just call the device strategy routine */ static int dead_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *ap; { if (ap->a_bp->b_vp == NULL || !chkvnlock(ap->a_bp->b_vp)) { ap->a_bp->b_flags |= B_ERROR; biodone(ap->a_bp); return (EIO); } return (VOP_STRATEGY(ap->a_bp)); } /* * Wait until the vnode has finished changing state. */ static int dead_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; /* * Since we are not using the lock manager, we must clear * the interlock here. */ if (ap->a_flags & LK_INTERLOCK) { simple_unlock(&vp->v_interlock); ap->a_flags &= ~LK_INTERLOCK; } if (!chkvnlock(vp)) return (0); return (VCALL(vp, VOFFSET(vop_lock), ap)); } /* * Wait until the vnode has finished changing state. */ static int dead_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ *ap; { if (!chkvnlock(ap->a_vp)) return (EIO); return (VOP_BMAP(ap->a_vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp, ap->a_runb)); } /* * Print out the contents of a dead vnode. */ /* ARGSUSED */ static int dead_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_NON, dead vnode\n"); return (0); } /* * Empty vnode failed operation */ static int dead_ebadf() { return (EBADF); } /* * Empty vnode bad operation */ static int dead_badop() { panic("dead_badop called"); /* NOTREACHED */ } /* * We have to wait during times when the vnode is * in a state of change. */ int chkvnlock(vp) register struct vnode *vp; { int locked = 0; while (vp->v_flag & VXLOCK) { vp->v_flag |= VXWANT; (void) tsleep((caddr_t)vp, PINOD, "ckvnlk", 0); locked = 1; } return (locked); } diff --git a/sys/miscfs/devfs/devfs_vnops.c b/sys/miscfs/devfs/devfs_vnops.c index 73d6ff562b83..f735ba8940ce 100644 --- a/sys/miscfs/devfs/devfs_vnops.c +++ b/sys/miscfs/devfs/devfs_vnops.c @@ -1,1829 +1,1843 @@ /* * Written by Julian Elischer (julian@DIALix.oz.au) * - * $Header: /home/ncvs/src/sys/miscfs/devfs/devfs_vnops.c,v 1.37 1997/08/25 20:31:00 phk Exp $ + * $Header: /home/ncvs/src/sys/miscfs/devfs/devfs_vnops.c,v 1.38 1997/08/27 02:58:40 julian Exp $ * * symlinks can wait 'til later. */ #include #include #include #include #include #include #include #include #include #include /* definitions of spec functions we use */ #include #include /* * Insert description here */ /* * Convert a component of a pathname into a pointer to a locked node. * This is a very central and rather complicated routine. * If the file system is not maintained in a strict tree hierarchy, * this can result in a deadlock situation (see comments in code below). * * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on * whether the name is to be looked up, created, renamed, or deleted. * When CREATE, RENAME, or DELETE is specified, information usable in * creating, renaming, or deleting a directory entry may be calculated. * If flag has LOCKPARENT or'ed into it and the target of the pathname * exists, lookup returns both the target and its parent directory locked. * When creating or renaming and LOCKPARENT is specified, the target may * not be ".". When deleting and LOCKPARENT is specified, the target may * be "."., but the caller must check to ensure it does an vrele and DNUNLOCK * instead of two DNUNLOCKs. * * Overall outline of devfs_lookup: * * check accessibility of directory * null terminate the component (lookup leaves the whole string alone) * look for name in cache, if found, then if at end of path * and deleting or creating, drop it, else return name * search for name in directory, to found or notfound * notfound: * if creating, return locked directory, * else return error * found: * if at end of path and deleting, return information to allow delete * if at end of path and rewriting (RENAME and LOCKPARENT), lock target * node and return info to allow rewrite * if not at end, add name to cache; if at end and neither creating * nor deleting, add name to cache * On return to lookup, remove the null termination we put in at the start. * * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent node unlocked. */ static int devfs_lookup(struct vop_lookup_args *ap) /*struct vop_lookup_args { struct vnode * a_dvp; directory vnode ptr struct vnode ** a_vpp; where to put the result struct componentname * a_cnp; the name we want };*/ { struct componentname *cnp = ap->a_cnp; struct vnode *dir_vnode = ap->a_dvp; struct vnode **result_vnode = ap->a_vpp; dn_p dir_node; /* the directory we are searching */ dn_p new_node; /* the node we are searching for */ devnm_p new_nodename; int flags = cnp->cn_flags; int op = cnp->cn_nameiop; /* LOOKUP, CREATE, RENAME, or DELETE */ int lockparent = flags & LOCKPARENT; int wantparent = flags & (LOCKPARENT|WANTPARENT); int error = 0; struct proc *p = cnp->cn_proc; char heldchar; /* the char at the end of the name componet */ *result_vnode = NULL; /* safe not sorry */ /*XXX*/ DBPRINT(("lookup\n")); if (dir_vnode->v_usecount == 0) printf("dir had no refs "); if (devfs_vntodn(dir_vnode,&dir_node)) { printf("vnode has changed?\n"); vprint("=",dir_vnode); return(EINVAL); } /* * Check accessiblity of directory. */ if (dir_node->type != DEV_DIR) /* XXX or symlink? */ { return (ENOTDIR); } if (error = VOP_ACCESS(dir_vnode, VEXEC, cnp->cn_cred, p)) { return (error); } /* * We now have a segment name to search for, and a directory to search. * */ /***********************************************************************\ * SEARCH FOR NAME * * while making sure the component is null terminated for the strcmp * \***********************************************************************/ heldchar = cnp->cn_nameptr[cnp->cn_namelen]; cnp->cn_nameptr[cnp->cn_namelen] = '\0'; new_nodename = dev_findname(dir_node,cnp->cn_nameptr); cnp->cn_nameptr[cnp->cn_namelen] = heldchar; if(!new_nodename) { /*******************************************************\ * Failed to find it.. (That may be good) * \*******************************************************/ new_node = NULL; /* to be safe */ /* * If creating, and at end of pathname * then can consider * allowing file to be created. */ if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) { return ENOENT; } /* * Access for write is interpreted as allowing * creation of files in the directory. */ if (error = VOP_ACCESS(dir_vnode, VWRITE, cnp->cn_cred, p)) { DBPRINT(("MKACCESS ")); return (error); } /* * We return with the directory locked, so that * the parameters we set up above will still be * valid if we actually decide to add a new entry. * We return ni_vp == NULL to indicate that the entry * does not currently exist; we leave a pointer to * the (locked) directory vnode in namei_data->ni_dvp. * The pathname buffer is saved so that the name * can be obtained later. * * NB - if the directory is unlocked, then this * information cannot be used. */ cnp->cn_flags |= SAVENAME; /*XXX why? */ if (!lockparent) VOP_UNLOCK(dir_vnode, 0, p); return (EJUSTRETURN); } /***************************************************************\ * Found it.. this is not always a good thing.. * \***************************************************************/ new_node = new_nodename->dnp; new_node->last_lookup = new_nodename; /* for unlink */ /* * If deleting, and at end of pathname, return * parameters which can be used to remove file. * If the wantparent flag isn't set, we return only * the directory (in namei_data->ni_dvp), otherwise we go * on and lock the node, being careful with ".". */ if (op == DELETE && (flags & ISLASTCN)) { /* * Write access to directory required to delete files. */ if (error = VOP_ACCESS(dir_vnode, VWRITE, cnp->cn_cred, p)) return (error); /* * we are trying to delete '.'. What does this mean? XXX */ if (dir_node == new_node) { VREF(dir_vnode); *result_vnode = dir_vnode; return (0); } /* * If directory is "sticky", then user must own * the directory, or the file in it, else she * may not delete it (unless she's root). This * implements append-only directories. */ devfs_dntovn(new_node,result_vnode); #ifdef NOTYET if ((dir_node->mode & ISVTX) && cnp->cn_cred->cr_uid != 0 && cnp->cn_cred->cr_uid != dir_node->uid && cnp->cn_cred->cr_uid != new_node->uid) { VOP_UNLOCK(*result_vnode, 0, p); return (EPERM); } #endif if (!lockparent) VOP_UNLOCK(dir_vnode, 0, p); return (0); } /* * If rewriting (RENAME), return the vnode and the * information required to rewrite the present directory * Must get node of directory entry to verify it's a * regular file, or empty directory. */ if (op == RENAME && wantparent && (flags & ISLASTCN)) { /* * Are we allowed to change the holding directory? */ if (error = VOP_ACCESS(dir_vnode, VWRITE, cnp->cn_cred, p)) return (error); /* * Careful about locking second node. * This can only occur if the target is ".". */ if (dir_node == new_node) return (EISDIR); devfs_dntovn(new_node,result_vnode); /* hmm save the 'from' name (we need to delete it) */ cnp->cn_flags |= SAVENAME; if (!lockparent) VOP_UNLOCK(dir_vnode, 0, p); return (0); } /* * Step through the translation in the name. We do not unlock the * directory because we may need it again if a symbolic link * is relative to the current directory. Instead we save it * unlocked as "saved_dir_node" XXX. We must get the target * node before unlocking * the directory to insure that the node will not be removed * before we get it. We prevent deadlock by always fetching * nodes from the root, moving down the directory tree. Thus * when following backward pointers ".." we must unlock the * parent directory before getting the requested directory. * There is a potential race condition here if both the current * and parent directories are removed before the lock for the * node associated with ".." returns. We hope that this occurs * infrequently since we cannot avoid this race condition without * implementing a sophisticated deadlock detection algorithm. * Note also that this simple deadlock detection scheme will not * work if the file system has any hard links other than ".." * that point backwards in the directory structure. */ if (flags & ISDOTDOT) { VOP_UNLOCK(dir_vnode, 0, p); /* race to get the node */ devfs_dntovn(new_node,result_vnode); if (lockparent && (flags & ISLASTCN)) vn_lock(dir_vnode, LK_EXCLUSIVE | LK_RETRY, p); } else if (dir_node == new_node) { VREF(dir_vnode); /* we want ourself, ie "." */ *result_vnode = dir_vnode; } else { devfs_dntovn(new_node,result_vnode); if (!lockparent || (flags & ISLASTCN)) VOP_UNLOCK(dir_vnode, 0, p); } DBPRINT(("GOT\n")); return (0); } /* * Create a regular file. * We must also free the pathname buffer pointed at * by ndp->ni_pnbuf, always on error, or only if the * SAVESTART bit in ni_nameiop is clear on success. * * * Always error... no such thing in this FS */ #ifdef notyet static int devfs_create(struct vop_mknod_args *ap) /*struct vop_mknod_args { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ { DBPRINT(("create\n")); vput(ap->a_dvp); return EINVAL; } static int devfs_mknod( struct vop_mknod_args *ap) /*struct vop_mknod_args { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ { int error; DBPRINT(("mknod\n")); switch (ap->a_vap->va_type) { case VDIR: #ifdef VNSLEAZE return devfs_mkdir(ap); /*XXX check for WILLRELE settings (different)*/ #else error = VOP_MKDIR(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap); #endif break; /* * devfs_create() sets ndp->ni_vp. */ case VREG: #ifdef VNSLEAZE return devfs_create(ap); /*XXX check for WILLRELE settings (different)*/ #else error = VOP_CREATE(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap); #endif break; default: return EINVAL; break; } return error; } #endif /* notyet */ static int devfs_open(struct vop_open_args *ap) /*struct vop_open_args { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ { DBPRINT(("open\n")); return 0; } #ifdef notyet static int devfs_close( struct vop_close_args *ap) /*struct vop_close_args { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ { DBPRINT(("close\n")); return 0; } #endif /* notyet */ static int devfs_access(struct vop_access_args *ap) /*struct vop_access_args { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ { /* * mode is filled with a combination of VREAD, VWRITE, * and/or VEXEC bits turned on. In an octal number these * are the Y in 0Y00. */ struct vnode *vp = ap->a_vp; int mode = ap->a_mode; struct ucred *cred = ap->a_cred; dn_p file_node; int error; gid_t *gp; int i; DBPRINT(("access\n")); if (error = devfs_vntodn(vp,&file_node)) { printf("devfs_vntodn returned %d ",error); return error; } /* * if we are not running as a process, we are in the * kernel and we DO have permission */ if (ap->a_p == NULL) return 0; /* * Access check is based on only one of owner, group, public. * If not owner, then check group. If not a member of the * group, then check public access. */ if (cred->cr_uid != file_node->uid) { /* failing that.. try groups */ mode >>= 3; gp = cred->cr_groups; for (i = 0; i < cred->cr_ngroups; i++, gp++) { if (file_node->gid == *gp) { goto found; } } /* failing that.. try general access */ mode >>= 3; found: ; } if ((file_node->mode & mode) == mode) return (0); /* * Root gets to do anything. * but only use suser prives as a last resort * (Use of super powers is recorded in ap->a_p->p_acflag) */ if( suser(cred, &ap->a_p->p_acflag) == 0) /* XXX what if no proc? */ return 0; return (EACCES); } static int devfs_getattr(struct vop_getattr_args *ap) /*struct vop_getattr_args { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ { struct vnode *vp = ap->a_vp; struct vattr *vap = ap->a_vap; dn_p file_node; int error; DBPRINT(("getattr\n")); if (error = devfs_vntodn(vp,&file_node)) { printf("devfs_vntodn returned %d ",error); return error; } vap->va_rdev = 0;/* default value only */ vap->va_mode = file_node->mode; switch (file_node->type) { case DEV_DIR: vap->va_rdev = (dev_t)file_node->dvm; vap->va_mode |= (S_IFDIR); break; case DEV_CDEV: vap->va_rdev = file_node->by.Cdev.dev; vap->va_mode |= (S_IFCHR); break; case DEV_BDEV: vap->va_rdev = file_node->by.Bdev.dev; vap->va_mode |= (S_IFBLK); break; case DEV_SLNK: break; } vap->va_type = vp->v_type; vap->va_nlink = file_node->links; vap->va_uid = file_node->uid; vap->va_gid = file_node->gid; vap->va_fsid = (long)file_node->dvm; vap->va_fileid = (long)file_node; vap->va_size = file_node->len; /* now a u_quad_t */ vap->va_blocksize = 512; if(file_node->ctime.tv_sec) { vap->va_ctime = file_node->ctime; } else { TIMEVAL_TO_TIMESPEC(&boottime,&(vap->va_ctime)); } if(file_node->mtime.tv_sec) { vap->va_mtime = file_node->mtime; } else { TIMEVAL_TO_TIMESPEC(&boottime,&(vap->va_mtime)); } if(file_node->atime.tv_sec) { vap->va_atime = file_node->atime; } else { TIMEVAL_TO_TIMESPEC(&boottime,&(vap->va_atime)); } vap->va_gen = 0; vap->va_flags = 0; vap->va_bytes = file_node->len; /* u_quad_t */ vap->va_filerev = 0; /* XXX */ /* u_quad_t */ vap->va_vaflags = 0; /* XXX */ return 0; } static int devfs_setattr(struct vop_setattr_args *ap) /*struct vop_setattr_args { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ { struct vnode *vp = ap->a_vp; struct vattr *vap = ap->a_vap; struct ucred *cred = ap->a_cred; struct proc *p = ap->a_p; int error = 0; gid_t *gp; int i; dn_p file_node; struct timeval tv; if (error = devfs_vntodn(vp,&file_node)) { printf("devfs_vntodn returned %d ",error); return error; } DBPRINT(("setattr\n")); if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL )) { return EINVAL; } /* * Anyone can touch the files in such a way that the times are set * to NOW (e.g. run 'touch') if they have write permissions * however only the owner or root can set "un-natural times. * They also don't need write permissions. */ if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { #if 0 /* * This next test is pointless under devfs for now.. * as there is only one devfs hiding under potentially many * mountpoints and actual device node are really 'mounted' under * a FAKE mountpoint inside the kernel only, no matter where it * APPEARS they are mounted to the outside world.. * A readonly devfs doesn't exist anyway. */ if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); #endif if (((vap->va_vaflags & VA_UTIMES_NULL) == 0) && (cred->cr_uid != file_node->uid) && suser(cred, &p->p_acflag)) return (EPERM); if(VOP_ACCESS(vp, VWRITE, cred, p)) return (EACCES); file_node->atime = vap->va_atime; file_node->mtime = vap->va_mtime; microtime(&tv); TIMEVAL_TO_TIMESPEC(&tv, &file_node->ctime); return (0); } /* * Change the permissions.. must be root or owner to do this. */ if (vap->va_mode != (u_short)VNOVAL) { if ((cred->cr_uid != file_node->uid) && suser(cred, &p->p_acflag)) return (EPERM); /* set drwxwxrwx stuff */ file_node->mode &= ~07777; file_node->mode |= vap->va_mode & 07777; } /* * Change the owner.. must be root to do this. */ if (vap->va_uid != (uid_t)VNOVAL) { if (suser(cred, &p->p_acflag)) return (EPERM); file_node->uid = vap->va_uid; } /* * Change the group.. must be root or owner to do this. * If we are the owner, we must be in the target group too. * don't use suser() unless you have to as it reports * whether you needed suser powers or not. */ if (vap->va_gid != (gid_t)VNOVAL) { if (cred->cr_uid == file_node->uid){ gp = cred->cr_groups; for (i = 0; i < cred->cr_ngroups; i++, gp++) { if (vap->va_gid == *gp) goto cando; } } /* * we can't do it with normal privs, * do we have an ace up our sleeve? */ if( suser(cred, &p->p_acflag)) return (EPERM); cando: file_node->gid = vap->va_gid; } #if 0 /* * Copied from somewhere else * but only kept as a marker and reminder of the fact that * flags should be handled some day */ if (vap->va_flags != VNOVAL) { if (error = suser(cred, &p->p_acflag)) return error; if (cred->cr_uid == 0) ; else { } } #endif return error; } static int devfs_read(struct vop_read_args *ap) /*struct vop_read_args { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ { int error = 0; dn_p file_node; DBPRINT(("read\n")); if (error = devfs_vntodn(ap->a_vp,&file_node)) { printf("devfs_vntodn returned %d ",error); return error; } switch (ap->a_vp->v_type) { case VREG: return(EINVAL); case VDIR: return VOP_READDIR(ap->a_vp,ap->a_uio,ap->a_cred, NULL,NULL,NULL); case VCHR: case VBLK: error = spec_read(ap); TIMEVAL_TO_TIMESPEC(&time,&(file_node->atime)) return(error); default: panic("devfs_read(): bad file type"); break; } } /* * Write data to a file or directory. */ static int devfs_write(struct vop_write_args *ap) /*struct vop_write_args { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ { dn_p file_node; int error; DBPRINT(("write\n")); if (error = devfs_vntodn(ap->a_vp,&file_node)) { printf("devfs_vntodn returned %d ",error); return error; } switch (ap->a_vp->v_type) { case VREG: return(EINVAL); case VDIR: return(EISDIR); case VCHR: case VBLK: error = spec_write(ap); TIMEVAL_TO_TIMESPEC(&time,&(file_node->mtime)) return(error); default: panic("devfs_write(): bad file type"); break; } } /* presently not called from devices anyhow */ #ifdef notyet static int devfs_ioctl(struct vop_ioctl_args *ap) /*struct vop_ioctl_args { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ { DBPRINT(("ioctl\n")); return ENOTTY; } static int devfs_select(struct vop_select_args *ap) /*struct vop_select_args { struct vnode *a_vp; int a_which; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ { DBPRINT(("select\n")); return 1; /* filesystems never block? */ } static int devfs_mmap(struct vop_mmap_args *ap) /*struct vop_mmap_args { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ { DBPRINT(("mmap\n")); return EINVAL; } /* * Flush the blocks of a file to disk. */ static int devfs_fsync(struct vop_fsync_args *ap) /*struct vop_fsync_args { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ { DBPRINT(("fsync\n")); return(0); } static int devfs_seek(struct vop_seek_args *ap) /*struct vop_seek_args { struct vnode *a_vp; off_t a_oldoff; off_t a_newoff; struct ucred *a_cred; } */ { DBPRINT(("seek\n")); return 0; } #endif /* notyet */ static int devfs_remove(struct vop_remove_args *ap) /*struct vop_remove_args { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ { struct vnode *vp = ap->a_vp; struct vnode *dvp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; dn_p tp, tdp; devnm_p tnp; int doingdirectory = 0; int error = 0; uid_t ouruid = cnp->cn_cred->cr_uid; DBPRINT(("remove\n")); /* * Lock our directories and get our name pointers * assume that the names are null terminated as they * are the end of the path. Get pointers to all our * devfs structures. */ if ( error = devfs_vntodn(dvp,&tdp)) { abortit: VOP_ABORTOP(dvp, cnp); if (dvp == vp) /* eh? */ vrele(dvp); else vput(dvp); if (vp) vput(vp); return (error); } if ( error = devfs_vntodn(vp,&tp)) goto abortit; /* * Assuming we are atomic, dev_lookup left this for us */ tnp = tp->last_lookup; /* * Check we are doing legal things WRT the new flags */ if ((tp->flags & (IMMUTABLE | APPEND)) || (tdp->flags & APPEND) /*XXX eh?*/ ) { error = EPERM; goto abortit; } /* * Make sure that we don't try do something stupid */ if ((tp->type) == DEV_DIR) { /* * Avoid ".", "..", and aliases of "." for obvious reasons. */ if ( (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') || (cnp->cn_flags&ISDOTDOT) ) { error = EINVAL; goto abortit; } doingdirectory++; } /*********************************** * Start actually doing things.... * ***********************************/ TIMEVAL_TO_TIMESPEC(&time,&(tdp->mtime)); /* * own the parent directory, or the destination of the rename, * otherwise the destination may not be changed (except by * root). This implements append-only directories. * XXX shoudn't this be in generic code? */ if ((tdp->mode & S_ISTXT) && ouruid != 0 && ouruid != tdp->uid && ouruid != tp->uid ) { error = EPERM; goto abortit; } /* * Target must be empty if a directory and have no links * to it. Also, ensure source and target are compatible * (both directories, or both not directories). */ if (( doingdirectory) && (tp->links > 2)) { printf("nlink = %d\n",tp->links); /*XXX*/ error = ENOTEMPTY; goto abortit; } dev_free_name(tnp); tp = NULL; vput(vp); vput(dvp); return (error); } /* */ static int devfs_link(struct vop_link_args *ap) /*struct vop_link_args { struct vnode *a_tdvp; struct vnode *a_vp; struct componentname *a_cnp; } */ { struct vnode *vp = ap->a_vp; struct vnode *tdvp = ap->a_tdvp; struct componentname *cnp = ap->a_cnp; dn_p fp, tdp; devnm_p tnp; int error = 0; DBPRINT(("link\n")); /* * First catch an arbitrary restriction for this FS */ if(cnp->cn_namelen > DEVMAXNAMESIZE) { error = ENAMETOOLONG; goto abortit; } /* * Lock our directories and get our name pointers * assume that the names are null terminated as they * are the end of the path. Get pointers to all our * devfs structures. */ if ( error = devfs_vntodn(tdvp,&tdp)) goto abortit; if ( error = devfs_vntodn(vp,&fp)) goto abortit; /* * trying to move it out of devfs? (v_tag == VT_DEVFS) */ if ( (vp->v_tag != VT_DEVFS) || (vp->v_tag != tdvp->v_tag) ) { error = EXDEV; abortit: VOP_ABORTOP(tdvp, cnp); goto out; } /* * Check we are doing legal things WRT the new flags */ if (fp->flags & (IMMUTABLE | APPEND)) { error = EPERM; goto abortit; } /*********************************** * Start actually doing things.... * ***********************************/ TIMEVAL_TO_TIMESPEC(&time,&(tdp->atime)); error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp); out: vput(tdvp); return (error); } /* * Rename system call. Seems overly complicated to me... * rename("foo", "bar"); * is essentially * unlink("bar"); * link("foo", "bar"); * unlink("foo"); * but ``atomically''. * * When the target exists, both the directory * and target vnodes are locked. * the source and source-parent vnodes are referenced * * * Basic algorithm is: * * 1) Bump link count on source while we're linking it to the * target. This also ensure the inode won't be deleted out * from underneath us while we work (it may be truncated by * a concurrent `trunc' or `open' for creation). * 2) Link source to destination. If destination already exists, * delete it first. * 3) Unlink source reference to node if still around. If a * directory was moved and the parent of the destination * is different from the source, patch the ".." entry in the * directory. */ static int devfs_rename(struct vop_rename_args *ap) /*struct vop_rename_args { struct vnode *a_fdvp; struct vnode *a_fvp; struct componentname *a_fcnp; struct vnode *a_tdvp; struct vnode *a_tvp; struct componentname *a_tcnp; } */ { struct vnode *tvp = ap->a_tvp; struct vnode *tdvp = ap->a_tdvp; struct vnode *fvp = ap->a_fvp; struct vnode *fdvp = ap->a_fdvp; struct componentname *tcnp = ap->a_tcnp; struct componentname *fcnp = ap->a_fcnp; struct proc *p = fcnp->cn_proc; dn_p fp, fdp, tp, tdp; devnm_p fnp,tnp; int doingdirectory = 0; int error = 0; /* * First catch an arbitrary restriction for this FS */ if(tcnp->cn_namelen > DEVMAXNAMESIZE) { error = ENAMETOOLONG; goto abortit; } /* * Lock our directories and get our name pointers * assume that the names are null terminated as they * are the end of the path. Get pointers to all our * devfs structures. */ if ( error = devfs_vntodn(tdvp,&tdp)) goto abortit; if ( error = devfs_vntodn(fdvp,&fdp)) goto abortit; if ( error = devfs_vntodn(fvp,&fp)) goto abortit; fnp = fp->last_lookup; if (tvp) { if ( error = devfs_vntodn(tvp,&tp)) goto abortit; tnp = tp->last_lookup; } else { tp = NULL; tnp = NULL; } /* * trying to move it out of devfs? (v_tag == VT_DEVFS) * if we move a dir across mnt points. we need to fix all * the mountpoint pointers! XXX * so for now keep dirs within the same mount */ if ( (fvp->v_tag != VT_DEVFS) || (fvp->v_tag != tdvp->v_tag) || (tvp && (fvp->v_tag != tvp->v_tag)) || ((fp->type == DEV_DIR) && (fp->dvm != tdp->dvm ))) { error = EXDEV; abortit: VOP_ABORTOP(tdvp, tcnp); if (tdvp == tvp) /* eh? */ vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */ vrele(fdvp); vrele(fvp); return (error); } /* * Check we are doing legal things WRT the new flags */ if ((tp && (tp->flags & (IMMUTABLE | APPEND))) || (fp->flags & (IMMUTABLE | APPEND)) || (fdp->flags & APPEND)) { error = EPERM; goto abortit; } /* * Make sure that we don't try do something stupid */ if ((fp->type) == DEV_DIR) { /* * Avoid ".", "..", and aliases of "." for obvious reasons. */ if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || (fcnp->cn_flags&ISDOTDOT) || (tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.') || (tcnp->cn_flags&ISDOTDOT) || (tdp == fp )) { error = EINVAL; goto abortit; } doingdirectory++; } /* * If ".." must be changed (ie the directory gets a new * parent) then the source directory must not be in the * directory heirarchy above the target, as this would * orphan everything below the source directory. Also * the user must have write permission in the source so * as to be able to change "..". */ if (doingdirectory && (tdp != fdp)) { dn_p tmp,ntmp; error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); tmp = tdp; do { if(tmp == fp) { /* XXX unlock stuff here probably */ error = EINVAL; goto out; } ntmp = tmp; } while ((tmp = tmp->by.Dir.parent) != ntmp); } /*********************************** * Start actually doing things.... * ***********************************/ TIMEVAL_TO_TIMESPEC(&time,&(fp->atime)); /* * Check if just deleting a link name. */ if (fvp == tvp) { if (fvp->v_type == VDIR) { error = EINVAL; goto abortit; } /* Release destination completely. */ VOP_ABORTOP(tdvp, tcnp); vput(tdvp); vput(tvp); /* Delete source. */ VOP_ABORTOP(fdvp, fcnp); /*XXX*/ vrele(fdvp); vrele(fvp); dev_free_name(fnp); return 0; } /* * 1) Bump link count while we're moving stuff * around. If we crash somewhere before * completing our work, too bad :) */ fp->links++; /* * If the target exists zap it (unless it's a non-empty directory) * We could do that as well but won't */ if (tp) { int ouruid = tcnp->cn_cred->cr_uid; /* * If the parent directory is "sticky", then the user must * own the parent directory, or the destination of the rename, * otherwise the destination may not be changed (except by * root). This implements append-only directories. * XXX shoudn't this be in generic code? */ if ((tdp->mode & S_ISTXT) && ouruid != 0 && ouruid != tdp->uid && ouruid != tp->uid ) { error = EPERM; goto bad; } /* * Target must be empty if a directory and have no links * to it. Also, ensure source and target are compatible * (both directories, or both not directories). */ if (( doingdirectory) && (tp->links > 2)) { printf("nlink = %d\n",tp->links); /*XXX*/ error = ENOTEMPTY; goto bad; } dev_free_name(tnp); tp = NULL; } dev_add_name(tcnp->cn_nameptr,tdp,fnp->as.front.realthing,fp,&tnp); fnp->dnp = NULL; fp->links--; /* one less link to it.. */ dev_free_name(fnp); fp->links--; /* we added one earlier*/ if (tdp) vput(tdvp); if (tp) vput(fvp); vrele(ap->a_fvp); return (error); bad: if (tp) vput(tvp); vput(tdvp); out: if (vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p) == 0) { fp->links--; /* we added one earlier*/ vput(fvp); } else vrele(fvp); return (error); } #ifdef notyet static int devfs_mkdir(struct vop_mkdir_args *ap) /*struct vop_mkdir_args { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ { DBPRINT(("mkdir\n")); vput(ap->a_dvp); return EINVAL; } static int devfs_rmdir(struct vop_rmdir_args *ap) /*struct vop_rmdir_args { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ { DBPRINT(("rmdir\n")); vput(ap->a_dvp); vput(ap->a_vp); return 0; } #endif static int devfs_symlink(struct vop_symlink_args *ap) /*struct vop_symlink_args { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; char *a_target; } */ { int err; dn_p dnp; union typeinfo by; devnm_p nm_p; struct vnode *vp; DBPRINT(("symlink\n")); if(err = devfs_vntodn(ap->a_dvp,&dnp)) { return err; } by.Slnk.name = ap->a_target; by.Slnk.namelen = strlen(ap->a_target); dev_add_entry( ap->a_cnp->cn_nameptr, dnp, DEV_SLNK, &by, NULL, NULL, &nm_p); if(err = devfs_dntovn(nm_p->dnp,&vp) ) { vput(ap->a_dvp); return err; } VOP_SETATTR(vp, ap->a_vap, ap->a_cnp->cn_cred, ap->a_cnp->cn_proc); *ap->a_vpp = NULL; vput(vp); vput(ap->a_dvp); return 0; } /* * Vnode op for readdir */ static int devfs_readdir(struct vop_readdir_args *ap) /*struct vop_readdir_args { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *eofflag; int *ncookies; u_int **cookies; } */ { struct vnode *vp = ap->a_vp; struct uio *uio = ap->a_uio; struct dirent dirent; dn_p dir_node; devnm_p name_node; char *name; int error = 0; int reclen; int nodenumber; int startpos,pos; DBPRINT(("readdir\n")); /* set up refs to dir */ if (error = devfs_vntodn(vp,&dir_node)) return error; if(dir_node->type != DEV_DIR) return(ENOTDIR); pos = 0; startpos = uio->uio_offset; name_node = dir_node->by.Dir.dirlist; nodenumber = 0; TIMEVAL_TO_TIMESPEC(&time,&(dir_node->atime)) while ((name_node || (nodenumber < 2)) && (uio->uio_resid > 0)) { switch(nodenumber) { case 0: dirent.d_fileno = (unsigned long int)dir_node; name = "."; dirent.d_namlen = 1; dirent.d_type = DT_DIR; break; case 1: if(dir_node->by.Dir.parent) dirent.d_fileno = (unsigned long int)dir_node->by.Dir.parent; else dirent.d_fileno = (unsigned long int)dir_node; name = ".."; dirent.d_namlen = 2; dirent.d_type = DT_DIR; break; default: dirent.d_fileno = (unsigned long int)name_node->dnp; dirent.d_namlen = strlen(name_node->name); name = name_node->name; switch(name_node->dnp->type) { case DEV_BDEV: dirent.d_type = DT_BLK; break; case DEV_CDEV: dirent.d_type = DT_CHR; break; case DEV_DDEV: dirent.d_type = DT_SOCK; /*XXX*/ break; case DEV_DIR: dirent.d_type = DT_DIR; break; case DEV_SLNK: dirent.d_type = DT_LNK; break; default: dirent.d_type = DT_UNKNOWN; } } reclen = dirent.d_reclen = GENERIC_DIRSIZ(&dirent); if(pos >= startpos) /* made it to the offset yet? */ { if (uio->uio_resid < reclen) /* will it fit? */ break; strcpy( dirent.d_name,name); if (error = uiomove ((caddr_t)&dirent, dirent.d_reclen, uio)) break; } pos += reclen; if((nodenumber >1) && name_node) name_node = name_node->next; nodenumber++; } uio->uio_offset = pos; return (error); } /* */ static int devfs_readlink(struct vop_readlink_args *ap) /*struct vop_readlink_args { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ { struct vnode *vp = ap->a_vp; struct uio *uio = ap->a_uio; dn_p lnk_node; int error = 0; DBPRINT(("readlink\n")); /* set up refs to dir */ if (error = devfs_vntodn(vp,&lnk_node)) return error; if(lnk_node->type != DEV_SLNK) return(EINVAL); if (error = VOP_ACCESS(vp, VREAD, ap->a_cred, NULL)) { /* XXX */ return error; } error = uiomove(lnk_node->by.Slnk.name, lnk_node->by.Slnk.namelen, uio); return error; } #ifdef notyet static int devfs_abortop(struct vop_abortop_args *ap) /*struct vop_abortop_args { struct vnode *a_dvp; struct componentname *a_cnp; } */ { DBPRINT(("abortop\n")); if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); return 0; } #endif /* notyet */ static int devfs_inactive(struct vop_inactive_args *ap) /*struct vop_inactive_args { struct vnode *a_vp; } */ { DBPRINT(("inactive\n")); return 0; } #ifdef notyet static int devfs_lock(struct vop_lock_args *ap) { DBPRINT(("lock\n")); return 0; } static int devfs_unlock( struct vop_unlock_args *ap) { DBPRINT(("unlock\n")); return 0; } static int devfs_islocked(struct vop_islocked_args *ap) /*struct vop_islocked_args { struct vnode *a_vp; } */ { DBPRINT(("islocked\n")); return 0; } static int devfs_bmap(struct vop_bmap_args *ap) /*struct vop_bmap_args { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ { DBPRINT(("bmap\n")); return 0; } static int devfs_strategy(struct vop_strategy_args *ap) /*struct vop_strategy_args { struct buf *a_bp; } */ { DBPRINT(("strategy\n")); if (ap->a_bp->b_vp->v_type == VBLK || ap->a_bp->b_vp->v_type == VCHR) printf("devfs_strategy: spec"); return 0; } static int devfs_advlock(struct vop_advlock_args *ap) /*struct vop_advlock_args { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ { DBPRINT(("advlock\n")); return EINVAL; /* we don't do locking yet */ } #endif /* notyet */ static int devfs_reclaim(struct vop_reclaim_args *ap) /*struct vop_reclaim_args { struct vnode *a_vp; } */ { dn_p file_node; int error; DBPRINT(("reclaim\n")); if (error = devfs_vntodn(ap->a_vp,&file_node)) { printf("devfs_vntodn returned %d ",error); return error; } ap->a_vp->v_data = NULL; file_node->vn = 0; file_node->vn_id = 0; return(0); } /* * Return POSIX pathconf information applicable to special devices. */ static int devfs_pathconf(struct vop_pathconf_args *ap) /*struct vop_pathconf_args { struct vnode *a_vp; int a_name; int *a_retval; } */ { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_MAX_CANON: *ap->a_retval = MAX_CANON; return (0); case _PC_MAX_INPUT: *ap->a_retval = MAX_INPUT; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_VDISABLE: *ap->a_retval = _POSIX_VDISABLE; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Print out the contents of a /devfs vnode. */ static int devfs_print(struct vop_print_args *ap) /*struct vop_print_args { struct vnode *a_vp; } */ { printf("tag VT_DEVFS, devfs vnode\n"); return (0); } static int devfs_vfree(struct vop_vfree_args *ap) /*struct vop_vfree_args { struct vnode *a_pvp; ino_t a_ino; int a_mode; } */ { return (0); } /**************************************************************************\ * pseudo ops * \**************************************************************************/ /* * /devfs vnode unsupported operation */ static int devfs_enotsupp(void *junk) { return (EOPNOTSUPP); } /* * /devfs "should never get here" operation */ static int devfs_badop(void *junk) { panic("devfs: bad op"); /* NOTREACHED */ } /*proto*/ void devfs_dropvnode(dn_p dnp) { struct vnode *vn_p; #ifdef PARANOID if(!dnp) { printf("devfs: dn count dropped too early\n"); } #endif vn_p = dnp->vn; /* * check if we have a vnode....... */ if((vn_p) && ( dnp->vn_id == vn_p->v_id) && (dnp == (dn_p)vn_p->v_data)) { VOP_REVOKE(vn_p, REVOKEALL); } dnp->vn = NULL; /* be pedantic about this */ } #define devfs_create ((int (*) __P((struct vop_create_args *)))devfs_enotsupp) #define devfs_mknod ((int (*) __P((struct vop_mknod_args *)))devfs_enotsupp) #define devfs_close ((int (*) __P((struct vop_close_args *)))nullop) #define devfs_ioctl ((int (*) __P((struct vop_ioctl_args *)))devfs_enotsupp) -#define devfs_select ((int (*) __P((struct vop_select_args *)))devfs_enotsupp) +#define devfs_poll vop_nopoll #define devfs_mmap ((int (*) __P((struct vop_mmap_args *)))devfs_enotsupp) #define devfs_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define devfs_seek ((int (*) __P((struct vop_seek_args *)))nullop) #define devfs_mkdir ((int (*) __P((struct vop_mkdir_args *)))devfs_enotsupp) #define devfs_rmdir ((int (*) __P((struct vop_rmdir_args *)))devfs_enotsupp) /* #define devfs_symlink ((int (*) __P((struct vop_symlink_args *)))devfs_enotsupp) #define devfs_readlink \ ((int (*) __P((struct vop_readlink_args *)))devfs_enotsupp) */ #define devfs_abortop ((int (*) __P((struct vop_abortop_args *)))nullop) #define devfs_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock) #define devfs_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) #define devfs_bmap ((int (*) __P((struct vop_bmap_args *)))devfs_badop) #define devfs_strategy ((int (*) __P((struct vop_strategy_args *)))devfs_badop) #define devfs_islocked \ ((int (*) __P((struct vop_islocked_args *)))vop_noislocked) #define devfs_advlock ((int (*) __P((struct vop_advlock_args *)))devfs_enotsupp) #define devfs_blkatoff \ ((int (*) __P((struct vop_blkatoff_args *)))devfs_enotsupp) #define devfs_valloc ((int(*) __P(( \ struct vnode *pvp, \ int mode, \ struct ucred *cred, \ struct vnode **vpp))) devfs_enotsupp) #define devfs_truncate \ ((int (*) __P((struct vop_truncate_args *)))devfs_enotsupp) #define devfs_update ((int (*) __P((struct vop_update_args *)))devfs_enotsupp) #define devfs_bwrite ((int (*) __P((struct vop_bwrite_args *)))devfs_enotsupp) /* These are the operations used by directories etc in a devfs */ vop_t **devfs_vnodeop_p; static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)devfs_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)devfs_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)devfs_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)devfs_open }, /* open */ { &vop_close_desc, (vop_t *)devfs_close }, /* close */ { &vop_access_desc, (vop_t *)devfs_access }, /* access */ { &vop_getattr_desc, (vop_t *)devfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)devfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)devfs_read }, /* read */ { &vop_write_desc, (vop_t *)devfs_write }, /* write */ +/* XXX: vop_lease */ { &vop_ioctl_desc, (vop_t *)devfs_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)devfs_select }, /* select */ + { &vop_poll_desc, (vop_t *)devfs_poll }, /* poll */ +/* XXX: vop_revoke */ { &vop_mmap_desc, (vop_t *)devfs_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)devfs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)devfs_seek }, /* seek */ { &vop_remove_desc, (vop_t *)devfs_remove }, /* remove */ { &vop_link_desc, (vop_t *)devfs_link }, /* link */ { &vop_rename_desc, (vop_t *)devfs_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)devfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)devfs_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)devfs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)devfs_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)devfs_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)devfs_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)devfs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)devfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)devfs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)devfs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)devfs_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)devfs_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)devfs_print }, /* print */ { &vop_islocked_desc, (vop_t *)devfs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)devfs_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)devfs_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)devfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)devfs_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)devfs_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)devfs_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)devfs_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)devfs_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc devfs_vnodeop_opv_desc = { &devfs_vnodeop_p, devfs_vnodeop_entries }; VNODEOP_SET(devfs_vnodeop_opv_desc); /*copied in from specfs/spec_vnops.c.. (spot the changes )*/ /* These are the operations used by special devices in a devfs */ /* * 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. * * @(#)spec_vnops.c 8.6 (Berkeley) 4/9/94 * spec_vnops.c,v 1.9 1994/11/14 13:22:52 bde Exp */ vop_t **dev_spec_vnodeop_p; static struct vnodeopv_entry_desc dev_spec_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)spec_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)spec_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)spec_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)spec_open }, /* open */ { &vop_close_desc, (vop_t *)spec_close }, /* close */ { &vop_access_desc, (vop_t *)devfs_access }, /* access */ { &vop_getattr_desc, (vop_t *)devfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)devfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)devfs_read }, /* read */ { &vop_write_desc, (vop_t *)devfs_write }, /* write */ +/* XXX: vop_lease */ { &vop_ioctl_desc, (vop_t *)spec_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)spec_select }, /* select */ + { &vop_poll_desc, (vop_t *)spec_poll }, /* poll */ +/* XXX: vop_revoke */ { &vop_mmap_desc, (vop_t *)spec_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)spec_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)spec_seek }, /* seek */ { &vop_remove_desc, (vop_t *)spec_remove }, /* remove */ { &vop_link_desc, (vop_t *)spec_link }, /* link */ { &vop_rename_desc, (vop_t *)spec_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)spec_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)devfs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)spec_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)spec_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)spec_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)spec_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)devfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)spec_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)spec_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)spec_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)spec_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)spec_print }, /* print */ { &vop_islocked_desc, (vop_t *)spec_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)spec_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)spec_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)spec_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)spec_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)spec_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)spec_update }, /* update */ + { &vop_getpages_desc, (vop_t *)spec_getpages}, /* getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc dev_spec_vnodeop_opv_desc = { &dev_spec_vnodeop_p, dev_spec_vnodeop_entries }; VNODEOP_SET(dev_spec_vnodeop_opv_desc); diff --git a/sys/miscfs/fdesc/fdesc_vnops.c b/sys/miscfs/fdesc/fdesc_vnops.c index cb6490614c74..f63bd1b67dac 100644 --- a/sys/miscfs/fdesc/fdesc_vnops.c +++ b/sys/miscfs/fdesc/fdesc_vnops.c @@ -1,993 +1,998 @@ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. * * 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. * * @(#)fdesc_vnops.c 8.9 (Berkeley) 1/21/94 * - * $Id: fdesc_vnops.c,v 1.24 1997/09/02 20:06:09 bde Exp $ + * $Id: fdesc_vnops.c,v 1.25 1997/09/07 05:25:53 bde Exp $ */ /* * /dev/fd Filesystem */ #include #include #include #include /* boottime */ #include #include #include #include #include #include #include #include #include #include #include #include extern struct cdevsw ctty_cdevsw; #define cttyvp(p) ((p)->p_flag & P_CONTROLT ? (p)->p_session->s_ttyvp : NULL) #define FDL_WANT 0x01 #define FDL_LOCKED 0x02 static int fdcache_lock; static vop_t **fdesc_vnodeop_p; dev_t devctty; #if (FD_STDIN != FD_STDOUT-1) || (FD_STDOUT != FD_STDERR-1) FD_STDIN, FD_STDOUT, FD_STDERR must be a sequence n, n+1, n+2 #endif #define NFDCACHE 4 #define FD_NHASH(ix) \ (&fdhashtbl[(ix) & fdhash]) LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl; u_long fdhash; static int fdesc_attr __P((int fd, struct vattr *vap, struct ucred *cred, struct proc *p)); static int fdesc_badop __P((void)); static int fdesc_getattr __P((struct vop_getattr_args *ap)); static struct fdcache * fdesc_hash __P((int ix)); static int fdesc_inactive __P((struct vop_inactive_args *ap)); static int fdesc_ioctl __P((struct vop_ioctl_args *ap)); static int fdesc_lookup __P((struct vop_lookup_args *ap)); static int fdesc_open __P((struct vop_open_args *ap)); static int fdesc_pathconf __P((struct vop_pathconf_args *ap)); static int fdesc_print __P((struct vop_print_args *ap)); static int fdesc_read __P((struct vop_read_args *ap)); static int fdesc_readdir __P((struct vop_readdir_args *ap)); static int fdesc_readlink __P((struct vop_readlink_args *ap)); static int fdesc_reclaim __P((struct vop_reclaim_args *ap)); -static int fdesc_select __P((struct vop_select_args *ap)); +static int fdesc_poll __P((struct vop_poll_args *ap)); static int fdesc_setattr __P((struct vop_setattr_args *ap)); static int fdesc_vfree __P((struct vop_vfree_args *ap)); static int fdesc_write __P((struct vop_write_args *ap)); /* * Initialise cache headers */ int fdesc_init(vfsp) struct vfsconf *vfsp; { devctty = makedev(nchrdev, 0); fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash); return (0); } int fdesc_allocvp(ftype, ix, mp, vpp) fdntype ftype; int ix; struct mount *mp; struct vnode **vpp; { struct proc *p = curproc; /* XXX */ struct fdhashhead *fc; struct fdescnode *fd; int error = 0; fc = FD_NHASH(ix); loop: for (fd = fc->lh_first; fd != 0; fd = fd->fd_hash.le_next) { if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) { if (vget(fd->fd_vnode, 0, p)) goto loop; *vpp = fd->fd_vnode; return (error); } } /* * otherwise lock the array while we call getnewvnode * since that can block. */ if (fdcache_lock & FDL_LOCKED) { fdcache_lock |= FDL_WANT; (void) tsleep((caddr_t) &fdcache_lock, PINOD, "fdalvp", 0); goto loop; } fdcache_lock |= FDL_LOCKED; /* * Do the MALLOC before the getnewvnode since doing so afterward * might cause a bogus v_data pointer to get dereferenced * elsewhere if MALLOC should block. */ MALLOC(fd, struct fdescnode *, sizeof(struct fdescnode), M_TEMP, M_WAITOK); error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp); if (error) { FREE(fd, M_TEMP); goto out; } (*vpp)->v_data = fd; fd->fd_vnode = *vpp; fd->fd_type = ftype; fd->fd_fd = -1; fd->fd_link = 0; fd->fd_ix = ix; LIST_INSERT_HEAD(fc, fd, fd_hash); out:; fdcache_lock &= ~FDL_LOCKED; if (fdcache_lock & FDL_WANT) { fdcache_lock &= ~FDL_WANT; wakeup((caddr_t) &fdcache_lock); } return (error); } /* * vp is the current namei directory * ndp is the name to locate in that directory... */ static int fdesc_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { struct vnode **vpp = ap->a_vpp; struct vnode *dvp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; char *pname = cnp->cn_nameptr; struct proc *p = cnp->cn_proc; int nfiles = p->p_fd->fd_nfiles; unsigned fd; int error; struct vnode *fvp; char *ln; if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) { error = EROFS; goto bad; } VOP_UNLOCK(dvp, 0, p); if (cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); vn_lock(dvp, LK_SHARED | LK_RETRY, p); return (0); } switch (VTOFDESC(dvp)->fd_type) { default: case Flink: case Fdesc: case Fctty: error = ENOTDIR; goto bad; case Froot: if (cnp->cn_namelen == 2 && bcmp(pname, "fd", 2) == 0) { error = fdesc_allocvp(Fdevfd, FD_DEVFD, dvp->v_mount, &fvp); if (error) goto bad; *vpp = fvp; fvp->v_type = VDIR; vn_lock(fvp, LK_SHARED | LK_RETRY, p); return (0); } if (cnp->cn_namelen == 3 && bcmp(pname, "tty", 3) == 0) { struct vnode *ttyvp = cttyvp(p); if (ttyvp == NULL) { error = ENXIO; goto bad; } error = fdesc_allocvp(Fctty, FD_CTTY, dvp->v_mount, &fvp); if (error) goto bad; *vpp = fvp; fvp->v_type = VFIFO; vn_lock(fvp, LK_SHARED | LK_RETRY, p); return (0); } ln = 0; switch (cnp->cn_namelen) { case 5: if (bcmp(pname, "stdin", 5) == 0) { ln = "fd/0"; fd = FD_STDIN; } break; case 6: if (bcmp(pname, "stdout", 6) == 0) { ln = "fd/1"; fd = FD_STDOUT; } else if (bcmp(pname, "stderr", 6) == 0) { ln = "fd/2"; fd = FD_STDERR; } break; } if (ln) { error = fdesc_allocvp(Flink, fd, dvp->v_mount, &fvp); if (error) goto bad; VTOFDESC(fvp)->fd_link = ln; *vpp = fvp; fvp->v_type = VLNK; vn_lock(fvp, LK_SHARED | LK_RETRY, p); return (0); } else { error = ENOENT; goto bad; } /* FALL THROUGH */ case Fdevfd: if (cnp->cn_namelen == 2 && bcmp(pname, "..", 2) == 0) { if (error = fdesc_root(dvp->v_mount, vpp)) goto bad; return (0); } fd = 0; while (*pname >= '0' && *pname <= '9') { fd = 10 * fd + *pname++ - '0'; if (fd >= nfiles) break; } if (*pname != '\0') { error = ENOENT; goto bad; } if (fd >= nfiles || p->p_fd->fd_ofiles[fd] == NULL) { error = EBADF; goto bad; } error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp); if (error) goto bad; VTOFDESC(fvp)->fd_fd = fd; vn_lock(fvp, LK_SHARED | LK_RETRY, p); *vpp = fvp; return (0); } bad:; vn_lock(dvp, LK_SHARED | LK_RETRY, p); *vpp = NULL; return (error); } static int fdesc_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; int error = 0; switch (VTOFDESC(vp)->fd_type) { case Fdesc: /* * XXX Kludge: set p->p_dupfd to contain the value of the * the file descriptor being sought for duplication. The error * return ensures that the vnode for this device will be * released by vn_open. Open will detect this special error and * take the actions in dupfdopen. Other callers of vn_open or * VOP_OPEN will simply report the error. */ ap->a_p->p_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */ error = ENODEV; break; case Fctty: error = (*ctty_cdevsw.d_open)(devctty, ap->a_mode, 0, ap->a_p); break; } return (error); } static int fdesc_attr(fd, vap, cred, p) int fd; struct vattr *vap; struct ucred *cred; struct proc *p; { struct filedesc *fdp = p->p_fd; struct file *fp; struct stat stb; int error; if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) return (EBADF); switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: error = VOP_GETATTR((struct vnode *) fp->f_data, vap, cred, p); if (error == 0 && vap->va_type == VDIR) { /* * directories can cause loops in the namespace, * so turn off the 'x' bits to avoid trouble. */ vap->va_mode &= ~((VEXEC)|(VEXEC>>3)|(VEXEC>>6)); } break; case DTYPE_SOCKET: error = soo_stat((struct socket *)fp->f_data, &stb); if (error == 0) { vattr_null(vap); vap->va_type = VSOCK; vap->va_mode = stb.st_mode; vap->va_nlink = stb.st_nlink; vap->va_uid = stb.st_uid; vap->va_gid = stb.st_gid; vap->va_fsid = stb.st_dev; vap->va_fileid = stb.st_ino; vap->va_size = stb.st_size; vap->va_blocksize = stb.st_blksize; vap->va_atime = stb.st_atimespec; vap->va_mtime = stb.st_mtimespec; vap->va_ctime = stb.st_ctimespec; vap->va_gen = stb.st_gen; vap->va_flags = stb.st_flags; vap->va_rdev = stb.st_rdev; vap->va_bytes = stb.st_blocks * stb.st_blksize; } break; default: panic("fdesc attr"); break; } return (error); } static int fdesc_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct vattr *vap = ap->a_vap; unsigned fd; int error = 0; switch (VTOFDESC(vp)->fd_type) { case Froot: case Fdevfd: case Flink: case Fctty: bzero((caddr_t) vap, sizeof(*vap)); vattr_null(vap); vap->va_fileid = VTOFDESC(vp)->fd_ix; switch (VTOFDESC(vp)->fd_type) { case Flink: vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; vap->va_type = VLNK; vap->va_nlink = 1; vap->va_size = strlen(VTOFDESC(vp)->fd_link); break; case Fctty: vap->va_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; vap->va_type = VFIFO; vap->va_nlink = 1; vap->va_size = 0; break; default: vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; vap->va_type = VDIR; vap->va_nlink = 2; vap->va_size = DEV_BSIZE; break; } vap->va_uid = 0; vap->va_gid = 0; vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; vap->va_blocksize = DEV_BSIZE; vap->va_atime.tv_sec = boottime.tv_sec; vap->va_atime.tv_nsec = 0; vap->va_mtime = vap->va_atime; vap->va_ctime = vap->va_mtime; vap->va_gen = 0; vap->va_flags = 0; vap->va_rdev = 0; vap->va_bytes = 0; break; case Fdesc: fd = VTOFDESC(vp)->fd_fd; error = fdesc_attr(fd, vap, ap->a_cred, ap->a_p); break; default: panic("fdesc_getattr"); break; } if (error == 0) vp->v_type = vap->va_type; return (error); } static int fdesc_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct filedesc *fdp = ap->a_p->p_fd; struct file *fp; unsigned fd; int error; /* * Can't mess with the root vnode */ switch (VTOFDESC(ap->a_vp)->fd_type) { case Fdesc: break; case Fctty: return (0); default: return (EACCES); } fd = VTOFDESC(ap->a_vp)->fd_fd; if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) { return (EBADF); } /* * Can setattr the underlying vnode, but not sockets! */ switch (fp->f_type) { case DTYPE_FIFO: case DTYPE_VNODE: error = VOP_SETATTR((struct vnode *) fp->f_data, ap->a_vap, ap->a_cred, ap->a_p); break; case DTYPE_SOCKET: error = 0; break; default: error = EBADF; break; } return (error); } #define UIO_MX 16 static struct dirtmp { u_long d_fileno; u_short d_reclen; u_short d_namlen; char d_name[8]; } rootent[] = { { FD_DEVFD, UIO_MX, 2, "fd" }, { FD_STDIN, UIO_MX, 5, "stdin" }, { FD_STDOUT, UIO_MX, 6, "stdout" }, { FD_STDERR, UIO_MX, 6, "stderr" }, { FD_CTTY, UIO_MX, 3, "tty" }, { 0 } }; static int fdesc_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_long *a_cookies; int a_ncookies; } */ *ap; { struct uio *uio = ap->a_uio; struct filedesc *fdp; int i; int error; /* * We don't allow exporting fdesc mounts, and currently local * requests do not need cookies. */ if (ap->a_ncookies) panic("fdesc_readdir: not hungry"); switch (VTOFDESC(ap->a_vp)->fd_type) { case Fctty: return (0); case Fdesc: return (ENOTDIR); default: break; } fdp = uio->uio_procp->p_fd; if (VTOFDESC(ap->a_vp)->fd_type == Froot) { struct dirent d; struct dirent *dp = &d; struct dirtmp *dt; i = uio->uio_offset / UIO_MX; error = 0; while (uio->uio_resid > 0) { dt = &rootent[i]; if (dt->d_fileno == 0) { /**eofflagp = 1;*/ break; } i++; switch (dt->d_fileno) { case FD_CTTY: if (cttyvp(uio->uio_procp) == NULL) continue; break; case FD_STDIN: case FD_STDOUT: case FD_STDERR: if ((dt->d_fileno-FD_STDIN) >= fdp->fd_nfiles) continue; if (fdp->fd_ofiles[dt->d_fileno-FD_STDIN] == NULL) continue; break; } bzero((caddr_t) dp, UIO_MX); dp->d_fileno = dt->d_fileno; dp->d_namlen = dt->d_namlen; dp->d_type = DT_UNKNOWN; dp->d_reclen = dt->d_reclen; bcopy(dt->d_name, dp->d_name, dp->d_namlen+1); error = uiomove((caddr_t) dp, UIO_MX, uio); if (error) break; } uio->uio_offset = i * UIO_MX; return (error); } i = uio->uio_offset / UIO_MX; error = 0; while (uio->uio_resid > 0) { if (i >= fdp->fd_nfiles) break; if (fdp->fd_ofiles[i] != NULL) { struct dirent d; struct dirent *dp = &d; bzero((caddr_t) dp, UIO_MX); dp->d_namlen = sprintf(dp->d_name, "%d", i); dp->d_reclen = UIO_MX; dp->d_type = DT_UNKNOWN; dp->d_fileno = i + FD_STDIN; /* * And ship to userland */ error = uiomove((caddr_t) dp, UIO_MX, uio); if (error) break; } i++; } uio->uio_offset = i * UIO_MX; return (error); } static int fdesc_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { struct vnode *vp = ap->a_vp; int error; if (vp->v_type != VLNK) return (EPERM); if (VTOFDESC(vp)->fd_type == Flink) { char *ln = VTOFDESC(vp)->fd_link; error = uiomove(ln, strlen(ln), ap->a_uio); } else { error = EOPNOTSUPP; } return (error); } static int fdesc_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int error = EOPNOTSUPP; switch (VTOFDESC(ap->a_vp)->fd_type) { case Fctty: error = (*ctty_cdevsw.d_read)(devctty, ap->a_uio, ap->a_ioflag); break; default: error = EOPNOTSUPP; break; } return (error); } static int fdesc_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int error = EOPNOTSUPP; switch (VTOFDESC(ap->a_vp)->fd_type) { case Fctty: error = (*ctty_cdevsw.d_write)(devctty, ap->a_uio, ap->a_ioflag); break; default: error = EOPNOTSUPP; break; } return (error); } static int fdesc_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { int error = EOPNOTSUPP; switch (VTOFDESC(ap->a_vp)->fd_type) { case Fctty: error = (*ctty_cdevsw.d_ioctl)(devctty, ap->a_command, ap->a_data, ap->a_fflag, ap->a_p); break; default: error = EOPNOTSUPP; break; } return (error); } static int -fdesc_select(ap) - struct vop_select_args /* { +fdesc_poll(ap) + struct vop_poll_args /* { struct vnode *a_vp; - int a_which; - int a_fflags; + int a_events; struct ucred *a_cred; struct proc *a_p; } */ *ap; { - int error = EOPNOTSUPP; + int revents; switch (VTOFDESC(ap->a_vp)->fd_type) { case Fctty: - error = (*ctty_cdevsw.d_select)(devctty, ap->a_fflags, ap->a_p); + revents = (*ctty_cdevsw.d_poll)(devctty, ap->a_events, ap->a_p); break; default: - error = EOPNOTSUPP; + revents = seltrue(0, ap->a_events, ap->a_p); break; } - return (error); + return (revents); } static int fdesc_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; /* * Clear out the v_type field to avoid * nasty things happening in vgone(). */ VOP_UNLOCK(vp, 0, ap->a_p); vp->v_type = VNON; return (0); } static int fdesc_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; struct fdescnode *fd = VTOFDESC(vp); LIST_REMOVE(fd, fd_hash); FREE(vp->v_data, M_TEMP); vp->v_data = 0; return (0); } /* * Return POSIX pathconf information applicable to special devices. */ static int fdesc_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_MAX_CANON: *ap->a_retval = MAX_CANON; return (0); case _PC_MAX_INPUT: *ap->a_retval = MAX_INPUT; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_VDISABLE: *ap->a_retval = _POSIX_VDISABLE; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Print out the contents of a /dev/fd vnode. */ /* ARGSUSED */ static int fdesc_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_NON, fdesc vnode\n"); return (0); } /*void*/ static int fdesc_vfree(ap) struct vop_vfree_args /* { struct vnode *a_pvp; ino_t a_ino; int a_mode; } */ *ap; { return (0); } /* * /dev/fd "should never get here" operation */ static int fdesc_badop() { panic("fdesc: bad op"); /* NOTREACHED */ } #define fdesc_create ((int (*) __P((struct vop_create_args *)))eopnotsupp) #define fdesc_mknod ((int (*) __P((struct vop_mknod_args *)))eopnotsupp) #define fdesc_close ((int (*) __P((struct vop_close_args *)))nullop) #define fdesc_access ((int (*) __P((struct vop_access_args *)))nullop) #define fdesc_mmap ((int (*) __P((struct vop_mmap_args *)))eopnotsupp) #define fdesc_revoke vop_revoke #define fdesc_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define fdesc_seek ((int (*) __P((struct vop_seek_args *)))nullop) #define fdesc_remove ((int (*) __P((struct vop_remove_args *)))eopnotsupp) #define fdesc_link ((int (*) __P((struct vop_link_args *)))eopnotsupp) #define fdesc_rename ((int (*) __P((struct vop_rename_args *)))eopnotsupp) #define fdesc_mkdir ((int (*) __P((struct vop_mkdir_args *)))eopnotsupp) #define fdesc_rmdir ((int (*) __P((struct vop_rmdir_args *)))eopnotsupp) #define fdesc_symlink ((int (*) __P((struct vop_symlink_args *)))eopnotsupp) #define fdesc_abortop ((int (*) __P((struct vop_abortop_args *)))nullop) #define fdesc_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock) #define fdesc_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) #define fdesc_bmap ((int (*) __P((struct vop_bmap_args *)))fdesc_badop) #define fdesc_strategy ((int (*) __P((struct vop_strategy_args *)))fdesc_badop) #define fdesc_islocked \ ((int (*) __P((struct vop_islocked_args *)))vop_noislocked) #define fdesc_advlock ((int (*) __P((struct vop_advlock_args *)))eopnotsupp) #define fdesc_blkatoff \ ((int (*) __P((struct vop_blkatoff_args *)))eopnotsupp) #define fdesc_valloc ((int(*) __P(( \ struct vnode *pvp, \ int mode, \ struct ucred *cred, \ struct vnode **vpp))) eopnotsupp) #define fdesc_truncate \ ((int (*) __P((struct vop_truncate_args *)))eopnotsupp) #define fdesc_update ((int (*) __P((struct vop_update_args *)))eopnotsupp) #define fdesc_bwrite ((int (*) __P((struct vop_bwrite_args *)))eopnotsupp) static struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)fdesc_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)fdesc_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)fdesc_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)fdesc_open }, /* open */ { &vop_close_desc, (vop_t *)fdesc_close }, /* close */ { &vop_access_desc, (vop_t *)fdesc_access }, /* access */ { &vop_getattr_desc, (vop_t *)fdesc_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)fdesc_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)fdesc_read }, /* read */ +/* XXX: vop_lease */ { &vop_write_desc, (vop_t *)fdesc_write }, /* write */ { &vop_ioctl_desc, (vop_t *)fdesc_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)fdesc_select }, /* select */ + { &vop_poll_desc, (vop_t *)fdesc_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)fdesc_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)fdesc_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)fdesc_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)fdesc_seek }, /* seek */ { &vop_remove_desc, (vop_t *)fdesc_remove }, /* remove */ { &vop_link_desc, (vop_t *)fdesc_link }, /* link */ { &vop_rename_desc, (vop_t *)fdesc_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)fdesc_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)fdesc_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)fdesc_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)fdesc_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)fdesc_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)fdesc_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)fdesc_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)fdesc_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)fdesc_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)fdesc_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)fdesc_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)fdesc_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)fdesc_print }, /* print */ { &vop_islocked_desc, (vop_t *)fdesc_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)fdesc_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)fdesc_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)fdesc_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)fdesc_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)fdesc_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)fdesc_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)fdesc_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)fdesc_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc fdesc_vnodeop_opv_desc = { &fdesc_vnodeop_p, fdesc_vnodeop_entries }; VNODEOP_SET(fdesc_vnodeop_opv_desc); diff --git a/sys/miscfs/fifofs/fifo.h b/sys/miscfs/fifofs/fifo.h index 0a42690e3144..38314de9d1b9 100644 --- a/sys/miscfs/fifofs/fifo.h +++ b/sys/miscfs/fifofs/fifo.h @@ -1,86 +1,86 @@ /* * Copyright (c) 1991, 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. * * @(#)fifo.h 8.6 (Berkeley) 5/21/95 - * $Id$ + * $Id: fifo.h,v 1.13 1997/02/22 09:40:17 peter Exp $ */ extern vop_t **fifo_vnodeop_p; /* * Prototypes for fifo operations on vnodes. */ int fifo_badop __P((void)); int fifo_printinfo __P((struct vnode *)); int fifo_lookup __P((struct vop_lookup_args *)); #define fifo_create ((int (*) __P((struct vop_create_args *)))fifo_badop) #define fifo_mknod ((int (*) __P((struct vop_mknod_args *)))fifo_badop) int fifo_open __P((struct vop_open_args *)); int fifo_close __P((struct vop_close_args *)); #define fifo_access ((int (*) __P((struct vop_access_args *)))fifo_ebadf) #define fifo_getattr ((int (*) __P((struct vop_getattr_args *)))fifo_ebadf) #define fifo_setattr ((int (*) __P((struct vop_setattr_args *)))fifo_ebadf) int fifo_read __P((struct vop_read_args *)); int fifo_write __P((struct vop_write_args *)); #define fifo_lease_check ((int (*) __P((struct vop_lease_args *)))nullop) int fifo_ioctl __P((struct vop_ioctl_args *)); -int fifo_select __P((struct vop_select_args *)); +int fifo_poll __P((struct vop_poll_args *)); #define fifo_revoke vop_revoke #define fifo_mmap ((int (*) __P((struct vop_mmap_args *)))fifo_badop) #define fifo_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define fifo_seek ((int (*) __P((struct vop_seek_args *)))fifo_badop) #define fifo_remove ((int (*) __P((struct vop_remove_args *)))fifo_badop) #define fifo_link ((int (*) __P((struct vop_link_args *)))fifo_badop) #define fifo_rename ((int (*) __P((struct vop_rename_args *)))fifo_badop) #define fifo_mkdir ((int (*) __P((struct vop_mkdir_args *)))fifo_badop) #define fifo_rmdir ((int (*) __P((struct vop_rmdir_args *)))fifo_badop) #define fifo_symlink ((int (*) __P((struct vop_symlink_args *)))fifo_badop) #define fifo_readdir ((int (*) __P((struct vop_readdir_args *)))fifo_badop) #define fifo_readlink ((int (*) __P((struct vop_readlink_args *)))fifo_badop) #define fifo_abortop ((int (*) __P((struct vop_abortop_args *)))fifo_badop) int fifo_inactive __P((struct vop_inactive_args *)); #define fifo_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop) #define fifo_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock) #define fifo_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) int fifo_bmap __P((struct vop_bmap_args *)); #define fifo_strategy ((int (*) __P((struct vop_strategy_args *)))fifo_badop) #define fifo_islocked ((int(*) __P((struct vop_islocked_args *)))vop_noislocked) int fifo_pathconf __P((struct vop_pathconf_args *)); int fifo_advlock __P((struct vop_advlock_args *)); #define fifo_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))fifo_badop) #define fifo_valloc ((int (*) __P((struct vop_valloc_args *)))fifo_badop) #define fifo_reallocblks \ ((int (*) __P((struct vop_reallocblks_args *)))fifo_badop) #define fifo_vfree ((int (*) __P((struct vop_vfree_args *)))fifo_badop) #define fifo_truncate ((int (*) __P((struct vop_truncate_args *)))nullop) #define fifo_update ((int (*) __P((struct vop_update_args *)))nullop) #define fifo_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop) diff --git a/sys/miscfs/fifofs/fifo_vnops.c b/sys/miscfs/fifofs/fifo_vnops.c index 41b50c5d027a..bab3f53e8d78 100644 --- a/sys/miscfs/fifofs/fifo_vnops.c +++ b/sys/miscfs/fifofs/fifo_vnops.c @@ -1,543 +1,544 @@ /* * Copyright (c) 1990, 1993, 1995 * 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. * * @(#)fifo_vnops.c 8.10 (Berkeley) 5/27/95 - * $Id: fifo_vnops.c,v 1.25 1997/08/16 19:15:13 wollman Exp $ + * $Id: fifo_vnops.c,v 1.26 1997/09/02 20:06:11 bde Exp $ */ #include #include #include #include #include #include #include #include #include #include #include +#include #include #include /* * This structure is associated with the FIFO vnode and stores * the state associated with the FIFO. */ struct fifoinfo { struct socket *fi_readsock; struct socket *fi_writesock; long fi_readers; long fi_writers; }; static int fifo_ebadf __P((void)); static int fifo_print __P((struct vop_print_args *)); vop_t **fifo_vnodeop_p; static struct vnodeopv_entry_desc fifo_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)fifo_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)fifo_create }, /* create */ { &vop_mknod_desc, (vop_t *)fifo_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)fifo_open }, /* open */ { &vop_close_desc, (vop_t *)fifo_close }, /* close */ { &vop_access_desc, (vop_t *)fifo_access }, /* access */ { &vop_getattr_desc, (vop_t *)fifo_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)fifo_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)fifo_read }, /* read */ { &vop_write_desc, (vop_t *)fifo_write }, /* write */ { &vop_lease_desc, (vop_t *)fifo_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)fifo_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)fifo_select }, /* select */ + { &vop_poll_desc, (vop_t *)fifo_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)fifo_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)fifo_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)fifo_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)fifo_seek }, /* seek */ { &vop_remove_desc, (vop_t *)fifo_remove }, /* remove */ { &vop_link_desc, (vop_t *)fifo_link }, /* link */ { &vop_rename_desc, (vop_t *)fifo_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)fifo_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)fifo_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)fifo_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)fifo_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)fifo_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)fifo_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)fifo_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)fifo_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)fifo_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)fifo_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)fifo_print }, /* print */ { &vop_islocked_desc, (vop_t *)fifo_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)fifo_valloc }, /* valloc */ { &vop_vfree_desc, (vop_t *)fifo_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)fifo_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)fifo_update }, /* update */ { &vop_bwrite_desc, (vop_t *)fifo_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc fifo_vnodeop_opv_desc = { &fifo_vnodeop_p, fifo_vnodeop_entries }; VNODEOP_SET(fifo_vnodeop_opv_desc); /* * Trivial lookup routine that always fails. */ /* ARGSUSED */ int fifo_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { *ap->a_vpp = NULL; return (ENOTDIR); } /* * Open called to set up a new instance of a fifo or * to find an active instance of a fifo. */ /* ARGSUSED */ int fifo_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct fifoinfo *fip; struct proc *p = ap->a_p; struct socket *rso, *wso; int error; static char openstr[] = "fifo"; if ((fip = vp->v_fifoinfo) == NULL) { MALLOC(fip, struct fifoinfo *, sizeof(*fip), M_VNODE, M_WAITOK); vp->v_fifoinfo = fip; error = socreate(AF_LOCAL, &rso, SOCK_STREAM, 0, ap->a_p); if (error) { free(fip, M_VNODE); vp->v_fifoinfo = NULL; return (error); } fip->fi_readsock = rso; error = socreate(AF_LOCAL, &wso, SOCK_STREAM, 0, ap->a_p); if (error) { (void)soclose(rso); free(fip, M_VNODE); vp->v_fifoinfo = NULL; return (error); } fip->fi_writesock = wso; error = unp_connect2(wso, rso); if (error) { (void)soclose(wso); (void)soclose(rso); free(fip, M_VNODE); vp->v_fifoinfo = NULL; return (error); } fip->fi_readers = fip->fi_writers = 0; wso->so_state |= SS_CANTRCVMORE; rso->so_state |= SS_CANTSENDMORE; } if (ap->a_mode & FREAD) { fip->fi_readers++; if (fip->fi_readers == 1) { fip->fi_writesock->so_state &= ~SS_CANTSENDMORE; if (fip->fi_writers > 0) wakeup((caddr_t)&fip->fi_writers); } } if (ap->a_mode & FWRITE) { fip->fi_writers++; if (fip->fi_writers == 1) { fip->fi_readsock->so_state &= ~SS_CANTRCVMORE; if (fip->fi_readers > 0) wakeup((caddr_t)&fip->fi_readers); } } if ((ap->a_mode & FREAD) && (ap->a_mode & O_NONBLOCK) == 0) { while (fip->fi_writers == 0) { VOP_UNLOCK(vp, 0, p); error = tsleep((caddr_t)&fip->fi_readers, PCATCH | PSOCK, openstr, 0); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (error) goto bad; } } if (ap->a_mode & FWRITE) { if (ap->a_mode & O_NONBLOCK) { if (fip->fi_readers == 0) { error = ENXIO; goto bad; } } else { while (fip->fi_readers == 0) { VOP_UNLOCK(vp, 0, p); error = tsleep((caddr_t)&fip->fi_writers, PCATCH | PSOCK, openstr, 0); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (error) goto bad; } } } return (0); bad: VOP_CLOSE(vp, ap->a_mode, ap->a_cred, p); return (error); } /* * Vnode op for read */ /* ARGSUSED */ int fifo_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { struct uio *uio = ap->a_uio; struct socket *rso = ap->a_vp->v_fifoinfo->fi_readsock; struct proc *p = uio->uio_procp; int error, startresid; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_READ) panic("fifo_read mode"); #endif if (uio->uio_resid == 0) return (0); if (ap->a_ioflag & IO_NDELAY) rso->so_state |= SS_NBIO; startresid = uio->uio_resid; VOP_UNLOCK(ap->a_vp, 0, p); error = soreceive(rso, (struct sockaddr **)0, uio, (struct mbuf **)0, (struct mbuf **)0, (int *)0); vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p); /* * Clear EOF indication after first such return. */ if (uio->uio_resid == startresid) rso->so_state &= ~SS_CANTRCVMORE; if (ap->a_ioflag & IO_NDELAY) rso->so_state &= ~SS_NBIO; return (error); } /* * Vnode op for write */ /* ARGSUSED */ int fifo_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { struct socket *wso = ap->a_vp->v_fifoinfo->fi_writesock; struct proc *p = ap->a_uio->uio_procp; int error; #ifdef DIAGNOSTIC if (ap->a_uio->uio_rw != UIO_WRITE) panic("fifo_write mode"); #endif if (ap->a_ioflag & IO_NDELAY) wso->so_state |= SS_NBIO; VOP_UNLOCK(ap->a_vp, 0, p); error = sosend(wso, (struct sockaddr *)0, ap->a_uio, 0, (struct mbuf *)0, 0, p); vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p); if (ap->a_ioflag & IO_NDELAY) wso->so_state &= ~SS_NBIO; return (error); } /* * Device ioctl operation. */ /* ARGSUSED */ int fifo_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct file filetmp; int error; if (ap->a_command == FIONBIO) return (0); if (ap->a_fflag & FREAD) { filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock; error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p); if (error) return (error); } if (ap->a_fflag & FWRITE) { filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock; error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p); if (error) return (error); } return (0); } /* ARGSUSED */ int -fifo_select(ap) - struct vop_select_args /* { +fifo_poll(ap) + struct vop_poll_args /* { struct vnode *a_vp; - int a_which; - int a_fflags; + int a_events; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct file filetmp; - int ready; + int revents = 0; - if (ap->a_fflags & FREAD) { + if (ap->a_events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) { filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock; - ready = soo_select(&filetmp, ap->a_which, ap->a_p); - if (ready) - return (ready); + if (filetmp.f_data) + revents |= soo_poll(&filetmp, ap->a_events, ap->a_cred, + ap->a_p); } - if (ap->a_fflags & FWRITE) { + if (ap->a_events & (POLLOUT | POLLWRNORM | POLLWRBAND)) { filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock; - ready = soo_select(&filetmp, ap->a_which, ap->a_p); - if (ready) - return (ready); + if (filetmp.f_data) + revents |= soo_poll(&filetmp, ap->a_events, ap->a_cred, + ap->a_p); } - return (0); + return (revents); } int fifo_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { VOP_UNLOCK(ap->a_vp, 0, ap->a_p); return (0); } /* * This is a noop, simply returning what one has been given. */ int fifo_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; if (ap->a_runp != NULL) *ap->a_runp = 0; if (ap->a_runb != NULL) *ap->a_runb = 0; return (0); } /* * Device close routine */ /* ARGSUSED */ int fifo_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct fifoinfo *fip = vp->v_fifoinfo; int error1, error2; if (ap->a_fflag & FREAD) { fip->fi_readers--; if (fip->fi_readers == 0) socantsendmore(fip->fi_writesock); } if (ap->a_fflag & FWRITE) { fip->fi_writers--; if (fip->fi_writers == 0) socantrcvmore(fip->fi_readsock); } if (vp->v_usecount > 1) return (0); error1 = soclose(fip->fi_readsock); error2 = soclose(fip->fi_writesock); FREE(fip, M_VNODE); vp->v_fifoinfo = NULL; if (error1) return (error1); return (error2); } /* * Print out internal contents of a fifo vnode. */ int fifo_printinfo(vp) struct vnode *vp; { register struct fifoinfo *fip = vp->v_fifoinfo; printf(", fifo with %ld readers and %ld writers", fip->fi_readers, fip->fi_writers); return (0); } /* * Print out the contents of a fifo vnode. */ static int fifo_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_NON"); fifo_printinfo(ap->a_vp); printf("\n"); return (0); } /* * Return POSIX pathconf information applicable to fifo's. */ int fifo_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Fifo failed operation */ static int fifo_ebadf() { return (EBADF); } /* * Fifo advisory byte-level locks. */ /* ARGSUSED */ int fifo_advlock(ap) struct vop_advlock_args /* { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ *ap; { return (ap->a_flags & F_FLOCK ? EOPNOTSUPP : EINVAL); } /* * Fifo bad operation */ int fifo_badop() { panic("fifo_badop called"); /* NOTREACHED */ } diff --git a/sys/miscfs/kernfs/kernfs_vnops.c b/sys/miscfs/kernfs/kernfs_vnops.c index bcd3c87a9201..6232ec33067d 100644 --- a/sys/miscfs/kernfs/kernfs_vnops.c +++ b/sys/miscfs/kernfs/kernfs_vnops.c @@ -1,798 +1,804 @@ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. * * 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. * * @(#)kernfs_vnops.c 8.15 (Berkeley) 5/21/95 - * $Id: kernfs_vnops.c,v 1.19 1997/02/22 09:40:19 peter Exp $ + * $Id: kernfs_vnops.c,v 1.20 1997/09/02 20:06:12 bde Exp $ */ /* * Kernel parameter filesystem (/kern) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define KSTRING 256 /* Largest I/O available via this filesystem */ #define UIO_MX 32 #define READ_MODE (S_IRUSR|S_IRGRP|S_IROTH) #define WRITE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH) #define DIR_MODE (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) static struct kern_target { u_char kt_type; u_char kt_namlen; char *kt_name; void *kt_data; #define KTT_NULL 1 #define KTT_TIME 5 #define KTT_INT 17 #define KTT_STRING 31 #define KTT_HOSTNAME 47 #define KTT_BOOTFILE 49 #define KTT_AVENRUN 53 #define KTT_DEVICE 71 u_char kt_tag; u_char kt_vtype; mode_t kt_mode; } kern_targets[] = { /* NOTE: The name must be less than UIO_MX-16 chars in length */ #define N(s) sizeof(s)-1, s /* name data tag type ro/rw */ { DT_DIR, N("."), 0, KTT_NULL, VDIR, DIR_MODE }, { DT_DIR, N(".."), 0, KTT_NULL, VDIR, DIR_MODE }, { DT_REG, N("boottime"), &boottime.tv_sec, KTT_INT, VREG, READ_MODE }, { DT_REG, N("copyright"), copyright, KTT_STRING, VREG, READ_MODE }, { DT_REG, N("hostname"), 0, KTT_HOSTNAME, VREG, WRITE_MODE }, { DT_REG, N("bootfile"), 0, KTT_BOOTFILE, VREG, READ_MODE }, { DT_REG, N("hz"), &hz, KTT_INT, VREG, READ_MODE }, { DT_REG, N("loadavg"), 0, KTT_AVENRUN, VREG, READ_MODE }, { DT_REG, N("pagesize"), &cnt.v_page_size, KTT_INT, VREG, READ_MODE }, { DT_REG, N("physmem"), &physmem, KTT_INT, VREG, READ_MODE }, #if 0 { DT_DIR, N("root"), 0, KTT_NULL, VDIR, DIR_MODE }, { DT_BLK, N("rootdev"), &rootdev, KTT_DEVICE, VBLK, READ_MODE }, { DT_CHR, N("rrootdev"), &rrootdev, KTT_DEVICE, VCHR, READ_MODE }, #endif { DT_REG, N("time"), 0, KTT_TIME, VREG, READ_MODE }, { DT_REG, N("version"), version, KTT_STRING, VREG, READ_MODE }, #undef N }; static int nkern_targets = sizeof(kern_targets) / sizeof(kern_targets[0]); static int kernfs_access __P((struct vop_access_args *ap)); static int kernfs_badop __P((void)); static int kernfs_enotsupp __P((void)); static int kernfs_getattr __P((struct vop_getattr_args *ap)); static int kernfs_inactive __P((struct vop_inactive_args *ap)); static int kernfs_lookup __P((struct vop_lookup_args *ap)); static int kernfs_open __P((struct vop_open_args *ap)); static int kernfs_pathconf __P((struct vop_pathconf_args *ap)); static int kernfs_print __P((struct vop_print_args *ap)); static int kernfs_read __P((struct vop_read_args *ap)); static int kernfs_readdir __P((struct vop_readdir_args *ap)); static int kernfs_reclaim __P((struct vop_reclaim_args *ap)); static int kernfs_setattr __P((struct vop_setattr_args *ap)); static int kernfs_vfree __P((struct vop_vfree_args *ap)); static int kernfs_write __P((struct vop_write_args *ap)); static int kernfs_xread __P((struct kern_target *kt, char *buf, int len, int *lenp)); static int kernfs_xwrite __P((struct kern_target *kt, char *buf, int len)); static int kernfs_xread(kt, buf, len, lenp) struct kern_target *kt; char *buf; int len; int *lenp; { switch (kt->kt_tag) { case KTT_TIME: { struct timeval tv; microtime(&tv); sprintf(buf, "%ld %ld\n", tv.tv_sec, tv.tv_usec); break; } case KTT_INT: { int *ip = kt->kt_data; sprintf(buf, "%d\n", *ip); break; } case KTT_STRING: { char *cp = kt->kt_data; int xlen = strlen(cp) + 1; if (xlen >= len) return (EINVAL); bcopy(cp, buf, xlen); break; } case KTT_HOSTNAME: { char *cp = hostname; int xlen = strlen(hostname); if (xlen >= (len-2)) return (EINVAL); bcopy(cp, buf, xlen); buf[xlen] = '\n'; buf[xlen+1] = '\0'; break; } case KTT_BOOTFILE: { char *cp = kernelname; int xlen = strlen(cp) + 1; if (xlen >= (len-2)) return (EINVAL); bcopy(cp, buf, xlen); buf[xlen] = '\n'; buf[xlen+1] = '\0'; break; } case KTT_AVENRUN: sprintf(buf, "%ld %ld %ld %ld\n", averunnable.ldavg[0], averunnable.ldavg[1], averunnable.ldavg[2], averunnable.fscale); break; default: return (EIO); } *lenp = strlen(buf); return (0); } static int kernfs_xwrite(kt, buf, len) struct kern_target *kt; char *buf; int len; { switch (kt->kt_tag) { case KTT_HOSTNAME: /* XXX BOGUS !!! no check for the length */ if (buf[len-1] == '\n') --len; bcopy(buf, hostname, len); hostname[len] = '\0'; return (0); default: return (EIO); } } /* * vp is the current namei directory * ndp is the name to locate in that directory... */ static int kernfs_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { struct componentname *cnp = ap->a_cnp; struct vnode **vpp = ap->a_vpp; struct vnode *dvp = ap->a_dvp; char *pname = cnp->cn_nameptr; struct proc *p = cnp->cn_proc; struct kern_target *kt; struct vnode *fvp; int nameiop = cnp->cn_nameiop; int error, i; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_lookup(%x)\n", ap); printf("kernfs_lookup(dp = %x, vpp = %x, cnp = %x)\n", dvp, vpp, ap->a_cnp); printf("kernfs_lookup(%s)\n", pname); #endif *vpp = NULLVP; if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) return (EROFS); VOP_UNLOCK(dvp, 0, p); if (cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); vn_lock(dvp, LK_SHARED | LK_RETRY, p); return (0); } #if 0 if (cnp->cn_namelen == 4 && bcmp(pname, "root", 4) == 0) { *vpp = rootdir; VREF(rootdir); vn_lock(rootdir, LK_SHARED | LK_RETRY, p) return (0); } #endif for (kt = kern_targets, i = 0; i < nkern_targets; kt++, i++) { if (cnp->cn_namelen == kt->kt_namlen && bcmp(kt->kt_name, pname, cnp->cn_namelen) == 0) goto found; } #ifdef KERNFS_DIAGNOSTIC printf("kernfs_lookup: i = %d, failed", i); #endif vn_lock(dvp, LK_SHARED | LK_RETRY, p); return (cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS); found: if (kt->kt_tag == KTT_DEVICE) { dev_t *dp = kt->kt_data; loop: if (*dp == NODEV || !vfinddev(*dp, kt->kt_vtype, &fvp)) { vn_lock(dvp, LK_SHARED | LK_RETRY, p); return (ENOENT); } *vpp = fvp; if (vget(fvp, LK_EXCLUSIVE, p)) goto loop; return (0); } #ifdef KERNFS_DIAGNOSTIC printf("kernfs_lookup: allocate new vnode\n"); #endif if (error = getnewvnode(VT_KERNFS, dvp->v_mount, kernfs_vnodeop_p, &fvp)) { vn_lock(dvp, LK_SHARED | LK_RETRY, p); return (error); } MALLOC(fvp->v_data, void *, sizeof(struct kernfs_node), M_TEMP, M_WAITOK); VTOKERN(fvp)->kf_kt = kt; fvp->v_type = kt->kt_vtype; vn_lock(fvp, LK_SHARED | LK_RETRY, p); *vpp = fvp; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_lookup: newvp = %x\n", fvp); #endif return (0); } static int kernfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { /* Only need to check access permissions. */ return (0); } static int kernfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct ucred *cred = ap->a_cred; mode_t amode = ap->a_mode; mode_t fmode = (vp->v_flag & VROOT) ? DIR_MODE : VTOKERN(vp)->kf_kt->kt_mode; mode_t mask = 0; register gid_t *gp; int i; /* Some files are simply not modifiable. */ if ((amode & VWRITE) && (fmode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0) return (EPERM); /* Root can do anything else. */ if (cred->cr_uid == 0) return (0); /* Check for group 0 (wheel) permissions. */ for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) if (*gp == 0) { if (amode & VEXEC) mask |= S_IXGRP; if (amode & VREAD) mask |= S_IRGRP; if (amode & VWRITE) mask |= S_IWGRP; return ((fmode & mask) == mask ? 0 : EACCES); } /* Otherwise, check everyone else. */ if (amode & VEXEC) mask |= S_IXOTH; if (amode & VREAD) mask |= S_IROTH; if (amode & VWRITE) mask |= S_IWOTH; return ((fmode & mask) == mask ? 0 : EACCES); } static int kernfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct vattr *vap = ap->a_vap; struct timeval tv; int error = 0; char strbuf[KSTRING]; bzero((caddr_t) vap, sizeof(*vap)); vattr_null(vap); vap->va_uid = 0; vap->va_gid = 0; vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; vap->va_size = 0; vap->va_blocksize = DEV_BSIZE; { struct timeval tv; microtime(&tv); TIMEVAL_TO_TIMESPEC(&tv, &vap->va_atime); } vap->va_mtime = vap->va_atime; vap->va_ctime = vap->va_ctime; vap->va_gen = 0; vap->va_flags = 0; vap->va_rdev = 0; vap->va_bytes = 0; if (vp->v_flag & VROOT) { #ifdef KERNFS_DIAGNOSTIC printf("kernfs_getattr: stat rootdir\n"); #endif vap->va_type = VDIR; vap->va_mode = DIR_MODE; vap->va_nlink = 2; vap->va_fileid = 2; vap->va_size = DEV_BSIZE; } else { struct kern_target *kt = VTOKERN(vp)->kf_kt; int nbytes; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_getattr: stat target %s\n", kt->kt_name); #endif vap->va_type = kt->kt_vtype; vap->va_mode = kt->kt_mode; vap->va_nlink = 1; vap->va_fileid = 1 + (kt - kern_targets) / sizeof(*kt); error = kernfs_xread(kt, strbuf, sizeof(strbuf), &nbytes); vap->va_size = nbytes; } #ifdef KERNFS_DIAGNOSTIC printf("kernfs_getattr: return error %d\n", error); #endif return (error); } static int kernfs_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { /* * Silently ignore attribute changes. * This allows for open with truncate to have no * effect until some data is written. I want to * do it this way because all writes are atomic. */ return (0); } static int kernfs_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { struct vnode *vp = ap->a_vp; struct uio *uio = ap->a_uio; struct kern_target *kt; char strbuf[KSTRING]; int off = uio->uio_offset; int error, len; char *cp; if (vp->v_type == VDIR) return (EOPNOTSUPP); kt = VTOKERN(vp)->kf_kt; #ifdef KERNFS_DIAGNOSTIC printf("kern_read %s\n", kt->kt_name); #endif len = 0; if (error = kernfs_xread(kt, strbuf, sizeof(strbuf), &len)) return (error); if (len <= off) return (0); return (uiomove(&strbuf[off], len - off, uio)); } static int kernfs_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { struct vnode *vp = ap->a_vp; struct uio *uio = ap->a_uio; struct kern_target *kt; int error, xlen; char strbuf[KSTRING]; if (vp->v_type == VDIR) return (EOPNOTSUPP); kt = VTOKERN(vp)->kf_kt; if (uio->uio_offset != 0) return (EINVAL); xlen = min(uio->uio_resid, KSTRING-1); if (error = uiomove(strbuf, xlen, uio)) return (error); if (uio->uio_resid != 0) return (EIO); strbuf[xlen] = '\0'; xlen = strlen(strbuf); return (kernfs_xwrite(kt, strbuf, xlen)); } static int kernfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_long *a_cookies; int a_ncookies; } */ *ap; { int error, i; struct uio *uio = ap->a_uio; struct kern_target *kt; struct dirent d; if (ap->a_vp->v_type != VDIR) return (ENOTDIR); /* * We don't allow exporting kernfs mounts, and currently local * requests do not need cookies. */ if (ap->a_ncookies != NULL) panic("kernfs_readdir: not hungry"); i = uio->uio_offset / UIO_MX; error = 0; for (kt = &kern_targets[i]; uio->uio_resid >= UIO_MX && i < nkern_targets; kt++, i++) { struct dirent *dp = &d; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_readdir: i = %d\n", i); #endif if (kt->kt_tag == KTT_DEVICE) { dev_t *dp = kt->kt_data; struct vnode *fvp; if (*dp == NODEV || !vfinddev(*dp, kt->kt_vtype, &fvp)) continue; } bzero((caddr_t)dp, UIO_MX); dp->d_namlen = kt->kt_namlen; bcopy(kt->kt_name, dp->d_name, kt->kt_namlen+1); #ifdef KERNFS_DIAGNOSTIC printf("kernfs_readdir: name = %s, len = %d\n", dp->d_name, dp->d_namlen); #endif /* * Fill in the remaining fields */ dp->d_reclen = UIO_MX; dp->d_fileno = i + 3; dp->d_type = kt->kt_type; /* * And ship to userland */ if (error = uiomove((caddr_t)dp, UIO_MX, uio)) break; } uio->uio_offset = i * UIO_MX; return (error); } static int kernfs_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_inactive(%x)\n", vp); #endif /* * Clear out the v_type field to avoid * nasty things happening in vgone(). */ VOP_UNLOCK(vp, 0, ap->a_p); vp->v_type = VNON; return (0); } static int kernfs_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_reclaim(%x)\n", vp); #endif if (vp->v_data) { FREE(vp->v_data, M_TEMP); vp->v_data = 0; } return (0); } /* * Return POSIX pathconf information applicable to special devices. */ static int kernfs_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_MAX_CANON: *ap->a_retval = MAX_CANON; return (0); case _PC_MAX_INPUT: *ap->a_retval = MAX_INPUT; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_VDISABLE: *ap->a_retval = _POSIX_VDISABLE; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Print out the contents of a kernfs vnode. */ /* ARGSUSED */ static int kernfs_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_KERNFS, kernfs vnode\n"); return (0); } /*void*/ static int kernfs_vfree(ap) struct vop_vfree_args /* { struct vnode *a_pvp; ino_t a_ino; int a_mode; } */ *ap; { return (0); } /* * Kernfs "should never get here" operation */ static int kernfs_badop() { return (EIO); } #define kernfs_create ((int (*) __P((struct vop_create_args *)))eopnotsupp) #define kernfs_mknod ((int (*) __P((struct vop_mknod_args *)))eopnotsupp) #define kernfs_close ((int (*) __P((struct vop_close_args *)))nullop) #define kernfs_ioctl ((int (*) __P((struct vop_ioctl_args *)))eopnotsupp) -#define kernfs_select ((int (*) __P((struct vop_select_args *)))eopnotsupp) +#define kernfs_poll vop_nopoll #define kernfs_revoke vop_revoke #define kernfs_mmap ((int (*) __P((struct vop_mmap_args *)))eopnotsupp) #define kernfs_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define kernfs_seek ((int (*) __P((struct vop_seek_args *)))nullop) #define kernfs_remove ((int (*) __P((struct vop_remove_args *)))eopnotsupp) #define kernfs_link ((int (*) __P((struct vop_link_args *)))eopnotsupp) #define kernfs_rename ((int (*) __P((struct vop_rename_args *)))eopnotsupp) #define kernfs_mkdir ((int (*) __P((struct vop_mkdir_args *)))eopnotsupp) #define kernfs_rmdir ((int (*) __P((struct vop_rmdir_args *)))eopnotsupp) #define kernfs_symlink ((int (*) __P((struct vop_symlink_args *)))eopnotsupp) #define kernfs_readlink ((int (*) __P((struct vop_readlink_args *)))eopnotsupp) #define kernfs_abortop ((int (*) __P((struct vop_abortop_args *)))nullop) #define kernfs_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock) #define kernfs_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) #define kernfs_bmap ((int (*) __P((struct vop_bmap_args *)))kernfs_badop) #define kernfs_strategy \ ((int (*) __P((struct vop_strategy_args *)))kernfs_badop) #define kernfs_islocked \ ((int (*) __P((struct vop_islocked_args *)))vop_noislocked) #define kernfs_advlock ((int (*) __P((struct vop_advlock_args *)))eopnotsupp) #define kernfs_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))eopnotsupp) #define kernfs_valloc ((int(*) __P(( \ struct vnode *pvp, \ int mode, \ struct ucred *cred, \ struct vnode **vpp))) eopnotsupp) #define kernfs_truncate ((int (*) __P((struct vop_truncate_args *)))eopnotsupp) #define kernfs_update ((int (*) __P((struct vop_update_args *)))eopnotsupp) #define kernfs_bwrite ((int (*) __P((struct vop_bwrite_args *)))eopnotsupp) vop_t **kernfs_vnodeop_p; static struct vnodeopv_entry_desc kernfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)kernfs_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)kernfs_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)kernfs_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)kernfs_open }, /* open */ { &vop_close_desc, (vop_t *)kernfs_close }, /* close */ { &vop_access_desc, (vop_t *)kernfs_access }, /* access */ { &vop_getattr_desc, (vop_t *)kernfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)kernfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)kernfs_read }, /* read */ { &vop_write_desc, (vop_t *)kernfs_write }, /* write */ +/* XXX: vop_lease */ { &vop_ioctl_desc, (vop_t *)kernfs_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)kernfs_select }, /* select */ + { &vop_poll_desc, (vop_t *)kernfs_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)kernfs_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)kernfs_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)kernfs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)kernfs_seek }, /* seek */ { &vop_remove_desc, (vop_t *)kernfs_remove }, /* remove */ { &vop_link_desc, (vop_t *)kernfs_link }, /* link */ { &vop_rename_desc, (vop_t *)kernfs_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)kernfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)kernfs_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)kernfs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)kernfs_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)kernfs_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)kernfs_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)kernfs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)kernfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)kernfs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)kernfs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)kernfs_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)kernfs_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)kernfs_print }, /* print */ { &vop_islocked_desc, (vop_t *)kernfs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)kernfs_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)kernfs_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)kernfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)kernfs_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)kernfs_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)kernfs_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)kernfs_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)kernfs_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc kernfs_vnodeop_opv_desc = { &kernfs_vnodeop_p, kernfs_vnodeop_entries }; VNODEOP_SET(kernfs_vnodeop_opv_desc); diff --git a/sys/miscfs/portal/portal_vnops.c b/sys/miscfs/portal/portal_vnops.c index 281d5632f88d..a8b05cd6e124 100644 --- a/sys/miscfs/portal/portal_vnops.c +++ b/sys/miscfs/portal/portal_vnops.c @@ -1,743 +1,749 @@ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. * * 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. * * @(#)portal_vnops.c 8.14 (Berkeley) 5/21/95 * - * $Id: portal_vnops.c,v 1.19 1997/08/02 14:32:08 bde Exp $ + * $Id: portal_vnops.c,v 1.20 1997/08/16 19:15:18 wollman Exp $ */ /* * Portal Filesystem */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int portal_fileid = PORTAL_ROOTFILEID+1; static int portal_badop __P((void)); static void portal_closefd __P((struct proc *p, int fd)); static int portal_connect __P((struct socket *so, struct socket *so2)); static int portal_enotsupp __P((void)); static int portal_getattr __P((struct vop_getattr_args *ap)); static int portal_inactive __P((struct vop_inactive_args *ap)); static int portal_lookup __P((struct vop_lookup_args *ap)); static int portal_open __P((struct vop_open_args *ap)); static int portal_pathconf __P((struct vop_pathconf_args *ap)); static int portal_print __P((struct vop_print_args *ap)); static int portal_readdir __P((struct vop_readdir_args *ap)); static int portal_reclaim __P((struct vop_reclaim_args *ap)); static int portal_setattr __P((struct vop_setattr_args *ap)); static int portal_vfree __P((struct vop_vfree_args *ap)); static void portal_closefd(p, fd) struct proc *p; int fd; { int error; struct close_args ua; int rc; ua.fd = fd; error = close(p, &ua, &rc); /* * We should never get an error, and there isn't anything * we could do if we got one, so just print a message. */ if (error) printf("portal_closefd: error = %d\n", error); } /* * vp is the current namei directory * cnp is the name to locate in that directory... */ static int portal_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { struct componentname *cnp = ap->a_cnp; struct vnode **vpp = ap->a_vpp; struct vnode *dvp = ap->a_dvp; char *pname = cnp->cn_nameptr; struct portalnode *pt; int error; struct vnode *fvp = 0; char *path; int size; *vpp = NULLVP; if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) return (EROFS); if (cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); /*VOP_LOCK(dvp);*/ return (0); } /* * Do the MALLOC before the getnewvnode since doing so afterward * might cause a bogus v_data pointer to get dereferenced * elsewhere if MALLOC should block. */ MALLOC(pt, struct portalnode *, sizeof(struct portalnode), M_TEMP, M_WAITOK); error = getnewvnode(VT_PORTAL, dvp->v_mount, portal_vnodeop_p, &fvp); if (error) { FREE(pt, M_TEMP); goto bad; } fvp->v_type = VREG; fvp->v_data = pt; /* * Save all of the remaining pathname and * advance the namei next pointer to the end * of the string. */ for (size = 0, path = pname; *path; path++) size++; cnp->cn_consume = size - cnp->cn_namelen; pt->pt_arg = malloc(size+1, M_TEMP, M_WAITOK); pt->pt_size = size+1; bcopy(pname, pt->pt_arg, pt->pt_size); pt->pt_fileid = portal_fileid++; *vpp = fvp; /*VOP_LOCK(fvp);*/ return (0); bad:; if (fvp) vrele(fvp); return (error); } static int portal_connect(so, so2) struct socket *so; struct socket *so2; { /* from unp_connect, bypassing the namei stuff... */ struct socket *so3; struct unpcb *unp2; struct unpcb *unp3; if (so2 == 0) return (ECONNREFUSED); if (so->so_type != so2->so_type) return (EPROTOTYPE); if ((so2->so_options & SO_ACCEPTCONN) == 0) return (ECONNREFUSED); if ((so3 = sonewconn(so2, 0)) == 0) return (ECONNREFUSED); unp2 = sotounpcb(so2); unp3 = sotounpcb(so3); if (unp2->unp_addr) unp3->unp_addr = (struct sockaddr_un *) dup_sockaddr((struct sockaddr *)unp2->unp_addr, 0); so2 = so3; return (unp_connect2(so, so2)); } static int portal_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct socket *so = 0; struct portalnode *pt; struct proc *p = ap->a_p; struct vnode *vp = ap->a_vp; int s; struct uio auio; struct iovec aiov[2]; int res; struct mbuf *cm = 0; struct cmsghdr *cmsg; int newfds; int *ip; int fd; int error; int len; struct portalmount *fmp; struct file *fp; struct portal_cred pcred; /* * Nothing to do when opening the root node. */ if (vp->v_flag & VROOT) return (0); /* * Can't be opened unless the caller is set up * to deal with the side effects. Check for this * by testing whether the p_dupfd has been set. */ if (p->p_dupfd >= 0) return (ENODEV); pt = VTOPORTAL(vp); fmp = VFSTOPORTAL(vp->v_mount); /* * Create a new socket. */ error = socreate(AF_UNIX, &so, SOCK_STREAM, 0, ap->a_p); if (error) goto bad; /* * Reserve some buffer space */ res = pt->pt_size + sizeof(pcred) + 512; /* XXX */ error = soreserve(so, res, res); if (error) goto bad; /* * Kick off connection */ error = portal_connect(so, (struct socket *)fmp->pm_server->f_data); if (error) goto bad; /* * Wait for connection to complete */ /* * XXX: Since the mount point is holding a reference on the * underlying server socket, it is not easy to find out whether * the server process is still running. To handle this problem * we loop waiting for the new socket to be connected (something * which will only happen if the server is still running) or for * the reference count on the server socket to drop to 1, which * will happen if the server dies. Sleep for 5 second intervals * and keep polling the reference count. XXX. */ s = splnet(); while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) { if (fmp->pm_server->f_count == 1) { error = ECONNREFUSED; splx(s); goto bad; } (void) tsleep((caddr_t) &so->so_timeo, PSOCK, "portalcon", 5 * hz); } splx(s); if (so->so_error) { error = so->so_error; goto bad; } /* * Set miscellaneous flags */ so->so_rcv.sb_timeo = 0; so->so_snd.sb_timeo = 0; so->so_rcv.sb_flags |= SB_NOINTR; so->so_snd.sb_flags |= SB_NOINTR; pcred.pcr_flag = ap->a_mode; pcred.pcr_uid = ap->a_cred->cr_uid; pcred.pcr_ngroups = ap->a_cred->cr_ngroups; bcopy(ap->a_cred->cr_groups, pcred.pcr_groups, NGROUPS * sizeof(gid_t)); aiov[0].iov_base = (caddr_t) &pcred; aiov[0].iov_len = sizeof(pcred); aiov[1].iov_base = pt->pt_arg; aiov[1].iov_len = pt->pt_size; auio.uio_iov = aiov; auio.uio_iovcnt = 2; auio.uio_rw = UIO_WRITE; auio.uio_segflg = UIO_SYSSPACE; auio.uio_procp = p; auio.uio_offset = 0; auio.uio_resid = aiov[0].iov_len + aiov[1].iov_len; error = sosend(so, (struct sockaddr *) 0, &auio, (struct mbuf *) 0, (struct mbuf *) 0, 0, p); if (error) goto bad; len = auio.uio_resid = sizeof(int); do { struct mbuf *m = 0; int flags = MSG_WAITALL; error = soreceive(so, (struct sockaddr **) 0, &auio, &m, &cm, &flags); if (error) goto bad; /* * Grab an error code from the mbuf. */ if (m) { m = m_pullup(m, sizeof(int)); /* Needed? */ if (m) { error = *(mtod(m, int *)); m_freem(m); } else { error = EINVAL; } } else { if (cm == 0) { error = ECONNRESET; /* XXX */ #ifdef notdef break; #endif } } } while (cm == 0 && auio.uio_resid == len && !error); if (cm == 0) goto bad; if (auio.uio_resid) { error = 0; #ifdef notdef error = EMSGSIZE; goto bad; #endif } /* * XXX: Break apart the control message, and retrieve the * received file descriptor. Note that more than one descriptor * may have been received, or that the rights chain may have more * than a single mbuf in it. What to do? */ cmsg = mtod(cm, struct cmsghdr *); newfds = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof (int); if (newfds == 0) { error = ECONNREFUSED; goto bad; } /* * At this point the rights message consists of a control message * header, followed by a data region containing a vector of * integer file descriptors. The fds were allocated by the action * of receiving the control message. */ ip = (int *) (cmsg + 1); fd = *ip++; if (newfds > 1) { /* * Close extra fds. */ int i; printf("portal_open: %d extra fds\n", newfds - 1); for (i = 1; i < newfds; i++) { portal_closefd(p, *ip); ip++; } } /* * Check that the mode the file is being opened for is a subset * of the mode of the existing descriptor. */ fp = p->p_fd->fd_ofiles[fd]; if (((ap->a_mode & (FREAD|FWRITE)) | fp->f_flag) != fp->f_flag) { portal_closefd(p, fd); error = EACCES; goto bad; } /* * Save the dup fd in the proc structure then return the * special error code (ENXIO) which causes magic things to * happen in vn_open. The whole concept is, well, hmmm. */ p->p_dupfd = fd; error = ENXIO; bad:; /* * And discard the control message. */ if (cm) { m_freem(cm); } if (so) { soshutdown(so, 2); soclose(so); } return (error); } static int portal_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct vattr *vap = ap->a_vap; struct timeval tv; bzero(vap, sizeof(*vap)); vattr_null(vap); vap->va_uid = 0; vap->va_gid = 0; vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; vap->va_size = DEV_BSIZE; vap->va_blocksize = DEV_BSIZE; microtime(&tv); TIMEVAL_TO_TIMESPEC(&tv, &vap->va_atime); vap->va_mtime = vap->va_atime; vap->va_ctime = vap->va_ctime; vap->va_gen = 0; vap->va_flags = 0; vap->va_rdev = 0; /* vap->va_qbytes = 0; */ vap->va_bytes = 0; /* vap->va_qsize = 0; */ if (vp->v_flag & VROOT) { vap->va_type = VDIR; vap->va_mode = S_IRUSR|S_IWUSR|S_IXUSR| S_IRGRP|S_IWGRP|S_IXGRP| S_IROTH|S_IWOTH|S_IXOTH; vap->va_nlink = 2; vap->va_fileid = 2; } else { vap->va_type = VREG; vap->va_mode = S_IRUSR|S_IWUSR| S_IRGRP|S_IWGRP| S_IROTH|S_IWOTH; vap->va_nlink = 1; vap->va_fileid = VTOPORTAL(vp)->pt_fileid; } return (0); } static int portal_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { /* * Can't mess with the root vnode */ if (ap->a_vp->v_flag & VROOT) return (EACCES); return (0); } /* * Fake readdir, just return empty directory. * It is hard to deal with '.' and '..' so don't bother. */ static int portal_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_long *a_cookies; int a_ncookies; } */ *ap; { /* * We don't allow exporting portal mounts, and currently local * requests do not need cookies. */ if (ap->a_ncookies) panic("portal_readdir: not hungry"); return (0); } static int portal_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { VOP_UNLOCK(ap->a_vp, 0, ap->a_p); return (0); } static int portal_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { struct portalnode *pt = VTOPORTAL(ap->a_vp); if (pt->pt_arg) { free((caddr_t) pt->pt_arg, M_TEMP); pt->pt_arg = 0; } FREE(ap->a_vp->v_data, M_TEMP); ap->a_vp->v_data = 0; return (0); } /* * Return POSIX pathconf information applicable to special devices. */ static int portal_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_MAX_CANON: *ap->a_retval = MAX_CANON; return (0); case _PC_MAX_INPUT: *ap->a_retval = MAX_INPUT; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_VDISABLE: *ap->a_retval = _POSIX_VDISABLE; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Print out the contents of a Portal vnode. */ /* ARGSUSED */ static int portal_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_PORTAL, portal vnode\n"); return (0); } /*void*/ static int portal_vfree(ap) struct vop_vfree_args /* { struct vnode *a_pvp; ino_t a_ino; int a_mode; } */ *ap; { return (0); } /* * Portal vnode unsupported operation */ static int portal_enotsupp() { return (EOPNOTSUPP); } /* * Portal "should never get here" operation */ static int portal_badop() { panic("portal: bad op"); /* NOTREACHED */ } #define portal_create ((int (*) __P((struct vop_create_args *)))portal_enotsupp) #define portal_mknod ((int (*) __P((struct vop_mknod_args *)))portal_enotsupp) #define portal_close ((int (*) __P((struct vop_close_args *)))nullop) #define portal_access ((int (*) __P((struct vop_access_args *)))nullop) #define portal_read ((int (*) __P((struct vop_read_args *)))portal_enotsupp) #define portal_write ((int (*) __P((struct vop_write_args *)))portal_enotsupp) #define portal_ioctl ((int (*) __P((struct vop_ioctl_args *)))portal_enotsupp) -#define portal_select ((int (*) __P((struct vop_select_args *)))portal_enotsupp) #define portal_mmap ((int (*) __P((struct vop_mmap_args *)))portal_enotsupp) +#define portal_poll vop_nopoll #define portal_revoke vop_revoke #define portal_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define portal_seek ((int (*) __P((struct vop_seek_args *)))nullop) #define portal_remove ((int (*) __P((struct vop_remove_args *)))portal_enotsupp) #define portal_link ((int (*) __P((struct vop_link_args *)))portal_enotsupp) #define portal_rename ((int (*) __P((struct vop_rename_args *)))portal_enotsupp) #define portal_mkdir ((int (*) __P((struct vop_mkdir_args *)))portal_enotsupp) #define portal_rmdir ((int (*) __P((struct vop_rmdir_args *)))portal_enotsupp) #define portal_symlink \ ((int (*) __P((struct vop_symlink_args *)))portal_enotsupp) #define portal_readlink \ ((int (*) __P((struct vop_readlink_args *)))portal_enotsupp) #define portal_abortop ((int (*) __P((struct vop_abortop_args *)))nullop) #define portal_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock) #define portal_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) #define portal_bmap ((int (*) __P((struct vop_bmap_args *)))portal_badop) #define portal_strategy \ ((int (*) __P((struct vop_strategy_args *)))portal_badop) #define portal_islocked \ ((int (*) __P((struct vop_islocked_args *)))vop_noislocked) #define fifo_islocked ((int(*) __P((struct vop_islocked_args *)))vop_noislocked) #define portal_advlock \ ((int (*) __P((struct vop_advlock_args *)))portal_enotsupp) #define portal_blkatoff \ ((int (*) __P((struct vop_blkatoff_args *)))portal_enotsupp) #define portal_valloc ((int(*) __P(( \ struct vnode *pvp, \ int mode, \ struct ucred *cred, \ struct vnode **vpp))) portal_enotsupp) #define portal_truncate \ ((int (*) __P((struct vop_truncate_args *)))portal_enotsupp) #define portal_update ((int (*) __P((struct vop_update_args *)))portal_enotsupp) #define portal_bwrite ((int (*) __P((struct vop_bwrite_args *)))portal_enotsupp) vop_t **portal_vnodeop_p; static struct vnodeopv_entry_desc portal_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)portal_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)portal_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)portal_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)portal_open }, /* open */ { &vop_close_desc, (vop_t *)portal_close }, /* close */ { &vop_access_desc, (vop_t *)portal_access }, /* access */ { &vop_getattr_desc, (vop_t *)portal_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)portal_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)portal_read }, /* read */ { &vop_write_desc, (vop_t *)portal_write }, /* write */ +/* XXX: vop_lease */ { &vop_ioctl_desc, (vop_t *)portal_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)portal_select }, /* select */ + { &vop_poll_desc, (vop_t *)portal_poll }, /* poll */ { &vop_mmap_desc, (vop_t *)portal_mmap }, /* mmap */ { &vop_revoke_desc, (vop_t *)portal_revoke }, /* revoke */ { &vop_fsync_desc, (vop_t *)portal_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)portal_seek }, /* seek */ { &vop_remove_desc, (vop_t *)portal_remove }, /* remove */ { &vop_link_desc, (vop_t *)portal_link }, /* link */ { &vop_rename_desc, (vop_t *)portal_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)portal_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)portal_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)portal_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)portal_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)portal_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)portal_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)portal_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)portal_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)portal_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)portal_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)portal_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)portal_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)portal_print }, /* print */ { &vop_islocked_desc, (vop_t *)portal_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)portal_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)portal_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)portal_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)portal_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)portal_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)portal_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)portal_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)portal_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc portal_vnodeop_opv_desc = { &portal_vnodeop_p, portal_vnodeop_entries }; VNODEOP_SET(portal_vnodeop_opv_desc); diff --git a/sys/miscfs/procfs/procfs_vnops.c b/sys/miscfs/procfs/procfs_vnops.c index f876318e6aec..d34fb2d53135 100644 --- a/sys/miscfs/procfs/procfs_vnops.c +++ b/sys/miscfs/procfs/procfs_vnops.c @@ -1,1017 +1,1024 @@ /* * Copyright (c) 1993, 1995 Jan-Simon Pendry * Copyright (c) 1993, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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. * * @(#)procfs_vnops.c 8.18 (Berkeley) 5/21/95 * - * $Id: procfs_vnops.c,v 1.30 1997/08/02 14:32:20 bde Exp $ + * $Id: procfs_vnops.c,v 1.31 1997/08/12 04:34:30 sef Exp $ */ /* * procfs vnode interface */ #include #include #include #include #include #include #include #include #include #include #include #include #include static int procfs_abortop __P((struct vop_abortop_args *)); static int procfs_access __P((struct vop_access_args *)); static int procfs_badop __P((void)); static int procfs_bmap __P((struct vop_bmap_args *)); static int procfs_close __P((struct vop_close_args *)); static int procfs_getattr __P((struct vop_getattr_args *)); static int procfs_inactive __P((struct vop_inactive_args *)); static int procfs_ioctl __P((struct vop_ioctl_args *)); static int procfs_lookup __P((struct vop_lookup_args *)); static int procfs_open __P((struct vop_open_args *)); static int procfs_pathconf __P((struct vop_pathconf_args *ap)); static int procfs_print __P((struct vop_print_args *)); static int procfs_readdir __P((struct vop_readdir_args *)); static int procfs_readlink __P((struct vop_readlink_args *)); static int procfs_reclaim __P((struct vop_reclaim_args *)); static int procfs_setattr __P((struct vop_setattr_args *)); /* * This is a list of the valid names in the * process-specific sub-directories. It is * used in procfs_lookup and procfs_readdir */ struct proc_target { u_char pt_type; u_char pt_namlen; char *pt_name; pfstype pt_pfstype; int (*pt_valid) __P((struct proc *p)); } proc_targets[] = { #define N(s) sizeof(s)-1, s /* name type validp */ { DT_DIR, N("."), Pproc, NULL }, { DT_DIR, N(".."), Proot, NULL }, { DT_REG, N("file"), Pfile, procfs_validfile }, { DT_REG, N("mem"), Pmem, NULL }, { DT_REG, N("regs"), Pregs, procfs_validregs }, { DT_REG, N("fpregs"), Pfpregs, procfs_validfpregs }, { DT_REG, N("ctl"), Pctl, NULL }, { DT_REG, N("status"), Pstatus, NULL }, { DT_REG, N("note"), Pnote, NULL }, { DT_REG, N("notepg"), Pnotepg, NULL }, { DT_REG, N("map"), Pmap, procfs_validmap }, { DT_REG, N("etype"), Ptype, procfs_validtype }, #undef N }; static const int nproc_targets = sizeof(proc_targets) / sizeof(proc_targets[0]); static pid_t atopid __P((const char *, u_int)); /* * set things up for doing i/o on * the pfsnode (vp). (vp) is locked * on entry, and should be left locked * on exit. * * for procfs we don't need to do anything * in particular for i/o. all that is done * is to support exclusive open on process * memory images. */ static int procfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct proc *p1 = ap->a_p, *p2 = PFIND(pfs->pfs_pid); if (p2 == NULL) return ENOENT; switch (pfs->pfs_type) { case Pmem: if ((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL) || (pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE)) return (EBUSY); if (!CHECKIO(p1, p2) && (p1->p_cred->pc_ucred->cr_gid != KMEM_GROUP)) return EPERM; if (ap->a_mode & FWRITE) pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL); return (0); default: break; } return (0); } /* * close the pfsnode (vp) after doing i/o. * (vp) is not locked on entry or exit. * * nothing to do for procfs other than undo * any exclusive open flag (see _open above). */ static int procfs_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); switch (pfs->pfs_type) { case Pmem: if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL)) pfs->pfs_flags &= ~(FWRITE|O_EXCL); break; default: break; } return (0); } /* * do an ioctl operation on pfsnode (vp). * (vp) is not locked on entry or exit. */ static int procfs_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (ENOTTY); } /* * do block mapping for pfsnode (vp). * since we don't use the buffer cache * for procfs this function should never * be called. in any case, it's not clear * what part of the kernel ever makes use * of this function. for sanity, this is the * usual no-op bmap, although returning * (EIO) would be a reasonable alternative. */ static int procfs_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; } */ *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; if (ap->a_runp != NULL) *ap->a_runp = 0; return (0); } /* * procfs_inactive is called when the pfsnode * is vrele'd and the reference count goes * to zero. (vp) will be on the vnode free * list, so to get it back vget() must be * used. * * for procfs, check if the process is still * alive and if it isn't then just throw away * the vnode by calling vgone(). this may * be overkill and a waste of time since the * chances are that the process will still be * there and PFIND is not free. * * (vp) is locked on entry, but must be unlocked on exit. */ static int procfs_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; struct pfsnode *pfs = VTOPFS(vp); VOP_UNLOCK(vp, 0, ap->a_p); if (PFIND(pfs->pfs_pid) == 0) vgone(vp); return (0); } /* * _reclaim is called when getnewvnode() * wants to make use of an entry on the vnode * free list. at this time the filesystem needs * to free any private data and remove the node * from any private lists. */ static int procfs_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { return (procfs_freevp(ap->a_vp)); } /* * Return POSIX pathconf information applicable to special devices. */ static int procfs_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_MAX_CANON: *ap->a_retval = MAX_CANON; return (0); case _PC_MAX_INPUT: *ap->a_retval = MAX_INPUT; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_VDISABLE: *ap->a_retval = _POSIX_VDISABLE; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * _print is used for debugging. * just print a readable description * of (vp). */ static int procfs_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); printf("tag VT_PROCFS, type %s, pid %d, mode %x, flags %x\n", pfs->pfs_type, pfs->pfs_pid, pfs->pfs_mode, pfs->pfs_flags); return (0); } /* * _abortop is called when operations such as * rename and create fail. this entry is responsible * for undoing any side-effects caused by the lookup. * this will always include freeing the pathname buffer. */ static int procfs_abortop(ap) struct vop_abortop_args /* { struct vnode *a_dvp; struct componentname *a_cnp; } */ *ap; { if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); return (0); } /* * generic entry point for unsupported operations */ static int procfs_badop() { return (EIO); } /* * Invent attributes for pfsnode (vp) and store * them in (vap). * Directories lengths are returned as zero since * any real length would require the genuine size * to be computed, and nothing cares anyway. * * this is relatively minimal for procfs. */ static int procfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct vattr *vap = ap->a_vap; struct proc *procp; int error; /* * First make sure that the process and its credentials * still exist. */ switch (pfs->pfs_type) { case Proot: case Pcurproc: procp = 0; break; default: procp = PFIND(pfs->pfs_pid); if (procp == 0 || procp->p_cred == NULL || procp->p_ucred == NULL) return (ENOENT); } error = 0; /* start by zeroing out the attributes */ VATTR_NULL(vap); /* next do all the common fields */ vap->va_type = ap->a_vp->v_type; vap->va_mode = pfs->pfs_mode; vap->va_fileid = pfs->pfs_fileno; vap->va_flags = 0; vap->va_blocksize = PAGE_SIZE; vap->va_bytes = vap->va_size = 0; /* * Make all times be current TOD. * It would be possible to get the process start * time from the p_stat structure, but there's * no "file creation" time stamp anyway, and the * p_stat structure is not addressible if u. gets * swapped out for that process. */ { struct timeval tv; microtime(&tv); TIMEVAL_TO_TIMESPEC(&tv, &vap->va_ctime); } vap->va_atime = vap->va_mtime = vap->va_ctime; /* * If the process has exercised some setuid or setgid * privilege, then rip away read/write permission so * that only root can gain access. */ switch (pfs->pfs_type) { case Pctl: case Pregs: case Pfpregs: if (procp->p_flag & P_SUGID) vap->va_mode &= ~((VREAD|VWRITE)| ((VREAD|VWRITE)>>3)| ((VREAD|VWRITE)>>6)); break; case Pmem: /* Retain group kmem readablity. */ if (procp->p_flag & P_SUGID) vap->va_mode &= ~(VREAD|VWRITE); break; default: break; } /* * now do the object specific fields * * The size could be set from struct reg, but it's hardly * worth the trouble, and it puts some (potentially) machine * dependent data into this machine-independent code. If it * becomes important then this function should break out into * a per-file stat function in the corresponding .c file. */ switch (pfs->pfs_type) { case Proot: /* * Set nlink to 1 to tell fts(3) we don't actually know. */ vap->va_nlink = 1; vap->va_uid = 0; vap->va_gid = 0; vap->va_size = vap->va_bytes = DEV_BSIZE; break; case Pcurproc: { char buf[16]; /* should be enough */ vap->va_nlink = 1; vap->va_uid = 0; vap->va_gid = 0; vap->va_size = vap->va_bytes = sprintf(buf, "%ld", (long)curproc->p_pid); break; } case Pproc: vap->va_nlink = nproc_targets; vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = procp->p_ucred->cr_gid; vap->va_size = vap->va_bytes = DEV_BSIZE; break; case Pfile: error = EOPNOTSUPP; break; case Pmem: vap->va_nlink = 1; /* * If we denied owner access earlier, then we have to * change the owner to root - otherwise 'ps' and friends * will break even though they are setgid kmem. *SIGH* */ if (procp->p_flag & P_SUGID) vap->va_uid = 0; else vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = KMEM_GROUP; break; case Ptype: case Pmap: case Pregs: vap->va_bytes = vap->va_size = sizeof(struct reg); vap->va_nlink = 1; vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = procp->p_ucred->cr_gid; break; case Pfpregs: vap->va_bytes = vap->va_size = sizeof(struct fpreg); case Pctl: case Pstatus: case Pnote: case Pnotepg: vap->va_nlink = 1; vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = procp->p_ucred->cr_gid; break; default: panic("procfs_getattr"); } return (error); } static int procfs_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { /* * just fake out attribute setting * it's not good to generate an error * return, otherwise things like creat() * will fail when they try to set the * file length to 0. worse, this means * that echo $note > /proc/$pid/note will fail. */ return (0); } /* * implement access checking. * * something very similar to this code is duplicated * throughout the 4bsd kernel and should be moved * into kern/vfs_subr.c sometime. * * actually, the check for super-user is slightly * broken since it will allow read access to write-only * objects. this doesn't cause any particular trouble * but does mean that the i/o entry points need to check * that the operation really does make sense. */ static int procfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vattr *vap; struct vattr vattr; int error; /* * If you're the super-user, * you always get access. */ if (ap->a_cred->cr_uid == 0) return (0); vap = &vattr; error = VOP_GETATTR(ap->a_vp, vap, ap->a_cred, ap->a_p); if (error) return (error); /* * Access check is based on only one of owner, group, public. * If not owner, then check group. If not a member of the * group, then check public access. */ if (ap->a_cred->cr_uid != vap->va_uid) { gid_t *gp; int i; ap->a_mode >>= 3; gp = ap->a_cred->cr_groups; for (i = 0; i < ap->a_cred->cr_ngroups; i++, gp++) if (vap->va_gid == *gp) goto found; ap->a_mode >>= 3; found: ; } if ((vap->va_mode & ap->a_mode) == ap->a_mode) return (0); return (EACCES); } /* * lookup. this is incredibly complicated in the * general case, however for most pseudo-filesystems * very little needs to be done. * * unless you want to get a migraine, just make sure your * filesystem doesn't do any locking of its own. otherwise * read and inwardly digest ufs_lookup(). */ static int procfs_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { struct componentname *cnp = ap->a_cnp; struct vnode **vpp = ap->a_vpp; struct vnode *dvp = ap->a_dvp; char *pname = cnp->cn_nameptr; struct proc *curp = cnp->cn_proc; int error = 0; struct proc_target *pt; struct vnode *fvp; pid_t pid; struct pfsnode *pfs; struct proc *p; int i; *vpp = NULL; if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) return (EROFS); if (cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); /* vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, curp); */ return (0); } pfs = VTOPFS(dvp); switch (pfs->pfs_type) { case Proot: if (cnp->cn_flags & ISDOTDOT) return (EIO); if (CNEQ(cnp, "curproc", 7)) return (procfs_allocvp(dvp->v_mount, vpp, 0, Pcurproc)); pid = atopid(pname, cnp->cn_namelen); if (pid == NO_PID) break; p = PFIND(pid); if (p == 0) break; return (procfs_allocvp(dvp->v_mount, vpp, pid, Pproc)); case Pproc: if (cnp->cn_flags & ISDOTDOT) return (procfs_root(dvp->v_mount, vpp)); p = PFIND(pfs->pfs_pid); if (p == 0) break; for (pt = proc_targets, i = 0; i < nproc_targets; pt++, i++) { if (cnp->cn_namelen == pt->pt_namlen && bcmp(pt->pt_name, pname, cnp->cn_namelen) == 0 && (pt->pt_valid == NULL || (*pt->pt_valid)(p))) goto found; } break; found: if (pt->pt_pfstype == Pfile) { fvp = procfs_findtextvp(p); /* We already checked that it exists. */ VREF(fvp); vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, curp); *vpp = fvp; return (0); } return (procfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid, pt->pt_pfstype)); default: return (ENOTDIR); } return (cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS); } /* * Does this process have a text file? */ int procfs_validfile(p) struct proc *p; { return (procfs_findtextvp(p) != NULLVP); } /* * readdir returns directory entries from pfsnode (vp). * * the strategy here with procfs is to generate a single * directory entry at a time (struct pfsdent) and then * copy that out to userland using uiomove. a more efficent * though more complex implementation, would try to minimize * the number of calls to uiomove(). for procfs, this is * hardly worth the added code complexity. * * this should just be done through read() */ static int procfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_long *a_cookies; int a_ncookies; } */ *ap; { struct uio *uio = ap->a_uio; struct pfsdent d; struct pfsdent *dp = &d; struct pfsnode *pfs; int error; int count; int i; /* * We don't allow exporting procfs mounts, and currently local * requests do not need cookies. */ if (ap->a_ncookies) panic("procfs_readdir: not hungry"); pfs = VTOPFS(ap->a_vp); if (uio->uio_resid < UIO_MX) return (EINVAL); if (uio->uio_offset & (UIO_MX-1)) return (EINVAL); if (uio->uio_offset < 0) return (EINVAL); error = 0; count = 0; i = uio->uio_offset / UIO_MX; switch (pfs->pfs_type) { /* * this is for the process-specific sub-directories. * all that is needed to is copy out all the entries * from the procent[] table (top of this file). */ case Pproc: { struct proc *p; struct proc_target *pt; p = PFIND(pfs->pfs_pid); if (p == NULL) break; for (pt = &proc_targets[i]; uio->uio_resid >= UIO_MX && i < nproc_targets; pt++, i++) { if (pt->pt_valid && (*pt->pt_valid)(p) == 0) continue; dp->d_reclen = UIO_MX; dp->d_fileno = PROCFS_FILENO(pfs->pfs_pid, pt->pt_pfstype); dp->d_namlen = pt->pt_namlen; bcopy(pt->pt_name, dp->d_name, pt->pt_namlen + 1); dp->d_type = pt->pt_type; if (error = uiomove((caddr_t)dp, UIO_MX, uio)) break; } break; } /* * this is for the root of the procfs filesystem * what is needed is a special entry for "curproc" * followed by an entry for each process on allproc #ifdef PROCFS_ZOMBIE * and zombproc. #endif */ case Proot: { #ifdef PROCFS_ZOMBIE int doingzomb = 0; #endif int pcnt = 0; volatile struct proc *p = allproc.lh_first; again: for (; p && uio->uio_resid >= UIO_MX; i++, pcnt++) { bzero((char *) dp, UIO_MX); dp->d_reclen = UIO_MX; switch (i) { case 0: /* `.' */ case 1: /* `..' */ dp->d_fileno = PROCFS_FILENO(0, Proot); dp->d_namlen = i + 1; bcopy("..", dp->d_name, dp->d_namlen); dp->d_name[i + 1] = '\0'; dp->d_type = DT_DIR; break; case 2: dp->d_fileno = PROCFS_FILENO(0, Pcurproc); dp->d_namlen = 7; bcopy("curproc", dp->d_name, 8); dp->d_type = DT_LNK; break; default: while (pcnt < i) { pcnt++; p = p->p_list.le_next; if (!p) goto done; } dp->d_fileno = PROCFS_FILENO(p->p_pid, Pproc); dp->d_namlen = sprintf(dp->d_name, "%ld", (long)p->p_pid); dp->d_type = DT_REG; p = p->p_list.le_next; break; } if (error = uiomove((caddr_t)dp, UIO_MX, uio)) break; } done: #ifdef PROCFS_ZOMBIE if (p == 0 && doingzomb == 0) { doingzomb = 1; p = zombproc.lh_first; goto again; } #endif break; } default: error = ENOTDIR; break; } uio->uio_offset = i * UIO_MX; return (error); } /* * readlink reads the link of `curproc' */ static int procfs_readlink(ap) struct vop_readlink_args *ap; { struct uio *uio = ap->a_uio; char buf[16]; /* should be enough */ int len; if (VTOPFS(ap->a_vp)->pfs_fileno != PROCFS_FILENO(0, Pcurproc)) return (EINVAL); len = sprintf(buf, "%ld", (long)curproc->p_pid); return (uiomove((caddr_t)buf, len, ap->a_uio)); } /* * convert decimal ascii to pid_t */ static pid_t atopid(b, len) const char *b; u_int len; { pid_t p = 0; while (len--) { char c = *b++; if (c < '0' || c > '9') return (NO_PID); p = 10 * p + (c - '0'); if (p > PID_MAX) return (NO_PID); } return (p); } #define procfs_create ((int (*) __P((struct vop_create_args *))) procfs_badop) #define procfs_mknod ((int (*) __P((struct vop_mknod_args *))) procfs_badop) #define procfs_read procfs_rw #define procfs_write procfs_rw -#define procfs_select ((int (*) __P((struct vop_select_args *))) procfs_badop) #define procfs_mmap ((int (*) __P((struct vop_mmap_args *))) procfs_badop) +#define procfs_poll vop_nopoll #define procfs_revoke vop_revoke #define procfs_fsync ((int (*) __P((struct vop_fsync_args *))) procfs_badop) #define procfs_seek ((int (*) __P((struct vop_seek_args *))) procfs_badop) #define procfs_remove ((int (*) __P((struct vop_remove_args *))) procfs_badop) #define procfs_link ((int (*) __P((struct vop_link_args *))) procfs_badop) #define procfs_rename ((int (*) __P((struct vop_rename_args *))) procfs_badop) #define procfs_mkdir ((int (*) __P((struct vop_mkdir_args *))) procfs_badop) #define procfs_rmdir ((int (*) __P((struct vop_rmdir_args *))) procfs_badop) #define procfs_symlink ((int (*) __P((struct vop_symlink_args *))) procfs_badop) #define procfs_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock) #define procfs_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) #define procfs_strategy ((int (*) __P((struct vop_strategy_args *))) procfs_badop) #define procfs_islocked \ ((int (*) __P((struct vop_islocked_args *)))vop_noislocked) #define procfs_advlock ((int (*) __P((struct vop_advlock_args *))) procfs_badop) #define procfs_blkatoff ((int (*) __P((struct vop_blkatoff_args *))) procfs_badop) #define procfs_valloc ((int (*) __P((struct vop_valloc_args *))) procfs_badop) #define procfs_vfree ((int (*) __P((struct vop_vfree_args *))) nullop) #define procfs_truncate ((int (*) __P((struct vop_truncate_args *))) procfs_badop) #define procfs_update ((int (*) __P((struct vop_update_args *))) nullop) /* * procfs vnode operations. */ vop_t **procfs_vnodeop_p; static struct vnodeopv_entry_desc procfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)procfs_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)procfs_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)procfs_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)procfs_open }, /* open */ { &vop_close_desc, (vop_t *)procfs_close }, /* close */ { &vop_access_desc, (vop_t *)procfs_access }, /* access */ { &vop_getattr_desc, (vop_t *)procfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)procfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)procfs_read }, /* read */ { &vop_write_desc, (vop_t *)procfs_write }, /* write */ +/* XXX: vop_lease */ { &vop_ioctl_desc, (vop_t *)procfs_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)procfs_select }, /* select */ + { &vop_poll_desc, (vop_t *)procfs_poll }, /* poll */ { &vop_mmap_desc, (vop_t *)procfs_mmap }, /* mmap */ { &vop_revoke_desc, (vop_t *)procfs_revoke }, /* revoke */ { &vop_fsync_desc, (vop_t *)procfs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)procfs_seek }, /* seek */ { &vop_remove_desc, (vop_t *)procfs_remove }, /* remove */ { &vop_link_desc, (vop_t *)procfs_link }, /* link */ { &vop_rename_desc, (vop_t *)procfs_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)procfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)procfs_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)procfs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)procfs_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)procfs_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)procfs_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)procfs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)procfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)procfs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)procfs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)procfs_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)procfs_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)procfs_print }, /* print */ { &vop_islocked_desc, (vop_t *)procfs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)procfs_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)procfs_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)procfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)procfs_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)procfs_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)procfs_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)procfs_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ +/* XXX: vop_bwrite */ { NULL, NULL } }; static struct vnodeopv_desc procfs_vnodeop_opv_desc = { &procfs_vnodeop_p, procfs_vnodeop_entries }; VNODEOP_SET(procfs_vnodeop_opv_desc); diff --git a/sys/miscfs/specfs/spec_vnops.c b/sys/miscfs/specfs/spec_vnops.c index 98119cb95f3a..b5407eebcd33 100644 --- a/sys/miscfs/specfs/spec_vnops.c +++ b/sys/miscfs/specfs/spec_vnops.c @@ -1,906 +1,909 @@ /* * Copyright (c) 1989, 1993, 1995 * 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. * * @(#)spec_vnops.c 8.14 (Berkeley) 5/21/95 - * $Id: spec_vnops.c,v 1.40 1997/05/29 13:29:13 tegge Exp $ + * $Id: spec_vnops.c,v 1.41 1997/09/02 20:06:12 bde Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int spec_ebadf __P((void)); static int spec_getattr __P((struct vop_getattr_args *)); struct vnode *speclisth[SPECHSZ]; vop_t **spec_vnodeop_p; static struct vnodeopv_entry_desc spec_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)spec_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)spec_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)spec_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)spec_open }, /* open */ { &vop_close_desc, (vop_t *)spec_close }, /* close */ { &vop_access_desc, (vop_t *)spec_access }, /* access */ { &vop_getattr_desc, (vop_t *)spec_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)spec_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)spec_read }, /* read */ { &vop_write_desc, (vop_t *)spec_write }, /* write */ { &vop_lease_desc, (vop_t *)spec_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)spec_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)spec_select }, /* select */ + { &vop_poll_desc, (vop_t *)spec_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)spec_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)spec_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)spec_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)spec_seek }, /* seek */ { &vop_remove_desc, (vop_t *)spec_remove }, /* remove */ { &vop_link_desc, (vop_t *)spec_link }, /* link */ { &vop_rename_desc, (vop_t *)spec_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)spec_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)spec_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)spec_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)spec_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)spec_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)spec_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)spec_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)spec_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)spec_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)spec_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)spec_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)spec_print }, /* print */ { &vop_islocked_desc, (vop_t *)spec_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)spec_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)spec_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)spec_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)spec_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)spec_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)spec_update }, /* update */ - { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { &vop_getpages_desc, (vop_t *)spec_getpages}, /* getpages */ +/* XXX: vop_putpages */ + { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc spec_vnodeop_opv_desc = { &spec_vnodeop_p, spec_vnodeop_entries }; VNODEOP_SET(spec_vnodeop_opv_desc); static void spec_getpages_iodone __P((struct buf *bp)); /* * Trivial lookup routine that always fails. */ int spec_lookup(ap) struct vop_lookup_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { *ap->a_vpp = NULL; return (ENOTDIR); } /* * Open a special file. */ /* ARGSUSED */ int spec_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct proc *p = ap->a_p; struct vnode *bvp, *vp = ap->a_vp; dev_t bdev, dev = (dev_t)vp->v_rdev; int maj = major(dev); int error; /* * Don't allow open if fs is mounted -nodev. */ if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV)) return (ENXIO); switch (vp->v_type) { case VCHR: if ((u_int)maj >= nchrdev) return (ENXIO); if ( (cdevsw[maj] == NULL) || (cdevsw[maj]->d_open == NULL)) return ENXIO; if (ap->a_cred != FSCRED && (ap->a_mode & FWRITE)) { /* * When running in very secure mode, do not allow * opens for writing of any disk character devices. */ if (securelevel >= 2 && cdevsw[maj]->d_bdev && cdevsw[maj]->d_bdev->d_flags == D_DISK) return (EPERM); /* * When running in secure mode, do not allow opens * for writing of /dev/mem, /dev/kmem, or character * devices whose corresponding block devices are * currently mounted. */ if (securelevel >= 1) { if ((bdev = chrtoblk(dev)) != NODEV && vfinddev(bdev, VBLK, &bvp) && bvp->v_usecount > 0 && (error = vfs_mountedon(bvp))) return (error); if (iskmemdev(dev)) return (EPERM); } } #if 0 /* * Lite2 stuff. We will almost certainly do this * differently with devfs. The only use of this flag * is in dead_read to make ttys return EOF instead of * EIO when they are dead. Pre-lite2 FreeBSD returns * EOF for all character devices. */ if (cdevsw[maj]->d_type == D_TTY) vp->v_flag |= VISTTY; #endif VOP_UNLOCK(vp, 0, p); error = (*cdevsw[maj]->d_open)(dev, ap->a_mode, S_IFCHR, p); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); return (error); case VBLK: if ((u_int)maj >= nblkdev) return (ENXIO); if ( (bdevsw[maj] == NULL) || (bdevsw[maj]->d_open == NULL)) return ENXIO; /* * When running in very secure mode, do not allow * opens for writing of any disk block devices. */ if (securelevel >= 2 && ap->a_cred != FSCRED && (ap->a_mode & FWRITE) && bdevsw[maj]->d_flags == D_DISK) return (EPERM); /* * Do not allow opens of block devices that are * currently mounted. */ error = vfs_mountedon(vp); if (error) return (error); return ((*bdevsw[maj]->d_open)(dev, ap->a_mode, S_IFBLK, p)); } return (0); } /* * Vnode op for read */ /* ARGSUSED */ int spec_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct uio *uio = ap->a_uio; struct proc *p = uio->uio_procp; struct buf *bp; daddr_t bn, nextbn; long bsize, bscale; struct partinfo dpart; int n, on, majordev; d_ioctl_t *ioctl; int error = 0; dev_t dev; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_READ) panic("spec_read mode"); if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc) panic("spec_read proc"); #endif if (uio->uio_resid == 0) return (0); switch (vp->v_type) { case VCHR: VOP_UNLOCK(vp, 0, p); error = (*cdevsw[major(vp->v_rdev)]->d_read) (vp->v_rdev, uio, ap->a_ioflag); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); return (error); case VBLK: if (uio->uio_offset < 0) return (EINVAL); bsize = BLKDEV_IOSIZE; dev = vp->v_rdev; if ((majordev = major(dev)) < nblkdev && (ioctl = bdevsw[majordev]->d_ioctl) != NULL && (*ioctl)(dev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0 && dpart.part->p_fstype == FS_BSDFFS && dpart.part->p_frag != 0 && dpart.part->p_fsize != 0) bsize = dpart.part->p_frag * dpart.part->p_fsize; bscale = btodb(bsize); do { bn = btodb(uio->uio_offset) & ~(bscale - 1); on = uio->uio_offset % bsize; n = min((unsigned)(bsize - on), uio->uio_resid); if (vp->v_lastr + bscale == bn) { nextbn = bn + bscale; error = breadn(vp, bn, (int)bsize, &nextbn, (int *)&bsize, 1, NOCRED, &bp); } else error = bread(vp, bn, (int)bsize, NOCRED, &bp); vp->v_lastr = bn; n = min(n, bsize - bp->b_resid); if (error) { brelse(bp); return (error); } error = uiomove((char *)bp->b_data + on, n, uio); brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); return (error); default: panic("spec_read type"); } /* NOTREACHED */ } /* * Vnode op for write */ /* ARGSUSED */ int spec_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct uio *uio = ap->a_uio; struct proc *p = uio->uio_procp; struct buf *bp; daddr_t bn; int bsize, blkmask; struct partinfo dpart; register int n, on; int error = 0; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_WRITE) panic("spec_write mode"); if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc) panic("spec_write proc"); #endif switch (vp->v_type) { case VCHR: VOP_UNLOCK(vp, 0, p); error = (*cdevsw[major(vp->v_rdev)]->d_write) (vp->v_rdev, uio, ap->a_ioflag); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); return (error); case VBLK: if (uio->uio_resid == 0) return (0); if (uio->uio_offset < 0) return (EINVAL); bsize = BLKDEV_IOSIZE; if ((*bdevsw[major(vp->v_rdev)]->d_ioctl)(vp->v_rdev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0) { if (dpart.part->p_fstype == FS_BSDFFS && dpart.part->p_frag != 0 && dpart.part->p_fsize != 0) bsize = dpart.part->p_frag * dpart.part->p_fsize; } blkmask = btodb(bsize) - 1; do { bn = btodb(uio->uio_offset) & ~blkmask; on = uio->uio_offset % bsize; n = min((unsigned)(bsize - on), uio->uio_resid); if (n == bsize) bp = getblk(vp, bn, bsize, 0, 0); else error = bread(vp, bn, bsize, NOCRED, &bp); n = min(n, bsize - bp->b_resid); if (error) { brelse(bp); return (error); } error = uiomove((char *)bp->b_data + on, n, uio); if (n + on == bsize) { /* bawrite(bp); */ cluster_write(bp, 0); } else bdwrite(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); return (error); default: panic("spec_write type"); } /* NOTREACHED */ } /* * Device ioctl operation. */ /* ARGSUSED */ int spec_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { dev_t dev = ap->a_vp->v_rdev; switch (ap->a_vp->v_type) { case VCHR: return ((*cdevsw[major(dev)]->d_ioctl)(dev, ap->a_command, ap->a_data, ap->a_fflag, ap->a_p)); case VBLK: if (ap->a_command == 0 && (int)ap->a_data == B_TAPE) if (bdevsw[major(dev)]->d_flags == D_TAPE) return (0); else return (1); return ((*bdevsw[major(dev)]->d_ioctl)(dev, ap->a_command, ap->a_data, ap->a_fflag, ap->a_p)); default: panic("spec_ioctl"); /* NOTREACHED */ } } /* ARGSUSED */ int -spec_select(ap) - struct vop_select_args /* { +spec_poll(ap) + struct vop_poll_args /* { struct vnode *a_vp; - int a_which; - int a_fflags; + int a_events; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register dev_t dev; switch (ap->a_vp->v_type) { - default: - return (1); /* XXX */ - case VCHR: dev = ap->a_vp->v_rdev; - return (*cdevsw[major(dev)]->d_select)(dev, ap->a_which, ap->a_p); + return (*cdevsw[major(dev)]->d_poll)(dev, ap->a_events, ap->a_p); + default: + return (vop_nopoll(ap)); + } } /* * Synch buffers associated with a block device */ /* ARGSUSED */ int spec_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct buf *bp; struct buf *nbp; int s; if (vp->v_type == VCHR) return (0); /* * Flush all dirty buffers associated with a block device. */ loop: s = splbio(); for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if ((bp->b_flags & B_BUSY)) continue; if ((bp->b_flags & B_DELWRI) == 0) panic("spec_fsync: not dirty"); bremfree(bp); bp->b_flags |= B_BUSY; splx(s); bawrite(bp); goto loop; } if (ap->a_waitfor == MNT_WAIT) { while (vp->v_numoutput) { vp->v_flag |= VBWAIT; (void) tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "spfsyn", 0); } #ifdef DIAGNOSTIC if (vp->v_dirtyblkhd.lh_first) { vprint("spec_fsync: dirty", vp); splx(s); goto loop; } #endif } splx(s); return (0); } int spec_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { VOP_UNLOCK(ap->a_vp, 0, ap->a_p); return (0); } /* * Just call the device strategy routine */ int spec_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *ap; { (*bdevsw[major(ap->a_bp->b_dev)]->d_strategy)(ap->a_bp); return (0); } /* * This is a noop, simply returning what one has been given. */ int spec_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; if (ap->a_runp != NULL) *ap->a_runp = 0; if (ap->a_runb != NULL) *ap->a_runb = 0; return (0); } /* * Device close routine */ /* ARGSUSED */ int spec_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; struct proc *p = ap->a_p; dev_t dev = vp->v_rdev; d_close_t *devclose; int mode, error; switch (vp->v_type) { case VCHR: /* * Hack: a tty device that is a controlling terminal * has a reference from the session structure. * We cannot easily tell that a character device is * a controlling terminal, unless it is the closing * process' controlling terminal. In that case, * if the reference count is 2 (this last descriptor * plus the session), release the reference from the session. */ if (vcount(vp) == 2 && ap->a_p && (vp->v_flag & VXLOCK) == 0 && vp == ap->a_p->p_session->s_ttyvp) { vrele(vp); ap->a_p->p_session->s_ttyvp = NULL; } /* * If the vnode is locked, then we are in the midst * of forcably closing the device, otherwise we only * close on last reference. */ if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0) return (0); devclose = cdevsw[major(dev)]->d_close; mode = S_IFCHR; break; case VBLK: /* * On last close of a block device (that isn't mounted) * we must invalidate any in core blocks, so that * we can, for instance, change floppy disks. */ error = vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 0, 0); if (error) return (error); /* * We do not want to really close the device if it * is still in use unless we are trying to close it * forcibly. Since every use (buffer, vnode, swap, cmap) * holds a reference to the vnode, and because we mark * any other vnodes that alias this device, when the * sum of the reference counts on all the aliased * vnodes descends to one, we are on last close. */ if ((vcount(vp) > (vp->v_object?2:1)) && (vp->v_flag & VXLOCK) == 0) return (0); if (vp->v_object) { vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); vnode_pager_uncache(vp, p); VOP_UNLOCK(vp, 0, p); } devclose = bdevsw[major(dev)]->d_close; mode = S_IFBLK; break; default: panic("spec_close: not special"); } return ((*devclose)(dev, ap->a_fflag, mode, ap->a_p)); } /* * Print out the contents of a special device vnode. */ int spec_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_NON, dev %d, %d\n", major(ap->a_vp->v_rdev), minor(ap->a_vp->v_rdev)); return (0); } /* * Return POSIX pathconf information applicable to special devices. */ int spec_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_MAX_CANON: *ap->a_retval = MAX_CANON; return (0); case _PC_MAX_INPUT: *ap->a_retval = MAX_INPUT; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_VDISABLE: *ap->a_retval = _POSIX_VDISABLE; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Special device advisory byte-level locks. */ /* ARGSUSED */ int spec_advlock(ap) struct vop_advlock_args /* { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ *ap; { return (ap->a_flags & F_FLOCK ? EOPNOTSUPP : EINVAL); } /* * Special device failed operation */ static int spec_ebadf() { return (EBADF); } /* * Special device bad operation */ int spec_badop() { panic("spec_badop called"); /* NOTREACHED */ } static void spec_getpages_iodone(bp) struct buf *bp; { bp->b_flags |= B_DONE; wakeup(bp); } int spec_getpages(ap) struct vop_getpages_args *ap; { vm_offset_t kva; int error; int i, pcount, size, s; daddr_t blkno; struct buf *bp; vm_ooffset_t offset; struct vnode *vp = ap->a_vp; int blksiz; error = 0; pcount = round_page(ap->a_count) / PAGE_SIZE; /* * Calculate the offset of the transfer. */ offset = IDX_TO_OFF(ap->a_m[0]->pindex) + ap->a_offset; /* XXX sanity check before we go into details. */ /* XXX limits should be defined elsewhere. */ #define DADDR_T_BIT 32 #define OFFSET_MAX ((1LL << (DADDR_T_BIT + DEV_BSHIFT)) - 1) if (offset < 0 || offset > OFFSET_MAX) { /* XXX still no %q in kernel. */ printf("spec_getpages: preposterous offset 0x%x%08x\n", (u_int)((u_quad_t)offset >> 32), (u_int)(offset & 0xffffffff)); return (VM_PAGER_ERROR); } blkno = btodb(offset); /* * Round up physical size for real devices, use the * fundamental blocksize of the fs if possible. */ if (vp && vp->v_mount) blksiz = vp->v_mount->mnt_stat.f_bsize; else blksiz = DEV_BSIZE; size = (ap->a_count + blksiz - 1) & ~(blksiz - 1); bp = getpbuf(); kva = (vm_offset_t)bp->b_data; /* * Map the pages to be read into the kva. */ pmap_qenter(kva, ap->a_m, pcount); /* Build a minimal buffer header. */ bp->b_flags = B_BUSY | B_READ | B_CALL; bp->b_iodone = spec_getpages_iodone; /* B_PHYS is not set, but it is nice to fill this in. */ bp->b_proc = curproc; bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred; if (bp->b_rcred != NOCRED) crhold(bp->b_rcred); if (bp->b_wcred != NOCRED) crhold(bp->b_wcred); bp->b_blkno = blkno; bp->b_lblkno = blkno; pbgetvp(ap->a_vp, bp); bp->b_bcount = size; bp->b_bufsize = size; cnt.v_vnodein++; cnt.v_vnodepgsin += pcount; /* Do the input. */ VOP_STRATEGY(bp); if (bp->b_flags & B_ASYNC) return (VM_PAGER_PEND); s = splbio(); /* We definitely need to be at splbio here. */ while ((bp->b_flags & B_DONE) == 0) tsleep(bp, PVM, "vnread", 0); splx(s); if ((bp->b_flags & B_ERROR) != 0) error = EIO; if (!error && ap->a_count != pcount * PAGE_SIZE) bzero((caddr_t)kva + ap->a_count, PAGE_SIZE * pcount - ap->a_count); pmap_qremove(kva, pcount); /* * Free the buffer header back to the swap buffer pool. */ relpbuf(bp); for (i = 0; i < pcount; i++) { ap->a_m[i]->dirty = 0; ap->a_m[i]->valid = VM_PAGE_BITS_ALL; ap->a_m[i]->flags &= ~PG_ZERO; if (i != ap->a_reqpage) { /* * Whether or not to leave the page activated is up in * the air, but we should put the page on a page queue * somewhere (it already is in the object). Result: * It appears that emperical results show that * deactivating pages is best. */ /* * Just in case someone was asking for this page we * now tell them that it is ok to use. */ if (!error) { vm_page_deactivate(ap->a_m[i]); PAGE_WAKEUP(ap->a_m[i]); } else vnode_pager_freepage(ap->a_m[i]); } } if (error) printf("spec_getpages: I/O read error\n"); return (error ? VM_PAGER_ERROR : VM_PAGER_OK); } /* ARGSUSED */ static int spec_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct vattr *vap = ap->a_vap; struct partinfo dpart; bzero(vap, sizeof (*vap)); if (vp->v_type == VBLK) vap->va_blocksize = BLKDEV_IOSIZE; else if (vp->v_type == VCHR) vap->va_blocksize = MAXBSIZE; if ((*bdevsw[major(vp->v_rdev)]->d_ioctl)(vp->v_rdev, DIOCGPART, (caddr_t)&dpart, FREAD, ap->a_p) == 0) { vap->va_bytes = dbtob(dpart.disklab->d_partitions [minor(vp->v_rdev)].p_size); vap->va_size = vap->va_bytes; } return (0); } diff --git a/sys/miscfs/specfs/specdev.h b/sys/miscfs/specfs/specdev.h index ce71afbae8dd..fe046cec0a74 100644 --- a/sys/miscfs/specfs/specdev.h +++ b/sys/miscfs/specfs/specdev.h @@ -1,128 +1,128 @@ /* * Copyright (c) 1990, 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. * * @(#)specdev.h 8.6 (Berkeley) 5/21/95 - * $Id$ + * $Id: specdev.h,v 1.11 1997/02/22 09:40:35 peter Exp $ */ /* * This structure defines the information maintained about * special devices. It is allocated in checkalias and freed * in vgone. */ struct specinfo { struct vnode **si_hashchain; struct vnode *si_specnext; long si_flags; dev_t si_rdev; }; /* * Exported shorthand */ #define v_rdev v_specinfo->si_rdev #define v_hashchain v_specinfo->si_hashchain #define v_specnext v_specinfo->si_specnext #define v_specflags v_specinfo->si_flags /* * Flags for specinfo */ #define SI_MOUNTEDON 0x0001 /* block special device is mounted on */ /* * Special device management */ #define SPECHSZ 64 #if ((SPECHSZ&(SPECHSZ-1)) == 0) #define SPECHASH(rdev) (((rdev>>5)+(rdev))&(SPECHSZ-1)) #else #define SPECHASH(rdev) (((unsigned)((rdev>>5)+(rdev)))%SPECHSZ) #endif extern struct vnode *speclisth[SPECHSZ]; /* * Prototypes for special file operations on vnodes. */ extern vop_t **spec_vnodeop_p; struct nameidata; struct componentname; struct ucred; struct flock; struct buf; struct uio; int spec_badop __P((void)); int spec_lookup __P((struct vop_lookup_args *)); #define spec_create ((int (*) __P((struct vop_create_args *)))spec_badop) #define spec_mknod ((int (*) __P((struct vop_mknod_args *)))spec_badop) int spec_open __P((struct vop_open_args *)); int spec_close __P((struct vop_close_args *)); #define spec_access ((int (*) __P((struct vop_access_args *)))spec_ebadf) #define spec_setattr ((int (*) __P((struct vop_setattr_args *)))spec_ebadf) int spec_read __P((struct vop_read_args *)); int spec_write __P((struct vop_write_args *)); #define spec_lease_check ((int (*) __P((struct vop_lease_args *)))nullop) int spec_ioctl __P((struct vop_ioctl_args *)); -int spec_select __P((struct vop_select_args *)); +int spec_poll __P((struct vop_poll_args *)); #define spec_revoke vop_revoke #define spec_mmap ((int (*) __P((struct vop_mmap_args *)))spec_badop) int spec_fsync __P((struct vop_fsync_args *)); #define spec_seek ((int (*) __P((struct vop_seek_args *)))spec_badop) #define spec_remove ((int (*) __P((struct vop_remove_args *)))spec_badop) #define spec_link ((int (*) __P((struct vop_link_args *)))spec_badop) #define spec_rename ((int (*) __P((struct vop_rename_args *)))spec_badop) #define spec_mkdir ((int (*) __P((struct vop_mkdir_args *)))spec_badop) #define spec_rmdir ((int (*) __P((struct vop_rmdir_args *)))spec_badop) #define spec_symlink ((int (*) __P((struct vop_symlink_args *)))spec_badop) #define spec_readdir ((int (*) __P((struct vop_readdir_args *)))spec_badop) #define spec_readlink ((int (*) __P((struct vop_readlink_args *)))spec_badop) #define spec_abortop ((int (*) __P((struct vop_abortop_args *)))spec_badop) int spec_inactive __P((struct vop_inactive_args *)); #define spec_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop) #define spec_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock) #define spec_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) int spec_bmap __P((struct vop_bmap_args *)); int spec_strategy __P((struct vop_strategy_args *)); int spec_print __P((struct vop_print_args *)); #define spec_islocked ((int(*) __P((struct vop_islocked_args *)))vop_noislocked) int spec_pathconf __P((struct vop_pathconf_args *)); int spec_advlock __P((struct vop_advlock_args *)); int spec_getpages __P((struct vop_getpages_args *)); #define spec_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))spec_badop) #define spec_valloc ((int (*) __P((struct vop_valloc_args *)))spec_badop) #define spec_reallocblks \ ((int (*) __P((struct vop_reallocblks_args *)))spec_badop) #define spec_vfree ((int (*) __P((struct vop_vfree_args *)))spec_badop) #define spec_truncate ((int (*) __P((struct vop_truncate_args *)))nullop) #define spec_update ((int (*) __P((struct vop_update_args *)))nullop) #define spec_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop) diff --git a/sys/miscfs/union/union_vnops.c b/sys/miscfs/union/union_vnops.c index ecb8fc15a809..469ae530b541 100644 --- a/sys/miscfs/union/union_vnops.c +++ b/sys/miscfs/union/union_vnops.c @@ -1,1810 +1,1813 @@ /* * Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry. * Copyright (c) 1992, 1993, 1994, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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. * * @(#)union_vnops.c 8.32 (Berkeley) 6/23/95 - * $Id: union_vnops.c,v 1.40 1997/09/04 03:14:49 kato Exp $ + * $Id: union_vnops.c,v 1.41 1997/09/07 06:46:34 bde Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define FIXUP(un, p) { \ if (((un)->un_flags & UN_ULOCK) == 0) { \ union_fixup(un, p); \ } \ } #define SETKLOCK(un) (un)->un_flags |= UN_KLOCK #define CLEARKLOCK(un) (un)->un_flags &= ~UN_KLOCK static int union_abortop __P((struct vop_abortop_args *ap)); static int union_access __P((struct vop_access_args *ap)); static int union_advlock __P((struct vop_advlock_args *ap)); static int union_bmap __P((struct vop_bmap_args *ap)); static int union_close __P((struct vop_close_args *ap)); static int union_create __P((struct vop_create_args *ap)); static void union_fixup __P((struct union_node *un, struct proc *p)); static int union_fsync __P((struct vop_fsync_args *ap)); static int union_getattr __P((struct vop_getattr_args *ap)); static int union_inactive __P((struct vop_inactive_args *ap)); static int union_ioctl __P((struct vop_ioctl_args *ap)); static int union_islocked __P((struct vop_islocked_args *ap)); static int union_lease __P((struct vop_lease_args *ap)); static int union_link __P((struct vop_link_args *ap)); static int union_lock __P((struct vop_lock_args *ap)); static int union_lookup __P((struct vop_lookup_args *ap)); static int union_lookup1 __P((struct vnode *udvp, struct vnode **dvpp, struct vnode **vpp, struct componentname *cnp)); static int union_mkdir __P((struct vop_mkdir_args *ap)); static int union_mknod __P((struct vop_mknod_args *ap)); static int union_mmap __P((struct vop_mmap_args *ap)); static int union_open __P((struct vop_open_args *ap)); static int union_pathconf __P((struct vop_pathconf_args *ap)); static int union_print __P((struct vop_print_args *ap)); static int union_read __P((struct vop_read_args *ap)); static int union_readdir __P((struct vop_readdir_args *ap)); static int union_readlink __P((struct vop_readlink_args *ap)); static int union_reclaim __P((struct vop_reclaim_args *ap)); static int union_remove __P((struct vop_remove_args *ap)); static int union_rename __P((struct vop_rename_args *ap)); static int union_revoke __P((struct vop_revoke_args *ap)); static int union_rmdir __P((struct vop_rmdir_args *ap)); static int union_seek __P((struct vop_seek_args *ap)); -static int union_select __P((struct vop_select_args *ap)); +static int union_poll __P((struct vop_poll_args *ap)); static int union_setattr __P((struct vop_setattr_args *ap)); static int union_strategy __P((struct vop_strategy_args *ap)); static int union_symlink __P((struct vop_symlink_args *ap)); static int union_unlock __P((struct vop_unlock_args *ap)); static int union_whiteout __P((struct vop_whiteout_args *ap)); static int union_write __P((struct vop_read_args *ap)); static void union_fixup(un, p) struct union_node *un; struct proc *p; { vn_lock(un->un_uppervp, LK_EXCLUSIVE | LK_RETRY, p); un->un_flags |= UN_ULOCK; } static int union_lookup1(udvp, dvpp, vpp, cnp) struct vnode *udvp; struct vnode **dvpp; struct vnode **vpp; struct componentname *cnp; { int error; struct proc *p = cnp->cn_proc; struct vnode *tdvp; struct vnode *dvp; struct mount *mp; dvp = *dvpp; /* * If stepping up the directory tree, check for going * back across the mount point, in which case do what * lookup would do by stepping back down the mount * hierarchy. */ if (cnp->cn_flags & ISDOTDOT) { while ((dvp != udvp) && (dvp->v_flag & VROOT)) { /* * Don't do the NOCROSSMOUNT check * at this level. By definition, * union fs deals with namespaces, not * filesystems. */ tdvp = dvp; *dvpp = dvp = dvp->v_mount->mnt_vnodecovered; vput(tdvp); VREF(dvp); vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); } } error = VOP_LOOKUP(dvp, &tdvp, cnp); if (error) return (error); /* * The parent directory will have been unlocked, unless lookup * found the last component. In which case, re-lock the node * here to allow it to be unlocked again (phew) in union_lookup. */ if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN)) vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); dvp = tdvp; /* * Lastly check if the current node is a mount point in * which case walk up the mount hierarchy making sure not to * bump into the root of the mount tree (ie. dvp != udvp). */ while (dvp != udvp && (dvp->v_type == VDIR) && (mp = dvp->v_mountedhere)) { if (vfs_busy(mp, 0, 0, p)) continue; error = VFS_ROOT(mp, &tdvp); vfs_unbusy(mp, p); if (error) { vput(dvp); return (error); } vput(dvp); dvp = tdvp; } *vpp = dvp; return (0); } static int union_lookup(ap) struct vop_lookup_args /* { struct vnodeop_desc *a_desc; struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { int error; int uerror, lerror; struct vnode *uppervp, *lowervp; struct vnode *upperdvp, *lowerdvp; struct vnode *dvp = ap->a_dvp; struct union_node *dun = VTOUNION(dvp); struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; int lockparent = cnp->cn_flags & LOCKPARENT; struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount); struct ucred *saved_cred; int iswhiteout; struct vattr va; /* * Disallow write attemps to the filesystem mounted read-only. */ if ((cnp->cn_flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) return (EROFS); #ifdef notyet if (cnp->cn_namelen == 3 && cnp->cn_nameptr[2] == '.' && cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.') { dvp = *ap->a_vpp = LOWERVP(ap->a_dvp); if (dvp == NULLVP) return (ENOENT); VREF(dvp); vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); if (!lockparent || !(cnp->cn_flags & ISLASTCN)) VOP_UNLOCK(ap->a_dvp, 0, p); return (0); } #endif cnp->cn_flags |= LOCKPARENT; upperdvp = dun->un_uppervp; lowerdvp = dun->un_lowervp; uppervp = NULLVP; lowervp = NULLVP; iswhiteout = 0; /* * do the lookup in the upper level. * if that level comsumes additional pathnames, * then assume that something special is going * on and just return that vnode. */ if (upperdvp != NULLVP) { FIXUP(dun, p); /* * If we're doing `..' in the underlying filesystem, * we must drop our lock on the union node before * going up the tree in the lower file system--if we block * on the lowervp lock, and that's held by someone else * coming down the tree and who's waiting for our lock, * we would be hosed. */ if (cnp->cn_flags & ISDOTDOT) { /* retain lock on underlying VP: */ SETKLOCK(dun); VOP_UNLOCK(dvp, 0, p); CLEARKLOCK(dun); } uerror = union_lookup1(um->um_uppervp, &upperdvp, &uppervp, cnp); /* * Disallow write attemps to the filesystem mounted read-only. */ if (uerror == EJUSTRETURN && (cnp->cn_flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) && (cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME)) { if (!lockparent) cnp->cn_flags &= ~LOCKPARENT; return (EROFS); } if (cnp->cn_flags & ISDOTDOT) { if (dun->un_uppervp == upperdvp) { /* * We got the underlying bugger back locked... * now take back the union node lock. Since we * hold the uppervp lock, we can diddle union * locking flags at will. :) */ dun->un_flags |= UN_ULOCK; } /* * If upperdvp got swapped out, it means we did * some mount point magic, and we do not have * dun->un_uppervp locked currently--so we get it * locked here (don't set the UN_ULOCK flag). */ vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p); } /*if (uppervp == upperdvp) dun->un_flags |= UN_KLOCK;*/ if (cnp->cn_consume != 0) { *ap->a_vpp = uppervp; if (!lockparent) cnp->cn_flags &= ~LOCKPARENT; return (uerror); } if (uerror == ENOENT || uerror == EJUSTRETURN) { if (cnp->cn_flags & ISWHITEOUT) { iswhiteout = 1; } else if (lowerdvp != NULLVP) { lerror = VOP_GETATTR(upperdvp, &va, cnp->cn_cred, cnp->cn_proc); if (lerror == 0 && (va.va_flags & OPAQUE)) iswhiteout = 1; } } } else { uerror = ENOENT; } /* * in a similar way to the upper layer, do the lookup * in the lower layer. this time, if there is some * component magic going on, then vput whatever we got * back from the upper layer and return the lower vnode * instead. */ if (lowerdvp != NULLVP && !iswhiteout) { int nameiop; vn_lock(lowerdvp, LK_EXCLUSIVE | LK_RETRY, p); /* * Only do a LOOKUP on the bottom node, since * we won't be making changes to it anyway. */ nameiop = cnp->cn_nameiop; cnp->cn_nameiop = LOOKUP; if (um->um_op == UNMNT_BELOW) { saved_cred = cnp->cn_cred; cnp->cn_cred = um->um_cred; } /* * We shouldn't have to worry about locking interactions * between the lower layer and our union layer (w.r.t. * `..' processing) because we don't futz with lowervp * locks in the union-node instantiation code path. */ lerror = union_lookup1(um->um_lowervp, &lowerdvp, &lowervp, cnp); if (um->um_op == UNMNT_BELOW) cnp->cn_cred = saved_cred; cnp->cn_nameiop = nameiop; if (lowervp != lowerdvp) VOP_UNLOCK(lowerdvp, 0, p); if (cnp->cn_consume != 0 || lerror == EACCES) { if (lerror == EACCES) lowervp = NULLVP; if (uppervp != NULLVP) { if (uppervp == upperdvp) vrele(uppervp); else vput(uppervp); uppervp = NULLVP; } *ap->a_vpp = lowervp; if (!lockparent) cnp->cn_flags &= ~LOCKPARENT; return (lerror); } } else { lerror = ENOENT; if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) { lowervp = LOWERVP(dun->un_pvp); if (lowervp != NULLVP) { VREF(lowervp); vn_lock(lowervp, LK_EXCLUSIVE | LK_RETRY, p); lerror = 0; } } } if (!lockparent) cnp->cn_flags &= ~LOCKPARENT; /* * at this point, we have uerror and lerror indicating * possible errors with the lookups in the upper and lower * layers. additionally, uppervp and lowervp are (locked) * references to existing vnodes in the upper and lower layers. * * there are now three cases to consider. * 1. if both layers returned an error, then return whatever * error the upper layer generated. * * 2. if the top layer failed and the bottom layer succeeded * then two subcases occur. * a. the bottom vnode is not a directory, in which * case just return a new union vnode referencing * an empty top layer and the existing bottom layer. * b. the bottom vnode is a directory, in which case * create a new directory in the top-level and * continue as in case 3. * * 3. if the top layer succeeded then return a new union * vnode referencing whatever the new top layer and * whatever the bottom layer returned. */ *ap->a_vpp = NULLVP; /* case 1. */ if ((uerror != 0) && (lerror != 0)) { return (uerror); } /* case 2. */ if (uerror != 0 /* && (lerror == 0) */ ) { if (lowervp->v_type == VDIR) { /* case 2b. */ dun->un_flags &= ~UN_ULOCK; VOP_UNLOCK(upperdvp, 0, p); uerror = union_mkshadow(um, upperdvp, cnp, &uppervp); vn_lock(upperdvp, LK_EXCLUSIVE | LK_RETRY, p); dun->un_flags |= UN_ULOCK; if (uerror) { if (lowervp != NULLVP) { vput(lowervp); lowervp = NULLVP; } return (uerror); } } } if (lowervp != NULLVP) VOP_UNLOCK(lowervp, 0, p); error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp, uppervp, lowervp, 1); if (error) { if (uppervp != NULLVP) vput(uppervp); if (lowervp != NULLVP) vrele(lowervp); } else { if (*ap->a_vpp != dvp) if (!lockparent || !(cnp->cn_flags & ISLASTCN)) VOP_UNLOCK(dvp, 0, p); #ifdef DIAGNOSTIC if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.' && *ap->a_vpp != dvp) { panic("union_lookup returning . (%p) not same as startdir (%p)", ap->a_vpp, dvp); } #endif } return (error); } static int union_create(ap) struct vop_create_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct union_node *un = VTOUNION(ap->a_dvp); struct vnode *dvp = un->un_uppervp; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (dvp != NULLVP) { int error; struct vnode *vp; struct mount *mp; FIXUP(un, p); VREF(dvp); SETKLOCK(un); mp = ap->a_dvp->v_mount; vput(ap->a_dvp); CLEARKLOCK(un); error = VOP_CREATE(dvp, &vp, cnp, ap->a_vap); if (error) return (error); error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP, cnp, vp, NULLVP, 1); if (error) vput(vp); return (error); } vput(ap->a_dvp); return (EROFS); } static int union_whiteout(ap) struct vop_whiteout_args /* { struct vnode *a_dvp; struct componentname *a_cnp; int a_flags; } */ *ap; { struct union_node *un = VTOUNION(ap->a_dvp); struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (un->un_uppervp == NULLVP) return (EOPNOTSUPP); FIXUP(un, p); return (VOP_WHITEOUT(un->un_uppervp, cnp, ap->a_flags)); } static int union_mknod(ap) struct vop_mknod_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct union_node *un = VTOUNION(ap->a_dvp); struct vnode *dvp = un->un_uppervp; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (dvp != NULLVP) { int error; struct vnode *vp; struct mount *mp; FIXUP(un, p); VREF(dvp); SETKLOCK(un); mp = ap->a_dvp->v_mount; vput(ap->a_dvp); CLEARKLOCK(un); error = VOP_MKNOD(dvp, &vp, cnp, ap->a_vap); if (error) return (error); if (vp != NULLVP) { error = union_allocvp(ap->a_vpp, mp, NULLVP, NULLVP, cnp, vp, NULLVP, 1); if (error) vput(vp); } return (error); } vput(ap->a_dvp); return (EROFS); } static int union_open(ap) struct vop_open_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct union_node *un = VTOUNION(ap->a_vp); struct vnode *tvp; int mode = ap->a_mode; struct ucred *cred = ap->a_cred; struct proc *p = ap->a_p; int error; /* * If there is an existing upper vp then simply open that. */ tvp = un->un_uppervp; if (tvp == NULLVP) { /* * If the lower vnode is being opened for writing, then * copy the file contents to the upper vnode and open that, * otherwise can simply open the lower vnode. */ tvp = un->un_lowervp; if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) { error = union_copyup(un, (mode&O_TRUNC) == 0, cred, p); if (error == 0) error = VOP_OPEN(un->un_uppervp, mode, cred, p); return (error); } /* * Just open the lower vnode */ un->un_openl++; vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_OPEN(tvp, mode, cred, p); VOP_UNLOCK(tvp, 0, p); return (error); } FIXUP(un, p); error = VOP_OPEN(tvp, mode, cred, p); return (error); } static int union_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct union_node *un = VTOUNION(ap->a_vp); struct vnode *vp; if ((vp = un->un_uppervp) == NULLVP) { #ifdef UNION_DIAGNOSTIC if (un->un_openl <= 0) panic("union: un_openl cnt"); #endif --un->un_openl; vp = un->un_lowervp; } ap->a_vp = vp; return (VCALL(vp, VOFFSET(vop_close), ap)); } /* * Check access permission on the union vnode. * The access check being enforced is to check * against both the underlying vnode, and any * copied vnode. This ensures that no additional * file permissions are given away simply because * the user caused an implicit file copy. */ static int union_access(ap) struct vop_access_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct union_node *un = VTOUNION(ap->a_vp); struct proc *p = ap->a_p; int error = EACCES; struct vnode *vp; struct vnode *savedvp; /* * Disallow write attempts on filesystems mounted read-only. */ if (ap->a_mode & VWRITE && (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)) { switch (ap->a_vp->v_type) { case VREG: case VDIR: case VLNK: return (EROFS); } } if ((vp = un->un_uppervp) != NULLVP) { FIXUP(un, p); ap->a_vp = vp; return (VCALL(vp, VOFFSET(vop_access), ap)); } if ((vp = un->un_lowervp) != NULLVP) { vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); savedvp = ap->a_vp; ap->a_vp = vp; error = VCALL(vp, VOFFSET(vop_access), ap); if (error == 0) { struct union_mount *um = MOUNTTOUNIONMOUNT(savedvp->v_mount); if (um->um_op == UNMNT_BELOW) { ap->a_cred = um->um_cred; error = VCALL(vp, VOFFSET(vop_access), ap); } } VOP_UNLOCK(vp, 0, p); if (error) return (error); } return (error); } /* * We handle getattr only to change the fsid and * track object sizes */ static int union_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { int error; struct union_node *un = VTOUNION(ap->a_vp); struct vnode *vp = un->un_uppervp; struct proc *p = ap->a_p; struct vattr *vap; struct vattr va; /* * Some programs walk the filesystem hierarchy by counting * links to directories to avoid stat'ing all the time. * This means the link count on directories needs to be "correct". * The only way to do that is to call getattr on both layers * and fix up the link count. The link count will not necessarily * be accurate but will be large enough to defeat the tree walkers. */ vap = ap->a_vap; vp = un->un_uppervp; if (vp != NULLVP) { /* * It's not clear whether VOP_GETATTR is to be * called with the vnode locked or not. stat() calls * it with (vp) locked, and fstat calls it with * (vp) unlocked. * In the mean time, compensate here by checking * the union_node's lock flag. */ if (un->un_flags & UN_LOCKED) FIXUP(un, p); error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); if (error) return (error); union_newsize(ap->a_vp, vap->va_size, VNOVAL); } if (vp == NULLVP) { vp = un->un_lowervp; } else if (vp->v_type == VDIR) { vp = un->un_lowervp; vap = &va; } else { vp = NULLVP; } if (vp != NULLVP) { error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); if (error) return (error); union_newsize(ap->a_vp, VNOVAL, vap->va_size); } if ((vap != ap->a_vap) && (vap->va_type == VDIR)) ap->a_vap->va_nlink += vap->va_nlink; ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; return (0); } static int union_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct union_node *un = VTOUNION(ap->a_vp); struct proc *p = ap->a_p; struct vattr *vap = ap->a_vap; int error; /* * Disallow write attempts on filesystems mounted read-only. */ if ((ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) && (vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL)) return (EROFS); /* * Handle case of truncating lower object to zero size, * by creating a zero length upper object. This is to * handle the case of open with O_TRUNC and O_CREAT. */ if ((un->un_uppervp == NULLVP) && /* assert(un->un_lowervp != NULLVP) */ (un->un_lowervp->v_type == VREG)) { error = union_copyup(un, (ap->a_vap->va_size != 0), ap->a_cred, ap->a_p); if (error) return (error); } /* * Try to set attributes in upper layer, * otherwise return read-only filesystem error. */ if (un->un_uppervp != NULLVP) { FIXUP(un, p); error = VOP_SETATTR(un->un_uppervp, ap->a_vap, ap->a_cred, ap->a_p); if ((error == 0) && (ap->a_vap->va_size != VNOVAL)) union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL); } else { error = EROFS; } return (error); } static int union_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int error; struct proc *p = ap->a_uio->uio_procp; struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); else FIXUP(VTOUNION(ap->a_vp), p); error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred); if (dolock) VOP_UNLOCK(vp, 0, p); /* * XXX * perhaps the size of the underlying object has changed under * our feet. take advantage of the offset information present * in the uio structure. */ if (error == 0) { struct union_node *un = VTOUNION(ap->a_vp); off_t cur = ap->a_uio->uio_offset; if (vp == un->un_uppervp) { if (cur > un->un_uppersz) union_newsize(ap->a_vp, cur, VNOVAL); } else { if (cur > un->un_lowersz) union_newsize(ap->a_vp, VNOVAL, cur); } } return (error); } static int union_write(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int error; struct vnode *vp; struct union_node *un = VTOUNION(ap->a_vp); struct proc *p = ap->a_uio->uio_procp; vp = UPPERVP(ap->a_vp); if (vp == NULLVP) panic("union: missing upper layer in write"); FIXUP(un, p); error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred); /* * the size of the underlying object may be changed by the * write. */ if (error == 0) { off_t cur = ap->a_uio->uio_offset; if (cur > un->un_uppersz) union_newsize(ap->a_vp, cur, VNOVAL); } return (error); } static int union_lease(ap) struct vop_lease_args /* { struct vnode *a_vp; struct proc *a_p; struct ucred *a_cred; int a_flag; } */ *ap; { register struct vnode *ovp = OTHERVP(ap->a_vp); ap->a_vp = ovp; return (VCALL(ovp, VOFFSET(vop_lease), ap)); } static int union_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *ovp = OTHERVP(ap->a_vp); ap->a_vp = ovp; return (VCALL(ovp, VOFFSET(vop_ioctl), ap)); } static int -union_select(ap) - struct vop_select_args /* { +union_poll(ap) + struct vop_poll_args /* { struct vnode *a_vp; - int a_which; - int a_fflags; + int a_events; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *ovp = OTHERVP(ap->a_vp); ap->a_vp = ovp; - return (VCALL(ovp, VOFFSET(vop_select), ap)); + return (VCALL(ovp, VOFFSET(vop_poll), ap)); } static int union_revoke(ap) struct vop_revoke_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; if (UPPERVP(vp)) VOP_REVOKE(UPPERVP(vp), ap->a_flags); if (LOWERVP(vp)) VOP_REVOKE(LOWERVP(vp), ap->a_flags); vgone(vp); return (0); } static int union_mmap(ap) struct vop_mmap_args /* { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *ovp = OTHERVP(ap->a_vp); ap->a_vp = ovp; return (VCALL(ovp, VOFFSET(vop_mmap), ap)); } static int union_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { int error = 0; struct proc *p = ap->a_p; struct vnode *targetvp = OTHERVP(ap->a_vp); struct union_node *un; int isupperlocked = 0; if (targetvp != NULLVP) { int dolock = (targetvp == LOWERVP(ap->a_vp)); un = VTOUNION(ap->a_vp); if (dolock) vn_lock(targetvp, LK_EXCLUSIVE | LK_RETRY, p); else if ((un->un_flags & UN_ULOCK) == 0 && VOP_ISLOCKED(targetvp) == 0) { isupperlocked = 1; vn_lock(targetvp, LK_EXCLUSIVE | LK_RETRY, p); un->un_flags |= UN_ULOCK; } error = VOP_FSYNC(targetvp, ap->a_cred, ap->a_waitfor, p); if (dolock) VOP_UNLOCK(targetvp, 0, p); else if (isupperlocked) { un->un_flags &= ~UN_ULOCK; VOP_UNLOCK(targetvp, 0, p); } } return (error); } static int union_seek(ap) struct vop_seek_args /* { struct vnode *a_vp; off_t a_oldoff; off_t a_newoff; struct ucred *a_cred; } */ *ap; { register struct vnode *ovp = OTHERVP(ap->a_vp); ap->a_vp = ovp; return (VCALL(ovp, VOFFSET(vop_seek), ap)); } static int union_remove(ap) struct vop_remove_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { int error; struct union_node *dun = VTOUNION(ap->a_dvp); struct union_node *un = VTOUNION(ap->a_vp); struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (dun->un_uppervp == NULLVP) panic("union remove: null upper vnode"); if (un->un_uppervp != NULLVP) { struct vnode *dvp = dun->un_uppervp; struct vnode *vp = un->un_uppervp; FIXUP(dun, p); VREF(dvp); SETKLOCK(dun); vput(ap->a_dvp); CLEARKLOCK(dun); FIXUP(un, p); VREF(vp); SETKLOCK(un); vput(ap->a_vp); CLEARKLOCK(un); if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc)) cnp->cn_flags |= DOWHITEOUT; error = VOP_REMOVE(dvp, vp, cnp); if (!error) union_removed_upper(un); } else { FIXUP(dun, p); error = union_mkwhiteout( MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount), dun->un_uppervp, ap->a_cnp, un->un_path); vput(ap->a_dvp); vput(ap->a_vp); } return (error); } static int union_link(ap) struct vop_link_args /* { struct vnode *a_tdvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { int error = 0; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; struct union_node *un; struct vnode *vp; struct vnode *tdvp; un = VTOUNION(ap->a_tdvp); if (ap->a_tdvp->v_op != ap->a_vp->v_op) { vp = ap->a_vp; } else { struct union_node *tun = VTOUNION(ap->a_vp); if (tun->un_uppervp == NULLVP) { vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY, p); if (un->un_uppervp == tun->un_dirvp) { un->un_flags &= ~UN_ULOCK; VOP_UNLOCK(un->un_uppervp, 0, p); } error = union_copyup(tun, 1, cnp->cn_cred, p); if (un->un_uppervp == tun->un_dirvp) { vn_lock(un->un_uppervp, LK_EXCLUSIVE | LK_RETRY, p); un->un_flags |= UN_ULOCK; } VOP_UNLOCK(ap->a_vp, 0, p); } vp = tun->un_uppervp; } tdvp = un->un_uppervp; if (tdvp == NULLVP) error = EROFS; if (error) { vput(ap->a_tdvp); return (error); } FIXUP(un, p); VREF(tdvp); SETKLOCK(un); vput(ap->a_tdvp); CLEARKLOCK(un); return (VOP_LINK(tdvp, vp, cnp)); } static int union_rename(ap) struct vop_rename_args /* { struct vnode *a_fdvp; struct vnode *a_fvp; struct componentname *a_fcnp; struct vnode *a_tdvp; struct vnode *a_tvp; struct componentname *a_tcnp; } */ *ap; { int error; struct vnode *fdvp = ap->a_fdvp; struct vnode *fvp = ap->a_fvp; struct vnode *tdvp = ap->a_tdvp; struct vnode *tvp = ap->a_tvp; int isklockset = 0; if (fdvp->v_op == union_vnodeop_p) { /* always true */ struct union_node *un = VTOUNION(fdvp); if (un->un_uppervp == NULLVP) { /* * this should never happen in normal * operation but might if there was * a problem creating the top-level shadow * directory. */ error = EXDEV; goto bad; } fdvp = un->un_uppervp; VREF(fdvp); vrele(ap->a_fdvp); } if (fvp->v_op == union_vnodeop_p) { /* always true */ struct union_node *un = VTOUNION(fvp); if (un->un_uppervp == NULLVP) { /* XXX: should do a copyup */ error = EXDEV; goto bad; } if (un->un_lowervp != NULLVP) ap->a_fcnp->cn_flags |= DOWHITEOUT; fvp = un->un_uppervp; VREF(fvp); vrele(ap->a_fvp); } if (tdvp->v_op == union_vnodeop_p) { struct union_node *un = VTOUNION(tdvp); if (un->un_uppervp == NULLVP) { /* * this should never happen in normal * operation but might if there was * a problem creating the top-level shadow * directory. */ error = EXDEV; goto bad; } tdvp = un->un_uppervp; VREF(tdvp); SETKLOCK(un); vput(ap->a_tdvp); CLEARKLOCK(un); } if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) { struct union_node *un = VTOUNION(tvp); tvp = un->un_uppervp; if (tvp != NULLVP) { VREF(tvp); SETKLOCK(un); isklockset = 1; } vput(ap->a_tvp); if (isklockset) CLEARKLOCK(un); } return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp)); bad: vrele(fdvp); vrele(fvp); vput(tdvp); if (tvp != NULLVP) vput(tvp); return (error); } static int union_mkdir(ap) struct vop_mkdir_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct union_node *un = VTOUNION(ap->a_dvp); struct vnode *dvp = un->un_uppervp; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (dvp != NULLVP) { int error; struct vnode *vp; FIXUP(un, p); VREF(dvp); SETKLOCK(un); VOP_UNLOCK(ap->a_dvp, 0, p); CLEARKLOCK(un); error = VOP_MKDIR(dvp, &vp, cnp, ap->a_vap); if (error) { vrele(ap->a_dvp); return (error); } error = union_allocvp(ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp, NULLVP, cnp, vp, NULLVP, 1); vrele(ap->a_dvp); if (error) vput(vp); return (error); } vput(ap->a_dvp); return (EROFS); } static int union_rmdir(ap) struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { int error; struct union_node *dun = VTOUNION(ap->a_dvp); struct union_node *un = VTOUNION(ap->a_vp); struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (dun->un_uppervp == NULLVP) panic("union rmdir: null upper vnode"); if (un->un_uppervp != NULLVP) { struct vnode *dvp = dun->un_uppervp; struct vnode *vp = un->un_uppervp; FIXUP(dun, p); VREF(dvp); SETKLOCK(dun); vput(ap->a_dvp); CLEARKLOCK(dun); FIXUP(un, p); VREF(vp); SETKLOCK(un); vput(ap->a_vp); CLEARKLOCK(un); if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc)) cnp->cn_flags |= DOWHITEOUT; error = VOP_RMDIR(dvp, vp, ap->a_cnp); if (!error) union_removed_upper(un); } else { FIXUP(dun, p); error = union_mkwhiteout( MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount), dun->un_uppervp, ap->a_cnp, un->un_path); vput(ap->a_dvp); vput(ap->a_vp); } return (error); } static int union_symlink(ap) struct vop_symlink_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; char *a_target; } */ *ap; { struct union_node *un = VTOUNION(ap->a_dvp); struct vnode *dvp = un->un_uppervp; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; if (dvp != NULLVP) { int error; struct vnode *vp; FIXUP(un, p); VREF(dvp); SETKLOCK(un); vput(ap->a_dvp); CLEARKLOCK(un); error = VOP_SYMLINK(dvp, &vp, cnp, ap->a_vap, ap->a_target); *ap->a_vpp = NULLVP; return (error); } vput(ap->a_dvp); return (EROFS); } /* * union_readdir works in concert with getdirentries and * readdir(3) to provide a list of entries in the unioned * directories. getdirentries is responsible for walking * down the union stack. readdir(3) is responsible for * eliminating duplicate names from the returned data stream. */ static int union_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_long *a_cookies; int a_ncookies; } */ *ap; { struct union_node *un = VTOUNION(ap->a_vp); struct vnode *uvp = un->un_uppervp; struct proc *p = ap->a_uio->uio_procp; if (uvp == NULLVP) return (0); FIXUP(un, p); ap->a_vp = uvp; return (VCALL(uvp, VOFFSET(vop_readdir), ap)); } static int union_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { int error; struct uio *uio = ap->a_uio; struct proc *p = uio->uio_procp; struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); else FIXUP(VTOUNION(ap->a_vp), p); ap->a_vp = vp; error = VCALL(vp, VOFFSET(vop_readlink), ap); if (dolock) VOP_UNLOCK(vp, 0, p); return (error); } static int union_abortop(ap) struct vop_abortop_args /* { struct vnode *a_dvp; struct componentname *a_cnp; } */ *ap; { int error; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; struct vnode *vp = OTHERVP(ap->a_dvp); struct union_node *un = VTOUNION(ap->a_dvp); int islocked = un->un_flags & UN_LOCKED; int dolock = (vp == LOWERVP(ap->a_dvp)); if (islocked) { if (dolock) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); else FIXUP(VTOUNION(ap->a_dvp), p); } ap->a_dvp = vp; error = VCALL(vp, VOFFSET(vop_abortop), ap); if (islocked && dolock) VOP_UNLOCK(vp, 0, p); return (error); } static int union_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct proc *p = ap->a_p; struct union_node *un = VTOUNION(vp); struct vnode **vpp; /* * Do nothing (and _don't_ bypass). * Wait to vrele lowervp until reclaim, * so that until then our union_node is in the * cache and reusable. * * NEEDSWORK: Someday, consider inactive'ing * the lowervp and then trying to reactivate it * with capabilities (v_id) * like they do in the name lookup cache code. * That's too much work for now. */ if (un->un_dircache != 0) { for (vpp = un->un_dircache; *vpp != NULLVP; vpp++) vrele(*vpp); free(un->un_dircache, M_TEMP); un->un_dircache = 0; } VOP_UNLOCK(vp, 0, p); if ((un->un_flags & UN_CACHED) == 0) vgone(vp); return (0); } static int union_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { union_freevp(ap->a_vp); return (0); } static int union_lock(ap) struct vop_lock_args *ap; { struct vnode *vp = ap->a_vp; struct proc *p = ap->a_p; int flags = ap->a_flags; struct union_node *un; int error; vop_nolock(ap); /* * Need to do real lockmgr-style locking here. * in the mean time, draining won't work quite right, * which could lead to a few race conditions. * the following test was here, but is not quite right, we * still need to take the lock: if ((flags & LK_TYPE_MASK) == LK_DRAIN) return (0); */ flags &= ~LK_INTERLOCK; start: un = VTOUNION(vp); if (un->un_uppervp != NULLVP) { if (((un->un_flags & (UN_ULOCK | UN_KLOCK)) == 0) && (vp->v_usecount != 0)) { error = vn_lock(un->un_uppervp, flags, p); if (error) return (error); un->un_flags |= UN_ULOCK; } } if (un->un_flags & UN_LOCKED) { #ifdef DIAGNOSTIC if (curproc && un->un_pid == curproc->p_pid && un->un_pid > -1 && curproc->p_pid > -1) panic("union: locking against myself"); #endif un->un_flags |= UN_WANT; tsleep((caddr_t)&un->un_flags, PINOD, "unionlk2", 0); goto start; } #ifdef DIAGNOSTIC if (curproc) un->un_pid = curproc->p_pid; else un->un_pid = -1; #endif un->un_flags |= UN_LOCKED; return (0); } /* * When operations want to vput() a union node yet retain a lock on * the upper vnode (say, to do some further operations like link(), * mkdir(), ...), they set UN_KLOCK on the union node, then call * vput() which calls VOP_UNLOCK() and comes here. union_unlock() * unlocks the union node (leaving the upper vnode alone), clears the * KLOCK flag, and then returns to vput(). The caller then does whatever * is left to do with the upper vnode, and ensures that it gets unlocked. * * If UN_KLOCK isn't set, then the upper vnode is unlocked here. */ static int union_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct union_node *un = VTOUNION(ap->a_vp); struct proc *p = ap->a_p; #ifdef DIAGNOSTIC if ((un->un_flags & UN_LOCKED) == 0) panic("union: unlock unlocked node"); if (curproc && un->un_pid != curproc->p_pid && curproc->p_pid > -1 && un->un_pid > -1) panic("union: unlocking other process's union node"); #endif un->un_flags &= ~UN_LOCKED; if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK) VOP_UNLOCK(un->un_uppervp, 0, p); un->un_flags &= ~UN_ULOCK; if (un->un_flags & UN_WANT) { un->un_flags &= ~UN_WANT; wakeup((caddr_t) &un->un_flags); } #ifdef DIAGNOSTIC un->un_pid = 0; #endif vop_nounlock(ap); return (0); } static int union_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ *ap; { int error; struct proc *p = curproc; /* XXX */ struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); else FIXUP(VTOUNION(ap->a_vp), p); ap->a_vp = vp; error = VCALL(vp, VOFFSET(vop_bmap), ap); if (dolock) VOP_UNLOCK(vp, 0, p); return (error); } static int union_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; printf("\ttag VT_UNION, vp=%p, uppervp=%p, lowervp=%p\n", vp, UPPERVP(vp), LOWERVP(vp)); if (UPPERVP(vp) != NULLVP) vprint("union: upper", UPPERVP(vp)); if (LOWERVP(vp) != NULLVP) vprint("union: lower", LOWERVP(vp)); return (0); } static int union_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0); } static int union_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { int error; struct proc *p = curproc; /* XXX */ struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); else FIXUP(VTOUNION(ap->a_vp), p); ap->a_vp = vp; error = VCALL(vp, VOFFSET(vop_pathconf), ap); if (dolock) VOP_UNLOCK(vp, 0, p); return (error); } static int union_advlock(ap) struct vop_advlock_args /* { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ *ap; { register struct vnode *ovp = OTHERVP(ap->a_vp); ap->a_vp = ovp; return (VCALL(ovp, VOFFSET(vop_advlock), ap)); } /* * XXX - vop_strategy must be hand coded because it has no * vnode in its arguments. * This goes away with a merged VM/buffer cache. */ static int union_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *ap; { struct buf *bp = ap->a_bp; int error; struct vnode *savedvp; savedvp = bp->b_vp; bp->b_vp = OTHERVP(bp->b_vp); #ifdef DIAGNOSTIC if (bp->b_vp == NULLVP) panic("union_strategy: nil vp"); if (((bp->b_flags & B_READ) == 0) && (bp->b_vp == LOWERVP(savedvp))) panic("union_strategy: writing to lowervp"); #endif error = VOP_STRATEGY(bp); bp->b_vp = savedvp; return (error); } /* * Global vfs data structures */ vop_t **union_vnodeop_p; static struct vnodeopv_entry_desc union_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)union_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)union_create }, /* create */ { &vop_whiteout_desc, (vop_t *)union_whiteout }, /* whiteout */ { &vop_mknod_desc, (vop_t *)union_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)union_open }, /* open */ { &vop_close_desc, (vop_t *)union_close }, /* close */ { &vop_access_desc, (vop_t *)union_access }, /* access */ { &vop_getattr_desc, (vop_t *)union_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)union_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)union_read }, /* read */ { &vop_write_desc, (vop_t *)union_write }, /* write */ { &vop_lease_desc, (vop_t *)union_lease }, /* lease */ { &vop_ioctl_desc, (vop_t *)union_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)union_select }, /* select */ + { &vop_poll_desc, (vop_t *)union_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)union_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)union_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)union_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)union_seek }, /* seek */ { &vop_remove_desc, (vop_t *)union_remove }, /* remove */ { &vop_link_desc, (vop_t *)union_link }, /* link */ { &vop_rename_desc, (vop_t *)union_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)union_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)union_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)union_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)union_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)union_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)union_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)union_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)union_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)union_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)union_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)union_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)union_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)union_print }, /* print */ { &vop_islocked_desc, (vop_t *)union_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)union_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)union_advlock }, /* advlock */ #ifdef notdef { &vop_blkatoff_desc, (vop_t *)union_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)union_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)union_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)union_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)union_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)union_bwrite }, /* bwrite */ #endif { NULL, NULL } }; static struct vnodeopv_desc union_vnodeop_opv_desc = { &union_vnodeop_p, union_vnodeop_entries }; VNODEOP_SET(union_vnodeop_opv_desc); diff --git a/sys/msdosfs/msdosfs_vnops.c b/sys/msdosfs/msdosfs_vnops.c index e1884c81a5fb..f4ce26a52278 100644 --- a/sys/msdosfs/msdosfs_vnops.c +++ b/sys/msdosfs/msdosfs_vnops.c @@ -1,2032 +1,2043 @@ -/* $Id: msdosfs_vnops.c,v 1.42 1997/05/17 18:32:40 phk Exp $ */ +/* $Id: msdosfs_vnops.c,v 1.43 1997/08/26 07:32:39 phk Exp $ */ /* $NetBSD: msdosfs_vnops.c,v 1.20 1994/08/21 18:44:13 ws Exp $ */ /*- * Copyright (C) 1994 Wolfgang Solfrank. * Copyright (C) 1994 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * * 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 TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. */ /* * Written by Paul Popelka (paulp@uts.amdahl.com) * * You can do anything you want with this software, just don't say you wrote * it, and don't remove this notice. * * This software is provided "as is". * * The author supplies this software to be publicly redistributed on the * understanding that the author is not responsible for the correct * functioning of this software in any circumstances and is not liable for * any damages caused by this software. * * October 1992 */ #include #include #include #include /* defines plimit structure in proc struct */ #include #include #include #include #include #include #include +#include #include /* XXX */ /* defines v_rdev */ #include #include #include #include #include #include #include #include #include #include /* * Prototypes for MSDOSFS vnode operations */ static int msdosfs_create __P((struct vop_create_args *)); static int msdosfs_mknod __P((struct vop_mknod_args *)); static int msdosfs_open __P((struct vop_open_args *)); static int msdosfs_close __P((struct vop_close_args *)); static int msdosfs_access __P((struct vop_access_args *)); static int msdosfs_getattr __P((struct vop_getattr_args *)); static int msdosfs_setattr __P((struct vop_setattr_args *)); static int msdosfs_read __P((struct vop_read_args *)); static int msdosfs_write __P((struct vop_write_args *)); static int msdosfs_ioctl __P((struct vop_ioctl_args *)); -static int msdosfs_select __P((struct vop_select_args *)); +static int msdosfs_poll __P((struct vop_poll_args *)); static int msdosfs_mmap __P((struct vop_mmap_args *)); static int msdosfs_fsync __P((struct vop_fsync_args *)); static int msdosfs_seek __P((struct vop_seek_args *)); static int msdosfs_remove __P((struct vop_remove_args *)); static int msdosfs_link __P((struct vop_link_args *)); static int msdosfs_rename __P((struct vop_rename_args *)); static int msdosfs_mkdir __P((struct vop_mkdir_args *)); static int msdosfs_rmdir __P((struct vop_rmdir_args *)); static int msdosfs_symlink __P((struct vop_symlink_args *)); static int msdosfs_readdir __P((struct vop_readdir_args *)); static int msdosfs_readlink __P((struct vop_readlink_args *)); static int msdosfs_abortop __P((struct vop_abortop_args *)); static int msdosfs_lock __P((struct vop_lock_args *)); static int msdosfs_unlock __P((struct vop_unlock_args *)); static int msdosfs_bmap __P((struct vop_bmap_args *)); static int msdosfs_strategy __P((struct vop_strategy_args *)); static int msdosfs_print __P((struct vop_print_args *)); static int msdosfs_islocked __P((struct vop_islocked_args *)); static int msdosfs_advlock __P((struct vop_advlock_args *)); static int msdosfs_pathconf __P((struct vop_pathconf_args *ap)); static int msdosfs_reallocblks __P((struct vop_reallocblks_args *)); /* * Some general notes: * * In the ufs filesystem the inodes, superblocks, and indirect blocks are * read/written using the vnode for the filesystem. Blocks that represent * the contents of a file are read/written using the vnode for the file * (including directories when they are read/written as files). This * presents problems for the dos filesystem because data that should be in * an inode (if dos had them) resides in the directory itself. Since we * must update directory entries without the benefit of having the vnode * for the directory we must use the vnode for the filesystem. This means * that when a directory is actually read/written (via read, write, or * readdir, or seek) we must use the vnode for the filesystem instead of * the vnode for the directory as would happen in ufs. This is to insure we * retreive the correct block from the buffer cache since the hash value is * based upon the vnode address and the desired block number. */ /* * Create a regular file. On entry the directory to contain the file being * created is locked. We must release before we return. We must also free * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or * only if the SAVESTART bit in cn_flags is clear on success. */ static int msdosfs_create(ap) struct vop_create_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct componentname *cnp = ap->a_cnp; struct denode ndirent; struct denode *dep; struct denode *pdep = VTODE(ap->a_dvp); struct timespec ts; int error; #ifdef MSDOSFS_DEBUG printf("msdosfs_create(cnp %08x, vap %08x\n", cnp, ap->a_vap); #endif /* * Create a directory entry for the file, then call createde() to * have it installed. NOTE: DOS files are always executable. We * use the absence of the owner write bit to make the file * readonly. */ #ifdef DIAGNOSTIC if ((cnp->cn_flags & SAVENAME) == 0) panic("msdosfs_create: no name"); #endif bzero(&ndirent, sizeof(ndirent)); TIMEVAL_TO_TIMESPEC(&time, &ts); unix2dostime(&ts, &ndirent.de_Date, &ndirent.de_Time); unix2dosfn((u_char *)cnp->cn_nameptr, ndirent.de_Name, cnp->cn_namelen); ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) ? ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY; ndirent.de_StartCluster = 0; ndirent.de_FileSize = 0; ndirent.de_dev = pdep->de_dev; ndirent.de_devvp = pdep->de_devvp; if ((error = createde(&ndirent, pdep, &dep)) == 0) { *ap->a_vpp = DETOV(dep); if ((cnp->cn_flags & SAVESTART) == 0) free(cnp->cn_pnbuf, M_NAMEI); } else { free(cnp->cn_pnbuf, M_NAMEI); } vput(ap->a_dvp); /* release parent dir */ return error; } static int msdosfs_mknod(ap) struct vop_mknod_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { int error; switch (ap->a_vap->va_type) { case VDIR: error = msdosfs_mkdir((struct vop_mkdir_args *)ap); break; case VREG: error = msdosfs_create((struct vop_create_args *)ap); break; default: error = EINVAL; free(ap->a_cnp->cn_pnbuf, M_NAMEI); vput(ap->a_dvp); break; } return error; } static int msdosfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return 0; } static int msdosfs_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(vp); simple_lock(&vp->v_interlock); if (vp->v_usecount > 1) DE_TIMES(dep, &time); simple_unlock(&vp->v_interlock); return 0; } static int msdosfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; struct ucred *cred = ap->a_cred; mode_t mask, file_mode, mode = ap->a_mode; register gid_t *gp; int i; file_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); file_mode &= pmp->pm_mask; /* * Disallow write attempts on read-only file systems; * unless the file is a socket, fifo, or a block or * character device resident on the file system. */ if (mode & VWRITE) { switch (vp->v_type) { case VDIR: case VLNK: case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); break; } } /* User id 0 always gets access. */ if (cred->cr_uid == 0) return 0; mask = 0; /* Otherwise, check the owner. */ if (cred->cr_uid == pmp->pm_uid) { if (mode & VEXEC) mask |= S_IXUSR; if (mode & VREAD) mask |= S_IRUSR; if (mode & VWRITE) mask |= S_IWUSR; return (file_mode & mask) == mask ? 0 : EACCES; } /* Otherwise, check the groups. */ for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) if (pmp->pm_gid == *gp) { if (mode & VEXEC) mask |= S_IXGRP; if (mode & VREAD) mask |= S_IRGRP; if (mode & VWRITE) mask |= S_IWGRP; return (file_mode & mask) == mask ? 0 : EACCES; } /* Otherwise, check everyone else. */ if (mode & VEXEC) mask |= S_IXOTH; if (mode & VREAD) mask |= S_IROTH; if (mode & VWRITE) mask |= S_IWOTH; return (file_mode & mask) == mask ? 0 : EACCES; } static int msdosfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { u_int cn; struct denode *dep = VTODE(ap->a_vp); struct vattr *vap = ap->a_vap; DE_TIMES(dep, &time); vap->va_fsid = dep->de_dev; /* * The following computation of the fileid must be the same as that * used in msdosfs_readdir() to compute d_fileno. If not, pwd * doesn't work. */ if (dep->de_Attributes & ATTR_DIRECTORY) { if ((cn = dep->de_StartCluster) == MSDOSFSROOT) cn = 1; } else { if ((cn = dep->de_dirclust) == MSDOSFSROOT) cn = 1; cn = (cn << 16) | (dep->de_diroffset & 0xffff); } vap->va_fileid = cn; vap->va_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); vap->va_mode &= dep->de_pmp->pm_mask; if (dep->de_Attributes & ATTR_DIRECTORY) vap->va_mode |= S_IFDIR; vap->va_nlink = 1; vap->va_gid = dep->de_pmp->pm_gid; vap->va_uid = dep->de_pmp->pm_uid; vap->va_rdev = 0; vap->va_size = dep->de_FileSize; dos2unixtime(dep->de_Date, dep->de_Time, &vap->va_atime); vap->va_mtime = vap->va_atime; #if 0 #ifndef MSDOSFS_NODIRMOD if (vap->va_mode & S_IFDIR) TIMEVAL_TO_TIMESPEC(&time, &vap->va_mtime); #endif #endif vap->va_ctime = vap->va_atime; vap->va_flags = (dep->de_Attributes & ATTR_ARCHIVE) ? 0 : SF_ARCHIVED; vap->va_gen = 0; vap->va_blocksize = dep->de_pmp->pm_bpcluster; vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) & ~(dep->de_pmp->pm_crbomask); vap->va_type = ap->a_vp->v_type; vap->va_filerev = dep->de_modrev; return 0; } static int msdosfs_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(ap->a_vp); struct vattr *vap = ap->a_vap; struct ucred *cred = ap->a_cred; int error = 0; /* * Check for unsettable attributes. */ if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { return (EINVAL); } if (vap->va_flags != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != dep->de_pmp->pm_uid && (error = suser(cred, &ap->a_p->p_acflag))) return (error); /* * We are very inconsistent about handling unsupported * attributes. We ignored the the access time and the * read and execute bits. We were strict for the other * attributes. * * Here we are strict, stricter than ufs in not allowing * users to attempt to set SF_SETTABLE bits or anyone to * set unsupported bits. However, we ignore attempts to * set ATTR_ARCHIVE for directories `cp -pr' from a more * sensible file system attempts it a lot. */ if (cred->cr_uid != 0) { if (vap->va_flags & SF_SETTABLE) return EPERM; } if (vap->va_flags & ~SF_ARCHIVED) return EINVAL; if (vap->va_flags & SF_ARCHIVED) dep->de_Attributes &= ~ATTR_ARCHIVE; else if (!(dep->de_Attributes & ATTR_DIRECTORY)) dep->de_Attributes |= ATTR_ARCHIVE; dep->de_flag |= DE_MODIFIED; } if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (uid_t)VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if ((cred->cr_uid != dep->de_pmp->pm_uid || vap->va_uid != dep->de_pmp->pm_uid || (vap->va_gid != dep->de_pmp->pm_gid && !groupmember(vap->va_gid, cred))) && (error = suser(cred, &ap->a_p->p_acflag))) return error; if (vap->va_uid != dep->de_pmp->pm_uid || vap->va_gid != dep->de_pmp->pm_gid) return EINVAL; } if (vap->va_size != VNOVAL) { /* * Disallow write attempts on read-only file systems; * unless the file is a socket, fifo, or a block or * character device resident on the file system. */ switch (vp->v_type) { case VDIR: return (EISDIR); case VLNK: case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); break; } error = detrunc(dep, vap->va_size, 0, cred, ap->a_p); if (error) return error; } if (vap->va_mtime.tv_sec != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != dep->de_pmp->pm_uid && (error = suser(cred, &ap->a_p->p_acflag)) && ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || (error = VOP_ACCESS(vp, VWRITE, cred, ap->a_p)))) return error; dep->de_flag |= DE_UPDATE; error = deupdat(dep, &vap->va_mtime, 1); if (error) return error; } /* * DOS files only have the ability to have their writability * attribute set, so we use the owner write bit to set the readonly * attribute. */ error = 0; if (vap->va_mode != (u_short) VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != dep->de_pmp->pm_uid && (error = suser(cred, &ap->a_p->p_acflag))) return error; /* We ignore the read and execute bits */ if (vap->va_mode & VWRITE) dep->de_Attributes &= ~ATTR_READONLY; else dep->de_Attributes |= ATTR_READONLY; dep->de_flag |= DE_MODIFIED; } return error; } static int msdosfs_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int error = 0; int diff; int isadir; long n; long on; daddr_t lbn; daddr_t rablock; int rasize; struct buf *bp; struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(vp); struct msdosfsmount *pmp = dep->de_pmp; struct uio *uio = ap->a_uio; /* * If they didn't ask for any data, then we are done. */ if (uio->uio_resid == 0) return 0; if (uio->uio_offset < 0) return EINVAL; isadir = dep->de_Attributes & ATTR_DIRECTORY; do { lbn = uio->uio_offset >> pmp->pm_cnshift; on = uio->uio_offset & pmp->pm_crbomask; n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid); diff = dep->de_FileSize - uio->uio_offset; if (diff <= 0) return 0; /* convert cluster # to block # if a directory */ if (isadir) { error = pcbmap(dep, lbn, &lbn, 0); if (error) return error; } if (diff < n) n = diff; /* * If we are operating on a directory file then be sure to * do i/o with the vnode for the filesystem instead of the * vnode for the directory. */ if (isadir) { error = bread(pmp->pm_devvp, lbn, pmp->pm_bpcluster, NOCRED, &bp); } else { rablock = lbn + 1; #ifdef PC98 /* * 1024byte/sector support */ if (pmp->pm_BytesPerSec == 1024) vp->v_flag |= 0x10000; #endif if (vp->v_lastr + 1 == lbn && rablock * pmp->pm_bpcluster < dep->de_FileSize) { rasize = pmp->pm_bpcluster; error = breadn(vp, lbn, pmp->pm_bpcluster, &rablock, &rasize, 1, NOCRED, &bp); } else { error = bread(vp, lbn, pmp->pm_bpcluster, NOCRED, &bp); } vp->v_lastr = lbn; } n = min(n, pmp->pm_bpcluster - bp->b_resid); if (error) { brelse(bp); return error; } error = uiomove(bp->b_data + on, (int) n, uio); /* * If we have read everything from this block or have read * to end of file then we are done with this block. Mark * it to say the buffer can be reused if need be. */ #if 0 if (n + on == pmp->pm_bpcluster || uio->uio_offset == dep->de_FileSize) bp->b_flags |= B_AGE; #endif brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); return error; } /* * Write data to a file or directory. */ static int msdosfs_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { int n; int isadir; int croffset; int resid; int osize; int error = 0; u_long count; daddr_t bn, lastcn; struct buf *bp; int ioflag = ap->a_ioflag; struct uio *uio = ap->a_uio; struct proc *p = uio->uio_procp; struct vnode *vp = ap->a_vp; struct vnode *thisvp; struct denode *dep = VTODE(vp); struct msdosfsmount *pmp = dep->de_pmp; struct ucred *cred = ap->a_cred; struct timespec ts; #ifdef MSDOSFS_DEBUG printf("msdosfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n", vp, uio, ioflag, cred); printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n", dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster); #endif switch (vp->v_type) { case VREG: if (ioflag & IO_APPEND) uio->uio_offset = dep->de_FileSize; isadir = 0; thisvp = vp; break; case VDIR: if ((ioflag & IO_SYNC) == 0) panic("msdosfs_write(): non-sync directory update"); isadir = 1; thisvp = pmp->pm_devvp; break; default: panic("msdosfs_write(): bad file type"); break; } if (uio->uio_offset < 0) return EINVAL; if (uio->uio_resid == 0) return 0; /* * If they've exceeded their filesize limit, tell them about it. */ if (vp->v_type == VREG && p && ((uio->uio_offset + uio->uio_resid) > p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) { psignal(p, SIGXFSZ); return EFBIG; } /* * If attempting to write beyond the end of the root directory we * stop that here because the root directory can not grow. */ if ((dep->de_Attributes & ATTR_DIRECTORY) && dep->de_StartCluster == MSDOSFSROOT && (uio->uio_offset + uio->uio_resid) > dep->de_FileSize) return ENOSPC; /* * If the offset we are starting the write at is beyond the end of * the file, then they've done a seek. Unix filesystems allow * files with holes in them, DOS doesn't so we must fill the hole * with zeroed blocks. */ if (uio->uio_offset > dep->de_FileSize) { error = deextend(dep, uio->uio_offset, cred); if (error) return error; } /* * Remember some values in case the write fails. */ resid = uio->uio_resid; osize = dep->de_FileSize; #ifdef PC98 /* * 1024byte/sector support */ if (pmp->pm_BytesPerSec == 1024) thisvp->v_flag |= 0x10000; #endif /* * If we write beyond the end of the file, extend it to its ultimate * size ahead of the time to hopefully get a contiguous area. */ if (uio->uio_offset + resid > osize) { count = de_clcount(pmp, uio->uio_offset + resid) - de_clcount(pmp, osize); if ((error = extendfile(dep, count, NULL, NULL, 0)) && (error != ENOSPC || (ioflag & IO_UNIT))) goto errexit; lastcn = dep->de_fc[FC_LASTFC].fc_frcn; } else lastcn = de_clcount(pmp, osize) - 1; do { bn = de_blk(pmp, uio->uio_offset); if (isadir) { error = pcbmap(dep, bn, &bn, 0); if (error) break; } else if (bn > lastcn) { error = ENOSPC; break; } croffset = uio->uio_offset & pmp->pm_crbomask; n = min(uio->uio_resid, pmp->pm_bpcluster - croffset); if (uio->uio_offset + n > dep->de_FileSize) { dep->de_FileSize = uio->uio_offset + n; /* The object size needs to be set before buffer is allocated */ vnode_pager_setsize(vp, dep->de_FileSize); } if ((uio->uio_offset & pmp->pm_crbomask) == 0 && (de_blk(pmp, uio->uio_offset + uio->uio_resid) > de_blk(pmp, uio->uio_offset) || uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) { /* * If either the whole cluster gets written, * or we write the cluster from its start beyond EOF, * then no need to read data from disk. */ bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0); clrbuf(bp); /* * Do the bmap now, since pcbmap needs buffers * for the fat table. (see msdosfs_strategy) */ if (!isadir) { if (bp->b_blkno == bp->b_lblkno) { error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0); if (error) bp->b_blkno = -1; } if (bp->b_blkno == -1) { brelse(bp); if (!error) error = EIO; /* XXX */ break; } } } else { /* * The block we need to write into exists, so read it in. */ error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp); if (error) break; } /* * Should these vnode_pager_* functions be done on dir * files? */ /* * Copy the data from user space into the buf header. */ error = uiomove(bp->b_data + croffset, n, uio); /* * If they want this synchronous then write it and wait for * it. Otherwise, if on a cluster boundary write it * asynchronously so we can move on to the next block * without delay. Otherwise do a delayed write because we * may want to write somemore into the block later. */ if (ioflag & IO_SYNC) (void) bwrite(bp); else if (n + croffset == pmp->pm_bpcluster) { bawrite(bp); } else bdwrite(bp); dep->de_flag |= DE_UPDATE; } while (error == 0 && uio->uio_resid > 0); /* * If the write failed and they want us to, truncate the file back * to the size it was before the write was attempted. */ errexit: if (error) { if (ioflag & IO_UNIT) { detrunc(dep, osize, ioflag & IO_SYNC, NOCRED, NULL); uio->uio_offset -= resid - uio->uio_resid; uio->uio_resid = resid; } else { detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED, NULL); if (uio->uio_resid != resid) error = 0; } } else if (ioflag & IO_SYNC) { TIMEVAL_TO_TIMESPEC(&time, &ts); error = deupdat(dep, &ts, 1); } return error; } static int msdosfs_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return ENOTTY; } static int -msdosfs_select(ap) - struct vop_select_args /* { +msdosfs_poll(ap) + struct vop_poll_args /* { struct vnode *a_vp; - int a_which; - int a_fflags; + int a_events; struct ucred *a_cred; struct proc *a_p; } */ *ap; { - return 1; /* DOS filesystems never block? */ + /* DOS filesystems never block? */ + return (ap->a_events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)); } static int msdosfs_mmap(ap) struct vop_mmap_args /* { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return EINVAL; } /* * Flush the blocks of a file to disk. * * This function is worthless for vnodes that represent directories. Maybe we * could just do a sync if they try an fsync on a directory file. */ static int msdosfs_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct buf *bp; int wait = ap->a_waitfor == MNT_WAIT; struct timespec ts; struct buf *nbp; int s; /* * Flush all dirty buffers associated with a vnode. */ loop: s = splbio(); for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if ((bp->b_flags & B_BUSY)) continue; if ((bp->b_flags & B_DELWRI) == 0) panic("msdosfs_fsync: not dirty"); bremfree(bp); bp->b_flags |= B_BUSY; splx(s); (void) bwrite(bp); goto loop; } while (vp->v_numoutput) { vp->v_flag |= VBWAIT; (void) tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "msdosfsn", 0); } #ifdef DIAGNOSTIC if (vp->v_dirtyblkhd.lh_first) { vprint("msdosfs_fsync: dirty", vp); goto loop; } #endif splx(s); TIMEVAL_TO_TIMESPEC(&time, &ts); return deupdat(VTODE(vp), &ts, wait); } /* * Now the whole work of extending a file is done in the write function. * So nothing to do here. */ static int msdosfs_seek(ap) struct vop_seek_args /* { struct vnode *a_vp; off_t a_oldoff; off_t a_newoff; struct ucred *a_cred; } */ *ap; { return 0; } static int msdosfs_remove(ap) struct vop_remove_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { int error; struct denode *dep = VTODE(ap->a_vp); struct denode *ddep = VTODE(ap->a_dvp); error = removede(ddep,dep); #ifdef MSDOSFS_DEBUG printf("msdosfs_remove(), dep %08x, v_usecount %d\n", dep, ap->a_vp->v_usecount); #endif if (ddep == dep) vrele(ap->a_vp); else vput(ap->a_vp); /* causes msdosfs_inactive() to be called * via vrele() */ vput(ap->a_dvp); return error; } /* * DOS filesystems don't know what links are. But since we already called * msdosfs_lookup() with create and lockparent, the parent is locked so we * have to free it before we return the error. */ static int msdosfs_link(ap) struct vop_link_args /* { struct vnode *a_tdvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { VOP_ABORTOP(ap->a_tdvp, ap->a_cnp); vput(ap->a_tdvp); return EOPNOTSUPP; } /* * Renames on files require moving the denode to a new hash queue since the * denode's location is used to compute which hash queue to put the file * in. Unless it is a rename in place. For example "mv a b". * * What follows is the basic algorithm: * * if (file move) { * if (dest file exists) { * remove dest file * } * if (dest and src in same directory) { * rewrite name in existing directory slot * } else { * write new entry in dest directory * update offset and dirclust in denode * move denode to new hash chain * clear old directory entry * } * } else { * directory move * if (dest directory exists) { * if (dest is not empty) { * return ENOTEMPTY * } * remove dest directory * } * if (dest and src in same directory) { * rewrite name in existing entry * } else { * be sure dest is not a child of src directory * write entry in dest directory * update "." and ".." in moved directory * clear old directory entry for moved directory * } * } * * On entry: * source's parent directory is unlocked * source file or directory is unlocked * destination's parent directory is locked * destination file or directory is locked if it exists * * On exit: * all denodes should be released * * Notes: * I'm not sure how the memory containing the pathnames pointed at by the * componentname structures is freed, there may be some memory bleeding * for each rename done. */ static int msdosfs_rename(ap) struct vop_rename_args /* { struct vnode *a_fdvp; struct vnode *a_fvp; struct componentname *a_fcnp; struct vnode *a_tdvp; struct vnode *a_tvp; struct componentname *a_tcnp; } */ *ap; { u_char toname[11]; int error; int newparent = 0; int sourceisadirectory = 0; u_long cn; daddr_t bn; struct vnode *tvp = ap->a_tvp; struct componentname *fcnp = ap->a_fcnp; struct proc *p = fcnp->cn_proc; struct denode *fddep; /* from file's parent directory */ struct denode *fdep; /* from file or directory */ struct denode *tddep; /* to file's parent directory */ struct denode *tdep; /* to file or directory */ struct msdosfsmount *pmp; struct direntry *dotdotp; struct direntry *ep; struct buf *bp; fddep = VTODE(ap->a_fdvp); fdep = VTODE(ap->a_fvp); tddep = VTODE(ap->a_tdvp); tdep = tvp ? VTODE(tvp) : NULL; pmp = fddep->de_pmp; /* Check for cross-device rename */ if ((ap->a_fvp->v_mount != ap->a_tdvp->v_mount) || (tvp && (ap->a_fvp->v_mount != tvp->v_mount))) { error = EXDEV; goto bad; } /* * Convert the filename in tcnp into a dos filename. We copy this * into the denode and directory entry for the destination * file/directory. */ unix2dosfn((u_char *) ap->a_tcnp->cn_nameptr, toname, ap->a_tcnp->cn_namelen); /* * At this point this is the lock state of the denodes: * fddep referenced * fdep referenced * tddep locked * tdep locked if it exists */ /* * Be sure we are not renaming ".", "..", or an alias of ".". This * leads to a crippled directory tree. It's pretty tough to do a * "ls" or "pwd" with the "." directory entry missing, and "cd .." * doesn't work if the ".." entry is missing. */ if (fdep->de_Attributes & ATTR_DIRECTORY) { if ((ap->a_fcnp->cn_namelen == 1 && ap->a_fcnp->cn_nameptr[0] == '.') || fddep == fdep || (ap->a_fcnp->cn_flags | ap->a_tcnp->cn_flags) & ISDOTDOT) { VOP_ABORTOP(ap->a_tdvp, ap->a_tcnp); vput(ap->a_tdvp); if (tvp) vput(tvp); VOP_ABORTOP(ap->a_fdvp, ap->a_fcnp); vrele(ap->a_fdvp); vrele(ap->a_fvp); return EINVAL; } sourceisadirectory = 1; } /* * If we are renaming a directory, and the directory is being moved * to another directory, then we must be sure the destination * directory is not in the subtree of the source directory. This * could orphan everything under the source directory. * doscheckpath() unlocks the destination's parent directory so we * must look it up again to relock it. */ if (fddep->de_StartCluster != tddep->de_StartCluster) newparent = 1; if (sourceisadirectory && newparent) { if (tdep) { vput(ap->a_tvp); tdep = NULL; } /* doscheckpath() vput()'s tddep */ error = doscheckpath(fdep, tddep); tddep = NULL; if (error) goto bad; if ((ap->a_tcnp->cn_flags & SAVESTART) == 0) panic("msdosfs_rename(): lost to startdir"); error = relookup(ap->a_tdvp, &tvp, ap->a_tcnp); if (error) goto bad; tddep = VTODE(ap->a_tdvp); tdep = tvp ? VTODE(tvp) : NULL; } /* * If the destination exists, then be sure its type (file or dir) * matches that of the source. And, if it is a directory make sure * it is empty. Then delete the destination. */ if (tdep) { if (tdep->de_Attributes & ATTR_DIRECTORY) { if (!sourceisadirectory) { error = ENOTDIR; goto bad; } if (!dosdirempty(tdep)) { error = ENOTEMPTY; goto bad; } cache_purge(DETOV(tddep)); } else { /* destination is file */ if (sourceisadirectory) { error = EISDIR; goto bad; } } error = removede(tddep,tdep); if (error) goto bad; vput(ap->a_tvp); tdep = NULL; } /* * If the source and destination are in the same directory then * just read in the directory entry, change the name in the * directory entry and write it back to disk. */ if (newparent == 0) { /* tddep and fddep point to the same denode here */ vn_lock(ap->a_fvp, LK_EXCLUSIVE, p); /* ap->a_fdvp is already locked */ error = readep(fddep->de_pmp, fdep->de_dirclust, fdep->de_diroffset, &bp, &ep); if (error) { VOP_UNLOCK(ap->a_fvp, 0, p); goto bad; } bcopy(toname, ep->deName, 11); error = bwrite(bp); if (error) { VOP_UNLOCK(ap->a_fvp, 0, p); goto bad; } bcopy(toname, fdep->de_Name, 11); /* update denode */ /* * fdep locked fddep and tddep point to the same denode * which is locked tdep is NULL */ } else { u_long dirsize = 0L; /* * If the source and destination are in different * directories, then mark the entry in the source directory * as deleted and write a new entry in the destination * directory. Then move the denode to the correct hash * chain for its new location in the filesystem. And, if * we moved a directory, then update its .. entry to point * to the new parent directory. If we moved a directory * will also insure that the directory entry on disk has a * filesize of zero. */ vn_lock(ap->a_fvp, LK_EXCLUSIVE, p); bcopy(toname, fdep->de_Name, 11); /* update denode */ if (fdep->de_Attributes & ATTR_DIRECTORY) { dirsize = fdep->de_FileSize; fdep->de_FileSize = 0; } error = createde(fdep, tddep, (struct denode **) 0); if (fdep->de_Attributes & ATTR_DIRECTORY) { fdep->de_FileSize = dirsize; } if (error) { /* should put back filename */ VOP_UNLOCK(ap->a_fvp, 0, p); goto bad; } vn_lock(ap->a_fdvp, LK_EXCLUSIVE, p); error = readep(fddep->de_pmp, fddep->de_fndclust, fddep->de_fndoffset, &bp, &ep); if (error) { VOP_UNLOCK(ap->a_fvp, 0, p); VOP_UNLOCK(ap->a_fdvp, 0, p); goto bad; } ep->deName[0] = SLOT_DELETED; error = bwrite(bp); if (error) { VOP_UNLOCK(ap->a_fvp, 0, p); VOP_UNLOCK(ap->a_fdvp, 0, p); goto bad; } if (!sourceisadirectory) { fdep->de_dirclust = tddep->de_fndclust; fdep->de_diroffset = tddep->de_fndoffset; reinsert(fdep); } VOP_UNLOCK(ap->a_fdvp, 0, p); } /* fdep is still locked here */ /* * If we moved a directory to a new parent directory, then we must * fixup the ".." entry in the moved directory. */ if (sourceisadirectory && newparent) { cn = fdep->de_StartCluster; if (cn == MSDOSFSROOT) { /* this should never happen */ panic("msdosfs_rename(): updating .. in root directory?"); } else { bn = cntobn(pmp, cn); } error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp); if (error) { /* should really panic here, fs is corrupt */ VOP_UNLOCK(ap->a_fvp, 0, p); goto bad; } dotdotp = (struct direntry *) bp->b_data + 1; putushort(dotdotp->deStartCluster, tddep->de_StartCluster); error = bwrite(bp); VOP_UNLOCK(ap->a_fvp, 0, p); if (error) { /* should really panic here, fs is corrupt */ goto bad; } } else VOP_UNLOCK(ap->a_fvp, 0, p); bad: ; vrele(DETOV(fdep)); vrele(DETOV(fddep)); if (tdep) vput(DETOV(tdep)); if (tddep) vput(DETOV(tddep)); return error; } static struct { struct direntry dot; struct direntry dotdot; } dosdirtemplate = { { ". ", " ", /* the . entry */ ATTR_DIRECTORY, /* file attribute */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* resevered */ {210, 4}, {210, 4}, /* time and date */ {0, 0}, /* startcluster */ {0, 0, 0, 0}, /* filesize */ },{ ".. ", " ", /* the .. entry */ ATTR_DIRECTORY, /* file attribute */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* resevered */ {210, 4}, {210, 4}, /* time and date */ {0, 0}, /* startcluster */ {0, 0, 0, 0}, /* filesize */ } }; static int msdosfs_mkdir(ap) struct vop_mkdir_args /* { struct vnode *a_dvp; struvt vnode **a_vpp; struvt componentname *a_cnp; struct vattr *a_vap; } */ *ap; { int bn; int error; u_long newcluster; struct denode *pdep; struct denode *ndep; struct direntry *denp; struct denode ndirent; struct msdosfsmount *pmp; struct buf *bp; struct timespec ts; u_short dDate, dTime; pdep = VTODE(ap->a_dvp); /* * If this is the root directory and there is no space left we * can't do anything. This is because the root directory can not * change size. */ if (pdep->de_StartCluster == MSDOSFSROOT && pdep->de_fndclust == (u_long)-1) { free(ap->a_cnp->cn_pnbuf, M_NAMEI); vput(ap->a_dvp); return ENOSPC; } pmp = pdep->de_pmp; /* * Allocate a cluster to hold the about to be created directory. */ error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL); if (error) { free(ap->a_cnp->cn_pnbuf, M_NAMEI); vput(ap->a_dvp); return error; } /* * Now fill the cluster with the "." and ".." entries. And write * the cluster to disk. This way it is there for the parent * directory to be pointing at if there were a crash. */ bn = cntobn(pmp, newcluster); /* always succeeds */ bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0); bzero(bp->b_data, pmp->pm_bpcluster); bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate); denp = (struct direntry *) bp->b_data; putushort(denp->deStartCluster, newcluster); TIMEVAL_TO_TIMESPEC(&time, &ts); unix2dostime(&ts, &dDate, &dTime); putushort(denp->deDate, dDate); putushort(denp->deTime, dTime); denp++; putushort(denp->deStartCluster, pdep->de_StartCluster); putushort(denp->deDate, dDate); putushort(denp->deTime, dTime); error = bwrite(bp); if (error) { clusterfree(pmp, newcluster, NULL); free(ap->a_cnp->cn_pnbuf, M_NAMEI); vput(ap->a_dvp); return error; } /* * Now build up a directory entry pointing to the newly allocated * cluster. This will be written to an empty slot in the parent * directory. */ ndep = &ndirent; bzero(ndep, sizeof(*ndep)); unix2dosfn((u_char *)ap->a_cnp->cn_nameptr, ndep->de_Name, ap->a_cnp->cn_namelen); TIMEVAL_TO_TIMESPEC(&time, &ts); unix2dostime(&ts, &ndep->de_Date, &ndep->de_Time); ndep->de_StartCluster = newcluster; ndep->de_Attributes = ATTR_DIRECTORY; error = createde(ndep, pdep, &ndep); if (error) { clusterfree(pmp, newcluster, NULL); } else { *ap->a_vpp = DETOV(ndep); } free(ap->a_cnp->cn_pnbuf, M_NAMEI); #ifdef MSDOSFS_DEBUG printf("msdosfs_mkdir(): vput(%08x)\n", ap->a_dvp); #endif vput(ap->a_dvp); return error; } static int msdosfs_rmdir(ap) struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { struct denode *ddep; struct denode *dep; int error = 0; ddep = VTODE(ap->a_dvp); /* parent dir of dir to delete */ dep = VTODE(ap->a_vp);/* directory to delete */ /* * Be sure the directory being deleted is empty. */ if (dosdirempty(dep) == 0) { error = ENOTEMPTY; goto out; } /* * Delete the entry from the directory. For dos filesystems this * gets rid of the directory entry on disk, the in memory copy * still exists but the de_refcnt is <= 0. This prevents it from * being found by deget(). When the vput() on dep is done we give * up access and eventually msdosfs_reclaim() will be called which * will remove it from the denode cache. */ error = removede(ddep,dep); if (error) goto out; /* * This is where we decrement the link count in the parent * directory. Since dos filesystems don't do this we just purge * the name cache and let go of the parent directory denode. */ cache_purge(DETOV(ddep)); vput(ap->a_dvp); ap->a_dvp = NULL; /* * Truncate the directory that is being deleted. */ error = detrunc(dep, (u_long) 0, IO_SYNC, NOCRED, NULL); cache_purge(DETOV(dep)); out: ; if (ap->a_dvp) vput(ap->a_dvp); vput(ap->a_vp); return error; } /* * DOS filesystems don't know what symlinks are. */ static int msdosfs_symlink(ap) struct vop_symlink_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; char *a_target; } */ *ap; { free(ap->a_cnp->cn_pnbuf, M_NAMEI); vput(ap->a_dvp); return EINVAL; } /* * Dummy dirents to simulate the "." and ".." entries of the root directory * in a dos filesystem. Dos doesn't provide these. Note that each entry * must be the same size as a dos directory entry (32 bytes). */ static struct dos_dirent { u_long d_fileno; u_short d_reclen; u_char d_type; u_char d_namlen; u_char d_name[24]; } rootdots[2] = { { 1, /* d_fileno */ sizeof(struct direntry), /* d_reclen */ DT_DIR, /* d_type */ 1, /* d_namlen */ "." /* d_name */ }, { 1, /* d_fileno */ sizeof(struct direntry), /* d_reclen */ DT_DIR, /* d_type */ 2, /* d_namlen */ ".." /* d_name */ } }; static int msdosfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; int *a_ncookies; u_long **a_cookies; } */ *ap; { int error = 0; int diff; char pushout; long n; long on; long lost; long count; u_long cn; u_long fileno; long bias = 0; daddr_t bn; daddr_t lbn; struct buf *bp; struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; struct direntry *dentp; struct dirent *prev; struct dirent *crnt; u_char dirbuf[512]; /* holds converted dos directories */ struct uio *uio = ap->a_uio; off_t off; int ncookies = 0; #ifdef MSDOSFS_DEBUG printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n", ap->a_vp, uio, ap->a_cred, ap->a_eofflag); #endif /* * msdosfs_readdir() won't operate properly on regular files since * it does i/o only with the the filesystem vnode, and hence can * retrieve the wrong block from the buffer cache for a plain file. * So, fail attempts to readdir() on a plain file. */ if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) return ENOTDIR; /* * If the user buffer is smaller than the size of one dos directory * entry or the file offset is not a multiple of the size of a * directory entry, then we fail the read. */ count = uio->uio_resid & ~(sizeof(struct direntry) - 1); lost = uio->uio_resid - count; if (count < sizeof(struct direntry) || (uio->uio_offset & (sizeof(struct direntry) - 1))) return EINVAL; uio->uio_resid = count; uio->uio_iov->iov_len = count; off = uio->uio_offset; /* * If they are reading from the root directory then, we simulate * the . and .. entries since these don't exist in the root * directory. We also set the offset bias to make up for having to * simulate these entries. By this I mean that at file offset 64 we * read the first entry in the root directory that lives on disk. */ if (dep->de_StartCluster == MSDOSFSROOT) { /* * printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n", * uio->uio_offset); */ bias = 2 * sizeof(struct direntry); if (uio->uio_offset < 2 * sizeof(struct direntry)) { if (uio->uio_offset && uio->uio_offset != sizeof(struct direntry)) { error = EINVAL; goto out; } n = 1; if (!uio->uio_offset) { n = 2; ncookies++; } ncookies++; error = uiomove((char *) rootdots + uio->uio_offset, n * sizeof(struct direntry), uio); } } while (!error && uio->uio_resid > 0) { lbn = (uio->uio_offset - bias) >> pmp->pm_cnshift; on = (uio->uio_offset - bias) & pmp->pm_crbomask; n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid); diff = dep->de_FileSize - (uio->uio_offset - bias); if (diff <= 0) break; if (diff < n) n = diff; error = pcbmap(dep, lbn, &bn, &cn); if (error) break; error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp); n = min(n, pmp->pm_bpcluster - bp->b_resid); if (error) { brelse(bp); return error; } /* * code to convert from dos directory entries to ufs * directory entries */ pushout = 0; dentp = (struct direntry *)(bp->b_data + on); prev = 0; crnt = (struct dirent *) dirbuf; while ((char *) dentp < bp->b_data + on + n) { /* * printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n", * dentp, prev, crnt, dentp->deName[0], dentp->deAttributes); */ /* * If we have an empty entry or a slot from a * deleted file, or a volume label entry just * concatenate its space onto the end of the * previous entry or, manufacture an empty entry if * there is no previous entry. */ if (dentp->deName[0] == SLOT_EMPTY || dentp->deName[0] == SLOT_DELETED || (dentp->deAttributes & ATTR_VOLUME)) { if (prev) { prev->d_reclen += sizeof(struct direntry); } else { prev = crnt; prev->d_fileno = 0; prev->d_reclen = sizeof(struct direntry); prev->d_type = DT_UNKNOWN; prev->d_namlen = 0; prev->d_name[0] = 0; ncookies++; } } else { /* * this computation of d_fileno must match * the computation of va_fileid in * msdosfs_getattr */ if (dentp->deAttributes & ATTR_DIRECTORY) { /* if this is the root directory */ fileno = getushort(dentp->deStartCluster); if (fileno == MSDOSFSROOT) fileno = 1; } else { /* * if the file's dirent lives in * root dir */ if ((fileno = cn) == MSDOSFSROOT) fileno = 1; fileno = (fileno << 16) | ((dentp - (struct direntry *) bp->b_data) & 0xffff); } crnt->d_fileno = fileno; crnt->d_reclen = sizeof(struct direntry); crnt->d_type = (dentp->deAttributes & ATTR_DIRECTORY) ? DT_DIR : DT_REG; crnt->d_namlen = dos2unixfn(dentp->deName, (u_char *)crnt->d_name); /* * printf("readdir: file %s, fileno %08x, attr %02x, start %08x\n", * crnt->d_name, crnt->d_fileno, dentp->deAttributes, * dentp->deStartCluster); */ prev = crnt; ncookies++; } dentp++; crnt = (struct dirent *) ((char *) crnt + sizeof(struct direntry)); pushout = 1; /* * If our intermediate buffer is full then copy its * contents to user space. I would just use the * buffer the buf header points to but, I'm afraid * that when we brelse() it someone else might find * it in the cache and think its contents are * valid. Maybe there is a way to invalidate the * buffer before brelse()'ing it. */ if ((u_char *) crnt >= &dirbuf[sizeof dirbuf]) { pushout = 0; error = uiomove(dirbuf, sizeof(dirbuf), uio); if (error) break; prev = 0; crnt = (struct dirent *) dirbuf; } } if (pushout) { pushout = 0; error = uiomove(dirbuf, (char *) crnt - (char *) dirbuf, uio); } #if 0 /* * If we have read everything from this block or have read * to end of file then we are done with this block. Mark * it to say the buffer can be reused if need be. */ if (n + on == pmp->pm_bpcluster || (uio->uio_offset - bias) == dep->de_FileSize) bp->b_flags |= B_AGE; #endif /* if 0 */ brelse(bp); if (n == 0) break; } out: ; uio->uio_resid += lost; if (!error && ap->a_ncookies != NULL) { struct dirent* dpStart; struct dirent* dpEnd; struct dirent* dp; u_long *cookies; u_long *cookiep; if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) panic("msdosfs_readdir: unexpected uio from NFS server"); dpStart = (struct dirent *) (uio->uio_iov->iov_base - (uio->uio_offset - off)); dpEnd = (struct dirent *) uio->uio_iov->iov_base; cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK); for (dp = dpStart, cookiep = cookies; dp < dpEnd; dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) { off += dp->d_reclen; *cookiep++ = (u_long) off; } *ap->a_ncookies = ncookies; *ap->a_cookies = cookies; } /* * Set the eofflag (NFS uses it) */ if (ap->a_eofflag) if (dep->de_FileSize - (uio->uio_offset - bias) <= 0) *ap->a_eofflag = 1; else *ap->a_eofflag = 0; return error; } /* * DOS filesystems don't know what symlinks are. */ static int msdosfs_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { return EINVAL; } static int msdosfs_abortop(ap) struct vop_abortop_args /* { struct vnode *a_dvp; struct componentname *a_cnp; } */ *ap; { if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); return 0; } static int msdosfs_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; return (lockmgr(&VTODE(vp)->de_lock, ap->a_flags, &vp->v_interlock, ap->a_p)); } int msdosfs_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; return (lockmgr(&VTODE(vp)->de_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock, ap->a_p)); } int msdosfs_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { return (lockstatus(&VTODE(ap->a_vp)->de_lock)); } /* * vp - address of vnode file the file * bn - which cluster we are interested in mapping to a filesystem block number. * vpp - returns the vnode for the block special file holding the filesystem * containing the file of interest * bnp - address of where to return the filesystem relative block number */ static int msdosfs_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; daddr_t a_bn; struct vnode **a_vpp; daddr_t *a_bnp; int *a_runp; int *a_runb; } */ *ap; { struct denode *dep = VTODE(ap->a_vp); if (ap->a_vpp != NULL) *ap->a_vpp = dep->de_devvp; if (ap->a_bnp == NULL) return 0; if (ap->a_runp) { /* * Sequential clusters should be counted here. */ *ap->a_runp = 0; } if (ap->a_runb) { *ap->a_runb = 0; } return pcbmap(dep, ap->a_bn, ap->a_bnp, 0); } static int msdosfs_reallocblks(ap) struct vop_reallocblks_args /* { struct vnode *a_vp; struct cluster_save *a_buflist; } */ *ap; { /* Currently no support for clustering */ /* XXX */ return ENOSPC; } static int msdosfs_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *ap; { struct buf *bp = ap->a_bp; struct denode *dep = VTODE(bp->b_vp); struct vnode *vp; int error = 0; if (bp->b_vp->v_type == VBLK || bp->b_vp->v_type == VCHR) panic("msdosfs_strategy: spec"); /* * If we don't already know the filesystem relative block number * then get it using pcbmap(). If pcbmap() returns the block * number as -1 then we've got a hole in the file. DOS filesystems * don't allow files with holes, so we shouldn't ever see this. */ if (bp->b_blkno == bp->b_lblkno) { error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0); if (error) bp->b_blkno = -1; if (bp->b_blkno == -1) clrbuf(bp); } if (bp->b_blkno == -1) { biodone(bp); return error; } #ifdef DIAGNOSTIC #endif /* * Read/write the block from/to the disk that contains the desired * file block. */ vp = dep->de_devvp; bp->b_dev = vp->v_rdev; VOCALL(vp->v_op, VOFFSET(vop_strategy), ap); return 0; } static int msdosfs_print(ap) struct vop_print_args /* { struct vnode *vp; } */ *ap; { struct denode *dep = VTODE(ap->a_vp); printf( "tag VT_MSDOSFS, startcluster %d, dircluster %ld, diroffset %ld ", dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset); printf(" dev %d, %d", major(dep->de_dev), minor(dep->de_dev)); lockmgr_printinfo(&dep->de_lock); printf("\n"); return 0; } static int msdosfs_advlock(ap) struct vop_advlock_args /* { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ *ap; { return EINVAL; /* we don't do locking yet */ } static int msdosfs_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = 1; return 0; case _PC_NAME_MAX: *ap->a_retval = 12; return 0; case _PC_PATH_MAX: *ap->a_retval = PATH_MAX; /* 255? */ return 0; case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return 0; case _PC_NO_TRUNC: *ap->a_retval = 0; return 0; default: return EINVAL; } } /* Global vfs data structures for msdosfs */ vop_t **msdosfs_vnodeop_p; static struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)vfs_cache_lookup }, /* lookup */ { &vop_cachedlookup_desc, (vop_t *)msdosfs_lookup }, /* lookup */ { &vop_create_desc, (vop_t *)msdosfs_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)msdosfs_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)msdosfs_open }, /* open */ { &vop_close_desc, (vop_t *)msdosfs_close }, /* close */ { &vop_access_desc, (vop_t *)msdosfs_access }, /* access */ { &vop_getattr_desc, (vop_t *)msdosfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)msdosfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)msdosfs_read }, /* read */ { &vop_write_desc, (vop_t *)msdosfs_write }, /* write */ +/* XXX: vop_lease */ { &vop_ioctl_desc, (vop_t *)msdosfs_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)msdosfs_select }, /* select */ + { &vop_poll_desc, (vop_t *)msdosfs_poll }, /* poll */ +/* XXX: vop_revoke */ { &vop_mmap_desc, (vop_t *)msdosfs_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)msdosfs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)msdosfs_seek }, /* seek */ { &vop_remove_desc, (vop_t *)msdosfs_remove }, /* remove */ { &vop_link_desc, (vop_t *)msdosfs_link }, /* link */ { &vop_rename_desc, (vop_t *)msdosfs_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)msdosfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)msdosfs_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)msdosfs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)msdosfs_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)msdosfs_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)msdosfs_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)msdosfs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)msdosfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)msdosfs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)msdosfs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)msdosfs_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)msdosfs_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)msdosfs_print }, /* print */ { &vop_islocked_desc, (vop_t *)msdosfs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)msdosfs_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)msdosfs_advlock }, /* advlock */ +/* XXX: vop_blkatoff */ +/* XXX: vop_valloc */ { &vop_reallocblks_desc, (vop_t *)msdosfs_reallocblks }, /* reallocblks */ +/* XXX: vop_vfree */ +/* XXX: vop_truncate */ +/* XXX: vop_update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc msdosfs_vnodeop_opv_desc = { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries }; VNODEOP_SET(msdosfs_vnodeop_opv_desc); diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c index 256488a7d59b..a546fb8e687f 100644 --- a/sys/ufs/ffs/ffs_vnops.c +++ b/sys/ufs/ffs/ffs_vnops.c @@ -1,345 +1,353 @@ /* * Copyright (c) 1982, 1986, 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. * * @(#)ffs_vnops.c 8.15 (Berkeley) 5/14/95 - * $Id: ffs_vnops.c,v 1.27 1997/08/26 07:32:48 phk Exp $ + * $Id: ffs_vnops.c,v 1.28 1997/09/02 20:06:46 bde Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int ffs_fsync __P((struct vop_fsync_args *)); static int ffs_getpages __P((struct vop_getpages_args *)); static int ffs_read __P((struct vop_read_args *)); static int ffs_write __P((struct vop_write_args *)); /* Global vfs data structures for ufs. */ vop_t **ffs_vnodeop_p; static struct vnodeopv_entry_desc ffs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)vfs_cache_lookup },/* lookup */ { &vop_cachedlookup_desc, (vop_t *)ufs_lookup },/* cachedlookup */ { &vop_create_desc, (vop_t *)ufs_create }, /* create */ { &vop_whiteout_desc, (vop_t *)ufs_whiteout }, /* whiteout */ { &vop_mknod_desc, (vop_t *)ufs_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)ufs_open }, /* open */ { &vop_close_desc, (vop_t *)ufs_close }, /* close */ { &vop_access_desc, (vop_t *)ufs_access }, /* access */ { &vop_getattr_desc, (vop_t *)ufs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)ufs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)ffs_read }, /* read */ { &vop_write_desc, (vop_t *)ffs_write }, /* write */ { &vop_lease_desc, (vop_t *)ufs_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)ufs_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)ufs_select }, /* select */ + { &vop_poll_desc, (vop_t *)ufs_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)ufs_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)ufs_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)ffs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)ufs_seek }, /* seek */ { &vop_remove_desc, (vop_t *)ufs_remove }, /* remove */ { &vop_link_desc, (vop_t *)ufs_link }, /* link */ { &vop_rename_desc, (vop_t *)ufs_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)ufs_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)ufs_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)ufs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)ufs_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)ufs_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)ufs_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)ufs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)ffs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)ufs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)ufs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)ufs_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)ufs_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)ufs_print }, /* print */ { &vop_islocked_desc, (vop_t *)ufs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)ufs_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)ufs_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)ffs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)ffs_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)ffs_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)ffs_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)ffs_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)ffs_update }, /* update */ { &vop_getpages_desc, (vop_t *)ffs_getpages}, /* getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc ffs_vnodeop_opv_desc = { &ffs_vnodeop_p, ffs_vnodeop_entries }; vop_t **ffs_specop_p; static struct vnodeopv_entry_desc ffs_specop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)spec_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)spec_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)spec_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)spec_open }, /* open */ { &vop_close_desc, (vop_t *)ufsspec_close }, /* close */ { &vop_access_desc, (vop_t *)ufs_access }, /* access */ { &vop_getattr_desc, (vop_t *)ufs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)ufs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)ufsspec_read }, /* read */ { &vop_write_desc, (vop_t *)ufsspec_write }, /* write */ { &vop_lease_desc, (vop_t *)spec_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)spec_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)spec_select }, /* select */ + { &vop_poll_desc, (vop_t *)spec_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)spec_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)spec_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)ffs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)spec_seek }, /* seek */ { &vop_remove_desc, (vop_t *)spec_remove }, /* remove */ { &vop_link_desc, (vop_t *)spec_link }, /* link */ { &vop_rename_desc, (vop_t *)spec_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)spec_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)spec_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)spec_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)spec_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)spec_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)ufs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)ffs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)ufs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)ufs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)spec_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)spec_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)ufs_print }, /* print */ { &vop_islocked_desc, (vop_t *)ufs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)spec_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)spec_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)spec_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)spec_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)ffs_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)spec_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)ffs_update }, /* update */ { &vop_getpages_desc, (vop_t *)spec_getpages}, /* getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc ffs_specop_opv_desc = { &ffs_specop_p, ffs_specop_entries }; vop_t **ffs_fifoop_p; static struct vnodeopv_entry_desc ffs_fifoop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)fifo_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)fifo_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)fifo_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)fifo_open }, /* open */ { &vop_close_desc, (vop_t *)ufsfifo_close }, /* close */ { &vop_access_desc, (vop_t *)ufs_access }, /* access */ { &vop_getattr_desc, (vop_t *)ufs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)ufs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)ufsfifo_read }, /* read */ { &vop_write_desc, (vop_t *)ufsfifo_write }, /* write */ { &vop_lease_desc, (vop_t *)fifo_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)fifo_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)fifo_select }, /* select */ + { &vop_poll_desc, (vop_t *)fifo_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)fifo_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)fifo_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)ffs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)fifo_seek }, /* seek */ { &vop_remove_desc, (vop_t *)fifo_remove }, /* remove */ { &vop_link_desc, (vop_t *)fifo_link }, /* link */ { &vop_rename_desc, (vop_t *)fifo_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)fifo_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)fifo_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)fifo_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)fifo_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)ufs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)ffs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)ufs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)ufs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)fifo_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)fifo_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)ufs_print }, /* print */ { &vop_islocked_desc, (vop_t *)ufs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)fifo_valloc }, /* valloc */ { &vop_reallocblks_desc, (vop_t *)fifo_reallocblks }, /* reallocblks */ { &vop_vfree_desc, (vop_t *)ffs_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)fifo_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)ffs_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)vn_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc ffs_fifoop_opv_desc = { &ffs_fifoop_p, ffs_fifoop_entries }; VNODEOP_SET(ffs_vnodeop_opv_desc); VNODEOP_SET(ffs_specop_opv_desc); VNODEOP_SET(ffs_fifoop_opv_desc); /* * Enabling cluster read/write operations. */ static int ffs_doclusterread = 1; static int ffs_doclusterwrite = 1; SYSCTL_NODE(_vfs, MOUNT_UFS, ffs, CTLFLAG_RW, 0, "FFS filesystem"); SYSCTL_INT(_vfs_ffs, FFS_CLUSTERREAD, doclusterread, CTLFLAG_RW, &ffs_doclusterread, 0, ""); SYSCTL_INT(_vfs_ffs, FFS_CLUSTERWRITE, doclusterwrite, CTLFLAG_RW, &ffs_doclusterwrite, 0, ""); #include /* * Synch an open file. */ /* ARGSUSED */ static int ffs_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct buf *bp; struct timeval tv; struct buf *nbp; int pass; int s; pass = 0; /* * Flush all dirty buffers associated with a vnode. */ loop: s = splbio(); for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if ((bp->b_flags & B_BUSY) || (pass == 0 && (bp->b_blkno < 0))) continue; if ((bp->b_flags & B_DELWRI) == 0) panic("ffs_fsync: not dirty"); if (bp->b_vp != vp || ap->a_waitfor != MNT_NOWAIT) { bremfree(bp); bp->b_flags |= B_BUSY; splx(s); /* * Wait for I/O associated with indirect blocks to complete, * since there is no way to quickly wait for them below. */ if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT) (void) bawrite(bp); else (void) bwrite(bp); } else { vfs_bio_awrite(bp); splx(s); } goto loop; } splx(s); if (pass == 0) { pass = 1; goto loop; } if (ap->a_waitfor == MNT_WAIT) { s = splbio(); while (vp->v_numoutput) { vp->v_flag |= VBWAIT; (void) tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "ffsfsn", 0); } splx(s); #ifdef DIAGNOSTIC if (vp->v_dirtyblkhd.lh_first) { vprint("ffs_fsync: dirty", vp); goto loop; } #endif } gettime(&tv); return (VOP_UPDATE(ap->a_vp, &tv, &tv, ap->a_waitfor == MNT_WAIT)); } /* * Reclaim an inode so that it can be used for other purposes. */ int ffs_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; int error; if (error = ufs_reclaim(vp, ap->a_p)) return (error); FREE(vp->v_data, VFSTOUFS(vp->v_mount)->um_devvp->v_tag == VT_MFS ? M_MFSNODE : M_FFSNODE); vp->v_data = NULL; return (0); } diff --git a/sys/ufs/lfs/lfs_vnops.c b/sys/ufs/lfs/lfs_vnops.c index cd9d9e5be6ec..80ba28f28a34 100644 --- a/sys/ufs/lfs/lfs_vnops.c +++ b/sys/ufs/lfs/lfs_vnops.c @@ -1,371 +1,385 @@ /* * Copyright (c) 1986, 1989, 1991, 1993, 1995 * 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. * * @(#)lfs_vnops.c 8.13 (Berkeley) 6/10/95 - * $Id: lfs_vnops.c,v 1.21 1997/03/23 00:45:27 bde Exp $ + * $Id: lfs_vnops.c,v 1.22 1997/09/02 20:06:49 bde Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int lfs_close __P((struct vop_close_args *)); static int lfs_fsync __P((struct vop_fsync_args *)); static int lfs_getattr __P((struct vop_getattr_args *)); static int lfs_inactive __P((struct vop_inactive_args *)); static int lfs_read __P((struct vop_read_args *)); static int lfs_write __P((struct vop_write_args *)); /* Global vfs data structures for lfs. */ vop_t **lfs_vnodeop_p; static struct vnodeopv_entry_desc lfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)ufs_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)ufs_create }, /* create */ - { &vop_mknod_desc, (vop_t *)ufs_mknod }, /* mknod */ { &vop_whiteout_desc, (vop_t *)ufs_whiteout }, /* whiteout */ + { &vop_mknod_desc, (vop_t *)ufs_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)ufs_open }, /* open */ { &vop_close_desc, (vop_t *)lfs_close }, /* close */ { &vop_access_desc, (vop_t *)ufs_access }, /* access */ { &vop_getattr_desc, (vop_t *)lfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)ufs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)lfs_read }, /* read */ { &vop_write_desc, (vop_t *)lfs_write }, /* write */ { &vop_lease_desc, (vop_t *)ufs_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)ufs_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)ufs_select }, /* select */ + { &vop_poll_desc, (vop_t *)ufs_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)ufs_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)ufs_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)lfs_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)ufs_seek }, /* seek */ { &vop_remove_desc, (vop_t *)ufs_remove }, /* remove */ { &vop_link_desc, (vop_t *)ufs_link }, /* link */ { &vop_rename_desc, (vop_t *)ufs_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)ufs_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)ufs_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)ufs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)ufs_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)ufs_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)ufs_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)ufs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)lfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)ufs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)ufs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)ufs_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)ufs_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)ufs_print }, /* print */ { &vop_islocked_desc, (vop_t *)ufs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)ufs_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)ufs_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)lfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)lfs_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)lfs_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)lfs_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)lfs_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)lfs_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc lfs_vnodeop_opv_desc = { &lfs_vnodeop_p, lfs_vnodeop_entries }; vop_t **lfs_specop_p; static struct vnodeopv_entry_desc lfs_specop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)spec_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)spec_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)spec_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)spec_open }, /* open */ { &vop_close_desc, (vop_t *)ufsspec_close }, /* close */ { &vop_access_desc, (vop_t *)ufs_access }, /* access */ { &vop_getattr_desc, (vop_t *)lfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)ufs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)ufsspec_read }, /* read */ { &vop_write_desc, (vop_t *)ufsspec_write }, /* write */ { &vop_lease_desc, (vop_t *)spec_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)spec_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)spec_select }, /* select */ + { &vop_poll_desc, (vop_t *)spec_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)spec_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)spec_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)spec_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)spec_seek }, /* seek */ { &vop_remove_desc, (vop_t *)spec_remove }, /* remove */ { &vop_link_desc, (vop_t *)spec_link }, /* link */ { &vop_rename_desc, (vop_t *)spec_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)spec_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)spec_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)spec_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)spec_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)spec_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)ufs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)lfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)ufs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)ufs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)spec_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)spec_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)ufs_print }, /* print */ { &vop_islocked_desc, (vop_t *)ufs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)spec_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)spec_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)spec_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)lfs_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)spec_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)lfs_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)lfs_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc lfs_specop_opv_desc = { &lfs_specop_p, lfs_specop_entries }; vop_t **lfs_fifoop_p; static struct vnodeopv_entry_desc lfs_fifoop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)fifo_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)fifo_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)fifo_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)fifo_open }, /* open */ { &vop_close_desc, (vop_t *)ufsfifo_close }, /* close */ { &vop_access_desc, (vop_t *)ufs_access }, /* access */ { &vop_getattr_desc, (vop_t *)lfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)ufs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)ufsfifo_read }, /* read */ { &vop_write_desc, (vop_t *)ufsfifo_write }, /* write */ { &vop_lease_desc, (vop_t *)fifo_lease_check }, /* lease */ { &vop_ioctl_desc, (vop_t *)fifo_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)fifo_select }, /* select */ + { &vop_poll_desc, (vop_t *)fifo_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)fifo_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)fifo_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)fifo_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)fifo_seek }, /* seek */ { &vop_remove_desc, (vop_t *)fifo_remove }, /* remove */ { &vop_link_desc, (vop_t *)fifo_link }, /* link */ { &vop_rename_desc, (vop_t *)fifo_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)fifo_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)fifo_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)fifo_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)fifo_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)ufs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)lfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)ufs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)ufs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)fifo_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)fifo_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)ufs_print }, /* print */ { &vop_islocked_desc, (vop_t *)ufs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)fifo_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)lfs_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)fifo_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)lfs_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)lfs_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc lfs_fifoop_opv_desc = { &lfs_fifoop_p, lfs_fifoop_entries }; VNODEOP_SET(lfs_vnodeop_opv_desc); VNODEOP_SET(lfs_specop_opv_desc); VNODEOP_SET(lfs_fifoop_opv_desc); #define LFS_READWRITE #include #undef LFS_READWRITE /* * Synch an open file. */ /* ARGSUSED */ static int lfs_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { struct timeval tv; int error; gettime(&tv); error = (VOP_UPDATE(ap->a_vp, &tv, &tv, ap->a_waitfor == MNT_WAIT ? LFS_SYNC : 0)); if(ap->a_waitfor == MNT_WAIT && ap->a_vp->v_dirtyblkhd.lh_first != NULL) panic("lfs_fsync: dirty bufs"); return( error ); } /* * These macros are used to bracket UFS directory ops, so that we can * identify all the pages touched during directory ops which need to * be ordered and flushed atomically, so that they may be recovered. */ #define SET_DIROP(fs) { \ if ((fs)->lfs_writer) \ tsleep(&(fs)->lfs_dirops, PRIBIO + 1, "lfs_dirop", 0); \ ++(fs)->lfs_dirops; \ (fs)->lfs_doifile = 1; \ } #define SET_ENDOP(fs) { \ --(fs)->lfs_dirops; \ if (!(fs)->lfs_dirops) \ wakeup(&(fs)->lfs_writer); \ } #define MARK_VNODE(dvp) (dvp)->v_flag |= VDIROP /* XXX hack to avoid calling ITIMES in getattr */ static int lfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); register struct vattr *vap = ap->a_vap; /* * Copy from inode table */ vap->va_fsid = ip->i_dev; vap->va_fileid = ip->i_number; vap->va_mode = ip->i_mode & ~IFMT; vap->va_nlink = ip->i_nlink; vap->va_uid = ip->i_uid; vap->va_gid = ip->i_gid; vap->va_rdev = (dev_t)ip->i_rdev; vap->va_size = ip->i_din.di_size; vap->va_atime.tv_sec = ip->i_atime; vap->va_atime.tv_nsec = ip->i_atimensec; vap->va_mtime.tv_sec = ip->i_mtime; vap->va_mtime.tv_nsec = ip->i_mtimensec; vap->va_ctime.tv_sec = ip->i_ctime; vap->va_ctime.tv_nsec = ip->i_ctimensec; vap->va_flags = ip->i_flags; vap->va_gen = ip->i_gen; /* this doesn't belong here */ if (vp->v_type == VBLK) vap->va_blocksize = BLKDEV_IOSIZE; else if (vp->v_type == VCHR) vap->va_blocksize = MAXBSIZE; else vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize; vap->va_bytes = dbtob(ip->i_blocks); vap->va_type = vp->v_type; vap->va_filerev = ip->i_modrev; return (0); } /* * Close called * * XXX -- we were using ufs_close, but since it updates the * times on the inode, we might need to bump the uinodes * count. */ /* ARGSUSED */ static int lfs_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); int mod; simple_lock(&vp->v_interlock); if (vp->v_usecount > 1) { mod = ip->i_flag & IN_MODIFIED; ITIMES(ip, &time, &time); if (!mod && ip->i_flag & IN_MODIFIED) ip->i_lfs->lfs_uinodes++; } simple_unlock(&vp->v_interlock); return (0); } /* * Reclaim an inode so that it can be used for other purposes. */ int lfs_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; int error; if (error = ufs_reclaim(vp, ap->a_p)) return (error); FREE(vp->v_data, M_LFSNODE); vp->v_data = NULL; return (0); } diff --git a/sys/ufs/mfs/mfs_vnops.c b/sys/ufs/mfs/mfs_vnops.c index 335c4e385ec4..06ea12d2db34 100644 --- a/sys/ufs/mfs/mfs_vnops.c +++ b/sys/ufs/mfs/mfs_vnops.c @@ -1,348 +1,354 @@ /* * 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. * * @(#)mfs_vnops.c 8.11 (Berkeley) 5/22/95 - * $Id: mfs_vnops.c,v 1.17 1997/02/22 09:47:33 peter Exp $ + * $Id: mfs_vnops.c,v 1.18 1997/08/02 14:33:23 bde Exp $ */ #include #include #include #include #include #include #include #include #include #include static int mfs_badop __P((void)); static int mfs_bmap __P((struct vop_bmap_args *)); static int mfs_close __P((struct vop_close_args *)); static int mfs_ioctl __P((struct vop_ioctl_args *)); static int mfs_inactive __P((struct vop_inactive_args *)); /* XXX */ static int mfs_open __P((struct vop_open_args *)); static int mfs_print __P((struct vop_print_args *)); /* XXX */ static int mfs_reclaim __P((struct vop_reclaim_args *)); /* XXX */ static int mfs_strategy __P((struct vop_strategy_args *)); /* XXX */ /* * mfs vnode operations. */ vop_t **mfs_vnodeop_p; static struct vnodeopv_entry_desc mfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *)vn_default_error }, { &vop_lookup_desc, (vop_t *)mfs_lookup }, /* lookup */ +/* XXX: vop_cachedlookup */ { &vop_create_desc, (vop_t *)mfs_create }, /* create */ +/* XXX: vop_whiteout */ { &vop_mknod_desc, (vop_t *)mfs_mknod }, /* mknod */ { &vop_open_desc, (vop_t *)mfs_open }, /* open */ { &vop_close_desc, (vop_t *)mfs_close }, /* close */ { &vop_access_desc, (vop_t *)mfs_access }, /* access */ { &vop_getattr_desc, (vop_t *)mfs_getattr }, /* getattr */ { &vop_setattr_desc, (vop_t *)mfs_setattr }, /* setattr */ { &vop_read_desc, (vop_t *)mfs_read }, /* read */ { &vop_write_desc, (vop_t *)mfs_write }, /* write */ +/* XXX: vop_lease */ { &vop_ioctl_desc, (vop_t *)mfs_ioctl }, /* ioctl */ - { &vop_select_desc, (vop_t *)mfs_select }, /* select */ + { &vop_poll_desc, (vop_t *)mfs_poll }, /* poll */ { &vop_revoke_desc, (vop_t *)mfs_revoke }, /* revoke */ { &vop_mmap_desc, (vop_t *)mfs_mmap }, /* mmap */ { &vop_fsync_desc, (vop_t *)spec_fsync }, /* fsync */ { &vop_seek_desc, (vop_t *)mfs_seek }, /* seek */ { &vop_remove_desc, (vop_t *)mfs_remove }, /* remove */ { &vop_link_desc, (vop_t *)mfs_link }, /* link */ { &vop_rename_desc, (vop_t *)mfs_rename }, /* rename */ { &vop_mkdir_desc, (vop_t *)mfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, (vop_t *)mfs_rmdir }, /* rmdir */ { &vop_symlink_desc, (vop_t *)mfs_symlink }, /* symlink */ { &vop_readdir_desc, (vop_t *)mfs_readdir }, /* readdir */ { &vop_readlink_desc, (vop_t *)mfs_readlink }, /* readlink */ { &vop_abortop_desc, (vop_t *)mfs_abortop }, /* abortop */ { &vop_inactive_desc, (vop_t *)mfs_inactive }, /* inactive */ { &vop_reclaim_desc, (vop_t *)mfs_reclaim }, /* reclaim */ { &vop_lock_desc, (vop_t *)mfs_lock }, /* lock */ { &vop_unlock_desc, (vop_t *)mfs_unlock }, /* unlock */ { &vop_bmap_desc, (vop_t *)mfs_bmap }, /* bmap */ { &vop_strategy_desc, (vop_t *)mfs_strategy }, /* strategy */ { &vop_print_desc, (vop_t *)mfs_print }, /* print */ { &vop_islocked_desc, (vop_t *)mfs_islocked }, /* islocked */ { &vop_pathconf_desc, (vop_t *)mfs_pathconf }, /* pathconf */ { &vop_advlock_desc, (vop_t *)mfs_advlock }, /* advlock */ { &vop_blkatoff_desc, (vop_t *)mfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, (vop_t *)mfs_valloc }, /* valloc */ +/* XXX: vop_reallocblks */ { &vop_vfree_desc, (vop_t *)mfs_vfree }, /* vfree */ { &vop_truncate_desc, (vop_t *)mfs_truncate }, /* truncate */ { &vop_update_desc, (vop_t *)mfs_update }, /* update */ +/* XXX: vop_getpages */ +/* XXX: vop_putpages */ { &vop_bwrite_desc, (vop_t *)mfs_bwrite }, /* bwrite */ { NULL, NULL } }; static struct vnodeopv_desc mfs_vnodeop_opv_desc = { &mfs_vnodeop_p, mfs_vnodeop_entries }; VNODEOP_SET(mfs_vnodeop_opv_desc); /* * Vnode Operations. * * Open called to allow memory filesystem to initialize and * validate before actual IO. Record our process identifier * so we can tell when we are doing I/O to ourself. */ /* ARGSUSED */ static int mfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { if (ap->a_vp->v_type != VBLK) { panic("mfs_open not VBLK"); /* NOTREACHED */ } return (0); } /* * Ioctl operation. */ /* ARGSUSED */ static int mfs_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (ENOTTY); } /* * Pass I/O requests to the memory filesystem process. */ static int mfs_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *ap; { register struct buf *bp = ap->a_bp; register struct mfsnode *mfsp; struct vnode *vp; struct proc *p = curproc; /* XXX */ if (!vfinddev(bp->b_dev, VBLK, &vp) || vp->v_usecount == 0) panic("mfs_strategy: bad dev"); mfsp = VTOMFS(vp); /* check for mini-root access */ if (mfsp->mfs_pid == 0) { caddr_t base; base = mfsp->mfs_baseoff + (bp->b_blkno << DEV_BSHIFT); if (bp->b_flags & B_READ) bcopy(base, bp->b_data, bp->b_bcount); else bcopy(bp->b_data, base, bp->b_bcount); biodone(bp); } else if (mfsp->mfs_pid == p->p_pid) { mfs_doio(bp, mfsp->mfs_baseoff); } else { TAILQ_INSERT_TAIL(&mfsp->buf_queue, bp, b_act); wakeup((caddr_t)vp); } return (0); } /* * Memory file system I/O. * * Trivial on the HP since buffer has already been mapping into KVA space. */ void mfs_doio(bp, base) register struct buf *bp; caddr_t base; { base += (bp->b_blkno << DEV_BSHIFT); if (bp->b_flags & B_READ) bp->b_error = copyin(base, bp->b_data, bp->b_bcount); else bp->b_error = copyout(bp->b_data, base, bp->b_bcount); if (bp->b_error) bp->b_flags |= B_ERROR; biodone(bp); } /* * This is a noop, simply returning what one has been given. */ static int mfs_bmap(ap) struct vop_bmap_args /* { struct vnode *a_vp; ufs_daddr_t a_bn; struct vnode **a_vpp; ufs_daddr_t *a_bnp; int *a_runp; } */ *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; if (ap->a_runp != NULL) *ap->a_runp = 0; return (0); } /* * Memory filesystem close routine */ /* ARGSUSED */ static int mfs_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct mfsnode *mfsp = VTOMFS(vp); register struct buf *bp; int error; /* * Finish any pending I/O requests. */ while (bp = TAILQ_FIRST(&mfsp->buf_queue)) { TAILQ_REMOVE(&mfsp->buf_queue, bp, b_act) mfs_doio(bp, mfsp->mfs_baseoff); wakeup((caddr_t)bp); } /* * On last close of a memory filesystem * we must invalidate any in core blocks, so that * we can, free up its vnode. */ if (error = vinvalbuf(vp, 1, ap->a_cred, ap->a_p, 0, 0)) return (error); /* * There should be no way to have any more uses of this * vnode, so if we find any other uses, it is a panic. */ if (vp->v_usecount > 1) printf("mfs_close: ref count %d > 1\n", vp->v_usecount); if (vp->v_usecount > 1 || !TAILQ_EMPTY(&mfsp->buf_queue)) panic("mfs_close"); /* * Send a request to the filesystem server to exit. */ mfsp->mfs_active = 0; wakeup((caddr_t)vp); return (0); } /* * Memory filesystem inactive routine */ /* ARGSUSED */ static int mfs_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct mfsnode *mfsp = VTOMFS(vp); if (!TAILQ_EMPTY(&mfsp->buf_queue)) panic("mfs_inactive: not inactive (next buffer %p)", TAILQ_FIRST(&mfsp->buf_queue)); VOP_UNLOCK(vp, 0, ap->a_p); return (0); } /* * Reclaim a memory filesystem devvp so that it can be reused. */ static int mfs_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { register struct vnode *vp = ap->a_vp; FREE(vp->v_data, M_MFSNODE); vp->v_data = NULL; return (0); } /* * Print out the contents of an mfsnode. */ static int mfs_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { register struct mfsnode *mfsp = VTOMFS(ap->a_vp); printf("tag VT_MFS, pid %ld, base %p, size %ld\n", mfsp->mfs_pid, mfsp->mfs_baseoff, mfsp->mfs_size); return (0); } /* * Block device bad operation */ static int mfs_badop() { panic("mfs_badop called"); /* NOTREACHED */ } diff --git a/sys/ufs/mfs/mfsnode.h b/sys/ufs/mfs/mfsnode.h index eb0a510ff6f1..53fe40a897ce 100644 --- a/sys/ufs/mfs/mfsnode.h +++ b/sys/ufs/mfs/mfsnode.h @@ -1,93 +1,93 @@ /* * 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. * * @(#)mfsnode.h 8.3 (Berkeley) 5/19/95 - * $Id$ + * $Id: mfsnode.h,v 1.8 1997/02/22 09:47:34 peter Exp $ */ #ifndef _UFS_MFS_MFSNODE_H_ #define _UFS_MFS_MFSNODE_H_ /* * This structure defines the control data for the memory based file system. */ struct mfsnode { struct vnode *mfs_vnode; /* vnode associated with this mfsnode */ caddr_t mfs_baseoff; /* base of file system in memory */ long mfs_size; /* size of memory file system */ pid_t mfs_pid; /* supporting process pid */ struct buf_queue_head buf_queue; /* list of I/O requests */ int mfs_active; long mfs_spare[1]; }; /* * Convert between mfsnode pointers and vnode pointers */ #define VTOMFS(vp) ((struct mfsnode *)(vp)->v_data) #define MFSTOV(mfsp) ((mfsp)->mfs_vnode) /* Prototypes for MFS operations on vnodes. */ #define mfs_lookup ((int (*) __P((struct vop_lookup_args *)))mfs_badop) #define mfs_create ((int (*) __P((struct vop_create_args *)))mfs_badop) #define mfs_mknod ((int (*) __P((struct vop_mknod_args *)))mfs_badop) #define mfs_access ((int (*) __P((struct vop_access_args *)))mfs_badop) #define mfs_getattr ((int (*) __P((struct vop_getattr_args *)))mfs_badop) #define mfs_setattr ((int (*) __P((struct vop_setattr_args *)))mfs_badop) #define mfs_read ((int (*) __P((struct vop_read_args *)))mfs_badop) #define mfs_write ((int (*) __P((struct vop_write_args *)))mfs_badop) -#define mfs_select ((int (*) __P((struct vop_select_args *)))mfs_badop) +#define mfs_poll ((int (*) __P((struct vop_poll_args *)))mfs_badop) #define mfs_mmap ((int (*) __P((struct vop_mmap_args *)))mfs_badop) #define mfs_seek ((int (*) __P((struct vop_seek_args *)))mfs_badop) #define mfs_remove ((int (*) __P((struct vop_remove_args *)))mfs_badop) #define mfs_link ((int (*) __P((struct vop_link_args *)))mfs_badop) #define mfs_rename ((int (*) __P((struct vop_rename_args *)))mfs_badop) #define mfs_mkdir ((int (*) __P((struct vop_mkdir_args *)))mfs_badop) #define mfs_rmdir ((int (*) __P((struct vop_rmdir_args *)))mfs_badop) #define mfs_symlink ((int (*) __P((struct vop_symlink_args *)))mfs_badop) #define mfs_readdir ((int (*) __P((struct vop_readdir_args *)))mfs_badop) #define mfs_readlink ((int (*) __P((struct vop_readlink_args *)))mfs_badop) #define mfs_abortop ((int (*) __P((struct vop_abortop_args *)))mfs_badop) #define mfs_lock ((int (*) __P((struct vop_lock_args *)))vop_nolock) #define mfs_unlock ((int (*) __P((struct vop_unlock_args *)))vop_nounlock) #define mfs_islocked ((int(*) __P((struct vop_islocked_args *)))vop_noislocked) #define mfs_pathconf ((int (*) __P((struct vop_pathconf_args *)))mfs_badop) #define mfs_advlock ((int (*) __P((struct vop_advlock_args *)))mfs_badop) #define mfs_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))mfs_badop) #define mfs_valloc ((int (*) __P((struct vop_valloc_args *)))mfs_badop) #define mfs_vfree ((int (*) __P((struct vop_vfree_args *)))mfs_badop) #define mfs_truncate ((int (*) __P((struct vop_truncate_args *)))mfs_badop) #define mfs_update ((int (*) __P((struct vop_update_args *)))mfs_badop) #define mfs_bwrite ((int (*) __P((struct vop_bwrite_args *)))vn_bwrite) #endif diff --git a/sys/ufs/ufs/ufs_extern.h b/sys/ufs/ufs/ufs_extern.h index 690e31c2366e..b4880addb173 100644 --- a/sys/ufs/ufs/ufs_extern.h +++ b/sys/ufs/ufs/ufs_extern.h @@ -1,129 +1,129 @@ /*- * Copyright (c) 1991, 1993, 1994 * 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. * * @(#)ufs_extern.h 8.10 (Berkeley) 5/14/95 - * $Id: ufs_extern.h,v 1.15 1997/08/16 19:16:26 wollman Exp $ + * $Id: ufs_extern.h,v 1.16 1997/08/26 07:32:50 phk Exp $ */ #ifndef _UFS_UFS_EXTERN_H_ #define _UFS_UFS_EXTERN_H_ struct buf; struct direct; struct fid; struct flock; struct inode; struct mount; struct nameidata; struct proc; struct sockaddr; struct ucred; struct uio; struct vattr; struct vfsconf; struct vnode; int ufs_abortop __P((struct vop_abortop_args *)); int ufs_access __P((struct vop_access_args *)); int ufs_advlock __P((struct vop_advlock_args *)); int ufs_bmap __P((struct vop_bmap_args *)); int ufs_bmaparray __P((struct vnode *, daddr_t, daddr_t *, struct indir *, int *, int *, int *)); int ufs_check_export __P((struct mount *, struct ufid *, struct sockaddr *, struct vnode **, int *exflagsp, struct ucred **)); int ufs_checkpath __P((struct inode *, struct inode *, struct ucred *)); int ufs_close __P((struct vop_close_args *)); int ufs_create __P((struct vop_create_args *)); void ufs_dirbad __P((struct inode *, doff_t, char *)); int ufs_dirbadentry __P((struct vnode *, struct direct *, int)); int ufs_dirempty __P((struct inode *, ino_t, struct ucred *)); int ufs_direnter __P((struct inode *, struct vnode *,struct componentname *)); int ufs_direnter2 __P((struct vnode *, struct direct *, struct ucred *, struct proc *)); int ufs_dirremove __P((struct vnode *, struct componentname*)); int ufs_dirrewrite __P((struct inode *, struct inode *, struct componentname *)); int ufs_getattr __P((struct vop_getattr_args *)); int ufs_getlbns __P((struct vnode *, ufs_daddr_t, struct indir *, int *)); struct vnode * ufs_ihashget __P((dev_t, ino_t)); void ufs_ihashinit __P((void)); void ufs_ihashins __P((struct inode *)); struct vnode * ufs_ihashlookup __P((dev_t, ino_t)); void ufs_ihashrem __P((struct inode *)); int ufs_inactive __P((struct vop_inactive_args *)); int ufs_init __P((struct vfsconf *)); int ufs_ioctl __P((struct vop_ioctl_args *)); int ufs_islocked __P((struct vop_islocked_args *)); #ifdef NFS #define ufs_lease_check lease_check #else #define ufs_lease_check ((int (*) __P((struct vop_lease_args *)))nullop) #endif int ufs_link __P((struct vop_link_args *)); int ufs_lock __P((struct vop_lock_args *)); int ufs_lookup __P((struct vop_cachedlookup_args *)); int ufs_makeinode __P((int mode, struct vnode *, struct vnode **, struct componentname *)); int ufs_mkdir __P((struct vop_mkdir_args *)); int ufs_mknod __P((struct vop_mknod_args *)); int ufs_mmap __P((struct vop_mmap_args *)); int ufs_open __P((struct vop_open_args *)); int ufs_pathconf __P((struct vop_pathconf_args *)); int ufs_print __P((struct vop_print_args *)); int ufs_readdir __P((struct vop_readdir_args *)); int ufs_readlink __P((struct vop_readlink_args *)); int ufs_reclaim __P((struct vnode *, struct proc *)); int ufs_remove __P((struct vop_remove_args *)); int ufs_rename __P((struct vop_rename_args *)); #define ufs_revoke vop_revoke int ufs_rmdir __P((struct vop_rmdir_args *)); int ufs_root __P((struct mount *, struct vnode **)); int ufs_seek __P((struct vop_seek_args *)); -int ufs_select __P((struct vop_select_args *)); +#define ufs_poll vop_nopoll int ufs_setattr __P((struct vop_setattr_args *)); int ufs_start __P((struct mount *, int, struct proc *)); int ufs_strategy __P((struct vop_strategy_args *)); int ufs_symlink __P((struct vop_symlink_args *)); int ufs_unlock __P((struct vop_unlock_args *)); int ufs_vinit __P((struct mount *, vop_t **, vop_t **, struct vnode **)); int ufs_whiteout __P((struct vop_whiteout_args *)); int ufsspec_close __P((struct vop_close_args *)); int ufsspec_read __P((struct vop_read_args *)); int ufsspec_write __P((struct vop_write_args *)); int ufsfifo_read __P((struct vop_read_args *)); int ufsfifo_write __P((struct vop_write_args *)); int ufsfifo_close __P((struct vop_close_args *)); #endif /* !_UFS_UFS_EXTERN_H_ */ diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c index 471977f5e79e..5576990578f1 100644 --- a/sys/ufs/ufs/ufs_vnops.c +++ b/sys/ufs/ufs/ufs_vnops.c @@ -1,2178 +1,2160 @@ /* * Copyright (c) 1982, 1986, 1989, 1993, 1995 * 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. * * @(#)ufs_vnops.c 8.27 (Berkeley) 5/27/95 - * $Id: ufs_vnops.c,v 1.55 1997/08/26 04:36:27 dyson Exp $ + * $Id: ufs_vnops.c,v 1.56 1997/09/02 20:06:59 bde Exp $ */ #include "opt_quota.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int ufs_chmod __P((struct vnode *, int, struct ucred *, struct proc *)); static int ufs_chown __P((struct vnode *, uid_t, gid_t, struct ucred *, struct proc *)); #ifdef EXT2FS #include #include #endif /* EXT2FS */ union _qcvt { int64_t qcvt; int32_t val[2]; }; #define SETHIGH(q, h) { \ union _qcvt tmp; \ tmp.qcvt = (q); \ tmp.val[_QUAD_HIGHWORD] = (h); \ (q) = tmp.qcvt; \ } #define SETLOW(q, l) { \ union _qcvt tmp; \ tmp.qcvt = (q); \ tmp.val[_QUAD_LOWWORD] = (l); \ (q) = tmp.qcvt; \ } /* * Create a regular file */ int ufs_create(ap) struct vop_create_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { int error; error = ufs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode), ap->a_dvp, ap->a_vpp, ap->a_cnp); if (error) return (error); return (0); } /* * Mknod vnode call */ /* ARGSUSED */ int ufs_mknod(ap) struct vop_mknod_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { struct vattr *vap = ap->a_vap; struct vnode **vpp = ap->a_vpp; struct inode *ip; int error; error = ufs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode), ap->a_dvp, vpp, ap->a_cnp); if (error) return (error); ip = VTOI(*vpp); ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; if (vap->va_rdev != VNOVAL) { /* * Want to be able to use this to make badblock * inodes, so don't truncate the dev number. */ ip->i_rdev = vap->va_rdev; } /* * Remove inode so that it will be reloaded by VFS_VGET and * checked to see if it is an alias of an existing entry in * the inode cache. */ vput(*vpp); (*vpp)->v_type = VNON; vgone(*vpp); *vpp = 0; return (0); } /* * Open called. * * Nothing to do. */ /* ARGSUSED */ int ufs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { /* * Files marked append-only must be opened for appending. */ if ((VTOI(ap->a_vp)->i_flags & APPEND) && (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) return (EPERM); return (0); } /* * Close called. * * Update the times on the inode. */ /* ARGSUSED */ int ufs_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); simple_lock(&vp->v_interlock); if (vp->v_usecount > 1) ITIMES(ip, &time, &time); simple_unlock(&vp->v_interlock); return (0); } int ufs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); struct ucred *cred = ap->a_cred; mode_t mask, mode = ap->a_mode; register gid_t *gp; int i; #ifdef QUOTA int error; #endif /* * Disallow write attempts on read-only file systems; * unless the file is a socket, fifo, or a block or * character device resident on the file system. */ if (mode & VWRITE) { switch (vp->v_type) { case VDIR: case VLNK: case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); #ifdef QUOTA if (error = getinoquota(ip)) return (error); #endif break; } } /* If immutable bit set, nobody gets to write it. */ if ((mode & VWRITE) && (ip->i_flags & IMMUTABLE)) return (EPERM); /* Otherwise, user id 0 always gets access. */ if (cred->cr_uid == 0) return (0); mask = 0; /* Otherwise, check the owner. */ if (cred->cr_uid == ip->i_uid) { if (mode & VEXEC) mask |= S_IXUSR; if (mode & VREAD) mask |= S_IRUSR; if (mode & VWRITE) mask |= S_IWUSR; return ((ip->i_mode & mask) == mask ? 0 : EACCES); } /* Otherwise, check the groups. */ for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) if (ip->i_gid == *gp) { if (mode & VEXEC) mask |= S_IXGRP; if (mode & VREAD) mask |= S_IRGRP; if (mode & VWRITE) mask |= S_IWGRP; return ((ip->i_mode & mask) == mask ? 0 : EACCES); } /* Otherwise, check everyone else. */ if (mode & VEXEC) mask |= S_IXOTH; if (mode & VREAD) mask |= S_IROTH; if (mode & VWRITE) mask |= S_IWOTH; return ((ip->i_mode & mask) == mask ? 0 : EACCES); } /* ARGSUSED */ int ufs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); register struct vattr *vap = ap->a_vap; ITIMES(ip, &time, &time); /* * Copy from inode table */ vap->va_fsid = ip->i_dev; vap->va_fileid = ip->i_number; vap->va_mode = ip->i_mode & ~IFMT; vap->va_nlink = ip->i_nlink; vap->va_uid = ip->i_uid; vap->va_gid = ip->i_gid; vap->va_rdev = (dev_t)ip->i_rdev; vap->va_size = ip->i_din.di_size; vap->va_atime.tv_sec = ip->i_atime; vap->va_atime.tv_nsec = ip->i_atimensec; vap->va_mtime.tv_sec = ip->i_mtime; vap->va_mtime.tv_nsec = ip->i_mtimensec; vap->va_ctime.tv_sec = ip->i_ctime; vap->va_ctime.tv_nsec = ip->i_ctimensec; vap->va_flags = ip->i_flags; vap->va_gen = ip->i_gen; /* this doesn't belong here */ if (vp->v_type == VBLK) vap->va_blocksize = BLKDEV_IOSIZE; else if (vp->v_type == VCHR) vap->va_blocksize = MAXBSIZE; else vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize; vap->va_bytes = dbtob((u_quad_t)ip->i_blocks); vap->va_type = vp->v_type; vap->va_filerev = ip->i_modrev; return (0); } /* * Set attribute vnode op. called from several syscalls */ int ufs_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vattr *vap = ap->a_vap; struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); struct ucred *cred = ap->a_cred; struct proc *p = ap->a_p; struct timeval atimeval, mtimeval; int error; /* * Check for unsettable attributes. */ if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { return (EINVAL); } if (vap->va_flags != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != ip->i_uid && (error = suser(cred, &p->p_acflag))) return (error); if (cred->cr_uid == 0) { if ((ip->i_flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) && securelevel > 0) return (EPERM); ip->i_flags = vap->va_flags; } else { if (ip->i_flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) || (vap->va_flags & UF_SETTABLE) != vap->va_flags) return (EPERM); ip->i_flags &= SF_SETTABLE; ip->i_flags |= (vap->va_flags & UF_SETTABLE); } ip->i_flag |= IN_CHANGE; if (vap->va_flags & (IMMUTABLE | APPEND)) return (0); } if (ip->i_flags & (IMMUTABLE | APPEND)) return (EPERM); /* * Go through the fields and update iff not VNOVAL. */ if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (error = ufs_chown(vp, vap->va_uid, vap->va_gid, cred, p)) return (error); } if (vap->va_size != VNOVAL) { /* * Disallow write attempts on read-only file systems; * unless the file is a socket, fifo, or a block or * character device resident on the file system. */ switch (vp->v_type) { case VDIR: return (EISDIR); case VLNK: case VREG: if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); break; } if (error = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p)) return (error); } ip = VTOI(vp); if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (cred->cr_uid != ip->i_uid && (error = suser(cred, &p->p_acflag)) && ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || (error = VOP_ACCESS(vp, VWRITE, cred, p)))) return (error); if (vap->va_atime.tv_sec != VNOVAL) ip->i_flag |= IN_ACCESS; if (vap->va_mtime.tv_sec != VNOVAL) ip->i_flag |= IN_CHANGE | IN_UPDATE; atimeval.tv_sec = vap->va_atime.tv_sec; atimeval.tv_usec = vap->va_atime.tv_nsec / 1000; mtimeval.tv_sec = vap->va_mtime.tv_sec; mtimeval.tv_usec = vap->va_mtime.tv_nsec / 1000; error = VOP_UPDATE(vp, &atimeval, &mtimeval, 1); if (error) return (error); } error = 0; if (vap->va_mode != (mode_t)VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); error = ufs_chmod(vp, (int)vap->va_mode, cred, p); } return (error); } /* * Change the mode on a file. * Inode must be locked before calling. */ static int ufs_chmod(vp, mode, cred, p) register struct vnode *vp; register int mode; register struct ucred *cred; struct proc *p; { register struct inode *ip = VTOI(vp); int error; if (cred->cr_uid != ip->i_uid) { error = suser(cred, &p->p_acflag); if (error) return (error); } if (cred->cr_uid) { if (vp->v_type != VDIR && (mode & S_ISTXT)) return (EFTYPE); if (!groupmember(ip->i_gid, cred) && (mode & ISGID)) return (EPERM); } ip->i_mode &= ~ALLPERMS; ip->i_mode |= (mode & ALLPERMS); ip->i_flag |= IN_CHANGE; return (0); } /* * Perform chown operation on inode ip; * inode must be locked prior to call. */ static int ufs_chown(vp, uid, gid, cred, p) register struct vnode *vp; uid_t uid; gid_t gid; struct ucred *cred; struct proc *p; { register struct inode *ip = VTOI(vp); uid_t ouid; gid_t ogid; int error = 0; #ifdef QUOTA register int i; long change; #endif if (uid == (uid_t)VNOVAL) uid = ip->i_uid; if (gid == (gid_t)VNOVAL) gid = ip->i_gid; /* * If we don't own the file, are trying to change the owner * of the file, or are not a member of the target group, * the caller must be superuser or the call fails. */ if ((cred->cr_uid != ip->i_uid || uid != ip->i_uid || (gid != ip->i_gid && !groupmember((gid_t)gid, cred))) && (error = suser(cred, &p->p_acflag))) return (error); ogid = ip->i_gid; ouid = ip->i_uid; #ifdef QUOTA if (error = getinoquota(ip)) return (error); if (ouid == uid) { dqrele(vp, ip->i_dquot[USRQUOTA]); ip->i_dquot[USRQUOTA] = NODQUOT; } if (ogid == gid) { dqrele(vp, ip->i_dquot[GRPQUOTA]); ip->i_dquot[GRPQUOTA] = NODQUOT; } change = ip->i_blocks; (void) chkdq(ip, -change, cred, CHOWN); (void) chkiq(ip, -1, cred, CHOWN); for (i = 0; i < MAXQUOTAS; i++) { dqrele(vp, ip->i_dquot[i]); ip->i_dquot[i] = NODQUOT; } #endif ip->i_gid = gid; ip->i_uid = uid; #ifdef QUOTA if ((error = getinoquota(ip)) == 0) { if (ouid == uid) { dqrele(vp, ip->i_dquot[USRQUOTA]); ip->i_dquot[USRQUOTA] = NODQUOT; } if (ogid == gid) { dqrele(vp, ip->i_dquot[GRPQUOTA]); ip->i_dquot[GRPQUOTA] = NODQUOT; } if ((error = chkdq(ip, change, cred, CHOWN)) == 0) { if ((error = chkiq(ip, 1, cred, CHOWN)) == 0) goto good; else (void) chkdq(ip, -change, cred, CHOWN|FORCE); } for (i = 0; i < MAXQUOTAS; i++) { dqrele(vp, ip->i_dquot[i]); ip->i_dquot[i] = NODQUOT; } } ip->i_gid = ogid; ip->i_uid = ouid; if (getinoquota(ip) == 0) { if (ouid == uid) { dqrele(vp, ip->i_dquot[USRQUOTA]); ip->i_dquot[USRQUOTA] = NODQUOT; } if (ogid == gid) { dqrele(vp, ip->i_dquot[GRPQUOTA]); ip->i_dquot[GRPQUOTA] = NODQUOT; } (void) chkdq(ip, change, cred, FORCE|CHOWN); (void) chkiq(ip, 1, cred, FORCE|CHOWN); (void) getinoquota(ip); } return (error); good: if (getinoquota(ip)) panic("ufs_chown: lost quota"); #endif /* QUOTA */ ip->i_flag |= IN_CHANGE; if (cred->cr_uid != 0 && (ouid != uid || ogid != gid)) ip->i_mode &= ~(ISUID | ISGID); return (0); } /* ARGSUSED */ int ufs_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; int a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (ENOTTY); } -/* ARGSUSED */ -int -ufs_select(ap) - struct vop_select_args /* { - struct vnode *a_vp; - int a_which; - int a_fflags; - struct ucred *a_cred; - struct proc *a_p; - } */ *ap; -{ - - /* - * We should really check to see if I/O is possible. - */ - return (1); -} - /* * Mmap a file * * NB Currently unsupported. */ /* ARGSUSED */ int ufs_mmap(ap) struct vop_mmap_args /* { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (EINVAL); } /* * Seek on a file * * Nothing to do, so just return. */ /* ARGSUSED */ int ufs_seek(ap) struct vop_seek_args /* { struct vnode *a_vp; off_t a_oldoff; off_t a_newoff; struct ucred *a_cred; } */ *ap; { return (0); } int ufs_remove(ap) struct vop_remove_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { struct inode *ip; struct vnode *vp = ap->a_vp; struct vnode *dvp = ap->a_dvp; int error; ip = VTOI(vp); if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) || (VTOI(dvp)->i_flags & APPEND)) { error = EPERM; goto out; } #ifdef EXT2FS if (IS_EXT2_VNODE(dvp)) { error = ext2_dirremove(dvp, ap->a_cnp); } else { error = ufs_dirremove(dvp, ap->a_cnp); } #else error = ufs_dirremove(dvp, ap->a_cnp); #endif /* EXT2FS */ if (error == 0) { ip->i_nlink--; ip->i_flag |= IN_CHANGE; } out: if (dvp == vp) vrele(vp); else vput(vp); vput(dvp); return (error); } /* * link vnode call */ int ufs_link(ap) struct vop_link_args /* { struct vnode *a_tdvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { struct vnode *vp = ap->a_vp; struct vnode *tdvp = ap->a_tdvp; struct componentname *cnp = ap->a_cnp; struct proc *p = cnp->cn_proc; struct inode *ip; struct timeval tv; int error; #ifdef DIAGNOSTIC if ((cnp->cn_flags & HASBUF) == 0) panic("ufs_link: no name"); #endif if (tdvp->v_mount != vp->v_mount) { VOP_ABORTOP(tdvp, cnp); error = EXDEV; goto out2; } if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) { VOP_ABORTOP(tdvp, cnp); goto out2; } ip = VTOI(vp); if ((nlink_t)ip->i_nlink >= LINK_MAX) { VOP_ABORTOP(tdvp, cnp); error = EMLINK; goto out1; } if (ip->i_flags & (IMMUTABLE | APPEND)) { VOP_ABORTOP(tdvp, cnp); error = EPERM; goto out1; } ip->i_nlink++; ip->i_flag |= IN_CHANGE; gettime(&tv); error = VOP_UPDATE(vp, &tv, &tv, 1); if (!error) { #ifdef EXT2FS if (IS_EXT2_VNODE(tdvp)) { error = ext2_direnter(ip, tdvp, cnp); } else { error = ufs_direnter(ip, tdvp, cnp); } #else error = ufs_direnter(ip, tdvp, cnp); #endif /* EXT2FS */ } if (error) { ip->i_nlink--; ip->i_flag |= IN_CHANGE; } FREE(cnp->cn_pnbuf, M_NAMEI); out1: if (tdvp != vp) VOP_UNLOCK(vp, 0, p); out2: vput(tdvp); return (error); } /* * whiteout vnode call */ int ufs_whiteout(ap) struct vop_whiteout_args /* { struct vnode *a_dvp; struct componentname *a_cnp; int a_flags; } */ *ap; { struct vnode *dvp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; struct direct newdir; int error = 0; switch (ap->a_flags) { case LOOKUP: /* 4.4 format directories support whiteout operations */ if (dvp->v_mount->mnt_maxsymlinklen > 0) return (0); return (EOPNOTSUPP); case CREATE: /* create a new directory whiteout */ #ifdef DIAGNOSTIC if ((cnp->cn_flags & SAVENAME) == 0) panic("ufs_whiteout: missing name"); if (dvp->v_mount->mnt_maxsymlinklen <= 0) panic("ufs_whiteout: old format filesystem"); #endif newdir.d_ino = WINO; newdir.d_namlen = cnp->cn_namelen; bcopy(cnp->cn_nameptr, newdir.d_name, (unsigned)cnp->cn_namelen + 1); newdir.d_type = DT_WHT; error = ufs_direnter2(dvp, &newdir, cnp->cn_cred, cnp->cn_proc); break; case DELETE: /* remove an existing directory whiteout */ #ifdef DIAGNOSTIC if (dvp->v_mount->mnt_maxsymlinklen <= 0) panic("ufs_whiteout: old format filesystem"); #endif cnp->cn_flags &= ~DOWHITEOUT; error = ufs_dirremove(dvp, cnp); break; } if (cnp->cn_flags & HASBUF) { FREE(cnp->cn_pnbuf, M_NAMEI); cnp->cn_flags &= ~HASBUF; } return (error); } /* * Rename system call. * rename("foo", "bar"); * is essentially * unlink("bar"); * link("foo", "bar"); * unlink("foo"); * but ``atomically''. Can't do full commit without saving state in the * inode on disk which isn't feasible at this time. Best we can do is * always guarantee the target exists. * * Basic algorithm is: * * 1) Bump link count on source while we're linking it to the * target. This also ensure the inode won't be deleted out * from underneath us while we work (it may be truncated by * a concurrent `trunc' or `open' for creation). * 2) Link source to destination. If destination already exists, * delete it first. * 3) Unlink source reference to inode if still around. If a * directory was moved and the parent of the destination * is different from the source, patch the ".." entry in the * directory. */ int ufs_rename(ap) struct vop_rename_args /* { struct vnode *a_fdvp; struct vnode *a_fvp; struct componentname *a_fcnp; struct vnode *a_tdvp; struct vnode *a_tvp; struct componentname *a_tcnp; } */ *ap; { struct vnode *tvp = ap->a_tvp; register struct vnode *tdvp = ap->a_tdvp; struct vnode *fvp = ap->a_fvp; struct vnode *fdvp = ap->a_fdvp; struct componentname *tcnp = ap->a_tcnp; struct componentname *fcnp = ap->a_fcnp; struct proc *p = fcnp->cn_proc; struct inode *ip, *xp, *dp; struct dirtemplate dirbuf; struct timeval tv; int doingdirectory = 0, oldparent = 0, newparent = 0; int error = 0; u_char namlen; #ifdef DIAGNOSTIC if ((tcnp->cn_flags & HASBUF) == 0 || (fcnp->cn_flags & HASBUF) == 0) panic("ufs_rename: no name"); #endif /* * Check for cross-device rename. */ if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { error = EXDEV; abortit: VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */ if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */ vrele(fdvp); vrele(fvp); return (error); } if (tvp && ((VTOI(tvp)->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) || (VTOI(tdvp)->i_flags & APPEND))) { error = EPERM; goto abortit; } /* * Check if just deleting a link name or if we've lost a race. * If another process completes the same rename after we've looked * up the source and have blocked looking up the target, then the * source and target inodes may be identical now although the * names were never linked. */ if (fvp == tvp) { if (fvp->v_type == VDIR) { /* * Linked directories are impossible, so we must * have lost the race. Pretend that the rename * completed before the lookup. */ #ifdef UFS_RENAME_DEBUG printf("ufs_rename: fvp == tvp for directories\n"); #endif error = ENOENT; goto abortit; } /* Release destination completely. */ VOP_ABORTOP(tdvp, tcnp); vput(tdvp); vput(tvp); /* * Delete source. There is another race now that everything * is unlocked, but this doesn't cause any new complications. * Relookup() may find a file that is unrelated to the * original one, or it may fail. Too bad. */ vrele(fdvp); vrele(fvp); fcnp->cn_flags &= ~MODMASK; fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; if ((fcnp->cn_flags & SAVESTART) == 0) panic("ufs_rename: lost from startdir"); fcnp->cn_nameiop = DELETE; VREF(fdvp); error = relookup(fdvp, &fvp, fcnp); if (error == 0) vrele(fdvp); if (fvp == NULL) { #ifdef UFS_RENAME_DEBUG printf("ufs_rename: from name disappeared\n"); #endif return (ENOENT); } return (VOP_REMOVE(fdvp, fvp, fcnp)); } if (error = vn_lock(fvp, LK_EXCLUSIVE, p)) goto abortit; dp = VTOI(fdvp); ip = VTOI(fvp); if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) || (dp->i_flags & APPEND)) { VOP_UNLOCK(fvp, 0, p); error = EPERM; goto abortit; } if ((ip->i_mode & IFMT) == IFDIR) { /* * Avoid ".", "..", and aliases of "." for obvious reasons. */ if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || dp == ip || (fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT || (ip->i_flag & IN_RENAME)) { VOP_UNLOCK(fvp, 0, p); error = EINVAL; goto abortit; } ip->i_flag |= IN_RENAME; oldparent = dp->i_number; doingdirectory++; } vrele(fdvp); /* * When the target exists, both the directory * and target vnodes are returned locked. */ dp = VTOI(tdvp); xp = NULL; if (tvp) xp = VTOI(tvp); /* * 1) Bump link count while we're moving stuff * around. If we crash somewhere before * completing our work, the link count * may be wrong, but correctable. */ ip->i_nlink++; ip->i_flag |= IN_CHANGE; gettime(&tv); if (error = VOP_UPDATE(fvp, &tv, &tv, 1)) { VOP_UNLOCK(fvp, 0, p); goto bad; } /* * If ".." must be changed (ie the directory gets a new * parent) then the source directory must not be in the * directory heirarchy above the target, as this would * orphan everything below the source directory. Also * the user must have write permission in the source so * as to be able to change "..". We must repeat the call * to namei, as the parent directory is unlocked by the * call to checkpath(). */ error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); VOP_UNLOCK(fvp, 0, p); if (oldparent != dp->i_number) newparent = dp->i_number; if (doingdirectory && newparent) { if (error) /* write access check above */ goto bad; if (xp != NULL) vput(tvp); #ifdef EXT2FS if (IS_EXT2_VNODE(tdvp)) { error = ext2_checkpath(ip, dp, tcnp->cn_cred); } else { error = ufs_checkpath(ip, dp, tcnp->cn_cred); } #else error = ufs_checkpath(ip, dp, tcnp->cn_cred); #endif /* EXT2FS */ if (error) goto out; if ((tcnp->cn_flags & SAVESTART) == 0) panic("ufs_rename: lost to startdir"); VREF(tdvp); error = relookup(tdvp, &tvp, tcnp); if (error) goto out; vrele(tdvp); dp = VTOI(tdvp); xp = NULL; if (tvp) xp = VTOI(tvp); } /* * 2) If target doesn't exist, link the target * to the source and unlink the source. * Otherwise, rewrite the target directory * entry to reference the source inode and * expunge the original entry's existence. */ if (xp == NULL) { if (dp->i_dev != ip->i_dev) panic("ufs_rename: EXDEV"); /* * Account for ".." in new directory. * When source and destination have the same * parent we don't fool with the link count. */ if (doingdirectory && newparent) { if ((nlink_t)dp->i_nlink >= LINK_MAX) { error = EMLINK; goto bad; } dp->i_nlink++; dp->i_flag |= IN_CHANGE; error = VOP_UPDATE(tdvp, &tv, &tv, 1); if (error) goto bad; } #ifdef EXT2FS if (IS_EXT2_VNODE(tdvp)) { error = ext2_direnter(ip, tdvp, tcnp); } else { error = ufs_direnter(ip, tdvp, tcnp); } #else error = ufs_direnter(ip, tdvp, tcnp); #endif /* EXT2FS */ if (error) { if (doingdirectory && newparent) { dp->i_nlink--; dp->i_flag |= IN_CHANGE; (void)VOP_UPDATE(tdvp, &tv, &tv, 1); } goto bad; } vput(tdvp); } else { if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) panic("ufs_rename: EXDEV"); /* * Short circuit rename(foo, foo). */ if (xp->i_number == ip->i_number) panic("ufs_rename: same file"); /* * If the parent directory is "sticky", then the user must * own the parent directory, or the destination of the rename, * otherwise the destination may not be changed (except by * root). This implements append-only directories. */ if ((dp->i_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 && tcnp->cn_cred->cr_uid != dp->i_uid && xp->i_uid != tcnp->cn_cred->cr_uid) { error = EPERM; goto bad; } /* * Target must be empty if a directory and have no links * to it. Also, ensure source and target are compatible * (both directories, or both not directories). */ if ((xp->i_mode&IFMT) == IFDIR) { #ifdef EXT2FS if (! (IS_EXT2_VNODE(ITOV(xp)) ? ext2_dirempty : ufs_dirempty) #else if (! ufs_dirempty #endif /* EXT2FS */ (xp, dp->i_number, tcnp->cn_cred) || xp->i_nlink > 2) { error = ENOTEMPTY; goto bad; } if (!doingdirectory) { error = ENOTDIR; goto bad; } cache_purge(tdvp); } else if (doingdirectory) { error = EISDIR; goto bad; } #ifdef EXT2FS if (IS_EXT2_VNODE(ITOV(dp))) { error = ext2_dirrewrite(dp, ip, tcnp); } else { error = ufs_dirrewrite(dp, ip, tcnp); } #else error = ufs_dirrewrite(dp, ip, tcnp); #endif /* EXT2FS */ if (error) goto bad; /* * If the target directory is in the same * directory as the source directory, * decrement the link count on the parent * of the target directory. */ if (doingdirectory && !newparent) { dp->i_nlink--; dp->i_flag |= IN_CHANGE; } vput(tdvp); /* * Adjust the link count of the target to * reflect the dirrewrite above. If this is * a directory it is empty and there are * no links to it, so we can squash the inode and * any space associated with it. We disallowed * renaming over top of a directory with links to * it above, as the remaining link would point to * a directory without "." or ".." entries. */ xp->i_nlink--; if (doingdirectory) { if (--xp->i_nlink != 0) panic("ufs_rename: linked directory"); error = VOP_TRUNCATE(tvp, (off_t)0, IO_SYNC, tcnp->cn_cred, tcnp->cn_proc); } xp->i_flag |= IN_CHANGE; vput(tvp); xp = NULL; } /* * 3) Unlink the source. */ fcnp->cn_flags &= ~MODMASK; fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; if ((fcnp->cn_flags & SAVESTART) == 0) panic("ufs_rename: lost from startdir"); VREF(fdvp); error = relookup(fdvp, &fvp, fcnp); if (error == 0) vrele(fdvp); if (fvp != NULL) { xp = VTOI(fvp); dp = VTOI(fdvp); } else { /* * From name has disappeared. */ if (doingdirectory) panic("ufs_rename: lost dir entry"); vrele(ap->a_fvp); return (0); } /* * Ensure that the directory entry still exists and has not * changed while the new name has been entered. If the source is * a file then the entry may have been unlinked or renamed. In * either case there is no further work to be done. If the source * is a directory then it cannot have been rmdir'ed; its link * count of three would cause a rmdir to fail with ENOTEMPTY. * The IN_RENAME flag ensures that it cannot be moved by another * rename. */ if (xp != ip) { if (doingdirectory) panic("ufs_rename: lost dir entry"); } else { /* * If the source is a directory with a * new parent, the link count of the old * parent directory must be decremented * and ".." set to point to the new parent. */ if (doingdirectory && newparent) { dp->i_nlink--; dp->i_flag |= IN_CHANGE; error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf, sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, tcnp->cn_cred, (int *)0, (struct proc *)0); if (error == 0) { # if (BYTE_ORDER == LITTLE_ENDIAN) if (fvp->v_mount->mnt_maxsymlinklen <= 0) namlen = dirbuf.dotdot_type; else namlen = dirbuf.dotdot_namlen; # else namlen = dirbuf.dotdot_namlen; # endif #ifdef EXT2FS if(IS_EXT2_VNODE(fvp)) namlen = ((struct odirtemplate *) &dirbuf)->dotdot_namlen; #endif /* EXT2FS */ if (namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') { ufs_dirbad(xp, (doff_t)12, "rename: mangled dir"); } else { dirbuf.dotdot_ino = newparent; (void) vn_rdwr(UIO_WRITE, fvp, (caddr_t)&dirbuf, sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_SYNC, tcnp->cn_cred, (int *)0, (struct proc *)0); cache_purge(fdvp); } } } #ifdef EXT2FS if (IS_EXT2_VNODE(fdvp)) { error = ext2_dirremove(fdvp, fcnp); } else { error = ufs_dirremove(fdvp, fcnp); } #else error = ufs_dirremove(fdvp, fcnp); #endif /* EXT2FS */ if (!error) { xp->i_nlink--; xp->i_flag |= IN_CHANGE; } xp->i_flag &= ~IN_RENAME; } if (dp) vput(fdvp); if (xp) vput(fvp); vrele(ap->a_fvp); return (error); bad: if (xp) vput(ITOV(xp)); vput(ITOV(dp)); out: if (doingdirectory) ip->i_flag &= ~IN_RENAME; if (vn_lock(fvp, LK_EXCLUSIVE, p) == 0) { ip->i_nlink--; ip->i_flag |= IN_CHANGE; ip->i_flag &= ~IN_RENAME; vput(fvp); } else vrele(fvp); return (error); } /* * A virgin directory (no blushing please). */ static struct dirtemplate mastertemplate = { 0, 12, DT_DIR, 1, { '.', 0 }, 0, DIRBLKSIZ - 12, DT_DIR, 2, { '.', '.', 0 } }; static struct odirtemplate omastertemplate = { 0, 12, 1, { '.', 0 }, 0, DIRBLKSIZ - 12, 2, { '.', '.', 0 } }; /* * Mkdir system call */ int ufs_mkdir(ap) struct vop_mkdir_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { register struct vnode *dvp = ap->a_dvp; register struct vattr *vap = ap->a_vap; register struct componentname *cnp = ap->a_cnp; register struct inode *ip, *dp; struct vnode *tvp; struct dirtemplate dirtemplate, *dtp; struct timeval tv; int error, dmode; #ifdef DIAGNOSTIC if ((cnp->cn_flags & HASBUF) == 0) panic("ufs_mkdir: no name"); #endif dp = VTOI(dvp); if ((nlink_t)dp->i_nlink >= LINK_MAX) { error = EMLINK; goto out; } dmode = vap->va_mode & 0777; dmode |= IFDIR; /* * Must simulate part of ufs_makeinode here to acquire the inode, * but not have it entered in the parent directory. The entry is * made later after writing "." and ".." entries. */ error = VOP_VALLOC(dvp, dmode, cnp->cn_cred, &tvp); if (error) goto out; ip = VTOI(tvp); ip->i_uid = cnp->cn_cred->cr_uid; ip->i_gid = dp->i_gid; #ifdef QUOTA if ((error = getinoquota(ip)) || (error = chkiq(ip, 1, cnp->cn_cred, 0))) { free(cnp->cn_pnbuf, M_NAMEI); VOP_VFREE(tvp, ip->i_number, dmode); vput(tvp); vput(dvp); return (error); } #endif ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; ip->i_mode = dmode; tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */ ip->i_nlink = 2; if (cnp->cn_flags & ISWHITEOUT) ip->i_flags |= UF_OPAQUE; gettime(&tv); error = VOP_UPDATE(tvp, &tv, &tv, 1); /* * Bump link count in parent directory * to reflect work done below. Should * be done before reference is created * so reparation is possible if we crash. */ dp->i_nlink++; dp->i_flag |= IN_CHANGE; error = VOP_UPDATE(dvp, &tv, &tv, 1); if (error) goto bad; /* Initialize directory with "." and ".." from static template. */ if (dvp->v_mount->mnt_maxsymlinklen > 0 #ifdef EXT2FS /* omastertemplate is want we want for EXT2 */ && !IS_EXT2_VNODE(dvp) #endif /* EXT2FS */ ) dtp = &mastertemplate; else dtp = (struct dirtemplate *)&omastertemplate; dirtemplate = *dtp; dirtemplate.dot_ino = ip->i_number; dirtemplate.dotdot_ino = dp->i_number; #ifdef EXT2FS /* note that in ext2 DIRBLKSIZ == blocksize, not DEV_BSIZE * so let's just redefine it - for this function only */ #undef DIRBLKSIZ #define DIRBLKSIZ (IS_EXT2_VNODE(dvp) ? \ VTOI(dvp)->i_e2fs->s_blocksize : DEV_BSIZE) if(IS_EXT2_VNODE(dvp)) dirtemplate.dotdot_reclen = DIRBLKSIZ - 12; #endif /* EXT2FS */ error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate, sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (int *)0, (struct proc *)0); if (error) { dp->i_nlink--; dp->i_flag |= IN_CHANGE; goto bad; } if (DIRBLKSIZ > VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) panic("ufs_mkdir: blksize"); /* XXX should grow with balloc() */ else { ip->i_size = DIRBLKSIZ; ip->i_flag |= IN_CHANGE; } /* Directory set up, now install it's entry in the parent directory. */ #ifdef EXT2FS if (IS_EXT2_VNODE(dvp)) { error = ext2_direnter(ip, dvp, cnp); } else { error = ufs_direnter(ip, dvp, cnp); } #else error = ufs_direnter(ip, dvp, cnp); #endif /* EXT2FS */ if (error) { dp->i_nlink--; dp->i_flag |= IN_CHANGE; } bad: /* * No need to do an explicit VOP_TRUNCATE here, vrele will do this * for us because we set the link count to 0. */ if (error) { ip->i_nlink = 0; ip->i_flag |= IN_CHANGE; vput(tvp); } else *ap->a_vpp = tvp; out: FREE(cnp->cn_pnbuf, M_NAMEI); vput(dvp); return (error); #ifdef EXT2FS #undef DIRBLKSIZ #define DIRBLKSIZ DEV_BSIZE #endif /* EXT2FS */ } /* * Rmdir system call. */ int ufs_rmdir(ap) struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { struct vnode *vp = ap->a_vp; struct vnode *dvp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; struct inode *ip, *dp; int error; ip = VTOI(vp); dp = VTOI(dvp); /* * Verify the directory is empty (and valid). * (Rmdir ".." won't be valid since * ".." will contain a reference to * the current directory and thus be * non-empty.) */ error = 0; if (ip->i_nlink != 2 || #ifdef EXT2FS !(IS_EXT2_VNODE(ITOV(ip)) ? ext2_dirempty : ufs_dirempty) (ip, dp->i_number, cnp->cn_cred)) { #else !ufs_dirempty(ip, dp->i_number, cnp->cn_cred)) { #endif /* EXT2FS */ error = ENOTEMPTY; goto out; } if ((dp->i_flags & APPEND) || (ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))) { error = EPERM; goto out; } /* * Delete reference to directory before purging * inode. If we crash in between, the directory * will be reattached to lost+found, */ #ifdef EXT2FS if (IS_EXT2_VNODE(dvp)) { error = ext2_dirremove(dvp, cnp); } else { error = ufs_dirremove(dvp, cnp); } #else error = ufs_dirremove(dvp, cnp); #endif /* EXT2FS */ if (error) goto out; dp->i_nlink--; dp->i_flag |= IN_CHANGE; cache_purge(dvp); vput(dvp); dvp = NULL; /* * Truncate inode. The only stuff left * in the directory is "." and "..". The * "." reference is inconsequential since * we're quashing it. The ".." reference * has already been adjusted above. We've * removed the "." reference and the reference * in the parent directory, but there may be * other hard links so decrement by 2 and * worry about them later. */ ip->i_nlink -= 2; error = VOP_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred, cnp->cn_proc); cache_purge(ITOV(ip)); out: if (dvp) vput(dvp); vput(vp); return (error); } /* * symlink -- make a symbolic link */ int ufs_symlink(ap) struct vop_symlink_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; char *a_target; } */ *ap; { register struct vnode *vp, **vpp = ap->a_vpp; register struct inode *ip; int len, error; error = ufs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp, vpp, ap->a_cnp); if (error) return (error); vp = *vpp; len = strlen(ap->a_target); if (len < vp->v_mount->mnt_maxsymlinklen) { ip = VTOI(vp); bcopy(ap->a_target, (char *)ip->i_shortlink, len); ip->i_size = len; ip->i_flag |= IN_CHANGE | IN_UPDATE; } else error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int *)0, (struct proc *)0); vput(vp); return (error); } /* * Vnode op for reading directories. * * The routine below assumes that the on-disk format of a directory * is the same as that defined by . If the on-disk * format changes, then it will be necessary to do a conversion * from the on-disk format that read returns to the format defined * by . */ int ufs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; int *ncookies; u_long **a_cookies; } */ *ap; { register struct uio *uio = ap->a_uio; int error; size_t count, lost; off_t off; if (ap->a_ncookies != NULL) /* * Ensure that the block is aligned. The caller can use * the cookies to determine where in the block to start. */ uio->uio_offset &= ~(DIRBLKSIZ - 1); off = uio->uio_offset; count = uio->uio_resid; /* Make sure we don't return partial entries. */ count -= (uio->uio_offset + count) & (DIRBLKSIZ -1); if (count <= 0) return (EINVAL); lost = uio->uio_resid - count; uio->uio_resid = count; uio->uio_iov->iov_len = count; # if (BYTE_ORDER == LITTLE_ENDIAN) if (ap->a_vp->v_mount->mnt_maxsymlinklen > 0) { error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred); } else { struct dirent *dp, *edp; struct uio auio; struct iovec aiov; caddr_t dirbuf; int readcnt; u_char tmp; auio = *uio; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_segflg = UIO_SYSSPACE; aiov.iov_len = count; MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK); aiov.iov_base = dirbuf; error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred); if (error == 0) { readcnt = count - auio.uio_resid; edp = (struct dirent *)&dirbuf[readcnt]; for (dp = (struct dirent *)dirbuf; dp < edp; ) { tmp = dp->d_namlen; dp->d_namlen = dp->d_type; dp->d_type = tmp; if (dp->d_reclen > 0) { dp = (struct dirent *) ((char *)dp + dp->d_reclen); } else { error = EIO; break; } } if (dp >= edp) error = uiomove(dirbuf, readcnt, uio); } FREE(dirbuf, M_TEMP); } # else error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred); # endif if (!error && ap->a_ncookies != NULL) { struct dirent* dpStart; struct dirent* dpEnd; struct dirent* dp; int ncookies; u_long *cookies; u_long *cookiep; if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) panic("ufs_readdir: unexpected uio from NFS server"); dpStart = (struct dirent *) (uio->uio_iov->iov_base - (uio->uio_offset - off)); dpEnd = (struct dirent *) uio->uio_iov->iov_base; for (dp = dpStart, ncookies = 0; dp < dpEnd; dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) ncookies++; MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK); for (dp = dpStart, cookiep = cookies; dp < dpEnd; dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) { off += dp->d_reclen; *cookiep++ = (u_long) off; } *ap->a_ncookies = ncookies; *ap->a_cookies = cookies; } uio->uio_resid += lost; if (ap->a_eofflag) *ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset; return (error); } /* * Return target name of a symbolic link */ int ufs_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); int isize; isize = ip->i_size; if ((isize < vp->v_mount->mnt_maxsymlinklen) || (ip->i_din.di_blocks == 0)) { /* XXX - for old fastlink support */ uiomove((char *)ip->i_shortlink, isize, ap->a_uio); return (0); } return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred)); } /* * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually * done. If a buffer has been saved in anticipation of a CREATE, delete it. */ /* ARGSUSED */ int ufs_abortop(ap) struct vop_abortop_args /* { struct vnode *a_dvp; struct componentname *a_cnp; } */ *ap; { if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); return (0); } /* * Lock an inode. If its already locked, set the WANT bit and sleep. */ int ufs_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags, &vp->v_interlock, ap->a_p)); } /* * Unlock an inode. */ int ufs_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock, ap->a_p)); } /* * Check for a locked inode. */ int ufs_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { return (lockstatus(&VTOI(ap->a_vp)->i_lock)); } /* * Calculate the logical to physical mapping if not done already, * then call the device strategy routine. */ int ufs_strategy(ap) struct vop_strategy_args /* { struct buf *a_bp; } */ *ap; { register struct buf *bp = ap->a_bp; register struct vnode *vp = bp->b_vp; register struct inode *ip; int error; ip = VTOI(vp); if (vp->v_type == VBLK || vp->v_type == VCHR) panic("ufs_strategy: spec"); if (bp->b_blkno == bp->b_lblkno) { error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL, NULL); if (error) { bp->b_error = error; bp->b_flags |= B_ERROR; biodone(bp); return (error); } if ((long)bp->b_blkno == -1) vfs_bio_clrbuf(bp); } if ((long)bp->b_blkno == -1) { biodone(bp); return (0); } vp = ip->i_devvp; bp->b_dev = vp->v_rdev; VOCALL (vp->v_op, VOFFSET(vop_strategy), ap); return (0); } /* * Print out the contents of an inode. */ int ufs_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); printf("tag VT_UFS, ino %ld, on dev %d, %d", ip->i_number, major(ip->i_dev), minor(ip->i_dev)); if (vp->v_type == VFIFO) fifo_printinfo(vp); lockmgr_printinfo(&ip->i_lock); printf("\n"); return (0); } /* * Read wrapper for special devices. */ int ufsspec_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { /* * Set access flag. */ VTOI(ap->a_vp)->i_flag |= IN_ACCESS; return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for special devices. */ int ufsspec_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { /* * Set update and change flags. */ VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE; return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap)); } /* * Close wrapper for special devices. * * Update the times on the inode then do device close. */ int ufsspec_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); simple_lock(&vp->v_interlock); if (ap->a_vp->v_usecount > 1) ITIMES(ip, &time, &time); simple_unlock(&vp->v_interlock); return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap)); } /* * Read wrapper for fifo's */ int ufsfifo_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { /* * Set access flag. */ VTOI(ap->a_vp)->i_flag |= IN_ACCESS; return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for fifo's. */ int ufsfifo_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { /* * Set update and change flags. */ VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE; return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_write), ap)); } /* * Close wrapper for fifo's. * * Update the times on the inode then do device close. */ int ufsfifo_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); simple_lock(&vp->v_interlock); if (ap->a_vp->v_usecount > 1) ITIMES(ip, &time, &time); simple_unlock(&vp->v_interlock); return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_close), ap)); } /* * Return POSIX pathconf information applicable to ufs filesystems. */ int ufs_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_NAME_MAX: *ap->a_retval = NAME_MAX; return (0); case _PC_PATH_MAX: *ap->a_retval = PATH_MAX; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_NO_TRUNC: *ap->a_retval = 1; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Advisory record locking support */ int ufs_advlock(ap) struct vop_advlock_args /* { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ *ap; { register struct inode *ip = VTOI(ap->a_vp); return (lf_advlock(ap, &(ip->i_lockf), ip->i_size)); } /* * Initialize the vnode associated with a new inode, handle aliased * vnodes. */ int ufs_vinit(mntp, specops, fifoops, vpp) struct mount *mntp; vop_t **specops; vop_t **fifoops; struct vnode **vpp; { struct inode *ip; struct vnode *vp, *nvp; vp = *vpp; ip = VTOI(vp); switch(vp->v_type = IFTOVT(ip->i_mode)) { case VCHR: case VBLK: vp->v_op = specops; nvp = checkalias(vp, ip->i_rdev, mntp); if (nvp) { /* * Discard unneeded vnode, but save its inode. * Note that the lock is carried over in the inode * to the replacement vnode. */ nvp->v_data = vp->v_data; vp->v_data = NULL; vp->v_op = spec_vnodeop_p; vrele(vp); vgone(vp); /* * Reinitialize aliased inode. */ vp = nvp; ip->i_vnode = vp; } break; case VFIFO: vp->v_op = fifoops; break; default: break; } if (ip->i_number == ROOTINO) vp->v_flag |= VROOT; /* * Initialize modrev times */ SETHIGH(ip->i_modrev, mono_time.tv_sec); SETLOW(ip->i_modrev, mono_time.tv_usec * 4294); *vpp = vp; return (0); } /* * Allocate a new inode. */ int ufs_makeinode(mode, dvp, vpp, cnp) int mode; struct vnode *dvp; struct vnode **vpp; struct componentname *cnp; { register struct inode *ip, *pdir; struct timeval tv; struct vnode *tvp; int error; pdir = VTOI(dvp); #ifdef DIAGNOSTIC if ((cnp->cn_flags & HASBUF) == 0) panic("ufs_makeinode: no name"); #endif *vpp = NULL; if ((mode & IFMT) == 0) mode |= IFREG; error = VOP_VALLOC(dvp, mode, cnp->cn_cred, &tvp); if (error) { free(cnp->cn_pnbuf, M_NAMEI); vput(dvp); return (error); } ip = VTOI(tvp); ip->i_gid = pdir->i_gid; ip->i_uid = cnp->cn_cred->cr_uid; #ifdef QUOTA if ((error = getinoquota(ip)) || (error = chkiq(ip, 1, cnp->cn_cred, 0))) { free(cnp->cn_pnbuf, M_NAMEI); VOP_VFREE(tvp, ip->i_number, mode); vput(tvp); vput(dvp); return (error); } #endif ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE; ip->i_mode = mode; tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */ ip->i_nlink = 1; if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, cnp->cn_cred) && suser(cnp->cn_cred, NULL)) ip->i_mode &= ~ISGID; if (cnp->cn_flags & ISWHITEOUT) ip->i_flags |= UF_OPAQUE; /* * Make sure inode goes to disk before directory entry. */ gettime(&tv); error = VOP_UPDATE(tvp, &tv, &tv, 1); if (error) goto bad; #ifdef EXT2FS if (IS_EXT2_VNODE(dvp)) { error = ext2_direnter(ip, dvp, cnp); } else { error = ufs_direnter(ip, dvp, cnp); } #else error = ufs_direnter(ip, dvp, cnp); #endif /* EXT2FS */ if (error) goto bad; if ((cnp->cn_flags & SAVESTART) == 0) FREE(cnp->cn_pnbuf, M_NAMEI); vput(dvp); *vpp = tvp; return (0); bad: /* * Write error occurred trying to update the inode * or the directory so must deallocate the inode. */ free(cnp->cn_pnbuf, M_NAMEI); vput(dvp); ip->i_nlink = 0; ip->i_flag |= IN_CHANGE; vput(tvp); return (error); }