blob: 6a94aeee3464900767071b48f1660e7a05fd846a [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#ifndef ZIRCON_KERNEL_LIB_USER_COPY_INCLUDE_LIB_USER_COPY_USER_IOVEC_H_
#define ZIRCON_KERNEL_LIB_USER_COPY_INCLUDE_LIB_USER_COPY_USER_IOVEC_H_
#include <lib/user_copy/user_ptr.h>
#include <stddef.h>
#include <zircon/types.h>
#include <ktl/forward.h>
#include <ktl/type_traits.h>
namespace internal {
template <InOutPolicy Policy>
class user_iovec {
public:
enum { is_out = ((Policy & kOut) == kOut) };
// special operator to return the nullness of the pointer
explicit operator bool() const { return vector_ != nullptr; }
using DataType = typename ktl::conditional<is_out, char, const char>::type;
using PtrType = user_ptr<DataType, Policy>;
using VecType = typename ktl::conditional<is_out, zx_iovec_t, const zx_iovec_t>::type;
explicit user_iovec(VecType* vector, size_t count) : vector_(vector), count_(count) {}
zx_status_t GetTotalCapacity(size_t* out_capacity) const {
size_t total_capacity = 0;
zx_status_t status = ForEach([&total_capacity](PtrType ptr, size_t capacity) {
if (add_overflow(total_capacity, capacity, &total_capacity)) {
return ZX_ERR_INVALID_ARGS;
}
return ZX_ERR_NEXT;
});
if (status != ZX_OK) {
return status;
}
*out_capacity = total_capacity;
return ZX_OK;
}
// Iterate through the iovec.
//
// The expected signature for the callback is:
// zx_status_t (callback)(PtrType ptr, size_t capacity).
//
// To continue to the next buffer in the vector, return ZX_ERR_NEXT. To stop
// iterating successfully, return ZX_ERR_STOP. Returning any other error will
// also stop the iteration but will cause ForEach to retun that error instead
// of ZX_OK.
template <typename Callback>
zx_status_t ForEach(Callback callback) const {
user_in_ptr<const zx_iovec_t> base(vector_);
for (size_t i = 0; i < count_; ++i) {
user_in_ptr<const zx_iovec_t> user_current = base.element_offset(i);
zx_iovec_t current = {};
zx_status_t status = user_current.copy_from_user(&current);
if (status != ZX_OK) {
return status;
}
status = callback(PtrType(static_cast<DataType*>(current.buffer)), current.capacity);
if (status == ZX_ERR_NEXT) {
continue;
}
if (status == ZX_ERR_STOP) {
break;
}
return status;
}
return ZX_OK;
}
private:
VecType* vector_;
size_t count_;
};
} // namespace internal
using user_in_iovec_t = internal::user_iovec<internal::kIn>;
using user_out_iovec_t = internal::user_iovec<internal::kOut>;
using user_inout_iovec_t = internal::user_iovec<internal::kInOut>;
inline user_in_iovec_t make_user_in_iovec(user_in_ptr<const zx_iovec_t> vector, size_t count) {
return user_in_iovec_t(vector.get(), count);
}
inline user_out_iovec_t make_user_out_iovec(user_out_ptr<zx_iovec_t> vector, size_t count) {
return user_out_iovec_t(vector.get(), count);
}
inline user_inout_iovec_t make_user_inout_iovec(user_inout_ptr<zx_iovec_t> vector, size_t count) {
return user_inout_iovec_t(vector.get(), count);
}
#endif // ZIRCON_KERNEL_LIB_USER_COPY_INCLUDE_LIB_USER_COPY_USER_IOVEC_H_