How Do I Copy File Metadata to a New File?

How to use Rust to implement the cp -p function? I want to copy the file metadata when copying the file content. The std::fs::copy provided by the standard library ensures that the file permission remains unchanged, but the time attribute is not inherited.

You won't be able to do it with Rust's standard library. Different operating systems have different weird corners here, and you will need to use native OS-specific APIs for it.

So check how to implement it in C, and then use FFI or -sys crates to do it.

1 Like

Thank you. Is there a suitable API for modifying the file time attribute in UNIX?

You are probably looking for nix::sys::stat::utimes() from the nix crate. That crate has safe wrappers for a lot of Unix-specific functionality.

You can read it:

You can't write it yet:

1 Like

Thank you. After upgrading the rust version, I finally saw the API you said.

I found that the API for modifying file time is not stable. Are there any other stable APIs?

Doesn't seem to support the Windows platform.

Well you did ask for a UNIX solution.

If you want something that works with Windows, check out the windows::Win32::Storage::FileSystem::SetFileTime() function from the windows crate.

Our current scenario is Unix, but I want the code to be portable.

The best way to do that is by writing a cross-platform abstraction which calls into the right platform-specific API - kinda like what std does already.

For example,

pub fn copy_metadata(from: impl AsRef<Path>,  to: impl AsRef<Path>) -> Result<(), io::Error> {
  let from = from.as_ref();
  let to = to.as_ref();

  cfg_if! {
    if #[cfg(windows)] {
      windows::copy_metadata(from, to)
    } else if #[cfg(unix)] {
      unix::copy_metadata(from, to)
    } else {
      Err(io::ErrorKind::Unsupported.into())
    }
  }
}

#[cfg(windows)]
mod windows {
  pub(crate) fn copy_metadata(from: &Path, to: &Path) -> Result<(), io::Error> {
    // use windows::Win32::Storage::FileSystem::SetFileTime()
  }
}

#[cfg(unix)]
mod unix {
  pub(crate) fn copy_metadata(from: &Path, to: &Path) -> Result<(), io::Error> {
    // use nix::sys::stat::utimes()
  }
}

You could publish this as its own crate, or even make a PR to a crate like fs-extra which provides additional capabilities for working with the filesystem.