1#![warn(clippy::cargo)]
2
3#[cfg(unix)]
4use std::os::unix::fs::{chown, MetadataExt as _, PermissionsExt as _};
5use std::{io, path::Path};
6
7use filetime::{set_file_times, FileTime};
8
9#[cfg(unix)]
13fn copy_permission_inner(
14 to: &Path,
15 from_meta: &std::fs::Metadata,
16 to_meta: &std::fs::Metadata,
17) -> io::Result<()> {
18 let from_gid = from_meta.gid();
19 let to_gid = to_meta.gid();
20
21 let mut perms = from_meta.permissions();
22 perms.set_mode(perms.mode() & 0o0777);
23 if from_gid != to_gid && chown(to, None, Some(from_gid)).is_err() {
24 let new_perms = (perms.mode() & 0o0707) | ((perms.mode() & 0o07) << 3);
25 perms.set_mode(new_perms);
26 }
27 std::fs::set_permissions(to, perms)?;
28 Ok(())
29}
30
31#[cfg(windows)]
32fn copy_permission_inner(
33 to: &Path,
34 from_meta: &std::fs::Metadata,
35 _to_meta: &std::fs::Metadata,
36) -> io::Result<()> {
37 let permissions = from_meta.permissions();
38 std::fs::set_permissions(to, permissions)?;
39 Ok(())
40}
41
42fn copy_time_inner(to: &Path, from_meta: &std::fs::Metadata) -> io::Result<()> {
43 let atime = FileTime::from_last_access_time(from_meta);
44 let mtime = FileTime::from_last_modification_time(from_meta);
45 set_file_times(to, atime, mtime)
46}
47
48pub fn copy_metadata(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
50 let (from, to) = (from.as_ref(), to.as_ref());
51 let from_meta = std::fs::metadata(from)?;
52 let to_meta = std::fs::metadata(to)?;
53
54 let res = copy_time_inner(to, &from_meta);
57 copy_permission_inner(to, &from_meta, &to_meta)?;
58 if let Err(err) = res {
59 if err.kind() == io::ErrorKind::PermissionDenied {
60 copy_time_inner(to, &from_meta)?;
61 }
62 }
63 Ok(())
64}
65
66pub fn copy_permission(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
68 let (from, to) = (from.as_ref(), to.as_ref());
69 let from_meta = std::fs::metadata(from)?;
70 let to_meta = std::fs::metadata(to)?;
71 copy_permission_inner(to, &from_meta, &to_meta)
72}
73
74pub fn copy_time(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
78 let (from, to) = (from.as_ref(), to.as_ref());
79 let from_meta = std::fs::metadata(from)?;
80 copy_time_inner(to, &from_meta)
81}