diff options
author | Jakub Hrozek <jhrozek@redhat.com> | 2016-09-20 18:46:40 +0200 |
---|---|---|
committer | Lukas Slebodnik <lslebodn@redhat.com> | 2017-03-14 13:31:47 +0100 |
commit | 9a9b5e115b079751422be22fd252c0b283611c62 (patch) | |
tree | 6ca3c632c78c3428cb76f879bbf58b0320157af7 /src/util | |
parent | cab319e2db4b3d85dcadbfdf4c88939df103892e (diff) | |
download | sssd-9a9b5e115b079751422be22fd252c0b283611c62.tar.gz sssd-9a9b5e115b079751422be22fd252c0b283611c62.tar.xz sssd-9a9b5e115b079751422be22fd252c0b283611c62.zip |
UTIL: Add a generic iobuf module
The KCM responder reads bytes and writes bytes from a buffer of bytes.
Instead of letting the caller deal with low-level handling using the
SAFEALIGN macros, this patch adds a new iobuf.c module with more
high-level functions.
The core is a iobuf struct that keeps track of the buffer, its total
capacity and a current read or write position.
There are helper function to read or write a generic buffer with a set
length. Later, we will also add convenience functions to read C data
types using the SAFEALIGN macros.
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/sss_iobuf.c | 205 | ||||
-rw-r--r-- | src/util/sss_iobuf.h | 118 |
2 files changed, 323 insertions, 0 deletions
diff --git a/src/util/sss_iobuf.c b/src/util/sss_iobuf.c new file mode 100644 index 000000000..7c72ea94d --- /dev/null +++ b/src/util/sss_iobuf.c @@ -0,0 +1,205 @@ +#include <talloc.h> + +#include "util/util.h" +#include "util/sss_iobuf.h" + +/** + * @brief The iobuf structure that holds the data, its capacity and + * a pointer to the data. + * + * @see sss_iobuf_init_empty() + * @see sss_iobuf_init_readonly() + */ +struct sss_iobuf { + uint8_t *data; /* Start of the data buffer */ + + size_t dp; /* Data pointer */ + size_t size; /* Current data buffer size */ + size_t capacity; /* Maximum capacity */ +}; + +struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx, + size_t size, + size_t capacity) +{ + struct sss_iobuf *iobuf; + uint8_t *buf; + + iobuf = talloc_zero(mem_ctx, struct sss_iobuf); + if (iobuf == NULL) { + return NULL; + } + + buf = talloc_zero_array(iobuf, uint8_t, size); + if (buf == NULL) { + talloc_free(iobuf); + return NULL; + } + + if (capacity == 0) { + capacity = SIZE_MAX / 2; + } + + iobuf->data = buf; + iobuf->size = size; + iobuf->capacity = capacity; + iobuf->dp = 0; + + return iobuf; +} + +struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx, + uint8_t *data, + size_t size) +{ + struct sss_iobuf *iobuf; + + iobuf = sss_iobuf_init_empty(mem_ctx, size, size); + if (iobuf == NULL) { + return NULL; + } + + if (data != NULL) { + memcpy(iobuf->data, data, size); + } + + return iobuf; +} + +size_t sss_iobuf_get_len(struct sss_iobuf *iobuf) +{ + if (iobuf == NULL) { + return 0; + } + + return iobuf->dp; +} + +size_t sss_iobuf_get_capacity(struct sss_iobuf *iobuf) +{ + if (iobuf == NULL) { + return 0; + } + + return iobuf->capacity; +} + +size_t sss_iobuf_get_size(struct sss_iobuf *iobuf) +{ + if (iobuf == NULL) { + return 0; + } + + return iobuf->size; +} + +uint8_t *sss_iobuf_get_data(struct sss_iobuf *iobuf) +{ + if (iobuf == NULL) { + return NULL; + } + + return iobuf->data; +} + +static size_t iobuf_get_len(struct sss_iobuf *iobuf) +{ + if (iobuf == NULL) { + return 0; + } + + return (iobuf->size - iobuf->dp); +} + +static errno_t ensure_bytes(struct sss_iobuf *iobuf, + size_t nbytes) +{ + size_t wantsize; + size_t newsize; + uint8_t *newdata; + + if (iobuf == NULL) { + return EINVAL; + } + + wantsize = iobuf->dp + nbytes; + if (wantsize <= iobuf->size) { + /* Enough space already */ + return EOK; + } + + /* Else, try to extend the iobuf */ + if (wantsize > iobuf->capacity) { + /* We will never grow past capacity */ + return ENOBUFS; + } + + /* Double the size until we add at least nbytes, but stop if we double past capacity */ + for (newsize = iobuf->size; + (newsize < wantsize) && (newsize < iobuf->capacity); + newsize *= 2) + ; + + if (newsize > iobuf->capacity) { + newsize = iobuf->capacity; + } + + newdata = talloc_realloc(iobuf, iobuf->data, uint8_t, newsize); + if (newdata == NULL) { + return ENOMEM; + } + + iobuf->data = newdata; + iobuf->size = newsize; + + return EOK; +} + +static inline uint8_t *iobuf_ptr(struct sss_iobuf *iobuf) +{ + return iobuf->data + iobuf->dp; +} + +errno_t sss_iobuf_read(struct sss_iobuf *iobuf, + size_t len, + uint8_t *_buf, + size_t *_read) +{ + size_t remaining; + + if (iobuf == NULL || _buf == NULL) { + return EINVAL; + } + + remaining = iobuf_get_len(iobuf); + if (len > remaining) { + len = remaining; + } + + safealign_memcpy(_buf, iobuf_ptr(iobuf), len, &iobuf->dp); + if (_read != NULL) { + *_read = len; + } + + return EOK; +} + +errno_t sss_iobuf_write_len(struct sss_iobuf *iobuf, + uint8_t *buf, + size_t len) +{ + errno_t ret; + + if (iobuf == NULL || buf == NULL) { + return EINVAL; + } + + ret = ensure_bytes(iobuf, len); + if (ret != EOK) { + return ret; + } + + safealign_memcpy(iobuf_ptr(iobuf), buf, len, &iobuf->dp); + + return EOK; +} diff --git a/src/util/sss_iobuf.h b/src/util/sss_iobuf.h new file mode 100644 index 000000000..eae357a40 --- /dev/null +++ b/src/util/sss_iobuf.h @@ -0,0 +1,118 @@ +#ifndef __SSS_IOBUF_H_ +#define __SSS_IOBUF_H_ + +#include <talloc.h> +#include <stdint.h> +#include <errno.h> + +#include "util/util_errors.h" + +struct sss_iobuf; + +/* + * @brief Allocate an empty IO buffer + * + * @param[in] mem_ctx The talloc context that owns the iobuf + * + * When this buffer is written into, but the capacity is exceeded, the write + * function will return an error. + * + * @param[in] mem_ctx The talloc context that owns the iobuf + * @param[in] size The size of the data buffer + * @param[in] capacity The maximum capacity the buffer can grow into. + * Use 0 for an 'unlimited' buffer that will grow + * until SIZE_MAX/2. + * + * @return The newly created buffer on success or NULL on an error. + * + */ +struct sss_iobuf *sss_iobuf_init_empty(TALLOC_CTX *mem_ctx, + size_t size, + size_t capacity); + +/* + * @brief Allocate an IO buffer with a fixed size + * + * This function is useful for parsing an input buffer from an existing + * buffer pointed to by data. + * + * The iobuf does not assume ownership of the data buffer in talloc terms, + * but copies the data instead. + * + * @param[in] mem_ctx The talloc context that owns the iobuf + * @param[in] data The data to initialize the IO buffer with. This + * data is copied into the iobuf-owned buffer. + * @param[in] size The size of the data buffer + * + * @return The newly created buffer on success or NULL on an error. + */ +struct sss_iobuf *sss_iobuf_init_readonly(TALLOC_CTX *mem_ctx, + uint8_t *data, + size_t size); + +/* + * @brief Returns the number of bytes currently stored in the iobuf + * + * @return The number of bytes (the data pointer offset) + */ +size_t sss_iobuf_get_len(struct sss_iobuf *iobuf); + +/* + * @brief Returns the capacity of the IO buffer + * + * @return The capacity of the IO buffer. Returns zero + * for an unlimited buffer. + */ +size_t sss_iobuf_get_capacity(struct sss_iobuf *iobuf); + +/* + * @brief Returns the current size of the IO buffer + */ +size_t sss_iobuf_get_size(struct sss_iobuf *iobuf); + +/* + * @brief Returns the data pointer of the IO buffer + */ +uint8_t *sss_iobuf_get_data(struct sss_iobuf *iobuf); + +/* + * @brief Read from an IO buffer + * + * Read up to len bytes from an IO buffer. It is not an error to request + * more bytes than the buffer actually has - the function will succeed, but + * return the actual number of bytes read. Reading from an empty buffer just + * returns zero bytes read. + * + * @param[in] iobuf The IO buffer to read from + * @param[in] len The maximum number of bytes to read + * @param[out] _buf The buffer to read data into from iobuf + * @param[out] _read The actual number of bytes read from IO buffer. + * + * @return EOK on success, errno otherwise + */ +errno_t sss_iobuf_read(struct sss_iobuf *iobuf, + size_t len, + uint8_t *_buf, + size_t *_read); + +/* + * @brief Write into an IO buffer + * + * Attempts to write len bytes into the iobuf. If the capacity is exceeded, + * the iobuf module tries to extend the buffer up to the maximum capacity. + * + * If reallocating the internal buffer fails, the data pointers are not + * touched. + * + * @param[in] iobuf The IO buffer to write to + * @param[in] buf The data to write into the buffer + * @param[in] len The number of bytes to write + * + * @return EOK on success, errno otherwise. Notably returns ENOBUFS if + * the buffer capacity is exceeded. + */ +errno_t sss_iobuf_write_len(struct sss_iobuf *iobuf, + uint8_t *buf, + size_t len); + +#endif /* __SSS_IOBUF_H_ */ |