wasmerio / wasmer

🚀 The leading Wasm Runtime supporting WASIX, WASI and Emscripten
https://wasmer.io
MIT License
18.69k stars 801 forks source link

the `UnionFileSystem` and `mem_fs::FileSystem` don't allow mounting overlapping directories #3678

Open Michael-F-Bryan opened 1 year ago

Michael-F-Bryan commented 1 year ago

Describe the bug

As part of #3677, I wanted to mount different folders from the host system to overlapping folders in the guest (e.g. /tmp/first/ goes to /mapped/ and /home/michael/ goes to /mapped/nested/). It doesn't look like this use case is supported by either UnionFileSystem or mem_fs::FileSystem at the moment.

Steps to reproduce

Here are some tests we can add to the wasmer-vfs crate.

// lib/vfs/src/union_fs.rs

use crate::{FileSystem, FileSystemExt, unionFileSystem, mem_fs};

#[test]
fn mount_to_overlapping_directories() {
    let top_level = mem_fs::FileSystem::default();
    top_level.touch("/file.txt").unwrap();
    let nested = mem_fs::FileSystem::default();
    nested.touch("/another-file.txt").unwrap();

    let mut fs = UnionFileSystem::default();
    fs.mount(
        "top-level",
        "/",
        false,
        Box::new(top_level),
        Some("/top-level"),
    );
    fs.mount(
        "nested",
        "/",
        false,
        Box::new(nested),
        Some("/top-level/nested"),
    );

    assert!(fs.is_dir("/top-level"));
    assert!(fs.is_file("/top-level/file.txt"));
    assert!(fs.is_dir("/top-level/nested"));
    assert!(fs.is_file("/top-level/nested/another-file.txt"));
}
// lib/vfs/src/mem_fs/filesystem.rs

#[test]
fn mount_to_overlapping_directories() {
    let top_level = FileSystem::default();
    top_level.touch("/file.txt").unwrap();
    let nested = FileSystem::default();
    nested.touch("/another-file.txt").unwrap();
    let top_level: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(top_level);
    let nested: Arc<dyn crate::FileSystem + Send + Sync> = Arc::new(nested);

    let fs = FileSystem::default();
    fs.mount("/top-level".into(), &top_level, "/".into())
        .unwrap();
    fs.mount("/top-level/nested".into(), &nested, "/".into())
        .unwrap();

    assert!(fs.is_dir("/top-level"));
    assert!(fs.is_file("/top-level/file.txt"));
    assert!(fs.is_dir("/top-level/nested"));
    assert!(fs.is_file("/top-level/nested/another-file.txt"));
}

Expected behavior

The above test should pass.

Actual behavior

---- union_fs::tests::mount_to_overlapping_directories stdout ----
thread 'union_fs::tests::mount_to_overlapping_directories' panicked at 'assertion failed: fs.is_dir(\"/top-level\")', lib/vfs/src/union_fs.rs:1101:9

---- mem_fs::filesystem::test_filesystem::mount_to_overlapping_directories stdout ----
thread 'mem_fs::filesystem::test_filesystem::mount_to_overlapping_directories' panicked at 'called `Result::unwrap()` on an `Err` value: UnknownError', lib/vfs/src/mem_fs/filesystem.rs:1719:14

The mem_fs test fails because we don't know how to handle Some(Node::ArcDirectory(..)) in this match statement:

https://github.com/wasmerio/wasmer/blob/d1d1a477c801424e74c218ac311095cbfaec22bd/lib/vfs/src/mem_fs/filesystem.rs#L758-L772

syrusakbary commented 1 year ago

Thanks for creating this issue with a way to reproduce it, super clear to start the discussion later in time!

ptitSeb commented 1 year ago

Following https://github.com/wasmerio/wasmer/pull/3737 the test now do

thread 'mem_fs::filesystem::test_filesystem::mount_to_overlapping_directories' panicked at 'called `Result::unwrap()` on an `Err` value: AlreadyExists', lib/vfs/src/mem_fs/filesystem.rs:1719:14

Wich does not really add support, but at least this is coherent behavior.

Some testing for ArcDirectory support should be written.