| /* |
| * libdpkg - Debian packaging suite library routines |
| * buffer.c - buffer I/O handling routines |
| * |
| * Copyright © 1999, 2000 Wichert Akkerman <wakkerma@debian.org> |
| * Copyright © 2000-2003 Adam Heath <doogie@debian.org> |
| * Copyright © 2008-2011 Guillem Jover <guillem@debian.org> |
| * |
| * This 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 of the License, or |
| * (at your option) any later version. |
| * |
| * This 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, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <config.h> |
| #include <compat.h> |
| |
| #include <sys/types.h> |
| |
| #include <errno.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| |
| #include <dpkg/i18n.h> |
| #include <dpkg/dpkg.h> |
| #include <dpkg/varbuf.h> |
| #include <dpkg/md5.h> |
| #include <dpkg/fdio.h> |
| #include <dpkg/buffer.h> |
| |
| struct buffer_md5_ctx { |
| struct MD5Context ctx; |
| char *hash; |
| }; |
| |
| static void |
| buffer_md5_init(struct buffer_data *data) |
| { |
| struct buffer_md5_ctx *ctx; |
| |
| ctx = m_malloc(sizeof(*ctx)); |
| ctx->hash = data->arg.ptr; |
| data->arg.ptr = ctx; |
| MD5Init(&ctx->ctx); |
| } |
| |
| static off_t |
| buffer_filter_init(struct buffer_data *data) |
| { |
| switch (data->type) { |
| case BUFFER_FILTER_NULL: |
| break; |
| case BUFFER_FILTER_MD5: |
| buffer_md5_init(data); |
| break; |
| } |
| return 0; |
| } |
| |
| static off_t |
| buffer_filter_update(struct buffer_data *filter, const void *buf, off_t length) |
| { |
| off_t ret = length; |
| |
| switch (filter->type) { |
| case BUFFER_FILTER_NULL: |
| break; |
| case BUFFER_FILTER_MD5: |
| MD5Update(&(((struct buffer_md5_ctx *)filter->arg.ptr)->ctx), |
| buf, length); |
| break; |
| default: |
| internerr("unknown data type '%i' in buffer_filter_update", |
| filter->type); |
| } |
| |
| return ret; |
| } |
| |
| static void |
| buffer_md5_done(struct buffer_data *data) |
| { |
| struct buffer_md5_ctx *ctx; |
| unsigned char digest[16], *p = digest; |
| char *hash; |
| int i; |
| |
| ctx = (struct buffer_md5_ctx *)data->arg.ptr; |
| hash = ctx->hash; |
| MD5Final(digest, &ctx->ctx); |
| for (i = 0; i < 16; ++i) { |
| sprintf(hash, "%02x", *p++); |
| hash += 2; |
| } |
| *hash = '\0'; |
| free(ctx); |
| } |
| |
| static off_t |
| buffer_filter_done(struct buffer_data *data) |
| { |
| switch (data->type) { |
| case BUFFER_FILTER_NULL: |
| break; |
| case BUFFER_FILTER_MD5: |
| buffer_md5_done(data); |
| break; |
| } |
| return 0; |
| } |
| |
| static off_t |
| buffer_write(struct buffer_data *data, const void *buf, off_t length) |
| { |
| off_t ret = length; |
| |
| switch (data->type) { |
| case BUFFER_WRITE_VBUF: |
| varbuf_add_buf((struct varbuf *)data->arg.ptr, buf, length); |
| break; |
| case BUFFER_WRITE_FD: |
| ret = fd_write(data->arg.i, buf, length); |
| break; |
| case BUFFER_WRITE_NULL: |
| break; |
| default: |
| internerr("unknown data type '%i' in buffer_write", |
| data->type); |
| } |
| |
| return ret; |
| } |
| |
| static off_t |
| buffer_read(struct buffer_data *data, void *buf, off_t length) |
| { |
| off_t ret; |
| |
| switch (data->type) { |
| case BUFFER_READ_FD: |
| ret = fd_read(data->arg.i, buf, length); |
| break; |
| default: |
| internerr("unknown data type '%i' in buffer_read\n", |
| data->type); |
| } |
| |
| return ret; |
| } |
| |
| off_t |
| buffer_filter(const void *input, void *output, int type, off_t limit) |
| { |
| struct buffer_data data = { .arg.ptr = output, .type = type }; |
| off_t ret; |
| |
| buffer_filter_init(&data); |
| ret = buffer_filter_update(&data, input, limit); |
| buffer_filter_done(&data); |
| |
| return ret; |
| } |
| |
| static off_t |
| buffer_copy(struct buffer_data *read_data, |
| struct buffer_data *filter, |
| struct buffer_data *write_data, |
| off_t limit, const char *desc) |
| { |
| char *buf; |
| int bufsize = 32768; |
| off_t bytesread = 0, byteswritten = 0; |
| off_t totalread = 0, totalwritten = 0; |
| |
| if ((limit != -1) && (limit < bufsize)) |
| bufsize = limit; |
| if (bufsize == 0) |
| return 0; |
| |
| buf = m_malloc(bufsize); |
| |
| buffer_filter_init(filter); |
| |
| while (bufsize > 0) { |
| bytesread = buffer_read(read_data, buf, bufsize); |
| if (bytesread < 0) |
| ohshite(_("failed to read on buffer copy for %s"), desc); |
| if (bytesread == 0) |
| break; |
| |
| totalread += bytesread; |
| |
| if (limit != -1) { |
| limit -= bytesread; |
| if (limit < bufsize) |
| bufsize = limit; |
| } |
| |
| buffer_filter_update(filter, buf, bytesread); |
| |
| byteswritten = buffer_write(write_data, buf, bytesread); |
| if (byteswritten < 0) |
| ohshite(_("failed in write on buffer copy for %s"), desc); |
| if (byteswritten == 0) |
| break; |
| |
| totalwritten += byteswritten; |
| } |
| |
| if (limit > 0) |
| ohshit(_("short read on buffer copy for %s"), desc); |
| |
| buffer_filter_done(filter); |
| |
| free(buf); |
| |
| return totalread; |
| } |
| |
| off_t |
| buffer_copy_IntInt(int Iin, int Tin, |
| void *Pfilter, int Tfilter, |
| int Iout, int Tout, |
| off_t limit, const char *desc, ...) |
| { |
| va_list args; |
| struct buffer_data read_data = { .type = Tin, .arg.i = Iin }; |
| struct buffer_data filter = { .type = Tfilter, .arg.ptr = Pfilter }; |
| struct buffer_data write_data = { .type = Tout, .arg.i = Iout }; |
| struct varbuf v = VARBUF_INIT; |
| off_t ret; |
| |
| va_start(args, desc); |
| varbuf_vprintf(&v, desc, args); |
| va_end(args); |
| |
| ret = buffer_copy(&read_data, &filter, &write_data, limit, v.buf); |
| |
| varbuf_destroy(&v); |
| |
| return ret; |
| } |
| |
| off_t |
| buffer_copy_IntPtr(int Iin, int Tin, |
| void *Pfilter, int Tfilter, |
| void *Pout, int Tout, |
| off_t limit, const char *desc, ...) |
| { |
| va_list args; |
| struct buffer_data read_data = { .type = Tin, .arg.i = Iin }; |
| struct buffer_data filter = { .type = Tfilter, .arg.ptr = Pfilter }; |
| struct buffer_data write_data = { .type = Tout, .arg.ptr = Pout }; |
| struct varbuf v = VARBUF_INIT; |
| off_t ret; |
| |
| va_start(args, desc); |
| varbuf_vprintf(&v, desc, args); |
| va_end(args); |
| |
| ret = buffer_copy(&read_data, &filter, &write_data, limit, v.buf); |
| |
| varbuf_destroy(&v); |
| |
| return ret; |
| } |
| |
| static off_t |
| buffer_skip(struct buffer_data *input, off_t limit, const char *desc) |
| { |
| struct buffer_data output; |
| struct buffer_data filter; |
| |
| switch (input->type) { |
| case BUFFER_READ_FD: |
| if (lseek(input->arg.i, limit, SEEK_CUR) != -1) |
| return limit; |
| if (errno != ESPIPE) |
| ohshite(_("failed to seek %s"), desc); |
| break; |
| default: |
| internerr("unknown data type '%i' in buffer_skip\n", |
| input->type); |
| } |
| |
| output.type = BUFFER_WRITE_NULL; |
| output.arg.ptr = NULL; |
| filter.type = BUFFER_FILTER_NULL; |
| filter.arg.ptr = NULL; |
| |
| return buffer_copy(input, &filter, &output, limit, desc); |
| } |
| |
| off_t |
| buffer_skip_Int(int I, int T, off_t limit, const char *desc_fmt, ...) |
| { |
| va_list args; \ |
| struct buffer_data input = { .type = T, .arg.i = I }; |
| struct varbuf v = VARBUF_INIT; |
| off_t ret; |
| |
| va_start(args, desc_fmt); |
| varbuf_vprintf(&v, desc_fmt, args); |
| va_end(args); |
| |
| ret = buffer_skip(&input, limit, v.buf); |
| |
| varbuf_destroy(&v); |
| |
| return ret; |
| } |