Skip to content

Commit 7af0d74

Browse files
yyyeerboeryugey
authored andcommitted
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 <bo@hyper.sh>
1 parent 33643bf commit 7af0d74

File tree

9 files changed

+3238
-0
lines changed

9 files changed

+3238
-0
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ pub mod api;
120120
#[cfg(all(any(feature = "fusedev", feature = "virtiofs"), target_os = "linux"))]
121121
pub mod passthrough;
122122
pub mod transport;
123+
pub mod overlayfs;
123124

124125
pub mod common;
125126
pub use self::common::*;

src/overlayfs/config.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
2+
use std::time::Duration;
3+
use self::super::CachePolicy;
4+
use std::fmt;
5+
6+
#[derive(Default, Clone, Debug)]
7+
pub struct Config {
8+
pub upper: String,
9+
pub lower: Vec<String>,
10+
pub work: String,
11+
pub mountpoint: String,
12+
pub do_import: bool,
13+
pub writeback: bool,
14+
pub no_open: bool,
15+
pub no_opendir: bool,
16+
pub killpriv_v2: bool,
17+
pub no_readdir: bool,
18+
pub xattr: bool,
19+
pub xattr_permissions: bool,
20+
pub perfile_dax: bool,
21+
pub cache_policy: CachePolicy,
22+
pub attr_timeout: Duration,
23+
pub entry_timeout: Duration,
24+
}
25+
26+
impl Default for CachePolicy {
27+
fn default() -> Self {
28+
CachePolicy::Auto
29+
}
30+
}
31+
32+
impl Clone for CachePolicy {
33+
fn clone(&self) -> Self {
34+
match *self {
35+
CachePolicy::Never => CachePolicy::Never,
36+
CachePolicy::Always => CachePolicy::Always,
37+
CachePolicy::Auto => CachePolicy::Auto,
38+
}
39+
}
40+
}
41+
42+
impl fmt::Debug for CachePolicy {
43+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44+
let policy =
45+
match *self {
46+
CachePolicy::Never => "Never",
47+
CachePolicy::Always => "Always",
48+
CachePolicy::Auto => "Auto",
49+
};
50+
51+
write!(f, "CachePolicy: {}", policy)
52+
}
53+
}

src/overlayfs/datasource.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
///! DataSource trait
3+
pub trait DataSource {
4+
}

