868 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 device::{BlockFile, CharFile};
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider};
use libk_util::{
io::{ReadAt, WriteAt},
sync::IrqSafeSpinlock,
};
use yggdrasil_abi::{
error::Error,
io::{
DeviceRequest, DirectoryEntry, OpenOptions, PipeOptions, RawFd, SeekFrom, TerminalOptions,
TerminalSize, TimerOptions,
},
process::{ProcessWait, WaitFlags},
};
use crate::{
device::{block::BlockDeviceFile, char::CharDeviceFile},
task::process::Process,
vfs::{
channel::ChannelDescriptor,
// device::{BlockDeviceWrapper, CharDeviceWrapper},
node::NodeRef,
traits::{Read, Seek, Write},
FdPoll,
FileReadiness,
Node,
// PseudoTerminalMaster,
// PseudoTerminalSlave,
SharedMemory,
TimerFile,
},
};
use self::{
// device::{BlockFile, CharFile},
directory::DirectoryFile,
pipe::PipeEnd,
regular::RegularFile,
};
use super::{
pid::PidFile,
pty::{self, PseudoTerminalMaster, PseudoTerminalSlave},
socket::SocketWrapper,
};
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),
Socket(SocketWrapper),
AnonymousPipe(PipeEnd, AtomicBool),
Poll(FdPoll),
Timer(TimerFile),
Channel(ChannelDescriptor),
SharedMemory(Arc<SharedMemory>),
PtySlave(TerminalHalfWrapper<PseudoTerminalSlave>),
PtyMaster(TerminalHalfWrapper<PseudoTerminalMaster>),
Pid(PidFile),
}
#[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, options: PipeOptions) -> (Arc<Self>, Arc<Self>) {
let (read, write) = PipeEnd::new_pair(capacity);
(
Arc::new(Self::AnonymousPipe(
read,
AtomicBool::new(options.contains(PipeOptions::READ_NONBLOCKING)),
)),
Arc::new(Self::AnonymousPipe(
write,
AtomicBool::new(options.contains(PipeOptions::WRITE_NONBLOCKING)),
)),
)
}
/// 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,
})),
))
}
pub fn open_pty_master(pty: Arc<PseudoTerminalMaster>, node: Arc<Node>) -> Arc<Self> {
Arc::new(Self::PtyMaster(TerminalHalfWrapper {
blocking: AtomicBool::new(true),
half: pty,
node,
}))
}
pub fn open_pty_slave(pty: Arc<PseudoTerminalSlave>, node: Arc<Node>) -> Arc<Self> {
Arc::new(Self::PtySlave(TerminalHalfWrapper {
blocking: AtomicBool::new(true),
half: pty,
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)))
}
/// Creates a new [PidFile]-backed file
pub fn new_pid(
parent: Arc<Process>,
wait: &ProcessWait,
flags: WaitFlags,
) -> Result<FileRef, Error> {
PidFile::new(parent, wait, flags)
.map(Self::Pid)
.map(Arc::new)
}
/// Constructs a [File] from a [PacketSocket], [ConnectionSocket] or a [ListenerSocket].
pub fn from_socket<S: Into<SocketWrapper>>(socket: S) -> Arc<Self> {
Arc::new(Self::Socket(socket.into()))
}
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: BlockDeviceFile,
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_writeable() {
return Err(Error::ReadOnly);
}
Ok(Arc::new(Self::Block(BlockFile {
device,
node,
position: IrqSafeSpinlock::new(0),
read,
write,
})))
}
pub(crate) fn char(
device: CharDeviceFile,
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_writeable() {
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::Pid(pid) => Ok(Arc::new(Self::Pid(pid.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.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::Socket(socket) => socket.poll_read(cx),
Self::Timer(timer) => timer.poll_read(cx),
Self::Pid(pid) => pid.poll_read(cx),
Self::AnonymousPipe(pipe, _) => pipe.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.device_request(req),
Self::Block(f) => f.device.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<&SocketWrapper, Error> {
match self {
Self::Socket(socket) => Ok(socket),
_ => Err(Error::InvalidOperation),
}
}
}
impl PageProvider for File {
fn get_page(&self, offset: u64) -> Result<PhysicalAddress, Error> {
match self {
Self::Block(f) => f.device.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.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, nonblocking) => {
pipe.read(buf, nonblocking.load(Ordering::Acquire))
}
Self::PtySlave(half) => half.read(buf),
Self::PtyMaster(half) => half.read(buf),
Self::Timer(timer) => timer.read(buf),
Self::Pid(pid) => pid.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::Socket(_) => 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 allow sending signals via writes to PID FDs?
Self::Pid(_) => Err(Error::InvalidOperation),
// 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::Socket(_) => Err(Error::InvalidOperation),
Self::Directory(_) => Err(Error::IsADirectory),
}
}
}
impl ReadAt for File {
fn read_at(&self, pos: u64, buf: &mut [u8]) -> Result<usize, Error> {
match self {
Self::Regular(file) => file.read_at(pos, buf),
_ => todo!(),
}
}
}
impl WriteAt for File {
fn write_at(&self, pos: u64, buf: &[u8]) -> Result<usize, Error> {
match self {
Self::Regular(file) => file.write_at(pos, buf),
_ => todo!(),
}
}
}
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),
_ => Ok(0),
}
}
fn seek(&self, from: SeekFrom) -> Result<u64, Error> {
if from == SeekFrom::Current(0) {
return self.tell();
}
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::Socket(socket) => fmt::Debug::fmt(socket, f),
Self::Pid(pid) => fmt::Debug::fmt(pid, f),
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::{
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");
}
}