| // 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 |
| |
| #include <inttypes.h> |
| #include <lib/user_copy/user_iovec.h> |
| #include <lib/user_copy/user_ptr.h> |
| #include <platform.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <trace.h> |
| #include <zircon/errors.h> |
| #include <zircon/types.h> |
| |
| #include <fbl/ref_ptr.h> |
| #include <object/handle.h> |
| #include <object/stream_dispatcher.h> |
| #include <vm/vm_aspace.h> |
| |
| #include "priv.h" |
| |
| #define LOCAL_TRACE 0 |
| |
| // zx_status_t zx_stream_create |
| zx_status_t sys_stream_create(uint32_t options, zx_handle_t vmo_handle, zx_off_t seek, |
| user_out_handle* out_stream) { |
| if ((options & ~ZX_STREAM_CREATE_MASK) != 0) |
| return ZX_ERR_INVALID_ARGS; |
| |
| zx_rights_t vmo_rights = ZX_RIGHT_NONE; |
| if (options & ZX_STREAM_MODE_READ) { |
| vmo_rights |= ZX_RIGHT_READ; |
| } |
| if (options & ZX_STREAM_MODE_WRITE) { |
| vmo_rights |= ZX_RIGHT_WRITE; |
| } |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| fbl::RefPtr<VmObjectDispatcher> vmo; |
| zx_status_t status = up->handle_table().GetDispatcherWithRights(vmo_handle, vmo_rights, &vmo); |
| if (status != ZX_OK) |
| return status; |
| |
| KernelHandle<StreamDispatcher> new_handle; |
| zx_rights_t rights; |
| status = StreamDispatcher::Create(options, ktl::move(vmo), seek, &new_handle, &rights); |
| if (status != ZX_OK) |
| return status; |
| return out_stream->make(ktl::move(new_handle), rights); |
| } |
| |
| // zx_status_t zx_stream_writev |
| zx_status_t sys_stream_writev(zx_handle_t handle, uint32_t options, |
| user_in_ptr<const zx_iovec_t> vector, size_t vector_count, |
| user_out_ptr<size_t> out_actual) { |
| LTRACEF("handle %x\n", handle); |
| |
| if (options & ~ZX_STREAM_APPEND) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| if (!vector) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| fbl::RefPtr<StreamDispatcher> stream; |
| zx_status_t status = up->handle_table().GetDispatcherWithRights(handle, ZX_RIGHT_WRITE, &stream); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| size_t actual = 0; |
| if (options & ZX_STREAM_APPEND) { |
| status = |
| stream->AppendVector(up->aspace().get(), make_user_in_iovec(vector, vector_count), &actual); |
| } else { |
| status = |
| stream->WriteVector(up->aspace().get(), make_user_in_iovec(vector, vector_count), &actual); |
| } |
| |
| if (status == ZX_OK && out_actual) { |
| status = out_actual.copy_to_user(actual); |
| } |
| |
| return status; |
| } |
| |
| // zx_status_t zx_stream_writev_at |
| zx_status_t sys_stream_writev_at(zx_handle_t handle, uint32_t options, zx_off_t offset, |
| user_in_ptr<const zx_iovec_t> vector, size_t vector_count, |
| user_out_ptr<size_t> out_actual) { |
| LTRACEF("handle %x\n", handle); |
| |
| if (options != 0) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| if (!vector) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| fbl::RefPtr<StreamDispatcher> stream; |
| zx_status_t status = up->handle_table().GetDispatcherWithRights(handle, ZX_RIGHT_WRITE, &stream); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| size_t actual = 0; |
| status = stream->WriteVectorAt(up->aspace().get(), make_user_in_iovec(vector, vector_count), |
| offset, &actual); |
| |
| if (status == ZX_OK && out_actual) { |
| status = out_actual.copy_to_user(actual); |
| } |
| |
| return status; |
| } |
| |
| // zx_status_t zx_stream_readv |
| zx_status_t sys_stream_readv(zx_handle_t handle, uint32_t options, user_out_ptr<zx_iovec_t> vector, |
| size_t vector_count, user_out_ptr<size_t> out_actual) { |
| LTRACEF("handle %x\n", handle); |
| |
| if (options != 0) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| if (!vector) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| fbl::RefPtr<StreamDispatcher> stream; |
| zx_status_t status = up->handle_table().GetDispatcherWithRights(handle, ZX_RIGHT_READ, &stream); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| size_t actual = 0; |
| status = |
| stream->ReadVector(up->aspace().get(), make_user_out_iovec(vector, vector_count), &actual); |
| |
| if (status == ZX_OK && out_actual) { |
| status = out_actual.copy_to_user(actual); |
| } |
| |
| return status; |
| } |
| |
| // zx_status_t zx_stream_readv_at |
| zx_status_t sys_stream_readv_at(zx_handle_t handle, uint32_t options, zx_off_t offset, |
| user_out_ptr<zx_iovec_t> vector, size_t vector_count, |
| user_out_ptr<size_t> out_actual) { |
| LTRACEF("handle %x\n", handle); |
| |
| if (options != 0) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| if (!vector) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| fbl::RefPtr<StreamDispatcher> stream; |
| zx_status_t status = up->handle_table().GetDispatcherWithRights(handle, ZX_RIGHT_READ, &stream); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| size_t actual = 0; |
| status = stream->ReadVectorAt(up->aspace().get(), make_user_out_iovec(vector, vector_count), |
| offset, &actual); |
| |
| if (status == ZX_OK && out_actual) { |
| status = out_actual.copy_to_user(actual); |
| } |
| |
| return status; |
| } |
| |
| // zx_status_t zx_stream_seek |
| zx_status_t sys_stream_seek(zx_handle_t handle, zx_stream_seek_origin_t whence, int64_t offset, |
| user_out_ptr<zx_off_t> out_seek) { |
| LTRACEF("handle %x\n", handle); |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| fbl::RefPtr<StreamDispatcher> stream; |
| zx_rights_t rights; |
| zx_status_t status = up->handle_table().GetDispatcherAndRights(handle, &stream, &rights); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if ((rights & (ZX_RIGHT_READ | ZX_RIGHT_WRITE)) == 0) { |
| return ZX_ERR_ACCESS_DENIED; |
| } |
| zx_off_t seek = 0u; |
| status = stream->Seek(whence, offset, &seek); |
| |
| if (status == ZX_OK && out_seek) { |
| status = out_seek.copy_to_user(seek); |
| } |
| return status; |
| } |