Blame lib/iov.c

Packit Service 4684c1
/*
Packit Service 4684c1
 * Copyright (C) 2019 Red Hat, Inc.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Author: Daiki Ueno
Packit Service 4684c1
 *
Packit Service 4684c1
 * This file is part of GnuTLS.
Packit Service 4684c1
 *
Packit Service 4684c1
 * The GnuTLS is free software; you can redistribute it and/or
Packit Service 4684c1
 * modify it under the terms of the GNU Lesser General Public License
Packit Service 4684c1
 * as published by the Free Software Foundation; either version 2.1 of
Packit Service 4684c1
 * the License, or (at your option) any later version.
Packit Service 4684c1
 *
Packit Service 4684c1
 * This library is distributed in the hope that it will be useful, but
Packit Service 4684c1
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 4684c1
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 4684c1
 * Lesser General Public License for more details.
Packit Service 4684c1
 *
Packit Service 4684c1
 * You should have received a copy of the GNU Lesser General Public License
Packit Service 4684c1
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
Packit Service 4684c1
 *
Packit Service 4684c1
 */
Packit Service 4684c1
Packit Service 4684c1
#include "gnutls_int.h"
Packit Service 4684c1
#include "iov.h"
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * _gnutls_iov_iter_init:
Packit Service 4684c1
 * @iter: the iterator
Packit Service 4684c1
 * @iov: the data buffers
Packit Service 4684c1
 * @iov_count: the number of data buffers
Packit Service 4684c1
 * @block_size: block size to iterate
Packit Service 4684c1
 *
Packit Service 4684c1
 * Initialize the iterator.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
Packit Service 4684c1
 *   an error code is returned
Packit Service 4684c1
 */
Packit Service 4684c1
int
Packit Service 4684c1
_gnutls_iov_iter_init(struct iov_iter_st *iter,
Packit Service 4684c1
		      const giovec_t *iov, size_t iov_count,
Packit Service 4684c1
		      size_t block_size)
