uring_file/
metadata.rs

1//! File metadata types compatible with `std::fs::Metadata`.
2
3use std::fmt;
4use std::io;
5use std::time::Duration;
6use std::time::SystemTime;
7use std::time::UNIX_EPOCH;
8
9/// File metadata returned by statx. Provides an interface compatible with [`std::fs::Metadata`] and [`std::os::unix::fs::MetadataExt`].
10pub struct Metadata(pub(crate) libc::statx);
11
12impl Metadata {
13  // ===========================================================================
14  // std::fs::Metadata interface
15  // ===========================================================================
16
17  /// Returns the file type for this metadata.
18  pub fn file_type(&self) -> FileType {
19    FileType(self.0.stx_mode)
20  }
21
22  /// Returns `true` if this metadata is for a directory.
23  pub fn is_dir(&self) -> bool {
24    self.file_type().is_dir()
25  }
26
27  /// Returns `true` if this metadata is for a regular file.
28  pub fn is_file(&self) -> bool {
29    self.file_type().is_file()
30  }
31
32  /// Returns `true` if this metadata is for a symbolic link.
33  pub fn is_symlink(&self) -> bool {
34    self.file_type().is_symlink()
35  }
36
37  /// Returns the size of the file, in bytes.
38  pub fn len(&self) -> u64 {
39    self.0.stx_size
40  }
41
42  /// Returns `true` if the file size is 0 bytes.
43  pub fn is_empty(&self) -> bool {
44    self.0.stx_size == 0
45  }
46
47  /// Returns the permissions of the file.
48  pub fn permissions(&self) -> Permissions {
49    Permissions(self.0.stx_mode as u32 & 0o7777)
50  }
51
52  /// Returns the last modification time.
53  pub fn modified(&self) -> io::Result<SystemTime> {
54    Ok(system_time_from_unix(
55      self.0.stx_mtime.tv_sec,
56      self.0.stx_mtime.tv_nsec,
57    ))
58  }
59
60  /// Returns the last access time.
61  pub fn accessed(&self) -> io::Result<SystemTime> {
62    Ok(system_time_from_unix(
63      self.0.stx_atime.tv_sec,
64      self.0.stx_atime.tv_nsec,
65    ))
66  }
67
68  /// Returns the creation time (if supported by filesystem).
69  pub fn created(&self) -> io::Result<SystemTime> {
70    Ok(system_time_from_unix(
71      self.0.stx_btime.tv_sec,
72      self.0.stx_btime.tv_nsec,
73    ))
74  }
75
76  // ===========================================================================
77  // std::os::unix::fs::MetadataExt interface
78  // ===========================================================================
79
80  pub fn dev(&self) -> u64 {
81    libc::makedev(self.0.stx_dev_major, self.0.stx_dev_minor) as u64
82  }
83
84  pub fn ino(&self) -> u64 {
85    self.0.stx_ino
86  }
87
88  pub fn mode(&self) -> u32 {
89    self.0.stx_mode as u32
90  }
91
92  pub fn nlink(&self) -> u64 {
93    self.0.stx_nlink as u64
94  }
95
96  pub fn uid(&self) -> u32 {
97    self.0.stx_uid
98  }
99
100  pub fn gid(&self) -> u32 {
101    self.0.stx_gid
102  }
103
104  pub fn rdev(&self) -> u64 {
105    libc::makedev(self.0.stx_rdev_major, self.0.stx_rdev_minor) as u64
106  }
107
108  pub fn size(&self) -> u64 {
109    self.0.stx_size
110  }
111
112  pub fn atime(&self) -> i64 {
113    self.0.stx_atime.tv_sec
114  }
115
116  pub fn atime_nsec(&self) -> i64 {
117    self.0.stx_atime.tv_nsec as i64
118  }
119
120  pub fn mtime(&self) -> i64 {
121    self.0.stx_mtime.tv_sec
122  }
123
124  pub fn mtime_nsec(&self) -> i64 {
125    self.0.stx_mtime.tv_nsec as i64
126  }
127
128  pub fn ctime(&self) -> i64 {
129    self.0.stx_ctime.tv_sec
130  }
131
132  pub fn ctime_nsec(&self) -> i64 {
133    self.0.stx_ctime.tv_nsec as i64
134  }
135
136  pub fn blksize(&self) -> u64 {
137    self.0.stx_blksize as u64
138  }
139
140  pub fn blocks(&self) -> u64 {
141    self.0.stx_blocks
142  }
143}
144
145impl fmt::Debug for Metadata {
146  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147    f.debug_struct("Metadata")
148      .field("file_type", &self.file_type())
149      .field("permissions", &self.permissions())
150      .field("len", &self.len())
151      .field("uid", &self.uid())
152      .field("gid", &self.gid())
153      .field("ino", &self.ino())
154      .finish_non_exhaustive()
155  }
156}
157
158// =============================================================================
159// FileType
160// =============================================================================
161
162/// Representation of file types. Equivalent to [`std::fs::FileType`].
163#[derive(Clone, Copy, PartialEq, Eq, Hash)]
164pub struct FileType(u16);
165
166impl FileType {
167  pub fn is_dir(&self) -> bool {
168    (self.0 & libc::S_IFMT as u16) == libc::S_IFDIR as u16
169  }
170
171  pub fn is_file(&self) -> bool {
172    (self.0 & libc::S_IFMT as u16) == libc::S_IFREG as u16
173  }
174
175  pub fn is_symlink(&self) -> bool {
176    (self.0 & libc::S_IFMT as u16) == libc::S_IFLNK as u16
177  }
178
179  pub fn is_block_device(&self) -> bool {
180    (self.0 & libc::S_IFMT as u16) == libc::S_IFBLK as u16
181  }
182
183  pub fn is_char_device(&self) -> bool {
184    (self.0 & libc::S_IFMT as u16) == libc::S_IFCHR as u16
185  }
186
187  pub fn is_fifo(&self) -> bool {
188    (self.0 & libc::S_IFMT as u16) == libc::S_IFIFO as u16
189  }
190
191  pub fn is_socket(&self) -> bool {
192    (self.0 & libc::S_IFMT as u16) == libc::S_IFSOCK as u16
193  }
194}
195
196impl fmt::Debug for FileType {
197  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198    let kind = if self.is_file() {
199      "file"
200    } else if self.is_dir() {
201      "directory"
202    } else if self.is_symlink() {
203      "symlink"
204    } else if self.is_block_device() {
205      "block_device"
206    } else if self.is_char_device() {
207      "char_device"
208    } else if self.is_fifo() {
209      "fifo"
210    } else if self.is_socket() {
211      "socket"
212    } else {
213      "unknown"
214    };
215    write!(f, "FileType({kind})")
216  }
217}
218
219// =============================================================================
220// Permissions
221// =============================================================================
222
223/// Representation of file permissions. Equivalent to [`std::fs::Permissions`].
224#[derive(Clone, Copy, PartialEq, Eq)]
225pub struct Permissions(u32);
226
227impl Permissions {
228  pub fn readonly(&self) -> bool {
229    (self.0 & 0o200) == 0
230  }
231
232  pub fn mode(&self) -> u32 {
233    self.0
234  }
235
236  pub fn from_mode(mode: u32) -> Self {
237    Self(mode & 0o7777)
238  }
239
240  pub fn set_readonly(&mut self, readonly: bool) {
241    if readonly {
242      self.0 &= !0o222;
243    } else {
244      self.0 |= 0o200;
245    }
246  }
247}
248
249impl fmt::Debug for Permissions {
250  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251    write!(f, "Permissions({:04o})", self.0)
252  }
253}
254
255// =============================================================================
256// Helpers
257// =============================================================================
258
259fn system_time_from_unix(sec: i64, nsec: u32) -> SystemTime {
260  if sec >= 0 {
261    UNIX_EPOCH + Duration::new(sec as u64, nsec)
262  } else {
263    UNIX_EPOCH - Duration::new((-sec) as u64, nsec)
264  }
265}