/* * This Cplant(TM) source code is the property of Sandia National * Laboratories. * * This Cplant(TM) source code is copyrighted by Sandia National * Laboratories. * * The redistribution of this Cplant(TM) source code is subject to the * terms of the GNU Lesser General Public License * (see cit/LGPL or http://www.gnu.org/licenses/lgpl.html) * * Cplant(TM) Copyright 1998-2004 Sandia Corporation. * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive * license for use of this work by or on behalf of the US Government. * Export of this program may require a license from the United States * Government. */ /* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Questions or comments about this library should be sent to: * * Lee Ward * Sandia National Laboratories, New Mexico * P.O. Box 5800 * Albuquerque, NM 87185-1110 * * lee@sandia.gov */ #include #include #include #include #include #include #include #include #include "sysio.h" #include "xtio.h" /* * Extent-vector IO support. */ /* * Arguments to IO vector enumerator callback when used by _sysio_doio(). */ struct doio_helper_args { ssize_t (*f)(void *, size_t, _SYSIO_OFF_T, void *); /* base func */ void *arg; /* caller arg */ }; /* * General help validating strided-IO vectors. * * A driver may call this to make sure underflow/overflow of an off_t can't * occur and overflow of a ssize_t can't occur when writing. The sum * of the reconciled transfer length is returned or some appropriate * error depending on underflow/overflow. * * The following algorithm assumes: * * a) sizeof(size_t) >= sizeof(ssize_t) * b) 2's complement arithmetic * c) The compiler won't optimize away code because it's developers * believed that something with an undefined result in `C' can't happen. */ ssize_t _sysio_validx(const struct intnl_xtvec *xtv, size_t xtvlen, const struct iovec *iov, size_t iovlen, _SYSIO_OFF_T limit) { ssize_t acc, cc; struct iovec iovec; struct intnl_xtvec xtvec; _SYSIO_OFF_T off; if (!(xtvlen && iovlen)) return -EINVAL; acc = 0; xtvec.xtv_len = iovec.iov_len = 0; do { while (!xtvec.xtv_len) { if (!xtvlen--) break; if (!xtv->xtv_len) { xtv++; continue; } xtvec = *xtv++; if (xtvec.xtv_off < 0) return -EINVAL; } if (!xtvec.xtv_len) break; do { while (!iovec.iov_len) { if (!iovlen--) break; if (!iov->iov_len) { iov++; continue; } iovec = *iov++; } if (!iovec.iov_len) break; cc = iovec.iov_len; if (cc < 0) return -EINVAL; if ((size_t )cc > xtvec.xtv_len) cc = xtvec.xtv_len; xtvec.xtv_len -= cc; iovec.iov_len -= cc; off = xtvec.xtv_off + cc; if (xtvec.xtv_off && off <= xtvec.xtv_off) return off < 0 ? -EINVAL : -EOVERFLOW; if (off > limit) return -EFBIG; xtvec.xtv_off = off; cc += acc; if (acc && (cc <= acc)) return -EINVAL; acc = cc; } while (xtvec.xtv_len && iovlen); } while ((xtvlen || xtvec.xtv_len) && iovlen); return acc; } /* */ ssize_t _sysio_enumerate_extents(const struct intnl_xtvec *xtv, size_t xtvlen, const struct iovec *iov, size_t iovlen, ssize_t (*f)(const struct iovec *, int, _SYSIO_OFF_T, ssize_t, void *), void *arg) { ssize_t acc, tmp, cc; struct iovec iovec; struct intnl_xtvec xtvec; const struct iovec *start; _SYSIO_OFF_T off; size_t n; size_t remain; acc = 0; iovec.iov_len = 0; while (xtvlen) { /* * Coalesce contiguous extent vector entries. */ off = xtvec.xtv_off = xtv->xtv_off; off += xtvec.xtv_len = xtv->xtv_len; while (++xtv, --xtvlen) { if (off != xtv->xtv_off) { /* * Not contiguous. */ break; } if (!xtv->xtv_len) { /* * Zero length. */ continue; } off += xtv->xtv_len; xtvec.xtv_len += xtv->xtv_len; } while (xtvec.xtv_len) { if (iovec.iov_len) { tmp = iovec.iov_len; if (iovec.iov_len > xtvec.xtv_len) iovec.iov_len = xtvec.xtv_len; cc = (*f)(&iovec, 1, xtvec.xtv_off, xtvec.xtv_len, arg); if (cc <= 0) { if (acc) return acc; return cc; } iovec.iov_base = (char *)iovec.iov_base + cc; iovec.iov_len = tmp - cc; tmp = cc + acc; if (acc && tmp <= acc) abort(); /* paranoia */ acc = tmp; } else if (iovlen) { start = iov; n = xtvec.xtv_len; do { if (iov->iov_len > n) { /* * That'll do. */ break; } n -= iov->iov_len; iov++; } while (--iovlen); if (iov == start) { iovec = *iov++; iovlen--; continue; } remain = xtvec.xtv_len - n; cc = (*f)(start, iov - start, xtvec.xtv_off, remain, arg); if (cc <= 0) { if (acc) return acc; return cc; } tmp = cc + acc; if (acc && tmp <= acc) abort(); /* paranoia */ acc = tmp; remain -= cc; if (remain) return acc; /* short */ } else return acc; /* short out */ xtvec.xtv_off += cc; xtvec.xtv_len -= cc; } } return acc; } ssize_t _sysio_enumerate_iovec(const struct iovec *iov, size_t count, _SYSIO_OFF_T off, ssize_t limit, ssize_t (*f)(void *, size_t, _SYSIO_OFF_T, void *), void *arg) { ssize_t acc, cc; size_t n; unsigned indx; size_t remain; if (!count) return -EINVAL; assert(limit >= 0); acc = 0; n = limit; for (indx = 0; n && indx < count; indx++) { if (iov[indx].iov_len < n) { cc = (ssize_t )iov[indx].iov_len; if (cc < 0) return -EINVAL; } else cc = (ssize_t )n; if (!cc) continue; n -= cc; cc += acc; if (acc && cc <= acc) return -EINVAL; acc = cc; } if (!acc) return 0; acc = 0; do { if (!iov->iov_len) { iov++; continue; } n = iov->iov_len < (size_t )limit ? iov->iov_len : (size_t )limit; cc = (*f)(iov->iov_base, n, off, arg); if (cc <= 0) { if (acc) return acc; return cc; } off += cc; limit -= cc; remain = iov->iov_len - cc; cc += acc; if (acc && cc <= acc) abort(); /* bad driver! */ acc = cc; if (remain || !limit) break; /* short/limited read */ iov++; } while (--count); return acc; } static ssize_t _sysio_doio_helper(const struct iovec *iov, int count, _SYSIO_OFF_T off, ssize_t limit, struct doio_helper_args *args) { return _sysio_enumerate_iovec(iov, count, off, limit, args->f, args->arg); } /* * A meta-driver for the whole strided-io process. Appropriate when * the driver can't handle anything but simple p{read,write}-like * interface. */ ssize_t _sysio_doio(const struct intnl_xtvec *xtv, size_t xtvlen, const struct iovec *iov, size_t iovlen, ssize_t (*f)(void *, size_t, _SYSIO_OFF_T, void *), void *arg) { struct doio_helper_args arguments; arguments.f = f; arguments.arg = arg; return _sysio_enumerate_extents(xtv, xtvlen, iov, iovlen, (ssize_t (*)(const struct iovec *, int, _SYSIO_OFF_T, ssize_t, void *))_sysio_doio_helper, &arguments); }