Packit Service 4684c1
{
Packit Service 4684c1
	if (unlikely(block_size > MAX_CIPHER_BLOCK_SIZE))
Packit Service 4684c1
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
Packit Service 4684c1
Packit Service 4684c1
	iter->iov = iov;
Packit Service 4684c1
	iter->iov_count = iov_count;
Packit Service 4684c1
	iter->iov_index = 0;
Packit Service 4684c1
	iter->iov_offset = 0;
Packit Service 4684c1
	iter->block_size = block_size;
Packit Service 4684c1
	iter->block_offset = 0;
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * _gnutls_iov_iter_next:
Packit Service 4684c1
 * @iter: the iterator
Packit Service 4684c1
 * @data: the return location of extracted data
Packit Service 4684c1
 *
Packit Service 4684c1
 * Retrieve block(s) pointed by @iter and advance it to the next
Packit Service 4684c1
 * position.  It returns the number of bytes in @data.  At the end of
Packit Service 4684c1
 * iteration, 0 is returned.
Packit Service 4684c1
 *
Packit Service 4684c1
 * If the data stored in @iter is not multiple of the block size, the
Packit Service 4684c1
 * remaining data is stored in the "block" field of @iter with the
Packit Service 4684c1
 * size stored in the "block_offset" field.
Packit Service 4684c1
 *
Packit Service 4684c1
 * Returns: On success, a value greater than or equal to zero is
Packit Service 4684c1
 *   returned, otherwise a negative error code is returned
Packit Service 4684c1
 */
Packit Service 4684c1
ssize_t
Packit Service 4684c1
_gnutls_iov_iter_next(struct iov_iter_st *iter, uint8_t **data)
Packit Service 4684c1
{
Packit Service 4684c1
	while (iter->iov_index < iter->iov_count) {
Packit Service 4684c1
		const giovec_t *iov = &iter->iov[iter->iov_index];
Packit Service 4684c1
		uint8_t *p = iov->iov_base;
Packit Service 4684c1
		size_t len = iov->iov_len;
Packit Service 4684c1
		size_t block_left;
Packit Service 4684c1
Packit Service 4684c1
		if (!p) {
Packit Service 4684c1
			// skip NULL iov entries, else we run into issues below
Packit Service 4684c1
			iter->iov_index++;
Packit Service 4684c1
			continue;
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		if (unlikely(len < iter->iov_offset))
Packit Service 4684c1
			return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
Packit Service 4684c1
		len -= iter->iov_offset;
Packit Service 4684c1
		p += iter->iov_offset;
Packit Service 4684c1
Packit Service 4684c1
		/* We have at least one full block, return a whole set
Packit Service 4684c1
		 * of full blocks immediately. */
Packit Service 4684c1
		if (iter->block_offset == 0 && len >= iter->block_size) {
Packit Service 4684c1
			if ((len % iter->block_size) == 0) {
Packit Service 4684c1
				iter->iov_index++;
Packit Service 4684c1
				iter->iov_offset = 0;
Packit Service 4684c1
			} else {
Packit Service 4684c1
				len -= (len % iter->block_size);
Packit Service 4684c1
				iter->iov_offset += len;
Packit Service 4684c1
			}
Packit Service 4684c1
Packit Service 4684c1
			/* Return the blocks. */
Packit Service 4684c1
			*data = p;
Packit Service 4684c1
			return len;
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		/* We can complete one full block to return. */
Packit Service 4684c1
		block_left = iter->block_size - iter->block_offset;
Packit Service 4684c1
		if (len >= block_left) {
Packit Service 4684c1
			memcpy(iter->block + iter->block_offset, p, block_left);
Packit Service 4684c1
			if (len == block_left) {
Packit Service 4684c1
				iter->iov_index++;
Packit Service 4684c1
				iter->iov_offset = 0;
Packit Service 4684c1
			} else
Packit Service 4684c1
				iter->iov_offset += block_left;
Packit Service 4684c1
			iter->block_offset = 0;
Packit Service 4684c1
Packit Service 4684c1
			/* Return the filled block. */
Packit Service 4684c1
			*data = iter->block;
Packit Service 4684c1
			return iter->block_size;
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		/* Not enough data for a full block, store in temp
Packit Service 4684c1
		 * memory and continue. */
Packit Service 4684c1
		memcpy(iter->block + iter->block_offset, p, len);
Packit Service 4684c1
		iter->block_offset += len;
Packit Service 4684c1
		iter->iov_index++;
Packit Service 4684c1
		iter->iov_offset = 0;
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	if (iter->block_offset > 0) {
Packit Service 4684c1
		size_t len = iter->block_offset;
Packit Service 4684c1
Packit Service 4684c1
		/* Return the incomplete block. */
Packit Service 4684c1
		*data = iter->block;
Packit Service 4684c1
		iter->block_offset = 0;
Packit Service 4684c1
		return len;
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/**
Packit Service 4684c1
 * _gnutls_iov_iter_sync:
Packit Service 4684c1
 * @iter: the iterator
Packit Service 4684c1
 * @data: data returned by _gnutls_iov_iter_next
Packit Service 4684c1
 * @data_size: size of @data
Packit Service 4684c1
 *
Packit Service 4684c1
 * Flush the content of temp buffer (if any) to the data buffer.
Packit Service 4684c1
 */
Packit Service 4684c1
int
Packit Service 4684c1
_gnutls_iov_iter_sync(struct iov_iter_st *iter, const uint8_t *data,
Packit Service 4684c1
		      size_t data_size)
Packit Service 4684c1
{
Packit Service 4684c1
	size_t iov_index;
Packit Service 4684c1
	size_t iov_offset;
Packit Service 4684c1
Packit Service 4684c1
	/* We didn't return the cached block. */
Packit Service 4684c1
	if (data != iter->block)
Packit Service 4684c1
		return 0;
Packit Service 4684c1
Packit Service 4684c1
	iov_index = iter->iov_index;
Packit Service 4684c1
	iov_offset = iter->iov_offset;
Packit Service 4684c1
Packit Service 4684c1
	/* When syncing a cache block we walk backwards because we only have a
Packit Service 4684c1
	 * pointer to were the block ends in the iovec, walking backwards is
Packit Service 4684c1
	 * fine as we are always writing a full block, so the whole content
Packit Service 4684c1
	 * is written in the right places:
Packit Service 4684c1
	 * iovec:     |--0--|---1---|--2--|-3-|
Packit Service 4684c1
	 * block:     |-----------------------|
Packit Service 4684c1
	 * 1st write                      |---|
Packit Service 4684c1
	 * 2nd write                |-----
Packit Service 4684c1
	 * 3rd write        |-------
Packit Service 4684c1
	 * last write |-----
Packit Service 4684c1
	 */
Packit Service 4684c1
	while (data_size > 0) {
Packit Service 4684c1
		const giovec_t *iov;
Packit Service 4684c1
		uint8_t *p;
Packit Service 4684c1
		size_t to_write;
Packit Service 4684c1
Packit Service 4684c1
		while (iov_offset == 0) {
Packit Service 4684c1
			if (unlikely(iov_index == 0))
Packit Service 4684c1
				return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
Packit Service 4684c1
Packit Service 4684c1
			iov_index--;
Packit Service 4684c1
			iov_offset = iter->iov[iov_index].iov_len;
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		iov = &iter->iov[iov_index];
Packit Service 4684c1
		p = iov->iov_base;
Packit Service 4684c1
		to_write = MIN(data_size, iov_offset);
Packit Service 4684c1
Packit Service 4684c1
		iov_offset -= to_write;
Packit Service 4684c1
		data_size -= to_write;
Packit Service 4684c1
Packit Service 4684c1
		memcpy(p + iov_offset, &iter->block[data_size], to_write);
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}