]> git.proxmox.com Git - rustc.git/blame - vendor/gix-index/src/entry/stat.rs
New upstream version 1.75.0+dfsg1
[rustc.git] / vendor / gix-index / src / entry / stat.rs
CommitLineData
49aad941
FG
1use std::{
2 cmp::Ordering,
3 time::{SystemTime, SystemTimeError},
4};
5
6use filetime::FileTime;
7
8use crate::entry::Stat;
9
10impl Stat {
11 /// Detect whether this stat entry is racy if stored in a file index with `timestamp`.
12 ///
13 /// An index entry is considered racy if it's `mtime` is larger or equal to the index `timestamp`.
14 /// The index `timestamp` marks the point in time before which we definitely resolved the racy git problem
15 /// for all index entries so any index entries that changed afterwards will need to be examined for
16 /// changes by actually reading the file from disk at least once.
17 pub fn is_racy(
18 &self,
19 timestamp: FileTime,
20 Options {
21 check_stat, use_nsec, ..
22 }: Options,
23 ) -> bool {
24 match timestamp.unix_seconds().cmp(&(self.mtime.secs as i64)) {
25 Ordering::Less => true,
26 Ordering::Equal if use_nsec && check_stat => timestamp.nanoseconds() <= self.mtime.nsecs,
27 Ordering::Equal => true,
28 Ordering::Greater => false,
29 }
30 }
31
32 /// Compares the stat information of two index entries.
33 ///
34 /// Intuitively this is basically equivalent to `self == other`.
35 /// However there a lot of nobs in git that tweak whether certain stat information is used when checking
36 /// equality, see [`Options`].
37 /// This function respects those options while performing the stat comparison and may therefore ignore some fields.
38 pub fn matches(
39 &self,
40 other: &Self,
41 Options {
42 trust_ctime,
43 check_stat,
44 use_nsec,
45 use_stdev,
46 }: Options,
47 ) -> bool {
48 if self.mtime.secs != other.mtime.secs {
49 return false;
50 }
51 if check_stat && use_nsec && self.mtime.nsecs != other.mtime.nsecs {
52 return false;
53 }
54
55 if self.size != other.size {
56 return false;
57 }
58
59 if trust_ctime {
60 if self.ctime.secs != other.ctime.secs {
61 return false;
62 }
63 if check_stat && use_nsec && self.ctime.nsecs != other.ctime.nsecs {
64 return false;
65 }
66 }
67
68 if check_stat {
69 if use_stdev && self.dev != other.dev {
70 return false;
71 }
72 self.ino == other.ino && self.gid == other.gid && self.uid == other.uid
73 } else {
74 true
75 }
76 }
77
fe692bf9 78 /// Creates stat information from the result of `symlink_metadata`.
49aad941
FG
79 pub fn from_fs(fstat: &std::fs::Metadata) -> Result<Stat, SystemTimeError> {
80 let mtime = fstat.modified().unwrap_or(std::time::UNIX_EPOCH);
81 let ctime = fstat.created().unwrap_or(std::time::UNIX_EPOCH);
82
83 #[cfg(not(unix))]
84 let res = Stat {
85 mtime: mtime.try_into()?,
86 ctime: ctime.try_into()?,
87 dev: 0,
88 ino: 0,
89 uid: 0,
90 gid: 0,
91 // truncation to 32 bits is on purpose (git does the same).
92 size: fstat.len() as u32,
93 };
94 #[cfg(unix)]
ed00b5ec
FG
95 let res = {
96 use std::os::unix::fs::MetadataExt;
97 Stat {
98 mtime: mtime.try_into().unwrap_or_default(),
99 ctime: ctime.try_into().unwrap_or_default(),
100 // truncating to 32 bits is fine here because
101 // that's what the linux syscalls returns
102 // just rust upcasts to 64 bits for some reason?
103 // numbers this large are impractical anyway (that's a lot of hard-drives).
104 dev: fstat.dev() as u32,
105 ino: fstat.ino() as u32,
106 uid: fstat.uid(),
107 gid: fstat.gid(),
108 // truncation to 32 bits is on purpose (git does the same).
109 size: fstat.len() as u32,
110 }
49aad941
FG
111 };
112
113 Ok(res)
114 }
115}
116
117impl TryFrom<SystemTime> for Time {
118 type Error = SystemTimeError;
119 fn try_from(s: SystemTime) -> Result<Self, SystemTimeError> {
120 let d = s.duration_since(std::time::UNIX_EPOCH)?;
121 Ok(Time {
122 // truncation to 32 bits is on purpose (we only compare the low bits)
123 secs: d.as_secs() as u32,
124 nsecs: d.subsec_nanos(),
125 })
126 }
127}
128
129impl From<Time> for SystemTime {
130 fn from(s: Time) -> Self {
131 std::time::UNIX_EPOCH + std::time::Duration::new(s.secs.into(), s.nsecs)
132 }
133}
134
135/// The time component in a [`Stat`] struct.
136#[derive(Debug, Default, PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)]
137#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
138pub struct Time {
139 /// The amount of seconds elapsed since EPOCH.
140 pub secs: u32,
141 /// The amount of nanoseconds elapsed in the current second, ranging from 0 to 999.999.999 .
142 pub nsecs: u32,
143}
144
145impl From<FileTime> for Time {
146 fn from(value: FileTime) -> Self {
147 Time {
148 secs: value.unix_seconds().try_into().expect("can't represent non-unix times"),
149 nsecs: value.nanoseconds(),
150 }
151 }
152}
153
154impl PartialEq<FileTime> for Time {
155 fn eq(&self, other: &FileTime) -> bool {
156 *self == Time::from(*other)
157 }
158}
159
160impl PartialOrd<FileTime> for Time {
161 fn partial_cmp(&self, other: &FileTime) -> Option<Ordering> {
162 self.partial_cmp(&Time::from(*other))
163 }
164}
165
166/// Configuration for comparing stat entries
167#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
168pub struct Options {
169 /// If true, a files creation time is taken into consideration when checking if a file changed.
170 /// Can be set to false in case other tools alter the creation time in ways that interfere with our operation.
171 ///
172 /// Default `true`.
173 pub trust_ctime: bool,
174 /// If true, all stat fields will be used when checking for up-to-date'ness of the entry. Otherwise
175 /// nano-second parts of mtime and ctime,uid, gid, inode and device number _will not_ be used, leaving only
176 /// the whole-second part of ctime and mtime and the file size to be checked.
177 ///
178 /// Default `true`.
179 pub check_stat: bool,
180 /// Whether to compare nano secs when comparing timestamps. This currently
181 /// leads to many false positives on linux and is therefore disabled there.
182 ///
183 /// Default `false`
184 pub use_nsec: bool,
185 /// Whether to compare network devices secs when comparing timestamps.
186 /// Disabled by default because this can cause many false positives on network
187 /// devices where the device number is not stable
188 ///
189 /// Default `false`.
190 pub use_stdev: bool,
191}
192
193impl Default for Options {
194 fn default() -> Self {
195 Self {
196 trust_ctime: true,
197 check_stat: true,
198 use_nsec: false,
199 use_stdev: false,
200 }
201 }
202}