From e2bc4bf7bb18554758f1419d84e3f924f5b1a5e8 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Tue, 24 Oct 2023 17:35:16 +0800 Subject: [PATCH] overlay: add overlay implementation With help of newly introduced Overlay FileSystem in `fuse-backend-rs` library, now we can create writable rootfs in Nydus. Implementation of writable rootfs is based on one passthrough FS(as upper layer) over one readonly rafs(as lower layer). To do so, configuration is extended with some Overlay options. Signed-off-by: Wei Zhang --- Cargo.lock | 30 ++++++++- Cargo.toml | 2 +- api/src/config.rs | 15 +++++ builder/src/core/context.rs | 1 + clib/Cargo.toml | 2 +- docs/nydus-overlayfs.md | 1 + docs/nydusd.md | 47 ++++++++++++++ rafs/Cargo.toml | 2 +- rafs/src/fs.rs | 15 ++++- service/Cargo.toml | 2 +- service/src/fs_service.rs | 81 ++++++++++++++++++++++-- smoke/tests/overlay_fs_test.go | 110 +++++++++++++++++++++++++++++++++ smoke/tests/tool/context.go | 20 ++++-- smoke/tests/tool/layer.go | 2 +- smoke/tests/tool/nydusd.go | 64 +++++++++++++++++-- smoke/tests/tool/verify.go | 2 +- storage/Cargo.toml | 2 +- storage/src/device.rs | 9 +-- 18 files changed, 377 insertions(+), 30 deletions(-) create mode 100644 smoke/tests/overlay_fs_test.go diff --git a/Cargo.lock b/Cargo.lock index 60b1d1b7039..f6df483045c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -447,6 +447,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "equivalent" version = "1.0.1" @@ -568,9 +574,9 @@ dependencies = [ [[package]] name = "fuse-backend-rs" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5a63a89f40ec26a0a1434e89de3f4ee939a920eae15d641053ee09ee6ed44b" +checksum = "e1663480cae165243a6c7f75abecfb868c16d17346afc74faf61a2febcadd11b" dependencies = [ "arc-swap", "bitflags 1.3.2", @@ -582,6 +588,7 @@ dependencies = [ "log", "mio", "nix", + "radix_trie", "versionize", "versionize_derive", "vhost", @@ -1153,6 +1160,15 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + [[package]] name = "nix" version = "0.24.2" @@ -1658,6 +1674,16 @@ dependencies = [ "uuid", ] +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.8.5" diff --git a/Cargo.toml b/Cargo.toml index fe729262beb..fbd64232437 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ path = "src/lib.rs" anyhow = "1" clap = { version = "4.0.18", features = ["derive", "cargo"] } flexi_logger = { version = "0.25", features = ["compress"] } -fuse-backend-rs = "^0.11.0" +fuse-backend-rs = "^0.12.0" hex = "0.4.3" hyper = "0.14.11" hyperlocal = "0.8.0" diff --git a/api/src/config.rs b/api/src/config.rs index 0dd77fd0d8f..7a40e0e4211 100644 --- a/api/src/config.rs +++ b/api/src/config.rs @@ -29,6 +29,8 @@ pub struct ConfigV2 { pub cache: Option, /// Configuration information for RAFS filesystem. pub rafs: Option, + /// Overlay configuration information for the instance. + pub overlay: Option, /// Internal runtime configuration. #[serde(skip)] pub internal: ConfigV2Internal, @@ -42,6 +44,7 @@ impl Default for ConfigV2 { backend: None, cache: None, rafs: None, + overlay: None, internal: ConfigV2Internal::default(), } } @@ -56,6 +59,7 @@ impl ConfigV2 { backend: None, cache: None, rafs: None, + overlay: None, internal: ConfigV2Internal::default(), } } @@ -1024,6 +1028,7 @@ impl From<&BlobCacheEntryConfigV2> for ConfigV2 { backend: Some(c.backend.clone()), cache: Some(c.cache.clone()), rafs: None, + overlay: None, internal: ConfigV2Internal::default(), } } @@ -1395,6 +1400,7 @@ impl TryFrom for ConfigV2 { backend: Some(backend), cache: Some(cache), rafs: Some(rafs), + overlay: None, internal: ConfigV2Internal::default(), }) } @@ -1523,6 +1529,15 @@ impl TryFrom<&BlobCacheEntryConfig> for BlobCacheEntryConfigV2 { } } +/// Configuration information for Overlay filesystem. +/// OverlayConfig is used to configure the writable layer(upper layer), +/// The filesystem will be writable when OverlayConfig is set. +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +pub struct OverlayConfig { + pub upper_dir: String, + pub work_dir: String, +} + #[cfg(test)] mod tests { use super::*; diff --git a/builder/src/core/context.rs b/builder/src/core/context.rs index dceb066dc87..14f33db855c 100644 --- a/builder/src/core/context.rs +++ b/builder/src/core/context.rs @@ -1516,6 +1516,7 @@ mod tests { id: "id".to_owned(), cache: None, rafs: None, + overlay: None, internal: ConfigV2Internal { blob_accessible: Arc::new(AtomicBool::new(true)), }, diff --git a/clib/Cargo.toml b/clib/Cargo.toml index 101f6046ef2..7f076036e13 100644 --- a/clib/Cargo.toml +++ b/clib/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "staticlib"] [dependencies] libc = "0.2.137" log = "0.4.17" -fuse-backend-rs = "^0.11.0" +fuse-backend-rs = "^0.12.0" nydus-api = { version = "0.3", path = "../api" } nydus-rafs = { version = "0.3.1", path = "../rafs" } nydus-storage = { version = "0.6.3", path = "../storage" } diff --git a/docs/nydus-overlayfs.md b/docs/nydus-overlayfs.md index 6f62c00f6c9..7229a2bbb58 100644 --- a/docs/nydus-overlayfs.md +++ b/docs/nydus-overlayfs.md @@ -39,3 +39,4 @@ mount -t overlay overlay ./foo/merged -o lowerdir=./foo/lower2:./foo/lower1,uppe Meanwhile, when containerd passes the `nydus-snapshotter` mount slice to `containerd-shim-kata-v2`, it can parse the mount slice and pass the `extraoption` to `nydusd`, to support nydus image format natively. So in summary, `containerd` and `containerd-shim-runc-v2` rely on the `nydus-overlay` mount helper to handle the mount slice returned by `nydus-snapshotter`, while `containerd-shim-kata-v2` can parse and handle it on its own. + diff --git a/docs/nydusd.md b/docs/nydusd.md index a536e4ea62b..242941e4266 100644 --- a/docs/nydusd.md +++ b/docs/nydusd.md @@ -409,6 +409,53 @@ Once the configuration is loaded successfully on nydusd starting, we will see th INFO [storage/src/backend/connection.rs:136] backend config: CommonConfig { proxy: ProxyConfig { url: "http://p2p-proxy:65001", ping_url: "http://p2p-proxy:40901/server/ping", fallback: true, check_interval: 5 }, timeout: 5, connect_timeout: 5, retry_limit: 0 } ``` +### Mount writable Overlay FS + +`Nydusd` itself has a native userspace Overlay FS implementation, which can be enabled with several extra configurations. + +An example configuration `/etc/nydus/nydusd-config.overlay.json` is as follows: + +```json +{ + "version": 2, + "backend": { + "type": "localfs", + "localfs": "/var/lib/nydus/blobs" + }, + "cache": { + "type": "blobcache", + "filecache": { + "work_dir": "/var/lib/nydus/cache" + } + }, + "rafs": { + "mode": "direct", + "enable_xattr": true + }, + "overlay": { + "upper_dir": "/path/to/upperdir", + "work_dir": "/path/to/workdir" + } +} +``` + +An extra field `overlay` is added to the Nydusd configuration, which specifies the `upper_dir` and `work_dir` for the overlay filesystem. + +You can start `nydusd` with the above configuration and such command: + +```bash +sudo nydusd \ + --config /etc/nydus/nydusd-config.overlay.json \ + --mountpoint /path/to/mnt/ \ + --bootstrap /path/to/bootstrap \ + --log-level info \ + --writable +``` + +This will create a FUSE overlay mountpoint at `/path/to/mnt/`, with one `Nydus` image as readonly lower layer and the `/path/to/upperdir` as writable upper layer, so that it can take over whole rootfs of a container, any contents writen from container will be stored in `/path/to/upperdir`. + +Removing `--writable` flag will make the overlay filesystem readonly if you wish. + ### Mount Bootstrap Via API To mount a bootstrap via api, first launch nydusd without a bootstrap: diff --git a/rafs/Cargo.toml b/rafs/Cargo.toml index d6e3872c924..5cbb972bb3a 100644 --- a/rafs/Cargo.toml +++ b/rafs/Cargo.toml @@ -19,7 +19,7 @@ nix = "0.24" serde = { version = "1.0.110", features = ["serde_derive", "rc"] } serde_json = "1.0.53" vm-memory = "0.10" -fuse-backend-rs = "^0.11.0" +fuse-backend-rs = "^0.12.0" thiserror = "1" nydus-api = { version = "0.3", path = "../api" } diff --git a/rafs/src/fs.rs b/rafs/src/fs.rs index 15a1848c4ce..a3119477c3d 100644 --- a/rafs/src/fs.rs +++ b/rafs/src/fs.rs @@ -291,7 +291,8 @@ impl Rafs { // since nydusify gives root directory permission of 0o750 and fuse mount // options `rootmode=` does not affect root directory's permission bits, ending // up with preventing other users from accessing the container rootfs. - if attr.ino == self.root_ino() { + let root_ino = self.root_ino(); + if attr.ino == root_ino { attr.mode = attr.mode & !0o777 | 0o755; } @@ -684,9 +685,9 @@ impl FileSystem for Rafs { _inode: Self::Inode, _flags: u32, _fuse_flags: u32, - ) -> Result<(Option, OpenOptions)> { + ) -> Result<(Option, OpenOptions, Option)> { // Keep cache since we are readonly - Ok((None, OpenOptions::KEEP_CACHE)) + Ok((None, OpenOptions::KEEP_CACHE, None)) } fn release( @@ -886,6 +887,14 @@ impl FileSystem for Rafs { } } +#[cfg(target_os = "linux")] +// Let Rafs works as an OverlayFs layer. +impl Layer for Rafs { + fn root_inode(&self) -> Self::Inode { + self.root_ino() + } +} + #[cfg(all(test, feature = "backend-oss"))] pub(crate) mod tests { use super::*; diff --git a/service/Cargo.toml b/service/Cargo.toml index 74fee2af621..56c980b8926 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -12,7 +12,7 @@ resolver = "2" [dependencies] bytes = { version = "1", optional = true } dbs-allocator = { version = "0.1.1", optional = true } -fuse-backend-rs = { version = "^0.11.0", features = ["persist"] } +fuse-backend-rs = { version = "^0.12.0", features = ["persist"] } libc = "0.2" log = "0.4.8" mio = { version = "0.8", features = ["os-poll", "os-ext"] } diff --git a/service/src/fs_service.rs b/service/src/fs_service.rs index a1ee0a6b683..b37128e6f14 100644 --- a/service/src/fs_service.rs +++ b/service/src/fs_service.rs @@ -13,10 +13,14 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::{Arc, MutexGuard}; +#[cfg(target_os = "linux")] +use fuse_backend_rs::api::filesystem::{FileSystem, FsOptions, Layer}; use fuse_backend_rs::api::vfs::VfsError; use fuse_backend_rs::api::{BackFileSystem, Vfs}; #[cfg(target_os = "linux")] -use fuse_backend_rs::passthrough::{Config, PassthroughFs}; +use fuse_backend_rs::overlayfs::{config::Config as overlay_config, OverlayFs}; +#[cfg(target_os = "linux")] +use fuse_backend_rs::passthrough::{CachePolicy, Config as passthrough_config, PassthroughFs}; use nydus_api::ConfigV2; use nydus_rafs::fs::Rafs; use nydus_rafs::{RafsError, RafsIoRead}; @@ -244,8 +248,77 @@ fn fs_backend_factory(cmd: &FsBackendMountCmd) -> Result { let config = Arc::new(config); let (mut rafs, reader) = Rafs::new(&config, &cmd.mountpoint, Path::new(&cmd.source))?; rafs.import(reader, prefetch_files)?; - info!("RAFS filesystem imported"); - Ok(Box::new(rafs)) + + // Put a writable upper layer above the rafs to create an OverlayFS with two layers. + match &config.overlay { + Some(ovl_conf) => { + // check workdir and upperdir params. + if ovl_conf.work_dir.is_empty() || ovl_conf.upper_dir.is_empty() { + return Err(Error::InvalidArguments(String::from( + "workdir and upperdir must be specified for overlayfs", + ))); + } + + // Create an overlay upper layer with passthroughfs. + #[cfg(target_os = "macos")] + return Err(Error::InvalidArguments(String::from( + "not support OverlayFs since passthroughfs isn't supported on MacOS", + ))); + #[cfg(target_os = "linux")] + { + let fs_cfg = passthrough_config { + // Use upper_dir as root_dir as rw layer. + root_dir: ovl_conf.upper_dir.clone(), + do_import: true, + writeback: true, + no_open: true, + no_opendir: true, + xattr: true, + cache_policy: CachePolicy::Always, + ..Default::default() + }; + let fsopts = FsOptions::WRITEBACK_CACHE + | FsOptions::ZERO_MESSAGE_OPEN + | FsOptions::ZERO_MESSAGE_OPENDIR; + + let passthrough_fs = PassthroughFs::<()>::new(fs_cfg) + .map_err(|e| Error::InvalidConfig(format!("{}", e)))?; + passthrough_fs.init(fsopts).map_err(Error::PassthroughFs)?; + + type BoxedLayer = Box + Send + Sync>; + let upper_layer = Arc::new(Box::new(passthrough_fs) as BoxedLayer); + + // Create overlay lower layer with rafs, use lower_dir as root_dir of rafs. + let lower_layers = vec![Arc::new(Box::new(rafs) as BoxedLayer)]; + + let overlay_config = overlay_config { + work: ovl_conf.work_dir.clone(), + mountpoint: cmd.mountpoint.clone(), + do_import: false, + no_open: true, + no_opendir: true, + ..Default::default() + }; + let overlayfs = + OverlayFs::new(Some(upper_layer), lower_layers, overlay_config) + .map_err(|e| Error::InvalidConfig(format!("{}", e)))?; + info!( + "init overlay fs inode, upper {}, work {}\n", + ovl_conf.upper_dir.clone(), + ovl_conf.work_dir.clone() + ); + overlayfs + .import() + .map_err(|e| Error::InvalidConfig(format!("{}", e)))?; + info!("Overlay filesystem imported"); + Ok(Box::new(overlayfs)) + } + } + None => { + info!("RAFS filesystem imported"); + Ok(Box::new(rafs)) + } + } } FsBackendType::PassthroughFs => { #[cfg(target_os = "macos")] @@ -257,7 +330,7 @@ fn fs_backend_factory(cmd: &FsBackendMountCmd) -> Result { // Vfs by default enables no_open and writeback, passthroughfs // needs to specify them explicitly. // TODO(liubo): enable no_open_dir. - let fs_cfg = Config { + let fs_cfg = passthrough_config { root_dir: cmd.source.to_string(), do_import: false, writeback: true, diff --git a/smoke/tests/overlay_fs_test.go b/smoke/tests/overlay_fs_test.go new file mode 100644 index 00000000000..818bfe93ecb --- /dev/null +++ b/smoke/tests/overlay_fs_test.go @@ -0,0 +1,110 @@ +// Copyright 2023 Nydus Developers. All rights reserved. +// +// SPDX-License-Identifier: Apache-2.0 + +package tests + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/containerd/log" + "github.com/containerd/nydus-snapshotter/pkg/converter" + "github.com/dragonflyoss/nydus/smoke/tests/texture" + "github.com/dragonflyoss/nydus/smoke/tests/tool" + "github.com/dragonflyoss/nydus/smoke/tests/tool/test" + "github.com/opencontainers/go-digest" + "github.com/stretchr/testify/require" +) + +type OverlayFsTestSuite struct { + t *testing.T +} + +func (ts *OverlayFsTestSuite) prepareTestEnv(t *testing.T) *tool.Context { + ctx := tool.DefaultContext(t) + ctx.PrepareWorkDir(t) + + packOption := converter.PackOption{ + BuilderPath: ctx.Binary.Builder, + Compressor: ctx.Build.Compressor, + FsVersion: ctx.Build.FSVersion, + ChunkSize: ctx.Build.ChunkSize, + } + + lowerLayer := texture.MakeLowerLayer(t, filepath.Join(ctx.Env.WorkDir, "lower")) + lowerBlobDigest := lowerLayer.Pack(t, packOption, ctx.Env.BlobDir) + mergeOption := converter.MergeOption{ + BuilderPath: ctx.Binary.Builder, + ChunkDictPath: "", + OCIRef: true, + } + actualDigests, lowerBootstrap := tool.MergeLayers(t, *ctx, mergeOption, []converter.Layer{ + { + Digest: lowerBlobDigest, + }, + }) + require.Equal(t, []digest.Digest{lowerBlobDigest}, actualDigests) + + // Verify lower layer mounted by nydusd + ctx.Env.BootstrapPath = lowerBootstrap + tool.Verify(t, *ctx, lowerLayer.FileTree) + return ctx +} + +func (ts *OverlayFsTestSuite) TestSimpleOverlayFs(t *testing.T) { + ctx := ts.prepareTestEnv(t) + fmt.Printf("Workdir is %v\n", ctx.Env.WorkDir) + defer ctx.Destroy(t) + + nydusd, err := tool.NewNydusdWithOverlay(tool.NydusdConfig{ + NydusdPath: ctx.Binary.Nydusd, + BootstrapPath: ctx.Env.BootstrapPath, + ConfigPath: filepath.Join(ctx.Env.WorkDir, "nydusd-config.fusedev.json"), + MountPath: ctx.Env.MountDir, + APISockPath: filepath.Join(ctx.Env.WorkDir, "nydusd-api.sock"), + BackendType: "localfs", + BackendConfig: fmt.Sprintf(`{"dir": "%s"}`, ctx.Env.BlobDir), + EnablePrefetch: ctx.Runtime.EnablePrefetch, + BlobCacheDir: ctx.Env.CacheDir, + CacheType: ctx.Runtime.CacheType, + CacheCompressed: ctx.Runtime.CacheCompressed, + RafsMode: ctx.Runtime.RafsMode, + OvlUpperDir: ctx.Env.OvlUpperDir, + OvlWorkDir: ctx.Env.OvlWorkDir, + DigestValidate: false, + Writable: true, + }) + require.NoError(t, err) + + err = nydusd.Mount() + require.NoError(t, err) + defer func() { + if err := nydusd.Umount(); err != nil { + log.L.WithError(err).Errorf("umount") + } + }() + + // Write some file under mounted dir. + mountedDir := ctx.Env.MountDir + file := filepath.Join(mountedDir, "test.txt") + err = os.WriteFile(file, []byte("hello world"), 0644) + require.NoError(t, err) + + // Read it back + data, err := os.ReadFile(file) + require.NoError(t, err) + require.Equal(t, "hello world", string(data)) + + // Try to read from upper dir. + upperFile := filepath.Join(ctx.Env.OvlUpperDir, "test.txt") + data, err = os.ReadFile(upperFile) + require.NoError(t, err) + require.Equal(t, "hello world", string(data)) +} + +func TestOverlayFs(t *testing.T) { + test.Run(t, &OverlayFsTestSuite{t: t}) +} diff --git a/smoke/tests/tool/context.go b/smoke/tests/tool/context.go index 2d74ac02e97..04cf6d851cf 100644 --- a/smoke/tests/tool/context.go +++ b/smoke/tests/tool/context.go @@ -47,6 +47,8 @@ type EnvContext struct { CacheDir string MountDir string BootstrapPath string + OvlUpperDir string + OvlWorkDir string } type Context struct { @@ -98,11 +100,21 @@ func (ctx *Context) PrepareWorkDir(t *testing.T) { err = os.MkdirAll(mountDir, 0755) require.NoError(t, err) + // For overlay fs + ovlUpperDir := filepath.Join(workDir, "upper") + err = os.MkdirAll(ovlUpperDir, 0755) + require.NoError(t, err) + ovlWorkDir := filepath.Join(workDir, "work") + err = os.MkdirAll(ovlWorkDir, 0755) + require.NoError(t, err) + ctx.Env = EnvContext{ - WorkDir: workDir, - BlobDir: blobDir, - CacheDir: cacheDir, - MountDir: mountDir, + WorkDir: workDir, + BlobDir: blobDir, + CacheDir: cacheDir, + MountDir: mountDir, + OvlUpperDir: ovlUpperDir, + OvlWorkDir: ovlWorkDir, } } diff --git a/smoke/tests/tool/layer.go b/smoke/tests/tool/layer.go index a6f1290ac08..523ea301198 100644 --- a/smoke/tests/tool/layer.go +++ b/smoke/tests/tool/layer.go @@ -230,7 +230,7 @@ func (l *Layer) Overlay(_ *testing.T, upper *Layer) *Layer { func (l *Layer) recordFileTree(t *testing.T) { l.FileTree = map[string]*File{} - filepath.Walk(l.workDir, func(path string, fi os.FileInfo, err error) error { + filepath.Walk(l.workDir, func(path string, _ os.FileInfo, _ error) error { targetPath := l.TargetPath(t, path) l.FileTree[targetPath] = NewFile(t, path, targetPath) return nil diff --git a/smoke/tests/tool/nydusd.go b/smoke/tests/tool/nydusd.go index a56b85aeeb0..d1f155309b0 100644 --- a/smoke/tests/tool/nydusd.go +++ b/smoke/tests/tool/nydusd.go @@ -69,6 +69,10 @@ type NydusdConfig struct { AccessPattern bool PrefetchFiles []string AmplifyIO uint64 + // Overlay config. + OvlUpperDir string + OvlWorkDir string + Writable bool } type Nydusd struct { @@ -109,8 +113,48 @@ var configTpl = ` } ` -func makeConfig(conf NydusdConfig) error { - tpl := template.Must(template.New("").Parse(configTpl)) +var configOvlTpl = ` + { + "version": 2, + "backend": { + "type": "localfs", + "localfs": {{.BackendConfig}} + }, + "cache": { + "type": "blobcache", + "filecache": { + "work_dir": "{{.BlobCacheDir}}" + } + }, + "rafs": { + "mode": "{{.RafsMode}}", + "enable_xattr": true + }, + "overlay": { + "upper_dir": "{{.OvlUpperDir}}", + "work_dir": "{{.OvlWorkDir}}" + } +} + ` + +type TemplateType int + +const ( + NydusdConfigTpl TemplateType = iota + NydusdOvlConfigTpl +) + +func makeConfig(tplType TemplateType, conf NydusdConfig) error { + var tpl *template.Template + + switch tplType { + case NydusdConfigTpl: + tpl = template.Must(template.New("").Parse(configTpl)) + case NydusdOvlConfigTpl: + tpl = template.Must(template.New("").Parse(configOvlTpl)) + default: + return errors.New("unknown template type") + } var ret bytes.Buffer if err := tpl.Execute(&ret, conf); err != nil { @@ -180,7 +224,16 @@ func CheckReady(ctx context.Context, sock string) <-chan bool { } func NewNydusd(conf NydusdConfig) (*Nydusd, error) { - if err := makeConfig(conf); err != nil { + if err := makeConfig(NydusdConfigTpl, conf); err != nil { + return nil, errors.Wrap(err, "create config file for Nydusd") + } + return &Nydusd{ + NydusdConfig: conf, + }, nil +} + +func NewNydusdWithOverlay(conf NydusdConfig) (*Nydusd, error) { + if err := makeConfig(NydusdOvlConfigTpl, conf); err != nil { return nil, errors.Wrap(err, "create config file for Nydusd") } return &Nydusd{ @@ -205,6 +258,9 @@ func (nydusd *Nydusd) Mount() error { if len(nydusd.BootstrapPath) > 0 { args = append(args, "--bootstrap", nydusd.BootstrapPath) } + if nydusd.Writable { + args = append(args, "--writable") + } cmd := exec.Command(nydusd.NydusdPath, args...) cmd.Stdout = os.Stdout @@ -236,7 +292,7 @@ func (nydusd *Nydusd) Mount() error { func (nydusd *Nydusd) MountByAPI(config NydusdConfig) error { - err := makeConfig(config) + err := makeConfig(NydusdConfigTpl, config) if err != nil { return err } diff --git a/smoke/tests/tool/verify.go b/smoke/tests/tool/verify.go index e78c49ebe35..b3dd177d373 100644 --- a/smoke/tests/tool/verify.go +++ b/smoke/tests/tool/verify.go @@ -43,7 +43,7 @@ func Verify(t *testing.T, ctx Context, expectedFiles map[string]*File) { }() actualFiles := map[string]*File{} - err = filepath.WalkDir(ctx.Env.MountDir, func(path string, entry fs.DirEntry, err error) error { + err = filepath.WalkDir(ctx.Env.MountDir, func(path string, _ fs.DirEntry, err error) error { require.Nil(t, err) targetPath, err := filepath.Rel(ctx.Env.MountDir, path) diff --git a/storage/Cargo.toml b/storage/Cargo.toml index 44197ec108f..a45ba0a1fe1 100644 --- a/storage/Cargo.toml +++ b/storage/Cargo.toml @@ -42,7 +42,7 @@ tokio = { version = "1.19.0", features = [ ] } url = { version = "2.1.1", optional = true } vm-memory = "0.10" -fuse-backend-rs = "^0.11.0" +fuse-backend-rs = "^0.12.0" gpt = { version = "3.1.0", optional = true } nydus-api = { version = "0.3", path = "../api" } diff --git a/storage/src/device.rs b/storage/src/device.rs index 3eb45d0e215..0800f1c59c3 100644 --- a/storage/src/device.rs +++ b/storage/src/device.rs @@ -1338,12 +1338,9 @@ impl FileReadWriteVolatile for BlobDeviceIoVec<'_> { unimplemented!(); } - fn read_at_volatile( - &mut self, - _slice: FileVolatileSlice, - _offset: u64, - ) -> Result { - unimplemented!(); + fn read_at_volatile(&mut self, slice: FileVolatileSlice, offset: u64) -> Result { + let buffers = [slice]; + self.read_vectored_at_volatile(&buffers, offset) } // The default read_vectored_at_volatile only read to the first slice, so we have to overload it.