summaryrefslogtreecommitdiffstats
path: root/src/util
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2016-09-20 18:46:40 +0200
committerLukas Slebodnik <lslebodn@redhat.com>2017-03-14 13:31:47 +0100
commit9a9b5e115b079751422be22fd252c0b283611c62 (patch)
tree6ca3c632c78c3428cb76f879bbf58b0320157af7 /src/util
parentcab319e2db4b3d85dcadbfdf4c88939df103892e (diff)
downloadsssd-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.c205
-rw-r--r--src/util/sss_iobuf.h118
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_ */