src/overlayfs/direct.rs

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
use std::collections::LinkedList;
2+
use std::sync::Arc;
3+
use std::io::{Result, Error, ErrorKind};
4+
use std::fs;
5+
use std::ffi::{CStr, CString};
6+
use std::time::Duration;
7+
8+
9+
use self::super::layer::Layer;
10+
use self::super::BoxedLayer;
11+
use self::super::{Inode, Handle};
12+
use crate::passthrough::{PassthroughFs, Config as PassthroughConfig};
13+
use crate::api::filesystem::{Entry, FileSystem, FsOptions, OpenOptions, GetxattrReply, ListxattrReply, DirEntry, SetattrValid, ZeroCopyReader, ZeroCopyWriter, Context};
14+
use crate::abi::fuse_abi::CreateIn;
15+
16+
pub struct Direct {
17+
pub upper: bool,
18+
pub fs: PassthroughFs,
19+
}
20+
21+
impl Direct {
22+
pub fn new(path: String, upper: bool) -> Result<LinkedList<Arc<BoxedLayer>>> {
23+
let dir = fs::canonicalize(path.as_str())?;
24+
let root_dir = dir.to_string_lossy().into_owned().to_owned();
25+
let mut config = PassthroughConfig::default();
26+
config.root_dir = root_dir;
27+
28+
// enable xattr
29+
config.xattr = true;
30+
31+
// under overlay fs, no need to negotiate
32+
config.do_import = false;
33+
let fs = PassthroughFs::new(config)?;
34+
35+
let mut layer = Direct{
36+
upper,
37+
fs,
38+
};
39+
40+
layer.init_layer()?;
41+
42+
let mut list = LinkedList::new();
43+
list.push_back(Arc::new(Box::new(layer) as BoxedLayer));
44+
45+
Ok(list)
46+
}
47+
}
48+
49+
impl Layer for Direct {
50+
fn init_layer(&mut self) -> Result<()> {
51+
self.fs.import()
52+
}
53+
54+
fn cleanup(&mut self) -> Result<()> {
55+
Ok(())
56+
}
57+
58+
fn is_upper(&self) -> bool {
59+
self.upper
60+
}
61+
}
62+
63+
impl FileSystem for Direct {
64+
type Inode = Inode;
65+
type Handle = Handle;
66+
67+
fn init(&self, capable: FsOptions) -> Result<FsOptions> {
68+
Err(Error::from(ErrorKind::Unsupported))
69+
}
70+
71+
fn destroy(&self) {
72+
self.fs.destroy()
73+
}
74+
75+
fn statfs(&self, ctx: &Context, inode: Inode) -> Result<libc::statvfs64> {
76+
self.fs.statfs(ctx, inode)
77+
}
78+
79+
fn lookup(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result<Entry> {
80+
self.fs.lookup(ctx, parent, name)
81+
}
82+
83+
fn forget(&self, ctx: &Context, inode: Inode, count: u64) {
84+
self.fs.forget(ctx, inode, count)
85+
}
86+
87+
fn batch_forget(&self, ctx: &Context, requests: Vec<(Inode, u64)>) {
88+
self.fs.batch_forget(ctx, requests)
89+
}
90+
91+
fn opendir(&self, ctx: &Context, inode: Inode, flags: u32) -> Result<(Option<Handle>, OpenOptions)> {
92+
// should test flags to refuse write operations
93+
let iflags: i32 = flags as i32;
94+
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;
95+
if !self.upper && write {
96+
return Err(Error::from_raw_os_error(libc::EROFS));
97+
}
98+
99+
self.fs.opendir(ctx, inode, flags)
100+
}
101+
102+
fn releasedir(&self, ctx: &Context, inode: Inode, flags: u32, handle: Handle) -> Result<()> {
103+
self.fs.releasedir(ctx, inode, flags, handle)
104+
}
105+
106+
fn mkdir(&self, ctx: &Context, parent: Inode, name: &CStr, mode: u32, umask: u32) -> Result<Entry> {
107+
if !self.upper {
108+
return Err(Error::from_raw_os_error(libc::EROFS));
109+
}
110+
111+
self.fs.mkdir(ctx, parent, name, mode, umask)
112+
}
113+
114+
fn rmdir(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result<()> {
115+
if !self.upper {
116+
return Err(Error::from_raw_os_error(libc::EROFS));
117+
}
118+
119+
self.fs.rmdir(ctx, parent, name)
120+
}
121+
122+
fn readdir(&self, ctx: &Context, inode: Inode, handle: Handle, size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry) -> Result<usize>) -> Result<()> {
123+
self.fs.readdir(ctx, inode, handle, size, offset, add_entry)
124+
}
125+
126+
fn readdirplus(&self, ctx: &Context, inode: Inode, handle: Handle, size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry, Entry) -> Result<usize>) -> Result<()> {
127+
self.fs.readdirplus(ctx, inode, handle, size, offset, add_entry)
128+
}
129+
130+
fn open(&self, ctx: &Context, inode: Inode, flags: u32, fuse_flags: u32) -> Result<(Option<Handle>, OpenOptions)> {
131+
let iflags: i32 = flags as i32;
132+
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;
133+
if !self.upper && write {
134+
return Err(Error::from_raw_os_error(libc::EROFS));
135+
}
136+
137+
self.fs.open(ctx, inode, flags, fuse_flags)
138+
}
139+
140+
fn release(&self, ctx: &Context, inode: Inode, flags: u32, handle: Handle, flush: bool, flock_release: bool, lock_owner: Option<u64>) -> Result<()> {
141+
self.fs.release(ctx, inode, flags, handle, flush, flock_release, lock_owner)
142+
}
143+
144+
fn create(&self, ctx: &Context, parent: Inode, name: &CStr, args: CreateIn) -> Result<(Entry, Option<Handle>, OpenOptions)> {
145+
if !self.upper {
146+
return Err(Error::from_raw_os_error(libc::EROFS));
147+
}
148+
149+
self.fs.create(ctx, parent, name, args)
150+
}
151+
152+
fn unlink(&self, ctx: &Context, parent: Inode, name: &CStr) -> Result<()> {
153+
if !self.upper {
154+
return Err(Error::from_raw_os_error(libc::EROFS));
155+
}
156+
157+
self.fs.unlink(ctx, parent, name)
158+
}
159+
160+
fn read(&self, ctx: &Context, inode: Inode, handle: Handle, w: &mut dyn ZeroCopyWriter, size: u32, offset: u64, lock_owner: Option<u64>, flags: u32) -> Result<usize> {
161+
self.fs.read(ctx, inode, handle, w, size, offset, lock_owner, flags)
162+
}
163+
164+
fn write(&self, ctx: &Context, inode: Inode, handle: Handle, r: &mut dyn ZeroCopyReader, size: u32, offset: u64, lock_owner: Option<u64>, delay_write: bool, flags: u32, fuse_flags: u32) -> Result<usize> {
165+
if !self.upper {
166+
return Err(Error::from_raw_os_error(libc::EROFS));
167+
}
168+
169+
self.fs.write(ctx, inode, handle, r, size, offset, lock_owner, delay_write, flags, fuse_flags)
170+
}
171+
172+
fn getattr(&self, ctx: &Context, inode: Inode, handle: Option<Handle>) -> Result<(libc::stat64, Duration)> {
173+
self.fs.getattr(ctx, inode, handle)
174+
}
175+
176+
fn setattr(&self, ctx: &Context, inode: Inode, attr: libc::stat64, handle: Option<Handle>, valid: SetattrValid) -> Result<(libc::stat64, Duration)> {
177+
if !self.upper {
178+
return Err(Error::from_raw_os_error(libc::EROFS));
179+
}
180+
181+
self.fs.setattr(ctx, inode, attr, handle, valid)
182+
}
183+
184+
fn rename(&self, ctx: &Context, olddir: Inode, oldname: &CStr, newdir: Inode, newname: &CStr, flags: u32) -> Result<()> {
185+
if !self.upper {
186+
return Err(Error::from_raw_os_error(libc::EROFS));
187+
}
188+
189+
self.fs.rename(ctx, olddir, oldname, newdir, newname, flags)
190+
}
191+
192+
fn mknod(&self, ctx: &Context, parent: Inode, name: &CStr, mode: u32, rdev: u32, umask: u32) -> Result<Entry> {
193+
if !self.upper {
194+
return Err(Error::from_raw_os_error(libc::EROFS));
195+
}
196+
197+
self.fs.mknod(ctx, parent, name, mode, rdev, umask)
198+
}
199+
200+
fn link(&self, ctx: &Context, inode: Inode, newparent: Inode, newname: &CStr) -> Result<Entry> {
201+
if !self.upper {
202+
return Err(Error::from_raw_os_error(libc::EROFS));
203+
}
204+
205+
self.fs.link(ctx, inode, newparent, newname)
206+
}
207+
208+
fn symlink(&self, ctx: &Context, linkname: &CStr, parent: Inode, name: &CStr) -> Result<Entry> {
209+
if !self.upper {
210+
return Err(Error::from_raw_os_error(libc::EROFS));
211+
}
212+
213+
self.fs.symlink(ctx, linkname, parent, name)
214+
}
215+
216+
fn readlink(&self, ctx: &Context, inode: Inode) -> Result<Vec<u8>> {
217+
self.fs.readlink(ctx, inode)
218+
}
219+
220+
fn flush(&self, ctx: &Context, inode: Inode, handle: Handle, lock_owner: u64) -> Result<()> {
221+
// even readonly opened file can be flushed,
222+
// so it does't have to be in upper layer
223+
// if !self.upper {
224+
// return Err(Error::from_raw_os_error(libc::EROFS));
225+
// }
226+
227+
self.fs.flush(ctx, inode, handle, lock_owner)
228+
}
229+
230+
fn fsync(&self, ctx: &Context, inode: Inode, datasync: bool, handle: Handle) -> Result<()> {
231+
if !self.upper {
232+
return Err(Error::from_raw_os_error(libc::EROFS));
233+
}
234+
235+
self.fs.fsync(ctx, inode, datasync, handle)
236+
}
237+
238+
fn fsyncdir(&self, ctx: &Context, inode: Inode, datasync: bool, handle: Handle) -> Result<()> {
239+
if !self.upper {
240+
return Err(Error::from_raw_os_error(libc::EROFS));
241+
}
242+
243+
self.fs.fsyncdir(ctx, inode, datasync, handle)
244+
}
245+
246+
fn access(&self, ctx: &Context, inode: Inode, mask: u32) -> Result<()> {
247+
let write = mask as i32 & libc::W_OK != 0;
248+
if !self.upper && write {
249+
return Err(Error::from_raw_os_error(libc::EROFS));
250+
}
251+
252+
self.fs.access(ctx, inode, mask)
253+
}
254+
255+
fn setxattr(&self, ctx: &Context, inode: Inode, name: &CStr, value: &[u8], flags: u32) -> Result<()> {
256+
if !self.upper {
257+
return Err(Error::from_raw_os_error(libc::EROFS));
258+
}
259+
260+
self.fs.setxattr(ctx, inode, name, value, flags)
261+
}
262+
263+
fn getxattr(&self, ctx: &Context, inode: Inode, name: &CStr, size: u32) -> Result<GetxattrReply> {
264+
self.fs.getxattr(ctx, inode, name, size)
265+
}
266+
267+
fn listxattr(&self, ctx: &Context, inode: Inode, size: u32) -> Result<ListxattrReply> {
268+
self.fs.listxattr(ctx, inode, size)
269+
}
270+
271+
fn removexattr(&self, ctx: &Context, inode: Inode, name: &CStr) -> Result<()> {
272+
if !self.upper {
273+
return Err(Error::from_raw_os_error(libc::EROFS));
274+
}
275+
276+
self.fs.removexattr(ctx, inode, name)
277+
}
278+
279+
fn fallocate(&self, ctx: &Context, inode: Inode, handle: Handle, mode: u32, offset: u64, length: u64) -> Result<()> {
280+
if !self.upper {
281+
return Err(Error::from_raw_os_error(libc::EROFS));
282+
}
283+
284+
self.fs.fallocate(ctx, inode, handle, mode, offset, length)
285+
}
286+
287+
fn lseek(&self, ctx: &Context, inode: Inode, handle: Handle, offset: u64, whence: u32) -> Result<u64> {
288+
self.fs.lseek(ctx, inode, handle, offset, whence)
289+
}
290+
}

0 commit comments

Comments
 (0)