diff --git a/.github/workflows/xfstests.yml b/.github/workflows/xfstests.yml index 6e9db1279..57bec3c0d 100644 --- a/.github/workflows/xfstests.yml +++ b/.github/workflows/xfstests.yml @@ -43,12 +43,12 @@ jobs: profile: minimal toolchain: stable override: true - - name: Build and testoverlay binary + - name: Build overlay binary run: | - cd tests/testoverlay + cd tests/overlay cargo build --release - sudo install -t /usr/sbin/ -m 700 ./target/release/testoverlay + sudo install -t /usr/sbin/ -m 700 ./target/release/overlay - name: Setup and run xfstest run: | cd $GITHUB_WORKSPACE - sudo ./tests/scripts/xfstests.sh + sudo ./tests/scripts/xfstests_overlay.sh diff --git a/src/api/filesystem/mod.rs b/src/api/filesystem/mod.rs index 3417b14ad..3168f1eaa 100644 --- a/src/api/filesystem/mod.rs +++ b/src/api/filesystem/mod.rs @@ -30,7 +30,9 @@ 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. diff --git a/src/overlayfs/mod.rs b/src/overlayfs/mod.rs index 46f495db7..940f29615 100644 --- a/src/overlayfs/mod.rs +++ b/src/overlayfs/mod.rs @@ -119,9 +119,7 @@ pub struct RealHandle { pub struct HandleData { pub node: Arc, - // pub childrens: Option>>, pub offset: libc::off_t, - // others? pub real_handle: Option, } @@ -231,8 +229,8 @@ impl RealInode { layer: self.layer.clone(), in_upper_layer: self.in_upper_layer, inode: v.inode, - whiteout: whiteout, - opaque: opaque, + whiteout, + opaque, stat: Some(v.attr), })) } @@ -467,7 +465,7 @@ impl RealInode { in_upper_layer: true, inode: entry.inode, whiteout: false, - opaque: opaque, + opaque, stat: Some(entry.attr), }) } @@ -530,7 +528,7 @@ impl OverlayInode { } pub fn new_from_real_inodes(name: &str, ino: u64, real_inodes: Vec) -> Result { - if real_inodes.len() == 0 { + 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)); } @@ -705,6 +703,64 @@ impl OverlayInode { 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() { + self.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. pub(crate) fn add_upper_inode(self: &Arc, ri: RealInode, clear_lowers: bool) { let mut inodes = self.real_inodes.lock().unwrap(); @@ -1059,7 +1115,7 @@ impl OverlayFs { let name = child.name.clone(); child.inode = ino; // Create bi-directional link between parent and child. - child.parent = Mutex::new(Arc::downgrade(&node)); + child.parent = Mutex::new(Arc::downgrade(node)); let arc_child = Arc::new(child); node_children.insert(name, arc_child.clone()); @@ -1318,7 +1374,7 @@ impl OverlayFs { } // Copy parent node up if necessary. - let pnode = self.copy_node_up(ctx, Arc::clone(&parent_node))?; + 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, @@ -1350,7 +1406,7 @@ impl OverlayFs { } None => { // Copy parent node up if necessary. - let pnode = self.copy_node_up(ctx, Arc::clone(&parent_node))?; + let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?; let mut new_node = None; pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result { let parent_real_inode = match parent_real_inode { @@ -1406,7 +1462,7 @@ impl OverlayFs { } // Copy parent node up if necessary. - let pnode = self.copy_node_up(ctx, Arc::clone(&parent_node))?; + 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, @@ -1433,7 +1489,7 @@ impl OverlayFs { } None => { // Copy parent node up if necessary. - let pnode = self.copy_node_up(ctx, Arc::clone(&parent_node))?; + let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?; let mut new_node = None; pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result { let parent_real_inode = match parent_real_inode { @@ -1491,7 +1547,7 @@ impl OverlayFs { } // Copy parent node up if necessary. - let pnode = self.copy_node_up(ctx, Arc::clone(&parent_node))?; + 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, @@ -1521,7 +1577,7 @@ impl OverlayFs { } None => { // Copy parent node up if necessary. - let pnode = self.copy_node_up(ctx, Arc::clone(&parent_node))?; + let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?; let mut new_node = None; pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result { let parent_real_inode = match parent_real_inode { @@ -1603,8 +1659,8 @@ impl OverlayFs { 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_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)? { @@ -1695,7 +1751,7 @@ impl OverlayFs { } // Copy parent node up if necessary. - let pnode = self.copy_node_up(ctx, Arc::clone(&parent_node))?; + 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, @@ -1722,7 +1778,7 @@ impl OverlayFs { } None => { // Copy parent node up if necessary. - let pnode = self.copy_node_up(ctx, Arc::clone(&parent_node))?; + let pnode = self.copy_node_up(ctx, Arc::clone(parent_node))?; let mut new_node = None; pnode.handle_upper_inode_locked(&mut |parent_real_inode| -> Result { let parent_real_inode = match parent_real_inode { @@ -1752,65 +1808,6 @@ impl OverlayFs { Ok(()) } - // Create a new directory in upper layer for node, node must be directory. - pub fn create_upper_dir( - &self, - ctx: &Context, - node: &Arc, - mode_umask: Option<(u32, u32)>, - ) -> Result<()> { - let st = node.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 node.in_upper_layer() { - 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 !pnode.in_upper_layer() { - self.create_upper_dir(ctx, &pnode, 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, node.name.as_str(), mode, umask)? - } - None => parent_ri.mkdir(ctx, node.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. - node.add_upper_inode(ri, false); - } - - Ok(()) - } - pub fn copy_symlink_up( &self, ctx: &Context, @@ -1834,7 +1831,7 @@ impl OverlayFs { let (self_layer, _, self_inode) = node.first_layer_inode(); if !parent_node.in_upper_layer() { - self.create_upper_dir(ctx, &parent_node, None)?; + parent_node.create_upper_dir(ctx, None)?; } // Read the linkname from lower layer. @@ -1887,7 +1884,7 @@ impl OverlayFs { let (lower_layer, _, lower_inode) = node.first_layer_inode(); if !parent_node.in_upper_layer() { - self.create_upper_dir(ctx, &parent_node, None)?; + parent_node.create_upper_dir(ctx, None)?; } // create the file in upper layer using information from lower layer @@ -1941,32 +1938,25 @@ impl OverlayFs { file.seek(SeekFrom::Start(0))?; offset = 0; - loop { - match upper_real_inode { - Some(ref ri) => { - 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; - } - None => { - // Not possible. - break; - } + 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. @@ -1995,7 +1985,7 @@ impl OverlayFs { let st = node.stat64(ctx)?; // directory if utils::is_dir(st) { - self.create_upper_dir(ctx, &node, None)?; + node.create_upper_dir(ctx, None)?; return Ok(Arc::clone(&node)); } @@ -2252,16 +2242,12 @@ impl OverlayFs { ) -> Result<(Arc, Inode, Handle)> { match self.handles.lock().unwrap().get(&handle) { Some(h) => match h.real_handle { - Some(ref rhd) => { - return Ok(( - rhd.layer.clone(), - rhd.inode, - rhd.handle.load(Ordering::Relaxed), - )); - } - None => { - return Err(Error::from_raw_os_error(libc::ENOENT)); - } + 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)), @@ -2321,14 +2307,14 @@ impl OverlayFs { //let (_l, h, _) = node.open(ctx, flags as u32, fuse_flags)?; //if let Some(handle) = h { //let hd = self.next_handle.fetch_add(1, Ordering::Relaxed); - let (layer, is_upper_layer, inode) = node.first_layer_inode(); + let (layer, in_upper_layer, inode) = node.first_layer_inode(); let handle_data = HandleData { node: Arc::clone(&node), offset: 0, real_handle: Some(RealHandle { - layer: layer, - in_upper_layer: is_upper_layer, - inode: inode, + layer, + in_upper_layer, + inode, handle: AtomicU64::new(0), invalid: AtomicBool::new(true), }), diff --git a/src/overlayfs/sync_io.rs b/src/overlayfs/sync_io.rs index 9ead1767b..6ceef8726 100644 --- a/src/overlayfs/sync_io.rs +++ b/src/overlayfs/sync_io.rs @@ -340,15 +340,15 @@ impl FileSystem for OverlayFs { None => Err(Error::from_raw_os_error(libc::ENOENT)), Some(handle) => { let hd = self.next_handle.fetch_add(1, Ordering::Relaxed); - let (layer, is_upper_layer, inode) = node.first_layer_inode(); + let (layer, in_upper_layer, inode) = node.first_layer_inode(); let handle_data = HandleData { node: Arc::clone(&node), // childrens: None, offset: 0, real_handle: Some(RealHandle { - layer: layer, - in_upper_layer: is_upper_layer, - inode: inode, + layer, + in_upper_layer, + inode, handle: AtomicU64::new(handle), invalid: AtomicBool::new(false), }), @@ -838,17 +838,14 @@ impl FileSystem for OverlayFs { let (layer, _, real_inode) = node.first_layer_inode(); - let result = layer.setxattr(ctx, real_inode, name, value, flags); + layer.setxattr(ctx, real_inode, name, value, flags) - // Check if node is directory and becomes opaque now. - // TODO: refresh node. + // TODO: refresh node since setxattr may made dir opaque. // let st = node.stat64(ctx)?; // if utils::is_dir(st) { // // Setxattr may made the dir opaque, reload it if necessary. // self.lookup_node(ctx, inode, "")?; // } - - result } fn getxattr( @@ -906,17 +903,15 @@ impl FileSystem for OverlayFs { } let (layer, _, ino) = node.first_layer_inode(); - let result = layer.removexattr(ctx, ino, name); + layer.removexattr(ctx, ino, name) - // Check if node is directory. - // TODO: refresh the node now in case it becomes non-opaque. + // TODO: refresh the node since removexattr may remove the opaque xattr. // let st = node.stat64(ctx)?; // if utils::is_dir(st) { // // removexattr may remove the opaque xattr so we have to reload the node itself. // node.loaded.store(false, Ordering::Relaxed); // self.lookup_node(ctx, inode, "")?; // } - result } fn fallocate( 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 71c566df0..e81ba23e2 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.exclude b/tests/scripts/xfstests_overlay.exclude similarity index 85% rename from tests/scripts/xfstests.exclude rename to tests/scripts/xfstests_overlay.exclude index e367cce22..a23286a90 100644 --- a/tests/scripts/xfstests.exclude +++ b/tests/scripts/xfstests_overlay.exclude @@ -1,6 +1,6 @@ # Exclude list for tests that we know are broken in smb3 # -generic/011 # dirstress 5 processes, broken +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. @@ -18,7 +18,8 @@ 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, need more investigation. +generic/591 # Broken. generic/633 # Suppose to be FUSE compatibility issue. -generic/650 # Super slow but passed, skip it currently. -generic/697 # Suppose to be FUSE compatibility issue. \ No newline at end of file +generic/697 # Suppose to be FUSE compatibility issue. + +#generic/650 # Super slow but passed, skip it currently. \ No newline at end of file diff --git a/tests/scripts/xfstests.sh b/tests/scripts/xfstests_overlay.sh similarity index 94% rename from tests/scripts/xfstests.sh rename to tests/scripts/xfstests_overlay.sh index 86346d8e3..ba487fafd 100755 --- a/tests/scripts/xfstests.sh +++ b/tests/scripts/xfstests_overlay.sh @@ -32,7 +32,7 @@ sudo cat >/usr/sbin/mount.fuse.testoverlay <>/tmp/testoverlay.log 2>&1 & @@ -47,6 +47,6 @@ 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.exclude +sudo ./check -fuse -E $current_dir/xfstests_overlay.exclude