#![allow(missing_docs)]
pub mod config;
mod inode_store;
pub mod sync_io;
mod utils;
use core::panic;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::fs::File;
use std::io::{Error, ErrorKind, Result, Seek, SeekFrom};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Mutex, RwLock, Weak};
use crate::abi::fuse_abi::{stat64, statvfs64, CreateIn, ROOT_ID as FUSE_ROOT_ID};
use crate::api::filesystem::{
Context, DirEntry, Entry, Layer, OpenOptions, ZeroCopyReader, ZeroCopyWriter,
};
#[cfg(not(feature = "async-io"))]
use crate::api::BackendFileSystem;
use crate::api::{SLASH_ASCII, VFS_MAX_INO};
use crate::common::file_buf::FileVolatileSlice;
use crate::common::file_traits::FileReadWriteVolatile;
use vmm_sys_util::tempfile::TempFile;
use self::config::Config;
use self::inode_store::InodeStore;
pub type Inode = u64;
pub type Handle = u64;
pub const MAXNAMELEN: usize = 256;
pub const CURRENT_DIR: &str = ".";
pub const PARENT_DIR: &str = "..";
pub const MAXBUFSIZE: usize = 1 << 20;
pub type BoxedLayer = Box<dyn Layer<Inode = Inode, Handle = Handle> + Send + Sync>;
pub(crate) struct RealInode {
pub layer: Arc<BoxedLayer>,
pub in_upper_layer: bool,
pub inode: u64,
pub whiteout: bool,
pub opaque: bool,
pub stat: Option<stat64>,
}
#[derive(Default)]
pub(crate) struct OverlayInode {
pub childrens: Mutex<HashMap<String, Arc<OverlayInode>>>,
pub parent: Mutex<Weak<OverlayInode>>,
pub real_inodes: Mutex<Vec<RealInode>>,
pub inode: u64,
pub path: String,
pub name: String,
pub lookups: AtomicU64,
pub whiteout: AtomicBool,
pub loaded: AtomicBool,
}
#[derive(Default)]
pub enum CachePolicy {
Never,
#[default]
Auto,
Always,
}
pub struct OverlayFs {
config: Config,
lower_layers: Vec<Arc<BoxedLayer>>,
upper_layer: Option<Arc<BoxedLayer>>,
inodes: RwLock<InodeStore>,
handles: Mutex<HashMap<u64, Arc<HandleData>>>,
next_handle: AtomicU64,
writeback: AtomicBool,
no_open: AtomicBool,
no_opendir: AtomicBool,
killpriv_v2: AtomicBool,
perfile_dax: AtomicBool,
}
struct RealHandle {
layer: Arc<BoxedLayer>,
in_upper_layer: bool,
inode: u64,
handle: AtomicU64,
}
struct HandleData {
node: Arc<OverlayInode>,
real_handle: Option<RealHandle>,
}
impl RealInode {
fn new(
layer: Arc<BoxedLayer>,
in_upper_layer: bool,
inode: u64,
whiteout: bool,
opaque: bool,
) -> Self {
let mut ri = RealInode {
layer,
in_upper_layer,
inode,
whiteout,
opaque,
stat: None,
};
match ri.stat64_ignore_enoent(&Context::default()) {
Ok(v) => {
ri.stat = v;
}
Err(e) => {
error!("stat64 failed during RealInode creation: {}", e);
}
}
ri
}
fn stat64(&self, ctx: &Context) -> Result<stat64> {
let layer = self.layer.as_ref();
if self.inode == 0 {
return Err(Error::from_raw_os_error(libc::ENOENT));
}
match layer.getattr(ctx, self.inode, None) {
Ok((v1, _v2)) => Ok(v1),
Err(e) => Err(e),
}
}
fn stat64_ignore_enoent(&self, ctx: &Context) -> Result<Option<stat64>> {
match self.stat64(ctx) {
Ok(v1) => Ok(Some(v1)),
Err(e) => match e.raw_os_error() {
Some(raw_error) => {
if raw_error != libc::ENOENT || raw_error != libc::ENAMETOOLONG {
return Ok(None);
}
Err(e)
}
None => Err(e),
},
}
}
fn lookup_child_ignore_enoent(&self, ctx: &Context, name: &str) -> Result<Option<Entry>> {
let cname = CString::new(name).map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
let layer = self.layer.as_ref();
match layer.lookup(ctx, self.inode, cname.as_c_str()) {
Ok(v) => {
if v.inode == 0 {
return Ok(None);
}
Ok(Some(v))
}
Err(e) => {
if let Some(raw_error) = e.raw_os_error() {
if raw_error == libc::ENOENT || raw_error == libc::ENAMETOOLONG {
return Ok(None);
}
}
Err(e)
}
}
}
fn lookup_child(&self, ctx: &Context, name: &str) -> Result<Option<RealInode>> {
if self.whiteout {
return Ok(None);
}
let layer = self.layer.as_ref();
match self.lookup_child_ignore_enoent(ctx, name)? {
Some(v) => {
let (whiteout, opaque) = if utils::is_dir(v.attr) {
(false, layer.is_opaque(ctx, v.inode)?)
} else {
(layer.is_whiteout(ctx, v.inode)?, false)
};
Ok(Some(RealInode {
layer: self.layer.clone(),
in_upper_layer: self.in_upper_layer,
inode: v.inode,
whiteout,
opaque,
stat: Some(v.attr),
}))
}
None => Ok(None),
}
}
fn readdir(&self, ctx: &Context) -> Result<HashMap<String, RealInode>> {
if self.whiteout {
return Err(Error::from_raw_os_error(libc::ENOENT));
}
let stat = match self.stat {
Some(v) => v,
None => self.stat64(ctx)?,
};
if !utils::is_dir(stat) {
return Err(Error::from_raw_os_error(libc::ENOTDIR));
}
let opendir_res = self.layer.opendir(ctx, self.inode, libc::O_RDONLY as u32);
let handle = match opendir_res {
Ok((handle, _)) => match handle {
Some(h) => h,
_ => 0,
},
Err(e) => {
match e.raw_os_error() {
Some(raw_error) => {
if raw_error == libc::ENOSYS {
0
} else {
return Err(e);
}
}
None => {
return Err(e);
}
}
}
};
let mut child_names = vec![];
let mut more = true;
let mut offset = 0;
let bufsize = 1024;
while more {
more = false;
self.layer.readdir(
ctx,
self.inode,
handle,
bufsize,
offset,
&mut |d| -> Result<usize> {
more = true;
offset = d.offset;
let child_name = String::from_utf8_lossy(d.name).into_owned();
trace!("entry: {}", child_name.as_str());
if child_name.eq(CURRENT_DIR) || child_name.eq(PARENT_DIR) {
return Ok(1);
}
child_names.push(child_name);
Ok(1)
},
)?;
}
if handle > 0 {
if let Err(e) = self
.layer
.releasedir(ctx, self.inode, libc::O_RDONLY as u32, handle)
{
match e.raw_os_error() {
Some(raw_error) => {
if raw_error != libc::ENOSYS {
return Err(e);
}
}
None => {
return Err(e);
}
}
}
}
let mut child_real_inodes = HashMap::new();
for name in child_names {
if let Some(child) = self.lookup_child(ctx, name.as_str())? {
child_real_inodes.insert(name, child);
}
}
Ok(child_real_inodes)
}
fn create_whiteout(&self, ctx: &Context, name: &str) -> Result<RealInode> {
if !self.in_upper_layer {
return Err(Error::from_raw_os_error(libc::EROFS));
}
let cname = utils::to_cstring(name)?;
let entry = self
.layer
.create_whiteout(ctx, self.inode, cname.as_c_str())?;
Ok(RealInode {
layer: self.layer.clone(),
in_upper_layer: true,
inode: entry.inode,
whiteout: true,
opaque: false,
stat: Some(entry.attr),
})
}
fn mkdir(&self, ctx: &Context, name: &str, mode: u32, umask: u32) -> Result<RealInode> {
if !self.in_upper_layer {
return Err(Error::from_raw_os_error(libc::EROFS));
}
let cname = utils::to_cstring(name)?;
let entry = self
.layer
.mkdir(ctx, self.inode, cname.as_c_str(), mode, umask)?;
Ok(RealInode {
layer: self.layer.clone(),
in_upper_layer: true,
inode: entry.inode,
whiteout: false,
opaque: false,
stat: Some(entry.attr),
})
}
fn create(
&self,
ctx: &Context,
name: &str,
args: CreateIn,
) -> Result<(RealInode, Option<u64>)> {
if !self.in_upper_layer {
return Err(Error::from_raw_os_error(libc::EROFS));
}
let (entry, h, _, _) =
self.layer
.create(ctx, self.inode, utils::to_cstring(name)?.as_c_str(), args)?;
Ok((
RealInode {
layer: self.layer.clone(),
in_upper_layer: true,
inode: entry.inode,
whiteout: false,
opaque: false,
stat: Some(entry.attr),
},
h,
))
}
fn mknod(
&self,
ctx: &Context,
name: &str,
mode: u32,
rdev: u32,
umask: u32,
) -> Result<RealInode> {
if !self.in_upper_layer {
return Err(Error::from_raw_os_error(libc::EROFS));
}
let entry = self.layer.mknod(
ctx,
self.inode,
utils::to_cstring(name)?.as_c_str(),
mode,
rdev,
umask,
)?;
Ok(RealInode {
layer: self.layer.clone(),
in_upper_layer: true,
inode: entry.inode,
whiteout: false,
opaque: false,
stat: Some(entry.attr),
})
}
fn link(&self, ctx: &Context, ino: u64, name: &str) -> Result<RealInode> {
if !self.in_upper_layer {
return Err(Error::from_raw_os_error(libc::EROFS));
}
let entry = self
.layer
.link(ctx, ino, self.inode, utils::to_cstring(name)?.as_c_str())?;
let opaque = if utils::is_dir(entry.attr) {
self.layer.is_opaque(ctx, entry.inode)?
} else {
false
};
Ok(RealInode {
layer: self.layer.clone(),
in_upper_layer: true,
inode: entry.inode,
whiteout: false,
opaque,
stat: Some(entry.attr),
})
}
fn symlink(&self, ctx: &Context, link_name: &str, filename: &str) -> Result<RealInode> {
if !self.in_upper_layer {
return Err(Error::from_raw_os_error(libc::EROFS));
}
let entry = self.layer.symlink(
ctx,
utils::to_cstring(link_name)?.as_c_str(),
self.inode,
utils::to_cstring(filename)?.as_c_str(),
)?;
Ok(RealInode {
layer: self.layer.clone(),
in_upper_layer: self.in_upper_layer,
inode: entry.inode,
whiteout: false,
opaque: false,
stat: Some(entry.attr),
})
}
}
impl Drop for RealInode {
fn drop(&mut self) {
let ctx = Context::default();
let layer = self.layer.as_ref();
let inode = self.inode;
debug!("forget inode {} by 1 for backend inode in layer ", inode);
layer.forget(&ctx, inode, 1);
}
}
impl OverlayInode {
pub fn new() -> Self {
OverlayInode::default()
}
pub fn new_from_real_inode(name: &str, ino: u64, path: String, real_inode: RealInode) -> Self {
let mut new = OverlayInode::new();
new.inode = ino;
new.path = path.clone();
new.name = name.to_string();
new.whiteout.store(real_inode.whiteout, Ordering::Relaxed);
new.lookups = AtomicU64::new(1);
new.real_inodes = Mutex::new(vec![real_inode]);
new
}
pub fn new_from_real_inodes(
name: &str,
ino: u64,
path: String,
real_inodes: Vec<RealInode>,
) -> Result<Self> {
if real_inodes.is_empty() {
error!("BUG: new_from_real_inodes() called with empty real_inodes");
return Err(Error::from_raw_os_error(libc::EINVAL));
}
let mut first = true;
let mut new = Self::new();
for ri in real_inodes {
let whiteout = ri.whiteout;
let opaque = ri.opaque;
let stat = match ri.stat {
Some(v) => v,
None => ri.stat64(&Context::default())?,
};
if first {
first = false;
new = Self::new_from_real_inode(name, ino, path.clone(), ri);
if whiteout {
break;
}
if !utils::is_dir(stat) {
break;
}
if opaque {
break;
}
} else {
if ri.whiteout {
break;
}
if !utils::is_dir(stat) {
error!("invalid layout: non-directory has multiple real inodes");
break;
}
new.real_inodes.lock().unwrap().push(ri);
if opaque {
break;
}
}
}
Ok(new)
}
pub fn stat64(&self, ctx: &Context) -> Result<stat64> {
for l in self.real_inodes.lock().unwrap().iter() {
if let Some(v) = l.stat64_ignore_enoent(ctx)? {
return Ok(v);
}
}
Err(Error::from_raw_os_error(libc::ENOENT))
}
pub fn count_entries_and_whiteout(&self, ctx: &Context) -> Result<(u64, u64)> {
let mut count = 0;
let mut whiteouts = 0;
let st = self.stat64(ctx)?;
if !utils::is_dir(st) {
return Err(Error::from_raw_os_error(libc::ENOTDIR));
}
for (_, child) in self.childrens.lock().unwrap().iter() {
if child.whiteout.load(Ordering::Relaxed) {
whiteouts += 1;
} else {
count += 1;
}
}
Ok((count, whiteouts))
}
pub fn open(
&self,
ctx: &Context,
flags: u32,
fuse_flags: u32,
) -> Result<(Arc<BoxedLayer>, Option<Handle>, OpenOptions)> {
let (layer, _, inode) = self.first_layer_inode();
let (h, o, _) = layer.as_ref().open(ctx, inode, flags, fuse_flags)?;
Ok((layer, h, o))
}
pub fn scan_childrens(self: &Arc<Self>, ctx: &Context) -> Result<Vec<OverlayInode>> {
let st = self.stat64(ctx)?;
if !utils::is_dir(st) {
return Err(Error::from_raw_os_error(libc::ENOTDIR));
}
let mut all_layer_inodes: HashMap<String, Vec<RealInode>> = HashMap::new();
let mut counter = 1;
let layers_count = self.real_inodes.lock().unwrap().len();
for ri in self.real_inodes.lock().unwrap().iter() {
debug!(
"loading Layer {}/{} for dir '{}', is_upper_layer: {}",
counter,
layers_count,
self.path.as_str(),
ri.in_upper_layer
);
counter += 1;
if ri.whiteout {
debug!("directory is whiteout");
break;
}
let stat = match ri.stat {
Some(v) => v,
None => ri.stat64(ctx)?,
};
if !utils::is_dir(stat) {
debug!("{} is not a directory", self.path.as_str());
break;
}
let entries = ri.readdir(ctx)?;
for (name, inode) in entries {
match all_layer_inodes.get_mut(&name) {
Some(v) => {
v.push(inode)
}
None => {
all_layer_inodes.insert(name, vec![inode]);
}
};
}
if ri.opaque {
debug!("directory {} is opaque", self.path.as_str());
break;
}
}
let mut childrens = vec![];
for (name, real_inodes) in all_layer_inodes {
let path = format!("{}/{}", self.path, name);
let new = Self::new_from_real_inodes(name.as_str(), 0, path, real_inodes)?;
childrens.push(new);
}
Ok(childrens)
}
pub fn create_upper_dir(
self: &Arc<Self>,
ctx: &Context,
mode_umask: Option<(u32, u32)>,
) -> Result<()> {
let st = self.stat64(ctx)?;
if !utils::is_dir(st) {
return Err(Error::from_raw_os_error(libc::ENOTDIR));
}
if self.in_upper_layer() {
return Ok(());
}
let pnode = if let Some(n) = self.parent.lock().unwrap().upgrade() {
Arc::clone(&n)
} else {
return Err(Error::new(ErrorKind::Other, "no parent?"));
};
if !pnode.in_upper_layer() {
pnode.create_upper_dir(ctx, None)?; }
let mut child = None;
pnode.handle_upper_inode_locked(&mut |parent_upper_inode| -> Result<bool> {
match parent_upper_inode {
Some(parent_ri) => {
let ri = match mode_umask {
Some((mode, umask)) => {
parent_ri.mkdir(ctx, self.name.as_str(), mode, umask)?
}
None => parent_ri.mkdir(ctx, self.name.as_str(), st.st_mode, 0)?,
};
child.replace(ri);
}
None => {
error!(
"BUG: parent {} has no upper inode after create_upper_dir",
pnode.inode
);
return Err(Error::from_raw_os_error(libc::EINVAL));
}
}
Ok(false)
})?;
if let Some(ri) = child {
self.add_upper_inode(ri, false);
}
Ok(())
}
fn add_upper_inode(self: &Arc<Self>, ri: RealInode, clear_lowers: bool) {
let mut inodes = self.real_inodes.lock().unwrap();
self.whiteout.store(ri.whiteout, Ordering::Relaxed);
let mut new = vec![ri];
let lowers = inodes.drain(..).collect::<Vec<RealInode>>();
if !clear_lowers {
new.extend(lowers);
}
inodes.extend(new);
}
pub fn in_upper_layer(&self) -> bool {
let all_inodes = self.real_inodes.lock().unwrap();
let first = all_inodes.first();
match first {
Some(v) => v.in_upper_layer,
None => false,
}
}
pub fn upper_layer_only(&self) -> bool {
let real_inodes = self.real_inodes.lock().unwrap();
let first = real_inodes.first();
match first {
Some(v) => {
if !v.in_upper_layer {
false
} else {
real_inodes.len() == 1
}
}
None => false,
}
}
pub fn first_layer_inode(&self) -> (Arc<BoxedLayer>, bool, u64) {
let all_inodes = self.real_inodes.lock().unwrap();
let first = all_inodes.first();
match first {
Some(v) => (v.layer.clone(), v.in_upper_layer, v.inode),
None => panic!("BUG: dangling OverlayInode"),
}
}
pub fn child(&self, name: &str) -> Option<Arc<OverlayInode>> {
self.childrens.lock().unwrap().get(name).cloned()
}
pub fn remove_child(&self, name: &str) {
self.childrens.lock().unwrap().remove(name);
}
pub fn insert_child(&self, name: &str, node: Arc<OverlayInode>) {
self.childrens
.lock()
.unwrap()
.insert(name.to_string(), node);
}
pub fn handle_upper_inode_locked(
&self,
f: &mut dyn FnMut(Option<&RealInode>) -> Result<bool>,
) -> Result<bool> {
let all_inodes = self.real_inodes.lock().unwrap();
let first = all_inodes.first();
match first {
Some(v) => {
if v.in_upper_layer {
f(Some(v))
} else {
f(None)
}
}
None => Err(Error::new(
ErrorKind::Other,
format!(
"BUG: dangling OverlayInode {} without any backend inode",
self.inode
),
)),
}
}
}
fn entry_type_from_mode(mode: libc::mode_t) -> u8 {
match mode & libc::S_IFMT {
libc::S_IFBLK => libc::DT_BLK,
libc::S_IFCHR => libc::DT_CHR,
libc::S_IFDIR => libc::DT_DIR,
libc::S_IFIFO => libc::DT_FIFO,
libc::S_IFLNK => libc::DT_LNK,
libc::S_IFREG => libc::DT_REG,
libc::S_IFSOCK => libc::DT_SOCK,
_ => libc::DT_UNKNOWN,
}
}
impl OverlayFs {
pub fn new(
upper: Option<Arc<BoxedLayer>>,
lowers: Vec<Arc<BoxedLayer>>,
params: Config,
) -> Result<Self> {
Ok(OverlayFs {
config: params,
lower_layers: lowers,
upper_layer: upper,
inodes: RwLock::new(InodeStore::new()),
handles: Mutex::new(HashMap::new()),
next_handle: AtomicU64::new(1),
writeback: AtomicBool::new(false),
no_open: AtomicBool::new(false),
no_opendir: AtomicBool::new(false),
killpriv_v2: AtomicBool::new(false),
perfile_dax: AtomicBool::new(false),
})
}
pub fn root_inode(&self) -> Inode {
FUSE_ROOT_ID
}
fn alloc_inode(&self, path: &String) -> Result<u64> {
self.inodes.write().unwrap().alloc_inode(path)
}
pub fn import(&self) -> Result<()> {
let mut root = OverlayInode::new();
root.inode = FUSE_ROOT_ID;
root.path = String::from("");
root.name = String::from("");
root.lookups = AtomicU64::new(2);
root.real_inodes = Mutex::new(vec![]);
let ctx = Context::default();
if let Some(layer) = self.upper_layer.as_ref() {
let ino = layer.root_inode();
let real = RealInode::new(layer.clone(), true, ino, false, layer.is_opaque(&ctx, ino)?);
root.real_inodes.lock().unwrap().push(real);
}
for layer in self.lower_layers.iter() {
let ino = layer.root_inode();
let real = RealInode::new(
layer.clone(),
false,
ino,
false,
layer.is_opaque(&ctx, ino)?,
);
root.real_inodes.lock().unwrap().push(real);
}
let root_node = Arc::new(root);
self.insert_inode(FUSE_ROOT_ID, Arc::clone(&root_node));
info!("loading root directory\n");
self.load_directory(&ctx, &root_node)?;
Ok(())
}
fn root_node(&self) -> Arc<OverlayInode> {
self.get_active_inode(FUSE_ROOT_ID).unwrap()
}
fn insert_inode(&self, inode: u64, node: Arc<OverlayInode>) {
self.inodes.write().unwrap().insert_inode(inode, node);
}
fn get_active_inode(&self, inode: u64) -> Option<Arc<OverlayInode>> {
self.inodes.read().unwrap().get_inode(inode)
}
fn get_all_inode(&self, inode: u64) -> Option<Arc<OverlayInode>> {
let inode_store = self.inodes.read().unwrap();
match inode_store.get_inode(inode) {
Some(n) => Some(n),
None => inode_store.get_deleted_inode(inode),
}
}
fn remove_inode(&self, inode: u64, path_removed: Option<String>) -> Option<Arc<OverlayInode>> {
self.inodes
.write()
.unwrap()
.remove_inode(inode, path_removed)
}
fn lookup_node(&self, ctx: &Context, parent: Inode, name: &str) -> Result<Arc<OverlayInode>> {
if name.contains([SLASH_ASCII as char]) {
return Err(Error::from_raw_os_error(libc::EINVAL));
}
let pnode = match self.get_active_inode(parent) {
Some(v) => v,
None => return Err(Error::from_raw_os_error(libc::ENOENT)),
};
if pnode.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::ENOENT));
}
let st = pnode.stat64(ctx)?;
if utils::is_dir(st) && !pnode.loaded.load(Ordering::Relaxed) {
self.load_directory(ctx, &pnode)?;
}
if name.eq(".")
|| (parent == FUSE_ROOT_ID && name.eq(".."))
|| name.is_empty()
{
return Ok(Arc::clone(&pnode));
}
match pnode.child(name) {
Some(v) => Ok(v),
None => Err(Error::from_raw_os_error(libc::ENOENT)),
}
}
#[allow(dead_code)]
fn debug_print_all_inodes(&self) {
self.inodes.read().unwrap().debug_print_all_inodes();
}
fn lookup_node_ignore_enoent(
&self,
ctx: &Context,
parent: u64,
name: &str,
) -> Result<Option<Arc<OverlayInode>>> {
match self.lookup_node(ctx, parent, name) {
Ok(n) => Ok(Some(Arc::clone(&n))),
Err(e) => {
if let Some(raw_error) = e.raw_os_error() {
if raw_error == libc::ENOENT {
return Ok(None);
}
}
Err(e)
}
}
}
fn load_directory(&self, ctx: &Context, node: &Arc<OverlayInode>) -> Result<()> {
if node.loaded.load(Ordering::Relaxed) {
return Ok(());
}
let childrens = node.scan_childrens(ctx)?;
let mut inode_store = self.inodes.write().unwrap();
let mut node_children = node.childrens.lock().unwrap();
if node.loaded.load(Ordering::Relaxed) {
return Ok(());
}
for mut child in childrens.into_iter() {
let ino = inode_store.alloc_inode(&child.path)?;
let name = child.name.clone();
child.inode = ino;
child.parent = Mutex::new(Arc::downgrade(node));
let arc_child = Arc::new(child);
node_children.insert(name, arc_child.clone());
inode_store.insert_inode(ino, arc_child.clone());
}
node.loaded.store(true, Ordering::Relaxed);
Ok(())
}
fn forget_one(&self, inode: Inode, count: u64) {
if inode == self.root_inode() || inode == 0 {
return;
}
let v = match self.get_all_inode(inode) {
Some(n) => n,
None => {
trace!("forget unknown inode: {}", inode);
return;
}
};
let mut lookups = v.lookups.load(Ordering::Relaxed);
if lookups < count {
lookups = 0;
} else {
lookups -= count;
}
v.lookups.store(lookups, Ordering::Relaxed);
if lookups == 0 {
debug!("inode is forgotten: {}, name {}", inode, v.name);
let _ = self.remove_inode(inode, None);
let parent = v.parent.lock().unwrap();
if let Some(p) = parent.upgrade() {
p.remove_child(v.name.as_str());
}
}
}
fn do_lookup(&self, ctx: &Context, parent: Inode, name: &str) -> Result<Entry> {
let node = self.lookup_node(ctx, parent, name)?;
if node.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::ENOENT));
}
let st = node.stat64(ctx)?;
if utils::is_dir(st) && !node.loaded.load(Ordering::Relaxed) {
self.load_directory(ctx, &node)?;
}
let tmp = node.lookups.fetch_add(1, Ordering::Relaxed);
trace!("lookup count: {}", tmp + 1);
Ok(Entry {
inode: node.inode,
generation: 0,
attr: st,
attr_flags: 0,
attr_timeout: self.config.attr_timeout,
entry_timeout: self.config.entry_timeout,
})
}
fn do_statvfs(&self, ctx: &Context, inode: Inode) -> Result<statvfs64> {
match self.get_active_inode(inode) {
Some(ovi) => {
let all_inodes = ovi.real_inodes.lock().unwrap();
let real_inode = all_inodes
.first()
.ok_or(Error::new(ErrorKind::Other, "backend inode not found"))?;
real_inode.layer.statfs(ctx, real_inode.inode)
}
None => Err(Error::from_raw_os_error(libc::ENOENT)),
}
}
#[allow(clippy::too_many_arguments)]
fn do_readdir(
&self,
ctx: &Context,
inode: Inode,
handle: u64,
size: u32,
offset: u64,
is_readdirplus: bool,
add_entry: &mut dyn FnMut(DirEntry, Option<Entry>) -> Result<usize>,
) -> Result<()> {
trace!(
"do_readir: handle: {}, size: {}, offset: {}",
handle,
size,
offset
);
if size == 0 {
return Ok(());
}
let ovl_inode = match self.handles.lock().unwrap().get(&handle) {
Some(dir) => dir.node.clone(),
None => {
let node = self.lookup_node(ctx, inode, ".")?;
let st = node.stat64(ctx)?;
if !utils::is_dir(st) {
return Err(Error::from_raw_os_error(libc::ENOTDIR));
}
node.clone()
}
};
let mut childrens = Vec::new();
childrens.push((".".to_string(), ovl_inode.clone()));
let parent_node = match ovl_inode.parent.lock().unwrap().upgrade() {
Some(p) => p.clone(),
None => self.root_node(),
};
childrens.push(("..".to_string(), parent_node));
for (_, child) in ovl_inode.childrens.lock().unwrap().iter() {
if child.whiteout.load(Ordering::Relaxed) {
continue;
}
childrens.push((child.name.clone(), child.clone()));
}
let mut len: usize = 0;
if offset >= childrens.len() as u64 {
return Ok(());
}
for (index, (name, child)) in (0_u64..).zip(childrens.into_iter()) {
if index >= offset {
let st = child.stat64(ctx)?;
let dir_entry = DirEntry {
ino: st.st_ino,
offset: index + 1,
type_: entry_type_from_mode(st.st_mode) as u32,
name: name.as_bytes(),
};
let entry = if is_readdirplus {
child.lookups.fetch_add(1, Ordering::Relaxed);
Some(Entry {
inode: child.inode,
generation: 0,
attr: st,
attr_flags: 0,
attr_timeout: self.config.attr_timeout,
entry_timeout: self.config.entry_timeout,
})
} else {
None
};
match add_entry(dir_entry, entry) {
Ok(0) => break,
Ok(l) => {
len += l;
if len as u32 >= size {
return Ok(());
}
}
Err(e) => {
if len == 0 {
return Err(e);
} else {
return Ok(());
}
}
}
}
}
Ok(())
}
fn do_mkdir(
&self,
ctx: &Context,
parent_node: &Arc<OverlayInode>,
name: &str,
mode: u32,
umask: u32,
) -> Result<()> {
if self.upper_layer.is_none() {
return Err(Error::from_raw_os_error(libc::EROFS));
}
if parent_node.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::ENOENT));
}
let mut delete_whiteout = false;
let mut set_opaque = false;
if let Some(n) = self.lookup_node_ignore_enoent(ctx, parent_node.inode, name)? {
if !n.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::EEXIST));
}
if n.in_upper_layer() {
delete_whiteout = true;
}
if !n.upper_layer_only() {
set_opaque = true;
}
}
let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?;
let mut new_node = None;
let path = format!("{}/{}", pnode.path, name);
pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result<bool> {
let parent_real_inode = match parent_real_inode {
Some(inode) => inode,
None => {
error!("BUG: parent doesn't have upper inode after copied up");
return Err(Error::from_raw_os_error(libc::EINVAL));
}
};
if delete_whiteout {
let _ = parent_real_inode.layer.delete_whiteout(
ctx,
parent_real_inode.inode,
utils::to_cstring(name)?.as_c_str(),
);
}
let ino = self.alloc_inode(&path)?;
let child_dir = parent_real_inode.mkdir(ctx, name, mode, umask)?;
if set_opaque {
parent_real_inode.layer.set_opaque(ctx, child_dir.inode)?;
}
let ovi = OverlayInode::new_from_real_inode(name, ino, path.clone(), child_dir);
new_node.replace(ovi);
Ok(false)
})?;
let arc_node = Arc::new(new_node.unwrap());
self.insert_inode(arc_node.inode, arc_node.clone());
pnode.insert_child(name, arc_node);
Ok(())
}
fn do_mknod(
&self,
ctx: &Context,
parent_node: &Arc<OverlayInode>,
name: &str,
mode: u32,
rdev: u32,
umask: u32,
) -> Result<()> {
if self.upper_layer.is_none() {
return Err(Error::from_raw_os_error(libc::EROFS));
}
if parent_node.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::ENOENT));
}
match self.lookup_node_ignore_enoent(ctx, parent_node.inode, name)? {
Some(n) => {
if !n.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::EEXIST));
}
let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?;
pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result<bool> {
let parent_real_inode = match parent_real_inode {
Some(inode) => inode,
None => {
error!("BUG: parent doesn't have upper inode after copied up");
return Err(Error::from_raw_os_error(libc::EINVAL));
}
};
if n.in_upper_layer() {
let _ = parent_real_inode.layer.delete_whiteout(
ctx,
parent_real_inode.inode,
utils::to_cstring(name)?.as_c_str(),
);
}
let child_ri = parent_real_inode.mknod(ctx, name, mode, rdev, umask)?;
n.add_upper_inode(child_ri, true);
Ok(false)
})?;
}
None => {
let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?;
let mut new_node = None;
let path = format!("{}/{}", pnode.path, name);
pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result<bool> {
let parent_real_inode = match parent_real_inode {
Some(inode) => inode,
None => {
error!("BUG: parent doesn't have upper inode after copied up");
return Err(Error::from_raw_os_error(libc::EINVAL));
}
};
let ino = self.alloc_inode(&path)?;
let child_ri = parent_real_inode.mknod(ctx, name, mode, rdev, umask)?;
let ovi = OverlayInode::new_from_real_inode(name, ino, path.clone(), child_ri);
new_node.replace(ovi);
Ok(false)
})?;
let arc_node = Arc::new(new_node.unwrap());
self.insert_inode(arc_node.inode, arc_node.clone());
pnode.insert_child(name, arc_node);
}
}
Ok(())
}
fn do_create(
&self,
ctx: &Context,
parent_node: &Arc<OverlayInode>,
name: &str,
args: CreateIn,
) -> Result<Option<u64>> {
let upper = self
.upper_layer
.as_ref()
.cloned()
.ok_or_else(|| Error::from_raw_os_error(libc::EROFS))?;
if parent_node.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::ENOENT));
}
let mut handle = None;
let mut real_ino = 0u64;
let new_ovi = match self.lookup_node_ignore_enoent(ctx, parent_node.inode, name)? {
Some(n) => {
if !n.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::EEXIST));
}
let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?;
pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result<bool> {
let parent_real_inode = match parent_real_inode {
Some(inode) => inode,
None => {
error!("BUG: parent doesn't have upper inode after copied up");
return Err(Error::from_raw_os_error(libc::EINVAL));
}
};
if n.in_upper_layer() {
let _ = parent_real_inode.layer.delete_whiteout(
ctx,
parent_real_inode.inode,
utils::to_cstring(name)?.as_c_str(),
);
}
let (child_ri, hd) = parent_real_inode.create(ctx, name, args)?;
real_ino = child_ri.inode;
handle = hd;
n.add_upper_inode(child_ri, true);
Ok(false)
})?;
n.clone()
}
None => {
let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?;
let mut new_node = None;
let path = format!("{}/{}", pnode.path, name);
pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result<bool> {
let parent_real_inode = match parent_real_inode {
Some(inode) => inode,
None => {
error!("BUG: parent doesn't have upper inode after copied up");
return Err(Error::from_raw_os_error(libc::EINVAL));
}
};
let (child_ri, hd) = parent_real_inode.create(ctx, name, args)?;
real_ino = child_ri.inode;
handle = hd;
let ino = self.alloc_inode(&path)?;
let ovi = OverlayInode::new_from_real_inode(name, ino, path.clone(), child_ri);
new_node.replace(ovi);
Ok(false)
})?;
let arc_node = Arc::new(new_node.unwrap());
self.insert_inode(arc_node.inode, arc_node.clone());
pnode.insert_child(name, arc_node.clone());
arc_node
}
};
let final_handle = match handle {
Some(hd) => {
if self.no_open.load(Ordering::Relaxed) {
None
} else {
let handle = self.next_handle.fetch_add(1, Ordering::Relaxed);
let handle_data = HandleData {
node: new_ovi,
real_handle: Some(RealHandle {
layer: upper.clone(),
in_upper_layer: true,
inode: real_ino,
handle: AtomicU64::new(hd),
}),
};
self.handles
.lock()
.unwrap()
.insert(handle, Arc::new(handle_data));
Some(handle)
}
}
None => None,
};
Ok(final_handle)
}
fn do_link(
&self,
ctx: &Context,
src_node: &Arc<OverlayInode>,
new_parent: &Arc<OverlayInode>,
name: &str,
) -> Result<()> {
if self.upper_layer.is_none() {
return Err(Error::from_raw_os_error(libc::EROFS));
}
if src_node.whiteout.load(Ordering::Relaxed) || new_parent.whiteout.load(Ordering::Relaxed)
{
return Err(Error::from_raw_os_error(libc::ENOENT));
}
let st = src_node.stat64(ctx)?;
if utils::is_dir(st) {
return Err(Error::from_raw_os_error(libc::EPERM));
}
let src_node = self.copy_node_up(ctx, Arc::clone(src_node))?;
let new_parent = self.copy_node_up(ctx, Arc::clone(new_parent))?;
let src_ino = src_node.first_layer_inode().2;
match self.lookup_node_ignore_enoent(ctx, new_parent.inode, name)? {
Some(n) => {
if !n.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::EEXIST));
}
new_parent.handle_upper_inode_locked(&mut |parent_real_inode| -> Result<bool> {
let parent_real_inode = match parent_real_inode {
Some(inode) => inode,
None => {
error!("BUG: parent doesn't have upper inode after copied up");
return Err(Error::from_raw_os_error(libc::EINVAL));
}
};
if n.in_upper_layer() {
let _ = parent_real_inode.layer.delete_whiteout(
ctx,
parent_real_inode.inode,
utils::to_cstring(name)?.as_c_str(),
);
}
let child_ri = parent_real_inode.link(ctx, src_ino, name)?;
n.add_upper_inode(child_ri, true);
Ok(false)
})?;
}
None => {
let mut new_node = None;
new_parent.handle_upper_inode_locked(&mut |parent_real_inode| -> Result<bool> {
let parent_real_inode = match parent_real_inode {
Some(inode) => inode,
None => {
error!("BUG: parent doesn't have upper inode after copied up");
return Err(Error::from_raw_os_error(libc::EINVAL));
}
};
let path = format!("{}/{}", new_parent.path, name);
let ino = self.alloc_inode(&path)?;
let child_ri = parent_real_inode.link(ctx, src_ino, name)?;
let ovi = OverlayInode::new_from_real_inode(name, ino, path, child_ri);
new_node.replace(ovi);
Ok(false)
})?;
let arc_node = Arc::new(new_node.unwrap());
self.insert_inode(arc_node.inode, arc_node.clone());
new_parent.insert_child(name, arc_node);
}
}
Ok(())
}
fn do_symlink(
&self,
ctx: &Context,
linkname: &str,
parent_node: &Arc<OverlayInode>,
name: &str,
) -> Result<()> {
if self.upper_layer.is_none() {
return Err(Error::from_raw_os_error(libc::EROFS));
}
if parent_node.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::ENOENT));
}
match self.lookup_node_ignore_enoent(ctx, parent_node.inode, name)? {
Some(n) => {
if !n.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::EEXIST));
}
let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?;
pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result<bool> {
let parent_real_inode = match parent_real_inode {
Some(inode) => inode,
None => {
error!("BUG: parent doesn't have upper inode after copied up");
return Err(Error::from_raw_os_error(libc::EINVAL));
}
};
if n.in_upper_layer() {
let _ = parent_real_inode.layer.delete_whiteout(
ctx,
parent_real_inode.inode,
utils::to_cstring(name)?.as_c_str(),
);
}
let child_ri = parent_real_inode.symlink(ctx, linkname, name)?;
n.add_upper_inode(child_ri, true);
Ok(false)
})?;
}
None => {
let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?;
let mut new_node = None;
let path = format!("{}/{}", pnode.path, name);
pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result<bool> {
let parent_real_inode = match parent_real_inode {
Some(inode) => inode,
None => {
error!("BUG: parent doesn't have upper inode after copied up");
return Err(Error::from_raw_os_error(libc::EINVAL));
}
};
let ino = self.alloc_inode(&path)?;
let child_ri = parent_real_inode.symlink(ctx, linkname, name)?;
let ovi = OverlayInode::new_from_real_inode(name, ino, path.clone(), child_ri);
new_node.replace(ovi);
Ok(false)
})?;
let arc_node = Arc::new(new_node.unwrap());
self.insert_inode(arc_node.inode, arc_node.clone());
pnode.insert_child(name, arc_node);
}
}
Ok(())
}
fn copy_symlink_up(&self, ctx: &Context, node: Arc<OverlayInode>) -> Result<Arc<OverlayInode>> {
if node.in_upper_layer() {
return Ok(node);
}
let parent_node = if let Some(ref n) = node.parent.lock().unwrap().upgrade() {
Arc::clone(n)
} else {
return Err(Error::new(ErrorKind::Other, "no parent?"));
};
let (self_layer, _, self_inode) = node.first_layer_inode();
if !parent_node.in_upper_layer() {
parent_node.create_upper_dir(ctx, None)?;
}
let path = self_layer.readlink(ctx, self_inode)?;
let path =
std::str::from_utf8(&path).map_err(|_| Error::from_raw_os_error(libc::EINVAL))?;
let mut new_upper_real = None;
parent_node.handle_upper_inode_locked(&mut |parent_upper_inode| -> Result<bool> {
let parent_real_inode =
parent_upper_inode.ok_or_else(|| Error::from_raw_os_error(libc::EROFS))?;
new_upper_real.replace(parent_real_inode.symlink(ctx, path, node.name.as_str())?);
Ok(false)
})?;
if let Some(real_inode) = new_upper_real {
node.add_upper_inode(real_inode, true);
}
Ok(Arc::clone(&node))
}
fn copy_regfile_up(&self, ctx: &Context, node: Arc<OverlayInode>) -> Result<Arc<OverlayInode>> {
if node.in_upper_layer() {
return Ok(node);
}
let parent_node = if let Some(ref n) = node.parent.lock().unwrap().upgrade() {
Arc::clone(n)
} else {
return Err(Error::new(ErrorKind::Other, "no parent?"));
};
let st = node.stat64(ctx)?;
let (lower_layer, _, lower_inode) = node.first_layer_inode();
if !parent_node.in_upper_layer() {
parent_node.create_upper_dir(ctx, None)?;
}
let args = CreateIn {
flags: libc::O_WRONLY as u32,
mode: st.st_mode,
umask: 0,
fuse_flags: 0,
};
let mut upper_handle = 0u64;
let mut upper_real_inode = None;
parent_node.handle_upper_inode_locked(&mut |parent_upper_inode| -> Result<bool> {
let parent_real_inode = parent_upper_inode.ok_or_else(|| {
error!("parent {} has no upper inode", parent_node.inode);
Error::from_raw_os_error(libc::EINVAL)
})?;
let (inode, h) = parent_real_inode.create(ctx, node.name.as_str(), args)?;
upper_handle = h.unwrap_or(0);
upper_real_inode.replace(inode);
Ok(false)
})?;
let (h, _, _) = lower_layer.open(ctx, lower_inode, libc::O_RDONLY as u32, 0)?;
let lower_handle = h.unwrap_or(0);
let mut file = TempFile::new().unwrap().into_file();
let mut offset: usize = 0;
let size = 4 * 1024 * 1024;
loop {
let ret = lower_layer.read(
ctx,
lower_inode,
lower_handle,
&mut file,
size,
offset as u64,
None,
0,
)?;
if ret == 0 {
break;
}
offset += ret;
}
lower_layer.release(ctx, lower_inode, 0, lower_handle, true, true, None)?;
file.seek(SeekFrom::Start(0))?;
offset = 0;
while let Some(ref ri) = upper_real_inode {
let ret = ri.layer.write(
ctx,
ri.inode,
upper_handle,
&mut file,
size,
offset as u64,
None,
false,
0,
0,
)?;
if ret == 0 {
break;
}
offset += ret;
}
drop(file);
if let Some(ri) = upper_real_inode {
if let Err(e) = ri
.layer
.release(ctx, ri.inode, 0, upper_handle, true, true, None)
{
if e.raw_os_error() != Some(libc::ENOSYS) {
return Err(e);
}
}
node.add_upper_inode(ri, true);
}
Ok(Arc::clone(&node))
}
fn copy_node_up(&self, ctx: &Context, node: Arc<OverlayInode>) -> Result<Arc<OverlayInode>> {
if node.in_upper_layer() {
return Ok(node);
}
let st = node.stat64(ctx)?;
if utils::is_dir(st) {
node.create_upper_dir(ctx, None)?;
return Ok(Arc::clone(&node));
}
if st.st_mode & libc::S_IFMT == libc::S_IFLNK {
return self.copy_symlink_up(ctx, Arc::clone(&node));
}
self.copy_regfile_up(ctx, Arc::clone(&node))
}
fn do_rm(&self, ctx: &Context, parent: u64, name: &CStr, dir: bool) -> Result<()> {
if self.upper_layer.is_none() {
return Err(Error::from_raw_os_error(libc::EROFS));
}
let pnode = self.lookup_node(ctx, parent, "")?;
if pnode.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::ENOENT));
}
let sname = name.to_string_lossy().to_string();
let node = self.lookup_node(ctx, parent, sname.as_str())?;
if node.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::ENOENT));
}
if dir {
self.load_directory(ctx, &node)?;
let (count, whiteouts) = node.count_entries_and_whiteout(ctx)?;
trace!("entries: {}, whiteouts: {}\n", count, whiteouts);
if count > 0 {
return Err(Error::from_raw_os_error(libc::ENOTEMPTY));
}
if whiteouts > 0 && node.in_upper_layer() {
self.empty_node_directory(ctx, Arc::clone(&node))?;
}
trace!("whiteouts deleted!\n");
}
let mut need_whiteout = true;
let pnode = self.copy_node_up(ctx, Arc::clone(&pnode))?;
if node.upper_layer_only() {
need_whiteout = false;
}
let mut path_removed = None;
if node.in_upper_layer() {
pnode.handle_upper_inode_locked(&mut |parent_upper_inode| -> Result<bool> {
let parent_real_inode = parent_upper_inode.ok_or_else(|| {
error!(
"BUG: parent {} has no upper inode after copy up",
pnode.inode
);
Error::from_raw_os_error(libc::EINVAL)
})?;
if parent_real_inode.opaque {
need_whiteout = false;
}
if dir {
parent_real_inode
.layer
.rmdir(ctx, parent_real_inode.inode, name)?;
} else {
parent_real_inode
.layer
.unlink(ctx, parent_real_inode.inode, name)?;
}
Ok(false)
})?;
path_removed.replace(node.path.clone());
}
trace!(
"Remove inode {} from global hashmap and parent's children hashmap\n",
node.inode
);
node.lookups.fetch_sub(1, Ordering::Relaxed);
self.remove_inode(node.inode, path_removed);
pnode.remove_child(node.name.as_str());
if need_whiteout {
trace!("do_rm: creating whiteout\n");
pnode.handle_upper_inode_locked(&mut |parent_upper_inode| -> Result<bool> {
let parent_real_inode = parent_upper_inode.ok_or_else(|| {
error!(
"BUG: parent {} has no upper inode after copy up",
pnode.inode
);
Error::from_raw_os_error(libc::EINVAL)
})?;
let child_ri = parent_real_inode.create_whiteout(ctx, sname.as_str())?;
let path = format!("{}/{}", pnode.path, sname);
let ino = self.alloc_inode(&path)?;
let ovi = Arc::new(OverlayInode::new_from_real_inode(
sname.as_str(),
ino,
path.clone(),
child_ri,
));
self.insert_inode(ino, ovi.clone());
pnode.insert_child(sname.as_str(), ovi.clone());
Ok(false)
})?;
}
Ok(())
}
fn do_fsync(
&self,
ctx: &Context,
inode: Inode,
datasync: bool,
handle: Handle,
syncdir: bool,
) -> Result<()> {
let data = self.get_data(ctx, Some(handle), inode, libc::O_RDONLY as u32)?;
match data.real_handle {
None => Err(Error::from_raw_os_error(libc::ENOENT)),
Some(ref rh) => {
let real_handle = rh.handle.load(Ordering::Relaxed);
if syncdir {
rh.layer.fsyncdir(ctx, rh.inode, datasync, real_handle)
} else {
rh.layer.fsync(ctx, rh.inode, datasync, real_handle)
}
}
}
}
fn empty_node_directory(&self, ctx: &Context, node: Arc<OverlayInode>) -> Result<()> {
let st = node.stat64(ctx)?;
if !utils::is_dir(st) {
return Err(Error::from_raw_os_error(libc::ENOTDIR));
}
let (layer, in_upper, inode) = node.first_layer_inode();
if !in_upper {
return Ok(());
}
let iter = node
.childrens
.lock()
.unwrap()
.iter()
.map(|(_, v)| v.clone())
.collect::<Vec<_>>();
for child in iter {
if child.in_upper_layer() {
if child.whiteout.load(Ordering::Relaxed) {
layer.delete_whiteout(
ctx,
inode,
utils::to_cstring(child.name.as_str())?.as_c_str(),
)?
} else {
let s = child.stat64(ctx)?;
let cname = utils::to_cstring(&child.name)?;
if utils::is_dir(s) {
let (count, whiteouts) = child.count_entries_and_whiteout(ctx)?;
if count + whiteouts > 0 {
self.empty_node_directory(ctx, Arc::clone(&child))?;
}
layer.rmdir(ctx, inode, cname.as_c_str())?
} else {
layer.unlink(ctx, inode, cname.as_c_str())?;
}
}
self.remove_inode(child.inode, Some(child.path.clone()));
node.remove_child(child.name.as_str());
}
}
Ok(())
}
fn find_real_info_from_handle(
&self,
handle: Handle,
) -> Result<(Arc<BoxedLayer>, Inode, Handle)> {
match self.handles.lock().unwrap().get(&handle) {
Some(h) => match h.real_handle {
Some(ref rhd) => Ok((
rhd.layer.clone(),
rhd.inode,
rhd.handle.load(Ordering::Relaxed),
)),
None => Err(Error::from_raw_os_error(libc::ENOENT)),
},
None => Err(Error::from_raw_os_error(libc::ENOENT)),
}
}
fn find_real_inode(&self, inode: Inode) -> Result<(Arc<BoxedLayer>, Inode)> {
if let Some(n) = self.get_active_inode(inode) {
let (first_layer, _, first_inode) = n.first_layer_inode();
return Ok((first_layer, first_inode));
}
Err(Error::from_raw_os_error(libc::ENOENT))
}
fn get_data(
&self,
ctx: &Context,
handle: Option<Handle>,
inode: Inode,
flags: u32,
) -> Result<Arc<HandleData>> {
let no_open = self.no_open.load(Ordering::Relaxed);
if !no_open {
if let Some(h) = handle {
if let Some(v) = self.handles.lock().unwrap().get(&h) {
if v.node.inode == inode {
return Ok(Arc::clone(v));
}
}
}
} else {
let readonly: bool = flags
& (libc::O_APPEND | libc::O_CREAT | libc::O_TRUNC | libc::O_RDWR | libc::O_WRONLY)
as u32
== 0;
let node = self.lookup_node(ctx, inode, "")?;
if node.whiteout.load(Ordering::Relaxed) {
return Err(Error::from_raw_os_error(libc::ENOENT));
}
if !readonly {
self.upper_layer
.as_ref()
.cloned()
.ok_or_else(|| Error::from_raw_os_error(libc::EROFS))?;
self.copy_node_up(ctx, Arc::clone(&node))?;
}
let (layer, in_upper_layer, inode) = node.first_layer_inode();
let handle_data = HandleData {
node: Arc::clone(&node),
real_handle: Some(RealHandle {
layer,
in_upper_layer,
inode,
handle: AtomicU64::new(0),
}),
};
return Ok(Arc::new(handle_data));
}
Err(Error::from_raw_os_error(libc::ENOENT))
}
}
impl ZeroCopyReader for File {
fn read_to(
&mut self,
f: &mut dyn FileReadWriteVolatile,
count: usize,
off: u64,
) -> Result<usize> {
let mut buf = vec![0_u8; count];
let slice = unsafe { FileVolatileSlice::from_raw_ptr(buf.as_mut_ptr(), count) };
let ret = self.read_volatile(slice)?;
if ret > 0 {
let slice = unsafe { FileVolatileSlice::from_raw_ptr(buf.as_mut_ptr(), ret) };
f.write_at_volatile(slice, off)
} else {
Ok(0)
}
}
}
impl ZeroCopyWriter for File {
fn write_from(
&mut self,
f: &mut dyn FileReadWriteVolatile,
count: usize,
off: u64,
) -> Result<usize> {
let mut buf = vec![0_u8; count];
let slice = unsafe { FileVolatileSlice::from_raw_ptr(buf.as_mut_ptr(), count) };
let ret = f.read_at_volatile(slice, off)?;
if ret > 0 {
let slice = unsafe { FileVolatileSlice::from_raw_ptr(buf.as_mut_ptr(), ret) };
self.write_volatile(slice)
} else {
Ok(0)
}
}
fn available_bytes(&self) -> usize {
usize::MAX
}
}
#[cfg(not(feature = "async-io"))]
impl BackendFileSystem for OverlayFs {
fn mount(&self) -> Result<(Entry, u64)> {
let ctx = Context::default();
let entry = self.do_lookup(&ctx, self.root_inode(), "")?;
Ok((entry, VFS_MAX_INO))
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}