magic_mount: supports whiteout

This commit is contained in:
5ec1cff
2024-11-22 18:05:24 +08:00
parent af988ec8ec
commit ba8d85e356

View File

@@ -1,5 +1,5 @@
use crate::defs::{KSU_MOUNT_SOURCE, MODULE_DIR, SKIP_MOUNT_FILE_NAME, TEMP_DIR}; use crate::defs::{KSU_MOUNT_SOURCE, MODULE_DIR, SKIP_MOUNT_FILE_NAME, TEMP_DIR};
use crate::magic_mount::NodeFileType::{Directory, RegularFile, Symlink}; use crate::magic_mount::NodeFileType::{Directory, RegularFile, Symlink, Whiteout};
use crate::restorecon::{lgetfilecon, lsetfilecon}; use crate::restorecon::{lgetfilecon, lsetfilecon};
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use extattr::lgetxattr; use extattr::lgetxattr;
@@ -14,7 +14,7 @@ use std::collections::hash_map::Entry;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
use std::fs::{create_dir, create_dir_all, read_dir, DirEntry, FileType}; use std::fs::{create_dir, create_dir_all, read_dir, DirEntry, FileType};
use std::os::unix::fs::symlink; use std::os::unix::fs::{symlink, FileTypeExt};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
const REPLACE_DIR_XATTR: &str = "trusted.overlay.opaque"; const REPLACE_DIR_XATTR: &str = "trusted.overlay.opaque";
@@ -24,6 +24,7 @@ enum NodeFileType {
RegularFile, RegularFile,
Directory, Directory,
Symlink, Symlink,
Whiteout,
} }
impl NodeFileType { impl NodeFileType {
@@ -63,13 +64,9 @@ impl Node {
} }
} }
let file_type = entry.file_type()?;
let node = match self.children.entry(name.clone()) { let node = match self.children.entry(name.clone()) {
Entry::Occupied(o) => Some(o.into_mut()), Entry::Occupied(o) => Some(o.into_mut()),
Entry::Vacant(v) => { Entry::Vacant(v) => Self::new_module(&name, &entry, &path).map(|it| v.insert(it)),
Self::new_module(&name, file_type, &path).map(|it| v.insert(it))
}
}; };
if let Some(node) = node { if let Some(node) = node {
@@ -96,18 +93,30 @@ impl Node {
fn new_module<T: ToString, P: AsRef<Path>>( fn new_module<T: ToString, P: AsRef<Path>>(
name: T, name: T,
file_type: FileType, entry: &DirEntry,
module_path: P, module_path: P,
) -> Option<Self> { ) -> Option<Self> {
let file_type = NodeFileType::from_file_type(file_type)?; if let Ok(metadata) = entry.metadata() {
let file_type = if metadata.file_type().is_char_device()
Some(Node { && metadata.dev() == 0
&& metadata.ino() == 0
{
Some(Whiteout)
} else {
NodeFileType::from_file_type(metadata.file_type())
};
if let Some(file_type) = file_type {
return Some(Node {
name: name.to_string(), name: name.to_string(),
file_type, file_type,
children: Default::default(), children: Default::default(),
module_path: Some(PathBuf::from(module_path.as_ref())), module_path: Some(PathBuf::from(module_path.as_ref())),
replace: false, replace: false,
}) });
}
}
None
} }
} }
@@ -251,13 +260,19 @@ fn do_magic_mount<P: AsRef<Path>, WP: AsRef<Path>>(
if !has_tmpfs { if !has_tmpfs {
for (name, node) in &current.children { for (name, node) in &current.children {
let real_path = path.join(name); let real_path = path.join(name);
let need = if node.file_type == Symlink || !real_path.exists() { let need = match node.file_type {
true Symlink => true,
} else { Whiteout => real_path.exists(),
let file_type = real_path.metadata()?.file_type(); _ => {
let file_type = if let Ok(metadata) = real_path.metadata() {
NodeFileType::from_file_type(file_type).unwrap_or(RegularFile); let file_type = NodeFileType::from_file_type(metadata.file_type())
.unwrap_or(Whiteout);
file_type != node.file_type || file_type == Symlink file_type != node.file_type || file_type == Symlink
} else {
// real path not exists
true
}
}
}; };
if need { if need {
create_tmpfs = need; create_tmpfs = need;
@@ -333,6 +348,7 @@ fn do_magic_mount<P: AsRef<Path>, WP: AsRef<Path>>(
move_mount(&work_dir_path, &path)?; move_mount(&work_dir_path, &path)?;
} }
} }
Whiteout => {}
} }
Ok(()) Ok(())