2024-10-31 13:14:07 +02:00

866 lines
28 KiB
Rust

use core::{
any::Any,
fmt,
mem::MaybeUninit,
sync::atomic::{AtomicBool, Ordering},
task::{Context, Poll},
};
use alloc::{
boxed::Box,
collections::{btree_map::Entry, BTreeMap},
sync::Arc,
};
use async_trait::async_trait;
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider};
use libk_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::{
error::Error,
io::{
DeviceRequest, DirectoryEntry, OpenOptions, RawFd, SeekFrom, TerminalOptions, TerminalSize,
TimerOptions,
},
net::SocketAddr,
};
use crate::vfs::{
channel::ChannelDescriptor,
device::{BlockDeviceWrapper, CharDeviceWrapper},
node::NodeRef,
socket::{ConnectionSocketWrapper, ListenerSocketWrapper, PacketSocketWrapper},
traits::{Read, Seek, Write},
ConnectionSocket, FdPoll, FileReadiness, ListenerSocket, Node, PacketSocket,
PseudoTerminalMaster, PseudoTerminalSlave, SharedMemory, Socket, TimerFile,
};
use self::{
device::{BlockFile, CharFile},
directory::DirectoryFile,
pipe::PipeEnd,
regular::RegularFile,
};
use super::pty;
mod device;
mod directory;
mod pipe;
mod regular;
/// Per-file optional instance data created when a regular file is opened
pub type InstanceData = Arc<dyn Any + Send + Sync>;
/// Describes the starting position of the directory
pub enum DirectoryOpenPosition {
/// Contents should be fetched from the directory impl with given offset
FromPhysical(u64),
/// Contents should be fetched from the tree cache
FromCache,
}
/// Wrapper type for a [File] shared reference
pub type FileRef = Arc<File>;
// TODO some kind of a mutex instead?
/// Describes an open file
#[allow(missing_docs)]
pub enum File {
Directory(DirectoryFile),
Regular(RegularFile),
Block(BlockFile),
Char(CharFile),
PacketSocket(Arc<PacketSocketWrapper>),
ListenerSocket(Arc<ListenerSocketWrapper>),
StreamSocket(Arc<ConnectionSocketWrapper>),
AnonymousPipe(PipeEnd),
Poll(FdPoll),
Timer(TimerFile),
Channel(ChannelDescriptor),
SharedMemory(Arc<SharedMemory>),
PtySlave(TerminalHalfWrapper<PseudoTerminalSlave>),
PtyMaster(TerminalHalfWrapper<PseudoTerminalMaster>),
}
#[async_trait]
pub trait TerminalHalf {
async fn read(&self, buf: &mut [u8]) -> Result<usize, Error>;
fn read_nonblocking(&self, buf: &mut [u8]) -> Result<usize, Error>;
fn write(&self, buf: &[u8]) -> Result<usize, Error>;
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>>;
fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error>;
}
pub struct TerminalHalfWrapper<T: TerminalHalf> {
blocking: AtomicBool,
half: Arc<T>,
node: NodeRef,
}
/// Contains a per-process fd -> FileRef map
pub struct FileSet {
map: BTreeMap<RawFd, FileRef>,
}
impl File {
/// Constructs a pipe pair, returning its `(read, write)` ends
pub fn new_pipe_pair(capacity: usize) -> (Arc<Self>, Arc<Self>) {
let (read, write) = PipeEnd::new_pair(capacity);
(
Arc::new(Self::AnonymousPipe(read)),
Arc::new(Self::AnonymousPipe(write)),
)
}
/// Constructs a new poll channel file
pub fn new_poll_channel() -> Arc<Self> {
Arc::new(Self::Poll(FdPoll::new()))
}
/// Opens a new message channel, optionally subscribing to it as well
pub fn new_message_channel(name: &str, with_sub: bool) -> Arc<Self> {
let channel = ChannelDescriptor::open(name, with_sub);
Arc::new(Self::Channel(channel))
}
/// Creates a buffer of shared memory and associates a [File] with it
pub fn new_shared_memory(size: usize) -> Result<Arc<Self>, Error> {
let shm = SharedMemory::new(size)?;
Ok(Arc::new(Self::SharedMemory(Arc::new(shm))))
}
/// Creates a pair of PTY master/slave
pub fn new_pseudo_terminal(
config: TerminalOptions,
size: TerminalSize,
) -> Result<(Arc<Self>, Arc<Self>), Error> {
let (master, slave) = pty::create(config, size)?;
let master = Arc::new(master);
let slave = Arc::new(slave);
let (master_node, slave_node) = Node::pseudo_terminal_nodes(&master, &slave);
Ok((
Arc::new(Self::PtyMaster(TerminalHalfWrapper {
blocking: AtomicBool::new(true),
half: master,
node: master_node,
})),
Arc::new(Self::PtySlave(TerminalHalfWrapper {
blocking: AtomicBool::new(true),
half: slave,
node: slave_node,
})),
))
}
/// Creates a new [TimerFile]-backed File
pub fn new_timer(options: TimerOptions) -> FileRef {
let repeat = options.contains(TimerOptions::REPEAT);
let blocking = !options.contains(TimerOptions::NON_BLOCKING);
Arc::new(Self::Timer(TimerFile::new(repeat, blocking)))
}
/// Constructs a [File] from a [PacketSocket]
pub fn from_packet_socket(socket: Arc<dyn PacketSocket>) -> Arc<Self> {
Arc::new(Self::PacketSocket(Arc::new(PacketSocketWrapper(socket))))
}
/// Constructs a [File] from a [ListenerSocket]
pub fn from_listener_socket(socket: Arc<dyn ListenerSocket>) -> Arc<Self> {
Arc::new(Self::ListenerSocket(Arc::new(ListenerSocketWrapper(
socket,
))))
}
/// Constructs a [File] from a [ConnectionSocket]
pub fn from_stream_socket(socket: Arc<dyn ConnectionSocket>) -> Arc<Self> {
Arc::new(Self::StreamSocket(Arc::new(ConnectionSocketWrapper(
socket,
))))
}
pub(crate) fn directory(node: NodeRef, position: DirectoryOpenPosition) -> Arc<Self> {
let position = IrqSafeSpinlock::new(position.into());
Arc::new(Self::Directory(DirectoryFile { node, position }))
}
pub(crate) fn regular(
node: NodeRef,
position: u64,
instance_data: Option<InstanceData>,
opts: OpenOptions,
) -> Arc<Self> {
let read = opts.contains(OpenOptions::READ);
let write = opts.contains(OpenOptions::WRITE);
Arc::new(Self::Regular(RegularFile {
node,
read,
write,
instance_data,
position: IrqSafeSpinlock::new(position),
}))
}
pub(crate) fn block(
device: BlockDeviceWrapper,
node: NodeRef,
opts: OpenOptions,
) -> Result<Arc<Self>, Error> {
let read = opts.contains(OpenOptions::READ);
let write = opts.contains(OpenOptions::WRITE);
if read && !device.is_readable() {
return Err(Error::InvalidOperation);
}
if write && !device.is_writable() {
return Err(Error::ReadOnly);
}
Ok(Arc::new(Self::Block(BlockFile {
device,
node,
position: IrqSafeSpinlock::new(0),
read,
write,
})))
}
pub(crate) fn char(
device: CharDeviceWrapper,
node: NodeRef,
opts: OpenOptions,
) -> Result<Arc<Self>, Error> {
let read = opts.contains(OpenOptions::READ);
let write = opts.contains(OpenOptions::WRITE);
if read && !device.is_readable() {
return Err(Error::InvalidOperation);
}
if write && !device.is_writable() {
return Err(Error::ReadOnly);
}
Ok(Arc::new(Self::Char(CharFile {
device,
node,
read,
write,
blocking: AtomicBool::new(true),
})))
}
/// Clones an open file for sending it to another process
pub fn send(self: &Arc<Self>) -> Result<Arc<Self>, Error> {
match self.as_ref() {
Self::Char(_) => Ok(self.clone()),
Self::Block(_) => todo!(),
Self::Regular(file) => Ok(Arc::new(Self::Regular(file.clone()))),
Self::SharedMemory(shm) => Ok(Arc::new(Self::SharedMemory(shm.clone()))),
Self::PtySlave(half) => Ok(Arc::new(Self::PtySlave(half.clone()))),
Self::PtyMaster(half) => Ok(Arc::new(Self::PtyMaster(half.clone()))),
_ => {
log::info!("Invalid file send(): {:?}", self);
Err(Error::InvalidOperation)
}
}
}
/// Reads entries from the directory
pub fn read_dir(&self, entries: &mut [MaybeUninit<DirectoryEntry>]) -> Result<usize, Error> {
match self {
Self::Directory(dir) => dir.read_entries(entries),
_ => Err(Error::NotADirectory),
}
}
/// Returns the underlying [Node] the file contains
pub fn node(&self) -> Option<&NodeRef> {
match self {
Self::Directory(file) => Some(&file.node),
Self::Regular(file) => Some(&file.node),
Self::Block(file) => Some(&file.node),
Self::Char(file) => Some(&file.node),
Self::PtyMaster(half) => Some(&half.node),
Self::PtySlave(half) => Some(&half.node),
_ => None,
}
}
/// Polls a file for "read-readiness"
pub fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
match self {
Self::Char(f) => f.device.0.poll_read(cx),
Self::Channel(ch) => ch.poll_read(cx),
Self::Poll(ch) => ch.poll_read(cx),
Self::PtyMaster(half) => half.half.poll_read(cx),
Self::PtySlave(half) => half.half.poll_read(cx),
Self::PacketSocket(sock) => sock.poll_read(cx),
Self::StreamSocket(sock) => sock.poll_read(cx),
Self::ListenerSocket(sock) => sock.poll_read(cx),
Self::Timer(timer) => timer.poll_read(cx),
// Polling not implemented, return ready immediately (XXX ?)
_ => Poll::Ready(Err(Error::NotImplemented)),
}
}
/// Performs a device-specific request
pub fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
match self {
Self::Char(f) => f.device.0.device_request(req),
Self::Block(f) => f.device.0.device_request(req),
Self::PtySlave(half) => half.half.device_request(req),
Self::PtyMaster(half) => half.half.device_request(req),
_ => Err(Error::InvalidOperation),
}
}
/// Interprets the file as a poll channel
pub fn as_poll_channel(&self) -> Result<&FdPoll, Error> {
if let Self::Poll(poll) = self {
Ok(poll)
} else {
Err(Error::InvalidOperation)
}
}
/// Interprets the file as a message channel
pub fn as_message_channel(&self) -> Result<&ChannelDescriptor, Error> {
if let Self::Channel(ch) = self {
Ok(ch)
} else {
Err(Error::InvalidOperation)
}
}
/// Interprets the file as a socket
pub fn as_socket(&self) -> Result<&dyn Socket, Error> {
match self {
Self::PacketSocket(socket) => Ok(socket.0.as_ref()),
_ => Err(Error::InvalidOperation),
}
}
/// Sends data to a socket
pub fn send_to(&self, buffer: &[u8], recepient: Option<SocketAddr>) -> Result<usize, Error> {
match (self, recepient) {
(Self::PacketSocket(socket), recepient) => socket.send(recepient, buffer),
(Self::StreamSocket(socket), None) => socket.send(buffer),
(_, _) => todo!(),
}
}
/// Receives data from a socket
pub fn receive_from(
&self,
buffer: &mut [u8],
remote: &mut MaybeUninit<SocketAddr>,
) -> Result<usize, Error> {
match self {
Self::PacketSocket(socket) => {
let (addr, len) = socket.receive(buffer)?;
remote.write(addr);
Ok(len)
}
Self::StreamSocket(socket) => {
// Always the same
remote.write(socket.remote_address().unwrap());
socket.receive(buffer)
}
_ => Err(Error::InvalidOperation),
}
}
/// Waits for incoming connection to be accepted by the listener
pub fn accept(&self, remote: &mut MaybeUninit<SocketAddr>) -> Result<FileRef, Error> {
match self {
Self::ListenerSocket(socket) => {
let (address, incoming) = socket.accept()?;
remote.write(address);
Ok(File::from_stream_socket(incoming))
}
_ => Err(Error::InvalidOperation),
}
}
}
impl PageProvider for File {
fn get_page(&self, offset: u64) -> Result<PhysicalAddress, Error> {
match self {
Self::Block(f) => f.device.0.get_page(offset),
Self::SharedMemory(f) => f.get_page(offset),
_ => Err(Error::InvalidOperation),
}
}
fn release_page(&self, offset: u64, phys: PhysicalAddress) -> Result<(), Error> {
match self {
Self::Block(f) => f.device.0.release_page(offset, phys),
Self::SharedMemory(f) => f.release_page(offset, phys),
_ => Err(Error::InvalidOperation),
}
}
fn clone_page(
&self,
_offset: u64,
_src_phys: PhysicalAddress,
_src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
todo!()
}
}
impl Read for File {
fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
match self {
Self::Regular(file) => file.read(buf),
Self::Block(file) => file.read(buf),
Self::Char(file) => file.read(buf),
Self::AnonymousPipe(pipe) => pipe.read(buf),
Self::PtySlave(half) => half.read(buf),
Self::PtyMaster(half) => half.read(buf),
Self::Timer(timer) => timer.read(buf),
// TODO maybe allow reading FDs from poll channels as if they were regular streams?
Self::Poll(_) => Err(Error::InvalidOperation),
// TODO maybe allow reading messages from Channels?
Self::Channel(_) => Err(Error::InvalidOperation),
Self::SharedMemory(_) => Err(Error::InvalidOperation),
// TODO maybe allow reading messages from Packet/Stream sockets?
Self::PacketSocket(_) | Self::ListenerSocket(_) | Self::StreamSocket(_) => {
Err(Error::InvalidOperation)
}
Self::Directory(_) => Err(Error::IsADirectory),
}
}
}
impl Write for File {
fn write(&self, buf: &[u8]) -> Result<usize, Error> {
match self {
Self::Regular(file) => file.write(buf),
Self::Block(file) => file.write(buf),
Self::Char(file) => file.write(buf),
Self::AnonymousPipe(pipe) => pipe.write(buf),
Self::PtySlave(half) => half.write(buf),
Self::PtyMaster(half) => half.write(buf),
Self::Timer(timer) => timer.write(buf),
// TODO maybe allow adding FDs to poll channels this way
Self::Poll(_) => Err(Error::InvalidOperation),
// TODO maybe allow writing messages to Channels?
Self::Channel(_) => Err(Error::InvalidOperation),
Self::SharedMemory(_) => Err(Error::InvalidOperation),
// TODO maybe allow writing messages to Packet/Stream sockets?
Self::PacketSocket(_) | Self::ListenerSocket(_) | Self::StreamSocket(_) => {
Err(Error::InvalidOperation)
}
Self::Directory(_) => Err(Error::IsADirectory),
}
}
}
impl Seek for File {
fn tell(&self) -> Result<u64, Error> {
match self {
Self::Regular(file) => Ok(*file.position.lock()),
Self::Block(file) => Ok(*file.position.lock()),
Self::Directory(_) => Err(Error::IsADirectory),
_ => Err(Error::InvalidOperation),
}
}
fn seek(&self, from: SeekFrom) -> Result<u64, Error> {
match self {
Self::Regular(file) => file.seek(from),
Self::Block(file) => file.seek(from),
Self::Directory(_) => Err(Error::IsADirectory),
_ => Err(Error::InvalidOperation),
}
}
}
impl fmt::Debug for File {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Regular(file) => f
.debug_struct("RegularFile")
.field("position", &*file.position.lock())
.field("read", &file.read)
.field("write", &file.write)
.finish_non_exhaustive(),
Self::Block(file) => f
.debug_struct("BlockFile")
.field("position", &*file.position.lock())
.field("read", &file.read)
.field("write", &file.write)
.finish_non_exhaustive(),
Self::Char(file) => f
.debug_struct("CharFile")
.field("read", &file.read)
.field("write", &file.write)
.finish_non_exhaustive(),
Self::Directory(_) => f.debug_struct("DirectoryFile").finish_non_exhaustive(),
Self::AnonymousPipe(_) => f.debug_struct("AnonymousPipe").finish_non_exhaustive(),
Self::Poll(_) => f.debug_struct("Poll").finish_non_exhaustive(),
Self::Channel(_) => f.debug_struct("Channel").finish_non_exhaustive(),
Self::SharedMemory(_) => f.debug_struct("SharedMemory").finish_non_exhaustive(),
Self::PtySlave(_) => f.debug_struct("PtySlave").finish_non_exhaustive(),
Self::PtyMaster(_) => f.debug_struct("PtyMaster").finish_non_exhaustive(),
Self::PacketSocket(sock) => f
.debug_struct("PacketSocket")
.field("local", &sock.local_address())
.field("remote", &sock.remote_address())
.finish_non_exhaustive(),
Self::StreamSocket(sock) => f
.debug_struct("StreamSocket")
.field("local", &sock.local_address())
.field("remote", &sock.remote_address())
.finish_non_exhaustive(),
Self::ListenerSocket(sock) => f
.debug_struct("ListenerSocket")
.field("local", &sock.local_address())
.finish_non_exhaustive(),
Self::Timer(_) => f.debug_struct("Timer").finish_non_exhaustive(),
}
}
}
impl<T: TerminalHalf> TerminalHalfWrapper<T> {
pub fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
if self.blocking.load(Ordering::Acquire) {
block!(self.half.read(buf).await)?
} else {
self.half.read_nonblocking(buf)
}
}
pub fn write(&self, buf: &[u8]) -> Result<usize, Error> {
self.half.write(buf)
}
}
impl<T: TerminalHalf> Clone for TerminalHalfWrapper<T> {
fn clone(&self) -> Self {
Self {
half: self.half.clone(),
node: self.node.clone(),
blocking: AtomicBool::new(self.blocking.load(Ordering::Acquire)),
}
}
}
impl FileSet {
/// Creates an empty [FileSet]
pub fn new() -> Self {
Self {
map: BTreeMap::new(),
}
}
/// Returns the [FileRef] associated with given `fd` or an error if it does not exist
pub fn file(&self, fd: RawFd) -> Result<&FileRef, Error> {
self.map.get(&fd).ok_or(Error::InvalidFile)
}
/// Associates a `file` with `fd`, returning an error if requested `fd` is already taken
pub fn set_file(&mut self, fd: RawFd, file: FileRef) -> Result<(), Error> {
if self.map.contains_key(&fd) {
return Err(Error::AlreadyExists);
}
self.map.insert(fd, file);
Ok(())
}
/// Associates a `file` with any available [RawFd] and returns it
pub fn place_file(&mut self, file: FileRef, skip_stdio: bool) -> Result<RawFd, Error> {
let start = if skip_stdio { 3 } else { 0 };
for idx in start..64 {
let fd = RawFd::from(idx);
if let Entry::Vacant(e) = self.map.entry(fd) {
e.insert(file);
return Ok(fd);
}
}
// TODO OutOfFiles
Err(Error::OutOfMemory)
}
/// Removes and closes a [FileRef] from the struct
pub fn close_file(&mut self, fd: RawFd) -> Result<(), Error> {
// Do nothing, file will be dropped and closed
if self.map.remove(&fd).is_some() {
Ok(())
} else {
Err(Error::InvalidFile)
}
}
/// Removes all [FileRef]s from the struct which do not pass the `predicate` check
pub fn retain<F: Fn(&RawFd, &mut FileRef) -> bool>(&mut self, predicate: F) {
self.map.retain(predicate);
}
/// Returns an iterator over the file set
pub fn iter(&self) -> impl Iterator<Item = (&RawFd, &FileRef)> {
self.map.iter()
}
/// Closes all of the files
pub fn close_all(&mut self) {
self.map.clear();
}
}
#[cfg(test)]
mod tests {
use core::{
mem::MaybeUninit,
str::FromStr,
task::{Context, Poll},
};
use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use yggdrasil_abi::{
error::Error,
io::{DirectoryEntry, FileType, OpenOptions, SeekFrom},
util::FixedString,
};
use crate::vfs::{
device::CharDevice,
file::DirectoryOpenPosition,
impls::const_value_node,
node::{AccessToken, CommonImpl, DirectoryImpl, Node, NodeFlags, NodeRef, RegularImpl},
traits::{Read, Seek, Write},
FileReadiness, InstanceData,
};
#[test]
fn file_send_sync() {
fn file_send<T: Send>(_f: &T) {}
fn file_sync<T: Sync>(_f: &T) {}
let node = const_value_node("1234");
let file = node
.open(OpenOptions::READ, AccessToken::test_authorized())
.unwrap();
file_send(&file);
file_sync(&file);
}
#[test]
fn physical_dir_read() {
struct D {
entries: Vec<(String, NodeRef)>,
}
struct F;
impl CommonImpl for D {}
impl DirectoryImpl for D {
fn open(&self, _node: &NodeRef) -> Result<DirectoryOpenPosition, Error> {
Ok(DirectoryOpenPosition::FromPhysical(0))
}
fn read_entries(
&self,
_node: &NodeRef,
pos: u64,
entries: &mut [MaybeUninit<DirectoryEntry>],
) -> Result<(usize, u64), Error> {
let pos = pos as usize;
if pos == self.entries.len() {
return Ok((0, pos as u64));
}
let count = core::cmp::min(entries.len(), self.entries.len() - pos);
for i in 0..count {
let (name, node) = &self.entries[i];
let entry = DirectoryEntry {
name: FixedString::from_str(name)?,
ty: Some(node.ty()),
};
entries[i].write(entry);
}
Ok((count, (pos + count) as u64))
}
}
impl CommonImpl for F {}
impl RegularImpl for F {}
let d = Node::directory(
D {
entries: Vec::from_iter([
("f1".to_owned(), Node::regular(F, NodeFlags::empty())),
("f2".to_owned(), Node::regular(F, NodeFlags::empty())),
("f3".to_owned(), Node::regular(F, NodeFlags::empty())),
]),
},
NodeFlags::empty(),
);
let f = d.open_directory(AccessToken::test_authorized()).unwrap();
let mut entries = [MaybeUninit::uninit(); 16];
let count = f.read_dir(&mut entries).unwrap();
assert_eq!(count, 3);
unsafe {
assert_eq!(
MaybeUninit::slice_assume_init_ref(&entries[..count]),
&[
DirectoryEntry {
name: FixedString::from_str("f1").unwrap(),
ty: Some(FileType::File),
},
DirectoryEntry {
name: FixedString::from_str("f2").unwrap(),
ty: Some(FileType::File),
},
DirectoryEntry {
name: FixedString::from_str("f3").unwrap(),
ty: Some(FileType::File)
}
]
);
}
let count = f.read_dir(&mut entries).unwrap();
assert_eq!(count, 0);
}
#[test]
fn cache_dir_read() {
struct D;
impl CommonImpl for D {}
impl DirectoryImpl for D {
fn open(&self, _node: &NodeRef) -> Result<DirectoryOpenPosition, Error> {
Ok(DirectoryOpenPosition::FromCache)
}
}
let d = Node::directory(D, NodeFlags::empty());
let child = Node::directory(D, NodeFlags::empty());
d.add_child("child1", child).unwrap();
let f = d.open_directory(AccessToken::test_authorized()).unwrap();
let mut entries = [MaybeUninit::uninit(); 16];
let count = f.read_dir(&mut entries).unwrap();
assert_eq!(count, 3);
unsafe {
assert_eq!(
MaybeUninit::slice_assume_init_ref(&entries[..count]),
&[
DirectoryEntry {
name: FixedString::from_str(".").unwrap(),
ty: Some(FileType::Directory)
},
DirectoryEntry {
name: FixedString::from_str("..").unwrap(),
ty: Some(FileType::Directory)
},
DirectoryEntry {
name: FixedString::from_str("child1").unwrap(),
ty: Some(FileType::Directory)
}
]
);
}
let count = f.read_dir(&mut entries).unwrap();
assert_eq!(count, 0);
}
#[test]
fn file_read_write() {
struct F {
data: Arc<Mutex<Vec<u8>>>,
}
impl CommonImpl for F {
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
Ok(self.data.lock().unwrap().len() as _)
}
}
impl RegularImpl for F {
fn open(
&self,
_node: &NodeRef,
_opts: OpenOptions,
) -> Result<(u64, Option<InstanceData>), Error> {
Ok((0, None))
}
fn read(
&self,
_node: &NodeRef,
_instance: Option<&InstanceData>,
pos: u64,
buf: &mut [u8],
) -> Result<usize, Error> {
let pos = pos as usize;
let data = self.data.lock().unwrap();
if pos >= data.len() {
return Ok(0);
}
let count = core::cmp::min(data.len() - pos, buf.len());
buf[..count].copy_from_slice(&data[pos..pos + count]);
Ok(count)
}
fn write(
&self,
_node: &NodeRef,
_instance: Option<&InstanceData>,
pos: u64,
buf: &[u8],
) -> Result<usize, Error> {
let pos = pos as usize;
let mut data = self.data.lock().unwrap();
data.resize(pos + buf.len(), 0);
data[pos..pos + buf.len()].copy_from_slice(buf);
Ok(buf.len())
}
}
let data = Arc::new(Mutex::new(vec![]));
let node = Node::regular(F { data: data.clone() }, NodeFlags::empty());
let file = node
.open(
OpenOptions::READ | OpenOptions::WRITE,
AccessToken::test_authorized(),
)
.unwrap();
let mut buf = [0; 512];
assert_eq!(&*data.lock().unwrap(), b"");
assert_eq!(file.tell().unwrap(), 0);
assert_eq!(file.write(b"Hello").unwrap(), 5);
assert_eq!(file.tell().unwrap(), 5);
assert_eq!(&*data.lock().unwrap(), b"Hello");
assert_eq!(file.seek(SeekFrom::End(-2)).unwrap(), 3);
assert_eq!(file.tell().unwrap(), 3);
assert_eq!(file.write(b"123456").unwrap(), 6);
assert_eq!(file.tell().unwrap(), 9);
assert_eq!(&*data.lock().unwrap(), b"Hel123456");
assert_eq!(file.seek(SeekFrom::Start(2)).unwrap(), 2);
assert_eq!(file.read(&mut buf).unwrap(), 7);
assert_eq!(file.tell().unwrap(), 9);
assert_eq!(&buf[..7], b"l123456");
}
}