diff --git a/sys/fs/specfs/spec_vnops.c b/sys/fs/specfs/spec_vnops.c index 79a924fbf0f9..51334960757d 100644 --- a/sys/fs/specfs/spec_vnops.c +++ b/sys/fs/specfs/spec_vnops.c @@ -1,821 +1,640 @@ /* * Copyright (c) 1989, 1993, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)spec_vnops.c 8.14 (Berkeley) 5/21/95 * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int spec_advlock(struct vop_advlock_args *); static int spec_close(struct vop_close_args *); static int spec_fsync(struct vop_fsync_args *); -static int spec_getpages(struct vop_getpages_args *); static int spec_ioctl(struct vop_ioctl_args *); static int spec_kqfilter(struct vop_kqfilter_args *); static int spec_open(struct vop_open_args *); static int spec_poll(struct vop_poll_args *); static int spec_print(struct vop_print_args *); static int spec_read(struct vop_read_args *); static int spec_specstrategy(struct vop_specstrategy_args *); static int spec_write(struct vop_write_args *); vop_t **spec_vnodeop_p; static struct vnodeopv_entry_desc spec_vnodeop_entries[] = { { &vop_default_desc, (vop_t *) vop_defaultop }, { &vop_access_desc, (vop_t *) vop_ebadf }, { &vop_advlock_desc, (vop_t *) spec_advlock }, { &vop_bmap_desc, (vop_t *) vop_panic }, { &vop_close_desc, (vop_t *) spec_close }, { &vop_create_desc, (vop_t *) vop_panic }, { &vop_fsync_desc, (vop_t *) spec_fsync }, - { &vop_getpages_desc, (vop_t *) spec_getpages }, { &vop_getwritemount_desc, (vop_t *) vop_stdgetwritemount }, { &vop_ioctl_desc, (vop_t *) spec_ioctl }, { &vop_kqfilter_desc, (vop_t *) spec_kqfilter }, { &vop_lease_desc, (vop_t *) vop_null }, { &vop_link_desc, (vop_t *) vop_panic }, { &vop_mkdir_desc, (vop_t *) vop_panic }, { &vop_mknod_desc, (vop_t *) vop_panic }, { &vop_open_desc, (vop_t *) spec_open }, { &vop_pathconf_desc, (vop_t *) vop_stdpathconf }, { &vop_poll_desc, (vop_t *) spec_poll }, { &vop_print_desc, (vop_t *) spec_print }, { &vop_read_desc, (vop_t *) spec_read }, { &vop_readdir_desc, (vop_t *) vop_panic }, { &vop_readlink_desc, (vop_t *) vop_panic }, { &vop_reallocblks_desc, (vop_t *) vop_panic }, { &vop_reclaim_desc, (vop_t *) vop_null }, { &vop_remove_desc, (vop_t *) vop_panic }, { &vop_rename_desc, (vop_t *) vop_panic }, { &vop_rmdir_desc, (vop_t *) vop_panic }, { &vop_setattr_desc, (vop_t *) vop_ebadf }, { &vop_specstrategy_desc, (vop_t *) spec_specstrategy }, { &vop_strategy_desc, (vop_t *) vop_panic }, { &vop_symlink_desc, (vop_t *) vop_panic }, { &vop_write_desc, (vop_t *) spec_write }, { NULL, NULL } }; static struct vnodeopv_desc spec_vnodeop_opv_desc = { &spec_vnodeop_p, spec_vnodeop_entries }; VNODEOP_SET(spec_vnodeop_opv_desc); int spec_vnoperate(ap) struct vop_generic_args /* { struct vnodeop_desc *a_desc; } */ *ap; { return (VOCALL(spec_vnodeop_p, ap->a_desc->vdesc_offset, ap)); } /* * Open a special file. */ /* ARGSUSED */ static int spec_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct thread *a_td; } */ *ap; { struct thread *td = ap->a_td; struct vnode *vp = ap->a_vp; struct cdev *dev = vp->v_rdev; int error; struct cdevsw *dsw; if (vp->v_type == VBLK) return (ENXIO); /* Don't allow open if fs is mounted -nodev. */ if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV)) return (ENXIO); if (dev == NULL) return (ENXIO); dsw = devsw(dev); if (dsw == NULL || dsw->d_open == NULL) return (ENXIO); /* Make this field valid before any I/O in d_open. */ if (dev->si_iosize_max == 0) dev->si_iosize_max = DFLTPHYS; /* * XXX: Disks get special billing here, but it is mostly wrong. * XXX: Disk partitions can overlap and the real checks should * XXX: take this into account, and consequently they need to * XXX: live in the disk slice code. Some checks do. */ if (vn_isdisk(vp, NULL) && ap->a_cred != FSCRED && (ap->a_mode & FWRITE)) { /* * Never allow opens for write if the disk is mounted R/W. */ if (vp->v_rdev->si_mountpoint != NULL && !(vp->v_rdev->si_mountpoint->mnt_flag & MNT_RDONLY)) return (EBUSY); /* * When running in secure mode, do not allow opens * for writing if the disk is mounted. */ error = securelevel_ge(td->td_ucred, 1); if (error && vfs_mountedon(vp)) return (error); /* * When running in very secure mode, do not allow * opens for writing of any disks. */ error = securelevel_ge(td->td_ucred, 2); if (error) return (error); } /* XXX: Special casing of ttys for deadfs. Probably redundant. */ if (dsw->d_flags & D_TTY) vp->v_vflag |= VV_ISTTY; VOP_UNLOCK(vp, 0, td); dev_ref(dev); cdevsw_ref(dsw); if(!(dsw->d_flags & D_NEEDGIANT)) { DROP_GIANT(); if (dsw->d_fdopen != NULL) error = dsw->d_fdopen(dev, ap->a_mode, td, ap->a_fdidx); else error = dsw->d_open(dev, ap->a_mode, S_IFCHR, td); PICKUP_GIANT(); } else if (dsw->d_fdopen != NULL) error = dsw->d_fdopen(dev, ap->a_mode, td, ap->a_fdidx); else error = dsw->d_open(dev, ap->a_mode, S_IFCHR, td); cdevsw_rel(dsw); if (error != 0) dev_rel(dev); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (error) return (error); if (dsw->d_flags & D_TTY) { if (dev->si_tty) { struct tty *tp; tp = dev->si_tty; if (!tp->t_stop) { printf("Warning:%s: no t_stop, using nottystop\n", devtoname(dev)); tp->t_stop = nottystop; } } } if (vn_isdisk(vp, NULL)) { if (!dev->si_bsize_phys) dev->si_bsize_phys = DEV_BSIZE; } return (error); } /* * Vnode op for read */ /* ARGSUSED */ static int spec_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; struct thread *td; struct uio *uio; struct cdev *dev; int error, resid; struct cdevsw *dsw; vp = ap->a_vp; dev = vp->v_rdev; uio = ap->a_uio; td = uio->uio_td; resid = uio->uio_resid; if (resid == 0) return (0); dsw = devsw(dev); VOP_UNLOCK(vp, 0, td); KASSERT(dev->si_refcount > 0, ("specread() on un-referenced struct cdev *(%s)", devtoname(dev))); cdevsw_ref(dsw); if (!(dsw->d_flags & D_NEEDGIANT)) { DROP_GIANT(); error = dsw->d_read(dev, uio, ap->a_ioflag); PICKUP_GIANT(); } else error = dsw->d_read(dev, uio, ap->a_ioflag); cdevsw_rel(dsw); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (uio->uio_resid != resid || (error == 0 && resid != 0)) vfs_timestamp(&dev->si_atime); return (error); } /* * Vnode op for write */ /* ARGSUSED */ static int spec_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; struct thread *td; struct uio *uio; struct cdev *dev; int error, resid; struct cdevsw *dsw; vp = ap->a_vp; dev = vp->v_rdev; dsw = devsw(dev); uio = ap->a_uio; td = uio->uio_td; resid = uio->uio_resid; VOP_UNLOCK(vp, 0, td); KASSERT(dev->si_refcount > 0, ("spec_write() on un-referenced struct cdev *(%s)", devtoname(dev))); cdevsw_ref(dsw); if (!(dsw->d_flags & D_NEEDGIANT)) { DROP_GIANT(); error = dsw->d_write(dev, uio, ap->a_ioflag); PICKUP_GIANT(); } else error = dsw->d_write(dev, uio, ap->a_ioflag); cdevsw_rel(dsw); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); if (uio->uio_resid != resid || (error == 0 && resid != 0)) { vfs_timestamp(&dev->si_ctime); dev->si_mtime = dev->si_ctime; } return (error); } /* * Device ioctl operation. */ /* ARGSUSED */ static int spec_ioctl(ap) struct vop_ioctl_args /* { struct vnode *a_vp; u_long a_command; caddr_t a_data; int a_fflag; struct ucred *a_cred; struct thread *a_td; } */ *ap; { struct cdev *dev; int error; struct cdevsw *dsw; dev = ap->a_vp->v_rdev; dsw = devsw(dev); KASSERT(dev->si_refcount > 0, ("spec_ioctl() on un-referenced struct cdev *(%s)", devtoname(dev))); cdevsw_ref(dsw); if (!(dsw->d_flags & D_NEEDGIANT)) { DROP_GIANT(); error = dsw->d_ioctl(dev, ap->a_command, ap->a_data, ap->a_fflag, ap->a_td); PICKUP_GIANT(); } else error = dsw->d_ioctl(dev, ap->a_command, ap->a_data, ap->a_fflag, ap->a_td); cdevsw_rel(dsw); if (error == ENOIOCTL) error = ENOTTY; return (error); } /* ARGSUSED */ static int spec_poll(ap) struct vop_poll_args /* { struct vnode *a_vp; int a_events; struct ucred *a_cred; struct thread *a_td; } */ *ap; { struct cdev *dev; struct cdevsw *dsw; int error; dev = ap->a_vp->v_rdev; dsw = devsw(dev); KASSERT(dev->si_refcount > 0, ("spec_poll() on un-referenced struct cdev *(%s)", devtoname(dev))); cdevsw_ref(dsw); if (!(dsw->d_flags & D_NEEDGIANT)) { /* XXX: not yet DROP_GIANT(); */ error = dsw->d_poll(dev, ap->a_events, ap->a_td); /* XXX: not yet PICKUP_GIANT(); */ } else error = dsw->d_poll(dev, ap->a_events, ap->a_td); cdevsw_rel(dsw); return(error); } /* ARGSUSED */ static int spec_kqfilter(ap) struct vop_kqfilter_args /* { struct vnode *a_vp; struct knote *a_kn; } */ *ap; { struct cdev *dev; struct cdevsw *dsw; int error; dev = ap->a_vp->v_rdev; dsw = devsw(dev); KASSERT(dev->si_refcount > 0, ("spec_kqfilter() on un-referenced struct cdev *(%s)", devtoname(dev))); cdevsw_ref(dsw); if (!(dsw->d_flags & D_NEEDGIANT)) { DROP_GIANT(); error = dsw->d_kqfilter(dev, ap->a_kn); PICKUP_GIANT(); } else error = dsw->d_kqfilter(dev, ap->a_kn); cdevsw_rel(dsw); return (error); } /* * Synch buffers associated with a block device */ /* ARGSUSED */ static int spec_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct thread *a_td; } */ *ap; { if (!vn_isdisk(ap->a_vp, NULL)) return (0); return (vop_stdfsync(ap)); } /* * Mutex to use when delaying niced I/O bound processes in spec_strategy(). */ static struct mtx strategy_mtx; static void strategy_init(void) { mtx_init(&strategy_mtx, "strategy", NULL, MTX_DEF); } SYSINIT(strategy, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, strategy_init, NULL) static int doslowdown = 0; SYSCTL_INT(_debug, OID_AUTO, doslowdown, CTLFLAG_RW, &doslowdown, 0, ""); /* * Just call the device strategy routine */ static int spec_xstrategy(struct vnode *vp, struct buf *bp) { struct mount *mp; struct cdevsw *dsw; struct thread *td = curthread; KASSERT(bp->b_iocmd == BIO_READ || bp->b_iocmd == BIO_WRITE, ("Wrong b_iocmd buf=%p cmd=%d", bp, bp->b_iocmd)); /* * Slow down disk requests for niced processes. */ if (doslowdown && td && td->td_proc->p_nice > 0) { mtx_lock(&strategy_mtx); msleep(&strategy_mtx, &strategy_mtx, PPAUSE | PCATCH | PDROP, "ioslow", td->td_proc->p_nice); } /* * Collect statistics on synchronous and asynchronous read * and write counts for disks that have associated filesystems. */ if (vn_isdisk(vp, NULL) && (mp = vp->v_rdev->si_mountpoint) != NULL) { if (bp->b_iocmd == BIO_WRITE) { if (bp->b_lock.lk_lockholder == LK_KERNPROC) mp->mnt_stat.f_asyncwrites++; else mp->mnt_stat.f_syncwrites++; } else { if (bp->b_lock.lk_lockholder == LK_KERNPROC) mp->mnt_stat.f_asyncreads++; else mp->mnt_stat.f_syncreads++; } } dsw = devsw(bp->b_dev); if (dsw == NULL) { bp->b_error = ENXIO; bp->b_ioflags |= BIO_ERROR; bufdone(bp); return (0); } KASSERT(dsw->d_strategy != NULL, ("No strategy on dev %s responsible for buffer %p\n", devtoname(bp->b_dev), bp)); if (!(dsw->d_flags & D_NEEDGIANT)) { /* XXX: notyet DROP_GIANT(); */ DEV_STRATEGY(bp); /* XXX: notyet PICKUP_GIANT(); */ } else DEV_STRATEGY(bp); return (0); } static int spec_specstrategy(ap) struct vop_specstrategy_args /* { struct vnode *a_vp; struct buf *a_bp; } */ *ap; { KASSERT(ap->a_vp->v_rdev == ap->a_bp->b_dev, ("%s, dev %s != %s", __func__, devtoname(ap->a_vp->v_rdev), devtoname(ap->a_bp->b_dev))); return spec_xstrategy(ap->a_vp, ap->a_bp); } /* * Device close routine */ /* ARGSUSED */ static int spec_close(ap) struct vop_close_args /* { struct vnode *a_vp; int a_fflag; struct ucred *a_cred; struct thread *a_td; } */ *ap; { struct vnode *vp = ap->a_vp, *oldvp; struct thread *td = ap->a_td; struct cdev *dev = vp->v_rdev; struct cdevsw *dsw; int error; /* * 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. */ /* * This needs to be rewritten to take the vp interlock into * consideration. */ dsw = devsw(dev); oldvp = NULL; sx_xlock(&proctree_lock); if (td && vp == td->td_proc->p_session->s_ttyvp) { SESS_LOCK(td->td_proc->p_session); VI_LOCK(vp); if (count_dev(dev) == 2 && (vp->v_iflag & VI_XLOCK) == 0) { td->td_proc->p_session->s_ttyvp = NULL; oldvp = vp; } VI_UNLOCK(vp); SESS_UNLOCK(td->td_proc->p_session); } sx_xunlock(&proctree_lock); if (oldvp != NULL) vrele(oldvp); /* * 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. */ VI_LOCK(vp); if (vp->v_iflag & VI_XLOCK) { /* Forced close. */ } else if (dsw->d_flags & D_TRACKCLOSE) { /* Keep device updated on status. */ } else if (count_dev(dev) > 1) { VI_UNLOCK(vp); return (0); } VI_UNLOCK(vp); KASSERT(dev->si_refcount > 0, ("spec_close() on un-referenced struct cdev *(%s)", devtoname(dev))); cdevsw_ref(dsw); if (!(dsw->d_flags & D_NEEDGIANT)) { DROP_GIANT(); error = dsw->d_close(dev, ap->a_fflag, S_IFCHR, td); PICKUP_GIANT(); } else error = dsw->d_close(dev, ap->a_fflag, S_IFCHR, td); cdevsw_rel(dsw); dev_rel(dev); return (error); } /* * Print out the contents of a special device vnode. */ static int spec_print(ap) struct vop_print_args /* { struct vnode *a_vp; } */ *ap; { printf("\tdev %s\n", devtoname(ap->a_vp->v_rdev)); return (0); } /* * Special device advisory byte-level locks. */ /* ARGSUSED */ static int spec_advlock(ap) struct vop_advlock_args /* { struct vnode *a_vp; caddr_t a_id; int a_op; struct flock *a_fl; int a_flags; } */ *ap; { return (ap->a_flags & F_FLOCK ? EOPNOTSUPP : EINVAL); } - -static int -spec_getpages(ap) - struct vop_getpages_args *ap; -{ - vm_offset_t kva; - int error; - int i, pcount, size, s; - daddr_t blkno; - struct buf *bp; - vm_page_t m; - vm_ooffset_t offset; - int toff, nextoff, nread; - struct vnode *vp = ap->a_vp; - int blksiz; - int gotreqpage; - - GIANT_REQUIRED; - - error = 0; - pcount = round_page(ap->a_count) / PAGE_SIZE; - - /* - * Calculate the offset of the transfer and do a sanity check. - * FreeBSD currently only supports an 8 TB range due to b_blkno - * being in DEV_BSIZE ( usually 512 ) byte chunks on call to - * VOP_STRATEGY. XXX - */ - offset = IDX_TO_OFF(ap->a_m[0]->pindex) + ap->a_offset; - blkno = btodb(offset); - - /* - * Round up physical size for real devices. We cannot round using - * v_mount's block size data because v_mount has nothing to do with - * the device. i.e. it's usually '/dev'. We need the physical block - * size for the device itself. - * - * We can't use v_rdev->si_mountpoint because it only exists when the - * block device is mounted. However, we can use v_rdev. - */ - - if (vn_isdisk(vp, NULL)) - blksiz = vp->v_rdev->si_bsize_phys; - else - blksiz = DEV_BSIZE; - - size = (ap->a_count + blksiz - 1) & ~(blksiz - 1); - - bp = getpbuf(NULL); - kva = (vm_offset_t)bp->b_data; - - /* - * Map the pages to be read into the kva. - */ - pmap_qenter(kva, ap->a_m, pcount); - - /* Build a minimal buffer header. */ - bp->b_iocmd = BIO_READ; - bp->b_iodone = bdone; - - KASSERT(bp->b_rcred == NOCRED, ("leaking read ucred")); - KASSERT(bp->b_wcred == NOCRED, ("leaking write ucred")); - bp->b_rcred = crhold(curthread->td_ucred); - bp->b_wcred = crhold(curthread->td_ucred); - bp->b_iooffset = offset; - bp->b_blkno = blkno; - bp->b_lblkno = blkno; - pbgetvp(ap->a_vp, bp); - bp->b_bcount = size; - bp->b_bufsize = size; - bp->b_resid = 0; - bp->b_runningbufspace = bp->b_bufsize; - runningbufspace += bp->b_runningbufspace; - - cnt.v_vnodein++; - cnt.v_vnodepgsin += pcount; - - /* Do the input. */ - spec_xstrategy(bp->b_vp, bp); - - s = splbio(); - bwait(bp, PVM, "spread"); - splx(s); - - if ((bp->b_ioflags & BIO_ERROR) != 0) { - if (bp->b_error) - error = bp->b_error; - else - error = EIO; - } - - nread = size - bp->b_resid; - - if (nread < ap->a_count) { - bzero((caddr_t)kva + nread, - ap->a_count - nread); - } - pmap_qremove(kva, pcount); - - gotreqpage = 0; - /* - * While the page is busy, its object field is immutable. - */ - VM_OBJECT_LOCK(ap->a_m[ap->a_reqpage]->object); - vm_page_lock_queues(); - for (i = 0, toff = 0; i < pcount; i++, toff = nextoff) { - nextoff = toff + PAGE_SIZE; - m = ap->a_m[i]; - - if (nextoff <= nread) { - m->valid = VM_PAGE_BITS_ALL; - vm_page_undirty(m); - } else if (toff < nread) { - /* - * Since this is a VM request, we have to supply the - * unaligned offset to allow vm_page_set_validclean() - * to zero sub-DEV_BSIZE'd portions of the page. - */ - vm_page_set_validclean(m, 0, nread - toff); - } else { - m->valid = 0; - vm_page_undirty(m); - } - - if (i != ap->a_reqpage) { - /* - * Just in case someone was asking for this page we - * now tell them that it is ok to use. - */ - if (!error || (m->valid == VM_PAGE_BITS_ALL)) { - if (m->valid) { - if (m->flags & PG_WANTED) { - vm_page_activate(m); - } else { - vm_page_deactivate(m); - } - vm_page_wakeup(m); - } else { - vm_page_free(m); - } - } else { - vm_page_free(m); - } - } else if (m->valid) { - gotreqpage = 1; - /* - * Since this is a VM request, we need to make the - * entire page presentable by zeroing invalid sections. - */ - if (m->valid != VM_PAGE_BITS_ALL) - vm_page_zero_invalid(m, FALSE); - } - } - vm_page_unlock_queues(); - if (!gotreqpage) { - m = ap->a_m[ap->a_reqpage]; - printf( - "spec_getpages:(%s) I/O read failure: (error=%d) bp %p vp %p\n", - devtoname(bp->b_dev), error, bp, bp->b_vp); - printf( - " size: %d, resid: %ld, a_count: %d, valid: 0x%lx\n", - size, bp->b_resid, ap->a_count, (u_long)m->valid); - printf( - " nread: %d, reqpage: %d, pindex: %lu, pcount: %d\n", - nread, ap->a_reqpage, (u_long)m->pindex, pcount); - VM_OBJECT_UNLOCK(m->object); - /* - * Free the buffer header back to the swap buffer pool. - */ - relpbuf(bp, NULL); - return VM_PAGER_ERROR; - } - VM_OBJECT_UNLOCK(ap->a_m[ap->a_reqpage]->object); - /* - * Free the buffer header back to the swap buffer pool. - */ - relpbuf(bp, NULL); - return VM_PAGER_OK; -} diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c index bddd2e49cecc..4f446880cd96 100644 --- a/sys/ufs/ffs/ffs_vnops.c +++ b/sys/ufs/ffs/ffs_vnops.c @@ -1,1757 +1,1650 @@ /* * Copyright (c) 2002, 2003 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by Marshall * Kirk McKusick and Network Associates Laboratories, the Security * Research Division of Network Associates, Inc. under DARPA/SPAWAR * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS * research program * * 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. * 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. * * from: @(#)ufs_readwrite.c 8.11 (Berkeley) 5/8/95 * from: $FreeBSD: .../ufs/ufs_readwrite.c,v 1.96 2002/08/12 09:22:11 phk ... * @(#)ffs_vnops.c 8.15 (Berkeley) 5/14/95 */ #include __FBSDID("$FreeBSD$"); #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 #include #include #include #include #include "opt_directio.h" #ifdef DIRECTIO extern int ffs_rawread(struct vnode *vp, struct uio *uio, int *workdone); #endif static int ffs_fsync(struct vop_fsync_args *); static int ffs_getpages(struct vop_getpages_args *); static int ffs_read(struct vop_read_args *); static int ffs_write(struct vop_write_args *); static int ffs_extread(struct vnode *vp, struct uio *uio, int ioflag); static int ffs_extwrite(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred); static int ffsext_strategy(struct vop_strategy_args *); static int ffs_closeextattr(struct vop_closeextattr_args *); static int ffs_deleteextattr(struct vop_deleteextattr_args *); static int ffs_getextattr(struct vop_getextattr_args *); static int ffs_listextattr(struct vop_listextattr_args *); static int ffs_openextattr(struct vop_openextattr_args *); static int ffs_setextattr(struct vop_setextattr_args *); /* Global vfs data structures for ufs. */ vop_t **ffs_vnodeop_p; static struct vnodeopv_entry_desc ffs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *) ufs_vnoperate }, { &vop_fsync_desc, (vop_t *) ffs_fsync }, { &vop_getpages_desc, (vop_t *) ffs_getpages }, { &vop_read_desc, (vop_t *) ffs_read }, { &vop_reallocblks_desc, (vop_t *) ffs_reallocblks }, { &vop_write_desc, (vop_t *) ffs_write }, { &vop_closeextattr_desc, (vop_t *) ffs_closeextattr }, { &vop_deleteextattr_desc, (vop_t *) ffs_deleteextattr }, { &vop_getextattr_desc, (vop_t *) ffs_getextattr }, { &vop_listextattr_desc, (vop_t *) ffs_listextattr }, { &vop_openextattr_desc, (vop_t *) ffs_openextattr }, { &vop_setextattr_desc, (vop_t *) ffs_setextattr }, { NULL, NULL } }; static struct vnodeopv_desc ffs_vnodeop_opv_desc = { &ffs_vnodeop_p, ffs_vnodeop_entries }; vop_t **ffs_specop_p; static struct vnodeopv_entry_desc ffs_specop_entries[] = { { &vop_default_desc, (vop_t *) ufs_vnoperatespec }, { &vop_fsync_desc, (vop_t *) ffs_fsync }, { &vop_reallocblks_desc, (vop_t *) ffs_reallocblks }, { &vop_strategy_desc, (vop_t *) ffsext_strategy }, { &vop_closeextattr_desc, (vop_t *) ffs_closeextattr }, { &vop_deleteextattr_desc, (vop_t *) ffs_deleteextattr }, { &vop_getextattr_desc, (vop_t *) ffs_getextattr }, { &vop_listextattr_desc, (vop_t *) ffs_listextattr }, { &vop_openextattr_desc, (vop_t *) ffs_openextattr }, { &vop_setextattr_desc, (vop_t *) ffs_setextattr }, { NULL, NULL } }; static struct vnodeopv_desc ffs_specop_opv_desc = { &ffs_specop_p, ffs_specop_entries }; vop_t **ffs_fifoop_p; static struct vnodeopv_entry_desc ffs_fifoop_entries[] = { { &vop_default_desc, (vop_t *) ufs_vnoperatefifo }, { &vop_fsync_desc, (vop_t *) ffs_fsync }, { &vop_reallocblks_desc, (vop_t *) ffs_reallocblks }, { &vop_strategy_desc, (vop_t *) ffsext_strategy }, { &vop_closeextattr_desc, (vop_t *) ffs_closeextattr }, { &vop_deleteextattr_desc, (vop_t *) ffs_deleteextattr }, { &vop_getextattr_desc, (vop_t *) ffs_getextattr }, { &vop_listextattr_desc, (vop_t *) ffs_listextattr }, { &vop_openextattr_desc, (vop_t *) ffs_openextattr }, { &vop_setextattr_desc, (vop_t *) ffs_setextattr }, { NULL, NULL } }; static struct vnodeopv_desc ffs_fifoop_opv_desc = { &ffs_fifoop_p, ffs_fifoop_entries }; VNODEOP_SET(ffs_vnodeop_opv_desc); VNODEOP_SET(ffs_specop_opv_desc); VNODEOP_SET(ffs_fifoop_opv_desc); /* * Synch an open file. */ /* ARGSUSED */ static int ffs_fsync(ap) struct vop_fsync_args /* { struct vnode *a_vp; struct ucred *a_cred; int a_waitfor; struct thread *a_td; } */ *ap; { struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); struct buf *bp; struct buf *nbp; int s, error, wait, passes, skipmeta; ufs_lbn_t lbn; wait = (ap->a_waitfor == MNT_WAIT); if (vn_isdisk(vp, NULL)) { lbn = INT_MAX; if (vp->v_rdev->si_mountpoint != NULL && (vp->v_rdev->si_mountpoint->mnt_flag & MNT_SOFTDEP)) softdep_fsync_mountdev(vp); } else { lbn = lblkno(ip->i_fs, (ip->i_size + ip->i_fs->fs_bsize - 1)); } /* * Flush all dirty buffers associated with a vnode. */ passes = NIADDR + 1; skipmeta = 0; if (wait) skipmeta = 1; s = splbio(); VI_LOCK(vp); loop: TAILQ_FOREACH(bp, &vp->v_dirtyblkhd, b_vnbufs) bp->b_vflags &= ~BV_SCANNED; for (bp = TAILQ_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) { nbp = TAILQ_NEXT(bp, b_vnbufs); /* * Reasons to skip this buffer: it has already been considered * on this pass, this pass is the first time through on a * synchronous flush request and the buffer being considered * is metadata, the buffer has dependencies that will cause * it to be redirtied and it has not already been deferred, * or it is already being written. */ if ((bp->b_vflags & BV_SCANNED) != 0) continue; bp->b_vflags |= BV_SCANNED; if ((skipmeta == 1 && bp->b_lblkno < 0)) continue; if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL)) continue; if (!wait && LIST_FIRST(&bp->b_dep) != NULL && (bp->b_flags & B_DEFERRED) == 0 && buf_countdeps(bp, 0)) { bp->b_flags |= B_DEFERRED; BUF_UNLOCK(bp); continue; } VI_UNLOCK(vp); if ((bp->b_flags & B_DELWRI) == 0) panic("ffs_fsync: not dirty"); if (vp != bp->b_vp) panic("ffs_fsync: vp != vp->b_vp"); /* * If this is a synchronous flush request, or it is not a * file or device, start the write on this buffer immediatly. */ if (wait || (vp->v_type != VREG && vp->v_type != VBLK)) { /* * On our final pass through, do all I/O synchronously * so that we can find out if our flush is failing * because of write errors. */ if (passes > 0 || !wait) { if ((bp->b_flags & B_CLUSTEROK) && !wait) { (void) vfs_bio_awrite(bp); } else { bremfree(bp); splx(s); (void) bawrite(bp); s = splbio(); } } else { bremfree(bp); splx(s); if ((error = bwrite(bp)) != 0) return (error); s = splbio(); } } else if ((vp->v_type == VREG) && (bp->b_lblkno >= lbn)) { /* * If the buffer is for data that has been truncated * off the file, then throw it away. */ bremfree(bp); bp->b_flags |= B_INVAL | B_NOCACHE; splx(s); brelse(bp); s = splbio(); } else vfs_bio_awrite(bp); /* * Since we may have slept during the I/O, we need * to start from a known point. */ VI_LOCK(vp); nbp = TAILQ_FIRST(&vp->v_dirtyblkhd); } /* * If we were asked to do this synchronously, then go back for * another pass, this time doing the metadata. */ if (skipmeta) { skipmeta = 0; goto loop; } if (wait) { while (vp->v_numoutput) { vp->v_iflag |= VI_BWAIT; msleep((caddr_t)&vp->v_numoutput, VI_MTX(vp), PRIBIO + 4, "ffsfsn", 0); } VI_UNLOCK(vp); /* * Ensure that any filesystem metatdata associated * with the vnode has been written. */ splx(s); if ((error = softdep_sync_metadata(ap)) != 0) return (error); s = splbio(); VI_LOCK(vp); if (!TAILQ_EMPTY(&vp->v_dirtyblkhd)) { /* * Block devices associated with filesystems may * have new I/O requests posted for them even if * the vnode is locked, so no amount of trying will * get them clean. Thus we give block devices a * good effort, then just give up. For all other file * types, go around and try again until it is clean. */ if (passes > 0) { passes -= 1; goto loop; } #ifdef DIAGNOSTIC if (!vn_isdisk(vp, NULL)) vprint("ffs_fsync: dirty", vp); #endif } } VI_UNLOCK(vp); splx(s); return (UFS_UPDATE(vp, wait)); } /* * Vnode op for reading. */ /* ARGSUSED */ static int ffs_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; struct inode *ip; struct uio *uio; struct fs *fs; struct buf *bp; ufs_lbn_t lbn, nextlbn; off_t bytesinfile; long size, xfersize, blkoffset; int error, orig_resid; int seqcount; int ioflag; vp = ap->a_vp; uio = ap->a_uio; ioflag = ap->a_ioflag; if (ap->a_ioflag & IO_EXT) #ifdef notyet return (ffs_extread(vp, uio, ioflag)); #else panic("ffs_read+IO_EXT"); #endif #ifdef DIRECTIO if ((ioflag & IO_DIRECT) != 0) { int workdone; error = ffs_rawread(vp, uio, &workdone); if (error != 0 || workdone != 0) return error; } #endif GIANT_REQUIRED; seqcount = ap->a_ioflag >> IO_SEQSHIFT; ip = VTOI(vp); #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_READ) panic("ffs_read: mode"); if (vp->v_type == VLNK) { if ((int)ip->i_size < vp->v_mount->mnt_maxsymlinklen) panic("ffs_read: short symlink"); } else if (vp->v_type != VREG && vp->v_type != VDIR) panic("ffs_read: type %d", vp->v_type); #endif orig_resid = uio->uio_resid; KASSERT(orig_resid >= 0, ("ffs_read: uio->uio_resid < 0")); if (orig_resid == 0) return (0); KASSERT(uio->uio_offset >= 0, ("ffs_read: uio->uio_offset < 0")); fs = ip->i_fs; if (uio->uio_offset < ip->i_size && uio->uio_offset >= fs->fs_maxfilesize) return (EOVERFLOW); for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { if ((bytesinfile = ip->i_size - uio->uio_offset) <= 0) break; lbn = lblkno(fs, uio->uio_offset); nextlbn = lbn + 1; /* * size of buffer. The buffer representing the * end of the file is rounded up to the size of * the block type ( fragment or full block, * depending ). */ size = blksize(fs, ip, lbn); blkoffset = blkoff(fs, uio->uio_offset); /* * The amount we want to transfer in this iteration is * one FS block less the amount of the data before * our startpoint (duh!) */ xfersize = fs->fs_bsize - blkoffset; /* * But if we actually want less than the block, * or the file doesn't have a whole block more of data, * then use the lesser number. */ if (uio->uio_resid < xfersize) xfersize = uio->uio_resid; if (bytesinfile < xfersize) xfersize = bytesinfile; if (lblktosize(fs, nextlbn) >= ip->i_size) { /* * Don't do readahead if this is the end of the file. */ error = bread(vp, lbn, size, NOCRED, &bp); } else if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0) { /* * Otherwise if we are allowed to cluster, * grab as much as we can. * * XXX This may not be a win if we are not * doing sequential access. */ error = cluster_read(vp, ip->i_size, lbn, size, NOCRED, uio->uio_resid, seqcount, &bp); } else if (seqcount > 1) { /* * If we are NOT allowed to cluster, then * if we appear to be acting sequentially, * fire off a request for a readahead * as well as a read. Note that the 4th and 5th * arguments point to arrays of the size specified in * the 6th argument. */ int nextsize = blksize(fs, ip, nextlbn); error = breadn(vp, lbn, size, &nextlbn, &nextsize, 1, NOCRED, &bp); } else { /* * Failing all of the above, just read what the * user asked for. Interestingly, the same as * the first option above. */ error = bread(vp, lbn, size, NOCRED, &bp); } if (error) { brelse(bp); bp = NULL; break; } /* * If IO_DIRECT then set B_DIRECT for the buffer. This * will cause us to attempt to release the buffer later on * and will cause the buffer cache to attempt to free the * underlying pages. */ if (ioflag & IO_DIRECT) bp->b_flags |= B_DIRECT; /* * We should only get non-zero b_resid when an I/O error * has occurred, which should cause us to break above. * However, if the short read did not cause an error, * then we want to ensure that we do not uiomove bad * or uninitialized data. */ size -= bp->b_resid; if (size < xfersize) { if (size == 0) break; xfersize = size; } error = uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio); if (error) break; if ((ioflag & (IO_VMIO|IO_DIRECT)) && (LIST_FIRST(&bp->b_dep) == NULL)) { /* * If there are no dependencies, and it's VMIO, * then we don't need the buf, mark it available * for freeing. The VM has the data. */ bp->b_flags |= B_RELBUF; brelse(bp); } else { /* * Otherwise let whoever * made the request take care of * freeing it. We just queue * it onto another list. */ bqrelse(bp); } } /* * This can only happen in the case of an error * because the loop above resets bp to NULL on each iteration * and on normal completion has not set a new value into it. * so it must have come from a 'break' statement */ if (bp != NULL) { if ((ioflag & (IO_VMIO|IO_DIRECT)) && (LIST_FIRST(&bp->b_dep) == NULL)) { bp->b_flags |= B_RELBUF; brelse(bp); } else { bqrelse(bp); } } if ((error == 0 || uio->uio_resid != orig_resid) && (vp->v_mount->mnt_flag & MNT_NOATIME) == 0) ip->i_flag |= IN_ACCESS; return (error); } /* * Vnode op for writing. */ static int ffs_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; struct uio *uio; struct inode *ip; struct fs *fs; struct buf *bp; struct thread *td; ufs_lbn_t lbn; off_t osize; int seqcount; int blkoffset, error, extended, flags, ioflag, resid, size, xfersize; vp = ap->a_vp; uio = ap->a_uio; ioflag = ap->a_ioflag; if (ap->a_ioflag & IO_EXT) #ifdef notyet return (ffs_extwrite(vp, uio, ioflag, ap->a_cred)); #else panic("ffs_write+IO_EXT"); #endif GIANT_REQUIRED; extended = 0; seqcount = ap->a_ioflag >> IO_SEQSHIFT; ip = VTOI(vp); #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_WRITE) panic("ffs_write: mode"); #endif switch (vp->v_type) { case VREG: if (ioflag & IO_APPEND) uio->uio_offset = ip->i_size; if ((ip->i_flags & APPEND) && uio->uio_offset != ip->i_size) return (EPERM); /* FALLTHROUGH */ case VLNK: break; case VDIR: panic("ffs_write: dir write"); break; default: panic("ffs_write: type %p %d (%d,%d)", vp, (int)vp->v_type, (int)uio->uio_offset, (int)uio->uio_resid ); } KASSERT(uio->uio_resid >= 0, ("ffs_write: uio->uio_resid < 0")); KASSERT(uio->uio_offset >= 0, ("ffs_write: uio->uio_offset < 0")); fs = ip->i_fs; if ((uoff_t)uio->uio_offset + uio->uio_resid > fs->fs_maxfilesize) return (EFBIG); /* * Maybe this should be above the vnode op call, but so long as * file servers have no limits, I don't think it matters. */ td = uio->uio_td; if (vp->v_type == VREG && td != NULL) { PROC_LOCK(td->td_proc); if (uio->uio_offset + uio->uio_resid > lim_cur(td->td_proc, RLIMIT_FSIZE)) { psignal(td->td_proc, SIGXFSZ); PROC_UNLOCK(td->td_proc); return (EFBIG); } PROC_UNLOCK(td->td_proc); } resid = uio->uio_resid; osize = ip->i_size; if (seqcount > BA_SEQMAX) flags = BA_SEQMAX << BA_SEQSHIFT; else flags = seqcount << BA_SEQSHIFT; if ((ioflag & IO_SYNC) && !DOINGASYNC(vp)) flags |= IO_SYNC; for (error = 0; uio->uio_resid > 0;) { lbn = lblkno(fs, uio->uio_offset); blkoffset = blkoff(fs, uio->uio_offset); xfersize = fs->fs_bsize - blkoffset; if (uio->uio_resid < xfersize) xfersize = uio->uio_resid; if (uio->uio_offset + xfersize > ip->i_size) vnode_pager_setsize(vp, uio->uio_offset + xfersize); /* * We must perform a read-before-write if the transfer size * does not cover the entire buffer. */ if (fs->fs_bsize > xfersize) flags |= BA_CLRBUF; else flags &= ~BA_CLRBUF; /* XXX is uio->uio_offset the right thing here? */ error = UFS_BALLOC(vp, uio->uio_offset, xfersize, ap->a_cred, flags, &bp); if (error != 0) break; /* * If the buffer is not valid we have to clear out any * garbage data from the pages instantiated for the buffer. * If we do not, a failed uiomove() during a write can leave * the prior contents of the pages exposed to a userland * mmap(). XXX deal with uiomove() errors a better way. */ if ((bp->b_flags & B_CACHE) == 0 && fs->fs_bsize <= xfersize) vfs_bio_clrbuf(bp); if (ioflag & IO_DIRECT) bp->b_flags |= B_DIRECT; if ((ioflag & (IO_SYNC|IO_INVAL)) == (IO_SYNC|IO_INVAL)) bp->b_flags |= B_NOCACHE; if (uio->uio_offset + xfersize > ip->i_size) { ip->i_size = uio->uio_offset + xfersize; DIP_SET(ip, i_size, ip->i_size); extended = 1; } size = blksize(fs, ip, lbn) - bp->b_resid; if (size < xfersize) xfersize = size; error = uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio); if ((ioflag & (IO_VMIO|IO_DIRECT)) && (LIST_FIRST(&bp->b_dep) == NULL)) { bp->b_flags |= B_RELBUF; } /* * If IO_SYNC each buffer is written synchronously. Otherwise * if we have a severe page deficiency write the buffer * asynchronously. Otherwise try to cluster, and if that * doesn't do it then either do an async write (if O_DIRECT), * or a delayed write (if not). */ if (ioflag & IO_SYNC) { (void)bwrite(bp); } else if (vm_page_count_severe() || buf_dirty_count_severe() || (ioflag & IO_ASYNC)) { bp->b_flags |= B_CLUSTEROK; bawrite(bp); } else if (xfersize + blkoffset == fs->fs_bsize) { if ((vp->v_mount->mnt_flag & MNT_NOCLUSTERW) == 0) { bp->b_flags |= B_CLUSTEROK; cluster_write(bp, ip->i_size, seqcount); } else { bawrite(bp); } } else if (ioflag & IO_DIRECT) { bp->b_flags |= B_CLUSTEROK; bawrite(bp); } else { bp->b_flags |= B_CLUSTEROK; bdwrite(bp); } if (error || xfersize == 0) break; ip->i_flag |= IN_CHANGE | IN_UPDATE; } /* * If we successfully wrote any data, and we are not the superuser * we clear the setuid and setgid bits as a precaution against * tampering. */ if (resid > uio->uio_resid && ap->a_cred && suser_cred(ap->a_cred, SUSER_ALLOWJAIL)) { ip->i_mode &= ~(ISUID | ISGID); DIP_SET(ip, i_mode, ip->i_mode); } if (resid > uio->uio_resid) VN_KNOTE_UNLOCKED(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0)); if (error) { if (ioflag & IO_UNIT) { (void)UFS_TRUNCATE(vp, osize, IO_NORMAL | (ioflag & IO_SYNC), ap->a_cred, uio->uio_td); uio->uio_offset -= resid - uio->uio_resid; uio->uio_resid = resid; } } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) error = UFS_UPDATE(vp, 1); return (error); } /* * get page routine */ static int ffs_getpages(ap) struct vop_getpages_args *ap; { - off_t foff, physoffset; - int i, size, bsize; - struct vnode *dp, *vp; - vm_object_t obj; - vm_pindex_t pindex; + int i; vm_page_t mreq; - int bbackwards, bforwards; - int pbackwards, pforwards; - int firstpage; - ufs2_daddr_t reqblkno, reqlblkno; - int poff; int pcount; - int rtval; - int pagesperblock; GIANT_REQUIRED; pcount = round_page(ap->a_count) / PAGE_SIZE; mreq = ap->a_m[ap->a_reqpage]; /* * if ANY DEV_BSIZE blocks are valid on a large filesystem block, * then the entire page is valid. Since the page may be mapped, * user programs might reference data beyond the actual end of file * occuring within the page. We have to zero that data. */ VM_OBJECT_LOCK(mreq->object); if (mreq->valid) { if (mreq->valid != VM_PAGE_BITS_ALL) vm_page_zero_invalid(mreq, TRUE); vm_page_lock_queues(); for (i = 0; i < pcount; i++) { if (i != ap->a_reqpage) { vm_page_free(ap->a_m[i]); } } vm_page_unlock_queues(); VM_OBJECT_UNLOCK(mreq->object); return VM_PAGER_OK; } VM_OBJECT_UNLOCK(mreq->object); - vp = ap->a_vp; - obj = vp->v_object; - bsize = vp->v_mount->mnt_stat.f_iosize; - pindex = mreq->pindex; - foff = IDX_TO_OFF(pindex) /* + ap->a_offset should be zero */; - - if (bsize < PAGE_SIZE) - return vnode_pager_generic_getpages(ap->a_vp, ap->a_m, - ap->a_count, - ap->a_reqpage); - - /* - * foff is the file offset of the required page - * reqlblkno is the logical block that contains the page - * poff is the index of the page into the logical block - */ - reqlblkno = foff / bsize; - poff = (foff % bsize) / PAGE_SIZE; - - dp = VTOI(vp)->i_devvp; - if (ufs_bmaparray(vp, reqlblkno, &reqblkno, 0, &bforwards, &bbackwards) - || (reqblkno == -1)) { - VM_OBJECT_LOCK(obj); - vm_page_lock_queues(); - for(i = 0; i < pcount; i++) { - if (i != ap->a_reqpage) - vm_page_free(ap->a_m[i]); - } - vm_page_unlock_queues(); - if (reqblkno == -1) { - if ((mreq->flags & PG_ZERO) == 0) - pmap_zero_page(mreq); - vm_page_undirty(mreq); - mreq->valid = VM_PAGE_BITS_ALL; - VM_OBJECT_UNLOCK(obj); - return VM_PAGER_OK; - } else { - VM_OBJECT_UNLOCK(obj); - return VM_PAGER_ERROR; - } - } - - physoffset = (off_t)reqblkno * DEV_BSIZE + poff * PAGE_SIZE; - pagesperblock = bsize / PAGE_SIZE; - /* - * find the first page that is contiguous... - * note that pbackwards is the number of pages that are contiguous - * backwards. - */ - firstpage = 0; - if (ap->a_count) { - pbackwards = poff + bbackwards * pagesperblock; - if (ap->a_reqpage > pbackwards) { - firstpage = ap->a_reqpage - pbackwards; - VM_OBJECT_LOCK(obj); - vm_page_lock_queues(); - for(i=0;ia_m[i]); - vm_page_unlock_queues(); - VM_OBJECT_UNLOCK(obj); - } - /* - * pforwards is the number of pages that are contiguous - * after the current page. - */ - pforwards = (pagesperblock - (poff + 1)) + - bforwards * pagesperblock; - if (pforwards < (pcount - (ap->a_reqpage + 1))) { - VM_OBJECT_LOCK(obj); - vm_page_lock_queues(); - for( i = ap->a_reqpage + pforwards + 1; i < pcount; i++) - vm_page_free(ap->a_m[i]); - vm_page_unlock_queues(); - VM_OBJECT_UNLOCK(obj); - pcount = ap->a_reqpage + pforwards + 1; - } - - /* - * number of pages for I/O corrected for the non-contig pages at - * the beginning of the array. - */ - pcount -= firstpage; - } - - /* - * calculate the size of the transfer - */ - - size = pcount * PAGE_SIZE; - - if ((IDX_TO_OFF(ap->a_m[firstpage]->pindex) + size) > - obj->un_pager.vnp.vnp_size) - size = obj->un_pager.vnp.vnp_size - - IDX_TO_OFF(ap->a_m[firstpage]->pindex); - - physoffset -= foff; - rtval = VOP_GETPAGES(dp, &ap->a_m[firstpage], size, - (ap->a_reqpage - firstpage), physoffset); - - return (rtval); + return vnode_pager_generic_getpages(ap->a_vp, ap->a_m, + ap->a_count, + ap->a_reqpage); } + /* * Extended attribute area reading. */ static int ffs_extread(struct vnode *vp, struct uio *uio, int ioflag) { struct inode *ip; struct ufs2_dinode *dp; struct fs *fs; struct buf *bp; ufs_lbn_t lbn, nextlbn; off_t bytesinfile; long size, xfersize, blkoffset; int error, orig_resid; GIANT_REQUIRED; ip = VTOI(vp); fs = ip->i_fs; dp = ip->i_din2; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_READ || fs->fs_magic != FS_UFS2_MAGIC) panic("ffs_extread: mode"); #endif orig_resid = uio->uio_resid; KASSERT(orig_resid >= 0, ("ffs_extread: uio->uio_resid < 0")); if (orig_resid == 0) return (0); KASSERT(uio->uio_offset >= 0, ("ffs_extread: uio->uio_offset < 0")); for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { if ((bytesinfile = dp->di_extsize - uio->uio_offset) <= 0) break; lbn = lblkno(fs, uio->uio_offset); nextlbn = lbn + 1; /* * size of buffer. The buffer representing the * end of the file is rounded up to the size of * the block type ( fragment or full block, * depending ). */ size = sblksize(fs, dp->di_extsize, lbn); blkoffset = blkoff(fs, uio->uio_offset); /* * The amount we want to transfer in this iteration is * one FS block less the amount of the data before * our startpoint (duh!) */ xfersize = fs->fs_bsize - blkoffset; /* * But if we actually want less than the block, * or the file doesn't have a whole block more of data, * then use the lesser number. */ if (uio->uio_resid < xfersize) xfersize = uio->uio_resid; if (bytesinfile < xfersize) xfersize = bytesinfile; if (lblktosize(fs, nextlbn) >= dp->di_extsize) { /* * Don't do readahead if this is the end of the info. */ error = bread(vp, -1 - lbn, size, NOCRED, &bp); } else { /* * If we have a second block, then * fire off a request for a readahead * as well as a read. Note that the 4th and 5th * arguments point to arrays of the size specified in * the 6th argument. */ int nextsize = sblksize(fs, dp->di_extsize, nextlbn); nextlbn = -1 - nextlbn; error = breadn(vp, -1 - lbn, size, &nextlbn, &nextsize, 1, NOCRED, &bp); } if (error) { brelse(bp); bp = NULL; break; } /* * If IO_DIRECT then set B_DIRECT for the buffer. This * will cause us to attempt to release the buffer later on * and will cause the buffer cache to attempt to free the * underlying pages. */ if (ioflag & IO_DIRECT) bp->b_flags |= B_DIRECT; /* * We should only get non-zero b_resid when an I/O error * has occurred, which should cause us to break above. * However, if the short read did not cause an error, * then we want to ensure that we do not uiomove bad * or uninitialized data. */ size -= bp->b_resid; if (size < xfersize) { if (size == 0) break; xfersize = size; } error = uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio); if (error) break; if ((ioflag & (IO_VMIO|IO_DIRECT)) && (LIST_FIRST(&bp->b_dep) == NULL)) { /* * If there are no dependencies, and it's VMIO, * then we don't need the buf, mark it available * for freeing. The VM has the data. */ bp->b_flags |= B_RELBUF; brelse(bp); } else { /* * Otherwise let whoever * made the request take care of * freeing it. We just queue * it onto another list. */ bqrelse(bp); } } /* * This can only happen in the case of an error * because the loop above resets bp to NULL on each iteration * and on normal completion has not set a new value into it. * so it must have come from a 'break' statement */ if (bp != NULL) { if ((ioflag & (IO_VMIO|IO_DIRECT)) && (LIST_FIRST(&bp->b_dep) == NULL)) { bp->b_flags |= B_RELBUF; brelse(bp); } else { bqrelse(bp); } } if ((error == 0 || uio->uio_resid != orig_resid) && (vp->v_mount->mnt_flag & MNT_NOATIME) == 0) ip->i_flag |= IN_ACCESS; return (error); } /* * Extended attribute area writing. */ static int ffs_extwrite(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *ucred) { struct inode *ip; struct ufs2_dinode *dp; struct fs *fs; struct buf *bp; ufs_lbn_t lbn; off_t osize; int blkoffset, error, flags, resid, size, xfersize; GIANT_REQUIRED; ip = VTOI(vp); fs = ip->i_fs; dp = ip->i_din2; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_WRITE || fs->fs_magic != FS_UFS2_MAGIC) panic("ffs_extwrite: mode"); #endif if (ioflag & IO_APPEND) uio->uio_offset = dp->di_extsize; KASSERT(uio->uio_offset >= 0, ("ffs_extwrite: uio->uio_offset < 0")); KASSERT(uio->uio_resid >= 0, ("ffs_extwrite: uio->uio_resid < 0")); if ((uoff_t)uio->uio_offset + uio->uio_resid > NXADDR * fs->fs_bsize) return (EFBIG); resid = uio->uio_resid; osize = dp->di_extsize; flags = IO_EXT; if ((ioflag & IO_SYNC) && !DOINGASYNC(vp)) flags |= IO_SYNC; for (error = 0; uio->uio_resid > 0;) { lbn = lblkno(fs, uio->uio_offset); blkoffset = blkoff(fs, uio->uio_offset); xfersize = fs->fs_bsize - blkoffset; if (uio->uio_resid < xfersize) xfersize = uio->uio_resid; /* * We must perform a read-before-write if the transfer size * does not cover the entire buffer. */ if (fs->fs_bsize > xfersize) flags |= BA_CLRBUF; else flags &= ~BA_CLRBUF; error = UFS_BALLOC(vp, uio->uio_offset, xfersize, ucred, flags, &bp); if (error != 0) break; /* * If the buffer is not valid we have to clear out any * garbage data from the pages instantiated for the buffer. * If we do not, a failed uiomove() during a write can leave * the prior contents of the pages exposed to a userland * mmap(). XXX deal with uiomove() errors a better way. */ if ((bp->b_flags & B_CACHE) == 0 && fs->fs_bsize <= xfersize) vfs_bio_clrbuf(bp); if (ioflag & IO_DIRECT) bp->b_flags |= B_DIRECT; if (uio->uio_offset + xfersize > dp->di_extsize) dp->di_extsize = uio->uio_offset + xfersize; size = sblksize(fs, dp->di_extsize, lbn) - bp->b_resid; if (size < xfersize) xfersize = size; error = uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio); if ((ioflag & (IO_VMIO|IO_DIRECT)) && (LIST_FIRST(&bp->b_dep) == NULL)) { bp->b_flags |= B_RELBUF; } /* * If IO_SYNC each buffer is written synchronously. Otherwise * if we have a severe page deficiency write the buffer * asynchronously. Otherwise try to cluster, and if that * doesn't do it then either do an async write (if O_DIRECT), * or a delayed write (if not). */ if (ioflag & IO_SYNC) { (void)bwrite(bp); } else if (vm_page_count_severe() || buf_dirty_count_severe() || xfersize + blkoffset == fs->fs_bsize || (ioflag & (IO_ASYNC | IO_DIRECT))) bawrite(bp); else bdwrite(bp); if (error || xfersize == 0) break; ip->i_flag |= IN_CHANGE | IN_UPDATE; } /* * If we successfully wrote any data, and we are not the superuser * we clear the setuid and setgid bits as a precaution against * tampering. */ if (resid > uio->uio_resid && ucred && suser_cred(ucred, SUSER_ALLOWJAIL)) { ip->i_mode &= ~(ISUID | ISGID); dp->di_mode = ip->i_mode; } if (error) { if (ioflag & IO_UNIT) { (void)UFS_TRUNCATE(vp, osize, IO_EXT | (ioflag&IO_SYNC), ucred, uio->uio_td); uio->uio_offset -= resid - uio->uio_resid; uio->uio_resid = resid; } } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) error = UFS_UPDATE(vp, 1); return (error); } /* * Vnode operating to retrieve a named extended attribute. * * Locate a particular EA (nspace:name) in the area (ptr:length), and return * the length of the EA, and possibly the pointer to the entry and to the data. */ static int ffs_findextattr(u_char *ptr, u_int length, int nspace, const char *name, u_char **eap, u_char **eac) { u_char *p, *pe, *pn, *p0; int eapad1, eapad2, ealength, ealen, nlen; uint32_t ul; pe = ptr + length; nlen = strlen(name); for (p = ptr; p < pe; p = pn) { p0 = p; bcopy(p, &ul, sizeof(ul)); pn = p + ul; /* make sure this entry is complete */ if (pn > pe) break; p += sizeof(uint32_t); if (*p != nspace) continue; p++; eapad2 = *p++; if (*p != nlen) continue; p++; if (bcmp(p, name, nlen)) continue; ealength = sizeof(uint32_t) + 3 + nlen; eapad1 = 8 - (ealength % 8); if (eapad1 == 8) eapad1 = 0; ealength += eapad1; ealen = ul - ealength - eapad2; p += nlen + eapad1; if (eap != NULL) *eap = p0; if (eac != NULL) *eac = p; return (ealen); } return(-1); } static int ffs_rdextattr(u_char **p, struct vnode *vp, struct thread *td, int extra) { struct inode *ip; struct ufs2_dinode *dp; struct uio luio; struct iovec liovec; int easize, error; u_char *eae; ip = VTOI(vp); dp = ip->i_din2; easize = dp->di_extsize; eae = malloc(easize + extra, M_TEMP, M_WAITOK); liovec.iov_base = eae; liovec.iov_len = easize; luio.uio_iov = &liovec; luio.uio_iovcnt = 1; luio.uio_offset = 0; luio.uio_resid = easize; luio.uio_segflg = UIO_SYSSPACE; luio.uio_rw = UIO_READ; luio.uio_td = td; error = ffs_extread(vp, &luio, IO_EXT | IO_SYNC); if (error) { free(eae, M_TEMP); return(error); } *p = eae; return (0); } static int ffs_open_ea(struct vnode *vp, struct ucred *cred, struct thread *td) { struct inode *ip; struct ufs2_dinode *dp; int error; ip = VTOI(vp); if (ip->i_ea_area != NULL) return (EBUSY); dp = ip->i_din2; error = ffs_rdextattr(&ip->i_ea_area, vp, td, 0); if (error) return (error); ip->i_ea_len = dp->di_extsize; ip->i_ea_error = 0; return (0); } /* * Vnode extattr transaction commit/abort */ static int ffs_close_ea(struct vnode *vp, int commit, struct ucred *cred, struct thread *td) { struct inode *ip; struct uio luio; struct iovec liovec; int error; struct ufs2_dinode *dp; ip = VTOI(vp); if (ip->i_ea_area == NULL) return (EINVAL); dp = ip->i_din2; error = ip->i_ea_error; if (commit && error == 0) { if (cred == NOCRED) cred = vp->v_mount->mnt_cred; liovec.iov_base = ip->i_ea_area; liovec.iov_len = ip->i_ea_len; luio.uio_iov = &liovec; luio.uio_iovcnt = 1; luio.uio_offset = 0; luio.uio_resid = ip->i_ea_len; luio.uio_segflg = UIO_SYSSPACE; luio.uio_rw = UIO_WRITE; luio.uio_td = td; /* XXX: I'm not happy about truncating to zero size */ if (ip->i_ea_len < dp->di_extsize) error = ffs_truncate(vp, 0, IO_EXT, cred, td); error = ffs_extwrite(vp, &luio, IO_EXT | IO_SYNC, cred); } free(ip->i_ea_area, M_TEMP); ip->i_ea_area = NULL; ip->i_ea_len = 0; ip->i_ea_error = 0; return (error); } /* * Vnode extattr strategy routine for special devices and fifos. * * We need to check for a read or write of the external attributes. * Otherwise we just fall through and do the usual thing. */ static int ffsext_strategy(struct vop_strategy_args *ap) /* struct vop_strategy_args { struct vnodeop_desc *a_desc; struct vnode *a_vp; struct buf *a_bp; }; */ { struct vnode *vp; daddr_t lbn; KASSERT(ap->a_vp == ap->a_bp->b_vp, ("%s(%p != %p)", __func__, ap->a_vp, ap->a_bp->b_vp)); vp = ap->a_vp; lbn = ap->a_bp->b_lblkno; if (VTOI(vp)->i_fs->fs_magic == FS_UFS2_MAGIC && lbn < 0 && lbn >= -NXADDR) return (ufs_vnoperate((struct vop_generic_args *)ap)); if (vp->v_type == VFIFO) return (ufs_vnoperatefifo((struct vop_generic_args *)ap)); return (ufs_vnoperatespec((struct vop_generic_args *)ap)); } /* * Vnode extattr transaction commit/abort */ static int ffs_openextattr(struct vop_openextattr_args *ap) /* struct vop_openextattr_args { struct vnodeop_desc *a_desc; struct vnode *a_vp; IN struct ucred *a_cred; IN struct thread *a_td; }; */ { struct inode *ip; struct fs *fs; ip = VTOI(ap->a_vp); fs = ip->i_fs; if (fs->fs_magic == FS_UFS1_MAGIC) return (ufs_vnoperate((struct vop_generic_args *)ap)); if (ap->a_vp->v_type == VCHR) return (EOPNOTSUPP); return (ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td)); } /* * Vnode extattr transaction commit/abort */ static int ffs_closeextattr(struct vop_closeextattr_args *ap) /* struct vop_closeextattr_args { struct vnodeop_desc *a_desc; struct vnode *a_vp; int a_commit; IN struct ucred *a_cred; IN struct thread *a_td; }; */ { struct inode *ip; struct fs *fs; ip = VTOI(ap->a_vp); fs = ip->i_fs; if (fs->fs_magic == FS_UFS1_MAGIC) return (ufs_vnoperate((struct vop_generic_args *)ap)); if (ap->a_vp->v_type == VCHR) return (EOPNOTSUPP); return (ffs_close_ea(ap->a_vp, ap->a_commit, ap->a_cred, ap->a_td)); } /* * Vnode operation to remove a named attribute. */ static int ffs_deleteextattr(struct vop_deleteextattr_args *ap) /* vop_deleteextattr { IN struct vnode *a_vp; IN int a_attrnamespace; IN const char *a_name; IN struct ucred *a_cred; IN struct thread *a_td; }; */ { struct inode *ip; struct fs *fs; uint32_t ealength, ul; int ealen, olen, eapad1, eapad2, error, i, easize; u_char *eae, *p; int stand_alone; ip = VTOI(ap->a_vp); fs = ip->i_fs; if (fs->fs_magic == FS_UFS1_MAGIC) return (ufs_vnoperate((struct vop_generic_args *)ap)); if (ap->a_vp->v_type == VCHR) return (EOPNOTSUPP); if (strlen(ap->a_name) == 0) return (EINVAL); error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_td, IWRITE); if (error) { if (ip->i_ea_area != NULL && ip->i_ea_error == 0) ip->i_ea_error = error; return (error); } if (ip->i_ea_area == NULL) { error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td); if (error) return (error); stand_alone = 1; } else { stand_alone = 0; } ealength = eapad1 = ealen = eapad2 = 0; eae = malloc(ip->i_ea_len, M_TEMP, M_WAITOK); bcopy(ip->i_ea_area, eae, ip->i_ea_len); easize = ip->i_ea_len; olen = ffs_findextattr(eae, easize, ap->a_attrnamespace, ap->a_name, &p, NULL); if (olen == -1) { /* delete but nonexistent */ free(eae, M_TEMP); if (stand_alone) ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td); return(ENOATTR); } bcopy(p, &ul, sizeof ul); i = p - eae + ul; if (ul != ealength) { bcopy(p + ul, p + ealength, easize - i); easize += (ealength - ul); } if (easize > NXADDR * fs->fs_bsize) { free(eae, M_TEMP); if (stand_alone) ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td); else if (ip->i_ea_error == 0) ip->i_ea_error = ENOSPC; return(ENOSPC); } p = ip->i_ea_area; ip->i_ea_area = eae; ip->i_ea_len = easize; free(p, M_TEMP); if (stand_alone) error = ffs_close_ea(ap->a_vp, 1, ap->a_cred, ap->a_td); return(error); } /* * Vnode operation to retrieve a named extended attribute. */ static int ffs_getextattr(struct vop_getextattr_args *ap) /* vop_getextattr { IN struct vnode *a_vp; IN int a_attrnamespace; IN const char *a_name; INOUT struct uio *a_uio; OUT size_t *a_size; IN struct ucred *a_cred; IN struct thread *a_td; }; */ { struct inode *ip; struct fs *fs; u_char *eae, *p; unsigned easize; int error, ealen, stand_alone; ip = VTOI(ap->a_vp); fs = ip->i_fs; if (fs->fs_magic == FS_UFS1_MAGIC) return (ufs_vnoperate((struct vop_generic_args *)ap)); if (ap->a_vp->v_type == VCHR) return (EOPNOTSUPP); error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_td, IREAD); if (error) return (error); if (ip->i_ea_area == NULL) { error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td); if (error) return (error); stand_alone = 1; } else { stand_alone = 0; } eae = ip->i_ea_area; easize = ip->i_ea_len; ealen = ffs_findextattr(eae, easize, ap->a_attrnamespace, ap->a_name, NULL, &p); if (ealen >= 0) { error = 0; if (ap->a_size != NULL) *ap->a_size = ealen; else if (ap->a_uio != NULL) error = uiomove(p, ealen, ap->a_uio); } else error = ENOATTR; if (stand_alone) ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td); return(error); } /* * Vnode operation to retrieve extended attributes on a vnode. */ static int ffs_listextattr(struct vop_listextattr_args *ap) /* vop_listextattr { IN struct vnode *a_vp; IN int a_attrnamespace; INOUT struct uio *a_uio; OUT size_t *a_size; IN struct ucred *a_cred; IN struct thread *a_td; }; */ { struct inode *ip; struct fs *fs; u_char *eae, *p, *pe, *pn; unsigned easize; uint32_t ul; int error, ealen, stand_alone; ip = VTOI(ap->a_vp); fs = ip->i_fs; if (fs->fs_magic == FS_UFS1_MAGIC) return (ufs_vnoperate((struct vop_generic_args *)ap)); if (ap->a_vp->v_type == VCHR) return (EOPNOTSUPP); error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_td, IREAD); if (error) return (error); if (ip->i_ea_area == NULL) { error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td); if (error) return (error); stand_alone = 1; } else { stand_alone = 0; } eae = ip->i_ea_area; easize = ip->i_ea_len; error = 0; if (ap->a_size != NULL) *ap->a_size = 0; pe = eae + easize; for(p = eae; error == 0 && p < pe; p = pn) { bcopy(p, &ul, sizeof(ul)); pn = p + ul; if (pn > pe) break; p += sizeof(ul); if (*p++ != ap->a_attrnamespace) continue; p++; /* pad2 */ ealen = *p; if (ap->a_size != NULL) { *ap->a_size += ealen + 1; } else if (ap->a_uio != NULL) { error = uiomove(p, ealen + 1, ap->a_uio); } } if (stand_alone) ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td); return(error); } /* * Vnode operation to set a named attribute. */ static int ffs_setextattr(struct vop_setextattr_args *ap) /* vop_setextattr { IN struct vnode *a_vp; IN int a_attrnamespace; IN const char *a_name; INOUT struct uio *a_uio; IN struct ucred *a_cred; IN struct thread *a_td; }; */ { struct inode *ip; struct fs *fs; uint32_t ealength, ul; int ealen, olen, eapad1, eapad2, error, i, easize; u_char *eae, *p; int stand_alone; ip = VTOI(ap->a_vp); fs = ip->i_fs; if (fs->fs_magic == FS_UFS1_MAGIC) return (ufs_vnoperate((struct vop_generic_args *)ap)); if (ap->a_vp->v_type == VCHR) return (EOPNOTSUPP); if (strlen(ap->a_name) == 0) return (EINVAL); /* XXX Now unsupported API to delete EAs using NULL uio. */ if (ap->a_uio == NULL) return (EOPNOTSUPP); error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_td, IWRITE); if (error) { if (ip->i_ea_area != NULL && ip->i_ea_error == 0) ip->i_ea_error = error; return (error); } if (ip->i_ea_area == NULL) { error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td); if (error) return (error); stand_alone = 1; } else { stand_alone = 0; } ealen = ap->a_uio->uio_resid; ealength = sizeof(uint32_t) + 3 + strlen(ap->a_name); eapad1 = 8 - (ealength % 8); if (eapad1 == 8) eapad1 = 0; eapad2 = 8 - (ealen % 8); if (eapad2 == 8) eapad2 = 0; ealength += eapad1 + ealen + eapad2; eae = malloc(ip->i_ea_len + ealength, M_TEMP, M_WAITOK); bcopy(ip->i_ea_area, eae, ip->i_ea_len); easize = ip->i_ea_len; olen = ffs_findextattr(eae, easize, ap->a_attrnamespace, ap->a_name, &p, NULL); if (olen == -1) { /* new, append at end */ p = eae + easize; easize += ealength; } else { bcopy(p, &ul, sizeof ul); i = p - eae + ul; if (ul != ealength) { bcopy(p + ul, p + ealength, easize - i); easize += (ealength - ul); } } if (easize > NXADDR * fs->fs_bsize) { free(eae, M_TEMP); if (stand_alone) ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td); else if (ip->i_ea_error == 0) ip->i_ea_error = ENOSPC; return(ENOSPC); } bcopy(&ealength, p, sizeof(ealength)); p += sizeof(ealength); *p++ = ap->a_attrnamespace; *p++ = eapad2; *p++ = strlen(ap->a_name); strcpy(p, ap->a_name); p += strlen(ap->a_name); bzero(p, eapad1); p += eapad1; error = uiomove(p, ealen, ap->a_uio); if (error) { free(eae, M_TEMP); if (stand_alone) ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td); else if (ip->i_ea_error == 0) ip->i_ea_error = error; return(error); } p += ealen; bzero(p, eapad2); p = ip->i_ea_area; ip->i_ea_area = eae; ip->i_ea_len = easize; free(p, M_TEMP); if (stand_alone) error = ffs_close_ea(ap->a_vp, 1, ap->a_cred, ap->a_td); return(error); }