diff --git a/sys/fs/avgfs/avg.h b/sys/fs/avgfs/avg.h new file mode 100644 index 0000000..0c657eb --- /dev/null +++ b/sys/fs/avgfs/avg.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2010 Andriy Gapon + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _AVG_FS_H_ +#define _AVG_FS_H_ + +#define AVG_ROOTDIR_INO 1 +#define AVG_THEFILE_INO 2 +#define AVG_THEFILE_NAME "thefile" + +#define VFSTOAVGFS(mp) ((struct avg_mnt *)((mp)->mnt_data)) +#define VTON(vp) ((struct avg_node *)((vp)->v_data)) + +MALLOC_DECLARE(M_AVGFS); + +struct avg_mnt +{ + int im_flags; + struct mount *im_mountp; + struct g_consumer *im_cp; + struct bufobj *im_bo; + struct cdev *im_dev; + struct vnode *im_devvp; + off_t size; + int bsize; + int bshift; + int bmask; +}; + +struct avg_node +{ + struct vnode *i_vnode; + struct avg_mnt *avgmp; + ino_t hash_id; +}; + +struct ifid { + u_short ifid_len; + u_short ifid_pad; + int ifid_ino; + long ifid_start; +}; + +int avg_allocv(struct mount *, struct vnode **, struct thread *); +int avg_vget(struct mount *, ino_t, int, struct vnode **); + +#endif diff --git a/sys/fs/avgfs/avg_vfsops.c b/sys/fs/avgfs/avg_vfsops.c new file mode 100644 index 0000000..f615182 --- /dev/null +++ b/sys/fs/avgfs/avg_vfsops.c @@ -0,0 +1,383 @@ +/*- + * Copyright (c) 2010 Andriy Gapon + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "avg.h" + +MALLOC_DEFINE(M_AVGFS, "avgfs", "AVG FS"); + +static vfs_init_t avg_init; +static vfs_uninit_t avg_uninit; +static vfs_mount_t avg_mount; +static vfs_root_t avg_root; +static vfs_statfs_t avg_statfs; +static vfs_unmount_t avg_unmount; +static vfs_fhtovp_t avg_fhtovp; +/*static vfs_vget_t avg_vget;*/ + +static struct vfsops avg_vfsops = { + .vfs_fhtovp = avg_fhtovp, + .vfs_init = avg_init, + .vfs_mount = avg_mount, + .vfs_root = avg_root, + .vfs_statfs = avg_statfs, + .vfs_uninit = avg_uninit, + .vfs_unmount = avg_unmount, + .vfs_vget = avg_vget, +}; +VFS_SET(avg_vfsops, avg, VFCF_READONLY); + +MODULE_VERSION(avg, 1); + +static int avg_mountfs(struct vnode *, struct mount *); + +static int +avg_init(struct vfsconf *foo) +{ + + return (0); +} + +static int +avg_uninit(struct vfsconf *foo) +{ + + return (0); +} + +static int +avg_mount(struct mount *mp) +{ + struct vnode *devvp; /* vnode of the mount device */ + struct thread *td; + struct vfsoptlist *opts; + char *fspec; + int error, len; + struct nameidata nd, *ndp = &nd; + + td = curthread; + opts = mp->mnt_optnew; + + /* + * Unconditionally mount as read-only. + */ + MNT_ILOCK(mp); + mp->mnt_flag |= MNT_RDONLY; + MNT_IUNLOCK(mp); + + /* + * No root filesystem support. Probably not a big deal, since the + * bootloader doesn't understand AVG. + */ + if (mp->mnt_flag & MNT_ROOTFS) + return (ENOTSUP); + + fspec = NULL; + error = vfs_getopt(opts, "from", (void **)&fspec, &len); + if (!error && fspec[len - 1] != '\0') + return (EINVAL); + + if (mp->mnt_flag & MNT_UPDATE) { + return (0); + } + + /* Check that the mount device exists */ + if (fspec == NULL) + return (EINVAL); + NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec, td); + if ((error = namei(ndp))) + return (error); + NDFREE(ndp, NDF_ONLY_PNBUF); + devvp = ndp->ni_vp; + + if (vn_isdisk(devvp, &error) == 0) { + vput(devvp); + return (error); + } + + /* Check the access rights on the mount device */ + error = VOP_ACCESS(devvp, VREAD, td->td_ucred, td); + if (error) + error = priv_check(td, PRIV_VFS_MOUNT_PERM); + if (error) { + vput(devvp); + return (error); + } + + if ((error = avg_mountfs(devvp, mp))) { + vrele(devvp); + return (error); + } + + vfs_mountedfrom(mp, fspec); + return (0); +}; + +static int +avg_mountfs(struct vnode *devvp, struct mount *mp) +{ + struct buf *bp = NULL; + struct cdev *dev; + struct avg_mnt *avgmp = NULL; + int error = EINVAL; + struct g_consumer *cp; + struct bufobj *bo; + + dev = devvp->v_rdev; + dev_ref(dev); + DROP_GIANT(); + g_topology_lock(); + error = g_vfs_open(devvp, &cp, "avg", 0); + g_topology_unlock(); + PICKUP_GIANT(); + VOP_UNLOCK(devvp, 0); + if (error) + goto bail; + + bo = &devvp->v_bufobj; + + if (devvp->v_rdev->si_iosize_max != 0) + mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max; + if (mp->mnt_iosize_max > MAXPHYS) + mp->mnt_iosize_max = MAXPHYS; + + avgmp = malloc(sizeof(struct avg_mnt), M_AVGFS, + M_WAITOK | M_ZERO); + + mp->mnt_data = avgmp; + mp->mnt_stat.f_fsid.val[0] = dev2udev(devvp->v_rdev); + mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; + MNT_ILOCK(mp); + mp->mnt_flag |= MNT_LOCAL; + mp->mnt_kern_flag |= MNTK_MPSAFE | MNTK_LOOKUP_SHARED | + MNTK_EXTENDED_SHARED; + MNT_IUNLOCK(mp); + avgmp->im_mountp = mp; + avgmp->im_dev = dev; + avgmp->im_devvp = devvp; + avgmp->im_cp = cp; + avgmp->im_bo = bo; + avgmp->size = cp->provider->mediasize; + avgmp->bsize = cp->provider->sectorsize; + avgmp->bmask = avgmp->bsize - 1; + avgmp->bshift = ffs(avgmp->bsize) - 1; + + return (0); + +bail: + if (avgmp != NULL) + free(avgmp, M_AVGFS); + if (bp != NULL) + brelse(bp); + if (cp != NULL) { + DROP_GIANT(); + g_topology_lock(); + g_vfs_close(cp); + g_topology_unlock(); + PICKUP_GIANT(); + } + dev_rel(dev); + return error; +}; + +static int +avg_unmount(struct mount *mp, int mntflags) +{ + struct avg_mnt *avgmp; + int error, flags = 0; + + avgmp = VFSTOAVGFS(mp); + + if (mntflags & MNT_FORCE) + flags |= FORCECLOSE; + + if ((error = vflush(mp, 0, flags, curthread))) + return (error); + + DROP_GIANT(); + g_topology_lock(); + g_vfs_close(avgmp->im_cp); + g_topology_unlock(); + PICKUP_GIANT(); + vrele(avgmp->im_devvp); + dev_rel(avgmp->im_dev); + + free(avgmp, M_AVGFS); + mp->mnt_data = NULL; + MNT_ILOCK(mp); + mp->mnt_flag &= ~MNT_LOCAL; + MNT_IUNLOCK(mp); + + return (0); +} + +static int +avg_root(struct mount *mp, int flags, struct vnode **vpp) +{ + ino_t id; + + id = 1; + return (avg_vget(mp, id, flags, vpp)); +} + +static int +avg_statfs(struct mount *mp, struct statfs *sbp) +{ + struct avg_mnt *avgmp; + + avgmp = VFSTOAVGFS(mp); + + sbp->f_bsize = avgmp->bsize; + sbp->f_iosize = avgmp->bsize; + sbp->f_blocks = avgmp->size >> avgmp->bshift; + sbp->f_bfree = 0; + sbp->f_bavail = 0; + sbp->f_files = 0; + sbp->f_ffree = 0; + return 0; +} + +int +avg_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) +{ + struct avg_mnt *avgmp; + struct thread *td; + struct vnode *vp; + struct avg_node *unode; + int error; + + error = vfs_hash_get(mp, ino, flags, curthread, vpp, NULL, NULL); + if (error || *vpp != NULL) + return (error); + + /* + * We must promote to an exclusive lock for vnode creation. This + * can happen if lookup is passed LOCKSHARED. + */ + if ((flags & LK_TYPE_MASK) == LK_SHARED) { + flags &= ~LK_TYPE_MASK; + flags |= LK_EXCLUSIVE; + } + + /* + * We do not lock vnode creation as it is believed to be too + * expensive for such rare case as simultaneous creation of vnode + * for same ino by different processes. We just allow them to race + * and check later to decide who wins. Let the race begin! + */ + td = curthread; + if ((error = avg_allocv(mp, &vp, td))) { + printf("Error from udf_allocv\n"); + return (error); + } + + avgmp = VFSTOAVGFS(mp); + unode = malloc(sizeof(struct avg_node), M_AVGFS, M_WAITOK | M_ZERO); + unode->i_vnode = vp; + unode->hash_id = ino; + unode->avgmp = avgmp; + vp->v_data = unode; + + lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL); + error = insmntque(vp, mp); + if (error != 0) + return (error); + + error = vfs_hash_insert(vp, ino, flags, td, vpp, NULL, NULL); + if (error || *vpp != NULL) + return (error); + + switch (ino) { + default: + vp->v_type = VBAD; + break; + case AVG_ROOTDIR_INO: + vp->v_type = VDIR; + break; + case AVG_THEFILE_INO: + vp->v_type = VREG; + break; + } + + if (vp->v_type != VFIFO) + VN_LOCK_ASHARE(vp); + + if (ino == AVG_ROOTDIR_INO) + vp->v_vflag |= VV_ROOT; + + *vpp = vp; + + return (0); +} + +static int +avg_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) +{ + struct ifid *ifhp; + struct vnode *nvp; + off_t fsize; + int error; + + ifhp = (struct ifid *)fhp; + + if ((error = VFS_VGET(mp, ifhp->ifid_ino, LK_EXCLUSIVE, &nvp)) != 0) { + *vpp = NULLVP; + return (error); + } + + fsize = VTON(nvp)->avgmp->size; + + *vpp = nvp; + vnode_create_vobject(*vpp, fsize, curthread); + return (0); +} + diff --git a/sys/fs/avgfs/avg_vnops.c b/sys/fs/avgfs/avg_vnops.c new file mode 100644 index 0000000..fe78a7f --- /dev/null +++ b/sys/fs/avgfs/avg_vnops.c @@ -0,0 +1,604 @@ +/*- + * Copyright (c) 2010 Andriy Gapon + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "avg.h" + +static vop_access_t avg_access; +static vop_getattr_t avg_getattr; +static vop_open_t avg_open; +static vop_ioctl_t avg_ioctl; +static vop_pathconf_t avg_pathconf; +static vop_print_t avg_print; +static vop_read_t avg_read; +static vop_readdir_t avg_readdir; +static vop_setattr_t avg_setattr; +static vop_strategy_t avg_strategy; +static vop_bmap_t avg_bmap; +static vop_cachedlookup_t avg_lookup; +static vop_reclaim_t avg_reclaim; +static vop_vptofh_t avg_vptofh; + +static struct vop_vector avg_vnodeops = { + .vop_default = &default_vnodeops, + + .vop_access = avg_access, + .vop_bmap = avg_bmap, + .vop_cachedlookup = avg_lookup, + .vop_getattr = avg_getattr, + .vop_ioctl = avg_ioctl, + .vop_lookup = vfs_cache_lookup, + .vop_open = avg_open, + .vop_pathconf = avg_pathconf, + .vop_print = avg_print, + .vop_read = avg_read, + .vop_readdir = avg_readdir, + .vop_reclaim = avg_reclaim, + .vop_setattr = avg_setattr, + .vop_strategy = avg_strategy, + .vop_vptofh = avg_vptofh, +}; + + +int +avg_allocv(struct mount *mp, struct vnode **vpp, struct thread *td) +{ + int error; + struct vnode *vp; + + error = getnewvnode("avg", mp, &avg_vnodeops, &vp); + if (error) { + printf("avg_allocv: failed to allocate new vnode\n"); + return (error); + } + + *vpp = vp; + return (0); +} + +static int +avg_access(struct vop_access_args *a) +{ + struct vnode *vp; + accmode_t accmode; + + vp = a->a_vp; + accmode = a->a_accmode; + + if (accmode & VWRITE) { + switch (vp->v_type) { + case VDIR: + case VLNK: + case VREG: + return (EROFS); + /* NOT REACHED */ + default: + break; + } + } + + return (vaccess(vp->v_type, 0444, 0, 0, + accmode, a->a_cred, NULL)); +} + +static int +avg_open(struct vop_open_args *ap) { + struct avg_node *np = VTON(ap->a_vp); + off_t fsize; + + fsize = np->avgmp->size; + vnode_create_vobject(ap->a_vp, fsize, ap->a_td); + return 0; +} + + +static int +avg_getattr(struct vop_getattr_args *a) +{ + struct vnode *vp; + struct avg_node *node; + struct vattr *vap; + struct timespec ts; + + ts.tv_sec = 0; + + vp = a->a_vp; + vap = a->a_vap; + node = VTON(vp); + + vap->va_fsid = dev2udev(node->avgmp->im_dev); + vap->va_fileid = node->hash_id; + vap->va_uid = 0; + vap->va_gid = 0; + vap->va_atime.tv_sec = VNOVAL; + vap->va_atime.tv_nsec = VNOVAL; + vap->va_mtime.tv_sec = VNOVAL; + vap->va_mtime.tv_nsec = VNOVAL; + vap->va_ctime.tv_sec = VNOVAL; + vap->va_ctime.tv_nsec = VNOVAL; + vap->va_rdev = NODEV; + if (vp->v_type & VDIR) { + vap->va_size = 40; /*XXX*/ + vap->va_nlink = 2; + vap->va_mode = 0555; + } else { + vap->va_size = node->avgmp->size; + vap->va_nlink = 1; + vap->va_mode = 0444; + } + vap->va_flags = 0; + vap->va_gen = 1; + vap->va_blocksize = node->avgmp->bsize; + vap->va_bytes = vap->va_size; + vap->va_type = vp->v_type; + vap->va_filerev = 0; + return (0); +} + +static int +avg_setattr(struct vop_setattr_args *a) +{ + struct vnode *vp; + struct vattr *vap; + + vp = a->a_vp; + vap = a->a_vap; + if (vap->va_flags != (u_long)VNOVAL || vap->va_uid != (uid_t)VNOVAL || + vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || + vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) + return (EROFS); + if (vap->va_size != (u_quad_t)VNOVAL) { + switch (vp->v_type) { + case VDIR: + return (EISDIR); + case VLNK: + case VREG: + return (EROFS); + case VCHR: + case VBLK: + case VSOCK: + case VFIFO: + case VNON: + case VBAD: + case VMARKER: + return (0); + } + } + return (0); +} + +/* + * File specific ioctls. + */ +static int +avg_ioctl(struct vop_ioctl_args *a) +{ + printf("%s called\n", __func__); + return (ENOTTY); +} + +/* + * I'm not sure that this has much value in a read-only filesystem, but + * cd9660 has it too. + */ +static int +avg_pathconf(struct vop_pathconf_args *a) +{ + + switch (a->a_name) { + case _PC_LINK_MAX: + *a->a_retval = 65535; + return (0); + case _PC_NAME_MAX: + *a->a_retval = NAME_MAX; + return (0); + case _PC_PATH_MAX: + *a->a_retval = PATH_MAX; + return (0); + case _PC_NO_TRUNC: + *a->a_retval = 1; + return (0); + default: + return (EINVAL); + } +} + +static int +avg_print(struct vop_print_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct avg_node *node; + + node = VTON(vp); + printf(" ino %lu, on dev %s", (u_long)node->hash_id, + devtoname(node->avgmp->im_dev)); + printf("\n"); + return (0); +} + +#define lblkno(avgmp, loc) ((loc) >> (avgmp)->bshift) +#define blkoff(avgmp, loc) ((loc) & (avgmp)->bmask) +#define lblktosize(avgmp, blk) ((blk) << (avgmp)->bshift) + +static int +avg_read(struct vop_read_args *ap) +{ + static const int clustersize = MAXBSIZE; + + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + struct avg_node *node = VTON(vp); + struct avg_mnt *avgmp; + struct buf *bp; + daddr_t lbn; + off_t diff, fsize; + int error = 0; + long size, n, on; + + if (uio->uio_resid == 0) + return (0); + if (uio->uio_offset < 0) + return (EINVAL); + + if (vp->v_type == VCHR || vp->v_type == VBLK) + return (EOPNOTSUPP); + if (vp->v_type != VREG) + return (EINVAL); + + avgmp = node->avgmp; + fsize = avgmp->size; + + do { + lbn = lblkno(avgmp, uio->uio_offset); + on = blkoff(avgmp, uio->uio_offset); + n = min((u_int)(clustersize - on), uio->uio_resid); + diff = fsize - uio->uio_offset; + if (diff <= 0) + return (0); + if (diff < n) + n = diff; +// size = avgmp->bsize; + size = (n + avgmp->bmask) & ~avgmp->bmask; +#if 0 + error = bread(vp, lbn, size, NOCRED, &bp); +#else + error = bread(avgmp->im_devvp, lbn << (avgmp->bshift - DEV_BSHIFT), size, NOCRED, &bp); +#endif + n = min(n, size - bp->b_resid); + if (error) { + brelse(bp); + return (error); + } + error = uiomove(bp->b_data + on, (int)n, uio); + brelse(bp); + } while (error == 0 && uio->uio_resid > 0 && n != 0); + return (error); +} + + +struct avg_uiodir { + struct dirent *dirent; + u_long *cookies; + int ncookies; + int acookies; + int eofflag; +}; + +static int +avg_uiodir(struct avg_uiodir *uiodir, int de_size, struct uio *uio, long cookie) +{ + if (uiodir->cookies != NULL) { + if (++uiodir->acookies > uiodir->ncookies) { + uiodir->eofflag = 0; + return (-1); + } + *uiodir->cookies++ = cookie; + } + + if (uio->uio_resid < de_size) { + uiodir->eofflag = 0; + return (-1); + } + + return (uiomove(uiodir->dirent, de_size, uio)); +} + +static int +avg_readdir(struct vop_readdir_args *a) +{ + struct vnode *vp; + struct uio *uio; + struct dirent dir; + struct avg_node *node; + struct avg_mnt *avgmp; + struct avg_uiodir uiodir; + u_long *cookies = NULL; + int ncookies; + int error = 0; + + vp = a->a_vp; + uio = a->a_uio; + node = VTON(vp); + avgmp = node->avgmp; + uiodir.eofflag = 1; + + if (a->a_ncookies != NULL) { + /* + * Guess how many entries are needed. If we run out, this + * function will be called again and thing will pick up were + * it left off. + */ + ncookies = uio->uio_resid / 8; + cookies = malloc(sizeof(u_long) * ncookies, + M_TEMP, M_WAITOK); + if (cookies == NULL) + return (ENOMEM); + uiodir.ncookies = ncookies; + uiodir.cookies = cookies; + uiodir.acookies = 0; + } else { + uiodir.cookies = NULL; + } + + /* Do up the '.' and '..' entries. Dummy values are + * used for the cookies since the offset here is + * usually zero, and NFS doesn't like that value + */ + if (uio->uio_offset == 0) { + dir.d_fileno = node->hash_id; /* AVG_ROOTDIR_INO */ + dir.d_type = DT_DIR; + dir.d_name[0] = '.'; + dir.d_name[1] = '\0'; + dir.d_namlen = 1; + dir.d_reclen = GENERIC_DIRSIZ(&dir); + uiodir.dirent = &dir; + error = avg_uiodir(&uiodir, dir.d_reclen, uio, 1); + if (error) + goto finished; + + dir.d_fileno = node->hash_id; /* AVG_ROOTDIR_INO */ + dir.d_type = DT_DIR; + dir.d_name[0] = '.'; + dir.d_name[1] = '.'; + dir.d_name[2] = '\0'; + dir.d_namlen = 2; + dir.d_reclen = GENERIC_DIRSIZ(&dir); + uiodir.dirent = &dir; + error = avg_uiodir(&uiodir, dir.d_reclen, uio, 2); + if (error) + goto finished; + + strcpy(&dir.d_name[0], AVG_THEFILE_NAME); + dir.d_namlen = strlen(AVG_THEFILE_NAME); + dir.d_fileno = AVG_THEFILE_INO; + dir.d_type = DT_REG; + dir.d_reclen = GENERIC_DIRSIZ(&dir); + uiodir.dirent = &dir; + error = avg_uiodir(&uiodir, dir.d_reclen, uio, 3); + if (error) + goto finished; + } + +finished: + + /* tell the calling layer whether we need to be called again */ + *a->a_eofflag = uiodir.eofflag; + + if (error < 0) + error = 0; + + if (a->a_ncookies != NULL) { + if (error) + free(cookies, M_TEMP); + else { + *a->a_ncookies = uiodir.acookies; + *a->a_cookies = cookies; + } + } + + return (error); +} + +static int +avg_strategy(struct vop_strategy_args *a) +{ + struct buf *bp; + struct vnode *vp; + struct avg_node *node; + struct bufobj *bo; + + bp = a->a_bp; + vp = a->a_vp; + node = VTON(vp); + + if (bp->b_blkno == bp->b_lblkno) { + bp->b_blkno = bp->b_lblkno << (node->avgmp->bshift - DEV_BSHIFT); + } + bo = node->avgmp->im_bo; + bp->b_iooffset = dbtob(bp->b_blkno); + BO_STRATEGY(bo, bp); + return (0); +} + +static int +avg_bmap(struct vop_bmap_args *a) +{ + struct avg_node *node; + + node = VTON(a->a_vp); + + if (a->a_bop != NULL) + *a->a_bop = &node->avgmp->im_devvp->v_bufobj; + if (a->a_bnp == NULL) + return (0); + if (a->a_runb) + *a->a_runb = 0; + + /* Translate logical to physical sector number */ + *a->a_bnp = a->a_bn << (node->avgmp->bshift - DEV_BSHIFT); + + if (a->a_runp) + *a->a_runp = 0; + if (a->a_runb) + *a->a_runb = 0; + + return (0); +} + +/* + * The all powerful VOP_LOOKUP(). + */ +static int +avg_lookup(struct vop_cachedlookup_args *a) +{ + struct vnode *dvp; + struct vnode *tdp = NULL; + struct vnode **vpp = a->a_vpp; + struct avg_node *node; + struct avg_mnt *avgmp; + u_long nameiop; + u_long flags; + char *nameptr; + long namelen; + ino_t id = 0; + int offset, error = 0; + int lkflags, ltype; + + dvp = a->a_dvp; + node = VTON(dvp); + avgmp = node->avgmp; + nameiop = a->a_cnp->cn_nameiop; + flags = a->a_cnp->cn_flags; + lkflags = a->a_cnp->cn_lkflags; + nameptr = a->a_cnp->cn_nameptr; + namelen = a->a_cnp->cn_namelen; + + offset = 0; + + if (strcmp(nameptr, AVG_THEFILE_NAME) == 0) + id = AVG_THEFILE_INO; + else if (flags & ISDOTDOT) + id = AVG_ROOTDIR_INO; + + /* Did we have a match? */ + if (id) { + if (flags & ISDOTDOT) { + error = vn_vget_ino(dvp, id, lkflags, &tdp); + } else if (node->hash_id == id) { + VREF(dvp); /* we want ourself, ie "." */ + /* + * When we lookup "." we still can be asked to lock it + * differently. + */ + ltype = lkflags & LK_TYPE_MASK; + if (ltype != VOP_ISLOCKED(dvp)) { + if (ltype == LK_EXCLUSIVE) + vn_lock(dvp, LK_UPGRADE | LK_RETRY); + else /* if (ltype == LK_SHARED) */ + vn_lock(dvp, LK_DOWNGRADE | LK_RETRY); + } + tdp = dvp; + } else + error = avg_vget(avgmp->im_mountp, id, lkflags, &tdp); + if (!error) { + *vpp = tdp; + /* Put this entry in the cache */ + if (flags & MAKEENTRY) + cache_enter(dvp, *vpp, a->a_cnp); + } + } else { + /* Enter name into cache as non-existant */ + if (flags & MAKEENTRY) + cache_enter(dvp, *vpp, a->a_cnp); + + if ((flags & ISLASTCN) && + (nameiop == CREATE || nameiop == RENAME)) { + error = EROFS; + } else { + error = ENOENT; + } + } + + return (error); +} + +static int +avg_reclaim(struct vop_reclaim_args *a) +{ + struct vnode *vp; + struct avg_node *unode; + + vp = a->a_vp; + unode = VTON(vp); + + /* + * Destroy the vm object and flush associated pages. + */ + vnode_destroy_vobject(vp); + + if (unode != NULL) { + vfs_hash_remove(vp); + free(unode, M_AVGFS); + vp->v_data = NULL; + } + + return (0); +} + +static int +avg_vptofh(struct vop_vptofh_args *a) +{ + struct avg_node *node; + struct ifid *ifhp; + + node = VTON(a->a_vp); + ifhp = (struct ifid *)a->a_fhp; + ifhp->ifid_len = sizeof(struct ifid); + ifhp->ifid_ino = node->hash_id; + + return (0); +} + diff --git a/sys/modules/avgfs/Makefile b/sys/modules/avgfs/Makefile new file mode 100644 index 0000000..32977f7 --- /dev/null +++ b/sys/modules/avgfs/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../fs/avgfs + +KMOD= avgfs + +SRCS= avg_vfsops.c avg_vnops.c +SRCS+= vnode_if.h + +.include