-
Phil Schwan authoredPhil Schwan authored
reconcile.c 7.84 KiB
/*
* 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 <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/queue.h>
#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);
}