use std::{
io,
sync::{Arc, Mutex, atomic::Ordering},
};
use super::{
MemFs, inode,
types::{DirHandle, DirSnapshot, InodeContent, MemDirEntry, ROOT_INODE},
};
use crate::{
AddDirEntry, AddDirEntryPlus, Context, DirEntry, Entry, OpenOptions,
backends::shared::{
dir_snapshot::{self, SnapshotEntry},
init_binary, platform,
},
};
pub(crate) fn do_opendir(
fs: &MemFs,
_ctx: Context,
ino: u64,
_flags: u32,
) -> io::Result<(Option<u64>, OpenOptions)> {
let node = inode::get_node(fs, ino)?;
if node.kind != platform::MODE_DIR {
return Err(platform::enotdir());
}
let handle = fs.next_handle.fetch_add(1, Ordering::Relaxed);
let dh = Arc::new(DirHandle {
node: Arc::clone(&node),
snapshot: Mutex::new(None),
});
fs.dir_handles.write().unwrap().insert(handle, dh);
Ok((Some(handle), fs.cache_dir_options()))
}
pub(crate) fn do_readdir(
fs: &MemFs,
_ctx: Context,
ino: u64,
handle: u64,
_size: u32,
offset: u64,
) -> io::Result<Vec<DirEntry<'static>>> {
serve_snapshot_entries(fs, ino, handle, offset)
}
pub(crate) fn do_readdir_for_each(
fs: &MemFs,
_ctx: Context,
ino: u64,
handle: u64,
_size: u32,
offset: u64,
add_entry: &mut AddDirEntry<'_>,
) -> io::Result<()> {
stream_snapshot_entries(fs, ino, handle, offset, add_entry)
}
pub(crate) fn do_readdirplus(
fs: &MemFs,
_ctx: Context,
ino: u64,
handle: u64,
_size: u32,
offset: u64,
) -> io::Result<Vec<(DirEntry<'static>, Entry)>> {
let dir_entries = serve_snapshot_entries(fs, ino, handle, offset)?;
let mut result = Vec::with_capacity(dir_entries.len());
for de in dir_entries {
let name_bytes = de.name;
if name_bytes == b"." || name_bytes == b".." {
continue;
}
if name_bytes == init_binary::INIT_FILENAME {
let entry = init_binary::init_entry(fs.cfg.entry_timeout, fs.cfg.attr_timeout);
result.push((de, entry));
continue;
}
let child_ino = de.ino;
if let Ok(node) = inode::get_node(fs, child_ino) {
inode::inc_lookup(&node);
let entry = inode::build_entry(fs, &node);
result.push((de, entry));
}
}
Ok(result)
}
pub(crate) fn do_readdirplus_for_each(
fs: &MemFs,
_ctx: Context,
ino: u64,
handle: u64,
_size: u32,
offset: u64,
add_entry: &mut AddDirEntryPlus<'_>,
) -> io::Result<()> {
let dh = dir_handle(fs, handle)?;
let mut snapshot_lock = dh.snapshot.lock().unwrap();
if snapshot_lock.is_none() {
*snapshot_lock = Some(build_snapshot(fs, ino, &dh.node)?);
}
let snapshot = snapshot_lock.as_ref().unwrap();
let mut emitted_lookup_refs = Vec::new();
for snapshot_entry in dir_snapshot::snapshot_entries_after(&snapshot.entries, offset) {
let name_bytes = snapshot_entry.name();
if name_bytes == b"." || name_bytes == b".." {
continue;
}
let dir_entry = DirEntry {
ino: snapshot_entry.inode(),
offset: snapshot_entry.offset(),
type_: snapshot_entry.file_type(),
name: name_bytes,
};
if name_bytes == init_binary::INIT_FILENAME {
let entry = init_binary::init_entry(fs.cfg.entry_timeout, fs.cfg.attr_timeout);
if add_entry(dir_entry, entry)? == 0 {
break;
}
continue;
}
let node = match inode::get_node(fs, dir_entry.ino) {
Ok(node) => node,
Err(_) => continue,
};
inode::inc_lookup(&node);
let entry = inode::build_entry(fs, &node);
let looked_up_inode = entry.inode;
match add_entry(dir_entry, entry) {
Ok(0) => {
inode::forget_one(fs, looked_up_inode, 1);
break;
}
Ok(_) => emitted_lookup_refs.push(looked_up_inode),
Err(err) => {
inode::forget_one(fs, looked_up_inode, 1);
for emitted_inode in emitted_lookup_refs {
inode::forget_one(fs, emitted_inode, 1);
}
return Err(err);
}
}
}
Ok(())
}
pub(crate) fn do_releasedir(
fs: &MemFs,
_ctx: Context,
_ino: u64,
_flags: u32,
handle: u64,
) -> io::Result<()> {
fs.dir_handles.write().unwrap().remove(&handle);
Ok(())
}
impl SnapshotEntry for MemDirEntry {
fn inode(&self) -> u64 {
self.inode
}
fn offset(&self) -> u64 {
self.offset
}
fn file_type(&self) -> u32 {
self.file_type
}
fn name(&self) -> &[u8] {
&self.name
}
}
fn serve_snapshot_entries(
fs: &MemFs,
ino: u64,
handle: u64,
offset: u64,
) -> io::Result<Vec<DirEntry<'static>>> {
let handles = fs.dir_handles.read().unwrap();
let dh = handles.get(&handle).ok_or_else(platform::ebadf)?;
let mut snapshot_lock = dh.snapshot.lock().unwrap();
if snapshot_lock.is_none() {
*snapshot_lock = Some(build_snapshot(fs, ino, &dh.node)?);
}
let snapshot = snapshot_lock.as_ref().unwrap();
Ok(dir_snapshot::serve_snapshot_entries(
&snapshot.entries,
offset,
))
}
fn stream_snapshot_entries(
fs: &MemFs,
ino: u64,
handle: u64,
offset: u64,
add_entry: &mut AddDirEntry<'_>,
) -> io::Result<()> {
let dh = dir_handle(fs, handle)?;
let mut snapshot_lock = dh.snapshot.lock().unwrap();
if snapshot_lock.is_none() {
*snapshot_lock = Some(build_snapshot(fs, ino, &dh.node)?);
}
let snapshot = snapshot_lock.as_ref().unwrap();
dir_snapshot::serve_snapshot_entries_for_each(&snapshot.entries, offset, add_entry)
}
fn dir_handle(fs: &MemFs, handle: u64) -> io::Result<Arc<DirHandle>> {
fs.dir_handles
.read()
.unwrap()
.get(&handle)
.cloned()
.ok_or_else(platform::ebadf)
}
fn build_snapshot(fs: &MemFs, ino: u64, node: &super::types::MemNode) -> io::Result<DirSnapshot> {
let children = match &node.content {
InodeContent::Directory { children, .. } => children.read().unwrap(),
_ => return Err(platform::enotdir()),
};
let mut entries = Vec::with_capacity(children.len() + 3);
entries.push(MemDirEntry {
name: b".".to_vec(),
inode: ino,
offset: 0,
file_type: platform::DIRENT_DIR,
});
let parent_ino = match &node.content {
InodeContent::Directory { parent, .. } => parent.load(Ordering::Relaxed),
_ => ino,
};
entries.push(MemDirEntry {
name: b"..".to_vec(),
inode: parent_ino,
offset: 0,
file_type: platform::DIRENT_DIR,
});
if ino == ROOT_INODE {
entries.push(MemDirEntry {
name: init_binary::INIT_FILENAME.to_vec(),
inode: init_binary::INIT_INODE,
offset: 0,
file_type: platform::DIRENT_REG,
});
}
for (name, &child_ino) in children.iter() {
let child_type = {
let nodes = fs.nodes.read().unwrap();
match nodes.get(&child_ino) {
Some(child) => inode::mode_to_dtype(child.kind),
None => libc::DT_UNKNOWN.into(),
}
};
entries.push(MemDirEntry {
name: name.clone(),
inode: child_ino,
offset: 0,
file_type: child_type,
});
}
for (i, entry) in entries.iter_mut().enumerate() {
entry.offset = (i + 1) as u64;
}
Ok(DirSnapshot { entries })
}