diff --git a/osdk/src/bundle/bin.rs b/osdk/src/bundle/bin.rs index b09532aa4..b98363000 100644 --- a/osdk/src/bundle/bin.rs +++ b/osdk/src/bundle/bin.rs @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MPL-2.0 -use std::{ - fs, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use super::file::BundleFile; -use crate::arch::Arch; +use crate::{arch::Arch, util::hard_link_or_copy}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct AsterBin { @@ -87,7 +84,7 @@ impl AsterBin { pub fn copy_to(self, base: impl AsRef) -> Self { let file_name = self.path.file_name().unwrap(); let copied_path = base.as_ref().join(file_name); - fs::copy(&self.path, copied_path).unwrap(); + hard_link_or_copy(&self.path, copied_path).unwrap(); Self { path: PathBuf::from(file_name), arch: self.arch, diff --git a/osdk/src/bundle/file.rs b/osdk/src/bundle/file.rs index ee2c55db5..57cfc87e1 100644 --- a/osdk/src/bundle/file.rs +++ b/osdk/src/bundle/file.rs @@ -7,6 +7,8 @@ use std::{ use sha2::{Digest, Sha256}; +use crate::util::hard_link_or_copy; + /// A trait for files in a bundle. The file in a bundle should have it's digest and be validatable. pub trait BundleFile { fn path(&self) -> &PathBuf; @@ -57,7 +59,7 @@ impl Initramfs { pub fn copy_to(self, base: impl AsRef) -> Self { let name = self.path.file_name().unwrap(); let dest = base.as_ref().join(name); - fs::copy(&self.path, dest).unwrap(); + hard_link_or_copy(&self.path, dest).unwrap(); Self { path: PathBuf::from(name), ..self diff --git a/osdk/src/bundle/vm_image.rs b/osdk/src/bundle/vm_image.rs index 6ebb4cd0e..664f6ae27 100644 --- a/osdk/src/bundle/vm_image.rs +++ b/osdk/src/bundle/vm_image.rs @@ -1,9 +1,8 @@ // SPDX-License-Identifier: MPL-2.0 -use std::{ - fs, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; + +use crate::util::hard_link_or_copy; use super::file::BundleFile; @@ -63,8 +62,7 @@ impl AsterVmImage { pub fn copy_to(self, base: impl AsRef) -> Self { let file_name = self.path.file_name().unwrap(); let copied_path = base.as_ref().join(file_name); - fs::copy(&self.path, copied_path).unwrap(); - fs::remove_file(&self.path).unwrap(); + hard_link_or_copy(&self.path, copied_path).unwrap(); Self { path: PathBuf::from(file_name), typ: self.typ, diff --git a/osdk/src/commands/build/bin.rs b/osdk/src/commands/build/bin.rs index 042a2bd24..d4377db72 100644 --- a/osdk/src/commands/build/bin.rs +++ b/osdk/src/commands/build/bin.rs @@ -17,7 +17,7 @@ use crate::{ bin::{AsterBin, AsterBinType, AsterBzImageMeta, AsterElfMeta}, file::BundleFile, }, - util::get_current_crate_info, + util::{get_current_crate_info, hard_link_or_copy}, }; pub fn make_install_bzimage( @@ -115,7 +115,7 @@ pub fn make_elf_for_qemu(install_dir: impl AsRef, elf: &AsterBin, strip: b } } else { // Copy the ELF file. - std::fs::copy(elf.path(), &result_elf_path).unwrap(); + hard_link_or_copy(elf.path(), &result_elf_path).unwrap(); } if elf.arch() == Arch::X86_64 { diff --git a/osdk/src/commands/build/grub.rs b/osdk/src/commands/build/grub.rs index c44f67ec9..9ad3d0081 100644 --- a/osdk/src/commands/build/grub.rs +++ b/osdk/src/commands/build/grub.rs @@ -16,7 +16,7 @@ use crate::{ scheme::{ActionChoice, BootProtocol}, Config, }, - util::get_current_crate_info, + util::{get_current_crate_info, hard_link_or_copy}, }; pub fn create_bootdev_image( @@ -42,7 +42,7 @@ pub fn create_bootdev_image( // Copy the initramfs to the boot directory. if let Some(init_path) = &initramfs_path { - fs::copy( + hard_link_or_copy( init_path.as_ref().to_str().unwrap(), iso_root.join("boot").join("initramfs.cpio.gz"), ) @@ -63,7 +63,7 @@ pub fn create_bootdev_image( _ => { // Copy the kernel image to the boot directory. let target_path = iso_root.join("boot").join(&target_name); - fs::copy(aster_bin.path(), target_path).unwrap(); + hard_link_or_copy(aster_bin.path(), target_path).unwrap(); } }; diff --git a/osdk/src/util.rs b/osdk/src/util.rs index 4b32d2891..2833c8ef2 100644 --- a/osdk/src/util.rs +++ b/osdk/src/util.rs @@ -3,7 +3,7 @@ use std::{ ffi::OsStr, fs::{self, File}, - io::{BufRead, BufReader, Write}, + io::{BufRead, BufReader, Result, Write}, path::{Path, PathBuf}, process::Command, }; @@ -290,3 +290,24 @@ impl Drop for DirGuard { std::env::set_current_dir(&self.0).unwrap(); } } + +/// Attempts to create a hard link from `from` to `to`. +/// If the hard link operation fails (e.g., due to crossing file systems), +/// it falls back to performing a file copy. +/// +/// # Arguments +/// - `from`: The source file path. +/// - `to`: The destination file path. +/// +/// # Returns +/// - `Ok(0)` if the hard link is successfully created (no data was copied). +/// - `Ok(size)` where `size` is the number of bytes copied if the hard link failed and a copy was performed. +/// - `Err(error)` if an error occurred during the copy operation. +pub fn hard_link_or_copy, Q: AsRef>(from: P, to: Q) -> Result { + if fs::hard_link(&from, &to).is_err() { + info!("Copying {:?} -> {:?}", from.as_ref(), to.as_ref()); + return fs::copy(from, to); + } + info!("Linking {:?} -> {:?}", from.as_ref(), to.as_ref()); + Ok(0) +}