Index: head/sys/fs/cd9660/cd9660_vfsops.c =================================================================== --- head/sys/fs/cd9660/cd9660_vfsops.c (revision 2945) +++ head/sys/fs/cd9660/cd9660_vfsops.c (revision 2946) @@ -1,686 +1,688 @@ /*- * 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_vfsops.c 8.3 (Berkeley) 1/31/94 - * $Id: cd9660_vfsops.c,v 1.5 1994/08/20 16:03:07 davidg Exp $ + * $Id: cd9660_vfsops.c,v 1.6 1994/09/15 19:46:02 bde Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct vfsops cd9660_vfsops = { cd9660_mount, cd9660_start, cd9660_unmount, cd9660_root, cd9660_quotactl, cd9660_statfs, cd9660_sync, cd9660_vget, cd9660_fhtovp, cd9660_vptofh, cd9660_init, }; +VFS_SET(cd9660_vfsops, cd9660, MOUNT_CD9660, 0); + /* * Called by vfs_mountroot when iso is going to be mounted as root. * * Name is updated by mount(8) after booting. */ #define ROOTNAME "root_device" static int iso_mountfs __P((struct vnode *devvp, struct mount *mp, struct proc *p, struct iso_args *argp)); int cd9660_mountroot() { register struct mount *mp; struct proc *p = curproc; /* XXX */ struct iso_mnt *imp; register struct fs *fs; u_int size; int error; struct iso_args args; /* * Get vnodes for swapdev and rootdev. */ if (bdevvp(swapdev, &swapdev_vp) || bdevvp(rootdev, &rootvp)) panic("cd9660_mountroot: can't setup bdevvp's"); mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); bzero((char *)mp, (u_long)sizeof(struct mount)); mp->mnt_op = &cd9660_vfsops; mp->mnt_flag = MNT_RDONLY; args.flags = ISOFSMNT_ROOT; if (error = iso_mountfs(rootvp, mp, p, &args)) { free(mp, M_MOUNT); return (error); } if (error = vfs_lock(mp)) { (void)cd9660_unmount(mp, 0, p); free(mp, M_MOUNT); return (error); } TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); mp->mnt_flag |= MNT_ROOTFS; mp->mnt_vnodecovered = NULLVP; imp = VFSTOISOFS(mp); bzero(imp->im_fsmnt, sizeof(imp->im_fsmnt)); imp->im_fsmnt[0] = '/'; bcopy((caddr_t)imp->im_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN); (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void) cd9660_statfs(mp, &mp->mnt_stat, p); vfs_unlock(mp); return (0); } /* * Flag to allow forcible unmounting. */ int iso_doforce = 1; /* * VFS Operations. * * mount system call */ int cd9660_mount(mp, path, data, ndp, p) register struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { struct vnode *devvp; struct iso_args args; u_int size; int error; struct iso_mnt *imp = 0; if (error = copyin(data, (caddr_t)&args, sizeof (struct iso_args))) return (error); if ((mp->mnt_flag & MNT_RDONLY) == 0) return (EROFS); /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { imp = VFSTOISOFS(mp); if (args.fspec == 0) return (vfs_export(mp, &imp->im_export, &args.export)); } /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible block device. */ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); if (error = namei(ndp)) return (error); devvp = ndp->ni_vp; if (devvp->v_type != VBLK) { vrele(devvp); return ENOTBLK; } if (major(devvp->v_rdev) >= nblkdev) { vrele(devvp); return ENXIO; } if ((mp->mnt_flag & MNT_UPDATE) == 0) error = iso_mountfs(devvp, mp, p, &args); else { if (devvp != imp->im_devvp) error = EINVAL; /* needs translation */ else vrele(devvp); } if (error) { vrele(devvp); return error; } imp = VFSTOISOFS(mp); (void) copyinstr(path, imp->im_fsmnt, sizeof(imp->im_fsmnt)-1, &size); bzero(imp->im_fsmnt + size, sizeof(imp->im_fsmnt) - size); bcopy((caddr_t)imp->im_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN); (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void) cd9660_statfs(mp, &mp->mnt_stat, p); return 0; } /* * Common code for mount and mountroot */ static int iso_mountfs(devvp, mp, p, argp) register struct vnode *devvp; struct mount *mp; struct proc *p; struct iso_args *argp; { register struct iso_mnt *isomp = (struct iso_mnt *)0; struct buf *bp = NULL; dev_t dev = devvp->v_rdev; caddr_t base, space; int havepart = 0, blks; int error = EINVAL, i, size; int needclose = 0; int ronly = (mp->mnt_flag & MNT_RDONLY) != 0; int j; int iso_bsize; int iso_blknum; struct iso_volume_descriptor *vdp; struct iso_primary_descriptor *pri; struct iso_directory_record *rootp; int logical_block_size; if (!ronly) return EROFS; /* * Disallow multiple mounts of the same device. * Disallow mounting of a device that is currently in use * (except for root, which might share swap device for miniroot). * Flush out any old buffers remaining from a previous use. */ if (error = vfs_mountedon(devvp)) return error; if (vcount(devvp) > 1 && devvp != rootvp) return EBUSY; if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0)) return (error); if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p)) return error; needclose = 1; /* This is the "logical sector size". The standard says this * should be 2048 or the physical sector size on the device, * whichever is greater. For now, we'll just use a constant. */ iso_bsize = ISO_DEFAULT_BLOCK_SIZE; for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) { if (error = bread (devvp, btodb(iso_blknum * iso_bsize), iso_bsize, NOCRED, &bp)) goto out; vdp = (struct iso_volume_descriptor *)bp->b_un.b_addr; if (bcmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) != 0) { error = EINVAL; goto out; } if (isonum_711 (vdp->type) == ISO_VD_END) { error = EINVAL; goto out; } if (isonum_711 (vdp->type) == ISO_VD_PRIMARY) break; brelse(bp); } if (isonum_711 (vdp->type) != ISO_VD_PRIMARY) { error = EINVAL; goto out; } pri = (struct iso_primary_descriptor *)vdp; logical_block_size = isonum_723 (pri->logical_block_size); if (logical_block_size < DEV_BSIZE || logical_block_size > MAXBSIZE || (logical_block_size & (logical_block_size - 1)) != 0) { error = EINVAL; goto out; } rootp = (struct iso_directory_record *)pri->root_directory_record; isomp = malloc(sizeof *isomp, M_ISOFSMNT, M_WAITOK); bzero((caddr_t)isomp, sizeof *isomp); isomp->logical_block_size = logical_block_size; isomp->volume_space_size = isonum_733 (pri->volume_space_size); bcopy (rootp, isomp->root, sizeof isomp->root); isomp->root_extent = isonum_733 (rootp->extent); isomp->root_size = isonum_733 (rootp->size); isomp->im_bmask = logical_block_size - 1; isomp->im_bshift = 0; while ((1 << isomp->im_bshift) < isomp->logical_block_size) isomp->im_bshift++; bp->b_flags |= B_AGE; brelse(bp); bp = NULL; mp->mnt_data = (qaddr_t)isomp; mp->mnt_stat.f_fsid.val[0] = (long)dev; mp->mnt_stat.f_fsid.val[1] = MOUNT_CD9660; mp->mnt_maxsymlinklen = 0; mp->mnt_flag |= MNT_LOCAL; isomp->im_mountp = mp; isomp->im_dev = dev; isomp->im_devvp = devvp; devvp->v_specflags |= SI_MOUNTEDON; /* Check the Rock Ridge Extention support */ if (!(argp->flags & ISOFSMNT_NORRIP)) { if (error = bread (isomp->im_devvp, (isomp->root_extent + isonum_711(rootp->ext_attr_length)) * isomp->logical_block_size / DEV_BSIZE, isomp->logical_block_size,NOCRED,&bp)) goto out; rootp = (struct iso_directory_record *)bp->b_un.b_addr; if ((isomp->rr_skip = cd9660_rrip_offset(rootp,isomp)) < 0) { argp->flags |= ISOFSMNT_NORRIP; } else { argp->flags &= ~ISOFSMNT_GENS; } /* * The contents are valid, * but they will get reread as part of another vnode, so... */ bp->b_flags |= B_AGE; brelse(bp); bp = NULL; } isomp->im_flags = argp->flags&(ISOFSMNT_NORRIP|ISOFSMNT_GENS|ISOFSMNT_EXTATT); switch (isomp->im_flags&(ISOFSMNT_NORRIP|ISOFSMNT_GENS)) { default: isomp->iso_ftype = ISO_FTYPE_DEFAULT; break; case ISOFSMNT_GENS|ISOFSMNT_NORRIP: isomp->iso_ftype = ISO_FTYPE_9660; break; case 0: isomp->iso_ftype = ISO_FTYPE_RRIP; break; } return 0; out: if (bp) brelse(bp); if (needclose) (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p); if (isomp) { free((caddr_t)isomp, M_ISOFSMNT); mp->mnt_data = (qaddr_t)0; } return error; } /* * Make a filesystem operational. * Nothing to do at the moment. */ /* ARGSUSED */ int cd9660_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return 0; } /* * unmount system call */ int cd9660_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { register struct iso_mnt *isomp; int i, error, ronly, flags = 0; if (mntflags & MNT_FORCE) { if (!iso_doforce) return (EINVAL); flags |= FORCECLOSE; } #if 0 mntflushbuf(mp, 0); if (mntinvalbuf(mp)) return EBUSY; #endif if (error = vflush(mp, NULLVP, flags)) return (error); isomp = VFSTOISOFS(mp); #ifdef ISODEVMAP if (isomp->iso_ftype == ISO_FTYPE_RRIP) iso_dunmap(isomp->im_dev); #endif isomp->im_devvp->v_specflags &= ~SI_MOUNTEDON; error = VOP_CLOSE(isomp->im_devvp, FREAD, NOCRED, p); vrele(isomp->im_devvp); free((caddr_t)isomp, M_ISOFSMNT); mp->mnt_data = (qaddr_t)0; mp->mnt_flag &= ~MNT_LOCAL; return (error); } /* * Return root of a filesystem */ int cd9660_root(mp, vpp) struct mount *mp; struct vnode **vpp; { register struct iso_node *ip; struct iso_node tip, *nip; struct vnode tvp; int error; struct iso_mnt *imp = VFSTOISOFS (mp); struct iso_directory_record *dp; tvp.v_mount = mp; tvp.v_data = &tip; ip = VTOI(&tvp); ip->i_vnode = &tvp; ip->i_dev = imp->im_dev; ip->i_diroff = 0; dp = (struct iso_directory_record *)imp->root; isodirino(&ip->i_number,dp,imp); /* * With RRIP we must use the `.' entry of the root directory. * Simply tell iget, that it's a relocated directory. */ error = iso_iget(ip,ip->i_number, imp->iso_ftype == ISO_FTYPE_RRIP, &nip,dp); if (error) return error; *vpp = ITOV(nip); return 0; } /* * Do operations associated with quotas, not supported */ /* ARGSUSED */ int cd9660_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } /* * Get file system statistics. */ int cd9660_statfs(mp, sbp, p) struct mount *mp; register struct statfs *sbp; struct proc *p; { register struct iso_mnt *isomp; register struct fs *fs; isomp = VFSTOISOFS(mp); sbp->f_type = MOUNT_CD9660; sbp->f_bsize = isomp->logical_block_size; sbp->f_iosize = sbp->f_bsize; /* XXX */ sbp->f_blocks = isomp->volume_space_size; sbp->f_bfree = 0; /* total free blocks */ sbp->f_bavail = 0; /* blocks free for non superuser */ sbp->f_files = 0; /* total files */ sbp->f_ffree = 0; /* free file nodes */ if (sbp != &mp->mnt_stat) { bcopy((caddr_t)mp->mnt_stat.f_mntonname, (caddr_t)&sbp->f_mntonname[0], MNAMELEN); bcopy((caddr_t)mp->mnt_stat.f_mntfromname, (caddr_t)&sbp->f_mntfromname[0], MNAMELEN); } /* Use the first spare for flags: */ sbp->f_spare[0] = isomp->im_flags; return 0; } /* ARGSUSED */ int cd9660_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { return (0); } /* * Flat namespace lookup. * Currently unsupported. */ /* ARGSUSED */ int cd9660_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } /* * File handle to vnode * * Have to be really careful about stale file handles: * - check that the inode number is in range * - call iget() to get the locked inode * - check for an unallocated inode (i_mode == 0) * - check that the generation number matches */ struct ifid { ushort ifid_len; ushort ifid_pad; int ifid_ino; long ifid_start; }; /* ARGSUSED */ int cd9660_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) register struct mount *mp; struct fid *fhp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred **credanonp; { struct vnode tvp; int error; int lbn, off; struct ifid *ifhp; struct iso_mnt *imp; struct buf *bp; struct iso_directory_record *dirp; struct iso_node tip, *ip, *nip; struct netcred *np; imp = VFSTOISOFS (mp); ifhp = (struct ifid *)fhp; #ifdef ISOFS_DBG printf("fhtovp: ino %d, start %ld\n", ifhp->ifid_ino, ifhp->ifid_start); #endif np = vfs_export_lookup(mp, &imp->im_export, nam); if (np == NULL) return (EACCES); lbn = iso_lblkno(imp, ifhp->ifid_ino); if (lbn >= imp->volume_space_size) { printf("fhtovp: lbn exceed volume space %d\n", lbn); return (ESTALE); } off = iso_blkoff(imp, ifhp->ifid_ino); if (off + ISO_DIRECTORY_RECORD_SIZE > imp->logical_block_size) { printf("fhtovp: crosses block boundary %d\n", off + ISO_DIRECTORY_RECORD_SIZE); return (ESTALE); } error = bread(imp->im_devvp, btodb(lbn * imp->logical_block_size), imp->logical_block_size, NOCRED, &bp); if (error) { printf("fhtovp: bread error %d\n",error); brelse(bp); return (error); } dirp = (struct iso_directory_record *)(bp->b_un.b_addr + off); if (off + isonum_711(dirp->length) > imp->logical_block_size) { brelse(bp); printf("fhtovp: directory crosses block boundary %d[off=%d/len=%d]\n", off+isonum_711(dirp->length), off, isonum_711(dirp->length)); return (ESTALE); } if (isonum_733(dirp->extent) + isonum_711(dirp->ext_attr_length) != ifhp->ifid_start) { brelse(bp); printf("fhtovp: file start miss %d vs %d\n", isonum_733(dirp->extent)+isonum_711(dirp->ext_attr_length), ifhp->ifid_start); return (ESTALE); } brelse(bp); ip = &tip; tvp.v_mount = mp; tvp.v_data = ip; ip->i_vnode = &tvp; ip->i_dev = imp->im_dev; if (error = iso_iget(ip, ifhp->ifid_ino, 0, &nip, dirp)) { *vpp = NULLVP; printf("fhtovp: failed to get inode\n"); return (error); } ip = nip; /* * XXX need generation number? */ if (ip->inode.iso_mode == 0) { iso_iput(ip); *vpp = NULLVP; printf("fhtovp: inode mode == 0\n"); return (ESTALE); } *vpp = ITOV(ip); *exflagsp = np->netc_exflags; *credanonp = &np->netc_anon; return 0; } /* * Vnode pointer to File handle */ /* ARGSUSED */ int cd9660_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { register struct iso_node *ip = VTOI(vp); register struct ifid *ifhp; register struct iso_mnt *mp = ip->i_mnt; ifhp = (struct ifid *)fhp; ifhp->ifid_len = sizeof(struct ifid); ifhp->ifid_ino = ip->i_number; ifhp->ifid_start = ip->iso_start; #ifdef ISOFS_DBG printf("vptofh: ino %d, start %ld\n", ifhp->ifid_ino,ifhp->ifid_start); #endif return 0; } Index: head/sys/fs/cd9660/cd9660_vnops.c =================================================================== --- head/sys/fs/cd9660/cd9660_vnops.c (revision 2945) +++ head/sys/fs/cd9660/cd9660_vnops.c (revision 2946) @@ -1,1041 +1,1045 @@ /*- * 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.3 (Berkeley) 1/23/94 - * $Id: cd9660_vnops.c,v 1.5 1994/09/09 11:10:59 dfr Exp $ + * $Id: cd9660_vnops.c,v 1.6 1994/09/15 19:46:03 bde Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if 0 /* * Mknod vnode call * Actually remap the device number */ cd9660_mknod(ndp, vap, cred, p) struct nameidata *ndp; struct ucred *cred; struct vattr *vap; struct proc *p; { #ifndef ISODEVMAP free(ndp->ni_pnbuf, M_NAMEI); vput(ndp->ni_dvp); vput(ndp->ni_vp); return EINVAL; #else register struct vnode *vp; struct iso_node *ip; struct iso_dnode *dp; int error; vp = ndp->ni_vp; ip = VTOI(vp); if (ip->i_mnt->iso_ftype != ISO_FTYPE_RRIP || vap->va_type != vp->v_type || (vap->va_type != VCHR && vap->va_type != VBLK)) { free(ndp->ni_pnbuf, M_NAMEI); vput(ndp->ni_dvp); vput(ndp->ni_vp); return EINVAL; } dp = iso_dmap(ip->i_dev,ip->i_number,1); if (ip->inode.iso_rdev == vap->va_rdev || vap->va_rdev == VNOVAL) { /* same as the unmapped one, delete the mapping */ remque(dp); FREE(dp,M_CACHE); } else /* enter new mapping */ dp->d_dev = vap->va_rdev; /* * Remove inode so that it will be reloaded by iget and * checked to see if it is an alias of an existing entry * in the inode cache. */ vput(vp); vp->v_type = VNON; vgone(vp); return (0); #endif } #endif /* * Open called. * * Nothing to do. */ /* ARGSUSED */ 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 */ 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 */ int cd9660_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (0); } 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); int i; 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; 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; return (0); } #if ISO_DEFAULT_BLOCK_SIZE >= NBPG #ifdef DEBUG extern int doclusterread; #else #define doclusterread 1 #endif #else /* XXX until cluster routines can handle block sizes less than one page */ #define doclusterread 0 #endif /* * Vnode op for reading. */ 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, bn, 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 |= IACC; imp = ip->i_mnt; do { lbn = iso_lblkno(imp, uio->uio_offset); on = iso_blkoff(imp, uio->uio_offset); n = min((unsigned)(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 = iso_blksize(imp, ip, lbn); rablock = lbn + 1; if (doclusterread) { if (iso_lblktosize(imp, rablock) <= ip->i_size) error = cluster_read(vp, (off_t)ip->i_size, lbn, size, NOCRED, &bp); else error = bread(vp, lbn, size, NOCRED, &bp); } else { if (vp->v_lastr + 1 == lbn && iso_lblktosize(imp, rablock) < ip->i_size) { rasize = iso_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_un.b_addr + on, (int)n, uio); brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); return (error); } /* ARGSUSED */ int cd9660_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; { printf("You did ioctl for isofs !!\n"); return (ENOTTY); } /* ARGSUSED */ 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 */ 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 */ 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; u_int *cookiep; int ncookies; int eof; }; static 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 = DIRSIZ(dp); if (idp->uio->uio_resid < dp->d_reclen) { idp->eof = 0; return -1; } if (idp->cookiep) { if (idp->ncookies <= 0) { idp->eof = 0; return -1; } *idp->cookiep++ = off; --idp->ncookies; } if (error = uiomove((caddr_t)dp,dp->d_reclen,idp->uio)) return error; idp->uio_off = off; return 0; } static 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; if (assoc = cl > 1 && *cname == ASSOCCHAR) { 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 = 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 * XXX make sure everything still works now that eofflagp and cookiep * are no longer args. */ int cd9660_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { register struct uio *uio = ap->a_uio; struct isoreaddir *idp; int entryoffsetinblock; int error = 0; int endsearch; struct iso_directory_record *ep; u_short elen; int reclen; struct iso_mnt *imp; struct iso_node *ip; struct buf *bp = NULL; u_short tmplen; ip = VTOI(ap->a_vp); imp = ip->i_mnt; MALLOC(idp,struct isoreaddir *,sizeof(*idp),M_TEMP,M_WAITOK); idp->saveent.d_namlen = 0; idp->assocent.d_namlen = 0; idp->uio = uio; #if 0 idp->cookiep = cookies; idp->ncookies = ncookies; idp->eof = 1; #else idp->cookiep = 0; #endif idp->curroff = uio->uio_offset; entryoffsetinblock = iso_blkoff(imp, idp->curroff); if (entryoffsetinblock != 0) { if (error = iso_blkatoff(ip, idp->curroff, &bp)) { FREE(idp,M_TEMP); return (error); } } endsearch = ip->i_size; while (idp->curroff < endsearch) { /* * If offset is on a block boundary, * read the next directory block. * Release previous if it exists. */ if (iso_blkoff(imp, idp->curroff) == 0) { if (bp != NULL) brelse(bp); if (error = iso_blkatoff(ip, idp->curroff, &bp)) break; entryoffsetinblock = 0; } /* * Get pointer to next entry. */ ep = (struct iso_directory_record *) (bp->b_un.b_addr + entryoffsetinblock); reclen = isonum_711 (ep->length); if (reclen == 0) { /* skip to next block, if any */ idp->curroff = roundup (idp->curroff, 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 (isonum_711(ep->flags)&2) isodirino(&idp->current.d_fileno,ep,imp); else idp->current.d_fileno = dbtob(bp->b_blkno) + idp->curroff; if (reclen < ISO_DIRECTORY_RECORD_SIZE + idp->current.d_namlen) { error = EINVAL; /* illegal entry, stop */ break; } idp->curroff += reclen; /* * */ switch (imp->iso_ftype) { case ISO_FTYPE_RRIP: cd9660_rrip_getname(ep,idp->current.d_name, &tmplen, &idp->current.d_fileno,imp); idp->current.d_namlen = tmplen; if (idp->current.d_namlen) error = iso_uiodir(idp,&idp->current,idp->curroff); break; default: /* ISO_FTYPE_DEFAULT || ISO_FTYPE_9660 */ 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, &elen, imp->iso_ftype == ISO_FTYPE_9660, isonum_711(ep->flags)&4); idp->current.d_namlen = (u_char)elen; 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 (bp) brelse (bp); uio->uio_offset = idp->uio_off; #if 0 *eofflagp = idp->eof; #endif 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; 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; u_short symlen; int error; char *symname; ino_t ino; ip = VTOI(ap->a_vp); imp = ip->i_mnt; if (imp->iso_ftype != ISO_FTYPE_RRIP) return EINVAL; /* * Get parents directory record block that this inode included. */ error = bread(imp->im_devvp, iso_dblkno(imp, ip->i_number), imp->logical_block_size, NOCRED, &bp); if (error) { brelse(bp); return EINVAL; } /* * Setup the directory pointer for this inode */ dirp = (ISODIR *)(bp->b_un.b_addr + (ip->i_number & imp->im_bmask)); #ifdef DEBUG printf("lbn=%d,off=%d,bsize=%d,DEV_BSIZE=%d, dirp= %08x, b_addr=%08x, offset=%08x(%08x)\n", (daddr_t)(ip->i_number >> imp->im_bshift), ip->i_number & imp->im_bmask, imp->logical_block_size, DEV_BSIZE, dirp, bp->b_un.b_addr, ip->i_number, ip->i_number & imp->im_bmask ); #endif /* * 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) > imp->logical_block_size) { brelse(bp); return EINVAL; } /* * Now get a buffer * Abuse a namei buffer for now. */ 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) { 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. */ error = uiomove(symname,symlen,ap->a_uio); FREE(symname,M_NAMEI); return error; } /* * 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. */ 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. */ int cd9660_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; } */ *ap; { register struct iso_node *ip = VTOI(ap->a_vp); ISO_ILOCK(ip); return 0; } /* * Unlock an inode. */ int cd9660_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; } */ *ap; { register struct iso_node *ip = VTOI(ap->a_vp); if (!(ip->i_flag & ILOCKED)) panic("cd9660_unlock NOT LOCKED"); ISO_IUNLOCK(ip); return 0; } /* * Check for a locked inode. */ int cd9660_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { if (VTOI(ap->a_vp)->i_flag & ILOCKED) return 1; return 0; } /* * Calculate the logical to physical mapping if not done already, * then call the device strategy routine. */ 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)) { 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. */ int cd9660_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_ISOFS, isofs vnode\n"); return 0; } /* * Unsupported operation */ int cd9660_enotsupp() { return (EOPNOTSUPP); } /* * Global vfs data structures for isofs */ #define cd9660_create \ ((int (*) __P((struct vop_create_args *)))cd9660_enotsupp) #define cd9660_mknod ((int (*) __P((struct vop_mknod_args *)))cd9660_enotsupp) #define cd9660_setattr \ ((int (*) __P((struct vop_setattr_args *)))cd9660_enotsupp) #define cd9660_write ((int (*) __P((struct vop_write_args *)))cd9660_enotsupp) #define cd9660_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define cd9660_remove \ ((int (*) __P((struct vop_remove_args *)))cd9660_enotsupp) #define cd9660_link ((int (*) __P((struct vop_link_args *)))cd9660_enotsupp) #define cd9660_rename \ ((int (*) __P((struct vop_rename_args *)))cd9660_enotsupp) #define cd9660_mkdir ((int (*) __P((struct vop_mkdir_args *)))cd9660_enotsupp) #define cd9660_rmdir ((int (*) __P((struct vop_rmdir_args *)))cd9660_enotsupp) #define cd9660_symlink \ ((int (*) __P((struct vop_symlink_args *)))cd9660_enotsupp) #define cd9660_pathconf \ ((int (*) __P((struct vop_pathconf_args *)))cd9660_enotsupp) #define cd9660_advlock \ ((int (*) __P((struct vop_advlock_args *)))cd9660_enotsupp) #define cd9660_blkatoff \ ((int (*) __P((struct vop_blkatoff_args *)))cd9660_enotsupp) #define cd9660_valloc ((int(*) __P(( \ struct vnode *pvp, \ int mode, \ struct ucred *cred, \ struct vnode **vpp))) cd9660_enotsupp) #define cd9660_vfree ((int (*) __P((struct vop_vfree_args *)))cd9660_enotsupp) #define cd9660_truncate \ ((int (*) __P((struct vop_truncate_args *)))cd9660_enotsupp) #define cd9660_update \ ((int (*) __P((struct vop_update_args *)))cd9660_enotsupp) #define cd9660_bwrite \ ((int (*) __P((struct vop_bwrite_args *)))cd9660_enotsupp) /* * Global vfs data structures for nfs */ int (**cd9660_vnodeop_p)(); struct vnodeopv_entry_desc cd9660_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, cd9660_lookup }, /* lookup */ { &vop_create_desc, cd9660_create }, /* create */ { &vop_mknod_desc, cd9660_mknod }, /* mknod */ { &vop_open_desc, cd9660_open }, /* open */ { &vop_close_desc, cd9660_close }, /* close */ { &vop_access_desc, cd9660_access }, /* access */ { &vop_getattr_desc, cd9660_getattr }, /* getattr */ { &vop_setattr_desc, cd9660_setattr }, /* setattr */ { &vop_read_desc, cd9660_read }, /* read */ { &vop_write_desc, cd9660_write }, /* write */ { &vop_ioctl_desc, cd9660_ioctl }, /* ioctl */ { &vop_select_desc, cd9660_select }, /* select */ { &vop_mmap_desc, cd9660_mmap }, /* mmap */ { &vop_fsync_desc, cd9660_fsync }, /* fsync */ { &vop_seek_desc, cd9660_seek }, /* seek */ { &vop_remove_desc, cd9660_remove }, /* remove */ { &vop_link_desc, cd9660_link }, /* link */ { &vop_rename_desc, cd9660_rename }, /* rename */ { &vop_mkdir_desc, cd9660_mkdir }, /* mkdir */ { &vop_rmdir_desc, cd9660_rmdir }, /* rmdir */ { &vop_symlink_desc, cd9660_symlink }, /* symlink */ { &vop_readdir_desc, cd9660_readdir }, /* readdir */ { &vop_readlink_desc, cd9660_readlink },/* readlink */ { &vop_abortop_desc, cd9660_abortop }, /* abortop */ { &vop_inactive_desc, cd9660_inactive },/* inactive */ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, cd9660_lock }, /* lock */ { &vop_unlock_desc, cd9660_unlock }, /* unlock */ { &vop_bmap_desc, cd9660_bmap }, /* bmap */ { &vop_strategy_desc, cd9660_strategy },/* strategy */ { &vop_print_desc, cd9660_print }, /* print */ { &vop_islocked_desc, cd9660_islocked },/* islocked */ { &vop_pathconf_desc, cd9660_pathconf },/* pathconf */ { &vop_advlock_desc, cd9660_advlock }, /* advlock */ { &vop_blkatoff_desc, cd9660_blkatoff },/* blkatoff */ { &vop_valloc_desc, cd9660_valloc }, /* valloc */ { &vop_vfree_desc, cd9660_vfree }, /* vfree */ { &vop_truncate_desc, cd9660_truncate },/* truncate */ { &vop_update_desc, cd9660_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc cd9660_vnodeop_opv_desc = { &cd9660_vnodeop_p, cd9660_vnodeop_entries }; +VNODEOP_SET(cd9660_vnodeop_opv_desc); /* * Special device vnode ops */ int (**cd9660_specop_p)(); struct vnodeopv_entry_desc cd9660_specop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, spec_lookup }, /* lookup */ { &vop_create_desc, cd9660_create }, /* create */ { &vop_mknod_desc, cd9660_mknod }, /* mknod */ { &vop_open_desc, spec_open }, /* open */ { &vop_close_desc, spec_close }, /* close */ { &vop_access_desc, cd9660_access }, /* access */ { &vop_getattr_desc, cd9660_getattr }, /* getattr */ { &vop_setattr_desc, cd9660_setattr }, /* setattr */ { &vop_read_desc, spec_read }, /* read */ { &vop_write_desc, spec_write }, /* write */ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ { &vop_select_desc, spec_select }, /* select */ { &vop_mmap_desc, spec_mmap }, /* mmap */ { &vop_fsync_desc, spec_fsync }, /* fsync */ { &vop_seek_desc, spec_seek }, /* seek */ { &vop_remove_desc, cd9660_remove }, /* remove */ { &vop_link_desc, cd9660_link }, /* link */ { &vop_rename_desc, cd9660_rename }, /* rename */ { &vop_mkdir_desc, cd9660_mkdir }, /* mkdir */ { &vop_rmdir_desc, cd9660_rmdir }, /* rmdir */ { &vop_symlink_desc, cd9660_symlink }, /* symlink */ { &vop_readdir_desc, spec_readdir }, /* readdir */ { &vop_readlink_desc, spec_readlink }, /* readlink */ { &vop_abortop_desc, spec_abortop }, /* abortop */ { &vop_inactive_desc, cd9660_inactive },/* inactive */ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, cd9660_lock }, /* lock */ { &vop_unlock_desc, cd9660_unlock }, /* unlock */ { &vop_bmap_desc, spec_bmap }, /* bmap */ /* XXX strategy: panics, should be notsupp instead? */ { &vop_strategy_desc, cd9660_strategy },/* strategy */ { &vop_print_desc, cd9660_print }, /* print */ { &vop_islocked_desc, cd9660_islocked },/* islocked */ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ { &vop_advlock_desc, spec_advlock }, /* advlock */ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, spec_valloc }, /* valloc */ { &vop_vfree_desc, spec_vfree }, /* vfree */ { &vop_truncate_desc, spec_truncate }, /* truncate */ { &vop_update_desc, cd9660_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc cd9660_specop_opv_desc = { &cd9660_specop_p, cd9660_specop_entries }; +VNODEOP_SET(cd9660_specop_opv_desc); #ifdef FIFO int (**cd9660_fifoop_p)(); struct vnodeopv_entry_desc cd9660_fifoop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, fifo_lookup }, /* lookup */ { &vop_create_desc, cd9660_create }, /* create */ { &vop_mknod_desc, cd9660_mknod }, /* mknod */ { &vop_open_desc, fifo_open }, /* open */ { &vop_close_desc, fifo_close }, /* close */ { &vop_access_desc, cd9660_access }, /* access */ { &vop_getattr_desc, cd9660_getattr }, /* getattr */ { &vop_setattr_desc, cd9660_setattr }, /* setattr */ { &vop_read_desc, fifo_read }, /* read */ { &vop_write_desc, fifo_write }, /* write */ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */ { &vop_select_desc, fifo_select }, /* select */ { &vop_mmap_desc, fifo_mmap }, /* mmap */ { &vop_fsync_desc, fifo_fsync }, /* fsync */ { &vop_seek_desc, fifo_seek }, /* seek */ { &vop_remove_desc, cd9660_remove }, /* remove */ { &vop_link_desc, cd9660_link }, /* link */ { &vop_rename_desc, cd9660_rename }, /* rename */ { &vop_mkdir_desc, cd9660_mkdir }, /* mkdir */ { &vop_rmdir_desc, cd9660_rmdir }, /* rmdir */ { &vop_symlink_desc, cd9660_symlink }, /* symlink */ { &vop_readdir_desc, fifo_readdir }, /* readdir */ { &vop_readlink_desc, fifo_readlink }, /* readlink */ { &vop_abortop_desc, fifo_abortop }, /* abortop */ { &vop_inactive_desc, cd9660_inactive },/* inactive */ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, cd9660_lock }, /* lock */ { &vop_unlock_desc, cd9660_unlock }, /* unlock */ { &vop_bmap_desc, fifo_bmap }, /* bmap */ { &vop_strategy_desc, fifo_badop }, /* strategy */ { &vop_print_desc, cd9660_print }, /* print */ { &vop_islocked_desc, cd9660_islocked },/* islocked */ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, fifo_valloc }, /* valloc */ { &vop_vfree_desc, fifo_vfree }, /* vfree */ { &vop_truncate_desc, fifo_truncate }, /* truncate */ { &vop_update_desc, cd9660_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc cd9660_fifoop_opv_desc = { &cd9660_fifoop_p, cd9660_fifoop_entries }; + +VNODEOP_SET(cd9660_fifoop_opv_desc); #endif /* FIFO */ Index: head/sys/fs/deadfs/dead_vnops.c =================================================================== --- head/sys/fs/deadfs/dead_vnops.c (revision 2945) +++ head/sys/fs/deadfs/dead_vnops.c (revision 2946) @@ -1,369 +1,372 @@ /* * 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$ + * $Id: dead_vnops.c,v 1.3 1994/08/02 07:44:51 davidg Exp $ */ #include #include #include +#include #include #include #include #include /* * Prototypes for dead operations on vnodes. */ int dead_badop(), dead_ebadf(); 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) 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) int dead_read __P((struct vop_read_args *)); int dead_write __P((struct vop_write_args *)); int dead_ioctl __P((struct vop_ioctl_args *)); int dead_select __P((struct vop_select_args *)); #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) int dead_lock __P((struct vop_lock_args *)); #define dead_unlock ((int (*) __P((struct vop_unlock_args *)))nullop) int dead_bmap __P((struct vop_bmap_args *)); int dead_strategy __P((struct vop_strategy_args *)); int dead_print __P((struct vop_print_args *)); #define dead_islocked ((int (*) __P((struct vop_islocked_args *)))nullop) #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) int (**dead_vnodeop_p)(); struct vnodeopv_entry_desc dead_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, dead_lookup }, /* lookup */ { &vop_create_desc, dead_create }, /* create */ { &vop_mknod_desc, dead_mknod }, /* mknod */ { &vop_open_desc, dead_open }, /* open */ { &vop_close_desc, dead_close }, /* close */ { &vop_access_desc, dead_access }, /* access */ { &vop_getattr_desc, dead_getattr }, /* getattr */ { &vop_setattr_desc, dead_setattr }, /* setattr */ { &vop_read_desc, dead_read }, /* read */ { &vop_write_desc, dead_write }, /* write */ { &vop_ioctl_desc, dead_ioctl }, /* ioctl */ { &vop_select_desc, dead_select }, /* select */ { &vop_mmap_desc, dead_mmap }, /* mmap */ { &vop_fsync_desc, dead_fsync }, /* fsync */ { &vop_seek_desc, dead_seek }, /* seek */ { &vop_remove_desc, dead_remove }, /* remove */ { &vop_link_desc, dead_link }, /* link */ { &vop_rename_desc, dead_rename }, /* rename */ { &vop_mkdir_desc, dead_mkdir }, /* mkdir */ { &vop_rmdir_desc, dead_rmdir }, /* rmdir */ { &vop_symlink_desc, dead_symlink }, /* symlink */ { &vop_readdir_desc, dead_readdir }, /* readdir */ { &vop_readlink_desc, dead_readlink }, /* readlink */ { &vop_abortop_desc, dead_abortop }, /* abortop */ { &vop_inactive_desc, dead_inactive }, /* inactive */ { &vop_reclaim_desc, dead_reclaim }, /* reclaim */ { &vop_lock_desc, dead_lock }, /* lock */ { &vop_unlock_desc, dead_unlock }, /* unlock */ { &vop_bmap_desc, dead_bmap }, /* bmap */ { &vop_strategy_desc, dead_strategy }, /* strategy */ { &vop_print_desc, dead_print }, /* print */ { &vop_islocked_desc, dead_islocked }, /* islocked */ { &vop_pathconf_desc, dead_pathconf }, /* pathconf */ { &vop_advlock_desc, dead_advlock }, /* advlock */ { &vop_blkatoff_desc, dead_blkatoff }, /* blkatoff */ { &vop_valloc_desc, dead_valloc }, /* valloc */ { &vop_vfree_desc, dead_vfree }, /* vfree */ { &vop_truncate_desc, dead_truncate }, /* truncate */ { &vop_update_desc, dead_update }, /* update */ { &vop_bwrite_desc, dead_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; 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 */ 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 */ 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 */ 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"); /* * Return EOF for character devices, EIO for others */ if (ap->a_vp->v_type != VCHR) return (EIO); return (0); } /* * Vnode op for write */ /* ARGSUSED */ 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 */ 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 */ 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 */ 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. */ int dead_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; } */ *ap; { if (!chkvnlock(ap->a_vp)) return (0); return (VCALL(ap->a_vp, VOFFSET(vop_lock), ap)); } /* * Wait until the vnode has finished changing state. */ 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; } */ *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)); } /* * Print out the contents of a dead vnode. */ /* ARGSUSED */ 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 */ int dead_ebadf() { return (EBADF); } /* * Empty vnode bad operation */ int dead_badop() { panic("dead_badop called"); /* NOTREACHED */ } /* * Empty vnode null operation */ int dead_nullop() { return (0); } /* * 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; sleep((caddr_t)vp, PINOD); locked = 1; } return (locked); } Index: head/sys/fs/fdescfs/fdesc_vfsops.c =================================================================== --- head/sys/fs/fdescfs/fdesc_vfsops.c (revision 2945) +++ head/sys/fs/fdescfs/fdesc_vfsops.c (revision 2946) @@ -1,288 +1,291 @@ /* * 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_vfsops.c 8.4 (Berkeley) 1/21/94 * - * $Id: fdesc_vfsops.c,v 1.9 1993/04/06 15:28:33 jsp Exp $ + * $Id: fdesc_vfsops.c,v 1.1.1.1 1994/05/24 10:04:59 rgrimes Exp $ */ /* * /dev/fd Filesystem */ #include #include +#include #include #include #include #include #include #include #include #include #include #include /* * Mount the per-process file descriptors (/dev/fd) */ int fdesc_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { int error = 0; u_int size; struct fdescmount *fmp; struct vnode *rvp; /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) return (EOPNOTSUPP); error = fdesc_allocvp(Froot, FD_ROOT, mp, &rvp); if (error) return (error); MALLOC(fmp, struct fdescmount *, sizeof(struct fdescmount), M_UFSMNT, M_WAITOK); /* XXX */ rvp->v_type = VDIR; rvp->v_flag |= VROOT; fmp->f_root = rvp; /* XXX -- don't mark as local to work around fts() problems */ /*mp->mnt_flag |= MNT_LOCAL;*/ mp->mnt_data = (qaddr_t) fmp; getnewfsid(mp, MOUNT_FDESC); (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); bzero(mp->mnt_stat.f_mntfromname, MNAMELEN); bcopy("fdesc", mp->mnt_stat.f_mntfromname, sizeof("fdesc")); return (0); } int fdesc_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } int fdesc_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { int error; int flags = 0; extern int doforce; struct vnode *rootvp = VFSTOFDESC(mp)->f_root; if (mntflags & MNT_FORCE) { /* fdesc can never be rootfs so don't check for it */ if (!doforce) return (EINVAL); flags |= FORCECLOSE; } /* * Clear out buffer cache. I don't think we * ever get anything cached at this level at the * moment, but who knows... */ if (rootvp->v_usecount > 1) return (EBUSY); if (error = vflush(mp, rootvp, flags)) return (error); /* * Release reference on underlying root vnode */ vrele(rootvp); /* * And blow it away for future re-use */ vgone(rootvp); /* * Finally, throw away the fdescmount structure */ free(mp->mnt_data, M_UFSMNT); /* XXX */ mp->mnt_data = 0; return (0); } int fdesc_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct vnode *vp; /* * Return locked reference to root. */ vp = VFSTOFDESC(mp)->f_root; VREF(vp); VOP_LOCK(vp); *vpp = vp; return (0); } int fdesc_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } int fdesc_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { struct filedesc *fdp; int lim; int i; int last; int freefd; /* * Compute number of free file descriptors. * [ Strange results will ensue if the open file * limit is ever reduced below the current number * of open files... ] */ lim = p->p_rlimit[RLIMIT_NOFILE].rlim_cur; fdp = p->p_fd; last = min(fdp->fd_nfiles, lim); freefd = 0; for (i = fdp->fd_freefile; i < last; i++) if (fdp->fd_ofiles[i] == NULL) freefd++; /* * Adjust for the fact that the fdesc array may not * have been fully allocated yet. */ if (fdp->fd_nfiles < lim) freefd += (lim - fdp->fd_nfiles); sbp->f_type = MOUNT_FDESC; sbp->f_flags = 0; sbp->f_bsize = DEV_BSIZE; sbp->f_iosize = DEV_BSIZE; sbp->f_blocks = 2; /* 1K to keep df happy */ sbp->f_bfree = 0; sbp->f_bavail = 0; sbp->f_files = lim + 1; /* Allow for "." */ sbp->f_ffree = freefd; /* See comments above */ if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } int fdesc_sync(mp, waitfor) struct mount *mp; int waitfor; { return (0); } /* * Fdesc flat namespace lookup. * Currently unsupported. */ int fdesc_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } int fdesc_fhtovp(mp, fhp, setgen, vpp) struct mount *mp; struct fid *fhp; int setgen; struct vnode **vpp; { return (EOPNOTSUPP); } int fdesc_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (EOPNOTSUPP); } struct vfsops fdesc_vfsops = { fdesc_mount, fdesc_start, fdesc_unmount, fdesc_root, fdesc_quotactl, fdesc_statfs, fdesc_sync, fdesc_vget, fdesc_fhtovp, fdesc_vptofh, fdesc_init, }; + +VFS_SET(fdesc_vfsops, fdesc, MOUNT_FDESC, 0); Index: head/sys/fs/fdescfs/fdesc_vnops.c =================================================================== --- head/sys/fs/fdescfs/fdesc_vnops.c (revision 2945) +++ head/sys/fs/fdescfs/fdesc_vnops.c (revision 2946) @@ -1,977 +1,979 @@ /* * 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.3 1994/09/09 13:23:20 davidg Exp $ + * $Id: fdesc_vnops.c,v 1.4 1994/09/09 13:24:26 davidg Exp $ */ /* * /dev/fd Filesystem */ #include #include #include #include #include #include /* boottime */ #include #include #include #include #include #include #include #include #include #include #include #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; 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) ((ix) & NFDCACHE-1) /* * Cache head */ struct fdcache { struct fdescnode *fc_forw; struct fdescnode *fc_back; }; static struct fdcache fdcache[NFDCACHE]; /* * Initialise cache headers */ int fdesc_init() { struct fdcache *fc; devctty = makedev(nchrdev, 0); for (fc = fdcache; fc < fdcache + NFDCACHE; fc++) fc->fc_forw = fc->fc_back = (struct fdescnode *) fc; return (0); } /* * Compute hash list for given target vnode */ static struct fdcache * fdesc_hash(ix) int ix; { return (&fdcache[FD_NHASH(ix)]); } int fdesc_allocvp(ftype, ix, mp, vpp) fdntype ftype; int ix; struct mount *mp; struct vnode **vpp; { struct fdcache *fc; struct fdescnode *fd; int error = 0; loop: fc = fdesc_hash(ix); for (fd = fc->fc_forw; fd != (struct fdescnode *) fc; fd = fd->fd_forw) { if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) { if (vget(fd->fd_vnode, 0)) 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; sleep((caddr_t) &fdcache_lock, PINOD); goto loop; } fdcache_lock |= FDL_LOCKED; error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp); if (error) goto out; MALLOC(fd, void *, sizeof(struct fdescnode), M_TEMP, M_WAITOK); (*vpp)->v_data = fd; fd->fd_vnode = *vpp; fd->fd_type = ftype; fd->fd_fd = -1; fd->fd_link = 0; fd->fd_ix = ix; fc = fdesc_hash(ix); insque(fd, fc); 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... */ 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; char *pname; struct proc *p; int nfiles; unsigned fd = 0; int error; struct vnode *fvp; char *ln; pname = ap->a_cnp->cn_nameptr; if (ap->a_cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); VOP_LOCK(dvp); return (0); } p = ap->a_cnp->cn_proc; nfiles = p->p_fd->fd_nfiles; switch (VTOFDESC(dvp)->fd_type) { default: case Flink: case Fdesc: case Fctty: error = ENOTDIR; goto bad; case Froot: if (ap->a_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; VOP_LOCK(fvp); return (0); } if (ap->a_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; VOP_LOCK(fvp); return (0); } ln = 0; switch (ap->a_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; VOP_LOCK(fvp); return (0); } else { error = ENOENT; goto bad; } /* FALL THROUGH */ case Fdevfd: if (ap->a_cnp->cn_namelen == 2 && bcmp(pname, "..", 2) == 0) { error = fdesc_root(dvp->v_mount, vpp); return (error); } 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; *vpp = fvp; return (0); } bad:; *vpp = NULL; return (error); } 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 = cttyopen(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_VNODE: error = VOP_GETATTR((struct vnode *) fp->f_data, vap, cred, p); if (error == 0 && vap->va_type == VDIR) { /* * don't allow directories to show up because * that causes loops in the namespace. */ vap->va_type = VFIFO; } 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); } 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.ts_sec = boottime.tv_sec; vap->va_atime.ts_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); } 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_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 } }; int fdesc_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { struct uio *uio = ap->a_uio; struct filedesc *fdp; int i; int error; 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); } 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); } 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 = cttyread(devctty, ap->a_uio, ap->a_ioflag); break; default: error = EOPNOTSUPP; break; } return (error); } 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 = cttywrite(devctty, ap->a_uio, ap->a_ioflag); break; default: error = EOPNOTSUPP; break; } return (error); } 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 = cttyioctl(devctty, ap->a_command, ap->a_data, ap->a_fflag, ap->a_p); break; default: error = EOPNOTSUPP; break; } return (error); } int fdesc_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; { int error = EOPNOTSUPP; switch (VTOFDESC(ap->a_vp)->fd_type) { case Fctty: error = cttyselect(devctty, ap->a_fflags, ap->a_p); break; default: error = EOPNOTSUPP; break; } return (error); } int fdesc_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; /* * Clear out the v_type field to avoid * nasty things happening in vgone(). */ vp->v_type = VNON; return (0); } int fdesc_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; remque(VTOFDESC(vp)); FREE(vp->v_data, M_TEMP); vp->v_data = 0; return (0); } /* * Return POSIX pathconf information applicable to special devices. */ 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 */ int fdesc_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_NON, fdesc vnode\n"); return (0); } /*void*/ int fdesc_vfree(ap) struct vop_vfree_args /* { struct vnode *a_pvp; ino_t a_ino; int a_mode; } */ *ap; { return (0); } /* * /dev/fd vnode unsupported operation */ int fdesc_enotsupp() { return (EOPNOTSUPP); } /* * /dev/fd "should never get here" operation */ int fdesc_badop() { panic("fdesc: bad op"); /* NOTREACHED */ } /* * /dev/fd vnode null operation */ int fdesc_nullop() { return (0); } #define fdesc_create ((int (*) __P((struct vop_create_args *)))fdesc_enotsupp) #define fdesc_mknod ((int (*) __P((struct vop_mknod_args *)))fdesc_enotsupp) #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 *)))fdesc_enotsupp) #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 *)))fdesc_enotsupp) #define fdesc_link ((int (*) __P((struct vop_link_args *)))fdesc_enotsupp) #define fdesc_rename ((int (*) __P((struct vop_rename_args *)))fdesc_enotsupp) #define fdesc_mkdir ((int (*) __P((struct vop_mkdir_args *)))fdesc_enotsupp) #define fdesc_rmdir ((int (*) __P((struct vop_rmdir_args *)))fdesc_enotsupp) #define fdesc_symlink ((int (*) __P((struct vop_symlink_args *)))fdesc_enotsupp) #define fdesc_abortop ((int (*) __P((struct vop_abortop_args *)))nullop) #define fdesc_lock ((int (*) __P((struct vop_lock_args *)))nullop) #define fdesc_unlock ((int (*) __P((struct vop_unlock_args *)))nullop) #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 *)))nullop) #define fdesc_advlock ((int (*) __P((struct vop_advlock_args *)))fdesc_enotsupp) #define fdesc_blkatoff \ ((int (*) __P((struct vop_blkatoff_args *)))fdesc_enotsupp) #define fdesc_vget ((int (*) __P((struct vop_vget_args *)))fdesc_enotsupp) #define fdesc_valloc ((int(*) __P(( \ struct vnode *pvp, \ int mode, \ struct ucred *cred, \ struct vnode **vpp))) fdesc_enotsupp) #define fdesc_truncate \ ((int (*) __P((struct vop_truncate_args *)))fdesc_enotsupp) #define fdesc_update ((int (*) __P((struct vop_update_args *)))fdesc_enotsupp) #define fdesc_bwrite ((int (*) __P((struct vop_bwrite_args *)))fdesc_enotsupp) int (**fdesc_vnodeop_p)(); struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, fdesc_lookup }, /* lookup */ { &vop_create_desc, fdesc_create }, /* create */ { &vop_mknod_desc, fdesc_mknod }, /* mknod */ { &vop_open_desc, fdesc_open }, /* open */ { &vop_close_desc, fdesc_close }, /* close */ { &vop_access_desc, fdesc_access }, /* access */ { &vop_getattr_desc, fdesc_getattr }, /* getattr */ { &vop_setattr_desc, fdesc_setattr }, /* setattr */ { &vop_read_desc, fdesc_read }, /* read */ { &vop_write_desc, fdesc_write }, /* write */ { &vop_ioctl_desc, fdesc_ioctl }, /* ioctl */ { &vop_select_desc, fdesc_select }, /* select */ { &vop_mmap_desc, fdesc_mmap }, /* mmap */ { &vop_fsync_desc, fdesc_fsync }, /* fsync */ { &vop_seek_desc, fdesc_seek }, /* seek */ { &vop_remove_desc, fdesc_remove }, /* remove */ { &vop_link_desc, fdesc_link }, /* link */ { &vop_rename_desc, fdesc_rename }, /* rename */ { &vop_mkdir_desc, fdesc_mkdir }, /* mkdir */ { &vop_rmdir_desc, fdesc_rmdir }, /* rmdir */ { &vop_symlink_desc, fdesc_symlink }, /* symlink */ { &vop_readdir_desc, fdesc_readdir }, /* readdir */ { &vop_readlink_desc, fdesc_readlink }, /* readlink */ { &vop_abortop_desc, fdesc_abortop }, /* abortop */ { &vop_inactive_desc, fdesc_inactive }, /* inactive */ { &vop_reclaim_desc, fdesc_reclaim }, /* reclaim */ { &vop_lock_desc, fdesc_lock }, /* lock */ { &vop_unlock_desc, fdesc_unlock }, /* unlock */ { &vop_bmap_desc, fdesc_bmap }, /* bmap */ { &vop_strategy_desc, fdesc_strategy }, /* strategy */ { &vop_print_desc, fdesc_print }, /* print */ { &vop_islocked_desc, fdesc_islocked }, /* islocked */ { &vop_pathconf_desc, fdesc_pathconf }, /* pathconf */ { &vop_advlock_desc, fdesc_advlock }, /* advlock */ { &vop_blkatoff_desc, fdesc_blkatoff }, /* blkatoff */ { &vop_valloc_desc, fdesc_valloc }, /* valloc */ { &vop_vfree_desc, fdesc_vfree }, /* vfree */ { &vop_truncate_desc, fdesc_truncate }, /* truncate */ { &vop_update_desc, fdesc_update }, /* update */ { &vop_bwrite_desc, fdesc_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc fdesc_vnodeop_opv_desc = { &fdesc_vnodeop_p, fdesc_vnodeop_entries }; + +VNODEOP_SET(fdesc_vnodeop_opv_desc); Index: head/sys/fs/fifofs/fifo_vnops.c =================================================================== --- head/sys/fs/fifofs/fifo_vnops.c (revision 2945) +++ head/sys/fs/fifofs/fifo_vnops.c (revision 2946) @@ -1,513 +1,515 @@ /* * 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. * * @(#)fifo_vnops.c 8.2 (Berkeley) 1/4/94 - * $Id$ + * $Id: fifo_vnops.c,v 1.3 1994/08/02 07:44:56 davidg 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; }; int (**fifo_vnodeop_p)(); struct vnodeopv_entry_desc fifo_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, fifo_lookup }, /* lookup */ { &vop_create_desc, fifo_create }, /* create */ { &vop_mknod_desc, fifo_mknod }, /* mknod */ { &vop_open_desc, fifo_open }, /* open */ { &vop_close_desc, fifo_close }, /* close */ { &vop_access_desc, fifo_access }, /* access */ { &vop_getattr_desc, fifo_getattr }, /* getattr */ { &vop_setattr_desc, fifo_setattr }, /* setattr */ { &vop_read_desc, fifo_read }, /* read */ { &vop_write_desc, fifo_write }, /* write */ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */ { &vop_select_desc, fifo_select }, /* select */ { &vop_mmap_desc, fifo_mmap }, /* mmap */ { &vop_fsync_desc, fifo_fsync }, /* fsync */ { &vop_seek_desc, fifo_seek }, /* seek */ { &vop_remove_desc, fifo_remove }, /* remove */ { &vop_link_desc, fifo_link }, /* link */ { &vop_rename_desc, fifo_rename }, /* rename */ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, fifo_symlink }, /* symlink */ { &vop_readdir_desc, fifo_readdir }, /* readdir */ { &vop_readlink_desc, fifo_readlink }, /* readlink */ { &vop_abortop_desc, fifo_abortop }, /* abortop */ { &vop_inactive_desc, fifo_inactive }, /* inactive */ { &vop_reclaim_desc, fifo_reclaim }, /* reclaim */ { &vop_lock_desc, fifo_lock }, /* lock */ { &vop_unlock_desc, fifo_unlock }, /* unlock */ { &vop_bmap_desc, fifo_bmap }, /* bmap */ { &vop_strategy_desc, fifo_strategy }, /* strategy */ { &vop_print_desc, fifo_print }, /* print */ { &vop_islocked_desc, fifo_islocked }, /* islocked */ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, fifo_valloc }, /* valloc */ { &vop_vfree_desc, fifo_vfree }, /* vfree */ { &vop_truncate_desc, fifo_truncate }, /* truncate */ { &vop_update_desc, fifo_update }, /* update */ { &vop_bwrite_desc, fifo_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; 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; { register struct vnode *vp = ap->a_vp; register struct fifoinfo *fip; struct socket *rso, *wso; int error; static char openstr[] = "fifo"; if ((ap->a_mode & (FREAD|FWRITE)) == (FREAD|FWRITE)) return (EINVAL); if ((fip = vp->v_fifoinfo) == NULL) { MALLOC(fip, struct fifoinfo *, sizeof(*fip), M_VNODE, M_WAITOK); vp->v_fifoinfo = fip; if (error = socreate(AF_UNIX, &rso, SOCK_STREAM, 0)) { free(fip, M_VNODE); vp->v_fifoinfo = NULL; return (error); } fip->fi_readsock = rso; if (error = socreate(AF_UNIX, &wso, SOCK_STREAM, 0)) { (void)soclose(rso); free(fip, M_VNODE); vp->v_fifoinfo = NULL; return (error); } fip->fi_writesock = wso; if (error = unp_connect2(wso, rso)) { (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; } error = 0; 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 & O_NONBLOCK) return (0); while (fip->fi_writers == 0) { VOP_UNLOCK(vp); error = tsleep((caddr_t)&fip->fi_readers, PCATCH | PSOCK, openstr, 0); VOP_LOCK(vp); if (error) break; } } else { fip->fi_writers++; if (fip->fi_readers == 0 && (ap->a_mode & O_NONBLOCK)) { error = ENXIO; } else { if (fip->fi_writers == 1) { fip->fi_readsock->so_state &= ~SS_CANTRCVMORE; if (fip->fi_readers > 0) wakeup((caddr_t)&fip->fi_readers); } while (fip->fi_readers == 0) { VOP_UNLOCK(vp); error = tsleep((caddr_t)&fip->fi_writers, PCATCH | PSOCK, openstr, 0); VOP_LOCK(vp); if (error) break; } } } if (error) VOP_CLOSE(vp, ap->a_mode, ap->a_cred, ap->a_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; { register struct uio *uio = ap->a_uio; register struct socket *rso = ap->a_vp->v_fifoinfo->fi_readsock; 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); error = soreceive(rso, (struct mbuf **)0, uio, (int *)0, (struct mbuf **)0, (struct mbuf **)0); VOP_LOCK(ap->a_vp); /* * 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; 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); error = sosend(wso, (struct mbuf *)0, ap->a_uio, 0, (struct mbuf *)0, 0); VOP_LOCK(ap->a_vp); 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; if (ap->a_command == FIONBIO) return (0); if (ap->a_fflag & FREAD) filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock; else filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock; return (soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p)); } /* ARGSUSED */ int fifo_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; { struct file filetmp; if (ap->a_fflags & FREAD) filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock; else filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock; return (soo_select(&filetmp, ap->a_which, ap->a_p)); } /* * 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; } */ *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; return (0); } /* * At the moment we do not do any locking. */ /* ARGSUSED */ int fifo_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; } */ *ap; { return (0); } /* ARGSUSED */ int fifo_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; } */ *ap; { 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 & FWRITE) { fip->fi_writers--; if (fip->fi_writers == 0) socantrcvmore(fip->fi_readsock); } else { fip->fi_readers--; if (fip->fi_readers == 0) socantsendmore(fip->fi_writesock); } 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 the contents of a fifo vnode. */ 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); } /* * 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 %d readers and %d writers", fip->fi_readers, fip->fi_writers); 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 */ 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 (EOPNOTSUPP); } /* * Fifo bad operation */ int fifo_badop() { panic("fifo_badop called"); /* NOTREACHED */ } Index: head/sys/fs/msdosfs/msdosfs_vfsops.c =================================================================== --- head/sys/fs/msdosfs/msdosfs_vfsops.c (revision 2945) +++ head/sys/fs/msdosfs/msdosfs_vfsops.c (revision 2946) @@ -1,729 +1,731 @@ -/* $Id: msdosfs_vfsops.c,v 1.1 1994/09/19 15:41:45 dfr Exp $ */ +/* $Id: msdosfs_vfsops.c,v 1.2 1994/09/19 19:24:44 dfr Exp $ */ /* $NetBSD: msdosfs_vfsops.c,v 1.19 1994/08/21 18:44:10 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 #include #include #include /* XXX */ /* defines v_rdev */ #include #include #include #include #include #include #include #include #include #include int msdosfsdoforce = 1; /* 1 = force unmount */ /* * mp - path - addr in user space of mount point (ie /usr or whatever) * data - addr in user space of mount params including the name of the block * special file to treat as a filesystem. */ int msdosfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { struct vnode *devvp; /* vnode for blk device to mount */ struct msdosfs_args args; /* will hold data from mount request */ struct msdosfsmount *pmp; /* msdosfs specific mount control block */ int error, flags; u_int size; struct ucred *cred, *scred; struct vattr va; /* * Copy in the args for the mount request. */ if (error = copyin(data, (caddr_t) & args, sizeof(struct msdosfs_args))) return error; /* * If they just want to update then be sure we can do what is * asked. Can't change a filesystem from read/write to read only. * Why? And if they've supplied a new device file name then we * continue, otherwise return. */ if (mp->mnt_flag & MNT_UPDATE) { pmp = (struct msdosfsmount *) mp->mnt_data; error = 0; if (pmp->pm_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; if (vfs_busy(mp)) return EBUSY; error = vflush(mp, NULLVP, flags); vfs_unbusy(mp); } if (!error && (mp->mnt_flag & MNT_RELOAD)) /* not yet implemented */ error = EINVAL; if (error) return error; if (pmp->pm_ronly && (mp->mnt_flag & MNT_RDONLY) == 0) pmp->pm_ronly = 0; if (args.fspec == 0) { /* * Process export requests. */ return vfs_export(mp, &pmp->pm_export, &args.export); } } else pmp = NULL; /* * check to see that the user in owns the target directory. * Note the very XXX trick to make sure we're checking as the * real user -- were mount() executable by anyone, this wouldn't * be a problem. * * XXX there should be one consistent error out. */ cred = crdup(p->p_ucred); /* XXX */ cred->cr_uid = p->p_cred->p_ruid; /* XXX */ error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p); if (error) { crfree(cred); /* XXX */ return error; } if (cred->cr_uid != 0) { if (va.va_uid != cred->cr_uid) { error = EACCES; crfree(cred); /* XXX */ return error; } /* a user mounted it; we'll verify permissions when unmounting */ mp->mnt_flag |= MNT_USER; } /* * Now, lookup the name of the block device this mount or name * update request is to apply to. */ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); scred = p->p_ucred; /* XXX */ p->p_ucred = cred; /* XXX */ error = namei(ndp); p->p_ucred = scred; /* XXX */ crfree(cred); /* XXX */ if (error != 0) return error; /* * Be sure they've given us a block device to treat as a * filesystem. And, that its major number is within the bdevsw * table. */ devvp = ndp->ni_vp; if (devvp->v_type != VBLK) { vrele(devvp); return ENOTBLK; } if (major(devvp->v_rdev) >= nblkdev) { vrele(devvp); return ENXIO; } /* * If this is an update, then make sure the vnode for the block * special device is the same as the one our filesystem is in. */ if (mp->mnt_flag & MNT_UPDATE) { if (devvp != pmp->pm_devvp) error = EINVAL; else vrele(devvp); } else { /* * Well, it's not an update, it's a real mount request. * Time to get dirty. */ error = mountmsdosfs(devvp, mp, p); } if (error) { vrele(devvp); return error; } /* * Copy in the name of the directory the filesystem is to be * mounted on. Then copy in the name of the block special file * representing the filesystem being mounted. And we clear the * remainder of the character strings to be tidy. Set up the * user id/group id/mask as specified by the user. Then, we try to * fill in the filesystem stats structure as best we can with * whatever applies from a dos file system. */ pmp = (struct msdosfsmount *) mp->mnt_data; copyinstr(path, (caddr_t) mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname) - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, sizeof(mp->mnt_stat.f_mntonname) - size); copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); pmp->pm_mounter = p->p_cred->p_ruid; pmp->pm_gid = args.gid; pmp->pm_uid = args.uid; pmp->pm_mask = args.mask; (void) msdosfs_statfs(mp, &mp->mnt_stat, p); #ifdef MSDOSFS_DEBUG printf("msdosfs_mount(): mp %x, pmp %x, inusemap %x\n", mp, pmp, pmp->pm_inusemap); #endif return 0; } int mountmsdosfs(devvp, mp, p) struct vnode *devvp; struct mount *mp; struct proc *p; { int i; int bpc; int bit; int error; int needclose; int ronly = (mp->mnt_flag & MNT_RDONLY) != 0; dev_t dev = devvp->v_rdev; union bootsector *bsp; struct msdosfsmount *pmp = NULL; struct buf *bp0 = NULL; struct byte_bpb33 *b33; struct byte_bpb50 *b50; /* * Multiple mounts of the same block special file aren't allowed. * Make sure no one else has the special file open. And flush any * old buffers from this filesystem. Presumably this prevents us * from running into buffers that are the wrong blocksize. */ if (error = vfs_mountedon(devvp)) return error; if (vcount(devvp) > 1) return EBUSY; if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0)) return error; /* * Now open the block special file. */ if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE, FSCRED, p)) return error; needclose = 1; #ifdef HDSUPPORT /* * Put this in when we support reading dos filesystems from * partitioned harddisks. */ if (VOP_IOCTL(devvp, DIOCGPART, &msdosfspart, FREAD, NOCRED, p) == 0) { } #endif /* * Read the boot sector of the filesystem, and then check the boot * signature. If not a dos boot sector then error out. We could * also add some checking on the bsOemName field. So far I've seen * the following values: "IBM 3.3" "MSDOS3.3" "MSDOS5.0" */ if (error = bread(devvp, 0, 512, NOCRED, &bp0)) goto error_exit; bp0->b_flags |= B_AGE; bsp = (union bootsector *) bp0->b_data; b33 = (struct byte_bpb33 *) bsp->bs33.bsBPB; b50 = (struct byte_bpb50 *) bsp->bs50.bsBPB; #ifdef MSDOSFS_CHECKSIG if (bsp->bs50.bsBootSectSig != BOOTSIG) { error = EINVAL; goto error_exit; } #endif if ( bsp->bs50.bsJump[0] != 0xe9 && (bsp->bs50.bsJump[0] != 0xeb || bsp->bs50.bsJump[2] != 0x90)) { error = EINVAL; goto error_exit; } pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK); bzero((caddr_t)pmp, sizeof *pmp); pmp->pm_mountp = mp; /* * Compute several useful quantities from the bpb in the * bootsector. Copy in the dos 5 variant of the bpb then fix up * the fields that are different between dos 5 and dos 3.3. */ pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); pmp->pm_SectPerClust = b50->bpbSecPerClust; pmp->pm_ResSectors = getushort(b50->bpbResSectors); pmp->pm_FATs = b50->bpbFATs; pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); pmp->pm_Sectors = getushort(b50->bpbSectors); pmp->pm_Media = b50->bpbMedia; pmp->pm_FATsecs = getushort(b50->bpbFATsecs); pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); pmp->pm_Heads = getushort(b50->bpbHeads); /* XXX - We should probably check more values here */ if (!pmp->pm_BytesPerSec || !pmp->pm_SectPerClust || !pmp->pm_Heads || pmp->pm_Heads > 255 || !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) { error = EINVAL; goto error_exit; } if (pmp->pm_Sectors == 0) { pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); } else { pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); pmp->pm_HugeSectors = pmp->pm_Sectors; } pmp->pm_fatblk = pmp->pm_ResSectors; pmp->pm_rootdirblk = pmp->pm_fatblk + (pmp->pm_FATs * pmp->pm_FATsecs); pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)) / pmp->pm_BytesPerSec;/* in sectors */ pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / pmp->pm_SectPerClust; pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1; pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec; if (FAT12(pmp)) /* * This will usually be a floppy disk. This size makes sure * that one fat entry will not be split across multiple * blocks. */ pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec; else /* * This will usually be a hard disk. Reading or writing one * block should be quite fast. */ pmp->pm_fatblocksize = MAXBSIZE; pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec; if ((pmp->pm_rootdirsize % pmp->pm_SectPerClust) != 0) printf("mountmsdosfs(): root directory is not a multiple of the clustersize in length\n"); /* * Compute mask and shift value for isolating cluster relative byte * offsets and cluster numbers from a file offset. */ bpc = pmp->pm_SectPerClust * pmp->pm_BytesPerSec; pmp->pm_bpcluster = bpc; pmp->pm_depclust = bpc / sizeof(struct direntry); pmp->pm_crbomask = bpc - 1; if (bpc == 0) { error = EINVAL; goto error_exit; } bit = 1; for (i = 0; i < 32; i++) { if (bit & bpc) { if (bit ^ bpc) { error = EINVAL; goto error_exit; } pmp->pm_cnshift = i; break; } bit <<= 1; } pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */ pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */ /* * Release the bootsector buffer. */ brelse(bp0); bp0 = NULL; /* * Allocate memory for the bitmap of allocated clusters, and then * fill it in. */ pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1) / N_INUSEBITS) * sizeof(*pmp->pm_inusemap), M_MSDOSFSFAT, M_WAITOK); /* * fillinusemap() needs pm_devvp. */ pmp->pm_dev = dev; pmp->pm_devvp = devvp; /* * Have the inuse map filled in. */ error = fillinusemap(pmp); if (error) goto error_exit; /* * If they want fat updates to be synchronous then let them suffer * the performance degradation in exchange for the on disk copy of * the fat being correct just about all the time. I suppose this * would be a good thing to turn on if the kernel is still flakey. */ pmp->pm_waitonfat = mp->mnt_flag & MNT_SYNCHRONOUS; /* * Finish up. */ pmp->pm_ronly = ronly; if (ronly == 0) pmp->pm_fmod = 1; mp->mnt_data = (qaddr_t) pmp; mp->mnt_stat.f_fsid.val[0] = (long)dev; mp->mnt_stat.f_fsid.val[1] = MOUNT_MSDOS; mp->mnt_flag |= MNT_LOCAL; #ifdef QUOTA /* * If we ever do quotas for DOS filesystems this would be a place * to fill in the info in the msdosfsmount structure. You dolt, * quotas on dos filesystems make no sense because files have no * owners on dos filesystems. of course there is some empty space * in the directory entry where we could put uid's and gid's. */ #endif devvp->v_specflags |= SI_MOUNTEDON; return 0; error_exit:; if (bp0) brelse(bp0); if (needclose) (void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE, NOCRED, p); if (pmp) { if (pmp->pm_inusemap) free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT); free((caddr_t) pmp, M_MSDOSFSMNT); mp->mnt_data = (qaddr_t) 0; } return error; } int msdosfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return 0; } /* * Unmount the filesystem described by mp. */ int msdosfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { int flags = 0; int error; struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; struct vnode *vp = pmp->pm_devvp; /* only the mounter, or superuser can unmount */ if ((p->p_cred->p_ruid != pmp->pm_mounter) && (error = suser(p->p_ucred, &p->p_acflag))) return error; if (mntflags & MNT_FORCE) { if (!msdosfsdoforce) return EINVAL; flags |= FORCECLOSE; } #ifdef QUOTA #endif if (error = vflush(mp, NULLVP, flags)) return error; pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON; #ifdef MSDOSFS_DEBUG printf("msdosfs_umount(): just before calling VOP_CLOSE()\n"); printf("flag %08x, usecount %d, writecount %d, holdcnt %d\n", vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt); printf("lastr %d, id %d, mount %08x, op %08x\n", vp->v_lastr, vp->v_id, vp->v_mount, vp->v_op); printf("freef %08x, freeb %08x, mountf %08x, mountb %08x\n", vp->v_freef, vp->v_freeb, vp->v_mountf, vp->v_mountb); printf("cleanblkhd %08x, dirtyblkhd %08x, numoutput %d, type %d\n", vp->v_cleanblkhd, vp->v_dirtyblkhd, vp->v_numoutput, vp->v_type); printf("union %08x, tag %d, data[0] %08x, data[1] %08x\n", vp->v_socket, vp->v_tag, vp->v_data[0], vp->v_data[1]); #endif error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD | FWRITE, NOCRED, p); vrele(pmp->pm_devvp); free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT); free((caddr_t) pmp, M_MSDOSFSMNT); mp->mnt_data = (qaddr_t) 0; mp->mnt_flag &= ~MNT_LOCAL; return error; } int msdosfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct denode *ndep; struct msdosfsmount *pmp = (struct msdosfsmount *) (mp->mnt_data); int error; error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, NULL, &ndep); #ifdef MSDOSFS_DEBUG printf("msdosfs_root(); mp %08x, pmp %08x, ndep %08x, vp %08x\n", mp, pmp, ndep, DETOV(ndep)); #endif if (error == 0) *vpp = DETOV(ndep); return error; } int msdosfs_quotactl(mp, cmds, uid, arg, p) struct mount *mp; int cmds; uid_t uid; caddr_t arg; struct proc *p; { #ifdef QUOTA #else return EOPNOTSUPP; #endif } int msdosfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; /* * Fill in the stat block. */ sbp->f_type = MOUNT_MSDOS; sbp->f_bsize = pmp->pm_bpcluster; sbp->f_iosize = pmp->pm_bpcluster; sbp->f_blocks = pmp->pm_nmbrofclusters; sbp->f_bfree = pmp->pm_freeclustercount; sbp->f_bavail = pmp->pm_freeclustercount; sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ sbp->f_ffree = 0; /* what to put in here? */ /* * Copy the mounted on and mounted from names into the passed in * stat block, if it is not the one in the mount structure. */ if (sbp != &mp->mnt_stat) { bcopy((caddr_t) mp->mnt_stat.f_mntonname, (caddr_t) & sbp->f_mntonname[0], MNAMELEN); bcopy((caddr_t) mp->mnt_stat.f_mntfromname, (caddr_t) & sbp->f_mntfromname[0], MNAMELEN); } #if 0 strncpy(&sbp->f_fstypename[0], mp->mnt_op->vfs_name, MFSNAMELEN); sbp->f_fstypename[MFSNAMELEN] = '\0'; #endif return 0; } int msdosfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { struct vnode *vp; struct denode *dep; struct msdosfsmount *pmp; int error; int allerror = 0; pmp = (struct msdosfsmount *) mp->mnt_data; /* * If we ever switch to not updating all of the fats all the time, * this would be the place to update them from the first one. */ if (pmp->pm_fmod) if (pmp->pm_ronly) panic("msdosfs_sync: rofs mod"); else { /* update fats here */ } /* * Go thru in memory denodes and write them out along with * unwritten file blocks. */ loop: for (vp = mp->mnt_vnodelist.lh_first; vp; vp = vp->v_mntvnodes.le_next) { if (vp->v_mount != mp) /* not ours anymore */ goto loop; if (VOP_ISLOCKED(vp)) /* file is busy */ continue; dep = VTODE(vp); if ((dep->de_flag & DE_UPDATE) == 0 && vp->v_dirtyblkhd.lh_first == NULL) continue; if (vget(vp, 1)) /* not there anymore? */ goto loop; if (error = VOP_FSYNC(vp, cred, waitfor, p)) allerror = error; vput(vp); /* done with this one */ } /* * Flush filesystem control info. */ if (error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, p)) allerror = error; return allerror; } int msdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) struct mount *mp; struct fid *fhp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred **credanonp; { struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; struct defid *defhp = (struct defid *) fhp; struct denode *dep; struct netcred *np; int error; np = vfs_export_lookup(mp, &pmp->pm_export, nam); if (np == NULL) return EACCES; error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, NULL, &dep); if (error) { *vpp = NULLVP; return error; } *vpp = DETOV(dep); *exflagsp = np->netc_exflags; *credanonp = &np->netc_anon; return 0; } int msdosfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { struct denode *dep = VTODE(vp); struct defid *defhp = (struct defid *) fhp; defhp->defid_len = sizeof(struct defid); defhp->defid_dirclust = dep->de_dirclust; defhp->defid_dirofs = dep->de_diroffset; /* defhp->defid_gen = ip->i_gen; */ return 0; } int msdosfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return EOPNOTSUPP; } struct vfsops msdosfs_vfsops = { msdosfs_mount, msdosfs_start, msdosfs_unmount, msdosfs_root, msdosfs_quotactl, msdosfs_statfs, msdosfs_sync, msdosfs_vget, msdosfs_fhtovp, msdosfs_vptofh, msdosfs_init }; + +VFS_SET(msdosfs_vfsops, msdos, MOUNT_MSDOS, 0); Index: head/sys/fs/msdosfs/msdosfs_vnops.c =================================================================== --- head/sys/fs/msdosfs/msdosfs_vnops.c (revision 2945) +++ head/sys/fs/msdosfs/msdosfs_vnops.c (revision 2946) @@ -1,1933 +1,1935 @@ -/* $Id$ */ +/* $Id: msdosfs_vnops.c,v 1.1 1994/09/19 15:41:46 dfr 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 /* define FWRITE ... */ #include #include #include #include #include #include /* XXX */ /* defines v_rdev */ #include #include /* defines dirent structure */ #include #include #include #include #include /* * 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. */ 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) ? 0 : 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; } 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; struct denode *pdep = VTODE(ap->a_dvp); 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; } 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; } 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); struct timespec ts; if (vp->v_usecount > 1 && !(dep->de_flag & DE_LOCKED)) { TIMEVAL_TO_TIMESPEC(&time, &ts); DE_TIMES(dep, &ts); } return 0; } /* * This routine will go into sys/kern/vfs_subr.c one day! * * Do the usual access checking. * file_node, uid and gid are from the vnode in question, * while acc_mode and cred are from the VOP_ACCESS parameter list. */ static int vaccess(file_mode, uid, gid, acc_mode, cred) mode_t file_mode; uid_t uid; gid_t gid; mode_t acc_mode; struct ucred *cred; { mode_t mask; int i; register gid_t *gp; /* User id 0 always gets access. */ if (cred->cr_uid == 0) return 0; mask = 0; /* Otherwise, check the owner. */ if (cred->cr_uid == uid) { if (acc_mode & VEXEC) mask |= S_IXUSR; if (acc_mode & VREAD) mask |= S_IRUSR; if (acc_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 (gid == *gp) { if (acc_mode & VEXEC) mask |= S_IXGRP; if (acc_mode & VREAD) mask |= S_IRGRP; if (acc_mode & VWRITE) mask |= S_IWGRP; return (file_mode & mask) == mask ? 0 : EACCES; } /* Otherwise, check everyone else. */ if (acc_mode & VEXEC) mask |= S_IXOTH; if (acc_mode & VREAD) mask |= S_IROTH; if (acc_mode & VWRITE) mask |= S_IWOTH; return (file_mode & mask) == mask ? 0 : EACCES; } int msdosfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { mode_t dosmode; struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; dosmode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); dosmode &= pmp->pm_mask; return vaccess(dosmode, pmp->pm_uid, pmp->pm_gid, ap->a_mode, ap->a_cred); } 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; struct timespec ts; TIMEVAL_TO_TIMESPEC(&time, &ts); DE_TIMES(dep, &ts); 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; #ifndef MSDOSFS_NODIRMOD if (vap->va_mode & S_IFDIR) TIMEVAL_TO_TIMESPEC(&time, &vap->va_mtime); #endif vap->va_ctime = vap->va_atime; vap->va_flags = dep->de_flag; 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; return 0; } 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; { int error = 0; struct denode *dep = VTODE(ap->a_vp); struct vattr *vap = ap->a_vap; struct ucred *cred = ap->a_cred; #ifdef MSDOSFS_DEBUG printf("msdosfs_setattr(): vp %08x, vap %08x, cred %08x, p %08x\n", ap->a_vp, vap, cred, ap->a_p); #endif 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) || (vap->va_uid != VNOVAL) || (vap->va_gid != VNOVAL)) { #ifdef MSDOSFS_DEBUG printf("msdosfs_setattr(): returning EINVAL\n"); printf(" va_type %d, va_nlink %x, va_fsid %x, va_fileid %x\n", vap->va_type, vap->va_nlink, vap->va_fsid, vap->va_fileid); printf(" va_blocksize %x, va_rdev %x, va_bytes %x, va_gen %x\n", vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen); printf(" va_uid %x, va_gid %x\n", vap->va_uid, vap->va_gid); #endif return EINVAL; } if (vap->va_size != VNOVAL) { if (ap->a_vp->v_type == VDIR) return EISDIR; if (error = detrunc(dep, vap->va_size, 0, cred, ap->a_p)) return error; } if (vap->va_mtime.ts_sec != VNOVAL) { dep->de_flag |= DE_UPDATE; if (error = deupdat(dep, &vap->va_mtime, 1)) return error; } /* * DOS files only have the ability to have thier writability * attribute set, so we use the owner write bit to set the readonly * attribute. */ if (vap->va_mode != (u_short) VNOVAL) { /* 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_UPDATE; } if (vap->va_flags != VNOVAL) { if (error = suser(cred, &ap->a_p->p_acflag)) return error; if (cred->cr_uid == 0) dep->de_flag = vap->va_flags; else { dep->de_flag &= 0xffff0000; dep->de_flag |= (vap->va_flags & 0xffff); } dep->de_flag |= DE_UPDATE; } return error; } 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 bn; 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; 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. */ 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; #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; /* * 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) { if (error = pcbmap(dep, bn, &bn, 0)) break; } else if (bn > lastcn) { error = ENOSPC; break; } 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) { if (error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0)) 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. */ if (error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp)) 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; vnode_pager_setsize(vp, dep->de_FileSize); /* why? */ } (void) vnode_pager_uncache(vp); /* why not? */ /* * 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) { bp->b_flags |= B_AGE; 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 { error = deupdat(dep, &time, 1); } return error; } 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; } int msdosfs_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; { return 1; /* DOS filesystems never block? */ } 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. */ int msdosfs_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; int wait = ap->a_waitfor == MNT_WAIT; int error; #if 0 /* * Does this call to vflushbuf() do anything? I can find no code * anywhere that sets v_dirtyblkhd in the vnode, which vflushbuf() * seems to depend upon. */ vflushbuf(vp, wait ? B_SYNC : 0); #endif return deupdat(VTODE(vp), &time, wait); } /* * Now the whole work of extending a file is done in the write function. * So nothing to do here. */ 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; } 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. */ int msdosfs_link(ap) struct vop_link_args /* { struct vnode *a_vp; struct vnode *a_tdvp; struct componentname *a_cnp; } */ *ap; { return VOP_ABORTOP(ap->a_vp, ap->a_cnp); } /* * 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 * update offset and dirclust in denode * move denode to new hash chain * 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. */ 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 to_dirclust; u_long to_diroffset; u_long cn; daddr_t bn; struct vnode *tvp = ap->a_tvp; 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; #ifdef __NetBSD__ /* 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; } #endif /* * 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 & 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"); if (error = relookup(ap->a_tdvp, &tvp, ap->a_tcnp)) { 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; } } else { /* destination is file */ if (sourceisadirectory) { error = EISDIR; goto bad; } } to_dirclust = tdep->de_dirclust; to_diroffset = tdep->de_diroffset; if (error = removede(tddep,tdep)) { goto bad; } vput(ap->a_tvp); tdep = NULL; /* * Remember where the slot was for createde(). */ tddep->de_fndclust = to_dirclust; tddep->de_fndoffset = to_diroffset; } /* * 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 */ VOP_LOCK(ap->a_fvp); /* ap->a_fdvp is already locked */ if (error = readep(fddep->de_pmp, fdep->de_dirclust, fdep->de_diroffset, &bp, &ep)) { VOP_UNLOCK(ap->a_fvp); goto bad; } bcopy(toname, ep->deName, 11); if (error = bwrite(bp)) { VOP_UNLOCK(ap->a_fvp); 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. */ VOP_LOCK(ap->a_fvp); 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); goto bad; } VOP_LOCK(ap->a_fdvp); if (error = readep(fddep->de_pmp, fddep->de_fndclust, fddep->de_fndoffset, &bp, &ep)) { VOP_UNLOCK(ap->a_fvp); VOP_UNLOCK(ap->a_fdvp); goto bad; } ep->deName[0] = SLOT_DELETED; if (error = bwrite(bp)) { VOP_UNLOCK(ap->a_fvp); VOP_UNLOCK(ap->a_fdvp); goto bad; } fdep->de_dirclust = tddep->de_fndclust; fdep->de_diroffset = tddep->de_fndoffset; reinsert(fdep); VOP_UNLOCK(ap->a_fdvp); } /* 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?\n"); } 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); goto bad; } dotdotp = (struct direntry *) bp->b_data + 1; putushort(dotdotp->deStartCluster, tddep->de_StartCluster); error = bwrite(bp); VOP_UNLOCK(ap->a_fvp); if (error) { /* should really panic here, fs is corrupt */ goto bad; } } else VOP_UNLOCK(ap->a_fvp); bad: ; vrele(DETOV(fdep)); vrele(DETOV(fddep)); if (tdep) vput(DETOV(tdep)); if (tddep) vput(DETOV(tddep)); return error; } 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 */ }; 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. */ if (error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL)) { 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); if (error = bwrite(bp)) { 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; } 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 */ /* * Don't let "rmdir ." go thru. */ if (ddep == dep) { vrele(ap->a_vp); vput(ap->a_vp); return EINVAL; } /* * 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. */ if (error = removede(ddep,dep)) 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); 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. */ 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; { struct denode *pdep = VTODE(ap->a_dvp); 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). */ 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 */ } }; int msdosfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_int *a_cookies; int a_ncookies; } */ *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 */ int i = 0; struct uio *uio = ap->a_uio; int ncookies = 1; u_int* cookies = NULL; #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 #if 0 if (!ap->a_cookies) ncookies = 1; #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; /* * 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; if (cookies) { *cookies++ = sizeof(struct direntry); ncookies--; } } if (cookies) { if (ncookies-- <= 0) n--; else *cookies++ = 2 * sizeof(struct direntry); } error = uiomove((char *) rootdots + uio->uio_offset, n * sizeof(struct direntry), uio); } } while (!error && uio->uio_resid > 0 && ncookies > 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) return 0; 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); if (cookies) { ncookies++; cookies--; } } 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; } } 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; } dentp++; if (cookies) { *cookies++ = (u_int)((char *)dentp - bp->b_data - on) + uio->uio_offset; ncookies--; } 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 (ncookies <= 0) break; } 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 0 /* * I don't know why we bother setting this eofflag, getdirentries() * in vfs_syscalls.c doesn't bother to look at it when we return. * (because NFS uses it in nfs_serv.c -- JMP) */ if (dep->de_FileSize - uio->uio_offset - bias <= 0) *ap->a_eofflag = 1; else *ap->a_eofflag = 0; #endif return error; } /* * DOS filesystems don't know what symlinks are. */ int msdosfs_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { return EINVAL; } 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; } int msdosfs_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; } */ *ap; { struct denode *dep = VTODE(ap->a_vp); while (dep->de_flag & DE_LOCKED) { dep->de_flag |= DE_WANTED; if (dep->de_lockholder == curproc->p_pid) panic("msdosfs_lock: locking against myself"); dep->de_lockwaiter = curproc->p_pid; (void) sleep((caddr_t) dep, PINOD); } dep->de_lockwaiter = 0; dep->de_lockholder = curproc->p_pid; dep->de_flag |= DE_LOCKED; return 0; } int msdosfs_unlock(ap) struct vop_unlock_args /* { struct vnode *vp; } */ *ap; { struct denode *dep = VTODE(ap->a_vp); if (!(dep->de_flag & DE_LOCKED)) panic("msdosfs_unlock: denode not locked"); dep->de_lockholder = 0; dep->de_flag &= ~DE_LOCKED; if (dep->de_flag & DE_WANTED) { dep->de_flag &= ~DE_WANTED; wakeup((caddr_t) dep); } return 0; } int msdosfs_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { return VTODE(ap->a_vp)->de_flag & DE_LOCKED ? 1 : 0; } /* * 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 */ 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; } */ *ap; { struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; 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; } return pcbmap(dep, ap->a_bn, ap->a_bnp, 0); } 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; } 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 msdosfsmount *pmp = dep->de_pmp; 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) { if (error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0)) 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; } 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 %d, diroffset %d ", dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset); printf(" dev %d, %d, %s\n", major(dep->de_dev), minor(dep->de_dev), dep->de_flag & DE_LOCKED ? "(LOCKED)" : ""); if (dep->de_lockholder) { printf(" owner pid %d", dep->de_lockholder); if (dep->de_lockwaiter) printf(" waiting pid %d", dep->de_lockwaiter); printf("\n"); } return 0; } 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 */ } 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 */ int (**msdosfs_vnodeop_p)(); struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, msdosfs_lookup }, /* lookup */ { &vop_create_desc, msdosfs_create }, /* create */ { &vop_mknod_desc, msdosfs_mknod }, /* mknod */ { &vop_open_desc, msdosfs_open }, /* open */ { &vop_close_desc, msdosfs_close }, /* close */ { &vop_access_desc, msdosfs_access }, /* access */ { &vop_getattr_desc, msdosfs_getattr }, /* getattr */ { &vop_setattr_desc, msdosfs_setattr }, /* setattr */ { &vop_read_desc, msdosfs_read }, /* read */ { &vop_write_desc, msdosfs_write }, /* write */ { &vop_ioctl_desc, msdosfs_ioctl }, /* ioctl */ { &vop_select_desc, msdosfs_select }, /* select */ { &vop_mmap_desc, msdosfs_mmap }, /* mmap */ { &vop_fsync_desc, msdosfs_fsync }, /* fsync */ { &vop_seek_desc, msdosfs_seek }, /* seek */ { &vop_remove_desc, msdosfs_remove }, /* remove */ { &vop_link_desc, msdosfs_link }, /* link */ { &vop_rename_desc, msdosfs_rename }, /* rename */ { &vop_mkdir_desc, msdosfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, msdosfs_rmdir }, /* rmdir */ { &vop_symlink_desc, msdosfs_symlink }, /* symlink */ { &vop_readdir_desc, msdosfs_readdir }, /* readdir */ { &vop_readlink_desc, msdosfs_readlink }, /* readlink */ { &vop_abortop_desc, msdosfs_abortop }, /* abortop */ { &vop_inactive_desc, msdosfs_inactive }, /* inactive */ { &vop_reclaim_desc, msdosfs_reclaim }, /* reclaim */ { &vop_lock_desc, msdosfs_lock }, /* lock */ { &vop_unlock_desc, msdosfs_unlock }, /* unlock */ { &vop_bmap_desc, msdosfs_bmap }, /* bmap */ { &vop_strategy_desc, msdosfs_strategy }, /* strategy */ { &vop_print_desc, msdosfs_print }, /* print */ { &vop_islocked_desc, msdosfs_islocked }, /* islocked */ { &vop_pathconf_desc, msdosfs_pathconf }, /* pathconf */ { &vop_advlock_desc, msdosfs_advlock }, /* advlock */ { &vop_reallocblks_desc, msdosfs_reallocblks }, /* reallocblks */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc *)NULL, (int (*)())NULL } }; struct vnodeopv_desc msdosfs_vnodeop_opv_desc = { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries }; + +VNODEOP_SET(msdosfs_vnodeop_opv_desc); Index: head/sys/fs/nullfs/null_vfsops.c =================================================================== --- head/sys/fs/nullfs/null_vfsops.c (revision 2945) +++ head/sys/fs/nullfs/null_vfsops.c (revision 2946) @@ -1,366 +1,368 @@ /* * 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. * * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94 * * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92 - * $Id: lofs_vfsops.c,v 1.9 1992/05/30 10:26:24 jsp Exp jsp $ + * $Id: null_vfsops.c,v 1.1.1.1 1994/05/24 10:05:03 rgrimes Exp $ */ /* * Null Layer * (See null_vnops.c for a description of what this does.) */ #include #include #include #include #include #include #include #include #include /* * Mount null layer */ int nullfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { int error = 0; struct null_args args; struct vnode *lowerrootvp, *vp; struct vnode *nullm_rootvp; struct null_mount *xmp; u_int size; #ifdef NULLFS_DIAGNOSTIC printf("nullfs_mount(mp = %x)\n", mp); #endif /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) { return (EOPNOTSUPP); /* return VFS_MOUNT(MOUNTTONULLMOUNT(mp)->nullm_vfs, path, data, ndp, p);*/ } /* * Get argument */ if (error = copyin(data, (caddr_t)&args, sizeof(struct null_args))) return (error); /* * Find lower node */ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF, UIO_USERSPACE, args.target, p); if (error = namei(ndp)) return (error); /* * Sanity check on lower vnode */ lowerrootvp = ndp->ni_vp; vrele(ndp->ni_dvp); ndp->ni_dvp = NULL; xmp = (struct null_mount *) malloc(sizeof(struct null_mount), M_UFSMNT, M_WAITOK); /* XXX */ /* * Save reference to underlying FS */ xmp->nullm_vfs = lowerrootvp->v_mount; /* * Save reference. Each mount also holds * a reference on the root vnode. */ error = null_node_create(mp, lowerrootvp, &vp); /* * Unlock the node (either the lower or the alias) */ VOP_UNLOCK(vp); /* * Make sure the node alias worked */ if (error) { vrele(lowerrootvp); free(xmp, M_UFSMNT); /* XXX */ return (error); } /* * Keep a held reference to the root vnode. * It is vrele'd in nullfs_unmount. */ nullm_rootvp = vp; nullm_rootvp->v_flag |= VROOT; xmp->nullm_rootvp = nullm_rootvp; if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) mp->mnt_flag |= MNT_LOCAL; mp->mnt_data = (qaddr_t) xmp; getnewfsid(mp, MOUNT_LOFS); (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); #ifdef NULLFS_DIAGNOSTIC printf("nullfs_mount: lower %s, alias at %s\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); #endif return (0); } /* * VFS start. Nothing needed here - the start routine * on the underlying filesystem will have been called * when that filesystem was mounted. */ int nullfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); /* return VFS_START(MOUNTTONULLMOUNT(mp)->nullm_vfs, flags, p); */ } /* * Free reference to null layer */ int nullfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { struct vnode *nullm_rootvp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; int error; int flags = 0; extern int doforce; #ifdef NULLFS_DIAGNOSTIC printf("nullfs_unmount(mp = %x)\n", mp); #endif if (mntflags & MNT_FORCE) { /* lofs can never be rootfs so don't check for it */ if (!doforce) return (EINVAL); flags |= FORCECLOSE; } /* * Clear out buffer cache. I don't think we * ever get anything cached at this level at the * moment, but who knows... */ #if 0 mntflushbuf(mp, 0); if (mntinvalbuf(mp, 1)) return (EBUSY); #endif if (nullm_rootvp->v_usecount > 1) return (EBUSY); if (error = vflush(mp, nullm_rootvp, flags)) return (error); #ifdef NULLFS_DIAGNOSTIC vprint("alias root of lower", nullm_rootvp); #endif /* * Release reference on underlying root vnode */ vrele(nullm_rootvp); /* * And blow it away for future re-use */ vgone(nullm_rootvp); /* * Finally, throw away the null_mount structure */ free(mp->mnt_data, M_UFSMNT); /* XXX */ mp->mnt_data = 0; return 0; } int nullfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct vnode *vp; #ifdef NULLFS_DIAGNOSTIC printf("nullfs_root(mp = %x, vp = %x->%x)\n", mp, MOUNTTONULLMOUNT(mp)->nullm_rootvp, NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp) ); #endif /* * Return locked reference to root. */ vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; VREF(vp); VOP_LOCK(vp); *vpp = vp; return 0; } int nullfs_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, p); } int nullfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { int error; struct statfs mstat; #ifdef NULLFS_DIAGNOSTIC printf("nullfs_statfs(mp = %x, vp = %x->%x)\n", mp, MOUNTTONULLMOUNT(mp)->nullm_rootvp, NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp) ); #endif bzero(&mstat, sizeof(mstat)); error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, p); if (error) return (error); /* now copy across the "interesting" information and fake the rest */ sbp->f_type = mstat.f_type; sbp->f_flags = mstat.f_flags; sbp->f_bsize = mstat.f_bsize; sbp->f_iosize = mstat.f_iosize; sbp->f_blocks = mstat.f_blocks; sbp->f_bfree = mstat.f_bfree; sbp->f_bavail = mstat.f_bavail; sbp->f_files = mstat.f_files; sbp->f_ffree = mstat.f_ffree; if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } int nullfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { /* * XXX - Assumes no data cached at null layer. */ return (0); } int nullfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, vpp); } int nullfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp) struct mount *mp; struct fid *fidp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred**credanonp; { return VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, nam, vpp, exflagsp,credanonp); } int nullfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return VFS_VPTOFH(NULLVPTOLOWERVP(vp), fhp); } int nullfs_init __P((void)); struct vfsops null_vfsops = { nullfs_mount, nullfs_start, nullfs_unmount, nullfs_root, nullfs_quotactl, nullfs_statfs, nullfs_sync, nullfs_vget, nullfs_fhtovp, nullfs_vptofh, nullfs_init, }; + +VFS_SET(null_vfsops, null, MOUNT_NULL, 0); Index: head/sys/fs/nullfs/null_vnops.c =================================================================== --- head/sys/fs/nullfs/null_vnops.c (revision 2945) +++ head/sys/fs/nullfs/null_vnops.c (revision 2946) @@ -1,457 +1,459 @@ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * John Heidemann of the UCLA Ficus project. * * 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. * * @(#)null_vnops.c 8.1 (Berkeley) 6/10/93 * - * $Id: null_vnops.c,v 1.2 1994/08/02 07:45:06 davidg Exp $ + * $Id: null_vnops.c,v 1.3 1994/08/20 03:48:51 davidg Exp $ */ /* * Null Layer * * (See mount_null(8) for more information.) * * The null layer duplicates a portion of the file system * name space under a new name. In this respect, it is * similar to the loopback file system. It differs from * the loopback fs in two respects: it is implemented using * a stackable layers techniques, and it's "null-node"s stack above * all lower-layer vnodes, not just over directory vnodes. * * The null layer has two purposes. First, it serves as a demonstration * of layering by proving a layer which does nothing. (It actually * does everything the loopback file system does, which is slightly * more than nothing.) Second, the null layer can serve as a prototype * layer. Since it provides all necessary layer framework, * new file system layers can be created very easily be starting * with a null layer. * * The remainder of this man page examines the null layer as a basis * for constructing new layers. * * * INSTANTIATING NEW NULL LAYERS * * New null layers are created with mount_null(8). * Mount_null(8) takes two arguments, the pathname * of the lower vfs (target-pn) and the pathname where the null * layer will appear in the namespace (alias-pn). After * the null layer is put into place, the contents * of target-pn subtree will be aliased under alias-pn. * * * OPERATION OF A NULL LAYER * * The null layer is the minimum file system layer, * simply bypassing all possible operations to the lower layer * for processing there. The majority of its activity centers * on the bypass routine, though which nearly all vnode operations * pass. * * The bypass routine accepts arbitrary vnode operations for * handling by the lower layer. It begins by examing vnode * operation arguments and replacing any null-nodes by their * lower-layer equivlants. It then invokes the operation * on the lower layer. Finally, it replaces the null-nodes * in the arguments and, if a vnode is return by the operation, * stacks a null-node on top of the returned vnode. * * Although bypass handles most operations, * vop_getattr, _inactive, _reclaim, and _print are not bypassed. * Vop_getattr must change the fsid being returned. * Vop_inactive and vop_reclaim are not bypassed so that * they can handle freeing null-layer specific data. * Vop_print is not bypassed to avoid excessive debugging * information. * * * INSTANTIATING VNODE STACKS * * Mounting associates the null layer with a lower layer, * effect stacking two VFSes. Vnode stacks are instead * created on demand as files are accessed. * * The initial mount creates a single vnode stack for the * root of the new null layer. All other vnode stacks * are created as a result of vnode operations on * this or other null vnode stacks. * * New vnode stacks come into existance as a result of * an operation which returns a vnode. * The bypass routine stacks a null-node above the new * vnode before returning it to the caller. * * For example, imagine mounting a null layer with * "mount_null /usr/include /dev/layer/null". * Changing directory to /dev/layer/null will assign * the root null-node (which was created when the null layer was mounted). * Now consider opening "sys". A vop_lookup would be * done on the root null-node. This operation would bypass through * to the lower layer which would return a vnode representing * the UFS "sys". Null_bypass then builds a null-node * aliasing the UFS "sys" and returns this to the caller. * Later operations on the null-node "sys" will repeat this * process when constructing other vnode stacks. * * * CREATING OTHER FILE SYSTEM LAYERS * * One of the easiest ways to construct new file system layers is to make * a copy of the null layer, rename all files and variables, and * then begin modifing the copy. Sed can be used to easily rename * all variables. * * The umap layer is an example of a layer descended from the * null layer. * * * INVOKING OPERATIONS ON LOWER LAYERS * * There are two techniques to invoke operations on a lower layer * when the operation cannot be completely bypassed. Each method * is appropriate in different situations. In both cases, * it is the responsibility of the aliasing layer to make * the operation arguments "correct" for the lower layer * by mapping an vnode arguments to the lower layer. * * The first approach is to call the aliasing layer's bypass routine. * This method is most suitable when you wish to invoke the operation * currently being hanldled on the lower layer. It has the advantage * that the bypass routine already must do argument mapping. * An example of this is null_getattrs in the null layer. * * A second approach is to directly invoked vnode operations on * the lower layer with the VOP_OPERATIONNAME interface. * The advantage of this method is that it is easy to invoke * arbitrary operations on the lower layer. The disadvantage * is that vnodes arguments must be manualy mapped. * */ #include #include #include #include #include #include #include #include #include #include #include int null_bug_bypass = 0; /* for debugging: enables bypass printf'ing */ /* * This is the 10-Apr-92 bypass routine. * This version has been optimized for speed, throwing away some * safety checks. It should still always work, but it's not as * robust to programmer errors. * Define SAFETY to include some error checking code. * * In general, we map all vnodes going down and unmap them on the way back. * As an exception to this, vnodes can be marked "unmapped" by setting * the Nth bit in operation's vdesc_flags. * * Also, some BSD vnode operations have the side effect of vrele'ing * their arguments. With stacking, the reference counts are held * by the upper node, not the lower one, so we must handle these * side-effects here. This is not of concern in Sun-derived systems * since there are no such side-effects. * * This makes the following assumptions: * - only one returned vpp * - no INOUT vpp's (Sun's vop_open has one of these) * - the vnode operation vector of the first vnode should be used * to determine what implementation of the op should be invoked * - all mapped vnodes are of our vnode-type (NEEDSWORK: * problems on rmdir'ing mount points and renaming?) */ int null_bypass(ap) struct vop_generic_args /* { struct vnodeop_desc *a_desc; } */ *ap; { register struct vnode **this_vp_p; int error; struct vnode *old_vps[VDESC_MAX_VPS]; struct vnode **vps_p[VDESC_MAX_VPS]; struct vnode ***vppp; struct vnodeop_desc *descp = ap->a_desc; int reles, i; if (null_bug_bypass) printf ("null_bypass: %s\n", descp->vdesc_name); #ifdef SAFETY /* * We require at least one vp. */ if (descp->vdesc_vp_offsets == NULL || descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET) panic ("null_bypass: no vp's in map.\n"); #endif /* * Map the vnodes going in. * Later, we'll invoke the operation based on * the first mapped vnode's operation vector. */ reles = descp->vdesc_flags; for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) { if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) break; /* bail out at end of list */ vps_p[i] = this_vp_p = VOPARG_OFFSETTO(struct vnode**,descp->vdesc_vp_offsets[i],ap); /* * We're not guaranteed that any but the first vnode * are of our type. Check for and don't map any * that aren't. (We must always map first vp or vclean fails.) */ if (i && (*this_vp_p)->v_op != null_vnodeop_p) { old_vps[i] = NULL; } else { old_vps[i] = *this_vp_p; *(vps_p[i]) = NULLVPTOLOWERVP(*this_vp_p); /* * XXX - Several operations have the side effect * of vrele'ing their vp's. We must account for * that. (This should go away in the future.) */ if (reles & 1) VREF(*this_vp_p); } } /* * Call the operation on the lower layer * with the modified argument structure. */ error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap); /* * Maintain the illusion of call-by-value * by restoring vnodes in the argument structure * to their original value. */ reles = descp->vdesc_flags; for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) { if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) break; /* bail out at end of list */ if (old_vps[i]) { *(vps_p[i]) = old_vps[i]; if (reles & 1) vrele(*(vps_p[i])); } } /* * Map the possible out-going vpp * (Assumes that the lower layer always returns * a VREF'ed vpp unless it gets an error.) */ if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET && !(descp->vdesc_flags & VDESC_NOMAP_VPP) && !error) { /* * XXX - even though some ops have vpp returned vp's, * several ops actually vrele this before returning. * We must avoid these ops. * (This should go away when these ops are regularized.) */ if (descp->vdesc_flags & VDESC_VPP_WILLRELE) goto out; vppp = VOPARG_OFFSETTO(struct vnode***, descp->vdesc_vpp_offset,ap); error = null_node_create(old_vps[0]->v_mount, **vppp, *vppp); } out: return (error); } /* * We handle getattr only to change the fsid. */ int null_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; if (error = null_bypass(ap)) return (error); /* Requires that arguments be restored. */ ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; return (0); } int null_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { /* * Do nothing (and _don't_ bypass). * Wait to vrele lowervp until reclaim, * so that until then our null_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. */ return (0); } int null_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; struct null_node *xp = VTONULL(vp); struct vnode *lowervp = xp->null_lowervp; /* * Note: in vop_reclaim, vp->v_op == dead_vnodeop_p, * so we can't call VOPs on ourself. */ /* After this assignment, this node will not be re-used. */ xp->null_lowervp = NULL; remque(xp); FREE(vp->v_data, M_TEMP); vp->v_data = NULL; vrele (lowervp); return (0); } int null_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { register struct vnode *vp = ap->a_vp; printf ("\ttag VT_NULLFS, vp=%x, lowervp=%x\n", vp, NULLVPTOLOWERVP(vp)); return (0); } /* * XXX - vop_strategy must be hand coded because it has no * vnode in its arguments. * This goes away with a merged VM/buffer cache. */ int null_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 = NULLVPTOLOWERVP(bp->b_vp); error = VOP_STRATEGY(bp); bp->b_vp = savedvp; return (error); } /* * XXX - like vop_strategy, vop_bwrite must be hand coded because it has no * vnode in its arguments. * This goes away with a merged VM/buffer cache. */ int null_bwrite(ap) struct vop_bwrite_args /* { struct buf *a_bp; } */ *ap; { struct buf *bp = ap->a_bp; int error; struct vnode *savedvp; savedvp = bp->b_vp; bp->b_vp = NULLVPTOLOWERVP(bp->b_vp); error = VOP_BWRITE(bp); bp->b_vp = savedvp; return (error); } /* * Global vfs data structures */ int (**null_vnodeop_p)(); struct vnodeopv_entry_desc null_vnodeop_entries[] = { { &vop_default_desc, null_bypass }, { &vop_getattr_desc, null_getattr }, { &vop_inactive_desc, null_inactive }, { &vop_reclaim_desc, null_reclaim }, { &vop_print_desc, null_print }, { &vop_strategy_desc, null_strategy }, { &vop_bwrite_desc, null_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc null_vnodeop_opv_desc = { &null_vnodeop_p, null_vnodeop_entries }; + +VNODEOP_SET(null_vnodeop_opv_desc); Index: head/sys/fs/portalfs/portal_vfsops.c =================================================================== --- head/sys/fs/portalfs/portal_vfsops.c (revision 2945) +++ head/sys/fs/portalfs/portal_vfsops.c (revision 2946) @@ -1,313 +1,315 @@ /* * 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_vfsops.c 8.6 (Berkeley) 1/21/94 * - * $Id: portal_vfsops.c,v 1.5 1992/05/30 10:25:27 jsp Exp jsp $ + * $Id: portal_vfsops.c,v 1.1.1.1 1994/05/24 10:05:06 rgrimes Exp $ */ /* * Portal Filesystem */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int portal_init() { return (0); } /* * Mount the per-process file descriptors (/dev/fd) */ int portal_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { struct file *fp; struct portal_args args; struct portalmount *fmp; struct socket *so; struct vnode *rvp; u_int size; int error; /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) return (EOPNOTSUPP); if (error = copyin(data, (caddr_t) &args, sizeof(struct portal_args))) return (error); if (error = getsock(p->p_fd, args.pa_socket, &fp)) return (error); so = (struct socket *) fp->f_data; if (so->so_proto->pr_domain->dom_family != AF_UNIX) return (ESOCKTNOSUPPORT); error = getnewvnode(VT_PORTAL, mp, portal_vnodeop_p, &rvp); /* XXX */ if (error) return (error); MALLOC(rvp->v_data, void *, sizeof(struct portalnode), M_TEMP, M_WAITOK); fmp = (struct portalmount *) malloc(sizeof(struct portalmount), M_UFSMNT, M_WAITOK); /* XXX */ rvp->v_type = VDIR; rvp->v_flag |= VROOT; VTOPORTAL(rvp)->pt_arg = 0; VTOPORTAL(rvp)->pt_size = 0; VTOPORTAL(rvp)->pt_fileid = PORTAL_ROOTFILEID; fmp->pm_root = rvp; fmp->pm_server = fp; fp->f_count++; mp->mnt_flag |= MNT_LOCAL; mp->mnt_data = (qaddr_t) fmp; getnewfsid(mp, MOUNT_PORTAL); (void)copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); (void)copyinstr(args.pa_config, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); #ifdef notdef bzero(mp->mnt_stat.f_mntfromname, MNAMELEN); bcopy("portal", mp->mnt_stat.f_mntfromname, sizeof("portal")); #endif return (0); } int portal_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } int portal_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { extern int doforce; struct vnode *rootvp = VFSTOPORTAL(mp)->pm_root; int error, flags = 0; if (mntflags & MNT_FORCE) { /* portal can never be rootfs so don't check for it */ if (!doforce) return (EINVAL); flags |= FORCECLOSE; } /* * Clear out buffer cache. I don't think we * ever get anything cached at this level at the * moment, but who knows... */ #ifdef notyet mntflushbuf(mp, 0); if (mntinvalbuf(mp, 1)) return (EBUSY); #endif if (rootvp->v_usecount > 1) return (EBUSY); if (error = vflush(mp, rootvp, flags)) return (error); /* * Release reference on underlying root vnode */ vrele(rootvp); /* * And blow it away for future re-use */ vgone(rootvp); /* * Shutdown the socket. This will cause the select in the * daemon to wake up, and then the accept will get ECONNABORTED * which it interprets as a request to go and bury itself. */ soshutdown((struct socket *) VFSTOPORTAL(mp)->pm_server->f_data, 2); /* * Discard reference to underlying file. Must call closef because * this may be the last reference. */ closef(VFSTOPORTAL(mp)->pm_server, (struct proc *) 0); /* * Finally, throw away the portalmount structure */ free(mp->mnt_data, M_UFSMNT); /* XXX */ mp->mnt_data = 0; return (0); } int portal_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct vnode *vp; /* * Return locked reference to root. */ vp = VFSTOPORTAL(mp)->pm_root; VREF(vp); VOP_LOCK(vp); *vpp = vp; return (0); } int portal_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } int portal_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { sbp->f_type = MOUNT_PORTAL; sbp->f_flags = 0; sbp->f_bsize = DEV_BSIZE; sbp->f_iosize = DEV_BSIZE; sbp->f_blocks = 2; /* 1K to keep df happy */ sbp->f_bfree = 0; sbp->f_bavail = 0; sbp->f_files = 1; /* Allow for "." */ sbp->f_ffree = 0; /* See comments above */ if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } int portal_sync(mp, waitfor) struct mount *mp; int waitfor; { return (0); } int portal_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } int portal_fhtovp(mp, fhp, vpp) struct mount *mp; struct fid *fhp; struct vnode **vpp; { return (EOPNOTSUPP); } int portal_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (EOPNOTSUPP); } struct vfsops portal_vfsops = { portal_mount, portal_start, portal_unmount, portal_root, portal_quotactl, portal_statfs, portal_sync, portal_vget, portal_fhtovp, portal_vptofh, portal_init, }; + +VFS_SET(portal_vfsops, portal, MOUNT_PORTAL, 0); Index: head/sys/fs/portalfs/portal_vnops.c =================================================================== --- head/sys/fs/portalfs/portal_vnops.c (revision 2945) +++ head/sys/fs/portalfs/portal_vnops.c (revision 2946) @@ -1,709 +1,711 @@ /* * 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.8 (Berkeley) 1/21/94 * - * $Id: portal_vnops.c,v 1.2 1994/05/25 09:08:21 rgrimes Exp $ + * $Id: portal_vnops.c,v 1.3 1994/08/20 03:48:52 davidg Exp $ */ /* * Portal Filesystem */ #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 void portal_closefd(p, fd) struct proc *p; int fd; { int error; struct { int fd; } 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... */ int portal_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { char *pname = ap->a_cnp->cn_nameptr; struct portalnode *pt; int error; struct vnode *fvp = 0; char *path; int size; if (ap->a_cnp->cn_namelen == 1 && *pname == '.') { *ap->a_vpp = ap->a_dvp; VREF(ap->a_dvp); /*VOP_LOCK(ap->a_dvp);*/ return (0); } error = getnewvnode(VT_PORTAL, ap->a_dvp->v_mount, portal_vnodeop_p, &fvp); if (error) goto bad; fvp->v_type = VREG; MALLOC(fvp->v_data, void *, sizeof(struct portalnode), M_TEMP, M_WAITOK); pt = VTOPORTAL(fvp); /* * 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++; ap->a_cnp->cn_consume = size - ap->a_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++; *ap->a_vpp = fvp; /*VOP_LOCK(fvp);*/ return (0); bad:; if (fvp) { vrele(fvp); } *ap->a_vpp = NULL; 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 = m_copy(unp2->unp_addr, 0, (int)M_COPYALL); so2 = so3; return (unp_connect2(so, so2)); } 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); 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 mbuf *) 0, &auio, (struct mbuf *) 0, (struct mbuf *) 0, 0); if (error) goto bad; len = auio.uio_resid = sizeof(int); do { struct mbuf *m = 0; int flags = MSG_WAITALL; error = soreceive(so, (struct mbuf **) 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); } 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; 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((struct timeval *)&vap->va_atime); TIMEVAL_TO_TIMESPEC((struct timeval *)&vap->va_atime, (struct timespec *)&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); } 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. */ int portal_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { return (0); } int portal_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { return (0); } 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. */ 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 */ int portal_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_PORTAL, portal vnode\n"); return (0); } /*void*/ 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 */ int portal_enotsupp() { return (EOPNOTSUPP); } /* * Portal "should never get here" operation */ int portal_badop() { panic("portal: bad op"); /* NOTREACHED */ } /* * Portal vnode null operation */ int portal_nullop() { return (0); } #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_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 *)))nullop) #define portal_unlock ((int (*) __P((struct vop_unlock_args *)))nullop) #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 *)))nullop) #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) int (**portal_vnodeop_p)(); struct vnodeopv_entry_desc portal_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, portal_lookup }, /* lookup */ { &vop_create_desc, portal_create }, /* create */ { &vop_mknod_desc, portal_mknod }, /* mknod */ { &vop_open_desc, portal_open }, /* open */ { &vop_close_desc, portal_close }, /* close */ { &vop_access_desc, portal_access }, /* access */ { &vop_getattr_desc, portal_getattr }, /* getattr */ { &vop_setattr_desc, portal_setattr }, /* setattr */ { &vop_read_desc, portal_read }, /* read */ { &vop_write_desc, portal_write }, /* write */ { &vop_ioctl_desc, portal_ioctl }, /* ioctl */ { &vop_select_desc, portal_select }, /* select */ { &vop_mmap_desc, portal_mmap }, /* mmap */ { &vop_fsync_desc, portal_fsync }, /* fsync */ { &vop_seek_desc, portal_seek }, /* seek */ { &vop_remove_desc, portal_remove }, /* remove */ { &vop_link_desc, portal_link }, /* link */ { &vop_rename_desc, portal_rename }, /* rename */ { &vop_mkdir_desc, portal_mkdir }, /* mkdir */ { &vop_rmdir_desc, portal_rmdir }, /* rmdir */ { &vop_symlink_desc, portal_symlink }, /* symlink */ { &vop_readdir_desc, portal_readdir }, /* readdir */ { &vop_readlink_desc, portal_readlink }, /* readlink */ { &vop_abortop_desc, portal_abortop }, /* abortop */ { &vop_inactive_desc, portal_inactive }, /* inactive */ { &vop_reclaim_desc, portal_reclaim }, /* reclaim */ { &vop_lock_desc, portal_lock }, /* lock */ { &vop_unlock_desc, portal_unlock }, /* unlock */ { &vop_bmap_desc, portal_bmap }, /* bmap */ { &vop_strategy_desc, portal_strategy }, /* strategy */ { &vop_print_desc, portal_print }, /* print */ { &vop_islocked_desc, portal_islocked }, /* islocked */ { &vop_pathconf_desc, portal_pathconf }, /* pathconf */ { &vop_advlock_desc, portal_advlock }, /* advlock */ { &vop_blkatoff_desc, portal_blkatoff }, /* blkatoff */ { &vop_valloc_desc, portal_valloc }, /* valloc */ { &vop_vfree_desc, portal_vfree }, /* vfree */ { &vop_truncate_desc, portal_truncate }, /* truncate */ { &vop_update_desc, portal_update }, /* update */ { &vop_bwrite_desc, portal_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc portal_vnodeop_opv_desc = { &portal_vnodeop_p, portal_vnodeop_entries }; + +VNODEOP_SET(portal_vnodeop_opv_desc); Index: head/sys/fs/procfs/procfs_vfsops.c =================================================================== --- head/sys/fs/procfs/procfs_vfsops.c (revision 2945) +++ head/sys/fs/procfs/procfs_vfsops.c (revision 2946) @@ -1,254 +1,256 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * 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_vfsops.c 8.4 (Berkeley) 1/21/94 * - * $Id: procfs_vfsops.c,v 1.3 1994/08/02 07:45:23 davidg Exp $ + * $Id: procfs_vfsops.c,v 1.4 1994/08/18 22:35:14 wollman Exp $ */ /* * procfs VFS interface */ #include #include #include #include #include #include #include #include #include #include #include #include /* for PAGE_SIZE */ /* * VFS Operations. * * mount system call */ /* ARGSUSED */ int procfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { u_int size; if (UIO_MX & (UIO_MX-1)) { log(LOG_ERR, "procfs: invalid directory entry size"); return (EINVAL); } if (mp->mnt_flag & MNT_UPDATE) return (EOPNOTSUPP); mp->mnt_flag |= MNT_LOCAL; mp->mnt_data = 0; getnewfsid(mp, MOUNT_PROCFS); (void) copyinstr(path, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); size = sizeof("procfs") - 1; bcopy("procfs", mp->mnt_stat.f_mntfromname, size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); return (0); } /* * unmount system call */ int procfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { int error; extern int doforce; int flags = 0; if (mntflags & MNT_FORCE) { /* procfs can never be rootfs so don't check for it */ if (!doforce) return (EINVAL); flags |= FORCECLOSE; } if (error = vflush(mp, 0, flags)) return (error); return (0); } int procfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct pfsnode *pfs; struct vnode *vp; int error; error = procfs_allocvp(mp, &vp, (pid_t) 0, Proot); if (error) return (error); vp->v_type = VDIR; vp->v_flag = VROOT; pfs = VTOPFS(vp); *vpp = vp; return (0); } /* */ /* ARGSUSED */ int procfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } /* * Get file system statistics. */ int procfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { sbp->f_type = MOUNT_PROCFS; sbp->f_bsize = PAGE_SIZE; sbp->f_iosize = PAGE_SIZE; sbp->f_blocks = 1; /* avoid divide by zero in some df's */ sbp->f_bfree = 0; sbp->f_bavail = 0; sbp->f_files = maxproc; /* approx */ sbp->f_ffree = maxproc - nprocs; /* approx */ if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } int procfs_quotactl(mp, cmds, uid, arg, p) struct mount *mp; int cmds; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } int procfs_sync(mp, waitfor) struct mount *mp; int waitfor; { return (0); } int procfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } int procfs_fhtovp(mp, fhp, vpp) struct mount *mp; struct fid *fhp; struct vnode **vpp; { return (EINVAL); } int procfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return EINVAL; } int procfs_init() { return (0); } struct vfsops procfs_vfsops = { procfs_mount, procfs_start, procfs_unmount, procfs_root, procfs_quotactl, procfs_statfs, procfs_sync, procfs_vget, procfs_fhtovp, procfs_vptofh, procfs_init, }; + +VFS_SET(procfs_vfsops, procfs, MOUNT_PROCFS, 0); Index: head/sys/fs/procfs/procfs_vnops.c =================================================================== --- head/sys/fs/procfs/procfs_vnops.c (revision 2945) +++ head/sys/fs/procfs/procfs_vnops.c (revision 2946) @@ -1,833 +1,835 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * 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.6 (Berkeley) 2/7/94 * - * $Id: procfs_vnops.c,v 1.3 1994/08/02 07:45:25 davidg Exp $ + * $Id: procfs_vnops.c,v 1.4 1994/08/18 22:35:15 wollman Exp $ */ /* * procfs vnode interface */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* for PAGE_SIZE */ /* * Vnode Operations. * */ /* * This is a list of the valid names in the * process-specific sub-directories. It is * used in procfs_lookup and procfs_readdir */ static struct pfsnames { u_short d_namlen; char d_name[PROCFS_NAMELEN]; pfstype d_pfstype; } procent[] = { #define N(s) sizeof(s)-1, s /* namlen, nam, type */ { N("file"), Pfile }, { N("mem"), Pmem }, { N("regs"), Pregs }, { N("fpregs"), Pfpregs }, { N("ctl"), Pctl }, { N("status"), Pstatus }, { N("note"), Pnote }, { N("notepg"), Pnotepg }, #undef N }; #define Nprocent (sizeof(procent)/sizeof(procent[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. */ int procfs_open(ap) struct vop_open_args *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); switch (pfs->pfs_type) { case Pmem: if (PFIND(pfs->pfs_pid) == 0) return (ENOENT); /* was ESRCH, jsp */ if ((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL) || (pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE)) return (EBUSY); 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). */ int procfs_close(ap) struct vop_close_args *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; } return (0); } /* * do an ioctl operation on pfsnode (vp). * (vp) is not locked on entry or exit. */ int procfs_ioctl(ap) struct vop_ioctl_args *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. */ int procfs_bmap(ap) struct vop_bmap_args *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; return (0); } /* * _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 not locked on entry or exit. */ int procfs_inactive(ap) struct vop_inactive_args *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); if (PFIND(pfs->pfs_pid) == 0) vgone(ap->a_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. */ int procfs_reclaim(ap) struct vop_reclaim_args *ap; { int error; error = procfs_freevp(ap->a_vp); return (error); } /* * Return POSIX pathconf information applicable to special devices. */ 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). */ int procfs_print(ap) struct vop_print_args *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); printf("tag VT_PROCFS, pid %d, mode %x, flags %x\n", 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. */ int procfs_abortop(ap) struct vop_abortop_args *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 */ 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. */ int procfs_getattr(ap) struct vop_getattr_args *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct vattr *vap = ap->a_vap; struct proc *procp; int error; /* first check the process still exists */ switch (pfs->pfs_type) { case Proot: procp = 0; break; default: procp = PFIND(pfs->pfs_pid); if (procp == 0) 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; /* * 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 Pregs: case Pfpregs: case Pmem: if (procp->p_flag & P_SUGID) vap->va_mode &= ~((VREAD|VWRITE)| ((VREAD|VWRITE)>>3)| ((VREAD|VWRITE)>>6)); break; } /* * 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; /* * 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: vap->va_nlink = 2; vap->va_uid = 0; vap->va_gid = 0; break; case Pproc: vap->va_nlink = 2; vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = procp->p_ucred->cr_gid; break; case Pfile: error = EOPNOTSUPP; break; case Pmem: vap->va_nlink = 1; vap->va_bytes = vap->va_size = ctob(procp->p_vmspace->vm_tsize + procp->p_vmspace->vm_dsize + procp->p_vmspace->vm_ssize); vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = procp->p_ucred->cr_gid; break; case Pregs: case Pfpregs: 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); } int procfs_setattr(ap) struct vop_setattr_args *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. */ int procfs_access(ap) struct vop_access_args *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 == (uid_t) 0) return (0); vap = &vattr; if (error = VOP_GETATTR(ap->a_vp, vap, ap->a_cred, ap->a_p)) 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(). */ int procfs_lookup(ap) struct vop_lookup_args *ap; { struct componentname *cnp = ap->a_cnp; struct vnode **vpp = ap->a_vpp; struct vnode *dvp = ap->a_dvp; char *pname = cnp->cn_nameptr; int error = 0; pid_t pid; struct vnode *nvp; struct pfsnode *pfs; struct proc *procp; pfstype pfs_type; int i; if (cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); /*VOP_LOCK(dvp);*/ return (0); } *vpp = NULL; pfs = VTOPFS(dvp); switch (pfs->pfs_type) { case Proot: if (cnp->cn_flags & ISDOTDOT) return (EIO); if (CNEQ(cnp, "curproc", 7)) pid = cnp->cn_proc->p_pid; else pid = atopid(pname, cnp->cn_namelen); if (pid == NO_PID) return (ENOENT); procp = PFIND(pid); if (procp == 0) return (ENOENT); error = procfs_allocvp(dvp->v_mount, &nvp, pid, Pproc); if (error) return (error); nvp->v_type = VDIR; pfs = VTOPFS(nvp); *vpp = nvp; return (0); case Pproc: if (cnp->cn_flags & ISDOTDOT) { error = procfs_root(dvp->v_mount, vpp); return (error); } procp = PFIND(pfs->pfs_pid); if (procp == 0) return (ENOENT); for (i = 0; i < Nprocent; i++) { struct pfsnames *dp = &procent[i]; if (cnp->cn_namelen == dp->d_namlen && bcmp(pname, dp->d_name, dp->d_namlen) == 0) { pfs_type = dp->d_pfstype; goto found; } } return (ENOENT); found: if (pfs_type == Pfile) { nvp = procfs_findtextvp(procp); if (nvp) { VREF(nvp); VOP_LOCK(nvp); } else { error = ENXIO; } } else { error = procfs_allocvp(dvp->v_mount, &nvp, pfs->pfs_pid, pfs_type); if (error) return (error); nvp->v_type = VREG; pfs = VTOPFS(nvp); } *vpp = nvp; return (error); default: return (ENOTDIR); } } /* * 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() */ int procfs_readdir(ap) struct vop_readdir_args *ap; { struct uio *uio = ap->a_uio; struct pfsdent d; struct pfsdent *dp = &d; struct pfsnode *pfs; int error; int count; int i; 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: { while (uio->uio_resid >= UIO_MX) { struct pfsnames *dt; if (i >= Nprocent) break; dt = &procent[i]; dp->d_reclen = UIO_MX; dp->d_fileno = PROCFS_FILENO(pfs->pfs_pid, dt->d_pfstype); dp->d_type = DT_REG; dp->d_namlen = dt->d_namlen; bcopy(dt->d_name, dp->d_name, sizeof(dt->d_name)-1); error = uiomove((caddr_t) dp, UIO_MX, uio); if (error) break; count += UIO_MX; i++; } 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: { int pcnt; #ifdef PROCFS_ZOMBIE int doingzomb = 0; #endif volatile struct proc *p; p = allproc; #define PROCFS_XFILES 1 /* number of other entries, like "curproc" */ pcnt = PROCFS_XFILES; while (p && uio->uio_resid >= UIO_MX) { bzero((char *) dp, UIO_MX); dp->d_type = DT_DIR; dp->d_reclen = UIO_MX; switch (i) { case 0: /* ship out entry for "curproc" */ dp->d_fileno = PROCFS_FILENO(PID_MAX+1, Pproc); dp->d_namlen = sprintf(dp->d_name, "curproc"); break; default: if (pcnt >= i) { dp->d_fileno = PROCFS_FILENO(p->p_pid, Pproc); dp->d_namlen = sprintf(dp->d_name, "%ld", (long) p->p_pid); } p = p->p_next; #ifdef PROCFS_ZOMBIE if (p == 0 && doingzomb == 0) { doingzomb = 1; p = zombproc; } #endif if (pcnt++ < i) continue; break; } error = uiomove((caddr_t) dp, UIO_MX, uio); if (error) break; count += UIO_MX; i++; } break; } default: error = ENOTDIR; break; } uio->uio_offset = i * UIO_MX; return (error); } /* * 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); } /* * procfs vnode operations. */ int (**procfs_vnodeop_p)(); struct vnodeopv_entry_desc procfs_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, procfs_lookup }, /* lookup */ { &vop_create_desc, procfs_create }, /* create */ { &vop_mknod_desc, procfs_mknod }, /* mknod */ { &vop_open_desc, procfs_open }, /* open */ { &vop_close_desc, procfs_close }, /* close */ { &vop_access_desc, procfs_access }, /* access */ { &vop_getattr_desc, procfs_getattr }, /* getattr */ { &vop_setattr_desc, procfs_setattr }, /* setattr */ { &vop_read_desc, procfs_read }, /* read */ { &vop_write_desc, procfs_write }, /* write */ { &vop_ioctl_desc, procfs_ioctl }, /* ioctl */ { &vop_select_desc, procfs_select }, /* select */ { &vop_mmap_desc, procfs_mmap }, /* mmap */ { &vop_fsync_desc, procfs_fsync }, /* fsync */ { &vop_seek_desc, procfs_seek }, /* seek */ { &vop_remove_desc, procfs_remove }, /* remove */ { &vop_link_desc, procfs_link }, /* link */ { &vop_rename_desc, procfs_rename }, /* rename */ { &vop_mkdir_desc, procfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, procfs_rmdir }, /* rmdir */ { &vop_symlink_desc, procfs_symlink }, /* symlink */ { &vop_readdir_desc, procfs_readdir }, /* readdir */ { &vop_readlink_desc, procfs_readlink }, /* readlink */ { &vop_abortop_desc, procfs_abortop }, /* abortop */ { &vop_inactive_desc, procfs_inactive }, /* inactive */ { &vop_reclaim_desc, procfs_reclaim }, /* reclaim */ { &vop_lock_desc, procfs_lock }, /* lock */ { &vop_unlock_desc, procfs_unlock }, /* unlock */ { &vop_bmap_desc, procfs_bmap }, /* bmap */ { &vop_strategy_desc, procfs_strategy }, /* strategy */ { &vop_print_desc, procfs_print }, /* print */ { &vop_islocked_desc, procfs_islocked }, /* islocked */ { &vop_pathconf_desc, procfs_pathconf }, /* pathconf */ { &vop_advlock_desc, procfs_advlock }, /* advlock */ { &vop_blkatoff_desc, procfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, procfs_valloc }, /* valloc */ { &vop_vfree_desc, procfs_vfree }, /* vfree */ { &vop_truncate_desc, procfs_truncate }, /* truncate */ { &vop_update_desc, procfs_update }, /* update */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc procfs_vnodeop_opv_desc = { &procfs_vnodeop_p, procfs_vnodeop_entries }; + +VNODEOP_SET(procfs_vnodeop_opv_desc); Index: head/sys/fs/specfs/spec_vnops.c =================================================================== --- head/sys/fs/specfs/spec_vnops.c (revision 2945) +++ head/sys/fs/specfs/spec_vnops.c (revision 2946) @@ -1,703 +1,705 @@ /* * 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 - * $Id: spec_vnops.c,v 1.3 1994/08/02 07:45:27 davidg Exp $ + * $Id: spec_vnops.c,v 1.4 1994/08/08 09:11:36 davidg Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* symbolic sleep message strings for devices */ char devopn[] = "devopn"; char devio[] = "devio"; char devwait[] = "devwait"; char devin[] = "devin"; char devout[] = "devout"; char devioc[] = "devioc"; char devcls[] = "devcls"; int (**spec_vnodeop_p)(); struct vnodeopv_entry_desc spec_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, spec_lookup }, /* lookup */ { &vop_create_desc, spec_create }, /* create */ { &vop_mknod_desc, spec_mknod }, /* mknod */ { &vop_open_desc, spec_open }, /* open */ { &vop_close_desc, spec_close }, /* close */ { &vop_access_desc, spec_access }, /* access */ { &vop_getattr_desc, spec_getattr }, /* getattr */ { &vop_setattr_desc, spec_setattr }, /* setattr */ { &vop_read_desc, spec_read }, /* read */ { &vop_write_desc, spec_write }, /* write */ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ { &vop_select_desc, spec_select }, /* select */ { &vop_mmap_desc, spec_mmap }, /* mmap */ { &vop_fsync_desc, spec_fsync }, /* fsync */ { &vop_seek_desc, spec_seek }, /* seek */ { &vop_remove_desc, spec_remove }, /* remove */ { &vop_link_desc, spec_link }, /* link */ { &vop_rename_desc, spec_rename }, /* rename */ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */ { &vop_symlink_desc, spec_symlink }, /* symlink */ { &vop_readdir_desc, spec_readdir }, /* readdir */ { &vop_readlink_desc, spec_readlink }, /* readlink */ { &vop_abortop_desc, spec_abortop }, /* abortop */ { &vop_inactive_desc, spec_inactive }, /* inactive */ { &vop_reclaim_desc, spec_reclaim }, /* reclaim */ { &vop_lock_desc, spec_lock }, /* lock */ { &vop_unlock_desc, spec_unlock }, /* unlock */ { &vop_bmap_desc, spec_bmap }, /* bmap */ { &vop_strategy_desc, spec_strategy }, /* strategy */ { &vop_print_desc, spec_print }, /* print */ { &vop_islocked_desc, spec_islocked }, /* islocked */ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ { &vop_advlock_desc, spec_advlock }, /* advlock */ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, spec_valloc }, /* valloc */ { &vop_vfree_desc, spec_vfree }, /* vfree */ { &vop_truncate_desc, spec_truncate }, /* truncate */ { &vop_update_desc, spec_update }, /* update */ { &vop_bwrite_desc, spec_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc spec_vnodeop_opv_desc = { &spec_vnodeop_p, spec_vnodeop_entries }; + +VNODEOP_SET(spec_vnodeop_opv_desc); /* * 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 vnode *bvp, *vp = ap->a_vp; dev_t bdev, dev = (dev_t)vp->v_rdev; register 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 (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 && isdisk(dev, VCHR)) 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); } } VOP_UNLOCK(vp); error = (*cdevsw[maj].d_open)(dev, ap->a_mode, S_IFCHR, ap->a_p); VOP_LOCK(vp); return (error); case VBLK: if ((u_int)maj >= nblkdev) 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) && isdisk(dev, VBLK)) return (EPERM); /* * Do not allow opens of block devices that are * currently mounted. */ if (error = vfs_mountedon(vp)) return (error); return ((*bdevsw[maj].d_open)(dev, ap->a_mode, S_IFBLK, ap->a_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, (*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); error = (*cdevsw[major(vp->v_rdev)].d_read) (vp->v_rdev, uio, ap->a_ioflag); VOP_LOCK(vp); 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 = bsize / DEV_BSIZE; do { bn = (uio->uio_offset / DEV_BSIZE) &~ (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); error = (*cdevsw[major(vp->v_rdev)].d_write) (vp->v_rdev, uio, ap->a_ioflag); VOP_LOCK(vp); 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 = (bsize / DEV_BSIZE) - 1; do { bn = (uio->uio_offset / DEV_BSIZE) &~ 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); } 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 & B_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 /* { struct vnode *a_vp; int a_which; int a_fflags; 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); } } /* * 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; sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1); } #ifdef DIAGNOSTIC if (vp->v_dirtyblkhd.lh_first) { vprint("spec_fsync: dirty", vp); goto loop; } #endif } splx(s); 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; } */ *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; return (0); } /* * At the moment we do not do any locking. */ /* ARGSUSED */ int spec_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; } */ *ap; { return (0); } /* ARGSUSED */ int spec_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; } */ *ap; { 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; dev_t dev = vp->v_rdev; int (*devclose) __P((dev_t, int, int, struct proc *)); 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 == 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. */ if (error = vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 0, 0)) 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) > 1 && (vp->v_flag & VXLOCK) == 0) return (0); 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 (EOPNOTSUPP); } /* * Special device failed operation */ int spec_ebadf() { return (EBADF); } /* * Special device bad operation */ int spec_badop() { panic("spec_badop called"); /* NOTREACHED */ } Index: head/sys/fs/umapfs/umap_vfsops.c =================================================================== --- head/sys/fs/umapfs/umap_vfsops.c (revision 2945) +++ head/sys/fs/umapfs/umap_vfsops.c (revision 2946) @@ -1,407 +1,410 @@ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * the UCLA Ficus project. * * 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. * * @(#)umap_vfsops.c 8.3 (Berkeley) 1/21/94 * - * $Id$ + * $Id: umap_vfsops.c,v 1.2 1994/08/02 07:45:36 davidg Exp $ */ /* * Umap Layer * (See mount_umap(8) for a description of this layer.) */ #include #include #include #include #include #include #include #include #include /* * Mount umap layer */ int umapfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { struct umap_args args; struct vnode *lowerrootvp, *vp; struct vnode *umapm_rootvp; struct umap_mount *amp; u_int size; int error; #ifdef UMAPFS_DIAGNOSTIC printf("umapfs_mount(mp = %x)\n", mp); #endif /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) { return (EOPNOTSUPP); /* return (VFS_MOUNT(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, path, data, ndp, p));*/ } /* * Get argument */ if (error = copyin(data, (caddr_t)&args, sizeof(struct umap_args))) return (error); /* * Find lower node */ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF, UIO_USERSPACE, args.target, p); if (error = namei(ndp)) return (error); /* * Sanity check on lower vnode */ lowerrootvp = ndp->ni_vp; #ifdef UMAPFS_DIAGNOSTIC printf("vp = %x, check for VDIR...\n", lowerrootvp); #endif vrele(ndp->ni_dvp); ndp->ni_dvp = 0; if (lowerrootvp->v_type != VDIR) { vput(lowerrootvp); return (EINVAL); } #ifdef UMAPFS_DIAGNOSTIC printf("mp = %x\n", mp); #endif amp = (struct umap_mount *) malloc(sizeof(struct umap_mount), M_UFSMNT, M_WAITOK); /* XXX */ /* * Save reference to underlying FS */ amp->umapm_vfs = lowerrootvp->v_mount; /* * Now copy in the number of entries and maps for umap mapping. */ amp->info_nentries = args.nentries; amp->info_gnentries = args.gnentries; error = copyin(args.mapdata, (caddr_t)amp->info_mapdata, 2*sizeof(u_long)*args.nentries); if (error) return (error); #ifdef UMAP_DIAGNOSTIC printf("umap_mount:nentries %d\n",args.nentries); for (i = 0; i < args.nentries; i++) printf(" %d maps to %d\n", amp->info_mapdata[i][0], amp->info_mapdata[i][1]); #endif error = copyin(args.gmapdata, (caddr_t)amp->info_gmapdata, 2*sizeof(u_long)*args.nentries); if (error) return (error); #ifdef UMAP_DIAGNOSTIC printf("umap_mount:gnentries %d\n",args.gnentries); for (i = 0; i < args.gnentries; i++) printf(" group %d maps to %d\n", amp->info_gmapdata[i][0], amp->info_gmapdata[i][1]); #endif /* * Save reference. Each mount also holds * a reference on the root vnode. */ error = umap_node_create(mp, lowerrootvp, &vp); /* * Unlock the node (either the lower or the alias) */ VOP_UNLOCK(vp); /* * Make sure the node alias worked */ if (error) { vrele(lowerrootvp); free(amp, M_UFSMNT); /* XXX */ return (error); } /* * Keep a held reference to the root vnode. * It is vrele'd in umapfs_unmount. */ umapm_rootvp = vp; umapm_rootvp->v_flag |= VROOT; amp->umapm_rootvp = umapm_rootvp; if (UMAPVPTOLOWERVP(umapm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) mp->mnt_flag |= MNT_LOCAL; mp->mnt_data = (qaddr_t) amp; getnewfsid(mp, MOUNT_LOFS); (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); #ifdef UMAPFS_DIAGNOSTIC printf("umapfs_mount: lower %s, alias at %s\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); #endif return (0); } /* * VFS start. Nothing needed here - the start routine * on the underlying filesystem will have been called * when that filesystem was mounted. */ int umapfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); /* return (VFS_START(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, flags, p)); */ } /* * Free reference to umap layer */ int umapfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { struct vnode *umapm_rootvp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp; int error; int flags = 0; extern int doforce; #ifdef UMAPFS_DIAGNOSTIC printf("umapfs_unmount(mp = %x)\n", mp); #endif if (mntflags & MNT_FORCE) { /* lofs can never be rootfs so don't check for it */ if (!doforce) return (EINVAL); flags |= FORCECLOSE; } /* * Clear out buffer cache. I don't think we * ever get anything cached at this level at the * moment, but who knows... */ #ifdef notyet mntflushbuf(mp, 0); if (mntinvalbuf(mp, 1)) return (EBUSY); #endif if (umapm_rootvp->v_usecount > 1) return (EBUSY); if (error = vflush(mp, umapm_rootvp, flags)) return (error); #ifdef UMAPFS_DIAGNOSTIC vprint("alias root of lower", umapm_rootvp); #endif /* * Release reference on underlying root vnode */ vrele(umapm_rootvp); /* * And blow it away for future re-use */ vgone(umapm_rootvp); /* * Finally, throw away the umap_mount structure */ free(mp->mnt_data, M_UFSMNT); /* XXX */ mp->mnt_data = 0; return (0); } int umapfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct vnode *vp; #ifdef UMAPFS_DIAGNOSTIC printf("umapfs_root(mp = %x, vp = %x->%x)\n", mp, MOUNTTOUMAPMOUNT(mp)->umapm_rootvp, UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp) ); #endif /* * Return locked reference to root. */ vp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp; VREF(vp); VOP_LOCK(vp); *vpp = vp; return (0); } int umapfs_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (VFS_QUOTACTL(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, cmd, uid, arg, p)); } int umapfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { int error; struct statfs mstat; #ifdef UMAPFS_DIAGNOSTIC printf("umapfs_statfs(mp = %x, vp = %x->%x)\n", mp, MOUNTTOUMAPMOUNT(mp)->umapm_rootvp, UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp) ); #endif bzero(&mstat, sizeof(mstat)); error = VFS_STATFS(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, &mstat, p); if (error) return (error); /* now copy across the "interesting" information and fake the rest */ sbp->f_type = mstat.f_type; sbp->f_flags = mstat.f_flags; sbp->f_bsize = mstat.f_bsize; sbp->f_iosize = mstat.f_iosize; sbp->f_blocks = mstat.f_blocks; sbp->f_bfree = mstat.f_bfree; sbp->f_bavail = mstat.f_bavail; sbp->f_files = mstat.f_files; sbp->f_ffree = mstat.f_ffree; if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } int umapfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { /* * XXX - Assumes no data cached at umap layer. */ return (0); } int umapfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (VFS_VGET(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, ino, vpp)); } int umapfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp) struct mount *mp; struct fid *fidp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred**credanonp; { return (VFS_FHTOVP(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, fidp, nam, vpp, exflagsp,credanonp)); } int umapfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (VFS_VPTOFH(UMAPVPTOLOWERVP(vp), fhp)); } int umapfs_init __P((void)); struct vfsops umap_vfsops = { umapfs_mount, umapfs_start, umapfs_unmount, umapfs_root, umapfs_quotactl, umapfs_statfs, umapfs_sync, umapfs_vget, umapfs_fhtovp, umapfs_vptofh, umapfs_init, }; + +VFS_SET(umap_vfsops, umap, MOUNT_UMAP, 0); + Index: head/sys/fs/umapfs/umap_vnops.c =================================================================== --- head/sys/fs/umapfs/umap_vnops.c (revision 2945) +++ head/sys/fs/umapfs/umap_vnops.c (revision 2946) @@ -1,488 +1,490 @@ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * the UCLA Ficus project. * * 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. * * @(#)umap_vnops.c 8.3 (Berkeley) 1/5/94 - * $Id: umap_vnops.c,v 1.3 1994/08/02 07:45:37 davidg Exp $ + * $Id: umap_vnops.c,v 1.4 1994/08/20 03:48:54 davidg Exp $ */ /* * Umap Layer */ #include #include #include #include #include #include #include #include #include #include int umap_bug_bypass = 0; /* for debugging: enables bypass printf'ing */ /* * This is the 10-Apr-92 bypass routine. * See null_vnops.c:null_bypass for more details. */ int umap_bypass(ap) struct vop_generic_args /* { struct vnodeop_desc *a_desc; } */ *ap; { struct ucred **credpp = 0, *credp = 0; struct ucred *savecredp = 0, *savecompcredp = 0; struct ucred *compcredp = 0; struct vnode **this_vp_p; int error; struct vnode *old_vps[VDESC_MAX_VPS]; struct vnode *vp1 = 0; struct vnode **vps_p[VDESC_MAX_VPS]; struct vnode ***vppp; struct vnodeop_desc *descp = ap->a_desc; int reles, i; struct componentname **compnamepp = 0; if (umap_bug_bypass) printf ("umap_bypass: %s\n", descp->vdesc_name); #ifdef SAFETY /* * We require at least one vp. */ if (descp->vdesc_vp_offsets == NULL || descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET) panic ("umap_bypass: no vp's in map.\n"); #endif /* * Map the vnodes going in. * Later, we'll invoke the operation based on * the first mapped vnode's operation vector. */ reles = descp->vdesc_flags; for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) { if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) break; /* bail out at end of list */ vps_p[i] = this_vp_p = VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[i], ap); if (i == 0) { vp1 = *vps_p[0]; } /* * We're not guaranteed that any but the first vnode * are of our type. Check for and don't map any * that aren't. (Must map first vp or vclean fails.) */ if (i && (*this_vp_p)->v_op != umap_vnodeop_p) { old_vps[i] = NULL; } else { old_vps[i] = *this_vp_p; *(vps_p[i]) = UMAPVPTOLOWERVP(*this_vp_p); if (reles & 1) VREF(*this_vp_p); } } /* * Fix the credentials. (That's the purpose of this layer.) */ if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) { credpp = VOPARG_OFFSETTO(struct ucred**, descp->vdesc_cred_offset, ap); /* Save old values */ savecredp = (*credpp); (*credpp) = crdup(savecredp); credp = *credpp; if (umap_bug_bypass && credp->cr_uid != 0) printf("umap_bypass: user was %d, group %d\n", credp->cr_uid, credp->cr_gid); /* Map all ids in the credential structure. */ umap_mapids(vp1->v_mount, credp); if (umap_bug_bypass && credp->cr_uid != 0) printf("umap_bypass: user now %d, group %d\n", credp->cr_uid, credp->cr_gid); } /* BSD often keeps a credential in the componentname structure * for speed. If there is one, it better get mapped, too. */ if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) { compnamepp = VOPARG_OFFSETTO(struct componentname**, descp->vdesc_componentname_offset, ap); compcredp = (*compnamepp)->cn_cred; savecompcredp = compcredp; compcredp = (*compnamepp)->cn_cred = crdup(savecompcredp); if (umap_bug_bypass && compcredp->cr_uid != 0) printf("umap_bypass: component credit user was %d, group %d\n", compcredp->cr_uid, compcredp->cr_gid); /* Map all ids in the credential structure. */ umap_mapids(vp1->v_mount, compcredp); if (umap_bug_bypass && compcredp->cr_uid != 0) printf("umap_bypass: component credit user now %d, group %d\n", compcredp->cr_uid, compcredp->cr_gid); } /* * Call the operation on the lower layer * with the modified argument structure. */ error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap); /* * Maintain the illusion of call-by-value * by restoring vnodes in the argument structure * to their original value. */ reles = descp->vdesc_flags; for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) { if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) break; /* bail out at end of list */ if (old_vps[i]) { *(vps_p[i]) = old_vps[i]; if (reles & 1) vrele(*(vps_p[i])); }; }; /* * Map the possible out-going vpp * (Assumes that the lower layer always returns * a VREF'ed vpp unless it gets an error.) */ if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET && !(descp->vdesc_flags & VDESC_NOMAP_VPP) && !error) { if (descp->vdesc_flags & VDESC_VPP_WILLRELE) goto out; vppp = VOPARG_OFFSETTO(struct vnode***, descp->vdesc_vpp_offset, ap); error = umap_node_create(old_vps[0]->v_mount, **vppp, *vppp); }; out: /* * Free duplicate cred structure and restore old one. */ if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) { if (umap_bug_bypass && credp && credp->cr_uid != 0) printf("umap_bypass: returning-user was %d\n", credp->cr_uid); crfree(credp); (*credpp) = savecredp; if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0) printf("umap_bypass: returning-user now %d\n\n", (*credpp)->cr_uid); } if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) { if (umap_bug_bypass && compcredp && compcredp->cr_uid != 0) printf("umap_bypass: returning-component-user was %d\n", compcredp->cr_uid); crfree(compcredp); (*compnamepp)->cn_cred = savecompcredp; if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0) printf("umap_bypass: returning-component-user now %d\n", compcredp->cr_uid); } return (error); } /* * We handle getattr to change the fsid. */ int umap_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { short uid, gid; int error, tmpid, nentries, gnentries; u_long (*mapdata)[2], (*gmapdata)[2]; struct vnode **vp1p; struct vnodeop_desc *descp = ap->a_desc; if (error = umap_bypass(ap)) return (error); /* Requires that arguments be restored. */ ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; /* * Umap needs to map the uid and gid returned by a stat * into the proper values for this site. This involves * finding the returned uid in the mapping information, * translating it into the uid on the other end, * and filling in the proper field in the vattr * structure pointed to by ap->a_vap. The group * is easier, since currently all groups will be * translate to the NULLGROUP. */ /* Find entry in map */ uid = ap->a_vap->va_uid; gid = ap->a_vap->va_gid; if (umap_bug_bypass) printf("umap_getattr: mapped uid = %d, mapped gid = %d\n", uid, gid); vp1p = VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[0], ap); nentries = MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_nentries; mapdata = (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_mapdata); gnentries = MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gnentries; gmapdata = (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gmapdata); /* Reverse map the uid for the vnode. Since it's a reverse map, we can't use umap_mapids() to do it. */ tmpid = umap_reverse_findid(uid, mapdata, nentries); if (tmpid != -1) { ap->a_vap->va_uid = (uid_t) tmpid; if (umap_bug_bypass) printf("umap_getattr: original uid = %d\n", uid); } else ap->a_vap->va_uid = (uid_t) NOBODY; /* Reverse map the gid for the vnode. */ tmpid = umap_reverse_findid(gid, gmapdata, gnentries); if (tmpid != -1) { ap->a_vap->va_gid = (gid_t) tmpid; if (umap_bug_bypass) printf("umap_getattr: original gid = %d\n", gid); } else ap->a_vap->va_gid = (gid_t) NULLGROUP; return (0); } int umap_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { /* * Do nothing (and _don't_ bypass). * Wait to vrele lowervp until reclaim, * so that until then our umap_node is in the * cache and reusable. * */ return (0); } int umap_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; struct umap_node *xp = VTOUMAP(vp); struct vnode *lowervp = xp->umap_lowervp; /* After this assignment, this node will not be re-used. */ xp->umap_lowervp = NULL; remque(xp); FREE(vp->v_data, M_TEMP); vp->v_data = NULL; vrele(lowervp); return (0); } int umap_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 = UMAPVPTOLOWERVP(bp->b_vp); error = VOP_STRATEGY(ap->a_bp); bp->b_vp = savedvp; return (error); } int umap_bwrite(ap) struct vop_bwrite_args /* { struct buf *a_bp; } */ *ap; { struct buf *bp = ap->a_bp; int error; struct vnode *savedvp; savedvp = bp->b_vp; bp->b_vp = UMAPVPTOLOWERVP(bp->b_vp); error = VOP_BWRITE(ap->a_bp); bp->b_vp = savedvp; return (error); } int umap_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; printf("\ttag VT_UMAPFS, vp=%x, lowervp=%x\n", vp, UMAPVPTOLOWERVP(vp)); return (0); } int umap_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 componentname *compnamep; struct ucred *compcredp, *savecompcredp; struct vnode *vp; /* * Rename is irregular, having two componentname structures. * We need to map the cre in the second structure, * and then bypass takes care of the rest. */ vp = ap->a_fdvp; compnamep = ap->a_tcnp; compcredp = compnamep->cn_cred; savecompcredp = compcredp; compcredp = compnamep->cn_cred = crdup(savecompcredp); if (umap_bug_bypass && compcredp->cr_uid != 0) printf("umap_rename: rename component credit user was %d, group %d\n", compcredp->cr_uid, compcredp->cr_gid); /* Map all ids in the credential structure. */ umap_mapids(vp->v_mount, compcredp); if (umap_bug_bypass && compcredp->cr_uid != 0) printf("umap_rename: rename component credit user now %d, group %d\n", compcredp->cr_uid, compcredp->cr_gid); error = umap_bypass(ap); /* Restore the additional mapped componentname cred structure. */ crfree(compcredp); compnamep->cn_cred = savecompcredp; return error; } /* * Global vfs data structures */ /* * XXX - strategy, bwrite are hand coded currently. They should * go away with a merged buffer/block cache. * */ int (**umap_vnodeop_p)(); struct vnodeopv_entry_desc umap_vnodeop_entries[] = { { &vop_default_desc, umap_bypass }, { &vop_getattr_desc, umap_getattr }, { &vop_inactive_desc, umap_inactive }, { &vop_reclaim_desc, umap_reclaim }, { &vop_print_desc, umap_print }, { &vop_rename_desc, umap_rename }, { &vop_strategy_desc, umap_strategy }, { &vop_bwrite_desc, umap_bwrite }, { (struct vnodeop_desc*) NULL, (int(*)()) NULL } }; struct vnodeopv_desc umap_vnodeop_opv_desc = { &umap_vnodeop_p, umap_vnodeop_entries }; + +VNODEOP_SET(umap_vnodeop_opv_desc); Index: head/sys/fs/unionfs/union_vfsops.c =================================================================== --- head/sys/fs/unionfs/union_vfsops.c (revision 2945) +++ head/sys/fs/unionfs/union_vfsops.c (revision 2946) @@ -1,551 +1,553 @@ /* * Copyright (c) 1994 The Regents of the University of California. * Copyright (c) 1994 Jan-Simon Pendry. * 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. * * @(#)union_vfsops.c 8.7 (Berkeley) 3/5/94 - * $Id$ + * $Id: union_vfsops.c,v 1.3 1994/08/02 07:45:45 davidg Exp $ */ /* * Union Layer */ #include #include #include #include #include #include #include #include #include #include #include #include /* * Mount union filesystem */ int union_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { int error = 0; struct union_args args; struct vnode *lowerrootvp = NULLVP; struct vnode *upperrootvp = NULLVP; struct union_mount *um; struct ucred *cred = 0; struct ucred *scred; struct vattr va; char *cp = 0; int len; u_int size; #ifdef UNION_DIAGNOSTIC printf("union_mount(mp = %x)\n", mp); #endif /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) { /* * Need to provide. * 1. a way to convert between rdonly and rdwr mounts. * 2. support for nfs exports. */ error = EOPNOTSUPP; goto bad; } /* * Take a copy of the process's credentials. This isn't * quite right since the euid will always be zero and we * want to get the "real" users credentials. So fix up * the uid field after taking the copy. */ cred = crdup(p->p_ucred); cred->cr_uid = p->p_cred->p_ruid; /* * Ensure the *real* user has write permission on the * mounted-on directory. This allows the mount_union * command to be made setuid root so allowing anyone * to do union mounts onto any directory on which they * have write permission and which they also own. */ error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p); if (error) goto bad; if ((va.va_uid != cred->cr_uid) && (cred->cr_uid != 0)) { error = EACCES; goto bad; } error = VOP_ACCESS(mp->mnt_vnodecovered, VWRITE, cred, p); if (error) goto bad; /* * Get argument */ if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args))) goto bad; lowerrootvp = mp->mnt_vnodecovered; VREF(lowerrootvp); /* * Find upper node. Use the real process credentials, * not the effective ones since this will have come * through a setuid process (mount_union). All this * messing around with permissions is entirely bogus * and should be removed by allowing any user straight * past the mount system call. */ scred = p->p_ucred; p->p_ucred = cred; NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT, UIO_USERSPACE, args.target, p); p->p_ucred = scred; if (error = namei(ndp)) goto bad; upperrootvp = ndp->ni_vp; vrele(ndp->ni_dvp); ndp->ni_dvp = NULL; if (upperrootvp->v_type != VDIR) { error = EINVAL; goto bad; } um = (struct union_mount *) malloc(sizeof(struct union_mount), M_UFSMNT, M_WAITOK); /* XXX */ /* * Keep a held reference to the target vnodes. * They are vrele'd in union_unmount. * * Depending on the _BELOW flag, the filesystems are * viewed in a different order. In effect, this is the * same as providing a mount under option to the mount syscall. */ um->um_op = args.mntflags & UNMNT_OPMASK; switch (um->um_op) { case UNMNT_ABOVE: um->um_lowervp = lowerrootvp; um->um_uppervp = upperrootvp; break; case UNMNT_BELOW: um->um_lowervp = upperrootvp; um->um_uppervp = lowerrootvp; break; case UNMNT_REPLACE: vrele(lowerrootvp); lowerrootvp = NULLVP; um->um_uppervp = upperrootvp; um->um_lowervp = lowerrootvp; break; default: error = EINVAL; goto bad; } um->um_cred = cred; um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask; /* * Depending on what you think the MNT_LOCAL flag might mean, * you may want the && to be || on the conditional below. * At the moment it has been defined that the filesystem is * only local if it is all local, ie the MNT_LOCAL flag implies * that the entire namespace is local. If you think the MNT_LOCAL * flag implies that some of the files might be stored locally * then you will want to change the conditional. */ if (um->um_op == UNMNT_ABOVE) { if (((um->um_lowervp == NULLVP) || (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) && (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) mp->mnt_flag |= MNT_LOCAL; } /* * Copy in the upper layer's RDONLY flag. This is for the benefit * of lookup() which explicitly checks the flag, rather than asking * the filesystem for it's own opinion. This means, that an update * mount of the underlying filesystem to go from rdonly to rdwr * will leave the unioned view as read-only. */ mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY); /* * This is a user mount. Privilege check for unmount * will be done in union_unmount. */ mp->mnt_flag |= MNT_USER; mp->mnt_data = (qaddr_t) um; getnewfsid(mp, MOUNT_UNION); (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); switch (um->um_op) { case UNMNT_ABOVE: cp = ""; break; case UNMNT_BELOW: cp = ""; break; case UNMNT_REPLACE: cp = ""; break; } len = strlen(cp); bcopy(cp, mp->mnt_stat.f_mntfromname, len); cp = mp->mnt_stat.f_mntfromname + len; len = MNAMELEN - len; (void) copyinstr(args.target, cp, len - 1, &size); bzero(cp + size, len - size); #ifdef UNION_DIAGNOSTIC printf("union_mount: from %s, on %s\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); #endif return (0); bad: if (cred) crfree(cred); if (upperrootvp) vrele(upperrootvp); if (lowerrootvp) vrele(lowerrootvp); return (error); } /* * VFS start. Nothing needed here - the start routine * on the underlying filesystem(s) will have been called * when that filesystem was mounted. */ int union_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } /* * Free reference to union layer */ int union_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { struct union_mount *um = MOUNTTOUNIONMOUNT(mp); struct vnode *um_rootvp; int error; int flags = 0; extern int doforce; #ifdef UNION_DIAGNOSTIC printf("union_unmount(mp = %x)\n", mp); #endif /* only the mounter, or superuser can unmount */ if ((p->p_cred->p_ruid != um->um_cred->cr_uid) && (error = suser(p->p_ucred, &p->p_acflag))) return (error); if (mntflags & MNT_FORCE) { /* union can never be rootfs so don't check for it */ if (!doforce) return (EINVAL); flags |= FORCECLOSE; } if (error = union_root(mp, &um_rootvp)) return (error); if (um_rootvp->v_usecount > 1) { vput(um_rootvp); return (EBUSY); } if (error = vflush(mp, um_rootvp, flags)) { vput(um_rootvp); return (error); } #ifdef UNION_DIAGNOSTIC vprint("alias root of lower", um_rootvp); #endif /* * Discard references to upper and lower target vnodes. */ if (um->um_lowervp) vrele(um->um_lowervp); vrele(um->um_uppervp); crfree(um->um_cred); /* * Release reference on underlying root vnode */ vput(um_rootvp); /* * And blow it away for future re-use */ vgone(um_rootvp); /* * Finally, throw away the union_mount structure */ free(mp->mnt_data, M_UFSMNT); /* XXX */ mp->mnt_data = 0; return (0); } int union_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct union_mount *um = MOUNTTOUNIONMOUNT(mp); int error; int loselock; #ifdef UNION_DIAGNOSTIC printf("union_root(mp = %x, lvp = %x, uvp = %x)\n", mp, um->um_lowervp, um->um_uppervp); #endif /* * Return locked reference to root. */ VREF(um->um_uppervp); if ((um->um_op == UNMNT_BELOW) && VOP_ISLOCKED(um->um_uppervp)) { loselock = 1; } else { VOP_LOCK(um->um_uppervp); loselock = 0; } if (um->um_lowervp) VREF(um->um_lowervp); error = union_allocvp(vpp, mp, (struct vnode *) 0, (struct vnode *) 0, (struct componentname *) 0, um->um_uppervp, um->um_lowervp); if (error) { if (!loselock) VOP_UNLOCK(um->um_uppervp); vrele(um->um_uppervp); if (um->um_lowervp) vrele(um->um_lowervp); } else { (*vpp)->v_flag |= VROOT; if (loselock) VTOUNION(*vpp)->un_flags &= ~UN_ULOCK; } return (error); } int union_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } int union_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { int error; struct union_mount *um = MOUNTTOUNIONMOUNT(mp); struct statfs mstat; int lbsize; #ifdef UNION_DIAGNOSTIC printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp, um->um_lowervp, um->um_uppervp); #endif bzero(&mstat, sizeof(mstat)); if (um->um_lowervp) { error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p); if (error) return (error); } /* now copy across the "interesting" information and fake the rest */ #if 0 sbp->f_type = mstat.f_type; sbp->f_flags = mstat.f_flags; sbp->f_bsize = mstat.f_bsize; sbp->f_iosize = mstat.f_iosize; #endif lbsize = mstat.f_bsize; sbp->f_blocks = mstat.f_blocks; sbp->f_bfree = mstat.f_bfree; sbp->f_bavail = mstat.f_bavail; sbp->f_files = mstat.f_files; sbp->f_ffree = mstat.f_ffree; error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p); if (error) return (error); sbp->f_type = MOUNT_UNION; sbp->f_flags = mstat.f_flags; sbp->f_bsize = mstat.f_bsize; sbp->f_iosize = mstat.f_iosize; /* * if the lower and upper blocksizes differ, then frig the * block counts so that the sizes reported by df make some * kind of sense. none of this makes sense though. */ if (mstat.f_bsize != lbsize) { sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize; sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize; sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize; } sbp->f_blocks += mstat.f_blocks; sbp->f_bfree += mstat.f_bfree; sbp->f_bavail += mstat.f_bavail; sbp->f_files += mstat.f_files; sbp->f_ffree += mstat.f_ffree; if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } int union_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { /* * XXX - Assumes no data cached at union layer. */ return (0); } int union_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } int union_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp) struct mount *mp; struct fid *fidp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred **credanonp; { return (EOPNOTSUPP); } int union_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (EOPNOTSUPP); } int union_init __P((void)); struct vfsops union_vfsops = { union_mount, union_start, union_unmount, union_root, union_quotactl, union_statfs, union_sync, union_vget, union_fhtovp, union_vptofh, union_init, }; + +VFS_SET(union_vfsops, union, MOUNT_UNION, 0); Index: head/sys/fs/unionfs/union_vnops.c =================================================================== --- head/sys/fs/unionfs/union_vnops.c (revision 2945) +++ head/sys/fs/unionfs/union_vnops.c (revision 2946) @@ -1,1496 +1,1498 @@ /* * Copyright (c) 1992, 1993, 1994 The Regents of the University of California. * Copyright (c) 1992, 1993, 1994 Jan-Simon Pendry. * 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.6 (Berkeley) 2/17/94 - * $Id$ + * $Id: union_vnops.c,v 1.3 1994/08/02 07:45:47 davidg Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define FIXUP(un) { \ if (((un)->un_flags & UN_ULOCK) == 0) { \ union_fixup(un); \ } \ } static void union_fixup(un) struct union_node *un; { VOP_LOCK(un->un_uppervp); un->un_flags |= UN_ULOCK; } static int union_lookup1(udvp, dvp, vpp, cnp) struct vnode *udvp; struct vnode *dvp; struct vnode **vpp; struct componentname *cnp; { int error; struct vnode *tdvp; struct mount *mp; /* * 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) { for (;;) { /* * Don't do the NOCROSSMOUNT check * at this level. By definition, * union fs deals with namespaces, not * filesystems. */ if ((dvp->v_flag & VROOT) == 0) break; tdvp = dvp; dvp = dvp->v_mount->mnt_vnodecovered; vput(tdvp); VREF(dvp); VOP_LOCK(dvp); } } 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)) VOP_LOCK(dvp); 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 (mp->mnt_flag & MNT_MLOCK) { mp->mnt_flag |= MNT_MWAIT; sleep((caddr_t) mp, PVFS); continue; } if (error = VFS_ROOT(mp, &tdvp)) { vput(dvp); return (error); } vput(dvp); dvp = tdvp; } *vpp = dvp; return (0); } 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; int lockparent = cnp->cn_flags & LOCKPARENT; int rdonly = cnp->cn_flags & RDONLY; struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount); struct ucred *saved_cred = 0; cnp->cn_flags |= LOCKPARENT; upperdvp = dun->un_uppervp; lowerdvp = dun->un_lowervp; uppervp = NULLVP; lowervp = NULLVP; /* * 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) { FIXUP(dun); uerror = union_lookup1(um->um_uppervp, upperdvp, &uppervp, cnp); /*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); } } 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) { int nameiop; VOP_LOCK(lowerdvp); /* * 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; } 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); if (cnp->cn_consume != 0) { if (uppervp) { 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 (!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); uerror = union_mkshadow(um, upperdvp, cnp, &uppervp); VOP_LOCK(upperdvp); dun->un_flags |= UN_ULOCK; if (uerror) { if (lowervp) { vput(lowervp); lowervp = NULLVP; } return (uerror); } } } if (lowervp) VOP_UNLOCK(lowervp); error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp, uppervp, lowervp); if (error) { if (uppervp) vput(uppervp); if (lowervp) vrele(lowervp); } else { if (*ap->a_vpp != dvp) if (!lockparent || !(cnp->cn_flags & ISLASTCN)) VOP_UNLOCK(dvp); } return (error); } 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; if (dvp) { int error; struct vnode *vp; FIXUP(un); VREF(dvp); un->un_flags |= UN_KLOCK; vput(ap->a_dvp); error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap); if (error) return (error); error = union_allocvp( ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp, NULLVP, ap->a_cnp, vp, NULLVP); if (error) vput(vp); return (error); } vput(ap->a_dvp); return (EROFS); } 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; if (dvp) { int error; struct vnode *vp; FIXUP(un); VREF(dvp); un->un_flags |= UN_KLOCK; vput(ap->a_dvp); error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap); if (error) return (error); if (vp) { error = union_allocvp( ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp, NULLVP, ap->a_cnp, vp, NULLVP); if (error) vput(vp); } return (error); } vput(ap->a_dvp); return (EROFS); } 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)) { struct vnode *vp; int i; /* * Open the named file in the upper layer. Note that * the file may have come into existence *since* the * lookup was done, since the upper layer may really * be a loopback mount of some other filesystem... * so open the file with exclusive create and barf if * it already exists. * XXX - perhaps should re-lookup the node (once more * with feeling) and simply open that. Who knows. */ error = union_vn_create(&vp, un, p); if (error) return (error); /* at this point, uppervp is locked */ union_newupper(un, vp); un->un_flags |= UN_ULOCK; /* * Now, if the file is being opened with truncation, * then the (new) upper vnode is ready to fly, * otherwise the data from the lower vnode must be * copied to the upper layer first. This only works * for regular files (check is made above). */ if ((mode & O_TRUNC) == 0) { /* * XXX - should not ignore errors * from VOP_CLOSE */ VOP_LOCK(tvp); error = VOP_OPEN(tvp, FREAD, cred, p); if (error == 0) { error = union_copyfile(p, cred, tvp, un->un_uppervp); VOP_UNLOCK(tvp); (void) VOP_CLOSE(tvp, FREAD); } else { VOP_UNLOCK(tvp); } #ifdef UNION_DIAGNOSTIC if (!error) uprintf("union: copied up %s\n", un->un_path); #endif } un->un_flags &= ~UN_ULOCK; VOP_UNLOCK(un->un_uppervp); union_vn_close(un->un_uppervp, FWRITE, cred, p); VOP_LOCK(un->un_uppervp); un->un_flags |= UN_ULOCK; /* * Subsequent IOs will go to the top layer, so * call close on the lower vnode and open on the * upper vnode to ensure that the filesystem keeps * its references counts right. This doesn't do * the right thing with (cred) and (FREAD) though. * Ignoring error returns is not righ, either. */ for (i = 0; i < un->un_openl; i++) { (void) VOP_CLOSE(tvp, FREAD); (void) VOP_OPEN(un->un_uppervp, FREAD, cred, p); } un->un_openl = 0; if (error == 0) error = VOP_OPEN(un->un_uppervp, mode, cred, p); return (error); } /* * Just open the lower vnode */ un->un_openl++; VOP_LOCK(tvp); error = VOP_OPEN(tvp, mode, cred, p); VOP_UNLOCK(tvp); return (error); } FIXUP(un); error = VOP_OPEN(tvp, mode, cred, p); return (error); } 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 (un->un_uppervp) { vp = un->un_uppervp; } else { #ifdef UNION_DIAGNOSTIC if (un->un_openl <= 0) panic("union: un_openl cnt"); #endif --un->un_openl; vp = un->un_lowervp; } return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p)); } /* * 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. */ 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); int error = EACCES; struct vnode *vp; if (vp = un->un_uppervp) { FIXUP(un); return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p)); } if (vp = un->un_lowervp) { VOP_LOCK(vp); error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p); if (error == 0) { struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount); if (um->um_op == UNMNT_BELOW) error = VOP_ACCESS(vp, ap->a_mode, um->um_cred, ap->a_p); } VOP_UNLOCK(vp); if (error) return (error); } return (error); } /* * We handle getattr only to change the fsid. */ 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 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) { FIXUP(un); error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); if (error) return (error); } 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) { VOP_LOCK(vp); error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); VOP_UNLOCK(vp); if (error) return (error); } if ((vap != ap->a_vap) && (vap->va_type == VDIR)) ap->a_vap->va_nlink += vap->va_nlink; vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; return (0); } 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); int error; /* * 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) && (ap->a_vap->va_size == 0)) { struct vnode *vp; error = union_vn_create(&vp, un, ap->a_p); if (error) return (error); /* at this point, uppervp is locked */ union_newupper(un, vp); VOP_UNLOCK(vp); union_vn_close(un->un_uppervp, FWRITE, ap->a_cred, ap->a_p); VOP_LOCK(vp); un->un_flags |= UN_ULOCK; } /* * Try to set attributes in upper layer, * otherwise return read-only filesystem error. */ if (un->un_uppervp != NULLVP) { FIXUP(un); error = VOP_SETATTR(un->un_uppervp, ap->a_vap, ap->a_cred, ap->a_p); } else { error = EROFS; } return (error); } 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 vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) VOP_LOCK(vp); else FIXUP(VTOUNION(ap->a_vp)); error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred); if (dolock) VOP_UNLOCK(vp); return (error); } 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 = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) VOP_LOCK(vp); else FIXUP(VTOUNION(ap->a_vp)); error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred); if (dolock) VOP_UNLOCK(vp); return (error); } 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; { return (VOP_IOCTL(OTHERVP(ap->a_vp), ap->a_command, ap->a_data, ap->a_fflag, ap->a_cred, ap->a_p)); } int union_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; { return (VOP_SELECT(OTHERVP(ap->a_vp), ap->a_which, ap->a_fflags, ap->a_cred, ap->a_p)); } int union_mmap(ap) struct vop_mmap_args /* { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (VOP_MMAP(OTHERVP(ap->a_vp), ap->a_fflags, ap->a_cred, ap->a_p)); } 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 vnode *targetvp = OTHERVP(ap->a_vp); if (targetvp) { int dolock = (targetvp == LOWERVP(ap->a_vp)); if (dolock) VOP_LOCK(targetvp); else FIXUP(VTOUNION(ap->a_vp)); error = VOP_FSYNC(targetvp, ap->a_cred, ap->a_waitfor, ap->a_p); if (dolock) VOP_UNLOCK(targetvp); } return (error); } 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; { return (VOP_SEEK(OTHERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred)); } 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); if (dun->un_uppervp && un->un_uppervp) { struct vnode *dvp = dun->un_uppervp; struct vnode *vp = un->un_uppervp; FIXUP(dun); VREF(dvp); dun->un_flags |= UN_KLOCK; vput(ap->a_dvp); FIXUP(un); VREF(vp); un->un_flags |= UN_KLOCK; vput(ap->a_vp); error = VOP_REMOVE(dvp, vp, ap->a_cnp); if (!error) union_removed_upper(un); /* * XXX: should create a whiteout here */ } else { /* * XXX: should create a whiteout here */ vput(ap->a_dvp); vput(ap->a_vp); error = EROFS; } return (error); } int union_link(ap) struct vop_link_args /* { struct vnode *a_vp; struct vnode *a_tdvp; struct componentname *a_cnp; } */ *ap; { int error; struct union_node *dun = VTOUNION(ap->a_vp); struct union_node *un = VTOUNION(ap->a_tdvp); if (dun->un_uppervp && un->un_uppervp) { struct vnode *dvp = dun->un_uppervp; struct vnode *vp = un->un_uppervp; FIXUP(dun); VREF(dvp); dun->un_flags |= UN_KLOCK; vput(ap->a_vp); FIXUP(un); VREF(vp); vrele(ap->a_tdvp); error = VOP_LINK(dvp, vp, ap->a_cnp); } else { /* * XXX: need to copy to upper layer * and do the link there. */ vput(ap->a_vp); vrele(ap->a_tdvp); error = EROFS; } return (error); } 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; if (fdvp->v_op == union_vnodeop_p) { /* always true */ struct union_node *un = VTOUNION(fdvp); if (un->un_uppervp == NULLVP) { error = EROFS; goto bad; } FIXUP(un); 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) { error = EROFS; goto bad; } FIXUP(un); 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) { error = EROFS; goto bad; } tdvp = un->un_uppervp; VREF(tdvp); un->un_flags |= UN_KLOCK; vput(ap->a_tdvp); } if (tvp && tvp->v_op == union_vnodeop_p) { struct union_node *un = VTOUNION(tvp); if (un->un_uppervp == NULLVP) { error = EROFS; goto bad; } tvp = un->un_uppervp; VREF(tvp); un->un_flags |= UN_KLOCK; vput(ap->a_tvp); } return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp)); bad: vrele(fdvp); vrele(fvp); vput(tdvp); if (tvp) vput(tvp); return (error); } 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; if (dvp) { int error; struct vnode *vp; FIXUP(un); VREF(dvp); un->un_flags |= UN_KLOCK; vput(ap->a_dvp); error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap); if (error) return (error); error = union_allocvp( ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp, NULLVP, ap->a_cnp, vp, NULLVP); if (error) vput(vp); return (error); } vput(ap->a_dvp); return (EROFS); } 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); if (dun->un_uppervp && un->un_uppervp) { struct vnode *dvp = dun->un_uppervp; struct vnode *vp = un->un_uppervp; FIXUP(dun); VREF(dvp); dun->un_flags |= UN_KLOCK; vput(ap->a_dvp); FIXUP(un); VREF(vp); un->un_flags |= UN_KLOCK; vput(ap->a_vp); error = VOP_RMDIR(dvp, vp, ap->a_cnp); if (!error) union_removed_upper(un); /* * XXX: should create a whiteout here */ } else { /* * XXX: should create a whiteout here */ vput(ap->a_dvp); vput(ap->a_vp); error = EROFS; } return (error); } 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; if (dvp) { int error; struct vnode *vp; struct mount *mp = ap->a_dvp->v_mount; FIXUP(un); VREF(dvp); un->un_flags |= UN_KLOCK; vput(ap->a_dvp); error = VOP_SYMLINK(dvp, &vp, ap->a_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. */ int union_readdir(ap) struct vop_readdir_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { int error = 0; struct union_node *un = VTOUNION(ap->a_vp); if (un->un_uppervp) { FIXUP(un); error = VOP_READDIR(un->un_uppervp, ap->a_uio, ap->a_cred); } return (error); } int union_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { int error; struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) VOP_LOCK(vp); else FIXUP(VTOUNION(ap->a_vp)); error = VOP_READLINK(vp, ap->a_uio, ap->a_cred); if (dolock) VOP_UNLOCK(vp); return (error); } int union_abortop(ap) struct vop_abortop_args /* { struct vnode *a_dvp; struct componentname *a_cnp; } */ *ap; { int error; 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) VOP_LOCK(vp); else FIXUP(VTOUNION(ap->a_dvp)); } error = VOP_ABORTOP(vp, ap->a_cnp); if (islocked && dolock) VOP_UNLOCK(vp); return (error); } int union_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { /* * 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. */ #ifdef UNION_DIAGNOSTIC struct union_node *un = VTOUNION(ap->a_vp); if (un->un_flags & UN_LOCKED) panic("union: inactivating locked node"); #endif return (0); } int union_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { union_freevp(ap->a_vp); return (0); } int union_lock(ap) struct vop_lock_args *ap; { struct vnode *vp = ap->a_vp; struct union_node *un; start: while (vp->v_flag & VXLOCK) { vp->v_flag |= VXWANT; sleep((caddr_t)vp, PINOD); } un = VTOUNION(vp); if (un->un_uppervp) { if ((un->un_flags & UN_ULOCK) == 0) { un->un_flags |= UN_ULOCK; VOP_LOCK(un->un_uppervp); } #ifdef DIAGNOSTIC if (un->un_flags & UN_KLOCK) panic("union: dangling upper lock"); #endif } 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; sleep((caddr_t) &un->un_flags, PINOD); 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); } int union_unlock(ap) struct vop_lock_args *ap; { struct union_node *un = VTOUNION(ap->a_vp); #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); un->un_flags &= ~(UN_ULOCK|UN_KLOCK); if (un->un_flags & UN_WANT) { un->un_flags &= ~UN_WANT; wakeup((caddr_t) &un->un_flags); } #ifdef DIAGNOSTIC un->un_pid = 0; #endif return (0); } 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; } */ *ap; { int error; struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) VOP_LOCK(vp); else FIXUP(VTOUNION(ap->a_vp)); error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp); if (dolock) VOP_UNLOCK(vp); return (error); } int union_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n", vp, UPPERVP(vp), LOWERVP(vp)); return (0); } int union_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0); } int union_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { int error; struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) VOP_LOCK(vp); else FIXUP(VTOUNION(ap->a_vp)); error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval); if (dolock) VOP_UNLOCK(vp); return (error); } 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; { return (VOP_ADVLOCK(OTHERVP(ap->a_vp), ap->a_id, ap->a_op, ap->a_fl, ap->a_flags)); } /* * XXX - vop_strategy must be hand coded because it has no * vnode in its arguments. * This goes away with a merged VM/buffer cache. */ 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 */ int (**union_vnodeop_p)(); struct vnodeopv_entry_desc union_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, union_lookup }, /* lookup */ { &vop_create_desc, union_create }, /* create */ { &vop_mknod_desc, union_mknod }, /* mknod */ { &vop_open_desc, union_open }, /* open */ { &vop_close_desc, union_close }, /* close */ { &vop_access_desc, union_access }, /* access */ { &vop_getattr_desc, union_getattr }, /* getattr */ { &vop_setattr_desc, union_setattr }, /* setattr */ { &vop_read_desc, union_read }, /* read */ { &vop_write_desc, union_write }, /* write */ { &vop_ioctl_desc, union_ioctl }, /* ioctl */ { &vop_select_desc, union_select }, /* select */ { &vop_mmap_desc, union_mmap }, /* mmap */ { &vop_fsync_desc, union_fsync }, /* fsync */ { &vop_seek_desc, union_seek }, /* seek */ { &vop_remove_desc, union_remove }, /* remove */ { &vop_link_desc, union_link }, /* link */ { &vop_rename_desc, union_rename }, /* rename */ { &vop_mkdir_desc, union_mkdir }, /* mkdir */ { &vop_rmdir_desc, union_rmdir }, /* rmdir */ { &vop_symlink_desc, union_symlink }, /* symlink */ { &vop_readdir_desc, union_readdir }, /* readdir */ { &vop_readlink_desc, union_readlink }, /* readlink */ { &vop_abortop_desc, union_abortop }, /* abortop */ { &vop_inactive_desc, union_inactive }, /* inactive */ { &vop_reclaim_desc, union_reclaim }, /* reclaim */ { &vop_lock_desc, union_lock }, /* lock */ { &vop_unlock_desc, union_unlock }, /* unlock */ { &vop_bmap_desc, union_bmap }, /* bmap */ { &vop_strategy_desc, union_strategy }, /* strategy */ { &vop_print_desc, union_print }, /* print */ { &vop_islocked_desc, union_islocked }, /* islocked */ { &vop_pathconf_desc, union_pathconf }, /* pathconf */ { &vop_advlock_desc, union_advlock }, /* advlock */ #ifdef notdef { &vop_blkatoff_desc, union_blkatoff }, /* blkatoff */ { &vop_valloc_desc, union_valloc }, /* valloc */ { &vop_vfree_desc, union_vfree }, /* vfree */ { &vop_truncate_desc, union_truncate }, /* truncate */ { &vop_update_desc, union_update }, /* update */ { &vop_bwrite_desc, union_bwrite }, /* bwrite */ #endif { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc union_vnodeop_opv_desc = { &union_vnodeop_p, union_vnodeop_entries }; + +VNODEOP_SET(union_vnodeop_opv_desc); Index: head/sys/isofs/cd9660/cd9660_vfsops.c =================================================================== --- head/sys/isofs/cd9660/cd9660_vfsops.c (revision 2945) +++ head/sys/isofs/cd9660/cd9660_vfsops.c (revision 2946) @@ -1,686 +1,688 @@ /*- * 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_vfsops.c 8.3 (Berkeley) 1/31/94 - * $Id: cd9660_vfsops.c,v 1.5 1994/08/20 16:03:07 davidg Exp $ + * $Id: cd9660_vfsops.c,v 1.6 1994/09/15 19:46:02 bde Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct vfsops cd9660_vfsops = { cd9660_mount, cd9660_start, cd9660_unmount, cd9660_root, cd9660_quotactl, cd9660_statfs, cd9660_sync, cd9660_vget, cd9660_fhtovp, cd9660_vptofh, cd9660_init, }; +VFS_SET(cd9660_vfsops, cd9660, MOUNT_CD9660, 0); + /* * Called by vfs_mountroot when iso is going to be mounted as root. * * Name is updated by mount(8) after booting. */ #define ROOTNAME "root_device" static int iso_mountfs __P((struct vnode *devvp, struct mount *mp, struct proc *p, struct iso_args *argp)); int cd9660_mountroot() { register struct mount *mp; struct proc *p = curproc; /* XXX */ struct iso_mnt *imp; register struct fs *fs; u_int size; int error; struct iso_args args; /* * Get vnodes for swapdev and rootdev. */ if (bdevvp(swapdev, &swapdev_vp) || bdevvp(rootdev, &rootvp)) panic("cd9660_mountroot: can't setup bdevvp's"); mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); bzero((char *)mp, (u_long)sizeof(struct mount)); mp->mnt_op = &cd9660_vfsops; mp->mnt_flag = MNT_RDONLY; args.flags = ISOFSMNT_ROOT; if (error = iso_mountfs(rootvp, mp, p, &args)) { free(mp, M_MOUNT); return (error); } if (error = vfs_lock(mp)) { (void)cd9660_unmount(mp, 0, p); free(mp, M_MOUNT); return (error); } TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); mp->mnt_flag |= MNT_ROOTFS; mp->mnt_vnodecovered = NULLVP; imp = VFSTOISOFS(mp); bzero(imp->im_fsmnt, sizeof(imp->im_fsmnt)); imp->im_fsmnt[0] = '/'; bcopy((caddr_t)imp->im_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN); (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void) cd9660_statfs(mp, &mp->mnt_stat, p); vfs_unlock(mp); return (0); } /* * Flag to allow forcible unmounting. */ int iso_doforce = 1; /* * VFS Operations. * * mount system call */ int cd9660_mount(mp, path, data, ndp, p) register struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { struct vnode *devvp; struct iso_args args; u_int size; int error; struct iso_mnt *imp = 0; if (error = copyin(data, (caddr_t)&args, sizeof (struct iso_args))) return (error); if ((mp->mnt_flag & MNT_RDONLY) == 0) return (EROFS); /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { imp = VFSTOISOFS(mp); if (args.fspec == 0) return (vfs_export(mp, &imp->im_export, &args.export)); } /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible block device. */ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); if (error = namei(ndp)) return (error); devvp = ndp->ni_vp; if (devvp->v_type != VBLK) { vrele(devvp); return ENOTBLK; } if (major(devvp->v_rdev) >= nblkdev) { vrele(devvp); return ENXIO; } if ((mp->mnt_flag & MNT_UPDATE) == 0) error = iso_mountfs(devvp, mp, p, &args); else { if (devvp != imp->im_devvp) error = EINVAL; /* needs translation */ else vrele(devvp); } if (error) { vrele(devvp); return error; } imp = VFSTOISOFS(mp); (void) copyinstr(path, imp->im_fsmnt, sizeof(imp->im_fsmnt)-1, &size); bzero(imp->im_fsmnt + size, sizeof(imp->im_fsmnt) - size); bcopy((caddr_t)imp->im_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN); (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void) cd9660_statfs(mp, &mp->mnt_stat, p); return 0; } /* * Common code for mount and mountroot */ static int iso_mountfs(devvp, mp, p, argp) register struct vnode *devvp; struct mount *mp; struct proc *p; struct iso_args *argp; { register struct iso_mnt *isomp = (struct iso_mnt *)0; struct buf *bp = NULL; dev_t dev = devvp->v_rdev; caddr_t base, space; int havepart = 0, blks; int error = EINVAL, i, size; int needclose = 0; int ronly = (mp->mnt_flag & MNT_RDONLY) != 0; int j; int iso_bsize; int iso_blknum; struct iso_volume_descriptor *vdp; struct iso_primary_descriptor *pri; struct iso_directory_record *rootp; int logical_block_size; if (!ronly) return EROFS; /* * Disallow multiple mounts of the same device. * Disallow mounting of a device that is currently in use * (except for root, which might share swap device for miniroot). * Flush out any old buffers remaining from a previous use. */ if (error = vfs_mountedon(devvp)) return error; if (vcount(devvp) > 1 && devvp != rootvp) return EBUSY; if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0)) return (error); if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p)) return error; needclose = 1; /* This is the "logical sector size". The standard says this * should be 2048 or the physical sector size on the device, * whichever is greater. For now, we'll just use a constant. */ iso_bsize = ISO_DEFAULT_BLOCK_SIZE; for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) { if (error = bread (devvp, btodb(iso_blknum * iso_bsize), iso_bsize, NOCRED, &bp)) goto out; vdp = (struct iso_volume_descriptor *)bp->b_un.b_addr; if (bcmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) != 0) { error = EINVAL; goto out; } if (isonum_711 (vdp->type) == ISO_VD_END) { error = EINVAL; goto out; } if (isonum_711 (vdp->type) == ISO_VD_PRIMARY) break; brelse(bp); } if (isonum_711 (vdp->type) != ISO_VD_PRIMARY) { error = EINVAL; goto out; } pri = (struct iso_primary_descriptor *)vdp; logical_block_size = isonum_723 (pri->logical_block_size); if (logical_block_size < DEV_BSIZE || logical_block_size > MAXBSIZE || (logical_block_size & (logical_block_size - 1)) != 0) { error = EINVAL; goto out; } rootp = (struct iso_directory_record *)pri->root_directory_record; isomp = malloc(sizeof *isomp, M_ISOFSMNT, M_WAITOK); bzero((caddr_t)isomp, sizeof *isomp); isomp->logical_block_size = logical_block_size; isomp->volume_space_size = isonum_733 (pri->volume_space_size); bcopy (rootp, isomp->root, sizeof isomp->root); isomp->root_extent = isonum_733 (rootp->extent); isomp->root_size = isonum_733 (rootp->size); isomp->im_bmask = logical_block_size - 1; isomp->im_bshift = 0; while ((1 << isomp->im_bshift) < isomp->logical_block_size) isomp->im_bshift++; bp->b_flags |= B_AGE; brelse(bp); bp = NULL; mp->mnt_data = (qaddr_t)isomp; mp->mnt_stat.f_fsid.val[0] = (long)dev; mp->mnt_stat.f_fsid.val[1] = MOUNT_CD9660; mp->mnt_maxsymlinklen = 0; mp->mnt_flag |= MNT_LOCAL; isomp->im_mountp = mp; isomp->im_dev = dev; isomp->im_devvp = devvp; devvp->v_specflags |= SI_MOUNTEDON; /* Check the Rock Ridge Extention support */ if (!(argp->flags & ISOFSMNT_NORRIP)) { if (error = bread (isomp->im_devvp, (isomp->root_extent + isonum_711(rootp->ext_attr_length)) * isomp->logical_block_size / DEV_BSIZE, isomp->logical_block_size,NOCRED,&bp)) goto out; rootp = (struct iso_directory_record *)bp->b_un.b_addr; if ((isomp->rr_skip = cd9660_rrip_offset(rootp,isomp)) < 0) { argp->flags |= ISOFSMNT_NORRIP; } else { argp->flags &= ~ISOFSMNT_GENS; } /* * The contents are valid, * but they will get reread as part of another vnode, so... */ bp->b_flags |= B_AGE; brelse(bp); bp = NULL; } isomp->im_flags = argp->flags&(ISOFSMNT_NORRIP|ISOFSMNT_GENS|ISOFSMNT_EXTATT); switch (isomp->im_flags&(ISOFSMNT_NORRIP|ISOFSMNT_GENS)) { default: isomp->iso_ftype = ISO_FTYPE_DEFAULT; break; case ISOFSMNT_GENS|ISOFSMNT_NORRIP: isomp->iso_ftype = ISO_FTYPE_9660; break; case 0: isomp->iso_ftype = ISO_FTYPE_RRIP; break; } return 0; out: if (bp) brelse(bp); if (needclose) (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p); if (isomp) { free((caddr_t)isomp, M_ISOFSMNT); mp->mnt_data = (qaddr_t)0; } return error; } /* * Make a filesystem operational. * Nothing to do at the moment. */ /* ARGSUSED */ int cd9660_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return 0; } /* * unmount system call */ int cd9660_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { register struct iso_mnt *isomp; int i, error, ronly, flags = 0; if (mntflags & MNT_FORCE) { if (!iso_doforce) return (EINVAL); flags |= FORCECLOSE; } #if 0 mntflushbuf(mp, 0); if (mntinvalbuf(mp)) return EBUSY; #endif if (error = vflush(mp, NULLVP, flags)) return (error); isomp = VFSTOISOFS(mp); #ifdef ISODEVMAP if (isomp->iso_ftype == ISO_FTYPE_RRIP) iso_dunmap(isomp->im_dev); #endif isomp->im_devvp->v_specflags &= ~SI_MOUNTEDON; error = VOP_CLOSE(isomp->im_devvp, FREAD, NOCRED, p); vrele(isomp->im_devvp); free((caddr_t)isomp, M_ISOFSMNT); mp->mnt_data = (qaddr_t)0; mp->mnt_flag &= ~MNT_LOCAL; return (error); } /* * Return root of a filesystem */ int cd9660_root(mp, vpp) struct mount *mp; struct vnode **vpp; { register struct iso_node *ip; struct iso_node tip, *nip; struct vnode tvp; int error; struct iso_mnt *imp = VFSTOISOFS (mp); struct iso_directory_record *dp; tvp.v_mount = mp; tvp.v_data = &tip; ip = VTOI(&tvp); ip->i_vnode = &tvp; ip->i_dev = imp->im_dev; ip->i_diroff = 0; dp = (struct iso_directory_record *)imp->root; isodirino(&ip->i_number,dp,imp); /* * With RRIP we must use the `.' entry of the root directory. * Simply tell iget, that it's a relocated directory. */ error = iso_iget(ip,ip->i_number, imp->iso_ftype == ISO_FTYPE_RRIP, &nip,dp); if (error) return error; *vpp = ITOV(nip); return 0; } /* * Do operations associated with quotas, not supported */ /* ARGSUSED */ int cd9660_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } /* * Get file system statistics. */ int cd9660_statfs(mp, sbp, p) struct mount *mp; register struct statfs *sbp; struct proc *p; { register struct iso_mnt *isomp; register struct fs *fs; isomp = VFSTOISOFS(mp); sbp->f_type = MOUNT_CD9660; sbp->f_bsize = isomp->logical_block_size; sbp->f_iosize = sbp->f_bsize; /* XXX */ sbp->f_blocks = isomp->volume_space_size; sbp->f_bfree = 0; /* total free blocks */ sbp->f_bavail = 0; /* blocks free for non superuser */ sbp->f_files = 0; /* total files */ sbp->f_ffree = 0; /* free file nodes */ if (sbp != &mp->mnt_stat) { bcopy((caddr_t)mp->mnt_stat.f_mntonname, (caddr_t)&sbp->f_mntonname[0], MNAMELEN); bcopy((caddr_t)mp->mnt_stat.f_mntfromname, (caddr_t)&sbp->f_mntfromname[0], MNAMELEN); } /* Use the first spare for flags: */ sbp->f_spare[0] = isomp->im_flags; return 0; } /* ARGSUSED */ int cd9660_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { return (0); } /* * Flat namespace lookup. * Currently unsupported. */ /* ARGSUSED */ int cd9660_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } /* * File handle to vnode * * Have to be really careful about stale file handles: * - check that the inode number is in range * - call iget() to get the locked inode * - check for an unallocated inode (i_mode == 0) * - check that the generation number matches */ struct ifid { ushort ifid_len; ushort ifid_pad; int ifid_ino; long ifid_start; }; /* ARGSUSED */ int cd9660_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) register struct mount *mp; struct fid *fhp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred **credanonp; { struct vnode tvp; int error; int lbn, off; struct ifid *ifhp; struct iso_mnt *imp; struct buf *bp; struct iso_directory_record *dirp; struct iso_node tip, *ip, *nip; struct netcred *np; imp = VFSTOISOFS (mp); ifhp = (struct ifid *)fhp; #ifdef ISOFS_DBG printf("fhtovp: ino %d, start %ld\n", ifhp->ifid_ino, ifhp->ifid_start); #endif np = vfs_export_lookup(mp, &imp->im_export, nam); if (np == NULL) return (EACCES); lbn = iso_lblkno(imp, ifhp->ifid_ino); if (lbn >= imp->volume_space_size) { printf("fhtovp: lbn exceed volume space %d\n", lbn); return (ESTALE); } off = iso_blkoff(imp, ifhp->ifid_ino); if (off + ISO_DIRECTORY_RECORD_SIZE > imp->logical_block_size) { printf("fhtovp: crosses block boundary %d\n", off + ISO_DIRECTORY_RECORD_SIZE); return (ESTALE); } error = bread(imp->im_devvp, btodb(lbn * imp->logical_block_size), imp->logical_block_size, NOCRED, &bp); if (error) { printf("fhtovp: bread error %d\n",error); brelse(bp); return (error); } dirp = (struct iso_directory_record *)(bp->b_un.b_addr + off); if (off + isonum_711(dirp->length) > imp->logical_block_size) { brelse(bp); printf("fhtovp: directory crosses block boundary %d[off=%d/len=%d]\n", off+isonum_711(dirp->length), off, isonum_711(dirp->length)); return (ESTALE); } if (isonum_733(dirp->extent) + isonum_711(dirp->ext_attr_length) != ifhp->ifid_start) { brelse(bp); printf("fhtovp: file start miss %d vs %d\n", isonum_733(dirp->extent)+isonum_711(dirp->ext_attr_length), ifhp->ifid_start); return (ESTALE); } brelse(bp); ip = &tip; tvp.v_mount = mp; tvp.v_data = ip; ip->i_vnode = &tvp; ip->i_dev = imp->im_dev; if (error = iso_iget(ip, ifhp->ifid_ino, 0, &nip, dirp)) { *vpp = NULLVP; printf("fhtovp: failed to get inode\n"); return (error); } ip = nip; /* * XXX need generation number? */ if (ip->inode.iso_mode == 0) { iso_iput(ip); *vpp = NULLVP; printf("fhtovp: inode mode == 0\n"); return (ESTALE); } *vpp = ITOV(ip); *exflagsp = np->netc_exflags; *credanonp = &np->netc_anon; return 0; } /* * Vnode pointer to File handle */ /* ARGSUSED */ int cd9660_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { register struct iso_node *ip = VTOI(vp); register struct ifid *ifhp; register struct iso_mnt *mp = ip->i_mnt; ifhp = (struct ifid *)fhp; ifhp->ifid_len = sizeof(struct ifid); ifhp->ifid_ino = ip->i_number; ifhp->ifid_start = ip->iso_start; #ifdef ISOFS_DBG printf("vptofh: ino %d, start %ld\n", ifhp->ifid_ino,ifhp->ifid_start); #endif return 0; } Index: head/sys/isofs/cd9660/cd9660_vnops.c =================================================================== --- head/sys/isofs/cd9660/cd9660_vnops.c (revision 2945) +++ head/sys/isofs/cd9660/cd9660_vnops.c (revision 2946) @@ -1,1041 +1,1045 @@ /*- * 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.3 (Berkeley) 1/23/94 - * $Id: cd9660_vnops.c,v 1.5 1994/09/09 11:10:59 dfr Exp $ + * $Id: cd9660_vnops.c,v 1.6 1994/09/15 19:46:03 bde Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if 0 /* * Mknod vnode call * Actually remap the device number */ cd9660_mknod(ndp, vap, cred, p) struct nameidata *ndp; struct ucred *cred; struct vattr *vap; struct proc *p; { #ifndef ISODEVMAP free(ndp->ni_pnbuf, M_NAMEI); vput(ndp->ni_dvp); vput(ndp->ni_vp); return EINVAL; #else register struct vnode *vp; struct iso_node *ip; struct iso_dnode *dp; int error; vp = ndp->ni_vp; ip = VTOI(vp); if (ip->i_mnt->iso_ftype != ISO_FTYPE_RRIP || vap->va_type != vp->v_type || (vap->va_type != VCHR && vap->va_type != VBLK)) { free(ndp->ni_pnbuf, M_NAMEI); vput(ndp->ni_dvp); vput(ndp->ni_vp); return EINVAL; } dp = iso_dmap(ip->i_dev,ip->i_number,1); if (ip->inode.iso_rdev == vap->va_rdev || vap->va_rdev == VNOVAL) { /* same as the unmapped one, delete the mapping */ remque(dp); FREE(dp,M_CACHE); } else /* enter new mapping */ dp->d_dev = vap->va_rdev; /* * Remove inode so that it will be reloaded by iget and * checked to see if it is an alias of an existing entry * in the inode cache. */ vput(vp); vp->v_type = VNON; vgone(vp); return (0); #endif } #endif /* * Open called. * * Nothing to do. */ /* ARGSUSED */ 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 */ 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 */ int cd9660_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (0); } 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); int i; 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; 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; return (0); } #if ISO_DEFAULT_BLOCK_SIZE >= NBPG #ifdef DEBUG extern int doclusterread; #else #define doclusterread 1 #endif #else /* XXX until cluster routines can handle block sizes less than one page */ #define doclusterread 0 #endif /* * Vnode op for reading. */ 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, bn, 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 |= IACC; imp = ip->i_mnt; do { lbn = iso_lblkno(imp, uio->uio_offset); on = iso_blkoff(imp, uio->uio_offset); n = min((unsigned)(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 = iso_blksize(imp, ip, lbn); rablock = lbn + 1; if (doclusterread) { if (iso_lblktosize(imp, rablock) <= ip->i_size) error = cluster_read(vp, (off_t)ip->i_size, lbn, size, NOCRED, &bp); else error = bread(vp, lbn, size, NOCRED, &bp); } else { if (vp->v_lastr + 1 == lbn && iso_lblktosize(imp, rablock) < ip->i_size) { rasize = iso_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_un.b_addr + on, (int)n, uio); brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); return (error); } /* ARGSUSED */ int cd9660_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; { printf("You did ioctl for isofs !!\n"); return (ENOTTY); } /* ARGSUSED */ 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 */ 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 */ 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; u_int *cookiep; int ncookies; int eof; }; static 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 = DIRSIZ(dp); if (idp->uio->uio_resid < dp->d_reclen) { idp->eof = 0; return -1; } if (idp->cookiep) { if (idp->ncookies <= 0) { idp->eof = 0; return -1; } *idp->cookiep++ = off; --idp->ncookies; } if (error = uiomove((caddr_t)dp,dp->d_reclen,idp->uio)) return error; idp->uio_off = off; return 0; } static 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; if (assoc = cl > 1 && *cname == ASSOCCHAR) { 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 = 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 * XXX make sure everything still works now that eofflagp and cookiep * are no longer args. */ int cd9660_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { register struct uio *uio = ap->a_uio; struct isoreaddir *idp; int entryoffsetinblock; int error = 0; int endsearch; struct iso_directory_record *ep; u_short elen; int reclen; struct iso_mnt *imp; struct iso_node *ip; struct buf *bp = NULL; u_short tmplen; ip = VTOI(ap->a_vp); imp = ip->i_mnt; MALLOC(idp,struct isoreaddir *,sizeof(*idp),M_TEMP,M_WAITOK); idp->saveent.d_namlen = 0; idp->assocent.d_namlen = 0; idp->uio = uio; #if 0 idp->cookiep = cookies; idp->ncookies = ncookies; idp->eof = 1; #else idp->cookiep = 0; #endif idp->curroff = uio->uio_offset; entryoffsetinblock = iso_blkoff(imp, idp->curroff); if (entryoffsetinblock != 0) { if (error = iso_blkatoff(ip, idp->curroff, &bp)) { FREE(idp,M_TEMP); return (error); } } endsearch = ip->i_size; while (idp->curroff < endsearch) { /* * If offset is on a block boundary, * read the next directory block. * Release previous if it exists. */ if (iso_blkoff(imp, idp->curroff) == 0) { if (bp != NULL) brelse(bp); if (error = iso_blkatoff(ip, idp->curroff, &bp)) break; entryoffsetinblock = 0; } /* * Get pointer to next entry. */ ep = (struct iso_directory_record *) (bp->b_un.b_addr + entryoffsetinblock); reclen = isonum_711 (ep->length); if (reclen == 0) { /* skip to next block, if any */ idp->curroff = roundup (idp->curroff, 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 (isonum_711(ep->flags)&2) isodirino(&idp->current.d_fileno,ep,imp); else idp->current.d_fileno = dbtob(bp->b_blkno) + idp->curroff; if (reclen < ISO_DIRECTORY_RECORD_SIZE + idp->current.d_namlen) { error = EINVAL; /* illegal entry, stop */ break; } idp->curroff += reclen; /* * */ switch (imp->iso_ftype) { case ISO_FTYPE_RRIP: cd9660_rrip_getname(ep,idp->current.d_name, &tmplen, &idp->current.d_fileno,imp); idp->current.d_namlen = tmplen; if (idp->current.d_namlen) error = iso_uiodir(idp,&idp->current,idp->curroff); break; default: /* ISO_FTYPE_DEFAULT || ISO_FTYPE_9660 */ 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, &elen, imp->iso_ftype == ISO_FTYPE_9660, isonum_711(ep->flags)&4); idp->current.d_namlen = (u_char)elen; 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 (bp) brelse (bp); uio->uio_offset = idp->uio_off; #if 0 *eofflagp = idp->eof; #endif 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; 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; u_short symlen; int error; char *symname; ino_t ino; ip = VTOI(ap->a_vp); imp = ip->i_mnt; if (imp->iso_ftype != ISO_FTYPE_RRIP) return EINVAL; /* * Get parents directory record block that this inode included. */ error = bread(imp->im_devvp, iso_dblkno(imp, ip->i_number), imp->logical_block_size, NOCRED, &bp); if (error) { brelse(bp); return EINVAL; } /* * Setup the directory pointer for this inode */ dirp = (ISODIR *)(bp->b_un.b_addr + (ip->i_number & imp->im_bmask)); #ifdef DEBUG printf("lbn=%d,off=%d,bsize=%d,DEV_BSIZE=%d, dirp= %08x, b_addr=%08x, offset=%08x(%08x)\n", (daddr_t)(ip->i_number >> imp->im_bshift), ip->i_number & imp->im_bmask, imp->logical_block_size, DEV_BSIZE, dirp, bp->b_un.b_addr, ip->i_number, ip->i_number & imp->im_bmask ); #endif /* * 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) > imp->logical_block_size) { brelse(bp); return EINVAL; } /* * Now get a buffer * Abuse a namei buffer for now. */ 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) { 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. */ error = uiomove(symname,symlen,ap->a_uio); FREE(symname,M_NAMEI); return error; } /* * 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. */ 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. */ int cd9660_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; } */ *ap; { register struct iso_node *ip = VTOI(ap->a_vp); ISO_ILOCK(ip); return 0; } /* * Unlock an inode. */ int cd9660_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; } */ *ap; { register struct iso_node *ip = VTOI(ap->a_vp); if (!(ip->i_flag & ILOCKED)) panic("cd9660_unlock NOT LOCKED"); ISO_IUNLOCK(ip); return 0; } /* * Check for a locked inode. */ int cd9660_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { if (VTOI(ap->a_vp)->i_flag & ILOCKED) return 1; return 0; } /* * Calculate the logical to physical mapping if not done already, * then call the device strategy routine. */ 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)) { 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. */ int cd9660_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_ISOFS, isofs vnode\n"); return 0; } /* * Unsupported operation */ int cd9660_enotsupp() { return (EOPNOTSUPP); } /* * Global vfs data structures for isofs */ #define cd9660_create \ ((int (*) __P((struct vop_create_args *)))cd9660_enotsupp) #define cd9660_mknod ((int (*) __P((struct vop_mknod_args *)))cd9660_enotsupp) #define cd9660_setattr \ ((int (*) __P((struct vop_setattr_args *)))cd9660_enotsupp) #define cd9660_write ((int (*) __P((struct vop_write_args *)))cd9660_enotsupp) #define cd9660_fsync ((int (*) __P((struct vop_fsync_args *)))nullop) #define cd9660_remove \ ((int (*) __P((struct vop_remove_args *)))cd9660_enotsupp) #define cd9660_link ((int (*) __P((struct vop_link_args *)))cd9660_enotsupp) #define cd9660_rename \ ((int (*) __P((struct vop_rename_args *)))cd9660_enotsupp) #define cd9660_mkdir ((int (*) __P((struct vop_mkdir_args *)))cd9660_enotsupp) #define cd9660_rmdir ((int (*) __P((struct vop_rmdir_args *)))cd9660_enotsupp) #define cd9660_symlink \ ((int (*) __P((struct vop_symlink_args *)))cd9660_enotsupp) #define cd9660_pathconf \ ((int (*) __P((struct vop_pathconf_args *)))cd9660_enotsupp) #define cd9660_advlock \ ((int (*) __P((struct vop_advlock_args *)))cd9660_enotsupp) #define cd9660_blkatoff \ ((int (*) __P((struct vop_blkatoff_args *)))cd9660_enotsupp) #define cd9660_valloc ((int(*) __P(( \ struct vnode *pvp, \ int mode, \ struct ucred *cred, \ struct vnode **vpp))) cd9660_enotsupp) #define cd9660_vfree ((int (*) __P((struct vop_vfree_args *)))cd9660_enotsupp) #define cd9660_truncate \ ((int (*) __P((struct vop_truncate_args *)))cd9660_enotsupp) #define cd9660_update \ ((int (*) __P((struct vop_update_args *)))cd9660_enotsupp) #define cd9660_bwrite \ ((int (*) __P((struct vop_bwrite_args *)))cd9660_enotsupp) /* * Global vfs data structures for nfs */ int (**cd9660_vnodeop_p)(); struct vnodeopv_entry_desc cd9660_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, cd9660_lookup }, /* lookup */ { &vop_create_desc, cd9660_create }, /* create */ { &vop_mknod_desc, cd9660_mknod }, /* mknod */ { &vop_open_desc, cd9660_open }, /* open */ { &vop_close_desc, cd9660_close }, /* close */ { &vop_access_desc, cd9660_access }, /* access */ { &vop_getattr_desc, cd9660_getattr }, /* getattr */ { &vop_setattr_desc, cd9660_setattr }, /* setattr */ { &vop_read_desc, cd9660_read }, /* read */ { &vop_write_desc, cd9660_write }, /* write */ { &vop_ioctl_desc, cd9660_ioctl }, /* ioctl */ { &vop_select_desc, cd9660_select }, /* select */ { &vop_mmap_desc, cd9660_mmap }, /* mmap */ { &vop_fsync_desc, cd9660_fsync }, /* fsync */ { &vop_seek_desc, cd9660_seek }, /* seek */ { &vop_remove_desc, cd9660_remove }, /* remove */ { &vop_link_desc, cd9660_link }, /* link */ { &vop_rename_desc, cd9660_rename }, /* rename */ { &vop_mkdir_desc, cd9660_mkdir }, /* mkdir */ { &vop_rmdir_desc, cd9660_rmdir }, /* rmdir */ { &vop_symlink_desc, cd9660_symlink }, /* symlink */ { &vop_readdir_desc, cd9660_readdir }, /* readdir */ { &vop_readlink_desc, cd9660_readlink },/* readlink */ { &vop_abortop_desc, cd9660_abortop }, /* abortop */ { &vop_inactive_desc, cd9660_inactive },/* inactive */ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, cd9660_lock }, /* lock */ { &vop_unlock_desc, cd9660_unlock }, /* unlock */ { &vop_bmap_desc, cd9660_bmap }, /* bmap */ { &vop_strategy_desc, cd9660_strategy },/* strategy */ { &vop_print_desc, cd9660_print }, /* print */ { &vop_islocked_desc, cd9660_islocked },/* islocked */ { &vop_pathconf_desc, cd9660_pathconf },/* pathconf */ { &vop_advlock_desc, cd9660_advlock }, /* advlock */ { &vop_blkatoff_desc, cd9660_blkatoff },/* blkatoff */ { &vop_valloc_desc, cd9660_valloc }, /* valloc */ { &vop_vfree_desc, cd9660_vfree }, /* vfree */ { &vop_truncate_desc, cd9660_truncate },/* truncate */ { &vop_update_desc, cd9660_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc cd9660_vnodeop_opv_desc = { &cd9660_vnodeop_p, cd9660_vnodeop_entries }; +VNODEOP_SET(cd9660_vnodeop_opv_desc); /* * Special device vnode ops */ int (**cd9660_specop_p)(); struct vnodeopv_entry_desc cd9660_specop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, spec_lookup }, /* lookup */ { &vop_create_desc, cd9660_create }, /* create */ { &vop_mknod_desc, cd9660_mknod }, /* mknod */ { &vop_open_desc, spec_open }, /* open */ { &vop_close_desc, spec_close }, /* close */ { &vop_access_desc, cd9660_access }, /* access */ { &vop_getattr_desc, cd9660_getattr }, /* getattr */ { &vop_setattr_desc, cd9660_setattr }, /* setattr */ { &vop_read_desc, spec_read }, /* read */ { &vop_write_desc, spec_write }, /* write */ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ { &vop_select_desc, spec_select }, /* select */ { &vop_mmap_desc, spec_mmap }, /* mmap */ { &vop_fsync_desc, spec_fsync }, /* fsync */ { &vop_seek_desc, spec_seek }, /* seek */ { &vop_remove_desc, cd9660_remove }, /* remove */ { &vop_link_desc, cd9660_link }, /* link */ { &vop_rename_desc, cd9660_rename }, /* rename */ { &vop_mkdir_desc, cd9660_mkdir }, /* mkdir */ { &vop_rmdir_desc, cd9660_rmdir }, /* rmdir */ { &vop_symlink_desc, cd9660_symlink }, /* symlink */ { &vop_readdir_desc, spec_readdir }, /* readdir */ { &vop_readlink_desc, spec_readlink }, /* readlink */ { &vop_abortop_desc, spec_abortop }, /* abortop */ { &vop_inactive_desc, cd9660_inactive },/* inactive */ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, cd9660_lock }, /* lock */ { &vop_unlock_desc, cd9660_unlock }, /* unlock */ { &vop_bmap_desc, spec_bmap }, /* bmap */ /* XXX strategy: panics, should be notsupp instead? */ { &vop_strategy_desc, cd9660_strategy },/* strategy */ { &vop_print_desc, cd9660_print }, /* print */ { &vop_islocked_desc, cd9660_islocked },/* islocked */ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ { &vop_advlock_desc, spec_advlock }, /* advlock */ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, spec_valloc }, /* valloc */ { &vop_vfree_desc, spec_vfree }, /* vfree */ { &vop_truncate_desc, spec_truncate }, /* truncate */ { &vop_update_desc, cd9660_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc cd9660_specop_opv_desc = { &cd9660_specop_p, cd9660_specop_entries }; +VNODEOP_SET(cd9660_specop_opv_desc); #ifdef FIFO int (**cd9660_fifoop_p)(); struct vnodeopv_entry_desc cd9660_fifoop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, fifo_lookup }, /* lookup */ { &vop_create_desc, cd9660_create }, /* create */ { &vop_mknod_desc, cd9660_mknod }, /* mknod */ { &vop_open_desc, fifo_open }, /* open */ { &vop_close_desc, fifo_close }, /* close */ { &vop_access_desc, cd9660_access }, /* access */ { &vop_getattr_desc, cd9660_getattr }, /* getattr */ { &vop_setattr_desc, cd9660_setattr }, /* setattr */ { &vop_read_desc, fifo_read }, /* read */ { &vop_write_desc, fifo_write }, /* write */ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */ { &vop_select_desc, fifo_select }, /* select */ { &vop_mmap_desc, fifo_mmap }, /* mmap */ { &vop_fsync_desc, fifo_fsync }, /* fsync */ { &vop_seek_desc, fifo_seek }, /* seek */ { &vop_remove_desc, cd9660_remove }, /* remove */ { &vop_link_desc, cd9660_link }, /* link */ { &vop_rename_desc, cd9660_rename }, /* rename */ { &vop_mkdir_desc, cd9660_mkdir }, /* mkdir */ { &vop_rmdir_desc, cd9660_rmdir }, /* rmdir */ { &vop_symlink_desc, cd9660_symlink }, /* symlink */ { &vop_readdir_desc, fifo_readdir }, /* readdir */ { &vop_readlink_desc, fifo_readlink }, /* readlink */ { &vop_abortop_desc, fifo_abortop }, /* abortop */ { &vop_inactive_desc, cd9660_inactive },/* inactive */ { &vop_reclaim_desc, cd9660_reclaim }, /* reclaim */ { &vop_lock_desc, cd9660_lock }, /* lock */ { &vop_unlock_desc, cd9660_unlock }, /* unlock */ { &vop_bmap_desc, fifo_bmap }, /* bmap */ { &vop_strategy_desc, fifo_badop }, /* strategy */ { &vop_print_desc, cd9660_print }, /* print */ { &vop_islocked_desc, cd9660_islocked },/* islocked */ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, fifo_valloc }, /* valloc */ { &vop_vfree_desc, fifo_vfree }, /* vfree */ { &vop_truncate_desc, fifo_truncate }, /* truncate */ { &vop_update_desc, cd9660_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc cd9660_fifoop_opv_desc = { &cd9660_fifoop_p, cd9660_fifoop_entries }; + +VNODEOP_SET(cd9660_fifoop_opv_desc); #endif /* FIFO */ Index: head/sys/kern/kern_lkm.c =================================================================== --- head/sys/kern/kern_lkm.c (revision 2945) +++ head/sys/kern/kern_lkm.c (revision 2946) @@ -1,893 +1,902 @@ /*- * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1994 Christopher G. Demetriou * 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 Terrence R. Lambert. * 4. The name Terrence R. Lambert may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY TERRENCE R. LAMBERT ``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 TERRENCE R. LAMBERT 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. */ /* * XXX it's not really safe to unload *any* of the types which are * currently loadable; e.g. you could unload a syscall which was being * blocked in, etc. In the long term, a solution should be come up * with, but "not right now." -- cgd */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PAGESIZE 1024 /* kmem_alloc() allocation quantum */ #define LKM_ALLOC 0x01 #define LKM_WANT 0x02 #define LKMS_IDLE 0x00 #define LKMS_RESERVED 0x01 #define LKMS_LOADING 0x02 #define LKMS_LOADED 0x04 #define LKMS_UNLOADING 0x08 static int lkm_v = 0; static int lkm_state = LKMS_IDLE; #ifndef MAXLKMS #define MAXLKMS 20 #endif static struct lkm_table lkmods[MAXLKMS]; /* table of loaded modules */ static struct lkm_table *curp; /* global for in-progress ops */ /*ARGSUSED*/ int lkmcopen(dev, flag, devtype, p) dev_t dev; int flag; int devtype; struct proc *p; { int error; if (minor(dev) != 0) return(ENXIO); /* bad minor # */ /* * Use of the loadable kernel module device must be exclusive; we * may try to remove this restriction later, but it's really no * hardship. */ while (lkm_v & LKM_ALLOC) { if (flag & FNONBLOCK) /* don't hang */ return(EBUSY); lkm_v |= LKM_WANT; /* * Sleep pending unlock; we use tsleep() to allow * an alarm out of the open. */ if (error = tsleep((caddr_t)&lkm_v, TTIPRI|PCATCH, "lkmopn", 0)) return(error); /* leave LKM_WANT set -- no problem */ } lkm_v |= LKM_ALLOC; return(0); /* pseudo-device open */ } /* * Unreserve the memory associated with the current loaded module; done on * a coerced close of the lkm device (close on premature exit of modload) * or explicitly by modload as a result of a link failure. */ static void lkmunreserve() { if (lkm_state == LKMS_IDLE) return; /* * Actually unreserve the memory */ if (curp && curp->area) { kmem_free(kmem_map, curp->area, curp->size);/**/ curp->area = 0; } lkm_state = LKMS_IDLE; } int lkmcclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { if (!(lkm_v & LKM_ALLOC)) { #ifdef DEBUG printf("LKM: close before open!\n"); #endif /* DEBUG */ return(EBADF); } /* do this before waking the herd... */ if (curp && !curp->used) { /* * If we close before setting used, we have aborted * by way of error or by way of close-on-exit from * a premature exit of "modload". */ lkmunreserve(); /* coerce state to LKM_IDLE */ } lkm_v &= ~LKM_ALLOC; wakeup((caddr_t)&lkm_v); /* thundering herd "problem" here */ return(0); /* pseudo-device closed */ } /*ARGSUSED*/ int lkmcioctl(dev, cmd, data, flag) dev_t dev; int cmd; caddr_t data; int flag; { int err = 0; int i; struct lmc_resrv *resrvp; struct lmc_loadbuf *loadbufp; struct lmc_unload *unloadp; struct lmc_stat *statp; int (*funcp)(); char istr[MAXLKMNAME]; switch(cmd) { case LMRESERV: /* reserve pages for a module */ if ((flag & FWRITE) == 0) /* only allow this if writing */ return EPERM; resrvp = (struct lmc_resrv *)data; /* * Find a free slot. */ for (i = 0; i < MAXLKMS; i++) if (!lkmods[i].used) break; if (i == MAXLKMS) { err = ENOMEM; /* no slots available */ break; } curp = &lkmods[i]; curp->id = i; /* self reference slot offset */ resrvp->slot = i; /* return slot */ /* * Get memory for module */ curp->size = resrvp->size; curp->area = kmem_alloc(kmem_map, curp->size);/**/ curp->offset = 0; /* load offset */ resrvp->addr = curp->area; /* ret kernel addr */ #ifdef DEBUG printf("LKM: LMRESERV (actual = 0x%08x)\n", curp->area); printf("LKM: LMRESERV (adjusted = 0x%08x)\n", trunc_page(curp->area)); #endif /* DEBUG */ lkm_state = LKMS_RESERVED; break; case LMLOADBUF: /* Copy in; stateful, follows LMRESERV */ if ((flag & FWRITE) == 0) /* only allow this if writing */ return EPERM; loadbufp = (struct lmc_loadbuf *)data; i = loadbufp->cnt; if ((lkm_state != LKMS_RESERVED && lkm_state != LKMS_LOADING) || i < 0 || i > MODIOBUF || i > curp->size - curp->offset) { err = ENOMEM; break; } /* copy in buffer full of data */ if (err = copyin((caddr_t)loadbufp->data, (caddr_t)curp->area + curp->offset, i)) break; if ((curp->offset + i) < curp->size) { lkm_state = LKMS_LOADING; #ifdef DEBUG printf("LKM: LMLOADBUF (loading @ %d of %d, i = %d)\n", curp->offset, curp->size, i); #endif /* DEBUG */ } else { lkm_state = LKMS_LOADED; #ifdef DEBUG printf("LKM: LMLOADBUF (loaded)\n"); #endif /* DEBUG */ } curp->offset += i; break; case LMUNRESRV: /* discard reserved pages for a module */ if ((flag & FWRITE) == 0) /* only allow this if writing */ return EPERM; lkmunreserve(); /* coerce state to LKM_IDLE */ #ifdef DEBUG printf("LKM: LMUNRESERV\n"); #endif /* DEBUG */ break; case LMREADY: /* module loaded: call entry */ if ((flag & FWRITE) == 0) /* only allow this if writing */ return EPERM; switch (lkm_state) { case LKMS_LOADED: break; case LKMS_LOADING: /* The remainder must be bss, so we clear it */ bzero((caddr_t)curp->area + curp->offset, curp->size - curp->offset); break; default: #ifdef DEBUG printf("lkm_state is %02x\n", lkm_state); #endif /* DEBUG */ return ENXIO; } curp->entry = (int (*)()) (*((int *) (data))); /* call entry(load)... (assigns "private" portion) */ if (err = (*(curp->entry))(curp, LKM_E_LOAD, LKM_VERSION)) { /* * Module may refuse loading or may have a * version mismatch... */ lkm_state = LKMS_UNLOADING; /* for lkmunreserve */ lkmunreserve(); /* free memory */ curp->used = 0; /* free slot */ break; } curp->used = 1; #ifdef DEBUG printf("LKM: LMREADY\n"); #endif /* DEBUG */ lkm_state = LKMS_IDLE; break; case LMUNLOAD: /* unload a module */ if ((flag & FWRITE) == 0) /* only allow this if writing */ return EPERM; unloadp = (struct lmc_unload *)data; if ((i = unloadp->id) == -1) { /* unload by name */ /* * Copy name and lookup id from all loaded * modules. May fail. */ if (err = copyinstr(unloadp->name, istr, MAXLKMNAME-1, NULL)) break; /* * look up id... */ for (i = 0; i < MAXLKMS; i++) { if (!lkmods[i].used) continue; if (!strcmp(istr, lkmods[i].private.lkm_any->lkm_name)) break; } } /* * Range check the value; on failure, return EINVAL */ if (i < 0 || i >= MAXLKMS) { err = EINVAL; break; } curp = &lkmods[i]; if (!curp->used) { err = ENOENT; break; } /* call entry(unload) */ if ((*(curp->entry))(curp, LKM_E_UNLOAD, LKM_VERSION)) { err = EBUSY; break; } lkm_state = LKMS_UNLOADING; /* non-idle for lkmunreserve */ lkmunreserve(); /* free memory */ curp->used = 0; /* free slot */ break; case LMSTAT: /* stat a module by id/name */ /* allow readers and writers to stat */ statp = (struct lmc_stat *)data; if ((i = statp->id) == -1) { /* stat by name */ /* * Copy name and lookup id from all loaded * modules. */ copystr(statp->name, istr, MAXLKMNAME-1, NULL); /* * look up id... */ for (i = 0; i < MAXLKMS; i++) { if (!lkmods[i].used) continue; if (!strcmp(istr, lkmods[i].private.lkm_any->lkm_name)) break; } if (i == MAXLKMS) { /* Not found */ err = ENOENT; break; } } /* * Range check the value; on failure, return EINVAL */ if (i < 0 || i >= MAXLKMS) { err = EINVAL; break; } curp = &lkmods[i]; if (!curp->used) { /* Not found */ err = ENOENT; break; } /* * Copy out stat information for this module... */ statp->id = curp->id; statp->offset = curp->private.lkm_any->lkm_offset; statp->type = curp->private.lkm_any->lkm_type; statp->area = curp->area; statp->size = curp->size / PAGESIZE; statp->private = (unsigned long)curp->private.lkm_any; statp->ver = curp->private.lkm_any->lkm_ver; copystr(curp->private.lkm_any->lkm_name, statp->name, MAXLKMNAME - 2, NULL); break; default: /* bad ioctl()... */ err = ENOTTY; break; } return (err); } /* * Acts like "nosys" but can be identified in sysent for dynamic call * number assignment for a limited number of calls. * * Place holder for system call slots reserved for loadable modules. */ int lkmnosys() { return(nosys()); } /* * Acts like "enodev", but can be identified in cdevsw and bdevsw for * dynamic driver major number assignment for a limited number of * drivers. * * Place holder for device switch slots reserved for loadable modules. */ int lkmenodev() { return(enodev()); } int lkmexists(lkmtp) struct lkm_table *lkmtp; { int i; /* * see if name exists... */ for (i = 0; i < MAXLKMS; i++) { /* * An unused module and the one we are testing are not * considered. */ if (!lkmods[i].used || &lkmods[i] == lkmtp) continue; if (!strcmp(lkmtp->private.lkm_any->lkm_name, lkmods[i].private.lkm_any->lkm_name)) return(1); /* already loaded... */ } return(0); /* module not loaded... */ } /* * For the loadable system call described by the structure pointed to * by lkmtp, load/unload/stat it depending on the cmd requested. */ static int _lkm_syscall(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { struct lkm_syscall *args = lkmtp->private.lkm_syscall; int i; int err = 0; extern struct sysentvec aout_sysvec; switch(cmd) { case LKM_E_LOAD: /* don't load twice! */ if (lkmexists(lkmtp)) return(EEXIST); if ((i = args->lkm_offset) == -1) { /* auto */ /* * Search the table looking for a slot... */ for (i = 0; i < aout_sysvec.sv_size; i++) if (aout_sysvec.sv_table[i].sy_call == lkmnosys) break; /* found it! */ /* out of allocable slots? */ if (i == aout_sysvec.sv_size) { err = ENFILE; break; } } else { /* assign */ if (i < 0 || i >= aout_sysvec.sv_size) { err = EINVAL; break; } } /* save old */ bcopy(&aout_sysvec.sv_table[i], &(args->lkm_oldent), sizeof(struct sysent)); /* replace with new */ bcopy(args->lkm_sysent, &aout_sysvec.sv_table[i], sizeof(struct sysent)); /* done! */ args->lkm_offset = i; /* slot in sysent[] */ break; case LKM_E_UNLOAD: /* current slot... */ i = args->lkm_offset; /* replace current slot contents with old contents */ bcopy(&(args->lkm_oldent), &aout_sysvec.sv_table[i], sizeof(struct sysent)); break; case LKM_E_STAT: /* no special handling... */ break; } return(err); } /* * For the loadable virtual file system described by the structure pointed * to by lkmtp, load/unload/stat it depending on the cmd requested. */ static int _lkm_vfs(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { struct lkm_vfs *args = lkmtp->private.lkm_vfs; + struct vfsconf *vfc = args->lkm_vfsconf; + extern struct vfsconf void_vfsconf; int i; int err = 0; -#if 0 switch(cmd) { case LKM_E_LOAD: /* don't load twice! */ if (lkmexists(lkmtp)) return(EEXIST); - /* make sure there's no VFS in the table with this name */ - for (i = 0; i < nvfssw; i++) - if (vfssw[i] != (struct vfsops *)0 && - strncmp(vfssw[i]->vfs_name, - args->lkm_vfsops->vfs_name, MFSNAMELEN) == 0) - return (EEXIST); + for(i = 0; i < MOUNT_MAXTYPE; i++) { + if(!strcmp(vfc->vfc_name, vfsconf[i]->vfc_name)) { + return EEXIST; + } + } - /* pick the last available empty slot */ - for (i = nvfssw - 1; i >= 0; i--) - if (vfssw[i] == (struct vfsops *)0) - break; - if (i == -1) { /* or if none, punt */ - err = EINVAL; - break; + if (args->lkm_offset != vfc->vfc_index) + return EINVAL; + + i = args->lkm_offset; + if (!args->lkm_offset) { + for (i = MOUNT_MAXTYPE - 1; i >= 0; i--) { + if(vfsconf[i] == &void_vfsconf) + break; + } } + if (i < 0) { + return EINVAL; + } + args->lkm_offset = vfc->vfc_index = i; + vfsconf[i] = vfc; + vfssw[i] = vfc->vfc_vfsops; - /* - * Set up file system - */ - vfssw[i] = args->lkm_vfsops; + /* like in vfs_op_init */ + for(i = 0; args->lkm_vnodeops->ls_items[i]; i++) { + struct vnodeopv_desc *opv = + (struct vnodeopv_desc *)args->lkm_vnodeops->ls_items[i]; + *(opv->opv_desc_vector_p) = NULL; + } + vfs_opv_init((struct vnodeopv_desc **)args->lkm_vnodeops->ls_items); /* * Call init function for this VFS... */ - (*(vfssw[i]->vfs_init))(); + (*(vfssw[vfc->vfc_index]->vfs_init))(); /* done! */ args->lkm_offset = i; /* slot in vfssw[] */ break; case LKM_E_UNLOAD: -#ifdef notyet /* current slot... */ i = args->lkm_offset; + if (vfsconf[i]->vfc_refcount) { + return EBUSY; + } + /* replace current slot contents with old contents */ vfssw[i] = (struct vfsops *)0; -#else - /* it's not safe to remove a vfs */ - err = EBUSY; -#endif + vfsconf[i] = &void_vfsconf; + break; case LKM_E_STAT: /* no special handling... */ break; } -#else - err = EINVAL; -#endif return(err); } /* * For the loadable device driver described by the structure pointed to * by lkmtp, load/unload/stat it depending on the cmd requested. */ static int _lkm_dev(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { struct lkm_dev *args = lkmtp->private.lkm_dev; int i; int err = 0; switch(cmd) { case LKM_E_LOAD: /* don't load twice! */ if (lkmexists(lkmtp)) return(EEXIST); switch(args->lkm_devtype) { case LM_DT_BLOCK: if ((i = args->lkm_offset) == -1) { /* auto */ /* * Search the table looking for a slot... */ for (i = 0; i < nblkdev; i++) if (bdevsw[i].d_open == lkmenodev) break; /* found it! */ /* out of allocable slots? */ if (i == nblkdev) { err = ENFILE; break; } } else { /* assign */ if (i < 0 || i >= nblkdev) { err = EINVAL; break; } } /* save old */ bcopy(&bdevsw[i], &(args->lkm_olddev.bdev), sizeof(struct bdevsw)); /* replace with new */ bcopy(args->lkm_dev.bdev, &bdevsw[i], sizeof(struct bdevsw)); /* done! */ args->lkm_offset = i; /* slot in bdevsw[] */ break; case LM_DT_CHAR: if ((i = args->lkm_offset) == -1) { /* auto */ /* * Search the table looking for a slot... */ for (i = 0; i < nchrdev; i++) if (cdevsw[i].d_open == lkmenodev) break; /* found it! */ /* out of allocable slots? */ if (i == nchrdev) { err = ENFILE; break; } } else { /* assign */ if (i < 0 || i >= nchrdev) { err = EINVAL; break; } } /* save old */ bcopy(&cdevsw[i], &(args->lkm_olddev.cdev), sizeof(struct cdevsw)); /* replace with new */ bcopy(args->lkm_dev.cdev, &cdevsw[i], sizeof(struct cdevsw)); /* done! */ args->lkm_offset = i; /* slot in cdevsw[] */ break; default: err = ENODEV; break; } break; case LKM_E_UNLOAD: /* current slot... */ i = args->lkm_offset; switch(args->lkm_devtype) { case LM_DT_BLOCK: /* replace current slot contents with old contents */ bcopy(&(args->lkm_olddev.bdev), &bdevsw[i], sizeof(struct bdevsw)); break; case LM_DT_CHAR: /* replace current slot contents with old contents */ bcopy(&(args->lkm_olddev.cdev), &cdevsw[i], sizeof(struct cdevsw)); break; default: err = ENODEV; break; } break; case LKM_E_STAT: /* no special handling... */ break; } return(err); } #ifdef STREAMS /* * For the loadable streams module described by the structure pointed to * by lkmtp, load/unload/stat it depending on the cmd requested. */ static int _lkm_strmod(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { struct lkm_strmod *args = lkmtp->private.lkm_strmod; int i; int err = 0; switch(cmd) { case LKM_E_LOAD: /* don't load twice! */ if (lkmexists(lkmtp)) return(EEXIST); break; case LKM_E_UNLOAD: break; case LKM_E_STAT: /* no special handling... */ break; } return(err); } #endif /* STREAMS */ /* * For the loadable execution class described by the structure pointed to * by lkmtp, load/unload/stat it depending on the cmd requested. */ static int _lkm_exec(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { struct lkm_exec *args = lkmtp->private.lkm_exec; int i; int err = 0; #if 0 switch(cmd) { case LKM_E_LOAD: /* don't load twice! */ if (lkmexists(lkmtp)) return(EEXIST); if ((i = args->lkm_offset) == -1) { /* auto */ /* * Search the table looking for a slot... */ for (i = 0; i < nexecs; i++) if (execsw[i].es_check == NULL) break; /* found it! */ /* out of allocable slots? */ if (i == nexecs) { err = ENFILE; break; } } else { /* assign */ if (i < 0 || i >= nexecs) { err = EINVAL; break; } } /* save old */ bcopy(&execsw[i], &(args->lkm_oldexec), sizeof(struct execsw)); /* replace with new */ bcopy(args->lkm_exec, &execsw[i], sizeof(struct execsw)); /* realize need to recompute max header size */ exec_maxhdrsz = 0; /* done! */ args->lkm_offset = i; /* slot in execsw[] */ break; case LKM_E_UNLOAD: /* current slot... */ i = args->lkm_offset; /* replace current slot contents with old contents */ bcopy(&(args->lkm_oldexec), &execsw[i], sizeof(struct execsw)); /* realize need to recompute max header size */ exec_maxhdrsz = 0; break; case LKM_E_STAT: /* no special handling... */ break; } #else err = EINVAL; #endif return(err); } /* * This code handles the per-module type "wiring-in" of loadable modules * into existing kernel tables. For "LM_MISC" modules, wiring and unwiring * is assumed to be done in their entry routines internal to the module * itself. */ int lkmdispatch(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { int err = 0; /* default = success */ switch(lkmtp->private.lkm_any->lkm_type) { case LM_SYSCALL: err = _lkm_syscall(lkmtp, cmd); break; case LM_VFS: err = _lkm_vfs(lkmtp, cmd); break; case LM_DEV: err = _lkm_dev(lkmtp, cmd); break; #ifdef STREAMS case LM_STRMOD: { struct lkm_strmod *args = lkmtp->private.lkm_strmod; } break; #endif /* STREAMS */ case LM_EXEC: err = _lkm_exec(lkmtp, cmd); break; case LM_MISC: /* ignore content -- no "misc-specific" procedure */ break; default: err = ENXIO; /* unknown type */ break; } return(err); } Index: head/sys/kern/kern_sysctl.c =================================================================== --- head/sys/kern/kern_sysctl.c (revision 2945) +++ head/sys/kern/kern_sysctl.c (revision 2946) @@ -1,832 +1,830 @@ /*- * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Karels at Berkeley Software Design, 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. * * @(#)kern_sysctl.c 8.4 (Berkeley) 4/14/94 - * $Id: kern_sysctl.c,v 1.12 1994/09/18 20:39:48 wollman Exp $ + * $Id: kern_sysctl.c,v 1.13 1994/09/19 21:07:00 ache Exp $ */ /* * sysctl system call. */ #include #include #include #include #include #include #include #include #include #include #include #include #include sysctlfn kern_sysctl; sysctlfn hw_sysctl; #ifdef DEBUG sysctlfn debug_sysctl; #endif extern sysctlfn vm_sysctl; extern sysctlfn fs_sysctl; extern sysctlfn net_sysctl; extern sysctlfn cpu_sysctl; extern sysctlfn ntp_sysctl; /* * Locking and stats */ static struct sysctl_lock { int sl_lock; int sl_want; int sl_locked; } memlock; struct sysctl_args { int *name; u_int namelen; void *old; size_t *oldlenp; void *new; size_t newlen; }; int __sysctl(p, uap, retval) struct proc *p; register struct sysctl_args *uap; int *retval; { int error, dolock = 1; u_int savelen = 0, oldlen = 0; sysctlfn *fn; int name[CTL_MAXNAME]; if (uap->new != NULL && (error = suser(p->p_ucred, &p->p_acflag))) return (error); /* * all top-level sysctl names are non-terminal */ if (uap->namelen > CTL_MAXNAME || uap->namelen < 2) return (EINVAL); if (error = copyin(uap->name, &name, uap->namelen * sizeof(int))) return (error); switch (name[0]) { case CTL_KERN: fn = kern_sysctl; if (name[1] != KERN_VNODE) /* XXX */ dolock = 0; break; case CTL_HW: fn = hw_sysctl; break; case CTL_VM: fn = vm_sysctl; break; case CTL_NET: fn = net_sysctl; break; -#ifdef notyet case CTL_FS: fn = fs_sysctl; break; -#endif case CTL_MACHDEP: fn = cpu_sysctl; break; #ifdef DEBUG case CTL_DEBUG: fn = debug_sysctl; break; #endif default: return (EOPNOTSUPP); } if (uap->oldlenp && (error = copyin(uap->oldlenp, &oldlen, sizeof(oldlen)))) return (error); if (uap->old != NULL) { if (!useracc(uap->old, oldlen, B_WRITE)) return (EFAULT); while (memlock.sl_lock) { memlock.sl_want = 1; sleep((caddr_t)&memlock, PRIBIO+1); memlock.sl_locked++; } memlock.sl_lock = 1; if (dolock) vslock(uap->old, oldlen); savelen = oldlen; } error = (*fn)(name + 1, uap->namelen - 1, uap->old, &oldlen, uap->new, uap->newlen, p); if (uap->old != NULL) { if (dolock) vsunlock(uap->old, savelen, B_WRITE); memlock.sl_lock = 0; if (memlock.sl_want) { memlock.sl_want = 0; wakeup((caddr_t)&memlock); } } if (error) return (error); if (uap->oldlenp) error = copyout(&oldlen, uap->oldlenp, sizeof(oldlen)); *retval = oldlen; return (0); } /* * Attributes stored in the kernel. */ char hostname[MAXHOSTNAMELEN]; int hostnamelen; char domainname[MAXHOSTNAMELEN]; int domainnamelen; long hostid; int securelevel = -1; extern int vfs_update_wakeup; extern int vfs_update_interval; extern int osreldate; /* * kernel related system variables. */ int kern_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) int *name; u_int namelen; void *oldp; size_t *oldlenp; void *newp; size_t newlen; struct proc *p; { int error, level, inthostid; extern char ostype[], osrelease[]; /* all sysctl names at this level are terminal */ if (namelen != 1 && !(name[0] == KERN_PROC || name[0] == KERN_PROF || name[0] == KERN_NTP_PLL)) return (ENOTDIR); /* overloaded */ switch (name[0]) { case KERN_OSTYPE: return (sysctl_rdstring(oldp, oldlenp, newp, ostype)); case KERN_OSRELEASE: return (sysctl_rdstring(oldp, oldlenp, newp, osrelease)); case KERN_OSREV: return (sysctl_rdint(oldp, oldlenp, newp, BSD)); case KERN_VERSION: return (sysctl_rdstring(oldp, oldlenp, newp, version)); case KERN_OSRELDATE: return (sysctl_rdint(oldp, oldlenp, newp, osreldate)); case KERN_MAXVNODES: return(sysctl_int(oldp, oldlenp, newp, newlen, &desiredvnodes)); case KERN_MAXPROC: return (sysctl_int(oldp, oldlenp, newp, newlen, &maxproc)); case KERN_MAXFILES: return (sysctl_int(oldp, oldlenp, newp, newlen, &maxfiles)); case KERN_UPDATEINTERVAL: /* * NB: this simple-minded approach only works because * `tsleep' takes a timeout argument of 0 as meaning * `no timeout'. */ error = sysctl_int(oldp, oldlenp, newp, newlen, &vfs_update_interval); if(!error) { wakeup(&vfs_update_wakeup); } return error; case KERN_ARGMAX: return (sysctl_rdint(oldp, oldlenp, newp, ARG_MAX)); case KERN_SECURELVL: level = securelevel; if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &level)) || newp == NULL) return (error); if (level < securelevel && p->p_pid != 1) return (EPERM); securelevel = level; return (0); case KERN_HOSTNAME: error = sysctl_string(oldp, oldlenp, newp, newlen, hostname, sizeof(hostname)); if (newp && !error) hostnamelen = newlen; return (error); case KERN_DOMAINNAME: error = sysctl_string(oldp, oldlenp, newp, newlen, domainname, sizeof(domainname)); if (newp && !error) domainnamelen = newlen; return (error); case KERN_HOSTID: inthostid = hostid; /* XXX assumes sizeof long <= sizeof int */ error = sysctl_int(oldp, oldlenp, newp, newlen, &inthostid); hostid = inthostid; return (error); case KERN_CLOCKRATE: return (sysctl_clockrate(oldp, oldlenp)); case KERN_BOOTTIME: return (sysctl_rdstruct(oldp, oldlenp, newp, &boottime, sizeof(struct timeval))); case KERN_VNODE: return (sysctl_vnode(oldp, oldlenp)); case KERN_PROC: return (sysctl_doproc(name + 1, namelen - 1, oldp, oldlenp)); case KERN_FILE: return (sysctl_file(oldp, oldlenp)); #ifdef GPROF case KERN_PROF: return (sysctl_doprof(name + 1, namelen - 1, oldp, oldlenp, newp, newlen)); #endif case KERN_POSIX1: return (sysctl_rdint(oldp, oldlenp, newp, _POSIX_VERSION)); case KERN_NGROUPS: return (sysctl_rdint(oldp, oldlenp, newp, NGROUPS_MAX)); case KERN_JOB_CONTROL: return (sysctl_rdint(oldp, oldlenp, newp, 1)); case KERN_SAVED_IDS: #ifdef _POSIX_SAVED_IDS return (sysctl_rdint(oldp, oldlenp, newp, 1)); #else return (sysctl_rdint(oldp, oldlenp, newp, 0)); #endif case KERN_NTP_PLL: return (ntp_sysctl(name + 1, namelen - 1, oldp, oldlenp, newp, newlen, p)); default: return (EOPNOTSUPP); } /* NOTREACHED */ } /* * hardware related system variables. */ int hw_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) int *name; u_int namelen; void *oldp; size_t *oldlenp; void *newp; size_t newlen; struct proc *p; { extern char machine[], cpu_model[]; extern int hw_float; /* all sysctl names at this level are terminal */ if (namelen != 1) return (ENOTDIR); /* overloaded */ switch (name[0]) { case HW_MACHINE: return (sysctl_rdstring(oldp, oldlenp, newp, machine)); case HW_MODEL: return (sysctl_rdstring(oldp, oldlenp, newp, cpu_model)); case HW_NCPU: return (sysctl_rdint(oldp, oldlenp, newp, 1)); /* XXX */ case HW_BYTEORDER: return (sysctl_rdint(oldp, oldlenp, newp, BYTE_ORDER)); case HW_PHYSMEM: return (sysctl_rdint(oldp, oldlenp, newp, ctob(physmem))); case HW_USERMEM: return (sysctl_rdint(oldp, oldlenp, newp, ctob(physmem - cnt.v_wire_count))); case HW_PAGESIZE: return (sysctl_rdint(oldp, oldlenp, newp, PAGE_SIZE)); case HW_FLOATINGPT: return (sysctl_rdint(oldp, oldlenp, newp, hw_float)); default: return (EOPNOTSUPP); } /* NOTREACHED */ } #ifdef DEBUG /* * Debugging related system variables. */ struct ctldebug debug0, debug1, debug2, debug3, debug4; struct ctldebug debug5, debug6, debug7, debug8, debug9; struct ctldebug debug10, debug11, debug12, debug13, debug14; struct ctldebug debug15, debug16, debug17, debug18, debug19; static struct ctldebug *debugvars[CTL_DEBUG_MAXID] = { &debug0, &debug1, &debug2, &debug3, &debug4, &debug5, &debug6, &debug7, &debug8, &debug9, &debug10, &debug11, &debug12, &debug13, &debug14, &debug15, &debug16, &debug17, &debug18, &debug19, }; int debug_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) int *name; u_int namelen; void *oldp; size_t *oldlenp; void *newp; size_t newlen; struct proc *p; { struct ctldebug *cdp; /* all sysctl names at this level are name and field */ if (namelen != 2) return (ENOTDIR); /* overloaded */ cdp = debugvars[name[0]]; if (cdp->debugname == 0) return (EOPNOTSUPP); switch (name[1]) { case CTL_DEBUG_NAME: return (sysctl_rdstring(oldp, oldlenp, newp, cdp->debugname)); case CTL_DEBUG_VALUE: return (sysctl_int(oldp, oldlenp, newp, newlen, cdp->debugvar)); default: return (EOPNOTSUPP); } /* NOTREACHED */ } #endif /* DEBUG */ /* * Validate parameters and get old / set new parameters * for an integer-valued sysctl function. */ int sysctl_int(oldp, oldlenp, newp, newlen, valp) void *oldp; size_t *oldlenp; void *newp; size_t newlen; int *valp; { int error = 0; if (oldp && *oldlenp < sizeof(int)) return (ENOMEM); if (newp && newlen != sizeof(int)) return (EINVAL); *oldlenp = sizeof(int); if (oldp) error = copyout(valp, oldp, sizeof(int)); if (error == 0 && newp) error = copyin(newp, valp, sizeof(int)); return (error); } /* * As above, but read-only. */ int sysctl_rdint(oldp, oldlenp, newp, val) void *oldp; size_t *oldlenp; void *newp; int val; { int error = 0; if (oldp && *oldlenp < sizeof(int)) return (ENOMEM); if (newp) return (EPERM); *oldlenp = sizeof(int); if (oldp) error = copyout((caddr_t)&val, oldp, sizeof(int)); return (error); } /* * Validate parameters and get old / set new parameters * for a string-valued sysctl function. */ int sysctl_string(oldp, oldlenp, newp, newlen, str, maxlen) void *oldp; size_t *oldlenp; void *newp; size_t newlen; char *str; int maxlen; { int len, error = 0; len = strlen(str) + 1; if (oldp && *oldlenp < len) return (ENOMEM); if (newp && newlen >= maxlen) return (EINVAL); if (oldp) { *oldlenp = len; error = copyout(str, oldp, len); } if (error == 0 && newp) { error = copyin(newp, str, newlen); str[newlen] = 0; } return (error); } /* * As above, but read-only. */ int sysctl_rdstring(oldp, oldlenp, newp, str) void *oldp; size_t *oldlenp; void *newp; char *str; { int len, error = 0; len = strlen(str) + 1; if (oldp && *oldlenp < len) return (ENOMEM); if (newp) return (EPERM); *oldlenp = len; if (oldp) error = copyout(str, oldp, len); return (error); } /* * Validate parameters and get old / set new parameters * for a structure oriented sysctl function. */ int sysctl_struct(oldp, oldlenp, newp, newlen, sp, len) void *oldp; size_t *oldlenp; void *newp; size_t newlen; void *sp; int len; { int error = 0; if (oldp && *oldlenp < len) return (ENOMEM); if (newp && newlen > len) return (EINVAL); if (oldp) { *oldlenp = len; error = copyout(sp, oldp, len); } if (error == 0 && newp) error = copyin(newp, sp, len); return (error); } /* * Validate parameters and get old parameters * for a structure oriented sysctl function. */ int sysctl_rdstruct(oldp, oldlenp, newp, sp, len) void *oldp; size_t *oldlenp; void *newp, *sp; int len; { int error = 0; if (oldp && *oldlenp < len) return (ENOMEM); if (newp) return (EPERM); *oldlenp = len; if (oldp) error = copyout(sp, oldp, len); return (error); } /* * Get file structures. */ int sysctl_file(where, sizep) char *where; size_t *sizep; { int buflen, error; struct file *fp; char *start = where; buflen = *sizep; if (where == NULL) { /* * overestimate by 10 files */ *sizep = sizeof(filehead) + (nfiles + 10) * sizeof(struct file); return (0); } /* * first copyout filehead */ if (buflen < sizeof(filehead)) { *sizep = 0; return (0); } if (error = copyout((caddr_t)&filehead, where, sizeof(filehead))) return (error); buflen -= sizeof(filehead); where += sizeof(filehead); /* * followed by an array of file structures */ for (fp = filehead; fp != NULL; fp = fp->f_filef) { if (buflen < sizeof(struct file)) { *sizep = where - start; return (ENOMEM); } if (error = copyout((caddr_t)fp, where, sizeof (struct file))) return (error); buflen -= sizeof(struct file); where += sizeof(struct file); } *sizep = where - start; return (0); } /* * try over estimating by 5 procs */ #define KERN_PROCSLOP (5 * sizeof (struct kinfo_proc)) int sysctl_doproc(name, namelen, where, sizep) int *name; u_int namelen; char *where; size_t *sizep; { register struct proc *p; register struct kinfo_proc *dp = (struct kinfo_proc *)where; register int needed = 0; int buflen = where != NULL ? *sizep : 0; int doingzomb; struct eproc eproc; int error = 0; if (namelen != 2 && !(namelen == 1 && name[0] == KERN_PROC_ALL)) return (EINVAL); p = (struct proc *)allproc; doingzomb = 0; again: for (; p != NULL; p = p->p_next) { /* * Skip embryonic processes. */ if (p->p_stat == SIDL) continue; /* * TODO - make more efficient (see notes below). * do by session. */ switch (name[0]) { case KERN_PROC_PID: /* could do this with just a lookup */ if (p->p_pid != (pid_t)name[1]) continue; break; case KERN_PROC_PGRP: /* could do this by traversing pgrp */ if (p->p_pgrp->pg_id != (pid_t)name[1]) continue; break; case KERN_PROC_TTY: if ((p->p_flag & P_CONTROLT) == 0 || p->p_session->s_ttyp == NULL || p->p_session->s_ttyp->t_dev != (dev_t)name[1]) continue; break; case KERN_PROC_UID: if (p->p_ucred->cr_uid != (uid_t)name[1]) continue; break; case KERN_PROC_RUID: if (p->p_cred->p_ruid != (uid_t)name[1]) continue; break; } if (buflen >= sizeof(struct kinfo_proc)) { fill_eproc(p, &eproc); if (error = copyout((caddr_t)p, &dp->kp_proc, sizeof(struct proc))) return (error); if (error = copyout((caddr_t)&eproc, &dp->kp_eproc, sizeof(eproc))) return (error); dp++; buflen -= sizeof(struct kinfo_proc); } needed += sizeof(struct kinfo_proc); } if (doingzomb == 0) { p = zombproc; doingzomb++; goto again; } if (where != NULL) { *sizep = (caddr_t)dp - where; if (needed > *sizep) return (ENOMEM); } else { needed += KERN_PROCSLOP; *sizep = needed; } return (0); } /* * Fill in an eproc structure for the specified process. */ void fill_eproc(p, ep) register struct proc *p; register struct eproc *ep; { register struct tty *tp; ep->e_paddr = p; ep->e_sess = p->p_pgrp->pg_session; ep->e_pcred = *p->p_cred; ep->e_ucred = *p->p_ucred; if (p->p_stat == SIDL || p->p_stat == SZOMB) { ep->e_vm.vm_rssize = 0; ep->e_vm.vm_tsize = 0; ep->e_vm.vm_dsize = 0; ep->e_vm.vm_ssize = 0; #ifndef sparc /* ep->e_vm.vm_pmap = XXX; */ #endif } else { register struct vmspace *vm = p->p_vmspace; #ifdef pmap_resident_count ep->e_vm.vm_rssize = pmap_resident_count(&vm->vm_pmap); /*XXX*/ #else ep->e_vm.vm_rssize = vm->vm_rssize; #endif ep->e_vm.vm_tsize = vm->vm_tsize; ep->e_vm.vm_dsize = vm->vm_dsize; ep->e_vm.vm_ssize = vm->vm_ssize; #ifndef sparc ep->e_vm.vm_pmap = vm->vm_pmap; #endif } if (p->p_pptr) ep->e_ppid = p->p_pptr->p_pid; else ep->e_ppid = 0; ep->e_pgid = p->p_pgrp->pg_id; ep->e_jobc = p->p_pgrp->pg_jobc; if ((p->p_flag & P_CONTROLT) && (tp = ep->e_sess->s_ttyp)) { ep->e_tdev = tp->t_dev; ep->e_tpgid = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; ep->e_tsess = tp->t_session; } else ep->e_tdev = NODEV; ep->e_flag = ep->e_sess->s_ttyvp ? EPROC_CTTY : 0; if (SESS_LEADER(p)) ep->e_flag |= EPROC_SLEADER; if (p->p_wmesg) strncpy(ep->e_wmesg, p->p_wmesg, WMESGLEN); ep->e_xsize = ep->e_xrssize = 0; ep->e_xccount = ep->e_xswrss = 0; } #ifdef COMPAT_43 #include #define KINFO_PROC (0<<8) #define KINFO_RT (1<<8) #define KINFO_VNODE (2<<8) #define KINFO_FILE (3<<8) #define KINFO_METER (4<<8) #define KINFO_LOADAVG (5<<8) #define KINFO_CLOCKRATE (6<<8) struct getkerninfo_args { int op; char *where; int *size; int arg; }; int ogetkerninfo(p, uap, retval) struct proc *p; register struct getkerninfo_args *uap; int *retval; { int error, name[5]; u_int size; if (uap->size && (error = copyin((caddr_t)uap->size, (caddr_t)&size, sizeof(size)))) return (error); switch (uap->op & 0xff00) { case KINFO_RT: name[0] = PF_ROUTE; name[1] = 0; name[2] = (uap->op & 0xff0000) >> 16; name[3] = uap->op & 0xff; name[4] = uap->arg; error = net_sysctl(name, 5, uap->where, &size, NULL, 0, p); break; case KINFO_VNODE: name[0] = KERN_VNODE; error = kern_sysctl(name, 1, uap->where, &size, NULL, 0, p); break; case KINFO_PROC: name[0] = KERN_PROC; name[1] = uap->op & 0xff; name[2] = uap->arg; error = kern_sysctl(name, 3, uap->where, &size, NULL, 0, p); break; case KINFO_FILE: name[0] = KERN_FILE; error = kern_sysctl(name, 1, uap->where, &size, NULL, 0, p); break; case KINFO_METER: name[0] = VM_METER; error = vm_sysctl(name, 1, uap->where, &size, NULL, 0, p); break; case KINFO_LOADAVG: name[0] = VM_LOADAVG; error = vm_sysctl(name, 1, uap->where, &size, NULL, 0, p); break; case KINFO_CLOCKRATE: name[0] = KERN_CLOCKRATE; error = kern_sysctl(name, 1, uap->where, &size, NULL, 0, p); break; default: return (EOPNOTSUPP); } if (error) return (error); *retval = size; if (uap->size) error = copyout((caddr_t)&size, (caddr_t)uap->size, sizeof(size)); return (error); } #endif /* COMPAT_43 */ Index: head/sys/kern/vfs_conf.c =================================================================== --- head/sys/kern/vfs_conf.c (revision 2945) +++ head/sys/kern/vfs_conf.c (revision 2946) @@ -1,268 +1,52 @@ /* * 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. * * @(#)vfs_conf.c 8.8 (Berkeley) 3/31/94 - * $Id: vfs_conf.c,v 1.3 1994/08/20 16:03:12 davidg Exp $ + * $Id: vfs_conf.c,v 1.4 1994/09/19 15:41:13 dfr Exp $ */ #include #include #include #ifdef FFS #include /* * This specifies the filesystem used to mount the root. * This specification should be done by /etc/config. */ int (*mountroot)() = ffs_mountroot; #endif struct vnode *rootvnode; -/* - * Set up the filesystem operations for vnodes. - * The types are defined in mount.h. - */ -#ifdef FFS -extern struct vfsops ufs_vfsops; -#define UFS_VFSOPS &ufs_vfsops -#else -#define UFS_VFSOPS NULL -#endif - -#ifdef LFS -extern struct vfsops lfs_vfsops; -#define LFS_VFSOPS &lfs_vfsops -#else -#define LFS_VFSOPS NULL -#endif - -#ifdef MFS -extern struct vfsops mfs_vfsops; -#define MFS_VFSOPS &mfs_vfsops -#else -#define MFS_VFSOPS NULL -#endif - -#ifdef NFS -extern struct vfsops nfs_vfsops; -#define NFS_VFSOPS &nfs_vfsops -#else -#define NFS_VFSOPS NULL -#endif - -#ifdef FDESC -extern struct vfsops fdesc_vfsops; -#define FDESC_VFSOPS &fdesc_vfsops -#else -#define FDESC_VFSOPS NULL -#endif - -#ifdef PORTAL -extern struct vfsops portal_vfsops; -#define PORTAL_VFSOPS &portal_vfsops -#else -#define PORTAL_VFSOPS NULL -#endif - -#ifdef NULLFS -extern struct vfsops null_vfsops; -#define NULL_VFSOPS &null_vfsops -#else -#define NULL_VFSOPS NULL -#endif - -#ifdef UMAPFS -extern struct vfsops umap_vfsops; -#define UMAP_VFSOPS &umap_vfsops -#else -#define UMAP_VFSOPS NULL -#endif - -#ifdef KERNFS -extern struct vfsops kernfs_vfsops; -#define KERNFS_VFSOPS &kernfs_vfsops -#else -#define KERNFS_VFSOPS NULL -#endif - -#ifdef PROCFS -extern struct vfsops procfs_vfsops; -#define PROCFS_VFSOPS &procfs_vfsops -#else -#define PROCFS_VFSOPS NULL -#endif - -#ifdef AFS -extern struct vfsops afs_vfsops; -#define AFS_VFSOPS &afs_vfsops -#else -#define AFS_VFSOPS NULL -#endif - -#ifdef CD9660 -extern struct vfsops cd9660_vfsops; -#define CD9660_VFSOPS &cd9660_vfsops -#else -#define CD9660_VFSOPS NULL -#endif - -#ifdef MSDOSFS -extern struct vfsops msdosfs_vfsops; -#define MSDOSFS_VFSOPS &msdosfs_vfsops -#else -#define MSDOSFS_VFSOPS NULL -#endif - -#ifdef UNION -extern struct vfsops union_vfsops; -#define UNION_VFSOPS &union_vfsops -#else -#define UNION_VFSOPS NULL -#endif - -struct vfsops *vfssw[] = { - NULL, /* 0 = MOUNT_NONE */ - UFS_VFSOPS, /* 1 = MOUNT_UFS */ - NFS_VFSOPS, /* 2 = MOUNT_NFS */ - MFS_VFSOPS, /* 3 = MOUNT_MFS */ - MSDOSFS_VFSOPS, /* 4 = MOUNT_MSDOS */ - LFS_VFSOPS, /* 5 = MOUNT_LFS */ - NULL, /* 6 = MOUNT_LOFS */ - FDESC_VFSOPS, /* 7 = MOUNT_FDESC */ - PORTAL_VFSOPS, /* 8 = MOUNT_PORTAL */ - NULL_VFSOPS, /* 9 = MOUNT_NULL */ - UMAP_VFSOPS, /* 10 = MOUNT_UMAP */ - KERNFS_VFSOPS, /* 11 = MOUNT_KERNFS */ - PROCFS_VFSOPS, /* 12 = MOUNT_PROCFS */ - AFS_VFSOPS, /* 13 = MOUNT_AFS */ - CD9660_VFSOPS, /* 14 = MOUNT_CD9660 */ - UNION_VFSOPS, /* 15 = MOUNT_UNION */ - 0 -}; - - -/* - * - * vfs_opv_descs enumerates the list of vnode classes, each with it's own - * vnode operation vector. It is consulted at system boot to build operation - * vectors. It is NULL terminated. - * - */ -extern struct vnodeopv_desc ffs_vnodeop_opv_desc; -extern struct vnodeopv_desc ffs_specop_opv_desc; -extern struct vnodeopv_desc ffs_fifoop_opv_desc; -extern struct vnodeopv_desc lfs_vnodeop_opv_desc; -extern struct vnodeopv_desc lfs_specop_opv_desc; -extern struct vnodeopv_desc lfs_fifoop_opv_desc; -extern struct vnodeopv_desc mfs_vnodeop_opv_desc; -extern struct vnodeopv_desc dead_vnodeop_opv_desc; -extern struct vnodeopv_desc fifo_vnodeop_opv_desc; -extern struct vnodeopv_desc spec_vnodeop_opv_desc; -extern struct vnodeopv_desc nfsv2_vnodeop_opv_desc; -extern struct vnodeopv_desc spec_nfsv2nodeop_opv_desc; -extern struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc; -extern struct vnodeopv_desc fdesc_vnodeop_opv_desc; -extern struct vnodeopv_desc portal_vnodeop_opv_desc; -extern struct vnodeopv_desc null_vnodeop_opv_desc; -extern struct vnodeopv_desc umap_vnodeop_opv_desc; -extern struct vnodeopv_desc kernfs_vnodeop_opv_desc; -extern struct vnodeopv_desc procfs_vnodeop_opv_desc; -extern struct vnodeopv_desc cd9660_vnodeop_opv_desc; -extern struct vnodeopv_desc cd9660_specop_opv_desc; -extern struct vnodeopv_desc cd9660_fifoop_opv_desc; -extern struct vnodeopv_desc msdosfs_vnodeop_opv_desc; -extern struct vnodeopv_desc union_vnodeop_opv_desc; - -struct vnodeopv_desc *vfs_opv_descs[] = { - &ffs_vnodeop_opv_desc, - &ffs_specop_opv_desc, -#ifdef FIFO - &ffs_fifoop_opv_desc, -#endif - &dead_vnodeop_opv_desc, -#ifdef FIFO - &fifo_vnodeop_opv_desc, -#endif - &spec_vnodeop_opv_desc, -#ifdef LFS - &lfs_vnodeop_opv_desc, - &lfs_specop_opv_desc, -#ifdef FIFO - &lfs_fifoop_opv_desc, -#endif -#endif -#ifdef MFS - &mfs_vnodeop_opv_desc, -#endif -#ifdef NFS - &nfsv2_vnodeop_opv_desc, - &spec_nfsv2nodeop_opv_desc, -#ifdef FIFO - &fifo_nfsv2nodeop_opv_desc, -#endif -#endif -#ifdef FDESC - &fdesc_vnodeop_opv_desc, -#endif -#ifdef PORTAL - &portal_vnodeop_opv_desc, -#endif -#ifdef NULLFS - &null_vnodeop_opv_desc, -#endif -#ifdef UMAPFS - &umap_vnodeop_opv_desc, -#endif -#ifdef KERNFS - &kernfs_vnodeop_opv_desc, -#endif -#ifdef PROCFS - &procfs_vnodeop_opv_desc, -#endif -#ifdef CD9660 - &cd9660_vnodeop_opv_desc, - &cd9660_specop_opv_desc, -#ifdef FIFO - &cd9660_fifoop_opv_desc, -#endif -#endif -#ifdef MSDOSFS - &msdosfs_vnodeop_opv_desc, -#endif -#ifdef UNION - &union_vnodeop_opv_desc, -#endif - NULL -}; Index: head/sys/kern/vfs_extattr.c =================================================================== --- head/sys/kern/vfs_extattr.c (revision 2945) +++ head/sys/kern/vfs_extattr.c (revision 2946) @@ -1,2197 +1,2202 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94 - * $Id: vfs_syscalls.c,v 1.5 1994/09/02 04:14:44 davidg Exp $ + * $Id: vfs_syscalls.c,v 1.6 1994/09/02 10:23:43 davidg Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void cvtstat __P((struct stat *, struct ostat *)); static int change_dir __P((struct nameidata *ndp, struct proc *p)); /* * Virtual File System System Calls */ /* * Mount a file system. */ struct mount_args { int type; char *path; int flags; caddr_t data; }; /* ARGSUSED */ int mount(p, uap, retval) struct proc *p; register struct mount_args *uap; int *retval; { register struct vnode *vp; register struct mount *mp; int error, flag = 0; struct nameidata nd; /* * Must be super user */ if (error = suser(p->p_ucred, &p->p_acflag)) return (error); /* * Get vnode to be covered */ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (uap->flags & MNT_UPDATE) { if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } mp = vp->v_mount; flag = mp->mnt_flag; /* * We only allow the filesystem to be reloaded if it * is currently mounted read-only. */ if ((uap->flags & MNT_RELOAD) && ((mp->mnt_flag & MNT_RDONLY) == 0)) { vput(vp); return (EOPNOTSUPP); /* Needs translation */ } mp->mnt_flag |= uap->flags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE); VOP_UNLOCK(vp); goto update; } if (error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) return (error); if (vp->v_type != VDIR) { vput(vp); return (ENOTDIR); } if ((u_long)uap->type > MOUNT_MAXTYPE || vfssw[uap->type] == NULL) { vput(vp); return (ENODEV); } /* * Allocate and initialize the file system. */ mp = (struct mount *)malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); bzero((char *)mp, (u_long)sizeof(struct mount)); mp->mnt_op = vfssw[uap->type]; + mp->mnt_vfc = vfsconf[uap->type]; if (error = vfs_lock(mp)) { free((caddr_t)mp, M_MOUNT); vput(vp); return (error); } if (vp->v_mountedhere != NULL) { vfs_unlock(mp); free((caddr_t)mp, M_MOUNT); vput(vp); return (EBUSY); } vp->v_mountedhere = mp; mp->mnt_vnodecovered = vp; + vfsconf[uap->type]->vfc_refcount++; + update: /* * Set the mount level flags. */ if (uap->flags & MNT_RDONLY) mp->mnt_flag |= MNT_RDONLY; else if (mp->mnt_flag & MNT_RDONLY) mp->mnt_flag |= MNT_WANTRDWR; mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); mp->mnt_flag |= uap->flags & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); /* * Mount the filesystem. */ error = VFS_MOUNT(mp, uap->path, uap->data, &nd, p); if (mp->mnt_flag & MNT_UPDATE) { vrele(vp); if (mp->mnt_flag & MNT_WANTRDWR) mp->mnt_flag &= ~MNT_RDONLY; mp->mnt_flag &=~ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_WANTRDWR); if (error) mp->mnt_flag = flag; return (error); } /* * Put the new filesystem on the mount list after root. */ cache_purge(vp); if (!error) { TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); VOP_UNLOCK(vp); vfs_unlock(mp); error = VFS_START(mp, 0, p); } else { mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; vfs_unlock(mp); free((caddr_t)mp, M_MOUNT); vput(vp); + vfsconf[uap->type]->vfc_refcount--; } return (error); } /* * Unmount a file system. * * Note: unmount takes a path to the vnode mounted on as argument, * not special file (as before). */ struct unmount_args { char *path; int flags; }; /* ARGSUSED */ int unmount(p, uap, retval) struct proc *p; register struct unmount_args *uap; int *retval; { register struct vnode *vp; struct mount *mp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; /* * Unless this is a user mount, then must * have suser privilege. */ if (((vp->v_mount->mnt_flag & MNT_USER) == 0) && (error = suser(p->p_ucred, &p->p_acflag))) { vput(vp); return (error); } /* * Must be the root of the filesystem */ if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } mp = vp->v_mount; vput(vp); /* * Don't allow unmount of the root filesystem */ if (mp->mnt_flag & MNT_ROOTFS) return (EINVAL); return (dounmount(mp, uap->flags, p)); } /* * Do the actual file system unmount. */ int dounmount(mp, flags, p) register struct mount *mp; int flags; struct proc *p; { struct vnode *coveredvp; int error; coveredvp = mp->mnt_vnodecovered; if (vfs_busy(mp)) return (EBUSY); mp->mnt_flag |= MNT_UNMOUNT; if (error = vfs_lock(mp)) return (error); mp->mnt_flag &=~ MNT_ASYNC; vnode_pager_umount(mp); /* release cached vnodes */ cache_purgevfs(mp); /* remove cache entries for this file sys */ if ((error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0 || (flags & MNT_FORCE)) error = VFS_UNMOUNT(mp, flags, p); mp->mnt_flag &= ~MNT_UNMOUNT; vfs_unbusy(mp); if (error) { vfs_unlock(mp); } else { vrele(coveredvp); TAILQ_REMOVE(&mountlist, mp, mnt_list); mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; vfs_unlock(mp); + mp->mnt_vfc->vfc_refcount--; if (mp->mnt_vnodelist.lh_first != NULL) panic("unmount: dangling vnode"); free((caddr_t)mp, M_MOUNT); } return (error); } /* * Sync each mounted filesystem. */ #ifdef DIAGNOSTIC int syncprt = 0; struct ctldebug debug0 = { "syncprt", &syncprt }; #endif struct sync_args { int dummy; }; /* ARGSUSED */ int sync(p, uap, retval) struct proc *p; struct sync_args *uap; int *retval; { register struct mount *mp, *nmp; int asyncflag; for (mp = mountlist.tqh_first; mp != NULL; mp = nmp) { nmp = mp->mnt_list.tqe_next; /* * The lock check below is to avoid races with mount * and unmount. */ if ((mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_MPBUSY)) == 0 && !vfs_busy(mp)) { asyncflag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &= ~MNT_ASYNC; VFS_SYNC(mp, MNT_NOWAIT, p->p_ucred, p); if (asyncflag) mp->mnt_flag |= MNT_ASYNC; vfs_unbusy(mp); } } #ifdef DIAGNOSTIC if (syncprt) vfs_bufstats(); #endif /* DIAGNOSTIC */ return (0); } /* * Change filesystem quotas. */ struct quotactl_args { char *path; int cmd; int uid; caddr_t arg; }; /* ARGSUSED */ int quotactl(p, uap, retval) struct proc *p; register struct quotactl_args *uap; int *retval; { register struct mount *mp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); mp = nd.ni_vp->v_mount; vrele(nd.ni_vp); return (VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg, p)); } /* * Get filesystem statistics. */ struct statfs_args { char *path; struct statfs *buf; }; /* ARGSUSED */ int statfs(p, uap, retval) struct proc *p; register struct statfs_args *uap; int *retval; { register struct mount *mp; register struct statfs *sp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); mp = nd.ni_vp->v_mount; sp = &mp->mnt_stat; vrele(nd.ni_vp); if (error = VFS_STATFS(mp, sp, p)) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; return (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp))); } /* * Get filesystem statistics. */ struct fstatfs_args { int fd; struct statfs *buf; }; /* ARGSUSED */ int fstatfs(p, uap, retval) struct proc *p; register struct fstatfs_args *uap; int *retval; { struct file *fp; struct mount *mp; register struct statfs *sp; int error; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); mp = ((struct vnode *)fp->f_data)->v_mount; sp = &mp->mnt_stat; if (error = VFS_STATFS(mp, sp, p)) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; return (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp))); } /* * Get statistics on all filesystems. */ struct getfsstat_args { struct statfs *buf; long bufsize; int flags; }; int getfsstat(p, uap, retval) struct proc *p; register struct getfsstat_args *uap; int *retval; { register struct mount *mp, *nmp; register struct statfs *sp; caddr_t sfsp; long count, maxcount, error; maxcount = uap->bufsize / sizeof(struct statfs); sfsp = (caddr_t)uap->buf; for (count = 0, mp = mountlist.tqh_first; mp != NULL; mp = nmp) { nmp = mp->mnt_list.tqe_next; if (sfsp && count < maxcount && ((mp->mnt_flag & MNT_MLOCK) == 0)) { sp = &mp->mnt_stat; /* * If MNT_NOWAIT is specified, do not refresh the * fsstat cache. MNT_WAIT overrides MNT_NOWAIT. */ if (((uap->flags & MNT_NOWAIT) == 0 || (uap->flags & MNT_WAIT)) && (error = VFS_STATFS(mp, sp, p))) continue; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (error = copyout((caddr_t)sp, sfsp, sizeof(*sp))) return (error); sfsp += sizeof(*sp); } count++; } if (sfsp && count > maxcount) *retval = maxcount; else *retval = count; return (0); } /* * Change current working directory to a given file descriptor. */ struct fchdir_args { int fd; }; /* ARGSUSED */ int fchdir(p, uap, retval) struct proc *p; struct fchdir_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; register struct vnode *vp; struct file *fp; int error; if (error = getvnode(fdp, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; VOP_LOCK(vp); if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); VOP_UNLOCK(vp); if (error) return (error); VREF(vp); vrele(fdp->fd_cdir); fdp->fd_cdir = vp; return (0); } /* * Change current working directory (``.''). */ struct chdir_args { char *path; }; /* ARGSUSED */ int chdir(p, uap, retval) struct proc *p; struct chdir_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = change_dir(&nd, p)) return (error); vrele(fdp->fd_cdir); fdp->fd_cdir = nd.ni_vp; return (0); } /* * Change notion of root (``/'') directory. */ struct chroot_args { char *path; }; /* ARGSUSED */ int chroot(p, uap, retval) struct proc *p; struct chroot_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; if (error = suser(p->p_ucred, &p->p_acflag)) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = change_dir(&nd, p)) return (error); if (fdp->fd_rdir != NULL) vrele(fdp->fd_rdir); fdp->fd_rdir = nd.ni_vp; return (0); } /* * Common routine for chroot and chdir. */ static int change_dir(ndp, p) register struct nameidata *ndp; struct proc *p; { struct vnode *vp; int error; if (error = namei(ndp)) return (error); vp = ndp->ni_vp; if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); VOP_UNLOCK(vp); if (error) vrele(vp); return (error); } /* * Check permissions, allocate an open file structure, * and call the device open routine if any. */ struct open_args { char *path; int flags; int mode; }; int open(p, uap, retval) struct proc *p; register struct open_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; register struct file *fp; register struct vnode *vp; int flags, cmode; struct file *nfp; int type, indx, error; struct flock lf; struct nameidata nd; extern struct fileops vnops; if (error = falloc(p, &nfp, &indx)) return (error); fp = nfp; flags = FFLAGS(uap->flags); cmode = ((uap->mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); p->p_dupfd = -indx - 1; /* XXX check for fdopen */ if (error = vn_open(&nd, flags, cmode)) { ffree(fp); if ((error == ENODEV || error == ENXIO) && p->p_dupfd >= 0 && /* XXX from fdopen */ (error = dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) { *retval = indx; return (0); } if (error == ERESTART) error = EINTR; fdp->fd_ofiles[indx] = NULL; return (error); } p->p_dupfd = 0; vp = nd.ni_vp; fp->f_flag = flags & FMASK; fp->f_type = DTYPE_VNODE; fp->f_ops = &vnops; fp->f_data = (caddr_t)vp; if (flags & (O_EXLOCK | O_SHLOCK)) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (flags & O_EXLOCK) lf.l_type = F_WRLCK; else lf.l_type = F_RDLCK; type = F_FLOCK; if ((flags & FNONBLOCK) == 0) type |= F_WAIT; VOP_UNLOCK(vp); if (error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) { (void) vn_close(vp, fp->f_flag, fp->f_cred, p); ffree(fp); fdp->fd_ofiles[indx] = NULL; return (error); } VOP_LOCK(vp); fp->f_flag |= FHASLOCK; } VOP_UNLOCK(vp); *retval = indx; return (0); } #ifdef COMPAT_43 /* * Create a file. */ struct ocreat_args { char *path; int mode; }; int ocreat(p, uap, retval) struct proc *p; register struct ocreat_args *uap; int *retval; { struct open_args openuap; openuap.path = uap->path; openuap.mode = uap->mode; openuap.flags = O_WRONLY | O_CREAT | O_TRUNC; return (open(p, &openuap, retval)); } #endif /* COMPAT_43 */ /* * Create a special file. */ struct mknod_args { char *path; int mode; int dev; }; /* ARGSUSED */ int mknod(p, uap, retval) struct proc *p; register struct mknod_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; if (error = suser(p->p_ucred, &p->p_acflag)) return (error); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (vp != NULL) error = EEXIST; else { VATTR_NULL(&vattr); vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask; vattr.va_rdev = uap->dev; switch (uap->mode & S_IFMT) { case S_IFMT: /* used by badsect to flag bad sectors */ vattr.va_type = VBAD; break; case S_IFCHR: vattr.va_type = VCHR; break; case S_IFBLK: vattr.va_type = VBLK; break; default: error = EINVAL; break; } } if (!error) { LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (vp) vrele(vp); } return (error); } /* * Create named pipe. */ struct mkfifo_args { char *path; int mode; }; /* ARGSUSED */ int mkfifo(p, uap, retval) struct proc *p; register struct mkfifo_args *uap; int *retval; { struct vattr vattr; int error; struct nameidata nd; #ifndef FIFO return (EOPNOTSUPP); #else NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); if (nd.ni_vp != NULL) { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(nd.ni_vp); return (EEXIST); } VATTR_NULL(&vattr); vattr.va_type = VFIFO; vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask; LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr)); #endif /* FIFO */ } /* * Make a hard file link. */ struct link_args { char *path; char *link; }; /* ARGSUSED */ int link(p, uap, retval) struct proc *p; register struct link_args *uap; int *retval; { register struct vnode *vp; struct nameidata nd; int error; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (vp->v_type != VDIR || (error = suser(p->p_ucred, &p->p_acflag)) == 0) { nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT; nd.ni_dirp = uap->link; if ((error = namei(&nd)) == 0) { if (nd.ni_vp != NULL) error = EEXIST; if (!error) { LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vrele(nd.ni_vp); } } } vrele(vp); return (error); } /* * Make a symbolic link. */ struct symlink_args { char *path; char *link; }; /* ARGSUSED */ int symlink(p, uap, retval) struct proc *p; register struct symlink_args *uap; int *retval; { struct vattr vattr; char *path; int error; struct nameidata nd; MALLOC(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); if (error = copyinstr(uap->path, path, MAXPATHLEN, NULL)) goto out; NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->link, p); if (error = namei(&nd)) goto out; if (nd.ni_vp) { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(nd.ni_vp); error = EEXIST; goto out; } VATTR_NULL(&vattr); vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask; LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path); out: FREE(path, M_NAMEI); return (error); } /* * Delete a name from the filesystem. */ struct unlink_args { char *path; }; /* ARGSUSED */ int unlink(p, uap, retval) struct proc *p; struct unlink_args *uap; int *retval; { register struct vnode *vp; int error; struct nameidata nd; NDINIT(&nd, DELETE, LOCKPARENT, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_type != VDIR || (error = suser(p->p_ucred, &p->p_acflag)) == 0) { /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_flag & VROOT) error = EBUSY; else (void)vnode_pager_uncache(vp); } if (!error) { LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); } return (error); } /* * Reposition read/write file offset. */ struct lseek_args { int fd; int pad; off_t offset; int whence; }; int lseek(p, uap, retval) struct proc *p; register struct lseek_args *uap; int *retval; { struct ucred *cred = p->p_ucred; register struct filedesc *fdp = p->p_fd; register struct file *fp; struct vattr vattr; int error; if ((u_int)uap->fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[uap->fd]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE) return (ESPIPE); switch (uap->whence) { case L_INCR: fp->f_offset += uap->offset; break; case L_XTND: if (error = VOP_GETATTR((struct vnode *)fp->f_data, &vattr, cred, p)) return (error); fp->f_offset = uap->offset + vattr.va_size; break; case L_SET: fp->f_offset = uap->offset; break; default: return (EINVAL); } *(off_t *)retval = fp->f_offset; return (0); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Reposition read/write file offset. */ struct olseek_args { int fd; long offset; int whence; }; int olseek(p, uap, retval) struct proc *p; register struct olseek_args *uap; int *retval; { struct lseek_args nuap; off_t qret; int error; nuap.fd = uap->fd; nuap.offset = uap->offset; nuap.whence = uap->whence; error = lseek(p, &nuap, &qret); *(long *)retval = qret; return (error); } #endif /* COMPAT_43 */ /* * Check access permissions. */ struct access_args { char *path; int flags; }; int access(p, uap, retval) struct proc *p; register struct access_args *uap; int *retval; { register struct ucred *cred = p->p_ucred; register struct vnode *vp; int error, flags, t_gid, t_uid; struct nameidata nd; t_uid = cred->cr_uid; t_gid = cred->cr_groups[0]; cred->cr_uid = p->p_cred->p_ruid; cred->cr_groups[0] = p->p_cred->p_rgid; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) goto out1; vp = nd.ni_vp; /* Flags == 0 means only check for existence. */ if (uap->flags) { flags = 0; if (uap->flags & R_OK) flags |= VREAD; if (uap->flags & W_OK) flags |= VWRITE; if (uap->flags & X_OK) flags |= VEXEC; if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) error = VOP_ACCESS(vp, flags, cred, p); } vput(vp); out1: cred->cr_uid = t_uid; cred->cr_groups[0] = t_gid; return (error); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Get file status; this version follows links. */ struct ostat_args { char *path; struct ostat *ub; }; /* ARGSUSED */ int ostat(p, uap, retval) struct proc *p; register struct ostat_args *uap; int *retval; { struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); error = vn_stat(nd.ni_vp, &sb, p); vput(nd.ni_vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb)); return (error); } /* * Get file status; this version does not follow links. */ struct olstat_args { char *path; struct ostat *ub; }; /* ARGSUSED */ int olstat(p, uap, retval) struct proc *p; register struct olstat_args *uap; int *retval; { struct vnode *vp, *dvp; struct stat sb, sb1; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); /* * For symbolic links, always return the attributes of its * containing directory, except for mode, size, and links. */ vp = nd.ni_vp; dvp = nd.ni_dvp; if (vp->v_type != VLNK) { if (dvp == vp) vrele(dvp); else vput(dvp); error = vn_stat(vp, &sb, p); vput(vp); if (error) return (error); } else { error = vn_stat(dvp, &sb, p); vput(dvp); if (error) { vput(vp); return (error); } error = vn_stat(vp, &sb1, p); vput(vp); if (error) return (error); sb.st_mode &= ~S_IFDIR; sb.st_mode |= S_IFLNK; sb.st_nlink = sb1.st_nlink; sb.st_size = sb1.st_size; sb.st_blocks = sb1.st_blocks; } cvtstat(&sb, &osb); error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb)); return (error); } /* * Convert from an old to a new stat structure. */ void cvtstat(st, ost) struct stat *st; struct ostat *ost; { ost->st_dev = st->st_dev; ost->st_ino = st->st_ino; ost->st_mode = st->st_mode; ost->st_nlink = st->st_nlink; ost->st_uid = st->st_uid; ost->st_gid = st->st_gid; ost->st_rdev = st->st_rdev; if (st->st_size < (quad_t)1 << 32) ost->st_size = st->st_size; else ost->st_size = -2; ost->st_atime = st->st_atime; ost->st_mtime = st->st_mtime; ost->st_ctime = st->st_ctime; ost->st_blksize = st->st_blksize; ost->st_blocks = st->st_blocks; ost->st_flags = st->st_flags; ost->st_gen = st->st_gen; } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Get file status; this version follows links. */ struct stat_args { char *path; struct stat *ub; }; /* ARGSUSED */ int stat(p, uap, retval) struct proc *p; register struct stat_args *uap; int *retval; { struct stat sb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); error = vn_stat(nd.ni_vp, &sb, p); vput(nd.ni_vp); if (error) return (error); error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb)); return (error); } /* * Get file status; this version does not follow links. */ struct lstat_args { char *path; struct stat *ub; }; /* ARGSUSED */ int lstat(p, uap, retval) struct proc *p; register struct lstat_args *uap; int *retval; { int error; struct vnode *vp, *dvp; struct stat sb, sb1; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); /* * For symbolic links, always return the attributes of its * containing directory, except for mode, size, and links. */ vp = nd.ni_vp; dvp = nd.ni_dvp; if (vp->v_type != VLNK) { if (dvp == vp) vrele(dvp); else vput(dvp); error = vn_stat(vp, &sb, p); vput(vp); if (error) return (error); } else { error = vn_stat(dvp, &sb, p); vput(dvp); if (error) { vput(vp); return (error); } error = vn_stat(vp, &sb1, p); vput(vp); if (error) return (error); sb.st_mode &= ~S_IFDIR; sb.st_mode |= S_IFLNK; sb.st_nlink = sb1.st_nlink; sb.st_size = sb1.st_size; sb.st_blocks = sb1.st_blocks; } error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb)); return (error); } /* * Get configurable pathname variables. */ struct pathconf_args { char *path; int name; }; /* ARGSUSED */ int pathconf(p, uap, retval) struct proc *p; register struct pathconf_args *uap; int *retval; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); error = VOP_PATHCONF(nd.ni_vp, uap->name, retval); vput(nd.ni_vp); return (error); } /* * Return target name of a symbolic link. */ struct readlink_args { char *path; char *buf; int count; }; /* ARGSUSED */ int readlink(p, uap, retval) struct proc *p; register struct readlink_args *uap; int *retval; { register struct vnode *vp; struct iovec aiov; struct uio auio; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (vp->v_type != VLNK) error = EINVAL; else { aiov.iov_base = uap->buf; aiov.iov_len = uap->count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = uap->count; error = VOP_READLINK(vp, &auio, p->p_ucred); } vput(vp); *retval = uap->count - auio.uio_resid; return (error); } /* * Change flags of a file given a path name. */ struct chflags_args { char *path; int flags; }; /* ARGSUSED */ int chflags(p, uap, retval) struct proc *p; register struct chflags_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { VATTR_NULL(&vattr); vattr.va_flags = uap->flags; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); return (error); } /* * Change flags of a file given a file descriptor. */ struct fchflags_args { int fd; int flags; }; /* ARGSUSED */ int fchflags(p, uap, retval) struct proc *p; register struct fchflags_args *uap; int *retval; { struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { VATTR_NULL(&vattr); vattr.va_flags = uap->flags; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } VOP_UNLOCK(vp); return (error); } /* * Change mode of a file given path name. */ struct chmod_args { char *path; int mode; }; /* ARGSUSED */ int chmod(p, uap, retval) struct proc *p; register struct chmod_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { VATTR_NULL(&vattr); vattr.va_mode = uap->mode & ALLPERMS; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); return (error); } /* * Change mode of a file given a file descriptor. */ struct fchmod_args { int fd; int mode; }; /* ARGSUSED */ int fchmod(p, uap, retval) struct proc *p; register struct fchmod_args *uap; int *retval; { struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { VATTR_NULL(&vattr); vattr.va_mode = uap->mode & ALLPERMS; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } VOP_UNLOCK(vp); return (error); } /* * Set ownership given a path name. */ struct chown_args { char *path; int uid; int gid; }; /* ARGSUSED */ int chown(p, uap, retval) struct proc *p; register struct chown_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { VATTR_NULL(&vattr); vattr.va_uid = uap->uid; vattr.va_gid = uap->gid; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); return (error); } /* * Set ownership given a file descriptor. */ struct fchown_args { int fd; int uid; int gid; }; /* ARGSUSED */ int fchown(p, uap, retval) struct proc *p; register struct fchown_args *uap; int *retval; { struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { VATTR_NULL(&vattr); vattr.va_uid = uap->uid; vattr.va_gid = uap->gid; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } VOP_UNLOCK(vp); return (error); } /* * Set the access and modification times of a file. */ struct utimes_args { char *path; struct timeval *tptr; }; /* ARGSUSED */ int utimes(p, uap, retval) struct proc *p; register struct utimes_args *uap; int *retval; { register struct vnode *vp; struct timeval tv[2]; struct vattr vattr; int error; struct nameidata nd; VATTR_NULL(&vattr); if (uap->tptr == NULL) { microtime(&tv[0]); tv[1] = tv[0]; vattr.va_vaflags |= VA_UTIMES_NULL; } else if (error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv))) return (error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { vattr.va_atime.ts_sec = tv[0].tv_sec; vattr.va_atime.ts_nsec = tv[0].tv_usec * 1000; vattr.va_mtime.ts_sec = tv[1].tv_sec; vattr.va_mtime.ts_nsec = tv[1].tv_usec * 1000; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); return (error); } /* * Truncate a file given its path name. */ struct truncate_args { char *path; int pad; off_t length; }; /* ARGSUSED */ int truncate(p, uap, retval) struct proc *p; register struct truncate_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; if (uap->length < 0) return(EINVAL); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0 && (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0) { VATTR_NULL(&vattr); vattr.va_size = uap->length; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); return (error); } /* * Truncate a file given a file descriptor. */ struct ftruncate_args { int fd; int pad; off_t length; }; /* ARGSUSED */ int ftruncate(p, uap, retval) struct proc *p; register struct ftruncate_args *uap; int *retval; { struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (uap->length < 0) return(EINVAL); if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); if ((fp->f_flag & FWRITE) == 0) return (EINVAL); vp = (struct vnode *)fp->f_data; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0) { VATTR_NULL(&vattr); vattr.va_size = uap->length; error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); } VOP_UNLOCK(vp); return (error); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Truncate a file given its path name. */ struct otruncate_args { char *path; long length; }; /* ARGSUSED */ int otruncate(p, uap, retval) struct proc *p; register struct otruncate_args *uap; int *retval; { struct truncate_args nuap; nuap.path = uap->path; nuap.length = uap->length; return (truncate(p, &nuap, retval)); } /* * Truncate a file given a file descriptor. */ struct oftruncate_args { int fd; long length; }; /* ARGSUSED */ int oftruncate(p, uap, retval) struct proc *p; register struct oftruncate_args *uap; int *retval; { struct ftruncate_args nuap; nuap.fd = uap->fd; nuap.length = uap->length; return (ftruncate(p, &nuap, retval)); } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Sync an open file. */ struct fsync_args { int fd; }; /* ARGSUSED */ int fsync(p, uap, retval) struct proc *p; struct fsync_args *uap; int *retval; { register struct vnode *vp; struct file *fp; int error; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; VOP_LOCK(vp); error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p); VOP_UNLOCK(vp); return (error); } /* * Rename files. Source and destination must either both be directories, * or both not be directories. If target is a directory, it must be empty. */ struct rename_args { char *from; char *to; }; /* ARGSUSED */ int rename(p, uap, retval) struct proc *p; register struct rename_args *uap; int *retval; { register struct vnode *tvp, *fvp, *tdvp; struct nameidata fromnd, tond; int error; NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE, uap->from, p); if (error = namei(&fromnd)) return (error); fvp = fromnd.ni_vp; NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART, UIO_USERSPACE, uap->to, p); if (error = namei(&tond)) { VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; } tdvp = tond.ni_dvp; tvp = tond.ni_vp; if (tvp != NULL) { if (fvp->v_type == VDIR && tvp->v_type != VDIR) { error = ENOTDIR; goto out; } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { error = EISDIR; goto out; } } if (fvp == tdvp) error = EINVAL; /* * If source is the same as the destination (that is the * same inode number with the same name in the same directory), * then there is nothing to do. */ if (fvp == tvp && fromnd.ni_dvp == tdvp && fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen && !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr, fromnd.ni_cnd.cn_namelen)) error = -1; out: if (!error) { LEASE_CHECK(tdvp, p, p->p_ucred, LEASE_WRITE); if (fromnd.ni_dvp != tdvp) LEASE_CHECK(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); if (tvp) LEASE_CHECK(tvp, p, p->p_ucred, LEASE_WRITE); error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); } else { VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd); if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); vrele(fromnd.ni_dvp); vrele(fvp); } vrele(tond.ni_startdir); FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI); out1: if (fromnd.ni_startdir) vrele(fromnd.ni_startdir); FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); if (error == -1) return (0); return (error); } /* * Make a directory file. */ struct mkdir_args { char *path; int mode; }; /* ARGSUSED */ int mkdir(p, uap, retval) struct proc *p; register struct mkdir_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (vp != NULL) { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(vp); return (EEXIST); } VATTR_NULL(&vattr); vattr.va_type = VDIR; vattr.va_mode = (uap->mode & ACCESSPERMS) &~ p->p_fd->fd_cmask; LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (!error) vput(nd.ni_vp); return (error); } /* * Remove a directory file. */ struct rmdir_args { char *path; }; /* ARGSUSED */ int rmdir(p, uap, retval) struct proc *p; struct rmdir_args *uap; int *retval; { register struct vnode *vp; int error; struct nameidata nd; NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } /* * No rmdir "." please. */ if (nd.ni_dvp == vp) { error = EINVAL; goto out; } /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_flag & VROOT) error = EBUSY; out: if (!error) { LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); } return (error); } #ifdef COMPAT_43 /* * Read a block of directory entries in a file system independent format. */ struct ogetdirentries_args { int fd; char *buf; u_int count; long *basep; }; int ogetdirentries(p, uap, retval) struct proc *p; register struct ogetdirentries_args *uap; int *retval; { register struct vnode *vp; struct file *fp; struct uio auio, kuio; struct iovec aiov, kiov; struct dirent *dp, *edp; caddr_t dirbuf; int error, readcnt; long loff; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = uap->buf; aiov.iov_len = uap->count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = uap->count; VOP_LOCK(vp); loff = auio.uio_offset = fp->f_offset; # if (BYTE_ORDER != LITTLE_ENDIAN) if (vp->v_mount->mnt_maxsymlinklen <= 0) { error = VOP_READDIR(vp, &auio, fp->f_cred); fp->f_offset = auio.uio_offset; } else # endif { kuio = auio; kuio.uio_iov = &kiov; kuio.uio_segflg = UIO_SYSSPACE; kiov.iov_len = uap->count; MALLOC(dirbuf, caddr_t, uap->count, M_TEMP, M_WAITOK); kiov.iov_base = dirbuf; error = VOP_READDIR(vp, &kuio, fp->f_cred); fp->f_offset = kuio.uio_offset; if (error == 0) { readcnt = uap->count - kuio.uio_resid; edp = (struct dirent *)&dirbuf[readcnt]; for (dp = (struct dirent *)dirbuf; dp < edp; ) { # if (BYTE_ORDER == LITTLE_ENDIAN) /* * The expected low byte of * dp->d_namlen is our dp->d_type. * The high MBZ byte of dp->d_namlen * is our dp->d_namlen. */ dp->d_type = dp->d_namlen; dp->d_namlen = 0; # else /* * The dp->d_type is the high byte * of the expected dp->d_namlen, * so must be zero'ed. */ dp->d_type = 0; # endif if (dp->d_reclen > 0) { dp = (struct dirent *) ((char *)dp + dp->d_reclen); } else { error = EIO; break; } } if (dp >= edp) error = uiomove(dirbuf, readcnt, &auio); } FREE(dirbuf, M_TEMP); } VOP_UNLOCK(vp); if (error) return (error); error = copyout((caddr_t)&loff, (caddr_t)uap->basep, sizeof(long)); *retval = uap->count - auio.uio_resid; return (error); } #endif /* * Read a block of directory entries in a file system independent format. */ struct getdirentries_args { int fd; char *buf; u_int count; long *basep; }; int getdirentries(p, uap, retval) struct proc *p; register struct getdirentries_args *uap; int *retval; { register struct vnode *vp; struct file *fp; struct uio auio; struct iovec aiov; long loff; int error; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; unionread: if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = uap->buf; aiov.iov_len = uap->count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = uap->count; VOP_LOCK(vp); loff = auio.uio_offset = fp->f_offset; error = VOP_READDIR(vp, &auio, fp->f_cred); fp->f_offset = auio.uio_offset; VOP_UNLOCK(vp); if (error) return (error); #ifdef UNION { extern int (**union_vnodeop_p)(); extern struct vnode *union_lowervp __P((struct vnode *)); if ((uap->count == auio.uio_resid) && (vp->v_op == union_vnodeop_p)) { struct vnode *tvp = vp; vp = union_lowervp(vp); if (vp != NULLVP) { VOP_LOCK(vp); error = VOP_OPEN(vp, FREAD); VOP_UNLOCK(vp); if (error) { vrele(vp); return (error); } fp->f_data = (caddr_t) vp; fp->f_offset = 0; error = vn_close(tvp, FREAD, fp->f_cred, p); if (error) return (error); goto unionread; } } } #endif if ((uap->count == auio.uio_resid) && (vp->v_flag & VROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_data = (caddr_t) vp; fp->f_offset = 0; vrele(tvp); goto unionread; } error = copyout((caddr_t)&loff, (caddr_t)uap->basep, sizeof(long)); *retval = uap->count - auio.uio_resid; return (error); } /* * Set the mode mask for creation of filesystem nodes. */ struct umask_args { int newmask; }; mode_t /* XXX */ umask(p, uap, retval) struct proc *p; struct umask_args *uap; int *retval; { register struct filedesc *fdp; fdp = p->p_fd; *retval = fdp->fd_cmask; fdp->fd_cmask = uap->newmask & ALLPERMS; return (0); } /* * Void all references to file by ripping underlying filesystem * away from vnode. */ struct revoke_args { char *path; }; /* ARGSUSED */ int revoke(p, uap, retval) struct proc *p; register struct revoke_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (vp->v_type != VCHR && vp->v_type != VBLK) { error = EINVAL; goto out; } if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) goto out; if (p->p_ucred->cr_uid != vattr.va_uid && (error = suser(p->p_ucred, &p->p_acflag))) goto out; if (vp->v_usecount > 1 || (vp->v_flag & VALIASED)) vgoneall(vp); out: vrele(vp); return (error); } /* * Convert a user file descriptor to a kernel file entry. */ int getvnode(fdp, fd, fpp) struct filedesc *fdp; struct file **fpp; int fd; { struct file *fp; if ((u_int)fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE) return (EINVAL); *fpp = fp; return (0); } Index: head/sys/kern/vfs_init.c =================================================================== --- head/sys/kern/vfs_init.c (revision 2945) +++ head/sys/kern/vfs_init.c (revision 2946) @@ -1,249 +1,328 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed * to Berkeley by John Heidemann of the UCLA Ficus project. * * Source: * @(#)i405_init.c 2.10 92/04/27 UCLA Ficus project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vfs_init.c 8.3 (Berkeley) 1/4/94 - * $Id: vfs_init.c,v 1.3 1994/08/02 07:43:22 davidg Exp $ + * $Id: vfs_init.c,v 1.4 1994/08/18 22:35:08 wollman Exp $ */ #include #include +#include #include #include #include #include #include #include #include #include #include +#include +#include +#include /* * Sigh, such primitive tools are these... */ #if 0 #define DODEBUG(A) A #else #define DODEBUG(A) #endif -extern struct vnodeopv_desc *vfs_opv_descs[]; - /* a list of lists of vnodeops defns */ +struct vfsconf void_vfsconf; + +extern struct linker_set vfs_opv_descs_; +#define vfs_opv_descs ((struct vnodeopv_desc **)vfs_opv_descs_.ls_items) + +extern struct linker_set vfs_set; +struct vfsops *vfssw[MOUNT_MAXTYPE + 1]; +struct vfsconf *vfsconf[MOUNT_MAXTYPE + 1]; + extern struct vnodeop_desc *vfs_op_descs[]; /* and the operations they perform */ /* * This code doesn't work if the defn is **vnodop_defns with cc. * The problem is because of the compiler sometimes putting in an * extra level of indirection for arrays. It's an interesting * "feature" of C. */ int vfs_opv_numops; -typedef (*PFI)(); /* the standard Pointer to a Function returning an Int */ +typedef int (*PFI)(); /* the standard Pointer to a Function returning an Int */ /* * A miscellaneous routine. * A generic "default" routine that just returns an error. */ int vn_default_error() { return (EOPNOTSUPP); } /* * vfs_init.c * * Allocate and fill in operations vectors. * * An undocumented feature of this approach to defining operations is that * there can be multiple entries in vfs_opv_descs for the same operations * vector. This allows third parties to extend the set of operations * supported by another layer in a binary compatibile way. For example, * assume that NFS needed to be modified to support Ficus. NFS has an entry * (probably nfs_vnopdeop_decls) declaring all the operations NFS supports by * default. Ficus could add another entry (ficus_nfs_vnodeop_decl_entensions) * listing those new operations Ficus adds to NFS, all without modifying the * NFS code. (Of couse, the OTW NFS protocol still needs to be munged, but * that is a(whole)nother story.) This is a feature. */ void -vfs_opv_init() +vfs_opv_init(struct vnodeopv_desc **them) { int i, j, k; int (***opv_desc_vector_p)(); int (**opv_desc_vector)(); struct vnodeopv_entry_desc *opve_descp; /* * Allocate the dynamic vectors and fill them in. */ - for (i=0; vfs_opv_descs[i]; i++) { - opv_desc_vector_p = vfs_opv_descs[i]->opv_desc_vector_p; + for (i=0; them[i]; i++) { + opv_desc_vector_p = them[i]->opv_desc_vector_p; /* * Allocate and init the vector, if it needs it. * Also handle backwards compatibility. */ if (*opv_desc_vector_p == NULL) { /* XXX - shouldn't be M_VNODE */ MALLOC(*opv_desc_vector_p, PFI*, vfs_opv_numops*sizeof(PFI), M_VNODE, M_WAITOK); bzero (*opv_desc_vector_p, vfs_opv_numops*sizeof(PFI)); DODEBUG(printf("vector at %x allocated\n", opv_desc_vector_p)); } opv_desc_vector = *opv_desc_vector_p; - for (j=0; vfs_opv_descs[i]->opv_desc_ops[j].opve_op; j++) { - opve_descp = &(vfs_opv_descs[i]->opv_desc_ops[j]); + for (j=0; them[i]->opv_desc_ops[j].opve_op; j++) { + opve_descp = &(them[i]->opv_desc_ops[j]); /* * Sanity check: is this operation listed * in the list of operations? We check this * by seeing if its offest is zero. Since * the default routine should always be listed * first, it should be the only one with a zero * offset. Any other operation with a zero * offset is probably not listed in * vfs_op_descs, and so is probably an error. * * A panic here means the layer programmer * has committed the all-too common bug * of adding a new operation to the layer's * list of vnode operations but * not adding the operation to the system-wide * list of supported operations. */ if (opve_descp->opve_op->vdesc_offset == 0 && opve_descp->opve_op->vdesc_offset != VOFFSET(vop_default)) { printf("operation %s not listed in %s.\n", opve_descp->opve_op->vdesc_name, "vfs_op_descs"); panic ("vfs_opv_init: bad operation"); } /* * Fill in this entry. */ opv_desc_vector[opve_descp->opve_op->vdesc_offset] = opve_descp->opve_impl; } } /* * Finally, go back and replace unfilled routines * with their default. (Sigh, an O(n^3) algorithm. I * could make it better, but that'd be work, and n is small.) */ - for (i = 0; vfs_opv_descs[i]; i++) { - opv_desc_vector = *(vfs_opv_descs[i]->opv_desc_vector_p); + for (i = 0; them[i]; i++) { + opv_desc_vector = *(them[i]->opv_desc_vector_p); /* * Force every operations vector to have a default routine. */ if (opv_desc_vector[VOFFSET(vop_default)]==NULL) { panic("vfs_opv_init: operation vector without default routine."); } for (k = 0; kopv_desc_vector_p) = NULL; /* * Figure out how many ops there are by counting the table, * and assign each its offset. */ for (vfs_opv_numops = 0, i = 0; vfs_op_descs[i]; i++) { vfs_op_descs[i]->vdesc_offset = vfs_opv_numops; vfs_opv_numops++; } DODEBUG(printf ("vfs_opv_numops=%d\n", vfs_opv_numops)); } /* * Routines having to do with the management of the vnode table. */ extern struct vnodeops dead_vnodeops; extern struct vnodeops spec_vnodeops; extern void vclean(); struct vattr va_null; /* * Initialize the vnode structures and initialize each file system type. */ void vfsinit() { struct vfsops **vfsp; + struct vfsconf **vfc; + int i; /* + * Initialize the VFS switch table + */ + for(i = 0; i < MOUNT_MAXTYPE + 1; i++) { + vfsconf[i] = &void_vfsconf; + } + + vfc = (struct vfsconf **)vfs_set.ls_items; + while(*vfc) { + vfssw[(**vfc).vfc_index] = (**vfc).vfc_vfsops; + vfsconf[(**vfc).vfc_index] = *vfc; + vfc++; + } + + /* * Initialize the vnode table */ vntblinit(); /* * Initialize the vnode name cache */ nchinit(); /* * Build vnode operation vectors. */ vfs_op_init(); - vfs_opv_init(); /* finish the job */ + vfs_opv_init(vfs_opv_descs); /* finish the job */ /* * Initialize each file system type. */ vattr_null(&va_null); for (vfsp = &vfssw[0]; vfsp <= &vfssw[MOUNT_MAXTYPE]; vfsp++) { if (*vfsp == NULL) continue; (*(*vfsp)->vfs_init)(); } } + +/* + * kernel related system variables. + */ +int +fs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) + int *name; + u_int namelen; + void *oldp; + size_t *oldlenp; + void *newp; + size_t newlen; + struct proc *p; +{ + int i; + int error; + int buflen = *oldlenp; + caddr_t where = newp, start = newp; + + switch (name[0]) { + case FS_VFSCONF: + if (namelen != 1) return ENOTDIR; + + if (oldp == NULL) { + *oldlenp = (MOUNT_MAXTYPE+1) * sizeof(struct vfsconf); + return 0; + } + if (newp) { + return EINVAL; + } + + for(i = 0; i < MOUNT_MAXTYPE + 1; i++) { + if(buflen < sizeof *vfsconf[i]) { + *oldlenp = where - start; + return ENOMEM; + } + + if(error = copyout(vfsconf[i], where, + sizeof *vfsconf[i])) + return error; + where += sizeof *vfsconf[i]; + buflen -= sizeof *vfsconf[i]; + } + *oldlenp = where - start; + return 0; + + default: + return (EOPNOTSUPP); + } + /* NOTREACHED */ +} + Index: head/sys/kern/vfs_mount.c =================================================================== --- head/sys/kern/vfs_mount.c (revision 2945) +++ head/sys/kern/vfs_mount.c (revision 2946) @@ -1,268 +1,52 @@ /* * 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. * * @(#)vfs_conf.c 8.8 (Berkeley) 3/31/94 - * $Id: vfs_conf.c,v 1.3 1994/08/20 16:03:12 davidg Exp $ + * $Id: vfs_conf.c,v 1.4 1994/09/19 15:41:13 dfr Exp $ */ #include #include #include #ifdef FFS #include /* * This specifies the filesystem used to mount the root. * This specification should be done by /etc/config. */ int (*mountroot)() = ffs_mountroot; #endif struct vnode *rootvnode; -/* - * Set up the filesystem operations for vnodes. - * The types are defined in mount.h. - */ -#ifdef FFS -extern struct vfsops ufs_vfsops; -#define UFS_VFSOPS &ufs_vfsops -#else -#define UFS_VFSOPS NULL -#endif - -#ifdef LFS -extern struct vfsops lfs_vfsops; -#define LFS_VFSOPS &lfs_vfsops -#else -#define LFS_VFSOPS NULL -#endif - -#ifdef MFS -extern struct vfsops mfs_vfsops; -#define MFS_VFSOPS &mfs_vfsops -#else -#define MFS_VFSOPS NULL -#endif - -#ifdef NFS -extern struct vfsops nfs_vfsops; -#define NFS_VFSOPS &nfs_vfsops -#else -#define NFS_VFSOPS NULL -#endif - -#ifdef FDESC -extern struct vfsops fdesc_vfsops; -#define FDESC_VFSOPS &fdesc_vfsops -#else -#define FDESC_VFSOPS NULL -#endif - -#ifdef PORTAL -extern struct vfsops portal_vfsops; -#define PORTAL_VFSOPS &portal_vfsops -#else -#define PORTAL_VFSOPS NULL -#endif - -#ifdef NULLFS -extern struct vfsops null_vfsops; -#define NULL_VFSOPS &null_vfsops -#else -#define NULL_VFSOPS NULL -#endif - -#ifdef UMAPFS -extern struct vfsops umap_vfsops; -#define UMAP_VFSOPS &umap_vfsops -#else -#define UMAP_VFSOPS NULL -#endif - -#ifdef KERNFS -extern struct vfsops kernfs_vfsops; -#define KERNFS_VFSOPS &kernfs_vfsops -#else -#define KERNFS_VFSOPS NULL -#endif - -#ifdef PROCFS -extern struct vfsops procfs_vfsops; -#define PROCFS_VFSOPS &procfs_vfsops -#else -#define PROCFS_VFSOPS NULL -#endif - -#ifdef AFS -extern struct vfsops afs_vfsops; -#define AFS_VFSOPS &afs_vfsops -#else -#define AFS_VFSOPS NULL -#endif - -#ifdef CD9660 -extern struct vfsops cd9660_vfsops; -#define CD9660_VFSOPS &cd9660_vfsops -#else -#define CD9660_VFSOPS NULL -#endif - -#ifdef MSDOSFS -extern struct vfsops msdosfs_vfsops; -#define MSDOSFS_VFSOPS &msdosfs_vfsops -#else -#define MSDOSFS_VFSOPS NULL -#endif - -#ifdef UNION -extern struct vfsops union_vfsops; -#define UNION_VFSOPS &union_vfsops -#else -#define UNION_VFSOPS NULL -#endif - -struct vfsops *vfssw[] = { - NULL, /* 0 = MOUNT_NONE */ - UFS_VFSOPS, /* 1 = MOUNT_UFS */ - NFS_VFSOPS, /* 2 = MOUNT_NFS */ - MFS_VFSOPS, /* 3 = MOUNT_MFS */ - MSDOSFS_VFSOPS, /* 4 = MOUNT_MSDOS */ - LFS_VFSOPS, /* 5 = MOUNT_LFS */ - NULL, /* 6 = MOUNT_LOFS */ - FDESC_VFSOPS, /* 7 = MOUNT_FDESC */ - PORTAL_VFSOPS, /* 8 = MOUNT_PORTAL */ - NULL_VFSOPS, /* 9 = MOUNT_NULL */ - UMAP_VFSOPS, /* 10 = MOUNT_UMAP */ - KERNFS_VFSOPS, /* 11 = MOUNT_KERNFS */ - PROCFS_VFSOPS, /* 12 = MOUNT_PROCFS */ - AFS_VFSOPS, /* 13 = MOUNT_AFS */ - CD9660_VFSOPS, /* 14 = MOUNT_CD9660 */ - UNION_VFSOPS, /* 15 = MOUNT_UNION */ - 0 -}; - - -/* - * - * vfs_opv_descs enumerates the list of vnode classes, each with it's own - * vnode operation vector. It is consulted at system boot to build operation - * vectors. It is NULL terminated. - * - */ -extern struct vnodeopv_desc ffs_vnodeop_opv_desc; -extern struct vnodeopv_desc ffs_specop_opv_desc; -extern struct vnodeopv_desc ffs_fifoop_opv_desc; -extern struct vnodeopv_desc lfs_vnodeop_opv_desc; -extern struct vnodeopv_desc lfs_specop_opv_desc; -extern struct vnodeopv_desc lfs_fifoop_opv_desc; -extern struct vnodeopv_desc mfs_vnodeop_opv_desc; -extern struct vnodeopv_desc dead_vnodeop_opv_desc; -extern struct vnodeopv_desc fifo_vnodeop_opv_desc; -extern struct vnodeopv_desc spec_vnodeop_opv_desc; -extern struct vnodeopv_desc nfsv2_vnodeop_opv_desc; -extern struct vnodeopv_desc spec_nfsv2nodeop_opv_desc; -extern struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc; -extern struct vnodeopv_desc fdesc_vnodeop_opv_desc; -extern struct vnodeopv_desc portal_vnodeop_opv_desc; -extern struct vnodeopv_desc null_vnodeop_opv_desc; -extern struct vnodeopv_desc umap_vnodeop_opv_desc; -extern struct vnodeopv_desc kernfs_vnodeop_opv_desc; -extern struct vnodeopv_desc procfs_vnodeop_opv_desc; -extern struct vnodeopv_desc cd9660_vnodeop_opv_desc; -extern struct vnodeopv_desc cd9660_specop_opv_desc; -extern struct vnodeopv_desc cd9660_fifoop_opv_desc; -extern struct vnodeopv_desc msdosfs_vnodeop_opv_desc; -extern struct vnodeopv_desc union_vnodeop_opv_desc; - -struct vnodeopv_desc *vfs_opv_descs[] = { - &ffs_vnodeop_opv_desc, - &ffs_specop_opv_desc, -#ifdef FIFO - &ffs_fifoop_opv_desc, -#endif - &dead_vnodeop_opv_desc, -#ifdef FIFO - &fifo_vnodeop_opv_desc, -#endif - &spec_vnodeop_opv_desc, -#ifdef LFS - &lfs_vnodeop_opv_desc, - &lfs_specop_opv_desc, -#ifdef FIFO - &lfs_fifoop_opv_desc, -#endif -#endif -#ifdef MFS - &mfs_vnodeop_opv_desc, -#endif -#ifdef NFS - &nfsv2_vnodeop_opv_desc, - &spec_nfsv2nodeop_opv_desc, -#ifdef FIFO - &fifo_nfsv2nodeop_opv_desc, -#endif -#endif -#ifdef FDESC - &fdesc_vnodeop_opv_desc, -#endif -#ifdef PORTAL - &portal_vnodeop_opv_desc, -#endif -#ifdef NULLFS - &null_vnodeop_opv_desc, -#endif -#ifdef UMAPFS - &umap_vnodeop_opv_desc, -#endif -#ifdef KERNFS - &kernfs_vnodeop_opv_desc, -#endif -#ifdef PROCFS - &procfs_vnodeop_opv_desc, -#endif -#ifdef CD9660 - &cd9660_vnodeop_opv_desc, - &cd9660_specop_opv_desc, -#ifdef FIFO - &cd9660_fifoop_opv_desc, -#endif -#endif -#ifdef MSDOSFS - &msdosfs_vnodeop_opv_desc, -#endif -#ifdef UNION - &union_vnodeop_opv_desc, -#endif - NULL -}; Index: head/sys/kern/vfs_syscalls.c =================================================================== --- head/sys/kern/vfs_syscalls.c (revision 2945) +++ head/sys/kern/vfs_syscalls.c (revision 2946) @@ -1,2197 +1,2202 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94 - * $Id: vfs_syscalls.c,v 1.5 1994/09/02 04:14:44 davidg Exp $ + * $Id: vfs_syscalls.c,v 1.6 1994/09/02 10:23:43 davidg Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void cvtstat __P((struct stat *, struct ostat *)); static int change_dir __P((struct nameidata *ndp, struct proc *p)); /* * Virtual File System System Calls */ /* * Mount a file system. */ struct mount_args { int type; char *path; int flags; caddr_t data; }; /* ARGSUSED */ int mount(p, uap, retval) struct proc *p; register struct mount_args *uap; int *retval; { register struct vnode *vp; register struct mount *mp; int error, flag = 0; struct nameidata nd; /* * Must be super user */ if (error = suser(p->p_ucred, &p->p_acflag)) return (error); /* * Get vnode to be covered */ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (uap->flags & MNT_UPDATE) { if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } mp = vp->v_mount; flag = mp->mnt_flag; /* * We only allow the filesystem to be reloaded if it * is currently mounted read-only. */ if ((uap->flags & MNT_RELOAD) && ((mp->mnt_flag & MNT_RDONLY) == 0)) { vput(vp); return (EOPNOTSUPP); /* Needs translation */ } mp->mnt_flag |= uap->flags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE); VOP_UNLOCK(vp); goto update; } if (error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) return (error); if (vp->v_type != VDIR) { vput(vp); return (ENOTDIR); } if ((u_long)uap->type > MOUNT_MAXTYPE || vfssw[uap->type] == NULL) { vput(vp); return (ENODEV); } /* * Allocate and initialize the file system. */ mp = (struct mount *)malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); bzero((char *)mp, (u_long)sizeof(struct mount)); mp->mnt_op = vfssw[uap->type]; + mp->mnt_vfc = vfsconf[uap->type]; if (error = vfs_lock(mp)) { free((caddr_t)mp, M_MOUNT); vput(vp); return (error); } if (vp->v_mountedhere != NULL) { vfs_unlock(mp); free((caddr_t)mp, M_MOUNT); vput(vp); return (EBUSY); } vp->v_mountedhere = mp; mp->mnt_vnodecovered = vp; + vfsconf[uap->type]->vfc_refcount++; + update: /* * Set the mount level flags. */ if (uap->flags & MNT_RDONLY) mp->mnt_flag |= MNT_RDONLY; else if (mp->mnt_flag & MNT_RDONLY) mp->mnt_flag |= MNT_WANTRDWR; mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); mp->mnt_flag |= uap->flags & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); /* * Mount the filesystem. */ error = VFS_MOUNT(mp, uap->path, uap->data, &nd, p); if (mp->mnt_flag & MNT_UPDATE) { vrele(vp); if (mp->mnt_flag & MNT_WANTRDWR) mp->mnt_flag &= ~MNT_RDONLY; mp->mnt_flag &=~ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_WANTRDWR); if (error) mp->mnt_flag = flag; return (error); } /* * Put the new filesystem on the mount list after root. */ cache_purge(vp); if (!error) { TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); VOP_UNLOCK(vp); vfs_unlock(mp); error = VFS_START(mp, 0, p); } else { mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; vfs_unlock(mp); free((caddr_t)mp, M_MOUNT); vput(vp); + vfsconf[uap->type]->vfc_refcount--; } return (error); } /* * Unmount a file system. * * Note: unmount takes a path to the vnode mounted on as argument, * not special file (as before). */ struct unmount_args { char *path; int flags; }; /* ARGSUSED */ int unmount(p, uap, retval) struct proc *p; register struct unmount_args *uap; int *retval; { register struct vnode *vp; struct mount *mp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; /* * Unless this is a user mount, then must * have suser privilege. */ if (((vp->v_mount->mnt_flag & MNT_USER) == 0) && (error = suser(p->p_ucred, &p->p_acflag))) { vput(vp); return (error); } /* * Must be the root of the filesystem */ if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } mp = vp->v_mount; vput(vp); /* * Don't allow unmount of the root filesystem */ if (mp->mnt_flag & MNT_ROOTFS) return (EINVAL); return (dounmount(mp, uap->flags, p)); } /* * Do the actual file system unmount. */ int dounmount(mp, flags, p) register struct mount *mp; int flags; struct proc *p; { struct vnode *coveredvp; int error; coveredvp = mp->mnt_vnodecovered; if (vfs_busy(mp)) return (EBUSY); mp->mnt_flag |= MNT_UNMOUNT; if (error = vfs_lock(mp)) return (error); mp->mnt_flag &=~ MNT_ASYNC; vnode_pager_umount(mp); /* release cached vnodes */ cache_purgevfs(mp); /* remove cache entries for this file sys */ if ((error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0 || (flags & MNT_FORCE)) error = VFS_UNMOUNT(mp, flags, p); mp->mnt_flag &= ~MNT_UNMOUNT; vfs_unbusy(mp); if (error) { vfs_unlock(mp); } else { vrele(coveredvp); TAILQ_REMOVE(&mountlist, mp, mnt_list); mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; vfs_unlock(mp); + mp->mnt_vfc->vfc_refcount--; if (mp->mnt_vnodelist.lh_first != NULL) panic("unmount: dangling vnode"); free((caddr_t)mp, M_MOUNT); } return (error); } /* * Sync each mounted filesystem. */ #ifdef DIAGNOSTIC int syncprt = 0; struct ctldebug debug0 = { "syncprt", &syncprt }; #endif struct sync_args { int dummy; }; /* ARGSUSED */ int sync(p, uap, retval) struct proc *p; struct sync_args *uap; int *retval; { register struct mount *mp, *nmp; int asyncflag; for (mp = mountlist.tqh_first; mp != NULL; mp = nmp) { nmp = mp->mnt_list.tqe_next; /* * The lock check below is to avoid races with mount * and unmount. */ if ((mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_MPBUSY)) == 0 && !vfs_busy(mp)) { asyncflag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &= ~MNT_ASYNC; VFS_SYNC(mp, MNT_NOWAIT, p->p_ucred, p); if (asyncflag) mp->mnt_flag |= MNT_ASYNC; vfs_unbusy(mp); } } #ifdef DIAGNOSTIC if (syncprt) vfs_bufstats(); #endif /* DIAGNOSTIC */ return (0); } /* * Change filesystem quotas. */ struct quotactl_args { char *path; int cmd; int uid; caddr_t arg; }; /* ARGSUSED */ int quotactl(p, uap, retval) struct proc *p; register struct quotactl_args *uap; int *retval; { register struct mount *mp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); mp = nd.ni_vp->v_mount; vrele(nd.ni_vp); return (VFS_QUOTACTL(mp, uap->cmd, uap->uid, uap->arg, p)); } /* * Get filesystem statistics. */ struct statfs_args { char *path; struct statfs *buf; }; /* ARGSUSED */ int statfs(p, uap, retval) struct proc *p; register struct statfs_args *uap; int *retval; { register struct mount *mp; register struct statfs *sp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); mp = nd.ni_vp->v_mount; sp = &mp->mnt_stat; vrele(nd.ni_vp); if (error = VFS_STATFS(mp, sp, p)) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; return (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp))); } /* * Get filesystem statistics. */ struct fstatfs_args { int fd; struct statfs *buf; }; /* ARGSUSED */ int fstatfs(p, uap, retval) struct proc *p; register struct fstatfs_args *uap; int *retval; { struct file *fp; struct mount *mp; register struct statfs *sp; int error; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); mp = ((struct vnode *)fp->f_data)->v_mount; sp = &mp->mnt_stat; if (error = VFS_STATFS(mp, sp, p)) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; return (copyout((caddr_t)sp, (caddr_t)uap->buf, sizeof(*sp))); } /* * Get statistics on all filesystems. */ struct getfsstat_args { struct statfs *buf; long bufsize; int flags; }; int getfsstat(p, uap, retval) struct proc *p; register struct getfsstat_args *uap; int *retval; { register struct mount *mp, *nmp; register struct statfs *sp; caddr_t sfsp; long count, maxcount, error; maxcount = uap->bufsize / sizeof(struct statfs); sfsp = (caddr_t)uap->buf; for (count = 0, mp = mountlist.tqh_first; mp != NULL; mp = nmp) { nmp = mp->mnt_list.tqe_next; if (sfsp && count < maxcount && ((mp->mnt_flag & MNT_MLOCK) == 0)) { sp = &mp->mnt_stat; /* * If MNT_NOWAIT is specified, do not refresh the * fsstat cache. MNT_WAIT overrides MNT_NOWAIT. */ if (((uap->flags & MNT_NOWAIT) == 0 || (uap->flags & MNT_WAIT)) && (error = VFS_STATFS(mp, sp, p))) continue; sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (error = copyout((caddr_t)sp, sfsp, sizeof(*sp))) return (error); sfsp += sizeof(*sp); } count++; } if (sfsp && count > maxcount) *retval = maxcount; else *retval = count; return (0); } /* * Change current working directory to a given file descriptor. */ struct fchdir_args { int fd; }; /* ARGSUSED */ int fchdir(p, uap, retval) struct proc *p; struct fchdir_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; register struct vnode *vp; struct file *fp; int error; if (error = getvnode(fdp, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; VOP_LOCK(vp); if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); VOP_UNLOCK(vp); if (error) return (error); VREF(vp); vrele(fdp->fd_cdir); fdp->fd_cdir = vp; return (0); } /* * Change current working directory (``.''). */ struct chdir_args { char *path; }; /* ARGSUSED */ int chdir(p, uap, retval) struct proc *p; struct chdir_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = change_dir(&nd, p)) return (error); vrele(fdp->fd_cdir); fdp->fd_cdir = nd.ni_vp; return (0); } /* * Change notion of root (``/'') directory. */ struct chroot_args { char *path; }; /* ARGSUSED */ int chroot(p, uap, retval) struct proc *p; struct chroot_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; if (error = suser(p->p_ucred, &p->p_acflag)) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = change_dir(&nd, p)) return (error); if (fdp->fd_rdir != NULL) vrele(fdp->fd_rdir); fdp->fd_rdir = nd.ni_vp; return (0); } /* * Common routine for chroot and chdir. */ static int change_dir(ndp, p) register struct nameidata *ndp; struct proc *p; { struct vnode *vp; int error; if (error = namei(ndp)) return (error); vp = ndp->ni_vp; if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); VOP_UNLOCK(vp); if (error) vrele(vp); return (error); } /* * Check permissions, allocate an open file structure, * and call the device open routine if any. */ struct open_args { char *path; int flags; int mode; }; int open(p, uap, retval) struct proc *p; register struct open_args *uap; int *retval; { register struct filedesc *fdp = p->p_fd; register struct file *fp; register struct vnode *vp; int flags, cmode; struct file *nfp; int type, indx, error; struct flock lf; struct nameidata nd; extern struct fileops vnops; if (error = falloc(p, &nfp, &indx)) return (error); fp = nfp; flags = FFLAGS(uap->flags); cmode = ((uap->mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); p->p_dupfd = -indx - 1; /* XXX check for fdopen */ if (error = vn_open(&nd, flags, cmode)) { ffree(fp); if ((error == ENODEV || error == ENXIO) && p->p_dupfd >= 0 && /* XXX from fdopen */ (error = dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) { *retval = indx; return (0); } if (error == ERESTART) error = EINTR; fdp->fd_ofiles[indx] = NULL; return (error); } p->p_dupfd = 0; vp = nd.ni_vp; fp->f_flag = flags & FMASK; fp->f_type = DTYPE_VNODE; fp->f_ops = &vnops; fp->f_data = (caddr_t)vp; if (flags & (O_EXLOCK | O_SHLOCK)) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (flags & O_EXLOCK) lf.l_type = F_WRLCK; else lf.l_type = F_RDLCK; type = F_FLOCK; if ((flags & FNONBLOCK) == 0) type |= F_WAIT; VOP_UNLOCK(vp); if (error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) { (void) vn_close(vp, fp->f_flag, fp->f_cred, p); ffree(fp); fdp->fd_ofiles[indx] = NULL; return (error); } VOP_LOCK(vp); fp->f_flag |= FHASLOCK; } VOP_UNLOCK(vp); *retval = indx; return (0); } #ifdef COMPAT_43 /* * Create a file. */ struct ocreat_args { char *path; int mode; }; int ocreat(p, uap, retval) struct proc *p; register struct ocreat_args *uap; int *retval; { struct open_args openuap; openuap.path = uap->path; openuap.mode = uap->mode; openuap.flags = O_WRONLY | O_CREAT | O_TRUNC; return (open(p, &openuap, retval)); } #endif /* COMPAT_43 */ /* * Create a special file. */ struct mknod_args { char *path; int mode; int dev; }; /* ARGSUSED */ int mknod(p, uap, retval) struct proc *p; register struct mknod_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; if (error = suser(p->p_ucred, &p->p_acflag)) return (error); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (vp != NULL) error = EEXIST; else { VATTR_NULL(&vattr); vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask; vattr.va_rdev = uap->dev; switch (uap->mode & S_IFMT) { case S_IFMT: /* used by badsect to flag bad sectors */ vattr.va_type = VBAD; break; case S_IFCHR: vattr.va_type = VCHR; break; case S_IFBLK: vattr.va_type = VBLK; break; default: error = EINVAL; break; } } if (!error) { LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (vp) vrele(vp); } return (error); } /* * Create named pipe. */ struct mkfifo_args { char *path; int mode; }; /* ARGSUSED */ int mkfifo(p, uap, retval) struct proc *p; register struct mkfifo_args *uap; int *retval; { struct vattr vattr; int error; struct nameidata nd; #ifndef FIFO return (EOPNOTSUPP); #else NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); if (nd.ni_vp != NULL) { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(nd.ni_vp); return (EEXIST); } VATTR_NULL(&vattr); vattr.va_type = VFIFO; vattr.va_mode = (uap->mode & ALLPERMS) &~ p->p_fd->fd_cmask; LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr)); #endif /* FIFO */ } /* * Make a hard file link. */ struct link_args { char *path; char *link; }; /* ARGSUSED */ int link(p, uap, retval) struct proc *p; register struct link_args *uap; int *retval; { register struct vnode *vp; struct nameidata nd; int error; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (vp->v_type != VDIR || (error = suser(p->p_ucred, &p->p_acflag)) == 0) { nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT; nd.ni_dirp = uap->link; if ((error = namei(&nd)) == 0) { if (nd.ni_vp != NULL) error = EEXIST; if (!error) { LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); if (nd.ni_vp) vrele(nd.ni_vp); } } } vrele(vp); return (error); } /* * Make a symbolic link. */ struct symlink_args { char *path; char *link; }; /* ARGSUSED */ int symlink(p, uap, retval) struct proc *p; register struct symlink_args *uap; int *retval; { struct vattr vattr; char *path; int error; struct nameidata nd; MALLOC(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); if (error = copyinstr(uap->path, path, MAXPATHLEN, NULL)) goto out; NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->link, p); if (error = namei(&nd)) goto out; if (nd.ni_vp) { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(nd.ni_vp); error = EEXIST; goto out; } VATTR_NULL(&vattr); vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask; LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path); out: FREE(path, M_NAMEI); return (error); } /* * Delete a name from the filesystem. */ struct unlink_args { char *path; }; /* ARGSUSED */ int unlink(p, uap, retval) struct proc *p; struct unlink_args *uap; int *retval; { register struct vnode *vp; int error; struct nameidata nd; NDINIT(&nd, DELETE, LOCKPARENT, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_type != VDIR || (error = suser(p->p_ucred, &p->p_acflag)) == 0) { /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_flag & VROOT) error = EBUSY; else (void)vnode_pager_uncache(vp); } if (!error) { LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); } return (error); } /* * Reposition read/write file offset. */ struct lseek_args { int fd; int pad; off_t offset; int whence; }; int lseek(p, uap, retval) struct proc *p; register struct lseek_args *uap; int *retval; { struct ucred *cred = p->p_ucred; register struct filedesc *fdp = p->p_fd; register struct file *fp; struct vattr vattr; int error; if ((u_int)uap->fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[uap->fd]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE) return (ESPIPE); switch (uap->whence) { case L_INCR: fp->f_offset += uap->offset; break; case L_XTND: if (error = VOP_GETATTR((struct vnode *)fp->f_data, &vattr, cred, p)) return (error); fp->f_offset = uap->offset + vattr.va_size; break; case L_SET: fp->f_offset = uap->offset; break; default: return (EINVAL); } *(off_t *)retval = fp->f_offset; return (0); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Reposition read/write file offset. */ struct olseek_args { int fd; long offset; int whence; }; int olseek(p, uap, retval) struct proc *p; register struct olseek_args *uap; int *retval; { struct lseek_args nuap; off_t qret; int error; nuap.fd = uap->fd; nuap.offset = uap->offset; nuap.whence = uap->whence; error = lseek(p, &nuap, &qret); *(long *)retval = qret; return (error); } #endif /* COMPAT_43 */ /* * Check access permissions. */ struct access_args { char *path; int flags; }; int access(p, uap, retval) struct proc *p; register struct access_args *uap; int *retval; { register struct ucred *cred = p->p_ucred; register struct vnode *vp; int error, flags, t_gid, t_uid; struct nameidata nd; t_uid = cred->cr_uid; t_gid = cred->cr_groups[0]; cred->cr_uid = p->p_cred->p_ruid; cred->cr_groups[0] = p->p_cred->p_rgid; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) goto out1; vp = nd.ni_vp; /* Flags == 0 means only check for existence. */ if (uap->flags) { flags = 0; if (uap->flags & R_OK) flags |= VREAD; if (uap->flags & W_OK) flags |= VWRITE; if (uap->flags & X_OK) flags |= VEXEC; if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) error = VOP_ACCESS(vp, flags, cred, p); } vput(vp); out1: cred->cr_uid = t_uid; cred->cr_groups[0] = t_gid; return (error); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Get file status; this version follows links. */ struct ostat_args { char *path; struct ostat *ub; }; /* ARGSUSED */ int ostat(p, uap, retval) struct proc *p; register struct ostat_args *uap; int *retval; { struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); error = vn_stat(nd.ni_vp, &sb, p); vput(nd.ni_vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb)); return (error); } /* * Get file status; this version does not follow links. */ struct olstat_args { char *path; struct ostat *ub; }; /* ARGSUSED */ int olstat(p, uap, retval) struct proc *p; register struct olstat_args *uap; int *retval; { struct vnode *vp, *dvp; struct stat sb, sb1; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); /* * For symbolic links, always return the attributes of its * containing directory, except for mode, size, and links. */ vp = nd.ni_vp; dvp = nd.ni_dvp; if (vp->v_type != VLNK) { if (dvp == vp) vrele(dvp); else vput(dvp); error = vn_stat(vp, &sb, p); vput(vp); if (error) return (error); } else { error = vn_stat(dvp, &sb, p); vput(dvp); if (error) { vput(vp); return (error); } error = vn_stat(vp, &sb1, p); vput(vp); if (error) return (error); sb.st_mode &= ~S_IFDIR; sb.st_mode |= S_IFLNK; sb.st_nlink = sb1.st_nlink; sb.st_size = sb1.st_size; sb.st_blocks = sb1.st_blocks; } cvtstat(&sb, &osb); error = copyout((caddr_t)&osb, (caddr_t)uap->ub, sizeof (osb)); return (error); } /* * Convert from an old to a new stat structure. */ void cvtstat(st, ost) struct stat *st; struct ostat *ost; { ost->st_dev = st->st_dev; ost->st_ino = st->st_ino; ost->st_mode = st->st_mode; ost->st_nlink = st->st_nlink; ost->st_uid = st->st_uid; ost->st_gid = st->st_gid; ost->st_rdev = st->st_rdev; if (st->st_size < (quad_t)1 << 32) ost->st_size = st->st_size; else ost->st_size = -2; ost->st_atime = st->st_atime; ost->st_mtime = st->st_mtime; ost->st_ctime = st->st_ctime; ost->st_blksize = st->st_blksize; ost->st_blocks = st->st_blocks; ost->st_flags = st->st_flags; ost->st_gen = st->st_gen; } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Get file status; this version follows links. */ struct stat_args { char *path; struct stat *ub; }; /* ARGSUSED */ int stat(p, uap, retval) struct proc *p; register struct stat_args *uap; int *retval; { struct stat sb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); error = vn_stat(nd.ni_vp, &sb, p); vput(nd.ni_vp); if (error) return (error); error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb)); return (error); } /* * Get file status; this version does not follow links. */ struct lstat_args { char *path; struct stat *ub; }; /* ARGSUSED */ int lstat(p, uap, retval) struct proc *p; register struct lstat_args *uap; int *retval; { int error; struct vnode *vp, *dvp; struct stat sb, sb1; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); /* * For symbolic links, always return the attributes of its * containing directory, except for mode, size, and links. */ vp = nd.ni_vp; dvp = nd.ni_dvp; if (vp->v_type != VLNK) { if (dvp == vp) vrele(dvp); else vput(dvp); error = vn_stat(vp, &sb, p); vput(vp); if (error) return (error); } else { error = vn_stat(dvp, &sb, p); vput(dvp); if (error) { vput(vp); return (error); } error = vn_stat(vp, &sb1, p); vput(vp); if (error) return (error); sb.st_mode &= ~S_IFDIR; sb.st_mode |= S_IFLNK; sb.st_nlink = sb1.st_nlink; sb.st_size = sb1.st_size; sb.st_blocks = sb1.st_blocks; } error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb)); return (error); } /* * Get configurable pathname variables. */ struct pathconf_args { char *path; int name; }; /* ARGSUSED */ int pathconf(p, uap, retval) struct proc *p; register struct pathconf_args *uap; int *retval; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); error = VOP_PATHCONF(nd.ni_vp, uap->name, retval); vput(nd.ni_vp); return (error); } /* * Return target name of a symbolic link. */ struct readlink_args { char *path; char *buf; int count; }; /* ARGSUSED */ int readlink(p, uap, retval) struct proc *p; register struct readlink_args *uap; int *retval; { register struct vnode *vp; struct iovec aiov; struct uio auio; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (vp->v_type != VLNK) error = EINVAL; else { aiov.iov_base = uap->buf; aiov.iov_len = uap->count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = uap->count; error = VOP_READLINK(vp, &auio, p->p_ucred); } vput(vp); *retval = uap->count - auio.uio_resid; return (error); } /* * Change flags of a file given a path name. */ struct chflags_args { char *path; int flags; }; /* ARGSUSED */ int chflags(p, uap, retval) struct proc *p; register struct chflags_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { VATTR_NULL(&vattr); vattr.va_flags = uap->flags; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); return (error); } /* * Change flags of a file given a file descriptor. */ struct fchflags_args { int fd; int flags; }; /* ARGSUSED */ int fchflags(p, uap, retval) struct proc *p; register struct fchflags_args *uap; int *retval; { struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { VATTR_NULL(&vattr); vattr.va_flags = uap->flags; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } VOP_UNLOCK(vp); return (error); } /* * Change mode of a file given path name. */ struct chmod_args { char *path; int mode; }; /* ARGSUSED */ int chmod(p, uap, retval) struct proc *p; register struct chmod_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { VATTR_NULL(&vattr); vattr.va_mode = uap->mode & ALLPERMS; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); return (error); } /* * Change mode of a file given a file descriptor. */ struct fchmod_args { int fd; int mode; }; /* ARGSUSED */ int fchmod(p, uap, retval) struct proc *p; register struct fchmod_args *uap; int *retval; { struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { VATTR_NULL(&vattr); vattr.va_mode = uap->mode & ALLPERMS; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } VOP_UNLOCK(vp); return (error); } /* * Set ownership given a path name. */ struct chown_args { char *path; int uid; int gid; }; /* ARGSUSED */ int chown(p, uap, retval) struct proc *p; register struct chown_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { VATTR_NULL(&vattr); vattr.va_uid = uap->uid; vattr.va_gid = uap->gid; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); return (error); } /* * Set ownership given a file descriptor. */ struct fchown_args { int fd; int uid; int gid; }; /* ARGSUSED */ int fchown(p, uap, retval) struct proc *p; register struct fchown_args *uap; int *retval; { struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { VATTR_NULL(&vattr); vattr.va_uid = uap->uid; vattr.va_gid = uap->gid; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } VOP_UNLOCK(vp); return (error); } /* * Set the access and modification times of a file. */ struct utimes_args { char *path; struct timeval *tptr; }; /* ARGSUSED */ int utimes(p, uap, retval) struct proc *p; register struct utimes_args *uap; int *retval; { register struct vnode *vp; struct timeval tv[2]; struct vattr vattr; int error; struct nameidata nd; VATTR_NULL(&vattr); if (uap->tptr == NULL) { microtime(&tv[0]); tv[1] = tv[0]; vattr.va_vaflags |= VA_UTIMES_NULL; } else if (error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv))) return (error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_mount->mnt_flag & MNT_RDONLY) error = EROFS; else { vattr.va_atime.ts_sec = tv[0].tv_sec; vattr.va_atime.ts_nsec = tv[0].tv_usec * 1000; vattr.va_mtime.ts_sec = tv[1].tv_sec; vattr.va_mtime.ts_nsec = tv[1].tv_usec * 1000; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); return (error); } /* * Truncate a file given its path name. */ struct truncate_args { char *path; int pad; off_t length; }; /* ARGSUSED */ int truncate(p, uap, retval) struct proc *p; register struct truncate_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; if (uap->length < 0) return(EINVAL); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0 && (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0) { VATTR_NULL(&vattr); vattr.va_size = uap->length; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); return (error); } /* * Truncate a file given a file descriptor. */ struct ftruncate_args { int fd; int pad; off_t length; }; /* ARGSUSED */ int ftruncate(p, uap, retval) struct proc *p; register struct ftruncate_args *uap; int *retval; { struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (uap->length < 0) return(EINVAL); if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); if ((fp->f_flag & FWRITE) == 0) return (EINVAL); vp = (struct vnode *)fp->f_data; LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); VOP_LOCK(vp); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0) { VATTR_NULL(&vattr); vattr.va_size = uap->length; error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); } VOP_UNLOCK(vp); return (error); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Truncate a file given its path name. */ struct otruncate_args { char *path; long length; }; /* ARGSUSED */ int otruncate(p, uap, retval) struct proc *p; register struct otruncate_args *uap; int *retval; { struct truncate_args nuap; nuap.path = uap->path; nuap.length = uap->length; return (truncate(p, &nuap, retval)); } /* * Truncate a file given a file descriptor. */ struct oftruncate_args { int fd; long length; }; /* ARGSUSED */ int oftruncate(p, uap, retval) struct proc *p; register struct oftruncate_args *uap; int *retval; { struct ftruncate_args nuap; nuap.fd = uap->fd; nuap.length = uap->length; return (ftruncate(p, &nuap, retval)); } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Sync an open file. */ struct fsync_args { int fd; }; /* ARGSUSED */ int fsync(p, uap, retval) struct proc *p; struct fsync_args *uap; int *retval; { register struct vnode *vp; struct file *fp; int error; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); vp = (struct vnode *)fp->f_data; VOP_LOCK(vp); error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p); VOP_UNLOCK(vp); return (error); } /* * Rename files. Source and destination must either both be directories, * or both not be directories. If target is a directory, it must be empty. */ struct rename_args { char *from; char *to; }; /* ARGSUSED */ int rename(p, uap, retval) struct proc *p; register struct rename_args *uap; int *retval; { register struct vnode *tvp, *fvp, *tdvp; struct nameidata fromnd, tond; int error; NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE, uap->from, p); if (error = namei(&fromnd)) return (error); fvp = fromnd.ni_vp; NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART, UIO_USERSPACE, uap->to, p); if (error = namei(&tond)) { VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; } tdvp = tond.ni_dvp; tvp = tond.ni_vp; if (tvp != NULL) { if (fvp->v_type == VDIR && tvp->v_type != VDIR) { error = ENOTDIR; goto out; } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { error = EISDIR; goto out; } } if (fvp == tdvp) error = EINVAL; /* * If source is the same as the destination (that is the * same inode number with the same name in the same directory), * then there is nothing to do. */ if (fvp == tvp && fromnd.ni_dvp == tdvp && fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen && !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr, fromnd.ni_cnd.cn_namelen)) error = -1; out: if (!error) { LEASE_CHECK(tdvp, p, p->p_ucred, LEASE_WRITE); if (fromnd.ni_dvp != tdvp) LEASE_CHECK(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); if (tvp) LEASE_CHECK(tvp, p, p->p_ucred, LEASE_WRITE); error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); } else { VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd); if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); vrele(fromnd.ni_dvp); vrele(fvp); } vrele(tond.ni_startdir); FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI); out1: if (fromnd.ni_startdir) vrele(fromnd.ni_startdir); FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); if (error == -1) return (0); return (error); } /* * Make a directory file. */ struct mkdir_args { char *path; int mode; }; /* ARGSUSED */ int mkdir(p, uap, retval) struct proc *p; register struct mkdir_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (vp != NULL) { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vrele(vp); return (EEXIST); } VATTR_NULL(&vattr); vattr.va_type = VDIR; vattr.va_mode = (uap->mode & ACCESSPERMS) &~ p->p_fd->fd_cmask; LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (!error) vput(nd.ni_vp); return (error); } /* * Remove a directory file. */ struct rmdir_args { char *path; }; /* ARGSUSED */ int rmdir(p, uap, retval) struct proc *p; struct rmdir_args *uap; int *retval; { register struct vnode *vp; int error; struct nameidata nd; NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } /* * No rmdir "." please. */ if (nd.ni_dvp == vp) { error = EINVAL; goto out; } /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_flag & VROOT) error = EBUSY; out: if (!error) { LEASE_CHECK(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); LEASE_CHECK(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); } return (error); } #ifdef COMPAT_43 /* * Read a block of directory entries in a file system independent format. */ struct ogetdirentries_args { int fd; char *buf; u_int count; long *basep; }; int ogetdirentries(p, uap, retval) struct proc *p; register struct ogetdirentries_args *uap; int *retval; { register struct vnode *vp; struct file *fp; struct uio auio, kuio; struct iovec aiov, kiov; struct dirent *dp, *edp; caddr_t dirbuf; int error, readcnt; long loff; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = uap->buf; aiov.iov_len = uap->count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = uap->count; VOP_LOCK(vp); loff = auio.uio_offset = fp->f_offset; # if (BYTE_ORDER != LITTLE_ENDIAN) if (vp->v_mount->mnt_maxsymlinklen <= 0) { error = VOP_READDIR(vp, &auio, fp->f_cred); fp->f_offset = auio.uio_offset; } else # endif { kuio = auio; kuio.uio_iov = &kiov; kuio.uio_segflg = UIO_SYSSPACE; kiov.iov_len = uap->count; MALLOC(dirbuf, caddr_t, uap->count, M_TEMP, M_WAITOK); kiov.iov_base = dirbuf; error = VOP_READDIR(vp, &kuio, fp->f_cred); fp->f_offset = kuio.uio_offset; if (error == 0) { readcnt = uap->count - kuio.uio_resid; edp = (struct dirent *)&dirbuf[readcnt]; for (dp = (struct dirent *)dirbuf; dp < edp; ) { # if (BYTE_ORDER == LITTLE_ENDIAN) /* * The expected low byte of * dp->d_namlen is our dp->d_type. * The high MBZ byte of dp->d_namlen * is our dp->d_namlen. */ dp->d_type = dp->d_namlen; dp->d_namlen = 0; # else /* * The dp->d_type is the high byte * of the expected dp->d_namlen, * so must be zero'ed. */ dp->d_type = 0; # endif if (dp->d_reclen > 0) { dp = (struct dirent *) ((char *)dp + dp->d_reclen); } else { error = EIO; break; } } if (dp >= edp) error = uiomove(dirbuf, readcnt, &auio); } FREE(dirbuf, M_TEMP); } VOP_UNLOCK(vp); if (error) return (error); error = copyout((caddr_t)&loff, (caddr_t)uap->basep, sizeof(long)); *retval = uap->count - auio.uio_resid; return (error); } #endif /* * Read a block of directory entries in a file system independent format. */ struct getdirentries_args { int fd; char *buf; u_int count; long *basep; }; int getdirentries(p, uap, retval) struct proc *p; register struct getdirentries_args *uap; int *retval; { register struct vnode *vp; struct file *fp; struct uio auio; struct iovec aiov; long loff; int error; if (error = getvnode(p->p_fd, uap->fd, &fp)) return (error); if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; unionread: if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = uap->buf; aiov.iov_len = uap->count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = uap->count; VOP_LOCK(vp); loff = auio.uio_offset = fp->f_offset; error = VOP_READDIR(vp, &auio, fp->f_cred); fp->f_offset = auio.uio_offset; VOP_UNLOCK(vp); if (error) return (error); #ifdef UNION { extern int (**union_vnodeop_p)(); extern struct vnode *union_lowervp __P((struct vnode *)); if ((uap->count == auio.uio_resid) && (vp->v_op == union_vnodeop_p)) { struct vnode *tvp = vp; vp = union_lowervp(vp); if (vp != NULLVP) { VOP_LOCK(vp); error = VOP_OPEN(vp, FREAD); VOP_UNLOCK(vp); if (error) { vrele(vp); return (error); } fp->f_data = (caddr_t) vp; fp->f_offset = 0; error = vn_close(tvp, FREAD, fp->f_cred, p); if (error) return (error); goto unionread; } } } #endif if ((uap->count == auio.uio_resid) && (vp->v_flag & VROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_data = (caddr_t) vp; fp->f_offset = 0; vrele(tvp); goto unionread; } error = copyout((caddr_t)&loff, (caddr_t)uap->basep, sizeof(long)); *retval = uap->count - auio.uio_resid; return (error); } /* * Set the mode mask for creation of filesystem nodes. */ struct umask_args { int newmask; }; mode_t /* XXX */ umask(p, uap, retval) struct proc *p; struct umask_args *uap; int *retval; { register struct filedesc *fdp; fdp = p->p_fd; *retval = fdp->fd_cmask; fdp->fd_cmask = uap->newmask & ALLPERMS; return (0); } /* * Void all references to file by ripping underlying filesystem * away from vnode. */ struct revoke_args { char *path; }; /* ARGSUSED */ int revoke(p, uap, retval) struct proc *p; register struct revoke_args *uap; int *retval; { register struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p); if (error = namei(&nd)) return (error); vp = nd.ni_vp; if (vp->v_type != VCHR && vp->v_type != VBLK) { error = EINVAL; goto out; } if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) goto out; if (p->p_ucred->cr_uid != vattr.va_uid && (error = suser(p->p_ucred, &p->p_acflag))) goto out; if (vp->v_usecount > 1 || (vp->v_flag & VALIASED)) vgoneall(vp); out: vrele(vp); return (error); } /* * Convert a user file descriptor to a kernel file entry. */ int getvnode(fdp, fd, fpp) struct filedesc *fdp; struct file **fpp; int fd; { struct file *fp; if ((u_int)fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE) return (EINVAL); *fpp = fp; return (0); } Index: head/sys/miscfs/deadfs/dead_vnops.c =================================================================== --- head/sys/miscfs/deadfs/dead_vnops.c (revision 2945) +++ head/sys/miscfs/deadfs/dead_vnops.c (revision 2946) @@ -1,369 +1,372 @@ /* * 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$ + * $Id: dead_vnops.c,v 1.3 1994/08/02 07:44:51 davidg Exp $ */ #include #include #include +#include #include #include #include #include /* * Prototypes for dead operations on vnodes. */ int dead_badop(), dead_ebadf(); 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) 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) int dead_read __P((struct vop_read_args *)); int dead_write __P((struct vop_write_args *)); int dead_ioctl __P((struct vop_ioctl_args *)); int dead_select __P((struct vop_select_args *)); #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) int dead_lock __P((struct vop_lock_args *)); #define dead_unlock ((int (*) __P((struct vop_unlock_args *)))nullop) int dead_bmap __P((struct vop_bmap_args *)); int dead_strategy __P((struct vop_strategy_args *)); int dead_print __P((struct vop_print_args *)); #define dead_islocked ((int (*) __P((struct vop_islocked_args *)))nullop) #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) int (**dead_vnodeop_p)(); struct vnodeopv_entry_desc dead_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, dead_lookup }, /* lookup */ { &vop_create_desc, dead_create }, /* create */ { &vop_mknod_desc, dead_mknod }, /* mknod */ { &vop_open_desc, dead_open }, /* open */ { &vop_close_desc, dead_close }, /* close */ { &vop_access_desc, dead_access }, /* access */ { &vop_getattr_desc, dead_getattr }, /* getattr */ { &vop_setattr_desc, dead_setattr }, /* setattr */ { &vop_read_desc, dead_read }, /* read */ { &vop_write_desc, dead_write }, /* write */ { &vop_ioctl_desc, dead_ioctl }, /* ioctl */ { &vop_select_desc, dead_select }, /* select */ { &vop_mmap_desc, dead_mmap }, /* mmap */ { &vop_fsync_desc, dead_fsync }, /* fsync */ { &vop_seek_desc, dead_seek }, /* seek */ { &vop_remove_desc, dead_remove }, /* remove */ { &vop_link_desc, dead_link }, /* link */ { &vop_rename_desc, dead_rename }, /* rename */ { &vop_mkdir_desc, dead_mkdir }, /* mkdir */ { &vop_rmdir_desc, dead_rmdir }, /* rmdir */ { &vop_symlink_desc, dead_symlink }, /* symlink */ { &vop_readdir_desc, dead_readdir }, /* readdir */ { &vop_readlink_desc, dead_readlink }, /* readlink */ { &vop_abortop_desc, dead_abortop }, /* abortop */ { &vop_inactive_desc, dead_inactive }, /* inactive */ { &vop_reclaim_desc, dead_reclaim }, /* reclaim */ { &vop_lock_desc, dead_lock }, /* lock */ { &vop_unlock_desc, dead_unlock }, /* unlock */ { &vop_bmap_desc, dead_bmap }, /* bmap */ { &vop_strategy_desc, dead_strategy }, /* strategy */ { &vop_print_desc, dead_print }, /* print */ { &vop_islocked_desc, dead_islocked }, /* islocked */ { &vop_pathconf_desc, dead_pathconf }, /* pathconf */ { &vop_advlock_desc, dead_advlock }, /* advlock */ { &vop_blkatoff_desc, dead_blkatoff }, /* blkatoff */ { &vop_valloc_desc, dead_valloc }, /* valloc */ { &vop_vfree_desc, dead_vfree }, /* vfree */ { &vop_truncate_desc, dead_truncate }, /* truncate */ { &vop_update_desc, dead_update }, /* update */ { &vop_bwrite_desc, dead_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; 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 */ 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 */ 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 */ 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"); /* * Return EOF for character devices, EIO for others */ if (ap->a_vp->v_type != VCHR) return (EIO); return (0); } /* * Vnode op for write */ /* ARGSUSED */ 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 */ 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 */ 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 */ 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. */ int dead_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; } */ *ap; { if (!chkvnlock(ap->a_vp)) return (0); return (VCALL(ap->a_vp, VOFFSET(vop_lock), ap)); } /* * Wait until the vnode has finished changing state. */ 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; } */ *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)); } /* * Print out the contents of a dead vnode. */ /* ARGSUSED */ 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 */ int dead_ebadf() { return (EBADF); } /* * Empty vnode bad operation */ int dead_badop() { panic("dead_badop called"); /* NOTREACHED */ } /* * Empty vnode null operation */ int dead_nullop() { return (0); } /* * 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; sleep((caddr_t)vp, PINOD); locked = 1; } return (locked); } Index: head/sys/miscfs/fdesc/fdesc_vfsops.c =================================================================== --- head/sys/miscfs/fdesc/fdesc_vfsops.c (revision 2945) +++ head/sys/miscfs/fdesc/fdesc_vfsops.c (revision 2946) @@ -1,288 +1,291 @@ /* * 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_vfsops.c 8.4 (Berkeley) 1/21/94 * - * $Id: fdesc_vfsops.c,v 1.9 1993/04/06 15:28:33 jsp Exp $ + * $Id: fdesc_vfsops.c,v 1.1.1.1 1994/05/24 10:04:59 rgrimes Exp $ */ /* * /dev/fd Filesystem */ #include #include +#include #include #include #include #include #include #include #include #include #include #include /* * Mount the per-process file descriptors (/dev/fd) */ int fdesc_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { int error = 0; u_int size; struct fdescmount *fmp; struct vnode *rvp; /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) return (EOPNOTSUPP); error = fdesc_allocvp(Froot, FD_ROOT, mp, &rvp); if (error) return (error); MALLOC(fmp, struct fdescmount *, sizeof(struct fdescmount), M_UFSMNT, M_WAITOK); /* XXX */ rvp->v_type = VDIR; rvp->v_flag |= VROOT; fmp->f_root = rvp; /* XXX -- don't mark as local to work around fts() problems */ /*mp->mnt_flag |= MNT_LOCAL;*/ mp->mnt_data = (qaddr_t) fmp; getnewfsid(mp, MOUNT_FDESC); (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); bzero(mp->mnt_stat.f_mntfromname, MNAMELEN); bcopy("fdesc", mp->mnt_stat.f_mntfromname, sizeof("fdesc")); return (0); } int fdesc_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } int fdesc_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { int error; int flags = 0; extern int doforce; struct vnode *rootvp = VFSTOFDESC(mp)->f_root; if (mntflags & MNT_FORCE) { /* fdesc can never be rootfs so don't check for it */ if (!doforce) return (EINVAL); flags |= FORCECLOSE; } /* * Clear out buffer cache. I don't think we * ever get anything cached at this level at the * moment, but who knows... */ if (rootvp->v_usecount > 1) return (EBUSY); if (error = vflush(mp, rootvp, flags)) return (error); /* * Release reference on underlying root vnode */ vrele(rootvp); /* * And blow it away for future re-use */ vgone(rootvp); /* * Finally, throw away the fdescmount structure */ free(mp->mnt_data, M_UFSMNT); /* XXX */ mp->mnt_data = 0; return (0); } int fdesc_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct vnode *vp; /* * Return locked reference to root. */ vp = VFSTOFDESC(mp)->f_root; VREF(vp); VOP_LOCK(vp); *vpp = vp; return (0); } int fdesc_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } int fdesc_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { struct filedesc *fdp; int lim; int i; int last; int freefd; /* * Compute number of free file descriptors. * [ Strange results will ensue if the open file * limit is ever reduced below the current number * of open files... ] */ lim = p->p_rlimit[RLIMIT_NOFILE].rlim_cur; fdp = p->p_fd; last = min(fdp->fd_nfiles, lim); freefd = 0; for (i = fdp->fd_freefile; i < last; i++) if (fdp->fd_ofiles[i] == NULL) freefd++; /* * Adjust for the fact that the fdesc array may not * have been fully allocated yet. */ if (fdp->fd_nfiles < lim) freefd += (lim - fdp->fd_nfiles); sbp->f_type = MOUNT_FDESC; sbp->f_flags = 0; sbp->f_bsize = DEV_BSIZE; sbp->f_iosize = DEV_BSIZE; sbp->f_blocks = 2; /* 1K to keep df happy */ sbp->f_bfree = 0; sbp->f_bavail = 0; sbp->f_files = lim + 1; /* Allow for "." */ sbp->f_ffree = freefd; /* See comments above */ if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } int fdesc_sync(mp, waitfor) struct mount *mp; int waitfor; { return (0); } /* * Fdesc flat namespace lookup. * Currently unsupported. */ int fdesc_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } int fdesc_fhtovp(mp, fhp, setgen, vpp) struct mount *mp; struct fid *fhp; int setgen; struct vnode **vpp; { return (EOPNOTSUPP); } int fdesc_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (EOPNOTSUPP); } struct vfsops fdesc_vfsops = { fdesc_mount, fdesc_start, fdesc_unmount, fdesc_root, fdesc_quotactl, fdesc_statfs, fdesc_sync, fdesc_vget, fdesc_fhtovp, fdesc_vptofh, fdesc_init, }; + +VFS_SET(fdesc_vfsops, fdesc, MOUNT_FDESC, 0); Index: head/sys/miscfs/fdesc/fdesc_vnops.c =================================================================== --- head/sys/miscfs/fdesc/fdesc_vnops.c (revision 2945) +++ head/sys/miscfs/fdesc/fdesc_vnops.c (revision 2946) @@ -1,977 +1,979 @@ /* * 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.3 1994/09/09 13:23:20 davidg Exp $ + * $Id: fdesc_vnops.c,v 1.4 1994/09/09 13:24:26 davidg Exp $ */ /* * /dev/fd Filesystem */ #include #include #include #include #include #include /* boottime */ #include #include #include #include #include #include #include #include #include #include #include #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; 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) ((ix) & NFDCACHE-1) /* * Cache head */ struct fdcache { struct fdescnode *fc_forw; struct fdescnode *fc_back; }; static struct fdcache fdcache[NFDCACHE]; /* * Initialise cache headers */ int fdesc_init() { struct fdcache *fc; devctty = makedev(nchrdev, 0); for (fc = fdcache; fc < fdcache + NFDCACHE; fc++) fc->fc_forw = fc->fc_back = (struct fdescnode *) fc; return (0); } /* * Compute hash list for given target vnode */ static struct fdcache * fdesc_hash(ix) int ix; { return (&fdcache[FD_NHASH(ix)]); } int fdesc_allocvp(ftype, ix, mp, vpp) fdntype ftype; int ix; struct mount *mp; struct vnode **vpp; { struct fdcache *fc; struct fdescnode *fd; int error = 0; loop: fc = fdesc_hash(ix); for (fd = fc->fc_forw; fd != (struct fdescnode *) fc; fd = fd->fd_forw) { if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) { if (vget(fd->fd_vnode, 0)) 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; sleep((caddr_t) &fdcache_lock, PINOD); goto loop; } fdcache_lock |= FDL_LOCKED; error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp); if (error) goto out; MALLOC(fd, void *, sizeof(struct fdescnode), M_TEMP, M_WAITOK); (*vpp)->v_data = fd; fd->fd_vnode = *vpp; fd->fd_type = ftype; fd->fd_fd = -1; fd->fd_link = 0; fd->fd_ix = ix; fc = fdesc_hash(ix); insque(fd, fc); 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... */ 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; char *pname; struct proc *p; int nfiles; unsigned fd = 0; int error; struct vnode *fvp; char *ln; pname = ap->a_cnp->cn_nameptr; if (ap->a_cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); VOP_LOCK(dvp); return (0); } p = ap->a_cnp->cn_proc; nfiles = p->p_fd->fd_nfiles; switch (VTOFDESC(dvp)->fd_type) { default: case Flink: case Fdesc: case Fctty: error = ENOTDIR; goto bad; case Froot: if (ap->a_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; VOP_LOCK(fvp); return (0); } if (ap->a_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; VOP_LOCK(fvp); return (0); } ln = 0; switch (ap->a_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; VOP_LOCK(fvp); return (0); } else { error = ENOENT; goto bad; } /* FALL THROUGH */ case Fdevfd: if (ap->a_cnp->cn_namelen == 2 && bcmp(pname, "..", 2) == 0) { error = fdesc_root(dvp->v_mount, vpp); return (error); } 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; *vpp = fvp; return (0); } bad:; *vpp = NULL; return (error); } 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 = cttyopen(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_VNODE: error = VOP_GETATTR((struct vnode *) fp->f_data, vap, cred, p); if (error == 0 && vap->va_type == VDIR) { /* * don't allow directories to show up because * that causes loops in the namespace. */ vap->va_type = VFIFO; } 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); } 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.ts_sec = boottime.tv_sec; vap->va_atime.ts_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); } 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_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 } }; int fdesc_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { struct uio *uio = ap->a_uio; struct filedesc *fdp; int i; int error; 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); } 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); } 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 = cttyread(devctty, ap->a_uio, ap->a_ioflag); break; default: error = EOPNOTSUPP; break; } return (error); } 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 = cttywrite(devctty, ap->a_uio, ap->a_ioflag); break; default: error = EOPNOTSUPP; break; } return (error); } 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 = cttyioctl(devctty, ap->a_command, ap->a_data, ap->a_fflag, ap->a_p); break; default: error = EOPNOTSUPP; break; } return (error); } int fdesc_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; { int error = EOPNOTSUPP; switch (VTOFDESC(ap->a_vp)->fd_type) { case Fctty: error = cttyselect(devctty, ap->a_fflags, ap->a_p); break; default: error = EOPNOTSUPP; break; } return (error); } int fdesc_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; /* * Clear out the v_type field to avoid * nasty things happening in vgone(). */ vp->v_type = VNON; return (0); } int fdesc_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; remque(VTOFDESC(vp)); FREE(vp->v_data, M_TEMP); vp->v_data = 0; return (0); } /* * Return POSIX pathconf information applicable to special devices. */ 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 */ int fdesc_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_NON, fdesc vnode\n"); return (0); } /*void*/ int fdesc_vfree(ap) struct vop_vfree_args /* { struct vnode *a_pvp; ino_t a_ino; int a_mode; } */ *ap; { return (0); } /* * /dev/fd vnode unsupported operation */ int fdesc_enotsupp() { return (EOPNOTSUPP); } /* * /dev/fd "should never get here" operation */ int fdesc_badop() { panic("fdesc: bad op"); /* NOTREACHED */ } /* * /dev/fd vnode null operation */ int fdesc_nullop() { return (0); } #define fdesc_create ((int (*) __P((struct vop_create_args *)))fdesc_enotsupp) #define fdesc_mknod ((int (*) __P((struct vop_mknod_args *)))fdesc_enotsupp) #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 *)))fdesc_enotsupp) #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 *)))fdesc_enotsupp) #define fdesc_link ((int (*) __P((struct vop_link_args *)))fdesc_enotsupp) #define fdesc_rename ((int (*) __P((struct vop_rename_args *)))fdesc_enotsupp) #define fdesc_mkdir ((int (*) __P((struct vop_mkdir_args *)))fdesc_enotsupp) #define fdesc_rmdir ((int (*) __P((struct vop_rmdir_args *)))fdesc_enotsupp) #define fdesc_symlink ((int (*) __P((struct vop_symlink_args *)))fdesc_enotsupp) #define fdesc_abortop ((int (*) __P((struct vop_abortop_args *)))nullop) #define fdesc_lock ((int (*) __P((struct vop_lock_args *)))nullop) #define fdesc_unlock ((int (*) __P((struct vop_unlock_args *)))nullop) #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 *)))nullop) #define fdesc_advlock ((int (*) __P((struct vop_advlock_args *)))fdesc_enotsupp) #define fdesc_blkatoff \ ((int (*) __P((struct vop_blkatoff_args *)))fdesc_enotsupp) #define fdesc_vget ((int (*) __P((struct vop_vget_args *)))fdesc_enotsupp) #define fdesc_valloc ((int(*) __P(( \ struct vnode *pvp, \ int mode, \ struct ucred *cred, \ struct vnode **vpp))) fdesc_enotsupp) #define fdesc_truncate \ ((int (*) __P((struct vop_truncate_args *)))fdesc_enotsupp) #define fdesc_update ((int (*) __P((struct vop_update_args *)))fdesc_enotsupp) #define fdesc_bwrite ((int (*) __P((struct vop_bwrite_args *)))fdesc_enotsupp) int (**fdesc_vnodeop_p)(); struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, fdesc_lookup }, /* lookup */ { &vop_create_desc, fdesc_create }, /* create */ { &vop_mknod_desc, fdesc_mknod }, /* mknod */ { &vop_open_desc, fdesc_open }, /* open */ { &vop_close_desc, fdesc_close }, /* close */ { &vop_access_desc, fdesc_access }, /* access */ { &vop_getattr_desc, fdesc_getattr }, /* getattr */ { &vop_setattr_desc, fdesc_setattr }, /* setattr */ { &vop_read_desc, fdesc_read }, /* read */ { &vop_write_desc, fdesc_write }, /* write */ { &vop_ioctl_desc, fdesc_ioctl }, /* ioctl */ { &vop_select_desc, fdesc_select }, /* select */ { &vop_mmap_desc, fdesc_mmap }, /* mmap */ { &vop_fsync_desc, fdesc_fsync }, /* fsync */ { &vop_seek_desc, fdesc_seek }, /* seek */ { &vop_remove_desc, fdesc_remove }, /* remove */ { &vop_link_desc, fdesc_link }, /* link */ { &vop_rename_desc, fdesc_rename }, /* rename */ { &vop_mkdir_desc, fdesc_mkdir }, /* mkdir */ { &vop_rmdir_desc, fdesc_rmdir }, /* rmdir */ { &vop_symlink_desc, fdesc_symlink }, /* symlink */ { &vop_readdir_desc, fdesc_readdir }, /* readdir */ { &vop_readlink_desc, fdesc_readlink }, /* readlink */ { &vop_abortop_desc, fdesc_abortop }, /* abortop */ { &vop_inactive_desc, fdesc_inactive }, /* inactive */ { &vop_reclaim_desc, fdesc_reclaim }, /* reclaim */ { &vop_lock_desc, fdesc_lock }, /* lock */ { &vop_unlock_desc, fdesc_unlock }, /* unlock */ { &vop_bmap_desc, fdesc_bmap }, /* bmap */ { &vop_strategy_desc, fdesc_strategy }, /* strategy */ { &vop_print_desc, fdesc_print }, /* print */ { &vop_islocked_desc, fdesc_islocked }, /* islocked */ { &vop_pathconf_desc, fdesc_pathconf }, /* pathconf */ { &vop_advlock_desc, fdesc_advlock }, /* advlock */ { &vop_blkatoff_desc, fdesc_blkatoff }, /* blkatoff */ { &vop_valloc_desc, fdesc_valloc }, /* valloc */ { &vop_vfree_desc, fdesc_vfree }, /* vfree */ { &vop_truncate_desc, fdesc_truncate }, /* truncate */ { &vop_update_desc, fdesc_update }, /* update */ { &vop_bwrite_desc, fdesc_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc fdesc_vnodeop_opv_desc = { &fdesc_vnodeop_p, fdesc_vnodeop_entries }; + +VNODEOP_SET(fdesc_vnodeop_opv_desc); Index: head/sys/miscfs/fifofs/fifo_vnops.c =================================================================== --- head/sys/miscfs/fifofs/fifo_vnops.c (revision 2945) +++ head/sys/miscfs/fifofs/fifo_vnops.c (revision 2946) @@ -1,513 +1,515 @@ /* * 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. * * @(#)fifo_vnops.c 8.2 (Berkeley) 1/4/94 - * $Id$ + * $Id: fifo_vnops.c,v 1.3 1994/08/02 07:44:56 davidg 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; }; int (**fifo_vnodeop_p)(); struct vnodeopv_entry_desc fifo_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, fifo_lookup }, /* lookup */ { &vop_create_desc, fifo_create }, /* create */ { &vop_mknod_desc, fifo_mknod }, /* mknod */ { &vop_open_desc, fifo_open }, /* open */ { &vop_close_desc, fifo_close }, /* close */ { &vop_access_desc, fifo_access }, /* access */ { &vop_getattr_desc, fifo_getattr }, /* getattr */ { &vop_setattr_desc, fifo_setattr }, /* setattr */ { &vop_read_desc, fifo_read }, /* read */ { &vop_write_desc, fifo_write }, /* write */ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */ { &vop_select_desc, fifo_select }, /* select */ { &vop_mmap_desc, fifo_mmap }, /* mmap */ { &vop_fsync_desc, fifo_fsync }, /* fsync */ { &vop_seek_desc, fifo_seek }, /* seek */ { &vop_remove_desc, fifo_remove }, /* remove */ { &vop_link_desc, fifo_link }, /* link */ { &vop_rename_desc, fifo_rename }, /* rename */ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, fifo_symlink }, /* symlink */ { &vop_readdir_desc, fifo_readdir }, /* readdir */ { &vop_readlink_desc, fifo_readlink }, /* readlink */ { &vop_abortop_desc, fifo_abortop }, /* abortop */ { &vop_inactive_desc, fifo_inactive }, /* inactive */ { &vop_reclaim_desc, fifo_reclaim }, /* reclaim */ { &vop_lock_desc, fifo_lock }, /* lock */ { &vop_unlock_desc, fifo_unlock }, /* unlock */ { &vop_bmap_desc, fifo_bmap }, /* bmap */ { &vop_strategy_desc, fifo_strategy }, /* strategy */ { &vop_print_desc, fifo_print }, /* print */ { &vop_islocked_desc, fifo_islocked }, /* islocked */ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, fifo_valloc }, /* valloc */ { &vop_vfree_desc, fifo_vfree }, /* vfree */ { &vop_truncate_desc, fifo_truncate }, /* truncate */ { &vop_update_desc, fifo_update }, /* update */ { &vop_bwrite_desc, fifo_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; 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; { register struct vnode *vp = ap->a_vp; register struct fifoinfo *fip; struct socket *rso, *wso; int error; static char openstr[] = "fifo"; if ((ap->a_mode & (FREAD|FWRITE)) == (FREAD|FWRITE)) return (EINVAL); if ((fip = vp->v_fifoinfo) == NULL) { MALLOC(fip, struct fifoinfo *, sizeof(*fip), M_VNODE, M_WAITOK); vp->v_fifoinfo = fip; if (error = socreate(AF_UNIX, &rso, SOCK_STREAM, 0)) { free(fip, M_VNODE); vp->v_fifoinfo = NULL; return (error); } fip->fi_readsock = rso; if (error = socreate(AF_UNIX, &wso, SOCK_STREAM, 0)) { (void)soclose(rso); free(fip, M_VNODE); vp->v_fifoinfo = NULL; return (error); } fip->fi_writesock = wso; if (error = unp_connect2(wso, rso)) { (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; } error = 0; 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 & O_NONBLOCK) return (0); while (fip->fi_writers == 0) { VOP_UNLOCK(vp); error = tsleep((caddr_t)&fip->fi_readers, PCATCH | PSOCK, openstr, 0); VOP_LOCK(vp); if (error) break; } } else { fip->fi_writers++; if (fip->fi_readers == 0 && (ap->a_mode & O_NONBLOCK)) { error = ENXIO; } else { if (fip->fi_writers == 1) { fip->fi_readsock->so_state &= ~SS_CANTRCVMORE; if (fip->fi_readers > 0) wakeup((caddr_t)&fip->fi_readers); } while (fip->fi_readers == 0) { VOP_UNLOCK(vp); error = tsleep((caddr_t)&fip->fi_writers, PCATCH | PSOCK, openstr, 0); VOP_LOCK(vp); if (error) break; } } } if (error) VOP_CLOSE(vp, ap->a_mode, ap->a_cred, ap->a_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; { register struct uio *uio = ap->a_uio; register struct socket *rso = ap->a_vp->v_fifoinfo->fi_readsock; 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); error = soreceive(rso, (struct mbuf **)0, uio, (int *)0, (struct mbuf **)0, (struct mbuf **)0); VOP_LOCK(ap->a_vp); /* * 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; 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); error = sosend(wso, (struct mbuf *)0, ap->a_uio, 0, (struct mbuf *)0, 0); VOP_LOCK(ap->a_vp); 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; if (ap->a_command == FIONBIO) return (0); if (ap->a_fflag & FREAD) filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock; else filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock; return (soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p)); } /* ARGSUSED */ int fifo_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; { struct file filetmp; if (ap->a_fflags & FREAD) filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock; else filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock; return (soo_select(&filetmp, ap->a_which, ap->a_p)); } /* * 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; } */ *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; return (0); } /* * At the moment we do not do any locking. */ /* ARGSUSED */ int fifo_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; } */ *ap; { return (0); } /* ARGSUSED */ int fifo_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; } */ *ap; { 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 & FWRITE) { fip->fi_writers--; if (fip->fi_writers == 0) socantrcvmore(fip->fi_readsock); } else { fip->fi_readers--; if (fip->fi_readers == 0) socantsendmore(fip->fi_writesock); } 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 the contents of a fifo vnode. */ 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); } /* * 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 %d readers and %d writers", fip->fi_readers, fip->fi_writers); 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 */ 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 (EOPNOTSUPP); } /* * Fifo bad operation */ int fifo_badop() { panic("fifo_badop called"); /* NOTREACHED */ } Index: head/sys/miscfs/kernfs/kernfs_vfsops.c =================================================================== --- head/sys/miscfs/kernfs/kernfs_vfsops.c (revision 2945) +++ head/sys/miscfs/kernfs/kernfs_vfsops.c (revision 2946) @@ -1,342 +1,344 @@ /* * 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_vfsops.c 8.4 (Berkeley) 1/21/94 - * $Id$ + * $Id: kernfs_vfsops.c,v 1.3 1994/08/02 07:45:02 davidg Exp $ */ /* * Kernel params Filesystem */ #include #include #include #include #include #include #include #include #include #include #include struct vnode *rrootvp; /* * Create a vnode for a character device. */ int cdevvp(dev, vpp) dev_t dev; struct vnode **vpp; { register struct vnode *vp; struct vnode *nvp; int error; if (dev == NODEV) return (0); error = getnewvnode(VT_NON, (struct mount *)0, spec_vnodeop_p, &nvp); if (error) { *vpp = 0; return (error); } vp = nvp; vp->v_type = VCHR; if (nvp = checkalias(vp, dev, (struct mount *)0)) { vput(vp); vp = nvp; } *vpp = vp; return (0); } int kernfs_init() { int cmaj; int bmaj = major(rootdev); int error = ENXIO; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_init\n"); /* printed during system boot */ #endif for (cmaj = 0; cmaj < nchrdev; cmaj++) { if (cdevsw[cmaj].d_open == bdevsw[bmaj].d_open) { dev_t cdev = makedev(cmaj, minor(rootdev)); error = cdevvp(cdev, &rrootvp); if (error == 0) break; } } if (error) { printf("kernfs: no raw boot device\n"); rrootvp = 0; } return (0); } /* * Mount the Kernel params filesystem */ int kernfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { int error = 0; u_int size; struct kernfs_mount *fmp; struct vnode *rvp; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_mount(mp = %x)\n", mp); #endif /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) return (EOPNOTSUPP); error = getnewvnode(VT_KERNFS, mp, kernfs_vnodeop_p, &rvp); /* XXX */ if (error) return (error); MALLOC(fmp, struct kernfs_mount *, sizeof(struct kernfs_mount), M_UFSMNT, M_WAITOK); /* XXX */ rvp->v_type = VDIR; rvp->v_flag |= VROOT; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_mount: root vp = %x\n", rvp); #endif fmp->kf_root = rvp; mp->mnt_flag |= MNT_LOCAL; mp->mnt_data = (qaddr_t) fmp; getnewfsid(mp, MOUNT_KERNFS); (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); bzero(mp->mnt_stat.f_mntfromname, MNAMELEN); bcopy("kernfs", mp->mnt_stat.f_mntfromname, sizeof("kernfs")); #ifdef KERNFS_DIAGNOSTIC printf("kernfs_mount: at %s\n", mp->mnt_stat.f_mntonname); #endif return (0); } int kernfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } int kernfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { int error; int flags = 0; extern int doforce; struct vnode *rootvp = VFSTOKERNFS(mp)->kf_root; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_unmount(mp = %x)\n", mp); #endif if (mntflags & MNT_FORCE) { /* kernfs can never be rootfs so don't check for it */ if (!doforce) return (EINVAL); flags |= FORCECLOSE; } /* * Clear out buffer cache. I don't think we * ever get anything cached at this level at the * moment, but who knows... */ if (rootvp->v_usecount > 1) return (EBUSY); #ifdef KERNFS_DIAGNOSTIC printf("kernfs_unmount: calling vflush\n"); #endif if (error = vflush(mp, rootvp, flags)) return (error); #ifdef KERNFS_DIAGNOSTIC vprint("kernfs root", rootvp); #endif /* * Release reference on underlying root vnode */ vrele(rootvp); /* * And blow it away for future re-use */ vgone(rootvp); /* * Finally, throw away the kernfs_mount structure */ free(mp->mnt_data, M_UFSMNT); /* XXX */ mp->mnt_data = 0; return 0; } int kernfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct vnode *vp; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_root(mp = %x)\n", mp); #endif /* * Return locked reference to root. */ vp = VFSTOKERNFS(mp)->kf_root; VREF(vp); VOP_LOCK(vp); *vpp = vp; return (0); } int kernfs_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } int kernfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { #ifdef KERNFS_DIAGNOSTIC printf("kernfs_statfs(mp = %x)\n", mp); #endif sbp->f_type = MOUNT_KERNFS; sbp->f_flags = 0; sbp->f_bsize = DEV_BSIZE; sbp->f_iosize = DEV_BSIZE; sbp->f_blocks = 2; /* 1K to keep df happy */ sbp->f_bfree = 0; sbp->f_bavail = 0; sbp->f_files = 0; sbp->f_ffree = 0; if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } int kernfs_sync(mp, waitfor) struct mount *mp; int waitfor; { return (0); } /* * Kernfs flat namespace lookup. * Currently unsupported. */ int kernfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } int kernfs_fhtovp(mp, fhp, setgen, vpp) struct mount *mp; struct fid *fhp; int setgen; struct vnode **vpp; { return (EOPNOTSUPP); } int kernfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (EOPNOTSUPP); } struct vfsops kernfs_vfsops = { kernfs_mount, kernfs_start, kernfs_unmount, kernfs_root, kernfs_quotactl, kernfs_statfs, kernfs_sync, kernfs_vget, kernfs_fhtovp, kernfs_vptofh, kernfs_init, }; + +VFS_SET(kernfs_vfsops, kernfs, MOUNT_KERNFS, 0); Index: head/sys/miscfs/kernfs/kernfs_vnops.c =================================================================== --- head/sys/miscfs/kernfs/kernfs_vnops.c (revision 2945) +++ head/sys/miscfs/kernfs/kernfs_vnops.c (revision 2946) @@ -1,777 +1,779 @@ /* * 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.6 (Berkeley) 2/10/94 - * $Id: kernfs_vnops.c,v 1.3 1994/08/02 07:45:04 davidg Exp $ + * $Id: kernfs_vnops.c,v 1.4 1994/08/18 22:35:12 wollman Exp $ */ /* * Kernel parameter filesystem (/kern) */ #include #include #include #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) struct kern_target { 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_AVENRUN 53 int kt_tag; int kt_rw; int kt_vtype; } kern_targets[] = { /* NOTE: The name must be less than UIO_MX-16 chars in length */ /* name data tag ro/rw */ { ".", 0, KTT_NULL, VREAD, VDIR }, { "..", 0, KTT_NULL, VREAD, VDIR }, { "boottime", &boottime.tv_sec, KTT_INT, VREAD, VREG }, { "copyright", copyright, KTT_STRING, VREAD, VREG }, { "hostname", 0, KTT_HOSTNAME, VREAD|VWRITE, VREG }, { "hz", &hz, KTT_INT, VREAD, VREG }, { "loadavg", 0, KTT_AVENRUN, VREAD, VREG }, { "pagesize", &cnt.v_page_size, KTT_INT, VREAD, VREG }, { "physmem", &physmem, KTT_INT, VREAD, VREG }, #if 0 { "root", 0, KTT_NULL, VREAD, VDIR }, #endif { "rootdev", 0, KTT_NULL, VREAD, VBLK }, { "rrootdev", 0, KTT_NULL, VREAD, VCHR }, { "time", 0, KTT_TIME, VREAD, VREG }, { "version", version, KTT_STRING, VREAD, VREG }, }; static int nkern_targets = sizeof(kern_targets) / sizeof(kern_targets[0]); 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, "%d %d\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 = hostnamelen; 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 (EINVAL); } *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: { if (buf[len-1] == '\n') --len; bcopy(buf, hostname, len); hostname[len] = '\0'; hostnamelen = len; return (0); } default: return (EIO); } } /* * vp is the current namei directory * ndp is the name to locate in that directory... */ int kernfs_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; struct vnode *fvp; int error, i; char *pname; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_lookup(%x)\n", ap); printf("kernfs_lookup(dp = %x, vpp = %x, cnp = %x)\n", dvp, vpp, ap->a_cnp); #endif pname = cnp->cn_nameptr; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_lookup(%s)\n", pname); #endif if (cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); /*VOP_LOCK(dvp);*/ return (0); } #if 0 if (cnp->cn_namelen == 4 && bcmp(pname, "root", 4) == 0) { *vpp = rootdir; VREF(rootdir); VOP_LOCK(rootdir); return (0); } #endif /* * /kern/rootdev is the root device */ if (cnp->cn_namelen == 7 && bcmp(pname, "rootdev", 7) == 0) { *vpp = rootvp; VREF(rootvp); VOP_LOCK(rootvp); return (0); } /* * /kern/rrootdev is the raw root device */ if (cnp->cn_namelen == 8 && bcmp(pname, "rrootdev", 8) == 0) { if (rrootvp) { *vpp = rrootvp; VREF(rrootvp); VOP_LOCK(rrootvp); return (0); } error = ENXIO; goto bad; } error = ENOENT; for (i = 0; i < nkern_targets; i++) { struct kern_target *kt = &kern_targets[i]; if (cnp->cn_namelen == strlen(kt->kt_name) && bcmp(kt->kt_name, pname, cnp->cn_namelen) == 0) { error = 0; break; } } #ifdef KERNFS_DIAGNOSTIC printf("kernfs_lookup: i = %d, error = %d\n", i, error); #endif if (error) goto bad; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_lookup: allocate new vnode\n"); #endif error = getnewvnode(VT_KERNFS, dvp->v_mount, kernfs_vnodeop_p, &fvp); if (error) goto bad; MALLOC(fvp->v_data, void *, sizeof(struct kernfs_node), M_TEMP, M_WAITOK); VTOKERN(fvp)->kf_kt = &kern_targets[i]; fvp->v_type = VTOKERN(fvp)->kf_kt->kt_vtype; *vpp = fvp; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_lookup: newvp = %x\n", fvp); #endif return (0); bad:; *vpp = NULL; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_lookup: error = %d\n", error); #endif return (error); } int kernfs_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; /* * Can always open the root (modulo perms) */ if (vp->v_flag & VROOT) return (0); #ifdef KERNFS_DIAGNOSTIC printf("kernfs_open, mode = %x, file = %s\n", ap->a_mode, VTOKERN(vp)->kf_kt->kt_name); #endif if ((ap->a_mode & FWRITE) && !(VTOKERN(vp)->kf_kt->kt_rw & VWRITE)) return (EOPNOTSUPP); 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; { struct vnode *vp = ap->a_vp; struct ucred *cred = ap->a_cred; mode_t mode = ap->a_mode; if (mode & VEXEC) { if (vp->v_flag & VROOT) return (0); return (EACCES); } if (cred->cr_uid == 0) { if ((vp->v_flag & VROOT) == 0) { struct kern_target *kt = VTOKERN(vp)->kf_kt; if ((mode & VWRITE) && !(kt->kt_rw & VWRITE)) return (EROFS); } return (0); } if (mode & VWRITE) return (EACCES); return (0); } 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; 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_qsize = 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_qbytes = 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_rw & VWRITE ? WRITE_MODE : READ_MODE); vap->va_nlink = 1; vap->va_fileid = 3 + (kt - kern_targets) / sizeof(*kt); error = kernfs_xread(kt, strbuf, sizeof(strbuf), &nbytes); vap->va_size = nbytes; } vp->v_type = vap->va_type; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_getattr: return error %d\n", error); #endif return (error); } 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_flag & VROOT) return (EOPNOTSUPP); kt = VTOKERN(vp)->kf_kt; #ifdef KERNFS_DIAGNOSTIC printf("kern_read %s\n", kt->kt_name); #endif len = 0; error = kernfs_xread(kt, strbuf, sizeof(strbuf), &len); if (error) return (error); cp = strbuf + off; len -= off; return (uiomove(cp, len, 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_flag & VROOT) return (0); kt = VTOKERN(vp)->kf_kt; if (uio->uio_offset != 0) return (EINVAL); xlen = min(uio->uio_resid, KSTRING-1); error = uiomove(strbuf, xlen, uio); if (error) return (error); if (uio->uio_resid != 0) return (EIO); strbuf[xlen] = '\0'; xlen = strlen(strbuf); return (kernfs_xwrite(kt, strbuf, xlen)); } int kernfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { struct uio *uio = ap->a_uio; int i; int error; i = uio->uio_offset / UIO_MX; error = 0; while (uio->uio_resid > 0 && i < nkern_targets) { struct dirent d; struct dirent *dp = &d; struct kern_target *kt = &kern_targets[i]; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_readdir: i = %d\n", i); #endif bzero((caddr_t) dp, UIO_MX); dp->d_namlen = strlen(kt->kt_name); bcopy(kt->kt_name, dp->d_name, dp->d_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 = DT_UNKNOWN; /* XXX */ /* * And ship to userland */ error = uiomove((caddr_t) dp, UIO_MX, uio); if (error) break; i++; } uio->uio_offset = i * UIO_MX; return (error); } int kernfs_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; /* * Clear out the v_type field to avoid * nasty things happening in vgone(). */ vp->v_type = VNON; #ifdef KERNFS_DIAGNOSTIC printf("kernfs_inactive(%x)\n", vp); #endif return (0); } 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. */ 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 /dev/fd vnode. */ /* ARGSUSED */ int kernfs_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_KERNFS, kernfs vnode\n"); return (0); } /*void*/ int kernfs_vfree(ap) struct vop_vfree_args /* { struct vnode *a_pvp; ino_t a_ino; int a_mode; } */ *ap; { return (0); } /* * /dev/fd vnode unsupported operation */ int kernfs_enotsupp() { return (EOPNOTSUPP); } /* * /dev/fd "should never get here" operation */ int kernfs_badop() { panic("kernfs: bad op"); /* NOTREACHED */ } /* * kernfs vnode null operation */ int kernfs_nullop() { return (0); } #define kernfs_create ((int (*) __P((struct vop_create_args *)))kernfs_enotsupp) #define kernfs_mknod ((int (*) __P((struct vop_mknod_args *)))kernfs_enotsupp) #define kernfs_close ((int (*) __P((struct vop_close_args *)))nullop) #define kernfs_ioctl ((int (*) __P((struct vop_ioctl_args *)))kernfs_enotsupp) #define kernfs_select ((int (*) __P((struct vop_select_args *)))kernfs_enotsupp) #define kernfs_mmap ((int (*) __P((struct vop_mmap_args *)))kernfs_enotsupp) #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 *)))kernfs_enotsupp) #define kernfs_link ((int (*) __P((struct vop_link_args *)))kernfs_enotsupp) #define kernfs_rename ((int (*) __P((struct vop_rename_args *)))kernfs_enotsupp) #define kernfs_mkdir ((int (*) __P((struct vop_mkdir_args *)))kernfs_enotsupp) #define kernfs_rmdir ((int (*) __P((struct vop_rmdir_args *)))kernfs_enotsupp) #define kernfs_symlink ((int (*) __P((struct vop_symlink_args *)))kernfs_enotsupp) #define kernfs_readlink \ ((int (*) __P((struct vop_readlink_args *)))kernfs_enotsupp) #define kernfs_abortop ((int (*) __P((struct vop_abortop_args *)))nullop) #define kernfs_lock ((int (*) __P((struct vop_lock_args *)))nullop) #define kernfs_unlock ((int (*) __P((struct vop_unlock_args *)))nullop) #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 *)))nullop) #define kernfs_advlock ((int (*) __P((struct vop_advlock_args *)))kernfs_enotsupp) #define kernfs_blkatoff \ ((int (*) __P((struct vop_blkatoff_args *)))kernfs_enotsupp) #define kernfs_valloc ((int(*) __P(( \ struct vnode *pvp, \ int mode, \ struct ucred *cred, \ struct vnode **vpp))) kernfs_enotsupp) #define kernfs_truncate \ ((int (*) __P((struct vop_truncate_args *)))kernfs_enotsupp) #define kernfs_update ((int (*) __P((struct vop_update_args *)))kernfs_enotsupp) #define kernfs_bwrite ((int (*) __P((struct vop_bwrite_args *)))kernfs_enotsupp) int (**kernfs_vnodeop_p)(); struct vnodeopv_entry_desc kernfs_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, kernfs_lookup }, /* lookup */ { &vop_create_desc, kernfs_create }, /* create */ { &vop_mknod_desc, kernfs_mknod }, /* mknod */ { &vop_open_desc, kernfs_open }, /* open */ { &vop_close_desc, kernfs_close }, /* close */ { &vop_access_desc, kernfs_access }, /* access */ { &vop_getattr_desc, kernfs_getattr }, /* getattr */ { &vop_setattr_desc, kernfs_setattr }, /* setattr */ { &vop_read_desc, kernfs_read }, /* read */ { &vop_write_desc, kernfs_write }, /* write */ { &vop_ioctl_desc, kernfs_ioctl }, /* ioctl */ { &vop_select_desc, kernfs_select }, /* select */ { &vop_mmap_desc, kernfs_mmap }, /* mmap */ { &vop_fsync_desc, kernfs_fsync }, /* fsync */ { &vop_seek_desc, kernfs_seek }, /* seek */ { &vop_remove_desc, kernfs_remove }, /* remove */ { &vop_link_desc, kernfs_link }, /* link */ { &vop_rename_desc, kernfs_rename }, /* rename */ { &vop_mkdir_desc, kernfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, kernfs_rmdir }, /* rmdir */ { &vop_symlink_desc, kernfs_symlink }, /* symlink */ { &vop_readdir_desc, kernfs_readdir }, /* readdir */ { &vop_readlink_desc, kernfs_readlink },/* readlink */ { &vop_abortop_desc, kernfs_abortop }, /* abortop */ { &vop_inactive_desc, kernfs_inactive },/* inactive */ { &vop_reclaim_desc, kernfs_reclaim }, /* reclaim */ { &vop_lock_desc, kernfs_lock }, /* lock */ { &vop_unlock_desc, kernfs_unlock }, /* unlock */ { &vop_bmap_desc, kernfs_bmap }, /* bmap */ { &vop_strategy_desc, kernfs_strategy },/* strategy */ { &vop_print_desc, kernfs_print }, /* print */ { &vop_islocked_desc, kernfs_islocked },/* islocked */ { &vop_pathconf_desc, kernfs_pathconf },/* pathconf */ { &vop_advlock_desc, kernfs_advlock }, /* advlock */ { &vop_blkatoff_desc, kernfs_blkatoff },/* blkatoff */ { &vop_valloc_desc, kernfs_valloc }, /* valloc */ { &vop_vfree_desc, kernfs_vfree }, /* vfree */ { &vop_truncate_desc, kernfs_truncate },/* truncate */ { &vop_update_desc, kernfs_update }, /* update */ { &vop_bwrite_desc, kernfs_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc kernfs_vnodeop_opv_desc = { &kernfs_vnodeop_p, kernfs_vnodeop_entries }; + +VNODEOP_SET(kernfs_vnodeop_opv_desc); Index: head/sys/miscfs/nullfs/null_vfsops.c =================================================================== --- head/sys/miscfs/nullfs/null_vfsops.c (revision 2945) +++ head/sys/miscfs/nullfs/null_vfsops.c (revision 2946) @@ -1,366 +1,368 @@ /* * 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. * * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94 * * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92 - * $Id: lofs_vfsops.c,v 1.9 1992/05/30 10:26:24 jsp Exp jsp $ + * $Id: null_vfsops.c,v 1.1.1.1 1994/05/24 10:05:03 rgrimes Exp $ */ /* * Null Layer * (See null_vnops.c for a description of what this does.) */ #include #include #include #include #include #include #include #include #include /* * Mount null layer */ int nullfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { int error = 0; struct null_args args; struct vnode *lowerrootvp, *vp; struct vnode *nullm_rootvp; struct null_mount *xmp; u_int size; #ifdef NULLFS_DIAGNOSTIC printf("nullfs_mount(mp = %x)\n", mp); #endif /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) { return (EOPNOTSUPP); /* return VFS_MOUNT(MOUNTTONULLMOUNT(mp)->nullm_vfs, path, data, ndp, p);*/ } /* * Get argument */ if (error = copyin(data, (caddr_t)&args, sizeof(struct null_args))) return (error); /* * Find lower node */ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF, UIO_USERSPACE, args.target, p); if (error = namei(ndp)) return (error); /* * Sanity check on lower vnode */ lowerrootvp = ndp->ni_vp; vrele(ndp->ni_dvp); ndp->ni_dvp = NULL; xmp = (struct null_mount *) malloc(sizeof(struct null_mount), M_UFSMNT, M_WAITOK); /* XXX */ /* * Save reference to underlying FS */ xmp->nullm_vfs = lowerrootvp->v_mount; /* * Save reference. Each mount also holds * a reference on the root vnode. */ error = null_node_create(mp, lowerrootvp, &vp); /* * Unlock the node (either the lower or the alias) */ VOP_UNLOCK(vp); /* * Make sure the node alias worked */ if (error) { vrele(lowerrootvp); free(xmp, M_UFSMNT); /* XXX */ return (error); } /* * Keep a held reference to the root vnode. * It is vrele'd in nullfs_unmount. */ nullm_rootvp = vp; nullm_rootvp->v_flag |= VROOT; xmp->nullm_rootvp = nullm_rootvp; if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) mp->mnt_flag |= MNT_LOCAL; mp->mnt_data = (qaddr_t) xmp; getnewfsid(mp, MOUNT_LOFS); (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); #ifdef NULLFS_DIAGNOSTIC printf("nullfs_mount: lower %s, alias at %s\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); #endif return (0); } /* * VFS start. Nothing needed here - the start routine * on the underlying filesystem will have been called * when that filesystem was mounted. */ int nullfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); /* return VFS_START(MOUNTTONULLMOUNT(mp)->nullm_vfs, flags, p); */ } /* * Free reference to null layer */ int nullfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { struct vnode *nullm_rootvp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; int error; int flags = 0; extern int doforce; #ifdef NULLFS_DIAGNOSTIC printf("nullfs_unmount(mp = %x)\n", mp); #endif if (mntflags & MNT_FORCE) { /* lofs can never be rootfs so don't check for it */ if (!doforce) return (EINVAL); flags |= FORCECLOSE; } /* * Clear out buffer cache. I don't think we * ever get anything cached at this level at the * moment, but who knows... */ #if 0 mntflushbuf(mp, 0); if (mntinvalbuf(mp, 1)) return (EBUSY); #endif if (nullm_rootvp->v_usecount > 1) return (EBUSY); if (error = vflush(mp, nullm_rootvp, flags)) return (error); #ifdef NULLFS_DIAGNOSTIC vprint("alias root of lower", nullm_rootvp); #endif /* * Release reference on underlying root vnode */ vrele(nullm_rootvp); /* * And blow it away for future re-use */ vgone(nullm_rootvp); /* * Finally, throw away the null_mount structure */ free(mp->mnt_data, M_UFSMNT); /* XXX */ mp->mnt_data = 0; return 0; } int nullfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct vnode *vp; #ifdef NULLFS_DIAGNOSTIC printf("nullfs_root(mp = %x, vp = %x->%x)\n", mp, MOUNTTONULLMOUNT(mp)->nullm_rootvp, NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp) ); #endif /* * Return locked reference to root. */ vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; VREF(vp); VOP_LOCK(vp); *vpp = vp; return 0; } int nullfs_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, p); } int nullfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { int error; struct statfs mstat; #ifdef NULLFS_DIAGNOSTIC printf("nullfs_statfs(mp = %x, vp = %x->%x)\n", mp, MOUNTTONULLMOUNT(mp)->nullm_rootvp, NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp) ); #endif bzero(&mstat, sizeof(mstat)); error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, p); if (error) return (error); /* now copy across the "interesting" information and fake the rest */ sbp->f_type = mstat.f_type; sbp->f_flags = mstat.f_flags; sbp->f_bsize = mstat.f_bsize; sbp->f_iosize = mstat.f_iosize; sbp->f_blocks = mstat.f_blocks; sbp->f_bfree = mstat.f_bfree; sbp->f_bavail = mstat.f_bavail; sbp->f_files = mstat.f_files; sbp->f_ffree = mstat.f_ffree; if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } int nullfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { /* * XXX - Assumes no data cached at null layer. */ return (0); } int nullfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, vpp); } int nullfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp) struct mount *mp; struct fid *fidp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred**credanonp; { return VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, nam, vpp, exflagsp,credanonp); } int nullfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return VFS_VPTOFH(NULLVPTOLOWERVP(vp), fhp); } int nullfs_init __P((void)); struct vfsops null_vfsops = { nullfs_mount, nullfs_start, nullfs_unmount, nullfs_root, nullfs_quotactl, nullfs_statfs, nullfs_sync, nullfs_vget, nullfs_fhtovp, nullfs_vptofh, nullfs_init, }; + +VFS_SET(null_vfsops, null, MOUNT_NULL, 0); Index: head/sys/miscfs/nullfs/null_vnops.c =================================================================== --- head/sys/miscfs/nullfs/null_vnops.c (revision 2945) +++ head/sys/miscfs/nullfs/null_vnops.c (revision 2946) @@ -1,457 +1,459 @@ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * John Heidemann of the UCLA Ficus project. * * 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. * * @(#)null_vnops.c 8.1 (Berkeley) 6/10/93 * - * $Id: null_vnops.c,v 1.2 1994/08/02 07:45:06 davidg Exp $ + * $Id: null_vnops.c,v 1.3 1994/08/20 03:48:51 davidg Exp $ */ /* * Null Layer * * (See mount_null(8) for more information.) * * The null layer duplicates a portion of the file system * name space under a new name. In this respect, it is * similar to the loopback file system. It differs from * the loopback fs in two respects: it is implemented using * a stackable layers techniques, and it's "null-node"s stack above * all lower-layer vnodes, not just over directory vnodes. * * The null layer has two purposes. First, it serves as a demonstration * of layering by proving a layer which does nothing. (It actually * does everything the loopback file system does, which is slightly * more than nothing.) Second, the null layer can serve as a prototype * layer. Since it provides all necessary layer framework, * new file system layers can be created very easily be starting * with a null layer. * * The remainder of this man page examines the null layer as a basis * for constructing new layers. * * * INSTANTIATING NEW NULL LAYERS * * New null layers are created with mount_null(8). * Mount_null(8) takes two arguments, the pathname * of the lower vfs (target-pn) and the pathname where the null * layer will appear in the namespace (alias-pn). After * the null layer is put into place, the contents * of target-pn subtree will be aliased under alias-pn. * * * OPERATION OF A NULL LAYER * * The null layer is the minimum file system layer, * simply bypassing all possible operations to the lower layer * for processing there. The majority of its activity centers * on the bypass routine, though which nearly all vnode operations * pass. * * The bypass routine accepts arbitrary vnode operations for * handling by the lower layer. It begins by examing vnode * operation arguments and replacing any null-nodes by their * lower-layer equivlants. It then invokes the operation * on the lower layer. Finally, it replaces the null-nodes * in the arguments and, if a vnode is return by the operation, * stacks a null-node on top of the returned vnode. * * Although bypass handles most operations, * vop_getattr, _inactive, _reclaim, and _print are not bypassed. * Vop_getattr must change the fsid being returned. * Vop_inactive and vop_reclaim are not bypassed so that * they can handle freeing null-layer specific data. * Vop_print is not bypassed to avoid excessive debugging * information. * * * INSTANTIATING VNODE STACKS * * Mounting associates the null layer with a lower layer, * effect stacking two VFSes. Vnode stacks are instead * created on demand as files are accessed. * * The initial mount creates a single vnode stack for the * root of the new null layer. All other vnode stacks * are created as a result of vnode operations on * this or other null vnode stacks. * * New vnode stacks come into existance as a result of * an operation which returns a vnode. * The bypass routine stacks a null-node above the new * vnode before returning it to the caller. * * For example, imagine mounting a null layer with * "mount_null /usr/include /dev/layer/null". * Changing directory to /dev/layer/null will assign * the root null-node (which was created when the null layer was mounted). * Now consider opening "sys". A vop_lookup would be * done on the root null-node. This operation would bypass through * to the lower layer which would return a vnode representing * the UFS "sys". Null_bypass then builds a null-node * aliasing the UFS "sys" and returns this to the caller. * Later operations on the null-node "sys" will repeat this * process when constructing other vnode stacks. * * * CREATING OTHER FILE SYSTEM LAYERS * * One of the easiest ways to construct new file system layers is to make * a copy of the null layer, rename all files and variables, and * then begin modifing the copy. Sed can be used to easily rename * all variables. * * The umap layer is an example of a layer descended from the * null layer. * * * INVOKING OPERATIONS ON LOWER LAYERS * * There are two techniques to invoke operations on a lower layer * when the operation cannot be completely bypassed. Each method * is appropriate in different situations. In both cases, * it is the responsibility of the aliasing layer to make * the operation arguments "correct" for the lower layer * by mapping an vnode arguments to the lower layer. * * The first approach is to call the aliasing layer's bypass routine. * This method is most suitable when you wish to invoke the operation * currently being hanldled on the lower layer. It has the advantage * that the bypass routine already must do argument mapping. * An example of this is null_getattrs in the null layer. * * A second approach is to directly invoked vnode operations on * the lower layer with the VOP_OPERATIONNAME interface. * The advantage of this method is that it is easy to invoke * arbitrary operations on the lower layer. The disadvantage * is that vnodes arguments must be manualy mapped. * */ #include #include #include #include #include #include #include #include #include #include #include int null_bug_bypass = 0; /* for debugging: enables bypass printf'ing */ /* * This is the 10-Apr-92 bypass routine. * This version has been optimized for speed, throwing away some * safety checks. It should still always work, but it's not as * robust to programmer errors. * Define SAFETY to include some error checking code. * * In general, we map all vnodes going down and unmap them on the way back. * As an exception to this, vnodes can be marked "unmapped" by setting * the Nth bit in operation's vdesc_flags. * * Also, some BSD vnode operations have the side effect of vrele'ing * their arguments. With stacking, the reference counts are held * by the upper node, not the lower one, so we must handle these * side-effects here. This is not of concern in Sun-derived systems * since there are no such side-effects. * * This makes the following assumptions: * - only one returned vpp * - no INOUT vpp's (Sun's vop_open has one of these) * - the vnode operation vector of the first vnode should be used * to determine what implementation of the op should be invoked * - all mapped vnodes are of our vnode-type (NEEDSWORK: * problems on rmdir'ing mount points and renaming?) */ int null_bypass(ap) struct vop_generic_args /* { struct vnodeop_desc *a_desc; } */ *ap; { register struct vnode **this_vp_p; int error; struct vnode *old_vps[VDESC_MAX_VPS]; struct vnode **vps_p[VDESC_MAX_VPS]; struct vnode ***vppp; struct vnodeop_desc *descp = ap->a_desc; int reles, i; if (null_bug_bypass) printf ("null_bypass: %s\n", descp->vdesc_name); #ifdef SAFETY /* * We require at least one vp. */ if (descp->vdesc_vp_offsets == NULL || descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET) panic ("null_bypass: no vp's in map.\n"); #endif /* * Map the vnodes going in. * Later, we'll invoke the operation based on * the first mapped vnode's operation vector. */ reles = descp->vdesc_flags; for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) { if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) break; /* bail out at end of list */ vps_p[i] = this_vp_p = VOPARG_OFFSETTO(struct vnode**,descp->vdesc_vp_offsets[i],ap); /* * We're not guaranteed that any but the first vnode * are of our type. Check for and don't map any * that aren't. (We must always map first vp or vclean fails.) */ if (i && (*this_vp_p)->v_op != null_vnodeop_p) { old_vps[i] = NULL; } else { old_vps[i] = *this_vp_p; *(vps_p[i]) = NULLVPTOLOWERVP(*this_vp_p); /* * XXX - Several operations have the side effect * of vrele'ing their vp's. We must account for * that. (This should go away in the future.) */ if (reles & 1) VREF(*this_vp_p); } } /* * Call the operation on the lower layer * with the modified argument structure. */ error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap); /* * Maintain the illusion of call-by-value * by restoring vnodes in the argument structure * to their original value. */ reles = descp->vdesc_flags; for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) { if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) break; /* bail out at end of list */ if (old_vps[i]) { *(vps_p[i]) = old_vps[i]; if (reles & 1) vrele(*(vps_p[i])); } } /* * Map the possible out-going vpp * (Assumes that the lower layer always returns * a VREF'ed vpp unless it gets an error.) */ if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET && !(descp->vdesc_flags & VDESC_NOMAP_VPP) && !error) { /* * XXX - even though some ops have vpp returned vp's, * several ops actually vrele this before returning. * We must avoid these ops. * (This should go away when these ops are regularized.) */ if (descp->vdesc_flags & VDESC_VPP_WILLRELE) goto out; vppp = VOPARG_OFFSETTO(struct vnode***, descp->vdesc_vpp_offset,ap); error = null_node_create(old_vps[0]->v_mount, **vppp, *vppp); } out: return (error); } /* * We handle getattr only to change the fsid. */ int null_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; if (error = null_bypass(ap)) return (error); /* Requires that arguments be restored. */ ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; return (0); } int null_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { /* * Do nothing (and _don't_ bypass). * Wait to vrele lowervp until reclaim, * so that until then our null_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. */ return (0); } int null_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; struct null_node *xp = VTONULL(vp); struct vnode *lowervp = xp->null_lowervp; /* * Note: in vop_reclaim, vp->v_op == dead_vnodeop_p, * so we can't call VOPs on ourself. */ /* After this assignment, this node will not be re-used. */ xp->null_lowervp = NULL; remque(xp); FREE(vp->v_data, M_TEMP); vp->v_data = NULL; vrele (lowervp); return (0); } int null_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { register struct vnode *vp = ap->a_vp; printf ("\ttag VT_NULLFS, vp=%x, lowervp=%x\n", vp, NULLVPTOLOWERVP(vp)); return (0); } /* * XXX - vop_strategy must be hand coded because it has no * vnode in its arguments. * This goes away with a merged VM/buffer cache. */ int null_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 = NULLVPTOLOWERVP(bp->b_vp); error = VOP_STRATEGY(bp); bp->b_vp = savedvp; return (error); } /* * XXX - like vop_strategy, vop_bwrite must be hand coded because it has no * vnode in its arguments. * This goes away with a merged VM/buffer cache. */ int null_bwrite(ap) struct vop_bwrite_args /* { struct buf *a_bp; } */ *ap; { struct buf *bp = ap->a_bp; int error; struct vnode *savedvp; savedvp = bp->b_vp; bp->b_vp = NULLVPTOLOWERVP(bp->b_vp); error = VOP_BWRITE(bp); bp->b_vp = savedvp; return (error); } /* * Global vfs data structures */ int (**null_vnodeop_p)(); struct vnodeopv_entry_desc null_vnodeop_entries[] = { { &vop_default_desc, null_bypass }, { &vop_getattr_desc, null_getattr }, { &vop_inactive_desc, null_inactive }, { &vop_reclaim_desc, null_reclaim }, { &vop_print_desc, null_print }, { &vop_strategy_desc, null_strategy }, { &vop_bwrite_desc, null_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc null_vnodeop_opv_desc = { &null_vnodeop_p, null_vnodeop_entries }; + +VNODEOP_SET(null_vnodeop_opv_desc); Index: head/sys/miscfs/portal/portal_vfsops.c =================================================================== --- head/sys/miscfs/portal/portal_vfsops.c (revision 2945) +++ head/sys/miscfs/portal/portal_vfsops.c (revision 2946) @@ -1,313 +1,315 @@ /* * 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_vfsops.c 8.6 (Berkeley) 1/21/94 * - * $Id: portal_vfsops.c,v 1.5 1992/05/30 10:25:27 jsp Exp jsp $ + * $Id: portal_vfsops.c,v 1.1.1.1 1994/05/24 10:05:06 rgrimes Exp $ */ /* * Portal Filesystem */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int portal_init() { return (0); } /* * Mount the per-process file descriptors (/dev/fd) */ int portal_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { struct file *fp; struct portal_args args; struct portalmount *fmp; struct socket *so; struct vnode *rvp; u_int size; int error; /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) return (EOPNOTSUPP); if (error = copyin(data, (caddr_t) &args, sizeof(struct portal_args))) return (error); if (error = getsock(p->p_fd, args.pa_socket, &fp)) return (error); so = (struct socket *) fp->f_data; if (so->so_proto->pr_domain->dom_family != AF_UNIX) return (ESOCKTNOSUPPORT); error = getnewvnode(VT_PORTAL, mp, portal_vnodeop_p, &rvp); /* XXX */ if (error) return (error); MALLOC(rvp->v_data, void *, sizeof(struct portalnode), M_TEMP, M_WAITOK); fmp = (struct portalmount *) malloc(sizeof(struct portalmount), M_UFSMNT, M_WAITOK); /* XXX */ rvp->v_type = VDIR; rvp->v_flag |= VROOT; VTOPORTAL(rvp)->pt_arg = 0; VTOPORTAL(rvp)->pt_size = 0; VTOPORTAL(rvp)->pt_fileid = PORTAL_ROOTFILEID; fmp->pm_root = rvp; fmp->pm_server = fp; fp->f_count++; mp->mnt_flag |= MNT_LOCAL; mp->mnt_data = (qaddr_t) fmp; getnewfsid(mp, MOUNT_PORTAL); (void)copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); (void)copyinstr(args.pa_config, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); #ifdef notdef bzero(mp->mnt_stat.f_mntfromname, MNAMELEN); bcopy("portal", mp->mnt_stat.f_mntfromname, sizeof("portal")); #endif return (0); } int portal_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } int portal_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { extern int doforce; struct vnode *rootvp = VFSTOPORTAL(mp)->pm_root; int error, flags = 0; if (mntflags & MNT_FORCE) { /* portal can never be rootfs so don't check for it */ if (!doforce) return (EINVAL); flags |= FORCECLOSE; } /* * Clear out buffer cache. I don't think we * ever get anything cached at this level at the * moment, but who knows... */ #ifdef notyet mntflushbuf(mp, 0); if (mntinvalbuf(mp, 1)) return (EBUSY); #endif if (rootvp->v_usecount > 1) return (EBUSY); if (error = vflush(mp, rootvp, flags)) return (error); /* * Release reference on underlying root vnode */ vrele(rootvp); /* * And blow it away for future re-use */ vgone(rootvp); /* * Shutdown the socket. This will cause the select in the * daemon to wake up, and then the accept will get ECONNABORTED * which it interprets as a request to go and bury itself. */ soshutdown((struct socket *) VFSTOPORTAL(mp)->pm_server->f_data, 2); /* * Discard reference to underlying file. Must call closef because * this may be the last reference. */ closef(VFSTOPORTAL(mp)->pm_server, (struct proc *) 0); /* * Finally, throw away the portalmount structure */ free(mp->mnt_data, M_UFSMNT); /* XXX */ mp->mnt_data = 0; return (0); } int portal_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct vnode *vp; /* * Return locked reference to root. */ vp = VFSTOPORTAL(mp)->pm_root; VREF(vp); VOP_LOCK(vp); *vpp = vp; return (0); } int portal_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } int portal_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { sbp->f_type = MOUNT_PORTAL; sbp->f_flags = 0; sbp->f_bsize = DEV_BSIZE; sbp->f_iosize = DEV_BSIZE; sbp->f_blocks = 2; /* 1K to keep df happy */ sbp->f_bfree = 0; sbp->f_bavail = 0; sbp->f_files = 1; /* Allow for "." */ sbp->f_ffree = 0; /* See comments above */ if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } int portal_sync(mp, waitfor) struct mount *mp; int waitfor; { return (0); } int portal_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } int portal_fhtovp(mp, fhp, vpp) struct mount *mp; struct fid *fhp; struct vnode **vpp; { return (EOPNOTSUPP); } int portal_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (EOPNOTSUPP); } struct vfsops portal_vfsops = { portal_mount, portal_start, portal_unmount, portal_root, portal_quotactl, portal_statfs, portal_sync, portal_vget, portal_fhtovp, portal_vptofh, portal_init, }; + +VFS_SET(portal_vfsops, portal, MOUNT_PORTAL, 0); Index: head/sys/miscfs/portal/portal_vnops.c =================================================================== --- head/sys/miscfs/portal/portal_vnops.c (revision 2945) +++ head/sys/miscfs/portal/portal_vnops.c (revision 2946) @@ -1,709 +1,711 @@ /* * 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.8 (Berkeley) 1/21/94 * - * $Id: portal_vnops.c,v 1.2 1994/05/25 09:08:21 rgrimes Exp $ + * $Id: portal_vnops.c,v 1.3 1994/08/20 03:48:52 davidg Exp $ */ /* * Portal Filesystem */ #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 void portal_closefd(p, fd) struct proc *p; int fd; { int error; struct { int fd; } 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... */ int portal_lookup(ap) struct vop_lookup_args /* { struct vnode * a_dvp; struct vnode ** a_vpp; struct componentname * a_cnp; } */ *ap; { char *pname = ap->a_cnp->cn_nameptr; struct portalnode *pt; int error; struct vnode *fvp = 0; char *path; int size; if (ap->a_cnp->cn_namelen == 1 && *pname == '.') { *ap->a_vpp = ap->a_dvp; VREF(ap->a_dvp); /*VOP_LOCK(ap->a_dvp);*/ return (0); } error = getnewvnode(VT_PORTAL, ap->a_dvp->v_mount, portal_vnodeop_p, &fvp); if (error) goto bad; fvp->v_type = VREG; MALLOC(fvp->v_data, void *, sizeof(struct portalnode), M_TEMP, M_WAITOK); pt = VTOPORTAL(fvp); /* * 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++; ap->a_cnp->cn_consume = size - ap->a_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++; *ap->a_vpp = fvp; /*VOP_LOCK(fvp);*/ return (0); bad:; if (fvp) { vrele(fvp); } *ap->a_vpp = NULL; 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 = m_copy(unp2->unp_addr, 0, (int)M_COPYALL); so2 = so3; return (unp_connect2(so, so2)); } 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); 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 mbuf *) 0, &auio, (struct mbuf *) 0, (struct mbuf *) 0, 0); if (error) goto bad; len = auio.uio_resid = sizeof(int); do { struct mbuf *m = 0; int flags = MSG_WAITALL; error = soreceive(so, (struct mbuf **) 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); } 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; 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((struct timeval *)&vap->va_atime); TIMEVAL_TO_TIMESPEC((struct timeval *)&vap->va_atime, (struct timespec *)&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); } 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. */ int portal_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { return (0); } int portal_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { return (0); } 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. */ 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 */ int portal_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("tag VT_PORTAL, portal vnode\n"); return (0); } /*void*/ 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 */ int portal_enotsupp() { return (EOPNOTSUPP); } /* * Portal "should never get here" operation */ int portal_badop() { panic("portal: bad op"); /* NOTREACHED */ } /* * Portal vnode null operation */ int portal_nullop() { return (0); } #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_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 *)))nullop) #define portal_unlock ((int (*) __P((struct vop_unlock_args *)))nullop) #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 *)))nullop) #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) int (**portal_vnodeop_p)(); struct vnodeopv_entry_desc portal_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, portal_lookup }, /* lookup */ { &vop_create_desc, portal_create }, /* create */ { &vop_mknod_desc, portal_mknod }, /* mknod */ { &vop_open_desc, portal_open }, /* open */ { &vop_close_desc, portal_close }, /* close */ { &vop_access_desc, portal_access }, /* access */ { &vop_getattr_desc, portal_getattr }, /* getattr */ { &vop_setattr_desc, portal_setattr }, /* setattr */ { &vop_read_desc, portal_read }, /* read */ { &vop_write_desc, portal_write }, /* write */ { &vop_ioctl_desc, portal_ioctl }, /* ioctl */ { &vop_select_desc, portal_select }, /* select */ { &vop_mmap_desc, portal_mmap }, /* mmap */ { &vop_fsync_desc, portal_fsync }, /* fsync */ { &vop_seek_desc, portal_seek }, /* seek */ { &vop_remove_desc, portal_remove }, /* remove */ { &vop_link_desc, portal_link }, /* link */ { &vop_rename_desc, portal_rename }, /* rename */ { &vop_mkdir_desc, portal_mkdir }, /* mkdir */ { &vop_rmdir_desc, portal_rmdir }, /* rmdir */ { &vop_symlink_desc, portal_symlink }, /* symlink */ { &vop_readdir_desc, portal_readdir }, /* readdir */ { &vop_readlink_desc, portal_readlink }, /* readlink */ { &vop_abortop_desc, portal_abortop }, /* abortop */ { &vop_inactive_desc, portal_inactive }, /* inactive */ { &vop_reclaim_desc, portal_reclaim }, /* reclaim */ { &vop_lock_desc, portal_lock }, /* lock */ { &vop_unlock_desc, portal_unlock }, /* unlock */ { &vop_bmap_desc, portal_bmap }, /* bmap */ { &vop_strategy_desc, portal_strategy }, /* strategy */ { &vop_print_desc, portal_print }, /* print */ { &vop_islocked_desc, portal_islocked }, /* islocked */ { &vop_pathconf_desc, portal_pathconf }, /* pathconf */ { &vop_advlock_desc, portal_advlock }, /* advlock */ { &vop_blkatoff_desc, portal_blkatoff }, /* blkatoff */ { &vop_valloc_desc, portal_valloc }, /* valloc */ { &vop_vfree_desc, portal_vfree }, /* vfree */ { &vop_truncate_desc, portal_truncate }, /* truncate */ { &vop_update_desc, portal_update }, /* update */ { &vop_bwrite_desc, portal_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc portal_vnodeop_opv_desc = { &portal_vnodeop_p, portal_vnodeop_entries }; + +VNODEOP_SET(portal_vnodeop_opv_desc); Index: head/sys/miscfs/procfs/procfs_vfsops.c =================================================================== --- head/sys/miscfs/procfs/procfs_vfsops.c (revision 2945) +++ head/sys/miscfs/procfs/procfs_vfsops.c (revision 2946) @@ -1,254 +1,256 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * 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_vfsops.c 8.4 (Berkeley) 1/21/94 * - * $Id: procfs_vfsops.c,v 1.3 1994/08/02 07:45:23 davidg Exp $ + * $Id: procfs_vfsops.c,v 1.4 1994/08/18 22:35:14 wollman Exp $ */ /* * procfs VFS interface */ #include #include #include #include #include #include #include #include #include #include #include #include /* for PAGE_SIZE */ /* * VFS Operations. * * mount system call */ /* ARGSUSED */ int procfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { u_int size; if (UIO_MX & (UIO_MX-1)) { log(LOG_ERR, "procfs: invalid directory entry size"); return (EINVAL); } if (mp->mnt_flag & MNT_UPDATE) return (EOPNOTSUPP); mp->mnt_flag |= MNT_LOCAL; mp->mnt_data = 0; getnewfsid(mp, MOUNT_PROCFS); (void) copyinstr(path, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); size = sizeof("procfs") - 1; bcopy("procfs", mp->mnt_stat.f_mntfromname, size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); return (0); } /* * unmount system call */ int procfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { int error; extern int doforce; int flags = 0; if (mntflags & MNT_FORCE) { /* procfs can never be rootfs so don't check for it */ if (!doforce) return (EINVAL); flags |= FORCECLOSE; } if (error = vflush(mp, 0, flags)) return (error); return (0); } int procfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct pfsnode *pfs; struct vnode *vp; int error; error = procfs_allocvp(mp, &vp, (pid_t) 0, Proot); if (error) return (error); vp->v_type = VDIR; vp->v_flag = VROOT; pfs = VTOPFS(vp); *vpp = vp; return (0); } /* */ /* ARGSUSED */ int procfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } /* * Get file system statistics. */ int procfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { sbp->f_type = MOUNT_PROCFS; sbp->f_bsize = PAGE_SIZE; sbp->f_iosize = PAGE_SIZE; sbp->f_blocks = 1; /* avoid divide by zero in some df's */ sbp->f_bfree = 0; sbp->f_bavail = 0; sbp->f_files = maxproc; /* approx */ sbp->f_ffree = maxproc - nprocs; /* approx */ if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } int procfs_quotactl(mp, cmds, uid, arg, p) struct mount *mp; int cmds; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } int procfs_sync(mp, waitfor) struct mount *mp; int waitfor; { return (0); } int procfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } int procfs_fhtovp(mp, fhp, vpp) struct mount *mp; struct fid *fhp; struct vnode **vpp; { return (EINVAL); } int procfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return EINVAL; } int procfs_init() { return (0); } struct vfsops procfs_vfsops = { procfs_mount, procfs_start, procfs_unmount, procfs_root, procfs_quotactl, procfs_statfs, procfs_sync, procfs_vget, procfs_fhtovp, procfs_vptofh, procfs_init, }; + +VFS_SET(procfs_vfsops, procfs, MOUNT_PROCFS, 0); Index: head/sys/miscfs/procfs/procfs_vnops.c =================================================================== --- head/sys/miscfs/procfs/procfs_vnops.c (revision 2945) +++ head/sys/miscfs/procfs/procfs_vnops.c (revision 2946) @@ -1,833 +1,835 @@ /* * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * 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.6 (Berkeley) 2/7/94 * - * $Id: procfs_vnops.c,v 1.3 1994/08/02 07:45:25 davidg Exp $ + * $Id: procfs_vnops.c,v 1.4 1994/08/18 22:35:15 wollman Exp $ */ /* * procfs vnode interface */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* for PAGE_SIZE */ /* * Vnode Operations. * */ /* * This is a list of the valid names in the * process-specific sub-directories. It is * used in procfs_lookup and procfs_readdir */ static struct pfsnames { u_short d_namlen; char d_name[PROCFS_NAMELEN]; pfstype d_pfstype; } procent[] = { #define N(s) sizeof(s)-1, s /* namlen, nam, type */ { N("file"), Pfile }, { N("mem"), Pmem }, { N("regs"), Pregs }, { N("fpregs"), Pfpregs }, { N("ctl"), Pctl }, { N("status"), Pstatus }, { N("note"), Pnote }, { N("notepg"), Pnotepg }, #undef N }; #define Nprocent (sizeof(procent)/sizeof(procent[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. */ int procfs_open(ap) struct vop_open_args *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); switch (pfs->pfs_type) { case Pmem: if (PFIND(pfs->pfs_pid) == 0) return (ENOENT); /* was ESRCH, jsp */ if ((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL) || (pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE)) return (EBUSY); 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). */ int procfs_close(ap) struct vop_close_args *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; } return (0); } /* * do an ioctl operation on pfsnode (vp). * (vp) is not locked on entry or exit. */ int procfs_ioctl(ap) struct vop_ioctl_args *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. */ int procfs_bmap(ap) struct vop_bmap_args *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; return (0); } /* * _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 not locked on entry or exit. */ int procfs_inactive(ap) struct vop_inactive_args *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); if (PFIND(pfs->pfs_pid) == 0) vgone(ap->a_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. */ int procfs_reclaim(ap) struct vop_reclaim_args *ap; { int error; error = procfs_freevp(ap->a_vp); return (error); } /* * Return POSIX pathconf information applicable to special devices. */ 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). */ int procfs_print(ap) struct vop_print_args *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); printf("tag VT_PROCFS, pid %d, mode %x, flags %x\n", 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. */ int procfs_abortop(ap) struct vop_abortop_args *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 */ 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. */ int procfs_getattr(ap) struct vop_getattr_args *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct vattr *vap = ap->a_vap; struct proc *procp; int error; /* first check the process still exists */ switch (pfs->pfs_type) { case Proot: procp = 0; break; default: procp = PFIND(pfs->pfs_pid); if (procp == 0) 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; /* * 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 Pregs: case Pfpregs: case Pmem: if (procp->p_flag & P_SUGID) vap->va_mode &= ~((VREAD|VWRITE)| ((VREAD|VWRITE)>>3)| ((VREAD|VWRITE)>>6)); break; } /* * 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; /* * 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: vap->va_nlink = 2; vap->va_uid = 0; vap->va_gid = 0; break; case Pproc: vap->va_nlink = 2; vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = procp->p_ucred->cr_gid; break; case Pfile: error = EOPNOTSUPP; break; case Pmem: vap->va_nlink = 1; vap->va_bytes = vap->va_size = ctob(procp->p_vmspace->vm_tsize + procp->p_vmspace->vm_dsize + procp->p_vmspace->vm_ssize); vap->va_uid = procp->p_ucred->cr_uid; vap->va_gid = procp->p_ucred->cr_gid; break; case Pregs: case Pfpregs: 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); } int procfs_setattr(ap) struct vop_setattr_args *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. */ int procfs_access(ap) struct vop_access_args *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 == (uid_t) 0) return (0); vap = &vattr; if (error = VOP_GETATTR(ap->a_vp, vap, ap->a_cred, ap->a_p)) 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(). */ int procfs_lookup(ap) struct vop_lookup_args *ap; { struct componentname *cnp = ap->a_cnp; struct vnode **vpp = ap->a_vpp; struct vnode *dvp = ap->a_dvp; char *pname = cnp->cn_nameptr; int error = 0; pid_t pid; struct vnode *nvp; struct pfsnode *pfs; struct proc *procp; pfstype pfs_type; int i; if (cnp->cn_namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); /*VOP_LOCK(dvp);*/ return (0); } *vpp = NULL; pfs = VTOPFS(dvp); switch (pfs->pfs_type) { case Proot: if (cnp->cn_flags & ISDOTDOT) return (EIO); if (CNEQ(cnp, "curproc", 7)) pid = cnp->cn_proc->p_pid; else pid = atopid(pname, cnp->cn_namelen); if (pid == NO_PID) return (ENOENT); procp = PFIND(pid); if (procp == 0) return (ENOENT); error = procfs_allocvp(dvp->v_mount, &nvp, pid, Pproc); if (error) return (error); nvp->v_type = VDIR; pfs = VTOPFS(nvp); *vpp = nvp; return (0); case Pproc: if (cnp->cn_flags & ISDOTDOT) { error = procfs_root(dvp->v_mount, vpp); return (error); } procp = PFIND(pfs->pfs_pid); if (procp == 0) return (ENOENT); for (i = 0; i < Nprocent; i++) { struct pfsnames *dp = &procent[i]; if (cnp->cn_namelen == dp->d_namlen && bcmp(pname, dp->d_name, dp->d_namlen) == 0) { pfs_type = dp->d_pfstype; goto found; } } return (ENOENT); found: if (pfs_type == Pfile) { nvp = procfs_findtextvp(procp); if (nvp) { VREF(nvp); VOP_LOCK(nvp); } else { error = ENXIO; } } else { error = procfs_allocvp(dvp->v_mount, &nvp, pfs->pfs_pid, pfs_type); if (error) return (error); nvp->v_type = VREG; pfs = VTOPFS(nvp); } *vpp = nvp; return (error); default: return (ENOTDIR); } } /* * 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() */ int procfs_readdir(ap) struct vop_readdir_args *ap; { struct uio *uio = ap->a_uio; struct pfsdent d; struct pfsdent *dp = &d; struct pfsnode *pfs; int error; int count; int i; 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: { while (uio->uio_resid >= UIO_MX) { struct pfsnames *dt; if (i >= Nprocent) break; dt = &procent[i]; dp->d_reclen = UIO_MX; dp->d_fileno = PROCFS_FILENO(pfs->pfs_pid, dt->d_pfstype); dp->d_type = DT_REG; dp->d_namlen = dt->d_namlen; bcopy(dt->d_name, dp->d_name, sizeof(dt->d_name)-1); error = uiomove((caddr_t) dp, UIO_MX, uio); if (error) break; count += UIO_MX; i++; } 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: { int pcnt; #ifdef PROCFS_ZOMBIE int doingzomb = 0; #endif volatile struct proc *p; p = allproc; #define PROCFS_XFILES 1 /* number of other entries, like "curproc" */ pcnt = PROCFS_XFILES; while (p && uio->uio_resid >= UIO_MX) { bzero((char *) dp, UIO_MX); dp->d_type = DT_DIR; dp->d_reclen = UIO_MX; switch (i) { case 0: /* ship out entry for "curproc" */ dp->d_fileno = PROCFS_FILENO(PID_MAX+1, Pproc); dp->d_namlen = sprintf(dp->d_name, "curproc"); break; default: if (pcnt >= i) { dp->d_fileno = PROCFS_FILENO(p->p_pid, Pproc); dp->d_namlen = sprintf(dp->d_name, "%ld", (long) p->p_pid); } p = p->p_next; #ifdef PROCFS_ZOMBIE if (p == 0 && doingzomb == 0) { doingzomb = 1; p = zombproc; } #endif if (pcnt++ < i) continue; break; } error = uiomove((caddr_t) dp, UIO_MX, uio); if (error) break; count += UIO_MX; i++; } break; } default: error = ENOTDIR; break; } uio->uio_offset = i * UIO_MX; return (error); } /* * 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); } /* * procfs vnode operations. */ int (**procfs_vnodeop_p)(); struct vnodeopv_entry_desc procfs_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, procfs_lookup }, /* lookup */ { &vop_create_desc, procfs_create }, /* create */ { &vop_mknod_desc, procfs_mknod }, /* mknod */ { &vop_open_desc, procfs_open }, /* open */ { &vop_close_desc, procfs_close }, /* close */ { &vop_access_desc, procfs_access }, /* access */ { &vop_getattr_desc, procfs_getattr }, /* getattr */ { &vop_setattr_desc, procfs_setattr }, /* setattr */ { &vop_read_desc, procfs_read }, /* read */ { &vop_write_desc, procfs_write }, /* write */ { &vop_ioctl_desc, procfs_ioctl }, /* ioctl */ { &vop_select_desc, procfs_select }, /* select */ { &vop_mmap_desc, procfs_mmap }, /* mmap */ { &vop_fsync_desc, procfs_fsync }, /* fsync */ { &vop_seek_desc, procfs_seek }, /* seek */ { &vop_remove_desc, procfs_remove }, /* remove */ { &vop_link_desc, procfs_link }, /* link */ { &vop_rename_desc, procfs_rename }, /* rename */ { &vop_mkdir_desc, procfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, procfs_rmdir }, /* rmdir */ { &vop_symlink_desc, procfs_symlink }, /* symlink */ { &vop_readdir_desc, procfs_readdir }, /* readdir */ { &vop_readlink_desc, procfs_readlink }, /* readlink */ { &vop_abortop_desc, procfs_abortop }, /* abortop */ { &vop_inactive_desc, procfs_inactive }, /* inactive */ { &vop_reclaim_desc, procfs_reclaim }, /* reclaim */ { &vop_lock_desc, procfs_lock }, /* lock */ { &vop_unlock_desc, procfs_unlock }, /* unlock */ { &vop_bmap_desc, procfs_bmap }, /* bmap */ { &vop_strategy_desc, procfs_strategy }, /* strategy */ { &vop_print_desc, procfs_print }, /* print */ { &vop_islocked_desc, procfs_islocked }, /* islocked */ { &vop_pathconf_desc, procfs_pathconf }, /* pathconf */ { &vop_advlock_desc, procfs_advlock }, /* advlock */ { &vop_blkatoff_desc, procfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, procfs_valloc }, /* valloc */ { &vop_vfree_desc, procfs_vfree }, /* vfree */ { &vop_truncate_desc, procfs_truncate }, /* truncate */ { &vop_update_desc, procfs_update }, /* update */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc procfs_vnodeop_opv_desc = { &procfs_vnodeop_p, procfs_vnodeop_entries }; + +VNODEOP_SET(procfs_vnodeop_opv_desc); Index: head/sys/miscfs/specfs/spec_vnops.c =================================================================== --- head/sys/miscfs/specfs/spec_vnops.c (revision 2945) +++ head/sys/miscfs/specfs/spec_vnops.c (revision 2946) @@ -1,703 +1,705 @@ /* * 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 - * $Id: spec_vnops.c,v 1.3 1994/08/02 07:45:27 davidg Exp $ + * $Id: spec_vnops.c,v 1.4 1994/08/08 09:11:36 davidg Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* symbolic sleep message strings for devices */ char devopn[] = "devopn"; char devio[] = "devio"; char devwait[] = "devwait"; char devin[] = "devin"; char devout[] = "devout"; char devioc[] = "devioc"; char devcls[] = "devcls"; int (**spec_vnodeop_p)(); struct vnodeopv_entry_desc spec_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, spec_lookup }, /* lookup */ { &vop_create_desc, spec_create }, /* create */ { &vop_mknod_desc, spec_mknod }, /* mknod */ { &vop_open_desc, spec_open }, /* open */ { &vop_close_desc, spec_close }, /* close */ { &vop_access_desc, spec_access }, /* access */ { &vop_getattr_desc, spec_getattr }, /* getattr */ { &vop_setattr_desc, spec_setattr }, /* setattr */ { &vop_read_desc, spec_read }, /* read */ { &vop_write_desc, spec_write }, /* write */ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ { &vop_select_desc, spec_select }, /* select */ { &vop_mmap_desc, spec_mmap }, /* mmap */ { &vop_fsync_desc, spec_fsync }, /* fsync */ { &vop_seek_desc, spec_seek }, /* seek */ { &vop_remove_desc, spec_remove }, /* remove */ { &vop_link_desc, spec_link }, /* link */ { &vop_rename_desc, spec_rename }, /* rename */ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */ { &vop_symlink_desc, spec_symlink }, /* symlink */ { &vop_readdir_desc, spec_readdir }, /* readdir */ { &vop_readlink_desc, spec_readlink }, /* readlink */ { &vop_abortop_desc, spec_abortop }, /* abortop */ { &vop_inactive_desc, spec_inactive }, /* inactive */ { &vop_reclaim_desc, spec_reclaim }, /* reclaim */ { &vop_lock_desc, spec_lock }, /* lock */ { &vop_unlock_desc, spec_unlock }, /* unlock */ { &vop_bmap_desc, spec_bmap }, /* bmap */ { &vop_strategy_desc, spec_strategy }, /* strategy */ { &vop_print_desc, spec_print }, /* print */ { &vop_islocked_desc, spec_islocked }, /* islocked */ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ { &vop_advlock_desc, spec_advlock }, /* advlock */ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, spec_valloc }, /* valloc */ { &vop_vfree_desc, spec_vfree }, /* vfree */ { &vop_truncate_desc, spec_truncate }, /* truncate */ { &vop_update_desc, spec_update }, /* update */ { &vop_bwrite_desc, spec_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc spec_vnodeop_opv_desc = { &spec_vnodeop_p, spec_vnodeop_entries }; + +VNODEOP_SET(spec_vnodeop_opv_desc); /* * 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 vnode *bvp, *vp = ap->a_vp; dev_t bdev, dev = (dev_t)vp->v_rdev; register 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 (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 && isdisk(dev, VCHR)) 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); } } VOP_UNLOCK(vp); error = (*cdevsw[maj].d_open)(dev, ap->a_mode, S_IFCHR, ap->a_p); VOP_LOCK(vp); return (error); case VBLK: if ((u_int)maj >= nblkdev) 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) && isdisk(dev, VBLK)) return (EPERM); /* * Do not allow opens of block devices that are * currently mounted. */ if (error = vfs_mountedon(vp)) return (error); return ((*bdevsw[maj].d_open)(dev, ap->a_mode, S_IFBLK, ap->a_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, (*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); error = (*cdevsw[major(vp->v_rdev)].d_read) (vp->v_rdev, uio, ap->a_ioflag); VOP_LOCK(vp); 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 = bsize / DEV_BSIZE; do { bn = (uio->uio_offset / DEV_BSIZE) &~ (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); error = (*cdevsw[major(vp->v_rdev)].d_write) (vp->v_rdev, uio, ap->a_ioflag); VOP_LOCK(vp); 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 = (bsize / DEV_BSIZE) - 1; do { bn = (uio->uio_offset / DEV_BSIZE) &~ 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); } 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 & B_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 /* { struct vnode *a_vp; int a_which; int a_fflags; 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); } } /* * 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; sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1); } #ifdef DIAGNOSTIC if (vp->v_dirtyblkhd.lh_first) { vprint("spec_fsync: dirty", vp); goto loop; } #endif } splx(s); 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; } */ *ap; { if (ap->a_vpp != NULL) *ap->a_vpp = ap->a_vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn; return (0); } /* * At the moment we do not do any locking. */ /* ARGSUSED */ int spec_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; } */ *ap; { return (0); } /* ARGSUSED */ int spec_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; } */ *ap; { 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; dev_t dev = vp->v_rdev; int (*devclose) __P((dev_t, int, int, struct proc *)); 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 == 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. */ if (error = vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 0, 0)) 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) > 1 && (vp->v_flag & VXLOCK) == 0) return (0); 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 (EOPNOTSUPP); } /* * Special device failed operation */ int spec_ebadf() { return (EBADF); } /* * Special device bad operation */ int spec_badop() { panic("spec_badop called"); /* NOTREACHED */ } Index: head/sys/miscfs/umapfs/umap_vfsops.c =================================================================== --- head/sys/miscfs/umapfs/umap_vfsops.c (revision 2945) +++ head/sys/miscfs/umapfs/umap_vfsops.c (revision 2946) @@ -1,407 +1,410 @@ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * the UCLA Ficus project. * * 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. * * @(#)umap_vfsops.c 8.3 (Berkeley) 1/21/94 * - * $Id$ + * $Id: umap_vfsops.c,v 1.2 1994/08/02 07:45:36 davidg Exp $ */ /* * Umap Layer * (See mount_umap(8) for a description of this layer.) */ #include #include #include #include #include #include #include #include #include /* * Mount umap layer */ int umapfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { struct umap_args args; struct vnode *lowerrootvp, *vp; struct vnode *umapm_rootvp; struct umap_mount *amp; u_int size; int error; #ifdef UMAPFS_DIAGNOSTIC printf("umapfs_mount(mp = %x)\n", mp); #endif /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) { return (EOPNOTSUPP); /* return (VFS_MOUNT(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, path, data, ndp, p));*/ } /* * Get argument */ if (error = copyin(data, (caddr_t)&args, sizeof(struct umap_args))) return (error); /* * Find lower node */ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF, UIO_USERSPACE, args.target, p); if (error = namei(ndp)) return (error); /* * Sanity check on lower vnode */ lowerrootvp = ndp->ni_vp; #ifdef UMAPFS_DIAGNOSTIC printf("vp = %x, check for VDIR...\n", lowerrootvp); #endif vrele(ndp->ni_dvp); ndp->ni_dvp = 0; if (lowerrootvp->v_type != VDIR) { vput(lowerrootvp); return (EINVAL); } #ifdef UMAPFS_DIAGNOSTIC printf("mp = %x\n", mp); #endif amp = (struct umap_mount *) malloc(sizeof(struct umap_mount), M_UFSMNT, M_WAITOK); /* XXX */ /* * Save reference to underlying FS */ amp->umapm_vfs = lowerrootvp->v_mount; /* * Now copy in the number of entries and maps for umap mapping. */ amp->info_nentries = args.nentries; amp->info_gnentries = args.gnentries; error = copyin(args.mapdata, (caddr_t)amp->info_mapdata, 2*sizeof(u_long)*args.nentries); if (error) return (error); #ifdef UMAP_DIAGNOSTIC printf("umap_mount:nentries %d\n",args.nentries); for (i = 0; i < args.nentries; i++) printf(" %d maps to %d\n", amp->info_mapdata[i][0], amp->info_mapdata[i][1]); #endif error = copyin(args.gmapdata, (caddr_t)amp->info_gmapdata, 2*sizeof(u_long)*args.nentries); if (error) return (error); #ifdef UMAP_DIAGNOSTIC printf("umap_mount:gnentries %d\n",args.gnentries); for (i = 0; i < args.gnentries; i++) printf(" group %d maps to %d\n", amp->info_gmapdata[i][0], amp->info_gmapdata[i][1]); #endif /* * Save reference. Each mount also holds * a reference on the root vnode. */ error = umap_node_create(mp, lowerrootvp, &vp); /* * Unlock the node (either the lower or the alias) */ VOP_UNLOCK(vp); /* * Make sure the node alias worked */ if (error) { vrele(lowerrootvp); free(amp, M_UFSMNT); /* XXX */ return (error); } /* * Keep a held reference to the root vnode. * It is vrele'd in umapfs_unmount. */ umapm_rootvp = vp; umapm_rootvp->v_flag |= VROOT; amp->umapm_rootvp = umapm_rootvp; if (UMAPVPTOLOWERVP(umapm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) mp->mnt_flag |= MNT_LOCAL; mp->mnt_data = (qaddr_t) amp; getnewfsid(mp, MOUNT_LOFS); (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); #ifdef UMAPFS_DIAGNOSTIC printf("umapfs_mount: lower %s, alias at %s\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); #endif return (0); } /* * VFS start. Nothing needed here - the start routine * on the underlying filesystem will have been called * when that filesystem was mounted. */ int umapfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); /* return (VFS_START(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, flags, p)); */ } /* * Free reference to umap layer */ int umapfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { struct vnode *umapm_rootvp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp; int error; int flags = 0; extern int doforce; #ifdef UMAPFS_DIAGNOSTIC printf("umapfs_unmount(mp = %x)\n", mp); #endif if (mntflags & MNT_FORCE) { /* lofs can never be rootfs so don't check for it */ if (!doforce) return (EINVAL); flags |= FORCECLOSE; } /* * Clear out buffer cache. I don't think we * ever get anything cached at this level at the * moment, but who knows... */ #ifdef notyet mntflushbuf(mp, 0); if (mntinvalbuf(mp, 1)) return (EBUSY); #endif if (umapm_rootvp->v_usecount > 1) return (EBUSY); if (error = vflush(mp, umapm_rootvp, flags)) return (error); #ifdef UMAPFS_DIAGNOSTIC vprint("alias root of lower", umapm_rootvp); #endif /* * Release reference on underlying root vnode */ vrele(umapm_rootvp); /* * And blow it away for future re-use */ vgone(umapm_rootvp); /* * Finally, throw away the umap_mount structure */ free(mp->mnt_data, M_UFSMNT); /* XXX */ mp->mnt_data = 0; return (0); } int umapfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct vnode *vp; #ifdef UMAPFS_DIAGNOSTIC printf("umapfs_root(mp = %x, vp = %x->%x)\n", mp, MOUNTTOUMAPMOUNT(mp)->umapm_rootvp, UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp) ); #endif /* * Return locked reference to root. */ vp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp; VREF(vp); VOP_LOCK(vp); *vpp = vp; return (0); } int umapfs_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (VFS_QUOTACTL(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, cmd, uid, arg, p)); } int umapfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { int error; struct statfs mstat; #ifdef UMAPFS_DIAGNOSTIC printf("umapfs_statfs(mp = %x, vp = %x->%x)\n", mp, MOUNTTOUMAPMOUNT(mp)->umapm_rootvp, UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp) ); #endif bzero(&mstat, sizeof(mstat)); error = VFS_STATFS(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, &mstat, p); if (error) return (error); /* now copy across the "interesting" information and fake the rest */ sbp->f_type = mstat.f_type; sbp->f_flags = mstat.f_flags; sbp->f_bsize = mstat.f_bsize; sbp->f_iosize = mstat.f_iosize; sbp->f_blocks = mstat.f_blocks; sbp->f_bfree = mstat.f_bfree; sbp->f_bavail = mstat.f_bavail; sbp->f_files = mstat.f_files; sbp->f_ffree = mstat.f_ffree; if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } int umapfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { /* * XXX - Assumes no data cached at umap layer. */ return (0); } int umapfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (VFS_VGET(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, ino, vpp)); } int umapfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp) struct mount *mp; struct fid *fidp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred**credanonp; { return (VFS_FHTOVP(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, fidp, nam, vpp, exflagsp,credanonp)); } int umapfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (VFS_VPTOFH(UMAPVPTOLOWERVP(vp), fhp)); } int umapfs_init __P((void)); struct vfsops umap_vfsops = { umapfs_mount, umapfs_start, umapfs_unmount, umapfs_root, umapfs_quotactl, umapfs_statfs, umapfs_sync, umapfs_vget, umapfs_fhtovp, umapfs_vptofh, umapfs_init, }; + +VFS_SET(umap_vfsops, umap, MOUNT_UMAP, 0); + Index: head/sys/miscfs/umapfs/umap_vnops.c =================================================================== --- head/sys/miscfs/umapfs/umap_vnops.c (revision 2945) +++ head/sys/miscfs/umapfs/umap_vnops.c (revision 2946) @@ -1,488 +1,490 @@ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * the UCLA Ficus project. * * 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. * * @(#)umap_vnops.c 8.3 (Berkeley) 1/5/94 - * $Id: umap_vnops.c,v 1.3 1994/08/02 07:45:37 davidg Exp $ + * $Id: umap_vnops.c,v 1.4 1994/08/20 03:48:54 davidg Exp $ */ /* * Umap Layer */ #include #include #include #include #include #include #include #include #include #include int umap_bug_bypass = 0; /* for debugging: enables bypass printf'ing */ /* * This is the 10-Apr-92 bypass routine. * See null_vnops.c:null_bypass for more details. */ int umap_bypass(ap) struct vop_generic_args /* { struct vnodeop_desc *a_desc; } */ *ap; { struct ucred **credpp = 0, *credp = 0; struct ucred *savecredp = 0, *savecompcredp = 0; struct ucred *compcredp = 0; struct vnode **this_vp_p; int error; struct vnode *old_vps[VDESC_MAX_VPS]; struct vnode *vp1 = 0; struct vnode **vps_p[VDESC_MAX_VPS]; struct vnode ***vppp; struct vnodeop_desc *descp = ap->a_desc; int reles, i; struct componentname **compnamepp = 0; if (umap_bug_bypass) printf ("umap_bypass: %s\n", descp->vdesc_name); #ifdef SAFETY /* * We require at least one vp. */ if (descp->vdesc_vp_offsets == NULL || descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET) panic ("umap_bypass: no vp's in map.\n"); #endif /* * Map the vnodes going in. * Later, we'll invoke the operation based on * the first mapped vnode's operation vector. */ reles = descp->vdesc_flags; for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) { if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) break; /* bail out at end of list */ vps_p[i] = this_vp_p = VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[i], ap); if (i == 0) { vp1 = *vps_p[0]; } /* * We're not guaranteed that any but the first vnode * are of our type. Check for and don't map any * that aren't. (Must map first vp or vclean fails.) */ if (i && (*this_vp_p)->v_op != umap_vnodeop_p) { old_vps[i] = NULL; } else { old_vps[i] = *this_vp_p; *(vps_p[i]) = UMAPVPTOLOWERVP(*this_vp_p); if (reles & 1) VREF(*this_vp_p); } } /* * Fix the credentials. (That's the purpose of this layer.) */ if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) { credpp = VOPARG_OFFSETTO(struct ucred**, descp->vdesc_cred_offset, ap); /* Save old values */ savecredp = (*credpp); (*credpp) = crdup(savecredp); credp = *credpp; if (umap_bug_bypass && credp->cr_uid != 0) printf("umap_bypass: user was %d, group %d\n", credp->cr_uid, credp->cr_gid); /* Map all ids in the credential structure. */ umap_mapids(vp1->v_mount, credp); if (umap_bug_bypass && credp->cr_uid != 0) printf("umap_bypass: user now %d, group %d\n", credp->cr_uid, credp->cr_gid); } /* BSD often keeps a credential in the componentname structure * for speed. If there is one, it better get mapped, too. */ if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) { compnamepp = VOPARG_OFFSETTO(struct componentname**, descp->vdesc_componentname_offset, ap); compcredp = (*compnamepp)->cn_cred; savecompcredp = compcredp; compcredp = (*compnamepp)->cn_cred = crdup(savecompcredp); if (umap_bug_bypass && compcredp->cr_uid != 0) printf("umap_bypass: component credit user was %d, group %d\n", compcredp->cr_uid, compcredp->cr_gid); /* Map all ids in the credential structure. */ umap_mapids(vp1->v_mount, compcredp); if (umap_bug_bypass && compcredp->cr_uid != 0) printf("umap_bypass: component credit user now %d, group %d\n", compcredp->cr_uid, compcredp->cr_gid); } /* * Call the operation on the lower layer * with the modified argument structure. */ error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap); /* * Maintain the illusion of call-by-value * by restoring vnodes in the argument structure * to their original value. */ reles = descp->vdesc_flags; for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) { if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) break; /* bail out at end of list */ if (old_vps[i]) { *(vps_p[i]) = old_vps[i]; if (reles & 1) vrele(*(vps_p[i])); }; }; /* * Map the possible out-going vpp * (Assumes that the lower layer always returns * a VREF'ed vpp unless it gets an error.) */ if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET && !(descp->vdesc_flags & VDESC_NOMAP_VPP) && !error) { if (descp->vdesc_flags & VDESC_VPP_WILLRELE) goto out; vppp = VOPARG_OFFSETTO(struct vnode***, descp->vdesc_vpp_offset, ap); error = umap_node_create(old_vps[0]->v_mount, **vppp, *vppp); }; out: /* * Free duplicate cred structure and restore old one. */ if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) { if (umap_bug_bypass && credp && credp->cr_uid != 0) printf("umap_bypass: returning-user was %d\n", credp->cr_uid); crfree(credp); (*credpp) = savecredp; if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0) printf("umap_bypass: returning-user now %d\n\n", (*credpp)->cr_uid); } if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) { if (umap_bug_bypass && compcredp && compcredp->cr_uid != 0) printf("umap_bypass: returning-component-user was %d\n", compcredp->cr_uid); crfree(compcredp); (*compnamepp)->cn_cred = savecompcredp; if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0) printf("umap_bypass: returning-component-user now %d\n", compcredp->cr_uid); } return (error); } /* * We handle getattr to change the fsid. */ int umap_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { short uid, gid; int error, tmpid, nentries, gnentries; u_long (*mapdata)[2], (*gmapdata)[2]; struct vnode **vp1p; struct vnodeop_desc *descp = ap->a_desc; if (error = umap_bypass(ap)) return (error); /* Requires that arguments be restored. */ ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; /* * Umap needs to map the uid and gid returned by a stat * into the proper values for this site. This involves * finding the returned uid in the mapping information, * translating it into the uid on the other end, * and filling in the proper field in the vattr * structure pointed to by ap->a_vap. The group * is easier, since currently all groups will be * translate to the NULLGROUP. */ /* Find entry in map */ uid = ap->a_vap->va_uid; gid = ap->a_vap->va_gid; if (umap_bug_bypass) printf("umap_getattr: mapped uid = %d, mapped gid = %d\n", uid, gid); vp1p = VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[0], ap); nentries = MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_nentries; mapdata = (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_mapdata); gnentries = MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gnentries; gmapdata = (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gmapdata); /* Reverse map the uid for the vnode. Since it's a reverse map, we can't use umap_mapids() to do it. */ tmpid = umap_reverse_findid(uid, mapdata, nentries); if (tmpid != -1) { ap->a_vap->va_uid = (uid_t) tmpid; if (umap_bug_bypass) printf("umap_getattr: original uid = %d\n", uid); } else ap->a_vap->va_uid = (uid_t) NOBODY; /* Reverse map the gid for the vnode. */ tmpid = umap_reverse_findid(gid, gmapdata, gnentries); if (tmpid != -1) { ap->a_vap->va_gid = (gid_t) tmpid; if (umap_bug_bypass) printf("umap_getattr: original gid = %d\n", gid); } else ap->a_vap->va_gid = (gid_t) NULLGROUP; return (0); } int umap_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { /* * Do nothing (and _don't_ bypass). * Wait to vrele lowervp until reclaim, * so that until then our umap_node is in the * cache and reusable. * */ return (0); } int umap_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; struct umap_node *xp = VTOUMAP(vp); struct vnode *lowervp = xp->umap_lowervp; /* After this assignment, this node will not be re-used. */ xp->umap_lowervp = NULL; remque(xp); FREE(vp->v_data, M_TEMP); vp->v_data = NULL; vrele(lowervp); return (0); } int umap_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 = UMAPVPTOLOWERVP(bp->b_vp); error = VOP_STRATEGY(ap->a_bp); bp->b_vp = savedvp; return (error); } int umap_bwrite(ap) struct vop_bwrite_args /* { struct buf *a_bp; } */ *ap; { struct buf *bp = ap->a_bp; int error; struct vnode *savedvp; savedvp = bp->b_vp; bp->b_vp = UMAPVPTOLOWERVP(bp->b_vp); error = VOP_BWRITE(ap->a_bp); bp->b_vp = savedvp; return (error); } int umap_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; printf("\ttag VT_UMAPFS, vp=%x, lowervp=%x\n", vp, UMAPVPTOLOWERVP(vp)); return (0); } int umap_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 componentname *compnamep; struct ucred *compcredp, *savecompcredp; struct vnode *vp; /* * Rename is irregular, having two componentname structures. * We need to map the cre in the second structure, * and then bypass takes care of the rest. */ vp = ap->a_fdvp; compnamep = ap->a_tcnp; compcredp = compnamep->cn_cred; savecompcredp = compcredp; compcredp = compnamep->cn_cred = crdup(savecompcredp); if (umap_bug_bypass && compcredp->cr_uid != 0) printf("umap_rename: rename component credit user was %d, group %d\n", compcredp->cr_uid, compcredp->cr_gid); /* Map all ids in the credential structure. */ umap_mapids(vp->v_mount, compcredp); if (umap_bug_bypass && compcredp->cr_uid != 0) printf("umap_rename: rename component credit user now %d, group %d\n", compcredp->cr_uid, compcredp->cr_gid); error = umap_bypass(ap); /* Restore the additional mapped componentname cred structure. */ crfree(compcredp); compnamep->cn_cred = savecompcredp; return error; } /* * Global vfs data structures */ /* * XXX - strategy, bwrite are hand coded currently. They should * go away with a merged buffer/block cache. * */ int (**umap_vnodeop_p)(); struct vnodeopv_entry_desc umap_vnodeop_entries[] = { { &vop_default_desc, umap_bypass }, { &vop_getattr_desc, umap_getattr }, { &vop_inactive_desc, umap_inactive }, { &vop_reclaim_desc, umap_reclaim }, { &vop_print_desc, umap_print }, { &vop_rename_desc, umap_rename }, { &vop_strategy_desc, umap_strategy }, { &vop_bwrite_desc, umap_bwrite }, { (struct vnodeop_desc*) NULL, (int(*)()) NULL } }; struct vnodeopv_desc umap_vnodeop_opv_desc = { &umap_vnodeop_p, umap_vnodeop_entries }; + +VNODEOP_SET(umap_vnodeop_opv_desc); Index: head/sys/miscfs/union/union_vfsops.c =================================================================== --- head/sys/miscfs/union/union_vfsops.c (revision 2945) +++ head/sys/miscfs/union/union_vfsops.c (revision 2946) @@ -1,551 +1,553 @@ /* * Copyright (c) 1994 The Regents of the University of California. * Copyright (c) 1994 Jan-Simon Pendry. * 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. * * @(#)union_vfsops.c 8.7 (Berkeley) 3/5/94 - * $Id$ + * $Id: union_vfsops.c,v 1.3 1994/08/02 07:45:45 davidg Exp $ */ /* * Union Layer */ #include #include #include #include #include #include #include #include #include #include #include #include /* * Mount union filesystem */ int union_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { int error = 0; struct union_args args; struct vnode *lowerrootvp = NULLVP; struct vnode *upperrootvp = NULLVP; struct union_mount *um; struct ucred *cred = 0; struct ucred *scred; struct vattr va; char *cp = 0; int len; u_int size; #ifdef UNION_DIAGNOSTIC printf("union_mount(mp = %x)\n", mp); #endif /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) { /* * Need to provide. * 1. a way to convert between rdonly and rdwr mounts. * 2. support for nfs exports. */ error = EOPNOTSUPP; goto bad; } /* * Take a copy of the process's credentials. This isn't * quite right since the euid will always be zero and we * want to get the "real" users credentials. So fix up * the uid field after taking the copy. */ cred = crdup(p->p_ucred); cred->cr_uid = p->p_cred->p_ruid; /* * Ensure the *real* user has write permission on the * mounted-on directory. This allows the mount_union * command to be made setuid root so allowing anyone * to do union mounts onto any directory on which they * have write permission and which they also own. */ error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p); if (error) goto bad; if ((va.va_uid != cred->cr_uid) && (cred->cr_uid != 0)) { error = EACCES; goto bad; } error = VOP_ACCESS(mp->mnt_vnodecovered, VWRITE, cred, p); if (error) goto bad; /* * Get argument */ if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args))) goto bad; lowerrootvp = mp->mnt_vnodecovered; VREF(lowerrootvp); /* * Find upper node. Use the real process credentials, * not the effective ones since this will have come * through a setuid process (mount_union). All this * messing around with permissions is entirely bogus * and should be removed by allowing any user straight * past the mount system call. */ scred = p->p_ucred; p->p_ucred = cred; NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT, UIO_USERSPACE, args.target, p); p->p_ucred = scred; if (error = namei(ndp)) goto bad; upperrootvp = ndp->ni_vp; vrele(ndp->ni_dvp); ndp->ni_dvp = NULL; if (upperrootvp->v_type != VDIR) { error = EINVAL; goto bad; } um = (struct union_mount *) malloc(sizeof(struct union_mount), M_UFSMNT, M_WAITOK); /* XXX */ /* * Keep a held reference to the target vnodes. * They are vrele'd in union_unmount. * * Depending on the _BELOW flag, the filesystems are * viewed in a different order. In effect, this is the * same as providing a mount under option to the mount syscall. */ um->um_op = args.mntflags & UNMNT_OPMASK; switch (um->um_op) { case UNMNT_ABOVE: um->um_lowervp = lowerrootvp; um->um_uppervp = upperrootvp; break; case UNMNT_BELOW: um->um_lowervp = upperrootvp; um->um_uppervp = lowerrootvp; break; case UNMNT_REPLACE: vrele(lowerrootvp); lowerrootvp = NULLVP; um->um_uppervp = upperrootvp; um->um_lowervp = lowerrootvp; break; default: error = EINVAL; goto bad; } um->um_cred = cred; um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask; /* * Depending on what you think the MNT_LOCAL flag might mean, * you may want the && to be || on the conditional below. * At the moment it has been defined that the filesystem is * only local if it is all local, ie the MNT_LOCAL flag implies * that the entire namespace is local. If you think the MNT_LOCAL * flag implies that some of the files might be stored locally * then you will want to change the conditional. */ if (um->um_op == UNMNT_ABOVE) { if (((um->um_lowervp == NULLVP) || (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) && (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL)) mp->mnt_flag |= MNT_LOCAL; } /* * Copy in the upper layer's RDONLY flag. This is for the benefit * of lookup() which explicitly checks the flag, rather than asking * the filesystem for it's own opinion. This means, that an update * mount of the underlying filesystem to go from rdonly to rdwr * will leave the unioned view as read-only. */ mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY); /* * This is a user mount. Privilege check for unmount * will be done in union_unmount. */ mp->mnt_flag |= MNT_USER; mp->mnt_data = (qaddr_t) um; getnewfsid(mp, MOUNT_UNION); (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); switch (um->um_op) { case UNMNT_ABOVE: cp = ""; break; case UNMNT_BELOW: cp = ""; break; case UNMNT_REPLACE: cp = ""; break; } len = strlen(cp); bcopy(cp, mp->mnt_stat.f_mntfromname, len); cp = mp->mnt_stat.f_mntfromname + len; len = MNAMELEN - len; (void) copyinstr(args.target, cp, len - 1, &size); bzero(cp + size, len - size); #ifdef UNION_DIAGNOSTIC printf("union_mount: from %s, on %s\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); #endif return (0); bad: if (cred) crfree(cred); if (upperrootvp) vrele(upperrootvp); if (lowerrootvp) vrele(lowerrootvp); return (error); } /* * VFS start. Nothing needed here - the start routine * on the underlying filesystem(s) will have been called * when that filesystem was mounted. */ int union_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } /* * Free reference to union layer */ int union_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { struct union_mount *um = MOUNTTOUNIONMOUNT(mp); struct vnode *um_rootvp; int error; int flags = 0; extern int doforce; #ifdef UNION_DIAGNOSTIC printf("union_unmount(mp = %x)\n", mp); #endif /* only the mounter, or superuser can unmount */ if ((p->p_cred->p_ruid != um->um_cred->cr_uid) && (error = suser(p->p_ucred, &p->p_acflag))) return (error); if (mntflags & MNT_FORCE) { /* union can never be rootfs so don't check for it */ if (!doforce) return (EINVAL); flags |= FORCECLOSE; } if (error = union_root(mp, &um_rootvp)) return (error); if (um_rootvp->v_usecount > 1) { vput(um_rootvp); return (EBUSY); } if (error = vflush(mp, um_rootvp, flags)) { vput(um_rootvp); return (error); } #ifdef UNION_DIAGNOSTIC vprint("alias root of lower", um_rootvp); #endif /* * Discard references to upper and lower target vnodes. */ if (um->um_lowervp) vrele(um->um_lowervp); vrele(um->um_uppervp); crfree(um->um_cred); /* * Release reference on underlying root vnode */ vput(um_rootvp); /* * And blow it away for future re-use */ vgone(um_rootvp); /* * Finally, throw away the union_mount structure */ free(mp->mnt_data, M_UFSMNT); /* XXX */ mp->mnt_data = 0; return (0); } int union_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct union_mount *um = MOUNTTOUNIONMOUNT(mp); int error; int loselock; #ifdef UNION_DIAGNOSTIC printf("union_root(mp = %x, lvp = %x, uvp = %x)\n", mp, um->um_lowervp, um->um_uppervp); #endif /* * Return locked reference to root. */ VREF(um->um_uppervp); if ((um->um_op == UNMNT_BELOW) && VOP_ISLOCKED(um->um_uppervp)) { loselock = 1; } else { VOP_LOCK(um->um_uppervp); loselock = 0; } if (um->um_lowervp) VREF(um->um_lowervp); error = union_allocvp(vpp, mp, (struct vnode *) 0, (struct vnode *) 0, (struct componentname *) 0, um->um_uppervp, um->um_lowervp); if (error) { if (!loselock) VOP_UNLOCK(um->um_uppervp); vrele(um->um_uppervp); if (um->um_lowervp) vrele(um->um_lowervp); } else { (*vpp)->v_flag |= VROOT; if (loselock) VTOUNION(*vpp)->un_flags &= ~UN_ULOCK; } return (error); } int union_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } int union_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { int error; struct union_mount *um = MOUNTTOUNIONMOUNT(mp); struct statfs mstat; int lbsize; #ifdef UNION_DIAGNOSTIC printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp, um->um_lowervp, um->um_uppervp); #endif bzero(&mstat, sizeof(mstat)); if (um->um_lowervp) { error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p); if (error) return (error); } /* now copy across the "interesting" information and fake the rest */ #if 0 sbp->f_type = mstat.f_type; sbp->f_flags = mstat.f_flags; sbp->f_bsize = mstat.f_bsize; sbp->f_iosize = mstat.f_iosize; #endif lbsize = mstat.f_bsize; sbp->f_blocks = mstat.f_blocks; sbp->f_bfree = mstat.f_bfree; sbp->f_bavail = mstat.f_bavail; sbp->f_files = mstat.f_files; sbp->f_ffree = mstat.f_ffree; error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p); if (error) return (error); sbp->f_type = MOUNT_UNION; sbp->f_flags = mstat.f_flags; sbp->f_bsize = mstat.f_bsize; sbp->f_iosize = mstat.f_iosize; /* * if the lower and upper blocksizes differ, then frig the * block counts so that the sizes reported by df make some * kind of sense. none of this makes sense though. */ if (mstat.f_bsize != lbsize) { sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize; sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize; sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize; } sbp->f_blocks += mstat.f_blocks; sbp->f_bfree += mstat.f_bfree; sbp->f_bavail += mstat.f_bavail; sbp->f_files += mstat.f_files; sbp->f_ffree += mstat.f_ffree; if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } int union_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { /* * XXX - Assumes no data cached at union layer. */ return (0); } int union_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } int union_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp) struct mount *mp; struct fid *fidp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred **credanonp; { return (EOPNOTSUPP); } int union_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (EOPNOTSUPP); } int union_init __P((void)); struct vfsops union_vfsops = { union_mount, union_start, union_unmount, union_root, union_quotactl, union_statfs, union_sync, union_vget, union_fhtovp, union_vptofh, union_init, }; + +VFS_SET(union_vfsops, union, MOUNT_UNION, 0); Index: head/sys/miscfs/union/union_vnops.c =================================================================== --- head/sys/miscfs/union/union_vnops.c (revision 2945) +++ head/sys/miscfs/union/union_vnops.c (revision 2946) @@ -1,1496 +1,1498 @@ /* * Copyright (c) 1992, 1993, 1994 The Regents of the University of California. * Copyright (c) 1992, 1993, 1994 Jan-Simon Pendry. * 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.6 (Berkeley) 2/17/94 - * $Id$ + * $Id: union_vnops.c,v 1.3 1994/08/02 07:45:47 davidg Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define FIXUP(un) { \ if (((un)->un_flags & UN_ULOCK) == 0) { \ union_fixup(un); \ } \ } static void union_fixup(un) struct union_node *un; { VOP_LOCK(un->un_uppervp); un->un_flags |= UN_ULOCK; } static int union_lookup1(udvp, dvp, vpp, cnp) struct vnode *udvp; struct vnode *dvp; struct vnode **vpp; struct componentname *cnp; { int error; struct vnode *tdvp; struct mount *mp; /* * 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) { for (;;) { /* * Don't do the NOCROSSMOUNT check * at this level. By definition, * union fs deals with namespaces, not * filesystems. */ if ((dvp->v_flag & VROOT) == 0) break; tdvp = dvp; dvp = dvp->v_mount->mnt_vnodecovered; vput(tdvp); VREF(dvp); VOP_LOCK(dvp); } } 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)) VOP_LOCK(dvp); 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 (mp->mnt_flag & MNT_MLOCK) { mp->mnt_flag |= MNT_MWAIT; sleep((caddr_t) mp, PVFS); continue; } if (error = VFS_ROOT(mp, &tdvp)) { vput(dvp); return (error); } vput(dvp); dvp = tdvp; } *vpp = dvp; return (0); } 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; int lockparent = cnp->cn_flags & LOCKPARENT; int rdonly = cnp->cn_flags & RDONLY; struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount); struct ucred *saved_cred = 0; cnp->cn_flags |= LOCKPARENT; upperdvp = dun->un_uppervp; lowerdvp = dun->un_lowervp; uppervp = NULLVP; lowervp = NULLVP; /* * 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) { FIXUP(dun); uerror = union_lookup1(um->um_uppervp, upperdvp, &uppervp, cnp); /*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); } } 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) { int nameiop; VOP_LOCK(lowerdvp); /* * 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; } 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); if (cnp->cn_consume != 0) { if (uppervp) { 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 (!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); uerror = union_mkshadow(um, upperdvp, cnp, &uppervp); VOP_LOCK(upperdvp); dun->un_flags |= UN_ULOCK; if (uerror) { if (lowervp) { vput(lowervp); lowervp = NULLVP; } return (uerror); } } } if (lowervp) VOP_UNLOCK(lowervp); error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp, uppervp, lowervp); if (error) { if (uppervp) vput(uppervp); if (lowervp) vrele(lowervp); } else { if (*ap->a_vpp != dvp) if (!lockparent || !(cnp->cn_flags & ISLASTCN)) VOP_UNLOCK(dvp); } return (error); } 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; if (dvp) { int error; struct vnode *vp; FIXUP(un); VREF(dvp); un->un_flags |= UN_KLOCK; vput(ap->a_dvp); error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap); if (error) return (error); error = union_allocvp( ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp, NULLVP, ap->a_cnp, vp, NULLVP); if (error) vput(vp); return (error); } vput(ap->a_dvp); return (EROFS); } 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; if (dvp) { int error; struct vnode *vp; FIXUP(un); VREF(dvp); un->un_flags |= UN_KLOCK; vput(ap->a_dvp); error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap); if (error) return (error); if (vp) { error = union_allocvp( ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp, NULLVP, ap->a_cnp, vp, NULLVP); if (error) vput(vp); } return (error); } vput(ap->a_dvp); return (EROFS); } 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)) { struct vnode *vp; int i; /* * Open the named file in the upper layer. Note that * the file may have come into existence *since* the * lookup was done, since the upper layer may really * be a loopback mount of some other filesystem... * so open the file with exclusive create and barf if * it already exists. * XXX - perhaps should re-lookup the node (once more * with feeling) and simply open that. Who knows. */ error = union_vn_create(&vp, un, p); if (error) return (error); /* at this point, uppervp is locked */ union_newupper(un, vp); un->un_flags |= UN_ULOCK; /* * Now, if the file is being opened with truncation, * then the (new) upper vnode is ready to fly, * otherwise the data from the lower vnode must be * copied to the upper layer first. This only works * for regular files (check is made above). */ if ((mode & O_TRUNC) == 0) { /* * XXX - should not ignore errors * from VOP_CLOSE */ VOP_LOCK(tvp); error = VOP_OPEN(tvp, FREAD, cred, p); if (error == 0) { error = union_copyfile(p, cred, tvp, un->un_uppervp); VOP_UNLOCK(tvp); (void) VOP_CLOSE(tvp, FREAD); } else { VOP_UNLOCK(tvp); } #ifdef UNION_DIAGNOSTIC if (!error) uprintf("union: copied up %s\n", un->un_path); #endif } un->un_flags &= ~UN_ULOCK; VOP_UNLOCK(un->un_uppervp); union_vn_close(un->un_uppervp, FWRITE, cred, p); VOP_LOCK(un->un_uppervp); un->un_flags |= UN_ULOCK; /* * Subsequent IOs will go to the top layer, so * call close on the lower vnode and open on the * upper vnode to ensure that the filesystem keeps * its references counts right. This doesn't do * the right thing with (cred) and (FREAD) though. * Ignoring error returns is not righ, either. */ for (i = 0; i < un->un_openl; i++) { (void) VOP_CLOSE(tvp, FREAD); (void) VOP_OPEN(un->un_uppervp, FREAD, cred, p); } un->un_openl = 0; if (error == 0) error = VOP_OPEN(un->un_uppervp, mode, cred, p); return (error); } /* * Just open the lower vnode */ un->un_openl++; VOP_LOCK(tvp); error = VOP_OPEN(tvp, mode, cred, p); VOP_UNLOCK(tvp); return (error); } FIXUP(un); error = VOP_OPEN(tvp, mode, cred, p); return (error); } 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 (un->un_uppervp) { vp = un->un_uppervp; } else { #ifdef UNION_DIAGNOSTIC if (un->un_openl <= 0) panic("union: un_openl cnt"); #endif --un->un_openl; vp = un->un_lowervp; } return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p)); } /* * 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. */ 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); int error = EACCES; struct vnode *vp; if (vp = un->un_uppervp) { FIXUP(un); return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p)); } if (vp = un->un_lowervp) { VOP_LOCK(vp); error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p); if (error == 0) { struct union_mount *um = MOUNTTOUNIONMOUNT(vp->v_mount); if (um->um_op == UNMNT_BELOW) error = VOP_ACCESS(vp, ap->a_mode, um->um_cred, ap->a_p); } VOP_UNLOCK(vp); if (error) return (error); } return (error); } /* * We handle getattr only to change the fsid. */ 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 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) { FIXUP(un); error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); if (error) return (error); } 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) { VOP_LOCK(vp); error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p); VOP_UNLOCK(vp); if (error) return (error); } if ((vap != ap->a_vap) && (vap->va_type == VDIR)) ap->a_vap->va_nlink += vap->va_nlink; vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; return (0); } 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); int error; /* * 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) && (ap->a_vap->va_size == 0)) { struct vnode *vp; error = union_vn_create(&vp, un, ap->a_p); if (error) return (error); /* at this point, uppervp is locked */ union_newupper(un, vp); VOP_UNLOCK(vp); union_vn_close(un->un_uppervp, FWRITE, ap->a_cred, ap->a_p); VOP_LOCK(vp); un->un_flags |= UN_ULOCK; } /* * Try to set attributes in upper layer, * otherwise return read-only filesystem error. */ if (un->un_uppervp != NULLVP) { FIXUP(un); error = VOP_SETATTR(un->un_uppervp, ap->a_vap, ap->a_cred, ap->a_p); } else { error = EROFS; } return (error); } 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 vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) VOP_LOCK(vp); else FIXUP(VTOUNION(ap->a_vp)); error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred); if (dolock) VOP_UNLOCK(vp); return (error); } 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 = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) VOP_LOCK(vp); else FIXUP(VTOUNION(ap->a_vp)); error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred); if (dolock) VOP_UNLOCK(vp); return (error); } 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; { return (VOP_IOCTL(OTHERVP(ap->a_vp), ap->a_command, ap->a_data, ap->a_fflag, ap->a_cred, ap->a_p)); } int union_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; { return (VOP_SELECT(OTHERVP(ap->a_vp), ap->a_which, ap->a_fflags, ap->a_cred, ap->a_p)); } int union_mmap(ap) struct vop_mmap_args /* { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { return (VOP_MMAP(OTHERVP(ap->a_vp), ap->a_fflags, ap->a_cred, ap->a_p)); } 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 vnode *targetvp = OTHERVP(ap->a_vp); if (targetvp) { int dolock = (targetvp == LOWERVP(ap->a_vp)); if (dolock) VOP_LOCK(targetvp); else FIXUP(VTOUNION(ap->a_vp)); error = VOP_FSYNC(targetvp, ap->a_cred, ap->a_waitfor, ap->a_p); if (dolock) VOP_UNLOCK(targetvp); } return (error); } 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; { return (VOP_SEEK(OTHERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred)); } 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); if (dun->un_uppervp && un->un_uppervp) { struct vnode *dvp = dun->un_uppervp; struct vnode *vp = un->un_uppervp; FIXUP(dun); VREF(dvp); dun->un_flags |= UN_KLOCK; vput(ap->a_dvp); FIXUP(un); VREF(vp); un->un_flags |= UN_KLOCK; vput(ap->a_vp); error = VOP_REMOVE(dvp, vp, ap->a_cnp); if (!error) union_removed_upper(un); /* * XXX: should create a whiteout here */ } else { /* * XXX: should create a whiteout here */ vput(ap->a_dvp); vput(ap->a_vp); error = EROFS; } return (error); } int union_link(ap) struct vop_link_args /* { struct vnode *a_vp; struct vnode *a_tdvp; struct componentname *a_cnp; } */ *ap; { int error; struct union_node *dun = VTOUNION(ap->a_vp); struct union_node *un = VTOUNION(ap->a_tdvp); if (dun->un_uppervp && un->un_uppervp) { struct vnode *dvp = dun->un_uppervp; struct vnode *vp = un->un_uppervp; FIXUP(dun); VREF(dvp); dun->un_flags |= UN_KLOCK; vput(ap->a_vp); FIXUP(un); VREF(vp); vrele(ap->a_tdvp); error = VOP_LINK(dvp, vp, ap->a_cnp); } else { /* * XXX: need to copy to upper layer * and do the link there. */ vput(ap->a_vp); vrele(ap->a_tdvp); error = EROFS; } return (error); } 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; if (fdvp->v_op == union_vnodeop_p) { /* always true */ struct union_node *un = VTOUNION(fdvp); if (un->un_uppervp == NULLVP) { error = EROFS; goto bad; } FIXUP(un); 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) { error = EROFS; goto bad; } FIXUP(un); 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) { error = EROFS; goto bad; } tdvp = un->un_uppervp; VREF(tdvp); un->un_flags |= UN_KLOCK; vput(ap->a_tdvp); } if (tvp && tvp->v_op == union_vnodeop_p) { struct union_node *un = VTOUNION(tvp); if (un->un_uppervp == NULLVP) { error = EROFS; goto bad; } tvp = un->un_uppervp; VREF(tvp); un->un_flags |= UN_KLOCK; vput(ap->a_tvp); } return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp)); bad: vrele(fdvp); vrele(fvp); vput(tdvp); if (tvp) vput(tvp); return (error); } 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; if (dvp) { int error; struct vnode *vp; FIXUP(un); VREF(dvp); un->un_flags |= UN_KLOCK; vput(ap->a_dvp); error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap); if (error) return (error); error = union_allocvp( ap->a_vpp, ap->a_dvp->v_mount, ap->a_dvp, NULLVP, ap->a_cnp, vp, NULLVP); if (error) vput(vp); return (error); } vput(ap->a_dvp); return (EROFS); } 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); if (dun->un_uppervp && un->un_uppervp) { struct vnode *dvp = dun->un_uppervp; struct vnode *vp = un->un_uppervp; FIXUP(dun); VREF(dvp); dun->un_flags |= UN_KLOCK; vput(ap->a_dvp); FIXUP(un); VREF(vp); un->un_flags |= UN_KLOCK; vput(ap->a_vp); error = VOP_RMDIR(dvp, vp, ap->a_cnp); if (!error) union_removed_upper(un); /* * XXX: should create a whiteout here */ } else { /* * XXX: should create a whiteout here */ vput(ap->a_dvp); vput(ap->a_vp); error = EROFS; } return (error); } 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; if (dvp) { int error; struct vnode *vp; struct mount *mp = ap->a_dvp->v_mount; FIXUP(un); VREF(dvp); un->un_flags |= UN_KLOCK; vput(ap->a_dvp); error = VOP_SYMLINK(dvp, &vp, ap->a_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. */ int union_readdir(ap) struct vop_readdir_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { int error = 0; struct union_node *un = VTOUNION(ap->a_vp); if (un->un_uppervp) { FIXUP(un); error = VOP_READDIR(un->un_uppervp, ap->a_uio, ap->a_cred); } return (error); } int union_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { int error; struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) VOP_LOCK(vp); else FIXUP(VTOUNION(ap->a_vp)); error = VOP_READLINK(vp, ap->a_uio, ap->a_cred); if (dolock) VOP_UNLOCK(vp); return (error); } int union_abortop(ap) struct vop_abortop_args /* { struct vnode *a_dvp; struct componentname *a_cnp; } */ *ap; { int error; 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) VOP_LOCK(vp); else FIXUP(VTOUNION(ap->a_dvp)); } error = VOP_ABORTOP(vp, ap->a_cnp); if (islocked && dolock) VOP_UNLOCK(vp); return (error); } int union_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { /* * 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. */ #ifdef UNION_DIAGNOSTIC struct union_node *un = VTOUNION(ap->a_vp); if (un->un_flags & UN_LOCKED) panic("union: inactivating locked node"); #endif return (0); } int union_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { union_freevp(ap->a_vp); return (0); } int union_lock(ap) struct vop_lock_args *ap; { struct vnode *vp = ap->a_vp; struct union_node *un; start: while (vp->v_flag & VXLOCK) { vp->v_flag |= VXWANT; sleep((caddr_t)vp, PINOD); } un = VTOUNION(vp); if (un->un_uppervp) { if ((un->un_flags & UN_ULOCK) == 0) { un->un_flags |= UN_ULOCK; VOP_LOCK(un->un_uppervp); } #ifdef DIAGNOSTIC if (un->un_flags & UN_KLOCK) panic("union: dangling upper lock"); #endif } 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; sleep((caddr_t) &un->un_flags, PINOD); 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); } int union_unlock(ap) struct vop_lock_args *ap; { struct union_node *un = VTOUNION(ap->a_vp); #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); un->un_flags &= ~(UN_ULOCK|UN_KLOCK); if (un->un_flags & UN_WANT) { un->un_flags &= ~UN_WANT; wakeup((caddr_t) &un->un_flags); } #ifdef DIAGNOSTIC un->un_pid = 0; #endif return (0); } 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; } */ *ap; { int error; struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) VOP_LOCK(vp); else FIXUP(VTOUNION(ap->a_vp)); error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp); if (dolock) VOP_UNLOCK(vp); return (error); } int union_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { struct vnode *vp = ap->a_vp; printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n", vp, UPPERVP(vp), LOWERVP(vp)); return (0); } int union_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0); } int union_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { int error; struct vnode *vp = OTHERVP(ap->a_vp); int dolock = (vp == LOWERVP(ap->a_vp)); if (dolock) VOP_LOCK(vp); else FIXUP(VTOUNION(ap->a_vp)); error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval); if (dolock) VOP_UNLOCK(vp); return (error); } 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; { return (VOP_ADVLOCK(OTHERVP(ap->a_vp), ap->a_id, ap->a_op, ap->a_fl, ap->a_flags)); } /* * XXX - vop_strategy must be hand coded because it has no * vnode in its arguments. * This goes away with a merged VM/buffer cache. */ 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 */ int (**union_vnodeop_p)(); struct vnodeopv_entry_desc union_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, union_lookup }, /* lookup */ { &vop_create_desc, union_create }, /* create */ { &vop_mknod_desc, union_mknod }, /* mknod */ { &vop_open_desc, union_open }, /* open */ { &vop_close_desc, union_close }, /* close */ { &vop_access_desc, union_access }, /* access */ { &vop_getattr_desc, union_getattr }, /* getattr */ { &vop_setattr_desc, union_setattr }, /* setattr */ { &vop_read_desc, union_read }, /* read */ { &vop_write_desc, union_write }, /* write */ { &vop_ioctl_desc, union_ioctl }, /* ioctl */ { &vop_select_desc, union_select }, /* select */ { &vop_mmap_desc, union_mmap }, /* mmap */ { &vop_fsync_desc, union_fsync }, /* fsync */ { &vop_seek_desc, union_seek }, /* seek */ { &vop_remove_desc, union_remove }, /* remove */ { &vop_link_desc, union_link }, /* link */ { &vop_rename_desc, union_rename }, /* rename */ { &vop_mkdir_desc, union_mkdir }, /* mkdir */ { &vop_rmdir_desc, union_rmdir }, /* rmdir */ { &vop_symlink_desc, union_symlink }, /* symlink */ { &vop_readdir_desc, union_readdir }, /* readdir */ { &vop_readlink_desc, union_readlink }, /* readlink */ { &vop_abortop_desc, union_abortop }, /* abortop */ { &vop_inactive_desc, union_inactive }, /* inactive */ { &vop_reclaim_desc, union_reclaim }, /* reclaim */ { &vop_lock_desc, union_lock }, /* lock */ { &vop_unlock_desc, union_unlock }, /* unlock */ { &vop_bmap_desc, union_bmap }, /* bmap */ { &vop_strategy_desc, union_strategy }, /* strategy */ { &vop_print_desc, union_print }, /* print */ { &vop_islocked_desc, union_islocked }, /* islocked */ { &vop_pathconf_desc, union_pathconf }, /* pathconf */ { &vop_advlock_desc, union_advlock }, /* advlock */ #ifdef notdef { &vop_blkatoff_desc, union_blkatoff }, /* blkatoff */ { &vop_valloc_desc, union_valloc }, /* valloc */ { &vop_vfree_desc, union_vfree }, /* vfree */ { &vop_truncate_desc, union_truncate }, /* truncate */ { &vop_update_desc, union_update }, /* update */ { &vop_bwrite_desc, union_bwrite }, /* bwrite */ #endif { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc union_vnodeop_opv_desc = { &union_vnodeop_p, union_vnodeop_entries }; + +VNODEOP_SET(union_vnodeop_opv_desc); Index: head/sys/msdosfs/msdosfs_vfsops.c =================================================================== --- head/sys/msdosfs/msdosfs_vfsops.c (revision 2945) +++ head/sys/msdosfs/msdosfs_vfsops.c (revision 2946) @@ -1,729 +1,731 @@ -/* $Id: msdosfs_vfsops.c,v 1.1 1994/09/19 15:41:45 dfr Exp $ */ +/* $Id: msdosfs_vfsops.c,v 1.2 1994/09/19 19:24:44 dfr Exp $ */ /* $NetBSD: msdosfs_vfsops.c,v 1.19 1994/08/21 18:44:10 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 #include #include #include /* XXX */ /* defines v_rdev */ #include #include #include #include #include #include #include #include #include #include int msdosfsdoforce = 1; /* 1 = force unmount */ /* * mp - path - addr in user space of mount point (ie /usr or whatever) * data - addr in user space of mount params including the name of the block * special file to treat as a filesystem. */ int msdosfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { struct vnode *devvp; /* vnode for blk device to mount */ struct msdosfs_args args; /* will hold data from mount request */ struct msdosfsmount *pmp; /* msdosfs specific mount control block */ int error, flags; u_int size; struct ucred *cred, *scred; struct vattr va; /* * Copy in the args for the mount request. */ if (error = copyin(data, (caddr_t) & args, sizeof(struct msdosfs_args))) return error; /* * If they just want to update then be sure we can do what is * asked. Can't change a filesystem from read/write to read only. * Why? And if they've supplied a new device file name then we * continue, otherwise return. */ if (mp->mnt_flag & MNT_UPDATE) { pmp = (struct msdosfsmount *) mp->mnt_data; error = 0; if (pmp->pm_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; if (vfs_busy(mp)) return EBUSY; error = vflush(mp, NULLVP, flags); vfs_unbusy(mp); } if (!error && (mp->mnt_flag & MNT_RELOAD)) /* not yet implemented */ error = EINVAL; if (error) return error; if (pmp->pm_ronly && (mp->mnt_flag & MNT_RDONLY) == 0) pmp->pm_ronly = 0; if (args.fspec == 0) { /* * Process export requests. */ return vfs_export(mp, &pmp->pm_export, &args.export); } } else pmp = NULL; /* * check to see that the user in owns the target directory. * Note the very XXX trick to make sure we're checking as the * real user -- were mount() executable by anyone, this wouldn't * be a problem. * * XXX there should be one consistent error out. */ cred = crdup(p->p_ucred); /* XXX */ cred->cr_uid = p->p_cred->p_ruid; /* XXX */ error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p); if (error) { crfree(cred); /* XXX */ return error; } if (cred->cr_uid != 0) { if (va.va_uid != cred->cr_uid) { error = EACCES; crfree(cred); /* XXX */ return error; } /* a user mounted it; we'll verify permissions when unmounting */ mp->mnt_flag |= MNT_USER; } /* * Now, lookup the name of the block device this mount or name * update request is to apply to. */ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); scred = p->p_ucred; /* XXX */ p->p_ucred = cred; /* XXX */ error = namei(ndp); p->p_ucred = scred; /* XXX */ crfree(cred); /* XXX */ if (error != 0) return error; /* * Be sure they've given us a block device to treat as a * filesystem. And, that its major number is within the bdevsw * table. */ devvp = ndp->ni_vp; if (devvp->v_type != VBLK) { vrele(devvp); return ENOTBLK; } if (major(devvp->v_rdev) >= nblkdev) { vrele(devvp); return ENXIO; } /* * If this is an update, then make sure the vnode for the block * special device is the same as the one our filesystem is in. */ if (mp->mnt_flag & MNT_UPDATE) { if (devvp != pmp->pm_devvp) error = EINVAL; else vrele(devvp); } else { /* * Well, it's not an update, it's a real mount request. * Time to get dirty. */ error = mountmsdosfs(devvp, mp, p); } if (error) { vrele(devvp); return error; } /* * Copy in the name of the directory the filesystem is to be * mounted on. Then copy in the name of the block special file * representing the filesystem being mounted. And we clear the * remainder of the character strings to be tidy. Set up the * user id/group id/mask as specified by the user. Then, we try to * fill in the filesystem stats structure as best we can with * whatever applies from a dos file system. */ pmp = (struct msdosfsmount *) mp->mnt_data; copyinstr(path, (caddr_t) mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname) - 1, &size); bzero(mp->mnt_stat.f_mntonname + size, sizeof(mp->mnt_stat.f_mntonname) - size); copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); pmp->pm_mounter = p->p_cred->p_ruid; pmp->pm_gid = args.gid; pmp->pm_uid = args.uid; pmp->pm_mask = args.mask; (void) msdosfs_statfs(mp, &mp->mnt_stat, p); #ifdef MSDOSFS_DEBUG printf("msdosfs_mount(): mp %x, pmp %x, inusemap %x\n", mp, pmp, pmp->pm_inusemap); #endif return 0; } int mountmsdosfs(devvp, mp, p) struct vnode *devvp; struct mount *mp; struct proc *p; { int i; int bpc; int bit; int error; int needclose; int ronly = (mp->mnt_flag & MNT_RDONLY) != 0; dev_t dev = devvp->v_rdev; union bootsector *bsp; struct msdosfsmount *pmp = NULL; struct buf *bp0 = NULL; struct byte_bpb33 *b33; struct byte_bpb50 *b50; /* * Multiple mounts of the same block special file aren't allowed. * Make sure no one else has the special file open. And flush any * old buffers from this filesystem. Presumably this prevents us * from running into buffers that are the wrong blocksize. */ if (error = vfs_mountedon(devvp)) return error; if (vcount(devvp) > 1) return EBUSY; if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0)) return error; /* * Now open the block special file. */ if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE, FSCRED, p)) return error; needclose = 1; #ifdef HDSUPPORT /* * Put this in when we support reading dos filesystems from * partitioned harddisks. */ if (VOP_IOCTL(devvp, DIOCGPART, &msdosfspart, FREAD, NOCRED, p) == 0) { } #endif /* * Read the boot sector of the filesystem, and then check the boot * signature. If not a dos boot sector then error out. We could * also add some checking on the bsOemName field. So far I've seen * the following values: "IBM 3.3" "MSDOS3.3" "MSDOS5.0" */ if (error = bread(devvp, 0, 512, NOCRED, &bp0)) goto error_exit; bp0->b_flags |= B_AGE; bsp = (union bootsector *) bp0->b_data; b33 = (struct byte_bpb33 *) bsp->bs33.bsBPB; b50 = (struct byte_bpb50 *) bsp->bs50.bsBPB; #ifdef MSDOSFS_CHECKSIG if (bsp->bs50.bsBootSectSig != BOOTSIG) { error = EINVAL; goto error_exit; } #endif if ( bsp->bs50.bsJump[0] != 0xe9 && (bsp->bs50.bsJump[0] != 0xeb || bsp->bs50.bsJump[2] != 0x90)) { error = EINVAL; goto error_exit; } pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK); bzero((caddr_t)pmp, sizeof *pmp); pmp->pm_mountp = mp; /* * Compute several useful quantities from the bpb in the * bootsector. Copy in the dos 5 variant of the bpb then fix up * the fields that are different between dos 5 and dos 3.3. */ pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); pmp->pm_SectPerClust = b50->bpbSecPerClust; pmp->pm_ResSectors = getushort(b50->bpbResSectors); pmp->pm_FATs = b50->bpbFATs; pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); pmp->pm_Sectors = getushort(b50->bpbSectors); pmp->pm_Media = b50->bpbMedia; pmp->pm_FATsecs = getushort(b50->bpbFATsecs); pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); pmp->pm_Heads = getushort(b50->bpbHeads); /* XXX - We should probably check more values here */ if (!pmp->pm_BytesPerSec || !pmp->pm_SectPerClust || !pmp->pm_Heads || pmp->pm_Heads > 255 || !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) { error = EINVAL; goto error_exit; } if (pmp->pm_Sectors == 0) { pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); } else { pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); pmp->pm_HugeSectors = pmp->pm_Sectors; } pmp->pm_fatblk = pmp->pm_ResSectors; pmp->pm_rootdirblk = pmp->pm_fatblk + (pmp->pm_FATs * pmp->pm_FATsecs); pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)) / pmp->pm_BytesPerSec;/* in sectors */ pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / pmp->pm_SectPerClust; pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1; pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec; if (FAT12(pmp)) /* * This will usually be a floppy disk. This size makes sure * that one fat entry will not be split across multiple * blocks. */ pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec; else /* * This will usually be a hard disk. Reading or writing one * block should be quite fast. */ pmp->pm_fatblocksize = MAXBSIZE; pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec; if ((pmp->pm_rootdirsize % pmp->pm_SectPerClust) != 0) printf("mountmsdosfs(): root directory is not a multiple of the clustersize in length\n"); /* * Compute mask and shift value for isolating cluster relative byte * offsets and cluster numbers from a file offset. */ bpc = pmp->pm_SectPerClust * pmp->pm_BytesPerSec; pmp->pm_bpcluster = bpc; pmp->pm_depclust = bpc / sizeof(struct direntry); pmp->pm_crbomask = bpc - 1; if (bpc == 0) { error = EINVAL; goto error_exit; } bit = 1; for (i = 0; i < 32; i++) { if (bit & bpc) { if (bit ^ bpc) { error = EINVAL; goto error_exit; } pmp->pm_cnshift = i; break; } bit <<= 1; } pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */ pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */ /* * Release the bootsector buffer. */ brelse(bp0); bp0 = NULL; /* * Allocate memory for the bitmap of allocated clusters, and then * fill it in. */ pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1) / N_INUSEBITS) * sizeof(*pmp->pm_inusemap), M_MSDOSFSFAT, M_WAITOK); /* * fillinusemap() needs pm_devvp. */ pmp->pm_dev = dev; pmp->pm_devvp = devvp; /* * Have the inuse map filled in. */ error = fillinusemap(pmp); if (error) goto error_exit; /* * If they want fat updates to be synchronous then let them suffer * the performance degradation in exchange for the on disk copy of * the fat being correct just about all the time. I suppose this * would be a good thing to turn on if the kernel is still flakey. */ pmp->pm_waitonfat = mp->mnt_flag & MNT_SYNCHRONOUS; /* * Finish up. */ pmp->pm_ronly = ronly; if (ronly == 0) pmp->pm_fmod = 1; mp->mnt_data = (qaddr_t) pmp; mp->mnt_stat.f_fsid.val[0] = (long)dev; mp->mnt_stat.f_fsid.val[1] = MOUNT_MSDOS; mp->mnt_flag |= MNT_LOCAL; #ifdef QUOTA /* * If we ever do quotas for DOS filesystems this would be a place * to fill in the info in the msdosfsmount structure. You dolt, * quotas on dos filesystems make no sense because files have no * owners on dos filesystems. of course there is some empty space * in the directory entry where we could put uid's and gid's. */ #endif devvp->v_specflags |= SI_MOUNTEDON; return 0; error_exit:; if (bp0) brelse(bp0); if (needclose) (void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE, NOCRED, p); if (pmp) { if (pmp->pm_inusemap) free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT); free((caddr_t) pmp, M_MSDOSFSMNT); mp->mnt_data = (qaddr_t) 0; } return error; } int msdosfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return 0; } /* * Unmount the filesystem described by mp. */ int msdosfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { int flags = 0; int error; struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; struct vnode *vp = pmp->pm_devvp; /* only the mounter, or superuser can unmount */ if ((p->p_cred->p_ruid != pmp->pm_mounter) && (error = suser(p->p_ucred, &p->p_acflag))) return error; if (mntflags & MNT_FORCE) { if (!msdosfsdoforce) return EINVAL; flags |= FORCECLOSE; } #ifdef QUOTA #endif if (error = vflush(mp, NULLVP, flags)) return error; pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON; #ifdef MSDOSFS_DEBUG printf("msdosfs_umount(): just before calling VOP_CLOSE()\n"); printf("flag %08x, usecount %d, writecount %d, holdcnt %d\n", vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt); printf("lastr %d, id %d, mount %08x, op %08x\n", vp->v_lastr, vp->v_id, vp->v_mount, vp->v_op); printf("freef %08x, freeb %08x, mountf %08x, mountb %08x\n", vp->v_freef, vp->v_freeb, vp->v_mountf, vp->v_mountb); printf("cleanblkhd %08x, dirtyblkhd %08x, numoutput %d, type %d\n", vp->v_cleanblkhd, vp->v_dirtyblkhd, vp->v_numoutput, vp->v_type); printf("union %08x, tag %d, data[0] %08x, data[1] %08x\n", vp->v_socket, vp->v_tag, vp->v_data[0], vp->v_data[1]); #endif error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD | FWRITE, NOCRED, p); vrele(pmp->pm_devvp); free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT); free((caddr_t) pmp, M_MSDOSFSMNT); mp->mnt_data = (qaddr_t) 0; mp->mnt_flag &= ~MNT_LOCAL; return error; } int msdosfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct denode *ndep; struct msdosfsmount *pmp = (struct msdosfsmount *) (mp->mnt_data); int error; error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, NULL, &ndep); #ifdef MSDOSFS_DEBUG printf("msdosfs_root(); mp %08x, pmp %08x, ndep %08x, vp %08x\n", mp, pmp, ndep, DETOV(ndep)); #endif if (error == 0) *vpp = DETOV(ndep); return error; } int msdosfs_quotactl(mp, cmds, uid, arg, p) struct mount *mp; int cmds; uid_t uid; caddr_t arg; struct proc *p; { #ifdef QUOTA #else return EOPNOTSUPP; #endif } int msdosfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; /* * Fill in the stat block. */ sbp->f_type = MOUNT_MSDOS; sbp->f_bsize = pmp->pm_bpcluster; sbp->f_iosize = pmp->pm_bpcluster; sbp->f_blocks = pmp->pm_nmbrofclusters; sbp->f_bfree = pmp->pm_freeclustercount; sbp->f_bavail = pmp->pm_freeclustercount; sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ sbp->f_ffree = 0; /* what to put in here? */ /* * Copy the mounted on and mounted from names into the passed in * stat block, if it is not the one in the mount structure. */ if (sbp != &mp->mnt_stat) { bcopy((caddr_t) mp->mnt_stat.f_mntonname, (caddr_t) & sbp->f_mntonname[0], MNAMELEN); bcopy((caddr_t) mp->mnt_stat.f_mntfromname, (caddr_t) & sbp->f_mntfromname[0], MNAMELEN); } #if 0 strncpy(&sbp->f_fstypename[0], mp->mnt_op->vfs_name, MFSNAMELEN); sbp->f_fstypename[MFSNAMELEN] = '\0'; #endif return 0; } int msdosfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { struct vnode *vp; struct denode *dep; struct msdosfsmount *pmp; int error; int allerror = 0; pmp = (struct msdosfsmount *) mp->mnt_data; /* * If we ever switch to not updating all of the fats all the time, * this would be the place to update them from the first one. */ if (pmp->pm_fmod) if (pmp->pm_ronly) panic("msdosfs_sync: rofs mod"); else { /* update fats here */ } /* * Go thru in memory denodes and write them out along with * unwritten file blocks. */ loop: for (vp = mp->mnt_vnodelist.lh_first; vp; vp = vp->v_mntvnodes.le_next) { if (vp->v_mount != mp) /* not ours anymore */ goto loop; if (VOP_ISLOCKED(vp)) /* file is busy */ continue; dep = VTODE(vp); if ((dep->de_flag & DE_UPDATE) == 0 && vp->v_dirtyblkhd.lh_first == NULL) continue; if (vget(vp, 1)) /* not there anymore? */ goto loop; if (error = VOP_FSYNC(vp, cred, waitfor, p)) allerror = error; vput(vp); /* done with this one */ } /* * Flush filesystem control info. */ if (error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, p)) allerror = error; return allerror; } int msdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) struct mount *mp; struct fid *fhp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred **credanonp; { struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; struct defid *defhp = (struct defid *) fhp; struct denode *dep; struct netcred *np; int error; np = vfs_export_lookup(mp, &pmp->pm_export, nam); if (np == NULL) return EACCES; error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, NULL, &dep); if (error) { *vpp = NULLVP; return error; } *vpp = DETOV(dep); *exflagsp = np->netc_exflags; *credanonp = &np->netc_anon; return 0; } int msdosfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { struct denode *dep = VTODE(vp); struct defid *defhp = (struct defid *) fhp; defhp->defid_len = sizeof(struct defid); defhp->defid_dirclust = dep->de_dirclust; defhp->defid_dirofs = dep->de_diroffset; /* defhp->defid_gen = ip->i_gen; */ return 0; } int msdosfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return EOPNOTSUPP; } struct vfsops msdosfs_vfsops = { msdosfs_mount, msdosfs_start, msdosfs_unmount, msdosfs_root, msdosfs_quotactl, msdosfs_statfs, msdosfs_sync, msdosfs_vget, msdosfs_fhtovp, msdosfs_vptofh, msdosfs_init }; + +VFS_SET(msdosfs_vfsops, msdos, MOUNT_MSDOS, 0); Index: head/sys/msdosfs/msdosfs_vnops.c =================================================================== --- head/sys/msdosfs/msdosfs_vnops.c (revision 2945) +++ head/sys/msdosfs/msdosfs_vnops.c (revision 2946) @@ -1,1933 +1,1935 @@ -/* $Id$ */ +/* $Id: msdosfs_vnops.c,v 1.1 1994/09/19 15:41:46 dfr 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 /* define FWRITE ... */ #include #include #include #include #include #include /* XXX */ /* defines v_rdev */ #include #include /* defines dirent structure */ #include #include #include #include #include /* * 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. */ 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) ? 0 : 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; } 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; struct denode *pdep = VTODE(ap->a_dvp); 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; } 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; } 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); struct timespec ts; if (vp->v_usecount > 1 && !(dep->de_flag & DE_LOCKED)) { TIMEVAL_TO_TIMESPEC(&time, &ts); DE_TIMES(dep, &ts); } return 0; } /* * This routine will go into sys/kern/vfs_subr.c one day! * * Do the usual access checking. * file_node, uid and gid are from the vnode in question, * while acc_mode and cred are from the VOP_ACCESS parameter list. */ static int vaccess(file_mode, uid, gid, acc_mode, cred) mode_t file_mode; uid_t uid; gid_t gid; mode_t acc_mode; struct ucred *cred; { mode_t mask; int i; register gid_t *gp; /* User id 0 always gets access. */ if (cred->cr_uid == 0) return 0; mask = 0; /* Otherwise, check the owner. */ if (cred->cr_uid == uid) { if (acc_mode & VEXEC) mask |= S_IXUSR; if (acc_mode & VREAD) mask |= S_IRUSR; if (acc_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 (gid == *gp) { if (acc_mode & VEXEC) mask |= S_IXGRP; if (acc_mode & VREAD) mask |= S_IRGRP; if (acc_mode & VWRITE) mask |= S_IWGRP; return (file_mode & mask) == mask ? 0 : EACCES; } /* Otherwise, check everyone else. */ if (acc_mode & VEXEC) mask |= S_IXOTH; if (acc_mode & VREAD) mask |= S_IROTH; if (acc_mode & VWRITE) mask |= S_IWOTH; return (file_mode & mask) == mask ? 0 : EACCES; } int msdosfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { mode_t dosmode; struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; dosmode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); dosmode &= pmp->pm_mask; return vaccess(dosmode, pmp->pm_uid, pmp->pm_gid, ap->a_mode, ap->a_cred); } 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; struct timespec ts; TIMEVAL_TO_TIMESPEC(&time, &ts); DE_TIMES(dep, &ts); 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; #ifndef MSDOSFS_NODIRMOD if (vap->va_mode & S_IFDIR) TIMEVAL_TO_TIMESPEC(&time, &vap->va_mtime); #endif vap->va_ctime = vap->va_atime; vap->va_flags = dep->de_flag; 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; return 0; } 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; { int error = 0; struct denode *dep = VTODE(ap->a_vp); struct vattr *vap = ap->a_vap; struct ucred *cred = ap->a_cred; #ifdef MSDOSFS_DEBUG printf("msdosfs_setattr(): vp %08x, vap %08x, cred %08x, p %08x\n", ap->a_vp, vap, cred, ap->a_p); #endif 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) || (vap->va_uid != VNOVAL) || (vap->va_gid != VNOVAL)) { #ifdef MSDOSFS_DEBUG printf("msdosfs_setattr(): returning EINVAL\n"); printf(" va_type %d, va_nlink %x, va_fsid %x, va_fileid %x\n", vap->va_type, vap->va_nlink, vap->va_fsid, vap->va_fileid); printf(" va_blocksize %x, va_rdev %x, va_bytes %x, va_gen %x\n", vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen); printf(" va_uid %x, va_gid %x\n", vap->va_uid, vap->va_gid); #endif return EINVAL; } if (vap->va_size != VNOVAL) { if (ap->a_vp->v_type == VDIR) return EISDIR; if (error = detrunc(dep, vap->va_size, 0, cred, ap->a_p)) return error; } if (vap->va_mtime.ts_sec != VNOVAL) { dep->de_flag |= DE_UPDATE; if (error = deupdat(dep, &vap->va_mtime, 1)) return error; } /* * DOS files only have the ability to have thier writability * attribute set, so we use the owner write bit to set the readonly * attribute. */ if (vap->va_mode != (u_short) VNOVAL) { /* 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_UPDATE; } if (vap->va_flags != VNOVAL) { if (error = suser(cred, &ap->a_p->p_acflag)) return error; if (cred->cr_uid == 0) dep->de_flag = vap->va_flags; else { dep->de_flag &= 0xffff0000; dep->de_flag |= (vap->va_flags & 0xffff); } dep->de_flag |= DE_UPDATE; } return error; } 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 bn; 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; 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. */ 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; #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; /* * 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) { if (error = pcbmap(dep, bn, &bn, 0)) break; } else if (bn > lastcn) { error = ENOSPC; break; } 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) { if (error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0)) 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. */ if (error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp)) 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; vnode_pager_setsize(vp, dep->de_FileSize); /* why? */ } (void) vnode_pager_uncache(vp); /* why not? */ /* * 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) { bp->b_flags |= B_AGE; 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 { error = deupdat(dep, &time, 1); } return error; } 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; } int msdosfs_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; { return 1; /* DOS filesystems never block? */ } 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. */ int msdosfs_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; int wait = ap->a_waitfor == MNT_WAIT; int error; #if 0 /* * Does this call to vflushbuf() do anything? I can find no code * anywhere that sets v_dirtyblkhd in the vnode, which vflushbuf() * seems to depend upon. */ vflushbuf(vp, wait ? B_SYNC : 0); #endif return deupdat(VTODE(vp), &time, wait); } /* * Now the whole work of extending a file is done in the write function. * So nothing to do here. */ 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; } 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. */ int msdosfs_link(ap) struct vop_link_args /* { struct vnode *a_vp; struct vnode *a_tdvp; struct componentname *a_cnp; } */ *ap; { return VOP_ABORTOP(ap->a_vp, ap->a_cnp); } /* * 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 * update offset and dirclust in denode * move denode to new hash chain * 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. */ 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 to_dirclust; u_long to_diroffset; u_long cn; daddr_t bn; struct vnode *tvp = ap->a_tvp; 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; #ifdef __NetBSD__ /* 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; } #endif /* * 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 & 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"); if (error = relookup(ap->a_tdvp, &tvp, ap->a_tcnp)) { 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; } } else { /* destination is file */ if (sourceisadirectory) { error = EISDIR; goto bad; } } to_dirclust = tdep->de_dirclust; to_diroffset = tdep->de_diroffset; if (error = removede(tddep,tdep)) { goto bad; } vput(ap->a_tvp); tdep = NULL; /* * Remember where the slot was for createde(). */ tddep->de_fndclust = to_dirclust; tddep->de_fndoffset = to_diroffset; } /* * 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 */ VOP_LOCK(ap->a_fvp); /* ap->a_fdvp is already locked */ if (error = readep(fddep->de_pmp, fdep->de_dirclust, fdep->de_diroffset, &bp, &ep)) { VOP_UNLOCK(ap->a_fvp); goto bad; } bcopy(toname, ep->deName, 11); if (error = bwrite(bp)) { VOP_UNLOCK(ap->a_fvp); 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. */ VOP_LOCK(ap->a_fvp); 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); goto bad; } VOP_LOCK(ap->a_fdvp); if (error = readep(fddep->de_pmp, fddep->de_fndclust, fddep->de_fndoffset, &bp, &ep)) { VOP_UNLOCK(ap->a_fvp); VOP_UNLOCK(ap->a_fdvp); goto bad; } ep->deName[0] = SLOT_DELETED; if (error = bwrite(bp)) { VOP_UNLOCK(ap->a_fvp); VOP_UNLOCK(ap->a_fdvp); goto bad; } fdep->de_dirclust = tddep->de_fndclust; fdep->de_diroffset = tddep->de_fndoffset; reinsert(fdep); VOP_UNLOCK(ap->a_fdvp); } /* 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?\n"); } 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); goto bad; } dotdotp = (struct direntry *) bp->b_data + 1; putushort(dotdotp->deStartCluster, tddep->de_StartCluster); error = bwrite(bp); VOP_UNLOCK(ap->a_fvp); if (error) { /* should really panic here, fs is corrupt */ goto bad; } } else VOP_UNLOCK(ap->a_fvp); bad: ; vrele(DETOV(fdep)); vrele(DETOV(fddep)); if (tdep) vput(DETOV(tdep)); if (tddep) vput(DETOV(tddep)); return error; } 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 */ }; 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. */ if (error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL)) { 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); if (error = bwrite(bp)) { 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; } 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 */ /* * Don't let "rmdir ." go thru. */ if (ddep == dep) { vrele(ap->a_vp); vput(ap->a_vp); return EINVAL; } /* * 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. */ if (error = removede(ddep,dep)) 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); 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. */ 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; { struct denode *pdep = VTODE(ap->a_dvp); 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). */ 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 */ } }; int msdosfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; int *a_eofflag; u_int *a_cookies; int a_ncookies; } */ *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 */ int i = 0; struct uio *uio = ap->a_uio; int ncookies = 1; u_int* cookies = NULL; #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 #if 0 if (!ap->a_cookies) ncookies = 1; #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; /* * 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; if (cookies) { *cookies++ = sizeof(struct direntry); ncookies--; } } if (cookies) { if (ncookies-- <= 0) n--; else *cookies++ = 2 * sizeof(struct direntry); } error = uiomove((char *) rootdots + uio->uio_offset, n * sizeof(struct direntry), uio); } } while (!error && uio->uio_resid > 0 && ncookies > 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) return 0; 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); if (cookies) { ncookies++; cookies--; } } 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; } } 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; } dentp++; if (cookies) { *cookies++ = (u_int)((char *)dentp - bp->b_data - on) + uio->uio_offset; ncookies--; } 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 (ncookies <= 0) break; } 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 0 /* * I don't know why we bother setting this eofflag, getdirentries() * in vfs_syscalls.c doesn't bother to look at it when we return. * (because NFS uses it in nfs_serv.c -- JMP) */ if (dep->de_FileSize - uio->uio_offset - bias <= 0) *ap->a_eofflag = 1; else *ap->a_eofflag = 0; #endif return error; } /* * DOS filesystems don't know what symlinks are. */ int msdosfs_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { return EINVAL; } 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; } int msdosfs_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; } */ *ap; { struct denode *dep = VTODE(ap->a_vp); while (dep->de_flag & DE_LOCKED) { dep->de_flag |= DE_WANTED; if (dep->de_lockholder == curproc->p_pid) panic("msdosfs_lock: locking against myself"); dep->de_lockwaiter = curproc->p_pid; (void) sleep((caddr_t) dep, PINOD); } dep->de_lockwaiter = 0; dep->de_lockholder = curproc->p_pid; dep->de_flag |= DE_LOCKED; return 0; } int msdosfs_unlock(ap) struct vop_unlock_args /* { struct vnode *vp; } */ *ap; { struct denode *dep = VTODE(ap->a_vp); if (!(dep->de_flag & DE_LOCKED)) panic("msdosfs_unlock: denode not locked"); dep->de_lockholder = 0; dep->de_flag &= ~DE_LOCKED; if (dep->de_flag & DE_WANTED) { dep->de_flag &= ~DE_WANTED; wakeup((caddr_t) dep); } return 0; } int msdosfs_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { return VTODE(ap->a_vp)->de_flag & DE_LOCKED ? 1 : 0; } /* * 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 */ 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; } */ *ap; { struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; 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; } return pcbmap(dep, ap->a_bn, ap->a_bnp, 0); } 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; } 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 msdosfsmount *pmp = dep->de_pmp; 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) { if (error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0)) 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; } 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 %d, diroffset %d ", dep->de_StartCluster, dep->de_dirclust, dep->de_diroffset); printf(" dev %d, %d, %s\n", major(dep->de_dev), minor(dep->de_dev), dep->de_flag & DE_LOCKED ? "(LOCKED)" : ""); if (dep->de_lockholder) { printf(" owner pid %d", dep->de_lockholder); if (dep->de_lockwaiter) printf(" waiting pid %d", dep->de_lockwaiter); printf("\n"); } return 0; } 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 */ } 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 */ int (**msdosfs_vnodeop_p)(); struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, msdosfs_lookup }, /* lookup */ { &vop_create_desc, msdosfs_create }, /* create */ { &vop_mknod_desc, msdosfs_mknod }, /* mknod */ { &vop_open_desc, msdosfs_open }, /* open */ { &vop_close_desc, msdosfs_close }, /* close */ { &vop_access_desc, msdosfs_access }, /* access */ { &vop_getattr_desc, msdosfs_getattr }, /* getattr */ { &vop_setattr_desc, msdosfs_setattr }, /* setattr */ { &vop_read_desc, msdosfs_read }, /* read */ { &vop_write_desc, msdosfs_write }, /* write */ { &vop_ioctl_desc, msdosfs_ioctl }, /* ioctl */ { &vop_select_desc, msdosfs_select }, /* select */ { &vop_mmap_desc, msdosfs_mmap }, /* mmap */ { &vop_fsync_desc, msdosfs_fsync }, /* fsync */ { &vop_seek_desc, msdosfs_seek }, /* seek */ { &vop_remove_desc, msdosfs_remove }, /* remove */ { &vop_link_desc, msdosfs_link }, /* link */ { &vop_rename_desc, msdosfs_rename }, /* rename */ { &vop_mkdir_desc, msdosfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, msdosfs_rmdir }, /* rmdir */ { &vop_symlink_desc, msdosfs_symlink }, /* symlink */ { &vop_readdir_desc, msdosfs_readdir }, /* readdir */ { &vop_readlink_desc, msdosfs_readlink }, /* readlink */ { &vop_abortop_desc, msdosfs_abortop }, /* abortop */ { &vop_inactive_desc, msdosfs_inactive }, /* inactive */ { &vop_reclaim_desc, msdosfs_reclaim }, /* reclaim */ { &vop_lock_desc, msdosfs_lock }, /* lock */ { &vop_unlock_desc, msdosfs_unlock }, /* unlock */ { &vop_bmap_desc, msdosfs_bmap }, /* bmap */ { &vop_strategy_desc, msdosfs_strategy }, /* strategy */ { &vop_print_desc, msdosfs_print }, /* print */ { &vop_islocked_desc, msdosfs_islocked }, /* islocked */ { &vop_pathconf_desc, msdosfs_pathconf }, /* pathconf */ { &vop_advlock_desc, msdosfs_advlock }, /* advlock */ { &vop_reallocblks_desc, msdosfs_reallocblks }, /* reallocblks */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc *)NULL, (int (*)())NULL } }; struct vnodeopv_desc msdosfs_vnodeop_opv_desc = { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries }; + +VNODEOP_SET(msdosfs_vnodeop_opv_desc); Index: head/sys/nfs/nfs_vfsops.c =================================================================== --- head/sys/nfs/nfs_vfsops.c (revision 2945) +++ head/sys/nfs/nfs_vfsops.c (revision 2946) @@ -1,741 +1,742 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * 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. * * @(#)nfs_vfsops.c 8.3 (Berkeley) 1/4/94 - * $Id: nfs_vfsops.c,v 1.2 1994/08/02 07:52:16 davidg Exp $ + * $Id: nfs_vfsops.c,v 1.3 1994/08/20 16:03:19 davidg 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 /* * nfs vfs operations. */ struct vfsops nfs_vfsops = { nfs_mount, nfs_start, nfs_unmount, nfs_root, nfs_quotactl, nfs_statfs, nfs_sync, nfs_vget, nfs_fhtovp, nfs_vptofh, nfs_init, }; +VFS_SET(nfs_vfsops, nfs, MOUNT_NFS, 0); /* * This structure must be filled in by a primary bootstrap or bootstrap * server for a diskless/dataless machine. It is initialized below just * to ensure that it is allocated to initialized data (.data not .bss). */ struct nfs_diskless nfs_diskless = { 0 }; extern u_long nfs_procids[NFS_NPROCS]; extern u_long nfs_prog, nfs_vers; void nfs_disconnect __P((struct nfsmount *)); void nfsargs_ntoh __P((struct nfs_args *)); static struct mount *nfs_mountdiskless __P((char *, char *, int, struct sockaddr_in *, struct nfs_args *, register struct vnode **)); #define TRUE 1 #define FALSE 0 /* * nfs statfs call */ int nfs_statfs(mp, sbp, p) struct mount *mp; register struct statfs *sbp; struct proc *p; { register struct vnode *vp; register struct nfsv2_statfs *sfp; register caddr_t cp; register long t1; caddr_t bpos, dpos, cp2; int error = 0, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nfsmount *nmp; struct ucred *cred; struct nfsnode *np; nmp = VFSTONFS(mp); isnq = (nmp->nm_flag & NFSMNT_NQNFS); if (error = nfs_nget(mp, &nmp->nm_fh, &np)) return (error); vp = NFSTOV(np); nfsstats.rpccnt[NFSPROC_STATFS]++; cred = crget(); cred->cr_ngroups = 1; nfsm_reqhead(vp, NFSPROC_STATFS, NFSX_FH); nfsm_fhtom(vp); nfsm_request(vp, NFSPROC_STATFS, p, cred); nfsm_dissect(sfp, struct nfsv2_statfs *, NFSX_STATFS(isnq)); sbp->f_type = MOUNT_NFS; sbp->f_flags = nmp->nm_flag; sbp->f_iosize = NFS_MAXDGRAMDATA; sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize); sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks); sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree); sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail); if (isnq) { sbp->f_files = fxdr_unsigned(long, sfp->sf_files); sbp->f_ffree = fxdr_unsigned(long, sfp->sf_ffree); } else { sbp->f_files = 0; sbp->f_ffree = 0; } if (sbp != &mp->mnt_stat) { bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } nfsm_reqdone; vrele(vp); crfree(cred); return (error); } /* * Mount a remote root fs via. nfs. This depends on the info in the * nfs_diskless structure that has been filled in properly by some primary * bootstrap. * It goes something like this: * - do enough of "ifconfig" by calling ifioctl() so that the system * can talk to the server * - If nfs_diskless.mygateway is filled in, use that address as * a default gateway. * - hand craft the swap nfs vnode hanging off a fake mount point * if swdevt[0].sw_dev == NODEV * - build the rootfs mount point and call mountnfs() to do the rest. */ int nfs_mountroot() { register struct mount *mp; register struct nfs_diskless *nd = &nfs_diskless; struct socket *so; struct vnode *vp; struct proc *p = curproc; /* XXX */ int error, i; /* * XXX time must be non-zero when we init the interface or else * the arp code will wedge... */ if (time.tv_sec == 0) time.tv_sec = 1; #ifdef notyet /* Set up swap credentials. */ proc0.p_ucred->cr_uid = ntohl(nd->swap_ucred.cr_uid); proc0.p_ucred->cr_gid = ntohl(nd->swap_ucred.cr_gid); if ((proc0.p_ucred->cr_ngroups = ntohs(nd->swap_ucred.cr_ngroups)) > NGROUPS) proc0.p_ucred->cr_ngroups = NGROUPS; for (i = 0; i < proc0.p_ucred->cr_ngroups; i++) proc0.p_ucred->cr_groups[i] = ntohl(nd->swap_ucred.cr_groups[i]); #endif /* * Do enough of ifconfig(8) so that the critical net interface can * talk to the server. */ if (error = socreate(nd->myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0)) panic("nfs_mountroot: socreate: %d", error); if (error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, p)) panic("nfs_mountroot: SIOCAIFADDR: %d", error); soclose(so); /* * If the gateway field is filled in, set it as the default route. */ if (nd->mygateway.sin_len != 0) { struct sockaddr_in mask, sin; bzero((caddr_t)&mask, sizeof(mask)); sin = mask; sin.sin_family = AF_INET; sin.sin_len = sizeof(sin); if (error = rtrequest(RTM_ADD, (struct sockaddr *)&sin, (struct sockaddr *)&nd->mygateway, (struct sockaddr *)&mask, RTF_UP | RTF_GATEWAY, (struct rtentry **)0)) panic("nfs_mountroot: RTM_ADD: %d", error); } /* * If swapping to an nfs node (indicated by swdevt[0].sw_dev == NODEV): * Create a fake mount point just for the swap vnode so that the * swap file can be on a different server from the rootfs. */ if (swdevt[0].sw_dev == NODEV) { nd->swap_args.fh = (nfsv2fh_t *)nd->swap_fh; (void) nfs_mountdiskless(nd->swap_hostnam, "/swap", 0, &nd->swap_saddr, &nd->swap_args, &vp); /* * Since the swap file is not the root dir of a file system, * hack it to a regular file. */ vp->v_type = VREG; vp->v_flag = 0; swapdev_vp = vp; VREF(vp); swdevt[0].sw_vp = vp; swdevt[0].sw_nblks = ntohl(nd->swap_nblks); } else if (bdevvp(swapdev, &swapdev_vp)) panic("nfs_mountroot: can't setup swapdev_vp"); /* * Create the rootfs mount point. */ nd->root_args.fh = (nfsv2fh_t *)nd->root_fh; mp = nfs_mountdiskless(nd->root_hostnam, "/", MNT_RDONLY, &nd->root_saddr, &nd->root_args, &vp); if (vfs_lock(mp)) panic("nfs_mountroot: vfs_lock"); TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); mp->mnt_flag |= MNT_ROOTFS; mp->mnt_vnodecovered = NULLVP; vfs_unlock(mp); rootvp = vp; /* * This is not really an nfs issue, but it is much easier to * set hostname here and then let the "/etc/rc.xxx" files * mount the right /var based upon its preset value. */ bcopy(nd->my_hostnam, hostname, MAXHOSTNAMELEN); hostname[MAXHOSTNAMELEN - 1] = '\0'; for (i = 0; i < MAXHOSTNAMELEN; i++) if (hostname[i] == '\0') break; hostnamelen = i; inittodr(ntohl(nd->root_time)); return (0); } /* * Internal version of mount system call for diskless setup. */ static struct mount * nfs_mountdiskless(path, which, mountflag, sin, args, vpp) char *path; char *which; int mountflag; struct sockaddr_in *sin; struct nfs_args *args; register struct vnode **vpp; { register struct mount *mp; register struct mbuf *m; register int error; mp = (struct mount *)malloc((u_long)sizeof(struct mount), M_MOUNT, M_NOWAIT); if (mp == NULL) panic("nfs_mountroot: %s mount malloc", which); bzero((char *)mp, (u_long)sizeof(struct mount)); mp->mnt_op = &nfs_vfsops; mp->mnt_flag = mountflag; MGET(m, MT_SONAME, M_DONTWAIT); if (m == NULL) panic("nfs_mountroot: %s mount mbuf", which); bcopy((caddr_t)sin, mtod(m, caddr_t), sin->sin_len); m->m_len = sin->sin_len; nfsargs_ntoh(args); if (error = mountnfs(args, mp, m, which, path, vpp)) panic("nfs_mountroot: mount %s on %s: %d", path, which, error); return (mp); } /* * Convert the integer fields of the nfs_args structure from net byte order * to host byte order. Called by nfs_mountroot() above. */ void nfsargs_ntoh(nfsp) register struct nfs_args *nfsp; { NTOHL(nfsp->sotype); NTOHL(nfsp->proto); NTOHL(nfsp->flags); NTOHL(nfsp->wsize); NTOHL(nfsp->rsize); NTOHL(nfsp->timeo); NTOHL(nfsp->retrans); NTOHL(nfsp->maxgrouplist); NTOHL(nfsp->readahead); NTOHL(nfsp->leaseterm); NTOHL(nfsp->deadthresh); } /* * VFS Operations. * * mount system call * It seems a bit dumb to copyinstr() the host and path here and then * bcopy() them in mountnfs(), but I wanted to detect errors before * doing the sockargs() call because sockargs() allocates an mbuf and * an error after that means that I have to release the mbuf. */ /* ARGSUSED */ int nfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { int error; struct nfs_args args; struct mbuf *nam; struct vnode *vp; char pth[MNAMELEN], hst[MNAMELEN]; u_int len; nfsv2fh_t nfh; if (error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args))) return (error); if (error = copyin((caddr_t)args.fh, (caddr_t)&nfh, sizeof (nfsv2fh_t))) return (error); if (error = copyinstr(path, pth, MNAMELEN-1, &len)) return (error); bzero(&pth[len], MNAMELEN - len); if (error = copyinstr(args.hostname, hst, MNAMELEN-1, &len)) return (error); bzero(&hst[len], MNAMELEN - len); /* sockargs() call must be after above copyin() calls */ if (error = sockargs(&nam, (caddr_t)args.addr, args.addrlen, MT_SONAME)) return (error); args.fh = &nfh; error = mountnfs(&args, mp, nam, pth, hst, &vp); return (error); } /* * Common code for mount and mountroot */ int mountnfs(argp, mp, nam, pth, hst, vpp) register struct nfs_args *argp; register struct mount *mp; struct mbuf *nam; char *pth, *hst; struct vnode **vpp; { register struct nfsmount *nmp; struct nfsnode *np; int error; if (mp->mnt_flag & MNT_UPDATE) { nmp = VFSTONFS(mp); /* update paths, file handles, etc, here XXX */ m_freem(nam); return (0); } else { MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount), M_NFSMNT, M_WAITOK); bzero((caddr_t)nmp, sizeof (struct nfsmount)); mp->mnt_data = (qaddr_t)nmp; } getnewfsid(mp, MOUNT_NFS); nmp->nm_mountp = mp; nmp->nm_flag = argp->flags; if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_MYWRITE)) == (NFSMNT_NQNFS | NFSMNT_MYWRITE)) { error = EPERM; goto bad; } if (nmp->nm_flag & NFSMNT_NQNFS) /* * We have to set mnt_maxsymlink to a non-zero value so * that COMPAT_43 routines will know that we are setting * the d_type field in directories (and can zero it for * unsuspecting binaries). */ mp->mnt_maxsymlinklen = 1; nmp->nm_timeo = NFS_TIMEO; nmp->nm_retry = NFS_RETRANS; nmp->nm_wsize = NFS_WSIZE; nmp->nm_rsize = NFS_RSIZE; nmp->nm_numgrps = NFS_MAXGRPS; nmp->nm_readahead = NFS_DEFRAHEAD; nmp->nm_leaseterm = NQ_DEFLEASE; nmp->nm_deadthresh = NQ_DEADTHRESH; nmp->nm_tnext = (struct nfsnode *)nmp; nmp->nm_tprev = (struct nfsnode *)nmp; nmp->nm_inprog = NULLVP; bcopy((caddr_t)argp->fh, (caddr_t)&nmp->nm_fh, sizeof(nfsv2fh_t)); mp->mnt_stat.f_type = MOUNT_NFS; bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN); bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN); nmp->nm_nam = nam; if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) { nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10; if (nmp->nm_timeo < NFS_MINTIMEO) nmp->nm_timeo = NFS_MINTIMEO; else if (nmp->nm_timeo > NFS_MAXTIMEO) nmp->nm_timeo = NFS_MAXTIMEO; } if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) { nmp->nm_retry = argp->retrans; if (nmp->nm_retry > NFS_MAXREXMIT) nmp->nm_retry = NFS_MAXREXMIT; } if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) { nmp->nm_wsize = argp->wsize; /* Round down to multiple of blocksize */ nmp->nm_wsize &= ~0x1ff; if (nmp->nm_wsize <= 0) nmp->nm_wsize = 512; else if (nmp->nm_wsize > NFS_MAXDATA) nmp->nm_wsize = NFS_MAXDATA; } if (nmp->nm_wsize > MAXBSIZE) nmp->nm_wsize = MAXBSIZE; if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) { nmp->nm_rsize = argp->rsize; /* Round down to multiple of blocksize */ nmp->nm_rsize &= ~0x1ff; if (nmp->nm_rsize <= 0) nmp->nm_rsize = 512; else if (nmp->nm_rsize > NFS_MAXDATA) nmp->nm_rsize = NFS_MAXDATA; } if (nmp->nm_rsize > MAXBSIZE) nmp->nm_rsize = MAXBSIZE; if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 && argp->maxgrouplist <= NFS_MAXGRPS) nmp->nm_numgrps = argp->maxgrouplist; if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 && argp->readahead <= NFS_MAXRAHEAD) nmp->nm_readahead = argp->readahead; if ((argp->flags & NFSMNT_LEASETERM) && argp->leaseterm >= 2 && argp->leaseterm <= NQ_MAXLEASE) nmp->nm_leaseterm = argp->leaseterm; if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 1 && argp->deadthresh <= NQ_NEVERDEAD) nmp->nm_deadthresh = argp->deadthresh; /* Set up the sockets and per-host congestion */ nmp->nm_sotype = argp->sotype; nmp->nm_soproto = argp->proto; /* * For Connection based sockets (TCP,...) defer the connect until * the first request, in case the server is not responding. */ if (nmp->nm_sotype == SOCK_DGRAM && (error = nfs_connect(nmp, (struct nfsreq *)0))) goto bad; /* * This is silly, but it has to be set so that vinifod() works. * We do not want to do an nfs_statfs() here since we can get * stuck on a dead server and we are holding a lock on the mount * point. */ mp->mnt_stat.f_iosize = NFS_MAXDGRAMDATA; /* * A reference count is needed on the nfsnode representing the * remote root. If this object is not persistent, then backward * traversals of the mount point (i.e. "..") will not work if * the nfsnode gets flushed out of the cache. Ufs does not have * this problem, because one can identify root inodes by their * number == ROOTINO (2). */ if (error = nfs_nget(mp, &nmp->nm_fh, &np)) goto bad; *vpp = NFSTOV(np); return (0); bad: nfs_disconnect(nmp); free((caddr_t)nmp, M_NFSMNT); m_freem(nam); return (error); } /* * unmount system call */ int nfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { register struct nfsmount *nmp; struct nfsnode *np; struct vnode *vp; int error, flags = 0; extern int doforce; if (mntflags & MNT_FORCE) { if (!doforce) return (EINVAL); flags |= FORCECLOSE; } nmp = VFSTONFS(mp); /* * Goes something like this.. * - Check for activity on the root vnode (other than ourselves). * - Call vflush() to clear out vnodes for this file system, * except for the root vnode. * - Decrement reference on the vnode representing remote root. * - Close the socket * - Free up the data structures */ /* * We need to decrement the ref. count on the nfsnode representing * the remote root. See comment in mountnfs(). The VFS unmount() * has done vput on this vnode, otherwise we would get deadlock! */ if (error = nfs_nget(mp, &nmp->nm_fh, &np)) return(error); vp = NFSTOV(np); if (vp->v_usecount > 2) { vput(vp); return (EBUSY); } /* * Must handshake with nqnfs_clientd() if it is active. */ nmp->nm_flag |= NFSMNT_DISMINPROG; while (nmp->nm_inprog != NULLVP) (void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0); if (error = vflush(mp, vp, flags)) { vput(vp); nmp->nm_flag &= ~NFSMNT_DISMINPROG; return (error); } /* * We are now committed to the unmount. * For NQNFS, let the server daemon free the nfsmount structure. */ if (nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) nmp->nm_flag |= NFSMNT_DISMNT; /* * There are two reference counts to get rid of here. */ vrele(vp); vrele(vp); vgone(vp); nfs_disconnect(nmp); m_freem(nmp->nm_nam); if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) == 0) free((caddr_t)nmp, M_NFSMNT); return (0); } /* * Return root of a filesystem */ int nfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { register struct vnode *vp; struct nfsmount *nmp; struct nfsnode *np; int error; nmp = VFSTONFS(mp); if (error = nfs_nget(mp, &nmp->nm_fh, &np)) return (error); vp = NFSTOV(np); vp->v_type = VDIR; vp->v_flag = VROOT; *vpp = vp; return (0); } extern int syncprt; /* * Flush out the buffer cache */ /* ARGSUSED */ int nfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { register struct vnode *vp; int error, allerror = 0; /* * Force stale buffer cache information to be flushed. */ loop: for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = vp->v_mntvnodes.le_next) { /* * If the vnode that we are about to sync is no longer * associated with this mount point, start over. */ if (vp->v_mount != mp) goto loop; if (VOP_ISLOCKED(vp) || vp->v_dirtyblkhd.lh_first == NULL) continue; if (vget(vp, 1)) goto loop; if (error = VOP_FSYNC(vp, cred, waitfor, p)) allerror = error; vput(vp); } return (allerror); } /* * NFS flat namespace lookup. * Currently unsupported. */ /* ARGSUSED */ int nfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } /* * At this point, this should never happen */ /* ARGSUSED */ int nfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) register struct mount *mp; struct fid *fhp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred **credanonp; { return (EINVAL); } /* * Vnode pointer to File handle, should never happen either */ /* ARGSUSED */ int nfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (EINVAL); } /* * Vfs start routine, a no-op. */ /* ARGSUSED */ int nfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } /* * Do operations associated with quotas, not supported */ /* ARGSUSED */ int nfs_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } Index: head/sys/nfs/nfs_vnops.c =================================================================== --- head/sys/nfs/nfs_vnops.c (revision 2945) +++ head/sys/nfs/nfs_vnops.c (revision 2946) @@ -1,2545 +1,2548 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * 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. * * @(#)nfs_vnops.c 8.5 (Berkeley) 2/13/94 - * $Id: nfs_vnops.c,v 1.4 1994/08/08 17:30:53 davidg Exp $ + * $Id: nfs_vnops.c,v 1.5 1994/08/29 06:09:08 davidg Exp $ */ /* * vnode op calls for sun nfs version 2 */ #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 /* Defs */ #define TRUE 1 #define FALSE 0 /* * Global vfs data structures for nfs */ int (**nfsv2_vnodeop_p)(); struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, nfs_lookup }, /* lookup */ { &vop_create_desc, nfs_create }, /* create */ { &vop_mknod_desc, nfs_mknod }, /* mknod */ { &vop_open_desc, nfs_open }, /* open */ { &vop_close_desc, nfs_close }, /* close */ { &vop_access_desc, nfs_access }, /* access */ { &vop_getattr_desc, nfs_getattr }, /* getattr */ { &vop_setattr_desc, nfs_setattr }, /* setattr */ { &vop_read_desc, nfs_read }, /* read */ { &vop_write_desc, nfs_write }, /* write */ { &vop_ioctl_desc, nfs_ioctl }, /* ioctl */ { &vop_select_desc, nfs_select }, /* select */ { &vop_mmap_desc, nfs_mmap }, /* mmap */ { &vop_fsync_desc, nfs_fsync }, /* fsync */ { &vop_seek_desc, nfs_seek }, /* seek */ { &vop_remove_desc, nfs_remove }, /* remove */ { &vop_link_desc, nfs_link }, /* link */ { &vop_rename_desc, nfs_rename }, /* rename */ { &vop_mkdir_desc, nfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, nfs_rmdir }, /* rmdir */ { &vop_symlink_desc, nfs_symlink }, /* symlink */ { &vop_readdir_desc, nfs_readdir }, /* readdir */ { &vop_readlink_desc, nfs_readlink }, /* readlink */ { &vop_abortop_desc, nfs_abortop }, /* abortop */ { &vop_inactive_desc, nfs_inactive }, /* inactive */ { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */ { &vop_lock_desc, nfs_lock }, /* lock */ { &vop_unlock_desc, nfs_unlock }, /* unlock */ { &vop_bmap_desc, nfs_bmap }, /* bmap */ { &vop_strategy_desc, nfs_strategy }, /* strategy */ { &vop_print_desc, nfs_print }, /* print */ { &vop_islocked_desc, nfs_islocked }, /* islocked */ { &vop_pathconf_desc, nfs_pathconf }, /* pathconf */ { &vop_advlock_desc, nfs_advlock }, /* advlock */ { &vop_blkatoff_desc, nfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, nfs_valloc }, /* valloc */ { &vop_reallocblks_desc, nfs_reallocblks }, /* reallocblks */ { &vop_vfree_desc, nfs_vfree }, /* vfree */ { &vop_truncate_desc, nfs_truncate }, /* truncate */ { &vop_update_desc, nfs_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc nfsv2_vnodeop_opv_desc = { &nfsv2_vnodeop_p, nfsv2_vnodeop_entries }; +VNODEOP_SET(nfsv2_vnodeop_opv_desc); /* * Special device vnode ops */ int (**spec_nfsv2nodeop_p)(); struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, spec_lookup }, /* lookup */ { &vop_create_desc, spec_create }, /* create */ { &vop_mknod_desc, spec_mknod }, /* mknod */ { &vop_open_desc, spec_open }, /* open */ { &vop_close_desc, nfsspec_close }, /* close */ { &vop_access_desc, nfsspec_access }, /* access */ { &vop_getattr_desc, nfs_getattr }, /* getattr */ { &vop_setattr_desc, nfs_setattr }, /* setattr */ { &vop_read_desc, nfsspec_read }, /* read */ { &vop_write_desc, nfsspec_write }, /* write */ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ { &vop_select_desc, spec_select }, /* select */ { &vop_mmap_desc, spec_mmap }, /* mmap */ { &vop_fsync_desc, nfs_fsync }, /* fsync */ { &vop_seek_desc, spec_seek }, /* seek */ { &vop_remove_desc, spec_remove }, /* remove */ { &vop_link_desc, spec_link }, /* link */ { &vop_rename_desc, spec_rename }, /* rename */ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */ { &vop_symlink_desc, spec_symlink }, /* symlink */ { &vop_readdir_desc, spec_readdir }, /* readdir */ { &vop_readlink_desc, spec_readlink }, /* readlink */ { &vop_abortop_desc, spec_abortop }, /* abortop */ { &vop_inactive_desc, nfs_inactive }, /* inactive */ { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */ { &vop_lock_desc, nfs_lock }, /* lock */ { &vop_unlock_desc, nfs_unlock }, /* unlock */ { &vop_bmap_desc, spec_bmap }, /* bmap */ { &vop_strategy_desc, spec_strategy }, /* strategy */ { &vop_print_desc, nfs_print }, /* print */ { &vop_islocked_desc, nfs_islocked }, /* islocked */ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ { &vop_advlock_desc, spec_advlock }, /* advlock */ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, spec_valloc }, /* valloc */ { &vop_reallocblks_desc, spec_reallocblks }, /* reallocblks */ { &vop_vfree_desc, spec_vfree }, /* vfree */ { &vop_truncate_desc, spec_truncate }, /* truncate */ { &vop_update_desc, nfs_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc spec_nfsv2nodeop_opv_desc = { &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries }; +VNODEOP_SET(spec_nfsv2nodeop_opv_desc); #ifdef FIFO int (**fifo_nfsv2nodeop_p)(); struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, fifo_lookup }, /* lookup */ { &vop_create_desc, fifo_create }, /* create */ { &vop_mknod_desc, fifo_mknod }, /* mknod */ { &vop_open_desc, fifo_open }, /* open */ { &vop_close_desc, nfsfifo_close }, /* close */ { &vop_access_desc, nfsspec_access }, /* access */ { &vop_getattr_desc, nfs_getattr }, /* getattr */ { &vop_setattr_desc, nfs_setattr }, /* setattr */ { &vop_read_desc, nfsfifo_read }, /* read */ { &vop_write_desc, nfsfifo_write }, /* write */ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */ { &vop_select_desc, fifo_select }, /* select */ { &vop_mmap_desc, fifo_mmap }, /* mmap */ { &vop_fsync_desc, nfs_fsync }, /* fsync */ { &vop_seek_desc, fifo_seek }, /* seek */ { &vop_remove_desc, fifo_remove }, /* remove */ { &vop_link_desc, fifo_link }, /* link */ { &vop_rename_desc, fifo_rename }, /* rename */ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, fifo_symlink }, /* symlink */ { &vop_readdir_desc, fifo_readdir }, /* readdir */ { &vop_readlink_desc, fifo_readlink }, /* readlink */ { &vop_abortop_desc, fifo_abortop }, /* abortop */ { &vop_inactive_desc, nfs_inactive }, /* inactive */ { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */ { &vop_lock_desc, nfs_lock }, /* lock */ { &vop_unlock_desc, nfs_unlock }, /* unlock */ { &vop_bmap_desc, fifo_bmap }, /* bmap */ { &vop_strategy_desc, fifo_badop }, /* strategy */ { &vop_print_desc, nfs_print }, /* print */ { &vop_islocked_desc, nfs_islocked }, /* islocked */ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, fifo_valloc }, /* valloc */ { &vop_reallocblks_desc, fifo_reallocblks }, /* reallocblks */ { &vop_vfree_desc, fifo_vfree }, /* vfree */ { &vop_truncate_desc, fifo_truncate }, /* truncate */ { &vop_update_desc, nfs_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc = { &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries }; +VNODEOP_SET(fifo_nfsv2nodeop_opv_desc); #endif /* FIFO */ void nqnfs_clientlease(); /* * Global variables */ extern u_long nfs_procids[NFS_NPROCS]; extern u_long nfs_prog, nfs_vers, nfs_true, nfs_false; extern char nfsiobuf[MAXPHYS+NBPG]; struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON]; int nfs_numasync = 0; #define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1)) /* * nfs null call from vfs. */ int nfs_null(vp, cred, procp) struct vnode *vp; struct ucred *cred; struct proc *procp; { caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb; nfsm_reqhead(vp, NFSPROC_NULL, 0); nfsm_request(vp, NFSPROC_NULL, procp, cred); nfsm_reqdone; return (error); } /* * nfs access vnode op. * For nfs, just return ok. File accesses may fail later. * For nqnfs, use the access rpc to check accessibility. If file modes are * changed on the server, accesses might still fail later. */ int nfs_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 u_long *tl; register caddr_t cp; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; /* * For nqnfs, do an access rpc, otherwise you are stuck emulating * ufs_access() locally using the vattr. This may not be correct, * since the server may apply other access criteria such as * client uid-->server uid mapping that we do not know about, but * this is better than just returning anything that is lying about * in the cache. */ if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) { nfsstats.rpccnt[NQNFSPROC_ACCESS]++; nfsm_reqhead(vp, NQNFSPROC_ACCESS, NFSX_FH + 3 * NFSX_UNSIGNED); nfsm_fhtom(vp); nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); if (ap->a_mode & VREAD) *tl++ = nfs_true; else *tl++ = nfs_false; if (ap->a_mode & VWRITE) *tl++ = nfs_true; else *tl++ = nfs_false; if (ap->a_mode & VEXEC) *tl = nfs_true; else *tl = nfs_false; nfsm_request(vp, NQNFSPROC_ACCESS, ap->a_p, ap->a_cred); nfsm_reqdone; return (error); } else return (nfsspec_access(ap)); } /* * nfs open vnode op * Check to see if the type is ok * and that deletion is not in progress. * For paged in text files, you will need to flush the page cache * if consistency is lost. */ /* ARGSUSED */ int nfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; struct nfsnode *np = VTONFS(vp); struct nfsmount *nmp = VFSTONFS(vp->v_mount); struct vattr vattr; int error; if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) return (EACCES); /* * Get a valid lease. If cached data is stale, flush it. */ if (nmp->nm_flag & NFSMNT_NQNFS) { if (NQNFS_CKINVALID(vp, np, NQL_READ)) { do { error = nqnfs_getlease(vp, NQL_READ, ap->a_cred, ap->a_p); } while (error == NQNFS_EXPIRED); if (error) return (error); if (np->n_lrev != np->n_brev || (np->n_flag & NQNFSNONCACHE)) { if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); np->n_brev = np->n_lrev; } } } else { if (np->n_flag & NMODIFIED) { if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); np->n_attrstamp = 0; np->n_direofoffset = 0; if (error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p)) return (error); np->n_mtime = vattr.va_mtime.ts_sec; } else { if (error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p)) return (error); if (np->n_mtime != vattr.va_mtime.ts_sec) { np->n_direofoffset = 0; if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); np->n_mtime = vattr.va_mtime.ts_sec; } } } if ((nmp->nm_flag & NFSMNT_NQNFS) == 0) np->n_attrstamp = 0; /* For Open/Close consistency */ return (0); } /* * nfs close vnode op * For reg files, invalidate any buffer cache entries. */ /* ARGSUSED */ int nfs_close(ap) struct vop_close_args /* { struct vnodeop_desc *a_desc; 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 nfsnode *np = VTONFS(vp); int error = 0; if (vp->v_type == VREG) { if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) == 0 && (np->n_flag & NMODIFIED)) { error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); np->n_attrstamp = 0; } if (np->n_flag & NWRITEERR) { np->n_flag &= ~NWRITEERR; error = np->n_error; } } return (error); } /* * nfs getattr call from vfs. */ int nfs_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 nfsnode *np = VTONFS(vp); register caddr_t cp; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; /* * Update local times for special files. */ if (np->n_flag & (NACC | NUPD)) np->n_flag |= NCHG; /* * First look in the cache. */ if (nfs_getattrcache(vp, ap->a_vap) == 0) return (0); nfsstats.rpccnt[NFSPROC_GETATTR]++; nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH); nfsm_fhtom(vp); nfsm_request(vp, NFSPROC_GETATTR, ap->a_p, ap->a_cred); nfsm_loadattr(vp, ap->a_vap); nfsm_reqdone; return (error); } /* * nfs setattr call. */ int nfs_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; { register struct nfsv2_sattr *sp; register caddr_t cp; register long t1; caddr_t bpos, dpos, cp2; u_long *tl; int error = 0, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); register struct vattr *vap = ap->a_vap; u_quad_t frev, tsize = 0; if (vap->va_size != VNOVAL || vap->va_mtime.ts_sec != VNOVAL || vap->va_atime.ts_sec != VNOVAL) { if (vap->va_size != VNOVAL) { if (np->n_flag & NMODIFIED) { if (vap->va_size == 0) error = nfs_vinvalbuf(vp, 0, ap->a_cred, ap->a_p, 1); else error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); if (error) return (error); } tsize = np->n_size; np->n_size = np->n_vattr.va_size = vap->va_size; vnode_pager_setsize(vp, (u_long)np->n_size); } else if ((np->n_flag & NMODIFIED) && (error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); } nfsstats.rpccnt[NFSPROC_SETATTR]++; isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS); nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH+NFSX_SATTR(isnq)); nfsm_fhtom(vp); nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); if (vap->va_mode == (u_short)-1) sp->sa_mode = VNOVAL; else sp->sa_mode = vtonfs_mode(vp->v_type, vap->va_mode); if (vap->va_uid == (uid_t)-1) sp->sa_uid = VNOVAL; else sp->sa_uid = txdr_unsigned(vap->va_uid); if (vap->va_gid == (gid_t)-1) sp->sa_gid = VNOVAL; else sp->sa_gid = txdr_unsigned(vap->va_gid); if (isnq) { txdr_hyper(&vap->va_size, &sp->sa_nqsize); txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); sp->sa_nqflags = txdr_unsigned(vap->va_flags); sp->sa_nqrdev = VNOVAL; } else { sp->sa_nfssize = txdr_unsigned(vap->va_size); txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); } nfsm_request(vp, NFSPROC_SETATTR, ap->a_p, ap->a_cred); nfsm_loadattr(vp, (struct vattr *)0); if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) && NQNFS_CKCACHABLE(vp, NQL_WRITE)) { nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED); fxdr_hyper(tl, &frev); if (frev > np->n_brev) np->n_brev = frev; } nfsm_reqdone; if (error) { np->n_size = np->n_vattr.va_size = tsize; vnode_pager_setsize(vp, (u_long)np->n_size); } return (error); } /* * nfs lookup call, one step at a time... * First look in cache * If not found, unlock the directory nfsnode and do the rpc */ int nfs_lookup(ap) struct vop_lookup_args /* { struct vnodeop_desc *a_desc; struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { register struct componentname *cnp = ap->a_cnp; register struct vnode *dvp = ap->a_dvp; register struct vnode **vpp = ap->a_vpp; register int flags = cnp->cn_flags; register struct vnode *vdp; register u_long *tl; register caddr_t cp; register long t1, t2; struct nfsmount *nmp; caddr_t bpos, dpos, cp2; time_t reqtime = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vnode *newvp; long len; nfsv2fh_t *fhp; struct nfsnode *np; int lockparent, wantparent, error = 0; int nqlflag = 0, cachable = 0; u_quad_t frev; *vpp = NULL; if (dvp->v_type != VDIR) return (ENOTDIR); lockparent = flags & LOCKPARENT; wantparent = flags & (LOCKPARENT|WANTPARENT); nmp = VFSTONFS(dvp->v_mount); np = VTONFS(dvp); if ((error = cache_lookup(dvp, vpp, cnp)) && error != ENOENT) { struct vattr vattr; int vpid; vdp = *vpp; vpid = vdp->v_id; /* * See the comment starting `Step through' in ufs/ufs_lookup.c * for an explanation of the locking protocol */ if (dvp == vdp) { VREF(vdp); error = 0; } else error = vget(vdp, 1); if (!error) { if (vpid == vdp->v_id) { if (nmp->nm_flag & NFSMNT_NQNFS) { if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) == 0) { nfsstats.lookupcache_hits++; if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; return (0); } else if (NQNFS_CKCACHABLE(dvp, NQL_READ)) { if (np->n_lrev != np->n_brev || (np->n_flag & NMODIFIED)) { np->n_direofoffset = 0; cache_purge(dvp); error = nfs_vinvalbuf(dvp, 0, cnp->cn_cred, cnp->cn_proc, 1); if (error == EINTR) return (error); np->n_brev = np->n_lrev; } else { nfsstats.lookupcache_hits++; if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; return (0); } } } else if (!VOP_GETATTR(vdp, &vattr, cnp->cn_cred, cnp->cn_proc) && vattr.va_ctime.ts_sec == VTONFS(vdp)->n_ctime) { nfsstats.lookupcache_hits++; if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; return (0); } cache_purge(vdp); } vrele(vdp); } *vpp = NULLVP; } error = 0; nfsstats.lookupcache_misses++; nfsstats.rpccnt[NFSPROC_LOOKUP]++; len = cnp->cn_namelen; nfsm_reqhead(dvp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)); /* * For nqnfs optionally piggyback a getlease request for the name * being looked up. */ if (nmp->nm_flag & NFSMNT_NQNFS) { nfsm_build(tl, u_long *, NFSX_UNSIGNED); if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) && ((cnp->cn_flags & MAKEENTRY) && (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)))) *tl = txdr_unsigned(nmp->nm_leaseterm); else *tl = 0; } nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); reqtime = time.tv_sec; nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred); nfsmout: if (error) { if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && (flags & ISLASTCN) && error == ENOENT) error = EJUSTRETURN; if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; return (error); } if (nmp->nm_flag & NFSMNT_NQNFS) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); if (*tl) { nqlflag = fxdr_unsigned(int, *tl); nfsm_dissect(tl, u_long *, 4*NFSX_UNSIGNED); cachable = fxdr_unsigned(int, *tl++); reqtime += fxdr_unsigned(int, *tl++); fxdr_hyper(tl, &frev); } else nqlflag = 0; } nfsm_dissect(fhp, nfsv2fh_t *, NFSX_FH); /* * Handle RENAME case... */ if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) { if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) { m_freem(mrep); return (EISDIR); } if (error = nfs_nget(dvp->v_mount, fhp, &np)) { m_freem(mrep); return (error); } newvp = NFSTOV(np); if (error = nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr *)0)) { vrele(newvp); m_freem(mrep); return (error); } *vpp = newvp; m_freem(mrep); cnp->cn_flags |= SAVENAME; return (0); } if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) { VREF(dvp); newvp = dvp; } else { if (error = nfs_nget(dvp->v_mount, fhp, &np)) { m_freem(mrep); return (error); } newvp = NFSTOV(np); } if (error = nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr *)0)) { vrele(newvp); m_freem(mrep); return (error); } m_freem(mrep); *vpp = newvp; if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; if ((cnp->cn_flags & MAKEENTRY) && (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) { if ((nmp->nm_flag & NFSMNT_NQNFS) == 0) np->n_ctime = np->n_vattr.va_ctime.ts_sec; else if (nqlflag && reqtime > time.tv_sec) nqnfs_clientlease(nmp, np, nqlflag, cachable, reqtime, frev); cache_enter(dvp, *vpp, cnp); } return (0); } /* * nfs read call. * Just call nfs_bioread() to do the work. */ int nfs_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; if (vp->v_type != VREG) return (EPERM); return (nfs_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred)); } /* * nfs readlink call */ int nfs_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; if (vp->v_type != VLNK) return (EPERM); return (nfs_bioread(vp, ap->a_uio, 0, ap->a_cred)); } /* * Do a readlink rpc. * Called by nfs_doio() from below the buffer cache. */ int nfs_readlinkrpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; struct ucred *cred; { register u_long *tl; register caddr_t cp; register long t1; caddr_t bpos, dpos, cp2; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; long len; nfsstats.rpccnt[NFSPROC_READLINK]++; nfsm_reqhead(vp, NFSPROC_READLINK, NFSX_FH); nfsm_fhtom(vp); nfsm_request(vp, NFSPROC_READLINK, uiop->uio_procp, cred); nfsm_strsiz(len, NFS_MAXPATHLEN); nfsm_mtouio(uiop, len); nfsm_reqdone; return (error); } /* * nfs read rpc call * Ditto above */ int nfs_readrpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; struct ucred *cred; { register u_long *tl; register caddr_t cp; register long t1; caddr_t bpos, dpos, cp2; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nfsmount *nmp; long len, retlen, tsiz; nmp = VFSTONFS(vp->v_mount); tsiz = uiop->uio_resid; if (uiop->uio_offset + tsiz > 0xffffffff && (nmp->nm_flag & NFSMNT_NQNFS) == 0) return (EFBIG); while (tsiz > 0) { nfsstats.rpccnt[NFSPROC_READ]++; len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz; nfsm_reqhead(vp, NFSPROC_READ, NFSX_FH+NFSX_UNSIGNED*3); nfsm_fhtom(vp); nfsm_build(tl, u_long *, NFSX_UNSIGNED*3); if (nmp->nm_flag & NFSMNT_NQNFS) { txdr_hyper(&uiop->uio_offset, tl); *(tl + 2) = txdr_unsigned(len); } else { *tl++ = txdr_unsigned(uiop->uio_offset); *tl++ = txdr_unsigned(len); *tl = 0; } nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred); nfsm_loadattr(vp, (struct vattr *)0); nfsm_strsiz(retlen, nmp->nm_rsize); nfsm_mtouio(uiop, retlen); m_freem(mrep); if (retlen < len) tsiz = 0; else tsiz -= len; } nfsmout: return (error); } /* * nfs write call */ int nfs_writerpc(vp, uiop, cred, ioflags) register struct vnode *vp; struct uio *uiop; struct ucred *cred; int ioflags; { register u_long *tl; register caddr_t cp; register long t1; caddr_t bpos, dpos, cp2; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nfsmount *nmp; struct nfsnode *np = VTONFS(vp); u_quad_t frev; long len, tsiz; nmp = VFSTONFS(vp->v_mount); tsiz = uiop->uio_resid; if (uiop->uio_offset + tsiz > 0xffffffff && (nmp->nm_flag & NFSMNT_NQNFS) == 0) return (EFBIG); while (tsiz > 0) { nfsstats.rpccnt[NFSPROC_WRITE]++; len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz; nfsm_reqhead(vp, NFSPROC_WRITE, NFSX_FH+NFSX_UNSIGNED*4+nfsm_rndup(len)); nfsm_fhtom(vp); nfsm_build(tl, u_long *, NFSX_UNSIGNED * 4); if (nmp->nm_flag & NFSMNT_NQNFS) { txdr_hyper(&uiop->uio_offset, tl); tl += 2; if (ioflags & IO_APPEND) *tl++ = txdr_unsigned(1); else *tl++ = 0; } else { *++tl = txdr_unsigned(uiop->uio_offset); tl += 2; } *tl = txdr_unsigned(len); nfsm_uiotom(uiop, len); nfsm_request(vp, NFSPROC_WRITE, uiop->uio_procp, cred); nfsm_loadattr(vp, (struct vattr *)0); if (nmp->nm_flag & NFSMNT_MYWRITE) VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.va_mtime.ts_sec; else if ((nmp->nm_flag & NFSMNT_NQNFS) && NQNFS_CKCACHABLE(vp, NQL_WRITE)) { nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED); fxdr_hyper(tl, &frev); if (frev > np->n_brev) np->n_brev = frev; } m_freem(mrep); tsiz -= len; } nfsmout: if (error) uiop->uio_resid = tsiz; return (error); } /* * nfs mknod call * This is a kludge. Use a create rpc but with the IFMT bits of the mode * set to specify the file type and the size field for rdev. */ /* ARGSUSED */ int nfs_mknod(ap) struct vop_mknod_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 nfsv2_sattr *sp; register u_long *tl; register caddr_t cp; register long t1, t2; struct vnode *newvp = 0; struct vattr vattr; char *cp2; caddr_t bpos, dpos; int error = 0, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; u_long rdev; isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS); if (vap->va_type == VCHR || vap->va_type == VBLK) rdev = txdr_unsigned(vap->va_rdev); #ifdef FIFO else if (vap->va_type == VFIFO) rdev = 0xffffffff; #endif /* FIFO */ else { VOP_ABORTOP(dvp, cnp); vput(dvp); return (EOPNOTSUPP); } if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } nfsstats.rpccnt[NFSPROC_CREATE]++; nfsm_reqhead(dvp, NFSPROC_CREATE, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); sp->sa_mode = vtonfs_mode(vap->va_type, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(vattr.va_gid); if (isnq) { sp->sa_nqrdev = rdev; sp->sa_nqflags = 0; txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); } else { sp->sa_nfssize = rdev; txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); } nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred); nfsm_mtofh(dvp, newvp); nfsm_reqdone; if (!error && (cnp->cn_flags & MAKEENTRY)) cache_enter(dvp, newvp, cnp); FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; VTONFS(dvp)->n_attrstamp = 0; vrele(dvp); return (error); } /* * nfs file create call */ int nfs_create(ap) struct vop_create_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 nfsv2_sattr *sp; register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vattr vattr; if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } nfsstats.rpccnt[NFSPROC_CREATE]++; isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS); nfsm_reqhead(dvp, NFSPROC_CREATE, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); sp->sa_mode = vtonfs_mode(vap->va_type, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(vattr.va_gid); if (isnq) { u_quad_t qval = 0; txdr_hyper(&qval, &sp->sa_nqsize); sp->sa_nqflags = 0; sp->sa_nqrdev = -1; txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); } else { sp->sa_nfssize = 0; txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); } nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred); nfsm_mtofh(dvp, *ap->a_vpp); nfsm_reqdone; if (!error && (cnp->cn_flags & MAKEENTRY)) cache_enter(dvp, *ap->a_vpp, cnp); FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; VTONFS(dvp)->n_attrstamp = 0; vrele(dvp); return (error); } /* * nfs file remove call * To try and make nfs semantics closer to ufs semantics, a file that has * other processes using the vnode is renamed instead of removed and then * removed later on the last close. * - If v_usecount > 1 * If a rename is not already in the works * call nfs_sillyrename() to set it up * else * do the remove rpc */ int nfs_remove(ap) struct vop_remove_args /* { struct vnodeop_desc *a_desc; struct vnode * a_dvp; struct vnode * a_vp; struct componentname * a_cnp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct vnode *dvp = ap->a_dvp; register struct componentname *cnp = ap->a_cnp; register struct nfsnode *np = VTONFS(vp); register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; if (vp->v_usecount > 1) { if (!np->n_sillyrename) error = nfs_sillyrename(dvp, vp, cnp); } else { /* * Purge the name cache so that the chance of a lookup for * the name succeeding while the remove is in progress is * minimized. Without node locking it can still happen, such * that an I/O op returns ESTALE, but since you get this if * another host removes the file.. */ cache_purge(vp); /* * Throw away biocache buffers. Mainly to avoid * unnecessary delayed writes. */ error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, cnp->cn_proc, 1); if (error == EINTR) return (error); /* Do the rpc */ nfsstats.rpccnt[NFSPROC_REMOVE]++; nfsm_reqhead(dvp, NFSPROC_REMOVE, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_REMOVE, cnp->cn_proc, cnp->cn_cred); nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; VTONFS(dvp)->n_attrstamp = 0; /* * Kludge City: If the first reply to the remove rpc is lost.. * the reply to the retransmitted request will be ENOENT * since the file was in fact removed * Therefore, we cheat and return success. */ if (error == ENOENT) error = 0; } np->n_attrstamp = 0; vrele(dvp); vrele(vp); return (error); } /* * nfs file remove rpc called from nfs_inactive */ int nfs_removeit(sp) register struct sillyrename *sp; { register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; nfsstats.rpccnt[NFSPROC_REMOVE]++; nfsm_reqhead(sp->s_dvp, NFSPROC_REMOVE, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(sp->s_namlen)); nfsm_fhtom(sp->s_dvp); nfsm_strtom(sp->s_name, sp->s_namlen, NFS_MAXNAMLEN); nfsm_request(sp->s_dvp, NFSPROC_REMOVE, NULL, sp->s_cred); nfsm_reqdone; VTONFS(sp->s_dvp)->n_flag |= NMODIFIED; VTONFS(sp->s_dvp)->n_attrstamp = 0; return (error); } /* * nfs file rename call */ int nfs_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; { register struct vnode *fvp = ap->a_fvp; register struct vnode *tvp = ap->a_tvp; register struct vnode *fdvp = ap->a_fdvp; register struct vnode *tdvp = ap->a_tdvp; register struct componentname *tcnp = ap->a_tcnp; register struct componentname *fcnp = ap->a_fcnp; register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; /* Check for cross-device rename */ if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { error = EXDEV; goto out; } nfsstats.rpccnt[NFSPROC_RENAME]++; nfsm_reqhead(fdvp, NFSPROC_RENAME, (NFSX_FH+NFSX_UNSIGNED)*2+nfsm_rndup(fcnp->cn_namelen)+ nfsm_rndup(fcnp->cn_namelen)); /* or fcnp->cn_cred?*/ nfsm_fhtom(fdvp); nfsm_strtom(fcnp->cn_nameptr, fcnp->cn_namelen, NFS_MAXNAMLEN); nfsm_fhtom(tdvp); nfsm_strtom(tcnp->cn_nameptr, tcnp->cn_namelen, NFS_MAXNAMLEN); nfsm_request(fdvp, NFSPROC_RENAME, tcnp->cn_proc, tcnp->cn_cred); nfsm_reqdone; VTONFS(fdvp)->n_flag |= NMODIFIED; VTONFS(fdvp)->n_attrstamp = 0; VTONFS(tdvp)->n_flag |= NMODIFIED; VTONFS(tdvp)->n_attrstamp = 0; if (fvp->v_type == VDIR) { if (tvp != NULL && tvp->v_type == VDIR) cache_purge(tdvp); cache_purge(fdvp); } out: if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fdvp); vrele(fvp); /* * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry. */ if (error == ENOENT) error = 0; return (error); } /* * nfs file rename rpc called from nfs_remove() above */ int nfs_renameit(sdvp, scnp, sp) struct vnode *sdvp; struct componentname *scnp; register struct sillyrename *sp; { register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; nfsstats.rpccnt[NFSPROC_RENAME]++; nfsm_reqhead(sdvp, NFSPROC_RENAME, (NFSX_FH+NFSX_UNSIGNED)*2+nfsm_rndup(scnp->cn_namelen)+ nfsm_rndup(sp->s_namlen)); nfsm_fhtom(sdvp); nfsm_strtom(scnp->cn_nameptr, scnp->cn_namelen, NFS_MAXNAMLEN); nfsm_fhtom(sdvp); nfsm_strtom(sp->s_name, sp->s_namlen, NFS_MAXNAMLEN); nfsm_request(sdvp, NFSPROC_RENAME, scnp->cn_proc, scnp->cn_cred); nfsm_reqdone; FREE(scnp->cn_pnbuf, M_NAMEI); VTONFS(sdvp)->n_flag |= NMODIFIED; VTONFS(sdvp)->n_attrstamp = 0; return (error); } /* * nfs hard link create call */ int nfs_link(ap) struct vop_link_args /* { struct vnode *a_vp; struct vnode *a_tdvp; struct componentname *a_cnp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct vnode *tdvp = ap->a_tdvp; register struct componentname *cnp = ap->a_cnp; register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; if (vp->v_mount != tdvp->v_mount) { /*VOP_ABORTOP(vp, cnp);*/ if (tdvp == vp) vrele(vp); else vput(vp); return (EXDEV); } nfsstats.rpccnt[NFSPROC_LINK]++; nfsm_reqhead(tdvp, NFSPROC_LINK, NFSX_FH*2+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)); nfsm_fhtom(tdvp); nfsm_fhtom(vp); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_request(tdvp, NFSPROC_LINK, cnp->cn_proc, cnp->cn_cred); nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(tdvp)->n_attrstamp = 0; VTONFS(tdvp)->n_flag |= NMODIFIED; VTONFS(vp)->n_attrstamp = 0; vrele(vp); /* * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry. */ if (error == EEXIST) error = 0; return (error); } /* * nfs symbolic link create call */ /* start here */ int nfs_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 *dvp = ap->a_dvp; register struct vattr *vap = ap->a_vap; register struct componentname *cnp = ap->a_cnp; register struct nfsv2_sattr *sp; register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int slen, error = 0, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; nfsstats.rpccnt[NFSPROC_SYMLINK]++; slen = strlen(ap->a_target); isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS); nfsm_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH+2*NFSX_UNSIGNED+ nfsm_rndup(cnp->cn_namelen)+nfsm_rndup(slen)+NFSX_SATTR(isnq)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN); nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); sp->sa_mode = vtonfs_mode(VLNK, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(cnp->cn_cred->cr_gid); if (isnq) { quad_t qval = -1; txdr_hyper(&qval, &sp->sa_nqsize); sp->sa_nqflags = 0; txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); } else { sp->sa_nfssize = -1; txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); } nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred); nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; VTONFS(dvp)->n_attrstamp = 0; vrele(dvp); /* * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry. */ if (error == EEXIST) error = 0; return (error); } /* * nfs make dir call */ int nfs_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 vnode **vpp = ap->a_vpp; register struct nfsv2_sattr *sp; register u_long *tl; register caddr_t cp; register long t1, t2; register int len; caddr_t bpos, dpos, cp2; int error = 0, firsttry = 1, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vattr vattr; if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } len = cnp->cn_namelen; isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS); nfsstats.rpccnt[NFSPROC_MKDIR]++; nfsm_reqhead(dvp, NFSPROC_MKDIR, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)+NFSX_SATTR(isnq)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); sp->sa_mode = vtonfs_mode(VDIR, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(vattr.va_gid); if (isnq) { quad_t qval = -1; txdr_hyper(&qval, &sp->sa_nqsize); sp->sa_nqflags = 0; txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); } else { sp->sa_nfssize = -1; txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); } nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred); nfsm_mtofh(dvp, *vpp); nfsm_reqdone; VTONFS(dvp)->n_flag |= NMODIFIED; VTONFS(dvp)->n_attrstamp = 0; /* * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry * if we can succeed in looking up the directory. * "firsttry" is necessary since the macros may "goto nfsmout" which * is above the if on errors. (Ugh) */ if (error == EEXIST && firsttry) { firsttry = 0; error = 0; nfsstats.rpccnt[NFSPROC_LOOKUP]++; *vpp = NULL; nfsm_reqhead(dvp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred); nfsm_mtofh(dvp, *vpp); if ((*vpp)->v_type != VDIR) { vput(*vpp); error = EEXIST; } m_freem(mrep); } FREE(cnp->cn_pnbuf, M_NAMEI); vrele(dvp); return (error); } /* * nfs remove directory call */ int nfs_rmdir(ap) struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct vnode *dvp = ap->a_dvp; register struct componentname *cnp = ap->a_cnp; register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; if (dvp == vp) { vrele(dvp); vrele(dvp); FREE(cnp->cn_pnbuf, M_NAMEI); return (EINVAL); } nfsstats.rpccnt[NFSPROC_RMDIR]++; nfsm_reqhead(dvp, NFSPROC_RMDIR, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_RMDIR, cnp->cn_proc, cnp->cn_cred); nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; VTONFS(dvp)->n_attrstamp = 0; cache_purge(dvp); cache_purge(vp); vrele(vp); vrele(dvp); /* * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry. */ if (error == ENOENT) error = 0; return (error); } /* * nfs readdir call * Although cookie is defined as opaque, I translate it to/from net byte * order so that it looks more sensible. This appears consistent with the * Ultrix implementation of NFS. */ int nfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); register struct uio *uio = ap->a_uio; int tresid, error; struct vattr vattr; if (vp->v_type != VDIR) return (EPERM); /* * First, check for hit on the EOF offset cache */ if (uio->uio_offset != 0 && uio->uio_offset == np->n_direofoffset && (np->n_flag & NMODIFIED) == 0) { if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) { if (NQNFS_CKCACHABLE(vp, NQL_READ)) { nfsstats.direofcache_hits++; return (0); } } else if (VOP_GETATTR(vp, &vattr, ap->a_cred, uio->uio_procp) == 0 && np->n_mtime == vattr.va_mtime.ts_sec) { nfsstats.direofcache_hits++; return (0); } } /* * Call nfs_bioread() to do the real work. */ tresid = uio->uio_resid; error = nfs_bioread(vp, uio, 0, ap->a_cred); if (!error && uio->uio_resid == tresid) nfsstats.direofcache_misses++; return (error); } /* * Readdir rpc call. * Called from below the buffer cache by nfs_doio(). */ int nfs_readdirrpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; struct ucred *cred; { register long len; register struct dirent *dp = 0; register u_long *tl; register caddr_t cp; register long t1; long tlen, lastlen = 0; caddr_t bpos, dpos, cp2; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct mbuf *md2; caddr_t dpos2; int siz; int more_dirs = 1; u_long off, savoff = 0; struct dirent *savdp = 0; struct nfsmount *nmp; struct nfsnode *np = VTONFS(vp); long tresid; nmp = VFSTONFS(vp->v_mount); tresid = uiop->uio_resid; /* * Loop around doing readdir rpc's of size uio_resid or nm_rsize, * whichever is smaller, truncated to a multiple of NFS_DIRBLKSIZ. * The stopping criteria is EOF or buffer full. */ while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) { nfsstats.rpccnt[NFSPROC_READDIR]++; nfsm_reqhead(vp, NFSPROC_READDIR, NFSX_FH + 2 * NFSX_UNSIGNED); nfsm_fhtom(vp); nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); off = (u_long)uiop->uio_offset; *tl++ = txdr_unsigned(off); *tl = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ? nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1)); nfsm_request(vp, NFSPROC_READDIR, uiop->uio_procp, cred); siz = 0; nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = fxdr_unsigned(int, *tl); /* Save the position so that we can do nfsm_mtouio() later */ dpos2 = dpos; md2 = md; /* loop thru the dir entries, doctoring them to 4bsd form */ #ifdef lint dp = (struct dirent *)0; #endif /* lint */ while (more_dirs && siz < uiop->uio_resid) { savoff = off; /* Hold onto offset and dp */ savdp = dp; nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); dp = (struct dirent *)tl; dp->d_fileno = fxdr_unsigned(u_long, *tl++); len = fxdr_unsigned(int, *tl); if (len <= 0 || len > NFS_MAXNAMLEN) { error = EBADRPC; m_freem(mrep); goto nfsmout; } dp->d_namlen = (u_char)len; dp->d_type = DT_UNKNOWN; nfsm_adv(len); /* Point past name */ tlen = nfsm_rndup(len); /* * This should not be necessary, but some servers have * broken XDR such that these bytes are not null filled. */ if (tlen != len) { *dpos = '\0'; /* Null-terminate */ nfsm_adv(tlen - len); len = tlen; } nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); off = fxdr_unsigned(u_long, *tl); *tl++ = 0; /* Ensures null termination of name */ more_dirs = fxdr_unsigned(int, *tl); dp->d_reclen = len + 4 * NFSX_UNSIGNED; siz += dp->d_reclen; } /* * If at end of rpc data, get the eof boolean */ if (!more_dirs) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = (fxdr_unsigned(int, *tl) == 0); /* * If at EOF, cache directory offset */ if (!more_dirs) np->n_direofoffset = off; } /* * If there is too much to fit in the data buffer, use savoff and * savdp to trim off the last record. * --> we are not at eof */ if (siz > uiop->uio_resid) { off = savoff; siz -= dp->d_reclen; dp = savdp; more_dirs = 0; /* Paranoia */ } if (siz > 0) { lastlen = dp->d_reclen; md = md2; dpos = dpos2; nfsm_mtouio(uiop, siz); uiop->uio_offset = (off_t)off; } else more_dirs = 0; /* Ugh, never happens, but in case.. */ m_freem(mrep); } /* * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ * by increasing d_reclen for the last record. */ if (uiop->uio_resid < tresid) { len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1); if (len > 0) { dp = (struct dirent *) (uiop->uio_iov->iov_base - lastlen); dp->d_reclen += len; uiop->uio_iov->iov_base += len; uiop->uio_iov->iov_len -= len; uiop->uio_resid -= len; } } nfsmout: return (error); } /* * Nqnfs readdir_and_lookup RPC. Used in place of nfs_readdirrpc(). */ int nfs_readdirlookrpc(vp, uiop, cred) struct vnode *vp; register struct uio *uiop; struct ucred *cred; { register int len; register struct dirent *dp = 0; register u_long *tl; register caddr_t cp; register long t1; caddr_t bpos, dpos, cp2; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nameidata nami, *ndp = &nami; struct componentname *cnp = &ndp->ni_cnd; u_long off, endoff = 0, fileno; time_t reqtime, ltime = 0; struct nfsmount *nmp; struct nfsnode *np; struct vnode *newvp; nfsv2fh_t *fhp; u_quad_t frev; int error = 0, tlen, more_dirs = 1, tresid, doit, bigenough, i; int cachable = 0; if (uiop->uio_iovcnt != 1) panic("nfs rdirlook"); nmp = VFSTONFS(vp->v_mount); tresid = uiop->uio_resid; ndp->ni_dvp = vp; newvp = NULLVP; /* * Loop around doing readdir rpc's of size uio_resid or nm_rsize, * whichever is smaller, truncated to a multiple of NFS_DIRBLKSIZ. * The stopping criteria is EOF or buffer full. */ while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) { nfsstats.rpccnt[NQNFSPROC_READDIRLOOK]++; nfsm_reqhead(vp, NQNFSPROC_READDIRLOOK, NFSX_FH + 3 * NFSX_UNSIGNED); nfsm_fhtom(vp); nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); off = (u_long)uiop->uio_offset; *tl++ = txdr_unsigned(off); *tl++ = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ? nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1)); if (nmp->nm_flag & NFSMNT_NQLOOKLEASE) *tl = txdr_unsigned(nmp->nm_leaseterm); else *tl = 0; reqtime = time.tv_sec; nfsm_request(vp, NQNFSPROC_READDIRLOOK, uiop->uio_procp, cred); nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = fxdr_unsigned(int, *tl); /* loop thru the dir entries, doctoring them to 4bsd form */ bigenough = 1; while (more_dirs && bigenough) { doit = 1; nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED); if (nmp->nm_flag & NFSMNT_NQLOOKLEASE) { cachable = fxdr_unsigned(int, *tl++); ltime = reqtime + fxdr_unsigned(int, *tl++); fxdr_hyper(tl, &frev); } nfsm_dissect(fhp, nfsv2fh_t *, NFSX_FH); if (!bcmp(VTONFS(vp)->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) { VREF(vp); newvp = vp; np = VTONFS(vp); } else { if (error = nfs_nget(vp->v_mount, fhp, &np)) doit = 0; newvp = NFSTOV(np); } if (error = nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr *)0)) doit = 0; nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); fileno = fxdr_unsigned(u_long, *tl++); len = fxdr_unsigned(int, *tl); if (len <= 0 || len > NFS_MAXNAMLEN) { error = EBADRPC; m_freem(mrep); goto nfsmout; } tlen = (len + 4) & ~0x3; if ((tlen + DIRHDSIZ) > uiop->uio_resid) bigenough = 0; if (bigenough && doit) { dp = (struct dirent *)uiop->uio_iov->iov_base; dp->d_fileno = fileno; dp->d_namlen = len; dp->d_reclen = tlen + DIRHDSIZ; dp->d_type = IFTODT(VTTOIF(np->n_vattr.va_type)); uiop->uio_resid -= DIRHDSIZ; uiop->uio_iov->iov_base += DIRHDSIZ; uiop->uio_iov->iov_len -= DIRHDSIZ; cnp->cn_nameptr = uiop->uio_iov->iov_base; cnp->cn_namelen = len; ndp->ni_vp = newvp; nfsm_mtouio(uiop, len); cp = uiop->uio_iov->iov_base; tlen -= len; for (i = 0; i < tlen; i++) *cp++ = '\0'; uiop->uio_iov->iov_base += tlen; uiop->uio_iov->iov_len -= tlen; uiop->uio_resid -= tlen; cnp->cn_hash = 0; for (cp = cnp->cn_nameptr, i = 1; i <= len; i++, cp++) cnp->cn_hash += (unsigned char)*cp * i; if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) && ltime > time.tv_sec) nqnfs_clientlease(nmp, np, NQL_READ, cachable, ltime, frev); if (cnp->cn_namelen <= NCHNAMLEN) cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp); } else { nfsm_adv(nfsm_rndup(len)); } if (newvp != NULLVP) { vrele(newvp); newvp = NULLVP; } nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); if (bigenough) endoff = off = fxdr_unsigned(u_long, *tl++); else endoff = fxdr_unsigned(u_long, *tl++); more_dirs = fxdr_unsigned(int, *tl); } /* * If at end of rpc data, get the eof boolean */ if (!more_dirs) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = (fxdr_unsigned(int, *tl) == 0); /* * If at EOF, cache directory offset */ if (!more_dirs) VTONFS(vp)->n_direofoffset = endoff; } if (uiop->uio_resid < tresid) uiop->uio_offset = (off_t)off; else more_dirs = 0; m_freem(mrep); } /* * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ * by increasing d_reclen for the last record. */ if (uiop->uio_resid < tresid) { len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1); if (len > 0) { dp->d_reclen += len; uiop->uio_iov->iov_base += len; uiop->uio_iov->iov_len -= len; uiop->uio_resid -= len; } } nfsmout: if (newvp != NULLVP) vrele(newvp); return (error); } static char hextoasc[] = "0123456789abcdef"; /* * Silly rename. To make the NFS filesystem that is stateless look a little * more like the "ufs" a remove of an active vnode is translated to a rename * to a funny looking filename that is removed by nfs_inactive on the * nfsnode. There is the potential for another process on a different client * to create the same funny name between the nfs_lookitup() fails and the * nfs_rename() completes, but... */ int nfs_sillyrename(dvp, vp, cnp) struct vnode *dvp, *vp; struct componentname *cnp; { register struct nfsnode *np; register struct sillyrename *sp; int error; short pid; cache_purge(dvp); np = VTONFS(vp); #ifdef SILLYSEPARATE MALLOC(sp, struct sillyrename *, sizeof (struct sillyrename), M_NFSREQ, M_WAITOK); #else sp = &np->n_silly; #endif sp->s_cred = crdup(cnp->cn_cred); sp->s_dvp = dvp; VREF(dvp); /* Fudge together a funny name */ pid = cnp->cn_proc->p_pid; bcopy(".nfsAxxxx4.4", sp->s_name, 13); sp->s_namlen = 12; sp->s_name[8] = hextoasc[pid & 0xf]; sp->s_name[7] = hextoasc[(pid >> 4) & 0xf]; sp->s_name[6] = hextoasc[(pid >> 8) & 0xf]; sp->s_name[5] = hextoasc[(pid >> 12) & 0xf]; /* Try lookitups until we get one that isn't there */ while (nfs_lookitup(sp, (nfsv2fh_t *)0, cnp->cn_proc) == 0) { sp->s_name[4]++; if (sp->s_name[4] > 'z') { error = EINVAL; goto bad; } } if (error = nfs_renameit(dvp, cnp, sp)) goto bad; nfs_lookitup(sp, &np->n_fh, cnp->cn_proc); np->n_sillyrename = sp; return (0); bad: vrele(sp->s_dvp); crfree(sp->s_cred); #ifdef SILLYSEPARATE free((caddr_t)sp, M_NFSREQ); #endif return (error); } /* * Look up a file name for silly rename stuff. * Just like nfs_lookup() except that it doesn't load returned values * into the nfsnode table. * If fhp != NULL it copies the returned file handle out */ int nfs_lookitup(sp, fhp, procp) register struct sillyrename *sp; nfsv2fh_t *fhp; struct proc *procp; { register struct vnode *vp = sp->s_dvp; register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; long len; isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS); nfsstats.rpccnt[NFSPROC_LOOKUP]++; len = sp->s_namlen; nfsm_reqhead(vp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)); if (isnq) { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = 0; } nfsm_fhtom(vp); nfsm_strtom(sp->s_name, len, NFS_MAXNAMLEN); nfsm_request(vp, NFSPROC_LOOKUP, procp, sp->s_cred); if (fhp != NULL) { if (isnq) nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); nfsm_dissect(cp, caddr_t, NFSX_FH); bcopy(cp, (caddr_t)fhp, NFSX_FH); } nfsm_reqdone; return (error); } /* * Kludge City.. * - make nfs_bmap() essentially a no-op that does no translation * - do nfs_strategy() by faking physical I/O with nfs_readrpc/nfs_writerpc * after mapping the physical addresses into Kernel Virtual space in the * nfsiobuf area. * (Maybe I could use the process's page mapping, but I was concerned that * Kernel Write might not be enabled and also figured copyout() would do * a lot more work than bcopy() and also it currently happens in the * context of the swapper process (2). */ int nfs_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; { register struct vnode *vp = ap->a_vp; if (ap->a_vpp != NULL) *ap->a_vpp = vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize); return (0); } /* * Strategy routine. * For async requests when nfsiod(s) are running, queue the request by * calling nfs_asyncio(), otherwise just all nfs_doio() to do the * request. */ int nfs_strategy(ap) struct vop_strategy_args *ap; { register struct buf *bp = ap->a_bp; struct ucred *cr; struct proc *p; int error = 0; if (bp->b_flags & B_PHYS) panic("nfs physio"); if (bp->b_flags & B_ASYNC) p = (struct proc *)0; else p = curproc; /* XXX */ if (bp->b_flags & B_READ) cr = bp->b_rcred; else cr = bp->b_wcred; /* * If the op is asynchronous and an i/o daemon is waiting * queue the request, wake it up and wait for completion * otherwise just do it ourselves. */ if ((bp->b_flags & B_ASYNC) == 0 || nfs_asyncio(bp, NOCRED)) error = nfs_doio(bp, cr, p); return (error); } /* * Mmap a file * * NB Currently unsupported. */ /* ARGSUSED */ int nfs_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 all the blocks associated with a vnode. * Walk through the buffer pool and push any dirty pages * associated with the vnode. */ /* ARGSUSED */ int nfs_fsync(ap) struct vop_fsync_args /* { struct vnodeop_desc *a_desc; 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 nfsnode *np = VTONFS(vp); register struct buf *bp; struct buf *nbp; struct nfsmount *nmp; int s, error = 0, slptimeo = 0, slpflag = 0; nmp = VFSTONFS(vp->v_mount); if (nmp->nm_flag & NFSMNT_INT) slpflag = PCATCH; 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) { if (ap->a_waitfor != MNT_WAIT) continue; bp->b_flags |= B_WANTED; error = tsleep((caddr_t)bp, slpflag | (PRIBIO + 1), "nfsfsync", slptimeo); splx(s); if (error) { if (nfs_sigintr(nmp, (struct nfsreq *)0, ap->a_p)) return (EINTR); if (slpflag == PCATCH) { slpflag = 0; slptimeo = 2 * hz; } } goto loop; } if ((bp->b_flags & B_DELWRI) == 0) panic("nfs_fsync: not dirty"); bremfree(bp); bp->b_flags |= B_BUSY; splx(s); bp->b_flags |= B_ASYNC; VOP_BWRITE(bp); goto loop; } splx(s); if (ap->a_waitfor == MNT_WAIT) { while (vp->v_numoutput) { vp->v_flag |= VBWAIT; error = tsleep((caddr_t)&vp->v_numoutput, slpflag | (PRIBIO + 1), "nfsfsync", slptimeo); if (error) { if (nfs_sigintr(nmp, (struct nfsreq *)0, ap->a_p)) return (EINTR); if (slpflag == PCATCH) { slpflag = 0; slptimeo = 2 * hz; } } } if (vp->v_dirtyblkhd.lh_first) { #ifdef DIAGNOSTIC vprint("nfs_fsync: dirty", vp); #endif goto loop; } } if (np->n_flag & NWRITEERR) { error = np->n_error; np->n_flag &= ~NWRITEERR; } return (error); } /* * Return POSIX pathconf information applicable to nfs. * * Currently the NFS protocol does not support getting such * information from the remote server. */ /* ARGSUSED */ int nfs_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { return (EINVAL); } /* * NFS advisory byte-level locks. * Currently unsupported. */ int nfs_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 nfsnode *np = VTONFS(ap->a_vp); /* * The following kludge is to allow diskless support to work * until a real NFS lockd is implemented. Basically, just pretend * that this is a local lock. */ return (lf_advlock(ap, &(np->n_lockf), np->n_size)); } /* * Print out the contents of an nfsnode. */ int nfs_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); printf("tag VT_NFS, fileid %d fsid 0x%x", np->n_vattr.va_fileid, np->n_vattr.va_fsid); #ifdef FIFO if (vp->v_type == VFIFO) fifo_printinfo(vp); #endif /* FIFO */ printf("\n"); return (0); } /* * NFS directory offset lookup. * Currently unsupported. */ int nfs_blkatoff(ap) struct vop_blkatoff_args /* { struct vnode *a_vp; off_t a_offset; char **a_res; struct buf **a_bpp; } */ *ap; { return (EOPNOTSUPP); } /* * NFS flat namespace allocation. * Currently unsupported. */ int nfs_valloc(ap) struct vop_valloc_args /* { struct vnode *a_pvp; int a_mode; struct ucred *a_cred; struct vnode **a_vpp; } */ *ap; { return (EOPNOTSUPP); } /* * NFS flat namespace free. * Currently unsupported. */ int nfs_vfree(ap) struct vop_vfree_args /* { struct vnode *a_pvp; ino_t a_ino; int a_mode; } */ *ap; { return (EOPNOTSUPP); } /* * NFS file truncation. */ int nfs_truncate(ap) struct vop_truncate_args /* { struct vnode *a_vp; off_t a_length; int a_flags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { /* Use nfs_setattr */ printf("nfs_truncate: need to implement!!"); return (EOPNOTSUPP); } /* * NFS update. */ int nfs_update(ap) struct vop_update_args /* { struct vnode *a_vp; struct timeval *a_ta; struct timeval *a_tm; int a_waitfor; } */ *ap; { /* Use nfs_setattr */ printf("nfs_update: need to implement!!"); return (EOPNOTSUPP); } /* * nfs special file access vnode op. * Essentially just get vattr and then imitate iaccess() since the device is * local to the client. */ int nfsspec_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vattr *vap; register gid_t *gp; register struct ucred *cred = ap->a_cred; mode_t mode = ap->a_mode; struct vattr vattr; register int i; int error; /* * If you're the super-user, * you always get access. */ if (cred->cr_uid == 0) return (0); vap = &vattr; if (error = VOP_GETATTR(ap->a_vp, vap, cred, ap->a_p)) 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 (cred->cr_uid != vap->va_uid) { mode >>= 3; gp = cred->cr_groups; for (i = 0; i < cred->cr_ngroups; i++, gp++) if (vap->va_gid == *gp) goto found; mode >>= 3; found: ; } return ((vap->va_mode & mode) == mode ? 0 : EACCES); } /* * Read wrapper for special devices. */ int nfsspec_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); /* * Set access flag. */ np->n_flag |= NACC; np->n_atim = time; return (VOCALL(spec_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for special devices. */ int nfsspec_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); /* * Set update flag. */ np->n_flag |= NUPD; np->n_mtim = time; return (VOCALL(spec_vnodeop_p, VOFFSET(vop_write), ap)); } /* * Close wrapper for special devices. * * Update the times on the nfsnode then do device close. */ int nfsspec_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 nfsnode *np = VTONFS(vp); struct vattr vattr; if (np->n_flag & (NACC | NUPD)) { np->n_flag |= NCHG; if (vp->v_usecount == 1 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { VATTR_NULL(&vattr); if (np->n_flag & NACC) { vattr.va_atime.ts_sec = np->n_atim.tv_sec; vattr.va_atime.ts_nsec = np->n_atim.tv_usec * 1000; } if (np->n_flag & NUPD) { vattr.va_mtime.ts_sec = np->n_mtim.tv_sec; vattr.va_mtime.ts_nsec = np->n_mtim.tv_usec * 1000; } (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p); } } return (VOCALL(spec_vnodeop_p, VOFFSET(vop_close), ap)); } #ifdef FIFO /* * Read wrapper for fifos. */ int nfsfifo_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { extern int (**fifo_vnodeop_p)(); register struct nfsnode *np = VTONFS(ap->a_vp); /* * Set access flag. */ np->n_flag |= NACC; np->n_atim = time; return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for fifos. */ int nfsfifo_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { extern int (**fifo_vnodeop_p)(); register struct nfsnode *np = VTONFS(ap->a_vp); /* * Set update flag. */ np->n_flag |= NUPD; np->n_mtim = time; return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap)); } /* * Close wrapper for fifos. * * Update the times on the nfsnode then do fifo close. */ int nfsfifo_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 nfsnode *np = VTONFS(vp); struct vattr vattr; extern int (**fifo_vnodeop_p)(); if (np->n_flag & (NACC | NUPD)) { if (np->n_flag & NACC) np->n_atim = time; if (np->n_flag & NUPD) np->n_mtim = time; np->n_flag |= NCHG; if (vp->v_usecount == 1 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { VATTR_NULL(&vattr); if (np->n_flag & NACC) { vattr.va_atime.ts_sec = np->n_atim.tv_sec; vattr.va_atime.ts_nsec = np->n_atim.tv_usec * 1000; } if (np->n_flag & NUPD) { vattr.va_mtime.ts_sec = np->n_mtim.tv_sec; vattr.va_mtime.ts_nsec = np->n_mtim.tv_usec * 1000; } (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p); } } return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap)); } #endif /* FIFO */ Index: head/sys/nfsclient/nfs_vfsops.c =================================================================== --- head/sys/nfsclient/nfs_vfsops.c (revision 2945) +++ head/sys/nfsclient/nfs_vfsops.c (revision 2946) @@ -1,741 +1,742 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * 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. * * @(#)nfs_vfsops.c 8.3 (Berkeley) 1/4/94 - * $Id: nfs_vfsops.c,v 1.2 1994/08/02 07:52:16 davidg Exp $ + * $Id: nfs_vfsops.c,v 1.3 1994/08/20 16:03:19 davidg 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 /* * nfs vfs operations. */ struct vfsops nfs_vfsops = { nfs_mount, nfs_start, nfs_unmount, nfs_root, nfs_quotactl, nfs_statfs, nfs_sync, nfs_vget, nfs_fhtovp, nfs_vptofh, nfs_init, }; +VFS_SET(nfs_vfsops, nfs, MOUNT_NFS, 0); /* * This structure must be filled in by a primary bootstrap or bootstrap * server for a diskless/dataless machine. It is initialized below just * to ensure that it is allocated to initialized data (.data not .bss). */ struct nfs_diskless nfs_diskless = { 0 }; extern u_long nfs_procids[NFS_NPROCS]; extern u_long nfs_prog, nfs_vers; void nfs_disconnect __P((struct nfsmount *)); void nfsargs_ntoh __P((struct nfs_args *)); static struct mount *nfs_mountdiskless __P((char *, char *, int, struct sockaddr_in *, struct nfs_args *, register struct vnode **)); #define TRUE 1 #define FALSE 0 /* * nfs statfs call */ int nfs_statfs(mp, sbp, p) struct mount *mp; register struct statfs *sbp; struct proc *p; { register struct vnode *vp; register struct nfsv2_statfs *sfp; register caddr_t cp; register long t1; caddr_t bpos, dpos, cp2; int error = 0, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nfsmount *nmp; struct ucred *cred; struct nfsnode *np; nmp = VFSTONFS(mp); isnq = (nmp->nm_flag & NFSMNT_NQNFS); if (error = nfs_nget(mp, &nmp->nm_fh, &np)) return (error); vp = NFSTOV(np); nfsstats.rpccnt[NFSPROC_STATFS]++; cred = crget(); cred->cr_ngroups = 1; nfsm_reqhead(vp, NFSPROC_STATFS, NFSX_FH); nfsm_fhtom(vp); nfsm_request(vp, NFSPROC_STATFS, p, cred); nfsm_dissect(sfp, struct nfsv2_statfs *, NFSX_STATFS(isnq)); sbp->f_type = MOUNT_NFS; sbp->f_flags = nmp->nm_flag; sbp->f_iosize = NFS_MAXDGRAMDATA; sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize); sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks); sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree); sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail); if (isnq) { sbp->f_files = fxdr_unsigned(long, sfp->sf_files); sbp->f_ffree = fxdr_unsigned(long, sfp->sf_ffree); } else { sbp->f_files = 0; sbp->f_ffree = 0; } if (sbp != &mp->mnt_stat) { bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } nfsm_reqdone; vrele(vp); crfree(cred); return (error); } /* * Mount a remote root fs via. nfs. This depends on the info in the * nfs_diskless structure that has been filled in properly by some primary * bootstrap. * It goes something like this: * - do enough of "ifconfig" by calling ifioctl() so that the system * can talk to the server * - If nfs_diskless.mygateway is filled in, use that address as * a default gateway. * - hand craft the swap nfs vnode hanging off a fake mount point * if swdevt[0].sw_dev == NODEV * - build the rootfs mount point and call mountnfs() to do the rest. */ int nfs_mountroot() { register struct mount *mp; register struct nfs_diskless *nd = &nfs_diskless; struct socket *so; struct vnode *vp; struct proc *p = curproc; /* XXX */ int error, i; /* * XXX time must be non-zero when we init the interface or else * the arp code will wedge... */ if (time.tv_sec == 0) time.tv_sec = 1; #ifdef notyet /* Set up swap credentials. */ proc0.p_ucred->cr_uid = ntohl(nd->swap_ucred.cr_uid); proc0.p_ucred->cr_gid = ntohl(nd->swap_ucred.cr_gid); if ((proc0.p_ucred->cr_ngroups = ntohs(nd->swap_ucred.cr_ngroups)) > NGROUPS) proc0.p_ucred->cr_ngroups = NGROUPS; for (i = 0; i < proc0.p_ucred->cr_ngroups; i++) proc0.p_ucred->cr_groups[i] = ntohl(nd->swap_ucred.cr_groups[i]); #endif /* * Do enough of ifconfig(8) so that the critical net interface can * talk to the server. */ if (error = socreate(nd->myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0)) panic("nfs_mountroot: socreate: %d", error); if (error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, p)) panic("nfs_mountroot: SIOCAIFADDR: %d", error); soclose(so); /* * If the gateway field is filled in, set it as the default route. */ if (nd->mygateway.sin_len != 0) { struct sockaddr_in mask, sin; bzero((caddr_t)&mask, sizeof(mask)); sin = mask; sin.sin_family = AF_INET; sin.sin_len = sizeof(sin); if (error = rtrequest(RTM_ADD, (struct sockaddr *)&sin, (struct sockaddr *)&nd->mygateway, (struct sockaddr *)&mask, RTF_UP | RTF_GATEWAY, (struct rtentry **)0)) panic("nfs_mountroot: RTM_ADD: %d", error); } /* * If swapping to an nfs node (indicated by swdevt[0].sw_dev == NODEV): * Create a fake mount point just for the swap vnode so that the * swap file can be on a different server from the rootfs. */ if (swdevt[0].sw_dev == NODEV) { nd->swap_args.fh = (nfsv2fh_t *)nd->swap_fh; (void) nfs_mountdiskless(nd->swap_hostnam, "/swap", 0, &nd->swap_saddr, &nd->swap_args, &vp); /* * Since the swap file is not the root dir of a file system, * hack it to a regular file. */ vp->v_type = VREG; vp->v_flag = 0; swapdev_vp = vp; VREF(vp); swdevt[0].sw_vp = vp; swdevt[0].sw_nblks = ntohl(nd->swap_nblks); } else if (bdevvp(swapdev, &swapdev_vp)) panic("nfs_mountroot: can't setup swapdev_vp"); /* * Create the rootfs mount point. */ nd->root_args.fh = (nfsv2fh_t *)nd->root_fh; mp = nfs_mountdiskless(nd->root_hostnam, "/", MNT_RDONLY, &nd->root_saddr, &nd->root_args, &vp); if (vfs_lock(mp)) panic("nfs_mountroot: vfs_lock"); TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); mp->mnt_flag |= MNT_ROOTFS; mp->mnt_vnodecovered = NULLVP; vfs_unlock(mp); rootvp = vp; /* * This is not really an nfs issue, but it is much easier to * set hostname here and then let the "/etc/rc.xxx" files * mount the right /var based upon its preset value. */ bcopy(nd->my_hostnam, hostname, MAXHOSTNAMELEN); hostname[MAXHOSTNAMELEN - 1] = '\0'; for (i = 0; i < MAXHOSTNAMELEN; i++) if (hostname[i] == '\0') break; hostnamelen = i; inittodr(ntohl(nd->root_time)); return (0); } /* * Internal version of mount system call for diskless setup. */ static struct mount * nfs_mountdiskless(path, which, mountflag, sin, args, vpp) char *path; char *which; int mountflag; struct sockaddr_in *sin; struct nfs_args *args; register struct vnode **vpp; { register struct mount *mp; register struct mbuf *m; register int error; mp = (struct mount *)malloc((u_long)sizeof(struct mount), M_MOUNT, M_NOWAIT); if (mp == NULL) panic("nfs_mountroot: %s mount malloc", which); bzero((char *)mp, (u_long)sizeof(struct mount)); mp->mnt_op = &nfs_vfsops; mp->mnt_flag = mountflag; MGET(m, MT_SONAME, M_DONTWAIT); if (m == NULL) panic("nfs_mountroot: %s mount mbuf", which); bcopy((caddr_t)sin, mtod(m, caddr_t), sin->sin_len); m->m_len = sin->sin_len; nfsargs_ntoh(args); if (error = mountnfs(args, mp, m, which, path, vpp)) panic("nfs_mountroot: mount %s on %s: %d", path, which, error); return (mp); } /* * Convert the integer fields of the nfs_args structure from net byte order * to host byte order. Called by nfs_mountroot() above. */ void nfsargs_ntoh(nfsp) register struct nfs_args *nfsp; { NTOHL(nfsp->sotype); NTOHL(nfsp->proto); NTOHL(nfsp->flags); NTOHL(nfsp->wsize); NTOHL(nfsp->rsize); NTOHL(nfsp->timeo); NTOHL(nfsp->retrans); NTOHL(nfsp->maxgrouplist); NTOHL(nfsp->readahead); NTOHL(nfsp->leaseterm); NTOHL(nfsp->deadthresh); } /* * VFS Operations. * * mount system call * It seems a bit dumb to copyinstr() the host and path here and then * bcopy() them in mountnfs(), but I wanted to detect errors before * doing the sockargs() call because sockargs() allocates an mbuf and * an error after that means that I have to release the mbuf. */ /* ARGSUSED */ int nfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { int error; struct nfs_args args; struct mbuf *nam; struct vnode *vp; char pth[MNAMELEN], hst[MNAMELEN]; u_int len; nfsv2fh_t nfh; if (error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args))) return (error); if (error = copyin((caddr_t)args.fh, (caddr_t)&nfh, sizeof (nfsv2fh_t))) return (error); if (error = copyinstr(path, pth, MNAMELEN-1, &len)) return (error); bzero(&pth[len], MNAMELEN - len); if (error = copyinstr(args.hostname, hst, MNAMELEN-1, &len)) return (error); bzero(&hst[len], MNAMELEN - len); /* sockargs() call must be after above copyin() calls */ if (error = sockargs(&nam, (caddr_t)args.addr, args.addrlen, MT_SONAME)) return (error); args.fh = &nfh; error = mountnfs(&args, mp, nam, pth, hst, &vp); return (error); } /* * Common code for mount and mountroot */ int mountnfs(argp, mp, nam, pth, hst, vpp) register struct nfs_args *argp; register struct mount *mp; struct mbuf *nam; char *pth, *hst; struct vnode **vpp; { register struct nfsmount *nmp; struct nfsnode *np; int error; if (mp->mnt_flag & MNT_UPDATE) { nmp = VFSTONFS(mp); /* update paths, file handles, etc, here XXX */ m_freem(nam); return (0); } else { MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount), M_NFSMNT, M_WAITOK); bzero((caddr_t)nmp, sizeof (struct nfsmount)); mp->mnt_data = (qaddr_t)nmp; } getnewfsid(mp, MOUNT_NFS); nmp->nm_mountp = mp; nmp->nm_flag = argp->flags; if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_MYWRITE)) == (NFSMNT_NQNFS | NFSMNT_MYWRITE)) { error = EPERM; goto bad; } if (nmp->nm_flag & NFSMNT_NQNFS) /* * We have to set mnt_maxsymlink to a non-zero value so * that COMPAT_43 routines will know that we are setting * the d_type field in directories (and can zero it for * unsuspecting binaries). */ mp->mnt_maxsymlinklen = 1; nmp->nm_timeo = NFS_TIMEO; nmp->nm_retry = NFS_RETRANS; nmp->nm_wsize = NFS_WSIZE; nmp->nm_rsize = NFS_RSIZE; nmp->nm_numgrps = NFS_MAXGRPS; nmp->nm_readahead = NFS_DEFRAHEAD; nmp->nm_leaseterm = NQ_DEFLEASE; nmp->nm_deadthresh = NQ_DEADTHRESH; nmp->nm_tnext = (struct nfsnode *)nmp; nmp->nm_tprev = (struct nfsnode *)nmp; nmp->nm_inprog = NULLVP; bcopy((caddr_t)argp->fh, (caddr_t)&nmp->nm_fh, sizeof(nfsv2fh_t)); mp->mnt_stat.f_type = MOUNT_NFS; bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN); bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN); nmp->nm_nam = nam; if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) { nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10; if (nmp->nm_timeo < NFS_MINTIMEO) nmp->nm_timeo = NFS_MINTIMEO; else if (nmp->nm_timeo > NFS_MAXTIMEO) nmp->nm_timeo = NFS_MAXTIMEO; } if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) { nmp->nm_retry = argp->retrans; if (nmp->nm_retry > NFS_MAXREXMIT) nmp->nm_retry = NFS_MAXREXMIT; } if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) { nmp->nm_wsize = argp->wsize; /* Round down to multiple of blocksize */ nmp->nm_wsize &= ~0x1ff; if (nmp->nm_wsize <= 0) nmp->nm_wsize = 512; else if (nmp->nm_wsize > NFS_MAXDATA) nmp->nm_wsize = NFS_MAXDATA; } if (nmp->nm_wsize > MAXBSIZE) nmp->nm_wsize = MAXBSIZE; if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) { nmp->nm_rsize = argp->rsize; /* Round down to multiple of blocksize */ nmp->nm_rsize &= ~0x1ff; if (nmp->nm_rsize <= 0) nmp->nm_rsize = 512; else if (nmp->nm_rsize > NFS_MAXDATA) nmp->nm_rsize = NFS_MAXDATA; } if (nmp->nm_rsize > MAXBSIZE) nmp->nm_rsize = MAXBSIZE; if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 && argp->maxgrouplist <= NFS_MAXGRPS) nmp->nm_numgrps = argp->maxgrouplist; if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 && argp->readahead <= NFS_MAXRAHEAD) nmp->nm_readahead = argp->readahead; if ((argp->flags & NFSMNT_LEASETERM) && argp->leaseterm >= 2 && argp->leaseterm <= NQ_MAXLEASE) nmp->nm_leaseterm = argp->leaseterm; if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 1 && argp->deadthresh <= NQ_NEVERDEAD) nmp->nm_deadthresh = argp->deadthresh; /* Set up the sockets and per-host congestion */ nmp->nm_sotype = argp->sotype; nmp->nm_soproto = argp->proto; /* * For Connection based sockets (TCP,...) defer the connect until * the first request, in case the server is not responding. */ if (nmp->nm_sotype == SOCK_DGRAM && (error = nfs_connect(nmp, (struct nfsreq *)0))) goto bad; /* * This is silly, but it has to be set so that vinifod() works. * We do not want to do an nfs_statfs() here since we can get * stuck on a dead server and we are holding a lock on the mount * point. */ mp->mnt_stat.f_iosize = NFS_MAXDGRAMDATA; /* * A reference count is needed on the nfsnode representing the * remote root. If this object is not persistent, then backward * traversals of the mount point (i.e. "..") will not work if * the nfsnode gets flushed out of the cache. Ufs does not have * this problem, because one can identify root inodes by their * number == ROOTINO (2). */ if (error = nfs_nget(mp, &nmp->nm_fh, &np)) goto bad; *vpp = NFSTOV(np); return (0); bad: nfs_disconnect(nmp); free((caddr_t)nmp, M_NFSMNT); m_freem(nam); return (error); } /* * unmount system call */ int nfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { register struct nfsmount *nmp; struct nfsnode *np; struct vnode *vp; int error, flags = 0; extern int doforce; if (mntflags & MNT_FORCE) { if (!doforce) return (EINVAL); flags |= FORCECLOSE; } nmp = VFSTONFS(mp); /* * Goes something like this.. * - Check for activity on the root vnode (other than ourselves). * - Call vflush() to clear out vnodes for this file system, * except for the root vnode. * - Decrement reference on the vnode representing remote root. * - Close the socket * - Free up the data structures */ /* * We need to decrement the ref. count on the nfsnode representing * the remote root. See comment in mountnfs(). The VFS unmount() * has done vput on this vnode, otherwise we would get deadlock! */ if (error = nfs_nget(mp, &nmp->nm_fh, &np)) return(error); vp = NFSTOV(np); if (vp->v_usecount > 2) { vput(vp); return (EBUSY); } /* * Must handshake with nqnfs_clientd() if it is active. */ nmp->nm_flag |= NFSMNT_DISMINPROG; while (nmp->nm_inprog != NULLVP) (void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0); if (error = vflush(mp, vp, flags)) { vput(vp); nmp->nm_flag &= ~NFSMNT_DISMINPROG; return (error); } /* * We are now committed to the unmount. * For NQNFS, let the server daemon free the nfsmount structure. */ if (nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) nmp->nm_flag |= NFSMNT_DISMNT; /* * There are two reference counts to get rid of here. */ vrele(vp); vrele(vp); vgone(vp); nfs_disconnect(nmp); m_freem(nmp->nm_nam); if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) == 0) free((caddr_t)nmp, M_NFSMNT); return (0); } /* * Return root of a filesystem */ int nfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { register struct vnode *vp; struct nfsmount *nmp; struct nfsnode *np; int error; nmp = VFSTONFS(mp); if (error = nfs_nget(mp, &nmp->nm_fh, &np)) return (error); vp = NFSTOV(np); vp->v_type = VDIR; vp->v_flag = VROOT; *vpp = vp; return (0); } extern int syncprt; /* * Flush out the buffer cache */ /* ARGSUSED */ int nfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { register struct vnode *vp; int error, allerror = 0; /* * Force stale buffer cache information to be flushed. */ loop: for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = vp->v_mntvnodes.le_next) { /* * If the vnode that we are about to sync is no longer * associated with this mount point, start over. */ if (vp->v_mount != mp) goto loop; if (VOP_ISLOCKED(vp) || vp->v_dirtyblkhd.lh_first == NULL) continue; if (vget(vp, 1)) goto loop; if (error = VOP_FSYNC(vp, cred, waitfor, p)) allerror = error; vput(vp); } return (allerror); } /* * NFS flat namespace lookup. * Currently unsupported. */ /* ARGSUSED */ int nfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } /* * At this point, this should never happen */ /* ARGSUSED */ int nfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) register struct mount *mp; struct fid *fhp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred **credanonp; { return (EINVAL); } /* * Vnode pointer to File handle, should never happen either */ /* ARGSUSED */ int nfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (EINVAL); } /* * Vfs start routine, a no-op. */ /* ARGSUSED */ int nfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } /* * Do operations associated with quotas, not supported */ /* ARGSUSED */ int nfs_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } Index: head/sys/nfsclient/nfs_vnops.c =================================================================== --- head/sys/nfsclient/nfs_vnops.c (revision 2945) +++ head/sys/nfsclient/nfs_vnops.c (revision 2946) @@ -1,2545 +1,2548 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * 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. * * @(#)nfs_vnops.c 8.5 (Berkeley) 2/13/94 - * $Id: nfs_vnops.c,v 1.4 1994/08/08 17:30:53 davidg Exp $ + * $Id: nfs_vnops.c,v 1.5 1994/08/29 06:09:08 davidg Exp $ */ /* * vnode op calls for sun nfs version 2 */ #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 /* Defs */ #define TRUE 1 #define FALSE 0 /* * Global vfs data structures for nfs */ int (**nfsv2_vnodeop_p)(); struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, nfs_lookup }, /* lookup */ { &vop_create_desc, nfs_create }, /* create */ { &vop_mknod_desc, nfs_mknod }, /* mknod */ { &vop_open_desc, nfs_open }, /* open */ { &vop_close_desc, nfs_close }, /* close */ { &vop_access_desc, nfs_access }, /* access */ { &vop_getattr_desc, nfs_getattr }, /* getattr */ { &vop_setattr_desc, nfs_setattr }, /* setattr */ { &vop_read_desc, nfs_read }, /* read */ { &vop_write_desc, nfs_write }, /* write */ { &vop_ioctl_desc, nfs_ioctl }, /* ioctl */ { &vop_select_desc, nfs_select }, /* select */ { &vop_mmap_desc, nfs_mmap }, /* mmap */ { &vop_fsync_desc, nfs_fsync }, /* fsync */ { &vop_seek_desc, nfs_seek }, /* seek */ { &vop_remove_desc, nfs_remove }, /* remove */ { &vop_link_desc, nfs_link }, /* link */ { &vop_rename_desc, nfs_rename }, /* rename */ { &vop_mkdir_desc, nfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, nfs_rmdir }, /* rmdir */ { &vop_symlink_desc, nfs_symlink }, /* symlink */ { &vop_readdir_desc, nfs_readdir }, /* readdir */ { &vop_readlink_desc, nfs_readlink }, /* readlink */ { &vop_abortop_desc, nfs_abortop }, /* abortop */ { &vop_inactive_desc, nfs_inactive }, /* inactive */ { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */ { &vop_lock_desc, nfs_lock }, /* lock */ { &vop_unlock_desc, nfs_unlock }, /* unlock */ { &vop_bmap_desc, nfs_bmap }, /* bmap */ { &vop_strategy_desc, nfs_strategy }, /* strategy */ { &vop_print_desc, nfs_print }, /* print */ { &vop_islocked_desc, nfs_islocked }, /* islocked */ { &vop_pathconf_desc, nfs_pathconf }, /* pathconf */ { &vop_advlock_desc, nfs_advlock }, /* advlock */ { &vop_blkatoff_desc, nfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, nfs_valloc }, /* valloc */ { &vop_reallocblks_desc, nfs_reallocblks }, /* reallocblks */ { &vop_vfree_desc, nfs_vfree }, /* vfree */ { &vop_truncate_desc, nfs_truncate }, /* truncate */ { &vop_update_desc, nfs_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc nfsv2_vnodeop_opv_desc = { &nfsv2_vnodeop_p, nfsv2_vnodeop_entries }; +VNODEOP_SET(nfsv2_vnodeop_opv_desc); /* * Special device vnode ops */ int (**spec_nfsv2nodeop_p)(); struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, spec_lookup }, /* lookup */ { &vop_create_desc, spec_create }, /* create */ { &vop_mknod_desc, spec_mknod }, /* mknod */ { &vop_open_desc, spec_open }, /* open */ { &vop_close_desc, nfsspec_close }, /* close */ { &vop_access_desc, nfsspec_access }, /* access */ { &vop_getattr_desc, nfs_getattr }, /* getattr */ { &vop_setattr_desc, nfs_setattr }, /* setattr */ { &vop_read_desc, nfsspec_read }, /* read */ { &vop_write_desc, nfsspec_write }, /* write */ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ { &vop_select_desc, spec_select }, /* select */ { &vop_mmap_desc, spec_mmap }, /* mmap */ { &vop_fsync_desc, nfs_fsync }, /* fsync */ { &vop_seek_desc, spec_seek }, /* seek */ { &vop_remove_desc, spec_remove }, /* remove */ { &vop_link_desc, spec_link }, /* link */ { &vop_rename_desc, spec_rename }, /* rename */ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */ { &vop_symlink_desc, spec_symlink }, /* symlink */ { &vop_readdir_desc, spec_readdir }, /* readdir */ { &vop_readlink_desc, spec_readlink }, /* readlink */ { &vop_abortop_desc, spec_abortop }, /* abortop */ { &vop_inactive_desc, nfs_inactive }, /* inactive */ { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */ { &vop_lock_desc, nfs_lock }, /* lock */ { &vop_unlock_desc, nfs_unlock }, /* unlock */ { &vop_bmap_desc, spec_bmap }, /* bmap */ { &vop_strategy_desc, spec_strategy }, /* strategy */ { &vop_print_desc, nfs_print }, /* print */ { &vop_islocked_desc, nfs_islocked }, /* islocked */ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ { &vop_advlock_desc, spec_advlock }, /* advlock */ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, spec_valloc }, /* valloc */ { &vop_reallocblks_desc, spec_reallocblks }, /* reallocblks */ { &vop_vfree_desc, spec_vfree }, /* vfree */ { &vop_truncate_desc, spec_truncate }, /* truncate */ { &vop_update_desc, nfs_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc spec_nfsv2nodeop_opv_desc = { &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries }; +VNODEOP_SET(spec_nfsv2nodeop_opv_desc); #ifdef FIFO int (**fifo_nfsv2nodeop_p)(); struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, fifo_lookup }, /* lookup */ { &vop_create_desc, fifo_create }, /* create */ { &vop_mknod_desc, fifo_mknod }, /* mknod */ { &vop_open_desc, fifo_open }, /* open */ { &vop_close_desc, nfsfifo_close }, /* close */ { &vop_access_desc, nfsspec_access }, /* access */ { &vop_getattr_desc, nfs_getattr }, /* getattr */ { &vop_setattr_desc, nfs_setattr }, /* setattr */ { &vop_read_desc, nfsfifo_read }, /* read */ { &vop_write_desc, nfsfifo_write }, /* write */ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */ { &vop_select_desc, fifo_select }, /* select */ { &vop_mmap_desc, fifo_mmap }, /* mmap */ { &vop_fsync_desc, nfs_fsync }, /* fsync */ { &vop_seek_desc, fifo_seek }, /* seek */ { &vop_remove_desc, fifo_remove }, /* remove */ { &vop_link_desc, fifo_link }, /* link */ { &vop_rename_desc, fifo_rename }, /* rename */ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, fifo_symlink }, /* symlink */ { &vop_readdir_desc, fifo_readdir }, /* readdir */ { &vop_readlink_desc, fifo_readlink }, /* readlink */ { &vop_abortop_desc, fifo_abortop }, /* abortop */ { &vop_inactive_desc, nfs_inactive }, /* inactive */ { &vop_reclaim_desc, nfs_reclaim }, /* reclaim */ { &vop_lock_desc, nfs_lock }, /* lock */ { &vop_unlock_desc, nfs_unlock }, /* unlock */ { &vop_bmap_desc, fifo_bmap }, /* bmap */ { &vop_strategy_desc, fifo_badop }, /* strategy */ { &vop_print_desc, nfs_print }, /* print */ { &vop_islocked_desc, nfs_islocked }, /* islocked */ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, fifo_valloc }, /* valloc */ { &vop_reallocblks_desc, fifo_reallocblks }, /* reallocblks */ { &vop_vfree_desc, fifo_vfree }, /* vfree */ { &vop_truncate_desc, fifo_truncate }, /* truncate */ { &vop_update_desc, nfs_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc = { &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries }; +VNODEOP_SET(fifo_nfsv2nodeop_opv_desc); #endif /* FIFO */ void nqnfs_clientlease(); /* * Global variables */ extern u_long nfs_procids[NFS_NPROCS]; extern u_long nfs_prog, nfs_vers, nfs_true, nfs_false; extern char nfsiobuf[MAXPHYS+NBPG]; struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON]; int nfs_numasync = 0; #define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1)) /* * nfs null call from vfs. */ int nfs_null(vp, cred, procp) struct vnode *vp; struct ucred *cred; struct proc *procp; { caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb; nfsm_reqhead(vp, NFSPROC_NULL, 0); nfsm_request(vp, NFSPROC_NULL, procp, cred); nfsm_reqdone; return (error); } /* * nfs access vnode op. * For nfs, just return ok. File accesses may fail later. * For nqnfs, use the access rpc to check accessibility. If file modes are * changed on the server, accesses might still fail later. */ int nfs_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 u_long *tl; register caddr_t cp; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; /* * For nqnfs, do an access rpc, otherwise you are stuck emulating * ufs_access() locally using the vattr. This may not be correct, * since the server may apply other access criteria such as * client uid-->server uid mapping that we do not know about, but * this is better than just returning anything that is lying about * in the cache. */ if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) { nfsstats.rpccnt[NQNFSPROC_ACCESS]++; nfsm_reqhead(vp, NQNFSPROC_ACCESS, NFSX_FH + 3 * NFSX_UNSIGNED); nfsm_fhtom(vp); nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); if (ap->a_mode & VREAD) *tl++ = nfs_true; else *tl++ = nfs_false; if (ap->a_mode & VWRITE) *tl++ = nfs_true; else *tl++ = nfs_false; if (ap->a_mode & VEXEC) *tl = nfs_true; else *tl = nfs_false; nfsm_request(vp, NQNFSPROC_ACCESS, ap->a_p, ap->a_cred); nfsm_reqdone; return (error); } else return (nfsspec_access(ap)); } /* * nfs open vnode op * Check to see if the type is ok * and that deletion is not in progress. * For paged in text files, you will need to flush the page cache * if consistency is lost. */ /* ARGSUSED */ int nfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vnode *vp = ap->a_vp; struct nfsnode *np = VTONFS(vp); struct nfsmount *nmp = VFSTONFS(vp->v_mount); struct vattr vattr; int error; if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) return (EACCES); /* * Get a valid lease. If cached data is stale, flush it. */ if (nmp->nm_flag & NFSMNT_NQNFS) { if (NQNFS_CKINVALID(vp, np, NQL_READ)) { do { error = nqnfs_getlease(vp, NQL_READ, ap->a_cred, ap->a_p); } while (error == NQNFS_EXPIRED); if (error) return (error); if (np->n_lrev != np->n_brev || (np->n_flag & NQNFSNONCACHE)) { if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); np->n_brev = np->n_lrev; } } } else { if (np->n_flag & NMODIFIED) { if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); np->n_attrstamp = 0; np->n_direofoffset = 0; if (error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p)) return (error); np->n_mtime = vattr.va_mtime.ts_sec; } else { if (error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p)) return (error); if (np->n_mtime != vattr.va_mtime.ts_sec) { np->n_direofoffset = 0; if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); np->n_mtime = vattr.va_mtime.ts_sec; } } } if ((nmp->nm_flag & NFSMNT_NQNFS) == 0) np->n_attrstamp = 0; /* For Open/Close consistency */ return (0); } /* * nfs close vnode op * For reg files, invalidate any buffer cache entries. */ /* ARGSUSED */ int nfs_close(ap) struct vop_close_args /* { struct vnodeop_desc *a_desc; 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 nfsnode *np = VTONFS(vp); int error = 0; if (vp->v_type == VREG) { if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) == 0 && (np->n_flag & NMODIFIED)) { error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); np->n_attrstamp = 0; } if (np->n_flag & NWRITEERR) { np->n_flag &= ~NWRITEERR; error = np->n_error; } } return (error); } /* * nfs getattr call from vfs. */ int nfs_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 nfsnode *np = VTONFS(vp); register caddr_t cp; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; /* * Update local times for special files. */ if (np->n_flag & (NACC | NUPD)) np->n_flag |= NCHG; /* * First look in the cache. */ if (nfs_getattrcache(vp, ap->a_vap) == 0) return (0); nfsstats.rpccnt[NFSPROC_GETATTR]++; nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH); nfsm_fhtom(vp); nfsm_request(vp, NFSPROC_GETATTR, ap->a_p, ap->a_cred); nfsm_loadattr(vp, ap->a_vap); nfsm_reqdone; return (error); } /* * nfs setattr call. */ int nfs_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; { register struct nfsv2_sattr *sp; register caddr_t cp; register long t1; caddr_t bpos, dpos, cp2; u_long *tl; int error = 0, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); register struct vattr *vap = ap->a_vap; u_quad_t frev, tsize = 0; if (vap->va_size != VNOVAL || vap->va_mtime.ts_sec != VNOVAL || vap->va_atime.ts_sec != VNOVAL) { if (vap->va_size != VNOVAL) { if (np->n_flag & NMODIFIED) { if (vap->va_size == 0) error = nfs_vinvalbuf(vp, 0, ap->a_cred, ap->a_p, 1); else error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); if (error) return (error); } tsize = np->n_size; np->n_size = np->n_vattr.va_size = vap->va_size; vnode_pager_setsize(vp, (u_long)np->n_size); } else if ((np->n_flag & NMODIFIED) && (error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); } nfsstats.rpccnt[NFSPROC_SETATTR]++; isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS); nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH+NFSX_SATTR(isnq)); nfsm_fhtom(vp); nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); if (vap->va_mode == (u_short)-1) sp->sa_mode = VNOVAL; else sp->sa_mode = vtonfs_mode(vp->v_type, vap->va_mode); if (vap->va_uid == (uid_t)-1) sp->sa_uid = VNOVAL; else sp->sa_uid = txdr_unsigned(vap->va_uid); if (vap->va_gid == (gid_t)-1) sp->sa_gid = VNOVAL; else sp->sa_gid = txdr_unsigned(vap->va_gid); if (isnq) { txdr_hyper(&vap->va_size, &sp->sa_nqsize); txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); sp->sa_nqflags = txdr_unsigned(vap->va_flags); sp->sa_nqrdev = VNOVAL; } else { sp->sa_nfssize = txdr_unsigned(vap->va_size); txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); } nfsm_request(vp, NFSPROC_SETATTR, ap->a_p, ap->a_cred); nfsm_loadattr(vp, (struct vattr *)0); if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) && NQNFS_CKCACHABLE(vp, NQL_WRITE)) { nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED); fxdr_hyper(tl, &frev); if (frev > np->n_brev) np->n_brev = frev; } nfsm_reqdone; if (error) { np->n_size = np->n_vattr.va_size = tsize; vnode_pager_setsize(vp, (u_long)np->n_size); } return (error); } /* * nfs lookup call, one step at a time... * First look in cache * If not found, unlock the directory nfsnode and do the rpc */ int nfs_lookup(ap) struct vop_lookup_args /* { struct vnodeop_desc *a_desc; struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { register struct componentname *cnp = ap->a_cnp; register struct vnode *dvp = ap->a_dvp; register struct vnode **vpp = ap->a_vpp; register int flags = cnp->cn_flags; register struct vnode *vdp; register u_long *tl; register caddr_t cp; register long t1, t2; struct nfsmount *nmp; caddr_t bpos, dpos, cp2; time_t reqtime = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vnode *newvp; long len; nfsv2fh_t *fhp; struct nfsnode *np; int lockparent, wantparent, error = 0; int nqlflag = 0, cachable = 0; u_quad_t frev; *vpp = NULL; if (dvp->v_type != VDIR) return (ENOTDIR); lockparent = flags & LOCKPARENT; wantparent = flags & (LOCKPARENT|WANTPARENT); nmp = VFSTONFS(dvp->v_mount); np = VTONFS(dvp); if ((error = cache_lookup(dvp, vpp, cnp)) && error != ENOENT) { struct vattr vattr; int vpid; vdp = *vpp; vpid = vdp->v_id; /* * See the comment starting `Step through' in ufs/ufs_lookup.c * for an explanation of the locking protocol */ if (dvp == vdp) { VREF(vdp); error = 0; } else error = vget(vdp, 1); if (!error) { if (vpid == vdp->v_id) { if (nmp->nm_flag & NFSMNT_NQNFS) { if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) == 0) { nfsstats.lookupcache_hits++; if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; return (0); } else if (NQNFS_CKCACHABLE(dvp, NQL_READ)) { if (np->n_lrev != np->n_brev || (np->n_flag & NMODIFIED)) { np->n_direofoffset = 0; cache_purge(dvp); error = nfs_vinvalbuf(dvp, 0, cnp->cn_cred, cnp->cn_proc, 1); if (error == EINTR) return (error); np->n_brev = np->n_lrev; } else { nfsstats.lookupcache_hits++; if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; return (0); } } } else if (!VOP_GETATTR(vdp, &vattr, cnp->cn_cred, cnp->cn_proc) && vattr.va_ctime.ts_sec == VTONFS(vdp)->n_ctime) { nfsstats.lookupcache_hits++; if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; return (0); } cache_purge(vdp); } vrele(vdp); } *vpp = NULLVP; } error = 0; nfsstats.lookupcache_misses++; nfsstats.rpccnt[NFSPROC_LOOKUP]++; len = cnp->cn_namelen; nfsm_reqhead(dvp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)); /* * For nqnfs optionally piggyback a getlease request for the name * being looked up. */ if (nmp->nm_flag & NFSMNT_NQNFS) { nfsm_build(tl, u_long *, NFSX_UNSIGNED); if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) && ((cnp->cn_flags & MAKEENTRY) && (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)))) *tl = txdr_unsigned(nmp->nm_leaseterm); else *tl = 0; } nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); reqtime = time.tv_sec; nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred); nfsmout: if (error) { if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && (flags & ISLASTCN) && error == ENOENT) error = EJUSTRETURN; if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; return (error); } if (nmp->nm_flag & NFSMNT_NQNFS) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); if (*tl) { nqlflag = fxdr_unsigned(int, *tl); nfsm_dissect(tl, u_long *, 4*NFSX_UNSIGNED); cachable = fxdr_unsigned(int, *tl++); reqtime += fxdr_unsigned(int, *tl++); fxdr_hyper(tl, &frev); } else nqlflag = 0; } nfsm_dissect(fhp, nfsv2fh_t *, NFSX_FH); /* * Handle RENAME case... */ if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) { if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) { m_freem(mrep); return (EISDIR); } if (error = nfs_nget(dvp->v_mount, fhp, &np)) { m_freem(mrep); return (error); } newvp = NFSTOV(np); if (error = nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr *)0)) { vrele(newvp); m_freem(mrep); return (error); } *vpp = newvp; m_freem(mrep); cnp->cn_flags |= SAVENAME; return (0); } if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) { VREF(dvp); newvp = dvp; } else { if (error = nfs_nget(dvp->v_mount, fhp, &np)) { m_freem(mrep); return (error); } newvp = NFSTOV(np); } if (error = nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr *)0)) { vrele(newvp); m_freem(mrep); return (error); } m_freem(mrep); *vpp = newvp; if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; if ((cnp->cn_flags & MAKEENTRY) && (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) { if ((nmp->nm_flag & NFSMNT_NQNFS) == 0) np->n_ctime = np->n_vattr.va_ctime.ts_sec; else if (nqlflag && reqtime > time.tv_sec) nqnfs_clientlease(nmp, np, nqlflag, cachable, reqtime, frev); cache_enter(dvp, *vpp, cnp); } return (0); } /* * nfs read call. * Just call nfs_bioread() to do the work. */ int nfs_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; if (vp->v_type != VREG) return (EPERM); return (nfs_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred)); } /* * nfs readlink call */ int nfs_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; if (vp->v_type != VLNK) return (EPERM); return (nfs_bioread(vp, ap->a_uio, 0, ap->a_cred)); } /* * Do a readlink rpc. * Called by nfs_doio() from below the buffer cache. */ int nfs_readlinkrpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; struct ucred *cred; { register u_long *tl; register caddr_t cp; register long t1; caddr_t bpos, dpos, cp2; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; long len; nfsstats.rpccnt[NFSPROC_READLINK]++; nfsm_reqhead(vp, NFSPROC_READLINK, NFSX_FH); nfsm_fhtom(vp); nfsm_request(vp, NFSPROC_READLINK, uiop->uio_procp, cred); nfsm_strsiz(len, NFS_MAXPATHLEN); nfsm_mtouio(uiop, len); nfsm_reqdone; return (error); } /* * nfs read rpc call * Ditto above */ int nfs_readrpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; struct ucred *cred; { register u_long *tl; register caddr_t cp; register long t1; caddr_t bpos, dpos, cp2; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nfsmount *nmp; long len, retlen, tsiz; nmp = VFSTONFS(vp->v_mount); tsiz = uiop->uio_resid; if (uiop->uio_offset + tsiz > 0xffffffff && (nmp->nm_flag & NFSMNT_NQNFS) == 0) return (EFBIG); while (tsiz > 0) { nfsstats.rpccnt[NFSPROC_READ]++; len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz; nfsm_reqhead(vp, NFSPROC_READ, NFSX_FH+NFSX_UNSIGNED*3); nfsm_fhtom(vp); nfsm_build(tl, u_long *, NFSX_UNSIGNED*3); if (nmp->nm_flag & NFSMNT_NQNFS) { txdr_hyper(&uiop->uio_offset, tl); *(tl + 2) = txdr_unsigned(len); } else { *tl++ = txdr_unsigned(uiop->uio_offset); *tl++ = txdr_unsigned(len); *tl = 0; } nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred); nfsm_loadattr(vp, (struct vattr *)0); nfsm_strsiz(retlen, nmp->nm_rsize); nfsm_mtouio(uiop, retlen); m_freem(mrep); if (retlen < len) tsiz = 0; else tsiz -= len; } nfsmout: return (error); } /* * nfs write call */ int nfs_writerpc(vp, uiop, cred, ioflags) register struct vnode *vp; struct uio *uiop; struct ucred *cred; int ioflags; { register u_long *tl; register caddr_t cp; register long t1; caddr_t bpos, dpos, cp2; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nfsmount *nmp; struct nfsnode *np = VTONFS(vp); u_quad_t frev; long len, tsiz; nmp = VFSTONFS(vp->v_mount); tsiz = uiop->uio_resid; if (uiop->uio_offset + tsiz > 0xffffffff && (nmp->nm_flag & NFSMNT_NQNFS) == 0) return (EFBIG); while (tsiz > 0) { nfsstats.rpccnt[NFSPROC_WRITE]++; len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz; nfsm_reqhead(vp, NFSPROC_WRITE, NFSX_FH+NFSX_UNSIGNED*4+nfsm_rndup(len)); nfsm_fhtom(vp); nfsm_build(tl, u_long *, NFSX_UNSIGNED * 4); if (nmp->nm_flag & NFSMNT_NQNFS) { txdr_hyper(&uiop->uio_offset, tl); tl += 2; if (ioflags & IO_APPEND) *tl++ = txdr_unsigned(1); else *tl++ = 0; } else { *++tl = txdr_unsigned(uiop->uio_offset); tl += 2; } *tl = txdr_unsigned(len); nfsm_uiotom(uiop, len); nfsm_request(vp, NFSPROC_WRITE, uiop->uio_procp, cred); nfsm_loadattr(vp, (struct vattr *)0); if (nmp->nm_flag & NFSMNT_MYWRITE) VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.va_mtime.ts_sec; else if ((nmp->nm_flag & NFSMNT_NQNFS) && NQNFS_CKCACHABLE(vp, NQL_WRITE)) { nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED); fxdr_hyper(tl, &frev); if (frev > np->n_brev) np->n_brev = frev; } m_freem(mrep); tsiz -= len; } nfsmout: if (error) uiop->uio_resid = tsiz; return (error); } /* * nfs mknod call * This is a kludge. Use a create rpc but with the IFMT bits of the mode * set to specify the file type and the size field for rdev. */ /* ARGSUSED */ int nfs_mknod(ap) struct vop_mknod_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 nfsv2_sattr *sp; register u_long *tl; register caddr_t cp; register long t1, t2; struct vnode *newvp = 0; struct vattr vattr; char *cp2; caddr_t bpos, dpos; int error = 0, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; u_long rdev; isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS); if (vap->va_type == VCHR || vap->va_type == VBLK) rdev = txdr_unsigned(vap->va_rdev); #ifdef FIFO else if (vap->va_type == VFIFO) rdev = 0xffffffff; #endif /* FIFO */ else { VOP_ABORTOP(dvp, cnp); vput(dvp); return (EOPNOTSUPP); } if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } nfsstats.rpccnt[NFSPROC_CREATE]++; nfsm_reqhead(dvp, NFSPROC_CREATE, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); sp->sa_mode = vtonfs_mode(vap->va_type, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(vattr.va_gid); if (isnq) { sp->sa_nqrdev = rdev; sp->sa_nqflags = 0; txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); } else { sp->sa_nfssize = rdev; txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); } nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred); nfsm_mtofh(dvp, newvp); nfsm_reqdone; if (!error && (cnp->cn_flags & MAKEENTRY)) cache_enter(dvp, newvp, cnp); FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; VTONFS(dvp)->n_attrstamp = 0; vrele(dvp); return (error); } /* * nfs file create call */ int nfs_create(ap) struct vop_create_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 nfsv2_sattr *sp; register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vattr vattr; if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } nfsstats.rpccnt[NFSPROC_CREATE]++; isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS); nfsm_reqhead(dvp, NFSPROC_CREATE, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); sp->sa_mode = vtonfs_mode(vap->va_type, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(vattr.va_gid); if (isnq) { u_quad_t qval = 0; txdr_hyper(&qval, &sp->sa_nqsize); sp->sa_nqflags = 0; sp->sa_nqrdev = -1; txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); } else { sp->sa_nfssize = 0; txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); } nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred); nfsm_mtofh(dvp, *ap->a_vpp); nfsm_reqdone; if (!error && (cnp->cn_flags & MAKEENTRY)) cache_enter(dvp, *ap->a_vpp, cnp); FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; VTONFS(dvp)->n_attrstamp = 0; vrele(dvp); return (error); } /* * nfs file remove call * To try and make nfs semantics closer to ufs semantics, a file that has * other processes using the vnode is renamed instead of removed and then * removed later on the last close. * - If v_usecount > 1 * If a rename is not already in the works * call nfs_sillyrename() to set it up * else * do the remove rpc */ int nfs_remove(ap) struct vop_remove_args /* { struct vnodeop_desc *a_desc; struct vnode * a_dvp; struct vnode * a_vp; struct componentname * a_cnp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct vnode *dvp = ap->a_dvp; register struct componentname *cnp = ap->a_cnp; register struct nfsnode *np = VTONFS(vp); register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; if (vp->v_usecount > 1) { if (!np->n_sillyrename) error = nfs_sillyrename(dvp, vp, cnp); } else { /* * Purge the name cache so that the chance of a lookup for * the name succeeding while the remove is in progress is * minimized. Without node locking it can still happen, such * that an I/O op returns ESTALE, but since you get this if * another host removes the file.. */ cache_purge(vp); /* * Throw away biocache buffers. Mainly to avoid * unnecessary delayed writes. */ error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, cnp->cn_proc, 1); if (error == EINTR) return (error); /* Do the rpc */ nfsstats.rpccnt[NFSPROC_REMOVE]++; nfsm_reqhead(dvp, NFSPROC_REMOVE, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_REMOVE, cnp->cn_proc, cnp->cn_cred); nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; VTONFS(dvp)->n_attrstamp = 0; /* * Kludge City: If the first reply to the remove rpc is lost.. * the reply to the retransmitted request will be ENOENT * since the file was in fact removed * Therefore, we cheat and return success. */ if (error == ENOENT) error = 0; } np->n_attrstamp = 0; vrele(dvp); vrele(vp); return (error); } /* * nfs file remove rpc called from nfs_inactive */ int nfs_removeit(sp) register struct sillyrename *sp; { register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; nfsstats.rpccnt[NFSPROC_REMOVE]++; nfsm_reqhead(sp->s_dvp, NFSPROC_REMOVE, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(sp->s_namlen)); nfsm_fhtom(sp->s_dvp); nfsm_strtom(sp->s_name, sp->s_namlen, NFS_MAXNAMLEN); nfsm_request(sp->s_dvp, NFSPROC_REMOVE, NULL, sp->s_cred); nfsm_reqdone; VTONFS(sp->s_dvp)->n_flag |= NMODIFIED; VTONFS(sp->s_dvp)->n_attrstamp = 0; return (error); } /* * nfs file rename call */ int nfs_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; { register struct vnode *fvp = ap->a_fvp; register struct vnode *tvp = ap->a_tvp; register struct vnode *fdvp = ap->a_fdvp; register struct vnode *tdvp = ap->a_tdvp; register struct componentname *tcnp = ap->a_tcnp; register struct componentname *fcnp = ap->a_fcnp; register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; /* Check for cross-device rename */ if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { error = EXDEV; goto out; } nfsstats.rpccnt[NFSPROC_RENAME]++; nfsm_reqhead(fdvp, NFSPROC_RENAME, (NFSX_FH+NFSX_UNSIGNED)*2+nfsm_rndup(fcnp->cn_namelen)+ nfsm_rndup(fcnp->cn_namelen)); /* or fcnp->cn_cred?*/ nfsm_fhtom(fdvp); nfsm_strtom(fcnp->cn_nameptr, fcnp->cn_namelen, NFS_MAXNAMLEN); nfsm_fhtom(tdvp); nfsm_strtom(tcnp->cn_nameptr, tcnp->cn_namelen, NFS_MAXNAMLEN); nfsm_request(fdvp, NFSPROC_RENAME, tcnp->cn_proc, tcnp->cn_cred); nfsm_reqdone; VTONFS(fdvp)->n_flag |= NMODIFIED; VTONFS(fdvp)->n_attrstamp = 0; VTONFS(tdvp)->n_flag |= NMODIFIED; VTONFS(tdvp)->n_attrstamp = 0; if (fvp->v_type == VDIR) { if (tvp != NULL && tvp->v_type == VDIR) cache_purge(tdvp); cache_purge(fdvp); } out: if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fdvp); vrele(fvp); /* * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry. */ if (error == ENOENT) error = 0; return (error); } /* * nfs file rename rpc called from nfs_remove() above */ int nfs_renameit(sdvp, scnp, sp) struct vnode *sdvp; struct componentname *scnp; register struct sillyrename *sp; { register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; nfsstats.rpccnt[NFSPROC_RENAME]++; nfsm_reqhead(sdvp, NFSPROC_RENAME, (NFSX_FH+NFSX_UNSIGNED)*2+nfsm_rndup(scnp->cn_namelen)+ nfsm_rndup(sp->s_namlen)); nfsm_fhtom(sdvp); nfsm_strtom(scnp->cn_nameptr, scnp->cn_namelen, NFS_MAXNAMLEN); nfsm_fhtom(sdvp); nfsm_strtom(sp->s_name, sp->s_namlen, NFS_MAXNAMLEN); nfsm_request(sdvp, NFSPROC_RENAME, scnp->cn_proc, scnp->cn_cred); nfsm_reqdone; FREE(scnp->cn_pnbuf, M_NAMEI); VTONFS(sdvp)->n_flag |= NMODIFIED; VTONFS(sdvp)->n_attrstamp = 0; return (error); } /* * nfs hard link create call */ int nfs_link(ap) struct vop_link_args /* { struct vnode *a_vp; struct vnode *a_tdvp; struct componentname *a_cnp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct vnode *tdvp = ap->a_tdvp; register struct componentname *cnp = ap->a_cnp; register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; if (vp->v_mount != tdvp->v_mount) { /*VOP_ABORTOP(vp, cnp);*/ if (tdvp == vp) vrele(vp); else vput(vp); return (EXDEV); } nfsstats.rpccnt[NFSPROC_LINK]++; nfsm_reqhead(tdvp, NFSPROC_LINK, NFSX_FH*2+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)); nfsm_fhtom(tdvp); nfsm_fhtom(vp); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_request(tdvp, NFSPROC_LINK, cnp->cn_proc, cnp->cn_cred); nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(tdvp)->n_attrstamp = 0; VTONFS(tdvp)->n_flag |= NMODIFIED; VTONFS(vp)->n_attrstamp = 0; vrele(vp); /* * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry. */ if (error == EEXIST) error = 0; return (error); } /* * nfs symbolic link create call */ /* start here */ int nfs_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 *dvp = ap->a_dvp; register struct vattr *vap = ap->a_vap; register struct componentname *cnp = ap->a_cnp; register struct nfsv2_sattr *sp; register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int slen, error = 0, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; nfsstats.rpccnt[NFSPROC_SYMLINK]++; slen = strlen(ap->a_target); isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS); nfsm_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH+2*NFSX_UNSIGNED+ nfsm_rndup(cnp->cn_namelen)+nfsm_rndup(slen)+NFSX_SATTR(isnq)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN); nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); sp->sa_mode = vtonfs_mode(VLNK, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(cnp->cn_cred->cr_gid); if (isnq) { quad_t qval = -1; txdr_hyper(&qval, &sp->sa_nqsize); sp->sa_nqflags = 0; txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); } else { sp->sa_nfssize = -1; txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); } nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred); nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; VTONFS(dvp)->n_attrstamp = 0; vrele(dvp); /* * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry. */ if (error == EEXIST) error = 0; return (error); } /* * nfs make dir call */ int nfs_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 vnode **vpp = ap->a_vpp; register struct nfsv2_sattr *sp; register u_long *tl; register caddr_t cp; register long t1, t2; register int len; caddr_t bpos, dpos, cp2; int error = 0, firsttry = 1, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vattr vattr; if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } len = cnp->cn_namelen; isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS); nfsstats.rpccnt[NFSPROC_MKDIR]++; nfsm_reqhead(dvp, NFSPROC_MKDIR, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)+NFSX_SATTR(isnq)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); sp->sa_mode = vtonfs_mode(VDIR, vap->va_mode); sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); sp->sa_gid = txdr_unsigned(vattr.va_gid); if (isnq) { quad_t qval = -1; txdr_hyper(&qval, &sp->sa_nqsize); sp->sa_nqflags = 0; txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); } else { sp->sa_nfssize = -1; txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); } nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred); nfsm_mtofh(dvp, *vpp); nfsm_reqdone; VTONFS(dvp)->n_flag |= NMODIFIED; VTONFS(dvp)->n_attrstamp = 0; /* * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry * if we can succeed in looking up the directory. * "firsttry" is necessary since the macros may "goto nfsmout" which * is above the if on errors. (Ugh) */ if (error == EEXIST && firsttry) { firsttry = 0; error = 0; nfsstats.rpccnt[NFSPROC_LOOKUP]++; *vpp = NULL; nfsm_reqhead(dvp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred); nfsm_mtofh(dvp, *vpp); if ((*vpp)->v_type != VDIR) { vput(*vpp); error = EEXIST; } m_freem(mrep); } FREE(cnp->cn_pnbuf, M_NAMEI); vrele(dvp); return (error); } /* * nfs remove directory call */ int nfs_rmdir(ap) struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct vnode *dvp = ap->a_dvp; register struct componentname *cnp = ap->a_cnp; register u_long *tl; register caddr_t cp; register long t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; if (dvp == vp) { vrele(dvp); vrele(dvp); FREE(cnp->cn_pnbuf, M_NAMEI); return (EINVAL); } nfsstats.rpccnt[NFSPROC_RMDIR]++; nfsm_reqhead(dvp, NFSPROC_RMDIR, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)); nfsm_fhtom(dvp); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_RMDIR, cnp->cn_proc, cnp->cn_cred); nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; VTONFS(dvp)->n_attrstamp = 0; cache_purge(dvp); cache_purge(vp); vrele(vp); vrele(dvp); /* * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry. */ if (error == ENOENT) error = 0; return (error); } /* * nfs readdir call * Although cookie is defined as opaque, I translate it to/from net byte * order so that it looks more sensible. This appears consistent with the * Ultrix implementation of NFS. */ int nfs_readdir(ap) struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); register struct uio *uio = ap->a_uio; int tresid, error; struct vattr vattr; if (vp->v_type != VDIR) return (EPERM); /* * First, check for hit on the EOF offset cache */ if (uio->uio_offset != 0 && uio->uio_offset == np->n_direofoffset && (np->n_flag & NMODIFIED) == 0) { if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) { if (NQNFS_CKCACHABLE(vp, NQL_READ)) { nfsstats.direofcache_hits++; return (0); } } else if (VOP_GETATTR(vp, &vattr, ap->a_cred, uio->uio_procp) == 0 && np->n_mtime == vattr.va_mtime.ts_sec) { nfsstats.direofcache_hits++; return (0); } } /* * Call nfs_bioread() to do the real work. */ tresid = uio->uio_resid; error = nfs_bioread(vp, uio, 0, ap->a_cred); if (!error && uio->uio_resid == tresid) nfsstats.direofcache_misses++; return (error); } /* * Readdir rpc call. * Called from below the buffer cache by nfs_doio(). */ int nfs_readdirrpc(vp, uiop, cred) register struct vnode *vp; struct uio *uiop; struct ucred *cred; { register long len; register struct dirent *dp = 0; register u_long *tl; register caddr_t cp; register long t1; long tlen, lastlen = 0; caddr_t bpos, dpos, cp2; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct mbuf *md2; caddr_t dpos2; int siz; int more_dirs = 1; u_long off, savoff = 0; struct dirent *savdp = 0; struct nfsmount *nmp; struct nfsnode *np = VTONFS(vp); long tresid; nmp = VFSTONFS(vp->v_mount); tresid = uiop->uio_resid; /* * Loop around doing readdir rpc's of size uio_resid or nm_rsize, * whichever is smaller, truncated to a multiple of NFS_DIRBLKSIZ. * The stopping criteria is EOF or buffer full. */ while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) { nfsstats.rpccnt[NFSPROC_READDIR]++; nfsm_reqhead(vp, NFSPROC_READDIR, NFSX_FH + 2 * NFSX_UNSIGNED); nfsm_fhtom(vp); nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); off = (u_long)uiop->uio_offset; *tl++ = txdr_unsigned(off); *tl = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ? nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1)); nfsm_request(vp, NFSPROC_READDIR, uiop->uio_procp, cred); siz = 0; nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = fxdr_unsigned(int, *tl); /* Save the position so that we can do nfsm_mtouio() later */ dpos2 = dpos; md2 = md; /* loop thru the dir entries, doctoring them to 4bsd form */ #ifdef lint dp = (struct dirent *)0; #endif /* lint */ while (more_dirs && siz < uiop->uio_resid) { savoff = off; /* Hold onto offset and dp */ savdp = dp; nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); dp = (struct dirent *)tl; dp->d_fileno = fxdr_unsigned(u_long, *tl++); len = fxdr_unsigned(int, *tl); if (len <= 0 || len > NFS_MAXNAMLEN) { error = EBADRPC; m_freem(mrep); goto nfsmout; } dp->d_namlen = (u_char)len; dp->d_type = DT_UNKNOWN; nfsm_adv(len); /* Point past name */ tlen = nfsm_rndup(len); /* * This should not be necessary, but some servers have * broken XDR such that these bytes are not null filled. */ if (tlen != len) { *dpos = '\0'; /* Null-terminate */ nfsm_adv(tlen - len); len = tlen; } nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); off = fxdr_unsigned(u_long, *tl); *tl++ = 0; /* Ensures null termination of name */ more_dirs = fxdr_unsigned(int, *tl); dp->d_reclen = len + 4 * NFSX_UNSIGNED; siz += dp->d_reclen; } /* * If at end of rpc data, get the eof boolean */ if (!more_dirs) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = (fxdr_unsigned(int, *tl) == 0); /* * If at EOF, cache directory offset */ if (!more_dirs) np->n_direofoffset = off; } /* * If there is too much to fit in the data buffer, use savoff and * savdp to trim off the last record. * --> we are not at eof */ if (siz > uiop->uio_resid) { off = savoff; siz -= dp->d_reclen; dp = savdp; more_dirs = 0; /* Paranoia */ } if (siz > 0) { lastlen = dp->d_reclen; md = md2; dpos = dpos2; nfsm_mtouio(uiop, siz); uiop->uio_offset = (off_t)off; } else more_dirs = 0; /* Ugh, never happens, but in case.. */ m_freem(mrep); } /* * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ * by increasing d_reclen for the last record. */ if (uiop->uio_resid < tresid) { len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1); if (len > 0) { dp = (struct dirent *) (uiop->uio_iov->iov_base - lastlen); dp->d_reclen += len; uiop->uio_iov->iov_base += len; uiop->uio_iov->iov_len -= len; uiop->uio_resid -= len; } } nfsmout: return (error); } /* * Nqnfs readdir_and_lookup RPC. Used in place of nfs_readdirrpc(). */ int nfs_readdirlookrpc(vp, uiop, cred) struct vnode *vp; register struct uio *uiop; struct ucred *cred; { register int len; register struct dirent *dp = 0; register u_long *tl; register caddr_t cp; register long t1; caddr_t bpos, dpos, cp2; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nameidata nami, *ndp = &nami; struct componentname *cnp = &ndp->ni_cnd; u_long off, endoff = 0, fileno; time_t reqtime, ltime = 0; struct nfsmount *nmp; struct nfsnode *np; struct vnode *newvp; nfsv2fh_t *fhp; u_quad_t frev; int error = 0, tlen, more_dirs = 1, tresid, doit, bigenough, i; int cachable = 0; if (uiop->uio_iovcnt != 1) panic("nfs rdirlook"); nmp = VFSTONFS(vp->v_mount); tresid = uiop->uio_resid; ndp->ni_dvp = vp; newvp = NULLVP; /* * Loop around doing readdir rpc's of size uio_resid or nm_rsize, * whichever is smaller, truncated to a multiple of NFS_DIRBLKSIZ. * The stopping criteria is EOF or buffer full. */ while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) { nfsstats.rpccnt[NQNFSPROC_READDIRLOOK]++; nfsm_reqhead(vp, NQNFSPROC_READDIRLOOK, NFSX_FH + 3 * NFSX_UNSIGNED); nfsm_fhtom(vp); nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); off = (u_long)uiop->uio_offset; *tl++ = txdr_unsigned(off); *tl++ = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ? nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1)); if (nmp->nm_flag & NFSMNT_NQLOOKLEASE) *tl = txdr_unsigned(nmp->nm_leaseterm); else *tl = 0; reqtime = time.tv_sec; nfsm_request(vp, NQNFSPROC_READDIRLOOK, uiop->uio_procp, cred); nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = fxdr_unsigned(int, *tl); /* loop thru the dir entries, doctoring them to 4bsd form */ bigenough = 1; while (more_dirs && bigenough) { doit = 1; nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED); if (nmp->nm_flag & NFSMNT_NQLOOKLEASE) { cachable = fxdr_unsigned(int, *tl++); ltime = reqtime + fxdr_unsigned(int, *tl++); fxdr_hyper(tl, &frev); } nfsm_dissect(fhp, nfsv2fh_t *, NFSX_FH); if (!bcmp(VTONFS(vp)->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) { VREF(vp); newvp = vp; np = VTONFS(vp); } else { if (error = nfs_nget(vp->v_mount, fhp, &np)) doit = 0; newvp = NFSTOV(np); } if (error = nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr *)0)) doit = 0; nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); fileno = fxdr_unsigned(u_long, *tl++); len = fxdr_unsigned(int, *tl); if (len <= 0 || len > NFS_MAXNAMLEN) { error = EBADRPC; m_freem(mrep); goto nfsmout; } tlen = (len + 4) & ~0x3; if ((tlen + DIRHDSIZ) > uiop->uio_resid) bigenough = 0; if (bigenough && doit) { dp = (struct dirent *)uiop->uio_iov->iov_base; dp->d_fileno = fileno; dp->d_namlen = len; dp->d_reclen = tlen + DIRHDSIZ; dp->d_type = IFTODT(VTTOIF(np->n_vattr.va_type)); uiop->uio_resid -= DIRHDSIZ; uiop->uio_iov->iov_base += DIRHDSIZ; uiop->uio_iov->iov_len -= DIRHDSIZ; cnp->cn_nameptr = uiop->uio_iov->iov_base; cnp->cn_namelen = len; ndp->ni_vp = newvp; nfsm_mtouio(uiop, len); cp = uiop->uio_iov->iov_base; tlen -= len; for (i = 0; i < tlen; i++) *cp++ = '\0'; uiop->uio_iov->iov_base += tlen; uiop->uio_iov->iov_len -= tlen; uiop->uio_resid -= tlen; cnp->cn_hash = 0; for (cp = cnp->cn_nameptr, i = 1; i <= len; i++, cp++) cnp->cn_hash += (unsigned char)*cp * i; if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) && ltime > time.tv_sec) nqnfs_clientlease(nmp, np, NQL_READ, cachable, ltime, frev); if (cnp->cn_namelen <= NCHNAMLEN) cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp); } else { nfsm_adv(nfsm_rndup(len)); } if (newvp != NULLVP) { vrele(newvp); newvp = NULLVP; } nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); if (bigenough) endoff = off = fxdr_unsigned(u_long, *tl++); else endoff = fxdr_unsigned(u_long, *tl++); more_dirs = fxdr_unsigned(int, *tl); } /* * If at end of rpc data, get the eof boolean */ if (!more_dirs) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = (fxdr_unsigned(int, *tl) == 0); /* * If at EOF, cache directory offset */ if (!more_dirs) VTONFS(vp)->n_direofoffset = endoff; } if (uiop->uio_resid < tresid) uiop->uio_offset = (off_t)off; else more_dirs = 0; m_freem(mrep); } /* * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ * by increasing d_reclen for the last record. */ if (uiop->uio_resid < tresid) { len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1); if (len > 0) { dp->d_reclen += len; uiop->uio_iov->iov_base += len; uiop->uio_iov->iov_len -= len; uiop->uio_resid -= len; } } nfsmout: if (newvp != NULLVP) vrele(newvp); return (error); } static char hextoasc[] = "0123456789abcdef"; /* * Silly rename. To make the NFS filesystem that is stateless look a little * more like the "ufs" a remove of an active vnode is translated to a rename * to a funny looking filename that is removed by nfs_inactive on the * nfsnode. There is the potential for another process on a different client * to create the same funny name between the nfs_lookitup() fails and the * nfs_rename() completes, but... */ int nfs_sillyrename(dvp, vp, cnp) struct vnode *dvp, *vp; struct componentname *cnp; { register struct nfsnode *np; register struct sillyrename *sp; int error; short pid; cache_purge(dvp); np = VTONFS(vp); #ifdef SILLYSEPARATE MALLOC(sp, struct sillyrename *, sizeof (struct sillyrename), M_NFSREQ, M_WAITOK); #else sp = &np->n_silly; #endif sp->s_cred = crdup(cnp->cn_cred); sp->s_dvp = dvp; VREF(dvp); /* Fudge together a funny name */ pid = cnp->cn_proc->p_pid; bcopy(".nfsAxxxx4.4", sp->s_name, 13); sp->s_namlen = 12; sp->s_name[8] = hextoasc[pid & 0xf]; sp->s_name[7] = hextoasc[(pid >> 4) & 0xf]; sp->s_name[6] = hextoasc[(pid >> 8) & 0xf]; sp->s_name[5] = hextoasc[(pid >> 12) & 0xf]; /* Try lookitups until we get one that isn't there */ while (nfs_lookitup(sp, (nfsv2fh_t *)0, cnp->cn_proc) == 0) { sp->s_name[4]++; if (sp->s_name[4] > 'z') { error = EINVAL; goto bad; } } if (error = nfs_renameit(dvp, cnp, sp)) goto bad; nfs_lookitup(sp, &np->n_fh, cnp->cn_proc); np->n_sillyrename = sp; return (0); bad: vrele(sp->s_dvp); crfree(sp->s_cred); #ifdef SILLYSEPARATE free((caddr_t)sp, M_NFSREQ); #endif return (error); } /* * Look up a file name for silly rename stuff. * Just like nfs_lookup() except that it doesn't load returned values * into the nfsnode table. * If fhp != NULL it copies the returned file handle out */ int nfs_lookitup(sp, fhp, procp) register struct sillyrename *sp; nfsv2fh_t *fhp; struct proc *procp; { register struct vnode *vp = sp->s_dvp; register u_long *tl; register caddr_t cp; register long t1, t2; caddr_t bpos, dpos, cp2; int error = 0, isnq; struct mbuf *mreq, *mrep, *md, *mb, *mb2; long len; isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS); nfsstats.rpccnt[NFSPROC_LOOKUP]++; len = sp->s_namlen; nfsm_reqhead(vp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)); if (isnq) { nfsm_build(tl, u_long *, NFSX_UNSIGNED); *tl = 0; } nfsm_fhtom(vp); nfsm_strtom(sp->s_name, len, NFS_MAXNAMLEN); nfsm_request(vp, NFSPROC_LOOKUP, procp, sp->s_cred); if (fhp != NULL) { if (isnq) nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); nfsm_dissect(cp, caddr_t, NFSX_FH); bcopy(cp, (caddr_t)fhp, NFSX_FH); } nfsm_reqdone; return (error); } /* * Kludge City.. * - make nfs_bmap() essentially a no-op that does no translation * - do nfs_strategy() by faking physical I/O with nfs_readrpc/nfs_writerpc * after mapping the physical addresses into Kernel Virtual space in the * nfsiobuf area. * (Maybe I could use the process's page mapping, but I was concerned that * Kernel Write might not be enabled and also figured copyout() would do * a lot more work than bcopy() and also it currently happens in the * context of the swapper process (2). */ int nfs_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; { register struct vnode *vp = ap->a_vp; if (ap->a_vpp != NULL) *ap->a_vpp = vp; if (ap->a_bnp != NULL) *ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize); return (0); } /* * Strategy routine. * For async requests when nfsiod(s) are running, queue the request by * calling nfs_asyncio(), otherwise just all nfs_doio() to do the * request. */ int nfs_strategy(ap) struct vop_strategy_args *ap; { register struct buf *bp = ap->a_bp; struct ucred *cr; struct proc *p; int error = 0; if (bp->b_flags & B_PHYS) panic("nfs physio"); if (bp->b_flags & B_ASYNC) p = (struct proc *)0; else p = curproc; /* XXX */ if (bp->b_flags & B_READ) cr = bp->b_rcred; else cr = bp->b_wcred; /* * If the op is asynchronous and an i/o daemon is waiting * queue the request, wake it up and wait for completion * otherwise just do it ourselves. */ if ((bp->b_flags & B_ASYNC) == 0 || nfs_asyncio(bp, NOCRED)) error = nfs_doio(bp, cr, p); return (error); } /* * Mmap a file * * NB Currently unsupported. */ /* ARGSUSED */ int nfs_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 all the blocks associated with a vnode. * Walk through the buffer pool and push any dirty pages * associated with the vnode. */ /* ARGSUSED */ int nfs_fsync(ap) struct vop_fsync_args /* { struct vnodeop_desc *a_desc; 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 nfsnode *np = VTONFS(vp); register struct buf *bp; struct buf *nbp; struct nfsmount *nmp; int s, error = 0, slptimeo = 0, slpflag = 0; nmp = VFSTONFS(vp->v_mount); if (nmp->nm_flag & NFSMNT_INT) slpflag = PCATCH; 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) { if (ap->a_waitfor != MNT_WAIT) continue; bp->b_flags |= B_WANTED; error = tsleep((caddr_t)bp, slpflag | (PRIBIO + 1), "nfsfsync", slptimeo); splx(s); if (error) { if (nfs_sigintr(nmp, (struct nfsreq *)0, ap->a_p)) return (EINTR); if (slpflag == PCATCH) { slpflag = 0; slptimeo = 2 * hz; } } goto loop; } if ((bp->b_flags & B_DELWRI) == 0) panic("nfs_fsync: not dirty"); bremfree(bp); bp->b_flags |= B_BUSY; splx(s); bp->b_flags |= B_ASYNC; VOP_BWRITE(bp); goto loop; } splx(s); if (ap->a_waitfor == MNT_WAIT) { while (vp->v_numoutput) { vp->v_flag |= VBWAIT; error = tsleep((caddr_t)&vp->v_numoutput, slpflag | (PRIBIO + 1), "nfsfsync", slptimeo); if (error) { if (nfs_sigintr(nmp, (struct nfsreq *)0, ap->a_p)) return (EINTR); if (slpflag == PCATCH) { slpflag = 0; slptimeo = 2 * hz; } } } if (vp->v_dirtyblkhd.lh_first) { #ifdef DIAGNOSTIC vprint("nfs_fsync: dirty", vp); #endif goto loop; } } if (np->n_flag & NWRITEERR) { error = np->n_error; np->n_flag &= ~NWRITEERR; } return (error); } /* * Return POSIX pathconf information applicable to nfs. * * Currently the NFS protocol does not support getting such * information from the remote server. */ /* ARGSUSED */ int nfs_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { return (EINVAL); } /* * NFS advisory byte-level locks. * Currently unsupported. */ int nfs_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 nfsnode *np = VTONFS(ap->a_vp); /* * The following kludge is to allow diskless support to work * until a real NFS lockd is implemented. Basically, just pretend * that this is a local lock. */ return (lf_advlock(ap, &(np->n_lockf), np->n_size)); } /* * Print out the contents of an nfsnode. */ int nfs_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); printf("tag VT_NFS, fileid %d fsid 0x%x", np->n_vattr.va_fileid, np->n_vattr.va_fsid); #ifdef FIFO if (vp->v_type == VFIFO) fifo_printinfo(vp); #endif /* FIFO */ printf("\n"); return (0); } /* * NFS directory offset lookup. * Currently unsupported. */ int nfs_blkatoff(ap) struct vop_blkatoff_args /* { struct vnode *a_vp; off_t a_offset; char **a_res; struct buf **a_bpp; } */ *ap; { return (EOPNOTSUPP); } /* * NFS flat namespace allocation. * Currently unsupported. */ int nfs_valloc(ap) struct vop_valloc_args /* { struct vnode *a_pvp; int a_mode; struct ucred *a_cred; struct vnode **a_vpp; } */ *ap; { return (EOPNOTSUPP); } /* * NFS flat namespace free. * Currently unsupported. */ int nfs_vfree(ap) struct vop_vfree_args /* { struct vnode *a_pvp; ino_t a_ino; int a_mode; } */ *ap; { return (EOPNOTSUPP); } /* * NFS file truncation. */ int nfs_truncate(ap) struct vop_truncate_args /* { struct vnode *a_vp; off_t a_length; int a_flags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { /* Use nfs_setattr */ printf("nfs_truncate: need to implement!!"); return (EOPNOTSUPP); } /* * NFS update. */ int nfs_update(ap) struct vop_update_args /* { struct vnode *a_vp; struct timeval *a_ta; struct timeval *a_tm; int a_waitfor; } */ *ap; { /* Use nfs_setattr */ printf("nfs_update: need to implement!!"); return (EOPNOTSUPP); } /* * nfs special file access vnode op. * Essentially just get vattr and then imitate iaccess() since the device is * local to the client. */ int nfsspec_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { register struct vattr *vap; register gid_t *gp; register struct ucred *cred = ap->a_cred; mode_t mode = ap->a_mode; struct vattr vattr; register int i; int error; /* * If you're the super-user, * you always get access. */ if (cred->cr_uid == 0) return (0); vap = &vattr; if (error = VOP_GETATTR(ap->a_vp, vap, cred, ap->a_p)) 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 (cred->cr_uid != vap->va_uid) { mode >>= 3; gp = cred->cr_groups; for (i = 0; i < cred->cr_ngroups; i++, gp++) if (vap->va_gid == *gp) goto found; mode >>= 3; found: ; } return ((vap->va_mode & mode) == mode ? 0 : EACCES); } /* * Read wrapper for special devices. */ int nfsspec_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); /* * Set access flag. */ np->n_flag |= NACC; np->n_atim = time; return (VOCALL(spec_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for special devices. */ int nfsspec_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { register struct nfsnode *np = VTONFS(ap->a_vp); /* * Set update flag. */ np->n_flag |= NUPD; np->n_mtim = time; return (VOCALL(spec_vnodeop_p, VOFFSET(vop_write), ap)); } /* * Close wrapper for special devices. * * Update the times on the nfsnode then do device close. */ int nfsspec_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 nfsnode *np = VTONFS(vp); struct vattr vattr; if (np->n_flag & (NACC | NUPD)) { np->n_flag |= NCHG; if (vp->v_usecount == 1 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { VATTR_NULL(&vattr); if (np->n_flag & NACC) { vattr.va_atime.ts_sec = np->n_atim.tv_sec; vattr.va_atime.ts_nsec = np->n_atim.tv_usec * 1000; } if (np->n_flag & NUPD) { vattr.va_mtime.ts_sec = np->n_mtim.tv_sec; vattr.va_mtime.ts_nsec = np->n_mtim.tv_usec * 1000; } (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p); } } return (VOCALL(spec_vnodeop_p, VOFFSET(vop_close), ap)); } #ifdef FIFO /* * Read wrapper for fifos. */ int nfsfifo_read(ap) struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { extern int (**fifo_vnodeop_p)(); register struct nfsnode *np = VTONFS(ap->a_vp); /* * Set access flag. */ np->n_flag |= NACC; np->n_atim = time; return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap)); } /* * Write wrapper for fifos. */ int nfsfifo_write(ap) struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap; { extern int (**fifo_vnodeop_p)(); register struct nfsnode *np = VTONFS(ap->a_vp); /* * Set update flag. */ np->n_flag |= NUPD; np->n_mtim = time; return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap)); } /* * Close wrapper for fifos. * * Update the times on the nfsnode then do fifo close. */ int nfsfifo_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 nfsnode *np = VTONFS(vp); struct vattr vattr; extern int (**fifo_vnodeop_p)(); if (np->n_flag & (NACC | NUPD)) { if (np->n_flag & NACC) np->n_atim = time; if (np->n_flag & NUPD) np->n_mtim = time; np->n_flag |= NCHG; if (vp->v_usecount == 1 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { VATTR_NULL(&vattr); if (np->n_flag & NACC) { vattr.va_atime.ts_sec = np->n_atim.tv_sec; vattr.va_atime.ts_nsec = np->n_atim.tv_usec * 1000; } if (np->n_flag & NUPD) { vattr.va_mtime.ts_sec = np->n_mtim.tv_sec; vattr.va_mtime.ts_nsec = np->n_mtim.tv_usec * 1000; } (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p); } } return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap)); } #endif /* FIFO */ Index: head/sys/sys/lkm.h =================================================================== --- head/sys/sys/lkm.h (revision 2945) +++ head/sys/sys/lkm.h (revision 2946) @@ -1,363 +1,365 @@ /* * Header file used by loadable kernel modules and loadable kernel module * utilities. * * 23 Jan 93 Terry Lambert Original * * Copyright (c) 1992 Terrence R. Lambert. * 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 Terrence R. Lambert. * 4. The name Terrence R. Lambert may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY TERRENCE R. LAMBERT ``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 TERRENCE R. LAMBERT 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. * - * $Id$ + * $Id: lkm.h,v 1.1 1994/08/20 02:23:40 davidg Exp $ */ #ifndef _SYS_LKM_H_ #define _SYS_LKM_H_ /* * Supported module types */ typedef enum loadmod { LM_SYSCALL, LM_VFS, LM_DEV, LM_STRMOD, LM_EXEC, LM_MISC } MODTYPE; #define LKM_VERSION 1 /* version of module loader */ #define MAXLKMNAME 32 /****************************************************************************/ #ifdef KERNEL /* * Loadable system call */ struct lkm_syscall { MODTYPE lkm_type; int lkm_ver; char *lkm_name; u_long lkm_offset; /* save/assign area */ struct sysent *lkm_sysent; struct sysent lkm_oldent; /* save area for unload */ }; /* * Loadable file system */ struct lkm_vfs { MODTYPE lkm_type; int lkm_ver; char *lkm_name; u_long lkm_offset; - struct vfsops *lkm_vfsops; + struct linker_set *lkm_vnodeops; + struct vfsconf *lkm_vfsconf; }; /* * Supported device module types */ typedef enum devtype { LM_DT_BLOCK, LM_DT_CHAR } DEVTYPE; /* * Loadable device driver */ struct lkm_dev { MODTYPE lkm_type; int lkm_ver; char *lkm_name; u_long lkm_offset; DEVTYPE lkm_devtype; union { void *anon; struct bdevsw *bdev; struct cdevsw *cdev; } lkm_dev; union { struct bdevsw bdev; struct cdevsw cdev; } lkm_olddev; }; /* * Loadable streams module */ struct lkm_strmod { MODTYPE lkm_type; int lkm_ver; char *lkm_name; u_long lkm_offset; /* * Removed: future release */ }; /* * Exec loader */ struct lkm_exec { MODTYPE lkm_type; int lkm_ver; char *lkm_name; u_long lkm_offset; struct execsw *lkm_exec; struct execsw lkm_oldexec; }; /* * Miscellaneous module (complex load/unload, potentially complex stat */ struct lkm_misc { MODTYPE lkm_type; int lkm_ver; char *lkm_name; u_long lkm_offset; }; /* * Any module (to get type and name info without knowing type) */ struct lkm_any { MODTYPE lkm_type; int lkm_ver; char *lkm_name; u_long lkm_offset; }; /* * Generic reference ala XEvent to allow single entry point in the xxxinit() * routine. */ union lkm_generic { struct lkm_any *lkm_any; struct lkm_syscall *lkm_syscall; struct lkm_vfs *lkm_vfs; struct lkm_dev *lkm_dev; struct lkm_strmod *lkm_strmod; struct lkm_exec *lkm_exec; struct lkm_misc *lkm_misc; }; union lkm_all { struct lkm_any lkm_any; struct lkm_syscall lkm_syscall; struct lkm_vfs lkm_vfs; struct lkm_dev lkm_dev; struct lkm_strmod lkm_strmod; struct lkm_exec lkm_exec; struct lkm_misc lkm_misc; }; /* * Per module information structure */ struct lkm_table { int type; u_long size; u_long offset; u_long area; char used; int ver; /* version (INIT) */ int refcnt; /* reference count (INIT) */ int depcnt; /* dependency count (INIT) */ int id; /* identifier (INIT) */ int (*entry)(); /* entry function */ union lkm_generic private; /* module private data */ }; #define LKM_E_LOAD 1 #define LKM_E_UNLOAD 2 #define LKM_E_STAT 3 #define MOD_SYSCALL(name,callslot,sysentp) \ static struct lkm_syscall _module = { \ LM_SYSCALL, \ LKM_VERSION, \ name, \ callslot, \ sysentp \ }; -#define MOD_VFS(name,vfsslot,vfsopsp) \ +#define MOD_VFS(name,vfsslot,vnodeops,vfsconf) \ static struct lkm_vfs _module = { \ LM_VFS, \ LKM_VERSION, \ name, \ vfsslot, \ - vfsopsp \ + vnodeops, \ + vfsconf \ }; #define MOD_DEV(name,devtype,devslot,devp) \ static struct lkm_dev _module = { \ LM_DEV, \ LKM_VERSION, \ name, \ devslot, \ devtype, \ (void *)devp \ }; #define MOD_EXEC(name,execslot,execsw) \ static struct lkm_exec _module = { \ LM_EXEC, \ LKM_VERSION, \ name, \ execslot, \ execsw \ }; #define MOD_MISC(name) \ static struct lkm_misc _module = { \ LM_MISC, \ LKM_VERSION, \ name \ }; extern int nosys(); /* * DISPATCH -- body function for use in module entry point function; * generally, the function body will consist entirely of a single * DISPATCH line. * * If load/unload/stat are not "nosys", then they are called on each * corresponding entry instance. "cmd" is passed to each function so * that a single function can be used if desired. */ #define DISPATCH(lkmtp,cmd,ver,load,unload,stat) \ if (ver != LKM_VERSION) \ return EINVAL; /* version mismatch */ \ switch (cmd) { \ int error; \ case LKM_E_LOAD: \ lkmtp->private.lkm_any = (struct lkm_any *)&_module; \ if (load != nosys && (error = load(lkmtp, cmd))) \ return error; \ break; \ case LKM_E_UNLOAD: \ if (unload != nosys && (error = unload(lkmtp, cmd))) \ return error; \ break; \ case LKM_E_STAT: \ if (stat != nosys && (error = stat(lkmtp, cmd))) \ return error; \ break; \ } \ return lkmdispatch(lkmtp, cmd); #endif /* KERNEL */ /****************************************************************************/ /* * IOCTL's recognized by /dev/lkm */ #define LMRESERV _IOWR('K', 0, struct lmc_resrv) #define LMLOADBUF _IOW('K', 1, struct lmc_loadbuf) #define LMUNRESRV _IO('K', 2) #define LMREADY _IOW('K', 3, int) #define LMLOAD _IOW('K', 9, struct lmc_load) #define LMUNLOAD _IOWR('K', 10, struct lmc_unload) #define LMSTAT _IOWR('K', 11, struct lmc_stat) #define MODIOBUF 512 /* # of bytes at a time to loadbuf */ /* * IOCTL arguments */ /* * Reserve a page-aligned block of kernel memory for the module */ struct lmc_resrv { u_long size; /* IN: size of module to reserve */ char *name; /* IN: name (must be provided */ int slot; /* OUT: allocated slot (module ID) */ u_long addr; /* OUT: Link-to address */ }; /* * Copy a buffer at a time into the allocated area in the kernel; writes * are assumed to occur contiguously. */ struct lmc_loadbuf { int cnt; /* IN: # of chars pointed to by data */ char *data; /* IN: pointer to data buffer */ }; /* * Load a module (assumes it's been mmapped to address before call) */ struct lmc_load { caddr_t address; /* IN: user space mmap address */ int status; /* OUT: status of operation */ int id; /* OUT: module ID if loaded */ }; /* * Unload a module (by name/id) */ struct lmc_unload { int id; /* IN: module ID to unload */ char *name; /* IN: module name to unload if id -1 */ int status; /* OUT: status of operation */ }; /* * Get module information for a given id (or name if id == -1). */ struct lmc_stat { int id; /* IN: module ID to unload */ char name[MAXLKMNAME]; /* IN/OUT: name of module */ u_long offset; /* OUT: target table offset */ MODTYPE type; /* OUT: type of module */ u_long area; /* OUT: kernel load addr */ u_long size; /* OUT: module size (pages) */ u_long private; /* OUT: module private data */ int ver; /* OUT: lkm compile version */ }; #endif /* !_SYS_LKM_H_ */ Index: head/sys/sys/mount.h =================================================================== --- head/sys/sys/mount.h (revision 2945) +++ head/sys/sys/mount.h (revision 2946) @@ -1,439 +1,488 @@ /* * Copyright (c) 1989, 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. * * @(#)mount.h 8.13 (Berkeley) 3/27/94 - * $Id: mount.h,v 1.6 1994/09/15 20:24:26 bde Exp $ + * $Id: mount.h,v 1.7 1994/09/19 15:41:56 dfr Exp $ */ #ifndef _SYS_MOUNT_H_ #define _SYS_MOUNT_H_ #ifndef KERNEL #include #endif #include typedef struct { long val[2]; } fsid_t; /* file system id type */ /* * File identifier. * These are unique per filesystem on a single machine. */ #define MAXFIDSZ 16 struct fid { u_short fid_len; /* length of data in bytes */ u_short fid_reserved; /* force longword alignment */ char fid_data[MAXFIDSZ]; /* data (variable length) */ }; /* * file system statistics */ #define MNAMELEN 90 /* length of buffer for returned name */ struct statfs { short f_type; /* type of filesystem (see below) */ short f_flags; /* copy of mount flags */ long f_bsize; /* fundamental file system block size */ long f_iosize; /* optimal transfer block size */ long f_blocks; /* total data blocks in file system */ long f_bfree; /* free blocks in fs */ long f_bavail; /* free blocks avail to non-superuser */ long f_files; /* total file nodes in file system */ long f_ffree; /* free file nodes in fs */ fsid_t f_fsid; /* file system id */ long f_spare[9]; /* spare for later */ char f_mntonname[MNAMELEN]; /* directory on which mounted */ char f_mntfromname[MNAMELEN];/* mounted filesystem */ }; /* * File system types. */ #define MOUNT_NONE 0 #define MOUNT_UFS 1 /* Fast Filesystem */ #define MOUNT_NFS 2 /* Sun-compatible Network Filesystem */ #define MOUNT_MFS 3 /* Memory-based Filesystem */ #define MOUNT_MSDOS 4 /* MS/DOS Filesystem */ #define MOUNT_LFS 5 /* Log-based Filesystem */ #define MOUNT_LOFS 6 /* Loopback Filesystem */ #define MOUNT_FDESC 7 /* File Descriptor Filesystem */ #define MOUNT_PORTAL 8 /* Portal Filesystem */ #define MOUNT_NULL 9 /* Minimal Filesystem Layer */ #define MOUNT_UMAP 10 /* User/Group Identifer Remapping Filesystem */ #define MOUNT_KERNFS 11 /* Kernel Information Filesystem */ #define MOUNT_PROCFS 12 /* /proc Filesystem */ #define MOUNT_AFS 13 /* Andrew Filesystem */ #define MOUNT_CD9660 14 /* ISO9660 (aka CDROM) Filesystem */ #define MOUNT_UNION 15 /* Union (translucent) Filesystem */ #define MOUNT_MAXTYPE 15 #define INITMOUNTNAMES { \ "none", /* 0 MOUNT_NONE */ \ "ufs", /* 1 MOUNT_UFS */ \ "nfs", /* 2 MOUNT_NFS */ \ "mfs", /* 3 MOUNT_MFS */ \ "msdos", /* 4 MOUNT_MSDOS */ \ "lfs", /* 5 MOUNT_LFS */ \ "lofs", /* 6 MOUNT_LOFS */ \ "fdesc", /* 7 MOUNT_FDESC */ \ "portal", /* 8 MOUNT_PORTAL */ \ "null", /* 9 MOUNT_NULL */ \ "umap", /* 10 MOUNT_UMAP */ \ "kernfs", /* 11 MOUNT_KERNFS */ \ "procfs", /* 12 MOUNT_PROCFS */ \ "afs", /* 13 MOUNT_AFS */ \ "iso9660fs", /* 14 MOUNT_CD9660 */ \ "union", /* 15 MOUNT_UNION */ \ 0, /* 16 MOUNT_SPARE */ \ } /* * Structure per mounted file system. Each mounted file system has an * array of operations and an instance record. The file systems are * put on a doubly linked list. */ LIST_HEAD(vnodelst, vnode); struct mount { TAILQ_ENTRY(mount) mnt_list; /* mount list */ struct vfsops *mnt_op; /* operations on fs */ struct vnode *mnt_vnodecovered; /* vnode we mounted on */ struct vnodelst mnt_vnodelist; /* list of vnodes this mount */ int mnt_flag; /* flags */ int mnt_maxsymlinklen; /* max size of short symlink */ struct statfs mnt_stat; /* cache of filesystem stats */ qaddr_t mnt_data; /* private data */ + struct vfsconf *mnt_vfc; /* configuration info */ }; /* * Mount flags. * * Unmount uses MNT_FORCE flag. */ #define MNT_RDONLY 0x00000001 /* read only filesystem */ #define MNT_SYNCHRONOUS 0x00000002 /* file system written synchronously */ #define MNT_NOEXEC 0x00000004 /* can't exec from filesystem */ #define MNT_NOSUID 0x00000008 /* don't honor setuid bits on fs */ #define MNT_NODEV 0x00000010 /* don't interpret special files */ #define MNT_UNION 0x00000020 /* union with underlying filesystem */ #define MNT_ASYNC 0x00000040 /* file system written asynchronously */ /* * exported mount flags. */ #define MNT_EXRDONLY 0x00000080 /* exported read only */ #define MNT_EXPORTED 0x00000100 /* file system is exported */ #define MNT_DEFEXPORTED 0x00000200 /* exported to the world */ #define MNT_EXPORTANON 0x00000400 /* use anon uid mapping for everyone */ #define MNT_EXKERB 0x00000800 /* exported with Kerberos uid mapping */ /* * Flags set by internal operations. */ #define MNT_LOCAL 0x00001000 /* filesystem is stored locally */ #define MNT_QUOTA 0x00002000 /* quotas are enabled on filesystem */ #define MNT_ROOTFS 0x00004000 /* identifies the root filesystem */ #define MNT_USER 0x00008000 /* mounted by a user */ /* * Mask of flags that are visible to statfs() */ #define MNT_VISFLAGMASK 0x0000ffff /* * filesystem control flags. * * MNT_MLOCK lock the mount entry so that name lookup cannot proceed * past the mount point. This keeps the subtree stable during mounts * and unmounts. */ #define MNT_UPDATE 0x00010000 /* not a real mount, just an update */ #define MNT_DELEXPORT 0x00020000 /* delete export host lists */ #define MNT_RELOAD 0x00040000 /* reload filesystem data */ #define MNT_FORCE 0x00080000 /* force unmount or readonly change */ #define MNT_MLOCK 0x00100000 /* lock so that subtree is stable */ #define MNT_MWAIT 0x00200000 /* someone is waiting for lock */ #define MNT_MPBUSY 0x00400000 /* scan of mount point in progress */ #define MNT_MPWANT 0x00800000 /* waiting for mount point */ #define MNT_UNMOUNT 0x01000000 /* unmount in progress */ #define MNT_WANTRDWR 0x02000000 /* want upgrade to read/write */ /* + * used to get configured filesystems information + */ +#define VFS_MAXNAMELEN 32 +struct vfsconf { + void *vfc_vfsops; + char vfc_name[VFS_MAXNAMELEN]; + int vfc_index; + int vfc_refcount; + int vfc_flags; +}; + +/* * Operations supported on mounted file system. */ #ifdef KERNEL + +extern struct vfsconf *vfsconf[]; + #ifdef __STDC__ struct nameidata; struct mbuf; #endif struct vfsops { int (*vfs_mount) __P((struct mount *mp, char *path, caddr_t data, struct nameidata *ndp, struct proc *p)); int (*vfs_start) __P((struct mount *mp, int flags, struct proc *p)); int (*vfs_unmount) __P((struct mount *mp, int mntflags, struct proc *p)); int (*vfs_root) __P((struct mount *mp, struct vnode **vpp)); int (*vfs_quotactl) __P((struct mount *mp, int cmds, uid_t uid, caddr_t arg, struct proc *p)); int (*vfs_statfs) __P((struct mount *mp, struct statfs *sbp, struct proc *p)); int (*vfs_sync) __P((struct mount *mp, int waitfor, struct ucred *cred, struct proc *p)); int (*vfs_vget) __P((struct mount *mp, ino_t ino, struct vnode **vpp)); int (*vfs_fhtovp) __P((struct mount *mp, struct fid *fhp, struct mbuf *nam, struct vnode **vpp, int *exflagsp, struct ucred **credanonp)); int (*vfs_vptofh) __P((struct vnode *vp, struct fid *fhp)); int (*vfs_init) __P((void)); }; #define VFS_MOUNT(MP, PATH, DATA, NDP, P) \ (*(MP)->mnt_op->vfs_mount)(MP, PATH, DATA, NDP, P) #define VFS_START(MP, FLAGS, P) (*(MP)->mnt_op->vfs_start)(MP, FLAGS, P) #define VFS_UNMOUNT(MP, FORCE, P) (*(MP)->mnt_op->vfs_unmount)(MP, FORCE, P) #define VFS_ROOT(MP, VPP) (*(MP)->mnt_op->vfs_root)(MP, VPP) #define VFS_QUOTACTL(MP,C,U,A,P) (*(MP)->mnt_op->vfs_quotactl)(MP, C, U, A, P) #define VFS_STATFS(MP, SBP, P) (*(MP)->mnt_op->vfs_statfs)(MP, SBP, P) #define VFS_SYNC(MP, WAIT, C, P) (*(MP)->mnt_op->vfs_sync)(MP, WAIT, C, P) #define VFS_VGET(MP, INO, VPP) (*(MP)->mnt_op->vfs_vget)(MP, INO, VPP) #define VFS_FHTOVP(MP, FIDP, NAM, VPP, EXFLG, CRED) \ (*(MP)->mnt_op->vfs_fhtovp)(MP, FIDP, NAM, VPP, EXFLG, CRED) #define VFS_VPTOFH(VP, FIDP) (*(VP)->v_mount->mnt_op->vfs_vptofh)(VP, FIDP) + +#ifdef VFS_LKM +#include +#include +#include +#include + +#define VFS_SET(vfsops, fsname, index, flags) \ + static struct vfsconf _fs_vfsops = { \ + &vfsops, \ + #fsname, \ + index, \ + 1, \ + flags \ + }; \ + extern struct linker_set MODVNOPS; \ + MOD_VFS(#fsname,index,&MODVNOPS,&_fs_vfsops); \ + int \ + fsname ## _mod(struct lkm_table *lkmtp, int cmd, int ver) { \ + DISPATCH(lkmtp, cmd, ver, nosys, nosys, nosys); } +#else + +#define VFS_SET(vfsops, fsname, index, flags) \ + static struct vfsconf _fs_vfsops = { \ + &vfsops, \ + #fsname, \ + index, \ + 1, \ + flags \ + }; \ + DATA_SET(vfs_set,_fs_vfsops) +#endif /* VFS_LKM */ + #endif /* KERNEL */ /* * Flags for various system call interfaces. * * waitfor flags to vfs_sync() and getfsstat() */ #define MNT_WAIT 1 #define MNT_NOWAIT 2 /* * Generic file handle */ struct fhandle { fsid_t fh_fsid; /* File system id of mount point */ struct fid fh_fid; /* File sys specific id */ }; typedef struct fhandle fhandle_t; #ifdef KERNEL #include #include /* XXX for AF_MAX */ /* * Network address lookup element */ struct netcred { struct radix_node netc_rnodes[2]; int netc_exflags; struct ucred netc_anon; }; /* * Network export information */ struct netexport { struct netcred ne_defexported; /* Default export */ struct radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */ }; #endif /* KERNEL */ /* * Export arguments for local filesystem mount calls. */ struct export_args { int ex_flags; /* export related flags */ uid_t ex_root; /* mapping for root uid */ struct ucred ex_anon; /* mapping for anonymous user */ struct sockaddr *ex_addr; /* net address to which exported */ int ex_addrlen; /* and the net address length */ struct sockaddr *ex_mask; /* mask of valid bits in saddr */ int ex_masklen; /* and the smask length */ }; /* * Arguments to mount UFS-based filesystems */ struct ufs_args { char *fspec; /* block special device to mount */ struct export_args export; /* network export information */ }; #ifdef MFS /* * Arguments to mount MFS */ struct mfs_args { char *fspec; /* name to export for statfs */ struct export_args export; /* if exported MFSes are supported */ caddr_t base; /* base of file system in memory */ u_long size; /* size of file system */ }; #endif /* MFS */ #ifdef MSDOSFS /* * Arguments to mount MSDOS filesystems. */ struct msdosfs_args { char *fspec; /* blocks special holding the fs to mount */ struct export_args export; /* network export information */ uid_t uid; /* uid that owns msdosfs files */ gid_t gid; /* gid that owns msdosfs files */ mode_t mask; /* mask to be applied for msdosfs perms */ }; #endif #ifdef CD9660 /* * Arguments to mount ISO 9660 filesystems. */ struct iso_args { char *fspec; /* block special device to mount */ struct export_args export; /* network export info */ int flags; /* mounting flags, see below */ }; #define ISOFSMNT_NORRIP 0x00000001 /* disable Rock Ridge Ext.*/ #define ISOFSMNT_GENS 0x00000002 /* enable generation numbers */ #define ISOFSMNT_EXTATT 0x00000004 /* enable extended attributes */ #endif /* CD9660 */ #ifdef NFS /* * File Handle (32 bytes for version 2), variable up to 1024 for version 3 */ union nfsv2fh { fhandle_t fh_generic; u_char fh_bytes[32]; }; typedef union nfsv2fh nfsv2fh_t; /* * Arguments to mount NFS */ struct nfs_args { struct sockaddr *addr; /* file server address */ int addrlen; /* length of address */ int sotype; /* Socket type */ int proto; /* and Protocol */ nfsv2fh_t *fh; /* File handle to be mounted */ int flags; /* flags */ int wsize; /* write size in bytes */ int rsize; /* read size in bytes */ int timeo; /* initial timeout in .1 secs */ int retrans; /* times to retry send */ int maxgrouplist; /* Max. size of group list */ int readahead; /* # of blocks to readahead */ int leaseterm; /* Term (sec) of lease */ int deadthresh; /* Retrans threshold */ char *hostname; /* server's name */ }; /* * NFS mount option flags */ #define NFSMNT_SOFT 0x00000001 /* soft mount (hard is default) */ #define NFSMNT_WSIZE 0x00000002 /* set write size */ #define NFSMNT_RSIZE 0x00000004 /* set read size */ #define NFSMNT_TIMEO 0x00000008 /* set initial timeout */ #define NFSMNT_RETRANS 0x00000010 /* set number of request retrys */ #define NFSMNT_MAXGRPS 0x00000020 /* set maximum grouplist size */ #define NFSMNT_INT 0x00000040 /* allow interrupts on hard mount */ #define NFSMNT_NOCONN 0x00000080 /* Don't Connect the socket */ #define NFSMNT_NQNFS 0x00000100 /* Use Nqnfs protocol */ #define NFSMNT_MYWRITE 0x00000200 /* Assume writes were mine */ #define NFSMNT_KERB 0x00000400 /* Use Kerberos authentication */ #define NFSMNT_DUMBTIMR 0x00000800 /* Don't estimate rtt dynamically */ #define NFSMNT_RDIRALOOK 0x00001000 /* Do lookup with readdir (nqnfs) */ #define NFSMNT_LEASETERM 0x00002000 /* set lease term (nqnfs) */ #define NFSMNT_READAHEAD 0x00004000 /* set read ahead */ #define NFSMNT_DEADTHRESH 0x00008000 /* set dead server retry thresh */ #define NFSMNT_NQLOOKLEASE 0x00010000 /* Get lease for lookup */ #define NFSMNT_RESVPORT 0x00020000 /* Allocate a reserved port */ #define NFSMNT_INTERNAL 0xffe00000 /* Bits set internally */ #define NFSMNT_MNTD 0x00200000 /* Mnt server for mnt point */ #define NFSMNT_DISMINPROG 0x00400000 /* Dismount in progress */ #define NFSMNT_DISMNT 0x00800000 /* Dismounted */ #define NFSMNT_SNDLOCK 0x01000000 /* Send socket lock */ #define NFSMNT_WANTSND 0x02000000 /* Want above */ #define NFSMNT_RCVLOCK 0x04000000 /* Rcv socket lock */ #define NFSMNT_WANTRCV 0x08000000 /* Want above */ #define NFSMNT_WAITAUTH 0x10000000 /* Wait for authentication */ #define NFSMNT_HASAUTH 0x20000000 /* Has authenticator */ #define NFSMNT_WANTAUTH 0x40000000 /* Wants an authenticator */ #define NFSMNT_AUTHERR 0x80000000 /* Authentication error */ #endif /* NFS */ #ifdef KERNEL /* * exported vnode operations */ int dounmount __P((struct mount *, int, struct proc *)); struct mount *getvfs __P((fsid_t *)); /* return vfs given fsid */ int vflush __P((struct mount *mp, struct vnode *skipvp, int flags)); int vfs_export /* process mount export info */ __P((struct mount *, struct netexport *, struct export_args *)); struct netcred *vfs_export_lookup /* lookup host in fs export list */ __P((struct mount *, struct netexport *, struct mbuf *)); int vfs_lock __P((struct mount *)); /* lock a vfs */ int vfs_mountedon __P((struct vnode *));/* is a vfs mounted on vp */ void vfs_unlock __P((struct mount *)); /* unlock a vfs */ extern TAILQ_HEAD(mntlist, mount) mountlist; /* mounted filesystem list */ extern struct vfsops *vfssw[]; /* filesystem type table */ #else /* KERNEL */ #include __BEGIN_DECLS int fstatfs __P((int, struct statfs *)); int getfh __P((const char *, fhandle_t *)); int getfsstat __P((struct statfs *, long, int)); int getmntinfo __P((struct statfs **, int)); int mount __P((int, const char *, int, void *)); int statfs __P((const char *, struct statfs *)); int unmount __P((const char *, int)); __END_DECLS #endif /* KERNEL */ #endif /* !_SYS_MOUNT_H_ */ Index: head/sys/sys/sysctl.h =================================================================== --- head/sys/sys/sysctl.h (revision 2945) +++ head/sys/sys/sysctl.h (revision 2946) @@ -1,355 +1,365 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Karels at Berkeley Software Design, 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. * * @(#)sysctl.h 8.1 (Berkeley) 6/2/93 - * $Id: sysctl.h,v 1.9 1994/09/16 01:09:42 ache Exp $ + * $Id: sysctl.h,v 1.10 1994/09/18 20:40:00 wollman Exp $ */ #ifndef _SYS_SYSCTL_H_ #define _SYS_SYSCTL_H_ /* * These are for the eproc structure defined below. */ #ifndef KERNEL #include #include #include #include #endif /* * Definitions for sysctl call. The sysctl call uses a hierarchical name * for objects that can be examined or modified. The name is expressed as * a sequence of integers. Like a file path name, the meaning of each * component depends on its place in the hierarchy. The top-level and kern * identifiers are defined here, and other identifiers are defined in the * respective subsystem header files. */ #define CTL_MAXNAME 12 /* largest number of components supported */ /* * Each subsystem defined by sysctl defines a list of variables * for that subsystem. Each name is either a node with further * levels defined below it, or it is a leaf of some particular * type given below. Each sysctl level defines a set of name/type * pairs to be used by sysctl(1) in manipulating the subsystem. */ struct ctlname { char *ctl_name; /* subsystem name */ int ctl_type; /* type of name */ }; #define CTLTYPE_NODE 1 /* name is a node */ #define CTLTYPE_INT 2 /* name describes an integer */ #define CTLTYPE_STRING 3 /* name describes a string */ #define CTLTYPE_QUAD 4 /* name describes a 64-bit number */ #define CTLTYPE_STRUCT 5 /* name describes a structure */ /* * Top-level identifiers */ #define CTL_UNSPEC 0 /* unused */ #define CTL_KERN 1 /* "high kernel": proc, limits */ #define CTL_VM 2 /* virtual memory */ #define CTL_FS 3 /* file system, mount type is next */ #define CTL_NET 4 /* network, see socket.h */ #define CTL_DEBUG 5 /* debugging parameters */ #define CTL_HW 6 /* generic cpu/io */ #define CTL_MACHDEP 7 /* machine dependent */ #define CTL_USER 8 /* user-level */ #define CTL_MAXID 9 /* number of valid top-level ids */ #define CTL_NAMES { \ { 0, 0 }, \ { "kern", CTLTYPE_NODE }, \ { "vm", CTLTYPE_NODE }, \ { "fs", CTLTYPE_NODE }, \ { "net", CTLTYPE_NODE }, \ { "debug", CTLTYPE_NODE }, \ { "hw", CTLTYPE_NODE }, \ { "machdep", CTLTYPE_NODE }, \ { "user", CTLTYPE_NODE }, \ } /* * CTL_KERN identifiers */ #define KERN_OSTYPE 1 /* string: system version */ #define KERN_OSRELEASE 2 /* string: system release */ #define KERN_OSREV 3 /* int: system revision */ #define KERN_VERSION 4 /* string: compile time info */ #define KERN_MAXVNODES 5 /* int: max vnodes */ #define KERN_MAXPROC 6 /* int: max processes */ #define KERN_MAXFILES 7 /* int: max open files */ #define KERN_ARGMAX 8 /* int: max arguments to exec */ #define KERN_SECURELVL 9 /* int: system security level */ #define KERN_HOSTNAME 10 /* string: hostname */ #define KERN_HOSTID 11 /* int: host identifier */ #define KERN_CLOCKRATE 12 /* struct: struct clockrate */ #define KERN_VNODE 13 /* struct: vnode structures */ #define KERN_PROC 14 /* struct: process entries */ #define KERN_FILE 15 /* struct: file entries */ #define KERN_PROF 16 /* node: kernel profiling info */ #define KERN_POSIX1 17 /* int: POSIX.1 version */ #define KERN_NGROUPS 18 /* int: # of supplemental group ids */ #define KERN_JOB_CONTROL 19 /* int: is job control available */ #define KERN_SAVED_IDS 20 /* int: saved set-user/group-ID */ #define KERN_BOOTTIME 21 /* struct: time kernel was booted */ #define KERN_DOMAINNAME 22 /* string: YP domain name */ #define KERN_UPDATEINTERVAL 23 /* int: update process sleep time */ #define KERN_OSRELDATE 24 /* int: OS release date */ #define KERN_NTP_PLL 25 /* node: NTP PLL control */ #define KERN_MAXID 26 /* number of valid kern ids */ #define CTL_KERN_NAMES { \ { 0, 0 }, \ { "ostype", CTLTYPE_STRING }, \ { "osrelease", CTLTYPE_STRING }, \ { "osrevision", CTLTYPE_INT }, \ { "version", CTLTYPE_STRING }, \ { "maxvnodes", CTLTYPE_INT }, \ { "maxproc", CTLTYPE_INT }, \ { "maxfiles", CTLTYPE_INT }, \ { "argmax", CTLTYPE_INT }, \ { "securelevel", CTLTYPE_INT }, \ { "hostname", CTLTYPE_STRING }, \ { "hostid", CTLTYPE_INT }, \ { "clockrate", CTLTYPE_STRUCT }, \ { "vnode", CTLTYPE_STRUCT }, \ { "proc", CTLTYPE_STRUCT }, \ { "file", CTLTYPE_STRUCT }, \ { "profiling", CTLTYPE_NODE }, \ { "posix1version", CTLTYPE_INT }, \ { "ngroups", CTLTYPE_INT }, \ { "job_control", CTLTYPE_INT }, \ { "saved_ids", CTLTYPE_INT }, \ { "boottime", CTLTYPE_STRUCT }, \ { "domainname", CTLTYPE_STRING }, \ { "update", CTLTYPE_INT }, \ { "osreldate", CTLTYPE_INT }, \ { "ntp_pll", CTLTYPE_NODE }, \ +} + +/* + * CTL_FS identifiers + */ +#define FS_VFSCONF 0 /* get configured filesystems */ +#define FS_MAXID 1 /* number of items */ + +#define CTL_FS_NAMES { \ + { "vfsconf", CTLTYPE_STRUCT }, \ } /* * KERN_PROC subtypes */ #define KERN_PROC_ALL 0 /* everything */ #define KERN_PROC_PID 1 /* by process id */ #define KERN_PROC_PGRP 2 /* by process group id */ #define KERN_PROC_SESSION 3 /* by session of pid */ #define KERN_PROC_TTY 4 /* by controlling tty */ #define KERN_PROC_UID 5 /* by effective uid */ #define KERN_PROC_RUID 6 /* by real uid */ /* * KERN_PROC subtype ops return arrays of augmented proc structures: */ struct kinfo_proc { struct proc kp_proc; /* proc structure */ struct eproc { struct proc *e_paddr; /* address of proc */ struct session *e_sess; /* session pointer */ struct pcred e_pcred; /* process credentials */ struct ucred e_ucred; /* current credentials */ #ifdef sparc struct { segsz_t vm_rssize; /* resident set size */ segsz_t vm_tsize; /* text size */ segsz_t vm_dsize; /* data size */ segsz_t vm_ssize; /* stack size */ } e_vm; #else struct vmspace e_vm; /* address space */ #endif pid_t e_ppid; /* parent process id */ pid_t e_pgid; /* process group id */ short e_jobc; /* job control counter */ dev_t e_tdev; /* controlling tty dev */ pid_t e_tpgid; /* tty process group id */ struct session *e_tsess; /* tty session pointer */ #define WMESGLEN 7 char e_wmesg[WMESGLEN+1]; /* wchan message */ segsz_t e_xsize; /* text size */ short e_xrssize; /* text rss */ short e_xccount; /* text references */ short e_xswrss; long e_flag; #define EPROC_CTTY 0x01 /* controlling tty vnode active */ #define EPROC_SLEADER 0x02 /* session leader */ char e_login[MAXLOGNAME]; /* setlogin() name */ long e_spare[4]; } kp_eproc; }; /* * CTL_HW identifiers */ #define HW_MACHINE 1 /* string: machine class */ #define HW_MODEL 2 /* string: specific machine model */ #define HW_NCPU 3 /* int: number of cpus */ #define HW_BYTEORDER 4 /* int: machine byte order */ #define HW_PHYSMEM 5 /* int: total memory */ #define HW_USERMEM 6 /* int: non-kernel memory */ #define HW_PAGESIZE 7 /* int: software page size */ #define HW_DISKNAMES 8 /* strings: disk drive names */ #define HW_DISKSTATS 9 /* struct: diskstats[] */ #define HW_FLOATINGPT 10 /* int: has HW floating point? */ #define HW_MAXID 11 /* number of valid hw ids */ #define CTL_HW_NAMES { \ { 0, 0 }, \ { "machine", CTLTYPE_STRING }, \ { "model", CTLTYPE_STRING }, \ { "ncpu", CTLTYPE_INT }, \ { "byteorder", CTLTYPE_INT }, \ { "physmem", CTLTYPE_INT }, \ { "usermem", CTLTYPE_INT }, \ { "pagesize", CTLTYPE_INT }, \ { "disknames", CTLTYPE_STRUCT }, \ { "diskstats", CTLTYPE_STRUCT }, \ { "floatingpoint", CTLTYPE_INT }, \ } /* * CTL_USER definitions */ #define USER_CS_PATH 1 /* string: _CS_PATH */ #define USER_BC_BASE_MAX 2 /* int: BC_BASE_MAX */ #define USER_BC_DIM_MAX 3 /* int: BC_DIM_MAX */ #define USER_BC_SCALE_MAX 4 /* int: BC_SCALE_MAX */ #define USER_BC_STRING_MAX 5 /* int: BC_STRING_MAX */ #define USER_COLL_WEIGHTS_MAX 6 /* int: COLL_WEIGHTS_MAX */ #define USER_EXPR_NEST_MAX 7 /* int: EXPR_NEST_MAX */ #define USER_LINE_MAX 8 /* int: LINE_MAX */ #define USER_RE_DUP_MAX 9 /* int: RE_DUP_MAX */ #define USER_POSIX2_VERSION 10 /* int: POSIX2_VERSION */ #define USER_POSIX2_C_BIND 11 /* int: POSIX2_C_BIND */ #define USER_POSIX2_C_DEV 12 /* int: POSIX2_C_DEV */ #define USER_POSIX2_CHAR_TERM 13 /* int: POSIX2_CHAR_TERM */ #define USER_POSIX2_FORT_DEV 14 /* int: POSIX2_FORT_DEV */ #define USER_POSIX2_FORT_RUN 15 /* int: POSIX2_FORT_RUN */ #define USER_POSIX2_LOCALEDEF 16 /* int: POSIX2_LOCALEDEF */ #define USER_POSIX2_SW_DEV 17 /* int: POSIX2_SW_DEV */ #define USER_POSIX2_UPE 18 /* int: POSIX2_UPE */ #define USER_STREAM_MAX 19 /* int: POSIX2_STREAM_MAX */ #define USER_TZNAME_MAX 20 /* int: POSIX2_TZNAME_MAX */ #define USER_MAXID 21 /* number of valid user ids */ #define CTL_USER_NAMES { \ { 0, 0 }, \ { "cs_path", CTLTYPE_STRING }, \ { "bc_base_max", CTLTYPE_INT }, \ { "bc_dim_max", CTLTYPE_INT }, \ { "bc_scale_max", CTLTYPE_INT }, \ { "bc_string_max", CTLTYPE_INT }, \ { "coll_weights_max", CTLTYPE_INT }, \ { "expr_nest_max", CTLTYPE_INT }, \ { "line_max", CTLTYPE_INT }, \ { "re_dup_max", CTLTYPE_INT }, \ { "posix2_version", CTLTYPE_INT }, \ { "posix2_c_bind", CTLTYPE_INT }, \ { "posix2_c_dev", CTLTYPE_INT }, \ { "posix2_char_term", CTLTYPE_INT }, \ { "posix2_fort_dev", CTLTYPE_INT }, \ { "posix2_fort_run", CTLTYPE_INT }, \ { "posix2_localedef", CTLTYPE_INT }, \ { "posix2_sw_dev", CTLTYPE_INT }, \ { "posix2_upe", CTLTYPE_INT }, \ { "stream_max", CTLTYPE_INT }, \ { "tzname_max", CTLTYPE_INT }, \ } /* * CTL_DEBUG definitions * * Second level identifier specifies which debug variable. * Third level identifier specifies which stucture component. */ #define CTL_DEBUG_NAME 0 /* string: variable name */ #define CTL_DEBUG_VALUE 1 /* int: variable value */ #define CTL_DEBUG_MAXID 20 #ifdef KERNEL #ifdef DEBUG /* * CTL_DEBUG variables. * * These are declared as separate variables so that they can be * individually initialized at the location of their associated * variable. The loader prevents multiple use by issuing errors * if a variable is initialized in more than one place. They are * aggregated into an array in debug_sysctl(), so that it can * conveniently locate them when querried. If more debugging * variables are added, they must also be declared here and also * entered into the array. */ struct ctldebug { char *debugname; /* name of debugging variable */ int *debugvar; /* pointer to debugging variable */ }; extern struct ctldebug debug0, debug1, debug2, debug3, debug4; extern struct ctldebug debug5, debug6, debug7, debug8, debug9; extern struct ctldebug debug10, debug11, debug12, debug13, debug14; extern struct ctldebug debug15, debug16, debug17, debug18, debug19; #endif /* DEBUG */ /* * Internal sysctl function calling convention: * * (*sysctlfn)(name, namelen, oldval, oldlenp, newval, newlen); * * The name parameter points at the next component of the name to be * interpreted. The namelen parameter is the number of integers in * the name. */ typedef int (sysctlfn) __P((int *, u_int, void *, size_t *, void *, size_t, struct proc *)); int sysctl_int __P((void *, size_t *, void *, size_t, int *)); int sysctl_rdint __P((void *, size_t *, void *, int)); int sysctl_string __P((void *, size_t *, void *, size_t, char *, int)); int sysctl_rdstring __P((void *, size_t *, void *, char *)); int sysctl_rdstruct __P((void *, size_t *, void *, void *, int)); void fill_eproc __P((struct proc *, struct eproc *)); #else /* !KERNEL */ #include __BEGIN_DECLS int sysctl __P((int *, u_int, void *, size_t *, void *, size_t)); __END_DECLS #endif /* KERNEL */ #endif /* !_SYS_SYSCTL_H_ */ Index: head/sys/sys/vnode.h =================================================================== --- head/sys/sys/vnode.h (revision 2945) +++ head/sys/sys/vnode.h (revision 2946) @@ -1,409 +1,415 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vnode.h 8.7 (Berkeley) 2/4/94 - * $Id: vnode.h,v 1.5 1994/09/15 20:24:29 bde Exp $ + * $Id: vnode.h,v 1.6 1994/09/19 15:41:57 dfr Exp $ */ #ifndef _SYS_VNODE_H_ #define _SYS_VNODE_H_ #include /* * The vnode is the focus of all file activity in UNIX. There is a * unique vnode allocated for each active file, each current directory, * each mounted-on file, text file, and the root. */ /* * Vnode types. VNON means no type. */ enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD }; /* * Vnode tag types. * These are for the benefit of external programs only (e.g., pstat) * and should NEVER be inspected by the kernel. */ enum vtagtype { VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_PC, VT_LFS, VT_LOFS, VT_FDESC, VT_PORTAL, VT_NULL, VT_UMAP, VT_KERNFS, VT_PROCFS, VT_AFS, VT_ISOFS, VT_UNION, VT_MSDOSFS }; /* * Each underlying filesystem allocates its own private area and hangs * it from v_data. If non-null, this area is freed in getnewvnode(). */ LIST_HEAD(buflists, buf); struct vnode { u_long v_flag; /* vnode flags (see below) */ short v_usecount; /* reference count of users */ short v_writecount; /* reference count of writers */ long v_holdcnt; /* page & buffer references */ daddr_t v_lastr; /* last read (read-ahead) */ u_long v_id; /* capability identifier */ struct mount *v_mount; /* ptr to vfs we are in */ int (**v_op)(); /* vnode operations vector */ TAILQ_ENTRY(vnode) v_freelist; /* vnode freelist */ LIST_ENTRY(vnode) v_mntvnodes; /* vnodes for mount point */ struct buflists v_cleanblkhd; /* clean blocklist head */ struct buflists v_dirtyblkhd; /* dirty blocklist head */ long v_numoutput; /* num of writes in progress */ enum vtype v_type; /* vnode type */ union { struct mount *vu_mountedhere;/* ptr to mounted vfs (VDIR) */ struct socket *vu_socket; /* unix ipc (VSOCK) */ struct specinfo *vu_specinfo; /* device (VCHR, VBLK) */ struct fifoinfo *vu_fifoinfo; /* fifo (VFIFO) */ } v_un; struct nqlease *v_lease; /* Soft reference to lease */ daddr_t v_lastw; /* last write (write cluster) */ daddr_t v_cstart; /* start block of cluster */ daddr_t v_lasta; /* last allocation */ int v_clen; /* length of current cluster */ int v_ralen; /* Read-ahead length */ daddr_t v_maxra; /* last readahead block */ caddr_t v_vmdata; /* Place to store VM pager */ enum vtagtype v_tag; /* type of underlying data */ void *v_data; /* private data for fs */ }; #define v_mountedhere v_un.vu_mountedhere #define v_socket v_un.vu_socket #define v_specinfo v_un.vu_specinfo #define v_fifoinfo v_un.vu_fifoinfo /* * Vnode flags. */ #define VROOT 0x0001 /* root of its file system */ #define VTEXT 0x0002 /* vnode is a pure text prototype */ #define VSYSTEM 0x0004 /* vnode being used by kernel */ #define VXLOCK 0x0100 /* vnode is locked to change underlying type */ #define VXWANT 0x0200 /* process is waiting for vnode */ #define VBWAIT 0x0400 /* waiting for output to complete */ #define VALIASED 0x0800 /* vnode has an alias */ #define VDIROP 0x1000 /* LFS: vnode is involved in a directory op */ /* * Vnode attributes. A field value of VNOVAL represents a field whose value * is unavailable (getattr) or which is not to be changed (setattr). */ struct vattr { enum vtype va_type; /* vnode type (for create) */ u_short va_mode; /* files access mode and type */ short va_nlink; /* number of references to file */ uid_t va_uid; /* owner user id */ gid_t va_gid; /* owner group id */ long va_fsid; /* file system id (dev for now) */ long va_fileid; /* file id */ u_quad_t va_size; /* file size in bytes */ long va_blocksize; /* blocksize preferred for i/o */ struct timespec va_atime; /* time of last access */ struct timespec va_mtime; /* time of last modification */ struct timespec va_ctime; /* time file changed */ u_long va_gen; /* generation number of file */ u_long va_flags; /* flags defined for file */ dev_t va_rdev; /* device the special file represents */ u_quad_t va_bytes; /* bytes of disk space held by file */ u_quad_t va_filerev; /* file modification number */ u_int va_vaflags; /* operations flags, see below */ long va_spare; /* remain quad aligned */ }; /* * Flags for va_cflags. */ #define VA_UTIMES_NULL 0x01 /* utimes argument was NULL */ /* * Flags for ioflag. */ #define IO_UNIT 0x01 /* do I/O as atomic unit */ #define IO_APPEND 0x02 /* append write to end */ #define IO_SYNC 0x04 /* do I/O synchronously */ #define IO_NODELOCKED 0x08 /* underlying node already locked */ #define IO_NDELAY 0x10 /* FNDELAY flag set in file table */ /* * Modes. Some values same as Ixxx entries from inode.h for now. */ #define VSUID 04000 /* set user id on execution */ #define VSGID 02000 /* set group id on execution */ #define VSVTX 01000 /* save swapped text even after use */ #define VREAD 00400 /* read, write, execute permissions */ #define VWRITE 00200 #define VEXEC 00100 /* * Token indicating no attribute value yet assigned. */ #define VNOVAL (-1) #ifdef KERNEL /* * Convert between vnode types and inode formats (since POSIX.1 * defines mode word of stat structure in terms of inode formats). */ extern enum vtype iftovt_tab[]; extern int vttoif_tab[]; #define IFTOVT(mode) (iftovt_tab[((mode) & S_IFMT) >> 12]) #define VTTOIF(indx) (vttoif_tab[(int)(indx)]) #define MAKEIMODE(indx, mode) (int)(VTTOIF(indx) | (mode)) /* * Flags to various vnode functions. */ #define SKIPSYSTEM 0x0001 /* vflush: skip vnodes marked VSYSTEM */ #define FORCECLOSE 0x0002 /* vflush: force file closeure */ #define WRITECLOSE 0x0004 /* vflush: only close writeable files */ #define DOCLOSE 0x0008 /* vclean: close active files */ #define V_SAVE 0x0001 /* vinvalbuf: sync file first */ #define V_SAVEMETA 0x0002 /* vinvalbuf: leave indirect blocks */ #ifdef DIAGNOSTIC #define HOLDRELE(vp) holdrele(vp) #define VATTR_NULL(vap) vattr_null(vap) #define VHOLD(vp) vhold(vp) #define VREF(vp) vref(vp) void holdrele __P((struct vnode *)); void vattr_null __P((struct vattr *)); void vhold __P((struct vnode *)); void vref __P((struct vnode *)); #else #define HOLDRELE(vp) (vp)->v_holdcnt-- /* decrease buf or page ref */ #define VATTR_NULL(vap) (*(vap) = va_null) /* initialize a vattr */ #define VHOLD(vp) (vp)->v_holdcnt++ /* increase buf or page ref */ #define VREF(vp) (vp)->v_usecount++ /* increase reference */ #endif #define NULLVP ((struct vnode *)NULL) + +#ifdef VFS_LKM +#define VNODEOP_SET(f) DATA_SET(MODVNOPS,f) +#else +#define VNODEOP_SET(f) DATA_SET(vfs_opv_descs_,f) +#endif /* * Global vnode data. */ extern struct vnode *rootvnode; /* root (i.e. "/") vnode */ extern int desiredvnodes; /* number of vnodes desired */ extern struct vattr va_null; /* predefined null vattr structure */ /* * Macro/function to check for client cache inconsistency w.r.t. leasing. */ #define LEASE_READ 0x1 /* Check lease for readers */ #define LEASE_WRITE 0x2 /* Check lease for modifiers */ #ifdef NFS void lease_check __P((struct vnode *vp, struct proc *p, struct ucred *ucred, int flag)); void lease_updatetime __P((int deltat)); #define LEASE_CHECK(vp, p, cred, flag) lease_check((vp), (p), (cred), (flag)) #define LEASE_UPDATETIME(dt) lease_updatetime(dt) #else #define LEASE_CHECK(vp, p, cred, flag) #define LEASE_UPDATETIME(dt) #endif /* NFS */ #endif /* KERNEL */ /* * Mods for exensibility. */ /* * Flags for vdesc_flags: */ #define VDESC_MAX_VPS 16 /* Low order 16 flag bits are reserved for willrele flags for vp arguments. */ #define VDESC_VP0_WILLRELE 0x0001 #define VDESC_VP1_WILLRELE 0x0002 #define VDESC_VP2_WILLRELE 0x0004 #define VDESC_VP3_WILLRELE 0x0008 #define VDESC_NOMAP_VPP 0x0100 #define VDESC_VPP_WILLRELE 0x0200 /* * VDESC_NO_OFFSET is used to identify the end of the offset list * and in places where no such field exists. */ #define VDESC_NO_OFFSET -1 /* * This structure describes the vnode operation taking place. */ struct vnodeop_desc { int vdesc_offset; /* offset in vector--first for speed */ char *vdesc_name; /* a readable name for debugging */ int vdesc_flags; /* VDESC_* flags */ /* * These ops are used by bypass routines to map and locate arguments. * Creds and procs are not needed in bypass routines, but sometimes * they are useful to (for example) transport layers. * Nameidata is useful because it has a cred in it. */ int *vdesc_vp_offsets; /* list ended by VDESC_NO_OFFSET */ int vdesc_vpp_offset; /* return vpp location */ int vdesc_cred_offset; /* cred location, if any */ int vdesc_proc_offset; /* proc location, if any */ int vdesc_componentname_offset; /* if any */ /* * Finally, we've got a list of private data (about each operation) * for each transport layer. (Support to manage this list is not * yet part of BSD.) */ caddr_t *vdesc_transports; }; #ifdef KERNEL /* * A list of all the operation descs. */ extern struct vnodeop_desc *vnodeop_descs[]; /* * This macro is very helpful in defining those offsets in the vdesc struct. * * This is stolen from X11R4. I ingored all the fancy stuff for * Crays, so if you decide to port this to such a serious machine, * you might want to consult Intrisics.h's XtOffset{,Of,To}. */ #define VOPARG_OFFSET(p_type,field) \ ((int) (((char *) (&(((p_type)NULL)->field))) - ((char *) NULL))) #define VOPARG_OFFSETOF(s_type,field) \ VOPARG_OFFSET(s_type*,field) #define VOPARG_OFFSETTO(S_TYPE,S_OFFSET,STRUCT_P) \ ((S_TYPE)(((char*)(STRUCT_P))+(S_OFFSET))) /* * This structure is used to configure the new vnodeops vector. */ struct vnodeopv_entry_desc { struct vnodeop_desc *opve_op; /* which operation this is */ int (*opve_impl)(); /* code implementing this operation */ }; struct vnodeopv_desc { /* ptr to the ptr to the vector where op should go */ int (***opv_desc_vector_p)(); struct vnodeopv_entry_desc *opv_desc_ops; /* null terminated list */ }; /* * A default routine which just returns an error. */ int vn_default_error __P((void)); /* * A generic structure. * This can be used by bypass routines to identify generic arguments. */ struct vop_generic_args { struct vnodeop_desc *a_desc; /* other random data follows, presumably */ }; /* * VOCALL calls an op given an ops vector. We break it out because BSD's * vclean changes the ops vector and then wants to call ops with the old * vector. */ #define VOCALL(OPSV,OFF,AP) (( *((OPSV)[(OFF)])) (AP)) /* * This call works for vnodes in the kernel. */ #define VCALL(VP,OFF,AP) VOCALL((VP)->v_op,(OFF),(AP)) #define VDESC(OP) (& __CONCAT(OP,_desc)) #define VOFFSET(OP) (VDESC(OP)->vdesc_offset) /* * Finally, include the default set of vnode operations. */ #include /* * Public vnode manipulation functions. */ struct componentname; struct file; struct mount; struct nameidata; struct proc; struct stat; struct ucred; struct uio; struct vattr; struct vnode; struct vop_bwrite_args; int bdevvp __P((dev_t dev, struct vnode **vpp)); /* cache_* may belong in namei.h. */ void cache_enter __P((struct vnode *dvp, struct vnode *vp, struct componentname *cnp)); int cache_lookup __P((struct vnode *dvp, struct vnode **vpp, struct componentname *cnp)); void cache_purge __P((struct vnode *vp)); int getnewvnode __P((enum vtagtype tag, struct mount *mp, int (**vops)(), struct vnode **vpp)); int vinvalbuf __P((struct vnode *vp, int save, struct ucred *cred, struct proc *p, int slpflag, int slptimeo)); void vattr_null __P((struct vattr *vap)); int vcount __P((struct vnode *vp)); int vget __P((struct vnode *vp, int lockflag)); void vgone __P((struct vnode *vp)); void vgoneall __P((struct vnode *vp)); int vn_bwrite __P((struct vop_bwrite_args *ap)); int vn_close __P((struct vnode *vp, int flags, struct ucred *cred, struct proc *p)); int vn_closefile __P((struct file *fp, struct proc *p)); int vn_ioctl __P((struct file *fp, int com, caddr_t data, struct proc *p)); int vn_open __P((struct nameidata *ndp, int fmode, int cmode)); int vn_rdwr __P((enum uio_rw rw, struct vnode *vp, caddr_t base, int len, off_t offset, enum uio_seg segflg, int ioflg, struct ucred *cred, int *aresid, struct proc *p)); int vn_read __P((struct file *fp, struct uio *uio, struct ucred *cred)); int vn_select __P((struct file *fp, int which, struct proc *p)); int vn_stat __P((struct vnode *vp, struct stat *sb, struct proc *p)); int vn_write __P((struct file *fp, struct uio *uio, struct ucred *cred)); struct vnode * checkalias __P((struct vnode *vp, dev_t nvp_rdev, struct mount *mp)); void vprint __P((char *, struct vnode *)); void vput __P((struct vnode *vp)); void vref __P((struct vnode *vp)); void vrele __P((struct vnode *vp)); #endif /* KERNEL */ #endif /* !_SYS_VNODE_H_ */ Index: head/sys/ufs/ffs/ffs_vfsops.c =================================================================== --- head/sys/ufs/ffs/ffs_vfsops.c (revision 2945) +++ head/sys/ufs/ffs/ffs_vfsops.c (revision 2946) @@ -1,860 +1,862 @@ /* * Copyright (c) 1989, 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. * * @(#)ffs_vfsops.c 8.8 (Berkeley) 4/18/94 - * $Id: ffs_vfsops.c,v 1.4 1994/08/18 22:35:54 wollman Exp $ + * $Id: ffs_vfsops.c,v 1.5 1994/08/20 16:03:25 davidg Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int ffs_sbupdate __P((struct ufsmount *, int)); struct vfsops ufs_vfsops = { ffs_mount, ufs_start, ffs_unmount, ufs_root, ufs_quotactl, ffs_statfs, ffs_sync, ffs_vget, ffs_fhtovp, ffs_vptofh, ffs_init, }; + +VFS_SET(ufs_vfsops, ffs, MOUNT_UFS, 0); extern u_long nextgennumber; /* * Called by main() when ufs is going to be mounted as root. * * Name is updated by mount(8) after booting. */ #define ROOTNAME "root_device" int ffs_mountroot() { register struct fs *fs; register struct mount *mp; struct proc *p = curproc; /* XXX */ struct ufsmount *ump; u_int size; int error; /* * Get vnodes for swapdev and rootdev. */ if (bdevvp(swapdev, &swapdev_vp) || bdevvp(rootdev, &rootvp)) panic("ffs_mountroot: can't setup bdevvp's"); mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); bzero((char *)mp, (u_long)sizeof(struct mount)); mp->mnt_op = &ufs_vfsops; mp->mnt_flag = MNT_RDONLY; if (error = ffs_mountfs(rootvp, mp, p)) { free(mp, M_MOUNT); return (error); } if (error = vfs_lock(mp)) { (void)ffs_unmount(mp, 0, p); free(mp, M_MOUNT); return (error); } TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); mp->mnt_flag |= MNT_ROOTFS; mp->mnt_vnodecovered = NULLVP; ump = VFSTOUFS(mp); fs = ump->um_fs; bzero(fs->fs_fsmnt, sizeof(fs->fs_fsmnt)); fs->fs_fsmnt[0] = '/'; bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN); (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void)ffs_statfs(mp, &mp->mnt_stat, p); vfs_unlock(mp); inittodr(fs->fs_time); return (0); } /* * VFS Operations. * * mount system call */ int ffs_mount(mp, path, data, ndp, p) register struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { struct vnode *devvp; struct ufs_args args; struct ufsmount *ump = 0; register struct fs *fs; u_int size; int error, flags; if (error = copyin(data, (caddr_t)&args, sizeof (struct ufs_args))) return (error); /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { ump = VFSTOUFS(mp); fs = ump->um_fs; error = 0; if (fs->fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; if (vfs_busy(mp)) return (EBUSY); error = ffs_flushfiles(mp, flags, p); vfs_unbusy(mp); } if (!error && (mp->mnt_flag & MNT_RELOAD)) error = ffs_reload(mp, ndp->ni_cnd.cn_cred, p); if (error) return (error); if (fs->fs_ronly && (mp->mnt_flag & MNT_WANTRDWR)) fs->fs_ronly = 0; if (fs->fs_ronly == 0) { fs->fs_clean = 0; ffs_sbupdate(ump, MNT_WAIT); } if (args.fspec == 0) { /* * Process export requests. */ return (vfs_export(mp, &ump->um_export, &args.export)); } } /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible block device. */ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); if (error = namei(ndp)) return (error); devvp = ndp->ni_vp; if (devvp->v_type != VBLK) { vrele(devvp); return (ENOTBLK); } if (major(devvp->v_rdev) >= nblkdev) { vrele(devvp); return (ENXIO); } if ((mp->mnt_flag & MNT_UPDATE) == 0) error = ffs_mountfs(devvp, mp, p); else { if (devvp != ump->um_devvp) error = EINVAL; /* needs translation */ else vrele(devvp); } if (error) { vrele(devvp); return (error); } ump = VFSTOUFS(mp); fs = ump->um_fs; (void) copyinstr(path, fs->fs_fsmnt, sizeof(fs->fs_fsmnt) - 1, &size); bzero(fs->fs_fsmnt + size, sizeof(fs->fs_fsmnt) - size); bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN); (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void)ffs_statfs(mp, &mp->mnt_stat, p); return (0); } /* * Reload all incore data for a filesystem (used after running fsck on * the root filesystem and finding things to fix). The filesystem must * be mounted read-only. * * Things to do to update the mount: * 1) invalidate all cached meta-data. * 2) re-read superblock from disk. * 3) re-read summary information from disk. * 4) invalidate all inactive vnodes. * 5) invalidate all cached file data. * 6) re-read inode data for all active vnodes. */ int ffs_reload(mountp, cred, p) register struct mount *mountp; struct ucred *cred; struct proc *p; { register struct vnode *vp, *nvp, *devvp; struct inode *ip; struct csum *space; struct buf *bp; struct fs *fs; int i, blks, size, error; if ((mountp->mnt_flag & MNT_RDONLY) == 0) return (EINVAL); /* * Step 1: invalidate all cached meta-data. */ devvp = VFSTOUFS(mountp)->um_devvp; if (vinvalbuf(devvp, 0, cred, p, 0, 0)) panic("ffs_reload: dirty1"); /* * Step 2: re-read superblock from disk. */ if (error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp)) return (error); fs = (struct fs *)bp->b_data; if (fs->fs_magic != FS_MAGIC || fs->fs_bsize > MAXBSIZE || fs->fs_bsize < sizeof(struct fs)) { brelse(bp); return (EIO); /* XXX needs translation */ } fs = VFSTOUFS(mountp)->um_fs; bcopy(&fs->fs_csp[0], &((struct fs *)bp->b_data)->fs_csp[0], sizeof(fs->fs_csp)); bcopy(bp->b_data, fs, (u_int)fs->fs_sbsize); if (fs->fs_sbsize < SBSIZE) bp->b_flags |= B_INVAL; brelse(bp); ffs_oldfscompat(fs); /* * Step 3: re-read summary information from disk. */ blks = howmany(fs->fs_cssize, fs->fs_fsize); space = fs->fs_csp[0]; for (i = 0; i < blks; i += fs->fs_frag) { size = fs->fs_bsize; if (i + fs->fs_frag > blks) size = (blks - i) * fs->fs_fsize; if (error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size, NOCRED, &bp)) return (error); bcopy(bp->b_data, fs->fs_csp[fragstoblks(fs, i)], (u_int)size); brelse(bp); } loop: for (vp = mountp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) { nvp = vp->v_mntvnodes.le_next; /* * Step 4: invalidate all inactive vnodes. */ if (vp->v_usecount == 0) { vgone(vp); continue; } /* * Step 5: invalidate all cached file data. */ if (vget(vp, 1)) goto loop; if (vinvalbuf(vp, 0, cred, p, 0, 0)) panic("ffs_reload: dirty2"); /* * Step 6: re-read inode data for all active vnodes. */ ip = VTOI(vp); if (error = bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), (int)fs->fs_bsize, NOCRED, &bp)) { vput(vp); return (error); } ip->i_din = *((struct dinode *)bp->b_data + ino_to_fsbo(fs, ip->i_number)); brelse(bp); vput(vp); if (vp->v_mount != mountp) goto loop; } return (0); } /* * Common code for mount and mountroot */ int ffs_mountfs(devvp, mp, p) register struct vnode *devvp; struct mount *mp; struct proc *p; { register struct ufsmount *ump; struct buf *bp; register struct fs *fs; dev_t dev = devvp->v_rdev; struct partinfo dpart; caddr_t base, space; int havepart = 0, blks; int error, i, size; int ronly; /* * Disallow multiple mounts of the same device. * Disallow mounting of a device that is currently in use * (except for root, which might share swap device for miniroot). * Flush out any old buffers remaining from a previous use. */ if (error = vfs_mountedon(devvp)) return (error); if (vcount(devvp) > 1 && devvp != rootvp) return (EBUSY); if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0)) return (error); ronly = (mp->mnt_flag & MNT_RDONLY) != 0; if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p)) return (error); if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, NOCRED, p) != 0) size = DEV_BSIZE; else { havepart = 1; size = dpart.disklab->d_secsize; } bp = NULL; ump = NULL; if (error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp)) goto out; fs = (struct fs *)bp->b_data; if (fs->fs_magic != FS_MAGIC || fs->fs_bsize > MAXBSIZE || fs->fs_bsize < sizeof(struct fs)) { error = EINVAL; /* XXX needs translation */ goto out; } ump = malloc(sizeof *ump, M_UFSMNT, M_WAITOK); bzero((caddr_t)ump, sizeof *ump); ump->um_fs = malloc((u_long)fs->fs_sbsize, M_UFSMNT, M_WAITOK); bcopy(bp->b_data, ump->um_fs, (u_int)fs->fs_sbsize); if (fs->fs_sbsize < SBSIZE) bp->b_flags |= B_INVAL; brelse(bp); bp = NULL; fs = ump->um_fs; fs->fs_ronly = ronly; if (!fs->fs_clean) { printf("WARNING: %s was not properly dismounted\n",fs->fs_fsmnt); } if (ronly == 0) { fs->fs_fmod = 1; fs->fs_clean = 0; } blks = howmany(fs->fs_cssize, fs->fs_fsize); base = space = malloc((u_long)fs->fs_cssize, M_UFSMNT, M_WAITOK); for (i = 0; i < blks; i += fs->fs_frag) { size = fs->fs_bsize; if (i + fs->fs_frag > blks) size = (blks - i) * fs->fs_fsize; error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size, NOCRED, &bp); if (error) { free(base, M_UFSMNT); goto out; } bcopy(bp->b_data, space, (u_int)size); fs->fs_csp[fragstoblks(fs, i)] = (struct csum *)space; space += size; brelse(bp); bp = NULL; } mp->mnt_data = (qaddr_t)ump; mp->mnt_stat.f_fsid.val[0] = (long)dev; mp->mnt_stat.f_fsid.val[1] = MOUNT_UFS; mp->mnt_maxsymlinklen = fs->fs_maxsymlinklen; mp->mnt_flag |= MNT_LOCAL; ump->um_mountp = mp; ump->um_dev = dev; ump->um_devvp = devvp; ump->um_nindir = fs->fs_nindir; ump->um_bptrtodb = fs->fs_fsbtodb; ump->um_seqinc = fs->fs_frag; for (i = 0; i < MAXQUOTAS; i++) ump->um_quotas[i] = NULLVP; devvp->v_specflags |= SI_MOUNTEDON; ffs_oldfscompat(fs); if (ronly == 0) ffs_sbupdate(ump, MNT_WAIT); return (0); out: if (bp) brelse(bp); (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p); if (ump) { free(ump->um_fs, M_UFSMNT); free(ump, M_UFSMNT); mp->mnt_data = (qaddr_t)0; } return (error); } /* * Sanity checks for old file systems. * * XXX - goes away some day. */ int ffs_oldfscompat(fs) struct fs *fs; { int i; fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect); /* XXX */ fs->fs_interleave = max(fs->fs_interleave, 1); /* XXX */ if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */ fs->fs_nrpos = 8; /* XXX */ if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */ quad_t sizepb = fs->fs_bsize; /* XXX */ /* XXX */ fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1; /* XXX */ for (i = 0; i < NIADDR; i++) { /* XXX */ sizepb *= NINDIR(fs); /* XXX */ fs->fs_maxfilesize += sizepb; /* XXX */ } /* XXX */ fs->fs_qbmask = ~fs->fs_bmask; /* XXX */ fs->fs_qfmask = ~fs->fs_fmask; /* XXX */ } /* XXX */ return (0); } /* * unmount system call */ int ffs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { register struct ufsmount *ump; register struct fs *fs; int error, flags, ronly; flags = 0; if (mntflags & MNT_FORCE) { flags |= FORCECLOSE; } if (error = ffs_flushfiles(mp, flags, p)) return (error); ump = VFSTOUFS(mp); fs = ump->um_fs; ronly = fs->fs_ronly; if (!ronly) { fs->fs_clean = 1; ffs_sbupdate(ump, MNT_WAIT); } ump->um_devvp->v_specflags &= ~SI_MOUNTEDON; error = VOP_CLOSE(ump->um_devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p); vrele(ump->um_devvp); free(fs->fs_csp[0], M_UFSMNT); free(fs, M_UFSMNT); free(ump, M_UFSMNT); mp->mnt_data = (qaddr_t)0; mp->mnt_flag &= ~MNT_LOCAL; return (error); } /* * Flush out all the files in a filesystem. */ int ffs_flushfiles(mp, flags, p) register struct mount *mp; int flags; struct proc *p; { extern int doforce; register struct ufsmount *ump; int i, error; if (!doforce) flags &= ~FORCECLOSE; ump = VFSTOUFS(mp); #ifdef QUOTA if (mp->mnt_flag & MNT_QUOTA) { if (error = vflush(mp, NULLVP, SKIPSYSTEM|flags)) return (error); for (i = 0; i < MAXQUOTAS; i++) { if (ump->um_quotas[i] == NULLVP) continue; quotaoff(p, mp, i); } /* * Here we fall through to vflush again to ensure * that we have gotten rid of all the system vnodes. */ } #endif error = vflush(mp, NULLVP, flags); return (error); } /* * Get file system statistics. */ int ffs_statfs(mp, sbp, p) struct mount *mp; register struct statfs *sbp; struct proc *p; { register struct ufsmount *ump; register struct fs *fs; ump = VFSTOUFS(mp); fs = ump->um_fs; if (fs->fs_magic != FS_MAGIC) panic("ffs_statfs"); sbp->f_type = MOUNT_UFS; sbp->f_bsize = fs->fs_fsize; sbp->f_iosize = fs->fs_bsize; sbp->f_blocks = fs->fs_dsize; sbp->f_bfree = fs->fs_cstotal.cs_nbfree * fs->fs_frag + fs->fs_cstotal.cs_nffree; sbp->f_bavail = (fs->fs_dsize * (100 - fs->fs_minfree) / 100) - (fs->fs_dsize - sbp->f_bfree); sbp->f_files = fs->fs_ncg * fs->fs_ipg - ROOTINO; sbp->f_ffree = fs->fs_cstotal.cs_nifree; if (sbp != &mp->mnt_stat) { bcopy((caddr_t)mp->mnt_stat.f_mntonname, (caddr_t)&sbp->f_mntonname[0], MNAMELEN); bcopy((caddr_t)mp->mnt_stat.f_mntfromname, (caddr_t)&sbp->f_mntfromname[0], MNAMELEN); } return (0); } /* * Go through the disk queues to initiate sandbagged IO; * go through the inodes to write those that have been modified; * initiate the writing of the super block if it has been modified. * * Note: we are always called with the filesystem marked `MPBUSY'. */ int ffs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { register struct vnode *vp; register struct inode *ip; register struct ufsmount *ump = VFSTOUFS(mp); register struct fs *fs; int error, allerror = 0; fs = ump->um_fs; /* * Write back modified superblock. * Consistency check that the superblock * is still in the buffer cache. */ if (fs->fs_fmod != 0) { if (fs->fs_ronly != 0) { /* XXX */ printf("fs = %s\n", fs->fs_fsmnt); panic("update: rofs mod"); } fs->fs_fmod = 0; fs->fs_time = time.tv_sec; allerror = ffs_sbupdate(ump, waitfor); } /* * Write back each (modified) inode. */ loop: for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = vp->v_mntvnodes.le_next) { /* * If the vnode that we are about to sync is no longer * associated with this mount point, start over. */ if (vp->v_mount != mp) goto loop; if (VOP_ISLOCKED(vp)) continue; ip = VTOI(vp); if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 && vp->v_dirtyblkhd.lh_first == NULL) continue; if (vget(vp, 1)) goto loop; if (error = VOP_FSYNC(vp, cred, waitfor, p)) allerror = error; vput(vp); } /* * Force stale file system control information to be flushed. */ if (error = VOP_FSYNC(ump->um_devvp, cred, waitfor, p)) allerror = error; #ifdef QUOTA qsync(mp); #endif return (allerror); } /* * Look up a FFS dinode number to find its incore vnode, otherwise read it * in from disk. If it is in core, wait for the lock bit to clear, then * return the inode locked. Detection and handling of mount points must be * done by the calling routine. */ int ffs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { register struct fs *fs; register struct inode *ip; struct ufsmount *ump; struct buf *bp; struct vnode *vp; dev_t dev; int i, type, error; ump = VFSTOUFS(mp); dev = ump->um_dev; if ((*vpp = ufs_ihashget(dev, ino)) != NULL) return (0); /* Allocate a new vnode/inode. */ if (error = getnewvnode(VT_UFS, mp, ffs_vnodeop_p, &vp)) { *vpp = NULL; return (error); } type = ump->um_devvp->v_tag == VT_MFS ? M_MFSNODE : M_FFSNODE; /* XXX */ MALLOC(ip, struct inode *, sizeof(struct inode), type, M_WAITOK); bzero((caddr_t)ip, sizeof(struct inode)); vp->v_data = ip; ip->i_vnode = vp; ip->i_fs = fs = ump->um_fs; ip->i_dev = dev; ip->i_number = ino; #ifdef QUOTA for (i = 0; i < MAXQUOTAS; i++) ip->i_dquot[i] = NODQUOT; #endif /* * Put it onto its hash chain and lock it so that other requests for * this inode will block if they arrive while we are sleeping waiting * for old data structures to be purged or for the contents of the * disk portion of this inode to be read. */ ufs_ihashins(ip); /* Read in the disk contents for the inode, copy into the inode. */ if (error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)), (int)fs->fs_bsize, NOCRED, &bp)) { /* * The inode does not contain anything useful, so it would * be misleading to leave it on its hash chain. With mode * still zero, it will be unlinked and returned to the free * list by vput(). */ vput(vp); brelse(bp); *vpp = NULL; return (error); } ip->i_din = *((struct dinode *)bp->b_data + ino_to_fsbo(fs, ino)); brelse(bp); /* * Initialize the vnode from the inode, check for aliases. * Note that the underlying vnode may have changed. */ if (error = ufs_vinit(mp, ffs_specop_p, FFS_FIFOOPS, &vp)) { vput(vp); *vpp = NULL; return (error); } /* * Finish inode initialization now that aliasing has been resolved. */ ip->i_devvp = ump->um_devvp; VREF(ip->i_devvp); /* * Set up a generation number for this inode if it does not * already have one. This should only happen on old filesystems. */ if (ip->i_gen == 0) { if (++nextgennumber < (u_long)time.tv_sec) nextgennumber = time.tv_sec; ip->i_gen = nextgennumber; if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) ip->i_flag |= IN_MODIFIED; } /* * Ensure that uid and gid are correct. This is a temporary * fix until fsck has been changed to do the update. */ if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */ ip->i_uid = ip->i_din.di_ouid; /* XXX */ ip->i_gid = ip->i_din.di_ogid; /* XXX */ } /* XXX */ *vpp = vp; return (0); } /* * File handle to vnode * * Have to be really careful about stale file handles: * - check that the inode number is valid * - call ffs_vget() to get the locked inode * - check for an unallocated inode (i_mode == 0) * - check that the given client host has export rights and return * those rights via. exflagsp and credanonp */ int ffs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) register struct mount *mp; struct fid *fhp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred **credanonp; { register struct ufid *ufhp; struct fs *fs; ufhp = (struct ufid *)fhp; fs = VFSTOUFS(mp)->um_fs; if (ufhp->ufid_ino < ROOTINO || ufhp->ufid_ino >= fs->fs_ncg * fs->fs_ipg) return (ESTALE); return (ufs_check_export(mp, ufhp, nam, vpp, exflagsp, credanonp)); } /* * Vnode pointer to File handle */ /* ARGSUSED */ int ffs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { register struct inode *ip; register struct ufid *ufhp; ip = VTOI(vp); ufhp = (struct ufid *)fhp; ufhp->ufid_len = sizeof(struct ufid); ufhp->ufid_ino = ip->i_number; ufhp->ufid_gen = ip->i_gen; return (0); } /* * Write a superblock and associated information back to disk. */ int ffs_sbupdate(mp, waitfor) struct ufsmount *mp; int waitfor; { register struct fs *fs = mp->um_fs; register struct buf *bp; int blks; caddr_t space; int i, size, error = 0; bp = getblk(mp->um_devvp, SBLOCK, (int)fs->fs_sbsize, 0, 0); bcopy((caddr_t)fs, bp->b_data, (u_int)fs->fs_sbsize); /* Restore compatibility to old file systems. XXX */ if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */ ((struct fs *)bp->b_data)->fs_nrpos = -1; /* XXX */ if (waitfor == MNT_WAIT) error = bwrite(bp); else bawrite(bp); blks = howmany(fs->fs_cssize, fs->fs_fsize); space = (caddr_t)fs->fs_csp[0]; for (i = 0; i < blks; i += fs->fs_frag) { size = fs->fs_bsize; if (i + fs->fs_frag > blks) size = (blks - i) * fs->fs_fsize; bp = getblk(mp->um_devvp, fsbtodb(fs, fs->fs_csaddr + i), size, 0, 0); bcopy(space, bp->b_data, (u_int)size); space += size; if (waitfor == MNT_WAIT) error = bwrite(bp); else bawrite(bp); } return (error); } Index: head/sys/ufs/ffs/ffs_vnops.c =================================================================== --- head/sys/ufs/ffs/ffs_vnops.c (revision 2945) +++ head/sys/ufs/ffs/ffs_vnops.c (revision 2946) @@ -1,289 +1,295 @@ /* * 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.7 (Berkeley) 2/3/94 - * $Id: ffs_vnops.c,v 1.2 1994/08/02 07:54:25 davidg Exp $ + * $Id: ffs_vnops.c,v 1.3 1994/08/08 17:30:59 davidg Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Global vfs data structures for ufs. */ int (**ffs_vnodeop_p)(); struct vnodeopv_entry_desc ffs_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, ufs_lookup }, /* lookup */ { &vop_create_desc, ufs_create }, /* create */ { &vop_mknod_desc, ufs_mknod }, /* mknod */ { &vop_open_desc, ufs_open }, /* open */ { &vop_close_desc, ufs_close }, /* close */ { &vop_access_desc, ufs_access }, /* access */ { &vop_getattr_desc, ufs_getattr }, /* getattr */ { &vop_setattr_desc, ufs_setattr }, /* setattr */ { &vop_read_desc, ffs_read }, /* read */ { &vop_write_desc, ffs_write }, /* write */ { &vop_ioctl_desc, ufs_ioctl }, /* ioctl */ { &vop_select_desc, ufs_select }, /* select */ { &vop_mmap_desc, ufs_mmap }, /* mmap */ { &vop_fsync_desc, ffs_fsync }, /* fsync */ { &vop_seek_desc, ufs_seek }, /* seek */ { &vop_remove_desc, ufs_remove }, /* remove */ { &vop_link_desc, ufs_link }, /* link */ { &vop_rename_desc, ufs_rename }, /* rename */ { &vop_mkdir_desc, ufs_mkdir }, /* mkdir */ { &vop_rmdir_desc, ufs_rmdir }, /* rmdir */ { &vop_symlink_desc, ufs_symlink }, /* symlink */ { &vop_readdir_desc, ufs_readdir }, /* readdir */ { &vop_readlink_desc, ufs_readlink }, /* readlink */ { &vop_abortop_desc, ufs_abortop }, /* abortop */ { &vop_inactive_desc, ufs_inactive }, /* inactive */ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */ { &vop_lock_desc, ufs_lock }, /* lock */ { &vop_unlock_desc, ufs_unlock }, /* unlock */ { &vop_bmap_desc, ufs_bmap }, /* bmap */ { &vop_strategy_desc, ufs_strategy }, /* strategy */ { &vop_print_desc, ufs_print }, /* print */ { &vop_islocked_desc, ufs_islocked }, /* islocked */ { &vop_pathconf_desc, ufs_pathconf }, /* pathconf */ { &vop_advlock_desc, ufs_advlock }, /* advlock */ { &vop_blkatoff_desc, ffs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, ffs_valloc }, /* valloc */ { &vop_reallocblks_desc, ffs_reallocblks }, /* reallocblks */ { &vop_vfree_desc, ffs_vfree }, /* vfree */ { &vop_truncate_desc, ffs_truncate }, /* truncate */ { &vop_update_desc, ffs_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc ffs_vnodeop_opv_desc = { &ffs_vnodeop_p, ffs_vnodeop_entries }; int (**ffs_specop_p)(); struct vnodeopv_entry_desc ffs_specop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, spec_lookup }, /* lookup */ { &vop_create_desc, spec_create }, /* create */ { &vop_mknod_desc, spec_mknod }, /* mknod */ { &vop_open_desc, spec_open }, /* open */ { &vop_close_desc, ufsspec_close }, /* close */ { &vop_access_desc, ufs_access }, /* access */ { &vop_getattr_desc, ufs_getattr }, /* getattr */ { &vop_setattr_desc, ufs_setattr }, /* setattr */ { &vop_read_desc, ufsspec_read }, /* read */ { &vop_write_desc, ufsspec_write }, /* write */ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ { &vop_select_desc, spec_select }, /* select */ { &vop_mmap_desc, spec_mmap }, /* mmap */ { &vop_fsync_desc, ffs_fsync }, /* fsync */ { &vop_seek_desc, spec_seek }, /* seek */ { &vop_remove_desc, spec_remove }, /* remove */ { &vop_link_desc, spec_link }, /* link */ { &vop_rename_desc, spec_rename }, /* rename */ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */ { &vop_symlink_desc, spec_symlink }, /* symlink */ { &vop_readdir_desc, spec_readdir }, /* readdir */ { &vop_readlink_desc, spec_readlink }, /* readlink */ { &vop_abortop_desc, spec_abortop }, /* abortop */ { &vop_inactive_desc, ufs_inactive }, /* inactive */ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */ { &vop_lock_desc, ufs_lock }, /* lock */ { &vop_unlock_desc, ufs_unlock }, /* unlock */ { &vop_bmap_desc, spec_bmap }, /* bmap */ { &vop_strategy_desc, spec_strategy }, /* strategy */ { &vop_print_desc, ufs_print }, /* print */ { &vop_islocked_desc, ufs_islocked }, /* islocked */ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ { &vop_advlock_desc, spec_advlock }, /* advlock */ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, spec_valloc }, /* valloc */ { &vop_reallocblks_desc, spec_reallocblks }, /* reallocblks */ { &vop_vfree_desc, ffs_vfree }, /* vfree */ { &vop_truncate_desc, spec_truncate }, /* truncate */ { &vop_update_desc, ffs_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc ffs_specop_opv_desc = { &ffs_specop_p, ffs_specop_entries }; #ifdef FIFO int (**ffs_fifoop_p)(); struct vnodeopv_entry_desc ffs_fifoop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, fifo_lookup }, /* lookup */ { &vop_create_desc, fifo_create }, /* create */ { &vop_mknod_desc, fifo_mknod }, /* mknod */ { &vop_open_desc, fifo_open }, /* open */ { &vop_close_desc, ufsfifo_close }, /* close */ { &vop_access_desc, ufs_access }, /* access */ { &vop_getattr_desc, ufs_getattr }, /* getattr */ { &vop_setattr_desc, ufs_setattr }, /* setattr */ { &vop_read_desc, ufsfifo_read }, /* read */ { &vop_write_desc, ufsfifo_write }, /* write */ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */ { &vop_select_desc, fifo_select }, /* select */ { &vop_mmap_desc, fifo_mmap }, /* mmap */ { &vop_fsync_desc, ffs_fsync }, /* fsync */ { &vop_seek_desc, fifo_seek }, /* seek */ { &vop_remove_desc, fifo_remove }, /* remove */ { &vop_link_desc, fifo_link }, /* link */ { &vop_rename_desc, fifo_rename }, /* rename */ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, fifo_symlink }, /* symlink */ { &vop_readdir_desc, fifo_readdir }, /* readdir */ { &vop_readlink_desc, fifo_readlink }, /* readlink */ { &vop_abortop_desc, fifo_abortop }, /* abortop */ { &vop_inactive_desc, ufs_inactive }, /* inactive */ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */ { &vop_lock_desc, ufs_lock }, /* lock */ { &vop_unlock_desc, ufs_unlock }, /* unlock */ { &vop_bmap_desc, fifo_bmap }, /* bmap */ { &vop_strategy_desc, fifo_strategy }, /* strategy */ { &vop_print_desc, ufs_print }, /* print */ { &vop_islocked_desc, ufs_islocked }, /* islocked */ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, fifo_valloc }, /* valloc */ { &vop_reallocblks_desc, fifo_reallocblks }, /* reallocblks */ { &vop_vfree_desc, ffs_vfree }, /* vfree */ { &vop_truncate_desc, fifo_truncate }, /* truncate */ { &vop_update_desc, ffs_update }, /* update */ { &vop_bwrite_desc, vn_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc ffs_fifoop_opv_desc = { &ffs_fifoop_p, ffs_fifoop_entries }; #endif /* FIFO */ + +VNODEOP_SET(ffs_vnodeop_opv_desc); +VNODEOP_SET(ffs_specop_opv_desc); +#ifdef FIFO +VNODEOP_SET(ffs_fifoop_opv_desc); +#endif #ifdef DEBUG /* * Enabling cluster read/write operations. */ #include int doclusterread = 1; struct ctldebug debug11 = { "doclusterread", &doclusterread }; int doclusterwrite = 1; struct ctldebug debug12 = { "doclusterwrite", &doclusterwrite }; #else /* XXX for ufs_readwrite */ #define doclusterread 1 #define doclusterwrite 1 #endif #include /* * Synch an open file. */ /* ARGSUSED */ 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 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("ffs_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; sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1); } #ifdef DIAGNOSTIC if (vp->v_dirtyblkhd.lh_first) { vprint("ffs_fsync: dirty", vp); goto loop; } #endif } splx(s); tv = time; return (VOP_UPDATE(ap->a_vp, &tv, &tv, ap->a_waitfor == MNT_WAIT)); } Index: head/sys/ufs/lfs/lfs_vfsops.c =================================================================== --- head/sys/ufs/lfs/lfs_vfsops.c (revision 2945) +++ head/sys/ufs/lfs/lfs_vfsops.c (revision 2946) @@ -1,578 +1,580 @@ /* * Copyright (c) 1989, 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. * * @(#)lfs_vfsops.c 8.7 (Berkeley) 4/16/94 - * $Id: lfs_vfsops.c,v 1.4 1994/08/20 03:49:02 davidg Exp $ + * $Id: lfs_vfsops.c,v 1.5 1994/08/20 16:03:26 davidg Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int lfs_mountfs __P((struct vnode *, struct mount *, struct proc *)); struct vfsops lfs_vfsops = { lfs_mount, ufs_start, lfs_unmount, ufs_root, ufs_quotactl, lfs_statfs, lfs_sync, lfs_vget, lfs_fhtovp, lfs_vptofh, lfs_init, }; + +VFS_SET(lfs_vfsops, lfs, MOUNT_LFS, 0); int lfs_mountroot() { panic("lfs_mountroot"); /* XXX -- implement */ } /* * VFS Operations. * * mount system call */ int lfs_mount(mp, path, data, ndp, p) register struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { struct vnode *devvp; struct ufs_args args; struct ufsmount *ump = 0; register struct lfs *fs; /* LFS */ u_int size; int error; if (error = copyin(data, (caddr_t)&args, sizeof (struct ufs_args))) return (error); /* Until LFS can do NFS right. XXX */ if (args.export.ex_flags & MNT_EXPORTED) return (EINVAL); /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { ump = VFSTOUFS(mp); #ifdef NOTLFS /* LFS */ fs = ump->um_fs; if (fs->fs_ronly && (mp->mnt_flag & MNT_RDONLY) == 0) fs->fs_ronly = 0; #else fs = ump->um_lfs; if (fs->lfs_ronly && (mp->mnt_flag & MNT_RDONLY) == 0) fs->lfs_ronly = 0; #endif if (args.fspec == 0) { /* * Process export requests. */ return (vfs_export(mp, &ump->um_export, &args.export)); } } /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible block device. */ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); if (error = namei(ndp)) return (error); devvp = ndp->ni_vp; if (devvp->v_type != VBLK) { vrele(devvp); return (ENOTBLK); } if (major(devvp->v_rdev) >= nblkdev) { vrele(devvp); return (ENXIO); } if ((mp->mnt_flag & MNT_UPDATE) == 0) error = lfs_mountfs(devvp, mp, p); /* LFS */ else { if (devvp != ump->um_devvp) error = EINVAL; /* needs translation */ else vrele(devvp); } if (error) { vrele(devvp); return (error); } ump = VFSTOUFS(mp); fs = ump->um_lfs; /* LFS */ #ifdef NOTLFS /* LFS */ (void) copyinstr(path, fs->fs_fsmnt, sizeof(fs->fs_fsmnt) - 1, &size); bzero(fs->fs_fsmnt + size, sizeof(fs->fs_fsmnt) - size); bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN); (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void) ufs_statfs(mp, &mp->mnt_stat, p); #else (void)copyinstr(path, fs->lfs_fsmnt, sizeof(fs->lfs_fsmnt) - 1, &size); bzero(fs->lfs_fsmnt + size, sizeof(fs->lfs_fsmnt) - size); bcopy((caddr_t)fs->lfs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN); (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void) lfs_statfs(mp, &mp->mnt_stat, p); #endif return (0); } /* * Common code for mount and mountroot * LFS specific */ int lfs_mountfs(devvp, mp, p) register struct vnode *devvp; struct mount *mp; struct proc *p; { register struct lfs *fs; register struct ufsmount *ump; struct vnode *vp; struct buf *bp; struct partinfo dpart; dev_t dev; int error, i, ronly, size; /* * Disallow multiple mounts of the same device. * Disallow mounting of a device that is currently in use * (except for root, which might share swap device for miniroot). * Flush out any old buffers remaining from a previous use. */ if (error = vfs_mountedon(devvp)) return (error); if (vcount(devvp) > 1 && devvp != rootvp) return (EBUSY); if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0)) return (error); ronly = (mp->mnt_flag & MNT_RDONLY) != 0; if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p)) return (error); if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, NOCRED, p) != 0) size = DEV_BSIZE; else { size = dpart.disklab->d_secsize; #ifdef NEVER_USED dpart.part->p_fstype = FS_LFS; dpart.part->p_fsize = fs->lfs_fsize; /* frag size */ dpart.part->p_frag = fs->lfs_frag; /* frags per block */ dpart.part->p_cpg = fs->lfs_segshift; /* segment shift */ #endif } /* Don't free random space on error. */ bp = NULL; ump = NULL; /* Read in the superblock. */ if (error = bread(devvp, LFS_LABELPAD / size, LFS_SBPAD, NOCRED, &bp)) goto out; fs = (struct lfs *)bp->b_data; /* Check the basics. */ if (fs->lfs_magic != LFS_MAGIC || fs->lfs_bsize > MAXBSIZE || fs->lfs_bsize < sizeof(struct lfs)) { error = EINVAL; /* XXX needs translation */ goto out; } /* Allocate the mount structure, copy the superblock into it. */ ump = (struct ufsmount *)malloc(sizeof *ump, M_UFSMNT, M_WAITOK); fs = ump->um_lfs = malloc(sizeof(struct lfs), M_UFSMNT, M_WAITOK); bcopy(bp->b_data, fs, sizeof(struct lfs)); if (sizeof(struct lfs) < LFS_SBPAD) /* XXX why? */ bp->b_flags |= B_INVAL; brelse(bp); bp = NULL; /* Set up the I/O information */ fs->lfs_iocount = 0; /* Set up the ifile and lock aflags */ fs->lfs_doifile = 0; fs->lfs_writer = 0; fs->lfs_dirops = 0; fs->lfs_seglock = 0; /* Set the file system readonly/modify bits. */ fs->lfs_ronly = ronly; if (ronly == 0) fs->lfs_fmod = 1; /* Initialize the mount structure. */ dev = devvp->v_rdev; mp->mnt_data = (qaddr_t)ump; mp->mnt_stat.f_fsid.val[0] = (long)dev; mp->mnt_stat.f_fsid.val[1] = MOUNT_LFS; mp->mnt_flag |= MNT_LOCAL; ump->um_mountp = mp; ump->um_dev = dev; ump->um_devvp = devvp; ump->um_bptrtodb = 0; ump->um_seqinc = 1 << fs->lfs_fsbtodb; ump->um_nindir = fs->lfs_nindir; for (i = 0; i < MAXQUOTAS; i++) ump->um_quotas[i] = NULLVP; devvp->v_specflags |= SI_MOUNTEDON; /* * We use the ifile vnode for almost every operation. Instead of * retrieving it from the hash table each time we retrieve it here, * artificially increment the reference count and keep a pointer * to it in the incore copy of the superblock. */ if (error = VFS_VGET(mp, LFS_IFILE_INUM, &vp)) goto out; fs->lfs_ivnode = vp; VREF(vp); vput(vp); return (0); out: if (bp) brelse(bp); (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p); if (ump) { free(ump->um_lfs, M_UFSMNT); free(ump, M_UFSMNT); mp->mnt_data = (qaddr_t)0; } return (error); } /* * unmount system call */ int lfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { extern int doforce; register struct ufsmount *ump; register struct lfs *fs; int i, error, flags, ronly; flags = 0; if (mntflags & MNT_FORCE) { if (!doforce) return (EINVAL); flags |= FORCECLOSE; } ump = VFSTOUFS(mp); fs = ump->um_lfs; #ifdef QUOTA if (mp->mnt_flag & MNT_QUOTA) { if (error = vflush(mp, fs->lfs_ivnode, SKIPSYSTEM|flags)) return (error); for (i = 0; i < MAXQUOTAS; i++) { if (ump->um_quotas[i] == NULLVP) continue; quotaoff(p, mp, i); } /* * Here we fall through to vflush again to ensure * that we have gotten rid of all the system vnodes. */ } #endif if (error = vflush(mp, fs->lfs_ivnode, flags)) return (error); fs->lfs_clean = 1; if (error = VFS_SYNC(mp, 1, p->p_ucred, p)) return (error); if (fs->lfs_ivnode->v_dirtyblkhd.lh_first) panic("lfs_unmount: still dirty blocks on ifile vnode\n"); vrele(fs->lfs_ivnode); vgone(fs->lfs_ivnode); ronly = fs->lfs_ronly; ump->um_devvp->v_specflags &= ~SI_MOUNTEDON; error = VOP_CLOSE(ump->um_devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p); vrele(ump->um_devvp); free(fs, M_UFSMNT); free(ump, M_UFSMNT); mp->mnt_data = (qaddr_t)0; mp->mnt_flag &= ~MNT_LOCAL; return (error); } /* * Get file system statistics. */ int lfs_statfs(mp, sbp, p) struct mount *mp; register struct statfs *sbp; struct proc *p; { register struct lfs *fs; register struct ufsmount *ump; ump = VFSTOUFS(mp); fs = ump->um_lfs; if (fs->lfs_magic != LFS_MAGIC) panic("lfs_statfs: magic"); sbp->f_type = MOUNT_LFS; sbp->f_bsize = fs->lfs_bsize; sbp->f_iosize = fs->lfs_bsize; sbp->f_blocks = dbtofsb(fs,fs->lfs_dsize); sbp->f_bfree = dbtofsb(fs, fs->lfs_bfree); sbp->f_bavail = (fs->lfs_dsize * (100 - fs->lfs_minfree) / 100) - (fs->lfs_dsize - fs->lfs_bfree); sbp->f_bavail = dbtofsb(fs, sbp->f_bavail); sbp->f_files = fs->lfs_nfiles; sbp->f_ffree = sbp->f_bfree * INOPB(fs); if (sbp != &mp->mnt_stat) { bcopy((caddr_t)mp->mnt_stat.f_mntonname, (caddr_t)&sbp->f_mntonname[0], MNAMELEN); bcopy((caddr_t)mp->mnt_stat.f_mntfromname, (caddr_t)&sbp->f_mntfromname[0], MNAMELEN); } return (0); } /* * Go through the disk queues to initiate sandbagged IO; * go through the inodes to write those that have been modified; * initiate the writing of the super block if it has been modified. * * Note: we are always called with the filesystem marked `MPBUSY'. */ int lfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { int error; /* All syncs must be checkpoints until roll-forward is implemented. */ error = lfs_segwrite(mp, SEGM_CKP | (waitfor ? SEGM_SYNC : 0)); #ifdef QUOTA qsync(mp); #endif return (error); } /* * Look up an LFS dinode number to find its incore vnode. If not already * in core, read it in from the specified device. Return the inode locked. * Detection and handling of mount points must be done by the calling routine. */ int lfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { register struct lfs *fs; register struct inode *ip; struct buf *bp; struct ifile *ifp; struct vnode *vp; struct ufsmount *ump; daddr_t daddr; dev_t dev; int error; ump = VFSTOUFS(mp); dev = ump->um_dev; if ((*vpp = ufs_ihashget(dev, ino)) != NULL) return (0); /* Translate the inode number to a disk address. */ fs = ump->um_lfs; if (ino == LFS_IFILE_INUM) daddr = fs->lfs_idaddr; else { LFS_IENTRY(ifp, fs, ino, bp); daddr = ifp->if_daddr; brelse(bp); if (daddr == LFS_UNUSED_DADDR) return (ENOENT); } /* Allocate new vnode/inode. */ if (error = lfs_vcreate(mp, ino, &vp)) { *vpp = NULL; return (error); } /* * Put it onto its hash chain and lock it so that other requests for * this inode will block if they arrive while we are sleeping waiting * for old data structures to be purged or for the contents of the * disk portion of this inode to be read. */ ip = VTOI(vp); ufs_ihashins(ip); /* * XXX * This may not need to be here, logically it should go down with * the i_devvp initialization. * Ask Kirk. */ ip->i_lfs = ump->um_lfs; /* Read in the disk contents for the inode, copy into the inode. */ if (error = bread(ump->um_devvp, daddr, (int)fs->lfs_bsize, NOCRED, &bp)) { /* * The inode does not contain anything useful, so it would * be misleading to leave it on its hash chain. With mode * still zero, it will be unlinked and returned to the free * list by vput(). */ vput(vp); brelse(bp); *vpp = NULL; return (error); } ip->i_din = *lfs_ifind(fs, ino, (struct dinode *)bp->b_data); brelse(bp); /* * Initialize the vnode from the inode, check for aliases. In all * cases re-init ip, the underlying vnode/inode may have changed. */ if (error = ufs_vinit(mp, lfs_specop_p, LFS_FIFOOPS, &vp)) { vput(vp); *vpp = NULL; return (error); } /* * Finish inode initialization now that aliasing has been resolved. */ ip->i_devvp = ump->um_devvp; VREF(ip->i_devvp); *vpp = vp; return (0); } /* * File handle to vnode * * Have to be really careful about stale file handles: * - check that the inode number is valid * - call lfs_vget() to get the locked inode * - check for an unallocated inode (i_mode == 0) * - check that the given client host has export rights and return * those rights via. exflagsp and credanonp * * XXX * use ifile to see if inode is allocated instead of reading off disk * what is the relationship between my generational number and the NFS * generational number. */ int lfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) register struct mount *mp; struct fid *fhp; struct mbuf *nam; struct vnode **vpp; int *exflagsp; struct ucred **credanonp; { register struct ufid *ufhp; ufhp = (struct ufid *)fhp; if (ufhp->ufid_ino < ROOTINO) return (ESTALE); return (ufs_check_export(mp, ufhp, nam, vpp, exflagsp, credanonp)); } /* * Vnode pointer to File handle */ /* ARGSUSED */ int lfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { register struct inode *ip; register struct ufid *ufhp; ip = VTOI(vp); ufhp = (struct ufid *)fhp; ufhp->ufid_len = sizeof(struct ufid); ufhp->ufid_ino = ip->i_number; ufhp->ufid_gen = ip->i_gen; return (0); } Index: head/sys/ufs/lfs/lfs_vnops.c =================================================================== --- head/sys/ufs/lfs/lfs_vnops.c (revision 2945) +++ head/sys/ufs/lfs/lfs_vnops.c (revision 2946) @@ -1,489 +1,495 @@ /* * Copyright (c) 1986, 1989, 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. * * @(#)lfs_vnops.c 8.5 (Berkeley) 12/30/93 - * $Id$ + * $Id: lfs_vnops.c,v 1.3 1994/08/02 07:54:40 davidg Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Global vfs data structures for lfs. */ int (**lfs_vnodeop_p)(); struct vnodeopv_entry_desc lfs_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, ufs_lookup }, /* lookup */ { &vop_create_desc, ufs_create }, /* create */ { &vop_mknod_desc, ufs_mknod }, /* mknod */ { &vop_open_desc, ufs_open }, /* open */ { &vop_close_desc, lfs_close }, /* close */ { &vop_access_desc, ufs_access }, /* access */ { &vop_getattr_desc, lfs_getattr }, /* getattr */ { &vop_setattr_desc, ufs_setattr }, /* setattr */ { &vop_read_desc, lfs_read }, /* read */ { &vop_write_desc, lfs_write }, /* write */ { &vop_ioctl_desc, ufs_ioctl }, /* ioctl */ { &vop_select_desc, ufs_select }, /* select */ { &vop_mmap_desc, ufs_mmap }, /* mmap */ { &vop_fsync_desc, lfs_fsync }, /* fsync */ { &vop_seek_desc, ufs_seek }, /* seek */ { &vop_remove_desc, ufs_remove }, /* remove */ { &vop_link_desc, ufs_link }, /* link */ { &vop_rename_desc, ufs_rename }, /* rename */ { &vop_mkdir_desc, ufs_mkdir }, /* mkdir */ { &vop_rmdir_desc, ufs_rmdir }, /* rmdir */ { &vop_symlink_desc, ufs_symlink }, /* symlink */ { &vop_readdir_desc, ufs_readdir }, /* readdir */ { &vop_readlink_desc, ufs_readlink }, /* readlink */ { &vop_abortop_desc, ufs_abortop }, /* abortop */ { &vop_inactive_desc, lfs_inactive }, /* inactive */ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */ { &vop_lock_desc, ufs_lock }, /* lock */ { &vop_unlock_desc, ufs_unlock }, /* unlock */ { &vop_bmap_desc, ufs_bmap }, /* bmap */ { &vop_strategy_desc, ufs_strategy }, /* strategy */ { &vop_print_desc, ufs_print }, /* print */ { &vop_islocked_desc, ufs_islocked }, /* islocked */ { &vop_pathconf_desc, ufs_pathconf }, /* pathconf */ { &vop_advlock_desc, ufs_advlock }, /* advlock */ { &vop_blkatoff_desc, lfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, lfs_valloc }, /* valloc */ { &vop_vfree_desc, lfs_vfree }, /* vfree */ { &vop_truncate_desc, lfs_truncate }, /* truncate */ { &vop_update_desc, lfs_update }, /* update */ { &vop_bwrite_desc, lfs_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc lfs_vnodeop_opv_desc = { &lfs_vnodeop_p, lfs_vnodeop_entries }; int (**lfs_specop_p)(); struct vnodeopv_entry_desc lfs_specop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, spec_lookup }, /* lookup */ { &vop_create_desc, spec_create }, /* create */ { &vop_mknod_desc, spec_mknod }, /* mknod */ { &vop_open_desc, spec_open }, /* open */ { &vop_close_desc, ufsspec_close }, /* close */ { &vop_access_desc, ufs_access }, /* access */ { &vop_getattr_desc, lfs_getattr }, /* getattr */ { &vop_setattr_desc, ufs_setattr }, /* setattr */ { &vop_read_desc, ufsspec_read }, /* read */ { &vop_write_desc, ufsspec_write }, /* write */ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ { &vop_select_desc, spec_select }, /* select */ { &vop_mmap_desc, spec_mmap }, /* mmap */ { &vop_fsync_desc, spec_fsync }, /* fsync */ { &vop_seek_desc, spec_seek }, /* seek */ { &vop_remove_desc, spec_remove }, /* remove */ { &vop_link_desc, spec_link }, /* link */ { &vop_rename_desc, spec_rename }, /* rename */ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */ { &vop_symlink_desc, spec_symlink }, /* symlink */ { &vop_readdir_desc, spec_readdir }, /* readdir */ { &vop_readlink_desc, spec_readlink }, /* readlink */ { &vop_abortop_desc, spec_abortop }, /* abortop */ { &vop_inactive_desc, lfs_inactive }, /* inactive */ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */ { &vop_lock_desc, ufs_lock }, /* lock */ { &vop_unlock_desc, ufs_unlock }, /* unlock */ { &vop_bmap_desc, spec_bmap }, /* bmap */ { &vop_strategy_desc, spec_strategy }, /* strategy */ { &vop_print_desc, ufs_print }, /* print */ { &vop_islocked_desc, ufs_islocked }, /* islocked */ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ { &vop_advlock_desc, spec_advlock }, /* advlock */ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */ { &vop_valloc_desc, spec_valloc }, /* valloc */ { &vop_vfree_desc, lfs_vfree }, /* vfree */ { &vop_truncate_desc, spec_truncate }, /* truncate */ { &vop_update_desc, lfs_update }, /* update */ { &vop_bwrite_desc, lfs_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc lfs_specop_opv_desc = { &lfs_specop_p, lfs_specop_entries }; #ifdef FIFO int (**lfs_fifoop_p)(); struct vnodeopv_entry_desc lfs_fifoop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, fifo_lookup }, /* lookup */ { &vop_create_desc, fifo_create }, /* create */ { &vop_mknod_desc, fifo_mknod }, /* mknod */ { &vop_open_desc, fifo_open }, /* open */ { &vop_close_desc, ufsfifo_close }, /* close */ { &vop_access_desc, ufs_access }, /* access */ { &vop_getattr_desc, lfs_getattr }, /* getattr */ { &vop_setattr_desc, ufs_setattr }, /* setattr */ { &vop_read_desc, ufsfifo_read }, /* read */ { &vop_write_desc, ufsfifo_write }, /* write */ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */ { &vop_select_desc, fifo_select }, /* select */ { &vop_mmap_desc, fifo_mmap }, /* mmap */ { &vop_fsync_desc, fifo_fsync }, /* fsync */ { &vop_seek_desc, fifo_seek }, /* seek */ { &vop_remove_desc, fifo_remove }, /* remove */ { &vop_link_desc, fifo_link }, /* link */ { &vop_rename_desc, fifo_rename }, /* rename */ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */ { &vop_symlink_desc, fifo_symlink }, /* symlink */ { &vop_readdir_desc, fifo_readdir }, /* readdir */ { &vop_readlink_desc, fifo_readlink }, /* readlink */ { &vop_abortop_desc, fifo_abortop }, /* abortop */ { &vop_inactive_desc, lfs_inactive }, /* inactive */ { &vop_reclaim_desc, ufs_reclaim }, /* reclaim */ { &vop_lock_desc, ufs_lock }, /* lock */ { &vop_unlock_desc, ufs_unlock }, /* unlock */ { &vop_bmap_desc, fifo_bmap }, /* bmap */ { &vop_strategy_desc, fifo_strategy }, /* strategy */ { &vop_print_desc, ufs_print }, /* print */ { &vop_islocked_desc, ufs_islocked }, /* islocked */ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */ { &vop_advlock_desc, fifo_advlock }, /* advlock */ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */ { &vop_valloc_desc, fifo_valloc }, /* valloc */ { &vop_vfree_desc, lfs_vfree }, /* vfree */ { &vop_truncate_desc, fifo_truncate }, /* truncate */ { &vop_update_desc, lfs_update }, /* update */ { &vop_bwrite_desc, lfs_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc lfs_fifoop_opv_desc = { &lfs_fifoop_p, lfs_fifoop_entries }; #endif /* FIFO */ + +VNODEOP_SET(lfs_vnodeop_opv_desc); +VNODEOP_SET(lfs_specop_opv_desc); +#ifdef FIFO +VNODEOP_SET(lfs_fifoop_opv_desc); +#endif #define LFS_READWRITE #include #undef LFS_READWRITE /* * Synch an open file. */ /* ARGSUSED */ 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; tv = time; return (VOP_UPDATE(ap->a_vp, &tv, &tv, ap->a_waitfor == MNT_WAIT ? LFS_SYNC : 0)); } /* * 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 int lfs_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; { int ret; SET_DIROP(VTOI(ap->a_dvp)->i_lfs); MARK_VNODE(ap->a_dvp); ret = ufs_symlink(ap); SET_ENDOP(VTOI(ap->a_dvp)->i_lfs); return (ret); } int lfs_mknod(ap) struct vop_mknod_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { int ret; SET_DIROP(VTOI(ap->a_dvp)->i_lfs); MARK_VNODE(ap->a_dvp); ret = ufs_mknod(ap); SET_ENDOP(VTOI(ap->a_dvp)->i_lfs); return (ret); } int lfs_create(ap) struct vop_create_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { int ret; SET_DIROP(VTOI(ap->a_dvp)->i_lfs); MARK_VNODE(ap->a_dvp); ret = ufs_create(ap); SET_ENDOP(VTOI(ap->a_dvp)->i_lfs); return (ret); } int lfs_mkdir(ap) struct vop_mkdir_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { int ret; SET_DIROP(VTOI(ap->a_dvp)->i_lfs); MARK_VNODE(ap->a_dvp); ret = ufs_mkdir(ap); SET_ENDOP(VTOI(ap->a_dvp)->i_lfs); return (ret); } int lfs_remove(ap) struct vop_remove_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { int ret; SET_DIROP(VTOI(ap->a_dvp)->i_lfs); MARK_VNODE(ap->a_dvp); MARK_VNODE(ap->a_vp); ret = ufs_remove(ap); SET_ENDOP(VTOI(ap->a_dvp)->i_lfs); return (ret); } int lfs_rmdir(ap) struct vop_rmdir_args /* { struct vnodeop_desc *a_desc; struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { int ret; SET_DIROP(VTOI(ap->a_dvp)->i_lfs); MARK_VNODE(ap->a_dvp); MARK_VNODE(ap->a_vp); ret = ufs_rmdir(ap); SET_ENDOP(VTOI(ap->a_dvp)->i_lfs); return (ret); } int lfs_link(ap) struct vop_link_args /* { struct vnode *a_vp; struct vnode *a_tdvp; struct componentname *a_cnp; } */ *ap; { int ret; SET_DIROP(VTOI(ap->a_vp)->i_lfs); MARK_VNODE(ap->a_vp); ret = ufs_link(ap); SET_ENDOP(VTOI(ap->a_vp)->i_lfs); return (ret); } int lfs_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 ret; SET_DIROP(VTOI(ap->a_fdvp)->i_lfs); MARK_VNODE(ap->a_fdvp); MARK_VNODE(ap->a_tdvp); ret = ufs_rename(ap); SET_ENDOP(VTOI(ap->a_fdvp)->i_lfs); return (ret); } /* XXX hack to avoid calling ITIMES in getattr */ 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 = ip->i_atime; vap->va_mtime = ip->i_mtime; vap->va_ctime = ip->i_ctime; 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 */ 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; if (vp->v_usecount > 1 && !(ip->i_flag & IN_LOCKED)) { mod = ip->i_flag & IN_MODIFIED; ITIMES(ip, &time, &time); if (!mod && ip->i_flag & IN_MODIFIED) ip->i_lfs->lfs_uinodes++; } return (0); } /* * Stub inactive routine that avoid calling ufs_inactive in some cases. */ int lfs_no_inactive = 0; int lfs_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { if (lfs_no_inactive) return (0); return (ufs_inactive(ap)); } Index: head/sys/ufs/mfs/mfs_vfsops.c =================================================================== --- head/sys/ufs/mfs/mfs_vfsops.c (revision 2945) +++ head/sys/ufs/mfs/mfs_vfsops.c (revision 2946) @@ -1,310 +1,312 @@ /* * Copyright (c) 1989, 1990, 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. * * @(#)mfs_vfsops.c 8.4 (Berkeley) 4/16/94 - * $Id: mfs_vfsops.c,v 1.3 1994/08/02 07:54:43 davidg Exp $ + * $Id: mfs_vfsops.c,v 1.4 1994/08/18 22:35:55 wollman Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include caddr_t mfs_rootbase; /* address of mini-root in kernel virtual memory */ u_long mfs_rootsize; /* size of mini-root in bytes */ static int mfs_minor; /* used for building internal dev_t */ extern int (**mfs_vnodeop_p)(); /* * mfs vfs operations. */ struct vfsops mfs_vfsops = { mfs_mount, mfs_start, ffs_unmount, ufs_root, ufs_quotactl, mfs_statfs, ffs_sync, ffs_vget, ffs_fhtovp, ffs_vptofh, mfs_init, }; + +VFS_SET(mfs_vfsops, mfs, MOUNT_MFS, 0); /* * Called by main() when mfs is going to be mounted as root. * * Name is updated by mount(8) after booting. */ #define ROOTNAME "mfs_root" int mfs_mountroot() { register struct fs *fs; register struct mount *mp; struct proc *p = curproc; /* XXX */ struct ufsmount *ump; struct mfsnode *mfsp; u_int size; int error; /* * Get vnodes for swapdev and rootdev. */ if (bdevvp(swapdev, &swapdev_vp) || bdevvp(rootdev, &rootvp)) panic("mfs_mountroot: can't setup bdevvp's"); mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); bzero((char *)mp, (u_long)sizeof(struct mount)); mp->mnt_op = &mfs_vfsops; mp->mnt_flag = MNT_RDONLY; mfsp = malloc(sizeof *mfsp, M_MFSNODE, M_WAITOK); rootvp->v_data = mfsp; rootvp->v_op = mfs_vnodeop_p; rootvp->v_tag = VT_MFS; mfsp->mfs_baseoff = mfs_rootbase; mfsp->mfs_size = mfs_rootsize; mfsp->mfs_vnode = rootvp; mfsp->mfs_pid = p->p_pid; mfsp->mfs_buflist = (struct buf *)0; if (error = ffs_mountfs(rootvp, mp, p)) { free(mp, M_MOUNT); free(mfsp, M_MFSNODE); return (error); } if (error = vfs_lock(mp)) { (void)ffs_unmount(mp, 0, p); free(mp, M_MOUNT); free(mfsp, M_MFSNODE); return (error); } TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); mp->mnt_flag |= MNT_ROOTFS; mp->mnt_vnodecovered = NULLVP; ump = VFSTOUFS(mp); fs = ump->um_fs; bzero(fs->fs_fsmnt, sizeof(fs->fs_fsmnt)); fs->fs_fsmnt[0] = '/'; bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN); (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void)ffs_statfs(mp, &mp->mnt_stat, p); vfs_unlock(mp); inittodr((time_t)0); return (0); } /* * This is called early in boot to set the base address and size * of the mini-root. */ int mfs_initminiroot(base) caddr_t base; { struct fs *fs = (struct fs *)(base + SBOFF); extern int (*mountroot)(); /* check for valid super block */ if (fs->fs_magic != FS_MAGIC || fs->fs_bsize > MAXBSIZE || fs->fs_bsize < sizeof(struct fs)) return (0); mountroot = mfs_mountroot; mfs_rootbase = base; mfs_rootsize = fs->fs_fsize * fs->fs_size; rootdev = makedev(255, mfs_minor++); return (mfs_rootsize); } /* * VFS Operations. * * mount system call */ /* ARGSUSED */ int mfs_mount(mp, path, data, ndp, p) register struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { struct vnode *devvp; struct mfs_args args; struct ufsmount *ump; register struct fs *fs; register struct mfsnode *mfsp; u_int size; int flags, error; if (error = copyin(data, (caddr_t)&args, sizeof (struct mfs_args))) return (error); /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { ump = VFSTOUFS(mp); fs = ump->um_fs; if (fs->fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; if (vfs_busy(mp)) return (EBUSY); error = ffs_flushfiles(mp, flags, p); vfs_unbusy(mp); if (error) return (error); } if (fs->fs_ronly && (mp->mnt_flag & MNT_WANTRDWR)) fs->fs_ronly = 0; #ifdef EXPORTMFS if (args.fspec == 0) return (vfs_export(mp, &ump->um_export, &args.export)); #endif return (0); } error = getnewvnode(VT_MFS, (struct mount *)0, mfs_vnodeop_p, &devvp); if (error) return (error); devvp->v_type = VBLK; if (checkalias(devvp, makedev(255, mfs_minor++), (struct mount *)0)) panic("mfs_mount: dup dev"); mfsp = (struct mfsnode *)malloc(sizeof *mfsp, M_MFSNODE, M_WAITOK); devvp->v_data = mfsp; mfsp->mfs_baseoff = args.base; mfsp->mfs_size = args.size; mfsp->mfs_vnode = devvp; mfsp->mfs_pid = p->p_pid; mfsp->mfs_buflist = (struct buf *)0; if (error = ffs_mountfs(devvp, mp, p)) { mfsp->mfs_buflist = (struct buf *)-1; vrele(devvp); return (error); } ump = VFSTOUFS(mp); fs = ump->um_fs; (void) copyinstr(path, fs->fs_fsmnt, sizeof(fs->fs_fsmnt) - 1, &size); bzero(fs->fs_fsmnt + size, sizeof(fs->fs_fsmnt) - size); bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN); (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void) mfs_statfs(mp, &mp->mnt_stat, p); return (0); } int mfs_pri = PWAIT | PCATCH; /* XXX prob. temp */ /* * Used to grab the process and keep it in the kernel to service * memory filesystem I/O requests. * * Loop servicing I/O requests. * Copy the requested data into or out of the memory filesystem * address space. */ /* ARGSUSED */ int mfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { register struct vnode *vp = VFSTOUFS(mp)->um_devvp; register struct mfsnode *mfsp = VTOMFS(vp); register struct buf *bp; register caddr_t base; int error = 0; base = mfsp->mfs_baseoff; while (mfsp->mfs_buflist != (struct buf *)(-1)) { while (bp = mfsp->mfs_buflist) { mfsp->mfs_buflist = bp->b_actf; mfs_doio(bp, base); wakeup((caddr_t)bp); } /* * If a non-ignored signal is received, try to unmount. * If that fails, clear the signal (it has been "processed"), * otherwise we will loop here, as tsleep will always return * EINTR/ERESTART. */ if (error = tsleep((caddr_t)vp, mfs_pri, "mfsidl", 0)) if (dounmount(mp, 0, p) != 0) CLRSIG(p, CURSIG(p)); } return (error); } /* * Get file system statistics. */ int mfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { int error; error = ffs_statfs(mp, sbp, p); sbp->f_type = MOUNT_MFS; return (error); } Index: head/sys/ufs/mfs/mfs_vnops.c =================================================================== --- head/sys/ufs/mfs/mfs_vnops.c (revision 2945) +++ head/sys/ufs/mfs/mfs_vnops.c (revision 2946) @@ -1,355 +1,357 @@ /* * 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.3 (Berkeley) 9/21/93 - * $Id: mfs_vnops.c,v 1.3 1994/08/02 07:54:44 davidg Exp $ + * $Id: mfs_vnops.c,v 1.4 1994/09/12 11:38:28 davidg Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(hp300) && !defined(i386) && !defined(mips) && !defined(sparc) && !defined(luna68k) static int mfsmap_want; /* 1 => need kernel I/O resources */ struct map mfsmap[MFS_MAPSIZE]; extern char mfsiobuf[]; #endif /* * mfs vnode operations. */ int (**mfs_vnodeop_p)(); struct vnodeopv_entry_desc mfs_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, mfs_lookup }, /* lookup */ { &vop_create_desc, mfs_create }, /* create */ { &vop_mknod_desc, mfs_mknod }, /* mknod */ { &vop_open_desc, mfs_open }, /* open */ { &vop_close_desc, mfs_close }, /* close */ { &vop_access_desc, mfs_access }, /* access */ { &vop_getattr_desc, mfs_getattr }, /* getattr */ { &vop_setattr_desc, mfs_setattr }, /* setattr */ { &vop_read_desc, mfs_read }, /* read */ { &vop_write_desc, mfs_write }, /* write */ { &vop_ioctl_desc, mfs_ioctl }, /* ioctl */ { &vop_select_desc, mfs_select }, /* select */ { &vop_mmap_desc, mfs_mmap }, /* mmap */ { &vop_fsync_desc, spec_fsync }, /* fsync */ { &vop_seek_desc, mfs_seek }, /* seek */ { &vop_remove_desc, mfs_remove }, /* remove */ { &vop_link_desc, mfs_link }, /* link */ { &vop_rename_desc, mfs_rename }, /* rename */ { &vop_mkdir_desc, mfs_mkdir }, /* mkdir */ { &vop_rmdir_desc, mfs_rmdir }, /* rmdir */ { &vop_symlink_desc, mfs_symlink }, /* symlink */ { &vop_readdir_desc, mfs_readdir }, /* readdir */ { &vop_readlink_desc, mfs_readlink }, /* readlink */ { &vop_abortop_desc, mfs_abortop }, /* abortop */ { &vop_inactive_desc, mfs_inactive }, /* inactive */ { &vop_reclaim_desc, mfs_reclaim }, /* reclaim */ { &vop_lock_desc, mfs_lock }, /* lock */ { &vop_unlock_desc, mfs_unlock }, /* unlock */ { &vop_bmap_desc, mfs_bmap }, /* bmap */ { &vop_strategy_desc, mfs_strategy }, /* strategy */ { &vop_print_desc, mfs_print }, /* print */ { &vop_islocked_desc, mfs_islocked }, /* islocked */ { &vop_pathconf_desc, mfs_pathconf }, /* pathconf */ { &vop_advlock_desc, mfs_advlock }, /* advlock */ { &vop_blkatoff_desc, mfs_blkatoff }, /* blkatoff */ { &vop_valloc_desc, mfs_valloc }, /* valloc */ { &vop_vfree_desc, mfs_vfree }, /* vfree */ { &vop_truncate_desc, mfs_truncate }, /* truncate */ { &vop_update_desc, mfs_update }, /* update */ { &vop_bwrite_desc, mfs_bwrite }, /* bwrite */ { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; 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 */ 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_ioctl not VBLK"); /* NOTREACHED */ } return (0); } /* * Ioctl operation. */ /* ARGSUSED */ 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. */ 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 { bp->b_actf = mfsp->mfs_buflist; mfsp->mfs_buflist = bp; 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. */ int mfs_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; return (0); } /* * Memory filesystem close routine */ /* ARGSUSED */ 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 = mfsp->mfs_buflist) { mfsp->mfs_buflist = bp->b_actf; 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 || mfsp->mfs_buflist) panic("mfs_close"); /* * Send a request to the filesystem server to exit. */ mfsp->mfs_buflist = (struct buf *)(-1); wakeup((caddr_t)vp); return (0); } /* * Memory filesystem inactive routine */ /* ARGSUSED */ int mfs_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; } */ *ap; { register struct mfsnode *mfsp = VTOMFS(ap->a_vp); if (mfsp->mfs_buflist && mfsp->mfs_buflist != (struct buf *)(-1)) panic("mfs_inactive: not inactive (mfs_buflist %x)", mfsp->mfs_buflist); return (0); } /* * Reclaim a memory filesystem devvp so that it can be reused. */ int mfs_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; } */ *ap; { FREE(ap->a_vp->v_data, M_MFSNODE); ap->a_vp->v_data = NULL; return (0); } /* * Print out the contents of an mfsnode. */ 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 %d, base %d, size %d\n", mfsp->mfs_pid, mfsp->mfs_baseoff, mfsp->mfs_size); return (0); } /* * Block device bad operation */ int mfs_badop() { panic("mfs_badop called\n"); /* NOTREACHED */ } /* * Memory based filesystem initialization. */ int mfs_init() { #if !defined(hp300) && !defined(i386) && !defined(mips) && !defined(sparc) && !defined(luna68k) rminit(mfsmap, (long)MFS_MAPREG, (long)1, "mfs mapreg", MFS_MAPSIZE); #endif return (0); }