blob: b0adbded66d67a0554b4ed7f8bb7ce622ab96d57 [file]
// Copyright 2026 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
use crate::object::{Dispatcher, HandleValue, StreamDispatcher, VmObjectDispatcher};
use crate::user_copy::{UserInPtr, UserOutPtr, make_user_in_iovec, make_user_out_iovec};
use debug::ltracef;
use syscalls_macro::syscall;
use zx_status::Status;
use zx_types::{
ZX_RIGHT_READ, ZX_RIGHT_RESIZE, ZX_RIGHT_WRITE, ZX_STREAM_APPEND, ZX_STREAM_CREATE_MASK,
zx_iovec_t, zx_off_t, zx_stream_seek_origin_t,
};
const LOCAL_TRACE: u32 = 0;
#[syscall]
pub fn sys_stream_create(
options: u32,
vmo_handle: HandleValue,
seek: zx_off_t,
out_stream: &mut HandleValue,
) -> Result<(), Status> {
if (options & !ZX_STREAM_CREATE_MASK) != 0 {
return Err(Status::INVALID_ARGS);
}
let (mut stream_options, desired_vmo_rights) =
StreamDispatcher::parse_create_syscall_flags(options)?;
let (vmo_disp, actual_vmo_rights) = Dispatcher::get_with_rights_and_actual::<VmObjectDispatcher>(
vmo_handle,
desired_vmo_rights,
)?;
// Cannot create a stream from a physical or contiguous VMO.
if !vmo_disp.vmo().is_paged() || vmo_disp.vmo().is_contiguous() {
return Err(Status::WRONG_TYPE);
}
// Remember whether this stream can resize the underlying VMO when required. Note that it
// might be possible for stream writes / appends to proceed by manipulating only the stream
// size, without having to change the VMO size. This flag will be checked only for stream
// operations that would require manipulating the VMO size, more specifically when expanding
// the VMO size if the requested stream size needs to surpass the current VMO size.
if (actual_vmo_rights & ZX_RIGHT_RESIZE) != 0 {
stream_options.set_can_resize_vmo(true);
}
let (kernel_handle, rights) = StreamDispatcher::create(stream_options, &vmo_disp, seek)?;
let user_handle = kernel_handle.make_and_add_handle(rights)?;
*out_stream = user_handle;
Ok(())
}
#[syscall]
pub fn sys_stream_writev(
handle: HandleValue,
options: u32,
vector: UserInPtr<zx_iovec_t>,
vector_count: usize,
out_actual: UserOutPtr<usize>,
) -> Result<(), Status> {
ltracef!("handle {:#x}\n", handle.raw_value());
if (options & !ZX_STREAM_APPEND) != 0 {
return Err(Status::INVALID_ARGS);
}
if vector.is_null() {
return Err(Status::INVALID_ARGS);
}
let stream = Dispatcher::get_with_rights::<StreamDispatcher>(handle, ZX_RIGHT_WRITE)?;
let user_data = make_user_in_iovec(vector, vector_count);
let actual = if (options & ZX_STREAM_APPEND) != 0 {
stream.append_vector(user_data)?
} else {
stream.write_vector(user_data)?
};
if !out_actual.is_null() {
out_actual.write(actual)?;
}
Ok(())
}
#[syscall]
pub fn sys_stream_writev_at(
handle: HandleValue,
options: u32,
offset: zx_off_t,
vector: UserInPtr<zx_iovec_t>,
vector_count: usize,
out_actual: UserOutPtr<usize>,
) -> Result<(), Status> {
ltracef!("handle {:#x}\n", handle.raw_value());
if options != 0 {
return Err(Status::INVALID_ARGS);
}
if vector.is_null() {
return Err(Status::INVALID_ARGS);
}
let stream = Dispatcher::get_with_rights::<StreamDispatcher>(handle, ZX_RIGHT_WRITE)?;
let actual = stream.write_vector_at(make_user_in_iovec(vector, vector_count), offset)?;
if !out_actual.is_null() {
out_actual.write(actual)?;
}
Ok(())
}
#[syscall]
pub fn sys_stream_readv(
handle: HandleValue,
options: u32,
vector: UserOutPtr<zx_iovec_t>,
vector_count: usize,
out_actual: UserOutPtr<usize>,
) -> Result<(), Status> {
ltracef!("handle {:#x}\n", handle.raw_value());
if options != 0 {
return Err(Status::INVALID_ARGS);
}
if vector.is_null() {
return Err(Status::INVALID_ARGS);
}
let stream = Dispatcher::get_with_rights::<StreamDispatcher>(handle, ZX_RIGHT_READ)?;
let actual = stream.read_vector(make_user_out_iovec(vector, vector_count))?;
if !out_actual.is_null() {
out_actual.write(actual)?;
}
Ok(())
}
#[syscall]
pub fn sys_stream_readv_at(
handle: HandleValue,
options: u32,
offset: zx_off_t,
vector: UserOutPtr<zx_iovec_t>,
vector_count: usize,
out_actual: UserOutPtr<usize>,
) -> Result<(), Status> {
ltracef!("handle {:#x}\n", handle.raw_value());
if options != 0 {
return Err(Status::INVALID_ARGS);
}
if vector.is_null() {
return Err(Status::INVALID_ARGS);
}
let stream = Dispatcher::get_with_rights::<StreamDispatcher>(handle, ZX_RIGHT_READ)?;
let actual = stream.read_vector_at(make_user_out_iovec(vector, vector_count), offset)?;
if !out_actual.is_null() {
out_actual.write(actual)?;
}
Ok(())
}
#[syscall]
pub fn sys_stream_seek(
handle: HandleValue,
whence: zx_stream_seek_origin_t,
offset: i64,
out_seek: UserOutPtr<zx_off_t>,
) -> Result<(), Status> {
ltracef!("handle {:#x}\n", handle.raw_value());
let (stream, rights) = Dispatcher::get_and_rights::<StreamDispatcher>(handle)?;
if (rights & (ZX_RIGHT_READ | ZX_RIGHT_WRITE)) == 0 {
return Err(Status::ACCESS_DENIED);
}
let seek = stream.seek(whence, offset)?;
if !out_seek.is_null() {
out_seek.write(seek)?;
}
Ok(())
}