From a1193cc7a2614249e38102e7442ce3683115d845 Mon Sep 17 00:00:00 2001 From: Yang Bo Date: Thu, 24 Nov 2022 18:11:34 +0800 Subject: [PATCH 1/5] overlay: add userspace overlay implementation Add first implementation of overlay fs which can imitate kernel overlay fs. This allow you to customize a union fs with many customized layers, for example, one passthrough fs as upper layer and one NFS as lower layer. This can be quite useful for some projects relying on fuse-backend-rs, e.g. Nydus, which can add writable upper layer on readonly RAFS lower layer, to act as a complete container rootfs. More details will be added in docs. Signed-off-by: Yang Bo --- src/lib.rs | 1 + src/overlayfs/config.rs | 53 + src/overlayfs/datasource.rs | 4 + src/overlayfs/direct.rs | 290 +++++ src/overlayfs/layer.rs | 223 ++++ src/overlayfs/mod.rs | 2432 +++++++++++++++++++++++++++++++++++ src/overlayfs/plugin.rs | 92 ++ testoverlay/Cargo.toml | 15 + testoverlay/src/main.rs | 128 ++ 9 files changed, 3238 insertions(+) create mode 100644 src/overlayfs/config.rs create mode 100644 src/overlayfs/datasource.rs create mode 100644 src/overlayfs/direct.rs create mode 100644 src/overlayfs/layer.rs create mode 100644 src/overlayfs/mod.rs create mode 100644 src/overlayfs/plugin.rs create mode 100644 testoverlay/Cargo.toml create mode 100644 testoverlay/src/main.rs diff --git a/src/lib.rs b/src/lib.rs index b8756920a..caf3bd086 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,6 +120,7 @@ pub mod api; #[cfg(all(any(feature = "fusedev", feature = "virtiofs"), target_os = "linux"))] pub mod passthrough; pub mod transport; +pub mod overlayfs; pub mod common; pub use self::common::*; diff --git a/src/overlayfs/config.rs b/src/overlayfs/config.rs new file mode 100644 index 000000000..ce0e14606 --- /dev/null +++ b/src/overlayfs/config.rs @@ -0,0 +1,53 @@ + +use std::time::Duration; +use self::super::CachePolicy; +use std::fmt; + +#[derive(Default, Clone, Debug)] +pub struct Config { + pub upper: String, + pub lower: Vec, + pub work: String, + pub mountpoint: String, + pub do_import: bool, + pub writeback: bool, + pub no_open: bool, + pub no_opendir: bool, + pub killpriv_v2: bool, + pub no_readdir: bool, + pub xattr: bool, + pub xattr_permissions: bool, + pub perfile_dax: bool, + pub cache_policy: CachePolicy, + pub attr_timeout: Duration, + pub entry_timeout: Duration, +} + +impl Default for CachePolicy { + fn default() -> Self { + CachePolicy::Auto + } +} + +impl Clone for CachePolicy { + fn clone(&self) -> Self { + match *self { + CachePolicy::Never => CachePolicy::Never, + CachePolicy::Always => CachePolicy::Always, + CachePolicy::Auto => CachePolicy::Auto, + } + } +} + +impl fmt::Debug for CachePolicy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let policy = + match *self { + CachePolicy::Never => "Never", + CachePolicy::Always => "Always", + CachePolicy::Auto => "Auto", + }; + + write!(f, "CachePolicy: {}", policy) + } +} diff --git a/src/overlayfs/datasource.rs b/src/overlayfs/datasource.rs new file mode 100644 index 000000000..501df060a --- /dev/null +++ b/src/overlayfs/datasource.rs @@ -0,0 +1,4 @@ + +///! DataSource trait +pub trait DataSource { +} diff --git a/src/overlayfs/direct.rs b/src/overlayfs/direct.rs new file mode 100644 index 000000000..427bc83e1 --- /dev/null +++ b/src/overlayfs/direct.rs @@ -0,0 +1,290 @@ +use std::collections::LinkedList; +use std::sync::Arc; +use std::io::{Result, Error, ErrorKind}; +use std::fs; +use std::ffi::{CStr, CString}; +use std::time::Duration; + + +use self::super::layer::Layer; +use self::super::BoxedLayer; +use self::super::{Inode, Handle}; +use crate::passthrough::{PassthroughFs, Config as PassthroughConfig}; +use crate::api::filesystem::{Entry, FileSystem, FsOptions, OpenOptions, GetxattrReply, ListxattrReply, DirEntry, SetattrValid, ZeroCopyReader, ZeroCopyWriter, Context}; +use crate::abi::fuse_abi::CreateIn; + +pub struct Direct { + pub upper: bool, + pub fs: PassthroughFs, +} + +impl Direct { + pub fn new(path: String, upper: bool) -> Result>> { + let dir = fs::canonicalize(path.as_str())?; + let root_dir = dir.to_string_lossy().into_owned().to_owned(); + let mut config = PassthroughConfig::default(); + config.root_dir = root_dir; + + // enable xattr + config.xattr = true; + + // under overlay fs, no need to negotiate + config.do_import = false; + let fs = PassthroughFs::new(config)?; + + let mut layer = Direct{ + upper, + fs, + }; + + layer.init_layer()?; + + let mut list = LinkedList::new(); + list.push_back(Arc::new(Box::new(layer) as BoxedLayer)); + + Ok(list) + } +} + +impl Layer for Direct { + fn init_layer(&mut self) -> Result<()> { + self.fs.import() + } + + fn cleanup(&mut self) -> Result<()> { + Ok(()) + } + + fn is_upper(&self) -> bool { + self.upper + } +} + +impl FileSystem for Direct { + type Inode = Inode; + type Handle = Handle; + + fn init(&self, capable: FsOptions) -> Result { + Err(Error::from(ErrorKind::Unsupported)) + } + + fn destroy(&self) { + self.fs.destroy() + } + + fn statfs(&self, ctx: &Context, inode: Inode) -> Result { + self.fs.statfs(ctx, inode) + } + + fn lookup(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result { + self.fs.lookup(ctx, parent, name) + } + + fn forget(&self, ctx: &Context, inode: Inode, count: u64) { + self.fs.forget(ctx, inode, count) + } + + fn batch_forget(&self, ctx: &Context, requests: Vec<(Inode, u64)>) { + self.fs.batch_forget(ctx, requests) + } + + fn opendir(&self, ctx: &Context, inode: Inode, flags: u32) -> Result<(Option, OpenOptions)> { + // should test flags to refuse write operations + let iflags: i32 = flags as i32; + let write = iflags & libc::O_RDWR != 0 || iflags & libc::O_WRONLY != 0 || iflags & libc::O_CREAT != 0 || iflags & libc::O_APPEND != 0 || iflags & libc::O_TRUNC != 0; + if !self.upper && write { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.opendir(ctx, inode, flags) + } + + fn releasedir(&self, ctx: &Context, inode: Inode, flags: u32, handle: Handle) -> Result<()> { + self.fs.releasedir(ctx, inode, flags, handle) + } + + fn mkdir(&self, ctx: &Context, parent: Inode, name: &CStr, mode: u32, umask: u32) -> Result { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.mkdir(ctx, parent, name, mode, umask) + } + + fn rmdir(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result<()> { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.rmdir(ctx, parent, name) + } + + fn readdir(&self, ctx: &Context, inode: Inode, handle: Handle, size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry) -> Result) -> Result<()> { + self.fs.readdir(ctx, inode, handle, size, offset, add_entry) + } + + fn readdirplus(&self, ctx: &Context, inode: Inode, handle: Handle, size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry, Entry) -> Result) -> Result<()> { + self.fs.readdirplus(ctx, inode, handle, size, offset, add_entry) + } + + fn open(&self, ctx: &Context, inode: Inode, flags: u32, fuse_flags: u32) -> Result<(Option, OpenOptions)> { + let iflags: i32 = flags as i32; + let write = iflags & libc::O_RDWR != 0 || iflags & libc::O_WRONLY != 0 || iflags & libc::O_CREAT != 0 || iflags & libc::O_APPEND != 0 || iflags & libc::O_TRUNC != 0; + if !self.upper && write { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.open(ctx, inode, flags, fuse_flags) + } + + fn release(&self, ctx: &Context, inode: Inode, flags: u32, handle: Handle, flush: bool, flock_release: bool, lock_owner: Option) -> Result<()> { + self.fs.release(ctx, inode, flags, handle, flush, flock_release, lock_owner) + } + + fn create(&self, ctx: &Context, parent: Inode, name: &CStr, args: CreateIn) -> Result<(Entry, Option, OpenOptions)> { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.create(ctx, parent, name, args) + } + + fn unlink(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result<()> { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.unlink(ctx, parent, name) + } + + fn read(&self, ctx: &Context, inode: Inode, handle: Handle, w: &mut dyn ZeroCopyWriter, size: u32, offset: u64, lock_owner: Option, flags: u32) -> Result { + self.fs.read(ctx, inode, handle, w, size, offset, lock_owner, flags) + } + + fn write(&self, ctx: &Context, inode: Inode, handle: Handle, r: &mut dyn ZeroCopyReader, size: u32, offset: u64, lock_owner: Option, delay_write: bool, flags: u32, fuse_flags: u32) -> Result { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.write(ctx, inode, handle, r, size, offset, lock_owner, delay_write, flags, fuse_flags) + } + + fn getattr(&self, ctx: &Context, inode: Inode, handle: Option) -> Result<(libc::stat64, Duration)> { + self.fs.getattr(ctx, inode, handle) + } + + fn setattr(&self, ctx: &Context, inode: Inode, attr: libc::stat64, handle: Option, valid: SetattrValid) -> Result<(libc::stat64, Duration)> { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.setattr(ctx, inode, attr, handle, valid) + } + + fn rename(&self, ctx: &Context, olddir: Inode, oldname: &CStr, newdir: Inode, newname: &CStr, flags: u32) -> Result<()> { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.rename(ctx, olddir, oldname, newdir, newname, flags) + } + + fn mknod(&self, ctx: &Context, parent: Inode, name: &CStr, mode: u32, rdev: u32, umask: u32) -> Result { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.mknod(ctx, parent, name, mode, rdev, umask) + } + + fn link(&self, ctx: &Context, inode: Inode, newparent: Inode, newname: &CStr) -> Result { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.link(ctx, inode, newparent, newname) + } + + fn symlink(&self, ctx: &Context, linkname: &CStr, parent: Inode, name: &CStr) -> Result { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.symlink(ctx, linkname, parent, name) + } + + fn readlink(&self, ctx: &Context, inode: Inode) -> Result> { + self.fs.readlink(ctx, inode) + } + + fn flush(&self, ctx: &Context, inode: Inode, handle: Handle, lock_owner: u64) -> Result<()> { + // even readonly opened file can be flushed, + // so it does't have to be in upper layer + // if !self.upper { + // return Err(Error::from_raw_os_error(libc::EROFS)); + // } + + self.fs.flush(ctx, inode, handle, lock_owner) + } + + fn fsync(&self, ctx: &Context, inode: Inode, datasync: bool, handle: Handle) -> Result<()> { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.fsync(ctx, inode, datasync, handle) + } + + fn fsyncdir(&self, ctx: &Context, inode: Inode, datasync: bool, handle: Handle) -> Result<()> { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.fsyncdir(ctx, inode, datasync, handle) + } + + fn access(&self, ctx: &Context, inode: Inode, mask: u32) -> Result<()> { + let write = mask as i32 & libc::W_OK != 0; + if !self.upper && write { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.access(ctx, inode, mask) + } + + fn setxattr(&self, ctx: &Context, inode: Inode, name: &CStr, value: &[u8], flags: u32) -> Result<()> { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.setxattr(ctx, inode, name, value, flags) + } + + fn getxattr(&self, ctx: &Context, inode: Inode, name: &CStr, size: u32) -> Result { + self.fs.getxattr(ctx, inode, name, size) + } + + fn listxattr(&self, ctx: &Context, inode: Inode, size: u32) -> Result { + self.fs.listxattr(ctx, inode, size) + } + + fn removexattr(&self, ctx: &Context, inode: Inode, name: &CStr) -> Result<()> { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.removexattr(ctx, inode, name) + } + + fn fallocate(&self, ctx: &Context, inode: Inode, handle: Handle, mode: u32, offset: u64, length: u64) -> Result<()> { + if !self.upper { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + self.fs.fallocate(ctx, inode, handle, mode, offset, length) + } + + fn lseek(&self, ctx: &Context, inode: Inode, handle: Handle, offset: u64, whence: u32) -> Result { + self.fs.lseek(ctx, inode, handle, offset, whence) + } +} diff --git a/src/overlayfs/layer.rs b/src/overlayfs/layer.rs new file mode 100644 index 000000000..33f08a47a --- /dev/null +++ b/src/overlayfs/layer.rs @@ -0,0 +1,223 @@ +#![allow(missing_docs)] + +use std::sync::{Arc, Weak}; +use std::io::{Result, Error}; +use std::os::unix::io::RawFd; +use std::fs::File; +use std::path::PathBuf; +use std::cmp::Eq; +use std::ffi::{CString, CStr}; +use libc; + + +use self::super::super::abi::fuse_abi::Dirent; +use self::super::datasource::DataSource; +use self::super::{OverlayFs, Inode, Handle}; +use self::super::{WHITEOUT_PREFIX, ORIGIN_XATTR, OPAQUE_XATTR, PRIVILEGED_OPAQUE_XATTR, PRIVILEGED_ORIGIN_XATTR, UNPRIVILEGED_OPAQUE_XATTR, OPAQUE_WHITEOUT}; +use crate::api::filesystem::{FileSystem, Context, Entry, GetxattrReply}; +use crate::abi::fuse_abi::{CreateIn}; +use crate::api::VFS_MAX_INO; + +pub const OPAQUE_XATTR_LEN: u32 = 16; + +// we cannot constraint Layer with Eq, as Eq requires Sized +// So we need api to get an identifier and then use identifier +// to do comparation.. Here may have better solutions.. +pub trait Layer: FileSystem { + fn init_layer(&mut self) -> Result<()>; + fn cleanup(&mut self) -> Result<()>; + fn is_upper(&self) -> bool; + // file exists, returns true, not exists return false + // otherwise, error out + // Ok(None) denotes no entry + fn lookup_ignore_enoent(&self, ctx: &Context, ino: u64, name: &str) -> Result> { + let cname = CString::new(name).expect("invalid c string"); + match self.lookup(ctx, Self::Inode::from(ino), cname.as_c_str()) { + Ok(v) => return 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); + } + } + + return Err(e); + }, + } + } + + fn getxattr_ignore_nodata(&self, ctx: &Context, inode: u64, name: &str, size: u32) -> Result>> { + let cname = CString::new(name).expect("invalid c string"); + match self.getxattr(ctx, Self::Inode::from(inode), cname.as_c_str(), size) + { + Ok(v) => { + if let GetxattrReply::Value(buf) = v { + return Ok(Some(buf)); + } else { + return Ok(None); + } + }, + Err(e) => { + if let Some(raw_error) = e.raw_os_error() { + if raw_error == libc::ENODATA || raw_error == libc::ENOTSUP || raw_error == libc::ENOSYS { + return Ok(None); + } + } + + return Err(e); + }, + } + } + + fn whiteout_exists(&self, ctx: &Context, ino: u64, name: &CStr) -> Result<(bool, u64, String)> { + let sname = name.to_string_lossy().into_owned().to_owned(); + + let mut wh_name = String::from(WHITEOUT_PREFIX); + wh_name.push_str(sname.as_str()); + + // .wh.name exists + if let Some(v) = self.lookup_ignore_enoent(ctx, ino, wh_name.as_str())? { + return Ok((true, v.inode, wh_name)); + } + + // char node whiteout + if let Some(st) = self.lookup_ignore_enoent(ctx, ino, sname.as_str())? { + let major = unsafe { libc::major(st.attr.st_rdev) }; + let minor = unsafe { libc::minor(st.attr.st_rdev) }; + if st.attr.st_mode & libc::S_IFMT == libc::S_IFCHR && major == 0 && minor == 0 { + return Ok((true, st.inode, sname)); + } + } + + Ok((false, VFS_MAX_INO, String::from(""))) + } + + fn is_opaque_whiteout(&self, ctx: &Context, inode: u64) -> Result<(bool, Option)> { + let (st, _d) = self.getattr(ctx, Self::Inode::from(inode), None)?; + if st.st_mode & libc::S_IFMT != libc::S_IFDIR { + return Err(Error::from_raw_os_error(libc::ENOTDIR)); + } + + // check xattr first + if let Some(v) = self.getxattr_ignore_nodata(ctx, inode, PRIVILEGED_OPAQUE_XATTR, OPAQUE_XATTR_LEN)? { + if v[0].to_ascii_lowercase() == b'y' { + return Ok((true, None)); + } else { + return Ok((false, None)); + } + } + + if let Some(v) = self.getxattr_ignore_nodata(ctx, inode, UNPRIVILEGED_OPAQUE_XATTR, OPAQUE_XATTR_LEN)? { + if v[0].to_ascii_lowercase() == b'y' { + return Ok((true, None)); + } else { + return Ok((false, None)); + } + } + + if let Some(v) = self.getxattr_ignore_nodata(ctx, inode, OPAQUE_XATTR, OPAQUE_XATTR_LEN)? { + if v[0].to_ascii_lowercase() == b'y' { + return Ok((true, None)); + } else { + return Ok((false, None)); + } + } + + // check .wh..wh..opaque, hwoever, we have no parent inode number for the parent of .wh..wh..opaque + if let Some(v) = self.lookup_ignore_enoent(ctx, inode, OPAQUE_WHITEOUT)? { + return Ok((true, Some(v.inode))); + } + + Ok((false, None)) + } + + fn delete_whiteout(&self, ctx: &Context, parent: u64, name: &str) -> Result<()> { + if !self.is_upper() { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + let mut wh_name = String::from(WHITEOUT_PREFIX); + wh_name.push_str(name); + let cwh_name = CString::new(wh_name.as_str()).expect("invalid c string"); + let cname = CString::new(name).expect("invalid c string"); + + // .wh.name exists + if let Some(v) = self.lookup_ignore_enoent(ctx, parent, wh_name.as_str())? { + self.unlink(ctx, Self::Inode::from(parent), cwh_name.as_c_str())?; + } + + // char node whiteout + if let Some(st) = self.lookup_ignore_enoent(ctx, parent, name)? { + let major = unsafe { libc::major(st.attr.st_rdev) }; + let minor = unsafe { libc::minor(st.attr.st_rdev) }; + if st.attr.st_mode & libc::S_IFMT == libc::S_IFCHR && major == 0 && minor == 0 { + self.unlink(ctx, Self::Inode::from(parent), cname.as_c_str())?; + } + } + + Ok(()) + } + fn create_whiteout(&self, ctx: &Context, parent: u64, name: &str) -> Result { + if !self.is_upper() { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + let mut wh_name = String::from(WHITEOUT_PREFIX); + wh_name.push_str(name); + let cwh_name = CString::new(wh_name.as_str()).expect("invalid c string"); + let cname = CString::new(name).expect("invalid c string"); + + let (exists, _inode, name) = self.whiteout_exists(ctx, parent, cname.as_c_str())?; + + if exists { + return self.lookup(ctx, Self::Inode::from(parent), CString::new(name.as_str()).expect("invalid c string").as_c_str()); + } + + // try to creat .wh.name + let args = CreateIn { + flags: 0, + mode: 0o777, + umask: 0, + fuse_flags: 0, + }; + + if let Ok((entry, h, o)) = self.create(ctx, Self::Inode::from(parent), cwh_name.as_c_str(), args) { + // if let Some(handle) = h { + // self.release(ctx, Self::Inode::from(entry.inode), 0, handle, true, true, None)?; + // } + + return Ok(entry); + } + + // try mknod + let dev = unsafe { libc::makedev(0, 0) }; + let mode = libc::S_IFCHR | 0o777; + self.mknod(ctx, Self::Inode::from(parent), cname.as_c_str(), mode, dev as u32, 0) + } + + fn create_opaque_whiteout(&self, ctx: &Context, parent: u64) -> Result { + if !self.is_upper() { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + let cname = CString::new(OPAQUE_WHITEOUT).expect("invalid c string"); + + // opaque whiteout exists + if let Some(v) = self.lookup_ignore_enoent(ctx, parent, OPAQUE_WHITEOUT)? { + return Ok(v); + } + + // create + let args = CreateIn { + flags: 0, + mode: 0o777, + umask: 0, + fuse_flags: 0, + }; + + let (entry, h, _o) = self.create(ctx, Self::Inode::from(parent), cname.as_c_str(), args)?; + + Ok(entry) + } + +} diff --git a/src/overlayfs/mod.rs b/src/overlayfs/mod.rs new file mode 100644 index 000000000..3fedc3a5e --- /dev/null +++ b/src/overlayfs/mod.rs @@ -0,0 +1,2432 @@ +#![allow(missing_docs)] +#![feature(io_error_more)] + +pub mod layer; +pub mod datasource; +pub mod plugin; +pub mod config; +pub mod direct; + +use std::collections::{LinkedList, HashMap}; +use std::io::{Result, SeekFrom, Seek}; +use std::fs::File; +use std::sync::{Arc, Mutex, Weak}; +use std::sync::atomic::{AtomicBool, Ordering, AtomicU64}; +use std::any::Any; +use std::ffi::{CStr, CString}; +use std::time::Duration; +use std::string::ToString; +use std::mem::{size_of, MaybeUninit}; +use std::os::unix::io::FromRawFd; + +use crate::api::filesystem::{Entry, FileSystem, FsOptions, OpenOptions, GetxattrReply, ListxattrReply, DirEntry, SetattrValid, ZeroCopyReader, ZeroCopyWriter, Context}; +use crate::api::{BackendFileSystem, SLASH_ASCII, VFS_MAX_INO}; +use crate::abi::fuse_abi::{CreateIn, ROOT_ID as FUSE_ROOT_ID}; +use crate::common::file_buf::FileVolatileSlice; +use crate::common::file_traits::FileReadWriteVolatile; + +use self::plugin::PluginManager; +use self::layer::Layer; +use self::config::Config; +use std::io::{Error, ErrorKind}; +use libc; + + +pub type Inode = u64; +pub type Handle = u64; +pub const PLUGIN_PREFIX: &str = "//"; +pub const WHITEOUT_PREFIX: &str = ".wh."; +pub const XATTR_PREFIX: &str = "user.fuseoverlayfs"; +pub const ORIGIN_XATTR: &str = "user.fuseoverlayfs.origin"; +pub const OPAQUE_XATTR: &str = "user.fuseoverlayfs.opaque"; +pub const XATTR_CONTAINERS_PREFIX: &str = "user.containers"; +pub const UNPRIVILEGED_XATTR_PREFIX: &str = "user.overlay"; +pub const UNPRIVILEGED_OPAQUE_XATTR: &str = "user.overlay.opaque"; +pub const PRIVILEGED_XATTR_PREFIX: &str = "trusted.overlay"; +pub const PRIVILEGED_OPAQUE_XATTR: &str = "trusted.overlay.opaque"; +pub const PRIVILEGED_ORIGIN_XATTR: &str = "trusted.overlay.origin"; +pub const OPAQUE_WHITEOUT: &str = ".wh..wh..opq"; +pub const MAXNAMELEN: usize = 256; +pub const CURRENT_DIR: &str = "."; +pub const PARENT_DIR: &str = ".."; +pub const WHITEOUT_MAX_LEN: u64 = ".wh.".len() as u64; +pub const MAXBUFSIZE: usize = 1<< 20; + +pub type BoxedLayer = Box + Send + Sync>; + +// need real inode from layers, need inode to do layer +// operations +#[derive(Default)] +pub struct RealInode { + pub layer: Option>, + pub inode: AtomicU64, + pub whiteout: AtomicBool, + pub opaque: AtomicBool, + pub hidden: AtomicBool, + pub invalid: AtomicBool, +} + +#[derive(Default, Debug)] +pub struct RealInodeStats { + pub inode: u64, + pub whiteout: bool, + pub opaque: bool, + pub stat: Option, + pub wh_name: Option, + pub opaque_inode: Option, +} + +#[derive(Default)] +pub struct OverlayInode { + pub childrens: Mutex>>, + pub parent: Mutex>, + pub lower_inodes: Vec>, + pub upper_inode: Mutex>>, + pub first_inode: Mutex>, + pub last_inode: Mutex>, + pub inode: u64, + pub st_ino: libc::ino64_t, + pub st_dev: libc::dev_t, + pub mode: libc::mode_t, + pub entry_type: u32, + pub path: String, + pub name: String, + pub lookups: Mutex, + + pub hidden: AtomicBool, + pub whiteout: AtomicBool, + pub loaded: AtomicBool, + + // what about data source related data for each inode + // put it into layer struct, ino -> private data hash +} + +pub enum CachePolicy { + Never, + Auto, + Always, +} + +pub struct OverlayFs { + // should be in daemon structure + pub config: Config, + pub layers: LinkedList>, + pub upper_layer: Option>, + // inode management.. + pub root: Option>, + pub inodes: Mutex>>, + pub next_inode: AtomicU64, + + // manage opened fds.. + pub handles: Mutex>, + pub next_handle: AtomicU64, + pub writeback: AtomicBool, + pub no_open: AtomicBool, + pub no_opendir: AtomicBool, + pub killpriv_v2: AtomicBool, + pub perfile_dax: AtomicBool, +} + +pub struct RealHandle { + pub real_inode: Arc, + pub handle: AtomicU64, + pub invalid: AtomicBool, +} + +pub struct HandleData { + pub node: Arc, + pub childrens: Option>>, + pub offset: libc::off_t, + + // others? + pub real_handle: Option, +} + +impl RealInode { + pub fn stat64_ignore_enoent(&self, ctx: &Context) -> Result> { + if self.invalid.load(Ordering::Relaxed) { + return Ok(None); + } + + match self.layer.as_ref().unwrap().getattr(ctx, self.inode.load(Ordering::Relaxed), None) { + Ok((v1, _v2)) => { + return Ok(Some(v1)); + }, + + Err(e) => { + match e.raw_os_error() { + Some(raw_error) => { + if raw_error != libc::ENOENT && raw_error != libc::ENOTDIR && raw_error != libc::ENAMETOOLONG { + return Ok(None); + } + return Err(e); + }, + + None => { + return Err(e); + }, + } + }, + } + } + + // Ok(None) represents noent + pub fn lookup_node(&self, ctx: &Context, name: &CStr) -> Result> { + + if self.whiteout.load(Ordering::Relaxed) || self.invalid.load(Ordering::Relaxed) { + return Ok(None); + } + + let sname = name.to_string_lossy().into_owned().to_owned(); + if sname.starts_with(WHITEOUT_PREFIX){ + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + + let layer = self.layer.as_ref().unwrap(); + + let (whiteout, inode, wh_name) = layer.whiteout_exists(ctx, self.inode.load(Ordering::Relaxed), name)?; + + if whiteout { + return Ok(Some(RealInodeStats { + inode, + whiteout, + opaque: false, + stat: None, + wh_name: Some(wh_name), + opaque_inode: None, + })); + } + + if let Some(v) = layer.lookup_ignore_enoent(ctx, self.inode.load(Ordering::Relaxed), sname.as_str())? { + // directory? + if v.attr.st_mode & libc::S_IFMT != libc::S_IFDIR { + return Ok(Some(RealInodeStats { + inode: v.inode, + whiteout: false, + opaque: false, + stat: Some(v.attr), + wh_name: None, + opaque_inode: None, + })); + } + + let (opaque, opaque_inode) = layer.is_opaque_whiteout(ctx, v.inode)?; + + return Ok(Some(RealInodeStats { + inode: v.inode, + whiteout: false, + opaque, + stat: Some(v.attr), + wh_name: None, + opaque_inode, + })); + } else { + return Ok(None); + } + } +} + +impl OverlayInode { + pub fn stat64(&self, ctx: &Context) -> Result { + // try upper layer if there is + if let Some(ref l) = *self.upper_inode.lock().unwrap() { + if let Some(v) = l.stat64_ignore_enoent(ctx)? { + return Ok(v); + } + } + + // try layers in order or just take stst from first layer? + for l in &self.lower_inodes { + if let Some(v) = l.stat64_ignore_enoent(ctx)? { + return Ok(v); + } + } + + // not in any layer + Err(Error::from_raw_os_error(libc::ENOENT)) + } + + pub fn count_entries_and_whiteout(&self, ctx: &Context) -> Result<(u64, u64, u64)> { + let mut count = 0; + let mut whiteouts = 0; + let mut opaque = 0; + + let st = self.stat64(ctx)?; + + // must be directory + assert!(st.st_mode & libc::S_IFMT == libc::S_IFDIR); + if let Some(ref ri) = *self.upper_inode.lock().unwrap() { + if ri.opaque.load(Ordering::Relaxed) { + opaque = 1; + } + } + + for (_, child) in self.childrens.lock().unwrap().iter() { + if child.whiteout.load(Ordering::Relaxed) { + whiteouts += 1; + } else { + count += 1; + } + } + + Ok((count, whiteouts, opaque)) + } + + pub fn open(&self, ctx: &Context, flags: u32, fuse_flags: u32) -> Result<(Arc, Option, OpenOptions)> { + let ri = Arc::clone(&self.first_inode.lock().unwrap()); + if let Some(ref l) = ri.layer { + let (h, o) = l.open(ctx, ri.inode.load(Ordering::Relaxed), flags, fuse_flags)?; + Ok((Arc::clone(l), h, o)) + } else { + Err(Error::new(ErrorKind::Other, "no first layer")) + } + } + + pub fn in_upper_layer(&self) -> bool { + self.upper_inode.lock().unwrap().is_some() + } + + pub fn upper_layer_only(&self) -> bool { + self.lower_inodes.len() == 0 + } +} + +fn process_lower_layer(manager: &PluginManager, opaque: &[String]) -> Result>> { + let mut layers = LinkedList::new(); + + for lower in opaque { + let mut lower_layers = plugin::process_onelayer(manager, lower.into(), false)?; + layers.append(&mut lower_layers); + } + + Ok(layers) +} + +impl OverlayInode { + pub fn new() -> Self { + OverlayInode::default() + } +} + +fn entry_type_from_mode(mode: libc::mode_t) -> u8 { + if mode & libc::S_IFBLK != 0 { + return libc::DT_BLK; + } + + if mode & libc::S_IFCHR != 0 { + return libc::DT_CHR; + } + + if mode & libc::S_IFDIR != 0 { + return libc::DT_DIR; + } + + if mode & libc::S_IFIFO != 0 { + return libc::DT_FIFO; + } + + if mode & libc::S_IFLNK != 0 { + return libc::DT_LNK; + } + + if mode & libc::S_IFREG != 0 { + return libc::DT_REG; + } + + if mode & libc::S_IFSOCK != 0 { + return libc::DT_SOCK; + } + + return libc::DT_UNKNOWN; +} + +impl OverlayFs { + pub fn new(manager: &PluginManager, params: Config) -> Result { + // upper dir + let mut layers = plugin::process_onelayer(manager, String::from(params.upper.as_str()), true)?; + + let upper_layer = if let Some(ref v) = layers.front() { + Some(Arc::clone(v)) + }else { + None + }; + + // lower dir + let mut lower_layers = process_lower_layer(manager, params.lower.as_slice())?; + + layers.append(&mut lower_layers); + // load root inode + Ok(OverlayFs { + config: params, + upper_layer, + layers, + inodes: Mutex::new(HashMap::new()), + root: None, + next_inode: AtomicU64::new(FUSE_ROOT_ID + 1), + 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 init_root(&mut self) -> Result<()> { + let mut root = OverlayInode::new(); + root.inode = FUSE_ROOT_ID; + root.path = String::from("."); + root.name = String::from(""); + root.entry_type = libc::DT_DIR as u32; + root.lookups = Mutex::new(2); + let ctx = Context::default(); + + let mut first= true; + for layer in self.layers.iter() { + let (opaque, _ino) = layer.is_opaque_whiteout(&ctx, FUSE_ROOT_ID)?; + let real = RealInode { + layer: Some(Arc::clone(layer)), + inode: AtomicU64::new(FUSE_ROOT_ID), + whiteout: AtomicBool::new(false), + opaque: AtomicBool::new(opaque), + hidden: AtomicBool::new(false), + invalid: AtomicBool::new(false), + }; + + let real_inode = Arc::new(real); + + if first { + first = false; + root.first_inode = Mutex::new(Arc::clone(&real_inode)); + } + + if layer.is_upper() { + root.upper_inode = Mutex::new(Some(Arc::clone(&real_inode))); + } else { + root.lower_inodes.push(Arc::clone(&real_inode)); + } + + root.last_inode = Mutex::new(Arc::clone(&real_inode)); + } + + let root_node = Arc::new(root); + + // insert root inode into hash + { + self.inodes.lock().unwrap().insert(FUSE_ROOT_ID, Arc::clone(&root_node)); + } + + let ctx = Context::default(); + info!("loading root directory\n"); + self.load_directory(&ctx, Arc::clone(&root_node))?; + + self.root = Some(root_node); + + Ok(()) + } + + pub fn import(&self) -> Result<()> { + Ok(()) + } + + pub fn make_overlay_inode(&self, ris: &RealInodeStats, layer: Arc) -> Result { + let mut new = OverlayInode::new(); + new.whiteout.store(ris.whiteout, Ordering::Relaxed); + let real_inode = Arc::new(RealInode { + layer: Some(Arc::clone(&layer)), + inode: AtomicU64::new(ris.inode), + whiteout: AtomicBool::new(ris.whiteout), + opaque: AtomicBool::new(ris.opaque), + hidden: AtomicBool::new(false), + invalid: AtomicBool::new(false), + }); + + new.first_inode = Mutex::new(Arc::clone(&real_inode)); + new.last_inode = Mutex::new(Arc::clone(&real_inode)); + new.lookups = Mutex::new(1); + if layer.is_upper() { + new.upper_inode = Mutex::new(Some(Arc::clone(&real_inode))); + } + + let inode = self.next_inode.fetch_add(1, Ordering::Relaxed); + if inode > VFS_MAX_INO { + error!("reached maximum inode number: {}", VFS_MAX_INO); + return Err(Error::new(ErrorKind::Other, format!("maximum inode number {} reached", VFS_MAX_INO))); + } + new.inode = inode; + if let Some(st) = ris.stat { + new.st_ino = st.st_ino; + new.st_dev= st.st_dev; + new.mode = st.st_mode; + new.entry_type = entry_type_from_mode(st.st_mode) as u32; + } + + Ok(new) + } + + pub fn lookup_node(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result> { + if name.to_bytes_with_nul().contains(&SLASH_ASCII) { + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + + // lookup name + let pnode = { + let inodes = self.inodes.lock().unwrap(); + if let Some(v) = inodes.get(&parent) { + Arc::clone(v) + } else { + // no parent inode? + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + }; + + let sname = name.to_string_lossy().into_owned().to_owned(); + if sname.starts_with(WHITEOUT_PREFIX){ + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + + if sname.eq(".") || (parent == FUSE_ROOT_ID && sname.eq("..")) || sname.is_empty() { + return Ok(Arc::clone(&pnode)); + } + + if pnode.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + // found the node + if let Some (v) = pnode.childrens.lock().unwrap().get(sname.as_str()) { + return Ok(Arc::clone(v)); + } + + // if the directory is already loaded, not found + // we will change dir/file, we will toggle loaded + // flag to scan directory + if pnode.loaded.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + // don't find it, lookup in layers + let mut path = String::from(pnode.path.as_str()); + path.push_str("/"); + path.push_str(sname.as_str()); + + let mut node_inited: bool = false; + let mut new = OverlayInode::new(); + + // lookup until meet whiteout/opaque whiteout/file in lower layer.. + { + if let Some(ref ri) = *pnode.upper_inode.lock().unwrap() { + // find an entry + if let Some(ris) = ri.lookup_node(ctx, name)? { + node_inited = true; + new = self.make_overlay_inode(&ris, Arc::clone(ri.layer.as_ref().unwrap()))?; + } + } + } + + 'layer_loop: + for ri in &pnode.lower_inodes { + if let Some(ris) = ri.lookup_node(ctx, name)? { + // find an entry + let layer = Arc::clone(ri.layer.as_ref().unwrap()); + let mut real_inode = Arc::new(RealInode { + layer: Some(Arc::clone(&layer)), + inode: AtomicU64::new(ris.inode), + whiteout: AtomicBool::new(ris.whiteout), + hidden: AtomicBool::new(false), + opaque: AtomicBool::new(ris.opaque), + invalid: AtomicBool::new(false), + }); + + if !node_inited { + node_inited = true; + new = self.make_overlay_inode(&ris, Arc::clone(&layer))?; + new.lower_inodes.push(Arc::clone(&real_inode)); + + // should stop? + if ris.whiteout { + break 'layer_loop; + } + + // not whiteout, must have stat + let st = ris.stat.as_ref().unwrap(); + if st.st_mode & libc::S_IFMT != libc::S_IFDIR { + break 'layer_loop; + } + + // opaque? + if ris.opaque { + break 'layer_loop; + } + } else { + + // should stop? + if ris.whiteout { + break 'layer_loop; + } + + // not whiteout, must have stat + let st = ris.stat.as_ref().unwrap(); + if st.st_mode & libc::S_IFMT != libc::S_IFDIR { + break 'layer_loop; + } + + // directory + if node_inited { + new.lower_inodes.push(Arc::clone(&real_inode)); + new.last_inode = Mutex::new(Arc::clone(&real_inode)); + } + + // opaque? + if ris.opaque { + break 'layer_loop; + } + } + } + } + + if node_inited { + new.path = String::from(path.as_str()); + new.name = String::from(sname.as_str()); + // set its parent node + *new.parent.lock().unwrap() = Arc::downgrade(&pnode); + // insert node into hashs + let new_node = Arc::new(new); + self.inodes.lock().unwrap().insert(new_node.inode as u64, Arc::clone(&new_node)); + pnode.childrens.lock().unwrap().insert(sname, Arc::clone(&new_node)); + return Ok(Arc::clone(&new_node)); + } + + // return specific errors? + Err(Error::from_raw_os_error(libc::ENOENT)) + } + + pub fn lookup_node_ignore_enoent(&self, ctx: &Context, parent: u64, name: &CStr) -> Result>> { + match self.lookup_node(ctx, parent, name) { + Ok(n) => { + return Ok(Some(Arc::clone(&n))); + }, + + Err(e) => { + if let Some(raw_error) = e.raw_os_error() { + if raw_error == libc::ENOENT { + return Ok(None); + } + } + return Err(e); + }, + } + + } + + pub fn get_node_from_inode(&self, inode: u64) -> Option> { + if let Some(v) = self.inodes.lock().unwrap().get(&inode) { + return Some(Arc::clone(v)); + } + + return None; + } + + pub fn load_directory_layer(&self, ctx: &Context, ovl_inode: u64, real: Arc) -> Result<()> { + if real.whiteout.load(Ordering::Relaxed) || real.invalid.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + if let Some(st) = real.stat64_ignore_enoent(ctx)? { + if st.st_mode & libc::S_IFMT != libc::S_IFDIR { + return Err(Error::from_raw_os_error(libc::ENOTDIR)); + } + + // process this directory + let l = Arc::clone(real.layer.as_ref().unwrap()); + let rinode = real.inode.load(Ordering::Relaxed); + + let handle = if let (Some(h), _) = l.opendir(ctx, rinode, libc::O_RDONLY as u32)? { + h + } else { + return Err(Error::new(ErrorKind::Other, "no dir handle")); + }; + + let mut more = true; + let mut offset = 0; + let bufsize = 1024; + while more { + more = false; + l.readdir(ctx, rinode, handle, bufsize, offset, &mut |d| -> Result { + more = true; + offset = d.offset; + let cname = unsafe { CString::from_vec_unchecked(d.name.to_vec()) }; + let cstr_name = cname.as_c_str(); + + let child_name = cstr_name.to_string_lossy().into_owned().to_owned(); + + info!("entry: {}", child_name.as_str()); + + if child_name.eq(CURRENT_DIR) || child_name.eq(PARENT_DIR) || child_name.starts_with(WHITEOUT_PREFIX) { + return Ok(1); + } + + self.lookup_node(ctx, ovl_inode, cstr_name)?; + + Ok(1) + })?; + } + + l.releasedir(ctx, rinode, libc::O_RDONLY as u32, handle)?; + + } else { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + Ok(()) + } + + pub fn load_directory(&self, ctx: &Context, node: Arc) -> Result<()> { + let tmp_ui = { + if let Some(ref v) = *node.upper_inode.lock().unwrap() { + Some(Arc::clone(v)) + } else { + None + } + }; + + if let Some(ref ui) = tmp_ui { + debug!("load upper for {}", node.path.as_str()); + // upper layer + if ui.whiteout.load(Ordering::Relaxed) || ui.invalid.load(Ordering::Relaxed) { + return Ok(()); + } + + if let Some(st) = ui.stat64_ignore_enoent(ctx)? { + if st.st_mode & libc::S_IFMT != libc::S_IFDIR { + // not directory + return Ok(()); + } + + // process this layer + self.load_directory_layer(ctx, node.inode, Arc::clone(ui))?; + } + + // if opaque, stop here + if ui.opaque.load(Ordering::Relaxed) { + node.loaded.store(true, Ordering::Relaxed); + return Ok(()); + } + } + + // read out directories from each layer + 'layer_loop: + for li in &node.lower_inodes { + debug!("loading lower for {}", node.path.as_str()); + if li.whiteout.load(Ordering::Relaxed) || li.invalid.load(Ordering::Relaxed) { + break 'layer_loop; + } + + if let Some(st) = li.stat64_ignore_enoent(ctx)? { + if st.st_mode & libc::S_IFMT != libc::S_IFDIR { + // not directory + break 'layer_loop; + } + + // process this layer + self.load_directory_layer(ctx, node.inode, Arc::clone(li))?; + } + + // if opaque, stop here + if li.opaque.load(Ordering::Relaxed) { + break 'layer_loop; + } + } + + node.loaded.store(true, Ordering::Relaxed); + + Ok(()) + } + + pub fn reload_directory(&self, ctx: &Context, node: Arc) -> Result<()> { + if node.loaded.load(Ordering::Relaxed) { + return Ok(()) + } + { + let mut children =node.childrens.lock().unwrap(); + *children = HashMap::new(); + } + + self.load_directory(ctx, node) + } + + pub fn get_first_layer(&self) -> Option> { + if let Some(v) = self.layers.front() { + Some(Arc::clone(v)) + } else { + None + } + } + + pub fn get_upper_layer(&self) -> Option> { + if let Some(ref v) = self.upper_layer.as_ref() { + Some(Arc::clone(v)) + } else { + None + } + } + + pub fn get_first_lower_layer(&self) -> Option> { + if let Some(ref _v) = self.upper_layer.as_ref() { + let mut index = 0; + for layer in self.layers.iter() { + if index == 1 { + return Some(Arc::clone(layer)); + } + index += 1; + } + + None + } else { + if let Some(v) = self.layers.front() { + Some(Arc::clone(v)) + } else { + None + } + } + } + + pub fn forget_one(&self, inode: Inode, count: u64) { + if inode == FUSE_ROOT_ID || inode == 0 { + return; + } + + let v = { + if let Some(n) = self.inodes.lock().unwrap().get(&inode) { + Arc::clone(n) + } else { + return; + } + }; + // lock up lookups + let mut lookups = v.lookups.lock().unwrap(); + + if *lookups < count { + *lookups = 0; + } else { + *lookups -= count; + } + + // remove it from hashmap + + if *lookups == 0 { + self.inodes.lock().unwrap().remove(&inode); + let parent = v.parent.lock().unwrap(); + + if let Some(p) = parent.upgrade() { + p.childrens.lock().unwrap().remove(v.name.as_str()); + p.loaded.store(true, Ordering::Relaxed); + } + } + + // FIXME: is it possible that the inode still in childrens map? + } + + pub fn do_statvfs(&self, ctx: &Context, inode: Inode) -> Result { + if let Some(v) = self.get_first_layer() { + if let Ok(sfs) = v.statfs(ctx, inode) { + return Ok(sfs); + } + } + + // otherwise stat on mountpoint + let mut sfs = MaybeUninit::::zeroed(); + let cpath = CString::new(self.config.mountpoint.as_str()).expect("invalid path"); + let path = cpath.as_c_str().as_ptr(); + + match unsafe { libc::statvfs64(path, sfs.as_mut_ptr()) } { + 0 => { + let mut sfs = unsafe { sfs.assume_init() }; + sfs.f_namemax -= WHITEOUT_MAX_LEN; + + Ok(sfs) + }, + + _ => { + Err(Error::last_os_error()) + }, + } + } + + pub fn get_fs_namemax(&self, ctx: &Context) -> u64 { + match self.do_statvfs(ctx, FUSE_ROOT_ID) { + Ok(sfs) => sfs.f_namemax, + Err(_) => 255 - WHITEOUT_MAX_LEN, + } + } + + pub fn do_readdir(&self, ctx: &Context, handle: u64, size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry, Entry) -> Result) -> Result<()> { + if size == 0 { + return Ok(()); + } + + // FIXME: if offset == 0, need to reconstruct dir for this handle + // if offset == 0 { + // reconstruct directory + // } + + // lookup the directory + if let Some(dir) = self.handles.lock().unwrap().get(&handle) { + let mut len: usize = 0; + debug!("overlay dir: {}, off: {}, size: {}", dir.node.path.as_str(), offset, size); + + let childrens = + if let Some(ref cs) = dir.childrens { + cs + } else { + return Err(Error::new(ErrorKind::Other, "no child!")); + }; + + if offset >= childrens.len() as u64 { + return Ok(()); + } + + let mut index: u64 = 0; + for child in childrens { + if index >= offset { + let name = match index { + 0 => ".", + 1 => "..", + _ => child.name.as_str(), + }; + + // make struct DireEntry and Entry + 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 = Entry { + inode: child.inode, + generation: 0, + attr: st, + attr_flags: 0, + attr_timeout: self.config.attr_timeout, + entry_timeout: self.config.entry_timeout, + }; + + match add_entry(dir_entry, entry) { + Ok(0) => break, + Ok(l) => { + len += l; + if len as u32 >= size { + // no more space, stop here + return Ok(()); + } + }, + + Err(e) => { + // when the buffer is still empty, return error, otherwise return the entry already added + if len == 0 { + return Err(e); + } else { + return Ok(()); + } + }, + } + } + + index += 1; + } + } + + Ok(()) + } + + pub fn node_in_upper_layer(&self, node: Arc) -> Result { + Ok(node.in_upper_layer()) + } + + pub fn create_node_directory(&self, ctx: &Context, node: Arc) -> Result<()> { + // recursively going up and update hashmaps + if self.node_in_upper_layer(Arc::clone(&node))? { + return Ok(()); + } + + // not in upper layer, check parent + let pnode = if let Some(n) = node.parent.lock().unwrap().upgrade() { + Arc::clone(&n) + } else { + return Err(Error::new(ErrorKind::Other, "no parent?")); + }; + + if self.node_in_upper_layer(Arc::clone(&pnode))? { + // create directory here + let upper = Arc::clone(pnode.upper_inode.lock().unwrap().as_ref().unwrap()); + let layer = Arc::clone(upper.layer.as_ref().unwrap()); + let cname = CString::new(node.name.as_str()).expect("invalid c string"); + let st = node.stat64(ctx)?; + let entry = layer.mkdir(ctx, upper.inode.load(Ordering::Relaxed), cname.as_c_str(), st.st_mode, 0)?; + + // update node's first_layer + let real_inode = Arc::new(RealInode { + layer: Some(Arc::clone(&layer)), + inode: AtomicU64::new(entry.inode), + whiteout: AtomicBool::new(false), + opaque: AtomicBool::new(false), + hidden: AtomicBool::new(false), + invalid: AtomicBool::new(false), + }); + + // what about st_ino/mode/dev.. + // FIXME: update st_ino/mode/dev, or query it from layer + // on fly? + *node.first_inode.lock().unwrap() = Arc::clone(&real_inode); + *node.upper_inode.lock().unwrap() = Some(Arc::clone(&real_inode)); + + return Ok(()); + + } else { + return self.create_node_directory(ctx, Arc::clone(&pnode)); + } + } + + pub fn copy_symlink_up(&self, ctx: &Context, node: Arc) -> Result> { + if self.node_in_upper_layer(Arc::clone(&node))? { + return Ok(node); + } + + let pnode = 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 empty_name =CString::new("").expect("invalid c string"); + let upper = self.get_upper_layer().unwrap(); + let pnode = self.lookup_node(ctx, pnode.inode, empty_name.as_c_str())?; + + assert!(pnode.in_upper_layer()); + assert!(st.st_mode & libc::S_IFMT == libc::S_IFLNK); + let parent_real_inode = Arc::clone(pnode.upper_inode.lock().unwrap().as_ref().unwrap()); + let cname = CString::new(node.name.as_str()).expect("invalid c string"); + let node = self.lookup_node(ctx, pnode.inode, cname.as_c_str())?; + let rinode = Arc::clone(&node.first_inode.lock().unwrap()); + let layer = Arc::clone(rinode.layer.as_ref().unwrap()); + + // symlink + // first inode, upper most layer inode + let path = layer.readlink(ctx, rinode.inode.load(Ordering::Relaxed))?; + let cpath = unsafe { CString::from_vec_unchecked(path) }; + let entry = upper.symlink(ctx, cpath.as_c_str(), parent_real_inode.inode.load(Ordering::Relaxed), cname.as_c_str())?; + + let real_inode = Arc::new(RealInode { + layer: Some(Arc::clone(&upper)), + inode: AtomicU64::new(entry.inode), + whiteout: AtomicBool::new(false), + opaque: AtomicBool::new(false), + hidden: AtomicBool::new(false), + invalid: AtomicBool::new(false), + }); + + // update first_inode and upper_inode + *node.upper_inode.lock().unwrap() = Some(Arc::clone(&real_inode)); + *node.first_inode.lock().unwrap() = Arc::clone(&real_inode); + + return Ok(Arc::clone(&node)); + } + + pub fn copy_regfile_up(&self, ctx: &Context, node: Arc) -> Result> { + if self.node_in_upper_layer(Arc::clone(&node))? { + return Ok(node); + } + + let pnode = 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 empty_name =CString::new("").expect("invalid c string"); + let upper = self.get_upper_layer().unwrap(); + let pnode = self.lookup_node(ctx, pnode.inode, empty_name.as_c_str())?; + + assert!(pnode.in_upper_layer()); + assert!(st.st_mode & libc::S_IFMT != libc::S_IFLNK && st.st_mode & libc::S_IFMT != libc::S_IFDIR); + + let parent_real_inode = Arc::clone(pnode.upper_inode.lock().unwrap().as_ref().unwrap()); + let cname = CString::new(node.name.as_str()).expect("invalid c string"); + let node = self.lookup_node(ctx, pnode.inode, cname.as_c_str())?; + let rinode = Arc::clone(&node.first_inode.lock().unwrap()); + let layer = Arc::clone(rinode.layer.as_ref().unwrap()); + + // create the file in upper layer using information from lower layer + + let args = CreateIn { + flags: 0, + mode: st.st_mode, + umask: 0, + fuse_flags: 0, + }; + + let (entry, h, o) = upper.create(ctx, parent_real_inode.inode.load(Ordering::Relaxed), cname.as_c_str(), args)?; + + let real_inode = Arc::new(RealInode { + layer: Some(Arc::clone(&upper)), + inode: AtomicU64::new(entry.inode), + whiteout: AtomicBool::new(false), + opaque: AtomicBool::new(false), + hidden: AtomicBool::new(false), + invalid: AtomicBool::new(false), + }); + + if h.is_none() { + error!("no handle!!!"); + return Err(Error::new(ErrorKind::Other, "non handle!")); + } + + let dst_handle = h.unwrap(); + + let (h, o) = layer.open(ctx, rinode.inode.load(Ordering::Relaxed), libc::O_RDONLY as u32, 0)?; + + if h.is_none() { + error!("no handle!!!"); + return Err(Error::new(ErrorKind::Other, "non handle!")); + } + + let src_handle = h.unwrap(); + + // copy... + // source: layer, rinode.inode, src_handle + // dst: upper, real_inode.inode, dst_handle + + // need to impl ZeroCopyReader/ZeroCopyWriter, somehow like a pipe.. + // stupid: to create a temp file for now.. + // FIXME: need to copy xattr, futimes, set origin.TODO + + let mut template = CString::new("/tmp/fuse-overlay-XXXXXX").expect("invalid c string"); + let mut template = template.into_raw(); + let suffixlen = 10; + let flags = libc::O_RDWR | libc::O_CREAT; + let fd = unsafe { libc::mkostemp(template, flags) }; + + if fd < 0 { + return Err(Error::last_os_error()); + } + + let mut file = unsafe { File::from_raw_fd(fd) }; + let mut offset: usize = 0; + let size = 4 * 1024 * 1024; + loop { + let ret = layer.read(ctx, rinode.inode.load(Ordering::Relaxed), src_handle, &mut file, size, offset as u64, None, 0)?; + if ret == 0 { + break; + } + + offset += ret; + } + + file.seek(SeekFrom::Start(0))?; + offset = 0; + loop { + let ret = upper.write(ctx, entry.inode, dst_handle, &mut file, size, offset as u64, None, false, 0, 0)?; + if ret == 0 { + break; + } + + offset += ret; + } + + drop(file); + unsafe { libc::unlink(template); } + + // close handles + layer.release(ctx, rinode.inode.load(Ordering::Relaxed), 0, src_handle, true, true, None)?; + upper.release(ctx, entry.inode, 0, dst_handle, true, true, None)?; + + // update upper_inode and first_inode + *node.upper_inode.lock().unwrap() = Some(Arc::clone(&real_inode)); + *node.first_inode.lock().unwrap() = Arc::clone(&real_inode); + + return Ok(Arc::clone(&node)); + } + + pub fn copy_node_up(&self, ctx: &Context, node: Arc) -> Result> { + if self.node_in_upper_layer(Arc::clone(&node))? { + return Ok(node); + } + // not in upper, copy it up + let pnode = if let Some(ref n) = node.parent.lock().unwrap().upgrade() { + Arc::clone(n) + } else { + return Err(Error::new(ErrorKind::Other, "no parent?")); + }; + + self.create_node_directory(ctx, Arc::clone(&pnode))?; + // parent prepared + let st = node.stat64(ctx)?; + + // directory + if st.st_mode & libc::S_IFMT == libc::S_IFDIR { + self.create_node_directory(ctx, Arc::clone(&node))?; + return Ok(Arc::clone(&node)); + } + + // other kind of files + + // symlink + if st.st_mode * libc::S_IFMT == libc::S_IFLNK { + return self.copy_symlink_up(ctx, Arc::clone(&node)); + } + + // reg file + // need to use work directory and then rename file to + // final destination for atomic reasons.. not deal with it for now, + // use stupid copy at present. FIXME: + // this need a lot of work here, ntimes, xattr, etc + self.copy_regfile_up(ctx, Arc::clone(&node)) + } + + pub fn is_upper_layer(&self, l: Arc) -> bool { + l.is_upper() + } + + pub fn do_rm(&self, ctx: &Context, parent: u64, name: &CStr, dir: bool) -> Result<()> { + let upper = if let Some(ref v) = self.upper_layer.as_ref() { + Arc::clone(v) + } else { + return Err(Error::from_raw_os_error(libc::EROFS)); + }; + + let pnode = self.lookup_node(ctx, parent, CString::new("").expect("invalid path!").as_c_str())?; + if pnode.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let node = self.lookup_node(ctx, parent, name)?; + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + if dir { + self.reload_directory(ctx, Arc::clone(&node))?; + let (count, whiteouts, opaque) = node.count_entries_and_whiteout(ctx)?; + trace!("files: {}, whiteouts: {} opaque: {}\n", count, whiteouts, opaque); + if count > 0 { + return Err(Error::from_raw_os_error(libc::ENOTEMPTY)); + } + + // need to delete whiteouts? + if whiteouts + opaque > 0 { + if 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; + } + + // parent opaqued + let real_pnode = Arc::clone(pnode.upper_inode.lock().unwrap().as_ref().unwrap()); + let real_parent_inode = real_pnode.inode.load(Ordering::Relaxed); + if real_pnode.opaque.load(Ordering::Relaxed) { + need_whiteout = false; + } + + let layer = Arc::clone(real_pnode.layer.as_ref().unwrap()); + + if node.in_upper_layer() { + if dir { + layer.rmdir(ctx, real_parent_inode, name)?; + } else { + layer.unlink(ctx, real_parent_inode, name)?; + } + } + + trace!("toggling children and inodes hash\n"); + + { + pnode.childrens.lock().unwrap().remove(node.name.as_str()); + self.inodes.lock().unwrap().remove(&node.inode); + } + + let sname = name.to_string_lossy().into_owned().to_owned(); + + if need_whiteout { + trace!("do_rm: creating whiteout\n"); + layer.create_whiteout(ctx, real_parent_inode, sname.as_str())?; + pnode.loaded.store(false, Ordering::Relaxed); + // readd whiteout node + self.lookup_node(ctx, parent, name)?; + pnode.loaded.store(true, Ordering::Relaxed); + } + + Ok(()) + } + + pub fn node_upper_layer_only(&self, node: Arc) -> bool { + node.upper_layer_only() + } + + pub fn empty_node_directory(&self, ctx: &Context, node: Arc) -> Result<()> { + let st = node.stat64(ctx)?; + if st.st_mode & libc::S_IFMT != libc::S_IFDIR { + return Err(Error::from_raw_os_error(libc::ENOTDIR)); + } + + self.reload_directory(ctx, Arc::clone(&node))?; + if !node.in_upper_layer() { + return Ok(()); + } + + // find the real inode + let real_node = Arc::clone(node.upper_inode.lock().unwrap().as_ref().unwrap()); + let layer = Arc::clone(real_node.layer.as_ref().unwrap()); + let real_inode = real_node.inode.load(Ordering::Relaxed); + + // delete opaque + let copaque = CString::new(OPAQUE_WHITEOUT).expect("invalid c string"); + let (opaque, _ino) = layer.is_opaque_whiteout(ctx, real_inode)?; + if opaque { + if let Some(v) = layer.lookup_ignore_enoent(ctx, real_inode, OPAQUE_WHITEOUT)? { + layer.unlink(ctx, real_inode, copaque.as_c_str())?; + } + } + + let iter = { + let mut all = Vec::new(); + for (_, v) in node.childrens.lock().unwrap().iter() { + all.push(Arc::clone(v)); + } + + all + }; + + for child in &iter { + if child.in_upper_layer() { + if child.whiteout.load(Ordering::Relaxed) { + layer.delete_whiteout(ctx, real_inode, child.name.as_str())? + } else { + let s = child.stat64(ctx)?; + let cname = CString::new(child.name.as_str()).expect("invalid c string"); + if s.st_mode & libc::S_IFMT == libc::S_IFDIR { + let (count, whiteouts, opaque) = child.count_entries_and_whiteout(ctx)?; + if count + whiteouts + opaque > 0 { + self.empty_node_directory(ctx, Arc::clone(&child))?; + } + + layer.rmdir(ctx, real_inode, cname.as_c_str())? + } else { + layer.unlink(ctx, real_inode, cname.as_c_str())?; + } + } + + { + // delete the child + self.inodes.lock().unwrap().remove(&child.inode); + node.childrens.lock().unwrap().remove(child.name.as_str()); + } + } + + } + + Ok(()) + } + + pub fn do_open(&self, inode: Inode, flags: u32, fuse_flags: u32) -> Result<(Option, OpenOptions)> { + Err(Error::from(ErrorKind::Unsupported)) + } + + pub fn delete_whiteout_node(&self, ctx: &Context, node: Arc) -> Result<()> { + if !node.whiteout.load(Ordering::Relaxed) { + return Ok(()); + } + + if !self.node_in_upper_layer(Arc::clone(&node))? { + return Ok(()); + } + + let name = CString::new(node.name.as_str()).expect("invalid c string"); + let (layer, real_parent, pnode) = { + let pnode = if let Some(ref n) = node.parent.lock().unwrap().upgrade() { + Arc::clone(n) + } else { + return Err(Error::new(ErrorKind::Other, "no parent")); + }; + + let first_inode = pnode.first_inode.lock().unwrap(); + + (Arc::clone(first_inode.layer.as_ref().unwrap()), first_inode.inode.load(Ordering::Relaxed), Arc::clone(&pnode)) + }; + + // delete white out and update hash + layer.delete_whiteout(ctx, real_parent, node.name.as_str())?; + self.inodes.lock().unwrap().remove(&node.inode); + pnode.childrens.lock().unwrap().remove(node.name.as_str()); + + Ok(()) + } + + pub fn find_real_info_from_handle(&self, ctx: &Context, handle: Handle) -> Result<(Arc, Inode, Handle)> { + if let Some(h) = self.handles.lock().unwrap().get(&handle) { + if let Some( ref rhd) = h.real_handle { + let real_handle = rhd.handle.load(Ordering::Relaxed); + let ri = Arc::clone(&rhd.real_inode); + let layer = Arc::clone(ri.layer.as_ref().unwrap()); + let real_inode = ri.inode.load(Ordering::Relaxed); + return Ok((layer, real_inode, real_handle)) + } + } + + Err(Error::from_raw_os_error(libc::ENOENT)) + } + + pub fn find_real_inode(&self, ctx: &Context, inode: Inode) -> Result<(Arc, Inode)> { + if let Some(n) = self.inodes.lock().unwrap().get(&inode) { + let first = n.first_inode.lock().unwrap(); + let layer = Arc::clone(first.layer.as_ref().unwrap()); + let real_inode = first.inode.load(Ordering::Relaxed); + + return Ok((layer, real_inode)) + } + + Err(Error::from_raw_os_error(libc::ENOENT)) + } +} + +impl BackendFileSystem for OverlayFs { + fn mount(&self) -> Result<(Entry, u64)> { + if let Some(ref root) = self.root.as_ref() { + let ctx = Context::default(); + Ok((Entry { + inode: root.inode, + generation: 0, + attr: root.stat64(&ctx)?, + attr_flags: 0, + attr_timeout: self.config.attr_timeout, + entry_timeout: self.config.entry_timeout, + }, VFS_MAX_INO)) + } else { + Err(Error::new(ErrorKind::Other, "fs not inited")) + } + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl FileSystem for OverlayFs { + type Inode = Inode; + type Handle = Handle; + + fn init(&self, capable: FsOptions) -> Result { + // use vfs' negotiated capability if imported + // other wise, do our own negotiation + let mut opts = FsOptions::DO_READDIRPLUS | FsOptions::READDIRPLUS_AUTO; + + if self.config.do_import { + self.import()?; + } + + if (!self.config.do_import || self.config.writeback) && capable.contains(FsOptions::WRITEBACK_CACHE) { + opts |= FsOptions::WRITEBACK_CACHE; + self.writeback.store(true, Ordering::Relaxed); + } + + if (!self.config.do_import || self.config.no_open) && capable.contains(FsOptions::ZERO_MESSAGE_OPEN) { + opts |= FsOptions::ZERO_MESSAGE_OPEN; + opts.remove(FsOptions::ATOMIC_O_TRUNC); + self.no_open.store(true, Ordering::Relaxed); + } + + if (!self.config.do_import || self.config.no_opendir) && capable.contains(FsOptions::ZERO_MESSAGE_OPENDIR) { + opts |= FsOptions::ZERO_MESSAGE_OPENDIR; + self.no_opendir.store(true, Ordering::Relaxed); + } + + if (!self.config.do_import || self.config.killpriv_v2) && capable.contains(FsOptions::HANDLE_KILLPRIV_V2) { + opts |= FsOptions::HANDLE_KILLPRIV_V2; + self.killpriv_v2.store(true, Ordering::Relaxed); + } + + if self.config.perfile_dax && capable.contains(FsOptions::PERFILE_DAX) { + opts |= FsOptions::PERFILE_DAX; + self.perfile_dax.store(true, Ordering::Relaxed); + } + + Ok(opts) + } + + fn destroy(&self) { + } + + fn statfs(&self, ctx: &Context, inode: Inode) -> Result { + self.do_statvfs(ctx, inode) + } + + fn lookup(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result { + let tmp =name.to_string_lossy().into_owned().to_owned(); + trace!("LOOKUP: parent: {}, name: {}\n", parent, tmp); + let node = self.lookup_node(ctx, parent, name)?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let pnode = if let Some(v) = self.get_node_from_inode(parent) { + v + } else { + return Err(Error::from_raw_os_error(libc::EINVAL)); + }; + + let ppath = String::from(pnode.path.as_str()); + let sname = name.to_string_lossy().into_owned().to_owned(); + let st = node.stat64(ctx)?; + + // load this directory here + if st.st_mode & libc::S_IFMT == libc::S_IFDIR { + self.load_directory(ctx, Arc::clone(&node))?; + node.loaded.store(true, Ordering::Relaxed); + } + + // FIXME: can forget happen between found and increase reference counter? + + *node.lookups.lock().unwrap() += 1; + + Ok(Entry{ + inode: node.inode as u64, + generation: 0, + attr: st,//libc::stat64 + attr_flags: 0, + attr_timeout: self.config.attr_timeout, + entry_timeout: self.config.entry_timeout, + }) + } + + fn forget(&self, ctx: &Context, inode: Inode, count: u64) { + self.forget_one(inode, count) + } + + fn batch_forget(&self, ctx: &Context, requests: Vec<(Inode, u64)>) { + for (inode, count) in requests { + self.forget_one(inode, count); + } + } + + fn opendir(&self, ctx: &Context, inode: Inode, flags: u32) -> Result<(Option, OpenOptions)> { + let mut opts = OpenOptions::empty(); + + match self.config.cache_policy { + CachePolicy::Always => { + opts |= OpenOptions::KEEP_CACHE; + }, + + _ => { + }, + } + + // lookup node + let node = self.lookup_node(ctx, inode, CString::new(".").expect("invalid path!").as_c_str())?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::new(ErrorKind::InvalidInput, "invalid inode number")); + } + + let st = node.stat64(ctx)?; + if st.st_mode & libc::S_IFDIR == 0 { + return Err(Error::from_raw_os_error(libc::ENOTDIR)); + } + + let handle = self.next_handle.fetch_add(1, Ordering::Relaxed); + + // reload directory? + self.reload_directory(ctx, Arc::clone(&node))?; + + let mut cs = Vec::new(); + //add myself + cs.push(Arc::clone(&node)); + + //add parent + if let Some(p) = node.parent.lock().unwrap().upgrade() { + cs.push(p); + } else { + cs.push(Arc::clone(self.root.as_ref().unwrap())); + }; + + for (_, child) in node.childrens.lock().unwrap().iter() { + // skip whiteout node + if child.whiteout.load(Ordering::Relaxed) || child.hidden.load(Ordering::Relaxed) { + continue; + } + // *child.lookups.lock().unwrap() += 1; + cs.push(Arc::clone(child)); + } + + for c in cs.iter() { + *c.lookups.lock().unwrap() += 1; + } + + *node.lookups.lock().unwrap() += 1; + + self.handles.lock().unwrap().insert(handle, HandleData{ + node: Arc::clone(&node), + childrens: Some(cs), + offset: 0, + real_handle: None, + }); + + Ok((Some(handle), opts)) + } + + fn releasedir(&self, ctx: &Context, inode: Inode, flags: u32, handle: Handle) -> Result<()> { + trace!("RELEASEDIR: inode: {}, handle: {}\n", inode, handle); + { + if let Some(v) = self.handles.lock().unwrap().get(&handle) { + for child in v.childrens.as_ref().unwrap() { + self.forget_one(child.inode, 1); + } + + self.forget_one(v.node.inode, 1); + } + } + + trace!("RELEASEDIR: returning"); + + self.handles.lock().unwrap().remove(&handle); + + Ok(()) + } + + // for mkdir or create file + // 1. lookup name, if exists and not whiteout, return EEXIST + // 2. not exists and no whiteout, copy up parent node, ususally a mkdir on upper layer would do the work + // 3. find whiteout, if whiteout in upper layer, shoudl set opaque. if in lower layer, just mkdir? + fn mkdir(&self, ctx: &Context, parent: Inode, name: &CStr, mode: u32, umask: u32) -> Result { + let mut delete_whiteout: bool = false; + let mut has_whiteout: bool = false; + let mut upper_layer_only: bool = false; + let mut opaque = false; + let mut node: Arc = Arc::new(OverlayInode::default()); + + let sname = name.to_string_lossy().into_owned().to_owned(); + if let Some(n) = self.lookup_node_ignore_enoent(ctx, parent, name)? { + if !n.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::EEXIST)); + } + + node = Arc::clone(&n); + has_whiteout = true; + } + + let upper = if let Some(l) = self.get_upper_layer() { + l + } else { + return Err(Error::from_raw_os_error(libc::EROFS)); + // return Err(Error::new(ErrorKind::ReadOnlyFilesystem, "readonly filesystem!")); + }; + + if has_whiteout { + if node.in_upper_layer() { + // whiteout in upper layer, other lower layers are readonly, don't try to delete it + delete_whiteout = true; + } + + if node.upper_layer_only() { + upper_layer_only = true; + } + } + + let pnode = self.lookup_node(ctx, parent, CString::new("").expect("invalid file name").as_c_str())?; + // actual work to copy pnode up.. + let pnode = self.copy_node_up(ctx, Arc::clone(&pnode))?; + + assert!(pnode.in_upper_layer()); + let real_pnode = Arc::clone(pnode.upper_inode.lock().unwrap().as_ref().unwrap()); + + let real_parent_inode = real_pnode.inode.load(Ordering::Relaxed); + + if delete_whiteout { + upper.delete_whiteout(ctx, real_parent_inode, sname.as_str()); + } + + // create dir in upper layer + let entry = upper.mkdir(ctx, real_parent_inode, name, mode, umask)?; + + if !upper_layer_only { + upper.create_opaque_whiteout(ctx, entry.inode)?; + opaque = true; + } + + pnode.loaded.store(false, Ordering::Relaxed); + // remove whiteout node from child and inode hash + // FIXME: maybe a reload from start better + if has_whiteout { + pnode.childrens.lock().unwrap().remove(sname.as_str()); + self.inodes.lock().unwrap().remove(&node.inode); + } + + let node = self.lookup_node(ctx, parent, name)?; + + pnode.loaded.store(true, Ordering::Relaxed); + + Ok(Entry { + inode: node.inode, + generation: 0, + attr: node.stat64(ctx)?, + attr_flags: 0, + attr_timeout: self.config.attr_timeout, + entry_timeout: self.config.entry_timeout, + }) + } + + fn rmdir(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result<()> { + self.do_rm(ctx, parent, name, true) + } + + fn readdir(&self, ctx: &Context, inode: Inode, handle: Handle, size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry) -> Result) -> Result<()> { + self.do_readdir(ctx, handle, size, offset, &mut |dir_entry, _entry| -> Result { + add_entry(dir_entry) + }) + } + + fn readdirplus(&self, ctx: &Context, inode: Inode, handle: Handle, size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry, Entry) -> Result) -> Result<()> { + self.do_readdir(ctx, handle, size, offset, &mut |dir_entry, entry| -> Result { + add_entry(dir_entry, entry) + }) + } + + fn open(&self, ctx: &Context, inode: Inode, flags: u32, fuse_flags: u32) -> Result<(Option, OpenOptions)> { + // open assume file always exist + + let readonly: bool = flags & (libc::O_APPEND | libc::O_CREAT | libc::O_TRUNC | libc::O_RDWR | libc::O_WRONLY) as u32== 0; + + trace!("OPEN: inode: {}, readonly: {}", inode, readonly); + + // toggle flags + let mut flags: i32 = flags as i32; + + flags |= libc::O_NOFOLLOW; + flags &= !libc::O_DIRECT; + if self.config.writeback { + if flags & libc::O_ACCMODE == libc::O_WRONLY { + flags &= !libc::O_ACCMODE; + flags |= libc::O_RDWR; + } + + if flags & libc::O_APPEND != 0 { + flags &= !libc::O_APPEND; + } + } + + // lookup node + let node = self.lookup_node(ctx, inode, CString::new("").expect("invalid c string").as_c_str())?; + + // whiteout node + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + if !readonly { + // copy up to upper layer + self.copy_node_up(ctx, Arc::clone(&node))?; + } + + // assign a handle in overlayfs and open it + let (l, h, o) = node.open(ctx, flags as u32, fuse_flags)?; + if let Some(handle) = h { + let hd = self.next_handle.fetch_add(1, Ordering::Relaxed); + let handle_data = HandleData { + node: Arc::clone(&node), + childrens: None, + offset: 0, + real_handle: Some(RealHandle { + real_inode: Arc::clone(&node.first_inode.lock().unwrap()), + handle: AtomicU64::new(handle), + invalid: AtomicBool::new(false), + }), + }; + + self.handles.lock().unwrap().insert(hd, handle_data); + + let mut opts = OpenOptions::empty(); + match self.config.cache_policy { + CachePolicy::Never => opts |= OpenOptions::DIRECT_IO, + CachePolicy::Always => opts |= OpenOptions::KEEP_CACHE, + _ => {}, + } + + trace!("OPEN: returning handle: {}", hd); + + return Ok((Some(hd), opts)); + } + + Err(Error::from_raw_os_error(libc::ENOENT)) + } + + fn release(&self, ctx: &Context, inode: Inode, flags: u32, handle: Handle, flush: bool, flock_release: bool, lock_owner: Option) -> Result<()> { + if let Some(hd) = self.handles.lock().unwrap().get(&handle) { + let rh = if let Some(ref h) = hd.real_handle { + h + } else { + return Err(Error::new(ErrorKind::Other, "no handle")); + }; + let real_handle = rh.handle.load(Ordering::Relaxed); + let ri = Arc::clone(&rh.real_inode); + let real_inode = ri.inode.load(Ordering::Relaxed); + let l = Arc::clone(&ri.layer.as_ref().unwrap()); + l.release(ctx, real_inode, flags, real_handle, flush, flock_release, lock_owner)?; + } + + self.handles.lock().unwrap().remove(&handle); + + Ok(()) + } + + fn create(&self, ctx: &Context, parent: Inode, name: &CStr, args: CreateIn) -> Result<(Entry, Option, OpenOptions)> { + let mut is_whiteout = false; + let node = self.lookup_node_ignore_enoent(ctx, parent, name)?; + let sname = name.to_string_lossy().into_owned().to_owned(); + + let mut hargs = args; + + let mut flags: i32 = args.flags as i32; + + flags |= libc::O_NOFOLLOW; + flags &= !libc::O_DIRECT; + if self.config.writeback { + if flags & libc::O_ACCMODE == libc::O_WRONLY { + flags &= !libc::O_ACCMODE; + flags |= libc::O_RDWR; + } + + if flags & libc::O_APPEND != 0 { + flags &= !libc::O_APPEND; + } + } + + hargs.flags = flags as u32; + + + if let Some(ref n) = node { + if !n.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::EEXIST)); + } else { + is_whiteout = true; + } + } + + // no entry or whiteout + let pnode = self.lookup_node(ctx, parent, CString::new("").expect("invalid c string").as_c_str())?; + if pnode.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let pnode = self.copy_node_up(ctx, Arc::clone(&pnode))?; + + assert!(pnode.upper_inode.lock().unwrap().is_some()); + + let real_parent_inode = pnode.first_inode.lock().unwrap().inode.load(Ordering::Relaxed); + + // need to delete whiteout? + if is_whiteout { + let node = Arc::clone(node.as_ref().unwrap()); + let first_inode = Arc::clone(&node.first_inode.lock().unwrap()); + let first_layer = first_inode.layer.as_ref().unwrap(); + if node.in_upper_layer() { + // whiteout in upper layer, need to delete + first_layer.delete_whiteout(ctx, real_parent_inode, sname.as_str())?; + } + + // delete inode from inodes and childrens + self.inodes.lock().unwrap().remove(&node.inode); + pnode.childrens.lock().unwrap().remove(sname.as_str()); + } + + // create file in upper layer + if let Some(ref upper_layer) = self.upper_layer.as_ref() { + let (entry, h, o) = upper_layer.create(ctx, real_parent_inode, name, hargs)?; + + // record inode, handle + // lookup will insert inode into children and inodes hash + //let real_inode = Arc::new(RealInode { + // layer: Arc::clone(&upper_layer), + // inode: entry.inode, + // whiteout: AtomicBool::new(false), + // opaque: AtomicBool::new(false), + // hidden: AtomicBool::new(false), + // invalid: AtomicBool::new(false), + //}); + + pnode.loaded.store(false, Ordering::Relaxed); + let node = self.lookup_node(ctx, parent, name)?; + pnode.loaded.store(true, Ordering::Relaxed); + + let real_inode = Arc::clone(node.upper_inode.lock().unwrap().as_ref().unwrap()); + + let final_handle = + if let Some(hd) = h { + let handle = self.next_handle.fetch_add(1, Ordering::Relaxed); + let handle_data = HandleData { + node: Arc::clone(&node), + childrens: None, + offset: 0, + real_handle: Some(RealHandle { + real_inode: Arc::clone(&real_inode), + handle: AtomicU64::new(hd), + invalid: AtomicBool::new(false), + }), + }; + self.handles.lock().unwrap().insert(handle, handle_data); + Some(handle) + } else { + None + }; + + // return data + let entry = Entry { + inode: node.inode, + generation: 0, + attr: node.stat64(ctx)?, + attr_flags: 0, + attr_timeout: self.config.attr_timeout, + entry_timeout: self.config.entry_timeout, + }; + + let mut opts = OpenOptions::empty(); + match self.config.cache_policy { + CachePolicy::Never => opts |= OpenOptions::DIRECT_IO, + CachePolicy::Always => opts |= OpenOptions::KEEP_CACHE, + _ => {}, + } + + return Ok((entry, final_handle, opts)); + + } else { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + Err(Error::new(ErrorKind::Other, "Unknown error")) + } + + fn unlink(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result<()> { + self.do_rm(ctx, parent, name, false) + } + + fn read(&self, ctx: &Context, inode: Inode, handle: Handle, w: &mut dyn ZeroCopyWriter, size: u32, offset: u64, lock_owner: Option, flags: u32) -> Result { + if let Some(v) = self.handles.lock().unwrap().get(&handle) { + if let Some(ref hd) = v.real_handle { + let real_handle = hd.handle.load(Ordering::Relaxed); + let ri = Arc::clone(&hd.real_inode); + let (real_inode, layer) = (ri.inode.load(Ordering::Relaxed), Arc::clone(ri.layer.as_ref().unwrap())); + + return layer.read(ctx, real_inode, real_handle, w, size, offset, lock_owner, flags); + } + } + + Err(Error::from_raw_os_error(libc::ENOENT)) + } + + fn write(&self, ctx: &Context, inode: Inode, handle: Handle, r: &mut dyn ZeroCopyReader, size: u32, offset: u64, lock_owner: Option, delayed_write: bool, flags: u32, fuse_flags: u32) -> Result { + if let Some(v) = self.handles.lock().unwrap().get(&handle) { + if let Some(ref hd) = v.real_handle { + let real_handle = hd.handle.load(Ordering::Relaxed); + let ri = Arc::clone(&hd.real_inode); + let (real_inode, layer) = (ri.inode.load(Ordering::Relaxed), Arc::clone(ri.layer.as_ref().unwrap())); + + return layer.write(ctx, real_inode, real_handle, r, size, offset, lock_owner, delayed_write, flags, fuse_flags); + // remove whiteout node from child and inode hash + } + } + + Err(Error::from_raw_os_error(libc::ENOENT)) + } + + fn getattr(&self, ctx: &Context, inode: Inode, handle: Option) -> Result<(libc::stat64, Duration)> { + trace!("GETATTR: inode: {}\n", inode); + if let Some(h) = handle { + if let Some(hd) = self.handles.lock().unwrap().get(&h) { + if let Some(ref v) = hd.real_handle { + let ri = Arc::clone(&v.real_inode); + let layer = Arc::clone(ri.layer.as_ref().unwrap()); + let real_inode = ri.inode.load(Ordering::Relaxed); + let real_handle = v.handle.load(Ordering::Relaxed); + let (st, _d) = layer.getattr(ctx, real_inode, Some(real_handle))?; + return Ok((st, self.config.attr_timeout)); + } + } + } else { + let node = self.lookup_node(ctx, inode, CString::new("").expect("invalid c string").as_c_str())?; + let rl = Arc::clone(&node.first_inode.lock().unwrap()); + if let Some(ref v) = rl.layer { + let layer = Arc::clone(v); + let real_inode = rl.inode.load(Ordering::Relaxed); + + let (st, _d) = layer.getattr(ctx, real_inode, None)?; + return Ok((st, self.config.attr_timeout)); + } + } + + Err(Error::from_raw_os_error(libc::ENOENT)) + } + + fn setattr(&self, ctx: &Context, inode: Inode, attr: libc::stat64, handle: Option, valid: SetattrValid) -> Result<(libc::stat64, Duration)> { + // find out real inode and real handle, if the first + // layer id not upper layer copy it up + + // deal with handle first + if let Some(h) = handle { + if let Some(hd) = self.handles.lock().unwrap().get(&h) { + if let Some(ref rhd) = hd.real_handle { + let ri = Arc::clone(&rhd.real_inode); + let layer = Arc::clone(ri.layer.as_ref().unwrap()); + let real_inode = ri.inode.load(Ordering::Relaxed); + let real_handle = rhd.handle.load(Ordering::Relaxed); + // handle opened in upper layer + if self.is_upper_layer(Arc::clone(&layer)) { + let (st, _d) = layer.setattr(ctx, real_inode, attr, Some(real_handle), valid)?; + + return Ok((st, self.config.attr_timeout)); + } + } + } + } + + let node = self.lookup_node(ctx, inode, CString::new("").expect("invalid c string").as_c_str())?; + + //layer is upper layer + let node = + if !self.node_in_upper_layer(Arc::clone(&node))? { + self.copy_node_up(ctx, Arc::clone(&node))? + } else { + Arc::clone(&node) + }; + + let v = Arc::clone(&node.first_inode.lock().unwrap()); + let (layer, real_inode) = (Arc::clone(v.layer.as_ref().unwrap()), v.inode.load(Ordering::Relaxed)); + + let (st, _d) = layer.setattr(ctx, real_inode, attr, None, valid)?; + Ok((st, self.config.attr_timeout)) + } + + fn rename(&self, ctx: &Context, olddir: Inode, odlname: &CStr, newdir: Inode, newname: &CStr, flags: u32) -> Result<()> { + // complex, implement it later + Err(Error::from_raw_os_error(libc::EXDEV)) + } + + fn mknod(&self, ctx: &Context, parent: Inode, name: &CStr, mode: u32, rdev: u32, umask: u32) -> Result { + let mut is_whiteout = false; + let node = self.lookup_node_ignore_enoent(ctx, parent, name)?; + let sname = name.to_string_lossy().into_owned().to_owned(); + + if let Some(ref n) = node { + if !n.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::EEXIST)); + } else { + is_whiteout = true; + } + } + + // no entry or whiteout + let pnode = self.lookup_node(ctx, parent, CString::new("").expect("invalid c string").as_c_str())?; + if pnode.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let pnode = self.copy_node_up(ctx, Arc::clone(&pnode))?; + + assert!(pnode.upper_inode.lock().unwrap().is_some()); + + let real_parent_inode = pnode.first_inode.lock().unwrap().inode.load(Ordering::Relaxed); + + // need to delete whiteout? + if is_whiteout { + let node = Arc::clone(node.as_ref().unwrap()); + let first_inode = Arc::clone(&node.first_inode.lock().unwrap()); + let first_layer = first_inode.layer.as_ref().unwrap(); + if node.in_upper_layer() { + // whiteout in upper layer, need to delete + first_layer.delete_whiteout(ctx, real_parent_inode, sname.as_str())?; + } + + // delete inode from inodes and childrens + self.inodes.lock().unwrap().remove(&node.inode); + pnode.childrens.lock().unwrap().remove(sname.as_str()); + } + + // make it + assert!(pnode.in_upper_layer()); + + let real_inode = Arc::clone(pnode.upper_inode.lock().unwrap().as_ref().unwrap()); + let layer = Arc::clone(real_inode.layer.as_ref().unwrap()); + let _entry = layer.mknod(ctx, real_parent_inode, name, mode, rdev, umask)?; + + pnode.loaded.store(false, Ordering::Relaxed); + let node = self.lookup_node(ctx, parent, name)?; + pnode.loaded.store(true, Ordering::Relaxed); + Ok(Entry { + inode: node.inode, + generation: 0, + attr: node.stat64(ctx)?, + attr_flags: 0, + attr_timeout: self.config.attr_timeout, + entry_timeout: self.config.entry_timeout, + }) + } + + fn link(&self, ctx: &Context, inode: Inode, newparent: Inode, name: &CStr) -> Result { + // hard link.. + let node = self.lookup_node(ctx, inode, CString::new("").expect("invalic c string").as_c_str())?; + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let newpnode = self.lookup_node(ctx, newparent, CString::new("").expect("invalid c string").as_c_str())?; + if newpnode.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let newnode = self.lookup_node_ignore_enoent(ctx, newparent, name)?; + + // copy node up + let node = self.copy_node_up(ctx, Arc::clone(&node))?; + let newpnode = self.copy_node_up(ctx, Arc::clone(&newpnode))?; + let sname = name.to_string_lossy().into_owned().to_owned(); + + if let Some(n) = newnode { + if !n.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::EEXIST)); + } + + // need to delete whiteout? if whiteout in upper layer + // delete it + if self.node_in_upper_layer(Arc::clone(&n))? { + // find out the real parent inode and delete whiteout + let pri = &newpnode.first_inode.lock().unwrap(); + let layer = Arc::clone(pri.layer.as_ref().unwrap()); + let real_parent_inode = pri.inode.load(Ordering::Relaxed); + layer.delete_whiteout(ctx, real_parent_inode, sname.as_str())?; + } + + // delete from hash + self.inodes.lock().unwrap().remove(&n.inode); + newpnode.childrens.lock().unwrap().remove(sname.as_str()); + } + + // create the link + let pri = &newpnode.first_inode.lock().unwrap(); + let layer = Arc::clone(pri.layer.as_ref().unwrap()); + let real_parent_inode = pri.inode.load(Ordering::Relaxed); + let real_inode = node.first_inode.lock().unwrap().inode.load(Ordering::Relaxed); + layer.link(ctx, real_inode, real_parent_inode, name)?; + + newpnode.loaded.store(false, Ordering::Relaxed); + let node = self.lookup_node(ctx, newparent, name)?; + newpnode.loaded.store(true, Ordering::Relaxed); + + Ok(Entry { + inode: node.inode, + generation: 0, + attr: node.stat64(ctx)?, + attr_flags: 0, + attr_timeout: self.config.attr_timeout, + entry_timeout: self.config.entry_timeout, + }) + } + + fn symlink(&self, ctx: &Context, linkname: &CStr, parent: Inode, name: &CStr) -> Result { + // soft link + let empty_name = CString::new("").expect("invalid c string"); + let sname = name.to_string_lossy().into_owned().to_owned(); + + let pnode = self.lookup_node(ctx, parent, empty_name.as_c_str())?; + + if pnode.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let node = self.lookup_node_ignore_enoent(ctx, parent, name)?; + if let Some(n) = node { + if !n.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::EEXIST)); + } + + // whiteout, may need to delete it + self.delete_whiteout_node(ctx, Arc::clone(&n))?; + + // delete from hash + self.inodes.lock().unwrap().remove(&n.inode); + pnode.childrens.lock().unwrap().remove(sname.as_str()); + } + + let pnode = self.copy_node_up(ctx, Arc::clone(&pnode))?; + // find out layer, real parent.. + let (layer, real_parent) = { + let first = pnode.first_inode.lock().unwrap(); + (Arc::clone(first.layer.as_ref().unwrap()), first.inode.load(Ordering::Relaxed)) + }; + + layer.symlink(ctx, linkname, real_parent, name)?; + + pnode.loaded.store(false, Ordering::Relaxed); + let node = self.lookup_node(ctx, parent, name)?; + pnode.loaded.store(true, Ordering::Relaxed); + + Ok(Entry { + inode: node.inode, + generation: 0, + attr: node.stat64(ctx)?, + attr_flags: 0, + attr_timeout: self.config.attr_timeout, + entry_timeout: self.config.entry_timeout, + }) + } + + fn readlink(&self, ctx: &Context, inode: Inode) -> Result> { + let empty_name =CString::new("").expect("invalid c string"); + let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + // find out real inode + let (layer, real_inode) = { + let first = node.first_inode.lock().unwrap(); + (Arc::clone(first.layer.as_ref().unwrap()), first.inode.load(Ordering::Relaxed)) + }; + + layer.readlink(ctx, real_inode) + + } + + fn flush(&self, ctx: &Context, inode: Inode, handle: Handle, lock_owner: u64) -> Result<()> { + let empty_name = CString::new("").expect("invalid c string"); + let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + // readonly file can also be flushed, pass flush request + // to lower layer instead of return EROFS here + // if !self.node_in_upper_layer(Arc::clone(&node))? { + // in lower layer, error out or just success? + // FIXME: + // return Err(Error::from_raw_os_error(libc::EROFS)); + // } + + let (layer, real_inode, real_handle) = self.find_real_info_from_handle(ctx, handle)?; + + // FIXME: need to test if inode matches corresponding handle? + + layer.flush(ctx, real_inode, real_handle, lock_owner) + } + + fn fsync(&self, ctx: &Context, inode: Inode, datasync: bool, handle: Handle) -> Result<()> { + let empty_name = CString::new("").expect("invalid c string"); + let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + if !self.node_in_upper_layer(Arc::clone(&node))? { + // in lower layer, error out or just success? + // FIXME: + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + let (layer, real_inode, real_handle) = self.find_real_info_from_handle(ctx, handle)?; + + // FIXME: need to test if inode matches corresponding handle? + + layer.fsync(ctx, real_inode, datasync, real_handle) + } + + fn fsyncdir(&self, ctx: &Context, inode: Inode, datasync: bool, handle: Handle) -> Result<()> { + let empty_name = CString::new("").expect("invalid c string"); + let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + if !self.node_in_upper_layer(Arc::clone(&node))? { + // in lower layer, error out or just success? + // FIXME: + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + let (layer, real_inode, real_handle) = self.find_real_info_from_handle(ctx, handle)?; + + // FIXME: need to test if inode matches corresponding handle? + + layer.fsyncdir(ctx, real_inode, datasync, real_handle) + } + + fn access(&self, ctx: &Context, inode: Inode, mask: u32) -> Result<()> { + let empty_name = CString::new("").expect("invalid c string"); + let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let (layer, real_inode) = self.find_real_inode(ctx, inode)?; + layer.access(ctx, real_inode, mask) + } + + fn setxattr(&self, ctx: &Context, inode: Inode, name: &CStr, value: &[u8], flags: u32) -> Result<()> { + let empty_name = CString::new("").expect("invalid c string"); + let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + if !self.node_in_upper_layer(Arc::clone(&node))? { + // copy node into upper layer + // FIXME: + self.copy_node_up(ctx, Arc::clone(&node))?; + } + + let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; + + let (layer, real_inode) = self.find_real_inode(ctx, inode)?; + + layer.setxattr(ctx, real_inode, name, value, flags) + } + + fn getxattr(&self, ctx: &Context, inode: Inode, name: &CStr, size: u32) -> Result { + let empty_name = CString::new("").expect("invalid c string"); + let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let (layer, real_inode) = self.find_real_inode(ctx, inode)?; + + layer.getxattr(ctx, real_inode, name, size) + } + + fn listxattr(&self, ctx: &Context, inode: Inode, size: u32) -> Result { + let empty_name = CString::new("").expect("invalid c string"); + let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let (layer, real_inode) = self.find_real_inode(ctx, inode)?; + + layer.listxattr(ctx, real_inode, size) + } + + fn removexattr(&self, ctx: &Context, inode: Inode, name: &CStr) -> Result<()> { + let empty_name = CString::new("").expect("invalid c string"); + let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + if !self.node_in_upper_layer(Arc::clone(&node))? { + // copy node into upper layer + // FIXME: + self.copy_node_up(ctx, Arc::clone(&node))?; + } + + let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; + + let (layer, real_inode) = self.find_real_inode(ctx, inode)?; + + layer.removexattr(ctx, real_inode, name) + } + + fn fallocate(&self, ctx: &Context, inode: Inode, handle: Handle, mode: u32, offset: u64, length: u64) -> Result<()> { + let empty_name = CString::new("").expect("invalid c string"); + let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + if !self.node_in_upper_layer(Arc::clone(&node))? { + // copy node into upper layer + // FIXME: + // only for node in upper layer, not in upper layer + // indicates open in readonly mode, and cannot fallocate + return Err(Error::from_raw_os_error(libc::EPERM)); + } + + let (layer, real_inode, real_handle) = self.find_real_info_from_handle(ctx, handle)?; + + layer.fallocate(ctx, real_inode, real_handle, mode, offset, length) + } + + fn lseek(&self, ctx: &Context, inode: Inode, handle: Handle, offset: u64, whence: u32) -> Result { + // can this be on dir? FIXME: assume file for now + // we need special process if it can be called on dir + let empty_name = CString::new("").expect("invalid c string"); + let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let st = node.stat64(ctx)?; + if st.st_mode & libc::S_IFMT == libc::S_IFDIR { + error!("lseek on directory"); + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + + let (layer, real_inode, real_handle) = self.find_real_info_from_handle(ctx, handle)?; + layer.lseek(ctx, real_inode, real_handle, offset, whence) + } + +} + + +impl ZeroCopyReader for File { + fn read_to(&mut self, f: &mut dyn FileReadWriteVolatile, count: usize, off: u64) -> Result { + let mut buf = Vec::::with_capacity(count); + unsafe { buf.set_len(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) }; + f.write_volatile(slice) + } else { + Ok(0) + } + } +} + +impl ZeroCopyWriter for File { + fn write_from(&mut self, f: &mut dyn FileReadWriteVolatile, count: usize, off: u64) -> Result { + let mut buf = Vec::::with_capacity(count); + unsafe { buf.set_len(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) + } + } +} diff --git a/src/overlayfs/plugin.rs b/src/overlayfs/plugin.rs new file mode 100644 index 000000000..11480d947 --- /dev/null +++ b/src/overlayfs/plugin.rs @@ -0,0 +1,92 @@ + +#![allow(missing_docs)] + +use std::collections::{HashMap, LinkedList}; +use std::io::Result; +use std::sync::{Arc}; +use std::io::{Error, ErrorKind}; +use libc; + +use self::super::layer::Layer; +use self::super::PLUGIN_PREFIX; +use self::super::direct::Direct; +use self::super::BoxedLayer; + +pub type BoxedPlugin = Box; + +/// ! plugin trait +pub trait Plugin { + ///! name + fn name(&self) -> String; + ///! load data source + fn load(&self, opaque: String, upper: bool) -> Result>>; + ///! release plugin + fn release(&self) -> Result<()>; +} + +///! plugin manager +pub struct PluginManager { + ///! inner data + pub plugins: HashMap>>, +} + +impl PluginManager { + ///! new + pub fn new() -> Self { + PluginManager { + plugins: HashMap::new(), + } + } + + ///! register a plugin + pub fn register(&mut self, name: String, plugin: Arc>) -> Result<()> { + if self.plugins.get(name.as_str()).is_some() { + return Err(Error::from_raw_os_error(libc::EEXIST)); + } + self.plugins.insert(name, plugin); + + Ok(()) + } + + ///! find a registerd plugin + pub fn get_plugin(&self, name: String) -> Option>> { + if let Some(ref v) = self.plugins.get(name.as_str()) { + Some(Arc::clone(v)) + } else { + None + } + } +} + +pub fn find_plugin(manager: &PluginManager, name: String) -> Option>> { + manager.get_plugin(name) +} + +pub fn process_onelayer(manager: &PluginManager, opaque: String, upper: bool) -> Result>> { + if opaque.starts_with(PLUGIN_PREFIX) { + // plugin + let striped = opaque.strip_prefix(PLUGIN_PREFIX).unwrap(); + let ps: Vec<&str> = striped.splitn(2, "/").collect(); + if ps.len() != 2 { + error!("invalid upperdir parameters!"); + return Err(Error::from(ErrorKind::InvalidData)); + } + + let plugin_name = ps[0]; + let plugin_params = ps[1]; + let plugin = match manager.get_plugin(plugin_name.into()) { + Some(v) => v, + None => { + error!("unknown plugin"); + return Err(Error::from(ErrorKind::InvalidData)); + } + }; + + // load layers from plugin + return plugin.load(plugin_params.into(), upper); + + } else { + // directory + return Direct::new(opaque, upper); + } +} diff --git a/testoverlay/Cargo.toml b/testoverlay/Cargo.toml new file mode 100644 index 000000000..9aac03820 --- /dev/null +++ b/testoverlay/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "testoverlay" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +fuse-backend-rs = { path = "../../fuse-backend-overlay.git", features = ["fusedev"] } +log = ">=0.4.6" +vmm-sys-util = ">=0.4" +libc = ">=0.2.68" +simple_logger = ">=1.13.0" +signal-hook = ">=0.3.10" +lazy_static = ">=1.4.0" diff --git a/testoverlay/src/main.rs b/testoverlay/src/main.rs new file mode 100644 index 000000000..fb41aa124 --- /dev/null +++ b/testoverlay/src/main.rs @@ -0,0 +1,128 @@ +extern crate fuse_backend_rs; +extern crate log; +extern crate vmm_sys_util; +extern crate libc; +extern crate simple_logger; +extern crate signal_hook; +#[macro_use] +extern crate lazy_static; + +use std::io::{Result, Error}; +use std::sync::{Arc, Mutex}; +use std::path::Path; + +use fuse_backend_rs::overlayfs::{OverlayFs}; +use fuse_backend_rs::overlayfs::config::Config; +use fuse_backend_rs::overlayfs::plugin::PluginManager; +use fuse_backend_rs::api::server::Server; +use fuse_backend_rs::transport::{FuseChannel, FuseSession}; +use fuse_backend_rs::api::{Vfs, VfsOptions}; +use signal_hook::{consts::TERM_SIGNALS, iterator::Signals}; +use std::thread; +use simple_logger::SimpleLogger; +use log::LevelFilter; + +pub struct FuseServer { + server: Arc>>, + ch: FuseChannel, +} + +fn main() -> Result<()> { + SimpleLogger::new().with_level(LevelFilter::Trace).init().unwrap(); + let upperdir = String::from("/home/boyang/sources/testoverlay/testdir/upper"); + let mut lowerdir = Vec::new(); + lowerdir.push(String::from("/home/boyang/sources/testoverlay/testdir/lower1")); + lowerdir.push(String::from("/home/boyang/sources/testoverlay/testdir/lower2")); + let workdir = String::from("/home/boyang/sources/testoverlay/testdir/work"); + let mountpoint = String::from("/home/boyang/sources/testoverlay/testdir/merged"); + + let mut config = Config::default(); + config.upper = upperdir; + config.lower = lowerdir; + config.work = workdir; + config.mountpoint = String::from(mountpoint.as_str()); + config.do_import = true; + + let manager = PluginManager::new(); + + print!("new overlay fs\n"); + let mut fs = OverlayFs::new(&manager, config)?; + print!("init root inode\n"); + fs.init_root()?; + + // let vfs = Vfs::new(VfsOptions { + // no_open: false, + // no_opendir: false, + // ..Default::default() + // }); + + // vfs.mount(Box::new(fs), "/")?; + print!("open fuse session\n"); + let mut se = FuseSession::new(Path::new(mountpoint.as_str()), "testoverlay", "", false).unwrap(); + print!("session opened\n"); + se.mount().unwrap(); + + let mut server = FuseServer { + server: Arc::new(Server::new(Arc::new(fs))), + ch: se.new_channel().unwrap(), + }; + + let quit = Arc::new(Mutex::new(false)); + let quit1 = Arc::clone(&quit); + + let handle = thread::spawn(move || { + let _ = server.svc_loop(quit1); + }); + + // main thread + let mut signals = Signals::new(TERM_SIGNALS).unwrap(); + for _sig in signals.forever() { + *quit.lock().unwrap() = true; + break; + } + + let _ = handle.join(); + + se.umount().unwrap(); + se.wake().unwrap(); + + Ok(()) +} + +impl FuseServer { + pub fn svc_loop(&mut self, quit: Arc>) -> Result<()>{ + let _ebadf = std::io::Error::from_raw_os_error(libc::EBADF); + print!("entering server loop\n"); + loop { + + if *quit.lock().unwrap() { + break; + } + + if let Some((reader, writer)) = self + .ch + .get_request() + .map_err(|_| std::io::Error::from_raw_os_error(libc::EINVAL))? + { + if let Err(e) = self + .server + .handle_message(reader, writer.into(), None, None) + { + match e { + fuse_backend_rs::Error::EncodeMessage(_ebadf) => { + break; + } + _ => { + print!("Handling fuse message failed"); + continue; + } + } + } + } else { + print!("fuse server exits"); + break; + } + } + Ok(()) + } +} From cfaa6966fa99a008310d35fec0ad25201c7659b7 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Tue, 26 Sep 2023 16:11:19 +0800 Subject: [PATCH 2/5] overlay: refactor first implementation This commit refactor a lot to previous one, including: * Modified layer trait * Lots of bugfixes to make xfstests happy. * Performance improvement. * Whiteout logics * Remove plugin & Direct layer logics. * A better inode allocator with inode consistency guarantee. Signed-off-by: Wei Zhang --- Cargo.toml | 1 + src/api/filesystem/mod.rs | 5 + src/api/filesystem/overlay.rs | 205 + src/lib.rs | 3 +- src/overlayfs/config.rs | 70 +- src/overlayfs/datasource.rs | 4 - src/overlayfs/direct.rs | 290 -- src/overlayfs/inode_store.rs | 238 + src/overlayfs/layer.rs | 223 - src/overlayfs/mod.rs | 4555 ++++++++--------- src/overlayfs/plugin.rs | 92 - src/overlayfs/sync_io.rs | 913 ++++ src/overlayfs/utils.rs | 14 + src/passthrough/mod.rs | 58 +- src/passthrough/overlay.rs | 14 + src/passthrough/sync_io.rs | 7 +- testoverlay/src/main.rs | 128 - {testoverlay => tests/testoverlay}/Cargo.toml | 2 +- tests/testoverlay/src/main.rs | 237 + 19 files changed, 3841 insertions(+), 3218 deletions(-) create mode 100644 src/api/filesystem/overlay.rs delete mode 100644 src/overlayfs/datasource.rs delete mode 100644 src/overlayfs/direct.rs create mode 100644 src/overlayfs/inode_store.rs delete mode 100644 src/overlayfs/layer.rs delete mode 100644 src/overlayfs/plugin.rs create mode 100644 src/overlayfs/sync_io.rs create mode 100644 src/overlayfs/utils.rs create mode 100644 src/passthrough/overlay.rs delete mode 100644 testoverlay/src/main.rs rename {testoverlay => tests/testoverlay}/Cargo.toml (78%) create mode 100644 tests/testoverlay/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 34eb3d4b3..b046011bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ libc = "0.2.68" log = "0.4.6" mio = { version = "0.8", features = ["os-poll", "os-ext"] } nix = "0.24" +radix_trie = "0.2.1" tokio = { version = "1", optional = true } tokio-uring = { version = "0.4.0", optional = true } vmm-sys-util = { version = "0.11", optional = true } diff --git a/src/api/filesystem/mod.rs b/src/api/filesystem/mod.rs index 9ecadfb71..3168f1eaa 100644 --- a/src/api/filesystem/mod.rs +++ b/src/api/filesystem/mod.rs @@ -30,6 +30,11 @@ pub use async_io::{AsyncFileSystem, AsyncZeroCopyReader, AsyncZeroCopyWriter}; mod sync_io; pub use sync_io::FileSystem; +#[cfg(all(any(feature = "fusedev", feature = "virtiofs"), target_os = "linux"))] +mod overlay; +#[cfg(all(any(feature = "fusedev", feature = "virtiofs"), target_os = "linux"))] +pub use overlay::Layer; + /// Information about a path in the filesystem. #[derive(Copy, Clone, Debug)] pub struct Entry { diff --git a/src/api/filesystem/overlay.rs b/src/api/filesystem/overlay.rs new file mode 100644 index 000000000..bc175371a --- /dev/null +++ b/src/api/filesystem/overlay.rs @@ -0,0 +1,205 @@ +// Copyright (C) 2023 Ant Group. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-BSD-3-Clause file. + +#![allow(missing_docs)] + +use std::ffi::{CStr, CString}; +use std::io::{Error, ErrorKind, Result}; + +use super::{Context, Entry, FileSystem, GetxattrReply}; +use crate::abi::fuse_abi::stat64; + +pub const OPAQUE_XATTR_LEN: u32 = 16; +pub const OPAQUE_XATTR: &str = "user.fuseoverlayfs.opaque"; +pub const UNPRIVILEGED_OPAQUE_XATTR: &str = "user.overlay.opaque"; +pub const PRIVILEGED_OPAQUE_XATTR: &str = "trusted.overlay.opaque"; + +/// A filesystem must implement Layer trait, or it cannot be used as an OverlayFS layer. +pub trait Layer: FileSystem { + /// Return the root inode number + fn root_inode(&self) -> Self::Inode; + + /// Create whiteout file with name . + /// + /// If this call is successful then the lookup count of the `Inode` associated with the returned + /// `Entry` must be increased by 1. + fn create_whiteout(&self, ctx: &Context, parent: Self::Inode, name: &CStr) -> Result { + // Use temp value to avoid moved 'parent'. + let ino: u64 = parent.into(); + match self.lookup(ctx, ino.into(), name) { + Ok(v) => { + // Find whiteout char dev. + if is_whiteout(v.attr) { + return Ok(v); + } + // Non-negative entry with inode larger than 0 indicates file exists. + if v.inode != 0 { + // Decrease the refcount. + self.forget(ctx, v.inode.into(), 1); + // File exists with same name, create whiteout file is not allowed. + return Err(Error::from_raw_os_error(libc::EEXIST)); + } + } + Err(e) => match e.raw_os_error() { + Some(raw_error) => { + // We expect ENOENT error. + if raw_error != libc::ENOENT { + return Err(e); + } + } + None => return Err(e), + }, + } + + // Try to create whiteout char device with 0/0 device number. + let dev = libc::makedev(0, 0); + let mode = libc::S_IFCHR | 0o777; + self.mknod(ctx, ino.into(), name, mode, dev as u32, 0) + } + + /// Delete whiteout file with name . + fn delete_whiteout(&self, ctx: &Context, parent: Self::Inode, name: &CStr) -> Result<()> { + // Use temp value to avoid moved 'parent'. + let ino: u64 = parent.into(); + match self.lookup(ctx, ino.into(), name) { + Ok(v) => { + if v.inode != 0 { + // Decrease the refcount since we make a lookup call. + self.forget(ctx, v.inode.into(), 1); + } + + // Find whiteout so we can safely delete it. + if is_whiteout(v.attr) { + return self.unlink(ctx, ino.into(), name); + } + // Non-negative entry with inode larger than 0 indicates file exists. + if v.inode != 0 { + // File exists but not whiteout file. + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + } + Err(e) => match e.raw_os_error() { + Some(raw_error) => { + // ENOENT is acceptable. + if raw_error != libc::ENOENT { + return Err(e); + } + } + None => return Err(e), + }, + } + Ok(()) + } + + /// Check if the Inode is a whiteout file + fn is_whiteout(&self, ctx: &Context, inode: Self::Inode) -> Result { + let (st, _) = self.getattr(ctx, inode, None)?; + + // Check attributes of the inode to see if it's a whiteout char device. + Ok(is_whiteout(st)) + } + + /// Set the directory to opaque. + fn set_opaque(&self, ctx: &Context, inode: Self::Inode) -> Result<()> { + // Use temp value to avoid moved 'parent'. + let ino: u64 = inode.into(); + + // Get attributes and check if it's directory. + let (st, _d) = self.getattr(ctx, ino.into(), None)?; + if !is_dir(st) { + // Only directory can be set to opaque. + return Err(Error::from_raw_os_error(libc::ENOTDIR)); + } + // A directory is made opaque by setting the xattr "trusted.overlay.opaque" to "y". + // See ref: https://docs.kernel.org/filesystems/overlayfs.html#whiteouts-and-opaque-directories + self.setxattr( + ctx, + ino.into(), + to_cstring(OPAQUE_XATTR)?.as_c_str(), + b"y", + 0, + ) + } + + /// Check if the directory is opaque. + fn is_opaque(&self, ctx: &Context, inode: Self::Inode) -> Result { + // Use temp value to avoid moved 'parent'. + let ino: u64 = inode.into(); + + // Get attributes of the directory. + let (st, _d) = self.getattr(ctx, ino.into(), None)?; + if !is_dir(st) { + return Err(Error::from_raw_os_error(libc::ENOTDIR)); + } + + // Return Result. + let check_attr = |inode: Self::Inode, attr_name: &str, attr_size: u32| -> Result { + let cname = CString::new(attr_name)?; + match self.getxattr(ctx, inode, cname.as_c_str(), attr_size) { + Ok(v) => { + // xattr name exists and we get value. + if let GetxattrReply::Value(buf) = v { + if buf.len() == 1 && buf[0].to_ascii_lowercase() == b'y' { + return Ok(true); + } + } + // No value found, go on to next check. + Ok(false) + } + Err(e) => { + if let Some(raw_error) = e.raw_os_error() { + if raw_error == libc::ENODATA { + return Ok(false); + } + } + + Err(e) + } + } + }; + + // A directory is made opaque by setting some specific xattr to "y". + // See ref: https://docs.kernel.org/filesystems/overlayfs.html#whiteouts-and-opaque-directories + + // Check our customized version of the xattr "user.fuseoverlayfs.opaque". + let is_opaque = check_attr(ino.into(), OPAQUE_XATTR, OPAQUE_XATTR_LEN)?; + if is_opaque { + return Ok(true); + } + + // Also check for the unprivileged version of the xattr "trusted.overlay.opaque". + let is_opaque = check_attr(ino.into(), PRIVILEGED_OPAQUE_XATTR, OPAQUE_XATTR_LEN)?; + if is_opaque { + return Ok(true); + } + + // Also check for the unprivileged version of the xattr "user.overlay.opaque". + let is_opaque = check_attr(ino.into(), UNPRIVILEGED_OPAQUE_XATTR, OPAQUE_XATTR_LEN)?; + if is_opaque { + return Ok(true); + } + + Ok(false) + } +} + +pub(crate) fn is_dir(st: stat64) -> bool { + st.st_mode & libc::S_IFMT == libc::S_IFDIR +} + +pub(crate) fn is_chardev(st: stat64) -> bool { + st.st_mode & libc::S_IFMT == libc::S_IFCHR +} + +pub(crate) fn is_whiteout(st: stat64) -> bool { + // A whiteout is created as a character device with 0/0 device number. + // See ref: https://docs.kernel.org/filesystems/overlayfs.html#whiteouts-and-opaque-directories + let major = unsafe { libc::major(st.st_rdev) }; + let minor = unsafe { libc::minor(st.st_rdev) }; + is_chardev(st) && major == 0 && minor == 0 +} + +pub(crate) fn to_cstring(name: &str) -> Result { + CString::new(name).map_err(|e| Error::new(ErrorKind::InvalidData, e)) +} diff --git a/src/lib.rs b/src/lib.rs index caf3bd086..e1b89d79a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,10 +117,11 @@ pub type Result = ::std::result::Result; pub mod abi; pub mod api; +#[cfg(all(any(feature = "fusedev", feature = "virtiofs"), target_os = "linux"))] +pub mod overlayfs; #[cfg(all(any(feature = "fusedev", feature = "virtiofs"), target_os = "linux"))] pub mod passthrough; pub mod transport; -pub mod overlayfs; pub mod common; pub use self::common::*; diff --git a/src/overlayfs/config.rs b/src/overlayfs/config.rs index ce0e14606..37312bf5d 100644 --- a/src/overlayfs/config.rs +++ b/src/overlayfs/config.rs @@ -1,53 +1,45 @@ +// Copyright (C) 2023 Ant Group. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 -use std::time::Duration; use self::super::CachePolicy; use std::fmt; +use std::time::Duration; #[derive(Default, Clone, Debug)] pub struct Config { - pub upper: String, - pub lower: Vec, - pub work: String, - pub mountpoint: String, - pub do_import: bool, - pub writeback: bool, - pub no_open: bool, - pub no_opendir: bool, - pub killpriv_v2: bool, - pub no_readdir: bool, - pub xattr: bool, - pub xattr_permissions: bool, - pub perfile_dax: bool, - pub cache_policy: CachePolicy, - pub attr_timeout: Duration, - pub entry_timeout: Duration, -} - -impl Default for CachePolicy { - fn default() -> Self { - CachePolicy::Auto - } + pub mountpoint: String, + pub work: String, + pub do_import: bool, + // Filesystem options. + pub writeback: bool, + pub no_open: bool, + pub no_opendir: bool, + pub killpriv_v2: bool, + pub no_readdir: bool, + pub perfile_dax: bool, + pub cache_policy: CachePolicy, + pub attr_timeout: Duration, + pub entry_timeout: Duration, } impl Clone for CachePolicy { - fn clone(&self) -> Self { - match *self { - CachePolicy::Never => CachePolicy::Never, - CachePolicy::Always => CachePolicy::Always, - CachePolicy::Auto => CachePolicy::Auto, - } - } + fn clone(&self) -> Self { + match *self { + CachePolicy::Never => CachePolicy::Never, + CachePolicy::Always => CachePolicy::Always, + CachePolicy::Auto => CachePolicy::Auto, + } + } } impl fmt::Debug for CachePolicy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let policy = - match *self { - CachePolicy::Never => "Never", - CachePolicy::Always => "Always", - CachePolicy::Auto => "Auto", - }; + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let policy = match *self { + CachePolicy::Never => "Never", + CachePolicy::Always => "Always", + CachePolicy::Auto => "Auto", + }; - write!(f, "CachePolicy: {}", policy) - } + write!(f, "CachePolicy: {}", policy) + } } diff --git a/src/overlayfs/datasource.rs b/src/overlayfs/datasource.rs deleted file mode 100644 index 501df060a..000000000 --- a/src/overlayfs/datasource.rs +++ /dev/null @@ -1,4 +0,0 @@ - -///! DataSource trait -pub trait DataSource { -} diff --git a/src/overlayfs/direct.rs b/src/overlayfs/direct.rs deleted file mode 100644 index 427bc83e1..000000000 --- a/src/overlayfs/direct.rs +++ /dev/null @@ -1,290 +0,0 @@ -use std::collections::LinkedList; -use std::sync::Arc; -use std::io::{Result, Error, ErrorKind}; -use std::fs; -use std::ffi::{CStr, CString}; -use std::time::Duration; - - -use self::super::layer::Layer; -use self::super::BoxedLayer; -use self::super::{Inode, Handle}; -use crate::passthrough::{PassthroughFs, Config as PassthroughConfig}; -use crate::api::filesystem::{Entry, FileSystem, FsOptions, OpenOptions, GetxattrReply, ListxattrReply, DirEntry, SetattrValid, ZeroCopyReader, ZeroCopyWriter, Context}; -use crate::abi::fuse_abi::CreateIn; - -pub struct Direct { - pub upper: bool, - pub fs: PassthroughFs, -} - -impl Direct { - pub fn new(path: String, upper: bool) -> Result>> { - let dir = fs::canonicalize(path.as_str())?; - let root_dir = dir.to_string_lossy().into_owned().to_owned(); - let mut config = PassthroughConfig::default(); - config.root_dir = root_dir; - - // enable xattr - config.xattr = true; - - // under overlay fs, no need to negotiate - config.do_import = false; - let fs = PassthroughFs::new(config)?; - - let mut layer = Direct{ - upper, - fs, - }; - - layer.init_layer()?; - - let mut list = LinkedList::new(); - list.push_back(Arc::new(Box::new(layer) as BoxedLayer)); - - Ok(list) - } -} - -impl Layer for Direct { - fn init_layer(&mut self) -> Result<()> { - self.fs.import() - } - - fn cleanup(&mut self) -> Result<()> { - Ok(()) - } - - fn is_upper(&self) -> bool { - self.upper - } -} - -impl FileSystem for Direct { - type Inode = Inode; - type Handle = Handle; - - fn init(&self, capable: FsOptions) -> Result { - Err(Error::from(ErrorKind::Unsupported)) - } - - fn destroy(&self) { - self.fs.destroy() - } - - fn statfs(&self, ctx: &Context, inode: Inode) -> Result { - self.fs.statfs(ctx, inode) - } - - fn lookup(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result { - self.fs.lookup(ctx, parent, name) - } - - fn forget(&self, ctx: &Context, inode: Inode, count: u64) { - self.fs.forget(ctx, inode, count) - } - - fn batch_forget(&self, ctx: &Context, requests: Vec<(Inode, u64)>) { - self.fs.batch_forget(ctx, requests) - } - - fn opendir(&self, ctx: &Context, inode: Inode, flags: u32) -> Result<(Option, OpenOptions)> { - // should test flags to refuse write operations - let iflags: i32 = flags as i32; - let write = iflags & libc::O_RDWR != 0 || iflags & libc::O_WRONLY != 0 || iflags & libc::O_CREAT != 0 || iflags & libc::O_APPEND != 0 || iflags & libc::O_TRUNC != 0; - if !self.upper && write { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.opendir(ctx, inode, flags) - } - - fn releasedir(&self, ctx: &Context, inode: Inode, flags: u32, handle: Handle) -> Result<()> { - self.fs.releasedir(ctx, inode, flags, handle) - } - - fn mkdir(&self, ctx: &Context, parent: Inode, name: &CStr, mode: u32, umask: u32) -> Result { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.mkdir(ctx, parent, name, mode, umask) - } - - fn rmdir(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result<()> { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.rmdir(ctx, parent, name) - } - - fn readdir(&self, ctx: &Context, inode: Inode, handle: Handle, size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry) -> Result) -> Result<()> { - self.fs.readdir(ctx, inode, handle, size, offset, add_entry) - } - - fn readdirplus(&self, ctx: &Context, inode: Inode, handle: Handle, size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry, Entry) -> Result) -> Result<()> { - self.fs.readdirplus(ctx, inode, handle, size, offset, add_entry) - } - - fn open(&self, ctx: &Context, inode: Inode, flags: u32, fuse_flags: u32) -> Result<(Option, OpenOptions)> { - let iflags: i32 = flags as i32; - let write = iflags & libc::O_RDWR != 0 || iflags & libc::O_WRONLY != 0 || iflags & libc::O_CREAT != 0 || iflags & libc::O_APPEND != 0 || iflags & libc::O_TRUNC != 0; - if !self.upper && write { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.open(ctx, inode, flags, fuse_flags) - } - - fn release(&self, ctx: &Context, inode: Inode, flags: u32, handle: Handle, flush: bool, flock_release: bool, lock_owner: Option) -> Result<()> { - self.fs.release(ctx, inode, flags, handle, flush, flock_release, lock_owner) - } - - fn create(&self, ctx: &Context, parent: Inode, name: &CStr, args: CreateIn) -> Result<(Entry, Option, OpenOptions)> { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.create(ctx, parent, name, args) - } - - fn unlink(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result<()> { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.unlink(ctx, parent, name) - } - - fn read(&self, ctx: &Context, inode: Inode, handle: Handle, w: &mut dyn ZeroCopyWriter, size: u32, offset: u64, lock_owner: Option, flags: u32) -> Result { - self.fs.read(ctx, inode, handle, w, size, offset, lock_owner, flags) - } - - fn write(&self, ctx: &Context, inode: Inode, handle: Handle, r: &mut dyn ZeroCopyReader, size: u32, offset: u64, lock_owner: Option, delay_write: bool, flags: u32, fuse_flags: u32) -> Result { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.write(ctx, inode, handle, r, size, offset, lock_owner, delay_write, flags, fuse_flags) - } - - fn getattr(&self, ctx: &Context, inode: Inode, handle: Option) -> Result<(libc::stat64, Duration)> { - self.fs.getattr(ctx, inode, handle) - } - - fn setattr(&self, ctx: &Context, inode: Inode, attr: libc::stat64, handle: Option, valid: SetattrValid) -> Result<(libc::stat64, Duration)> { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.setattr(ctx, inode, attr, handle, valid) - } - - fn rename(&self, ctx: &Context, olddir: Inode, oldname: &CStr, newdir: Inode, newname: &CStr, flags: u32) -> Result<()> { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.rename(ctx, olddir, oldname, newdir, newname, flags) - } - - fn mknod(&self, ctx: &Context, parent: Inode, name: &CStr, mode: u32, rdev: u32, umask: u32) -> Result { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.mknod(ctx, parent, name, mode, rdev, umask) - } - - fn link(&self, ctx: &Context, inode: Inode, newparent: Inode, newname: &CStr) -> Result { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.link(ctx, inode, newparent, newname) - } - - fn symlink(&self, ctx: &Context, linkname: &CStr, parent: Inode, name: &CStr) -> Result { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.symlink(ctx, linkname, parent, name) - } - - fn readlink(&self, ctx: &Context, inode: Inode) -> Result> { - self.fs.readlink(ctx, inode) - } - - fn flush(&self, ctx: &Context, inode: Inode, handle: Handle, lock_owner: u64) -> Result<()> { - // even readonly opened file can be flushed, - // so it does't have to be in upper layer - // if !self.upper { - // return Err(Error::from_raw_os_error(libc::EROFS)); - // } - - self.fs.flush(ctx, inode, handle, lock_owner) - } - - fn fsync(&self, ctx: &Context, inode: Inode, datasync: bool, handle: Handle) -> Result<()> { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.fsync(ctx, inode, datasync, handle) - } - - fn fsyncdir(&self, ctx: &Context, inode: Inode, datasync: bool, handle: Handle) -> Result<()> { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.fsyncdir(ctx, inode, datasync, handle) - } - - fn access(&self, ctx: &Context, inode: Inode, mask: u32) -> Result<()> { - let write = mask as i32 & libc::W_OK != 0; - if !self.upper && write { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.access(ctx, inode, mask) - } - - fn setxattr(&self, ctx: &Context, inode: Inode, name: &CStr, value: &[u8], flags: u32) -> Result<()> { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.setxattr(ctx, inode, name, value, flags) - } - - fn getxattr(&self, ctx: &Context, inode: Inode, name: &CStr, size: u32) -> Result { - self.fs.getxattr(ctx, inode, name, size) - } - - fn listxattr(&self, ctx: &Context, inode: Inode, size: u32) -> Result { - self.fs.listxattr(ctx, inode, size) - } - - fn removexattr(&self, ctx: &Context, inode: Inode, name: &CStr) -> Result<()> { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.removexattr(ctx, inode, name) - } - - fn fallocate(&self, ctx: &Context, inode: Inode, handle: Handle, mode: u32, offset: u64, length: u64) -> Result<()> { - if !self.upper { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - self.fs.fallocate(ctx, inode, handle, mode, offset, length) - } - - fn lseek(&self, ctx: &Context, inode: Inode, handle: Handle, offset: u64, whence: u32) -> Result { - self.fs.lseek(ctx, inode, handle, offset, whence) - } -} diff --git a/src/overlayfs/inode_store.rs b/src/overlayfs/inode_store.rs new file mode 100644 index 000000000..a5d069072 --- /dev/null +++ b/src/overlayfs/inode_store.rs @@ -0,0 +1,238 @@ +// Copyright (C) 2023 Ant Group. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +use std::io::{Error, ErrorKind, Result}; +use std::{ + collections::HashMap, + sync::{atomic::Ordering, Arc}, +}; + +use super::{Inode, OverlayInode, VFS_MAX_INO}; + +use radix_trie::Trie; + +pub struct InodeStore { + // Active inodes. + inodes: HashMap>, + // Deleted inodes which were unlinked but have non zero lookup count. + deleted: HashMap>, + // Path to inode mapping, used to reserve inode number for same path. + path_mapping: Trie, + next_inode: u64, +} + +impl InodeStore { + pub(crate) fn new() -> Self { + Self { + inodes: HashMap::new(), + deleted: HashMap::new(), + path_mapping: Trie::new(), + next_inode: 1, + } + } + + pub(crate) fn alloc_unique_inode(&mut self) -> Result { + // Iter VFS_MAX_INO times to find a free inode number. + let mut ino = self.next_inode; + for _ in 0..VFS_MAX_INO { + if ino > VFS_MAX_INO { + ino = 1; + } + if !self.inodes.contains_key(&ino) && !self.deleted.contains_key(&ino) { + self.next_inode = ino + 1; + return Ok(ino); + } + ino += 1; + } + error!("reached maximum inode number: {}", VFS_MAX_INO); + Err(Error::new( + ErrorKind::Other, + format!("maximum inode number {} reached", VFS_MAX_INO), + )) + } + + pub(crate) fn alloc_inode(&mut self, path: &String) -> Result { + match self.path_mapping.get(path) { + // If the path is already in the mapping, return the reserved inode number. + Some(v) => Ok(*v), + // Or allocate a new inode number. + None => self.alloc_unique_inode(), + } + } + + pub(crate) fn insert_inode(&mut self, inode: Inode, node: Arc) { + self.path_mapping.insert(node.path.clone(), inode); + self.inodes.insert(inode, node); + } + + pub(crate) fn get_inode(&self, inode: Inode) -> Option> { + self.inodes.get(&inode).cloned() + } + + pub(crate) fn get_deleted_inode(&self, inode: Inode) -> Option> { + self.deleted.get(&inode).cloned() + } + + // Return the inode only if it's permanently deleted from both self.inodes and self.deleted_inodes. + pub(crate) fn remove_inode( + &mut self, + inode: Inode, + path_removed: Option, + ) -> Option> { + let removed = match self.inodes.remove(&inode) { + Some(v) => { + // Refcount is not 0, we have to delay the removal. + if v.lookups.load(Ordering::Relaxed) > 0 { + self.deleted.insert(inode, v.clone()); + return None; + } + Some(v) + } + None => { + // If the inode is not in hash, it must be in deleted_inodes. + match self.deleted.get(&inode) { + Some(v) => { + // Refcount is 0, the inode can be removed now. + if v.lookups.load(Ordering::Relaxed) == 0 { + self.deleted.remove(&inode) + } else { + // Refcount is not 0, the inode will be removed later. + None + } + } + None => None, + } + } + }; + + if let Some(path) = path_removed { + self.path_mapping.remove(&path); + } + removed + } + + // As a debug function, print all inode numbers in hash table. + // This function consumes quite lots of memory, so it's disabled by default. + #[allow(dead_code)] + pub(crate) fn debug_print_all_inodes(&self) { + // Convert the HashMap to Vector<(inode, pathname)> + let mut all_inodes = self + .inodes + .iter() + .map(|(inode, ovi)| (inode, ovi.path.clone(), ovi.lookups.load(Ordering::Relaxed))) + .collect::>(); + all_inodes.sort_by(|a, b| a.0.cmp(b.0)); + trace!("all active inodes: {:?}", all_inodes); + + let mut to_delete = self + .deleted + .iter() + .map(|(inode, ovi)| (inode, ovi.path.clone(), ovi.lookups.load(Ordering::Relaxed))) + .collect::>(); + to_delete.sort_by(|a, b| a.0.cmp(b.0)); + trace!("all deleted inodes: {:?}", to_delete); + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_alloc_unique() { + let mut store = InodeStore::new(); + let empty_node = Arc::new(OverlayInode::new()); + store.insert_inode(1, empty_node.clone()); + store.insert_inode(2, empty_node.clone()); + store.insert_inode(VFS_MAX_INO - 1, empty_node.clone()); + + let inode = store.alloc_unique_inode().unwrap(); + assert_eq!(inode, 3); + assert_eq!(store.next_inode, 4); + + store.next_inode = VFS_MAX_INO - 1; + let inode = store.alloc_unique_inode().unwrap(); + assert_eq!(inode, VFS_MAX_INO); + + let inode = store.alloc_unique_inode().unwrap(); + assert_eq!(inode, 3); + } + + #[test] + fn test_alloc_existing_path() { + let mut store = InodeStore::new(); + let mut node_a = OverlayInode::new(); + node_a.path = "/a".to_string(); + store.insert_inode(1, Arc::new(node_a)); + let mut node_b = OverlayInode::new(); + node_b.path = "/b".to_string(); + store.insert_inode(2, Arc::new(node_b)); + let mut node_c = OverlayInode::new(); + node_c.path = "/c".to_string(); + store.insert_inode(VFS_MAX_INO - 1, Arc::new(node_c)); + + let inode = store.alloc_inode(&"/a".to_string()).unwrap(); + assert_eq!(inode, 1); + + let inode = store.alloc_inode(&"/b".to_string()).unwrap(); + assert_eq!(inode, 2); + + let inode = store.alloc_inode(&"/c".to_string()).unwrap(); + assert_eq!(inode, VFS_MAX_INO - 1); + + let inode = store.alloc_inode(&"/notexist".to_string()).unwrap(); + assert_eq!(inode, 3); + } + + #[test] + fn test_remove_inode() { + let mut store = InodeStore::new(); + let mut node_a = OverlayInode::new(); + node_a.lookups.fetch_add(1, Ordering::Relaxed); + node_a.path = "/a".to_string(); + store.insert_inode(1, Arc::new(node_a)); + + let mut node_b = OverlayInode::new(); + node_b.path = "/b".to_string(); + store.insert_inode(2, Arc::new(node_b)); + + let mut node_c = OverlayInode::new(); + node_c.lookups.fetch_add(1, Ordering::Relaxed); + node_c.path = "/c".to_string(); + store.insert_inode(VFS_MAX_INO - 1, Arc::new(node_c)); + + let inode = store.alloc_inode(&"/new".to_string()).unwrap(); + assert_eq!(inode, 3); + + // Not existing. + let inode = store.remove_inode(4, None); + assert!(inode.is_none()); + + // Existing but with non-zero refcount. + let inode = store.remove_inode(1, None); + assert!(inode.is_none()); + assert!(store.get_deleted_inode(1).is_some()); + assert!(store.path_mapping.get(&"/a".to_string()).is_some()); + + // Remove again with file path. + let inode = store.remove_inode(1, Some("/a".to_string())); + assert!(inode.is_none()); + assert!(store.get_deleted_inode(1).is_some()); + assert!(store.path_mapping.get(&"/a".to_string()).is_none()); + + // Node b has refcount 0, removing will be permanent. + let inode = store.remove_inode(2, Some("/b".to_string())); + assert!(inode.is_some()); + assert!(store.get_deleted_inode(2).is_none()); + assert!(store.path_mapping.get(&"/b".to_string()).is_none()); + + // Allocate new inode, it should reuse inode 2 since inode 1 is still in deleted list. + store.next_inode = 1; + let inode = store.alloc_inode(&"/b".to_string()).unwrap(); + assert_eq!(inode, 2); + + // Allocate inode with path "/c" will reuse its inode number. + let inode = store.alloc_inode(&"/c".to_string()).unwrap(); + assert_eq!(inode, VFS_MAX_INO - 1); + } +} diff --git a/src/overlayfs/layer.rs b/src/overlayfs/layer.rs deleted file mode 100644 index 33f08a47a..000000000 --- a/src/overlayfs/layer.rs +++ /dev/null @@ -1,223 +0,0 @@ -#![allow(missing_docs)] - -use std::sync::{Arc, Weak}; -use std::io::{Result, Error}; -use std::os::unix::io::RawFd; -use std::fs::File; -use std::path::PathBuf; -use std::cmp::Eq; -use std::ffi::{CString, CStr}; -use libc; - - -use self::super::super::abi::fuse_abi::Dirent; -use self::super::datasource::DataSource; -use self::super::{OverlayFs, Inode, Handle}; -use self::super::{WHITEOUT_PREFIX, ORIGIN_XATTR, OPAQUE_XATTR, PRIVILEGED_OPAQUE_XATTR, PRIVILEGED_ORIGIN_XATTR, UNPRIVILEGED_OPAQUE_XATTR, OPAQUE_WHITEOUT}; -use crate::api::filesystem::{FileSystem, Context, Entry, GetxattrReply}; -use crate::abi::fuse_abi::{CreateIn}; -use crate::api::VFS_MAX_INO; - -pub const OPAQUE_XATTR_LEN: u32 = 16; - -// we cannot constraint Layer with Eq, as Eq requires Sized -// So we need api to get an identifier and then use identifier -// to do comparation.. Here may have better solutions.. -pub trait Layer: FileSystem { - fn init_layer(&mut self) -> Result<()>; - fn cleanup(&mut self) -> Result<()>; - fn is_upper(&self) -> bool; - // file exists, returns true, not exists return false - // otherwise, error out - // Ok(None) denotes no entry - fn lookup_ignore_enoent(&self, ctx: &Context, ino: u64, name: &str) -> Result> { - let cname = CString::new(name).expect("invalid c string"); - match self.lookup(ctx, Self::Inode::from(ino), cname.as_c_str()) { - Ok(v) => return 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); - } - } - - return Err(e); - }, - } - } - - fn getxattr_ignore_nodata(&self, ctx: &Context, inode: u64, name: &str, size: u32) -> Result>> { - let cname = CString::new(name).expect("invalid c string"); - match self.getxattr(ctx, Self::Inode::from(inode), cname.as_c_str(), size) - { - Ok(v) => { - if let GetxattrReply::Value(buf) = v { - return Ok(Some(buf)); - } else { - return Ok(None); - } - }, - Err(e) => { - if let Some(raw_error) = e.raw_os_error() { - if raw_error == libc::ENODATA || raw_error == libc::ENOTSUP || raw_error == libc::ENOSYS { - return Ok(None); - } - } - - return Err(e); - }, - } - } - - fn whiteout_exists(&self, ctx: &Context, ino: u64, name: &CStr) -> Result<(bool, u64, String)> { - let sname = name.to_string_lossy().into_owned().to_owned(); - - let mut wh_name = String::from(WHITEOUT_PREFIX); - wh_name.push_str(sname.as_str()); - - // .wh.name exists - if let Some(v) = self.lookup_ignore_enoent(ctx, ino, wh_name.as_str())? { - return Ok((true, v.inode, wh_name)); - } - - // char node whiteout - if let Some(st) = self.lookup_ignore_enoent(ctx, ino, sname.as_str())? { - let major = unsafe { libc::major(st.attr.st_rdev) }; - let minor = unsafe { libc::minor(st.attr.st_rdev) }; - if st.attr.st_mode & libc::S_IFMT == libc::S_IFCHR && major == 0 && minor == 0 { - return Ok((true, st.inode, sname)); - } - } - - Ok((false, VFS_MAX_INO, String::from(""))) - } - - fn is_opaque_whiteout(&self, ctx: &Context, inode: u64) -> Result<(bool, Option)> { - let (st, _d) = self.getattr(ctx, Self::Inode::from(inode), None)?; - if st.st_mode & libc::S_IFMT != libc::S_IFDIR { - return Err(Error::from_raw_os_error(libc::ENOTDIR)); - } - - // check xattr first - if let Some(v) = self.getxattr_ignore_nodata(ctx, inode, PRIVILEGED_OPAQUE_XATTR, OPAQUE_XATTR_LEN)? { - if v[0].to_ascii_lowercase() == b'y' { - return Ok((true, None)); - } else { - return Ok((false, None)); - } - } - - if let Some(v) = self.getxattr_ignore_nodata(ctx, inode, UNPRIVILEGED_OPAQUE_XATTR, OPAQUE_XATTR_LEN)? { - if v[0].to_ascii_lowercase() == b'y' { - return Ok((true, None)); - } else { - return Ok((false, None)); - } - } - - if let Some(v) = self.getxattr_ignore_nodata(ctx, inode, OPAQUE_XATTR, OPAQUE_XATTR_LEN)? { - if v[0].to_ascii_lowercase() == b'y' { - return Ok((true, None)); - } else { - return Ok((false, None)); - } - } - - // check .wh..wh..opaque, hwoever, we have no parent inode number for the parent of .wh..wh..opaque - if let Some(v) = self.lookup_ignore_enoent(ctx, inode, OPAQUE_WHITEOUT)? { - return Ok((true, Some(v.inode))); - } - - Ok((false, None)) - } - - fn delete_whiteout(&self, ctx: &Context, parent: u64, name: &str) -> Result<()> { - if !self.is_upper() { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - let mut wh_name = String::from(WHITEOUT_PREFIX); - wh_name.push_str(name); - let cwh_name = CString::new(wh_name.as_str()).expect("invalid c string"); - let cname = CString::new(name).expect("invalid c string"); - - // .wh.name exists - if let Some(v) = self.lookup_ignore_enoent(ctx, parent, wh_name.as_str())? { - self.unlink(ctx, Self::Inode::from(parent), cwh_name.as_c_str())?; - } - - // char node whiteout - if let Some(st) = self.lookup_ignore_enoent(ctx, parent, name)? { - let major = unsafe { libc::major(st.attr.st_rdev) }; - let minor = unsafe { libc::minor(st.attr.st_rdev) }; - if st.attr.st_mode & libc::S_IFMT == libc::S_IFCHR && major == 0 && minor == 0 { - self.unlink(ctx, Self::Inode::from(parent), cname.as_c_str())?; - } - } - - Ok(()) - } - fn create_whiteout(&self, ctx: &Context, parent: u64, name: &str) -> Result { - if !self.is_upper() { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - let mut wh_name = String::from(WHITEOUT_PREFIX); - wh_name.push_str(name); - let cwh_name = CString::new(wh_name.as_str()).expect("invalid c string"); - let cname = CString::new(name).expect("invalid c string"); - - let (exists, _inode, name) = self.whiteout_exists(ctx, parent, cname.as_c_str())?; - - if exists { - return self.lookup(ctx, Self::Inode::from(parent), CString::new(name.as_str()).expect("invalid c string").as_c_str()); - } - - // try to creat .wh.name - let args = CreateIn { - flags: 0, - mode: 0o777, - umask: 0, - fuse_flags: 0, - }; - - if let Ok((entry, h, o)) = self.create(ctx, Self::Inode::from(parent), cwh_name.as_c_str(), args) { - // if let Some(handle) = h { - // self.release(ctx, Self::Inode::from(entry.inode), 0, handle, true, true, None)?; - // } - - return Ok(entry); - } - - // try mknod - let dev = unsafe { libc::makedev(0, 0) }; - let mode = libc::S_IFCHR | 0o777; - self.mknod(ctx, Self::Inode::from(parent), cname.as_c_str(), mode, dev as u32, 0) - } - - fn create_opaque_whiteout(&self, ctx: &Context, parent: u64) -> Result { - if !self.is_upper() { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - let cname = CString::new(OPAQUE_WHITEOUT).expect("invalid c string"); - - // opaque whiteout exists - if let Some(v) = self.lookup_ignore_enoent(ctx, parent, OPAQUE_WHITEOUT)? { - return Ok(v); - } - - // create - let args = CreateIn { - flags: 0, - mode: 0o777, - umask: 0, - fuse_flags: 0, - }; - - let (entry, h, _o) = self.create(ctx, Self::Inode::from(parent), cname.as_c_str(), args)?; - - Ok(entry) - } - -} diff --git a/src/overlayfs/mod.rs b/src/overlayfs/mod.rs index 3fedc3a5e..c6a5e03be 100644 --- a/src/overlayfs/mod.rs +++ b/src/overlayfs/mod.rs @@ -1,2432 +1,2223 @@ -#![allow(missing_docs)] -#![feature(io_error_more)] +// Copyright (C) 2023 Ant Group. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 -pub mod layer; -pub mod datasource; -pub mod plugin; +#![allow(missing_docs)] pub mod config; -pub mod direct; +mod inode_store; +pub mod sync_io; +mod utils; -use std::collections::{LinkedList, HashMap}; -use std::io::{Result, SeekFrom, Seek}; -use std::fs::File; -use std::sync::{Arc, Mutex, Weak}; -use std::sync::atomic::{AtomicBool, Ordering, AtomicU64}; -use std::any::Any; +use core::panic; +use std::collections::HashMap; use std::ffi::{CStr, CString}; -use std::time::Duration; -use std::string::ToString; -use std::mem::{size_of, MaybeUninit}; -use std::os::unix::io::FromRawFd; - -use crate::api::filesystem::{Entry, FileSystem, FsOptions, OpenOptions, GetxattrReply, ListxattrReply, DirEntry, SetattrValid, ZeroCopyReader, ZeroCopyWriter, Context}; -use crate::api::{BackendFileSystem, SLASH_ASCII, VFS_MAX_INO}; -use crate::abi::fuse_abi::{CreateIn, ROOT_ID as FUSE_ROOT_ID}; +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::plugin::PluginManager; -use self::layer::Layer; use self::config::Config; -use std::io::{Error, ErrorKind}; -use libc; - +use self::inode_store::InodeStore; pub type Inode = u64; pub type Handle = u64; -pub const PLUGIN_PREFIX: &str = "//"; -pub const WHITEOUT_PREFIX: &str = ".wh."; -pub const XATTR_PREFIX: &str = "user.fuseoverlayfs"; -pub const ORIGIN_XATTR: &str = "user.fuseoverlayfs.origin"; -pub const OPAQUE_XATTR: &str = "user.fuseoverlayfs.opaque"; -pub const XATTR_CONTAINERS_PREFIX: &str = "user.containers"; -pub const UNPRIVILEGED_XATTR_PREFIX: &str = "user.overlay"; -pub const UNPRIVILEGED_OPAQUE_XATTR: &str = "user.overlay.opaque"; -pub const PRIVILEGED_XATTR_PREFIX: &str = "trusted.overlay"; -pub const PRIVILEGED_OPAQUE_XATTR: &str = "trusted.overlay.opaque"; -pub const PRIVILEGED_ORIGIN_XATTR: &str = "trusted.overlay.origin"; -pub const OPAQUE_WHITEOUT: &str = ".wh..wh..opq"; pub const MAXNAMELEN: usize = 256; pub const CURRENT_DIR: &str = "."; pub const PARENT_DIR: &str = ".."; -pub const WHITEOUT_MAX_LEN: u64 = ".wh.".len() as u64; -pub const MAXBUFSIZE: usize = 1<< 20; +pub const MAXBUFSIZE: usize = 1 << 20; +//type BoxedFileSystem = Box + Send + Sync>; pub type BoxedLayer = Box + Send + Sync>; -// need real inode from layers, need inode to do layer -// operations -#[derive(Default)] -pub struct RealInode { - pub layer: Option>, - pub inode: AtomicU64, - pub whiteout: AtomicBool, - pub opaque: AtomicBool, - pub hidden: AtomicBool, - pub invalid: AtomicBool, -} - -#[derive(Default, Debug)] -pub struct RealInodeStats { - pub inode: u64, - pub whiteout: bool, - pub opaque: bool, - pub stat: Option, - pub wh_name: Option, - pub opaque_inode: Option, +// RealInode represents one inode object in specific layer. +// Also, each RealInode maps to one Entry, which should be 'forgotten' after drop. +// Important note: do not impl Clone trait for it or refcount will be messed up. +pub(crate) struct RealInode { + pub layer: Arc, + pub in_upper_layer: bool, + pub inode: u64, + // File is whiteouted, we need to hide it. + pub whiteout: bool, + // Directory is opaque, we need to hide all entries inside it. + pub opaque: bool, + pub stat: Option, } +// OverlayInode must be protected by lock, it can be operated by multiple threads. #[derive(Default)] -pub struct OverlayInode { - pub childrens: Mutex>>, - pub parent: Mutex>, - pub lower_inodes: Vec>, - pub upper_inode: Mutex>>, - pub first_inode: Mutex>, - pub last_inode: Mutex>, - pub inode: u64, - pub st_ino: libc::ino64_t, - pub st_dev: libc::dev_t, - pub mode: libc::mode_t, - pub entry_type: u32, - pub path: String, - pub name: String, - pub lookups: Mutex, - - pub hidden: AtomicBool, - pub whiteout: AtomicBool, - pub loaded: AtomicBool, - - // what about data source related data for each inode - // put it into layer struct, ino -> private data hash +pub(crate) struct OverlayInode { + // Inode hash table, map from 'name' to 'OverlayInode'. + pub childrens: Mutex>>, + pub parent: Mutex>, + // Backend inodes from all layers. + pub real_inodes: Mutex>, + // Inode number. + pub inode: u64, + pub path: String, + pub name: String, + pub lookups: AtomicU64, + // Node is whiteout-ed. + pub whiteout: AtomicBool, + // Directory is loaded. + pub loaded: AtomicBool, } +#[derive(Default)] pub enum CachePolicy { - Never, - Auto, - Always, + Never, + #[default] + Auto, + Always, } - pub struct OverlayFs { - // should be in daemon structure - pub config: Config, - pub layers: LinkedList>, - pub upper_layer: Option>, - // inode management.. - pub root: Option>, - pub inodes: Mutex>>, - pub next_inode: AtomicU64, - - // manage opened fds.. - pub handles: Mutex>, - pub next_handle: AtomicU64, - pub writeback: AtomicBool, - pub no_open: AtomicBool, - pub no_opendir: AtomicBool, - pub killpriv_v2: AtomicBool, - pub perfile_dax: AtomicBool, + config: Config, + lower_layers: Vec>, + upper_layer: Option>, + // All inodes in FS. + inodes: RwLock, + // Open file handles. + handles: Mutex>>, + next_handle: AtomicU64, + writeback: AtomicBool, + no_open: AtomicBool, + no_opendir: AtomicBool, + killpriv_v2: AtomicBool, + perfile_dax: AtomicBool, } -pub struct RealHandle { - pub real_inode: Arc, - pub handle: AtomicU64, - pub invalid: AtomicBool, +struct RealHandle { + layer: Arc, + in_upper_layer: bool, + inode: u64, + handle: AtomicU64, } -pub struct HandleData { - pub node: Arc, - pub childrens: Option>>, - pub offset: libc::off_t, - - // others? - pub real_handle: Option, +struct HandleData { + node: Arc, + //offset: libc::off_t, + real_handle: Option, } +// RealInode is a wrapper of one inode in specific layer. +// All layer operations returning Entry should be wrapped in RealInode implementation +// so that we can increase the refcount(lookup count) of each inode and decrease it after Drop. +// Important: do not impl 'Copy' trait for it or refcount will be messed up. impl RealInode { - pub fn stat64_ignore_enoent(&self, ctx: &Context) -> Result> { - if self.invalid.load(Ordering::Relaxed) { - return Ok(None); - } - - match self.layer.as_ref().unwrap().getattr(ctx, self.inode.load(Ordering::Relaxed), None) { - Ok((v1, _v2)) => { - return Ok(Some(v1)); - }, - - Err(e) => { - match e.raw_os_error() { - Some(raw_error) => { - if raw_error != libc::ENOENT && raw_error != libc::ENOTDIR && raw_error != libc::ENAMETOOLONG { - return Ok(None); - } - return Err(e); - }, - - None => { - return Err(e); - }, - } - }, - } - } - - // Ok(None) represents noent - pub fn lookup_node(&self, ctx: &Context, name: &CStr) -> Result> { - - if self.whiteout.load(Ordering::Relaxed) || self.invalid.load(Ordering::Relaxed) { - return Ok(None); - } - - let sname = name.to_string_lossy().into_owned().to_owned(); - if sname.starts_with(WHITEOUT_PREFIX){ - return Err(Error::from_raw_os_error(libc::EINVAL)); - } - - let layer = self.layer.as_ref().unwrap(); - - let (whiteout, inode, wh_name) = layer.whiteout_exists(ctx, self.inode.load(Ordering::Relaxed), name)?; - - if whiteout { - return Ok(Some(RealInodeStats { - inode, - whiteout, - opaque: false, - stat: None, - wh_name: Some(wh_name), - opaque_inode: None, - })); - } - - if let Some(v) = layer.lookup_ignore_enoent(ctx, self.inode.load(Ordering::Relaxed), sname.as_str())? { - // directory? - if v.attr.st_mode & libc::S_IFMT != libc::S_IFDIR { - return Ok(Some(RealInodeStats { - inode: v.inode, - whiteout: false, - opaque: false, - stat: Some(v.attr), - wh_name: None, - opaque_inode: None, - })); - } - - let (opaque, opaque_inode) = layer.is_opaque_whiteout(ctx, v.inode)?; - - return Ok(Some(RealInodeStats { - inode: v.inode, - whiteout: false, - opaque, - stat: Some(v.attr), - wh_name: None, - opaque_inode, - })); - } else { - return Ok(None); - } - } -} - -impl OverlayInode { - pub fn stat64(&self, ctx: &Context) -> Result { - // try upper layer if there is - if let Some(ref l) = *self.upper_inode.lock().unwrap() { - if let Some(v) = l.stat64_ignore_enoent(ctx)? { - return Ok(v); - } - } - - // try layers in order or just take stst from first layer? - for l in &self.lower_inodes { - if let Some(v) = l.stat64_ignore_enoent(ctx)? { - return Ok(v); - } - } - - // not in any layer - Err(Error::from_raw_os_error(libc::ENOENT)) - } - - pub fn count_entries_and_whiteout(&self, ctx: &Context) -> Result<(u64, u64, u64)> { - let mut count = 0; - let mut whiteouts = 0; - let mut opaque = 0; - - let st = self.stat64(ctx)?; - - // must be directory - assert!(st.st_mode & libc::S_IFMT == libc::S_IFDIR); - if let Some(ref ri) = *self.upper_inode.lock().unwrap() { - if ri.opaque.load(Ordering::Relaxed) { - opaque = 1; - } - } - - for (_, child) in self.childrens.lock().unwrap().iter() { - if child.whiteout.load(Ordering::Relaxed) { - whiteouts += 1; - } else { - count += 1; - } - } - - Ok((count, whiteouts, opaque)) - } - - pub fn open(&self, ctx: &Context, flags: u32, fuse_flags: u32) -> Result<(Arc, Option, OpenOptions)> { - let ri = Arc::clone(&self.first_inode.lock().unwrap()); - if let Some(ref l) = ri.layer { - let (h, o) = l.open(ctx, ri.inode.load(Ordering::Relaxed), flags, fuse_flags)?; - Ok((Arc::clone(l), h, o)) - } else { - Err(Error::new(ErrorKind::Other, "no first layer")) - } - } - - pub fn in_upper_layer(&self) -> bool { - self.upper_inode.lock().unwrap().is_some() - } - - pub fn upper_layer_only(&self) -> bool { - self.lower_inodes.len() == 0 - } + fn new( + layer: Arc, + 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 { + 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> { + 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), + }, + } + } + + // Do real lookup action in specific layer, this call will increase Entry refcount which must be released later. + fn lookup_child_ignore_enoent(&self, ctx: &Context, name: &str) -> Result> { + let cname = CString::new(name).map_err(|e| Error::new(ErrorKind::InvalidData, e))?; + // Real inode must have a layer. + let layer = self.layer.as_ref(); + match layer.lookup(ctx, self.inode, cname.as_c_str()) { + Ok(v) => { + // Negative entry also indicates missing entry. + 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) + } + } + } + + // Find child inode in same layer under this directory(Self). + // Return None if not found. + fn lookup_child(&self, ctx: &Context, name: &str) -> Result> { + if self.whiteout { + return Ok(None); + } + + let layer = self.layer.as_ref(); + + // Find child Entry with under directory with inode . + match self.lookup_child_ignore_enoent(ctx, name)? { + Some(v) => { + // The Entry must be forgotten in each layer, which will be done automatically by Drop operation. + 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), + } + } + + // Read directory entries from specific RealInode, error out if it's not directory. + fn readdir(&self, ctx: &Context) -> Result> { + // Deleted inode should not be read. + if self.whiteout { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let stat = match self.stat { + Some(v) => v, + None => self.stat64(ctx)?, + }; + + // Must be directory. + if !utils::is_dir(stat) { + return Err(Error::from_raw_os_error(libc::ENOTDIR)); + } + + // Open the directory and load each entry. + 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, + }, + // opendir may not be supported if no_opendir is set, so we can ignore this error. + Err(e) => { + match e.raw_os_error() { + Some(raw_error) => { + if raw_error == libc::ENOSYS { + // We can still call readdir with inode if opendir is not supported in this layer. + 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 { + 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) + }, + )?; + } + + // Non-zero handle indicates successful 'open', we should 'release' it. + if handle > 0 { + if let Err(e) = self + .layer + .releasedir(ctx, self.inode, libc::O_RDONLY as u32, handle) + { + // ignore ENOSYS + match e.raw_os_error() { + Some(raw_error) => { + if raw_error != libc::ENOSYS { + return Err(e); + } + } + None => { + return Err(e); + } + } + } + } + + // Lookup all child and construct "RealInode"s. + 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 { + 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())?; + + // Wrap whiteout to RealInode. + 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 { + 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)?; + + // update node's first_layer + 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)> { + 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 { + 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 { + 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), + }) + } + + // Create a symlink in self directory. + fn symlink(&self, ctx: &Context, link_name: &str, filename: &str) -> Result { + 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), + }) + } } -fn process_lower_layer(manager: &PluginManager, opaque: &[String]) -> Result>> { - let mut layers = LinkedList::new(); - - for lower in opaque { - let mut lower_layers = plugin::process_onelayer(manager, lower.into(), false)?; - layers.append(&mut lower_layers); - } - - Ok(layers) +impl Drop for RealInode { + fn drop(&mut self) { + // Release refcount of inode in layer. + 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() -> Self { + OverlayInode::default() + } + + // Allocate new OverlayInode based on one RealInode, + // inode number is always 0 since only OverlayFs has global unique inode allocator. + 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, + ) -> Result { + 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); + + // This is whiteout, no need to check lower layers. + if whiteout { + break; + } + + // A non-directory file shadows all lower layers as default. + if !utils::is_dir(stat) { + break; + } + + // Opaque directory shadows all lower layers. + if opaque { + break; + } + } else { + // This is whiteout, no need to record this, break directly. + if ri.whiteout { + break; + } + + // Only directory have multiple real inodes, so if this is non-first real-inode + // and it's not directory, it should indicates some invalid layout. @weizhang555 + if !utils::is_dir(stat) { + error!("invalid layout: non-directory has multiple real inodes"); + break; + } + + // Valid directory. + new.real_inodes.lock().unwrap().push(ri); + // Opaque directory shadows all lower layers. + if opaque { + break; + } + } + } + Ok(new) + } + + pub fn stat64(&self, ctx: &Context) -> Result { + // try layers in order or just take stat from first layer? + for l in self.real_inodes.lock().unwrap().iter() { + if let Some(v) = l.stat64_ignore_enoent(ctx)? { + return Ok(v); + } + } + + // not in any layer + 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)?; + + // must be directory + 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, Option, OpenOptions)> { + let (layer, _, inode) = self.first_layer_inode(); + let (h, o, _) = layer.as_ref().open(ctx, inode, flags, fuse_flags)?; + Ok((layer, h, o)) + } + + // Self is directory, fill all childrens. + pub fn scan_childrens(self: &Arc, ctx: &Context) -> Result> { + 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> = HashMap::new(); + // read out directories from each layer + let mut counter = 1; + let layers_count = self.real_inodes.lock().unwrap().len(); + // Scan from upper layer to lower layer. + 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 { + // Node is deleted from some upper layer, skip it. + 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()); + // not directory + break; + } + + // Read all entries from one layer. + let entries = ri.readdir(ctx)?; + + // Merge entries from one layer to all_layer_inodes. + for (name, inode) in entries { + match all_layer_inodes.get_mut(&name) { + Some(v) => { + // Append additional RealInode to the end of vector. + v.push(inode) + } + None => { + all_layer_inodes.insert(name, vec![inode]); + } + }; + } + + // if opaque, stop here + if ri.opaque { + debug!("directory {} is opaque", self.path.as_str()); + break; + } + } + + // Construct OverlayInode for each entry. + let mut childrens = vec![]; + for (name, real_inodes) in all_layer_inodes { + // Inode numbers are not allocated yet. + 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) + } + + // Create a new directory in upper layer for node, node must be directory. + pub fn create_upper_dir( + self: &Arc, + 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 node already has upper layer, we can just return here. + if self.in_upper_layer() { + return Ok(()); + } + + // not in upper layer, check parent. + 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)?; // recursive call + } + let mut child = None; + pnode.handle_upper_inode_locked(&mut |parent_upper_inode| -> Result { + 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)?, + }; + // create directory here + 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 { + // Push the new real inode to the front of vector. + self.add_upper_inode(ri, false); + } + + Ok(()) + } + + // Add new upper RealInode to OverlayInode, clear all lower RealInodes if 'clear_lowers' is true. + fn add_upper_inode(self: &Arc, ri: RealInode, clear_lowers: bool) { + let mut inodes = self.real_inodes.lock().unwrap(); + // Update self according to upper attribute. + self.whiteout.store(ri.whiteout, Ordering::Relaxed); + + // Push the new real inode to the front of vector. + let mut new = vec![ri]; + // Drain lower RealInodes. + let lowers = inodes.drain(..).collect::>(); + if !clear_lowers { + // If not clear lowers, append them to the end of vector. + 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, 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> { + 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) { + self.childrens + .lock() + .unwrap() + .insert(name.to_string(), node); + } + + pub fn handle_upper_inode_locked( + &self, + f: &mut dyn FnMut(Option<&RealInode>) -> Result, + ) -> Result { + 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 { - if mode & libc::S_IFBLK != 0 { - return libc::DT_BLK; - } - - if mode & libc::S_IFCHR != 0 { - return libc::DT_CHR; - } - - if mode & libc::S_IFDIR != 0 { - return libc::DT_DIR; - } - - if mode & libc::S_IFIFO != 0 { - return libc::DT_FIFO; - } - - if mode & libc::S_IFLNK != 0 { - return libc::DT_LNK; - } - - if mode & libc::S_IFREG != 0 { - return libc::DT_REG; - } - - if mode & libc::S_IFSOCK != 0 { - return libc::DT_SOCK; - } - - return libc::DT_UNKNOWN; + 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(manager: &PluginManager, params: Config) -> Result { - // upper dir - let mut layers = plugin::process_onelayer(manager, String::from(params.upper.as_str()), true)?; - - let upper_layer = if let Some(ref v) = layers.front() { - Some(Arc::clone(v)) - }else { - None - }; - - // lower dir - let mut lower_layers = process_lower_layer(manager, params.lower.as_slice())?; - - layers.append(&mut lower_layers); - // load root inode - Ok(OverlayFs { - config: params, - upper_layer, - layers, - inodes: Mutex::new(HashMap::new()), - root: None, - next_inode: AtomicU64::new(FUSE_ROOT_ID + 1), - 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 init_root(&mut self) -> Result<()> { - let mut root = OverlayInode::new(); - root.inode = FUSE_ROOT_ID; - root.path = String::from("."); - root.name = String::from(""); - root.entry_type = libc::DT_DIR as u32; - root.lookups = Mutex::new(2); - let ctx = Context::default(); - - let mut first= true; - for layer in self.layers.iter() { - let (opaque, _ino) = layer.is_opaque_whiteout(&ctx, FUSE_ROOT_ID)?; - let real = RealInode { - layer: Some(Arc::clone(layer)), - inode: AtomicU64::new(FUSE_ROOT_ID), - whiteout: AtomicBool::new(false), - opaque: AtomicBool::new(opaque), - hidden: AtomicBool::new(false), - invalid: AtomicBool::new(false), - }; - - let real_inode = Arc::new(real); - - if first { - first = false; - root.first_inode = Mutex::new(Arc::clone(&real_inode)); - } - - if layer.is_upper() { - root.upper_inode = Mutex::new(Some(Arc::clone(&real_inode))); - } else { - root.lower_inodes.push(Arc::clone(&real_inode)); - } - - root.last_inode = Mutex::new(Arc::clone(&real_inode)); - } - - let root_node = Arc::new(root); - - // insert root inode into hash - { - self.inodes.lock().unwrap().insert(FUSE_ROOT_ID, Arc::clone(&root_node)); - } - - let ctx = Context::default(); - info!("loading root directory\n"); - self.load_directory(&ctx, Arc::clone(&root_node))?; - - self.root = Some(root_node); - - Ok(()) - } - - pub fn import(&self) -> Result<()> { - Ok(()) - } - - pub fn make_overlay_inode(&self, ris: &RealInodeStats, layer: Arc) -> Result { - let mut new = OverlayInode::new(); - new.whiteout.store(ris.whiteout, Ordering::Relaxed); - let real_inode = Arc::new(RealInode { - layer: Some(Arc::clone(&layer)), - inode: AtomicU64::new(ris.inode), - whiteout: AtomicBool::new(ris.whiteout), - opaque: AtomicBool::new(ris.opaque), - hidden: AtomicBool::new(false), - invalid: AtomicBool::new(false), - }); - - new.first_inode = Mutex::new(Arc::clone(&real_inode)); - new.last_inode = Mutex::new(Arc::clone(&real_inode)); - new.lookups = Mutex::new(1); - if layer.is_upper() { - new.upper_inode = Mutex::new(Some(Arc::clone(&real_inode))); - } - - let inode = self.next_inode.fetch_add(1, Ordering::Relaxed); - if inode > VFS_MAX_INO { - error!("reached maximum inode number: {}", VFS_MAX_INO); - return Err(Error::new(ErrorKind::Other, format!("maximum inode number {} reached", VFS_MAX_INO))); - } - new.inode = inode; - if let Some(st) = ris.stat { - new.st_ino = st.st_ino; - new.st_dev= st.st_dev; - new.mode = st.st_mode; - new.entry_type = entry_type_from_mode(st.st_mode) as u32; - } - - Ok(new) - } - - pub fn lookup_node(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result> { - if name.to_bytes_with_nul().contains(&SLASH_ASCII) { - return Err(Error::from_raw_os_error(libc::EINVAL)); - } - - // lookup name - let pnode = { - let inodes = self.inodes.lock().unwrap(); - if let Some(v) = inodes.get(&parent) { - Arc::clone(v) - } else { - // no parent inode? - return Err(Error::from_raw_os_error(libc::EINVAL)); - } - }; - - let sname = name.to_string_lossy().into_owned().to_owned(); - if sname.starts_with(WHITEOUT_PREFIX){ - return Err(Error::from_raw_os_error(libc::EINVAL)); - } - - if sname.eq(".") || (parent == FUSE_ROOT_ID && sname.eq("..")) || sname.is_empty() { - return Ok(Arc::clone(&pnode)); - } - - if pnode.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - // found the node - if let Some (v) = pnode.childrens.lock().unwrap().get(sname.as_str()) { - return Ok(Arc::clone(v)); - } - - // if the directory is already loaded, not found - // we will change dir/file, we will toggle loaded - // flag to scan directory - if pnode.loaded.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - // don't find it, lookup in layers - let mut path = String::from(pnode.path.as_str()); - path.push_str("/"); - path.push_str(sname.as_str()); - - let mut node_inited: bool = false; - let mut new = OverlayInode::new(); - - // lookup until meet whiteout/opaque whiteout/file in lower layer.. - { - if let Some(ref ri) = *pnode.upper_inode.lock().unwrap() { - // find an entry - if let Some(ris) = ri.lookup_node(ctx, name)? { - node_inited = true; - new = self.make_overlay_inode(&ris, Arc::clone(ri.layer.as_ref().unwrap()))?; - } - } - } - - 'layer_loop: - for ri in &pnode.lower_inodes { - if let Some(ris) = ri.lookup_node(ctx, name)? { - // find an entry - let layer = Arc::clone(ri.layer.as_ref().unwrap()); - let mut real_inode = Arc::new(RealInode { - layer: Some(Arc::clone(&layer)), - inode: AtomicU64::new(ris.inode), - whiteout: AtomicBool::new(ris.whiteout), - hidden: AtomicBool::new(false), - opaque: AtomicBool::new(ris.opaque), - invalid: AtomicBool::new(false), - }); - - if !node_inited { - node_inited = true; - new = self.make_overlay_inode(&ris, Arc::clone(&layer))?; - new.lower_inodes.push(Arc::clone(&real_inode)); - - // should stop? - if ris.whiteout { - break 'layer_loop; - } - - // not whiteout, must have stat - let st = ris.stat.as_ref().unwrap(); - if st.st_mode & libc::S_IFMT != libc::S_IFDIR { - break 'layer_loop; - } - - // opaque? - if ris.opaque { - break 'layer_loop; - } - } else { - - // should stop? - if ris.whiteout { - break 'layer_loop; - } - - // not whiteout, must have stat - let st = ris.stat.as_ref().unwrap(); - if st.st_mode & libc::S_IFMT != libc::S_IFDIR { - break 'layer_loop; - } - - // directory - if node_inited { - new.lower_inodes.push(Arc::clone(&real_inode)); - new.last_inode = Mutex::new(Arc::clone(&real_inode)); - } - - // opaque? - if ris.opaque { - break 'layer_loop; - } - } - } - } - - if node_inited { - new.path = String::from(path.as_str()); - new.name = String::from(sname.as_str()); - // set its parent node - *new.parent.lock().unwrap() = Arc::downgrade(&pnode); - // insert node into hashs - let new_node = Arc::new(new); - self.inodes.lock().unwrap().insert(new_node.inode as u64, Arc::clone(&new_node)); - pnode.childrens.lock().unwrap().insert(sname, Arc::clone(&new_node)); - return Ok(Arc::clone(&new_node)); - } - - // return specific errors? - Err(Error::from_raw_os_error(libc::ENOENT)) - } - - pub fn lookup_node_ignore_enoent(&self, ctx: &Context, parent: u64, name: &CStr) -> Result>> { - match self.lookup_node(ctx, parent, name) { - Ok(n) => { - return Ok(Some(Arc::clone(&n))); - }, - - Err(e) => { - if let Some(raw_error) = e.raw_os_error() { - if raw_error == libc::ENOENT { - return Ok(None); - } - } - return Err(e); - }, - } - - } - - pub fn get_node_from_inode(&self, inode: u64) -> Option> { - if let Some(v) = self.inodes.lock().unwrap().get(&inode) { - return Some(Arc::clone(v)); - } - - return None; - } - - pub fn load_directory_layer(&self, ctx: &Context, ovl_inode: u64, real: Arc) -> Result<()> { - if real.whiteout.load(Ordering::Relaxed) || real.invalid.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - if let Some(st) = real.stat64_ignore_enoent(ctx)? { - if st.st_mode & libc::S_IFMT != libc::S_IFDIR { - return Err(Error::from_raw_os_error(libc::ENOTDIR)); - } - - // process this directory - let l = Arc::clone(real.layer.as_ref().unwrap()); - let rinode = real.inode.load(Ordering::Relaxed); - - let handle = if let (Some(h), _) = l.opendir(ctx, rinode, libc::O_RDONLY as u32)? { - h - } else { - return Err(Error::new(ErrorKind::Other, "no dir handle")); - }; - - let mut more = true; - let mut offset = 0; - let bufsize = 1024; - while more { - more = false; - l.readdir(ctx, rinode, handle, bufsize, offset, &mut |d| -> Result { - more = true; - offset = d.offset; - let cname = unsafe { CString::from_vec_unchecked(d.name.to_vec()) }; - let cstr_name = cname.as_c_str(); - - let child_name = cstr_name.to_string_lossy().into_owned().to_owned(); - - info!("entry: {}", child_name.as_str()); - - if child_name.eq(CURRENT_DIR) || child_name.eq(PARENT_DIR) || child_name.starts_with(WHITEOUT_PREFIX) { - return Ok(1); - } - - self.lookup_node(ctx, ovl_inode, cstr_name)?; - - Ok(1) - })?; - } - - l.releasedir(ctx, rinode, libc::O_RDONLY as u32, handle)?; - - } else { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - Ok(()) - } - - pub fn load_directory(&self, ctx: &Context, node: Arc) -> Result<()> { - let tmp_ui = { - if let Some(ref v) = *node.upper_inode.lock().unwrap() { - Some(Arc::clone(v)) - } else { - None - } - }; - - if let Some(ref ui) = tmp_ui { - debug!("load upper for {}", node.path.as_str()); - // upper layer - if ui.whiteout.load(Ordering::Relaxed) || ui.invalid.load(Ordering::Relaxed) { - return Ok(()); - } - - if let Some(st) = ui.stat64_ignore_enoent(ctx)? { - if st.st_mode & libc::S_IFMT != libc::S_IFDIR { - // not directory - return Ok(()); - } - - // process this layer - self.load_directory_layer(ctx, node.inode, Arc::clone(ui))?; - } - - // if opaque, stop here - if ui.opaque.load(Ordering::Relaxed) { - node.loaded.store(true, Ordering::Relaxed); - return Ok(()); - } - } - - // read out directories from each layer - 'layer_loop: - for li in &node.lower_inodes { - debug!("loading lower for {}", node.path.as_str()); - if li.whiteout.load(Ordering::Relaxed) || li.invalid.load(Ordering::Relaxed) { - break 'layer_loop; - } - - if let Some(st) = li.stat64_ignore_enoent(ctx)? { - if st.st_mode & libc::S_IFMT != libc::S_IFDIR { - // not directory - break 'layer_loop; - } - - // process this layer - self.load_directory_layer(ctx, node.inode, Arc::clone(li))?; - } - - // if opaque, stop here - if li.opaque.load(Ordering::Relaxed) { - break 'layer_loop; - } - } - - node.loaded.store(true, Ordering::Relaxed); - - Ok(()) - } - - pub fn reload_directory(&self, ctx: &Context, node: Arc) -> Result<()> { - if node.loaded.load(Ordering::Relaxed) { - return Ok(()) - } - { - let mut children =node.childrens.lock().unwrap(); - *children = HashMap::new(); - } - - self.load_directory(ctx, node) - } - - pub fn get_first_layer(&self) -> Option> { - if let Some(v) = self.layers.front() { - Some(Arc::clone(v)) - } else { - None - } - } - - pub fn get_upper_layer(&self) -> Option> { - if let Some(ref v) = self.upper_layer.as_ref() { - Some(Arc::clone(v)) - } else { - None - } - } - - pub fn get_first_lower_layer(&self) -> Option> { - if let Some(ref _v) = self.upper_layer.as_ref() { - let mut index = 0; - for layer in self.layers.iter() { - if index == 1 { - return Some(Arc::clone(layer)); - } - index += 1; - } - - None - } else { - if let Some(v) = self.layers.front() { - Some(Arc::clone(v)) - } else { - None - } - } - } - - pub fn forget_one(&self, inode: Inode, count: u64) { - if inode == FUSE_ROOT_ID || inode == 0 { - return; - } - - let v = { - if let Some(n) = self.inodes.lock().unwrap().get(&inode) { - Arc::clone(n) - } else { - return; - } - }; - // lock up lookups - let mut lookups = v.lookups.lock().unwrap(); - - if *lookups < count { - *lookups = 0; - } else { - *lookups -= count; - } - - // remove it from hashmap - - if *lookups == 0 { - self.inodes.lock().unwrap().remove(&inode); - let parent = v.parent.lock().unwrap(); - - if let Some(p) = parent.upgrade() { - p.childrens.lock().unwrap().remove(v.name.as_str()); - p.loaded.store(true, Ordering::Relaxed); - } - } - - // FIXME: is it possible that the inode still in childrens map? - } - - pub fn do_statvfs(&self, ctx: &Context, inode: Inode) -> Result { - if let Some(v) = self.get_first_layer() { - if let Ok(sfs) = v.statfs(ctx, inode) { - return Ok(sfs); - } - } - - // otherwise stat on mountpoint - let mut sfs = MaybeUninit::::zeroed(); - let cpath = CString::new(self.config.mountpoint.as_str()).expect("invalid path"); - let path = cpath.as_c_str().as_ptr(); - - match unsafe { libc::statvfs64(path, sfs.as_mut_ptr()) } { - 0 => { - let mut sfs = unsafe { sfs.assume_init() }; - sfs.f_namemax -= WHITEOUT_MAX_LEN; - - Ok(sfs) - }, - - _ => { - Err(Error::last_os_error()) - }, - } - } - - pub fn get_fs_namemax(&self, ctx: &Context) -> u64 { - match self.do_statvfs(ctx, FUSE_ROOT_ID) { - Ok(sfs) => sfs.f_namemax, - Err(_) => 255 - WHITEOUT_MAX_LEN, - } - } - - pub fn do_readdir(&self, ctx: &Context, handle: u64, size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry, Entry) -> Result) -> Result<()> { - if size == 0 { - return Ok(()); - } - - // FIXME: if offset == 0, need to reconstruct dir for this handle - // if offset == 0 { - // reconstruct directory - // } - - // lookup the directory - if let Some(dir) = self.handles.lock().unwrap().get(&handle) { - let mut len: usize = 0; - debug!("overlay dir: {}, off: {}, size: {}", dir.node.path.as_str(), offset, size); - - let childrens = - if let Some(ref cs) = dir.childrens { - cs - } else { - return Err(Error::new(ErrorKind::Other, "no child!")); - }; - - if offset >= childrens.len() as u64 { - return Ok(()); - } - - let mut index: u64 = 0; - for child in childrens { - if index >= offset { - let name = match index { - 0 => ".", - 1 => "..", - _ => child.name.as_str(), - }; - - // make struct DireEntry and Entry - 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 = Entry { - inode: child.inode, - generation: 0, - attr: st, - attr_flags: 0, - attr_timeout: self.config.attr_timeout, - entry_timeout: self.config.entry_timeout, - }; - - match add_entry(dir_entry, entry) { - Ok(0) => break, - Ok(l) => { - len += l; - if len as u32 >= size { - // no more space, stop here - return Ok(()); - } - }, - - Err(e) => { - // when the buffer is still empty, return error, otherwise return the entry already added - if len == 0 { - return Err(e); - } else { - return Ok(()); - } - }, - } - } - - index += 1; - } - } - - Ok(()) - } - - pub fn node_in_upper_layer(&self, node: Arc) -> Result { - Ok(node.in_upper_layer()) - } - - pub fn create_node_directory(&self, ctx: &Context, node: Arc) -> Result<()> { - // recursively going up and update hashmaps - if self.node_in_upper_layer(Arc::clone(&node))? { - return Ok(()); - } - - // not in upper layer, check parent - let pnode = if let Some(n) = node.parent.lock().unwrap().upgrade() { - Arc::clone(&n) - } else { - return Err(Error::new(ErrorKind::Other, "no parent?")); - }; - - if self.node_in_upper_layer(Arc::clone(&pnode))? { - // create directory here - let upper = Arc::clone(pnode.upper_inode.lock().unwrap().as_ref().unwrap()); - let layer = Arc::clone(upper.layer.as_ref().unwrap()); - let cname = CString::new(node.name.as_str()).expect("invalid c string"); - let st = node.stat64(ctx)?; - let entry = layer.mkdir(ctx, upper.inode.load(Ordering::Relaxed), cname.as_c_str(), st.st_mode, 0)?; - - // update node's first_layer - let real_inode = Arc::new(RealInode { - layer: Some(Arc::clone(&layer)), - inode: AtomicU64::new(entry.inode), - whiteout: AtomicBool::new(false), - opaque: AtomicBool::new(false), - hidden: AtomicBool::new(false), - invalid: AtomicBool::new(false), - }); - - // what about st_ino/mode/dev.. - // FIXME: update st_ino/mode/dev, or query it from layer - // on fly? - *node.first_inode.lock().unwrap() = Arc::clone(&real_inode); - *node.upper_inode.lock().unwrap() = Some(Arc::clone(&real_inode)); - - return Ok(()); - - } else { - return self.create_node_directory(ctx, Arc::clone(&pnode)); - } - } - - pub fn copy_symlink_up(&self, ctx: &Context, node: Arc) -> Result> { - if self.node_in_upper_layer(Arc::clone(&node))? { - return Ok(node); - } - - let pnode = 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 empty_name =CString::new("").expect("invalid c string"); - let upper = self.get_upper_layer().unwrap(); - let pnode = self.lookup_node(ctx, pnode.inode, empty_name.as_c_str())?; - - assert!(pnode.in_upper_layer()); - assert!(st.st_mode & libc::S_IFMT == libc::S_IFLNK); - let parent_real_inode = Arc::clone(pnode.upper_inode.lock().unwrap().as_ref().unwrap()); - let cname = CString::new(node.name.as_str()).expect("invalid c string"); - let node = self.lookup_node(ctx, pnode.inode, cname.as_c_str())?; - let rinode = Arc::clone(&node.first_inode.lock().unwrap()); - let layer = Arc::clone(rinode.layer.as_ref().unwrap()); - - // symlink - // first inode, upper most layer inode - let path = layer.readlink(ctx, rinode.inode.load(Ordering::Relaxed))?; - let cpath = unsafe { CString::from_vec_unchecked(path) }; - let entry = upper.symlink(ctx, cpath.as_c_str(), parent_real_inode.inode.load(Ordering::Relaxed), cname.as_c_str())?; - - let real_inode = Arc::new(RealInode { - layer: Some(Arc::clone(&upper)), - inode: AtomicU64::new(entry.inode), - whiteout: AtomicBool::new(false), - opaque: AtomicBool::new(false), - hidden: AtomicBool::new(false), - invalid: AtomicBool::new(false), - }); - - // update first_inode and upper_inode - *node.upper_inode.lock().unwrap() = Some(Arc::clone(&real_inode)); - *node.first_inode.lock().unwrap() = Arc::clone(&real_inode); - - return Ok(Arc::clone(&node)); - } - - pub fn copy_regfile_up(&self, ctx: &Context, node: Arc) -> Result> { - if self.node_in_upper_layer(Arc::clone(&node))? { - return Ok(node); - } - - let pnode = 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 empty_name =CString::new("").expect("invalid c string"); - let upper = self.get_upper_layer().unwrap(); - let pnode = self.lookup_node(ctx, pnode.inode, empty_name.as_c_str())?; - - assert!(pnode.in_upper_layer()); - assert!(st.st_mode & libc::S_IFMT != libc::S_IFLNK && st.st_mode & libc::S_IFMT != libc::S_IFDIR); - - let parent_real_inode = Arc::clone(pnode.upper_inode.lock().unwrap().as_ref().unwrap()); - let cname = CString::new(node.name.as_str()).expect("invalid c string"); - let node = self.lookup_node(ctx, pnode.inode, cname.as_c_str())?; - let rinode = Arc::clone(&node.first_inode.lock().unwrap()); - let layer = Arc::clone(rinode.layer.as_ref().unwrap()); - - // create the file in upper layer using information from lower layer - - let args = CreateIn { - flags: 0, - mode: st.st_mode, - umask: 0, - fuse_flags: 0, - }; - - let (entry, h, o) = upper.create(ctx, parent_real_inode.inode.load(Ordering::Relaxed), cname.as_c_str(), args)?; - - let real_inode = Arc::new(RealInode { - layer: Some(Arc::clone(&upper)), - inode: AtomicU64::new(entry.inode), - whiteout: AtomicBool::new(false), - opaque: AtomicBool::new(false), - hidden: AtomicBool::new(false), - invalid: AtomicBool::new(false), - }); - - if h.is_none() { - error!("no handle!!!"); - return Err(Error::new(ErrorKind::Other, "non handle!")); - } - - let dst_handle = h.unwrap(); - - let (h, o) = layer.open(ctx, rinode.inode.load(Ordering::Relaxed), libc::O_RDONLY as u32, 0)?; - - if h.is_none() { - error!("no handle!!!"); - return Err(Error::new(ErrorKind::Other, "non handle!")); - } - - let src_handle = h.unwrap(); - - // copy... - // source: layer, rinode.inode, src_handle - // dst: upper, real_inode.inode, dst_handle - - // need to impl ZeroCopyReader/ZeroCopyWriter, somehow like a pipe.. - // stupid: to create a temp file for now.. - // FIXME: need to copy xattr, futimes, set origin.TODO - - let mut template = CString::new("/tmp/fuse-overlay-XXXXXX").expect("invalid c string"); - let mut template = template.into_raw(); - let suffixlen = 10; - let flags = libc::O_RDWR | libc::O_CREAT; - let fd = unsafe { libc::mkostemp(template, flags) }; - - if fd < 0 { - return Err(Error::last_os_error()); - } - - let mut file = unsafe { File::from_raw_fd(fd) }; - let mut offset: usize = 0; - let size = 4 * 1024 * 1024; - loop { - let ret = layer.read(ctx, rinode.inode.load(Ordering::Relaxed), src_handle, &mut file, size, offset as u64, None, 0)?; - if ret == 0 { - break; - } - - offset += ret; - } - - file.seek(SeekFrom::Start(0))?; - offset = 0; - loop { - let ret = upper.write(ctx, entry.inode, dst_handle, &mut file, size, offset as u64, None, false, 0, 0)?; - if ret == 0 { - break; - } - - offset += ret; - } - - drop(file); - unsafe { libc::unlink(template); } - - // close handles - layer.release(ctx, rinode.inode.load(Ordering::Relaxed), 0, src_handle, true, true, None)?; - upper.release(ctx, entry.inode, 0, dst_handle, true, true, None)?; - - // update upper_inode and first_inode - *node.upper_inode.lock().unwrap() = Some(Arc::clone(&real_inode)); - *node.first_inode.lock().unwrap() = Arc::clone(&real_inode); - - return Ok(Arc::clone(&node)); - } - - pub fn copy_node_up(&self, ctx: &Context, node: Arc) -> Result> { - if self.node_in_upper_layer(Arc::clone(&node))? { - return Ok(node); - } - // not in upper, copy it up - let pnode = if let Some(ref n) = node.parent.lock().unwrap().upgrade() { - Arc::clone(n) - } else { - return Err(Error::new(ErrorKind::Other, "no parent?")); - }; - - self.create_node_directory(ctx, Arc::clone(&pnode))?; - // parent prepared - let st = node.stat64(ctx)?; - - // directory - if st.st_mode & libc::S_IFMT == libc::S_IFDIR { - self.create_node_directory(ctx, Arc::clone(&node))?; - return Ok(Arc::clone(&node)); - } - - // other kind of files - - // symlink - if st.st_mode * libc::S_IFMT == libc::S_IFLNK { - return self.copy_symlink_up(ctx, Arc::clone(&node)); - } - - // reg file - // need to use work directory and then rename file to - // final destination for atomic reasons.. not deal with it for now, - // use stupid copy at present. FIXME: - // this need a lot of work here, ntimes, xattr, etc - self.copy_regfile_up(ctx, Arc::clone(&node)) - } - - pub fn is_upper_layer(&self, l: Arc) -> bool { - l.is_upper() - } - - pub fn do_rm(&self, ctx: &Context, parent: u64, name: &CStr, dir: bool) -> Result<()> { - let upper = if let Some(ref v) = self.upper_layer.as_ref() { - Arc::clone(v) - } else { - return Err(Error::from_raw_os_error(libc::EROFS)); - }; - - let pnode = self.lookup_node(ctx, parent, CString::new("").expect("invalid path!").as_c_str())?; - if pnode.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - let node = self.lookup_node(ctx, parent, name)?; - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - if dir { - self.reload_directory(ctx, Arc::clone(&node))?; - let (count, whiteouts, opaque) = node.count_entries_and_whiteout(ctx)?; - trace!("files: {}, whiteouts: {} opaque: {}\n", count, whiteouts, opaque); - if count > 0 { - return Err(Error::from_raw_os_error(libc::ENOTEMPTY)); - } - - // need to delete whiteouts? - if whiteouts + opaque > 0 { - if 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; - } - - // parent opaqued - let real_pnode = Arc::clone(pnode.upper_inode.lock().unwrap().as_ref().unwrap()); - let real_parent_inode = real_pnode.inode.load(Ordering::Relaxed); - if real_pnode.opaque.load(Ordering::Relaxed) { - need_whiteout = false; - } - - let layer = Arc::clone(real_pnode.layer.as_ref().unwrap()); - - if node.in_upper_layer() { - if dir { - layer.rmdir(ctx, real_parent_inode, name)?; - } else { - layer.unlink(ctx, real_parent_inode, name)?; - } - } - - trace!("toggling children and inodes hash\n"); - - { - pnode.childrens.lock().unwrap().remove(node.name.as_str()); - self.inodes.lock().unwrap().remove(&node.inode); - } - - let sname = name.to_string_lossy().into_owned().to_owned(); - - if need_whiteout { - trace!("do_rm: creating whiteout\n"); - layer.create_whiteout(ctx, real_parent_inode, sname.as_str())?; - pnode.loaded.store(false, Ordering::Relaxed); - // readd whiteout node - self.lookup_node(ctx, parent, name)?; - pnode.loaded.store(true, Ordering::Relaxed); - } - - Ok(()) - } - - pub fn node_upper_layer_only(&self, node: Arc) -> bool { - node.upper_layer_only() - } - - pub fn empty_node_directory(&self, ctx: &Context, node: Arc) -> Result<()> { - let st = node.stat64(ctx)?; - if st.st_mode & libc::S_IFMT != libc::S_IFDIR { - return Err(Error::from_raw_os_error(libc::ENOTDIR)); - } - - self.reload_directory(ctx, Arc::clone(&node))?; - if !node.in_upper_layer() { - return Ok(()); - } - - // find the real inode - let real_node = Arc::clone(node.upper_inode.lock().unwrap().as_ref().unwrap()); - let layer = Arc::clone(real_node.layer.as_ref().unwrap()); - let real_inode = real_node.inode.load(Ordering::Relaxed); - - // delete opaque - let copaque = CString::new(OPAQUE_WHITEOUT).expect("invalid c string"); - let (opaque, _ino) = layer.is_opaque_whiteout(ctx, real_inode)?; - if opaque { - if let Some(v) = layer.lookup_ignore_enoent(ctx, real_inode, OPAQUE_WHITEOUT)? { - layer.unlink(ctx, real_inode, copaque.as_c_str())?; - } - } - - let iter = { - let mut all = Vec::new(); - for (_, v) in node.childrens.lock().unwrap().iter() { - all.push(Arc::clone(v)); - } - - all - }; - - for child in &iter { - if child.in_upper_layer() { - if child.whiteout.load(Ordering::Relaxed) { - layer.delete_whiteout(ctx, real_inode, child.name.as_str())? - } else { - let s = child.stat64(ctx)?; - let cname = CString::new(child.name.as_str()).expect("invalid c string"); - if s.st_mode & libc::S_IFMT == libc::S_IFDIR { - let (count, whiteouts, opaque) = child.count_entries_and_whiteout(ctx)?; - if count + whiteouts + opaque > 0 { - self.empty_node_directory(ctx, Arc::clone(&child))?; - } - - layer.rmdir(ctx, real_inode, cname.as_c_str())? - } else { - layer.unlink(ctx, real_inode, cname.as_c_str())?; - } - } - - { - // delete the child - self.inodes.lock().unwrap().remove(&child.inode); - node.childrens.lock().unwrap().remove(child.name.as_str()); - } - } - - } - - Ok(()) - } - - pub fn do_open(&self, inode: Inode, flags: u32, fuse_flags: u32) -> Result<(Option, OpenOptions)> { - Err(Error::from(ErrorKind::Unsupported)) - } - - pub fn delete_whiteout_node(&self, ctx: &Context, node: Arc) -> Result<()> { - if !node.whiteout.load(Ordering::Relaxed) { - return Ok(()); - } - - if !self.node_in_upper_layer(Arc::clone(&node))? { - return Ok(()); - } - - let name = CString::new(node.name.as_str()).expect("invalid c string"); - let (layer, real_parent, pnode) = { - let pnode = if let Some(ref n) = node.parent.lock().unwrap().upgrade() { - Arc::clone(n) - } else { - return Err(Error::new(ErrorKind::Other, "no parent")); - }; - - let first_inode = pnode.first_inode.lock().unwrap(); - - (Arc::clone(first_inode.layer.as_ref().unwrap()), first_inode.inode.load(Ordering::Relaxed), Arc::clone(&pnode)) - }; - - // delete white out and update hash - layer.delete_whiteout(ctx, real_parent, node.name.as_str())?; - self.inodes.lock().unwrap().remove(&node.inode); - pnode.childrens.lock().unwrap().remove(node.name.as_str()); - - Ok(()) - } - - pub fn find_real_info_from_handle(&self, ctx: &Context, handle: Handle) -> Result<(Arc, Inode, Handle)> { - if let Some(h) = self.handles.lock().unwrap().get(&handle) { - if let Some( ref rhd) = h.real_handle { - let real_handle = rhd.handle.load(Ordering::Relaxed); - let ri = Arc::clone(&rhd.real_inode); - let layer = Arc::clone(ri.layer.as_ref().unwrap()); - let real_inode = ri.inode.load(Ordering::Relaxed); - return Ok((layer, real_inode, real_handle)) - } - } - - Err(Error::from_raw_os_error(libc::ENOENT)) - } - - pub fn find_real_inode(&self, ctx: &Context, inode: Inode) -> Result<(Arc, Inode)> { - if let Some(n) = self.inodes.lock().unwrap().get(&inode) { - let first = n.first_inode.lock().unwrap(); - let layer = Arc::clone(first.layer.as_ref().unwrap()); - let real_inode = first.inode.load(Ordering::Relaxed); - - return Ok((layer, real_inode)) - } - - Err(Error::from_raw_os_error(libc::ENOENT)) - } -} - -impl BackendFileSystem for OverlayFs { - fn mount(&self) -> Result<(Entry, u64)> { - if let Some(ref root) = self.root.as_ref() { - let ctx = Context::default(); - Ok((Entry { - inode: root.inode, - generation: 0, - attr: root.stat64(&ctx)?, - attr_flags: 0, - attr_timeout: self.config.attr_timeout, - entry_timeout: self.config.entry_timeout, - }, VFS_MAX_INO)) - } else { - Err(Error::new(ErrorKind::Other, "fs not inited")) - } - } - - fn as_any(&self) -> &dyn Any { - self - } -} - -impl FileSystem for OverlayFs { - type Inode = Inode; - type Handle = Handle; - - fn init(&self, capable: FsOptions) -> Result { - // use vfs' negotiated capability if imported - // other wise, do our own negotiation - let mut opts = FsOptions::DO_READDIRPLUS | FsOptions::READDIRPLUS_AUTO; - - if self.config.do_import { - self.import()?; - } - - if (!self.config.do_import || self.config.writeback) && capable.contains(FsOptions::WRITEBACK_CACHE) { - opts |= FsOptions::WRITEBACK_CACHE; - self.writeback.store(true, Ordering::Relaxed); - } - - if (!self.config.do_import || self.config.no_open) && capable.contains(FsOptions::ZERO_MESSAGE_OPEN) { - opts |= FsOptions::ZERO_MESSAGE_OPEN; - opts.remove(FsOptions::ATOMIC_O_TRUNC); - self.no_open.store(true, Ordering::Relaxed); - } - - if (!self.config.do_import || self.config.no_opendir) && capable.contains(FsOptions::ZERO_MESSAGE_OPENDIR) { - opts |= FsOptions::ZERO_MESSAGE_OPENDIR; - self.no_opendir.store(true, Ordering::Relaxed); - } - - if (!self.config.do_import || self.config.killpriv_v2) && capable.contains(FsOptions::HANDLE_KILLPRIV_V2) { - opts |= FsOptions::HANDLE_KILLPRIV_V2; - self.killpriv_v2.store(true, Ordering::Relaxed); - } - - if self.config.perfile_dax && capable.contains(FsOptions::PERFILE_DAX) { - opts |= FsOptions::PERFILE_DAX; - self.perfile_dax.store(true, Ordering::Relaxed); - } - - Ok(opts) - } - - fn destroy(&self) { - } - - fn statfs(&self, ctx: &Context, inode: Inode) -> Result { - self.do_statvfs(ctx, inode) - } - - fn lookup(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result { - let tmp =name.to_string_lossy().into_owned().to_owned(); - trace!("LOOKUP: parent: {}, name: {}\n", parent, tmp); - let node = self.lookup_node(ctx, parent, name)?; - - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - let pnode = if let Some(v) = self.get_node_from_inode(parent) { - v - } else { - return Err(Error::from_raw_os_error(libc::EINVAL)); - }; - - let ppath = String::from(pnode.path.as_str()); - let sname = name.to_string_lossy().into_owned().to_owned(); - let st = node.stat64(ctx)?; - - // load this directory here - if st.st_mode & libc::S_IFMT == libc::S_IFDIR { - self.load_directory(ctx, Arc::clone(&node))?; - node.loaded.store(true, Ordering::Relaxed); - } - - // FIXME: can forget happen between found and increase reference counter? - - *node.lookups.lock().unwrap() += 1; - - Ok(Entry{ - inode: node.inode as u64, - generation: 0, - attr: st,//libc::stat64 - attr_flags: 0, - attr_timeout: self.config.attr_timeout, - entry_timeout: self.config.entry_timeout, - }) - } - - fn forget(&self, ctx: &Context, inode: Inode, count: u64) { - self.forget_one(inode, count) - } - - fn batch_forget(&self, ctx: &Context, requests: Vec<(Inode, u64)>) { - for (inode, count) in requests { - self.forget_one(inode, count); - } - } - - fn opendir(&self, ctx: &Context, inode: Inode, flags: u32) -> Result<(Option, OpenOptions)> { - let mut opts = OpenOptions::empty(); - - match self.config.cache_policy { - CachePolicy::Always => { - opts |= OpenOptions::KEEP_CACHE; - }, - - _ => { - }, - } - - // lookup node - let node = self.lookup_node(ctx, inode, CString::new(".").expect("invalid path!").as_c_str())?; - - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::new(ErrorKind::InvalidInput, "invalid inode number")); - } - - let st = node.stat64(ctx)?; - if st.st_mode & libc::S_IFDIR == 0 { - return Err(Error::from_raw_os_error(libc::ENOTDIR)); - } - - let handle = self.next_handle.fetch_add(1, Ordering::Relaxed); - - // reload directory? - self.reload_directory(ctx, Arc::clone(&node))?; - - let mut cs = Vec::new(); - //add myself - cs.push(Arc::clone(&node)); - - //add parent - if let Some(p) = node.parent.lock().unwrap().upgrade() { - cs.push(p); - } else { - cs.push(Arc::clone(self.root.as_ref().unwrap())); - }; - - for (_, child) in node.childrens.lock().unwrap().iter() { - // skip whiteout node - if child.whiteout.load(Ordering::Relaxed) || child.hidden.load(Ordering::Relaxed) { - continue; - } - // *child.lookups.lock().unwrap() += 1; - cs.push(Arc::clone(child)); - } - - for c in cs.iter() { - *c.lookups.lock().unwrap() += 1; - } - - *node.lookups.lock().unwrap() += 1; - - self.handles.lock().unwrap().insert(handle, HandleData{ - node: Arc::clone(&node), - childrens: Some(cs), - offset: 0, - real_handle: None, - }); - - Ok((Some(handle), opts)) - } - - fn releasedir(&self, ctx: &Context, inode: Inode, flags: u32, handle: Handle) -> Result<()> { - trace!("RELEASEDIR: inode: {}, handle: {}\n", inode, handle); - { - if let Some(v) = self.handles.lock().unwrap().get(&handle) { - for child in v.childrens.as_ref().unwrap() { - self.forget_one(child.inode, 1); - } - - self.forget_one(v.node.inode, 1); - } - } - - trace!("RELEASEDIR: returning"); - - self.handles.lock().unwrap().remove(&handle); - - Ok(()) - } - - // for mkdir or create file - // 1. lookup name, if exists and not whiteout, return EEXIST - // 2. not exists and no whiteout, copy up parent node, ususally a mkdir on upper layer would do the work - // 3. find whiteout, if whiteout in upper layer, shoudl set opaque. if in lower layer, just mkdir? - fn mkdir(&self, ctx: &Context, parent: Inode, name: &CStr, mode: u32, umask: u32) -> Result { - let mut delete_whiteout: bool = false; - let mut has_whiteout: bool = false; - let mut upper_layer_only: bool = false; - let mut opaque = false; - let mut node: Arc = Arc::new(OverlayInode::default()); - - let sname = name.to_string_lossy().into_owned().to_owned(); - if let Some(n) = self.lookup_node_ignore_enoent(ctx, parent, name)? { - if !n.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::EEXIST)); - } - - node = Arc::clone(&n); - has_whiteout = true; - } - - let upper = if let Some(l) = self.get_upper_layer() { - l - } else { - return Err(Error::from_raw_os_error(libc::EROFS)); - // return Err(Error::new(ErrorKind::ReadOnlyFilesystem, "readonly filesystem!")); - }; - - if has_whiteout { - if node.in_upper_layer() { - // whiteout in upper layer, other lower layers are readonly, don't try to delete it - delete_whiteout = true; - } - - if node.upper_layer_only() { - upper_layer_only = true; - } - } - - let pnode = self.lookup_node(ctx, parent, CString::new("").expect("invalid file name").as_c_str())?; - // actual work to copy pnode up.. - let pnode = self.copy_node_up(ctx, Arc::clone(&pnode))?; - - assert!(pnode.in_upper_layer()); - let real_pnode = Arc::clone(pnode.upper_inode.lock().unwrap().as_ref().unwrap()); - - let real_parent_inode = real_pnode.inode.load(Ordering::Relaxed); - - if delete_whiteout { - upper.delete_whiteout(ctx, real_parent_inode, sname.as_str()); - } - - // create dir in upper layer - let entry = upper.mkdir(ctx, real_parent_inode, name, mode, umask)?; - - if !upper_layer_only { - upper.create_opaque_whiteout(ctx, entry.inode)?; - opaque = true; - } - - pnode.loaded.store(false, Ordering::Relaxed); - // remove whiteout node from child and inode hash - // FIXME: maybe a reload from start better - if has_whiteout { - pnode.childrens.lock().unwrap().remove(sname.as_str()); - self.inodes.lock().unwrap().remove(&node.inode); - } - - let node = self.lookup_node(ctx, parent, name)?; - - pnode.loaded.store(true, Ordering::Relaxed); - - Ok(Entry { - inode: node.inode, - generation: 0, - attr: node.stat64(ctx)?, - attr_flags: 0, - attr_timeout: self.config.attr_timeout, - entry_timeout: self.config.entry_timeout, - }) - } - - fn rmdir(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result<()> { - self.do_rm(ctx, parent, name, true) - } - - fn readdir(&self, ctx: &Context, inode: Inode, handle: Handle, size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry) -> Result) -> Result<()> { - self.do_readdir(ctx, handle, size, offset, &mut |dir_entry, _entry| -> Result { - add_entry(dir_entry) - }) - } - - fn readdirplus(&self, ctx: &Context, inode: Inode, handle: Handle, size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry, Entry) -> Result) -> Result<()> { - self.do_readdir(ctx, handle, size, offset, &mut |dir_entry, entry| -> Result { - add_entry(dir_entry, entry) - }) - } - - fn open(&self, ctx: &Context, inode: Inode, flags: u32, fuse_flags: u32) -> Result<(Option, OpenOptions)> { - // open assume file always exist - - let readonly: bool = flags & (libc::O_APPEND | libc::O_CREAT | libc::O_TRUNC | libc::O_RDWR | libc::O_WRONLY) as u32== 0; - - trace!("OPEN: inode: {}, readonly: {}", inode, readonly); - - // toggle flags - let mut flags: i32 = flags as i32; - - flags |= libc::O_NOFOLLOW; - flags &= !libc::O_DIRECT; - if self.config.writeback { - if flags & libc::O_ACCMODE == libc::O_WRONLY { - flags &= !libc::O_ACCMODE; - flags |= libc::O_RDWR; - } - - if flags & libc::O_APPEND != 0 { - flags &= !libc::O_APPEND; - } - } - - // lookup node - let node = self.lookup_node(ctx, inode, CString::new("").expect("invalid c string").as_c_str())?; - - // whiteout node - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - if !readonly { - // copy up to upper layer - self.copy_node_up(ctx, Arc::clone(&node))?; - } - - // assign a handle in overlayfs and open it - let (l, h, o) = node.open(ctx, flags as u32, fuse_flags)?; - if let Some(handle) = h { - let hd = self.next_handle.fetch_add(1, Ordering::Relaxed); - let handle_data = HandleData { - node: Arc::clone(&node), - childrens: None, - offset: 0, - real_handle: Some(RealHandle { - real_inode: Arc::clone(&node.first_inode.lock().unwrap()), - handle: AtomicU64::new(handle), - invalid: AtomicBool::new(false), - }), - }; - - self.handles.lock().unwrap().insert(hd, handle_data); - - let mut opts = OpenOptions::empty(); - match self.config.cache_policy { - CachePolicy::Never => opts |= OpenOptions::DIRECT_IO, - CachePolicy::Always => opts |= OpenOptions::KEEP_CACHE, - _ => {}, - } - - trace!("OPEN: returning handle: {}", hd); - - return Ok((Some(hd), opts)); - } - - Err(Error::from_raw_os_error(libc::ENOENT)) - } - - fn release(&self, ctx: &Context, inode: Inode, flags: u32, handle: Handle, flush: bool, flock_release: bool, lock_owner: Option) -> Result<()> { - if let Some(hd) = self.handles.lock().unwrap().get(&handle) { - let rh = if let Some(ref h) = hd.real_handle { - h - } else { - return Err(Error::new(ErrorKind::Other, "no handle")); - }; - let real_handle = rh.handle.load(Ordering::Relaxed); - let ri = Arc::clone(&rh.real_inode); - let real_inode = ri.inode.load(Ordering::Relaxed); - let l = Arc::clone(&ri.layer.as_ref().unwrap()); - l.release(ctx, real_inode, flags, real_handle, flush, flock_release, lock_owner)?; - } - - self.handles.lock().unwrap().remove(&handle); - - Ok(()) - } - - fn create(&self, ctx: &Context, parent: Inode, name: &CStr, args: CreateIn) -> Result<(Entry, Option, OpenOptions)> { - let mut is_whiteout = false; - let node = self.lookup_node_ignore_enoent(ctx, parent, name)?; - let sname = name.to_string_lossy().into_owned().to_owned(); - - let mut hargs = args; - - let mut flags: i32 = args.flags as i32; - - flags |= libc::O_NOFOLLOW; - flags &= !libc::O_DIRECT; - if self.config.writeback { - if flags & libc::O_ACCMODE == libc::O_WRONLY { - flags &= !libc::O_ACCMODE; - flags |= libc::O_RDWR; - } - - if flags & libc::O_APPEND != 0 { - flags &= !libc::O_APPEND; - } - } - - hargs.flags = flags as u32; - - - if let Some(ref n) = node { - if !n.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::EEXIST)); - } else { - is_whiteout = true; - } - } - - // no entry or whiteout - let pnode = self.lookup_node(ctx, parent, CString::new("").expect("invalid c string").as_c_str())?; - if pnode.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - let pnode = self.copy_node_up(ctx, Arc::clone(&pnode))?; - - assert!(pnode.upper_inode.lock().unwrap().is_some()); - - let real_parent_inode = pnode.first_inode.lock().unwrap().inode.load(Ordering::Relaxed); - - // need to delete whiteout? - if is_whiteout { - let node = Arc::clone(node.as_ref().unwrap()); - let first_inode = Arc::clone(&node.first_inode.lock().unwrap()); - let first_layer = first_inode.layer.as_ref().unwrap(); - if node.in_upper_layer() { - // whiteout in upper layer, need to delete - first_layer.delete_whiteout(ctx, real_parent_inode, sname.as_str())?; - } - - // delete inode from inodes and childrens - self.inodes.lock().unwrap().remove(&node.inode); - pnode.childrens.lock().unwrap().remove(sname.as_str()); - } - - // create file in upper layer - if let Some(ref upper_layer) = self.upper_layer.as_ref() { - let (entry, h, o) = upper_layer.create(ctx, real_parent_inode, name, hargs)?; - - // record inode, handle - // lookup will insert inode into children and inodes hash - //let real_inode = Arc::new(RealInode { - // layer: Arc::clone(&upper_layer), - // inode: entry.inode, - // whiteout: AtomicBool::new(false), - // opaque: AtomicBool::new(false), - // hidden: AtomicBool::new(false), - // invalid: AtomicBool::new(false), - //}); - - pnode.loaded.store(false, Ordering::Relaxed); - let node = self.lookup_node(ctx, parent, name)?; - pnode.loaded.store(true, Ordering::Relaxed); - - let real_inode = Arc::clone(node.upper_inode.lock().unwrap().as_ref().unwrap()); - - let final_handle = - if let Some(hd) = h { - let handle = self.next_handle.fetch_add(1, Ordering::Relaxed); - let handle_data = HandleData { - node: Arc::clone(&node), - childrens: None, - offset: 0, - real_handle: Some(RealHandle { - real_inode: Arc::clone(&real_inode), - handle: AtomicU64::new(hd), - invalid: AtomicBool::new(false), - }), - }; - self.handles.lock().unwrap().insert(handle, handle_data); - Some(handle) - } else { - None - }; - - // return data - let entry = Entry { - inode: node.inode, - generation: 0, - attr: node.stat64(ctx)?, - attr_flags: 0, - attr_timeout: self.config.attr_timeout, - entry_timeout: self.config.entry_timeout, - }; - - let mut opts = OpenOptions::empty(); - match self.config.cache_policy { - CachePolicy::Never => opts |= OpenOptions::DIRECT_IO, - CachePolicy::Always => opts |= OpenOptions::KEEP_CACHE, - _ => {}, - } - - return Ok((entry, final_handle, opts)); - - } else { - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - Err(Error::new(ErrorKind::Other, "Unknown error")) - } - - fn unlink(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result<()> { - self.do_rm(ctx, parent, name, false) - } - - fn read(&self, ctx: &Context, inode: Inode, handle: Handle, w: &mut dyn ZeroCopyWriter, size: u32, offset: u64, lock_owner: Option, flags: u32) -> Result { - if let Some(v) = self.handles.lock().unwrap().get(&handle) { - if let Some(ref hd) = v.real_handle { - let real_handle = hd.handle.load(Ordering::Relaxed); - let ri = Arc::clone(&hd.real_inode); - let (real_inode, layer) = (ri.inode.load(Ordering::Relaxed), Arc::clone(ri.layer.as_ref().unwrap())); - - return layer.read(ctx, real_inode, real_handle, w, size, offset, lock_owner, flags); - } - } - - Err(Error::from_raw_os_error(libc::ENOENT)) - } - - fn write(&self, ctx: &Context, inode: Inode, handle: Handle, r: &mut dyn ZeroCopyReader, size: u32, offset: u64, lock_owner: Option, delayed_write: bool, flags: u32, fuse_flags: u32) -> Result { - if let Some(v) = self.handles.lock().unwrap().get(&handle) { - if let Some(ref hd) = v.real_handle { - let real_handle = hd.handle.load(Ordering::Relaxed); - let ri = Arc::clone(&hd.real_inode); - let (real_inode, layer) = (ri.inode.load(Ordering::Relaxed), Arc::clone(ri.layer.as_ref().unwrap())); - - return layer.write(ctx, real_inode, real_handle, r, size, offset, lock_owner, delayed_write, flags, fuse_flags); - // remove whiteout node from child and inode hash - } - } - - Err(Error::from_raw_os_error(libc::ENOENT)) - } - - fn getattr(&self, ctx: &Context, inode: Inode, handle: Option) -> Result<(libc::stat64, Duration)> { - trace!("GETATTR: inode: {}\n", inode); - if let Some(h) = handle { - if let Some(hd) = self.handles.lock().unwrap().get(&h) { - if let Some(ref v) = hd.real_handle { - let ri = Arc::clone(&v.real_inode); - let layer = Arc::clone(ri.layer.as_ref().unwrap()); - let real_inode = ri.inode.load(Ordering::Relaxed); - let real_handle = v.handle.load(Ordering::Relaxed); - let (st, _d) = layer.getattr(ctx, real_inode, Some(real_handle))?; - return Ok((st, self.config.attr_timeout)); - } - } - } else { - let node = self.lookup_node(ctx, inode, CString::new("").expect("invalid c string").as_c_str())?; - let rl = Arc::clone(&node.first_inode.lock().unwrap()); - if let Some(ref v) = rl.layer { - let layer = Arc::clone(v); - let real_inode = rl.inode.load(Ordering::Relaxed); - - let (st, _d) = layer.getattr(ctx, real_inode, None)?; - return Ok((st, self.config.attr_timeout)); - } - } - - Err(Error::from_raw_os_error(libc::ENOENT)) - } - - fn setattr(&self, ctx: &Context, inode: Inode, attr: libc::stat64, handle: Option, valid: SetattrValid) -> Result<(libc::stat64, Duration)> { - // find out real inode and real handle, if the first - // layer id not upper layer copy it up - - // deal with handle first - if let Some(h) = handle { - if let Some(hd) = self.handles.lock().unwrap().get(&h) { - if let Some(ref rhd) = hd.real_handle { - let ri = Arc::clone(&rhd.real_inode); - let layer = Arc::clone(ri.layer.as_ref().unwrap()); - let real_inode = ri.inode.load(Ordering::Relaxed); - let real_handle = rhd.handle.load(Ordering::Relaxed); - // handle opened in upper layer - if self.is_upper_layer(Arc::clone(&layer)) { - let (st, _d) = layer.setattr(ctx, real_inode, attr, Some(real_handle), valid)?; - - return Ok((st, self.config.attr_timeout)); - } - } - } - } - - let node = self.lookup_node(ctx, inode, CString::new("").expect("invalid c string").as_c_str())?; - - //layer is upper layer - let node = - if !self.node_in_upper_layer(Arc::clone(&node))? { - self.copy_node_up(ctx, Arc::clone(&node))? - } else { - Arc::clone(&node) - }; - - let v = Arc::clone(&node.first_inode.lock().unwrap()); - let (layer, real_inode) = (Arc::clone(v.layer.as_ref().unwrap()), v.inode.load(Ordering::Relaxed)); - - let (st, _d) = layer.setattr(ctx, real_inode, attr, None, valid)?; - Ok((st, self.config.attr_timeout)) - } - - fn rename(&self, ctx: &Context, olddir: Inode, odlname: &CStr, newdir: Inode, newname: &CStr, flags: u32) -> Result<()> { - // complex, implement it later - Err(Error::from_raw_os_error(libc::EXDEV)) - } - - fn mknod(&self, ctx: &Context, parent: Inode, name: &CStr, mode: u32, rdev: u32, umask: u32) -> Result { - let mut is_whiteout = false; - let node = self.lookup_node_ignore_enoent(ctx, parent, name)?; - let sname = name.to_string_lossy().into_owned().to_owned(); - - if let Some(ref n) = node { - if !n.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::EEXIST)); - } else { - is_whiteout = true; - } - } - - // no entry or whiteout - let pnode = self.lookup_node(ctx, parent, CString::new("").expect("invalid c string").as_c_str())?; - if pnode.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - let pnode = self.copy_node_up(ctx, Arc::clone(&pnode))?; - - assert!(pnode.upper_inode.lock().unwrap().is_some()); - - let real_parent_inode = pnode.first_inode.lock().unwrap().inode.load(Ordering::Relaxed); - - // need to delete whiteout? - if is_whiteout { - let node = Arc::clone(node.as_ref().unwrap()); - let first_inode = Arc::clone(&node.first_inode.lock().unwrap()); - let first_layer = first_inode.layer.as_ref().unwrap(); - if node.in_upper_layer() { - // whiteout in upper layer, need to delete - first_layer.delete_whiteout(ctx, real_parent_inode, sname.as_str())?; - } - - // delete inode from inodes and childrens - self.inodes.lock().unwrap().remove(&node.inode); - pnode.childrens.lock().unwrap().remove(sname.as_str()); - } - - // make it - assert!(pnode.in_upper_layer()); - - let real_inode = Arc::clone(pnode.upper_inode.lock().unwrap().as_ref().unwrap()); - let layer = Arc::clone(real_inode.layer.as_ref().unwrap()); - let _entry = layer.mknod(ctx, real_parent_inode, name, mode, rdev, umask)?; - - pnode.loaded.store(false, Ordering::Relaxed); - let node = self.lookup_node(ctx, parent, name)?; - pnode.loaded.store(true, Ordering::Relaxed); - Ok(Entry { - inode: node.inode, - generation: 0, - attr: node.stat64(ctx)?, - attr_flags: 0, - attr_timeout: self.config.attr_timeout, - entry_timeout: self.config.entry_timeout, - }) - } - - fn link(&self, ctx: &Context, inode: Inode, newparent: Inode, name: &CStr) -> Result { - // hard link.. - let node = self.lookup_node(ctx, inode, CString::new("").expect("invalic c string").as_c_str())?; - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - let newpnode = self.lookup_node(ctx, newparent, CString::new("").expect("invalid c string").as_c_str())?; - if newpnode.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - let newnode = self.lookup_node_ignore_enoent(ctx, newparent, name)?; - - // copy node up - let node = self.copy_node_up(ctx, Arc::clone(&node))?; - let newpnode = self.copy_node_up(ctx, Arc::clone(&newpnode))?; - let sname = name.to_string_lossy().into_owned().to_owned(); - - if let Some(n) = newnode { - if !n.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::EEXIST)); - } - - // need to delete whiteout? if whiteout in upper layer - // delete it - if self.node_in_upper_layer(Arc::clone(&n))? { - // find out the real parent inode and delete whiteout - let pri = &newpnode.first_inode.lock().unwrap(); - let layer = Arc::clone(pri.layer.as_ref().unwrap()); - let real_parent_inode = pri.inode.load(Ordering::Relaxed); - layer.delete_whiteout(ctx, real_parent_inode, sname.as_str())?; - } - - // delete from hash - self.inodes.lock().unwrap().remove(&n.inode); - newpnode.childrens.lock().unwrap().remove(sname.as_str()); - } - - // create the link - let pri = &newpnode.first_inode.lock().unwrap(); - let layer = Arc::clone(pri.layer.as_ref().unwrap()); - let real_parent_inode = pri.inode.load(Ordering::Relaxed); - let real_inode = node.first_inode.lock().unwrap().inode.load(Ordering::Relaxed); - layer.link(ctx, real_inode, real_parent_inode, name)?; - - newpnode.loaded.store(false, Ordering::Relaxed); - let node = self.lookup_node(ctx, newparent, name)?; - newpnode.loaded.store(true, Ordering::Relaxed); - - Ok(Entry { - inode: node.inode, - generation: 0, - attr: node.stat64(ctx)?, - attr_flags: 0, - attr_timeout: self.config.attr_timeout, - entry_timeout: self.config.entry_timeout, - }) - } - - fn symlink(&self, ctx: &Context, linkname: &CStr, parent: Inode, name: &CStr) -> Result { - // soft link - let empty_name = CString::new("").expect("invalid c string"); - let sname = name.to_string_lossy().into_owned().to_owned(); - - let pnode = self.lookup_node(ctx, parent, empty_name.as_c_str())?; - - if pnode.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - let node = self.lookup_node_ignore_enoent(ctx, parent, name)?; - if let Some(n) = node { - if !n.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::EEXIST)); - } - - // whiteout, may need to delete it - self.delete_whiteout_node(ctx, Arc::clone(&n))?; - - // delete from hash - self.inodes.lock().unwrap().remove(&n.inode); - pnode.childrens.lock().unwrap().remove(sname.as_str()); - } - - let pnode = self.copy_node_up(ctx, Arc::clone(&pnode))?; - // find out layer, real parent.. - let (layer, real_parent) = { - let first = pnode.first_inode.lock().unwrap(); - (Arc::clone(first.layer.as_ref().unwrap()), first.inode.load(Ordering::Relaxed)) - }; - - layer.symlink(ctx, linkname, real_parent, name)?; - - pnode.loaded.store(false, Ordering::Relaxed); - let node = self.lookup_node(ctx, parent, name)?; - pnode.loaded.store(true, Ordering::Relaxed); - - Ok(Entry { - inode: node.inode, - generation: 0, - attr: node.stat64(ctx)?, - attr_flags: 0, - attr_timeout: self.config.attr_timeout, - entry_timeout: self.config.entry_timeout, - }) - } - - fn readlink(&self, ctx: &Context, inode: Inode) -> Result> { - let empty_name =CString::new("").expect("invalid c string"); - let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; - - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - // find out real inode - let (layer, real_inode) = { - let first = node.first_inode.lock().unwrap(); - (Arc::clone(first.layer.as_ref().unwrap()), first.inode.load(Ordering::Relaxed)) - }; - - layer.readlink(ctx, real_inode) - - } - - fn flush(&self, ctx: &Context, inode: Inode, handle: Handle, lock_owner: u64) -> Result<()> { - let empty_name = CString::new("").expect("invalid c string"); - let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; - - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - // readonly file can also be flushed, pass flush request - // to lower layer instead of return EROFS here - // if !self.node_in_upper_layer(Arc::clone(&node))? { - // in lower layer, error out or just success? - // FIXME: - // return Err(Error::from_raw_os_error(libc::EROFS)); - // } - - let (layer, real_inode, real_handle) = self.find_real_info_from_handle(ctx, handle)?; - - // FIXME: need to test if inode matches corresponding handle? - - layer.flush(ctx, real_inode, real_handle, lock_owner) - } - - fn fsync(&self, ctx: &Context, inode: Inode, datasync: bool, handle: Handle) -> Result<()> { - let empty_name = CString::new("").expect("invalid c string"); - let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; - - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - if !self.node_in_upper_layer(Arc::clone(&node))? { - // in lower layer, error out or just success? - // FIXME: - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - let (layer, real_inode, real_handle) = self.find_real_info_from_handle(ctx, handle)?; - - // FIXME: need to test if inode matches corresponding handle? - - layer.fsync(ctx, real_inode, datasync, real_handle) - } - - fn fsyncdir(&self, ctx: &Context, inode: Inode, datasync: bool, handle: Handle) -> Result<()> { - let empty_name = CString::new("").expect("invalid c string"); - let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; - - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - if !self.node_in_upper_layer(Arc::clone(&node))? { - // in lower layer, error out or just success? - // FIXME: - return Err(Error::from_raw_os_error(libc::EROFS)); - } - - let (layer, real_inode, real_handle) = self.find_real_info_from_handle(ctx, handle)?; - - // FIXME: need to test if inode matches corresponding handle? - - layer.fsyncdir(ctx, real_inode, datasync, real_handle) - } - - fn access(&self, ctx: &Context, inode: Inode, mask: u32) -> Result<()> { - let empty_name = CString::new("").expect("invalid c string"); - let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; - - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - let (layer, real_inode) = self.find_real_inode(ctx, inode)?; - layer.access(ctx, real_inode, mask) - } - - fn setxattr(&self, ctx: &Context, inode: Inode, name: &CStr, value: &[u8], flags: u32) -> Result<()> { - let empty_name = CString::new("").expect("invalid c string"); - let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; - - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - if !self.node_in_upper_layer(Arc::clone(&node))? { - // copy node into upper layer - // FIXME: - self.copy_node_up(ctx, Arc::clone(&node))?; - } - - let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; - - let (layer, real_inode) = self.find_real_inode(ctx, inode)?; - - layer.setxattr(ctx, real_inode, name, value, flags) - } - - fn getxattr(&self, ctx: &Context, inode: Inode, name: &CStr, size: u32) -> Result { - let empty_name = CString::new("").expect("invalid c string"); - let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; - - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - let (layer, real_inode) = self.find_real_inode(ctx, inode)?; - - layer.getxattr(ctx, real_inode, name, size) - } - - fn listxattr(&self, ctx: &Context, inode: Inode, size: u32) -> Result { - let empty_name = CString::new("").expect("invalid c string"); - let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; - - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - let (layer, real_inode) = self.find_real_inode(ctx, inode)?; - - layer.listxattr(ctx, real_inode, size) - } - - fn removexattr(&self, ctx: &Context, inode: Inode, name: &CStr) -> Result<()> { - let empty_name = CString::new("").expect("invalid c string"); - let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; - - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - if !self.node_in_upper_layer(Arc::clone(&node))? { - // copy node into upper layer - // FIXME: - self.copy_node_up(ctx, Arc::clone(&node))?; - } - - let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; - - let (layer, real_inode) = self.find_real_inode(ctx, inode)?; - - layer.removexattr(ctx, real_inode, name) - } - - fn fallocate(&self, ctx: &Context, inode: Inode, handle: Handle, mode: u32, offset: u64, length: u64) -> Result<()> { - let empty_name = CString::new("").expect("invalid c string"); - let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; - - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - if !self.node_in_upper_layer(Arc::clone(&node))? { - // copy node into upper layer - // FIXME: - // only for node in upper layer, not in upper layer - // indicates open in readonly mode, and cannot fallocate - return Err(Error::from_raw_os_error(libc::EPERM)); - } - - let (layer, real_inode, real_handle) = self.find_real_info_from_handle(ctx, handle)?; - - layer.fallocate(ctx, real_inode, real_handle, mode, offset, length) - } - - fn lseek(&self, ctx: &Context, inode: Inode, handle: Handle, offset: u64, whence: u32) -> Result { - // can this be on dir? FIXME: assume file for now - // we need special process if it can be called on dir - let empty_name = CString::new("").expect("invalid c string"); - let node = self.lookup_node(ctx, inode, empty_name.as_c_str())?; - - if node.whiteout.load(Ordering::Relaxed) { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } - - let st = node.stat64(ctx)?; - if st.st_mode & libc::S_IFMT == libc::S_IFDIR { - error!("lseek on directory"); - return Err(Error::from_raw_os_error(libc::EINVAL)); - } - - let (layer, real_inode, real_handle) = self.find_real_info_from_handle(ctx, handle)?; - layer.lseek(ctx, real_inode, real_handle, offset, whence) - } - + pub fn new( + upper: Option>, + lowers: Vec>, + params: Config, + ) -> Result { + // load root inode + 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 { + 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(); + + // Update upper inode + 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); + } + + // Update lower inodes. + 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); + + // insert root inode into hash + 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 { + // Root node must exist. + self.get_active_inode(FUSE_ROOT_ID).unwrap() + } + + fn insert_inode(&self, inode: u64, node: Arc) { + self.inodes.write().unwrap().insert_inode(inode, node); + } + + fn get_active_inode(&self, inode: u64) -> Option> { + self.inodes.read().unwrap().get_inode(inode) + } + + // Get inode which is active or deleted. + fn get_all_inode(&self, inode: u64) -> Option> { + let inode_store = self.inodes.read().unwrap(); + match inode_store.get_inode(inode) { + Some(n) => Some(n), + None => inode_store.get_deleted_inode(inode), + } + } + + // Return the inode only if it's permanently deleted from both self.inodes and self.deleted_inodes. + fn remove_inode(&self, inode: u64, path_removed: Option) -> Option> { + self.inodes + .write() + .unwrap() + .remove_inode(inode, path_removed) + } + + // Lookup child OverlayInode with under directory. + // If name is empty, return parent itself. + // Parent dir will be loaded, but returned OverlayInode won't. + fn lookup_node(&self, ctx: &Context, parent: Inode, name: &str) -> Result> { + if name.contains([SLASH_ASCII as char]) { + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + + // Parent inode is expected to be loaded before this function is called. + let pnode = match self.get_active_inode(parent) { + Some(v) => v, + None => return Err(Error::from_raw_os_error(libc::ENOENT)), + }; + + // Parent is whiteout-ed, return 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) { + // Parent is expected to be directory, load it first. + self.load_directory(ctx, &pnode)?; + } + + // Current file or dir. + if name.eq(".") + // Root directory has no parent. + || (parent == FUSE_ROOT_ID && name.eq("..")) + // Special convention: empty name indicates current dir. + || name.is_empty() + { + return Ok(Arc::clone(&pnode)); + } + + match pnode.child(name) { + // Child is found. + Some(v) => Ok(v), + None => Err(Error::from_raw_os_error(libc::ENOENT)), + } + } + + // As a debug function, print all inode numbers in hash table. + #[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>> { + 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) + } + } + } + + // Load entries of the directory from all layers, if node is not directory, return directly. + fn load_directory(&self, ctx: &Context, node: &Arc) -> Result<()> { + if node.loaded.load(Ordering::Relaxed) { + return Ok(()); + } + + // We got all childrens without inode. + let childrens = node.scan_childrens(ctx)?; + + // =============== Start Lock Area =================== + // Lock OverlayFs inodes. + let mut inode_store = self.inodes.write().unwrap(); + // Lock the OverlayInode and its childrens. + let mut node_children = node.childrens.lock().unwrap(); + + // Check again in case another 'load_directory' function call gets locks and want to do duplicated work. + if node.loaded.load(Ordering::Relaxed) { + return Ok(()); + } + + // Now we have two locks' protection, Fs inodes lock and OverlayInode's childrens lock. + for mut child in childrens.into_iter() { + // Allocate inode for each child. + let ino = inode_store.alloc_inode(&child.path)?; + + let name = child.name.clone(); + child.inode = ino; + // Create bi-directional link between parent and child. + child.parent = Mutex::new(Arc::downgrade(node)); + + let arc_child = Arc::new(child); + node_children.insert(name, arc_child.clone()); + // Record overlay inode in whole OverlayFs. + 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; + } + }; + + // FIXME: need atomic protection around lookups' load & store. @weizhang555 + let mut lookups = v.lookups.load(Ordering::Relaxed); + + if lookups < count { + lookups = 0; + } else { + lookups -= count; + } + v.lookups.store(lookups, Ordering::Relaxed); + + // TODO: use compare_exchange. + //v.lookups.compare_exchange(old, new, Ordering::Acquire, 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() { + // remove it from hashmap + p.remove_child(v.name.as_str()); + } + } + } + + fn do_lookup(&self, ctx: &Context, parent: Inode, name: &str) -> Result { + 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)?; + } + + // FIXME: can forget happen between found and increase reference counter? + 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 { + 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) -> Result, + ) -> Result<()> { + trace!( + "do_readir: handle: {}, size: {}, offset: {}", + handle, + size, + offset + ); + if size == 0 { + return Ok(()); + } + + // lookup the directory + let ovl_inode = match self.handles.lock().unwrap().get(&handle) { + Some(dir) => dir.node.clone(), + None => { + // Try to get data with inode. + 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(); + //add myself as "." + childrens.push((".".to_string(), ovl_inode.clone())); + + //add parent + 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() { + // skip whiteout node + 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 { + // make struct DireEntry and Entry + 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 { + // no more space, stop here + return Ok(()); + } + } + + Err(e) => { + // when the buffer is still empty, return error, otherwise return the entry already added + if len == 0 { + return Err(e); + } else { + return Ok(()); + } + } + } + } + } + + Ok(()) + } + + fn do_mkdir( + &self, + ctx: &Context, + parent_node: &Arc, + name: &str, + mode: u32, + umask: u32, + ) -> Result<()> { + if self.upper_layer.is_none() { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + // Parent node was deleted. + 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)? { + // Node with same name exists, let's check if it's whiteout. + if !n.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::EEXIST)); + } + + if n.in_upper_layer() { + delete_whiteout = true; + } + + // Set opaque if child dir has lower layers. + if !n.upper_layer_only() { + set_opaque = true; + } + } + + // Copy parent node up if necessary. + 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 { + 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(), + ); + } + // Allocate inode number. + let ino = self.alloc_inode(&path)?; + let child_dir = parent_real_inode.mkdir(ctx, name, mode, umask)?; + // Set opaque if child dir has lower layers. + 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) + })?; + + // new_node is always 'Some' + 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, + name: &str, + mode: u32, + rdev: u32, + umask: u32, + ) -> Result<()> { + if self.upper_layer.is_none() { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + // Parent node was deleted. + 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) => { + // Node with same name exists, let's check if it's whiteout. + if !n.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::EEXIST)); + } + + // Copy parent node up if necessary. + let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?; + pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result { + 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)?; + + // Replace existing real inodes with new one. + n.add_upper_inode(child_ri, true); + Ok(false) + })?; + } + None => { + // Copy parent node up if necessary. + 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 { + 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)); + } + }; + + // Allocate inode number. + 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) + })?; + + // new_node is always 'Some' + 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, + name: &str, + args: CreateIn, + ) -> Result> { + let upper = self + .upper_layer + .as_ref() + .cloned() + .ok_or_else(|| Error::from_raw_os_error(libc::EROFS))?; + + // Parent node was deleted. + 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) => { + // Node with same name exists, let's check if it's whiteout. + if !n.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::EEXIST)); + } + + // Copy parent node up if necessary. + let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?; + pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result { + 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; + + // Replace existing real inodes with new one. + n.add_upper_inode(child_ri, true); + Ok(false) + })?; + n.clone() + } + None => { + // Copy parent node up if necessary. + 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 { + 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; + // Allocate inode number. + 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) + })?; + + // new_node is always 'Some' + 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, + new_parent: &Arc, + name: &str, + ) -> Result<()> { + if self.upper_layer.is_none() { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + // Node is whiteout. + 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) { + // Directory can't be hardlinked. + 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) => { + // Node with same name exists, let's check if it's whiteout. + if !n.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::EEXIST)); + } + + // Node is definitely a whiteout now. + new_parent.handle_upper_inode_locked(&mut |parent_real_inode| -> Result { + 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)); + } + }; + + // Whiteout file exists in upper level, let's delete it. + 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)?; + + // Replace existing real inodes with new one. + n.add_upper_inode(child_ri, true); + Ok(false) + })?; + } + None => { + // Copy parent node up if necessary. + let mut new_node = None; + new_parent.handle_upper_inode_locked(&mut |parent_real_inode| -> Result { + 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)); + } + }; + + // Allocate inode number. + 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) + })?; + + // new_node is always 'Some' + 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, + name: &str, + ) -> Result<()> { + if self.upper_layer.is_none() { + return Err(Error::from_raw_os_error(libc::EROFS)); + } + + // parent was deleted. + 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) => { + // Node with same name exists, let's check if it's whiteout. + if !n.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::EEXIST)); + } + + // Copy parent node up if necessary. + let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?; + pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result { + 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)?; + + // Replace existing real inodes with new one. + n.add_upper_inode(child_ri, true); + Ok(false) + })?; + } + None => { + // Copy parent node up if necessary. + 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 { + 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)); + } + }; + + // Allocate inode number. + 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) + })?; + + // new_node is always 'Some' + 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) -> Result> { + 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)?; + } + + // Read the linkname from lower layer. + let path = self_layer.readlink(ctx, self_inode)?; + // Convert path to &str. + 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 { + // We already create upper dir for parent_node above. + 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 { + // update upper_inode and first_inode() + node.add_upper_inode(real_inode, true); + } + + Ok(Arc::clone(&node)) + } + + // Copy regular file from lower layer to upper layer. + // Caller must ensure node doesn't have upper layer. + fn copy_regfile_up(&self, ctx: &Context, node: Arc) -> Result> { + 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)?; + } + + // create the file in upper layer using information from lower layer + 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 { + // We already create upper dir for parent_node. + 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); + + // need to use work directory and then rename file to + // final destination for atomic reasons.. not deal with it for now, + // use stupid copy at present. + // FIXME: this need a lot of work here, ntimes, xattr, etc. + + // Copy from lower real inode to upper real inode. + 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; + } + // close handles + 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 will remove file automatically. + 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) + { + // Ignore ENOSYS. + if e.raw_os_error() != Some(libc::ENOSYS) { + return Err(e); + } + } + + // update upper_inode and first_inode() + node.add_upper_inode(ri, true); + } + + Ok(Arc::clone(&node)) + } + + fn copy_node_up(&self, ctx: &Context, node: Arc) -> Result> { + if node.in_upper_layer() { + return Ok(node); + } + + let st = node.stat64(ctx)?; + // directory + if utils::is_dir(st) { + node.create_upper_dir(ctx, None)?; + return Ok(Arc::clone(&node)); + } + + // For symlink. + if st.st_mode & libc::S_IFMT == libc::S_IFLNK { + return self.copy_symlink_up(ctx, Arc::clone(&node)); + } + + // For regular file. + 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)); + } + + // Find parent Overlay Inode. + let pnode = self.lookup_node(ctx, parent, "")?; + if pnode.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + // Find the Overlay Inode for child with . + let sname = name.to_string_lossy().to_string(); + let node = self.lookup_node(ctx, parent, sname.as_str())?; + if node.whiteout.load(Ordering::Relaxed) { + // already deleted. + 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)); + } + + // Delete all whiteouts. + 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 { + 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) + })?; + + // Parent is opaque, it shadows everything in lower layers so no need to create extra whiteouts. + 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 + ); + + // lookups decrease by 1. + node.lookups.fetch_sub(1, Ordering::Relaxed); + + // remove it from hashmap + self.remove_inode(node.inode, path_removed); + pnode.remove_child(node.name.as_str()); + + if need_whiteout { + trace!("do_rm: creating whiteout\n"); + // pnode is copied up, so it has upper layer. + pnode.handle_upper_inode_locked(&mut |parent_upper_inode| -> Result { + 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<()> { + // Use O_RDONLY flags which indicates no copy up. + let data = self.get_data(ctx, Some(handle), inode, libc::O_RDONLY as u32)?; + + match data.real_handle { + // FIXME: need to test if inode matches corresponding handle? + None => Err(Error::from_raw_os_error(libc::ENOENT)), + Some(ref rh) => { + let real_handle = rh.handle.load(Ordering::Relaxed); + // TODO: check if it's in upper layer? @weizhang555 + if syncdir { + rh.layer.fsyncdir(ctx, rh.inode, datasync, real_handle) + } else { + rh.layer.fsync(ctx, rh.inode, datasync, real_handle) + } + } + } + } + + // Delete everything in the directory only on upper layer, ignore lower layers. + fn empty_node_directory(&self, ctx: &Context, node: Arc) -> Result<()> { + let st = node.stat64(ctx)?; + if !utils::is_dir(st) { + // This function can only be called on directories. + return Err(Error::from_raw_os_error(libc::ENOTDIR)); + } + + let (layer, in_upper, inode) = node.first_layer_inode(); + if !in_upper { + return Ok(()); + } + + // Copy node.childrens Hashmap to Vector, the Vector is also used as temp storage, + // Without this, Rust won't allow us to remove them from node.childrens. + let iter = node + .childrens + .lock() + .unwrap() + .iter() + .map(|(_, v)| v.clone()) + .collect::>(); + + for child in iter { + // We only care about upper layer, ignore lower layers. + 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())?; + } + } + + // delete the child + 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, 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, 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, + inode: Inode, + flags: u32, + ) -> Result> { + 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; + + // lookup node + let node = self.lookup_node(ctx, inode, "")?; + + // whiteout node + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + if !readonly { + // Check if upper layer exists, return EROFS is not exists. + self.upper_layer + .as_ref() + .cloned() + .ok_or_else(|| Error::from_raw_os_error(libc::EROFS))?; + // copy up to upper layer + 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 { - let mut buf = Vec::::with_capacity(count); - unsafe { buf.set_len(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) }; - f.write_volatile(slice) - } else { - Ok(0) - } - } + // Copies at most count bytes from self directly into f at offset off + // without storing it in any intermediate buffers. + fn read_to( + &mut self, + f: &mut dyn FileReadWriteVolatile, + count: usize, + off: u64, + ) -> Result { + let mut buf = vec![0_u8; count]; + let slice = unsafe { FileVolatileSlice::from_raw_ptr(buf.as_mut_ptr(), count) }; + + // Read from self to slice. + let ret = self.read_volatile(slice)?; + if ret > 0 { + let slice = unsafe { FileVolatileSlice::from_raw_ptr(buf.as_mut_ptr(), ret) }; + // Write from slice to f at offset off. + 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 { - let mut buf = Vec::::with_capacity(count); - unsafe { buf.set_len(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) - } - } + // Copies at most count bytes from f at offset off directly into self + // without storing it in any intermediate buffers. + fn write_from( + &mut self, + f: &mut dyn FileReadWriteVolatile, + count: usize, + off: u64, + ) -> Result { + let mut buf = vec![0_u8; count]; + let slice = unsafe { FileVolatileSlice::from_raw_ptr(buf.as_mut_ptr(), count) }; + // Read from f at offset off to slice. + let ret = f.read_at_volatile(slice, off)?; + + if ret > 0 { + let slice = unsafe { FileVolatileSlice::from_raw_ptr(buf.as_mut_ptr(), ret) }; + // Write from slice to self. + self.write_volatile(slice) + } else { + Ok(0) + } + } + + fn available_bytes(&self) -> usize { + // Max usize + usize::MAX + } +} + +#[cfg(not(feature = "async-io"))] +impl BackendFileSystem for OverlayFs { + /// mount returns the backend file system root inode entry and + /// the largest inode number it has. + fn mount(&self) -> Result<(Entry, u64)> { + let ctx = Context::default(); + let entry = self.do_lookup(&ctx, self.root_inode(), "")?; + Ok((entry, VFS_MAX_INO)) + } + + /// Provides a reference to the Any trait. This is useful to let + /// the caller have access to the underlying type behind the + /// trait. + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/src/overlayfs/plugin.rs b/src/overlayfs/plugin.rs deleted file mode 100644 index 11480d947..000000000 --- a/src/overlayfs/plugin.rs +++ /dev/null @@ -1,92 +0,0 @@ - -#![allow(missing_docs)] - -use std::collections::{HashMap, LinkedList}; -use std::io::Result; -use std::sync::{Arc}; -use std::io::{Error, ErrorKind}; -use libc; - -use self::super::layer::Layer; -use self::super::PLUGIN_PREFIX; -use self::super::direct::Direct; -use self::super::BoxedLayer; - -pub type BoxedPlugin = Box; - -/// ! plugin trait -pub trait Plugin { - ///! name - fn name(&self) -> String; - ///! load data source - fn load(&self, opaque: String, upper: bool) -> Result>>; - ///! release plugin - fn release(&self) -> Result<()>; -} - -///! plugin manager -pub struct PluginManager { - ///! inner data - pub plugins: HashMap>>, -} - -impl PluginManager { - ///! new - pub fn new() -> Self { - PluginManager { - plugins: HashMap::new(), - } - } - - ///! register a plugin - pub fn register(&mut self, name: String, plugin: Arc>) -> Result<()> { - if self.plugins.get(name.as_str()).is_some() { - return Err(Error::from_raw_os_error(libc::EEXIST)); - } - self.plugins.insert(name, plugin); - - Ok(()) - } - - ///! find a registerd plugin - pub fn get_plugin(&self, name: String) -> Option>> { - if let Some(ref v) = self.plugins.get(name.as_str()) { - Some(Arc::clone(v)) - } else { - None - } - } -} - -pub fn find_plugin(manager: &PluginManager, name: String) -> Option>> { - manager.get_plugin(name) -} - -pub fn process_onelayer(manager: &PluginManager, opaque: String, upper: bool) -> Result>> { - if opaque.starts_with(PLUGIN_PREFIX) { - // plugin - let striped = opaque.strip_prefix(PLUGIN_PREFIX).unwrap(); - let ps: Vec<&str> = striped.splitn(2, "/").collect(); - if ps.len() != 2 { - error!("invalid upperdir parameters!"); - return Err(Error::from(ErrorKind::InvalidData)); - } - - let plugin_name = ps[0]; - let plugin_params = ps[1]; - let plugin = match manager.get_plugin(plugin_name.into()) { - Some(v) => v, - None => { - error!("unknown plugin"); - return Err(Error::from(ErrorKind::InvalidData)); - } - }; - - // load layers from plugin - return plugin.load(plugin_params.into(), upper); - - } else { - // directory - return Direct::new(opaque, upper); - } -} diff --git a/src/overlayfs/sync_io.rs b/src/overlayfs/sync_io.rs new file mode 100644 index 000000000..57d111f42 --- /dev/null +++ b/src/overlayfs/sync_io.rs @@ -0,0 +1,913 @@ +// Copyright (C) 2023 Ant Group. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +use super::*; +use std::ffi::CStr; +use std::io::Result; + +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::Arc; +use std::time::Duration; + +use crate::abi::fuse_abi::{stat64, statvfs64, CreateIn}; +use crate::api::filesystem::{ + Context, DirEntry, Entry, FileSystem, FsOptions, GetxattrReply, ListxattrReply, OpenOptions, + SetattrValid, ZeroCopyReader, ZeroCopyWriter, +}; + +use libc; +use std::io::{Error, ErrorKind}; + +impl FileSystem for OverlayFs { + type Inode = Inode; + type Handle = Handle; + + fn init(&self, capable: FsOptions) -> Result { + // use vfs' negotiated capability if imported + // other wise, do our own negotiation + let mut opts = FsOptions::DO_READDIRPLUS | FsOptions::READDIRPLUS_AUTO; + + if self.config.do_import { + self.import()?; + } + + if (!self.config.do_import || self.config.writeback) + && capable.contains(FsOptions::WRITEBACK_CACHE) + { + opts |= FsOptions::WRITEBACK_CACHE; + self.writeback.store(true, Ordering::Relaxed); + } + + if (!self.config.do_import || self.config.no_open) + && capable.contains(FsOptions::ZERO_MESSAGE_OPEN) + { + opts |= FsOptions::ZERO_MESSAGE_OPEN; + opts.remove(FsOptions::ATOMIC_O_TRUNC); + self.no_open.store(true, Ordering::Relaxed); + } + + if (!self.config.do_import || self.config.no_opendir) + && capable.contains(FsOptions::ZERO_MESSAGE_OPENDIR) + { + opts |= FsOptions::ZERO_MESSAGE_OPENDIR; + self.no_opendir.store(true, Ordering::Relaxed); + } + + if (!self.config.do_import || self.config.killpriv_v2) + && capable.contains(FsOptions::HANDLE_KILLPRIV_V2) + { + opts |= FsOptions::HANDLE_KILLPRIV_V2; + self.killpriv_v2.store(true, Ordering::Relaxed); + } + + if self.config.perfile_dax && capable.contains(FsOptions::PERFILE_DAX) { + opts |= FsOptions::PERFILE_DAX; + self.perfile_dax.store(true, Ordering::Relaxed); + } + + Ok(opts) + } + + fn destroy(&self) {} + + fn statfs(&self, ctx: &Context, inode: Inode) -> Result { + trace!("STATFS: inode: {}\n", inode); + self.do_statvfs(ctx, inode) + } + + fn lookup(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result { + let tmp = name.to_string_lossy().to_string(); + trace!("LOOKUP: parent: {}, name: {}\n", parent, tmp); + let result = self.do_lookup(ctx, parent, tmp.as_str()); + if result.is_ok() { + trace!("LOOKUP result: {:?}", result.as_ref().unwrap()); + } + //self.debug_print_all_inodes(); + result + } + + fn forget(&self, _ctx: &Context, inode: Inode, count: u64) { + trace!("FORGET: inode: {}, count: {}\n", inode, count); + self.forget_one(inode, count); + //self.debug_print_all_inodes(); + } + + fn batch_forget(&self, _ctx: &Context, requests: Vec<(Inode, u64)>) { + trace!("BATCH_FORGET: requests: {:?}\n", requests); + for (inode, count) in requests { + self.forget_one(inode, count); + } + } + + fn opendir( + &self, + ctx: &Context, + inode: Inode, + _flags: u32, + ) -> Result<(Option, OpenOptions)> { + trace!("OPENDIR: inode: {}\n", inode); + if self.no_opendir.load(Ordering::Relaxed) { + info!("fuse: opendir is not supported."); + return Err(Error::from_raw_os_error(libc::ENOSYS)); + } + + let mut opts = OpenOptions::empty(); + + if let CachePolicy::Always = self.config.cache_policy { + opts |= OpenOptions::KEEP_CACHE; + } + + // lookup node + let node = self.lookup_node(ctx, inode, ".")?; + + 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) { + return Err(Error::from_raw_os_error(libc::ENOTDIR)); + } + + let handle = self.next_handle.fetch_add(1, Ordering::Relaxed); + + self.handles.lock().unwrap().insert( + handle, + Arc::new(HandleData { + node: Arc::clone(&node), + real_handle: None, + }), + ); + + Ok((Some(handle), opts)) + } + + fn releasedir(&self, _ctx: &Context, inode: Inode, _flags: u32, handle: Handle) -> Result<()> { + trace!("RELEASEDIR: inode: {}, handle: {}\n", inode, handle); + if self.no_opendir.load(Ordering::Relaxed) { + info!("fuse: releasedir is not supported."); + return Err(Error::from_raw_os_error(libc::ENOSYS)); + } + + self.handles.lock().unwrap().remove(&handle); + + Ok(()) + } + + // for mkdir or create file + // 1. lookup name, if exists and not whiteout, return EEXIST + // 2. not exists and no whiteout, copy up parent node, ususally a mkdir on upper layer would do the work + // 3. find whiteout, if whiteout in upper layer, should set opaque. if in lower layer, just mkdir? + fn mkdir( + &self, + ctx: &Context, + parent: Inode, + name: &CStr, + mode: u32, + umask: u32, + ) -> Result { + let sname = name.to_string_lossy().to_string(); + + trace!("MKDIR: parent: {}, name: {}\n", parent, sname); + + // no entry or whiteout + let pnode = self.lookup_node(ctx, parent, "")?; + if pnode.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + self.do_mkdir(ctx, &pnode, sname.as_str(), mode, umask)?; + let entry = self.do_lookup(ctx, parent, sname.as_str()); + entry + } + + fn rmdir(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result<()> { + trace!( + "RMDIR: parent: {}, name: {}\n", + parent, + name.to_string_lossy() + ); + self.do_rm(ctx, parent, name, true) + } + + fn readdir( + &self, + ctx: &Context, + inode: Inode, + handle: Handle, + size: u32, + offset: u64, + add_entry: &mut dyn FnMut(DirEntry) -> Result, + ) -> Result<()> { + trace!("READDIR: inode: {}, handle: {}\n", inode, handle); + if self.config.no_readdir { + info!("fuse: readdir is not supported."); + return Ok(()); + } + self.do_readdir(ctx, inode, handle, size, offset, false, &mut |dir_entry, + _| + -> Result< + usize, + > { + add_entry(dir_entry) + }) + } + + fn readdirplus( + &self, + ctx: &Context, + inode: Inode, + handle: Handle, + size: u32, + offset: u64, + add_entry: &mut dyn FnMut(DirEntry, Entry) -> Result, + ) -> Result<()> { + trace!("READDIRPLUS: inode: {}, handle: {}\n", inode, handle); + if self.config.no_readdir { + info!("fuse: readdirplus is not supported."); + return Ok(()); + } + self.do_readdir(ctx, inode, handle, size, offset, true, &mut |dir_entry, + entry| + -> Result< + usize, + > { + match entry { + Some(e) => add_entry(dir_entry, e), + None => Err(Error::from_raw_os_error(libc::ENOENT)), + } + }) + } + + fn open( + &self, + ctx: &Context, + inode: Inode, + flags: u32, + fuse_flags: u32, + ) -> Result<(Option, OpenOptions, Option)> { + // open assume file always exist + trace!("OPEN: inode: {}, flags: {}\n", inode, flags); + if self.no_open.load(Ordering::Relaxed) { + info!("fuse: open is not supported."); + return Err(Error::from_raw_os_error(libc::ENOSYS)); + } + + let readonly: bool = flags + & (libc::O_APPEND | libc::O_CREAT | libc::O_TRUNC | libc::O_RDWR | libc::O_WRONLY) + as u32 + == 0; + // toggle flags + let mut flags: i32 = flags as i32; + + flags |= libc::O_NOFOLLOW; + + if self.config.writeback { + if flags & libc::O_ACCMODE == libc::O_WRONLY { + flags &= !libc::O_ACCMODE; + flags |= libc::O_RDWR; + } + + if flags & libc::O_APPEND != 0 { + flags &= !libc::O_APPEND; + } + } + // lookup node + let node = self.lookup_node(ctx, inode, "")?; + + // whiteout node + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + if !readonly { + // copy up to upper layer + self.copy_node_up(ctx, Arc::clone(&node))?; + } + + // assign a handle in overlayfs and open it + let (_l, h, _) = node.open(ctx, flags as u32, fuse_flags)?; + match h { + None => Err(Error::from_raw_os_error(libc::ENOENT)), + Some(handle) => { + let hd = self.next_handle.fetch_add(1, Ordering::Relaxed); + 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(handle), + }), + }; + + self.handles + .lock() + .unwrap() + .insert(hd, Arc::new(handle_data)); + + let mut opts = OpenOptions::empty(); + match self.config.cache_policy { + CachePolicy::Never => opts |= OpenOptions::DIRECT_IO, + CachePolicy::Always => opts |= OpenOptions::KEEP_CACHE, + _ => {} + } + + trace!("OPEN: returning handle: {}", hd); + + Ok((Some(hd), opts, None)) + } + } + } + + fn release( + &self, + ctx: &Context, + _inode: Inode, + flags: u32, + handle: Handle, + flush: bool, + flock_release: bool, + lock_owner: Option, + ) -> Result<()> { + trace!( + "RELEASE: inode: {}, flags: {}, handle: {}, flush: {}, flock_release: {}, lock_owner: {:?}\n", + _inode, + flags, + handle, + flush, + flock_release, + lock_owner + ); + + if self.no_open.load(Ordering::Relaxed) { + info!("fuse: release is not supported."); + return Err(Error::from_raw_os_error(libc::ENOSYS)); + } + + if let Some(hd) = self.handles.lock().unwrap().get(&handle) { + let rh = if let Some(ref h) = hd.real_handle { + h + } else { + return Err(Error::new(ErrorKind::Other, "no handle")); + }; + let real_handle = rh.handle.load(Ordering::Relaxed); + let real_inode = rh.inode; + rh.layer.release( + ctx, + real_inode, + flags, + real_handle, + flush, + flock_release, + lock_owner, + )?; + } + + self.handles.lock().unwrap().remove(&handle); + + Ok(()) + } + + fn create( + &self, + ctx: &Context, + parent: Inode, + name: &CStr, + args: CreateIn, + ) -> Result<(Entry, Option, OpenOptions, Option)> { + let sname = name.to_string_lossy().to_string(); + trace!("CREATE: parent: {}, name: {}\n", parent, sname); + + // Parent doesn't exist. + let pnode = self.lookup_node(ctx, parent, "")?; + if pnode.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let mut hargs = args; + let mut flags: i32 = args.flags as i32; + flags |= libc::O_NOFOLLOW; + flags &= !libc::O_DIRECT; + if self.config.writeback { + if flags & libc::O_ACCMODE == libc::O_WRONLY { + flags &= !libc::O_ACCMODE; + flags |= libc::O_RDWR; + } + + if flags & libc::O_APPEND != 0 { + flags &= !libc::O_APPEND; + } + } + hargs.flags = flags as u32; + + let final_handle = self.do_create(ctx, &pnode, sname.as_str(), hargs)?; + let entry = self.do_lookup(ctx, parent, sname.as_str())?; + + let mut opts = OpenOptions::empty(); + match self.config.cache_policy { + CachePolicy::Never => opts |= OpenOptions::DIRECT_IO, + CachePolicy::Always => opts |= OpenOptions::KEEP_CACHE, + _ => {} + } + + Ok((entry, final_handle, opts, None)) + } + + fn unlink(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result<()> { + trace!( + "UNLINK: parent: {}, name: {}\n", + parent, + name.to_string_lossy() + ); + self.do_rm(ctx, parent, name, false) + } + + fn read( + &self, + ctx: &Context, + inode: Inode, + handle: Handle, + w: &mut dyn ZeroCopyWriter, + size: u32, + offset: u64, + lock_owner: Option, + flags: u32, + ) -> Result { + trace!( + "READ: inode: {}, handle: {}, size: {}, offset: {}, lock_owner: {:?}, flags: {}\n", + inode, + handle, + size, + offset, + lock_owner, + flags + ); + + let data = self.get_data(ctx, Some(handle), inode, flags)?; + + match data.real_handle { + None => Err(Error::from_raw_os_error(libc::ENOENT)), + Some(ref hd) => hd.layer.read( + ctx, + hd.inode, + hd.handle.load(Ordering::Relaxed), + w, + size, + offset, + lock_owner, + flags, + ), + } + } + + fn write( + &self, + ctx: &Context, + inode: Inode, + handle: Handle, + r: &mut dyn ZeroCopyReader, + size: u32, + offset: u64, + lock_owner: Option, + delayed_write: bool, + flags: u32, + fuse_flags: u32, + ) -> Result { + trace!( + "WRITE: inode: {}, handle: {}, size: {}, offset: {}, lock_owner: {:?}, delayed_write: {}, flags: {}, fuse_flags: {}\n", + inode, + handle, + size, + offset, + lock_owner, + delayed_write, + flags, + fuse_flags + ); + + let data = self.get_data(ctx, Some(handle), inode, flags)?; + + match data.real_handle { + None => Err(Error::from_raw_os_error(libc::ENOENT)), + Some(ref hd) => hd.layer.write( + ctx, + hd.inode, + hd.handle.load(Ordering::Relaxed), + r, + size, + offset, + lock_owner, + delayed_write, + flags, + fuse_flags, + ), + } + } + + fn getattr( + &self, + ctx: &Context, + inode: Inode, + handle: Option, + ) -> Result<(stat64, Duration)> { + trace!( + "GETATTR: inode: {}, handle: {}\n", + inode, + handle.unwrap_or_default() + ); + + if !self.no_open.load(Ordering::Relaxed) { + if let Some(h) = handle { + if let Some(hd) = self.handles.lock().unwrap().get(&h) { + if let Some(ref rh) = hd.real_handle { + let (st, _d) = rh.layer.getattr( + ctx, + rh.inode, + Some(rh.handle.load(Ordering::Relaxed)), + )?; + return Ok((st, self.config.attr_timeout)); + } + } + } + } + + let node = self.lookup_node(ctx, inode, "")?; + let (layer, _, inode) = node.first_layer_inode(); + let (st, _) = layer.getattr(ctx, inode, None)?; + Ok((st, self.config.attr_timeout)) + } + + fn setattr( + &self, + ctx: &Context, + inode: Inode, + attr: stat64, + handle: Option, + valid: SetattrValid, + ) -> Result<(stat64, Duration)> { + trace!("SETATTR: inode: {}\n", inode); + + // Check if upper layer exists. + self.upper_layer + .as_ref() + .cloned() + .ok_or_else(|| Error::from_raw_os_error(libc::EROFS))?; + + // deal with handle first + if !self.no_open.load(Ordering::Relaxed) { + if let Some(h) = handle { + if let Some(hd) = self.handles.lock().unwrap().get(&h) { + if let Some(ref rhd) = hd.real_handle { + // handle opened in upper layer + if rhd.in_upper_layer { + let (st, _d) = rhd.layer.setattr( + ctx, + rhd.inode, + attr, + Some(rhd.handle.load(Ordering::Relaxed)), + valid, + )?; + + return Ok((st, self.config.attr_timeout)); + } + } + } + } + } + + let mut node = self.lookup_node(ctx, inode, "")?; + + if !node.in_upper_layer() { + node = self.copy_node_up(ctx, Arc::clone(&node))? + } + + let (layer, _, real_inode) = node.first_layer_inode(); + let (st, _d) = layer.setattr(ctx, real_inode, attr, None, valid)?; + Ok((st, self.config.attr_timeout)) + } + + fn rename( + &self, + _ctx: &Context, + _olddir: Inode, + _odlname: &CStr, + _newdir: Inode, + _newname: &CStr, + _flags: u32, + ) -> Result<()> { + // complex, implement it later + trace!( + "RENAME: olddir: {}, oldname: {}, newdir: {}, newname: {}, flags: {}\n", + _olddir, + _odlname.to_string_lossy(), + _newdir, + _newname.to_string_lossy(), + _flags + ); + Err(Error::from_raw_os_error(libc::EXDEV)) + } + + fn mknod( + &self, + ctx: &Context, + parent: Inode, + name: &CStr, + mode: u32, + rdev: u32, + umask: u32, + ) -> Result { + let sname = name.to_string_lossy().to_string(); + trace!("MKNOD: parent: {}, name: {}\n", parent, sname); + + // Check if parent exists. + let pnode = self.lookup_node(ctx, parent, "")?; + if pnode.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + self.do_mknod(ctx, &pnode, sname.as_str(), mode, rdev, umask)?; + let entry = self.do_lookup(ctx, parent, sname.as_str()); + entry + } + + fn link(&self, ctx: &Context, inode: Inode, newparent: Inode, name: &CStr) -> Result { + let sname = name.to_string_lossy().to_string(); + trace!( + "LINK: inode: {}, newparent: {}, name: {}\n", + inode, + newparent, + sname.as_str() + ); + + let node = self.lookup_node(ctx, inode, "")?; + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let newpnode = self.lookup_node(ctx, newparent, "")?; + if newpnode.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + self.do_link(ctx, &node, &newpnode, sname.as_str())?; + let entry = self.do_lookup(ctx, newparent, sname.as_str()); + entry + } + + fn symlink(&self, ctx: &Context, linkname: &CStr, parent: Inode, name: &CStr) -> Result { + // soft link + let sname = name.to_string_lossy().into_owned().to_owned(); + let slinkname = linkname.to_string_lossy().into_owned().to_owned(); + trace!( + "SYMLINK: linkname: {}, parent: {}, name: {}\n", + linkname.to_string_lossy(), + parent, + sname.as_str() + ); + + let pnode = self.lookup_node(ctx, parent, "")?; + self.do_symlink(ctx, slinkname.as_str(), &pnode, sname.as_str())?; + + let entry = self.do_lookup(ctx, parent, sname.as_str()); + entry + } + + fn readlink(&self, ctx: &Context, inode: Inode) -> Result> { + trace!("READLINK: inode: {}\n", inode); + + let node = self.lookup_node(ctx, inode, "")?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let (layer, _, inode) = node.first_layer_inode(); + layer.readlink(ctx, inode) + } + + fn flush(&self, ctx: &Context, inode: Inode, handle: Handle, lock_owner: u64) -> Result<()> { + trace!( + "FLUSH: inode: {}, handle: {}, lock_owner: {}\n", + inode, + handle, + lock_owner + ); + + if self.no_open.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOSYS)); + } + + let node = self.lookup_node(ctx, inode, "")?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let (layer, real_inode, real_handle) = self.find_real_info_from_handle(handle)?; + + // FIXME: need to test if inode matches corresponding handle? + + layer.flush(ctx, real_inode, real_handle, lock_owner) + } + + fn fsync(&self, ctx: &Context, inode: Inode, datasync: bool, handle: Handle) -> Result<()> { + trace!( + "FSYNC: inode: {}, datasync: {}, handle: {}\n", + inode, + datasync, + handle + ); + + self.do_fsync(ctx, inode, datasync, handle, false) + } + + fn fsyncdir(&self, ctx: &Context, inode: Inode, datasync: bool, handle: Handle) -> Result<()> { + trace!( + "FSYNCDIR: inode: {}, datasync: {}, handle: {}\n", + inode, + datasync, + handle + ); + + self.do_fsync(ctx, inode, datasync, handle, true) + } + + fn access(&self, ctx: &Context, inode: Inode, mask: u32) -> Result<()> { + trace!("ACCESS: inode: {}, mask: {}\n", inode, mask); + let node = self.lookup_node(ctx, inode, "")?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let (layer, real_inode) = self.find_real_inode(inode)?; + layer.access(ctx, real_inode, mask) + } + + fn setxattr( + &self, + ctx: &Context, + inode: Inode, + name: &CStr, + value: &[u8], + flags: u32, + ) -> Result<()> { + trace!( + "SETXATTR: inode: {}, name: {}, value: {:?}, flags: {}\n", + inode, + name.to_string_lossy(), + value, + flags + ); + let node = self.lookup_node(ctx, inode, "")?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + if !node.in_upper_layer() { + // Copy node up. + self.copy_node_up(ctx, Arc::clone(&node))?; + } + + let (layer, _, real_inode) = node.first_layer_inode(); + + layer.setxattr(ctx, real_inode, name, value, flags) + + // TODO: recreate node since setxattr may made dir opaque. @weizhang555.zw + } + + fn getxattr( + &self, + ctx: &Context, + inode: Inode, + name: &CStr, + size: u32, + ) -> Result { + trace!( + "GETXATTR: inode: {}, name: {}, size: {}\n", + inode, + name.to_string_lossy(), + size + ); + let node = self.lookup_node(ctx, inode, "")?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let (layer, real_inode) = self.find_real_inode(inode)?; + + layer.getxattr(ctx, real_inode, name, size) + } + + fn listxattr(&self, ctx: &Context, inode: Inode, size: u32) -> Result { + trace!("LISTXATTR: inode: {}, size: {}\n", inode, size); + let node = self.lookup_node(ctx, inode, "")?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + let (layer, real_inode) = self.find_real_inode(inode)?; + + layer.listxattr(ctx, real_inode, size) + } + + fn removexattr(&self, ctx: &Context, inode: Inode, name: &CStr) -> Result<()> { + trace!( + "REMOVEXATTR: inode: {}, name: {}\n", + inode, + name.to_string_lossy() + ); + let node = self.lookup_node(ctx, inode, "")?; + + if node.whiteout.load(Ordering::Relaxed) { + return Err(Error::from_raw_os_error(libc::ENOENT)); + } + + if !node.in_upper_layer() { + // copy node into upper layer + self.copy_node_up(ctx, Arc::clone(&node))?; + } + + let (layer, _, ino) = node.first_layer_inode(); + layer.removexattr(ctx, ino, name) + + // TODO: recreate the node since removexattr may remove the opaque xattr. @weizhang555.zw + } + + fn fallocate( + &self, + ctx: &Context, + inode: Inode, + handle: Handle, + mode: u32, + offset: u64, + length: u64, + ) -> Result<()> { + trace!( + "FALLOCATE: inode: {}, handle: {}, mode: {}, offset: {}, length: {}\n", + inode, + handle, + mode, + offset, + length + ); + // Use O_RDONLY flags which indicates no copy up. + 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 rhd) => { + if !rhd.in_upper_layer { + // TODO: in lower layer, error out or just success? + return Err(Error::from_raw_os_error(libc::EROFS)); + } + rhd.layer.fallocate( + ctx, + rhd.inode, + rhd.handle.load(Ordering::Relaxed), + mode, + offset, + length, + ) + } + } + } + + fn lseek( + &self, + ctx: &Context, + inode: Inode, + handle: Handle, + offset: u64, + whence: u32, + ) -> Result { + trace!( + "LSEEK: inode: {}, handle: {}, offset: {}, whence: {}\n", + inode, + handle, + offset, + whence + ); + // can this be on dir? FIXME: assume file for now + // we need special process if it can be called on dir + let node = self.lookup_node(ctx, inode, "")?; + + 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) { + error!("lseek on directory"); + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + + let (layer, real_inode, real_handle) = self.find_real_info_from_handle(handle)?; + layer.lseek(ctx, real_inode, real_handle, offset, whence) + } +} diff --git a/src/overlayfs/utils.rs b/src/overlayfs/utils.rs new file mode 100644 index 000000000..bf7d050a5 --- /dev/null +++ b/src/overlayfs/utils.rs @@ -0,0 +1,14 @@ +// Copyright (C) 2023 Ant Group. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +use crate::abi::fuse_abi::stat64; +use std::ffi::CString; +use std::io::{self, Error, Result}; + +pub(super) fn is_dir(st: stat64) -> bool { + st.st_mode & libc::S_IFMT == libc::S_IFDIR +} + +pub(super) fn to_cstring(name: &str) -> Result { + CString::new(name).map_err(|e| Error::new(io::ErrorKind::InvalidData, e)) +} diff --git a/src/passthrough/mod.rs b/src/passthrough/mod.rs index f4859006e..76d654caa 100644 --- a/src/passthrough/mod.rs +++ b/src/passthrough/mod.rs @@ -52,6 +52,7 @@ mod file_handle; mod inode_store; mod mount_fd; mod os_compat; +mod overlay; mod statx; mod sync_io; mod util; @@ -980,11 +981,7 @@ mod tests { use super::*; use crate::abi::fuse_abi::CreateIn; use crate::api::filesystem::*; - use crate::api::filesystem::{ZeroCopyReader, ZeroCopyWriter}; use crate::api::{Vfs, VfsOptions}; - use crate::common::file_buf::FileVolatileSlice; - use crate::common::file_traits::FileReadWriteVolatile; - use caps::{CapSet, Capability}; use log; use std::io::{Read, Seek, SeekFrom, Write}; @@ -1484,59 +1481,6 @@ mod tests { assert!(!fs.cfg.writeback); } - impl ZeroCopyReader for File { - // Copies at most count bytes from self directly into f at offset off - // without storing it in any intermediate buffers. - fn read_to( - &mut self, - f: &mut dyn FileReadWriteVolatile, - count: usize, - off: u64, - ) -> io::Result { - let mut buf = vec![0_u8; count]; - let slice = unsafe { FileVolatileSlice::from_raw_ptr(buf.as_mut_ptr(), count) }; - - // Read from self to slice. - let ret = self.read_volatile(slice)?; - if ret > 0 { - let slice = unsafe { FileVolatileSlice::from_raw_ptr(buf.as_mut_ptr(), ret) }; - // Write from slice to f at offset off. - f.write_at_volatile(slice, off) - } else { - Ok(0) - } - } - } - - impl ZeroCopyWriter for File { - // Copies at most count bytes from f at offset off directly into self - // without storing it in any intermediate buffers. - fn write_from( - &mut self, - f: &mut dyn FileReadWriteVolatile, - count: usize, - off: u64, - ) -> io::Result { - let mut buf = vec![0_u8; count]; - let slice = unsafe { FileVolatileSlice::from_raw_ptr(buf.as_mut_ptr(), count) }; - // Read from f at offset off to slice. - let ret = f.read_at_volatile(slice, off)?; - - if ret > 0 { - let slice = unsafe { FileVolatileSlice::from_raw_ptr(buf.as_mut_ptr(), ret) }; - // Write from slice to self. - self.write_volatile(slice) - } else { - Ok(0) - } - } - - fn available_bytes(&self) -> usize { - // Max usize - usize::MAX - } - } - #[test] fn test_generic_read_write_noopen() { let tmpdir = TempDir::new().expect("Cannot create temporary directory."); diff --git a/src/passthrough/overlay.rs b/src/passthrough/overlay.rs new file mode 100644 index 000000000..ec39e78e4 --- /dev/null +++ b/src/passthrough/overlay.rs @@ -0,0 +1,14 @@ +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-BSD-3-Clause file. + +use super::PassthroughFs; +use crate::abi::fuse_abi; +use crate::api::filesystem::Layer; + +// Implment Layer trait for PassthroughFs. +impl Layer for PassthroughFs { + // Return root inode of this layer. + fn root_inode(&self) -> Self::Inode { + fuse_abi::ROOT_ID + } +} diff --git a/src/passthrough/sync_io.rs b/src/passthrough/sync_io.rs index 584d2b93d..925bc2b2f 100644 --- a/src/passthrough/sync_io.rs +++ b/src/passthrough/sync_io.rs @@ -407,7 +407,12 @@ impl FileSystem for PassthroughFs { _flags: u32, handle: Handle, ) -> io::Result<()> { - self.do_release(inode, handle) + if self.no_opendir.load(Ordering::Relaxed) { + info!("fuse: releasedir is not supported."); + Err(io::Error::from_raw_os_error(libc::ENOSYS)) + } else { + self.do_release(inode, handle) + } } fn mkdir( diff --git a/testoverlay/src/main.rs b/testoverlay/src/main.rs deleted file mode 100644 index fb41aa124..000000000 --- a/testoverlay/src/main.rs +++ /dev/null @@ -1,128 +0,0 @@ -extern crate fuse_backend_rs; -extern crate log; -extern crate vmm_sys_util; -extern crate libc; -extern crate simple_logger; -extern crate signal_hook; -#[macro_use] -extern crate lazy_static; - -use std::io::{Result, Error}; -use std::sync::{Arc, Mutex}; -use std::path::Path; - -use fuse_backend_rs::overlayfs::{OverlayFs}; -use fuse_backend_rs::overlayfs::config::Config; -use fuse_backend_rs::overlayfs::plugin::PluginManager; -use fuse_backend_rs::api::server::Server; -use fuse_backend_rs::transport::{FuseChannel, FuseSession}; -use fuse_backend_rs::api::{Vfs, VfsOptions}; -use signal_hook::{consts::TERM_SIGNALS, iterator::Signals}; -use std::thread; -use simple_logger::SimpleLogger; -use log::LevelFilter; - -pub struct FuseServer { - server: Arc>>, - ch: FuseChannel, -} - -fn main() -> Result<()> { - SimpleLogger::new().with_level(LevelFilter::Trace).init().unwrap(); - let upperdir = String::from("/home/boyang/sources/testoverlay/testdir/upper"); - let mut lowerdir = Vec::new(); - lowerdir.push(String::from("/home/boyang/sources/testoverlay/testdir/lower1")); - lowerdir.push(String::from("/home/boyang/sources/testoverlay/testdir/lower2")); - let workdir = String::from("/home/boyang/sources/testoverlay/testdir/work"); - let mountpoint = String::from("/home/boyang/sources/testoverlay/testdir/merged"); - - let mut config = Config::default(); - config.upper = upperdir; - config.lower = lowerdir; - config.work = workdir; - config.mountpoint = String::from(mountpoint.as_str()); - config.do_import = true; - - let manager = PluginManager::new(); - - print!("new overlay fs\n"); - let mut fs = OverlayFs::new(&manager, config)?; - print!("init root inode\n"); - fs.init_root()?; - - // let vfs = Vfs::new(VfsOptions { - // no_open: false, - // no_opendir: false, - // ..Default::default() - // }); - - // vfs.mount(Box::new(fs), "/")?; - print!("open fuse session\n"); - let mut se = FuseSession::new(Path::new(mountpoint.as_str()), "testoverlay", "", false).unwrap(); - print!("session opened\n"); - se.mount().unwrap(); - - let mut server = FuseServer { - server: Arc::new(Server::new(Arc::new(fs))), - ch: se.new_channel().unwrap(), - }; - - let quit = Arc::new(Mutex::new(false)); - let quit1 = Arc::clone(&quit); - - let handle = thread::spawn(move || { - let _ = server.svc_loop(quit1); - }); - - // main thread - let mut signals = Signals::new(TERM_SIGNALS).unwrap(); - for _sig in signals.forever() { - *quit.lock().unwrap() = true; - break; - } - - let _ = handle.join(); - - se.umount().unwrap(); - se.wake().unwrap(); - - Ok(()) -} - -impl FuseServer { - pub fn svc_loop(&mut self, quit: Arc>) -> Result<()>{ - let _ebadf = std::io::Error::from_raw_os_error(libc::EBADF); - print!("entering server loop\n"); - loop { - - if *quit.lock().unwrap() { - break; - } - - if let Some((reader, writer)) = self - .ch - .get_request() - .map_err(|_| std::io::Error::from_raw_os_error(libc::EINVAL))? - { - if let Err(e) = self - .server - .handle_message(reader, writer.into(), None, None) - { - match e { - fuse_backend_rs::Error::EncodeMessage(_ebadf) => { - break; - } - _ => { - print!("Handling fuse message failed"); - continue; - } - } - } - } else { - print!("fuse server exits"); - break; - } - } - Ok(()) - } -} diff --git a/testoverlay/Cargo.toml b/tests/testoverlay/Cargo.toml similarity index 78% rename from testoverlay/Cargo.toml rename to tests/testoverlay/Cargo.toml index 9aac03820..023c13edd 100644 --- a/testoverlay/Cargo.toml +++ b/tests/testoverlay/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fuse-backend-rs = { path = "../../fuse-backend-overlay.git", features = ["fusedev"] } +fuse-backend-rs = { path = "../../", features = ["fusedev"] } log = ">=0.4.6" vmm-sys-util = ">=0.4" libc = ">=0.2.68" diff --git a/tests/testoverlay/src/main.rs b/tests/testoverlay/src/main.rs new file mode 100644 index 000000000..129a7fb6b --- /dev/null +++ b/tests/testoverlay/src/main.rs @@ -0,0 +1,237 @@ +extern crate fuse_backend_rs; +extern crate lazy_static; +extern crate libc; +extern crate log; +extern crate signal_hook; +extern crate simple_logger; +extern crate vmm_sys_util; + +use std::env; +use std::io::{Error, Result}; +use std::path::Path; +use std::sync::Arc; +use std::thread; + +use fuse_backend_rs::api::filesystem::Layer; +use fuse_backend_rs::api::server::Server; +use fuse_backend_rs::overlayfs::config::Config; +use fuse_backend_rs::overlayfs::OverlayFs; +use fuse_backend_rs::passthrough::{self, PassthroughFs}; +use fuse_backend_rs::transport::{FuseChannel, FuseSession}; +use log::LevelFilter; +use signal_hook::{consts::TERM_SIGNALS, iterator::Signals}; +use simple_logger::SimpleLogger; + +#[derive(Debug, Default)] +pub struct Args { + name: String, + mountpoint: String, + lowerdir: Vec, + upperdir: String, + workdir: String, + log_level: String, +} + +pub struct FuseServer { + server: Arc>>, + ch: FuseChannel, +} + +type BoxedLayer = Box + Send + Sync>; + +fn new_passthroughfs_layer(rootdir: &str) -> Result { + let mut config = passthrough::Config::default(); + config.root_dir = String::from(rootdir); + // enable xattr + config.xattr = true; + config.do_import = true; + let fs = Box::new(PassthroughFs::<()>::new(config)?); + fs.import()?; + Ok(fs as BoxedLayer) +} + +fn help() { + println!( + "Usage:\n testoverlay -o lowerdir=::,upperdir=,workdir= [-l log_level]\n" + ); +} + +fn parse_args() -> Result { + let args = env::args().collect::>(); + // We expect at least 5 arguments. + if args.len() < 5 { + help(); + return Err(std::io::Error::from_raw_os_error(libc::EINVAL)); + } + + let mut cmd_args = Args { + name: "".to_string(), + mountpoint: "".to_string(), + ..Default::default() + }; + + let mut i = 0; + loop { + i += 1; + if i >= args.len() { + break; + } + if args[i].as_str() == "-h" { + help(); + return Err(std::io::Error::from_raw_os_error(libc::EINVAL)); + } + + if args[i].as_str() == "-o" { + i += 1; + // Parse options. + let option = args[i].clone(); + option.split(",").try_for_each(|value| -> Result<()> { + let kv = value.split("=").collect::>(); + if kv.len() != 2 { + println!("unknown option: {}", value); + return Ok(()); + } + + match kv[0] { + "lowerdir" => { + cmd_args.lowerdir = kv[1] + .split(":") + .map(|s| s.to_string()) + .collect::>(); + } + "upperdir" => { + cmd_args.upperdir = kv[1].to_string(); + } + "workdir" => { + cmd_args.workdir = kv[1].to_string(); + } + _ => { + // Ignore unknown options. + println!("unknown option: {}", kv[0]); + } + } + Ok(()) + })?; + continue; + } + + if args[i].as_str() == "-l" { + i += 1; + cmd_args.log_level = args[i].clone(); + } + + if cmd_args.name.is_empty() { + cmd_args.name = args[i].clone(); + continue; + } else if cmd_args.mountpoint.is_empty() { + cmd_args.mountpoint = args[i].clone(); + continue; + } + } + + // All fields should be set. + if cmd_args.lowerdir.is_empty() || cmd_args.upperdir.is_empty() || cmd_args.workdir.is_empty() { + println!("lowerdir, upperdir and workdir should be set"); + help(); + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + + Ok(cmd_args) +} + +fn set_log(args: &Args) { + let log_level = match args.log_level.as_str() { + "trace" => LevelFilter::Trace, + "debug" => LevelFilter::Debug, + "info" => LevelFilter::Info, + "warn" => LevelFilter::Warn, + "error" => LevelFilter::Error, + _ => LevelFilter::Info, + }; + + SimpleLogger::new().with_level(log_level).init().unwrap(); +} + +fn main() -> Result<()> { + let args = parse_args()?; + println!("args: {:?}", args); + + set_log(&args); + + // let basedir = "/home/zhangwei/program/test-overlay/test2/"; + let upper_layer = Arc::new(new_passthroughfs_layer(&args.upperdir)?); + let mut lower_layers = Vec::new(); + for lower in args.lowerdir { + lower_layers.push(Arc::new(new_passthroughfs_layer(&lower)?)); + } + + let mut config = Config::default(); + config.work = args.workdir.clone(); + config.mountpoint = args.mountpoint.clone(); + config.do_import = true; + + print!("new overlay fs\n"); + let fs = OverlayFs::new(Some(upper_layer), lower_layers, config)?; + print!("init root inode\n"); + fs.import()?; + + print!("open fuse session\n"); + let mut se = FuseSession::new(Path::new(&args.mountpoint), &args.name, "", false).unwrap(); + print!("session opened\n"); + se.mount().unwrap(); + + let mut server = FuseServer { + server: Arc::new(Server::new(Arc::new(fs))), + ch: se.new_channel().unwrap(), + }; + + let handle = thread::spawn(move || { + let _ = server.svc_loop(); + }); + + // main thread + let mut signals = Signals::new(TERM_SIGNALS).unwrap(); + for _sig in signals.forever() { + break; + } + + se.umount().unwrap(); + se.wake().unwrap(); + + let _ = handle.join(); + + Ok(()) +} + +impl FuseServer { + pub fn svc_loop(&mut self) -> Result<()> { + let _ebadf = std::io::Error::from_raw_os_error(libc::EBADF); + print!("entering server loop\n"); + loop { + if let Some((reader, writer)) = self + .ch + .get_request() + .map_err(|_| std::io::Error::from_raw_os_error(libc::EINVAL))? + { + if let Err(e) = self + .server + .handle_message(reader, writer.into(), None, None) + { + match e { + fuse_backend_rs::Error::EncodeMessage(_ebadf) => { + break; + } + _ => { + print!("Handling fuse message failed"); + continue; + } + } + } + } else { + print!("fuse server exits"); + break; + } + } + Ok(()) + } +} From b6dc040a478c7ebe3ea9afc19bda0b04dc2547c8 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Thu, 4 Jan 2024 19:26:09 +0800 Subject: [PATCH 3/5] CI: add xfstests Add xfstests into CI for overlayfs, it will work as Github Actions. Currently not all test cases can be run, only about half of `generic/*` for the Fuse Overlay FS, some test cases are expected to fail due to missing function implementation, little are unexpected due to potential bugs which will be investigated laterly. Signed-off-by: Wei Zhang --- .github/workflows/xfstests.yml | 20 +++++++++ Makefile | 7 +++ tests/{testoverlay => overlay}/Cargo.toml | 2 +- tests/{testoverlay => overlay}/src/main.rs | 2 +- tests/scripts/xfstests_overlay.exclude | 24 ++++++++++ tests/scripts/xfstests_overlay.sh | 52 ++++++++++++++++++++++ 6 files changed, 105 insertions(+), 2 deletions(-) rename tests/{testoverlay => overlay}/Cargo.toml (94%) rename tests/{testoverlay => overlay}/src/main.rs (97%) create mode 100644 tests/scripts/xfstests_overlay.exclude create mode 100755 tests/scripts/xfstests_overlay.sh diff --git a/.github/workflows/xfstests.yml b/.github/workflows/xfstests.yml index 2e528dd48..57bec3c0d 100644 --- a/.github/workflows/xfstests.yml +++ b/.github/workflows/xfstests.yml @@ -32,3 +32,23 @@ jobs: cd $GITHUB_WORKSPACE sudo ./tests/scripts/xfstests_pathr.sh + xfstests_on_overlayfs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - name: Build overlay binary + run: | + cd tests/overlay + cargo build --release + sudo install -t /usr/sbin/ -m 700 ./target/release/overlay + - name: Setup and run xfstest + run: | + cd $GITHUB_WORKSPACE + sudo ./tests/scripts/xfstests_overlay.sh diff --git a/Makefile b/Makefile index 2637aeb1d..bde1a50be 100644 --- a/Makefile +++ b/Makefile @@ -54,3 +54,10 @@ smoke-macos: check-macos docker-smoke: docker run --env RUST_BACKTRACE=1 --rm --privileged --volume ${current_dir}:/fuse-rs rust:1.68 sh -c "rustup component add clippy rustfmt; cd /fuse-rs; make smoke-all" + +testoverlay: + cd tests/testoverlay && cargo build + +# Setup xfstests env and run. +xfstests: + ./tests/scripts/xfstests.sh \ No newline at end of file diff --git a/tests/testoverlay/Cargo.toml b/tests/overlay/Cargo.toml similarity index 94% rename from tests/testoverlay/Cargo.toml rename to tests/overlay/Cargo.toml index 023c13edd..751ddd7da 100644 --- a/tests/testoverlay/Cargo.toml +++ b/tests/overlay/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "testoverlay" +name = "overlay" version = "0.1.0" edition = "2021" diff --git a/tests/testoverlay/src/main.rs b/tests/overlay/src/main.rs similarity index 97% rename from tests/testoverlay/src/main.rs rename to tests/overlay/src/main.rs index 129a7fb6b..b9492506c 100644 --- a/tests/testoverlay/src/main.rs +++ b/tests/overlay/src/main.rs @@ -52,7 +52,7 @@ fn new_passthroughfs_layer(rootdir: &str) -> Result { fn help() { println!( - "Usage:\n testoverlay -o lowerdir=::,upperdir=,workdir= [-l log_level]\n" + "Usage:\n overlay -o lowerdir=::,upperdir=,workdir= [-l log_level]\n" ); } diff --git a/tests/scripts/xfstests_overlay.exclude b/tests/scripts/xfstests_overlay.exclude new file mode 100644 index 000000000..db58a8e17 --- /dev/null +++ b/tests/scripts/xfstests_overlay.exclude @@ -0,0 +1,24 @@ +# Exclude list for tests that we know are broken in smb3 +# +generic/011 # Broken: dirstress 5 processes. +generic/020 # ENOSPC, suppose to be FUSE compatibility issue. +generic/023 # Rename is not supported currently. +generic/024 # Rename is not supported currently. +generic/025 # Rename is not supported currently. +generic/035 # Rename is not supported currently. +generic/078 # Rename is not supported currently. +generic/089 # Rename is not supported currently. +generic/099 # Suppose to be FUSE compatibility issue. +generic/184 # Special device isn't supported due to 'nodev' mount option. +generic/241 # Rename is not supported currently. +generic/245 # Rename is not supported currently. +generic/375 # Suppose to be FUSE compatibility issue, about posix acl support +generic/426 # Suppose to be FUSE compatibility issue: 'open_by_handle' +generic/434 # Special device isn't supported due to 'nodev' mount option. +generic/444 # Suppose to be FUSE compatibility issue, about posix acl support +generic/467 # Suppose to be FUSE compatibility issue: 'open_by_handle' +generic/477 # Suppose to be FUSE compatibility issue: 'open_by_handle' +generic/591 # Broken. +generic/633 # Suppose to be FUSE compatibility issue. +generic/697 # Suppose to be FUSE compatibility issue. +generic/736 \ No newline at end of file diff --git a/tests/scripts/xfstests_overlay.sh b/tests/scripts/xfstests_overlay.sh new file mode 100755 index 000000000..ca76797d8 --- /dev/null +++ b/tests/scripts/xfstests_overlay.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +current_dir=$(dirname $(realpath $0)) + +sudo apt-get update +sudo apt-get install acl attr automake bc dbench dump e2fsprogs fio gawk \ + gcc git indent libacl1-dev libaio-dev libcap-dev libgdbm-dev libtool \ + libtool-bin liburing-dev libuuid1 lvm2 make psmisc python3 quota sed \ + uuid-dev uuid-runtime xfsprogs linux-headers-$(uname -r) sqlite3 +sudo apt-get install exfatprogs f2fs-tools ocfs2-tools udftools xfsdump \ + xfslibs-dev + +# clone xfstests and install. +cd /tmp/ +git clone -b v2023.12.10 git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git +cd xfstests-dev +make +sudo make install +# overwrite local config. +cat >local.config </usr/sbin/mount.fuse.testoverlay <>/tmp/testoverlay.log 2>&1 & +sleep 1 +EOF +sudo chmod +x /usr/sbin/mount.fuse.testoverlay + +# create related directories. +mkdir -p /tmp/testoverlay/{upper,work,merged,lower2,lower1} + +echo "====> Start to run xfstests." +# run tests. +cd /tmp/xfstests-dev +# Some tests are not supported by fuse or cannot pass currently. +sudo ./check -fuse -E $current_dir/xfstests_overlay.exclude + + From 119ea2c46bebd850c69f79c558060372d2090225 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Tue, 21 Nov 2023 15:11:45 +0800 Subject: [PATCH 4/5] docs: add some documentation for overlayfs Add basic introduction for overlay fs implementations, also architecture design are illustrated. Signed-off-by: Wei Zhang --- docs/images/overlayfs.drawio | 242 +++++++++++++++++++++++++ docs/images/overlayfs_dir.png | Bin 0 -> 192193 bytes docs/images/overlayfs_non_dir_file.png | Bin 0 -> 171958 bytes docs/images/overlayfs_structs.png | Bin 0 -> 172698 bytes docs/overlayfs.md | 60 ++++++ 5 files changed, 302 insertions(+) create mode 100644 docs/images/overlayfs.drawio create mode 100644 docs/images/overlayfs_dir.png create mode 100644 docs/images/overlayfs_non_dir_file.png create mode 100644 docs/images/overlayfs_structs.png create mode 100644 docs/overlayfs.md diff --git a/docs/images/overlayfs.drawio b/docs/images/overlayfs.drawio new file mode 100644 index 000000000..09c5b0b0d --- /dev/null +++ b/docs/images/overlayfs.drawio @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/overlayfs_dir.png b/docs/images/overlayfs_dir.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed7acc29b2205fd9f1620375154856b97b3a5b8 GIT binary patch literal 192193 zcmeFZXIN9)@;{DrumGZT1wm12K$PATg#!oz(pwNjIsuUm3DF~n2*{y#1Sv}IouJaB zBS?oJy(6I+LXzJOD&W26-tWEt=l}A);5^%%z4n^*na|9cnKk=4p-yCXySCA%eUWZiRj)P0nf-5S!niYy6Nph%vu$ z+ko-7+l(6Zd{w>=qzu2c2c7J z$rlaJh-lMm&=EJo&U~aUX%Lcs&Zy!X#ipn1?9a9qIdrk1ki(HgUisNaI;&$TUR0<} zPDh45jg^2w-L zligUSj_;UMcBTefD`dnpYJanmFpmYl&By#TdMo25-c>X?x4T(8sF?n*3mNkEniv0g z@kIZudGnLe?@rG;VkYf%`(6d>pS~Q7B%Tzn6S^p$7kbw6#nY)X+m>iDg^fm0tK);3 z)HMhpsSDak>^Z@0VU09%PfR9{#|A1$?ma1_Q*imZPV#ixr8#N+S>QT#B2DTCKXJ5v z0%vQG=HS%By&75`2zjC14b2*J{v>Tm#y7_7U1!cVU2K_Z2yzj>FO2f|X30TZAkUFP zLY2d8cDm)*lg_#(S&z$WRODeG0cbF9P%iEQS_YPz;xYE;B9yD}u~_QV}tj&|d0W z+Ppok*OJWZt3=X&vFBPL2VeE8&OgDVvWWuIk|A%Tq$H||IIwjsU@*ko5_$4g=9HA! z8V!+;Avpy_sL$L7L!!aCW99g-YY8Vwppp^C#Jywq=omjq7<*l)FaMKe)MmGyDTahq zoZV{TR1I8?lK4Ic%SFF4LdO)7PYzTv-8m*wZ>&Mx`u=JGOAbYRy>0>h@-fT}E^%u0 zCn6KX6{LA^DW|PXKgJaH8j@r>(hC=~|MZ{d;(mU~JXDimct?^JJSW!@+g(6heMH7LqYMvwp%t3=F~P=CLtM@y{af zpVhyv8*u&OoDbPj({4eiI9=w~$D_ycCN!k!<|f0}4ZfTE$kfq8>PNoueJ815$1A3s zK0&oj3L=gdih6ZB=H``4+O%c#r|8r_*7hlQC|x}5`n>-ZO$Fn<=MoJI4UG*a8@wi| z%o*3|oc(TbE`%>D%f7Kb9iaUO@174Fhkg5h3&ut!8h-ib(vd=jY@`KQVym9r|< z5g~jgx?b-#V{_EYE_#QheUe69LusKzABmtjD+BgoQ*RVzeAhUAMIxdyqBKIX)uz>? zmDSSHQpED^jJ_ot#Wqvg>K)m|E2n|WN_}gd`9VwhURV~t=Fsa$H)o<+S$QLEZ*OI0 zXdQp6nsvSJULU59=G`?NC7m+fyRK!n>9$W7Ex$zDKDe%WJ!6=()V!3sl*E?t%WmJN zMcqYy*N|QQEtRd0i(-8g*Q{C6uBTo{U1zXc{w$ukekc5mO*-?NPrV8Gl{y;{vs^5S zBAJEO#>3LXqQf?Zp$wXBhC#WJX*zkOO4;|a!AAE+bflEM-?^o43$HkyyVI-^kQ<&^ ztZjwpbWOZlrC4>xq;8dSOmeKPYHdq#9=)`_)w;R3mABygIdh3(VQvvL0O>adql1~Pnpuk1?C6VKZsNg`s7k2nyB2BmY-hUgHb!Frl58y!|0Bbnq0g={H3_a zm@n~TBAGEF?IIm|vE^|)Mlt%g4BHE`N-G?c%O2=IG|DTqFZY31Iz-x+*1zjnj4e&N zK2kMU)xVo}@=H_K!y>iP4jrRZfsdjCZckS(N%eJh2#(}^UQLy;U2_B#AkC21{}TG_ z^2X`GRtxJX-cxr1I0Fm=C_*Gd=t3@q1c!i>3|^|7EA`RFWI@y&)jicm5>{XKvz-&I zdhmE-exTE>(|As0E?ZDL+AMlVP)#u3c*O*r$7%wp>KWx6W2h*t@^#&DVyqsh-0{-) zc;l_@IcjDtY|a$KY$ZdLbS$Y@Ce#bM`DllCb9ikly8Nko-j3j!l|@eTw`P0Rjh5#2 zfT`lf%#rBT_|@v6@{z8-&?&u@oo?i4@*H9e&(j+f`*F0xhk4=*$0_kM-;!n;<335! zJty-gjUsa+w;{hoo=a23ZqJZ#)|hpaeUjm-%=E1*7p_S^_MAUiMi<|fcYi0q^UsbG zcE|OoLTSM4IH3=MH`#jF0u}vl=e?|9wPe2Z()J~J&+>d^&yIPH1$Q%B;H9Rf2Hhsv zK-P0&TzN7{406Iyy}jx`{r*g+azE{G`ch!7vaPa>@^$64aFTGni|(Q4uDi_+9_JzF zc@mMKt!1mVxZMYLzA>d6x}@x)%BNN>JMOjDw?4vG~Tr2w#mG&bra|f-E}Xt?B3bE%vjl2zN^WW=m)v&iyhl-c`d8-1*A)4 z`Hyb8h|bh>1$T~xNyS7b=q2YzBqzc2vv}IimZ@D$y5_1C-D0ZEyO^|m-E(Ui>RjRB zeN0QJF6FJpaa@c0ANCg!f1E#qX3$~qjrr@^OucsS+YfKg^)6=!#BG;|S9~vD`2Kix zv!6LbWVX{oC|ts&jsJmeQ%?WTZ1G&zYApR#t2yJ=(a}mow0LCB6;COUr|E`I8ro%6 zZuio3^sv?!E$4+`vhm}h0u7AR3~qvt3`!h(%CDLZp&w5K58!SIq4n$)Fkd-$rZm+*k;^Wd2%}LGYHQI99YWOn8Ys@E_`#L8qXTg-U zu*@d0vC49(E~m>BmU}g&sIIlZR`UzyWX;KFv0X^c$b*u%KE0V8PddcP|FpNsDtnhV zs4{9;vh}I#jl&8;7@9rgSWxSO894Xh+?dpqTiF+nzP{zFqopMbxsiR_tLh`ZHg_kI zG9&}41hyo%X1w2h`rRw##p`9SR*1 zGe6!Fj`Vnsy{Qmj)chJb29@hu7$0)#nKJLa)TP>C zVo?;I6YJ4Q=(|~Wd6VL#b3JGGmaQR9Yw2@S zNjo*Pwckheb~BrjbwCcL=l9z1`60MMtW17GJ~Oij-40MDzB0L_90e@)&t2SOY9@(vOLkh$@N5)tNa@ z!Rq=@rIz9IZ)^n;i*|}o^6z4|p|g7zR+Ei5bX>{g%JIb9UOqZGibKjkN+E8kul!I= zjffYxrX)HRU_(R-Tpa`cSb#qwBH|R{Uw2NNNFn)kO#vh&L`1^kVPA-d+)Aoj`=`oP;pV@vspU&>pkQ$Q)o@tcQ%QAn(((qzEyWq@=m>ARR8ESm) zc{%U0+sqoznp0A$7#I|t5dKsD;(Hgt&mTTBy)ft}6ACZ(k1kpJJs|3LU36aHrf z|FeSsS;7CT;D1)||ECoUs>At4B<$0;3$}>WgrDNJ9UALKm)G)_>q=7JB1Q5{hVLw$ zi-nEN6saWQ+y2VincB^-Hr9Q+gfDutgk45$!L;w7SIwowMpR-s0l}8v$-77wI>yGA zG6}TB?^%)CM8njBzh1`+tHftdxh&4wHJ`c2ruwTb9bbS$G|3sDP?~yUt@S=RL?T{QvWH|$98V$?t-ZX9xHu1Y%&5bS zj8}w&3{+b}qIG@&12Mel&#rtBg--yi_L$SkoS-#c*3AI=LUY zC6|j>f*HaF@Flo=V0>?<=^C(tRA2>fjbHSJ2n!Z`bJ->KuoHj0nB3cmXHlOdi*v$( z{L1|mOz?K{HS+W>b+}M-Q}>y*ULNP)+p8lQXE~L)(Y1E{ z4#b9j77<~ccD4p5)>>WSJb>3q4c66vjAg&wH5na@vP<|G?g`Pr-^xXFDZIZznFGAd zakg{HvfQ_(lAa(t16fh@F?(F}Yw6K|4LN%kW^ZHs{y&yrgR@t+mp8UxXX?wryWWzU_ zF5zcs;);8ZPKgKnvd>vwp_zcsm2pgra2?X&EFYt3Zj|hspZI6D|BnA-oKnx$O0R3a z6#!z6j@e@JWxK>X`NGNfyqM^7)O}4P@bn3rrqP`+8jB}719;N;TEQLkF66`40xRdR zFV!J5vM($j1_BT!*7vOykBRr!Zf^4oxoTW(OxRL3OC)x<<~Aq6BF!68SzT_j+GbK(#2@`D z9%rPpe(|(zC2cU-fD_=XAmudd<#ko=M~`b@1}~$&R8=(x=>J5*`(pR@td~)aSnWzs zj+@v-38PIzA;7ag{k~gXR!>vje++IwSyHR{9PGuUOZ9Uc=Hdh#?khPmTSW!uX6XPA z3#up;ia7PR%4U|#Nel{;Qrg$bvx?9n?6Ef2exw;f-f(Wkm`c^jX!Q?&{p4bS1@U>N!9sdl z$#6>Qqw7IpjN z<@xeo>CRP8t`Z&gwbTA!eWiXzyU+`UqmR$K#3&2+}A2;%#$`)>9ft zc^9ah(&@v*tRRtC?L>93aW$Iz1;Gm(OxCWL5*Stz)(C=g&>cR0bVgg^8Bp|yxODN+ z3E{qU*5;`W4`}XD92KNKhb)fCgw%a*^U|QurHD>&K6mTzDmxG!1e(&^;V`+S;ND8~>_pYZOGs zA(SZJWHYl=G?L_~?Ztg$z4Zpi*4qr|Fa0gLG1@7C<89#ahp`9TD1Z?=+^MXlsw`{A z0y0Kl?!Kj0I$*9Bg*lj-fJgg{l)xL03soFqf4_gIxrzsH(cNt9=k0HQHNTenrCm6h zvP3vy*s39qe+QtCy>3e2tcc)}sr$^;9p$4k#ypj$e@csJ|9Xds*X&L)$-t8G#v2 zF#qig-aM}mDJ%F7Wr*?B^ykX-zo^I$0B?mCv4F`CB+j`zAr!ZWXxd#`*{$mNO{;?Z z9MDRk*W?ExAH66rg>%O(A?-hdyTFUH&_e?tY>gj8AK^tF022TZB>=2Z03br-Rc-Ni z5p;40(=11K0|&qxar&ksDnDc|$cW|31OEll^Ne?;9$p#X^zmN@!zwtg7 z+4gX2mhHw7_Q8XxN_rkKmp>lqX!80ZbadXsh47yde_1;Qvct6TLm1B6Tt0XTUQDcZ z=A>_veV4A@O}~0#`0D|!sNWn$OgOMRsat*2KYZIEdpKx`)uvyA$IA-u9$K#x9bEKO zo#-oG3QoaAS>F22wm$;ksgh@HEFDmbx=wHlKO}{Z(OX-n zV7vS(1?UFJ;iBY22sg}9hv=Ppre7N?sV;-{)N|Zi-RMz&6ca1YI<)|ETI;8=5>@~B zDxe`0Tsd?)r1WF}rj`yGL#y|A9+iPLij;w+P!_>K_@4f5*HMBX62Ewlxes zujomC=-&oq;M^H~_~n7z@@g?1kt0hYY#&lEc$A zwb^FO>xh=B1-hFytGZxVOW@}0mF}jL-2vP3q?rQ});k_A5w2a`0K;MrZvy&-!!$oHphql6iyW_%BKU zQTwoUErx9n{IK$fG1rq4t8EV9oyK$76+KtOeZJZm3S_~K7;kIqyyz}pSDgz z;;cS6Vx3ag=cD9#UJaPLn`}p`8T5!A%+?94?BkDz=Byl_92(t2z*&1#(M)60Y34+v zH2sfw?0Qq`2loA;U#hkqg0A0*`(=n6WN|HHb02W1ZysR&i^SoS)7J+P+|^qT|LU2K z`1O$4DL_T37~?^LPJdg+V1YdA%8uMi_ay+FUtWlSX?+PXnw{GupE7R|s5&*4iRufLEgJfZ6+tk5Nwv%)`!odW<{J z4`5HMPX%BD$;p>45l3I@zNIu7-S)Yk<|kNY)nvm=b>A(kyI%JkxJmmkc~%PnIlno# zBZMMMEtD%yKNc-K^0h?av(y3HFM(IYcZG_`-jI?lY1%5EvKrj{rOTzW)@c&!ZC+}n ziU%{4pnK!=D*xDV*E4FrKU?(8L)Ha9tm_5-aFL@8#l zHh;X5qh#%Lzz>(xEv)vt2u#-&KM>LS%hM;8ME8UdlBFLm+U=(NUJ-p}&&3~i;2V_2 z5?wb!E)=B7vu>>|pL6lfVc#mtJh0W4MuZ*MpG9o+b@h8a;=`fc4>V0X}JB1Vqja*j6^J1TS;g= z1*l!`NHu!8mkx2qTfGsKDM#`hpZX}9 z6m`SoGm)(eBO=Ei1U2YTbs%0kLyO3{chBg6AcTtoQMi(l>e>rqT;|B_@{M>F#AAoF z(PV#H1nF4x(7@~}V|{j_FM`OF@P;2F>odq2jN%zgbFqf)g@(sRjH*T5*ZXbf?$wmW zMGuqq3ru9hy9U{Y0s>i)HGr(tGWg&6`X@hnh8Hh6N`qwSE4W`XT=o?k~%;E0>Ri8TF^A zpO?FRdlR?68{FY`(o3I{O3Qg0!AI~#KwQxP%8r-#VS*>9o`d}#MErCPQkQ|WGf^s? z8`#EAQI9gYc-c*Z^o7b{XE1d#sE=H7S5SLY_2RSg66DL}HHY=Nf}cfDY-G#hq6 z2yXihe`e^^Z9xcbw6>T?&7dF&GPjg(r);ZstCS}4zv~zS`RPf0fgY_4;O`Szh&?HTUJ*AtX^U;fq0k3Sk%h2mzNHYHX3Cof&gj*wmn!!ctDcG89l+2925hh26M&8Dihl$0WXAYK= zezd*uwPi_tCy-oD3YEq+CxL~9Y$P{4LTV)qlt9gVk3~t(mR5a=Fh#%Wx*RHn9@Af)+H#ThJ^stMuK*s$fE)M= zH{qp&B(B-NDDRw`y+Z5@uQpFxU~KRj|I?)hq`(tb0@PgKi43%e1ew~&=wj|_ZBD2J za_9^qp{qlabeDKCy3XJA$6L)ZNw2=zk3Y?lh&@GM0=}we@}?e6)EMkyxDXkiKe;Oz z?9|umHF!xx)d))^@VV=SLqq0mhke7QqT(B#L>7bKg%1h^}TI6Xy z*k=c0vsj>KNDgJk>+rn1!D!6#!uaAv22i6>kni#(pqh}zG_ex4aRCl(UHMU{GpqFb zK2sHj2cht{aftb4Ok;0a3!X92RrBUV4Y87Q%J-QXuMn=^Y&w$cJiU@}_B2?(GQE*v z`l$tb)@Hb`yI8NBof&>W5zV_Wq**DN*~Mf%?SBQPIURbEP0wzLOVfS10-9#Mvr?G2 zkj-f&Wr>jpf%1lS?Y4d9O8d~IBsgMPmN@k(emp?Sfgi&1PFe%18ZpqcUZR3EgrSL) z1!hQ&Sym(2EO&xNb+;p3t5>{JSEFyAk4O3cb!|R`z1qit&82rxJ#{>?#nZ8=3~#3? z21bJN*<4&A1XPn*&!WEi7h)}I6Yj~9avj8>#*DO(b8OGc)n)Gmm7hA*U^IYaZ7_nW z&<^Ivvv%yjlASAtR!h_gq@|-pJO}T}$u)u|qgOlB_C71?pB@ClxQ=ZktPvFN+BO@O-8AymTO(o|8?Ge2z9Y_Hw8jk_aS^LRd3*dTJ>~q+*0I8|t8KPC7$1e{ zyzzH!1C514O{v=Bn@Kb$q`6QUU`Agv4#-UqhHcR-dJHb2jzj6%hPg?=ASIc!C@QpRjr32X*>kd?DDHmNu>78d(&t zgVmUi4Ayw2!jk61Gzq;;fi27=87&|JxX@n8YdsD>%0IIgzI|X)ToH-%$X?Yl3@p!+ z3M_9j0nKc2jmI@(QZ#8}R3?NZJ9nPBy*oA^4J_E`3NXM09m`wZ2y%`7tg`oLOnL9> zsuxc_`+6Hgoej%X_;qSFMI&@wcV3~I+JORo#Pauu<<1(-fYMQlUoi>qN|SDeGZ0tUa8(%wu`Va0dI&kT?1d77ofw|Pd5fuArj z4Zr_ruC#J2x+veWAAK@D`Fh_cXEaT7k^K(mh!|)d1ldOXG2&_8iS8%!9lY&V|By7! zy38Bvm_Y#qx`11M2Slmi$!cR=Svt{^xlc*f`@gwf95>uDQT8RqTMV8jUc zy%VriHrD3n;091o-r4oGXUL3e@@AQ2afwNZpMC?Seww(HwJ3V*if`DB0mlNURsQ?S z4PGnJHeHKf3`%yBwL`jCi#ERcT^*w%E3GTyXE*jxpKexy+D_LfW9K+&zTi6t#UgB* z6jo~FSS0va-kk$$SGtQpF1{^IT#*$)Ss{%!CMIx&Nn!zWV5h8e8%?HTxJ8i8VrECo z=3tig($WU`sk{y>C(Vfm-k`t)2DHE~C}eN8rBv8*h>MFR9sKfK=~Pju{m!Q_Ie|wR z0k@!<(=@m8&V}d0`NHfL0yXuexZ8^It2yPwPd|l2&`A%?kbqvQ`#ZMc3kUV$d)-f+SZ1YIL`b!= zUvs*{fMlw43x0?M$Die02-ehP9YMZ8Wk>ExG)s|(EDSSynVHSMj*+6+ZC%}{8E~|1 zt@H2HIS(XLKxs}iZpn0NtjGYuXh`tga2LwQYoD;`9(GJxHLgw^ zfz->37R!L$O;^Xob`E#+X!=0B%yJ?f=C-F+bDfJ&yDDRB&1#o*$On>iBt$WuU+d{g zIBDytK3*a?ZuF#D4KjW@##I+$SOi_9%Cl_bp%rAjHt{dZYVtM0KhZ)$QsgH5LtXlyn^%hmnry+~2MS$Na1AEoU( zmjRTpF3L5o!(6b3LFJ42ch~!E7=b^UpQ#Yw1R8)`EeL>rrvems8eu;x zW0?Ijeg(k#3GZ<#>1!dYNd3WF81|W=iO)Bh6PrVFi}y)f9oO{U<=`_Tr%v&0o!5LD z?Sdax8GD_&mra(aSH6dax0c3#+=#K|mIC2P}A-r=2~|ok|}b4RAC=Txa2Fju=66)W>W-$VDyD9C3sO{WEND{L*Jr1DKTN$Bmttw-COEf!y(%ds(-7DBf7%GcXx&cJ+QebmX zK}*^Xd@fQXENVg;aC*IR(G5nKGAOGukELZk)f(6Si>eJqu8iqF)?L5*j~D@=h9Oca z?`T=^M`0nF|664b*wf;^BkT)#v$IlqSIwu|I|wGSnL+=$+BB(cfk=zG+>s%px-2co zvf7M^NNGIM<-BJvG}czE7Jt*g=Dpj>cf+lkz&%9BURte>QSPMtP<&d=HTEia`gW$9 z3l1%COX2NyG}pMKHoBrlbVrAs$piJ5Y}Ht3P~;<==SSaIT=tbKOEcnn%AR?vfyQOw zG_{Wxn)G>g=C)m`sJWuR!re_Bkrr}VZ!wmUUO_KRO>8XYeFSWqWr;~ULETNSXS|hM zaI;^#eHNDl{2*Dw& zOLw#nlC%RN6`%Ha@G=8)`!1~V6GG@4eWa|Mwx*|Py6N~UXsMQ1(0R5>xd2BjJaRWb zyRQS`Q((KBE1t5HX*3H4UAecCv11h-$zVSa0@lD9SI3FJ1MEU#vtB+aK$;y}RarN0 zz1rsT1hkJ2eo4}3Ax{QVNCwyRRuk0XyfdB#~)JvwNa6J%I?mah$E`HFm(-|Fm=^jft%6gnYw95a}j{}(>08KGqUHlE8%M1iq+|U20nEb3n z0rhVlxDG^o5>J+9JoT zrK-sBtL!Jfe;ju!k3&|OW`ru-iWmk|l(UM%|}L*0gmt z@N8G$-e3zl_r~2o<4&#@3>kMD#s_ZUaH;(p7E)a%vFFc`flV2jw&mVNnWOEYpT8V6?p40qWpN311EJCya3U%ZSwr5s`TmNBwL!%L3{r1< z?m9Oehf^i7`)zl%{5E4qs0&bS1qe`a5y-BnHkB-NYFsA^?bT{r4gaWd9aE@r-K{Fv zFW~^^IC}DXk0yDb5`|2~+)#(@SJ6Y;6bmSOle)-nj?H+LYx{0`E^Rlny-a2BetyYv1vlc<{CD#dZ8`i}b!Dd5sw#spH@aO_qxQk+$!0 zAb8U6B!d}pY@}ce)36uOHuEFf_<$mFdzjrc%1UMY0X89Z6;C&Uzo!`$zmrQ)yFDh^ z*edh35zUJgJw=?;v=8m%voJfcE4S94eTFpjpanLu5n{b!*e6q4jM}Z^+B`uEd49!lXbbE+f z6mQ=V+@F_>l(1qK(+$DEye*S^jSwx1F#+7NQ=I4cCAM*@+aqdz^|Pd?{1cn^=vjeY zp*cG1PM22}l(bp04gBMh_-7aPC~MN&U`XHRbdEE=gR_0dWYPYY-)vS?Owx}(RE zy%PJnCCKqfUHYR@nOY0ml{&6%kV>huPfsd|U@(xKDf)7;$ z$?2jXZ*`WHZH-EIS9*r3u-ZQ5rDur`_r@^h_Rr#q;3ST@n;Wh0s?te}b_fiXKH1E> z)wy9deJ;|iE^`10&R+(xch2ju8~ggEC&mX2BlX^jY<8%G8(B(cR8VR=ABes!_n;*ml}{b@LB)N;AE9NE4|+EDNit1}omrjaGw(%IXLtYdo}DoZrk5dwVg zYU^Z2rLyn?bs@nik^;;qUmL~ zygWN705j`3MuMr6Z3U0Efta%NFj0H(cV8xjn+I^8Z+5PTyc6g@j-+o)R4QlN2wLyT zVT+!_*iPeXiq-no;J{j-k7~Nlop{e;m7(!~RDOsP zd%Ue!=3I>JTen{C+;NSen5krDifEU2Sk$Ae(7i?zPanrnPcx1YAJv)f+^whhW|HYG zE$%t2-jfr0MI#-dE77U*S?tzKQSfLd1y$AKdpUiJKHhrw8;V5x?TdZPIM`2QZEZPl z?au3Y-b+u^8JRD)N+g1BQL!oB!wz@?VOkUAW`4Xeq?zr_sbj>MfMHQk5TgBn>K)zn z{qf-5y6VpC~q>eBGQLfzD)^dtZwNGB2dIrSRCAKBmOgc z(amZ|R>{V!%22Oj3Hnmx)T2(M^yFY(D@b?v`&UP+!kn>9Q*DC^iP`USw~o(v8qH+9 zNPfqbyb@dsPj@5n>6kxv+{(+%Y9FC*o5!;A#ijOaKb_Rv-Bw}E&+5zb4eA=)@WNZK zaYaH=HBms#W1uuZR+e@K9kedIz@z!LhTH}7{zoPheEeFE0-B}cYG6GlT+}UUpd)p6 z8?X?5j`7ow$hFK$fpJMp98d9JFuQt1lQI}JuMbLS1!8^j>m4XzwpIEsH3?(~HEKi=nBv+>7Iwp9!Y(!30yzX$<9d1L1qPH<@-UC)JbOXE-NRnu@a~VP< zaLyzaUbjjcd!MMll{1%IT#YNx%bvxw`5^sc)1u-(#f%S&c}BY=duUY*tX&rN@G`W_0+-R=UuBkx1v3JG2LKo5BX*9 zt$Jtl@%OK<&5}pFwkY;#Uq|O|m9lPcaIX#r&kwDJbKoeOV z&7y9``Jy|~Crg}T#R`1B)*ElIP1~hJp|+f@raxaS-?%Ok0hM*@3;85B=Y?p!A$EJH zXv5Ikj3eGlZ`u%?ABwu$MIHIrDB@(TXtLqk{O3gQ1=6_rDxYAh*jp*U4tL+Pwr`P# zOWv#o@M>P|oa2(7u(OaaU^yV$0?HSG&#nVoA)yB>^Sfo(D_>Gw;H3OHxOGQoT@~wk z5h~$Khe*)ZKN1-O@iwhB*ADFcJ`p$QJk(F5<5^5fpm+PQr7h@=MRpd?(YuPZHO{-z z3y9#S48e1ne4@KZ0c#MX1g0~+7P2SCg^)mXtMx1hJtlEmW^w%p>t|Idji0r11+za> zkd+u%d%pLf#a*!z@`&x-2&l^oeR2a7Si?WYaCWyjYAN5OU)~btunNlVYfClaiuQF} zWCax?YGhk+RVzCUb<3E1CEWWKwEU-LPIpY(%ts5P2FtE!Z(gu_13dqE!kmqP+?cS! zz>-B;@N4JUt>pZtJ~GowoZvRJg>d7K@!t_tA))-Hl>p?eckM9-Cnq7{;z#K zW%yNh0;z*`!80RPRdu#5$!#Pg$!DLo2Uz;IH$};n$1gvb0awe*q2+G?CuX|-pNq|v z3}9bY$`x7Xe1)v7ja#W`^+V<9J0kKIfL(HvYdDC3!ffj0J4vvotV|yjIuKbV?S&-vuwRG1dE#;7*)pK+D^2DYc)ynC5&pXO_!jboTb(pYQfq}A#Wo<8 zRJ`tEJ+Y=J+ck>f-pxPX1952QI#pfX%G)d_5@P5oh#t5QH#Lr8HMeahY>)9wyL_~s z7FbV5j{FW^LBOl|urpMOHpSh~U1lu`3tPy)Rcpu^)QZX{8r79poxS9Xdp4Ln=c75I zfH6JSJXZ0>x0;GJMt4G}Y7OBJ5Tl#X+9C}WJWY_(3y^abD1IUBcGsjTlaiGhjo{s? zlevupcRWu9mU}UV627)r2AZE-mhdN99Du%)E$Y$J9f4qV#K0!f9q{@~YI{ZB<9PBf z0JbNorE(;E0CE;y4f48`sLqcCRLkM=P}WvY#4|a_o6h)zwQ9wJ8iN`i&?i>qRoHvk zI?)hzjKliQtuZ_&NjY8uqFx4a11J%kh-i!7gzu zg(%;BsZv_^c}ezhS<5hMOt9vT zW|*EDT8{;FEaA0VHHximBZL)Zn~r-LvLj$P4c7P`*ji}9K64ek21aDvz);f01k2NN z@qj-TGbg~?Z>H494ja5|<)B>7036eD*BkFiZJ?|=P)!IRv~caM4(qzuT}=Q4xXIZo zFou$9NzDyTArb;!)=k_;x3FZ}#LgBxouhw)JiMM92-YSA*83+J zfqbwqw3c9-dT>#0LFmvxF=YH6a6Uo^`5W`)*W1k5y?=Fs>{kuUn#uAS_jW7C*3oh2 zfgXD&lA*eU1?LT)huTYsfw^7Q=5y}|yDz0N(Cy{Bh0Lb`6+%opfWGCgTlv9+#41Kz zzUF!&md%?1(y^YyL{E754vo_W`FiN{M4m4Pj^EO z22s)hG?D@X-R@2Z2&bd4vUtNE-+FJHwl)`^!MH{#leR9U-rkS5i|J?~9kbd@l!Teu z2|X&|^!J&7)now$MBXbv8`S|%fQ$M(pe^z}4&(?JGog|+cG|=KPA1+od1(!q%M*8! zFe`Ad`LGv;>~yd}kPXK`@o_Cb!~u>(2bmPn=LLWh&`M!yU^?0u#gt&jw&dX6m$&i> z0BQi>s?Y%e@ZqF`TyWNH>%0sv-HFcP+bV=#%$iB-@~ zm3%r*!eF69KMy-eZy9=y0tB(z9hu^ib3ll*B7Rp<*{>c7ENpv9I{wlz;(b8&KdbsV zVK~r-Qw;HpeF<&`-ZS{TzBld52&ddd6wr&{IgP5tacR0CTlTwU+Vv)c{G-;8mG+#f z7y%6&KV&=p<1#=8p;%z@t6t}T!?KBc#LVoLtFr7Cv6=Gj&?-xjuKO%!j}Z%L-HH13 z{{kR&1H##&jm$w(z*SbtHwng8cQuNz=Y2gmH(D-|XANJHjhVe#zTc`j=m>Xj`i5Cs zDcxEdTIFGp0Z`w*nG$I5Fp9F|h6YYLb6>N)K<`h6^8g?4t0erR{sK(YvFcn@F`c!= zMXXE2(*7duaB)Cmn@kAc&jUvdiGKp1{6HT#KAizM#D7wHKt#CVqzSh~5aBc)Pvjpr z)*f2le`o?lIz0c5y?O@l=K$e;n-Jw6KN>ZAsk_Ok0kk1`SrNz+ae#KWcShqS0N`nX z-u@|RCbFN?nXQox)6U~Riw^u)AryWaLDU&QmQ;bgca3CUB%u0gIoiMcWm%1I6ubd6 zUv(_`FaeCiT<&g&z$Ay#?1Y+ai>xLm8$%w-nUerpWc?~Iqub7C9P=^a5kg-m@Khsc z#CiPX!RJRVKuDW~e}C;KvQW5t! z86c`zO}q#6Ses!;Tf3S&w$<)iDJ9IMO3O(}8PEgqUp)iBv$Bbc%$&!Fr9c``Sw}Z$ z6Zap#HYp(~I7(q*ncP}_A6`K?rdbnw8qLQgCxcSp+OP8i?{K3o5nhHaM;#uj#dVr? z0VC!~Pg$hnnJX9E^wkBsn<#hZxPL4Ga3FhE6Ke_G&cA9``?@k?Al9=IrhW>1##J`I zoHbasU&|Qb`_?SHxdU1I7X0h$!2NK)_$&bLb{`YdF=FqFPVVic3mrS1``+?re_9?; z1Tm%p-W&t4_bOIGi1-I@QZ73wK*koK)YBB&2Mv|`Yo5LIN=$C6R}(0*{Nfl9T;`1U z@J9(qTLhq%M;R}}32uvE;uA%rA%fXl*h4>%MHK-`T70) zq5SoCNOzONTxw%KRc!k*&3F#E(`+1eF1{k3nVTj*Xe%dzx1GKs_tI1sv3oSDCW1Q? zJ7_O8I|fXhx)=0IEWi5B99GkQezoVej_aKCk&G@&|Drpvb=sI)6M3lvQb7*!y#T}+ zaukZcp5+jt0XB2#&Zn9`$6o;2D6w@X_CC8Ix)4K0&gO;g!bfH$_|p!LE&wVq1B01Q zlO3J?Pz?2yy?c5qj}>}Eel25v$tNg{gHX`(Ss5{h2$)iD%JI!rUCZ^`5Y9!6AMAg# z0uvjkny%2E{k6br=_;zkM6UOov8MJf`^mP0xqFX~o>#EWVE3mxgkq2y_&U(D2x3|V7}}C7`OyGksoX>W~jK|pC(e_3bct@xNou4RF^v*&U7&4&x?X!;ttVM!!bwS zrwV@vvU2!*u)u$Jp-6pAGat`Lj$)Pit@FNJ6Zobo5IpPg@Z}H7S&sEX2o+de9({ad zs()PL5i%Y53Hb6XZ@Kwe$1gv3z@ftnGbK_Kt0d+V-hJzO&QbDQ2Czx!^e^1U6)to3 z8zcGlW<&hJrxE{%hdl76xO1@qHb)dufFbT!z1wr*hv4u^Di1R=N2>;pzn6C37#OJ7 z1cq(cf9aes%fpLK*U}1^sDIom$Rr%U_0mIrb+;aU^xwP~_^DuoFmz?Z&h1|+25v%M zQ5~ak&wk`+ZdGp#?20ZD^i4m$jM~<(Cn53nL8&GF69_*I`iKsGzvHF{46XKd?0ETK z5B(2jEqF$()EEQd17e^f5|{}BcjX&EWY2$k9kUGr4mE}6|2vT>_%B zd}#m0@FA-2WsyUPr%8@FWqF;Hz|T_N-m4*7W#tb4+T|R9x}N74?Eb`=fRjh1X*W!X z-I_lU@*m)9rb{f=ZhMXV$Y)Fc!KfcR$hkeyYG~NApDG*}yRm- zy+-%n&Its~oB;3XkK6s1!U8Kdvm%uiDiF#1eCu!x{{+at_z5_Vp&lG?p$)Rd>68%dg!-++kD53`9f%W&vF>mE$lg>sjm4Ps?z%+2XrVc+~M6n z>u5w^0(8QD2hA$7o00Wc`}d9E5VU`T4*cAiZw?Vs9$G;>X#Z&9hW(GN_SFbDjBjXY z(B1LpucZ)Nue6Wg4Fu|w(FO_RPk-|;KV~T~p75X1>HC{s{t<|OB1@SXU_B#lO)m+} z#{az7e|zU8EkFy(i#q=&f&S^u^<{*ef21>J{fK-0KV|(f4{#KFo{0(m=I8eRACB4= zVNet>TKf@Z5!3&6sX+RE69DRWmHfu0e}mKrH(>7$QWj0W^Y7n`IQ&Tw@K9!?fBkH^ z`EPV&HbU?f4p!|R|85~FC;7N-|-19@&DL+>#!)hu64i8C zRabXBURC8FEcvhNw|{%^$YcMD75u+P{x?_l|0ltgSNb~e{*lLmbSn_zY(1J782ww3 z;5A?$?CiL?fcd|8WVrRI^1?u0{)uq>+|wnmRu0C=zu0xY6J!@_Jfj$W_;OsyD>7};n^1MaBhMOOwLM2vbx01i1vpK(@=u_tY_}6 z{M*s?tAMn_KuM|E{I}%)p_2iJM1as%){*z+f9%-6X;0wN|Ls$ChUTWCY2mJ_43S&owmIJLu|6&g1_@kah$>=>MMjZ}sxPDgX&<2=o^IM*&;i8p&VfE*XY_80bcHbAn9i-J$-fS)MUOB~vw{>hf+_do=! z(BF^6|5Sp13M9c)(}CtEHYdIKpI>TKh684pZE9Ry=$|;81E2Z7r~Wqf1FH3xb^HHH z!gW1!bco+ato+ST0XD53W8$jlg)Z^sxweJxu4T>)jFvU?jk?*@l11Ns3ZWXMyCOZ2mFcgFPkd|Rz{jJZD{8wlSzy7{9ss?b@+!z%T zpFdPDpBZA!(_vrLZ~X^J1j?;9JH}xJ$V+fnlI>3Voz5_06D5TGcr(Q)VGmajy79v@ z4?T9H5y~!o&Pi#f9&V2uBP$%nH642?k^BcG#7zZ2hfcenT{b(Jz-w`q<8H7)pRLx9`gGtwhUJ<$sV_A% z>NvwPActccN@8cHm!|UG23Zg3ruUdY(I>=Zh9}OIZY=< zO04r4JyE$3iLjeqFDRYY8WXOe^%BuKNHL_f$4fi%psP2xoQI8w+8ah zoB;PAD}l^E#WOo8Of8Eid+W0#-$nD0ob*SABL`;TLdIZNhj-Kv$b#V%es>piZGPBt zT5ifgUjFv&=Pb9*UnTI;TncF2w9n0QSReE*I~B||&sqeR^dgf z4GeS&7sy3kPEd-TU|mf!d=?z+&0}%? zjNkz|@k1Do_9v2E2O4qxrora7aeEB9=Jrri)Kx7`OYdlwRFb(^#}~9nSot@;P>YXL zs1DokFGRU@OQVhB5*c_VE05cPD3}>9eH=1(CMctbqD~z(uxoR;WBNFlZ`R0&bRABc;k|(0Xt;b?>IUvtu zi@8f-g?h4tOQrO=&Zk9)tej$KfVLi^#k?9B=}OMSJCkwqkk+z6zW!T#Hp@_q56m&n zV(_x?hwo~3n zu|DmJ0^bQb!3V5Is`72-++0Uflykc>%Sczh_q+!S1iwT69V4T!;{5oi%(_c*T*d{0 zUoHdw4&7WwYZ3*ifs9$X6ubgt;9s@l=0ng$x8hZWo? zl2rc5<}9Gd)g<bsZv^aeBwQ?V<(Ec%bLf~NYDl07gzWr%YTX~52wKQz9* zMrnXJZDOnFk6S&m9*A_%xeTL2gZk-rUJEN9dpmLx=R$QLW?02?>@4bJt{~06WcPfX zRPrHWyiS)M>MOX5uR$oT42Qq>Kknc91o28t34xgq(eUm@5>%pkhxR0WDqkGE{OCMa z^I8t_MnTr3wYp;Snw6?scb3E{n${w)L;{6gpPx(#p*T;}AN&2}Oo$3+y) zqf(|on_&UZF3GixbQ615bbd$r>xZ}e13O)J+ zSM9cqBi@Z|>?_aHHoW;a9X8FUl+N=-DM@Ur@*ZYT?ln+?WP>( zAy#?HL3ZEgQg}0iX27fRS~p*vvR9$%vV*e%b9l)(cghc#1J1tp6i4}zZ5Yq+O0re! zQ7W7e>)a2co&C0|9{L?#XiI`4->*6m)mXi4Q*MmD3)DPcw3 z#?s6D-P9Jkn^X$T`slta_7Q%mwhK7d?_HNQx_sAsa_v>nQo7w{;W;4>sUTVr&S{R* zn>FbU8EHA0;+cE^Lg_2IZmpU(CeXvHbIYieo;&Gxs~BmpYks9OqWP)!L^L3r#dC#k z)wncc5+0l;6cdnXR=b>5p95#@VQr!45Vtud;&npY<9j}3ho#RZ7pEn1xrYq&Qmv?QQN^arF)T}1pUKXyKpedZ{Njt+^&R&s6>fz) z9L~c@C9iGtye5{+m@pq|1$gwGjm^yM%t@t(YInEg_0BDadaSoO=NhXBr+s8I_OEKT ziXu?8ua7!ueES;u%^K-hJ%eYOqzMYUw4F#ZThsHoo2hQ-FVi+z1wBe60M(W z>)U|VMd^n>SsW_P>4 zUOVOf-D8cfS*N}>K9bjM`@sC6rKc|o+c7qjWRuwuvy{@=NS&HsIoZkge8my*x=5$1 zBuq6&xrcgfN`;Atoo_3Bd(vw&XSToey;Jpax?WMGW3_m_p|?$igD%G7%l8M*T|fFb zcAXDHy<(p>k7EC6agynz+^FSUk~t)%Y-(M0GK4nQW?`@uy%J;7CpZz&`^;kZbGrFO z%l-x46^<*kD;y3J&W_8kdc$)r8QtYc9aL= zpm6PgaT7_EgSd3k3rG;7FBR`KYwo(TqYUd2 zZMY>Wa5qjsB}?$SjDZKG@HSdpj&Z3DWQ`zac4im-fja#VKcqtkdT#cMgU0dD@ zScza1kDBaI^%akP+h|d{!sq8Cu1|O8jau&-&T6Gr9(<#0)N9^KuA;YU zGOp!>H7Jogg>~hBy%9*WIN2O9#TBi!e92IrY@t7e_S0HssrXL2#&q1>Ak=0KR7-nB z+JJ&E3L4FIdWECNpOpvoS>efk-bBU$@jpRtW5js0_;^FCX>$Eby9>blLoVORQs?N( z@75;VTAM@iU%Y6XYkuT6Mmvmu;hv8loCr~V^caBl(vo6xo}X?gxec9nT3Mjgb*r+$ zU*D<|RaI3g^mgrUaeZ*!`*@CKR;ouFpJw;58CXlO1Utu~7uN<1-dh5`snCK43JE~y zv#+D8J7P0~gt?BoDo^X)Q&PO2@UjY8-8eG}f++j?pkvk~1OhI1|5ve+=8TMysOEIePGZrZ_xg+VV*u7vjF;R}V8%z8;UMb% zq@_Cy9S!}G@#Ouh%?O+K9`XX`A4>X>zR_+lV46$@^W_RJi9n!%7EKfd@e`2PW9r8as& zrO?M64+oOnc~GThw_kmA`R0ar#c<+S{-lUC@Xz?9HVU0 zL|g~RP7||tT<8#N}U~-0vJzoGg(IJH)&wBgFV_jUi$@_bo2P19s8L@hMFy!I5dPi8;Yh@%HVP;O9G`;p*O- zdZcHb-bd`Bt2<%jzU9-EvL@bJ)<~FytqhMJWrMToNswSnXFX~;uA9O$R zYL5|uFn0Vc$12nsw`ngkjE_?9WoRP)^N7|llxV5#$SZo}PNN8i3|6%ic*wTB{6cJs!+N{@ zLYrq?l}S2ymKPcDuksTMPh)3N`; zlf!hxkQs1lWls59GX?jdv8JZei1O$d9-+Mem0e(aR@l>C9t@Ts5J$cX3hm& zWzlq(sieFXKws=f9uzrP=>?^ejpz3qAXzk|q3bpBjDSb!D+zCev5%~*?AI$yLh~u27A%pGo7z_0kqvyR{oB~cE1@wVDOr+%+ zcLfBz`PWsH1?G>qD+|2oh?3B~E1dR%eS~Q@X)~ZWCXmY_yWWGi*rMyMp2rFSaJnlS zB;n2SD1VG_z`!J%v3^0#roNggiUYk<#2MQQEqr`#d?|Uj^V>9G;t4ZFUMA9eY$X;* zxduJ` zmcPF^%sW>9w*7@h0!^QFv8V65#K|K~p10olxC7_!%PMR1K_pMe=7V!^)|`O8m&&YO zY|ii{LtWL-2IeA(&QO9lbmkc-{^nwUY>tLWcg`eM%Dv^4+lB1mhwCSSn+0%U$PYxc>-c+?1ec+@(*&6}X4I_H z^|i)f6L=i{^V%CSXFd`?@%+66C)p(5Nq*4NA_PH;fa3+>f5)7 zHB21%zVXj>6;oS-Rb~m6$ch5XM$S#EWwZ9A8HewSj(MS)T6)8G!9M@m8Nta0XZ_fO z{DtT4Tjy-5pC&7J-HL~ae9Nm{tSrT*inx6QZm#=VujD$?h78?+4~;y*?D0lTZF0)a z{RD^2u@4Om%>l`10sh&cn!Y9eBkNn*{MH3%4XBo__m!s@p7y!&Z+6Rz6eolNygb#j zlodqgfj2Ea^UL2PCgPIY=i5QnBI7}v&L0Q4CM{_1K+jdpAgYR8^Z@HJi;jX$ae{D0 zapAY%_Wr8$S&E2pxoFOyj=Hfg_kH1i#Cfp7Jg#Km!62tLzSHhB7@=P}qCYr7BeWMP zd$MSXYRp?=g{KKeft|UlgbUk1s$jD)ww)r)B&j6I75+qy6JMMhn*>T|M04?qZX$$XIk4HE;(WLY`a2MRGw28hY+2 zSD{0qjlvdbLn_ie9Wep{ZpLt3Bi}S*07hQVZPljjvcaI@pIFbrrADjl92R1Wb>+P_ zI#0!}EnQX7JUz{q%^X=;r5b=Fh$(q+iG$PHu&gEPUdHE?Vz(;iX_QkA`7b##$N*72 z#P?jgI_nxJ*6-ECG2?Sh;I#hRxlK!@M<&Qj5aZPTzkt^saFTpO^5MgWViij* zt(f$1g8duPNOPDM^N4N;DIfsbZ&r~LO$Y?(JQ}!tCy_xz2%7(m`9xd15la&5`w_V(tP0XsO|y z>urBU+G@G>FV%)c>1BuSMQ-WoTv<2PDzl!`bF{*iY;mbr4S{^NkaZ9W-}RE@z5dc=z#=Doyz;9FW;ZtVE%JA7Z#hlcouv$|~m!fq0GcSKVIe1{c%$>kUhmuvG(3|pb65LgV-86;tBccnM{(GBtJu;ixF}~kw(OpI zrcM(yVEJ@2#xomTXBrTxeIuyMcy^#bK49`x`n`&zY~7k(RZ~pwoZb$5T!IH;O0eo( zM4)!@qN$$vBb0065+iE{NRqR)Dw?Sacec@!`Xw%bZ_XC>O&mL>b+b&8IMy$egGQjL zhfH&QQW&V5MbFZlw})D;TYT^1GI;3ZcVfKh%7Xn23An-6IKog+!*C(qb9q8OdF(y$ z7IGy{?2bcIA2WgzP1ED>7_!ym!DglieBue5PbAt$B7d>mAOA|eJA}fm0|PyNsr7JX ziB^%i0!M_l7g+fhA03`dHi`IpM60`dvk9F009zsp2hn zb|`0mzKySXW1qwcR+q17ug0tr-?Y&X!mN;AGlcDaYCJ8@4emd#}`V<$_k1`ND-Ycm%*j>((0M7GVT8adZEh<$7!VOET@?HMd{PJkIl zxZQZF<*6wbDbyG_om#m>yp&p4WV`xNnT1Y&P;-pp=QuBu0aUTsm^*x&KXD%XJ!J?T6lDjBi6&0 zzrN1w_)5`;RXEdM;u+b|h@`!fe&@V&=rx8sv)f+>!vtryt{?q54Wc)|m?bTCyLzLq zn3*hp2p-SCx;Gn-POr}%_QhTtg3i%>DvNa$KEJ}D#4FSo4V@DIcY)=gwvy?BYL-+V z`hGE}F)+f(ndBds@y^#Sx_+r~DC=mnm0ZN1jenOLf1G|^-pVR_j;uRODuAj3(>2bf zf!?vEDW&CoXlLi9-=nhVsasuy0gmx)=1tFN2)cCC;d`>#4KA`yJ+u~D8jFYzFkUc7i=fQ)c^SCbYyZ+(L3twD65>4%u@e7l|0 z7xHH=o5ynJ4>2pFVQ{EfM+-XYZi>9QQ}50crlX|y3Dcc&8ABY|TawimvzAVC*F(0y zzv4aC5AKUO5!0SS0(*K?i)nviuBXq$^brcu!5@$T2w9%B-D_!V0?skk@?}+4V`?a^ z$;>I`x(BT9ZRnZ@9bmPsqoA&(hM+z!6@F+XGQD!}DCJM}J}^~=I8dh}n#__$0dHbL z(B0NMO-PECvYkvS_1>A%$nZAwG`rl_vFR`|spU^Xz?^6^TdHT)6N`sA;ph2IGC4KS zWIobCq1-!XSAGDbBP@jlxAC=Flru!>DRP?gC@<1B(6N!I!)kYqkbCryFRYlxV;0BW~*gYHFPD+h@}I^5Zi+ z)$T|*15gu&BlIw{5S3xf2hQyJ>F%uLdR2qo$>`6Uk1eg&zYzFK@aZ5nP*kb99kol< zEQX1xc3}}t(Jy$EZK~4JPH~&m{yg5mwx(;8&&9h{@G6x+YjRmq2b}<2H(5Vlqv_#T)F>z zRK>dJSAYjTZVFO?vZI`Wgh(J@4P--oAL)56N-;>`b6R_6^oC(QH!?t#NC{xlhTyYM zs!Nnc5CBYHqaRF`(rU2}LUIO*yDO_*-`{TnbUpxHE^}W_4&>&RIrbR262H8xb{$m2 zEES%+d&W@~k+MfsN#g3m!M-reknuDJM8k1!mP}rn_0^ zSs7>M4htIS!p@-BpeSKng(`N~*2tQzY!Jg%)vWV0!Ik)1k*#H|beSLa$j3irvzRA&|9)zE&%MWSy4ay6$^+o#BqvDK{PYcQ zq!|J8rz$8^R_696;ZNB1U3&xUVUBsajWOSh0hO7d^&(nfhytfglG8+*sf1;LEopa# zzVxh3JN42a(U($F?cDduN<-L7D>ONJa+9YxBKjniMl&$I7Caq5j>?c~OXE-%n z0XMY#N$f;b^RU&b++2#9b}g)?dgcB+vH0}lZpF0@^F}s`@Qo2fadmYHsL%|EjrG&5 zeqzAkSW4#;>2@_+t(NoW6Uw}`DzA&A$~x>+<*NN+OF>iy{&qNV(nCnYGO3BI{KCKfQUulCIC2l%9S>7pR_0YEJh~g)I}Pk7#cUF33JQG# z;!X}LG+cq9=dsEs_zZ}Tz{f~KOFq1~iQ*vf&z&4-M4U*oa01MFF0|{Wm(E5q;P8Uz zgjkh!P%69(PClizo{ej`YA;88Umc&Wh%9i?#rIY1-`9EJnRaa{q<(-8i8#wQWI>zS zG*I7GR5oHknq*PJIZe0&a&$(JtC!8&zl_{oOxEez(o&Yd_KxQki{zY_cC9lQvR#<3 z`MgqzH2sofTH4skXS2O*rqkO%*`{xGOm;MY#**jP>PucD=0-KgZub@4w>&mGh`4d9 zX8V4;29*4HNtinkH;VT?Hz40kcW?cu7X3|Zs;0R7O_$GNkFeRe_-Utio_8t7`KB0r z&{MO@EvMLWQ19$j8-_rx$~)~@_VGBzW7BZ>1g^Bdo@wP@E(s{8@o(x>0hP+g3U=M% zZo5$z9i8kery>$S={&M(zDlK`b~7jg>wkhaLZot)68&M*?buAyW=V&0HQy*G9Yr!p zu%C2NsdCD=lkre=XF#RbR_u`lD&KzC~(c#jB&Gj>}PMzm! zSxJr$R##A)MN&~1H3LoR}qWu|wVsdl%znO|8FZD_ES93*jMb}KoQ z&89Nh|91RFfqB_-?ar3o-HREImnNl+K?_gBXh-0q6{_*AwZkem=t!oU;(8^DR12l8 ztX#Yk#EwRC|B$?20gmqG8eXW~eN9_Ef~1$qan(J`XMH)GRioMRO3-LDlm}(+6@v44 z8^2S!J@>z;k!ai)6m;Xtl`H;RNWvd9*`6Ac<>DHYX2O$Ez}-A7p+Ncz%R z5EX8APn%a{@0czR37}jD6hx))way6iRxuw(l8%LGJmrA32P&|(eJxFxnOBk z>pyM22~%n<6G3K;>V@2$d|8M*{i45~gDW)hR_$frCQs#ScPp&LZ)JuO;LK z021E%S4b#XO#0@{8$qH7I!xp|#67=J9992VBdSm4vVmcTO9ca8O)whd#6vlprOt3Kt8 zdJ3hh^eOVXPiZ8PTZ5hfm93r*vz}=+1^<^U+6}6ulsHC8JO=%`L!z%8W;5>@cGP>u zltnfD#T64w#@6et$d_>@RVbA^w2@11A4p&DOB0U&#^$B-dhYJ1=`SpJT^aD?3mT@# z-Z=!ls*f}%L_G({ariOF+)0)9Bf|w*&~xb&q$M4AmdWDxr3VtXkD7qyi1X~nCGGf9 zl0(}P*29yZL5o`6Koz$0YV<}e@!cd7*&Ht`ooq@KI4DP^x_Df#&X{FC#WUz#cI!ot7fz}x8JtB>e zO)Zx(S;9p{?+m(nbh1rFqzN55-gzExEG#ee$$+^1#*>Vu!ts`vX4s?03TNedzulhd znS@mhl@{RK!VJZ&=CBS478pvm%C{SNLcBeOeO=Txc*uq$;hG{XCl#NZ>O!q zHd>>vEN{LvPtBK8PrqC9jOEthpNc}n1oRD|8)z&Yyan?KE*br{Dijl}5S4ia#cL-> zf>XKEppw3D{nXwSaG6uwyR=0)T7B7?7ynNF43mum6CUjDCnf8o;f7t9I6H|qnEIWe z04wRIb<=8DOqLCi%QMgPO=l)@>_LNC21qkhb7Aqt<;9OiFHMG~tV+QzeInrLZML;Nkxu>QrB0dnyr_PRVi7Z+qDuum6O{1?BYnf zany|{smlyLlIb7-k%Y4;=kFLG)be*XonRB%tz}Wr@jfN0Im%)HgGBWYch#2B1g+={|_BeU`lXX$9uBKH!o^XEZERJRa#-i|3Lm!efaX+HFd!M@2{mrs;= z;N(AyLx~|=iShSy)gwNVf45&j16CEMsd^!=llV@cilX_zX zRo)92vy2a84dy9T;()+vl^m z&q%%`NoVdwfm*`y8V$4xU)%+Q=ek(18~P$@n;gTbl;B$a*PNtXe&ynw z-0MP{-!RmhosNhySB-&J%bk9~V~elf=Q^}SZ9N)vls=&m9au-|vC-}ibg3`(u~NL~ z6k26NVrTJ6S3%~d`OH!+_Ro0Dot_!MY8`7*vfQ8Sl_U0T(F3HUPE>MzvbdXYdWD%(5?uZuoB>1IHT(W&1q8o~vNWFjgP+QUKwm6QPI2k@er ztIl)$Db_zkEjbL>w~w*((zue2@_8~EDw<-uON^KB=X5shl5z!{!*Mj1g&P2cymO=G zxx6%Rs`9Zy`QlsQnB*P8<_HDtVoz4v#k%v`mIX2=Jhq1$Q_q)z z`j@cI(A!hVIQ82f$qNaf7}d_Ei18Y=L<`&)8Npy_K0|(f;P*#={WYeLcxU+d zONDi^b)E%VkA-?z;dr|fBZ-wxnX-G9&q^K6=f8=jm+A>a<5Ef-9qQu#q|0!ix`T;D zs7Z6|N;3h@yO5t5bUo@o6h;KPT!>Bs@BZ4J>H87LCL7>>y*=l&%n+t&-*lgEMmQ^W z>Hw{Y&qGY1kb%8Xc@xnu3CW1K;6P)vvFC{L4u8_d837z}66_opS#N-Z1aF0Dc|*Ut zpF!&rAKG6~04_pC;)9BQ)G;?9O;p_xATy8yxL$~G#-pX~m>BfQ_FJk?M5w5Z%GJu< z<2bb3>sz7U-eUxc_^^OTlp9NCD8bA3(iF_Gp#)o^E?i9zm_C`&ZxuLy9*lk?5)SI< z6Rl<2Wur3t=J{Kx4*W$FCwY4$CW-0CM{G$3kyt&P1r=Ss7*EfF<5xj*es-NuN=FEe znm8-Vw{1}_yK-rHvn)Xty^@7-@+hZs-1ubJ+*h9HME`NX+3}Y!ZofauY{}Z?P#JcrV+o_tYDp0^N#KS?oS3sB|X&$=Q;hm z2bfG2*XC8aDb|h#m8eiQV`?D~b}SW@#$? zOQnYlN8Z8FZ}|R$LV;NWy6&Tr?!f-`f}g?dfX@92ct2l*Lr#BeZnlrBRZ0+_vM_Fq z;t5hJX**D6l6-^f%9+xLTG}_AUoPbD-5^;#Bsw~}z4L@nRml9&#oztTKHz2_q_z%G z)Bgw8^!N7#kM6t-_>rr6P7S{p-T(OU1BS5wIkF|l%?nyU+Xuv zNB3yN8l8f$B|eYj(4MfkDkP-Y5J)r0T6A&~!otE5bPYDs%OSQb-9mJLu;d0SX|R^) z_oog0+d(8jGBPq8RQqlPo*n;gsHLU#WbLKRqla}FDrvZV zqUpVf6%oF!!BC|WulZ-jE>{t!X-0*Zt336W|FY(O2|&JIo!h?u=2HIS6v?>Z9NMKa z>go|0HCiPWA>0P!um6;SUz+4?7g(&`pZ@V5t>6BQBo%RX%}l%YSEC^lg*&!nWSh3w zJp1Ezl5pZT+U-_*?THMw5FQ?$IaSM@Ep#kJxw`r9EhN88`iTyJa~IYJczOSH2=GPn zGe%Q3l_|FmbXx&I<3cPK2(d}|^wiYU4n6t6KhZr(Hv9O;DE7qWvjB*T4I4@u>EaSc zm)yVJ=|5V`!KGz@0B`i9F2I616s;Ebh#m90i}GI|`fI_+U=YBlIjmMZ=u84FR9`*j zu${@$reH%MIR(Y2U`NGdJlk|NK?QCn3(6bI0SMgCFlHt2b!nzdoSeA~RVKpD+?cdn zrPtRVG4k{GE}2i`AjW}FGc@DIwaj(sz$TglZ~qeU{%Z{9@Vk#;946btzZ99Z43t=o ze+;>pYZJ+)p?LP{1BO>3&Jn2!v6VKn&T5OsqmlYvc5w?J<*{cZy%(FVRgwva4;iqn z?G4!4+dH&*cev0rz+(s9$7|Mp&L56uOp%X_fXxjwg=^@spr?CUmL@hk#4)$MFmnRZ z6f?a!Mf^-%sS07WyIU=%7l4DgYX13yS}Bsq1Uoz6+|Zk+zrcOxxCPQvxi7_aWx{^Q zsZX{ohHuEZAn=z&aT+O=0!&2N^$YdYf8u$+p0@?;a5Rtcio3p51xXrjrY` zScGv~#<6hKrCRJxM*+W8|D9b73M*G)(PMQ&Gv5Htu2cRAmOzrZl+ja>9=jmMbaYG_ z8#n2MRfUG15rJjjj^?cwvl@V{xleaz;9OtJ#b8mR0i|E;z}`wd;fH$?1; z>h;^s$(A9knj@Q&2^~C|b;3`8F6qwKtm`Q&RjoT|mRN)jx=y~TxI}snx$`Z6>y24Q zeA2KS+4q}Fhu@^6jK{DWh%GdSv(b9qg_3;iV7j*>;`sf=>P#P3qsieJL9@1)ahVuF zyKjC@RHlC_p9QWyA0yu;dB8FMt@Z(w3$|tkR8ArLZVr8j2PKs*!|D52geT^in-)SM zKzP#UFrEo;6T9bZ+@D*h3^CTi=CqJI!}$7Mo2JoX?xg~1p1Bm+yQaWe60iAg+utj- zicV)F|Aq1Pw4FhV;F;7+9ksweQ4S)%+&X>l9VQB9#o{>wd;7v-iyk?!Fc<3ss3Ik> z+bUpt%MoP(e@xLiQI|XMF5ifeH)i_sxccFm&XfT zmW*Dqz%8b1qLA0EFZ}k4kA_MT1Max+nRdD;;_zCi<$J|L=zrS z>MI4hZ2$EUm2hsb9Q&Q7!>regfSJuJ~+Qaq9G!)S7pteomG?5zTA*ta_<|Cbztd2JGHFg)a7(v7db9ESINO^I=CPoJ}J_ zo~uR`Z16wsy4=KU3gAZSdoXyWX~K1I{y@w(1Z8*ZX#{rHeJ&sD5Y{0)e(o}pe1zki ziY#99NGZ}LjF{rm6SphAPRdKKZ}?ZfWBnqT(_SCLeJ4*(tWoe4CMRKYNMdqRmAuY< zp=OdjMm)UM{jTo-aGUZK;}P0YVA}+^@712P-xjjxIFcoAUp*ef^N%8-z%S@6&*Quxq}APvtlbuSpW8C)ky&O?5Hr=A<704P*+qZi}v- z``LfHmIvMd3=f#j8+4(~P^JA`NuPzCWg<|YptL#d(fH;BudR&IEU%p{d*IbA!CUtV zjCfrZ4J^SIi9bZofR~_nq?GKRY(j&BgCmd7xzuG{>efCPbJaYlN4L^3gK~wLEPMBr zxaYvy9M~;OG7ptryh@0sBpPpc_`6Ja?ErXU_@B8DPdKPhG)*P|Vo_CWPQT`6UF>*n z)%2Q^Os`tF#Zp8Vr~z3sw4qKO&^2&#izs&RL2h8Dk3xfy;b#Z{TzUa ziNv%01vf-AdNS3{;SJq%ksk>y@xm_Um&kZLtD< zYta7uYXPn__o`hA)YSr;B2zSr?rWjJQ3DTnydiI=Q4?*kCG%jd6yzc~bLzzis$JP0 zr5Qi{Q<*~;?l6)QzCA-mqyf*6zxCL%Z|YA+NcI^JEZ4V|KMDQe%7N>&KhM+^+^}N=%OTBEh)`mX{j~xta)s_&>DgQ{u9VWGpVb zc<;vGNOu4`OMr;7h!=R{AjbI%i2405gE#)HO>y46f}{@Sm5DP|9}YRGc=qM$dWt8Y zeZZDZ%^P~*b6anYR|B6=Ei7Ip2f$bu*s(E~={iW?y8ETZFwtXknGQ2y?(c?_vT3>) zAH+mQy_a*q&p^{%Q?ap{}xa^tyq&o)_VtD#Vkdat$^5Om1cyd6N2S&ap&Uf1T83_?C zBN@hfy)Hxo)bwKG&hY&6z`(YW>96no9dbsYk3clsaQYr{QF>dr9qGG07g@Apu+}l3 za|lF5XITAAhf)1ASKY|Jfc`hcAh$Nxc)8>kUI)l_l1FO(Q|v#2u+*!%{3*|Nsi8z_ zP^px{kqbY%V!_DZPr#P;@X3}~8}qkXrL(C|aA=QA5duO&ccmp`08UiyYqL!`&s^kK z)Aeh;avLYqU#sV6Rj3sVElf?UNv_J4yo!o?;I0SteLJgPVLxsHv>-KiGb}&feceWd z2A9Rb8Ni;Y_8)tuen<&|YbG}kGY9+74k+A5rNiWkD=Lwt2T=_}9RA~b!m&0iV8<2s zG?6i!RW0*D4XVN+4YYraw8fGDnE0B{q5ibS$C0aA`$@pBUz+E@yt;> z@^W&Y$q7{@xe79(tK|O#!o0prMnTb~ zh|U6nakyUKy~@H30TH;S^EYHw&fFzA*3ayw%ig~=JODw^dU74!*O;g^$Tc=$=9 zKwiL0?S*I?ZIqzFfVs-Y_Vx&1Jzr+e|CwD&`2zsaY4?iyN1xV#u<}sE--|IM1IVa?@n8>b=800Xj1+GrPx^cQEYaD3)-8WCx z%T&{2`*Q?J1*`{DXB(1UeIic>X3n&%q|C(Iou$zMOVC~H&QRgC>}3~nn0RjK4DyW> zU~9RB5;SjT6NZqNaPXpHZNRc-ehXmRnq2vt2iI7Vft;W^Vd&Pk@6;6kM9M^dFC~GK zE8b0Kp6z%gq5+P30o$SAQ(bAuM|`U0s9^`jW6>>q(O9ITK7BRtq1rX zd}632CnTZbWRKLbe;VPF*f1vl&PR^YWvQM@!LxEA#&S81o2+%$Rqjkb$fx>B!!9ZC zQs4PAyd)_wTpA$!v61$Gs{Z+@A8#-?5_}sSO#xwC>qy$|=%o5t2;g5|G=(uE=RO;O zmLZVXRZu1f+9mCgA4meQeftE5reu$0PiDBTQ=SnZ6uPH9!T1V2F)$7rDnURG0S-qU z+4K6w5snEZhI>2mii)Lk!(jis)oAZX44Iv!%`8pdtNs-+%r^ zzM2!3rI8nL^5U&Amy1|1;_)60{cxa+=8f(otKU0sAWO` zw5cpxvju6y@@kioJn-yJk>`Z$rRco3?Z95WbyM^1^pNzP%JkUz1owF%A+R^lDKfd* zsGel%OHo?3PQ_iMcMC_^10H<{F)^_{&?-y+)&vRt`C6>k7vOI|&rdfE5uL~4HD$$K z9E1#c_lQ*ceNdsU{rlN3MW!9`Vx?%`FSP*T$pT+k>~)lezZDjj`3Gxm7Y;?ae~A?i(R|4HRNg{?Z{1o zPl?w)S2;uHHh^_Efgz^YjW&P{RvawKNq(dESHY6GO-H_&5O49o!I+o*J$_*l`4RW$=S{FK#{rP4!a12pU>=*_; z!(0WM`#gX{f$4l@DFDMR_ZtVx0s=@>U2RlD77sRQ*HCJWye}*swF3@^dNq2sHCCXb z`-s||KI!_oH6ba5N5)JpQGCgr%n(aWdS3?e?~PMo==Q_gZ*^l#O9!elMcx0 zJcJdQ&Qzm&waMw}7rN})n*rJnIe+8vn2Rx@D7P$Oyd^RctnTJc!$SaaKYs5_#_`KS z@rAZu-+eAJ>o_;lKX+6eYh+}^C?=*W?D*ZFFYuhP?~xM`_W`?b&;QlIvHD0d;%NFN zjmUz(GLHXlF~}w$AO#N-KzXQiw=Gm~OyBoLryfGze^_2Fm{VEFBc3+?kdzbW{#pVB zhND#Yx)5VyCa!B=%xIy^`66lV;~*ZbCmC0PWJjx2EOD5O+oJojE6C|;6z>DLff${` z2giO;p;HN=l;MksgC4pMdulJ-eDaZv|qX%#h{^M_vqfxjsnS2&*S~m zfPPL(@-mT&7fR>yYLGbo^^xZ(Cvz(Z7^CVzqefJ2BLGX~bZ@H<{R;jblq2g@2~C2B z;MGJ?6Qs9pv{HF|8tY)A&rko^nF@+?3tX7Xr;B~TF z`<3JP?J+=cg@VLW9S~Iv$TBEZ_@O%)cbOUoGS%3Wp~RQ9iXTYcDs*1Z(=2`v!me2; z1)J?Z53rWqtGw%vNP-*yX`uz^2@1grqQM@4pCNC_U<%LW_EVN2pMIs za~^~;3)v$($=>sK9l|-KqR;pB`u;wz@AKb%&VAk2c<*&x7dXs8t}{g=FH>iFn)=_t zs3Cu;L;9VC1v=9iKJ`8q_cB~U2#p=p6ZjahR}4ijxLYCv*~>UQV(2r0nGu?aG@npE zB`uE98cLUd83I2$-KRIUK_{qj&dyM zAY~C+q5It*-)M{ROoC2Sb^rVIL%!CCdV&MwvA^8XEpPU+)Vm{xahJ}Q z=Dvm5W?%>L9O_vnI=tD#A2{KscCb|BF)Ft>j~v-iaBv%ZAl^U;Yx0`5&Vxi}ER)EwA%acA@<_2xmH+jW5cpEkX(pI`T&L6%AT$Kn?i zmYcQFAZ~I88Q0;GqTB&rmCM19r>P|@Bcomy{eyynqT-%!RnKiMoo*Ot53EJ$^6v#r zKf!df zBh1v}nF@1;%>iKn9Ii=&^E8>znT*AdlC`mx=% z!(u1`$0GW+#Yqeth3H)KnZnYRV&$6ITK{|J93#`b`93`<4K@C$DP3sGNlbHMDhHqLz@7@i|Y6CSIbRAft#!2{J^U}}TF-*AVDOpu==`dbs zg=(RM^T7a)gyrSsA&Eoyv|??+#qnF^L7TLDb|VpG8-Nv$apy~EOTEu{LYPV^xskR` z)mV@1A@m;URia;TXV-XTyrR9Q@UA(n^i-{nju6W61zXhe<5LRe_59=uAo7DHus4uk z>Sjh_WMW}0@Zi~YosH^t`f@(h~ zM}R=IS;$&Qg^5$S3{*CG6%Jimzp*x$l&vDUAHmI<%9Vyc%-zO)(OvzKVT4^d4ni z_p`zGE}vfC5I_%cZD5;#g$3XnM=ElX-ggy3)*Rk#%wP>qqj@uYr4LiD@j>Z;a>YvK z+F?f<*aiU0M-WK+gCmgM))K@UTlO3eOHSgye#F!2O>3b>X{Ks}h*4--Le}AIPDan7 z8>I;|TU*}Og142LMPCd*C~c$6VezEy4~ITTmUXpK7F%jQ*QxisjGdm!70;|8rknIh zcds-bg&szb@?AyG+nCax^r)bsBk+P)o8rhHQld-*NwkKWL4>ydQu4kxzIFZnPUJS0 zJ5bYmweh~3>nAU;&^(j-#*#w|GNiY7SlZ}{*Thlj$}8qCtGv&Kksn2yIbA45x7WWN zMm$~2o=xY%viYWx__P92QXdlTKq^A%=pYi1k2qe)(sS%^?o%%!)xS9VyTsy0h%~z8 zDxgR1pGibN;jTT*fA5GX(Hq<&Tz6GUL%TEIJWkViuRNRgwrMT=yoIN2pTK~}@G$N_ zK1YBhv^S7z%rmOM(1_aRc~seLa&&OMgH##s&nj^@&U)y8BYJ%}zAtk!AWio59V)Ca z6cYP^;5UE5ko$(sg3|9p1+h!0B3{{Bzq;voRp3)-X+mU>N{zD04@j|u#oC{bZ7#Wu z^XjB^!p7MdXrKmEd-s@cV9d3p;~$YnF-;5ItRydsYq^Oc6zD!8y^kr@$~l5LNhl*m zI`=2*(w2A16;!NfoJDu%jwyu1I3Z;ooM67c^kag^p0P@R$DXj&&`n+(wFf4;vDzSTEb)&W z%Usg3vzu?)ci<4UrzADc+p=sv)X$LT*hgc*9?&PqW9)?(L*%^QwbGw6CY_UMt*S&h zK_7Yt`ZX@Y$$#r|Wn?c&DTt+|rP=2OU5Ev-hB{e~%DGd1P~USG(It=8F`eUmH1#?YLOj{@ejyELRWZ)O;}L zCt14C{UeXumOti-*4{%7NoNFmrIuCQe!GHwR^4TJx@z=H)7DfJ7^AhL6Z)>+l7Nt% zd&Qhl?_g9}z|fuR9?OkDAJd3z&M`c6<>dQ^M)0;LXqOyt{j>MrKB5v! zA;-N&WYn(aYv8rSg{{F}Kq&WLCe(r4z{<~#@mzJ!>3pa!rlQqky_8Bsy?%?)dcV*I z5Y1aJ?dn}&bv-o9v+VFox6l@sD%F*#_?#9$vg4$U%a!2Le7njwSKhjK$n)X4G>4`wz_>pTFWIrHE|;OG7Ny+NvF~&@kXKX&?WBquf_+^2rK$F zOjvcuH1rK^>&Z_3hVa??UPRgTr3l?>^b1n)1lh(4<_5m1vR$LKfP?jm zPbZOwcZ!?q!sqYrrP@R$^1+j(miM5@fKW|y2FUjlrdY1swO-FcaBv@ON}37Z*5yt5 z7`%A4M+jKtx>zrLEMUK}Y*D$aUg5}NC3SXEiJjAA|GrE&{15?I6jp_WVu+Ey?P$6@ z#>YLoW$>(Ichn-aU?5#1j|g#qi(o3Y@`%a=qkVMY-4I1!Gz&pDp0T-0y0$-<)~0($66;L z<1cxYOxv>4zWLmd>EAnYS}PmMDa3e$+3ny4J^7Q=a&f81*)Y_Sz~GP0ItzE-54sGZ-Cbeb6KrUq4w;gG&|+~g&V^gIQS$-w_MTpbyio79vZFLl}oNp{b2_8cv;z! zog{rBUfVTd$(chULz|)!!3F#F4!c12*7-Y7QHYYnOE|6hWl_e&0A!1E1lbJL#?Dup zuqrPj7K2rW9KPSzZ8pd0sRP8f2(16go@-p^RH~@@co;Yy)oO_EkCxs79YOrLF2ze< z)XL&B4T_xYO5Dn_KQV4X3i;rRb*m}$^A+yt72TF~u1mtoUoEeB1oks;HB2BywaG?~*&`j3_^4PTIFA{m)9pnl=P_q#Z-$sN z=3WhPkJ#t^U2c8v8^(>%KQNHuUXgsH=W;L-< zH!3PBd^|MqoVs6(jykVFE(*FNGk1AOCOc`K;<4t;$#sE@e8r^#bcX-5r2s^OM4u88 zN^Z4yPJQe#1NTryHJU1rg~(bUZt#<}1_*j zWk|Id?Z}Z03x$8>wxJ8fD&2ZR}H?rp8c zIPNpy?#j_a;;j_OiOc&TB1IkgJEoIusx+*dZ@7g=5d52pZ z`7(W5C{=y~Y$?7gLVvo1?=}G_kmyOCdjT_k{53D-{mFQ12HOA)2)|4S75}a5#ihxbPLWnNk+o~@nb^#1c(K)wfb$XyYg~rP3r4+D zq$OvZP_F=z18AXj{S#JLB(*An*uy|cNjk%cmcq*oDtZyqp-nF5mR^6M7;lUhNUlto z{t8k(S0n7-F$O15`kXQocYdCKGyhutHAAO@lG)+qanVI0LR$QL?c>K!3V2`ttiDAF ze7O4L@pjV<7g2N=nzme9Gf>LbBW*dvee7@J@Tgx+WcPSGIN)_4%d42n8kx&DpGH!{ z?<~A%aYz+M-HOw0=qX}Fb8$yEM%o|QgSR{EESM&?E{pLup zC7|_t;jSmpxC?d?BNT3}k0x*qK|0{QsZzJq8O`SVf@iPFmUNYKMy`}_MwN2L&``mn z7nY}%0?m18*;0lDG<(D*mwPVFw)=Flq)?=gr38P}B6jaI25n(AR%Z=wcEJylQj;Kqz_|UIY?K!fsPR{3HId}KKnz81b z`KWBagR>jko%F0=PtkmG%Z#p0MpIVK+rzWmEE5!V^5PN1M+4QedPU9tn$#5B$9q)=7YhI&9- zxeju{X-(>9(f-}h>T}rvrdq!;2=9zDP=Uo21eW~2Yt+=HZ&SLG=uk=^9ApfPUH475 zKz5U%XnVS4u&P5lOwGDEE6Jk(NZ-RA?{QC|!#7(cmM%pc*RJcH3w~ za$vV>Vae}x6JX|JW7IjbYb>zH0|OA|5gK55=47NC`oKTSk8S>uk4Bt^@g0A-En#i9 z_RUo&a01d<2;TEuT%bdB3s#%$w;ot7fk{71Okmpea^tUoFQ+;8?)mM^wno}ou>3PX zt%sEqWq1E&0r+xA69VqA-s)~AG65QW7WkZu$q8%mja|S8L=o)4mkn@?J-Z$94)h@i zdP=D)^8HSaY`YHVH25;X1$)o7Z+}N0u*1M|3&>6|{BDl!98j5xS%4}&YzXL5Xd#!YWblE#?G*+&Zi4GPK+BT4d zvTy`1W`CTW#q3tl0>nnnL0bLQ9o{veg5Ph^>2u;9;x9!1u6;b`pf@qW2Pz_eI8D;I=< zz+WRE+H7wWm;4-rHANK$_k0ikM7DT+^+SUp%T}o1*B@`uDacb$?mwY!$*6R+cIL}b zV+%zsmQ9awtJ!b5f_i}-4Arer0@OQXcMzqc-2X`PIwo4|`8PvtzWN`H>G4)69*V9f zeBBV9U%Wa`1}QZpH~fA$}tkz^U?FOt0nG|=cviXT?iNHM?3|I zwf|_~p-mo2rvsD&1!rVEw&(G&kn9l>LQZeNqx9{ev9Lsj6|wHjsc7z>*YVqb?6BB+ zAIGv`6NKDBIJW5+l>{?fJM!TZ8$q4hfF^~esaH1FX`M@SD2cq^$cD{Vy-d;CbXg?+ zU{^UN$0KP@s_}`&lKEB2H0_eF;k%41AUpvaD{c3RL?TY%Ed3i&b4noDp*^HA*K?E3 zcJu(Fz}6=ZA z1Tm(Grvsd;JGPf%m87SFfy2x8#_c4E5qDdIiu~id?DiB(7t#Jx*({goxg=m}Jsoip zFRCD{J|g78E;Q!v-kY{ z%uT}YIWL|FOSQPz@nH`X6GqDkGswTJiW%MCsi&_^jFGoO8Qix3%DIR`{2i9M?5Vz1 zGk(laEn?V}b3#JZPL>t(Ss9g2c4>7+-vf$lovj#QRe(I2*#}1d0)(j4>OwrLq25#A z^Kf$ISzlYt|8P?N-G!VgJq0z7CNIiV7ci94Ug*a|4f*^?TxeXhPUM5gFpxKgXF8Gx zo@I}|JTtN!An2@EA!Pb6hc=@!?C*gl0qg1MkS65xw3R=*`pHq;_FTz9!YaGv)^_gf z*Dgh3zg09pQ+hk8^02l2YX+zm4n&p*ae#h>1C)c@BNV4f6QVaJc7Iu{Hu(i2F8F$M zE8n%VAthe>4NuGdTR$941)RWum>hF z3{()n!)B&!v*GUjUs+lM%_pQ84td>vjG7GCNq-?ozp^<6hKD6{rVDzz&h+iv8$vkc z_|snH*3^04yW-w+{_eDy5nA56lv^gaY}Ue}&4hGZv4|LR4WchIeIawj?#vS5WagJk zdgw#@pXPa&M9wWd1YlmvuGVJ;GFwnmjOn07YVek6{gvRweua zdlY$tR72=46Urdq-5HRi!`pkJYpnl;YYZ~&zdL~MgdODYp18ceot`GYR}U*|xBLZt45gx!am63D=p%hG#x1Qj&-5WoUl)e=QTBD8ot%?Okcm4 zpe{2c7O})@I?{pqU-@}02R|duAMz)xAd75ygR%FYHFJ}@Yn~$hO|Nj(LQw68+m^TSzn}@+5g!j6~v_<83#A7Qr z=)Cr=Lk(dMoHS+p8#SoFMd)G#`L|a7ZG2$Y<~s&bkEXv4LG?X$$RKtg98>PFtou{J z0_u9U@9V!ox1pFH!1n(Vc-i_R3Ww3dJWy^(!3)3t+~Oao0#)E{|LQ-t_>V&Hf8rLW zDi?sVk7;b))edp)?$3m>b+u`Pnh)m;OKj-TcQgH3Q*I!Rm=3t-cO(=bFg~1c>Vblj z-SdQH_}Mo#D>Vtt%}Yf0Rdb{6jrqwM`dPR__d}{Q$+8IPE2j+<3SOgU_V!Q%*j>CKKpT={yXr_w_CpC{O!W#uLw|4CO5tqohLz#eS zcT$A~+;noLE5p2sg1xbw$|A+5iTH!#T?97R^wC7|A#+(rMl9(sGF+^+OPIF-!1}O7 z#H8${rB$*@+j!_lMue^9h6Tpox%-^ujgqQ`AECY1>8|hHyru5{3|7F+v z3t_;yMPasL`qt6q4b;9xQm5OPFy+FbrJJ67c*j_Nk#ynWfnA&E{j-!r*#bh)L~vnA zMVj7)$igPGGVY2C6_G(Pf~U^kb5j7{q~{X%kF#e%CAeJXRM87BgFQF*BOxVGEy>Pz=^>hC^v_%9%SoNMYaimYa* zK@Ye7Cq0aP3wlr6w)=b`%BI6f_Q0s7zv+P#H(AyI!;oa|y7cGg{CMFsvw}IJV&adM zXU|a&g}vFWOxP@`*OGy?swO+wS8CLs6xSd6C^%N%+oj08a`B|zF2mhWNlemHp~30E zgVpTv4wA0%$|=jN%#!6Bh^d0mdI(iPj8)OMZvIhxStcZpCAixowbTp=2i{Z?%vg901aJDe#d2VtA-!hl+; zfWBiHTO$@Zte8ojBt1v`YkRehD>bg~iBf4>vinyC6K`AHk}rgE?&q@{bh>j0m#Z+p z2$gO#?ca&!?^)^_MaLtAJ}>5Ih(Q`9638UgYyBq1xotWg>kBzr4oReC%?_3yLRrjC zMrp=k)AfIO7Mp-vvP;qYgIZa3>bx#2Sklqf(@0~NNpR=^kg&1TB76bWdIYI39~C$! z!ronHonLl#SiNWd;&wnpf%T7*2RUJnBb67VFtB8igD zhfNO0Ong!VB!@lFb|U_!yNy@AIUvPXNhZI5agJC&bRP%RIAz=}gEqcT5vN^+q@VoG zkoa*m--ll>F7_?n33|}m&`AP;ER-A?j>-#vHsz4I9zg&;J#&Y8*CFY>5FwC7D>rZG zMP-U#EN#Ph+^{ZOK@@=;(#y5O<|sT+j;uxYPGr(UEo#{47Ny;fBO-+Zuv!oVbYKhY zHZOk*EvPqO=iBOhLc1ge&?)RF_|$ce&|d)QHnDOgx54kmRA5hiJ~Tvv63hS>3xTb2 z>8iKa3m(5GIs;LPZlP(AiVHV>%z9>EO&K0nh(IALUC!EyLI~z}SgGK$=|&Xv?jCe| zU{?Wwy<%h9{io0l0J@25;?3w#w>ldcws97~PUz?V_yqnN6qQ;2_kq{LE{@XyU3|!U zLOEX^LoN75a>DkG%7$bIW(4K_n~bA5C`<#mMJod_(@>mq?Ic=h1XKwl-{s4Dsq%Xp zpKs`#O%aSEm@o8F`#5^ia;w$P-);T_g@KvisMo)ELFG5@h(7RUqu)KiAef{$Nf$1l zuVg1<|Bp64tfz%@u=3|aCxb2CDMs#c{9Z;-a5 z1x3JO9HacrjDGcM8xv@!>jeh^9sC0)Y||AGrl39~J6>v+C7Ve2Q0~v)zlp*fKYW+} z3V%_L25hry-&1?rEajJk8w~{?Vsa$-?Rbuxy(3Ru_+`JprX#TL@%qpE9(!3*8OmPt zp%~ZDKp-p?a)uxM?9h~7(h>uWMf?!AZ)ft!L4R4J3me8*TBU7fe{z3IG%B!mElX!e zr_l&7gbV0Nl zvWUq@RCn!26O4^Rg?>f+zf`C&9N>~Gq?SwVI)@5d0AJQ;^`H0|z<-#fa-6rH?Y|+o|N?jUu9|XICTy!2rbDz^mO8ZR3s(ii7?-`cj z!e|NX@bwD$>CL8Gi$-9&lG?6XeLwvH$uY2xgkzJ^Un(;mubHv1d}5Z0ifdocPq@@2 ziZ91nc*3#o?4QnELdp}Z$Ivi|xa!}CmS4SsT_^PAl4s*Fp#G)k_$3bLB%no59M>zm zMi!ks8|3i*M>m+fw(*XB4a;?)z5q0G{$DexO%U(+;SqAaX_YU`cZgn;j$y=rypEG! z+%<{Zfu0TyvT5Fzv{Hr_j1%bP;aRnges6%*I7 zo_A0BJPjqbgQ}`@bW#S*FuJ2&s|9=H$+{#Hy!UdK#H3mmQT*DmnCmvJ)Okb=CnImO z>(68*-vqHHXBnPPlp9^t<)w_cbanSCl%9Bu|2=(pz2{N8V(IaL^Zc8JvBJ;(?k zfA&ObABrXVOIh=S73cxP2it42zc?i9(K&#<4>B>ZZK(E#K~TwOO{Yo#g+i(#xXZK> za!nEpKxpa5euVB`Xd&of)*crlen)KOE--NYa;(+uv95m&ZeXIYH&(C5ohZ$T8%$H{ zN-!W9s#1twK!5-J>SgaoQ`e=u>ewJRZhX0-{x7G89|00A8*ow8MAVqR59TT#L7Gt7Xx(!wHd%qf;@KmUB}3rA<9#=yaBK`wZVtVFpU@s}}>jLOEdkIVVbX zuhGC!ktD!o$hL?Z*qYMj-2U)D7Y8USocF{Znr%aD(9bwPk+$X3S)f2-hu!#I0hD!- zl4SSujW)wPhDSWo_C*L40XrME{5VkG%w9OVpRas2AZHw#k*VDsKd~lSdWn6xeErY; zMSze+9jO_FZS4P|(*8C;PWgrBDAH}G@FGqI6j2l*EQ)%{>z0uGZ8t)gEqzIse$brf{i;&Afsu9KX#T7Augi8F%!V4_ z^g3k*fNmyxJ~#i8reRA&W3JUpVML-^+6YRQ2j=!V&;77I;a*|9bgVaS;5_41i17s6 z!+w{ACTlq2RRElI=56|4AP*DickN-iK(+&aQqTm|Bqp|HhIrNyvOfI$PmL7kK(e$$bD3@oM-DA z2zHD-;yS~To~XU4AHGh#6Lb{PmBVn1lu>7k^;U5gtq=V>CK@Wq*5ZA|8OYejDZ{RbGK zEb(7Hs{Dl(6bJff+T!Cb&jmr!C~*K#DtALUBDN9sf07daEp{903zcx!vHyP!v(UV| zL$9jQ$rwgYLVn6pzomRXK27-5tEk=rv=HchM$Mb2#nCBqZAJBft8Dx~8DAni-RE*# zQo^1)rW7sQ@<=s0iEUXj8%La$q`@()^I7P{ds|MCd_Bb3`S{QmK0#FKCia1r!c(rU z7#S^yXOq^P_d>FL22PW9^wr3s6lzg>%NDwYsW1Lluj;G7EF`JhU*k{le(wNEgp1d* zyueiWy!Cf(#nu9A)qy?>mFAZl0e91OrbpWDGoW;X>`YE?vGJp;(Xt=BD-qj(=$BId zI~ic(0tWy5PGB?XxZ|-|uEDI|3iC2B00U&v#P0fv&1L*`TL9)qYTK2;Ds8{CAEnmm zXByo2C)yWtm`lF=P2A|#l6JZjIyWFHsJV)E|0Ng|_y!ZhUxatO>MddC!O^l@_KTe*fv}?2&ZHf7Q!fBA?sKQF{ zZG{(PR}8~%60)#LM|I;ks|2WWSdO?qucOHv4PuTBB-x<$d8RRhky5OcLG4Im9P)4d zQ+vhXd=W9io8p8Reg`g|H9|dUs^KEHX}7bm?R9bCNSgoHN>5a-EUuChdeZ5i8;=h* znt`r;rK4q&anNpSvGpc6zHPHTV`OnAjdSCqHK>vE08)SxoqXgu^=jw>a zzu@X{q&)Z-zbOlM{_ubmxQu247i<2)WxNQp7BW)!acS&KT)O4Hqz*v`Hu(cIiRK1+ z-M}-_Xq$1;lYqVmnx5Iv*xz-Kuc8(R@u~FOMb#ta0)Y8?TRjKyf7TGNHY6BYi6vyg z-Mx>lQ|||1445NH$a2H*Sn*w2!`w#goI~0#V}cTn3vXE*e==S{fFTIX?~|YD(*bWv zzJv#nz1(0%ETXUwmeeY_Oj)YA$7Qq>`y;KSRM|+a+brX_bXq(^=Tt=-aeTavT)JX? z`ZwJC<12kbpo?VLht@9lkv|C`c}may=>aYo({*kxo9edoxyCo}i>zc8+8u3nPfbUU zoLQprxgpF(b^e>>rIXU{}dfFQ+ubK@Y4^A=Dh#)9$8>>}x2s zfnqmQ;$-u)Y!c7k`bB6+NY6*&QDAmG*DQY-!azfQ>2eRB^3E6*DHS;Z2DbvZ#7_}M zoN$^_kwr&B?@HfIR{7HdbOkrPVwGbE+`Wawlp~DQG?%_If+yuBT?Kn*T7SN>9Fu1h zf%oD!^S7r&dQgHnzXcb#PmmMX7^89L47L`CTTKfO zvL@$W9&C%ba?T@&wZmm``JZRd3Ok3xXz2Zs(YM9}x2~lC)GtUGNFwJb!AU@b(J?nb z_4%P&6xQ=78lsyrVV8LNk#w3nJ4oZ4Gr*ypvTp`QW$UsL ztI@^y3CES9*pyT0!e`?qi|@|kCY;gsS#~~>*^6(){oShQfbbma@kcHG`MuC(QciU2qnc zt>H<=Z!lA`yWXc~joFN}2pJV2Y&xB3NAkWErmH1s6k015Azlfzk#d|`e3pI+Eh+Ik zup!aI0j83pyOuSG*2U;>nOz=h6~(`xR;$_A)x)u;E>N+n4=k93$%?L z3TjsV4PLfdXG6H0Py4s0yfjUp$u%0tzac|z_J_r6QS@HOD`XbPs!eoli}1wRuI{73 z_c_H@>5WWhg!D&KKg3c!i2OQ@#}iwH*V92sMw8tGdn?6JzHrBWzXc;MTdQ_+YD;M1 zgAtNI(~?p;3y*g_S;Ka z$x;{5-Ebra5_=+}?>Mn3r%-a~Py<{Ld`}NpJ$w1%x8mb^k<^EpCu@h~-?;b)dC!a@;9;LrVb9<#EkP12Gq(To$z8;k0fCThr?mrp}qlNfia=dlG@hW#ccA&kW zUT5_Dns#1cD`R&vB+RtHuE{>{)f+`VX*MR7^Q8KusscL?DoG#>7X7$SYmRj^Kg@^e zqLc!@;7gZsM+=K*m!zUQ370QRRXOCAIXm!m$>2wX+zA@e(im7yi_Ae6-%~LAnF4lJ-N! zVEtsZylsx9`jyC+F9BUYvb}xK3(4fKaUJ=W-wcx+^b86NVpIb=i`Q(D3=I1zb=L90u#`MTHuX6j`bhS%s(jPaw?;Rhx09*%CIS)a z7(ua&ByiYGk|x8}SBGb#xQ)-*n%8?gw9P~m&x%p^S49NBlG;r%gfAK7+r%@n@AWsT z%3X2div~@ZqnX;n@o5HfVqbgT6s0<>(!6&FjC*?p7pWIG#79$J9^J@`!_MI%ef@7V zfAsay*?kBZ_Q-lnip0143rJjqlPbM-mq!g8Yc;RTwAjjKiTzgQ^v#EleVpwr1xI{) zh|D(_0jh%uQVo9AxY+v)sBusTruQ+j+CcxL<<`BIoB9uW1R!N-&xxs_56>mz4nCEO z5?ilT2zU*hU-;^}?@+sVn?*%|0p4VPH7>k;(l_;LO~&a_c6&D~m)93V0Ho&zJW}ps z_b|W6Q2YzjJz*MB>MV4s8&SA)>G4|l7WxvG+GSx}dw4LoDB#_d!^Hkf7jec}8$SS@ zsQu+eP;!QBveDaZ+_PU2Oci@=Yz}SL${I}QqA*X{e4{pT>CLQtldpF#mm(!^#2fyA zB=1#I^jF^9lsbciF7PA#W~htE3yH8Omd7&kG zcK=Z(+@!*OLq7bxnHeLaY9nEa`A-%ZijDPe@87eDgI~GAG39S9oz9vtPQ2hah<1ni z@8%YObV5Idnm%8%YEEHXRke$$9d&nB)U@JN+|6+@o z>DT10uQ+7X%YlTx zKcNDGrICJF|K&h>xt7TNO{V(O191*CjGi{?^P2w)dq$?5uzZ=FzZAF~t=DTN=zgIq zwo#|i=1Aon9+kLmS|#G%%5N_H-2P=W-Co=hU9kKPWd!A%pdz5R(Q)wLhA{cl61JeF ze;9nri~T%o>#uFa0Q4!gCynSSPYNEUlF!|8{|s$gR?snv6l|#E)zGN9?-^ITmkW;u{0=<3 zyH#}zT?Qfi`c5h7KY*JBy7vMEQ_vt6xV$htSO_lx{=0pOcq?w3Ac7VkdkT|6+2OZN zGn*~eA6R%{g-(ktveC6nvMvB)@@`P83FxhxWw2AJ3Y)>S0Az5&Zx!n|cRdjYA!?tV zsXKM+vGQ8NXH}DK<=W%iXkIEA5Kub3)S~Tn#4tgpd96C;6*Ga&gNJ1y+&T$`Ww))8 zPWVoUH{=~G8C|&;0_ovRK?a_c>Ynnf)AccA;JN<)NZ`5pV~v{Eqy@5+5g@d70^;0Z z7@h+c7pP82Q<^kQivAj=M#pi&3wQE1JBJtc-UbgT#sj>wx3ZC#99!RVXj{Q5TEjfK1K-{!vb00YR|LX2@zD(wH5;qZf) z6B+=O^M(nx1oOr7b6yF6Hb?`Qmd}D-d}|=w&8*Y26w+)p z$n^TdaZ(Z|bfT`(1m%EgEiqu12oaN$oast^n{Nc)V)HuDcV?Wz^r3L zY;`tc@|!HgVhl6wcOoiW?r){MAJf7~a>{a8{))999HLpdaI%ei*gN#lNM>X5Q$4$y z(APweSBzs5lbmjitMlg?dV6{M&&Uq|C;J^Raqrmb5|8i|=*C9|+CeQqM{~C3mNtCxI%$A-q-v_!S=B8}{{}9Ot zxCuM-_Vl~1I@kM)rf_(!^0ZU0TFnEKL`!K85;bHXQ2UeKo1*ZPf%_z@w@AKRL$f3h)(?pG4L7FEKj?c`LU8Q=Av?D$(u*yfUBH(t*^1=>kQr^ zb-K-9?&Zd$Z=pPMLrqF`ewfyC^EE}T1p>FKC36K}_PE1#h{(?gaP=dFb4wlxM%By3 zQN?mus|JJFj%D_pXTA%KXl;m2spkcCIu*Kq)@<2lq~(FU1U9tpc~vMi^}s4~&OekC zpoeJMMA4SC?1dpLd7=5B&|3ee!T`rM}zGcY_&Y;vc$^leIZU z1EI-Ck{_#vcf8yTq=jCjv(S#$g~h(HVg@8l{0u5XS>ARD%R_==$x5K_2}En@ zwpX!3VL%wn*{t+bpY!TYJ3xzj=AzL`)^t_47>~Mrb&P?N=+KMjAK&93XJQcyYl?ds z7ez$6lFCJ!8;8Hf9MSq1uM4^FlqJVpoDkvQ%C}znUqHy=k*B zdn^b3jo?T`{& zKLEPKb9B1q0dN%D<^ppTq0ec$udm7WHOmAOxdQ~sBqi`OVXDeS=PTK{YreH^yOfg$ z&Fl$*mEG?=D8ip^+{k~)X)ev}K_X`%Q=9$rtB1Ky;o{696HCQM3pEN}`rxlj{$o3q z?Do;bp%+^cneFWLhb~!}HF3MSR;)PM$!2k+h;&-jXu6V|dL>U}@jZpX>jhQcV`u}5 zO#{jTkiu2hxSI!OPynAerB{Vg7=5%YO$Eu6z#d%x9 zxkjVagxt}J8aQ=#BM^kN{WVchGvTppB{e-I*hIx)DfueX%U_!{1^T`vj!ZWMYbrV= zcQ=Wx4EBtD6X@ih=jl#~1F44OT$iP#Qi-v*x7n7owL_+xlep%K-yL;6M=30+p)^G4 zoKchV(bgnqB#1y9_xm;}t#Qn5s%KYx<}&q7Jui}&uN6C7qHB%9_uKEtzbWmcE=6k` zKhzR}PHxabY~4Z%?#_icaSR*@I*wB3iN|q8R{}N1MW&S#2UN9N#admL@tZl5-gKOL zsc02hmemhmoS6%?Hh%xU=xG@>Wsr*XlThX9cd14zgks@i+E(1RnjG$#)qXyp7}(R@ z1CLy7-lkTd+e%gzt;av@*0U zWrfuxbW$}=VeiZ8v{h^2x&ArWVI})g_Raz@v3e)vwO8{C4<|$nc#6e)H;4cn{2M(JRZ1qVDDk4KiG+nGO)U z9UakzuP)2#!z~xjI%&YNuarEW6{n=hpM1*Y-1e+Sf5>W_hg5vh;Yg}pQ^v*Y;$=P*nP=FnIY4&-NaEig+j z>+@V)S`K8BU$VG$9}vH?IxM>aYUl@*IW;*@+HIJ-rP}3@D%nj+}5_6?qQY zpT_tclAe%yJYaScf0Ajo_~uEJA|qb@L^tc_be)U%nvN?H-JzebWWr9dk2ghq%RfVZ zRZS~mdc{=R)-Vi22L&>k1}AArmk4OGC;v@foSK)2s?Cv(OIJ32VCzGblFzN~G|rd7 zm-y0QW_$ox?3RhL#q&zx!B&w*luD6pHmy!at4*hoArD3DeVks-$MFPHXO2*H%r^=j zb#Ax(Cj0qrVl}>+X_nQrvElMcyG^H?5o;L{k;2faZpgF?e0($^<*8mc(OTA^)VJ!}t&?}F3-400Ike%dR0^|3V{kHK5L z*XNmAQUPPv;z3@%+gD}gkhK-J_YHJzEN7v z$H$#}(1S1fw4<@4{Xn-~Z#R6IaX53Fx!GEU zgKH5;-{STRrN=Zb|xEwq|@_?RZApX+x(?d#x_zkt2GIjj{c9=Ls z+{Ib&OSwN)0GoUjaVHjS=Ws_`SuZ}bn-2bphJ|?Szq6}Acs(?t%(b0|kIKok=@fTR zF?V}+HS(5{X{uDkQt5kk$ zzV^#s$|5ff49ve4*SxCt8fvWbsKz7j=F)V2#TD8J!Ib!TtbGUYZpj0$MRsJVEW#iU zSD|e3v%`{lxP7I;!HcBlzb)Rg5hbn`p3JY8e>KqTr+&@hONMI7rSEy@dO5`1ngt;P zka&b$CM{ojbGFaD9tHswWS); z&&*WGWc@Xo1Rbwr4%rSrEh`4Ls;cGO6(+fN^Z66k`n2asf@)Mc!%39z7LDd6JUESL z(&{fuKY0E6b?9vk=JA8yo<~te=x6?0T5ve66yvpM4>Q%ui)&f=Ut{x%a2Yhur+ayP z@i}69tI2lKt7w5LnYQ~Pwt3b=m%=fr?60)VDG#yt6ibQ{=O#5Vn!|G;q7@FVI{Rse z_}g^v)xMM-7<)VLrbsQNfW#z!VpK)khYY_!u+6r{OT|=Jr=`+twrcj+PmI%^%{M=>JVRxQ|I&0(?pq_!824qf zT6mKRF8XjSCN^uU_l;7S0_ikX?&K58pLhyB5adw2KkOFy%(pDLXB<4K???-H&U}vH z=6n!g-F}>qY|{}&KEPs0nm}yhqY1eUIk||fnX1fI-V@4=tVZo`=KJZ8Tqq$fGD+WN z_V%EPr0oYjGS~LmcF|WRZc(dj)GK+#zz=eDl^msge6`W<0ZcFAuybK+P4Q5BW4vm8 zyI;*kr%yN4OoLQ!jm=jzb{;PEp^C~jDLD8^x`b3G`=Ip@r}8AXq;oRmw!^+WF^Ry6OcCBykj=8ekDYqbx! zibt%*)y7smtGXu4(RkESGtXgex)6To-u}o4+qz|GoomUe@wAKncAkzCIhFG6OiJyJ zHke_m1yY-}@=n9+4BsZu>kKty?RfZv+InT8{0q?^FsHo=Fl zh*B}r{7{ejLBX;3p@h&;7tz&}&q|9+C(=#f3bJgi@8s+mT{J{QyPt3k7X8y4>hZm@ zCj+ZQBJ!nVKYeZesg2ACisU}ZwxD!^a+3|W9&pZgjk`r+ft36Csks%s5$R*N3IeL2 zAf)(#k|y~f;hEGSc@HNa+=fy6rE0U@amlOfq^7NzW~(VVN@azV9Ky4&?`u%^SPPp3 zyK|a!c`0?ASI=$WS7W-~44Lh)H>Iauv{_`xV)VnjOjW8udPr!&cMQIafAOGF1b&{q za)a6c5s454X^fqH4K=LGj$Uo6s5#g=)6UPQZnYBp{{91?+I`(!mOhj{Y*HZ$e{!C* z{8l~49e>Uh>|hEHc7KZi;erRq#7M(iU6|WB(U9|`@g~In^cNR;E*u>Z^Gzue zPtH01Ap-xYT8-iLRF$=r9pKqe=)boS=tS)uCb8DWzsQlCcWsZO7d z*HKeD=rLDVEGbKyOW-X-?Agu8(kB0~ki#C}x>V-oL05?u+~#5@bG>ygik&KXTf=Ur zZokZ^-BZPH@=lV!;>`%K`po7EC3OpCg@O+qWVKX1YSiA15hoWBnSJY(mHn~nPhg{k zi(8Z@D3Yw_BRy7!#O)z188u z>GZGjt*H+#%rO~W7YL|~b1$i~DQUM)lNE@DQ4Q(|h- zZS{)IEnHXxaSF2Q@QH78w@UT2=R}K%+9~b7_$FCIG>;*(a}6d3pNu23lRN<#Z8{Md zhsRg*7oF)hM0!1kZ%#23ghvhGP z?@Fwre0ZBq z@Y!i9PI#pI4cro;(GM1{ZYe%g28lSP4l!CP*=1X)XWwWRM=wn`dqs85@r@;2Ic$6z zvv6hTtGc4W_(LKl*SSDDYRi&;o;*$JLwXQ8blXkDiQ6`moD>iu1#6u>{YANSZBCj78kHWKF1fT#6OCW_491TYYo zY1%-t=V`?wjl<*WuukfH)8qsupVe5+3em)u!s0F~OJ;Q$0&W_aEsXhGCl|x&vp})b zSeeCYZN1N-X3{HVx<0wyS)Go;A9RtWRHv*)0^A!(-91)#d<3pJ@c`uJ%l5SnQr*4< zud{yGA0@MJjIh->MFUwdqCTnE`CuMM4J?7>Z?PrIuQA0UPO=kmuH=+Lkm*IxxR!-`!ZP)_SaxP5hK1&xL;9*F zA<1pKeCVb9!L&IX#lnPOc4&qftl3)&Qh~xMDcZ=Bke`0SHQIRr|KySxC>9k6$6tLj z*{)6GOoKjG_{$xwX&Dhih*yV6y>%bQ71DQv+TbcTUPQ0ni+cSNmH&pofjZ<0q&2ar zl3yCver%%{7^YCvw#E0FjgCot!S7#c8KGfA-`Xg~oGcCpraZW&vASpDYMgO(p89my z4uV*S_lS-9a^a-KQG^MFYQX9$l3Ktfd8=?Z)61)z(oT0Y;72(e2p+NN>FEQb8U1}Go21%#=XguF_!VL$P4#Y zn~f4rebL%NTR_Kx4C85vcq#8u+c0dx!0l=vC}?ph$98(W{bf3Mu#Wan__@PyK4TVug_f6dMCL{Uc+q^XA@yJfEnVybHPXoNGVYKH2A6csn zXrd+_VH>X;u<^<8Hf!m6z|TC$aqrV@=Yxh&&moC%1&R9Qi?bAydy|fu3qzr5mMx{G z(F#=#NweCUcW4$ShHs!w&6i3-x!V@tmLc`;P1Cr>*ce*#AB#ezrjn6L*;)_Pr77e! zqmSTfLI=YqmI|ABBgwwS|G8ViAKj$#Pd5HQVQ|8`XW*o5YwdP~x&wq!_r5#3~U zlPdVepBSa#mO1FA@G`vL_(MB{n~}kM;`6Jh)4< zuG?i6LHAoOAl*=cr5;H%_6!j=+lDDc?RFD|CLwOCUOAFtg^|~Dg|Y)`vMP^LI|Y~v zZL7u|WJGo(``sG^tCwiy{B({ANZ=cNAB4`%PWG1HjD{RIRnCENbs zUTxS_q3F+Q^^+idFPAdc7wtDad4w;{{2_X>{i|%~)gDnl!F{E-KrsOpNuq_Nbsa)H zfwfAjITWFwXd&+~BWo&swrJA!LSy2vjY;wxsl5Eoa7Rq0eP2?gkYih+>Z9^KJzg~m zyY$|#LVkwUU-&e;Kt`|%9SPFBO!hO{&qh`c<>=Jyj+#XeWHr$^KUgV6W*X)wDe3rd z*M7Pq`{w)H+}!lZ-elGtvYD2mNheC7qm{h%IznNf*{gHsn@v_;^Dvb9dlqdr!Z@N{ z*x6pB`ek~5lnQtd#$JU<2Fm7Ue(n?1y)HyR832=+d11@QR8JE zQG)ggtEm1A)gpAk#JumtQ#XTX$HH@B@xL@z-*!MZzzKVZ{aaiH?m4tzcg?D>q0dw7 z4uV(H_FjTqc+w*o8j3_IjcuE@H*USy(WI;y4qJztOw5|pZ-Uo=0v-MqrKp*}mMzYe zd?`IgVRLI^bL*^qAb&u~;j8m}4lwo_xaWpw+8-38Z>ZIfJ*@~!9?9Nw=gy&VKIQ-b zW_Ug>jV*nTa#J{R)3D>@jev5+5ryY+Xrs4u{TsaUjZGwQ)F|^J^ORwQp7>~e+>Y@S z?b7hHce+1a_c%Qh%@W*?K)rMx3B=xXWMJwc*p|}ysx<`Xaesa=GNP8`Ls|N`c^!VN z5vcY$pPjy%Z4P+V88OSMde5Nq($}Yc1iTY(b%tygBI|5MO~}N2Bp9BOCBD=6S^uIH zp%n(#W*8K0|3hE3h$#Xk7-;5CbF#g-E9;>8`%zXFMI~DGiDR>-+1B zmuCd^r#PmwbqFQvRq4Ljdr6DV$DJImuxIQ0%^FNqj-v1wPfuM_c{c|Qn4>%Pnae?9 zc-#wM5F?;p8(YO~S@0Hx;13N?);`a8`|ymu=?yQ;W4b~Q+%$@^nLv+!4Md8=aEN7JDpkk4L_TCK&?v;tQkiztX-xoqa|2QS9j5@!CxLQ%;tU40Zz>OSpIE z6nnnq76~i(UJI5x?11{Y}FoIH3%SLZn&l0Yhvh-Zo9iS~vSt zJ;tk5*dTO_@t*k>jMu*F>)acKCZhupNm0x>x+kRT!h+wI*bTqO_eoksnU&ja zN*{M~GN^A0#A$XSQ<>0mDEs{ILYf!MX>F{)Dz2lLcT&8b^SqUQ7p|h3xt{)g1KTgu zPT|R30>%&I4f94d${3b&u}gAN3L*M{cS6RfOj}?^rt$XG5ZKCfsIs*v3L8)dLd2mb`i0cjhd-b;;vMO0*>4qt zKkAeJLS{dNqCk?xz7pAM_g`yiuheB;C%x{xxbgS_H#<0p)9FFj3GNa=OQdnWL^!_l zTziZ|nfBg8mhNP%^K-~hMV1H|$64f=gJ40}PWhuIf@#;*aZFA>bn&2^>oB3+JdTQN zu&MBK(;8He=boI=!HO)lhgsD@;Q$X>Ktq+#ACPv|F~M!0MS9JJXtE+vIA;#i(FGI2 z3Y7<~UtUeQ$Rdpu$F&lI6)y^$S~e|fckC4_oqpvHB&a<8-p0P4e}j9lZy~o$7Q+h9Nv{zpV7U0qy<%_Kp5Z66gG0nEf zYHzyzuX9h5Q~iigkmS09~N)&1SWwaOD#w7Qnjb${yHN!_Y*m0&$@bJF$m1P|^;z#F2`DKVHL zlg}9(|LISsExMoZ?ai(gu9lfdmmNmbS6(mCJTtG6U+_Q~!DY&BF_@^?FHP1?-mT*F zD;B7&ISGoM>?j=JdT_LFW!NDjxSVeelp!~Oq6{~b!uTBZi{}ni>pGmv_+BVd?>o#J z^d}ik>ePy++NXPaZ4nO5xtMokD0-kqj%EL=4cdFo0mu-6MJ#zE-f4 z$U!$y)4MW~ue^==g66ksy9;e~Mr;)!JEbGNzu)RAr&R+jPB&PyC$xg_A30DS zAla%amGHc;@Up$DZVD3WEUcexpW%a_3F1D`J1Fm|f6n`cUA^i4j?g>T_Z( znGKxIf0*PRbQB#(k;yN-6`xm&mdLgzw1cucWd`u^?h4afVRX3mee^Jo{$;%vnmqMP z>_-?!$*nzG%9w!pDS+^M}Qz0hn<$ludL4SzD>#obafgMUqGHSCRQ{cxa`k zE28*=^BVOYuKmg$vbF0-KiHE*(&O_zefPn}N>c63N9s)-gX5PgGo-3N;0^AbtvcO6 zbrSh?6izU2r%APzY|+3I$8QR)WjhiOav-!p)Ke)zJ`7!#*L&BgnsvgILnORft8#JU z^O;|T03t8vkmmkrBu5L+v)xZC%5lws^hePA7ZQPS`Nt#ipZrM!=QK~A_soZWymjl- zwId4dfmz;w97XMqhr_2`d-EE5%Bc1VII4YY&PpQ^WmVZ_+iBH#1sEVs)E63uus?xWwKh>l6K9Jd{vcdq9kIJ7Q5 z{t>x_N5yCPI8o}eHf1d^BI-5!-zY2=c%G@o_gToXl*@SNd0k zlVx4!;cc_dRWq#m`-fRcXY|6RlS}(fiC*nGj*Xi6Yl5HlX-lD{Nnl4VJ=Kpg> zYBi^3TY}=0;T5exj3<$ux%OP(ez1^er$h16<1Lvx>ZSQ4ObX1ZW$f@w$8NJA2I(Gk z<|&lc>G6SHtt)O%a)2#kQGwB5Y1;b7zt1HOEzwQH*3o^BYnKAp)QSC2d3W6bY0uFYC8o~Q20?ymDrIwZ?c|MEg> z{X_|#m6*KX0xZ!>4NaQ=YjpyTF~D3>XImyV-`f3?Q{~)(!}M;+7;Gk z{=5^;>ZTkvaySfgZ2p=Ij>jQ=sj_WrZG-9TGIl-VxOe3;r}lQ_9{=m*4}Eb!-TGecWTj<_VqcxGkTlpsddt zb%)w?QA=u{U6{IOaL0CQu7LZ?QSDe^*oAc1`_E&mL*_W|8GW&ZavCN_Rh!nV$mdaQ1WS1&H*? z^(7_{HAbw2zMfz86RJ7u%}Q#UyUN;?TxxXrm~l%ugwhn>A;A6gOT=zJSBi~Z!KJm{ zjYi*GtyM_(20f`%et#8vms?>NTtwLH5;gZ@2598eVDYv}~0-Rw%%_u8i4_4@ztT8%q zPzODDg(NsQs-Jwh85sC-Iq+qs-xler%C(>+ys&TzrES^b_kd$D zdque{ZSRtZluUT+ayJTzUSP=#Ps_;%IxQ~Ay)L8j2)viANa33(>*;Y~fk(B*etEZ98u ztq3YE!|ulv8iUURd2dPf3`)*6nF#1sanzhG27G;)o39eWB_%0tq7(2WZ=i!y?c22V zT1j+Afm_-5a0e;f&lBmN2LXkwOw3K|iMzk__ zqsp`^O0bZp?bC*H>~5m;7vn)PHVw7QS602=nOw#FGb#Rc!1r0I-(ggxB!Bqp8-M#b z5eiazKgKcCCX))~nBaVh*O#s?YyEP6eyvmHVl=1WN9(ozj=cT4=z(p+1K9GQg`V@W zM=DfdTo%r@Zh(Crd7rg>T{LB~xROb=D?w4A>S8ccvE{-SfdobF-UaI#vD>63`@O`} zp{P=#=1#oR{4^LenxAD&nv3n`oBmk6LZ6OFres1abc6AjD) zr$EG_=i}pR8zD@5rC$CmN9So*0xm8thV(9cB=#X(hW593=BnU=bv||{p8M|^{^!YP zh9Iz`9Yjz_i>jnK^~NiUyy@fqt3hXi)Klj$gfU6kl$fhdHov6Izn2ku{dc6$tc<8S zJ(RTDv7E4lM%y?atrkg+m%`?HB-w&jM~m7v=i25@_By;o^K>vPN%0nY_uGP*7=!Q1 zxB8lfp}Na$t-7hC!nut?9&9$#6-t^_9_Xuj|F$F|^O#EP{(QojV*hxiWg&-{n**@X zW*S3h-BfC4wo~02M&bkOt;P;Y$#Hj>-gql4Di#{~AQaeZ0yv&o@|Xp;NLDWglFmMv=O^YVRAgj~U6se))`lbfy$J_- zWY5kR4<+4NuRh=)Ai^QTP#@boDVqFJy0tD#__>wjXJoWgnCeTB-2|&L%x^KM4sVGU z54v@u##Z7|*!VT!XIb z8wIkWpE>o^W=f~t;Tf-C|FuE?+((FZ+ZE}e&BE>jTb|)sXir2}v+3vhZb-g2o@vrxwHNX-&*_IU-7teud z14&JY46m3eltoqH0s++`tA1M=ikaQaTo)j}(HX7wzv?SRrdQE+Z1 z+$gh!^r`8)U-nmhxXYI1zLKm~`QPUMM=+Spt_C8gl;AZ^-47{JU?g#hw2Gpv!i(QZ>!ysYP7?XvrP1Ru2kf^KqhlGS^6!=HlAIya& zyzY*>VZ^3J5zcECRlZ(}x4GRb)7C(yNyR!INAt^oMfz~EwP@c=NL={!`Dlq+Adfr< zxM9&)=NSktg5Pm@N-Hz*V6%2;+Hx(!QIKO?Zz_0$PEe14sM!#)kp*RkJrvfbvxO7( zYTZUpggY4Z`YZ6F`{p_@Fb=zKW;vg-kf^ z>nx?_THSF^UdCYxSh;PV_h|gN6NyA<=dqO7iRNe^U?~%cd5i|y!P5V-B!7ltk$7A# zh+-atb+^>k>ZQ_Ev4n@pn2k^ixw` zdo%caX6CzIwLhcwt08aVzs|si`-t%}_~@YnC<6^?lVe%mbhe3CDR`sTWFl(k2clH# z6`7$bnp5ER)Z3hG;k1l*OuDbm#SUAR1Be;^>_}ASZv&)t5n;vyTP9@EvC;P*=il`r zNy+|A+qFD+UEH2o`B`ZOjYax3e{SK-!jgK?n5~Y;$M=5XY1(qEZSlPUIY2LsZr54b zuv}}76o9)sdVabH;(Z`7o!GN9g^YL@a-h((3T)+`G(|Q~Kfj{Lza{z~`+%tUoUl5U zY2(#j2K#e_E>A_su$A%_bT82;Nh>`juA{khiTbZO!g++~>+AF1zAF>;;D#Y1DP=d- zd7UQT0(?Pw`X^wywY;wUx}HQhP>>pHN-VDaD+m01tABs>I9X)6*3Cpv*nT&iCFF&R z=PEgFf^(()9?JKs&FaY9HOAi%1$Y{~3lQF!i~pQ?e;dpPEyTsEcZcj~+HdVVUzO8B zN`+xhyq&>+@o4PWQ7Rl=nI zO<(dWuiwtz|8=dhtFJ1my+WS2;$AW4&_wj7DYk)A;t=V({5C-%*my>@*gW?5U?IBj ztsR0zwQvZ^$iVPX_NegV?~inRS*ylHQ%df)Dfn&i{`?a0`ts|+%t({_Yvo(MR6-6z zr0|R7fDu8b!#)JFX-@D!$75fC+eHhDv#J4fm_uM+&yF-5%8so^PxFP}n9Kihn$-|M zE?3(&+#Rq6Af-8g?rHif*@M-h$q>Mn!nJecnnT&5t&syAJhOmLwT+cnnOGQ2fhdm7 zECQ%+-_n)u8NphAJ9?jqK+t1YFDCixnEmsU|M}CKvN6wNMLFN}-Bw*tOjo+$L&+QB zc(gm*83{rzxclJd-h_SDs#-dhhTRf<(+&7?pE!Dt^I3d%ow41?P|S(~9mpd#l_0eJ zk!l!!%MV25BBJY|mh}JAiKQhffdF6-0KiPMFSUTw`=L@;8d^nvS!AkSvO8jwU_6|g zYuL-640c0&Vq*Am;~S7NF2}VJa}2)R^!1 zvVgSiYr@8-xFwq?6#%k0pRYWb1|XtKri6g_6bQ?wyhn#9q9+hdhYdGt)Yvdr+y#7! z=2ZDKDGqe5|MAmu5r_Z7Yz;&3DS1B4wS`J*Yio;~?|yQDUs)(bHsIzH-JRwBmz53& zG347LY*oiEquze^j2WF0MV}T#^VR*WE@6{MVVjjOmVbfWcj#=nZlmV;?~(M+EdTc( zyr7Kz~`(}qgBV7ZLIL|6vW@eQ1sjQb3=BcIrHX)@_*Cce%oFyBF`|b z0~6sC0QkWJbp8ki?=BZ__ejoxXAGU4)ja^GMLlQ=zBa+)e6oXf`p{bfye0`yvzpgM z*3!kBU4noZBl)0fT7Z=UYu;fcfX(-^#8OXjUtL5P+hI64wb<#P{u=NSI)OXVbl&{y z8$Xu(6&;bG^Q}vi_xXQ;f-e}^trws7hAFXYeUJI^3;yC5k=^Dsk)|C0*#e`nzQa3%MLk{KM4d$U>eKkubHle5c^4di(+)I-rrN1rA zFQfnGbK)ZM3HO1vcT&h;7?iS!km7(XOUcf|kCM^ot^BilzBIdc%LrkRqZ3qZ$)KP2 zAWbJVFNa1!2J1rW1l!2GHJuFmrrp{VAzvir#;(SKwdHA=*uWDEvTFwnbcF1dCUmN@ zsl@x=nzP7lqj%z?^&;^yXBO>H+}+cqSH)Hw>AKiA%?K7EMP(>zQZ*R+zE3W+md=ex zidBv`qmEKkAJPTYJ_j)3)qdhP`rnhsKj-3VO$3OG5`cT1WJIOdfqWE5<7D2I*D5Eg z_LBh<*+Y0K9u2e{0uJ$YYV93@(!12BoQ=mflJt@s=l4gqhDql*@xbA4kdWWH?)_nrnD2EmA>O@?7%POo}UOAl>=U1$<%+2i> z0r%*a+ENu8*jR+P$%;YQV7yQ}Hp|<3pI|j2cywv8C~c9CL?Ea=Zl-*~`+G|M51D`w z0h|sJD&a!k?jdDu#u<@MYpH)ugtnAOw9}be0IasGYDwtp_h2!U@hsh`hHhV)wytBG zGi}haChxK$&*oP#A(L6}m%?A)vA)LP^5J1E?r$EXJP>gqLqf3BF&}1IAI3E}ue%Hdn;$^O`xMC;3ptr-5@c;XwAdE?x(I8};g@c1*6xHDK$8eij z5wF8#V7n{I2f#@*2kIzslXPn4HCQj>9^(T1+Qr!ka$8Hyd+fP5FV?^|wyFnhs7G}; zx-UhdeQBh67`LhnXH7BG9Y^p9K2uvFW~#62tau%n ziEoF02ZClt2r~+GG1_ZrxZhUqc|~s|-!L#R@cWSKsrCZ!I0f0SRVeaXFHRlmcjw%E z_&+>bHgPf#P8VlIF8`S`_t}V+59IuW4wH@ID&d57d#KfLzd7o*#r>3|C`;+ez0AGp z^MuXZgK9I>oYc_(l>uphKHB-T1le!+G)ByaLOe^OVm9%MY2eK#AHy>DW<2lm!RAs(QE32-5iuZEm`E%(MMrlu(j8N2 z(K)D%NNFq?7K8HS`YyepGjdd4(HdWkDJ1T7!xi*dk+~ZUrLUMYTZ&kaBS1$>kI8eU zej*BZi_6j{`f_9!bw6!WHUP1p)N`pa*ngs`gL zrQ|VYCq7~tInQO8-&3xJV9)FakI0xE(g;4uNw zn*>n;ojT>qap#PBICgk8FSU8Hh#d%vT{oH46JyoUU9owmj~a)rx@yjqN3*zx37M~ItLEbu2GIlut-sSj~M|>5%Slz`iG?cZ}FUH38Ksw zy$m2v;f7Qszq~Jvu&LM4#YI+(0l7*C+wO7Kh@LNIl}jrA{@vOb$O1q~iqXYNGa zex*!yJKuOXumM6Y@#2OzmdZ89JXX?mR3>zDZHoFnTy5~}DvD_DssuMQpF4L?8^cIl zJ%YWNf~&PuE9TuJHTcEbYeZ&m>eIcj%F|XkFP1)fx7!kaJ?{eNZ5qx_WG$}x`1aQT zWo9i}S9|Rmk>aXH+k^;OEbA|K)Ef-d3*MMOix3iQchcBd>-hcDTF-1LJTX?)l52) z1>@v#3?6Q}p)2vwYkdAsa7vEx7uDh0L5j3nEoR)ySvfqSeC1dW+xK<$x3a zyAP}n&}iLwqFfo2v2PLz?Z=?SG6hWOD``xO1eUjb46@L-lG`if%qsE`6k59E_ymd$*v)xv4unu%W>Hn zU$R~Cg>Wp(E-divJ{qMsOj6ppizH$WWEHW!>&3!2?1(ePl?NF`=E!z)*208i2iQ*A z;>AnreE8_;=|7+U^fSBt*H;Lf%gw$Aa_|Y;BZuADcL9+N6+jg~vgU=LF~kHRd9lsmk*+C&Z;{U-df+}s+jP8dTl99JaB%V!C&;P8H+RjfC1MDU z?(LY#DO<)9ck{7gi#V_IJ(7E4R(|C>1FCVV&=T1Gm|i#;;)8wV>J? zmgNRJTcr9k75!7p(9IIDX-``2O9`5ZsXUmc+<7g--(Bu+vsw`f<1q6W|zPxR1QiZ29IiaFe}db&-7aKY+~;L{n6T1IC=2-d~@{*c~*=g!9)9-4zM ze~M%O->C2-NZ>FcR>EZxw(=qv4!XiP=+u1$?RT4jTwx5e?hJ+V1HH+cimyjtmfvYP zB?;{;sEh%`HIo`^Om7bfT(}HxX3Y-4#mvggQ*5L=wc@n?<5*)S6Ei~Vb}S396{LAt z))k@eNk01oZG7XGXGOEZSmm-4g~UY2@G%2Dlqoq{$ppZGfnJC1d~o}GPT-R3Fr_G* z2**uCO)cUQ)&1w?j0Z(VgPm)Mfbl3+Az%JWmqd?Wln#)KZ{N5cGyM`5L`d;~6Oi;kVT0Aw@Cu#BQ*_DO)Ch5 z(mD0@kI5gr&At<&F^z^mptAJE=FT%o$)JueypkQ^+`VOeFen)Zm)XRFIy*TLS}#mX zy}0qO57m%dpL-0yR~9U3pY1I29jN|au-l{_bn6y_UVn>uT~c~tP&5)I+` zK6GUeH{ZoLxv=-yAPP7205DKsm$7?3XbJZH&%KJthQ5}5ifvxv7x+a>i3L8(hBBw* z7dg@OG5UcEUU!RHU#^1I!WjiM@?Qs_%M#R_6;9jTPZM)V?RHlTy)rZ^Mo5juigRY$ zLc2)!u0Ho-RWEG@E_Z2po0XyI!FLFIR7sOHH5O*Ba*P%O3V!Q@Q6xjpS(l&9J7sqN z(b(~-a&5PA-$#y%)@#ov-|znUOwrUurUU89He>GgkHe}rW}2Q9<@6i_GTR1h-E$zm z1e#Ty3=~4RYuy2q{v~ULR!>3AZX*8T>EQgXvmO59`OUdS)OsKX}zM7JyO4;7D?g-=kQV+)b^!0r3UG zt-0=v4n zWXj4XjYA@qZJX}el66v>S$fgQOeIw|`Fj2C%X2aIRjW5`g9j%6a;U zVP)pq$-;y@@Iiu=9p|$nO)awLl!$M*`(;Meu+Qas9_O;M-t-minz#{5Zheg;b?V#5 z{4VF50>_%UKm2k)TiQcOEVfh(Yy5Myz(U*N&CaKVAkaPDJ$kxEY)*m}u;%8!n4yIj zzNkShZS6QUSigMc6D**Hg#tBF9E6#<+&0!A;LmIWqOyZV8}2Ws|NkH6X2^IQ$Z*t3}u;IRDWZhq$d*Bj{JZ+DJW#9L#(8nKCxjgWJv65dKu*{mG3gw z=8jl6G#i{6ago}HelhO`NM(K8t?M-OGg{YhXs77c!M!8)!KZx2J@KCDnpG1!9k0Z^ zu(l~>|4&2)AHv5SovO$d$z}|Vc1$GPBONX>i)L0WY`1<%gJZwb&krBr11zuIMth+% zvhW%PX04J|)uTxir?wvTchoof#+oveD1)Xw#h^j{x`^Vu!_3{uKnf%`hEs z7I_1qS56n&Kht@74!G%c`m9UG=i$Bh-#6o$$W8R{eMHbO9n`j$GJf!&KrtGnh7}csG5_Sr{;&0SSDxP?;>)bCLI> z+r_J!MxQ`QF1SB!2g&qbQ5dj<0LR8A2;TOkHeg>$6uddies&o$eaC#ZnfS8+JktTV z2y>m!oQCrZ{1zt@@knk@g|pkX(1$();8Xoz#3(D6ltpF#gcr3=Wzo6)29yYMt(RU7 zcHoh*GW!YcDs@`BmZ3%3R}B6^TPduS``RVb?v%P9%3Z(ecx?r3lbP`o-mM?QK6F&K zOLNt|oNDVqbUK>{F!g0HGGWuHYLx3>H=9*Y#bMuam+F&zO{EXNS#vkxd zigA&RD>rlHGd$Du#Y{m-0IVHq=u2#^q7(~r=C&lHGom)KtHXIZ#2mqWaEX9kh_OSJMJQ{^}r|psk4z7Q0P@+OZq8c{s&t4>el*fkW4F> zc)^1%;WO(UR9aSLATX|~4v2$00)aL;IS~l|#FrK0U#Vid$SykvBxFsYe zg3qc^jxFpRG@@|i!AAX+C7zvHJhq`sv^8{zB1uSf?aJ~dFo8)q7N4CSnVekO5!`Xa z;aWQ4aXhqM8YxI4x%0AqC+D8s_Uq65uvBnnrO>4vna2}=Z#tap493Z*UI#7#Bgj{% zjTtECl>;7;iG_uju%BxU<_XejoU)$0E1xq2*>*eps@tZ6|5UgURX1Yji4E5LRQ2~#F z^G`gy(`B}tzcmBLOZ>G3>m$EBF#?``E>^0ytVXJ6_m%JYB1*oBZ8wWeWtywjud0FU z)_@@X0P&+-cw3r}Aj0#+I-j%7dap6;aFaBB3QMEC426Z`cr3<@omj^ujVWD~BY5&4&dekx zf)BAZ*V9sAsx_b}6ZzH!jcH-C;EGgo0s{p-O7#)N3q z4HJV|K@5!rb@k}P@^*o#rX1Fn#ly`{kCTz=3DGaU8J!nLdbje6IaeMybtfm`5x$e& zBPUya2YKm@vwt?UaSh{D7}`bxNGUw}M!kbp54O~L5PV)pSiJdO zBq*zjdU^834nl8W9HYm`d3B_~Hwh-ncjplJ(Bn{cM5*!4U8aDfY_Cmfq5(`xFq2|( zxNoTqaGlVB!(6_wotHFIZxNMl#9{G4E+bUeg3JU(3>g+`;Bt z(5Pvd^n@KU!`h=8UTTh=ABhzt{kanDx^O&OKCLduZv* zcE9Gr#^!0bPF}m*ou;dPhgi2|Tf~LjApYRz7y8Atv}Ie4*hDG1IHrn zGON)7DiA+Uj$j}|&4J$MAYhBRge?e6XWdWk@ejW|V})9heoK-?-o)6ejUCi<1`QW0 zOT(V^2Y2HD%@x_lMMu2Ps83F(y;$d`*`rf|v1^I-+G1~#AIa_K)ma>%yz+**_*E#!=GRUhGCIM|MsaQ{&a(y0vmI-zJr( zWTbBy9pAjyAHVK}b$)`A>c12O{C6Ng+YAB(tH=85!}k|Hx2nAEPYpJyacQhNLJ6Ri zz3cj6AuOsPrqm~@@x2v}>YqM+(iKRM$;|*@ndQ162DJxNY@65d^kAVd2?JSw0P4br zqppjB_asIVQTK^cXS;6I0+wJKWxQssxf&#t3xGY$mrfLFD zr+l=hf!%g%9wzPT?d7#4bXB3L)ch&gqx~Rj?K9B1ZY&Qzik1y*pMr)+$PN-oRpuNB z3&rqR4oV(OSEj?omAb;;RF4NTRt!30^^99?8^zi`4BcTVK0=jc#9Frjju{9VKOaUp z?kcMap71XtjZzA7OZSfi<91Y9aa^BG<#%_sTJb%WKJ0Hd6U%TulE1eVY(pWvn0Z0@ zb0N8p5yOqC+to49D%1VFcY{-hlh8RYd{!MoI zw^W1HC5R%br)u6ylK~#_c)KNMAl@P=-*Qf(it)W85C);rZ?#KxYJe4H7F6gq>m8^U zJ1;=-h^zxux#HVc%cO+%r_NV-^pwHPMol+>^+0^jv(AYZCBFfTmY>ji3sYc$2l#BP z3hUKWLKzse&Vw-YDUGAt=0ew+(nH2GFlD{L1yQJ*>mb#Sk}57Xn1M8j)Hwh&8*a=* z^^Q<*+kbL=AC!fdKt9Uvk7j@M?h}jZ0O=m1*0`P)@Hw#d0!n#g#Rmhy0g+7741#0n zb7&P$74RJJOtuCywG|9%7%Wy6#(FD|0ap__1Y zx^~REf#=Us@MLHZRC3(N)XWiSf)$;Elj*P|hlPg#gekVuQ0t8da({Q|pHJB>GxR)w4b`B2epKIgIxL0?1gw z7Z=pym@4ZH=;8K-j5HaFZsd0819XNp!CzAM`_umMPeD6ZRSRU#VMj@n2iS$P$<-j} z8iI7i0=-n_&6=;Vv0SWx$NtnQsVcM^>vFP7rf;PZ}6S+|m? zR@fDQ3Em&Ut#OPRY$o0ZxcD(VGMho2IUBfvQh+^f=ac5#a(`MGDJ5EvD2%)SR2_6_ zkyE=Kr)uP5gG6bY3+rWbMs9n6T;0~bj@>N3>F)?RE|Z*8Va!7A-5w6Mv=j<~n zwA(hnctj1BCENixR&q^7dC&~Us8SJ#_ABVB<30-b#IJU!$W%EqDd)ydny{&t_N|rv z(Y=MEiH@9W_K&)Va6Y@R;(VieeODzvSqQrA>g#k-Iw*><5nAx&+$mJNQE^_9xgbCcaNp?_Q@_UVX$}u7sGT!HZ7;QA8 z8S_p%$8HEBBEn<}-fn2ChN6mR2%-g+hwd5>p`r0>hCg7i*3}P17jFNT^za7@5_yYr zJb88!ok0gG`$Y}q=K;;xWwrBl6kMH@t-1e=x;-PsWV8_2g2-f*qE*sla-`XobS=m6 z$HVz;R$k-3@O_TpHX0bR$@><&y)xXJMSc62+~(}`BmtP43sVp49n_0OH(Ai-=3a4d zVT)twpB8%BBh1s2HRn-No3oR>QSCoL2TlR-ri!vi*#EZxiypnJ2)g}|({@u=RuYKv zA=(We0cQ~fzOZ=H@zR0pbhVNm(k+5xP;%n5nwMJhH;OA1N>L!gR|@Ji#hicv{rc5N zaJ3^8E<*dn!v%G7>QjUbrg`du471rpIod7Qlse$2*d)P0)z|O{*tZ!lkP9Te;(yrK zlo_%?v2b)x89e?cfHL%l1r33ofNkz zv-x=~pAXd`bfG%@O@jgSrxnCOHn{# z4+RkSi#BL5p^b+?g_EfWm#8~==le2yPr5W~h#|ww04f__Sg`qT>w#XfeL8l7PAb)c zw9L|0Qmc#m;8VQvyFgn(AGU&l5ym(vT}Gfto|cFM zLdk1ty@EvBRIYUyX9MNgR~qz4)p0=AX%I$P6$4@$fK(6vx#2|_+25{fbz`YM`A8A~ zJd&;89uZ?7VNkjZ6o8wmMxb}O)RgL1`bfIf$R@XRnAM|^%st0#r8OB*&^vg!Hj&X9 z^FOT7ub^HHf=xi>4~&#yT+0I)OdyxtYjC)V1`OfJ`jPorSB?eXDC4sBB`4`{t^lxt z_RGt2}K&^hyaZC7Gsuc_Fg)g-J2fJ2k=H3HUIVqWlVj z_$MS;OK#n&O9kfCMOpt0ad^791UMkp7Y+CofNIpl<8*ccZ@OROj0qYk^}d1+mvQ%| z5uPCZ!%*ul2tfRL5wn_9kI{QTF3V4=-@yffNI7Z>Pk~o$!2xBZ9T}Z=EPzAK0aOMD{mhkVuq7p!U zPd`kXKV>yFEt9h$KL(V{TMV&X0NGYa6GskYm~cP>n5MX1oB?p$nv2oU(7;!?%5|qV zNBa`C7s(xM%9H6j5AdIUj3bsf49HzS6G3)H3s3IUs|gOo3tU^Ay>IjeXa(G}iY0G1zQ^gbNJ&UbceKxOIA&yd z9)iP{R|0NAs3*mfuU?vTEu>zo@_N3*7&u~~!akLM`%YlvV@(i-5#|7meg+5SYNvX< z*^hB^Fn~aI&AyJD^}nVTz?kuX=i%Exixz`ekzub`I^G4q_887zxU`;MWj-z1m2(sW zay#l#uMseN#)E|YT@XWaR2a7W>DH~|dsq*nlZ~WHbsmBmQ^u>RRzN1L)XJv@8y4>F ztd1%&NQaX&(-W)jbw8yE2F@5)-AnSRau&u-OSmnd(>fT~M?Pjfx!{X`K#uNcpf(}} zPH7MY%c}v2ai}y$H7{9tXI%azczw8tHcvK;Hq-tSy8Z2YapKiWOOrKH-j%3Qq%kvS z-m;u;CkF;dc;wM$8=DCXw5XYS{Xh2JJD%$I4IIvivMNzV3Y9G@l@(5<$ZjE{j8wAs z9;YEv5g8d#lx)gKglw`(_9i=o?Dbr4g(EGW-yhHGd4Au2dYz7Qzu)(L-PfKe>%k=T zf(BRFs;2!iaYpSY!j+R_*pjYQvX=3WAAv*h2=z2KlE+dJosE>2j!EhCyUTX@sZ+&u z96f!|V@|&@?U73OV%q}(W|u7AXb2jEx^MQ16&jt-MO_o1?-%Ij3LYd|;nLSQ2GQU6 zAz_}Ck@$5OE(%JhFAu*B<`1_Q%Sy|!bx&p)XX~&`x!Y9=L%*H7imzjK9K7Z%+$h4% zUUKYtJ|r@FA{uuaWONS)RNSab)R6>FiZu7&^#P@pw`%CJ9uB+#u%>Qc4kqe1*6nz@ zvlS&H8(qB{lma$o3gYeGNFPfuV^vLDRM}8I1HbVH?3Y&K8!MJBzK>(D~!e)n4TC*U!^?c@drL@vXGBdHujo zbyC05-bjF#>Z3B|=WK#YUiU&_z(?~&$cRzlZluce+h4Z{Jqr#_L@%!uj$jeTh3(VyI2E0!YI3dtVefx^1V1D;u-t$pG>F4?%FUm*gP0R`l%1{ z9y%=pW2gD$OEE3+n)$jGCB<0;$}7!2x+o+ox5_}aQ?MFpmWN6o89X5Wi9mk!-}_G^81aYP$f<)fIjiXL(&`gnNk zf!HmX-KY$2qp2stqy;})Hh?fxo`IkZ>3At#6O(en)mGKde0OjWE}j1&ZKBQf{*c=B zH?Q(u8@1~~O901j~tKhH@l5J$@mE2Khp<%pR6`V6#beGNZa)chO)P=S-PSe)@E_{ytu1#5qr( zX2F9;_7UO|lJ8?iTuklaGbaC{g&o*$aZ>cLtQyrKgO(=v3njzCkjgUR{wLbnjB6cnSDrQ`ECdx8e1NgHDiWQXA1| zS|NQJJP*o;8&ERfU-%SGrr#JUvbZfo3b-Ff5uE?TQSvV~t|QtVB~W{wslM-~j)f9v zXgfvhRlt|lwHfbQgdj?{@l9pdL9nWZtV(-CL8Lu{velRB*&D(tVtgxP2!z>8_FO`N zy|sPTl0eHElF^@mN_1a(3<|xG0{ru!2=~uGv2>{Y?9@}wzEtym)+y0Bu)Jz}cR{-; z(U=C5d&7f~DXwPv(Aw5R7O_CSl;6T_V)Y}4a|$Pyw^x)=RegS+t>m^Jn{$ZXM%$Va-#GOP1mZdF(w8`Je&)lorcgxdoQ$tcVjvFazP4E!Ky7g!(TA@zE5Q;z8VIxHbr|Ve)Xk zOE*~o)T`M-EaD>+@(^$I@%HXiDvyJdvg+%ecOjO1$$qwGJTd!-HSZ_4Y>4@XD&!_A z9_&DMSFADG%FC8i!8w(|&_J7xW~<(={H=aU!Dx%})*Y}qOf4X$AR zu4R#R90a$KQY7hkG$Gi(7Ev)oEjLa=EapiAbe+BLQx&OD#y!C1%WH5U{|uBt-5)|< zpZaE_Gi)YeAr!WYB0ytcYA%f&LU{5e;RZ<(J-GXA>;$C!Wwbtb#O$P{<%GOrQ=xDK zvQNoAt(O+y&Va-VkAQ$LQ-Ko9c`s<&kx&Tnjswgs!aakg(0qpuJQUtg%^+Of8mcS; z=JEZm(PsI}6<|xNq%vA2n*g0yQt)HTZww_#!HjI>`Maj_ITK}Ufd?txmKt``smU`k(q-JyR}N)!pF(tg z{G)Wk_I?B!?kX#4JHHEyn-Yjl9wVX>s~Eqhn^ElE4hHZ7Y@Q_RxjsNb@ssel8I1;~ z!!z(>zJw@)EN%WhY9>f?3Q}asv}QGQL{k!U=l6IiT<9sP((lo@9x%aIrAqS6g2yl9 zV9#J2us_Wmj|Vo8y1L>?;bXO$A#px}nV{Usa}4{se<+^U$q_e@9-D>UK5p()NcDGZ zib)vaF*b&dJJn}KOi{PbAn7p>+Sn@-=KM1SsgW)ibZNka&Zpa^fNvWkzOBF|7xkcf zn^X{#tMpIJJ{(U(G7y&PcE6*5^HxwIa|)hrO1s$Z>4;>j-<~FOV%IM*4BOr-mV3I;Yv;{j{)|15;Ov-R>TJW z-q^&FsC%}n1_~B;$t}QEOPKEaGKIU}EcI!bnI|d(u{BeCKoWa6$ zNMa_NolyWjk55CTDz*+2MIsJu*&w&d-;YMv_Vog#y_5x8@@1Ky;1A$Cz5+L3fU5IK z>``Yjgr?zfePp|i(s|=T1s4Zs#xFNbA`w#vsTPwbZvbEQC1tKS@OHqyY(E_x`s`V4p8Y}>Kp6~#z9qo2mkxtUnGUGB zdrpb>Gu&+n4nvdp=O;7Hgvoe8qw}c<87T1AkW&Lz@e){XS%H~Cu^5e2&Y8}oP!|Gu z4Oz!s8#D%muH;}VZr?@1%}j6~=qW(obOj0TecDj$1~%yl^MQsQ{yBWCTf&D;MLjUvGuW!Fv@%$bHmZ06hqqb{DEvS%i^Mn!p(K*y=;G z;v)XuL1AMoSm zuV4@bUPi_A!*L|Jckw_ApDW_HZ z`*&#_UWGdD3@AJz@`Oa2wj6?0-Cln35B>NrNVw}5Lo254jo46H;8Qmf(f4*7)SZ=- zmQKuF^{xe61ve)CzVk10K2e!dav8}j>s8J;n46T8dyg&m@Lc-aes)8oBT*eb)bW7( z2k9sF*qoGj(Cjjt?|G1E=9nqyc-qf&qonPo}#K1KiOU z30@($_?7(Yg{z&ry0ESfqqwzY$Si(XhDT=QmBP;p9-_gWtXEz3h(`=@k102>1dl=~ zsZz3ji3SeSK8+Q-b=-o}r;idKc<2%=9EZO^7O!!mE;1=z&D*$P>0sz60w_4FYBI`m z{Rsr?P)7(p#i%M5J!H{Lg~@x(Nx1D)<(Zd|&%Vz2P(o2ly@UJ#C3h|_}(Hw^q(5+W1Qfp$>O@)=2S zmI)Mi)Yfzh@`?(T4c>O_GTzm$|^P(hngFJhY z&N)YSx6F?2+1CGI`RwaZ510X6#civ*d6odO00s0Xg1AlQm(rdkH=6syjBcbWko2wr zBohv5hV|c$%DX^jQ-Gk|R01NIX{@_<%NE-O3zCChxkUBz_5~tb_y^;>wWZ6)*(&mB zQb<)UjsVr!R|jbl*AWvVf6e5?8L!?rSY%7v9{uh|kOSa?jzYSr6*X(yvygWfaAp70 zl&3%*qmp8rLQb3jUHQ$M!5=T4VLuxR300@xTALw701yp7jYPa7{e69zgKURgw2d%7 zx!(c*f>2Wya)J20GGF$$c(*Ls3B6S*BD!rAC;c^l+i6p{Yhb?Tt@Hi!W)CoyA z!KMrJ7-0g$91zn2!aaBU^FT*Iy00oE1%4w$>>F_Ab#HGaS%wPoiH-!v?r6gJbsV88 zmnVn$1xSwfB4YIeTku7)vFhh2dNXydFnW-bKU#=S#noe zg1eB)^y8;!@V{o}uM&|Ub%Gbl6KRE21|}&6Z!^ZRDvX^!Z8d z;<5%4tv7w(c-g{OmRRe9qw-A!SW0&Y*Or~UrSFJt+Wg(+cu*!txxq65zwAH#(|e)O zTL9zr-lsT$d=d){v79&Igyo_HkaYXJ4eLU%Bm~_q&`3gWWV2xJd4cb)`Umrs9Fdnu z+IZDT@=Z10b&FFh<=}5O8sumfLeJI(U)&2{f1Ur*D1G;mX8pRBuxk<;txTjjM`t!a z*iG(navyyKy>sF1XC&0GxQU3ix`@g(zM~XT-`kp9s-eL`Pw#xforL`IEdp}rfNa^V_FWn2eo5^gahictmEQX zreA%)c9no)4Atmbu-;Q6Pyf4<3r*&wC_igi_TfNrO%y38a5FF-?1Ma#X@5UO|g8dDh z2&Q$T*lmZ~24s}>?xqdZ^t4BJSv=-PZqWIdTFvKCXgHC#^)B=B{3ub@V0F5WS@^EM z-u==K?_X5=#w(M2aP!W2)$gMhI$5}Mw^7bb4@7f~oVDQmfhFrOXYmdYhbr+KElqjA z-r+K|GDT5h{u}P68>v}(bYfh(cZxc$!(7EZKp=XC5VN#|o)NYef1SYRFWb9JL0Ns6 zE3K}#xA%c7&1~7;_4BupC7m2xQ&oTYOQZ+vWVpExKNqN|kRX^vTN1;2Uv zL&lcm>pt>ElcY6=F(D=Evjy+!8#L`wef0hJ%+i@t&l2vu|Gv2#CfI7apR+Ue!!+>( z0%f%9xhT)(ZNR=X=Hr>SttG<~4lm<IX#O$9NfgAOH3g^f0XS;C%9HF#Pcu=ribSbVUyF+F>Mq0agw(te!ggcbHuz zyh?f1&v*3@n?K#=!Z8XF98bTwdIIY2p}Gi8Y0L$aA~+{M(F{0PkP32Q=u&So(G}#7 z1wD|cN}-j#A(Xc`dCCZ|bbabL@%M9JZ8w-1XVd!QUdk^Sh#=us1ksw*o73%DAzxUN zrS$MfaU7@?hGjzIH+f3=G{0~&Vo^fPPK=kASJAEMS$i#A?AyX9f@Pr*ev@zU3|JQ& z#Tl%wX17<2L|h4}bZmu=llW!uo;OyIvBLGGF?GD4xs@M6h;n?bH_57m%5bn2@65;? z9yadSvttA6PHZ|BrB|CJJ;i*L4MTDtDZnS&YgH2ToZ zL(X9{W8u^TF&YAgxydWgM39M*zkwU;m4Ck=TzpZ4JHEWGYsv?m5Y0L~ZoCWLf2csA z1rj9*A-9eg(uP}$PYO?d_H2d3Cqf8ZR}@zp?pY)5x^C^}=u8@raLl|+1aPv@eU(|r z6tok3Uu4jZ$nf(f6hteDVaS|O4KxC%ao5k79Yf0M?>1iXz~+U2KODvqFHN1Usm}Rs zW-$s%JV?`-h=yxZ zzHp|*x*3wx%wew0R{(W}$abvu3PMNsM!*qU=}XOP zRKyZ?6C9}NFZ*0M)A8YYl(kA%W9%Ql!Mw(^HoDCufWm#g9ZAmL5vJRmE)-x^b^hLD zk_$A?5CBwYsTqJ4vNzAj3XRs(Z1~+?e{-H8U{%f;C-%^Dd1V5RY2urMb~4wBXRC9d z1}N)m?2?9oJU4XD*OvKN2i7NdJb${+pTLfbUo0Igm@JsG6#&0=q9-h9$9U}l7`lPQSQh^rS-9;qVP6@x4mF0p+ZKd_|d9e z|M~vu41l>VpTqLs5x3aOQ7vl_Ka1X1K+yb!CPf(3VN4#NjE1nEgiGQup zbUQd)54jkYP$xq@5<-Rq^etO&QJe_7ue+t0tL1N|z)x3*Nx%h#{~YHBQeW|wDrJLB z$OO0c8ft?y?l1MpoSz(c`XN}l3yS1wxdx}0scTD;9NU2~pq`;GLy#4m^D==&)$ns&fh0?csnzA4P zzZS8@-joTes9A<77R3I*s(|qwAkI;x{jk;ddS{jlDq6xD+-CH? z?0jSil|-`lJf}86%ZoUbXBs{QRUg+d#j}P+90$SJgJ;*@D zV}-y6WT`RP*e{sx)VjNBLD_*Sl|wEto0|H)g?22H%CB0qsBm<2dSL%;!SLk)wE z2kc_s9rs@#K>|Kl9vE)vl@RJ=DIXl~vJxEMgGH^NvQ=^NRPaAo4b{vBprPVG+jL$P zm>4s-sqxU?0lV4}OerU&F33j}WLyb&39Thb!;m7ncS{jRl;u)Y-d#?Mt~{!O}-P z0pkDg8m!w#uCHx7+>wEv=zoClWza;pcmm0=ti08f^viG1X&_VEqVE;_5)eP_aOH0R zV|Q%0{M=-%1`*t$vjSKRX^C5@{sU2;M`vdio66Atac{pQhavC-I42+4^H*bIHJ0&! z_Uikn)MfQw{`L*s1fs(LI z-mnLYg4|=!VHM%HB9z3en(MI<_2C|1FOnzEJ;vG^JqLKlEOBmqcRueY>AviKW82E> z#C|!}%9SD#;|J)y!+H1G@2%hnT>0j657)1K=?j9ZlrJ!pSZDvoyT|MVuzi+0&cE^y z|FVPslY<8P93wTRbB{ob+eg>B+g16yla^U#doqDc#3ZG7@kEU&rA@`i9EZ0vdnp#}p_9SA=u4Zm1GFU#)D6 z{g+jM*m)b>-xaANq8B}vIm*Y;e-1DI2Lc@J1>voB$@3yw_$hGEY;+Wv$yx!tWZ?xbTBggUXRxIg+%p6r=RyavwwNF zFUjNV3*V%9U|%R&7?l_l8)K(-XsWEZ^9Cmw3Ri>ls}sgIBAy^VDkerg=1oGvSt{yZ z9_vrS1^pQH!9vn1J99|L(mwnB&?LYA(mBvlsPqiC@Ez2&u2cGH;cQqW6~_<0Z6!4o%l1zqc zU;4k7E?xU?Kl~58zen078S{UFNNCR50z8@DzeFxHv#3adVbDI~;!0AdBgg6UQDoh{ zc>(d|V$e)upEhE#p8H$7O(Pg+zEX!=x`Tbat`u?h%C?(Cmv--d-*GUALYZyOb=1OQ zk&3|SU^^cm|5nuh5xRfGQzz(olzs62SEkRvu)!R{=+|!^S-}RDp7zHN5Q*Kf;bU}Q z!GBOgBZ1E4cL=UsCHdb=|A8T+|1SbBI@Zi>*H74+*?{ZqQ15*y;Az zuMLl3;Rqu0txj@w3rk8$61{m%|BLJCe1cl3js z|G`#&4z@a1v~T;`!GuNulO$#KAX)oT@Z#^-@O-<;;a{Ex>#kR6)g(({y0tI$l?Q`6 zLF4}K^E`fw7zALAhzE-w{SUT!1=#Ao^2I~{ib2b?LWcu}48gXO&TC()rUXkX?(ido zxyIG>bmfwCCwBjddifZ^d1$L!zap}y0j7aVih#y<(?AQ~R72K;B&#myLF^GTzL)uo z*c*9C4l-ZzHD>fw)u{KM6SMMn%S})A*2yYIiWmyrmlX7Z6R{AOo*|U~4@Q(@<|RNp zMib+0*t_{7zAa&(xCEH}r#ndZt$nGl6d>Lunfq7_>-UP(^8F*fISC#E@d%ac{~;~L z6PEfG7oJ+<{R0iqu>kg*^pcEQ`%<<2u+)>Ee}yXdZ;zp8fCyZ;F@g8maaIcQAJq0U ziTwxPYACMt$>V~=NfjqKF5zHj3_!Rf%I(mt%H#jQ-cs-_v(X%04>ztIH=yKcaMSv| z@9KYIEh+d>pGbSE^PI4O=X^YKiosjL`NnDxWlo_2N4K2+vh@Z@)!f7iOv6l}N;Ozw zuN<`-i3rnO#`e$qz?EiVwjyB%V~bZ+^=gzsFTJn`@ipv4%?(k%D=;+)_pZLCr;8Woz_JEi^Hkg)Z_D>&z)iZfk(%L^g zVm~CfxvQUg23d-0edz&W%MDmC`qSpHHimWrmbHIh#KW~Ob#%fhTk|gQNebkE(>WdY zB3IW;-glO;NID}L`RnmNVpaTI@vDbW$& zG>dR1NzG974Gt{rPn%$3jz8Mkyb^B-`=!U%|Z^UjJ`gzyab* zif38=!yz}Vf4p|$qjP@(76h>?>mTm?cTfEvH~bx47RUwrsLNnUqXpK)LKqcFnc%xD zyGWQ%LlStwOG&>~528Dck-2lk)ru8!r2p!{*gwR73JE4A#f%xqxR$O7qX9AbJM3?B z7T$#32~KQC<~YQ2r?MpNwGOtv@=qjz4nW!0+Xk3z-M;jORUgo)j?Lw@`kGs?{$fO+ zn2~dC%ZKlrr49E@uGT*F-*XnMl}q%Ge2_z|W7M5}$Je}+Sr%lH)j=Qb-#z-b0%IE; zxa=MGNd6)nVbx8ogkcd`IhWjp5-q8fU;l#~#shPaIk@2TPdeQSSp0Ism|=+XJybBE zTKmo7fSN$a{Bj!>O8%Q94n7RBu%lAtHz{qu+~@aO_}_K@1;TKh)*a9GY5pGFp-M`g z)a~K~7HT})t2TRDCR$5k0qve{>lUT*3SNP9Y5|$ShqR>?)^q<0f=ws{PY*s{Yo-v%C!f~K-}yVu7(wW z`z;|C90&IDUkx~Lpj}u0Yx?nOFnxyY()mafEh$dF-QFrRy9*44@?G4?e_yfqdgHBN zzeqB499jF)6TqA9s@41p&i%CE6xeRTGmqh)zWx`q+KYPo06ICZyvEEnTyLG^7&o2N zP1x)XvTr-;0{hzQ^s%bDmY0$iHrPo;6sBQf;CXPa|~m~EHQ4iALh zwzIn7t6gEC?BHeAt~T)K_PMq(x@x77{!fPlRjv8NBlS}{ER&l!50YT*mw;$uPPnyA zn&i!tjN@mL@lHQ{wBtctKn`+G6(D&i`%w-C`vpvq9CFvXK_b8tc;+fxR zR#VI@&pXl<^ZTgsFNEp>pEDwy&>JzTI(~Nf4Xb!WKg#~tmDGDeSU<^OfPzp3A<^&o z^>@g+$$jE)uALX7|7X>$yzo`p!T*eOEUa4v9YEFpuNmpxUOq4H{((|RDHKm0wr^b$ zlW@ZO5FX-B^k!L*ZO*5I$+XU1UydFD8Dor4H|jzUs&2}0{|h=HwB2$MGmzn*w2I9C z0%+ttDxjNwK7A7DNb)D)@rzAhwqi;P%%l8oD6S{N?k@7zm!e5gEVh>q5r`ilrE~Xo z_3i~Jj$UEGFfO2N8vF^os`adoO2gH905rqJU_^f!2f6YOu ze{4PUi~B*}2ZPc$Dk9(?kG70t!It2F%oRbcTk}dfF<8kje@r@ zYhLLd0xNe$vNEmtHkpx@Fhg$Z)~$JE!6{gI|C|@unz3&i^g#)p5L>qbwf_l5pOH{@ zeninv8vZ}XygL-#+d0?$0!0=#LC1oWvq+@P+Gp%Pge}9+`=F>J{_N463%3%if{{Um zZ#kj4^E=zg$2S$IBpso?u`-1m=GkT(m&4t?v@`m>U{dFL-3jWoV@kO2g^J*8cMILqV3X8 zPqg1k)<`qfeu{I~iu1PE0mf^ea;EbrVn07p%Upl3x5By2&T$VujiqC-^YnAZfj2yj z?@PawmX;2U4ptka&Z#!4j*dz-J^YlhTyOPfz(x~&yK3iyZT}*OicY~j;YOnxBbl-p zn>z{6tId4eExd7A0xuCdb0qrM`Piy-*MmuiqvE;4YK(pQw_%)|8kCDU@lLL2sD@2f zFIaKr=T4nVmegFZ7-Ir(3Z#Ulb_>+& zC96`X)@m)U`!j_tp;Is$Uc_!?)-Es9WA|n+T8e5c$Hvl+zJD-%i=HDx%}$@HZYE=2 zIx0lhR5K?S8jn9j^WzR@I6Tsk&km?HE`JS5tQli@bm>%ND)t$%7XI{n>>USl;L;O+ z%GIknxn@c7f)|Hi{iu2V=-Rc|-lOO&v}(iUz%_@4FxF)5Tyh2$Dh_MEf&U%#A4Jms zRkw*TWH|zU`^5r;*A;_DIwWG zZE}a*q86LF(b7Z*4t(C+de`mRlzHEuv)6Z#6wHrT7Ka800!oA{p&3SO+nkKN#>Rhv8HVMYSMi zj*L$32}*32R@%8I$tPZY*99>sJdX4l%Sbe9B|gd3W0?~#HqoVi-#g|G1($HdgmNh6 zJEKB-A$--Vk*nknSMWPNb_-vel3DcRt~K*5Iyo&A>4u0ePTCyUQs$pDSrTT^V6oj^ zpLU~zO>ol_Yli|&G_EXe{-LJ z4ECHL3y`tXUrg~YVp6j`E3BjN!wc1WCX;$Y??`&z!8LF3(`(0@m>n7K-iZBy?f?2u zqbzjl!NP;77k(jWe|HT(Zt@L{D<|Nkp`Ek8~!2yWxQ9u@7jV=!o75x_qX zFoJ}V=dzTHAWU%aIwQaQCQvY*;ZtUuEeb6Em4>bdN4Q!qzG)>wmG_`vgi~pX(@o6I z&3IUUV-?Swcy?Xi={dGI{Q=jj$3VVOqbt*GU?>&8YK?wszi|=a+Ck$Fpt&}ZerWs} z%;GDlHwuXd9VhA^xH$Hq;Vu$c%A*gZK2uop4$#5rc%UDQ&sN)D9sS=(<;5w`4-Q&u znys-d7+`#_GQiKS+2QS)E3Ys+wu62U!YiNrZ=1vd)c=l(1&m)a2*98Jt8NuTbFhxH zaYbZcB|m;~3K>rS`5O#bImNGq%^vX2-T`~!Spz};cc_lC0dKV*eES%Sw}K$LckkZ0 zDvcHL58Z`-mqtjSD~EK0F&}oPniq69?LLHWsQkdyi4e)zVCBFu<>a;N1)z`sx9+Kr zG+8r4kc&kL^(MAYeUFPu?Ec!6I(SQ#-1ehwM3bHMZ3b_}rzyHv`DvJ5a%7eoOdz&5 zVIqfqtmQDgwe#h@qV1M2RVceAUellYHHEGfD*fIF$$u2)%5>>?EC7zuR~$J_%p?=){vZ~a^$ zQ{cf_XNT)jg_tMlE zlO+aOFma^mZ1LPZ&q^3k7(^DCm<6rvd;?AA-)*G5spWm1aUV3Fe4?COWEtLbi{b+1 z+*oqPLC-Bz9)k8Z&>JutXXDQDG!gwTFu}JKPF(8Z_4KQo!_3~9nJ$qs{z@1}p9Q^I zO*Eb{tH5;CRu4&zLjKB&^HbF%Oi4c0EZ?S2_4b*MPCl0nqKSc~k)4}v z8Ad&osS1`~Q#KwOAMoOKdBLH7U1ljoCPBFc`Z79%C>}nklQG$zGda@%y%zzpgGs6@<(1jLynN5`1g0E@j|%WiKqJ#GdLrGF4sZ4T#Ol(N zO9ADc8K0X6XJ-4^^O@p1IoX?49%RJs?MNuQs&rA%_}+8Pk+&aX8qX$s&kaB`)mC_z zj{ULRl&;6rhoIrH%*mn`47_$ff)EiGgUg#8M!AIVWW)!-@ER`rnF%iSteamu{S6rP z4R=yRLpMY9=)@{Qn=kQVJ`7taX*-&Bw1=p;-S@uhCcxT_RU6*1mFrCG;nJ8NbS&TU z*rA~!U?0iWmoDq?6<%_EgZI&BS=^r6W7uprehcQv-=4TMQ{{oDp#a0*o_!t9m=~IU zI6aXw2YO~$l#oiC+7Z6Ks8_}@{2DpV0WVRQ`}q6PC)!%$vN!7p=K^KDBET)dp zKk;IPhIlpz)Xm=1D@xy!-XkPh?#El4J!f+et11(tg2|K08Z>a2uK!vQG|#;p49in$t_f+;#~bLOOky zXf1C)k%de9W2j;Ik&U+|l509-9j4CIK!4Zl;zvBLLJ5td_uT5FyE6CS-gUc^d3re@ zgQg%5r|jNi>GNG}AEoi)y9VGT0BYkuV)?hAE}HdYfSgGpX$CU^#&@ z|3q|h6Cu)-vb1Uamcv}2jRkK*P0D__+jZ9JPAguZd3FYd9BFMr0!F zTi@_R_@d$)t5kX3#|~4IT*H|U66=I!mnDWMcLMB`E&&6+}Lyozb`uZf=m<}p|W1$Ilhr%9up2#g8m|H_oE+BLo9wnJk{T< z;3Zwz@O*NZ!W{dwtYa}Wfo3Q8)H~5#oiS%NVvsR2y*;1i;@q%Lwx3Q6Romwt=!h)T zU{5v=$`j{e z78=Z$?TO2+PI}?`IDLG;ruD%_noY^gw#t;_)v0|?%LJA~rF4YI;LX%3zZ15p=sII) z5q|sFW`26p!6u>jX~E`6nF+woZ7_m6FDLy~nXn)-(72Z1fJ@*mVjym|R%l#b%>UjZ zW4bZGSYf-js(Ec($)S4F$_~|JPuw=qIA~PMqyjC+OB9^z>kTsOx+}tDO^T5qGa{UV zn{clg*&cx5!CXddIXThL-AG)OdQu}25?Z^=WBB=4-xB< zs^t=va3?Z-={R(XX}zG(G6*&kn)bB}ZT_Jm_o}p1hd7XJrV`#`Yj&?BTV7`i0n<+F z>z6PB^HSZ&FqH)XiPpm}4iw~_8JwRpc*?_lss)&Xq%^s+mygo5aKi?pe1y+1-47JD zv+kw$7tJ-tb zAWyPneQVniYs0PE%LOC;xE$tY%4=$B9%l=V`X)D7v!#8_B5J+I+tmH3FTs2mnpTU% zv&m$Me9c@>Hk5$2Rl00jcX@8O&6TtBwt$K~^yNKfZ`1DCoNCrL;V^esGdsE2UT|UJ z!0_k7Rv2!uh5OOdNbd=&dwnqOSnw@D$CQ1}VDm;=TapWQqm^7HoyAcWrh~-{Q@MjR zAXv8{^wtF3ZOb3)jP+G>egWw7OTTS(qCT4NYEimINC&HA%-6MltZWa-nC&-kxg2C( zr>zb{S!HrY);CY&@n$JxOjXVpIve10-vwy$oIN(iYtKJ7jTt)C)gZFukAOs%K~&&~ zeX6xWr2}&RFC;L0+%5AoQyE{AmFLMBXJ#LBndB9B+JEb9WQ$6gx~3i`Lbzf zBWH_!E?V->Sbs9evYStfwVID>bh>52!JoRM626dG@TXnLPV@uk4~*7gfPvM*sd@e0 z?_th>UtmuB^g!zEN`;GWJ7NwcGsy%{BAxX+tv7Ajqz*!GlO#vVC6g%ac3?3jGIIK? zv2Vxf)Zw2Z^7hakk+~taR@(UqF=ts;H%VI!hq{V=uLK2$=5sFHn$ZdiG$(XrPg1n! zOKbogG!6!(I`LV(>F_Xb@}91LWmh51%dmNRsygGb^L8@d1KOvZyWRr#JiWzroPEpy z5rr_?S1P(<+g&pA;W8c=U(2PI>G+&AdM;4N#@v@n{U{}`zKXfAO-0up8j_%^Z5IY{Xrz5nB&Vz)sa~IY>RV66whNz| z`5yE&&(B$y1a6mC+zm54_bA`j9GIAO^F~HYm;`!L54rdn%@!%7f83u|&@VJrv)88d zxkNnYkU;ZHC$IleM4SY7;@-V@ZNR%%E|Ac)YA{2Inl`ujfkFD@<77qG`IlTDYyAe(^)H5k7GHGfq)KzSfEj!=Gq&~QQTqfF|{P18#S(z2eAwz5W86$CV(D

zJrNBq9P2l3DP|;Anaq#OinSk$OsH`Ep_Ct_;GO)rR(mMEVN zB4u|BOjR$Dp+sg4&`3D3-9FhUkA#k7V=hNqk9|nirpt}28|z7 z;YMZole+mQ71=XfhoULD)#>a9>?hw}OtLEBd%_q48jS@X^u#BUaCdRp#2p`|_3_w@ zlmw-C0}b%>YlsudN&k7wr)TIZ^K+4jLm%Kso)ZDOFAEuuGkoCy{m;cDCtfO?0&Jnf zbeR*~B0>AwLryzn2VimDQQ*8Unrsy0-Y3`Vr(W+3M@A}HlUwQ!o3v&MZ7zCL-s~-A zOO$z6o2M>9{^hL0Ty{rm6i+E@?WBa&)4q9g)kt$%yKkrXEX$5bSn+o)>m_mtnP9?Y zji-;lJ;X$qeAp;}YbR5&1L&OW*?~#BnB;p`J2DjHraWoYw>yALSH&0g_a8mZf6PCH2H>`#d(mzt7aA zkVtH&k?0sZ)h!ybKCkI%BZ$}CdR5A5Z#zBRJTL5zPog?C+NU5zkqycli41?o!>9LM zWVP-Y5b_VaZm&BWqnxa-{;K5Adn;E_DN|5)l(gIJ4L)IoxPmpopC{0%FeH}G zbocy%PGE+CX6@PILQg9R2BT@IvV!K^r%elvSN0dA3+6R#MU=h!0Z-4k?RwprDPMN6 z*99GYp#aN!8I8YBF^(<(1jKO$q62}f!b2H5-@`D&EI0daA;u~ZFLXi~h_?w&T7i1y zqLarY^&37?-!&Y0C3+qCr-&8rvI4pS9DcO=NK z3MkkL<^q?fJgYlmkdi?=)V~X4{{{d4Ytcug?l?Dljg*OqOpDG}^(Nkx4HACaeF^it z9p(b$kr1P*xcmeOqMhZEEt^u#%L=I6#Uva8d_66M|E7kvbB`WWN+IUU(~RwLVO z%>m?OuF4#@W=`lv&^A`&u?IfjayQ@A9`al;zB^+9r0i4nHCTl)Xl?LMKCG&!U4Uj0 zu079G)YnqwLWROSed1<=*toe9eYcP3Y$D{jQcNf-Ja>uGrliK4fx`6Wv&2i73vX?1 zd1WLRE9$lQ2pC)EV=3a=7(QS-aB|SfRco1uNyTlM-Ee-kBJMJ zpRzIxYTkx$?z#b)dk7QSf{bGuIf%GAzK~V>uh-Mvr}COhDu9pslnz*j5;+2-;;ARn z!k#Hs!JL|}B?k69IRr;Rv=GC{7>uS%?z+5_hb|x-a_JOqv6X z8>qF>2c)QS(>2}84Xl!c< zZ?jYYBo1iBuqbI5K|GX8OEKd&oq+4G7?C3O*XfMp7DX z4$2%u{gGrVA9`aU}w5bjYanO=E(~_LPH{q1T!-YJXt6F^|pB{tEU8v zu`H@hfB^1nB%bSH-tsAuA$e+fLnFP@CN0t?UFoCQfANOzWV-yiIAkG5LLg z-WPHGN%c-%@=7eGFt$9vr!+-AK(6sYhZD6vzLBxT-06tInW417v=N!M9YdQo7#TZ= zc|Y@3Vx7kQFhQklwPnNRos>?SckQTBF)8`dML)j-k z@Ol4ngfMo?47~1n?w^H6p;Eq+^HNAavzwd99AAJ@9+k`G<6VeDY1+ylg@^H~X#SvW zm!LX~b9Zr>8Jecbh~$13?90IDm?P?IKRea2Gbxu``6i4v;@@df%S7yUIdhEh+#H)$7ps=-S=}IwJ-SlY?-}IZDeRe)oC&P| zU?e%mf6wi%+pq|kD=RfEyF|Y5k4s0w2u$T>^KFVY-qs6MMdug|dj>fq6D+?9+{i9z z*O#FuI&e;fVMKJkZy@cE?zhT&en+*YQ)}h@ZwU@$2ocxo`Ch`j3{NFlGDZ#(HQ0?e zy13-Ui}xAu;WpJ~aaUn3^T?Rr&v7=uNcub*%#KVQe_bXlBww$j^;FsZ_(2FBBnR@=iMZqj%S z6pXjBzV@ou-qouGjGJc09p)|0ya>+dn6aN337RL?mc29EIB4%9 zPJXp^0#U?ffOl<~ogO!!D%H=w`B1}qVuK@MBR<$yZhGH0c*OPM%+v6i8Q%0(I+?U< zIxgdkj5G-!-OxQ%N^U$@_XpZ=f)#;Zt6lT!-Y zS~?rlvVtnM?WIAk>&Z$mljcmRXxZ!XvC0q=nZcb*J{Td4C{JjAIN6x>#QRMSCqD^J zCU_pr;2e|AQQlz9QOgtXt%6M1Nx)7p{d`MbFW7*2aXb96soZj8om~2&mFnQ5HP|$dh??KCDipb|>`~p)@TrBO?3fy<^PB#j6 ze+IapmzU>0R(11ehAX!=UHVpfhm`Rqn`2+Zo=65HR92CxRKu|3FaAQqWs~3p{CxZL z`^cC#*Y?bTiN>h0h?y8;A_S`8Q}O^+@L;gzB%jGO`xtuCg;(KV%1p091|hhMH6jZO zrAO;VswcWl4z`Dsv42du>-O<%&VpUMV1<540$)~LdWQ8v=rkI?!Mt71MW`<-L#D>M zyUN8ZC`jfjwoxDf&mfiXDGK zC-oQ4IihgoI|86x=`rVe4oQCS9($|_I_hpl+?=r2VeEqa31^%=4@sniN|(Eh$Ly8d z?K#pusc^xnBS))^tfM})I7mb!=aTD;R*v)5+OSKV`4vUCX{@g*@oi=68sD3K5b>Yz z>p2(lakGS#Yh`cZm6G~A52}4OTWs3ta+ajp%l;%m!RHG+#)S4AP^V+3V(jNp5*LI> z_V-ZF!_&b58S@e!29&_n()qPZWS;-BhRssJJYM{W1d!6;&C7*YM?)0?5f zaTrBo+natLS5jyBYh)?Jez!E>cl>SjAxUviCxt7uwcCL-WA z5L1@d4Rl(9jPzuZ)lb*2IH7w*S$5hWf_nbYfde0UvEzJ z@*^!$v(Dm2p3cwkK7!unA?jhiiF=WdZE%YwK@seKFY??kUu@E@kW&Qwldbt8b4C;I zFV1;nL}qJ&_xC8`0e~234sq>RQD7{!-I(2?D5gDXwVC8osE1g zu;uVqG~fKp$T2^QGn$%v-hB|G-Sq}W$!}w=ZMJB#Vls+JaG7zDh{mlte1_9^p>do4=#&1?@~Oj|RmFh96+4l~R-KP#-rwf5A98JlxQ0v) z#dh)3SnZSK+Fgp;&$99dhx#{8N@{9q+A}BaQ>XJf{-bQ>1?0|wxH)bSX=H2K8y@B~ zaXBKzq|0f+7X!Ro9f)*yIu)B9OtLdMI&&{Id$V9@zrI581askK+lL3Zg(du)mnHm* zjY!Iwq0i_-Zx&M2nTwD7oO4|!bN9WEO*qP3S+8o*Z&rP6Jbv@C)(sIWZP{`a{gjS7 zozAj4LHo5NKICt=LEF^He>o^|Uc^tu@Eu+&j2+S+O#uGB1#w)tS|7EKWYWITWBL(k>OHZ*UNB1R?IB51Z-6#$fv?&LW^ZvK z{t=hnA$DBwKg;ISQ-i;=h>Dg$`mUP?;~p%UIT9=|$MBM7;HEI5rlV_dn{3-lJX9i| zJlXAkRnEhcpI}k107k^HUEGE(6JlcLzb)wEPu;>R*SC3obohFM_9o0Sd|kb94MM97>CND!~VQClMAxnXb`blk2}JtQpg z5jk1W!4rVkb*{^Z-7SInm{uRdX46u)aN=Nb41`lDtVFFxCr3J&n$EgkID<9+6$D<6 z3+0gZJ$4_!*Ep^C;C`T}$YATl3q3&ao50tlbhX6rYstLopVQSY)CvG7O?%J z$6mxpFh2AyaZy}`BWLxgTlbE{VbGyp=OPg@O7H%OI{!Rl)@`TY5;Yc%x*g(1J_&ux zM`e}*cVFSyu6<6VBs9hs=wwtV;art#p-_FHe0ND_pXkY-B`zdV$(ic@XFm^OrlTQfthNs#5d8ivN!TjTAuq zTv|mBx$LICbt0P7Zbs5=so%h5Z6vOAUGM2>3{HCp{llSCA465Pwucy>OkK{{hNQzZ z^G$;*C(r~D#D3pfQF-uc> z&`>Ef(a=_DEA2gg*XyKu9dGaVo6qEr}$M5r(obx)b`@XOHy07`XuA5}_`YlX4 z4s%aKbQ z?B7j9Hnheql6K)i>c?<}FaMP+1e&-K;p4tA5$YOde6IAgn-vicYY9QTgrQ}=2v zZa$C$PzBYT1qEEe5hqIId*7tg=mayw1%HNnaX0tF@(;VfeYIaak22g`2d4389^@vi z>H^0$*H4-}s)5kV+Hs#8DH;XJ28Xs-{r-@Q^Dhes6v0N2?*aSF-!ww|tp`|3 zimNc!*HFp#yeu=BjG8%wKo@C2fM0vlQaPQ&0QnAXtjYhpNTM)sWlN z*)&v6C1Nkkz@3zLyAHuM({xBbx#QcBNOgr3U#WVi8z`HUor-16E-5>hng#FVG(gH& zza`IYvPHERc1kZD{299GHS0GA-b@kITZB|-rx-d`stm65J(IcFpy#zp&ALEz7-g#pk)g7?5z_;a7%V-W7$f?VM{(<2!^dpo|p6A(9;d zi^wPUKz{Yu{=1SnA0hFdFW@p+JY%VSA4#|mKq^-FLnl~Tyuq*hKYx(M?aknH*)>I{ zu>XT}Z1;qrjQ8fCz3(8f$BNw4P5u3tX)%~5v}F?G4b)OE-FV?p@uq%0qdz}8uCS*U zxwsPv?9mO*7iAFeH!2xy`wTN+V#{i|LWiemz%eJXjxPEAqQ+r{?cnX zMt4pHFB5&`+k%B5%s+5Fe3ngavy@Oo!1q_9@_Y5hSLI9BXfcU^oe<)bAlUMqF7NGi zZ`lG;^kLhNLx2?tXNx$)A?l(_{!%?ZMXAh$6e-9(-BJ!an?qF?(8)?I*$Q?vIHSr zbZ6e~#)O!6cr6m*DIlL9=x)J+nmQ@qSH65R^MK*$1c=zUo5p8}6r`qVdBX|)Hg1MQ z*C6O1Dk9<@sEmZ~*vCe{H%m>YdKGx{uPrdR4GuwP@|S4cS(DRN?>MZ_ECNk&akM)+ zzvsmDoHf=KmG5arlK)`5hgo}g&~dqcclv&%wz(}jCh>S#_UW5BE^Wan{D2TD$cD?5 zzh4>cxt|#?aR9Ogfy}Haou8EI&O$D}xBJ4fz&^eeVh|fV%Lq-RRCWqZ6q~v`Q8jTj zSiZR}))x*Y>0V2p>v>lXtjbdpnLU2mx=%Q-WaIAqL*w(&rD;@OLyn@lJ>&T4Z_*X| zKsu|EORSJ9RTpya`Vcsv8YKTh$-qlC*liDCvP%J}8K?LqA%|Z1Y-Y0DMgBG;9o)ih z`HpsVtF36=xyK={I3|F@bml<}k4ZGzT&ySZF)jJ31iH4u+r@Fg=9%YD8;9B^(tY4k z2-~~`pgIMIXAX*MmwlHBmvqW2cOFZ!ggdX5AAoTV=V4#zIBq}f_*m6( zSbZimd#Wee*d@HkU?i}lp)L_gMZ)H!rZo0>6N0AG`AT`KZu=a+*^TNVa-1E=efSOn zE?$?sB5bI+tsurc{3{SdBuFHOF&ytelqy(^L@bF z7OmF?J?w?TQsbXngKl_jVT#-_mGdeZ8mmRVfDFO~Y;4&dzCx z?J}%RdkRugI5;=(nz$6Vf#dVpBsw+^oTe?vt-1au-kKK`T&=#hYKX^Yv)HZ}h`~&@ z@Cy`uy*F2~;^_{{gFv^`UKUw%t$2|VjfjzDR8x?Af>>ZW3wZ1h0H=96=U zR4DUy>w@bnkGRiP_iy>U=jcNPkSQ3uR4=Ir6WnKPANh!8s&4vJYIGj{^>xo4t^NIv zO!?Q&27K48yvV&Amtj-X!I?K^xx<2BnHSbhQ=RJvEnLg~*?YuzVxXl3vfUI2(oRPHQ%^#n zO!y}zRwwh5{ooxf3V(vdj7x>|MzYxxLb#3{@FYW)k#qS>?? zuY1>)y*gv}F63-0^O}$TO|s)<(TuLwdU#DV(_VIrtG>bA zox(ij*-})Y@&VRoi^Rc@nR(yv8TMgj$=S3^#gbHjl|e8N`ZrkY$YgmF4i{_3ZfQ<3 zE)Q`?cmWO^#~GnJx}uWWi68wZhdb>xe21o+KTz5NmONs z`=$KSMLj6^Fc%Y5=04VS^2C-;hR4oWzwtN>;aFCR2R2*cuRxsEs9ElgUF^Bax>MfY zWr|IUuGj+d!JRMl^CQDcck7XKjGER(>CX^1K6Hq=?OpO*kt9Rr6PD)ctOvrTs+We| zaar@zgg2QkI?T$Cuwu^hPiLP+bqt;?o@VmZD+jIisM&!&e8vS3PKI>Xry-m<^bT~~#4 z*z!Aul#Fz0b&qHZl)wl8`O?LHW`2?%<$yff~tCSHG`$63ku%os1K$jb! zz;|=0-gX0^ru#&pE67PMxlnOo@V}Y2%40OZc1-G3)ik3=(2uHg%AVcUknu~98l0X$ zvfg{UC7oH%VoJfj3D90+5LS@`;3ro|M9_b_omi9A@xV0i4P)n=JR;ok1F}b+CZ@e1 zjg}?kZO>cIb?z{ZKw2JnQVU9QW1`EV{inxu`7RdC^N&L?*T^Nh=<5eUSO`)kTRAz4 zj@1pOeSu@LNfm;iFWz%Uy9Ne^`P(#XjG>S+eg8hmS?3ue`vfsE~Q>pV!SukO5qU97HKUGJoLZwy_v&XvA_Z%O4M_WjR-%?ZfPDlV6TuU7#=wD+ZmeVnhz z``OvkW+_z%wW2S3kh6$3T{>;KsVDn7xA+N`-4m;t<(V zZ|hwq{&pgAPa+|)dA}oDY*A7o622SrZrmuMhHkOCXxW2Cq91Q4HbX#E%nWXquQ}-W zF~SXNOo1dBOTr}fb<)C#JuR=ktX;7gq##21`W;T@PQ2|z-oBv4vF#L4fYsi_`~z@E zO1^*i>+Y5>S?hO3n#=Hv>Snv7Teq^XNZdwW_p{?5o8)W9aHlbH>&`O1*2ponSrCb* z?<>eO!wuf~N0`kvY=I1aM;U_Jyzt`nY6P`;oX@h+wx&JA3ev~xqvNw35?=uC845;H(Il-*Gn12)VHR zVx2YsXuE`>8$KDC*X4zIbpw#}xN*1Kvi&1|2(M`zQYl7ILs0NV&ildIBT+jBq8NrR z{VA8kD>d5yD`=kg)8PXEk9RzK>r`{wgu(c#^?TVmlOe=!2txwzA_2G%C6>!y?>=;y zpA!>W#2l=^eWU;=LH~J2<@EaxSfGTiC;j2>Wp?P2gG*Dp)OIzcgYdz6hfRA>lAv_@Zj3EG+p@~t9y0Z6=wuR3tBsRGIlLkN|Zt!e5<4n zMr9wxL1<8X|I9#ssqL9Zx$tC7ctwJz_Uv;qJf!^koIR3UYQ=!amVk()HlU3uSCAeQlRocjJ3V z-%m>pM|DPJua^rH+?uQv9lz!WRC#%hKui2y97r?Xwag+hllTdL+)kxudjia;yKd$p zh}1(=5|xggEOwu_7Lr3PAZkKUaPcm{-kep<%&r;KM5?;$Bh-Y{irC`W#2@K$T2)c%6 znH)NF=w`U^vVg!HXMkGDuP>Dv&2~#h^-o8PE-~ilb~rE2{V8F~=VEz4W*ky_5CW+H z+fTYIeBrs?`}&z$O%}B!5R(sxS!ERw6(0)!+LtE>F0L0ZNt#(@oHqK`hV{!5W%1dc zJ|WJmEzR_I{;=hS)yP}CGDlf*OXD0=`hRa%u|9SEVNEEZ{`1NzALK$!j#cI!hz&vY1)6cVr9oDm>UXsx zunTSqsZq=s*C}7n9E2w$ci|uv&TX2|fj+fRWegoLpvahfmDuJ*zaDvB`3^+ZNE_Fl zSU9-0+!e9%)3NoxLwe$8@XwF}&yl_=u^&S1w;7!}QVq$*IdqQxZr6|Hkxeu}E1YQ7 z30*e8ekSK%Pu2qQ91!Bg+w0_@h_@u({_&6hF7+SD^Fw4uszFzTx{I@%isYEHtYSH| z4<4>t-Z)TMVmM!U@#h^=PY$6!KIa1k3RfyO6QUF7FCtmMJc$gw@|+9(>F>-0w6Z#s z!9Un}ftVL?adb=)4mLH~4foMJpy&!mg64D-`uzM@6LGQMvf@9CJNfts4m}sF3Q{5d z#_Z$KUmOQ@;CJD<14ucy$*UVxjai@Uv(hv7;eV)JY$@f;jQ3-cY#;HXSFvUkT8;^W zs%A~PD|Jc%am@67G$9JAqbsN zKG5??ZlU47FMt*|&ISiTbAGJ-pZBm~`|o0ZZ-f4S+u4z>Yo?j(|8RP6c9c-1o!M9< zPShc`eS@I$&WKdgA9lidI7&niGqA657^r3ui#7R+Pk`4UIdXfGihy>N0!sKqX@TwV z-U7eSeFe9Px=VGHQ^k^V_zzS3`71UBP`X=g{s%H4k`yFgiY4RdlFR;+eNQZ)i3a`( z=&-k|PW}fHA%zVj0z>~J$$xnJ-+YDJ4->x3=J~raZ}|}58IXopdD@k0P`Lu8q{D)QQDtm z{mu2ECWj4!3Gb(t{(U0hhe@Ht!n>lP;!YBPvHU|_^wmJapCU;u-Sb4qLgLA(O%W_4 zCenDBSewIA#52MQQ{LLRn=@ka@sZo_i5+Qp!7W|xcKLJ5SE=H97z6j&n?Iuao-^;f z4Wc60QVQPtACq);3T?%Zh$CFJ>eBk3;%ZN65u2jyIndH_2Z~^d2QY;K^ftTqEW7;$xu6VW4&3EH#2mF_^F!& zYDTDC`01^YCvfH|wR}J9jX&c3QAGP@f6r<9b1(ZgQatp5Ia>3N21FiKY=&kMFQ*;; z-~#i?q+F=9%!hnD&9f813`nUn1y9T!ZAPfKh&p@z!iBZa4#GSTG66^~G#|2Z4F0;# zlNGU=of5hZP^h5~h|Y&lQ;wiIRE(d!7T)?o>ltITVfBRl0i(V!_2&060!G!fK`E*M z?s7%`?_v^~hj*ENn=UDtI&ah$WB9bhBcMFygY9vn6lKKh;7&jG;BRjm`0K9u$pZrK zc?$w-M`Fd7pu|*30m&@iFiWlF+j5S$>fdP08;WKUuluyf&lW`Gz;tM zB-{Oz5`DjGRx^uP8GMF{eNA_(R4J&5vxE|D_net_f!Z8a$AJT7bElbh864dU5Z?T> z?9teYc-@?Wx#ZkG<(xmz<33b(m1z zihb5|k9YNE2iYb`YxQQAWb;UAO6eTV`ffZH8;~}iC^tFx>a_FVRXM%sS9&4IF2{9v zV<(d2EJqpkwFZp4mehUT=AW~%)A{>d_3@c+-#<>$?`ro58ucs@7fH(YRi{*U_Ip0k z&OpyvdSQMp$*Q@edH#J&b7{u|aynBLa z0wB{H$Dr){o90@E%P04d+l1vd1u5y?QE&4dt=Gu*e@$y82|uvzV1x&|o= znUbsLdGy(gK3uqKuf={Drd7eBkMXH?&Cs-WDgzns3eCoNZ!rkhA=RS!aZW2=R$g1M}1M$pH zdIpmVo{g$vB37~@Mm;Ifx35i;{G&evKljieC7Ah0|B{<&3^y&v-|8Z@FCq}{pRr7Z zig!k1I@-G93~!|-pv=yhkQ_!JGdafEWK3mO z(F|Ww*aP)X{^ZjyQTjsDUYGY-nHtPXaZ z16U*Ntu8Vv+XL~I_C|cwnOBvfPsxL>Z?ClD!_aSgVcC*500Gk*z~;ILsCPY5e{r)m zhJlC@U4d5zRf}deFJe*l>Qi71APjU@vJ8dq&ZYiABATaJsY088h5YBo}8(*oP9 zpo~;%Y-3n8KT#xgs_p#i-uN6znk_Pwn?c{r9`s1NOOYE&}raMiLy4-a}P3JBthG4 zJ&L)PqSsqw@}m^O9d5|9z2CMZ?i9CTAdYN&({wKJ^=f9 z!wo^2AV{Mehg8S5+k-6~8O2b{#)JT%8}y#-vHSKJL1MWmAQ%`A?dHicd)kBv{Ac8d z(fHtI$D5@J3a%4!5Tlh!gsgcHi@nxq^Ng>n^3DzwBou84b_h%U0^qm?lWPZT8Vr0- zd|XerU6C7iVmH^6ArIHIEoZi=(QSnf-+k2?i(?YzSa+G$S;=#zYd3j|?rN7AI+KuY zcsyq3hVb0xx}n?Ec8(0qI}PV&cP0e-Svnuz?;ckgGw8y8=1jgr&}eRn!vUkv`|861 zw|C>J_Yd_J4&|2AXppK4f`}3C_f@4mmzE1jWdyFs43Ge7q@dS*;&E&W zhmlty>l?_t8s8)*4bZYWW~%ZA?Pn{FV+u8G=V?};w0y2HMC2uxqkmOw0|4>je6W!Y z)B1H9pbU~RFAjzn&xAh(JfnVeY$`>7j>Gw$%~9lZoO@JCC$B(qr`_-DR{769u&fp}NOI6V}Qvl+nw6Z(owh+O+z zzL$&FB?C$BJ~?)=-(pZU#Aj?~xctV%7@-*x6!K?J`0l-i)yG`S9;S~~Zsel#vDk8R z|Jinod) z=F+sxOE+|+tu;u!0`hLd9Z+44`;weRlbY_Rhrf&Q-Q8OiGsT^|}Yyo&){tf#~OqlMjyJB``uO^-nO_ zuv4O(hYGl6I|b^EFlUFa^SxKvRWI~GU&HJ4r;c|gm`UeuTkzbRolr4y+M2{)664Q} z^xHKQm9r+_z)hVbG7bukT9-K`l4EW_yV9yesK^y$;q?3=FY))NNAl)-GxKFQ_+4uV*S(u>Z zm)sEU!BTw%iBV z*U7;p5%r}&mp76*x~HCWpU>MWiTi$MtqwTvbWp<1tezjSayoU>)Ip3_`>0vY))W@e zHmPq{tZn0uzMo9vE--LcXs^4i@`}IqvRd5hm?y97COS%0gjP<%$zc@rm28E8>; zdE^bD1<}H$VKs7RcEsv0)Zsjv$aP( z)CrnhYV6giiFxnn+_|s$o!{9OFib6;zl0)Tjh^-5U3jF8jQ`XJ?niIHHRyO@)&5@2 z7mlGsfxu(4G+xcr7;U;~a=BFOhuaxK8As=sC+oG-MwtT--K^a$AYXms)S4nunpa#Z z-oa})dsQ+r4_UW=4xfnfu6feaR!?pRPbk?6$fhzrdw((M6T91YF0=3wmVK%h_ts`` zLwS_xhWH$QH8tfKjgwC^o9Mc}XYuy}&@q&3W;(~Xc?F#d8B|CnY1|_n>hzSVaBAn# zC5KjC!`0f%qULK60QAIlsfp{cw&=!rY>H|thhsCnU!^bTKHM(yn9~W{K850NI61(r z;GWY6N3Q+qz*;)O_t>n1`DCHSgdweWbF_qJ@*7e%Zd>Gk)kO*IKRM zdRDK=8j}LvP@6N@3rU>nUQ@~J&lR|MW{zD5(VP=j^L{h0qG<1OD$Vr3>}i#Gl^4V9 zq_)y3yu|MC|@DrPKNk<O}Qb0AvZ&3+@bs?g86Pi@h(Z&H(Jczw0Jceq}wj+e8z17&n1+an2Cc{h*%# z6c0ZzT>MpQ(00q3CuJL(1qbpBoLXx$XO*TO1P^LH2sdXq6)u#(7Akm0LGot&_IJ*Q zMs}~6AFmX95d2+Mq%||Pn4cv~@6e6DJvw2GgNQ&#c&j`cP1RBMZl-uMVLY?lrei4$FMpu#BF95qGewcOcd1x}u{#7F%8NAJZ@ z5uO)=R5%`*`B*iWeL^p1c8t=rCXU6PX^?Ma_{>+X-9_5a@+8O=hcw4RiXnnTK>U#3 zKC)e@h8H~G?WLv1<{JQ6^(6cgIl0`uTo<0b3Lmb|la^{k>3lv#mzPg!JO7~4NnDM! zc|(O9(;pTMWQyiRxMO+tTr^> z1_ElwMcKs<<%FSZdnsAYP>V;XssH7B|vDr6gEXfLG_$US5sl9Y}|? z5Sx$3TA--EHQiyt+y2#KI3$|QQ>uHR$QS&XZ-Q2FD$%MbA-g#@jf^u&)j?TZiT_wa z>(y05x~@8(FOuBi+K?rS?H{CB=gj<-QL<9SKrfspCCcVD+~Cob5Tssu|f$ zx2u0Dwtark;iY;Odu{rD^N&6bbi9pr zTp;xeqtXX297cOAp>fWw$?Fz9g2AZeQ?peZ$#slgcc&5cVdatBnMjdCe2PEJ zTdV>_h(piw7N~(U>7GVnYktX+nBSVwE2Blog1!;5zt~lelK`!QUeo?M)DiO+z za^24N2@`V$vl<7oxG`rT-pJ%zB#+7BL;N6jkbQ_wY+8T#JCP@~3slU#bvOG_g5X6X zNr_J?a~9>2Q+w;s7o?8w{Op68SDXmid26;hzLA!7B>e4A*!U|hUqR~fi|;rBZn3Xz z0K>cmVhiOVBAkNXfEj}_ZS=>!)pobO9fNYD52qroVc(E=(yr#b*Rne&*U;>Mc2duK zgYBV$)V<@f)e{3Q4jz3X5!`1I4>&r#eW_%7gQGrNuKMvQ+l#|5GpsTvifvkI`_wj6 zKHpZM3Yi+sZR^NJp5-FVDlEU=mGPc7!+?C!J^j)yW}fQX3976mGaC3i*ML0G;)MT` zVbp3XdNCa`S(4YL7UQdtH?WR^IbFE?8Rqp$}ga?g@uK9Es1c ze(EJXpRPH-{f^|LOIPNfOsQmOHy?km(9yGjhZo%Fyz4y4Qxy9L3+P(REUwfCzQYLZ z9#e^TP(ixSAw7jpzq)ksrj%l#nrY~dvwFs9r8LSKTY)=CQj&HAcakW~0~}x?`W3TC zypfn(=m@1bwI$rQ4nlsKJzm`Xu(y^_5J%rq{L-h1P5r3D5X>YulmPRE(lN^DF!bI*Bxo^<}1xdI-XL~Z4@zY2&}!__3(x} zg1}aaz~b34wZWr7@};tpXFle(w{^8$p{$#Gfsa!kwM@L|l=!T9$H0`9SZWskoPznz zsD0aOII}YiVl3>lg=!=jbOT}rS*Jr2DqauGJSpXPm#G=oS9NT%Un}&C8-RI~FIl>& z>JmB=H`uC~p3-(StU_>=>!oG}?iO5~qVOPeYFNy$(9sDtKL|uaHmM`Su1=mDEmS`I zLvG-rz%l(gEbHRZ=_3y%GU27Hl5s;Xp)U(qD*Gp(K$8OCi9y4g?LO<9g?HD1w=2`+ zdB^2!9>hc^y>r;cpd*5nY$9|h-OQ%WwtEu=2i+y^?(!UtUi}PbC*Ga*4+Je0KicW{ zPn@37IG~-`^YYkMun(HdQ+XV^q$G1U(y`c?iX4}uk~1qR_3|C<*3!;o^4)mm)PcEJ z5-u)Vu4l~N2~GzS1sZRB9(iVPpO>1$BzdH2hw4Mx$lJL{rybf}Q=Q=lmc@K&u7<__ zDYLJ4&95>ney5B&E?E3CFdc+hF6l2x2aurK^qeaXKKcQpg?!Z$3_z~cWXr4Lw3!#o zN5?ns@AKqC0|VdqEozexR@i*`t&~$mluGo+H+TESUv*0G*-m;QfiVTgR=UZa0i<0D z#8;m^mDC*sh%0XhIAic}gv(R}pDj0;pWK(qYjdweo=-QEj!s6+-&NMS8!z`h{8+Uv zePu*+b^!l|3%dT55-N(U(XH7D{yar>LEq{_V#0aO4db5lf4Mg0SjFS(snE{jJ2cN1 zsibldSN7>E_xi2-F)B1j2jxQ_R?Q>rc8aOO9=~yfc-pO0+2y{X5Ck;g(aTn;f5^BO z6%Z5y6KeBv?l|`eMZt}_=IN$!^I@^J>x07`rIbkH zZ%jDySm<8E0HezwBj1hnR8&6f^oUyKn}$SN+TFR7Z;A)-T9`r2nKsd#ox4pXLn}*9 zR8w)7ez-n;g?+S!!*ot_$~VeVbtYV-*zDchiFe}0es(ptPO(PEpQuh~A`z@kDE;KT zv-YO?!O1x)4qH~aYHMHN964F;GpAl(o@6;DmOc7qAL(cmH$MoZ)>XW(rpRM-bFw+Q zq`es2K;|br&5mq^7$2o*Ke#U0#bf$q5Q$ zkanJ_lLw(%dFE&yR##V-&@5^$1j#G}?lzX+iv%|ScFmG{p_Ud(LBcFS8My`uR+x){ zWR%&G>@w)3yV+krSJG%0%E-)1Tyf)B-rOQ=c1bb3X(b}sZ84@GP+3Vvg%syVEb`P=H^9* z=OS{`A3r_4Z$4tKw%j?j{SrOOgrR1mzBcLq5*0jIi76)wWX+&klxZXmC%#bXP8@`` z=RA4OPQ;e435WcTS-ywanKK6*JdTlG4229~y({kedhv7YVh_8f2BArEr%RZmZGE3; z_TcT7Tf581Tt?Mgsy>QvuD<2J`HsbA?e!21a7@{SOQg7PRGcA4MM`omrAX-Dx3_Oc0r510C@GFBarx-rOcVK95~QtR*B;*OME!5c2FvJ$GO78QigSDNpeOz355a3Z`H?07FD}$34sTJ&Bh+&vPu#>;{$@a+)DwdZD!@(-8wGGVkC_KJ$lf4?^xP4}uShok6C8qIz& z@3yvJN>Bf2-}e!oc29c15IUKcJATScymm(F*yA<#MrY(VF(!MEQ|a!ER8yhDWj`p` zDThg+0kc!$NTTZSN<_3!AxX~Rs_pH8-BaL)N;-Z!%gWQh$^%)?*C)qo3B{-j%#xc* zxpwVZ*OAOVV;lE(44~#Noa|fzoOY?;MjANGHG8=9oRxEg7#E8-@tPQDA@r^K`JuUn z8=I|;0%l=f9*xZA4`UAoV^?0xwg%aAo1L;?)tNkKZ8<1g!j*~`z;0{8pu$8S8XCDx z*QpeW(#?!SNUe*%(%4%k+?Y8Z`Ph4tN3ScL{JATQjM)Qjnm%64^|US>hbc(H=q5_7 zgEiAUds6%5M6&&3K*8177zX!En9Ub)n$A$=wwcv+*h}40E;xLB#_B}&Bm914MaJn} z)xqQx>o-21a)8_5{9=U2AAV|2e9|KMZmQjY#oW4rE@L(!+}{(q&t7`9RkGk2qsLEm zFiuWRNJZFsMDXmwAqDW;rCz*IJKF~l6@3Utw1i#hCr1(!GC}c!Yd!(xMG7v|Blclg zEGj3l0w+!e;V*r^H%r+d$f0q8!5O;a{1OJV!UOAg$>~yh#Q__ICqpn&OZh|Pyb(%a3Do_TlM&g4LDgJleY zoN_NeP`Hf942m^Zx5M6;#%`7=<$(?-h!5mC+FNr2azXnG=FA$>h4w?G-1c&>K9JB` z;}RM*n}+-cps`VMTaqqxzhp_Px|2&n>VB_{^PW%=zgYhrgT{U8XA4Vk@1_zB8im!{ z5{1;;(%9$ss<*8gOJg^EnYNPs#1ZZFthVK!_r)EzQ(({jP&SohP}J*d8*kurD3SkI zb>ltZLKfD6=7DdU>a`!lHozRN+I0?Pb(%De5-}>+vIZrKcJ%cYZ#@`VOi2{ z*TYwHH&7N>ygnwNJC``ATDPcc6eU6GPCj+Kt03<))cmUJyNqvJ4{sH9U=##_uP6)m zGoB=vJ-A(Da#iI5^*pM>0eu|0*}Xc}rNY}i$hyO_rW?O+xomwPvux}u`Pcbb4�| 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 Q1<=Rc<&bmP9@2&X0ICx#Z~y=R literal 0 HcmV?d00001 diff --git a/docs/images/overlayfs_non_dir_file.png b/docs/images/overlayfs_non_dir_file.png new file mode 100644 index 0000000000000000000000000000000000000000..74f9abd6ea231223ae672d178abbbf0e9ba74513 GIT binary patch literal 171958 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≻&)tpNY} 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 z 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 mUzzZi3;sXk0wBRMd{ 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 R`v@=qfv2mV%Q~loCIFelu805t literal 0 HcmV?d00001 diff --git a/docs/overlayfs.md b/docs/overlayfs.md new file mode 100644 index 000000000..4d956f3d7 --- /dev/null +++ b/docs/overlayfs.md @@ -0,0 +1,60 @@ +# Architecture of Overlay FS + +The implementation of userspace Overlay FS follows [the design of the kernel](https://docs.kernel.org/filesystems/overlayfs.html), +but it is not a direct port. +There are some differences between the kernel implementation and the userspace implementation due to FUSE limitations, +it's under heavy development to make it more stable and more compatible. + +## Basic Struct Definitions + +There're some important structs in the implementation of Overlay FS, they are: + +* `OverlayFs`: the main struct of the union FS, it's composed of multiple layers, normally one optional writable upper layer and many readonly lower layers. +* `OverlayInode`: inode struct in OverlayFs, one OverlayInode is composed of many `RealInode` in each layer. +* `RealInode`: wrapper for backend `inode` in one single layer. +* `HandleData`: opened file handle in OverlayFs, one OverlayInode reflects to one OverlayInode and one optional `RealHandle` in some layer. +* `RealHandle`: wrapper for backend opened file handle in one single layer. + +Also another trait named `Layer` is introducted to represent a single layer in OverlayFs, only filesystems which implement this trait can be used as a layer in OverlayFs. + +Relationship between these structs are illustrated in the following figure: + +![OverlayFs Structs](./images/overlayfs_structs.png) + +## Non-Directory File + +Following kernel Overlay semantics, OverlayFs uses the following rules to handle non-directory files: + +* If a file with same name exists in all layers, the topmost file will be choosed, any other files with same name in lower layers will be hidden. +* If a file in lower filesystem is accessed in a way the requires write-access, such as opening for write access, changing some metadata etc., +the file is first copied from the lower filesystem to the upper filesystem (copy_up). + +![OverlayFs Non-Directory File](./images/overlayfs_non_dir_file.png) + +## Directory + +Following kernel Overlay semantics, OverlayFs uses the following rules to handle directories: + +* If a directory with same name exists in all layers, the union directory will merge all entries of directory in all layers. +* If a directory is set as opaque, all entries in lower layers will be hidden. +* The copy up logic is similar to non-directory file, any write access to a directory will trigger copy up. + +![OverlayFs Directory](./images/overlayfs_dir.png) + +## Whiteout + +A whiteout is a special file in OverlayFs, it indicates a deletion of a file or directory in lower layer. +whiteout is device file with major number 0 and minor number 0, +and the name of whiteout file is the name of file or directory to be deleted. + +## Opaque + +Opaque is a special flag for directory in OverlayFs, it indicates that all entries of directory in lower layers will be ignored. +Opaque is implemented by setting one of these xattr to 'y': + +* `trusted.overlay.opaque` +* `user.overlay.opaque` +* `user.fuseoverlayfs.opaque` + +`user.fuseoverlayfs.opaque` is customized flag for our fuse-overlayfs. + From cec82c2b6cbf15d4270ed5cd7a77686e5f07e75d Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Tue, 16 Jan 2024 15:08:58 +0800 Subject: [PATCH 5/5] CI: add unionmount-testsuite Add unionmount-testsuite into CI for Overlay implementation Signed-off-by: Wei Zhang --- .github/workflows/xfstests.yml | 21 +++++ tests/scripts/unionmount_test_overlay.sh | 106 +++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100755 tests/scripts/unionmount_test_overlay.sh diff --git a/.github/workflows/xfstests.yml b/.github/workflows/xfstests.yml index 57bec3c0d..532ff0b15 100644 --- a/.github/workflows/xfstests.yml +++ b/.github/workflows/xfstests.yml @@ -52,3 +52,24 @@ jobs: run: | cd $GITHUB_WORKSPACE sudo ./tests/scripts/xfstests_overlay.sh + + unionmount_testsuite_on_overlayfs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - name: Build overlay binary + run: | + cd tests/overlay + cargo build --release + sudo install -t /usr/sbin/ -m 700 ./target/release/overlay + - name: Setup and run unionmount testsuite + run: | + cd $GITHUB_WORKSPACE + sudo ./tests/scripts/unionmount_test_overlay.sh \ No newline at end of file diff --git a/tests/scripts/unionmount_test_overlay.sh b/tests/scripts/unionmount_test_overlay.sh new file mode 100755 index 000000000..6268719a4 --- /dev/null +++ b/tests/scripts/unionmount_test_overlay.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +# create fuse overlay mount script. +# /tmp/testoverlay must exists. +sudo cat >/usr/sbin/mount.fuse.testoverlay <>/tmp/testoverlay.log 2>&1 & +sleep 1 +EOF +sudo chmod +x /usr/sbin/mount.fuse.testoverlay + + +# clone xfstests and install. +cd /tmp/ +git clone -b ci-fuse-backend-rs https://github.com/WeiZhang555/unionmount-testsuite.git +cd unionmount-testsuite + +echo "====> Start to run unionmount-testsuite." + +mkdir -p /tmp/unionmount/ +touch /tmp/summary +success=0 +fail=0 + +for testcase in dir-open-dir \ +dir-open \ +dir-sym1-open \ +dir-sym1-weird-open \ +dir-sym2-open \ +dir-sym2-weird-open \ +dir-weird-open \ +hard-link-dir \ +hard-link \ +hard-link-sym \ +impermissible \ +mkdir \ +noent-creat-excl \ +noent-creat-excl-trunc \ +noent-creat \ +noent-creat-trunc \ +noent-plain \ +noent-trunc \ +open-creat-excl \ +open-creat-excl-trunc \ +open-creat \ +open-creat-trunc \ +open-plain \ +open-trunc \ +readlink \ +rename-exdev \ +rmdir \ +rmtree-new \ +rmtree \ +sym1-creat-excl \ +sym1-creat \ +sym1-plain \ +sym1-trunc \ +sym2-creat-excl \ +sym2-creat \ +sym2-plain \ +sym2-trunc \ +symx-creat-excl \ +symx-creat \ +symx-creat-trunc \ +symx-plain \ +symx-trunc \ +truncate \ +unlink +# === Some test cases are not supported by unionmount currently === +# dir-weird-open-dir +# rename-dir +# rename-empty-dir +# rename-file +# rename-hard-link +# rename-mass-2 +# rename-mass-3 +# rename-mass-4 +# rename-mass-5 +# rename-mass-dir +# rename-mass +# rename-mass-sym +# rename-move-dir +# rename-new-dir +# rename-new-pop-dir +# rename-pop-dir +do + UNIONMOUNT_BASEDIR=/tmp/unionmount sudo -E ./run --ov --fuse=testoverlay --xdev $testcase + if [ $? -eq 0 ] + then + echo "===== SUCCESS: " $testcase >> /tmp/summary + let success+=1 + else + echo ">>>>>>>> FAIL: " $testcase >> /tmp/summary + let fail+=1 + fi +done; + +cat /tmp/summary && rm /tmp/summary +echo "Total: success: $success, fail: $fail" + +if [ $fail -gt 0 ] +then + exit 1 +fi