diff options
Diffstat (limited to 'agent/gpg-stream.c')
-rw-r--r-- | agent/gpg-stream.c | 438 |
1 files changed, 434 insertions, 4 deletions
diff --git a/agent/gpg-stream.c b/agent/gpg-stream.c index 1139cbe3d..1fb3c4033 100644 --- a/agent/gpg-stream.c +++ b/agent/gpg-stream.c @@ -33,10 +33,430 @@ #include "gpg-stream.h" #include "gpg-stream-config.h" -#include "buffer.h" +/* Buffer management layer. */ + +#define BUFFER_BLOCK_SIZE 1024 + + + +typedef struct buffer *buffer_t; + +/* Callbacks, necessary for filling/flushing/seeking. */ +typedef gpg_error_t (*buffer_func_read_t) (void *handle, + char *buffer, + size_t bytes_to_read, + size_t *bytes_read); +typedef gpg_error_t (*buffer_func_write_t) (void *handle, + const char *buffer, + size_t bytes_to_write, + size_t *bytes_written); +typedef gpg_error_t (*buffer_func_seek_t) (void *handle, + off_t offset, + int whence); + +typedef gpg_error_t (*buffer_func_stat_t) (void *handle, + size_t *size); + +typedef struct buffer_functions +{ + buffer_func_read_t func_read; /* Read callback. */ + buffer_func_write_t func_write; /* Write callback. */ + buffer_func_seek_t func_seek; /* Seek callback. */ + buffer_func_stat_t func_stat; /* Stat callback. */ +} buffer_functions_t; + +/* Buffer context. */ +struct buffer +{ + void *handle; /* Handle, passed to callbacks. */ + buffer_functions_t functions; /* Callback functions. */ + unsigned int flags; /* General flags. */ + struct buffer_in + { + char *container; /* Container holding data. */ + size_t container_size; /* Size of CONTAINER. */ + size_t data_size; /* Size of data in CONTAINER. */ + off_t data_offset; /* Offset inside of CONTAINER. */ + } buffer_in; + struct buffer_out + { + char *container; /* Container holding data. */ + size_t container_size; /* Size of CONTAINER. */ + size_t data_size; /* Size of data in CONTAINER. */ + off_t data_offset; /* Offset inside of CONTAINER. */ + size_t data_flushed; /* Amount of data already flushed. */ + } buffer_out; +}; + + + +/* Buffer contains unflushed data. */ +#define BUFFER_FLAG_DIRTY (1 << 0) + + + +/* Fill buffer. */ +static gpg_error_t +buffer_fill_do (buffer_t buffer) +{ + gpg_error_t err = GPG_ERR_NO_ERROR; + size_t bytes_read = 0; + + if (! buffer->functions.func_read) + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + else + { + buffer_func_read_t func_read = buffer->functions.func_read; + + err = (*func_read) (buffer->handle, + buffer->buffer_in.container, + buffer->buffer_in.container_size, + &bytes_read); + } + + buffer->buffer_in.data_offset = 0; + buffer->buffer_in.data_size = bytes_read; + + return err; +} + +/* Empty buffer input. */ +static gpg_error_t +buffer_empty (buffer_t buffer) +{ + gpg_error_t err = GPG_ERR_NO_ERROR; + + buffer->buffer_in.data_size = 0; + buffer->buffer_in.data_offset = 0; + + return err; +} + +/* Flush data contained in buffer. */ +static gpg_error_t +buffer_flush_do (buffer_t buffer) +{ + buffer_func_write_t func_write = buffer->functions.func_write; + gpg_error_t err = GPG_ERR_NO_ERROR; + size_t bytes_written = 0; + + if (! func_write) + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + else if (buffer->flags & BUFFER_FLAG_DIRTY) + while ((buffer->buffer_out.data_size + - buffer->buffer_out.data_flushed) && (! err)) + { + + err = (*func_write) (buffer->handle, + buffer->buffer_out.container + + buffer->buffer_out.data_flushed, + buffer->buffer_out.data_size + - buffer->buffer_out.data_flushed, + &bytes_written); + if (! err) + { + buffer->buffer_out.data_size = 0; + buffer->buffer_out.data_offset = 0; + buffer->buffer_out.data_flushed = 0; + buffer->flags &= ~BUFFER_FLAG_DIRTY; + } + else + buffer->buffer_out.data_flushed += bytes_written; + } + + return err; +} + +static gpg_error_t +buffer_stat_do (buffer_t buffer, + size_t *size) +{ + buffer_func_stat_t func_stat = buffer->functions.func_stat; + gpg_error_t err = GPG_ERR_NO_ERROR; + + err = (*func_stat) (buffer->handle, size); + + return err; +} + + + +/* Read from a buffer. */ +gpg_error_t +buffer_read (buffer_t buffer, + char *data, + size_t bytes_to_read, + size_t *bytes_read) +{ + gpg_error_t err = GPG_ERR_NO_ERROR; + size_t data_read = 0; + size_t data_to_copy = 0; + + if (! (buffer->flags & BUFFER_FLAG_DIRTY)) + err = buffer_flush_do (buffer); + + while ((bytes_to_read - data_read) && (! err)) + { + if (buffer->buffer_in.data_offset == buffer->buffer_in.data_size) + { + /* Nothing more to read in current container, try to + fill container with new data. */ + err = buffer_fill_do (buffer); + if (! err) + if (! buffer->buffer_in.data_size) + /* Filling did not result in any data read. */ + break; + } + + if (! err) + { + if ((bytes_to_read + - data_read) > (buffer->buffer_in.data_size + - buffer->buffer_in.data_offset)) + data_to_copy = (buffer->buffer_in.data_size + - buffer->buffer_in.data_offset); + else + data_to_copy = bytes_to_read - data_read; + + memcpy (data + data_read, + buffer->buffer_in.container + buffer->buffer_in.data_offset, + data_to_copy); + buffer->buffer_in.data_offset += data_to_copy; + data_read += data_to_copy; + } + } + + if (bytes_read) + *bytes_read = data_read; + + return err; +} + +/* Write to a buffer. */ +gpg_error_t +buffer_write (buffer_t buffer, + const char *data, + size_t bytes_to_write, + size_t *bytes_written) +{ + gpg_error_t err = GPG_ERR_NO_ERROR; + size_t data_written = 0; + size_t data_to_copy = 0; + + while ((bytes_to_write - data_written) && (! err)) + { + if (buffer->buffer_out.data_offset == buffer->buffer_out.container_size) + /* Container full, flush buffer. */ + err = buffer_flush_do (buffer); + + if (! err) + { + if ((bytes_to_write + - data_written) > (buffer->buffer_out.container_size + - buffer->buffer_out.data_offset)) + data_to_copy = (buffer->buffer_out.container_size + - buffer->buffer_out.data_offset); + else + data_to_copy = bytes_to_write - data_written; + + memcpy (buffer->buffer_out.container + + buffer->buffer_out.data_offset, + data + data_written, + data_to_copy); + if ((buffer->buffer_out.data_offset + + data_to_copy) > buffer->buffer_out.data_size) + buffer->buffer_out.data_size = (buffer->buffer_out.data_offset + + data_to_copy); + buffer->buffer_out.data_offset += data_to_copy; + data_written += data_to_copy; + + if (data_written) + if (! (buffer->flags & BUFFER_FLAG_DIRTY)) + buffer->flags |= BUFFER_FLAG_DIRTY; + } + } + + if (bytes_written) + *bytes_written = data_written; + + return err; +} + +/* Seek in a buffer. */ +gpg_error_t +buffer_seek (buffer_t buffer, + off_t offset, + int whence) +{ + buffer_func_seek_t func_seek = buffer->functions.func_seek; + gpg_error_t err = GPG_ERR_NO_ERROR; + + if (! func_seek) + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + else + { + if (buffer->flags & BUFFER_FLAG_DIRTY) + /* Flush data first in order to prevent flushing it to the + wrong offset. */ + err = buffer_flush_do (buffer); + + if (! err) + err = (*func_seek) (buffer->handle, offset, whence); + + if (! err) + err = buffer_empty (buffer); + } + + return err; +} + +/* Return the unread data contained in a buffer. */ +gpg_error_t +buffer_peek (buffer_t buffer, + char **data, + size_t *data_size) +{ + gpg_error_t err = GPG_ERR_NO_ERROR; + + if (buffer->buffer_in.data_offset == buffer->buffer_in.data_size) + /* Refill container. */ + err = buffer_fill_do (buffer); + + if (! err) + { + if (data) + *data = buffer->buffer_in.container + buffer->buffer_in.data_offset; + if (data_size) + *data_size = buffer->buffer_in.data_size - buffer->buffer_in.data_offset; + } + + return err; +} + +/* Skip SIZE bytes of input data contained in buffer. */ +gpg_error_t +buffer_skip (buffer_t buffer, + size_t size) +{ + gpg_error_t err = GPG_ERR_NO_ERROR; + + if (buffer->buffer_in.data_offset + size > buffer->buffer_in.data_size) + err = gpg_error (GPG_ERR_INV_ARG); + else + buffer->buffer_in.data_offset += size; + + return err; +} + + + +/* Create a new buffer. */ +gpg_error_t +buffer_create (buffer_t *buffer, + void *handle, + buffer_functions_t functions) +{ + gpg_error_t err = GPG_ERR_NO_ERROR; + + /* Allocate memory, initialize. */ + + buffer_t buffer_new = NULL; + char *container_in_new = NULL; + char *container_out_new = NULL; + + buffer_new = malloc (sizeof (*buffer_new)); + if (! buffer_new) + err = gpg_error_from_errno (errno); + + if (! err) + { + container_in_new = malloc (BUFFER_BLOCK_SIZE); + if (! container_in_new) + err = gpg_error_from_errno (errno); + } + if (! err) + { + container_out_new = malloc (BUFFER_BLOCK_SIZE); + if (! container_out_new) + err = gpg_error_from_errno (errno); + } + + if (! err) + { + buffer_new->handle = handle; + buffer_new->flags = 0; + buffer_new->functions = functions; + buffer_new->buffer_in.container = container_in_new; + buffer_new->buffer_in.container_size = BUFFER_BLOCK_SIZE; + buffer_new->buffer_in.data_size = 0; + buffer_new->buffer_in.data_offset = 0; + buffer_new->buffer_out.container = container_out_new; + buffer_new->buffer_out.container_size = BUFFER_BLOCK_SIZE; + buffer_new->buffer_out.data_size = 0; + buffer_new->buffer_out.data_offset = 0; + buffer_new->buffer_out.data_flushed = 0; + *buffer = buffer_new; + } + else + { + if (container_in_new) + free (container_in_new); + if (container_out_new) + free (container_out_new); + if (buffer_new) + free (buffer_new); + } + + return err; +} + +/* Destroy a buffer. */ +gpg_error_t +buffer_destroy (buffer_t buffer) +{ + gpg_error_t err = GPG_ERR_NO_ERROR; + + if (buffer) + { + err = buffer_flush_do (buffer); + free (buffer->buffer_in.container); + free (buffer->buffer_out.container); + free (buffer); + } + + return err; +} + +/* Write out unwritten data contained in buffer. */ +gpg_error_t +buffer_flush (buffer_t buffer) +{ + gpg_error_t err = GPG_ERR_NO_ERROR; + + err = buffer_flush_do (buffer); + + return err; +} + +/* Stat buffer. */ +gpg_error_t +buffer_stat (buffer_t buffer, + size_t *size) +{ + gpg_error_t err = GPG_ERR_NO_ERROR; + + err = buffer_stat_do (buffer, size); + + return err; +} + + + +/* Stream layer. */ + /* A Stream Context. */ struct gpg_stream { @@ -66,6 +486,9 @@ struct gpg_stream /* Implementation of Memory I/O. */ +typedef void *(*mem_func_realloc_t) (void *mem, size_t size); +typedef void (*mem_func_free_t) (void *mem); + typedef struct gpg_stream_handle_mem { char *memory; /* Data. */ @@ -73,6 +496,8 @@ typedef struct gpg_stream_handle_mem size_t data_size; /* Size of data in MEMORY. */ unsigned int grow: 1; /* MEMORY is allowed to grow. */ size_t offset; /* Current offset in MEMORY. */ + mem_func_realloc_t func_realloc; + mem_func_free_t func_free; } *gpg_stream_handle_mem_t; static gpg_error_t @@ -94,6 +519,10 @@ gpg_stream_func_mem_create (void **handle, mem_handle->data_size = 0; mem_handle->grow = mem_spec ? mem_spec->grow : 1; mem_handle->offset = 0; + mem_handle->func_realloc = ((mem_spec && mem_spec->func_realloc) + ? mem_spec->func_realloc : realloc); + mem_handle->func_free = ((mem_spec && mem_spec->func_free) + ? mem_spec->func_free : free); *handle = mem_handle; } @@ -136,8 +565,9 @@ gpg_stream_func_mem_write (void *handle, while (bytes_to_write > mem_handle->memory_size - mem_handle->offset) { - memory_new = realloc (mem_handle->memory, - mem_handle->memory_size + BUFFER_BLOCK_SIZE); + memory_new = (*mem_handle->func_realloc) + (mem_handle->memory, + mem_handle->memory_size + BUFFER_BLOCK_SIZE); if (! memory_new) err = gpg_error_from_errno (errno); else @@ -216,7 +646,7 @@ gpg_stream_func_mem_destroy (void *handle) gpg_error_t err = GPG_ERR_NO_ERROR; if (mem_handle->memory) - free (mem_handle->memory); + (*mem_handle->func_free) (mem_handle->memory); free (mem_handle); return err; |