/* $Id: suio.c,v 1.1 1999/03/05 06:41:33 dm Exp $ */ /* * * Copyright (C) 1998 David Mazieres (dm@uun.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "suio.h" #ifdef DMALLOC const char *suiocheck_file; int suiocheck_line; extern void panic (const char *, ...) __attribute__ ((format (printf, 1, 2))); struct memsum { struct iovec iov; u_int16_t sum; int line; const char *file; }; /* Simple, IP-like checksum */ u_int16_t cksum (void *_data, int len) { u_char *data = (u_char *) _data; union { u_char b[2]; u_int16_t w; } bwu; u_int32_t sum; u_char *end; if (!len) return 0; bwu.w = 0; if ((u_long) data & 1) { bwu.b[0] = *data; sum = cksum ((char *) data + 1, len - 1); sum = ((sum >> 8) & 0xff) | ((sum & 0xff) << 8); sum += bwu.w; len = 1; } else sum = 0; end = data + (len & ~1); while (data < end) { sum += *(u_int16_t*) data; data += sizeof (u_int16_t); } if (len & 1) { bwu.b[0] = *data; sum += bwu.w; } return ~sum; } static void memsum_check (void *_ms) { struct memsum *ms = (struct memsum *) _ms; if (cksum (ms->iov.iov_base, ms->iov.iov_len) != ms->sum) panic ("%s:%d: data in uio subesquently changed!\n", ms->file, ms->line); xfree (ms); } void __suio_check (const char *file, int line, struct suio *uio, const void *base, size_t len) { if (uio->uio_iovcnt > 0) { struct memsum *ms = (struct memsum *) xmalloc (sizeof (*ms)); ms->iov.iov_base = (iovbase_t) base; ms->iov.iov_len = len; ms->sum = cksum (ms->iov.iov_base, ms->iov.iov_len); if (suiocheck_file) { ms->file = suiocheck_file; ms->line = suiocheck_line; } else { ms->file = file; ms->line = line; } uio->uio_endp = NULL; suio_callback (uio, memsum_check, ms); } } #endif /* DMALLOC */ static inline void __suio_init (struct suio *uio) { bzero (uio, offsetof (struct suio, uio_defbuf.b_data)); uio->uio_mem = uio->uio_iov = uio->uio_defiov; uio->uio_buf1 = uio->uio_bufp = &uio->uio_defbuf; uio->uio_dp = uio->uio_defbuf.b_data; uio->uio_dspace = SBUFSIZ; uio->uio_iovmax = NDEFIOV; TAILQ_INIT (&uio->uio_cb); } void suio_construct (struct suio *uio) { __suio_init (uio); } static inline void __suio_freemem (struct suio *uio) { struct s_buf *b, *n; struct iovcb *cb1, *cb2; for (cb1 = uio->uio_cb.tqh_first; cb1; cb1 = cb2) { cb2 = cb1->cb_link.tqe_next; cb1->cb_fn (cb1->cb_arg); xfree (cb1); } for (b = uio->uio_buf1; b; b = n) { n = b->b_next; if (b != &uio->uio_defbuf) xfree (b); } if (uio->uio_memalloced) xfree (uio->uio_mem); } void suio_destruct (struct suio *uio) { __suio_freemem (uio); } static inline void __suio_bfree (struct suio *uio, struct s_buf *b) { if (b == &uio->uio_defbuf) uio->uio_defbfree = 1; else xfree (b); } struct suio * #ifndef DMALLOC suio_alloc (void) #else /* DMALLOC */ __suio_alloc (const char *file, int line) #endif /* DMALLOC */ { struct suio *uio; #ifndef DMALLOC uio = (struct suio *) xmalloc (sizeof (*uio)); #else /* DMALLOC */ uio = (struct suio *) _xmalloc_leap (file, line, sizeof (*uio)); #endif /* DMALLOC */ __suio_init (uio); return (uio); } void suio_free (struct suio *uio) { __suio_freemem (uio); xfree (uio); } void suio_rembytes (struct suio *uio, u_int nbytes) { u_int64_t niov, iovn; struct s_buf *b; struct iovcb *cb1; assert (nbytes <= uio->uio_resid); #if 0 /* Blow away everything if we are asked to remove all bytes */ if (nbytes == uio->uio_resid) { niov = uio->uio_nremiov + uio->uio_iovcnt; __suio_freemem (uio); __suio_init (uio); uio->uio_nremiov = niov; return; } /* Actually, the above is potentially race-prone of a bad idea in * the presence of arbirary callbacks. */ #endif /* Adjust byte count */ uio->uio_resid -= nbytes; if (uio->uio_resid) { /* Count IOV's that are consumed in their entirety, with nbytes of * last IOV left over */ for (niov = 0; nbytes >= uio->uio_iov[niov].iov_len; niov++) nbytes -= uio->uio_iov[niov].iov_len; /* Adjust IOV pointers and counts in last IOV */ uio->uio_nremiov += niov; uio->uio_iov += niov; uio->uio_iovcnt -= niov; uio->uio_iovmax -= niov; if (nbytes) { uio->uio_iov[0].iov_len -= nbytes; uio->uio_iov[0].iov_base = (iovbase_t) ((char *) uio->uio_iov[0].iov_base + nbytes); } } else { /* All IOV's have been consumed, so we might as well start over at * uio_mem. */ niov = uio->uio_iovcnt; uio->uio_nremiov += niov; uio->uio_iovcnt = 0; uio->uio_iovmax += uio->uio_mem - uio->uio_iov; uio->uio_iov = uio->uio_mem; /* No more last IOV to append onto. */ uio->uio_endp = NULL; } /* Free any dynamically allocated s_buf's no longer needed */ b = uio->uio_buf1; while (b->b_next && (u_int) b->b_uiono <= niov) { struct s_buf *n; niov -= b->b_uiono; n = b->b_next; __suio_bfree (uio, b); b = n; } uio->uio_buf1 = b; /* make any necessary callbaks */ iovn = uio->uio_nremiov; while ((cb1 = uio->uio_cb.tqh_first) && iovn >= cb1->cb_niov) { TAILQ_REMOVE (&uio->uio_cb, cb1, cb_link); cb1->cb_fn (cb1->cb_arg); xfree (cb1); } } #ifndef DMALLOC char * suio_flatten (const struct suio *uio) #else /* DMALLOC */ char * __suio_flatten (const struct suio *uio, const char *file, int line) #endif /* DMALLOC */ { struct iovec *iovp = uio->uio_iov; struct iovec *lastiov = uio->uio_iov + uio->uio_iovcnt; #ifndef DMALLOC char *buf = (char *) xmalloc (uio->uio_resid); #else /* DMALLOC */ char *buf = (char *) _xmalloc_leap (file, line, uio->uio_resid); #endif /* DMALLOC */ char *cp = buf; while (iovp < lastiov) { int n = iovp->iov_len; memcpy (cp, iovp->iov_base, n); cp += n; iovp++; } return buf; } char * __suio_newbuf (struct suio *uio) { struct s_buf *b; if (uio->uio_defbfree) { b = &uio->uio_defbuf; uio->uio_defbfree = 0; } else b = (struct s_buf *) xmalloc (sizeof (*b)); uio->uio_bufp->b_next = b; uio->uio_bufp = b; uio->uio_dp = b->b_data; uio->uio_dspace = SBUFSIZ; b->b_next = NULL; b->b_uiono = 0; return (b->b_data); } void __suio_newiov (struct suio *uio) { u_int n; struct iovec *newiov; n = uio->uio_iov - uio->uio_mem; /* Wasted slots below uio_iov */ if (n >= uio->uio_iovmax) { memmove (uio->uio_mem, uio->uio_iov, uio->uio_iovcnt * sizeof (struct iovec)); uio->uio_iov = uio->uio_mem; uio->uio_iovmax += n; return; } n = 2 * (n + uio->uio_iovmax); /* Allocate twice the number of old slots */ newiov = (struct iovec *) xmalloc (n * sizeof (struct iovec)); memcpy (newiov, uio->uio_iov, uio->uio_iovcnt * sizeof (struct iovec)); if (uio->uio_memalloced) xfree (uio->uio_mem); uio->uio_iov = uio->uio_mem = newiov; uio->uio_memalloced = 1; uio->uio_iovmax = n; } void __suio_copy (struct suio *uio, const char *data, u_int len) { char *dp; u_int n; if (len > SBUFSIZ) { dp = (char *) xmalloc (len); memcpy (dp, data, len); __suio_addiov (uio, dp, len); suio_callfree (uio, dp); return; } dp = uio->uio_dp; n = len < (u_int) uio->uio_dspace ? len : uio->uio_dspace; memcpy (dp, data, n); __suio_addiov (uio, dp, n); while (len - n >= SBUFSIZ) { dp = __suio_newbuf (uio); memcpy (dp, data + n, SBUFSIZ); __suio_addiov (uio, dp, SBUFSIZ); n += SBUFSIZ; } if (len > n) { dp = __suio_newbuf (uio); memcpy (dp, data + n, len - n); __suio_addiov (uio, dp, len - n); } uio->uio_dp = uio->uio_endp; uio->uio_dspace = SBUFSIZ - (uio->uio_dp - uio->uio_bufp->b_data); } void __suio_fill (struct suio *uio, char c, u_int n) { char *base; base = __suio_newbuf (uio); memset (base, c, SBUFSIZ); while (n > SBUFSIZ) { suio_print (uio, base, SBUFSIZ); n -= SBUFSIZ; } if (n > 0) suio_print (uio, base, n); } void suio_cat (struct suio *dst, const struct suio *src) { u_int i; for (i = 0; i < src->uio_iovcnt; i++) { if (dst->uio_iovcnt >= dst->uio_iovmax) __suio_newiov (dst); dst->uio_resid += src->uio_iov[i].iov_len; dst->uio_iov[dst->uio_iovcnt++] = src->uio_iov[i]; } } void suio_move (struct suio *dst, struct suio *src) { struct iovcb *cb, *ocb; cb = src->uio_cb.tqh_first; TAILQ_INIT (&src->uio_cb); while (src->uio_iovcnt) { if ((char *) src->uio_iov->iov_base >= src->uio_buf1->b_data && (char *) src->uio_iov->iov_base < src->uio_buf1->b_data + SBUFSIZ) suio_copy (dst, src->uio_iov->iov_base, src->uio_iov->iov_len); else __suio_addiov (dst, src->uio_iov->iov_base, src->uio_iov->iov_len); suio_rembytes (src, src->uio_iov->iov_len); while (cb && src->uio_nremiov >= cb->cb_niov) { ocb = cb; cb = cb->cb_link.tqe_next; ocb->cb_niov = dst->uio_nremiov + dst->uio_iovcnt; TAILQ_INSERT_TAIL (&dst->uio_cb, ocb, cb_link); } } assert (!cb); } void suio_copyv (struct suio *uio, const struct iovec *iov, int cnt, u_int skip) { u_int size = iovsize (iov, cnt); u_int n; const struct iovec *iovp; char *buf; char *cp; assert (skip <= size); size -= skip; n = size; iovp = iov + cnt; if (!size) return; buf = (char *) xmalloc (size); cp = buf + size; while (iovp-- > iov && iovp->iov_len <= n) { n -= iovp->iov_len; cp -= iovp->iov_len; memcpy (cp, iovp->iov_base, iovp->iov_len); } assert (cp - buf == (int) n && n <= size); if (n > 0) { assert (iovp > iov); memcpy (buf, (char *) iovp->iov_base + iovp->iov_len - n, n); } #if 0 { size_t nc = 0; for (int i = 0; i < cnt; i++) { u_char *p = (u_char *) iov[i].iov_base; u_char *e = p + iov[i].iov_len; while (p < e) { if (nc > size + skip) panic ("suio_copyv: calculated size was wrong\n"); if (nc >= skip && buf[nc - skip] != *p) panic ("suio_copyv: wrong byte\n"); nc++; p++; } } } #endif suio_print (uio, buf, size); suio_callfree (uio, buf); } #undef MIN3 #undef MIN2 #define MIN3(a, b, c) \ ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c))) #define MIN2(a, b) ((a) < (b) ? (a) : (b)) #ifndef _KERNEL int suio_output (struct suio *uio, int fd, int cnt) { ssize_t n = 0; if (cnt < 0) while (uio->uio_resid && (n = writev (fd, uio->uio_iov, MIN2 (uio->uio_iovcnt, UIO_MAXIOV))) > 0) suio_rembytes (uio, n); else { u_int64_t maxiovno = uio->uio_nremiov + cnt; while (uio->uio_resid && uio->uio_nremiov < maxiovno && (n = writev (fd, uio->uio_iov, MIN2((int) (maxiovno - uio->uio_nremiov), UIO_MAXIOV))) > 0) suio_rembytes (uio, n); } if (n > 0) return 1; if (n == 0 || errno == EAGAIN) return 0; return -1; } #endif /* !_KERNEL */ size_t iovsize (const struct iovec *iov, int cnt) { const struct iovec *end; size_t size = 0; for (end = iov + cnt; iov < end; iov++) size += iov->iov_len; return size; } #ifdef __cplusplus static inline void __makecbv (void *_cb) { callback::ref &cb = *static_cast::ref *> (_cb); (*cb) (); delete &cb; } #endif /* __cplusplus */