/* * mbuf.c * Memory buffer operations. * * Copyright (c) 2005 Marko Kreen * 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. * * contrib/pgcrypto/mbuf.c */ #include "postgres.h" #include "mbuf.h" #include "px.h" #define STEP (16*1024) struct MBuf { uint8 *data; uint8 *data_end; uint8 *read_pos; uint8 *buf_end; bool no_write; bool own_data; }; int mbuf_avail(MBuf *mbuf) { return mbuf->data_end - mbuf->read_pos; } int mbuf_size(MBuf *mbuf) { return mbuf->data_end - mbuf->data; } int mbuf_free(MBuf *mbuf) { if (mbuf->own_data) { px_memset(mbuf->data, 0, mbuf->buf_end - mbuf->data); pfree(mbuf->data); } pfree(mbuf); return 0; } static void prepare_room(MBuf *mbuf, int block_len) { uint8 *newbuf; unsigned newlen; if (mbuf->data_end + block_len <= mbuf->buf_end) return; newlen = (mbuf->buf_end - mbuf->data) + ((block_len + STEP + STEP - 1) & -STEP); newbuf = repalloc(mbuf->data, newlen); mbuf->buf_end = newbuf + newlen; mbuf->data_end = newbuf + (mbuf->data_end - mbuf->data); mbuf->read_pos = newbuf + (mbuf->read_pos - mbuf->data); mbuf->data = newbuf; } int mbuf_append(MBuf *dst, const uint8 *buf, int len) { if (dst->no_write) { px_debug("mbuf_append: no_write"); return PXE_BUG; } prepare_room(dst, len); memcpy(dst->data_end, buf, len); dst->data_end += len; return 0; } MBuf * mbuf_create(int len) { MBuf *mbuf; if (!len) len = 8192; mbuf = palloc(sizeof *mbuf); mbuf->data = palloc(len); mbuf->buf_end = mbuf->data + len; mbuf->data_end = mbuf->data; mbuf->read_pos = mbuf->data; mbuf->no_write = false; mbuf->own_data = true; return mbuf; } MBuf * mbuf_create_from_data(uint8 *data, int len) { MBuf *mbuf; mbuf = palloc(sizeof *mbuf); mbuf->data = (uint8 *) data; mbuf->buf_end = mbuf->data + len; mbuf->data_end = mbuf->data + len; mbuf->read_pos = mbuf->data; mbuf->no_write = true; mbuf->own_data = false; return mbuf; } int mbuf_grab(MBuf *mbuf, int len, uint8 **data_p) { if (len > mbuf_avail(mbuf)) len = mbuf_avail(mbuf); mbuf->no_write = true; *data_p = mbuf->read_pos; mbuf->read_pos += len; return len; } int mbuf_steal_data(MBuf *mbuf, uint8 **data_p) { int len = mbuf_size(mbuf); mbuf->no_write = true; mbuf->own_data = false; *data_p = mbuf->data; mbuf->data = mbuf->data_end = mbuf->read_pos = mbuf->buf_end = NULL; return len; } /* * PullFilter */ struct PullFilter { PullFilter *src; const PullFilterOps *op; int buflen; uint8 *buf; int pos; void *priv; }; int pullf_create(PullFilter **pf_p, const PullFilterOps *op, void *init_arg, PullFilter *src) { PullFilter *pf; void *priv; int res; if (op->init != NULL) { res = op->init(&priv, init_arg, src); if (res < 0) return res; } else { priv = init_arg; res = 0; } pf = palloc0(sizeof(*pf)); pf->buflen = res; pf->op = op; pf->priv = priv; pf->src = src; if (pf->buflen > 0) { pf->buf = palloc(pf->buflen); pf->pos = 0; } else { pf->buf = NULL; pf->pos = 0; } *pf_p = pf; return 0; } void pullf_free(PullFilter *pf) { if (pf->op->free) pf->op->free(pf->priv); if (pf->buf) { px_memset(pf->buf, 0, pf->buflen); pfree(pf->buf); } px_memset(pf, 0, sizeof(*pf)); pfree(pf); } /* may return less data than asked, 0 means eof */ int pullf_read(PullFilter *pf, int len, uint8 **data_p) { int res; if (pf->op->pull) { if (pf->buflen && len > pf->buflen) len = pf->buflen; res = pf->op->pull(pf->priv, pf->src, len, data_p, pf->buf, pf->buflen); } else res = pullf_read(pf->src, len, data_p); return res; } int pullf_read_max(PullFilter *pf, int len, uint8 **data_p, uint8 *tmpbuf) { int res, total; uint8 *tmp; res = pullf_read(pf, len, data_p); if (res <= 0 || res == len) return res; /* read was shorter, use tmpbuf */ memcpy(tmpbuf, *data_p, res); *data_p = tmpbuf; len -= res; total = res; while (len > 0) { res = pullf_read(pf, len, &tmp); if (res < 0) { /* so the caller must clear only on success */ px_memset(tmpbuf, 0, total); return res; } if (res == 0) break; memcpy(tmpbuf + total, tmp, res); total += res; len -= res; } return total; } /* * caller wants exactly len bytes and don't bother with references */ int pullf_read_fixed(PullFilter *src, int len, uint8 *dst) { int res; uint8 *p; res = pullf_read_max(src, len, &p, dst); if (res < 0) return res; if (res != len) { px_debug("pullf_read_fixed: need=%d got=%d", len, res); return PXE_PGP_CORRUPT_DATA; } if (p != dst) memcpy(dst, p, len); return 0; } /* * read from MBuf */ static int pull_from_mbuf(void *arg, PullFilter *src, int len, uint8 **data_p, uint8 *buf, int buflen) { MBuf *mbuf = arg; return mbuf_grab(mbuf, len, data_p); } static const struct PullFilterOps mbuf_reader = { NULL, pull_from_mbuf, NULL }; int pullf_create_mbuf_reader(PullFilter **mp_p, MBuf *src) { return pullf_create(mp_p, &mbuf_reader, src, NULL); } /* * PushFilter */ struct PushFilter { PushFilter *next; const PushFilterOps *op; int block_size; uint8 *buf; int pos; void *priv; }; int pushf_create(PushFilter **mp_p, const PushFilterOps *op, void *init_arg, PushFilter *next) { PushFilter *mp; void *priv; int res; if (op->init != NULL) { res = op->init(next, init_arg, &priv); if (res < 0) return res; } else { priv = init_arg; res = 0; } mp = palloc0(sizeof(*mp)); mp->block_size = res; mp->op = op; mp->priv = priv; mp->next = next; if (mp->block_size > 0) { mp->buf = palloc(mp->block_size); mp->pos = 0; } else { mp->buf = NULL; mp->pos = 0; } *mp_p = mp; return 0; } void pushf_free(PushFilter *mp) { if (mp->op->free) mp->op->free(mp->priv); if (mp->buf) { px_memset(mp->buf, 0, mp->block_size); pfree(mp->buf); } px_memset(mp, 0, sizeof(*mp)); pfree(mp); } void pushf_free_all(PushFilter *mp) { PushFilter *tmp; while (mp) { tmp = mp->next; pushf_free(mp); mp = tmp; } } static int wrap_process(PushFilter *mp, const uint8 *data, int len) { int res; if (mp->op->push != NULL) res = mp->op->push(mp->next, mp->priv, data, len); else res = pushf_write(mp->next, data, len); if (res > 0) return PXE_BUG; return res; } /* consumes all data, returns len on success */ int pushf_write(PushFilter *mp, const uint8 *data, int len) { int need, res; /* * no buffering */ if (mp->block_size <= 0) return wrap_process(mp, data, len); /* * try to empty buffer */ need = mp->block_size - mp->pos; if (need > 0) { if (len < need) { memcpy(mp->buf + mp->pos, data, len); mp->pos += len; return 0; } memcpy(mp->buf + mp->pos, data, need); len -= need; data += need; } /* * buffer full, process */ res = wrap_process(mp, mp->buf, mp->block_size); if (res < 0) return res; mp->pos = 0; /* * now process directly from data */ while (len > 0) { if (len > mp->block_size) { res = wrap_process(mp, data, mp->block_size); if (res < 0) return res; data += mp->block_size; len -= mp->block_size; } else { memcpy(mp->buf, data, len); mp->pos += len; break; } } return 0; } int pushf_flush(PushFilter *mp) { int res; while (mp) { if (mp->block_size > 0) { res = wrap_process(mp, mp->buf, mp->pos); if (res < 0) return res; } if (mp->op->flush) { res = mp->op->flush(mp->next, mp->priv); if (res < 0) return res; } mp = mp->next; } return 0; } /* * write to MBuf */ static int push_into_mbuf(PushFilter *next, void *arg, const uint8 *data, int len) { int res = 0; MBuf *mbuf = arg; if (len > 0) res = mbuf_append(mbuf, data, len); return res < 0 ? res : 0; } static const struct PushFilterOps mbuf_filter = { NULL, push_into_mbuf, NULL, NULL }; int pushf_create_mbuf_writer(PushFilter **res, MBuf *dst) { return pushf_create(res, &mbuf_filter, dst, NULL); }