Skip to content

Commit 9d035bc

Browse files
teohhanhuislp
authored andcommitted
Fix ordering of erofs images in overlay fs
Signed-off-by: Teoh Han Hui <[email protected]>
1 parent 28a04b5 commit 9d035bc

1 file changed

Lines changed: 56 additions & 36 deletions

File tree

crates/muvm/src/guest/mount.rs

Lines changed: 56 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::collections::HashSet;
22
use std::ffi::CString;
3-
use std::fs::{read_dir, read_link, File};
3+
use std::fs::{read_dir, read_link, DirEntry, File};
44
use std::io::Write;
55
use std::os::fd::AsFd;
66
use std::path::{Path, PathBuf};
@@ -14,7 +14,10 @@ use rustix::mount::{
1414
use rustix::path::Arg;
1515
use serde_json::json;
1616

17-
fn make_tmpfs(dir: &str) -> Result<()> {
17+
fn make_tmpfs<P>(dir: P) -> Result<()>
18+
where
19+
P: rustix::path::Arg,
20+
{
1821
mount2(
1922
Some("tmpfs"),
2023
dir,
@@ -25,7 +28,10 @@ fn make_tmpfs(dir: &str) -> Result<()> {
2528
.context("Failed to mount tmpfs")
2629
}
2730

28-
fn mkdir_fex(dir: &str) {
31+
fn mkdir_fex<P>(dir: P)
32+
where
33+
P: rustix::path::Arg,
34+
{
2935
// Must succeed since /run/ was just mounted and is now an empty tmpfs.
3036
mkdir(
3137
dir,
@@ -39,19 +45,19 @@ fn do_mount_recursive_bind(source: &str, target: PathBuf) -> Result<()> {
3945
// the /run/muvm-host thing.
4046
if source == "/run" {
4147
mount_bind(source, &target)
42-
.context(format!("Failed to mount {:?} on {:?}", &source, &target))?;
48+
.with_context(|| format!("Failed to mount {source:?} on {target:?}"))?;
4349
let host = target.join("muvm-host");
44-
mount_bind("/", &host).context(format!("Failed to mount / on {:?}", &host))?;
50+
mount_bind("/", &host).with_context(|| format!("Failed to mount / on {host:?}"))?;
4551
} else {
4652
mount_recursive_bind(source, &target)
47-
.context(format!("Failed to mount {:?} on {:?}", &source, &target))?;
53+
.with_context(|| format!("Failed to mount {source:?} on {target:?}"))?;
4854
}
4955
Ok(())
5056
}
5157

5258
fn mount_fex_rootfs(merged_rootfs: bool) -> Result<()> {
53-
let dir = "/run/fex-emu/";
54-
let dir_rootfs = dir.to_string() + "rootfs";
59+
let dir = Path::new("/run/fex-emu/");
60+
let dir_rootfs = dir.join("rootfs");
5561

5662
// Make base directories
5763
mkdir_fex(dir);
@@ -69,22 +75,31 @@ fn mount_fex_rootfs(merged_rootfs: bool) -> Result<()> {
6975
}
7076

7177
// Find /dev/vd*
72-
for x in read_dir("/dev").unwrap() {
73-
let file = x.unwrap();
74-
let name = file.file_name().into_string().unwrap();
75-
if !name.starts_with("vd") {
76-
continue;
77-
}
78-
79-
let path = file.path().into_os_string().into_string().unwrap();
80-
let dir = dir.to_string() + &name;
81-
78+
let mut dev_vd_entries: Vec<DirEntry> = read_dir("/dev")
79+
.context("Failed to read directory `/dev`")?
80+
.collect::<Result<Vec<_>, _>>()
81+
.context("Failed to read directory entry in `/dev`")?
82+
.into_iter()
83+
.filter(|entry| {
84+
entry
85+
.file_name()
86+
.into_string()
87+
.expect("file_name should not contain invalid UTF-8")
88+
.starts_with("vd")
89+
})
90+
.collect();
91+
// [readdir(3)](https://man7.org/linux/man-pages/man3/readdir.3.html#NOTES)
92+
//
93+
// > The order in which filenames are read by successive calls to readdir() depends on the
94+
// > filesystem implementation; it is unlikely that the names will be sorted in any fashion.
95+
dev_vd_entries.sort_unstable_by_key(|entry| entry.file_name());
96+
for entry in dev_vd_entries {
97+
let target_dir = dir.join(entry.file_name());
8298
// Mount the erofs images.
83-
mkdir_fex(&dir);
84-
mount2(Some(path), dir.clone(), Some("erofs"), flags, None)
85-
.context("Failed to mount erofs")
86-
.unwrap();
87-
images.push(dir);
99+
mkdir_fex(&target_dir);
100+
mount2(Some(entry.path()), &target_dir, Some("erofs"), flags, None)
101+
.context("Failed to mount erofs")?;
102+
images.push(target_dir);
88103
}
89104

90105
if images.is_empty() {
@@ -99,7 +114,7 @@ fn mount_fex_rootfs(merged_rootfs: bool) -> Result<()> {
99114
// For merged rootfs mode, we need to overlay subtrees separately
100115
// onto the real rootfs. First, insert the real rootfs as the
101116
// bottom-most "image".
102-
images.insert(0, "/".to_owned());
117+
images.insert(0, PathBuf::from("/"));
103118

104119
let mut merge_dirs = HashSet::new();
105120
let mut non_dirs = HashSet::new();
@@ -115,25 +130,25 @@ fn mount_fex_rootfs(merged_rootfs: bool) -> Result<()> {
115130
continue;
116131
};
117132
let source = entry.path();
118-
let file_name = entry.file_name().to_str().unwrap().to_owned();
119-
let target = Path::new(&dir_rootfs).join(&file_name);
133+
let file_name = entry.file_name();
134+
let target = dir_rootfs.join(&file_name);
120135

121136
if file_type.is_file() {
122137
// File in the root fs, bind mount it from the uppermost layer
123138
if non_dirs.insert(file_name) {
124139
File::create(&target)?;
125-
mount_bind(&source, &target)?;
140+
mount_bind(source, target)?;
126141
}
127142
} else if file_type.is_symlink() {
128143
// Symlink in the root fs, create it from the uppermost layer
129144
if non_dirs.insert(file_name) {
130145
let symlink_target = read_link(source)?;
131-
symlink(&symlink_target, &target)?;
146+
symlink(symlink_target, target)?;
132147
}
133148
} else {
134149
// Directory, so we potentially have to overlayfs it
135150
if merge_dirs.insert(file_name) {
136-
mkdir_fex(target.as_str()?);
151+
mkdir_fex(target);
137152
}
138153
}
139154
}
@@ -142,11 +157,11 @@ fn mount_fex_rootfs(merged_rootfs: bool) -> Result<()> {
142157
// Now, go through each potential merged dir and figure out which
143158
// layers have it, then mount an overlayfs (or bind if one layer).
144159
for dir in merge_dirs {
145-
let target = Path::new(&dir_rootfs).join(&dir);
160+
let target = dir_rootfs.join(&dir);
146161
let mut layers = Vec::new();
147162

148163
for image in images.iter() {
149-
let source = Path::new(image).join(&dir);
164+
let source = image.join(&dir);
150165
if source.is_dir() {
151166
layers.push(source.as_str().unwrap().to_owned());
152167
}
@@ -161,8 +176,8 @@ fn mount_fex_rootfs(merged_rootfs: bool) -> Result<()> {
161176
layers[0] = "/run/muvm-host/etc".to_owned();
162177
}
163178
let opts = format!(
164-
"lowerdir={},metacopy=off,redirect_dir=nofollow,userxattr",
165-
layers.into_iter().rev().collect::<Vec<String>>().join(":")
179+
"lowerdir={lowerdir},metacopy=off,redirect_dir=nofollow,userxattr",
180+
lowerdir = layers.into_iter().rev().collect::<Vec<String>>().join(":")
166181
);
167182
let opts = CString::new(opts).unwrap();
168183
let overlay = "overlay".to_string();
@@ -176,14 +191,19 @@ fn mount_fex_rootfs(merged_rootfs: bool) -> Result<()> {
176191
// Special case: Put back the /etc/resolv.conf overlay on top
177192
overlay_file(
178193
"/etc/resolv.conf",
179-
&(dir_rootfs.clone() + "/etc/resolv.conf"),
194+
dir_rootfs.join("etc/resolv.conf").as_str().unwrap(),
180195
)?;
181196
} else {
182197
if images.len() >= 2 {
183198
// Overlay the mounts together.
184199
let opts = format!(
185-
"lowerdir={}",
186-
images.into_iter().rev().collect::<Vec<String>>().join(":")
200+
"lowerdir={lowerdir}",
201+
lowerdir = images
202+
.into_iter()
203+
.rev()
204+
.map(|path| path.as_str().unwrap().to_owned())
205+
.collect::<Vec<_>>()
206+
.join(":")
187207
);
188208
let opts = CString::new(opts).unwrap();
189209
let overlay = "overlay".to_string();

0 commit comments

Comments
 (0)