diff --git a/src/action/base/create_directory.rs b/src/action/base/create_directory.rs index a28b95e2c..87ce4c572 100644 --- a/src/action/base/create_directory.rs +++ b/src/action/base/create_directory.rs @@ -4,13 +4,13 @@ use std::path::{Path, PathBuf}; use nix::unistd::{chown, Group, User}; use target_lexicon::OperatingSystem; -use tokio::fs::{create_dir_all, remove_dir_all, remove_file}; use tokio::process::Command; use tracing::{span, Span}; use crate::action::{Action, ActionDescription, ActionErrorKind, ActionState}; use crate::action::{ActionError, StatefulAction}; use crate::execute_command; +use crate::util::IgnoreNotFound; /** Create a directory at the given location, optionally with an owning user, group, and mode. @@ -184,7 +184,7 @@ impl Action for CreateDirectory { None }; - create_dir_all(&path) + tokio::fs::create_dir_all(&path) .await .map_err(|e| ActionErrorKind::CreateDirectory(path.clone(), e)) .map_err(Self::error)?; @@ -263,12 +263,12 @@ impl Action for CreateDirectory { .map_err(|e| ActionErrorKind::GettingMetadata(child_path_path.clone(), e)) .map_err(Self::error)?; if child_path_type.is_dir() { - remove_dir_all(child_path_path.clone()) + crate::util::remove_dir_all(&child_path_path, IgnoreNotFound::No) .await .map_err(|e| ActionErrorKind::Remove(path.clone(), e)) .map_err(Self::error)? } else { - remove_file(child_path_path) + crate::util::remove_file(&child_path_path, IgnoreNotFound::No) .await .map_err(|e| ActionErrorKind::Remove(path.clone(), e)) .map_err(Self::error)? @@ -278,10 +278,12 @@ impl Action for CreateDirectory { (true, _, false) => { tracing::debug!("Not cleaning mountpoint `{}`", path.display()); }, - (false, true, _) | (false, false, true) => remove_dir_all(path.clone()) - .await - .map_err(|e| ActionErrorKind::Remove(path.clone(), e)) - .map_err(Self::error)?, + (false, true, _) | (false, false, true) => { + crate::util::remove_dir_all(&path, IgnoreNotFound::No) + .await + .map_err(|e| ActionErrorKind::Remove(path.clone(), e)) + .map_err(Self::error)? + }, (false, false, false) => { tracing::debug!("Not removing `{}`, the folder is not empty", path.display()); }, diff --git a/src/action/base/create_file.rs b/src/action/base/create_file.rs index f5003f0f2..ee4264d29 100644 --- a/src/action/base/create_file.rs +++ b/src/action/base/create_file.rs @@ -6,12 +6,13 @@ use std::{ path::{Path, PathBuf}, }; use tokio::{ - fs::{remove_file, File, OpenOptions}, + fs::{File, OpenOptions}, io::{AsyncReadExt, AsyncWriteExt}, }; -use crate::action::{ - Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction, +use crate::{ + action::{Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction}, + util::IgnoreNotFound, }; /** Create a file at the given location with the provided `buf`, @@ -258,12 +259,8 @@ impl Action for CreateFile { buf: _, force: _, } = self; - // The user already deleted it - if !path.exists() { - return Ok(()); - } - remove_file(&path) + crate::util::remove_file(&path, IgnoreNotFound::Yes) .await .map_err(|e| ActionErrorKind::Remove(path.to_owned(), e)) .map_err(Self::error)?; diff --git a/src/action/base/move_unpacked_nix.rs b/src/action/base/move_unpacked_nix.rs index c2c18828d..d317f7b50 100644 --- a/src/action/base/move_unpacked_nix.rs +++ b/src/action/base/move_unpacked_nix.rs @@ -6,8 +6,9 @@ use std::{ use tracing::{span, Span}; use walkdir::WalkDir; -use crate::action::{ - Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction, +use crate::{ + action::{Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction}, + util::IgnoreNotFound, }; pub(crate) const DEST: &str = "/nix/"; @@ -99,7 +100,7 @@ impl Action for MoveUnpackedNix { let entry_dest = dest_store.join(entry.file_name()); if entry_dest.exists() { tracing::trace!(src = %entry.path().display(), dest = %entry_dest.display(), "Removing already existing package"); - tokio::fs::remove_dir_all(&entry_dest) + crate::util::remove_dir_all(&entry_dest, IgnoreNotFound::Yes) .await .map_err(|e| ActionErrorKind::Remove(entry_dest.clone(), e)) .map_err(Self::error)?; diff --git a/src/action/base/remove_directory.rs b/src/action/base/remove_directory.rs index d2dd73b38..f669831a7 100644 --- a/src/action/base/remove_directory.rs +++ b/src/action/base/remove_directory.rs @@ -1,10 +1,10 @@ use std::path::{Path, PathBuf}; -use tokio::fs::remove_dir_all; use tracing::{span, Span}; use crate::action::{Action, ActionDescription, ActionErrorKind, ActionState}; use crate::action::{ActionError, StatefulAction}; +use crate::util::IgnoreNotFound; /** Remove a directory, does nothing on revert. */ @@ -56,7 +56,10 @@ impl Action for RemoveDirectory { self.path.clone(), ))); } - remove_dir_all(&self.path) + + // At this point, we know the path exists, but just in case it was deleted between then + // and now, we still ignore the case where it no longer exists. + crate::util::remove_dir_all(&self.path, IgnoreNotFound::Yes) .await .map_err(|e| Self::error(ActionErrorKind::Remove(self.path.clone(), e)))?; } else { diff --git a/src/action/common/configure_determinate_nixd_init_service/mod.rs b/src/action/common/configure_determinate_nixd_init_service/mod.rs index a53884d19..6c57002fb 100644 --- a/src/action/common/configure_determinate_nixd_init_service/mod.rs +++ b/src/action/common/configure_determinate_nixd_init_service/mod.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use serde::{Deserialize, Serialize}; use tokio::io::AsyncWriteExt; @@ -9,6 +9,7 @@ use crate::action::common::configure_init_service::{SocketFile, UnitSrc}; use crate::action::{common::ConfigureInitService, Action, ActionDescription}; use crate::action::{ActionError, ActionErrorKind, ActionTag, StatefulAction}; use crate::settings::InitSystem; +use crate::util::IgnoreNotFound; // Linux const LINUX_NIXD_DAEMON_DEST: &str = "/etc/systemd/system/nix-daemon.service"; @@ -44,22 +45,17 @@ impl ConfigureDeterminateNixdInitService { // these service files wouldn't get removed, so we can't rely on them not being // there after phase 1 of the uninstall // [1]: https://github.com/DeterminateSystems/nix-installer/pull/1266 - if std::path::Path::new( - super::configure_upstream_init_service::DARWIN_NIX_DAEMON_DEST, + crate::util::remove_file( + Path::new(super::configure_upstream_init_service::DARWIN_NIX_DAEMON_DEST), + IgnoreNotFound::Yes, ) - .exists() - { - tokio::fs::remove_file( - super::configure_upstream_init_service::DARWIN_NIX_DAEMON_DEST, - ) - .await - .map_err(|e| { - Self::error(ActionErrorKind::Remove( - super::configure_upstream_init_service::DARWIN_NIX_DAEMON_DEST.into(), - e, - )) - })?; - } + .await + .map_err(|e| { + Self::error(ActionErrorKind::Remove( + super::configure_upstream_init_service::DARWIN_NIX_DAEMON_DEST.into(), + e, + )) + })?; Some(DARWIN_NIXD_DAEMON_DEST.into()) }, diff --git a/src/action/common/configure_init_service.rs b/src/action/common/configure_init_service.rs index 128fd6550..2686b60a9 100644 --- a/src/action/common/configure_init_service.rs +++ b/src/action/common/configure_init_service.rs @@ -10,6 +10,7 @@ use crate::execute_command; use crate::action::{Action, ActionDescription}; use crate::settings::InitSystem; +use crate::util::IgnoreNotFound; const TMPFILES_SRC: &str = "/nix/var/nix/profiles/default/lib/tmpfiles.d/nix-daemon.conf"; const TMPFILES_DEST: &str = "/etc/tmpfiles.d/nix-daemon.conf"; @@ -360,13 +361,12 @@ impl Action for ConfigureInitService { ) .await .map_err(Self::error)?; - if Path::new(service_dest).exists() { - tracing::trace!(path = %service_dest.display(), "Removing"); - tokio::fs::remove_file(service_dest) - .await - .map_err(|e| ActionErrorKind::Remove(service_dest.into(), e)) - .map_err(Self::error)?; - } + + crate::util::remove_file(service_dest, IgnoreNotFound::Yes) + .await + .map_err(|e| ActionErrorKind::Remove(service_dest.into(), e)) + .map_err(Self::error)?; + tracing::trace!(src = %service_src.display(), dest = %service_dest.display(), "Symlinking"); tokio::fs::symlink(service_src, service_dest) .await @@ -384,13 +384,10 @@ impl Action for ConfigureInitService { Self::check_if_systemd_unit_exists(src, dest) .await .map_err(Self::error)?; - if Path::new(dest).exists() { - tracing::trace!(path = %dest.display(), "Removing"); - tokio::fs::remove_file(dest) - .await - .map_err(|e| ActionErrorKind::Remove(dest.into(), e)) - .map_err(Self::error)?; - } + crate::util::remove_file(dest, IgnoreNotFound::Yes) + .await + .map_err(|e| ActionErrorKind::Remove(dest.into(), e)) + .map_err(Self::error)?; match src { UnitSrc::Path(src) => { @@ -610,13 +607,12 @@ impl Action for ConfigureInitService { errors.push(err); } - if Path::new(TMPFILES_DEST).exists() { - if let Err(err) = tokio::fs::remove_file(TMPFILES_DEST) + if let Err(err) = + crate::util::remove_file(Path::new(TMPFILES_DEST), IgnoreNotFound::Yes) .await .map_err(|e| ActionErrorKind::Remove(PathBuf::from(TMPFILES_DEST), e)) - { - errors.push(err); - } + { + errors.push(err); } if let Err(err) = execute_command( @@ -636,26 +632,20 @@ impl Action for ConfigureInitService { }; if let Some(dest) = &self.service_dest { - if dest.exists() { - tracing::trace!(path = %dest.display(), "Removing"); - if let Err(err) = tokio::fs::remove_file(dest) - .await - .map_err(|e| ActionErrorKind::Remove(PathBuf::from(dest), e)) - { - errors.push(err); - } + if let Err(err) = crate::util::remove_file(dest, IgnoreNotFound::Yes) + .await + .map_err(|e| ActionErrorKind::Remove(PathBuf::from(dest), e)) + { + errors.push(err); } } for socket in self.socket_files.iter() { - if socket.dest.exists() { - tracing::trace!(path = %socket.dest.display(), "Removing"); - if let Err(err) = tokio::fs::remove_file(&socket.dest) - .await - .map_err(|e| ActionErrorKind::Remove(socket.dest.to_path_buf(), e)) - { - errors.push(err); - } + if let Err(err) = crate::util::remove_file(&socket.dest, IgnoreNotFound::Yes) + .await + .map_err(|e| ActionErrorKind::Remove(socket.dest.to_path_buf(), e)) + { + errors.push(err); } } diff --git a/src/action/common/configure_upstream_init_service.rs b/src/action/common/configure_upstream_init_service.rs index 68a57210a..ed023965a 100644 --- a/src/action/common/configure_upstream_init_service.rs +++ b/src/action/common/configure_upstream_init_service.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use tracing::{span, Span}; @@ -7,6 +7,7 @@ use crate::action::{ActionError, ActionErrorKind, ActionTag, StatefulAction}; use crate::action::common::configure_init_service::{SocketFile, UnitSrc}; use crate::action::{common::ConfigureInitService, Action, ActionDescription}; use crate::settings::InitSystem; +use crate::util::IgnoreNotFound; // Linux const SERVICE_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.service"; @@ -45,23 +46,20 @@ impl ConfigureUpstreamInitService { // these service files wouldn't get removed, so we can't rely on them not being // there after phase 1 of the uninstall // [1]: https://github.com/DeterminateSystems/nix-installer/pull/1266 - if std::path::Path::new( - super::configure_determinate_nixd_init_service::DARWIN_NIXD_DAEMON_DEST, - ) - .exists() - { - tokio::fs::remove_file( + crate::util::remove_file( + Path::new( super::configure_determinate_nixd_init_service::DARWIN_NIXD_DAEMON_DEST, - ) - .await - .map_err(|e| { - Self::error(ActionErrorKind::Remove( - super::configure_determinate_nixd_init_service::DARWIN_NIXD_DAEMON_DEST - .into(), - e, - )) - })?; - } + ), + IgnoreNotFound::Yes, + ) + .await + .map_err(|e| { + Self::error(ActionErrorKind::Remove( + super::configure_determinate_nixd_init_service::DARWIN_NIXD_DAEMON_DEST + .into(), + e, + )) + })?; Some(DARWIN_NIX_DAEMON_DEST.into()) }, diff --git a/src/action/common/provision_determinate_nixd.rs b/src/action/common/provision_determinate_nixd.rs index 04fc31dfc..e7bd3e67d 100644 --- a/src/action/common/provision_determinate_nixd.rs +++ b/src/action/common/provision_determinate_nixd.rs @@ -1,11 +1,11 @@ use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; -use tokio::fs::{create_dir_all, remove_file}; use tracing::{span, Span}; -use crate::action::{ - Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction, +use crate::{ + action::{Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction}, + util::IgnoreNotFound, }; const DETERMINATE_NIXD_BINARY_PATH: &str = "/usr/local/bin/determinate-nixd"; @@ -62,15 +62,13 @@ impl Action for ProvisionDeterminateNixd { let bytes = crate::settings::DETERMINATE_NIXD_BINARY .ok_or_else(|| Self::error(ActionErrorKind::DeterminateNixUnavailable))?; - if self.binary_location.exists() { - remove_file(&self.binary_location) - .await - .map_err(|e| ActionErrorKind::Remove(self.binary_location.clone(), e)) - .map_err(Self::error)?; - } + crate::util::remove_file(&self.binary_location, IgnoreNotFound::Yes) + .await + .map_err(|e| ActionErrorKind::Remove(self.binary_location.clone(), e)) + .map_err(Self::error)?; if let Some(parent) = self.binary_location.parent() { - create_dir_all(&parent) + tokio::fs::create_dir_all(&parent) .await .map_err(|e| ActionErrorKind::CreateDirectory(parent.into(), e)) .map_err(Self::error)?; @@ -98,12 +96,10 @@ impl Action for ProvisionDeterminateNixd { #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { - if self.binary_location.exists() { - remove_file(&self.binary_location) - .await - .map_err(|e| ActionErrorKind::Remove(self.binary_location.clone(), e)) - .map_err(Self::error)?; - } + crate::util::remove_file(&self.binary_location, IgnoreNotFound::Yes) + .await + .map_err(|e| ActionErrorKind::Remove(self.binary_location.clone(), e)) + .map_err(Self::error)?; Ok(()) } diff --git a/src/action/linux/provision_selinux.rs b/src/action/linux/provision_selinux.rs index 756177e27..5b94f7693 100644 --- a/src/action/linux/provision_selinux.rs +++ b/src/action/linux/provision_selinux.rs @@ -1,6 +1,5 @@ use std::path::{Path, PathBuf}; -use tokio::fs::{create_dir_all, remove_file}; use tokio::process::Command; use tracing::{span, Span}; @@ -8,6 +7,7 @@ use crate::action::{ActionError, ActionErrorKind, ActionTag}; use crate::execute_command; use crate::action::{Action, ActionDescription, StatefulAction}; +use crate::util::IgnoreNotFound; pub const SELINUX_POLICY_PP_CONTENT: &[u8] = include_bytes!("selinux/nix.pp"); pub const DETERMINATE_SELINUX_POLICY_PP_CONTENT: &[u8] = @@ -77,7 +77,7 @@ impl Action for ProvisionSelinux { } if let Some(parent) = self.policy_path.parent() { - create_dir_all(&parent) + tokio::fs::create_dir_all(&parent) .await .map_err(|e| ActionErrorKind::CreateDirectory(parent.into(), e)) .map_err(Self::error)?; @@ -125,7 +125,7 @@ impl Action for ProvisionSelinux { async fn remove_existing_policy(policy_path: &Path) -> Result<(), ActionErrorKind> { execute_command(Command::new("semodule").arg("--remove").arg("nix")).await?; - remove_file(&policy_path) + crate::util::remove_file(&policy_path, IgnoreNotFound::Yes) .await .map_err(|e| ActionErrorKind::Remove(policy_path.into(), e))?; diff --git a/src/action/linux/revert_clean_steamos_nix_offload.rs b/src/action/linux/revert_clean_steamos_nix_offload.rs index deaad8d20..b54de4574 100644 --- a/src/action/linux/revert_clean_steamos_nix_offload.rs +++ b/src/action/linux/revert_clean_steamos_nix_offload.rs @@ -5,6 +5,7 @@ use tracing::{span, Span}; use crate::action::{ActionError, ActionErrorKind, ActionTag}; use crate::action::{Action, ActionDescription, StatefulAction}; +use crate::util::IgnoreNotFound; const OFFLOAD_PATH: &str = "/home/.steamos/offload/nix"; @@ -68,8 +69,7 @@ impl Action for RevertCleanSteamosNixOffload { for path in paths { let path = path.map_err(Self::error)?; - tracing::trace!(path = %path.display(), "Removing"); - tokio::fs::remove_dir_all(&path) + crate::util::remove_dir_all(&path, IgnoreNotFound::No) .await .map_err(|e| Self::error(ActionErrorKind::Remove(path, e)))?; } diff --git a/src/cli/subcommand/install.rs b/src/cli/subcommand/install.rs index 2458b2de0..65500c6b6 100644 --- a/src/cli/subcommand/install.rs +++ b/src/cli/subcommand/install.rs @@ -17,6 +17,7 @@ use crate::{ plan::RECEIPT_LOCATION, planner::Planner, settings::CommonSettings, + util::IgnoreNotFound, BuiltinPlanner, InstallPlan, NixInstallerError, }; use clap::{ArgAction, Parser}; @@ -322,15 +323,18 @@ impl CommandExecute for Install { .await .wrap_err("Copying `nix-installer` to `/nix/nix-installer`")?; - if Path::new(PHASE1_RECEIPT_LOCATION).exists() { + let phase1_receipt_path = Path::new(PHASE1_RECEIPT_LOCATION); + if phase1_receipt_path.exists() { tracing::debug!("Removing pre-existing uninstall phase 1 receipt at {PHASE1_RECEIPT_LOCATION} after successful install"); - tokio::fs::remove_file(PHASE1_RECEIPT_LOCATION) + crate::util::remove_file(phase1_receipt_path, IgnoreNotFound::Yes) .await .wrap_err_with(|| format!("Failed to remove uninstall phase 1 receipt at {PHASE1_RECEIPT_LOCATION}"))?; } - if Path::new(PHASE2_RECEIPT_LOCATION).exists() { + + let phase2_receipt_path = Path::new(PHASE2_RECEIPT_LOCATION); + if phase2_receipt_path.exists() { tracing::debug!("Removing pre-existing uninstall phase 2 receipt at {PHASE2_RECEIPT_LOCATION} after successful install"); - tokio::fs::remove_file(PHASE2_RECEIPT_LOCATION) + crate::util::remove_file(phase2_receipt_path, IgnoreNotFound::Yes) .await .wrap_err_with(|| format!("Failed to remove uninstall phase 2 receipt at {PHASE2_RECEIPT_LOCATION}"))?; } diff --git a/src/lib.rs b/src/lib.rs index a2c2793bd..0db472c27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,7 @@ mod plan; pub mod planner; pub mod self_test; pub mod settings; +mod util; use std::{ffi::OsStr, path::Path, process::Output}; diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 000000000..fecb9923b --- /dev/null +++ b/src/util.rs @@ -0,0 +1,47 @@ +use std::path::Path; + +#[derive(Debug, PartialEq, Eq)] +pub(crate) enum IgnoreNotFound { + Yes, + No, +} + +#[tracing::instrument(skip(path), fields(path = %path.display()))] +pub(crate) async fn remove_file( + path: &Path, + ignore_not_found: IgnoreNotFound, +) -> std::io::Result<()> { + tracing::trace!("Removing file"); + let res = tokio::fs::remove_file(path).await; + match res { + Ok(_) => Ok(()), + Err(e) + if e.kind() == std::io::ErrorKind::NotFound + && ignore_not_found == IgnoreNotFound::Yes => + { + tracing::trace!("Ignoring nonexistent file"); + Ok(()) + }, + e @ Err(_) => e, + } +} + +#[tracing::instrument(skip(path), fields(path = %path.display()))] +pub(crate) async fn remove_dir_all( + path: &Path, + ignore_not_found: IgnoreNotFound, +) -> std::io::Result<()> { + tracing::trace!("Removing directory and all contents"); + let res = tokio::fs::remove_dir_all(path).await; + match res { + Ok(_) => Ok(()), + Err(e) + if e.kind() == std::io::ErrorKind::NotFound + && ignore_not_found == IgnoreNotFound::Yes => + { + tracing::trace!("Ignoring nonexistent directory"); + Ok(()) + }, + e @ Err(_) => e, + } +}