Skip to content

Commit a354af3

Browse files
committed
address some edge cases in bind mount masking
* check files/links before mounting and ignore if the mountpoint already exists * if the mountpoint of a directory exists and isn't a dir, we can't recurse and mount the subdir siblings
1 parent ed56961 commit a354af3

File tree

1 file changed

+31
-20
lines changed

1 file changed

+31
-20
lines changed

src/main.rs

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,41 +47,51 @@ impl<'a> RunChroot<'a> {
4747
fn bind_mount_directory(&self, entry: &fs::DirEntry) {
4848
let mountpoint = self.rootdir.join(entry.file_name());
4949

50-
// if there is already a dir here, recurse into it,
51-
// and mount any subdirs which don't already exist
52-
if mountpoint.is_dir() {
53-
let dir = fs::read_dir(entry.path()).unwrap_or_else(|err| {
54-
panic!("failed to list dir {}: {}", entry.path().display(), err)
55-
});
56-
57-
let child = RunChroot::new(&mountpoint);
58-
for entry in dir {
59-
child.bind_mount_direntry(entry);
60-
}
61-
} else {
50+
// if the destination doesn't exist we can proceed as normal
51+
if !mountpoint.exists() {
6252
if let Err(e) = fs::create_dir(&mountpoint) {
6353
if e.kind() != io::ErrorKind::AlreadyExists {
6454
panic!("failed to create {}: {}", &mountpoint.display(), e);
6555
}
6656
}
6757

6858
bind_mount(&entry.path(), &mountpoint)
59+
} else {
60+
// otherwise, if the dest is also a dir, we can recurse into it
61+
// and mount subdirectory siblings of existing paths
62+
if mountpoint.is_dir() {
63+
let dir = fs::read_dir(entry.path()).unwrap_or_else(|err| {
64+
panic!("failed to list dir {}: {}", entry.path().display(), err)
65+
});
66+
67+
let child = RunChroot::new(&mountpoint);
68+
for entry in dir {
69+
let entry = entry.expect("error while listing subdir");
70+
child.bind_mount_direntry(&entry);
71+
}
72+
}
6973
}
7074
}
7175

7276
fn bind_mount_file(&self, entry: &fs::DirEntry) {
7377
let mountpoint = self.rootdir.join(entry.file_name());
78+
if mountpoint.exists() {
79+
return;
80+
}
7481
fs::File::create(&mountpoint)
7582
.unwrap_or_else(|err| panic!("failed to create {}: {}", &mountpoint.display(), err));
7683

7784
bind_mount(&entry.path(), &mountpoint)
7885
}
7986

8087
fn mirror_symlink(&self, entry: &fs::DirEntry) {
88+
let link_path = self.rootdir.join(entry.file_name());
89+
if link_path.exists() {
90+
return;
91+
}
8192
let path = entry.path();
8293
let target = fs::read_link(&path)
8394
.unwrap_or_else(|err| panic!("failed to resolve symlink {}: {}", &path.display(), err));
84-
let link_path = self.rootdir.join(entry.file_name());
8595
symlink(&target, &link_path).unwrap_or_else(|_| {
8696
panic!(
8797
"failed to create symlink {} -> {}",
@@ -91,16 +101,12 @@ impl<'a> RunChroot<'a> {
91101
});
92102
}
93103

94-
fn bind_mount_direntry(&self, entry: io::Result<fs::DirEntry>) {
95-
let entry = entry.expect("error while listing from /nix directory");
96-
// do not bind mount an existing nix installation
97-
if entry.file_name() == PathBuf::from("nix") {
98-
return;
99-
}
104+
fn bind_mount_direntry(&self, entry: &fs::DirEntry) {
100105
let path = entry.path();
101106
let stat = entry
102107
.metadata()
103108
.unwrap_or_else(|err| panic!("cannot get stat of {}: {}", path.display(), err));
109+
104110
if stat.is_dir() {
105111
self.bind_mount_directory(&entry);
106112
} else if stat.is_file() {
@@ -132,7 +138,12 @@ impl<'a> RunChroot<'a> {
132138
let nix_root = PathBuf::from("/");
133139
let dir = fs::read_dir(&nix_root).expect("failed to list /nix directory");
134140
for entry in dir {
135-
self.bind_mount_direntry(entry);
141+
let entry = entry.expect("error while listing from /nix directory");
142+
// do not bind mount an existing nix installation
143+
if entry.file_name() == PathBuf::from("nix") {
144+
continue;
145+
}
146+
self.bind_mount_direntry(&entry);
136147
}
137148

138149
// mount the store

0 commit comments

Comments
 (0)