Skip to content

Commit

Permalink
Re-own /nix/var to 0:0, except for the per-user profiles (#1337)
Browse files Browse the repository at this point in the history
* Re-own /nix/var to 0:0, except for the per-user profiles

Some users are seeing installation failures because /nix/var has corrupted permissions.

* Clean up the comments.

* Don't adjust ownership in the gcroots subdir

* Apply suggestions from code review

Co-authored-by: Cole Helbling <[email protected]>

* Move the chowning / chgrping to CreateNixTree, and prevent C.N.T. from erroring on UID mismatch.

---------

Co-authored-by: Cole Helbling <[email protected]>
  • Loading branch information
grahamc and cole-h authored Dec 4, 2024
1 parent 568e0f1 commit 7ed07a6
Showing 1 changed file with 82 additions and 5 deletions.
87 changes: 82 additions & 5 deletions src/action/common/create_nix_tree.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::os::unix::fs::MetadataExt;

use tracing::{span, Span};

use crate::action::base::CreateDirectory;
Expand Down Expand Up @@ -37,7 +39,7 @@ impl CreateNixTree {
for path in PATHS {
// We use `create_dir` over `create_dir_all` to ensure we always set permissions right
create_directories.push(
CreateDirectory::plan(path, String::from("root"), None, 0o0755, true)
CreateDirectory::plan(path, None, None, 0o0755, true)
.await
.map_err(Self::error)?,
)
Expand Down Expand Up @@ -70,10 +72,15 @@ impl Action for CreateNixTree {
create_directory_descriptions.push(val.description.clone())
}
}
vec![ActionDescription::new(
self.tracing_synopsis(),
create_directory_descriptions,
)]
vec![
ActionDescription::new(self.tracing_synopsis(), create_directory_descriptions),
ActionDescription::new(
"Synchronize /nix/var ownership".to_string(),
vec![format!(
"Will update existing files in /nix/var to be owned by User ID 0, Group ID 0"
)],
),
]
}

#[tracing::instrument(level = "debug", skip_all)]
Expand All @@ -83,6 +90,8 @@ impl Action for CreateNixTree {
create_directory.try_execute().await.map_err(Self::error)?;
}

ensure_nix_var_ownership().await.map_err(Self::error)?;

Ok(())
}

Expand Down Expand Up @@ -128,3 +137,71 @@ impl Action for CreateNixTree {
}
}
}

/// Everything under /nix/var (with two deprecated exceptions below) should be owned by 0:0.
///
/// * /nix/var/nix/profiles/per-user/*
/// * /nix/var/nix/gcroots/per-user/*
///
/// This function walks /nix/var and makes sure that is true.
async fn ensure_nix_var_ownership() -> Result<(), ActionErrorKind> {
let entryiter = walkdir::WalkDir::new("/nix/var")
.follow_links(false)
.same_file_system(true)
.contents_first(true)
.into_iter()
.filter_entry(|entry| {
let parent = entry.path().parent();

if parent == Some(std::path::Path::new("/nix/var/nix/profiles/per-user"))
|| parent == Some(std::path::Path::new("/nix/var/nix/gcroots/per-user"))
{
// False means do *not* descend into this directory
// ...which we don't want to do, because the per-user subdirectories are usually owned by that user.
return false;
}

true
})
.filter_map(|entry| match entry {
Ok(entry) => Some(entry),
Err(e) => {
tracing::warn!(%e, "Failed to get entry in /nix/var");
None
},
})
.filter_map(|entry| match entry.metadata() {
Ok(metadata) => Some((entry, metadata)),
Err(e) => {
tracing::warn!(
path = %entry.path().to_string_lossy(),
%e,
"Failed to read ownership and mode data"
);
None
},
})
.filter_map(|(entry, metadata)| {
// Dirents that are already 0:0 are to be skipped
if metadata.uid() == 0 && metadata.gid() == 0 {
return None;
}

Some((entry, metadata))
});
for (entry, _metadata) in entryiter {
tracing::debug!(
path = %entry.path().to_string_lossy(),
"Re-owning path to 0:0"
);

if let Err(e) = std::os::unix::fs::lchown(entry.path(), Some(0), Some(0)) {
tracing::warn!(
path = %entry.path().to_string_lossy(),
%e,
"Failed to set the owner:group to 0:0"
);
}
}
Ok(())
}

0 comments on commit 7ed07a6

Please sign in to comment.