Merge pull request #83 from nix-community/atomic-writes
tool: atomically write to ESP
This commit is contained in:
		
						commit
						57b56e104c
					
				|  | @ -143,7 +143,7 @@ impl Installer { | ||||||
|         println!("Appending secrets to initrd..."); |         println!("Appending secrets to initrd..."); | ||||||
| 
 | 
 | ||||||
|         let initrd_location = tempdir.path().join("initrd"); |         let initrd_location = tempdir.path().join("initrd"); | ||||||
|         copy( |         fs::copy( | ||||||
|             bootspec |             bootspec | ||||||
|                 .initrd |                 .initrd | ||||||
|                 .as_ref() |                 .as_ref() | ||||||
|  | @ -241,25 +241,39 @@ fn install_signed(key_pair: &KeyPair, from: &Path, to: &Path) -> Result<()> { | ||||||
| /// Sign and forcibly install a PE file.
 | /// Sign and forcibly install a PE file.
 | ||||||
| ///
 | ///
 | ||||||
| /// If the file already exists at the destination, it is overwritten.
 | /// If the file already exists at the destination, it is overwritten.
 | ||||||
|  | ///
 | ||||||
|  | /// This is implemented as an atomic write. The file is first written to the destination with a
 | ||||||
|  | /// `.tmp` suffix and then renamed to its final name. This is atomic, because a rename is an atomic
 | ||||||
|  | /// operation on POSIX platforms.
 | ||||||
| fn force_install_signed(key_pair: &KeyPair, from: &Path, to: &Path) -> Result<()> { | fn force_install_signed(key_pair: &KeyPair, from: &Path, to: &Path) -> Result<()> { | ||||||
|     println!("Signing and installing {}...", to.display()); |     println!("Signing and installing {}...", to.display()); | ||||||
|     ensure_parent_dir(to); |     let to_tmp = to.with_extension(".tmp"); | ||||||
|  |     ensure_parent_dir(&to_tmp); | ||||||
|     key_pair |     key_pair | ||||||
|         .sign_and_copy(from, to) |         .sign_and_copy(from, &to_tmp) | ||||||
|         .with_context(|| format!("Failed to copy and sign file from {from:?} to {to:?}"))?; |         .with_context(|| format!("Failed to copy and sign file from {from:?} to {to:?}"))?; | ||||||
|  |     fs::rename(&to_tmp, to).with_context(|| { | ||||||
|  |         format!("Failed to move temporary file {to_tmp:?} to final location {to:?}") | ||||||
|  |     })?; | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Install an arbitrary file
 | /// Install an arbitrary file.
 | ||||||
| ///
 | ///
 | ||||||
| /// The file is only copied if it doesn't exist at the destination
 | /// The file is only copied if it doesn't exist at the destination.
 | ||||||
|  | ///
 | ||||||
|  | /// This function is only designed to copy files to the ESP. It sets the permission bits of the
 | ||||||
|  | /// file at the destination to 0o755, the expected permissions for a vfat ESP. This is useful for
 | ||||||
|  | /// producing file systems trees which can then be converted to a file system image.
 | ||||||
| fn install(from: &Path, to: &Path) -> Result<()> { | fn install(from: &Path, to: &Path) -> Result<()> { | ||||||
|     if to.exists() { |     if to.exists() { | ||||||
|         println!("{} already exists, skipping...", to.display()); |         println!("{} already exists, skipping...", to.display()); | ||||||
|     } else { |     } else { | ||||||
|         println!("Installing {}...", to.display()); |         println!("Installing {}...", to.display()); | ||||||
|         ensure_parent_dir(to); |         ensure_parent_dir(to); | ||||||
|         copy(from, to)?; |         atomic_copy(from, to)?; | ||||||
|  |         set_permission_bits(to, 0o755) | ||||||
|  |             .with_context(|| format!("Failed to set permission bits to 0o755 on file: {to:?}"))?; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Ok(()) |     Ok(()) | ||||||
|  | @ -293,20 +307,29 @@ fn assemble_kernel_cmdline(init: &Path, kernel_params: Vec<String>) -> Vec<Strin | ||||||
|     kernel_cmdline |     kernel_cmdline | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn copy(from: &Path, to: &Path) -> Result<()> { | /// Atomically copy a file.
 | ||||||
|     ensure_parent_dir(to); | ///
 | ||||||
|     fs::copy(from, to) | /// The file is first written to the destination with a `.tmp` suffix and then renamed to its final
 | ||||||
|         .with_context(|| format!("Failed to copy from {} to {}", from.display(), to.display()))?; | /// name. This is atomic, because a rename is an atomic operation on POSIX platforms.
 | ||||||
|  | fn atomic_copy(from: &Path, to: &Path) -> Result<()> { | ||||||
|  |     let to_tmp = to.with_extension(".tmp"); | ||||||
| 
 | 
 | ||||||
|     // Set permission of all files copied to 0o755
 |     fs::copy(from, &to_tmp) | ||||||
|     let mut perms = fs::metadata(to) |         .with_context(|| format!("Failed to copy from {from:?} to {to_tmp:?}",))?; | ||||||
|         .with_context(|| format!("File {} doesn't have metadata", to.display()))? | 
 | ||||||
|  |     fs::rename(&to_tmp, to).with_context(|| { | ||||||
|  |         format!("Failed to move temporary file {to_tmp:?} to final location {to:?}") | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Set the octal permission bits of the specified file.
 | ||||||
|  | fn set_permission_bits(path: &Path, permission_bits: u32) -> Result<()> { | ||||||
|  |     let mut perms = fs::metadata(path) | ||||||
|  |         .with_context(|| format!("File {path:?} doesn't have any metadata"))? | ||||||
|         .permissions(); |         .permissions(); | ||||||
|     perms.set_mode(0o755); |     perms.set_mode(permission_bits); | ||||||
|     fs::set_permissions(to, perms) |     fs::set_permissions(path, perms) | ||||||
|         .with_context(|| format!("Failed to set permissions to: {}", to.display()))?; |         .with_context(|| format!("Failed to set permissions on {path:?}")) | ||||||
| 
 |  | ||||||
|     Ok(()) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Ensures the parent directory of an arbitrary path exists
 | // Ensures the parent directory of an arbitrary path exists
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 nikstur
						nikstur