Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chgrp existing store if GID is different #1330

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 56 additions & 14 deletions src/action/common/provision_nix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Place Nix and it's requirements onto the target
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
#[serde(tag = "action_name", rename = "provision_nix")]
pub struct ProvisionNix {
nix_store_gid: u32,

pub(crate) fetch_nix: StatefulAction<FetchAndUnpackNix>,
pub(crate) create_nix_tree: StatefulAction<CreateNixTree>,
pub(crate) move_unpacked_nix: StatefulAction<MoveUnpackedNix>,
Expand All @@ -27,12 +29,6 @@ pub struct ProvisionNix {
impl ProvisionNix {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
if std::path::Path::new(NIX_STORE_LOCATION).exists() {
check_existing_nix_store_gid_matches(settings.nix_build_group_id)
.await
.map_err(Self::error)?;
}

let fetch_nix = FetchAndUnpackNix::plan(
settings.nix_package_url.clone(),
PathBuf::from(SCRATCH_DIR),
Expand All @@ -46,6 +42,7 @@ impl ProvisionNix {
.await
.map_err(Self::error)?;
Ok(Self {
nix_store_gid: settings.nix_build_group_id,
fetch_nix,
create_nix_tree,
move_unpacked_nix,
Expand Down Expand Up @@ -73,6 +70,7 @@ impl Action for ProvisionNix {
fetch_nix,
create_nix_tree,
move_unpacked_nix,
nix_store_gid,
} = &self;

let mut buf = Vec::default();
Expand All @@ -81,6 +79,13 @@ impl Action for ProvisionNix {
buf.append(&mut create_nix_tree.describe_execute());
buf.append(&mut move_unpacked_nix.describe_execute());

buf.push(ActionDescription::new(
"Synchronize /nix/store ownership".to_string(),
vec![format!(
"Will update existing files in the Nix Store to use the Nix build group ID {nix_store_gid}"
)],
));

buf
}

Expand All @@ -107,6 +112,10 @@ impl Action for ProvisionNix {
.await
.map_err(Self::error)?;

ensure_nix_store_group(self.nix_store_gid)
.await
.map_err(Self::error)?;

Ok(())
}

Expand All @@ -115,6 +124,7 @@ impl Action for ProvisionNix {
fetch_nix,
create_nix_tree,
move_unpacked_nix,
nix_store_gid: _,
} = &self;

let mut buf = Vec::default();
Expand Down Expand Up @@ -157,19 +167,51 @@ impl Action for ProvisionNix {
/// If there is an existing /nix/store directory, ensure that the group ID we're going to use for
/// the nix build group matches the group that owns /nix/store to prevent weird mismatched-ownership
/// issues.
async fn check_existing_nix_store_gid_matches(
desired_nix_build_group_id: u32,
) -> Result<(), ActionErrorKind> {
async fn ensure_nix_store_group(desired_nix_build_group_id: u32) -> Result<(), ActionErrorKind> {
let previous_store_metadata = tokio::fs::metadata(NIX_STORE_LOCATION)
.await
.map_err(|e| ActionErrorKind::GettingMetadata(NIX_STORE_LOCATION.into(), e))?;
let previous_store_group_id = previous_store_metadata.gid();
if previous_store_group_id != desired_nix_build_group_id {
return Err(ActionErrorKind::PathGroupMismatch(
NIX_STORE_LOCATION.into(),
previous_store_group_id,
desired_nix_build_group_id,
));
for (entry, _metadata) in walkdir::WalkDir::new(NIX_STORE_LOCATION)
.follow_links(false)
.same_file_system(true)
// chown all of the contents of the dir before NIX_STORE_LOCATION,
// this means our test of "does /nix/store have the right gid?"
// is useful until the entire store is examined
.contents_first(true)
.into_iter()
cole-h marked this conversation as resolved.
Show resolved Hide resolved
.filter_map(|entry| {
match entry {
Ok(entry) => Some(entry),
Err(e) => {
tracing::warn!(%e, "Enumerating the Nix store");
None
}
}
})
.filter_map(|entry| match entry.metadata() {
Ok(metadata) => Some((entry, metadata)),
Err(e) => {
tracing::warn!(path = %entry.path().to_string_lossy(), %e, "Reading ownership and mode data");
None
}
})
.filter(|(_entry, metadata)| {
// If the dirent's group ID is the *previous* GID, reassign.
// NOTE(@grahamc, 2024-11-15): Nix on macOS has store paths with a group of nixbld, and sometimes a group of `wheel` (0).
// On NixOS, all the store paths have their GID set to 0.
// I've enquired into why these are different, but don't have an answer yet.
if metadata.gid() == previous_store_group_id {
return true;
}

false
}) {
if let Err(e) = std::os::unix::fs::chown(entry.path(), Some(0), Some(desired_nix_build_group_id)) {
grahamc marked this conversation as resolved.
Show resolved Hide resolved
tracing::warn!(path = %entry.path().to_string_lossy(), %e, "Failed to set the owner:group to 0:{}", desired_nix_build_group_id);
}
}
}

Ok(())
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/linux/linux.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
{
"action": {
"action_name": "provision_nix",
"nix_store_gid": 350,
"fetch_nix": {
"action": {
"action_name": "fetch_and_unpack_nix",
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/linux/steam-deck.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
{
"action": {
"action_name": "provision_nix",
"nix_store_gid": 350,
"fetch_nix": {
"action": {
"action_name": "fetch_and_unpack_nix",
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/macos/macos.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
{
"action": {
"action_name": "provision_nix",
"nix_store_gid": 350,
"fetch_nix": {
"action": {
"action_name": "fetch_and_unpack_nix",
Expand Down
Loading