Skip to content

Commit d14cc2b

Browse files
committed
env: detect the lowerdir overlay mount options at runtime
Replace the "legacy-mount-options" feature flag with runtime detection of this feature. We assume that systems with kernel version v6.8 and newer are capable of using the "lowerdir+" mount option. Related-to: #968 Related-to: #1164
1 parent 799e700 commit d14cc2b

File tree

3 files changed

+48
-12
lines changed

3 files changed

+48
-12
lines changed

crates/spfs/Cargo.toml

-5
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@ default = []
1818
# If enabled, will create the "local" repository in a subdirectory
1919
# of the standard storage root, named "ci/pipeline_${CI_PIPELINE_ID}".
2020
gitlab-ci-local-repo-isolation = []
21-
# Use "mount" commands that are compatible with older centos7-era kernels that
22-
# do not support the "lowerdir+=" overlayfs options. "legacy-mount-options"
23-
# can run into path length limits when mounting many layers.
24-
# https://github.com/spkenv/spk/issues/968
25-
legacy-mount-options = []
2621
sentry = ["dep:sentry"]
2722
server = ["hyper/server", "tokio-util/codec", "tokio-util/io-util"]
2823
"protobuf-src" = ["dep:protobuf-src"]

crates/spfs/src/env.rs

+21-7
Original file line numberDiff line numberDiff line change
@@ -1036,13 +1036,16 @@ const OVERLAY_ARGS_INDEX: &str = "index";
10361036
const OVERLAY_ARGS_INDEX_ON: &str = "index=on";
10371037
const OVERLAY_ARGS_METACOPY: &str = "metacopy";
10381038
const OVERLAY_ARGS_METACOPY_ON: &str = "metacopy=on";
1039+
pub(crate) const OVERLAY_ARGS_LOWERDIR_APPEND: &str = "lowerdir+";
10391040

10401041
/// A struct for holding the options that will be included
10411042
/// in the overlayfs mount command when mounting an environment.
10421043
#[derive(Default)]
10431044
pub(crate) struct OverlayMountOptions {
10441045
/// Specifies that the overlay file system is mounted as read-only
10451046
pub read_only: bool,
1047+
/// The lowerdir+ mount option will be used to append layers when true.
1048+
pub lowerdir_append: bool,
10461049
/// When true, inodes are indexed in the mount so that
10471050
/// files which share the same inode (hardlinks) are broken
10481051
/// in the final mount and changes to one file don't affect
@@ -1073,11 +1076,22 @@ impl OverlayMountOptions {
10731076
fn new(rt: &runtime::Runtime) -> Self {
10741077
Self {
10751078
read_only: !rt.status.editable,
1079+
lowerdir_append: true,
10761080
break_hardlinks: true,
10771081
metadata_copy_up: true,
10781082
}
10791083
}
10801084

1085+
/// Update state variables to match the features supported by the current overlay version.
1086+
fn query(mut self) -> Self {
1087+
let params = runtime::overlayfs::overlayfs_available_options();
1088+
if self.lowerdir_append && !params.contains(OVERLAY_ARGS_LOWERDIR_APPEND) {
1089+
self.lowerdir_append = false;
1090+
}
1091+
1092+
self
1093+
}
1094+
10811095
/// Return the options that should be included in the mount request.
10821096
pub fn to_options(&self) -> Vec<&'static str> {
10831097
let params = runtime::overlayfs::overlayfs_available_options();
@@ -1114,7 +1128,7 @@ pub(crate) fn get_overlay_args<P: AsRef<Path>>(
11141128
// Allocate a large buffer up front to avoid resizing/copying.
11151129
let mut args = String::with_capacity(4096);
11161130

1117-
let mount_options = OverlayMountOptions::new(rt);
1131+
let mount_options = OverlayMountOptions::new(rt).query();
11181132
for option in mount_options.to_options() {
11191133
args.push_str(option);
11201134
args.push(',');
@@ -1126,19 +1140,19 @@ pub(crate) fn get_overlay_args<P: AsRef<Path>>(
11261140
// the rightmost on the command line is the bottom layer, and the
11271141
// leftmost is on the top). For more details see:
11281142
// https://docs.kernel.org/filesystems/overlayfs.html#multiple-lower-layers
1129-
if cfg!(feature = "legacy-mount-options") {
1130-
args.push_str("lowerdir=");
1143+
if mount_options.lowerdir_append {
11311144
for path in layer_dirs.iter().rev() {
1145+
args.push_str("lowerdir+=");
11321146
args.push_str(&path.as_ref().to_string_lossy());
1133-
args.push(':');
1147+
args.push(',');
11341148
}
1149+
args.push_str("lowerdir+=");
11351150
} else {
1151+
args.push_str("lowerdir=");
11361152
for path in layer_dirs.iter().rev() {
1137-
args.push_str("lowerdir+=");
11381153
args.push_str(&path.as_ref().to_string_lossy());
1139-
args.push(',');
1154+
args.push(':');
11401155
}
1141-
args.push_str("lowerdir+=");
11421156
}
11431157
args.push_str(&rt.config.lower_dir.to_string_lossy());
11441158

crates/spfs/src/runtime/overlayfs.rs

+27
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::collections::HashSet;
66
use std::io::{BufRead, BufReader};
77
use std::os::unix::fs::MetadataExt;
88

9+
use crate::env::OVERLAY_ARGS_LOWERDIR_APPEND;
910
use crate::{Error, Result};
1011

1112
#[cfg(test)]
@@ -57,10 +58,36 @@ fn query_overlayfs_available_options() -> Result<HashSet<String>> {
5758
#[cfg(target_os = "linux")]
5859
fn parse_modinfo_params<R: BufRead>(reader: &mut R) -> Result<HashSet<String>> {
5960
let mut params = HashSet::new();
61+
let mut vermagic_seen = false;
6062
for line in reader.lines() {
6163
let line = line.map_err(|err| {
6264
Error::String(format!("Failed to read kernel module information: {err}"))
6365
})?;
66+
67+
// The output from "modinfo overlay" looks like this:
68+
// ...
69+
// vermagic: 6.12.9-amd64 SMP preempt mod_unload modversions
70+
// ...
71+
// parm: metacopy:Default to on or off for the metadata only copy up feature (bool)
72+
//
73+
// The "vermagic:" line appears before the "parm:" lines.
74+
75+
if !vermagic_seen {
76+
let version_string = match line.strip_prefix("vermagic:") {
77+
Some(remainder) => remainder.trim(),
78+
None => continue,
79+
};
80+
vermagic_seen = true;
81+
let mut parts = version_string.splitn(3, '.'); // ("6", "12", "9-...")
82+
let major_version = parts.next().unwrap_or("0").parse().unwrap_or(0);
83+
let minor_version = parts.next().unwrap_or("0").parse().unwrap_or(0);
84+
// The "lowerdir+" option was added in Linux v6.8.
85+
// https://docs.kernel.org/6.8/filesystems/overlayfs.html#multiple-lower-layers
86+
if major_version >= 7 || (major_version == 6 && minor_version >= 8) {
87+
params.insert(OVERLAY_ARGS_LOWERDIR_APPEND.to_string());
88+
}
89+
continue;
90+
}
6491
let param = match line.strip_prefix("parm:") {
6592
Some(remainder) => remainder.trim(),
6693
None => continue,

0 commit comments

Comments
 (0)