common/
filecmp.rs

1use std::os::unix::prelude::PermissionsExt;
2use tracing::instrument;
3
4#[derive(Copy, Clone, Debug, Default)]
5pub struct MetadataCmpSettings {
6    pub uid: bool,
7    pub gid: bool,
8    pub mode: bool,
9    pub size: bool,
10    pub mtime: bool,
11    pub ctime: bool,
12}
13
14#[instrument]
15pub fn metadata_equal<
16    M1: crate::preserve::Metadata + std::fmt::Debug,
17    M2: crate::preserve::Metadata + std::fmt::Debug,
18>(
19    settings: &MetadataCmpSettings,
20    metadata1: &M1,
21    metadata2: &M2,
22) -> bool {
23    if settings.uid && metadata1.uid() != metadata2.uid() {
24        return false;
25    }
26    if settings.gid && metadata1.gid() != metadata2.gid() {
27        return false;
28    }
29    if settings.mode && metadata1.permissions().mode() != metadata2.permissions().mode() {
30        return false;
31    }
32    if settings.size && metadata1.size() != metadata2.size() {
33        return false;
34    }
35    if settings.mtime {
36        if metadata1.mtime() != metadata2.mtime() {
37            return false;
38        }
39        // some filesystems do not support nanosecond precision, so we only compare nanoseconds if both files have them
40        if metadata1.mtime_nsec() != 0
41            && metadata2.mtime_nsec() != 0
42            && metadata1.mtime_nsec() != metadata2.mtime_nsec()
43        {
44            return false;
45        }
46    }
47    if settings.ctime {
48        // ctime() returns 0 if not available (e.g., in protocol::Metadata)
49        // only compare if both have ctime available
50        if metadata1.ctime() != 0 && metadata2.ctime() != 0 {
51            if metadata1.ctime() != metadata2.ctime() {
52                return false;
53            }
54            if metadata1.ctime_nsec() != 0
55                && metadata2.ctime_nsec() != 0
56                && metadata1.ctime_nsec() != metadata2.ctime_nsec()
57            {
58                return false;
59            }
60        }
61    }
62    true
63}