fmmap/metadata/
unix.rs

1use std::os::unix::fs::MetadataExt;
2use std::time::{SystemTime, UNIX_EPOCH};
3use crate::error::{Error, ErrorKind, Result};
4use crate::MetaData;
5use crate::metadata::{DiskMetaData, EmptyMetaData, MemoryMetaData};
6
7/// Utility methods to MetaData
8#[enum_dispatch]
9pub trait MetaDataExt {
10    /// Returns the last access time of this metadata.
11    ///
12    /// The returned value corresponds to the atime field of stat on Unix platforms and the ftLastAccessTime field on Windows platforms.
13    ///
14    /// Note that not all platforms will keep this field update in a file’s metadata,
15    /// for example Windows has an option to disable updating
16    /// this time when files are accessed and Linux similarly has noatime.
17    fn accessed(&self) -> std::result::Result<SystemTime, Error>;
18
19    /// Returns the creation time listed in this metadata.
20    ///
21    /// The returned value corresponds to the `btime` field of `statx` on Linux kernel starting from to 4.11,
22    /// the `birthtime` field of stat on other Unix platforms,
23    /// and the `ftCreationTime` field on Windows platforms.
24    fn created(&self) -> std::result::Result<SystemTime, Error>;
25
26    /// Returns true if this metadata is for a regular file.
27    ///
28    /// It will be false for symlink metadata obtained from [`symlink_metadata`].
29    ///
30    /// When the goal is simply to read from (or write to) the source,
31    /// the most reliable way to test the source can be read (or written to) is to open it.
32    /// Only using is_file can break workflows like diff <( prog_a ) on a Unix-like system for example.
33    ///
34    /// [`symlink_metadata`]: https://doc.rust-lang.org/std/fs/fn.symlink_metadata.html
35    fn is_file(&self) -> bool;
36
37    /// Returns `true` if this metadata is for a symbolic link.
38    #[cfg(feature = "nightly")]
39    #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))]
40    fn is_symlink(&self) -> bool;
41
42    /// Returns the size of the file, in bytes, this metadata is for.
43    fn len(&self) -> u64;
44
45    /// Returns the last modification time listed in this metadata.
46    ///
47    /// The returned value corresponds to the `mtime` field of `stat` on Unix platforms
48    /// and the `ftLastWriteTime` field on Windows platforms.
49    ///
50    /// # Errors
51    /// This field might not be available on all platforms, and
52    /// will return an `Err` on platforms where it is not available.
53    fn modified(&self) -> std::result::Result<SystemTime, Error>;
54
55    /// Returns the ID of the device containing the file.
56    fn dev(&self) -> u64;
57
58    /// Returns the inode number.
59    fn ino(&self) -> u64;
60
61    /// Returns the rights applied to this file.
62    fn mode(&self) -> u32;
63
64    /// Returns the number of hard links pointing to this file.
65    fn nlink(&self) -> u64;
66
67    /// Returns the user ID of the owner of this file.
68    fn uid(&self) -> u32;
69
70    /// Returns the group ID of the owner of this file.
71    fn gid(&self) -> u32;
72
73    /// Returns the device ID of this file (if it is a special one).
74    fn rdev(&self) -> u64;
75
76    /// Returns the total size of this file in bytes.
77    fn size(&self) -> u64;
78
79    /// Returns the last access time of the file, in seconds since Unix Epoch.
80    fn atime(&self) -> i64;
81
82    /// Returns the last access time of the file, in nanoseconds since atime.
83    fn atime_nsec(&self) -> i64;
84
85    /// Returns the last modification time of the file, in seconds since Unix Epoch.
86    fn mtime(&self) -> i64;
87
88    /// Returns the last modification time of the file, in nanoseconds since mtime.
89    fn mtime_nsec(&self) -> i64;
90
91    /// Returns the last status change time of the file, in seconds since Unix Epoch.
92    fn ctime(&self) -> i64;
93
94    /// Returns the last status change time of the file, in nanoseconds since ctime.
95    fn ctime_nsec(&self) -> i64;
96
97    /// Returns the block size for filesystem I/O.
98    fn blksize(&self) -> u64;
99
100    /// Returns the number of blocks allocated to the file, in 512-byte units.
101    ///
102    /// Please note that this may be smaller than st_size / 512 when the file has holes.
103    fn blocks(&self) -> u64;
104}
105
106impl MetaDataExt for MemoryMetaData {
107    fn accessed(&self) -> Result<SystemTime> {
108        Ok(self.create_at)
109    }
110
111    #[inline]
112    fn created(&self) -> Result<SystemTime> {
113        Ok(self.create_at)
114    }
115
116    fn is_file(&self) -> bool {
117        false
118    }
119
120    #[cfg(feature = "nightly")]
121    fn is_symlink(&self) -> bool {
122        false
123    }
124
125    #[inline]
126    fn len(&self) -> u64 {
127        self.size
128    }
129
130    fn modified(&self) -> Result<SystemTime> {
131        Ok(self.create_at)
132    }
133
134    fn dev(&self) -> u64 {
135        0
136    }
137
138    fn ino(&self) -> u64 {
139        0
140    }
141
142    fn mode(&self) -> u32 {
143        0
144    }
145
146    fn nlink(&self) -> u64 {
147        0
148    }
149
150    fn uid(&self) -> u32 {
151        0
152    }
153
154    fn gid(&self) -> u32 {
155        0
156    }
157
158    fn rdev(&self) -> u64 {
159        0
160    }
161
162    fn size(&self) -> u64 {
163        self.size
164    }
165
166    fn atime(&self) -> i64 {
167        self.create_at.duration_since(UNIX_EPOCH).unwrap().as_secs() as i64
168    }
169
170    fn atime_nsec(&self) -> i64 {
171        self.create_at.duration_since(UNIX_EPOCH).unwrap().as_nanos() as i64
172    }
173
174    fn mtime(&self) -> i64 {
175        self.create_at.duration_since(UNIX_EPOCH).unwrap().as_secs() as i64
176    }
177
178    fn mtime_nsec(&self) -> i64 {
179        self.create_at.duration_since(UNIX_EPOCH).unwrap().as_nanos() as i64
180    }
181
182    fn ctime(&self) -> i64 {
183        self.create_at.duration_since(UNIX_EPOCH).unwrap().as_secs() as i64
184    }
185
186    fn ctime_nsec(&self) -> i64 {
187        self.create_at.duration_since(UNIX_EPOCH).unwrap().as_nanos() as i64
188    }
189
190    fn blksize(&self) -> u64 {
191        0
192    }
193
194    fn blocks(&self) -> u64 {
195        0
196    }
197}
198
199impl MetaDataExt for DiskMetaData {
200    fn accessed(&self) -> Result<SystemTime> {
201        self.inner.accessed().map_err(|e| Error::new(ErrorKind::IO, e))
202    }
203
204    fn created(&self) -> Result<SystemTime> {
205        self.inner.created().map_err(|e| Error::new(ErrorKind::IO, e))
206    }
207
208    fn is_file(&self) -> bool {
209        self.inner.is_file()
210    }
211
212    #[cfg(feature = "nightly")]
213    #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))]
214    fn is_symlink(&self) -> bool {
215        self.inner.is_symlink()
216    }
217
218    fn len(&self) -> u64 {
219        self.inner.len()
220    }
221
222    fn modified(&self) -> Result<SystemTime> {
223        self.inner.modified().map_err(|e| Error::new(ErrorKind::IO, e))
224    }
225
226    fn dev(&self) -> u64 {
227        self.inner.dev()
228    }
229
230    fn ino(&self) -> u64 {
231        self.inner.ino()
232    }
233
234    fn mode(&self) -> u32 {
235        self.inner.mode()
236    }
237
238    fn nlink(&self) -> u64 {
239        self.inner.nlink()
240    }
241
242    fn uid(&self) -> u32 {
243        self.inner.uid()
244    }
245
246    fn gid(&self) -> u32 {
247        self.inner.gid()
248    }
249
250    fn rdev(&self) -> u64 {
251        self.inner.rdev()
252    }
253
254    fn size(&self) -> u64 {
255        self.inner.size()
256    }
257
258    fn atime(&self) -> i64 {
259        self.inner.atime()
260    }
261
262    fn atime_nsec(&self) -> i64 {
263        self.inner.atime_nsec()
264    }
265
266    fn mtime(&self) -> i64 {
267        self.inner.mtime()
268    }
269
270    fn mtime_nsec(&self) -> i64 {
271        self.inner.mtime_nsec()
272    }
273
274    fn ctime(&self) -> i64 {
275        self.inner.ctime()
276    }
277
278    fn ctime_nsec(&self) -> i64 {
279        self.inner.ctime_nsec()
280    }
281
282    fn blksize(&self) -> u64 {
283        self.inner.blksize()
284    }
285
286    fn blocks(&self) -> u64 {
287        self.inner.blocks()
288    }
289}
290
291impl MetaDataExt for EmptyMetaData {
292    fn accessed(&self) -> Result<SystemTime> {
293        Ok(UNIX_EPOCH)
294    }
295
296    fn created(&self) -> Result<SystemTime> {
297        Ok(UNIX_EPOCH)
298    }
299
300    fn is_file(&self) -> bool {
301        false
302    }
303
304    #[cfg(feature = "nightly")]
305    #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))]
306    fn is_symlink(&self) -> bool {
307        false
308    }
309
310    fn len(&self) -> u64 {
311        0
312    }
313
314    fn modified(&self) -> Result<SystemTime> {
315        Ok(UNIX_EPOCH)
316    }
317
318    fn dev(&self) -> u64 {
319        0
320    }
321
322    fn ino(&self) -> u64 {
323        0
324    }
325
326    fn mode(&self) -> u32 {
327        0
328    }
329
330    fn nlink(&self) -> u64 {
331        0
332    }
333
334    fn uid(&self) -> u32 {
335        0
336    }
337
338    fn gid(&self) -> u32 {
339        0
340    }
341
342    fn rdev(&self) -> u64 {
343        0
344    }
345
346    fn size(&self) -> u64 {
347        0 
348    }
349
350    fn atime(&self) -> i64 {
351        0
352    }
353
354    fn atime_nsec(&self) -> i64 {
355        0
356    }
357
358    fn mtime(&self) -> i64 {
359        0
360    }
361
362    fn mtime_nsec(&self) -> i64 {
363        0
364    }
365
366    fn ctime(&self) -> i64 {
367        0
368    }
369
370    fn ctime_nsec(&self) -> i64 {
371        0
372    }
373
374    fn blksize(&self) -> u64 {
375        0
376    }
377
378    fn blocks(&self) -> u64 {
379        0
380    }
381}
382
383impl MetaDataExt for MetaData {
384    fn accessed(&self) -> std::result::Result<SystemTime, Error> {
385        self.inner.accessed()
386    }
387
388    fn created(&self) -> std::result::Result<SystemTime, Error> {
389        self.inner.created()
390    }
391
392    fn is_file(&self) -> bool {
393        self.inner.is_file()
394    }
395
396    #[cfg(feature = "nightly")]
397    #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))]
398    fn is_symlink(&self) -> bool {
399        self.inner.is_symlink()
400    }
401
402    fn len(&self) -> u64 {
403        self.inner.len()
404    }
405
406    fn modified(&self) -> std::result::Result<SystemTime, Error> {
407        self.inner.modified()
408    }
409
410    fn dev(&self) -> u64 {
411        self.inner.dev()
412    }
413
414    fn ino(&self) -> u64 {
415        self.inner.ino()
416    }
417
418    fn mode(&self) -> u32 {
419        self.inner.mode()
420    }
421
422    fn nlink(&self) -> u64 {
423        self.inner.nlink()
424    }
425
426    fn uid(&self) -> u32 {
427        self.inner.uid()
428    }
429
430    fn gid(&self) -> u32 {
431        self.inner.gid()
432    }
433
434    fn rdev(&self) -> u64 {
435        self.inner.rdev()
436    }
437
438    fn size(&self) -> u64 {
439        self.inner.size()
440    }
441
442    fn atime(&self) -> i64 {
443        self.inner.atime()
444    }
445
446    fn atime_nsec(&self) -> i64 {
447        self.inner.atime_nsec()
448    }
449
450    fn mtime(&self) -> i64 {
451        self.inner.mtime()
452    }
453
454    fn mtime_nsec(&self) -> i64 {
455        self.inner.mtime_nsec()
456    }
457
458    fn ctime(&self) -> i64 {
459        self.inner.ctime()
460    }
461
462    fn ctime_nsec(&self) -> i64 {
463        self.inner.ctime_nsec()
464    }
465
466    fn blksize(&self) -> u64 {
467        self.inner.blksize()
468    }
469
470    fn blocks(&self) -> u64 {
471        self.inner.blocks()
472    }
473}
474
475
476#[cfg(test)]
477mod tests {
478    use bytes::Bytes;
479    use crate::empty::EmptyMmapFile;
480    use crate::{MmapFileExt, MmapFileMutExt, Options};
481    use crate::raw::MemoryMmapFile;
482    use crate::tests::get_random_filename;
483    use super::*;
484
485    macro_rules! metadata_test {
486        ($expr: expr) => {
487            let meta = $expr;
488            meta.accessed().unwrap();
489            meta.created().unwrap();
490            assert!(meta.is_file());
491            #[cfg(feature = "nightly")]
492            assert!(!meta.is_symlink());
493            assert_eq!(meta.len(), "Hello, fmmap!".len() as u64);
494            assert_eq!(meta.size(), "Hello, fmmap!".len() as u64);
495            meta.modified().unwrap();
496            meta.dev();
497            meta.ino();
498            meta.mode();
499            meta.nlink();
500            meta.uid();
501            meta.gid();
502            meta.rdev();
503            meta.size();
504            meta.atime();
505            meta.atime_nsec();
506            meta.mtime();
507            meta.mtime_nsec();
508            meta.ctime();
509            meta.ctime_nsec();
510            meta.blocks();
511            meta.blksize();
512        };
513    }
514
515    #[test]
516    fn test_metadata() {
517        let mut file = Options::new()
518            .max_size("Hello, fmmap!".len() as u64)
519            .create_mmap_file_mut(get_random_filename())
520            .unwrap();
521        file.set_remove_on_drop(true);
522        file.write_all("Hello, fmmap!".as_bytes(), 0).unwrap();
523        metadata_test!(file.metadata().unwrap());
524
525        // let meta = file.metadata().unwrap();
526        // meta.accessed().unwrap();
527        // meta.created().unwrap();
528        // assert!(meta.is_file());
529        // #[cfg(feature = "nightly")]
530        // assert!(!meta.is_symlink());
531        // assert_eq!(meta.len(), "Hello, fmmap!".len() as u64);
532        // assert_eq!(meta.size(), "Hello, fmmap!".len() as u64);
533        // meta.modified().unwrap();
534        // meta.dev();
535        // meta.ino();
536        // meta.mode();
537        // meta.nlink();
538        // meta.uid();
539        // meta.gid();
540        // meta.rdev();
541        // meta.size();
542        // meta.atime();
543        // meta.atime_nsec();
544        // meta.mtime();
545        // meta.mtime_nsec();
546        // meta.ctime();
547        // meta.ctime_nsec();
548        // meta.blocks();
549        // meta.blksize();
550    }
551
552    #[cfg(feature = "tokio")]
553    #[tokio::test]
554    async fn test_async_metadata() {
555        use crate::tokio::{AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncOptions};
556        let mut file = AsyncOptions::new()
557            .max_size("Hello, fmmap!".len() as u64)
558            .create_mmap_file_mut(get_random_filename())
559            .await
560            .unwrap();
561        file.set_remove_on_drop(true);
562        file.write_all("Hello, fmmap!".as_bytes(), 0).unwrap();
563        metadata_test!(file.metadata().await.unwrap());
564
565        // let meta = file.metadata().await.unwrap();
566        // meta.accessed().unwrap();
567        // meta.created().unwrap();
568        // assert!(meta.is_file());
569        // #[cfg(feature = "nightly")]
570        // assert!(!meta.is_symlink());
571        // assert_eq!(meta.len(), "Hello, fmmap!".len() as u64);
572        // assert_eq!(meta.size(), "Hello, fmmap!".len() as u64);
573        // meta.modified().unwrap();
574        // meta.dev();
575        // meta.ino();
576        // meta.mode();
577        // meta.nlink();
578        // meta.uid();
579        // meta.gid();
580        // meta.rdev();
581        // meta.size();
582        // meta.atime();
583        // meta.atime_nsec();
584        // meta.mtime();
585        // meta.mtime_nsec();
586        // meta.ctime();
587        // meta.ctime_nsec();
588        // meta.blocks();
589        // meta.blksize();
590    }
591
592    #[test]
593    fn test_memory_metadata() {
594        let file = MemoryMmapFile::new("test.mem", Bytes::from("Hello, fmmap!"));
595        let meta = file.metadata().unwrap();
596
597        assert!(!meta.is_file());
598        #[cfg(feature = "nightly")]
599        assert!(!meta.is_symlink());
600        assert_eq!(meta.len(), "Hello, fmmap!".len() as u64);
601        assert_eq!(meta.size(), "Hello, fmmap!".len() as u64);
602        assert!(meta.modified().unwrap() == meta.created().unwrap() && meta.created().unwrap() == meta.accessed().unwrap());
603        assert!(meta.atime() == meta.mtime() && meta.mtime() == meta.ctime());
604        assert!(meta.atime_nsec() == meta.mtime_nsec() && meta.mtime_nsec() == meta.ctime_nsec());
605        assert_eq!(meta.dev(), 0);
606        assert_eq!(meta.ino(), 0);
607        assert_eq!(meta.mode(), 0);
608        assert_eq!(meta.nlink(), 0);
609        assert_eq!(meta.uid(), 0);
610        assert_eq!(meta.gid(), 0);
611        assert_eq!(meta.rdev(), 0);
612        assert_eq!(meta.blocks(), 0);
613        assert_eq!(meta.blksize(), 0);
614    }
615
616    #[test]
617    fn test_empty_metadata() {
618        let file = EmptyMmapFile::default();
619        let meta = file.metadata().unwrap();
620
621        assert_eq!(meta.accessed().unwrap(), UNIX_EPOCH);
622        assert_eq!(meta.created().unwrap(), UNIX_EPOCH);
623        assert!(!meta.is_file());
624        #[cfg(feature = "nightly")]
625        assert!(!meta.is_symlink());
626        assert_eq!(meta.len(), 0);
627        assert_eq!(meta.modified().unwrap(), UNIX_EPOCH);
628        assert_eq!(meta.dev(), 0);
629        assert_eq!(meta.ino(), 0);
630        assert_eq!(meta.mode(), 0);
631        assert_eq!(meta.nlink(), 0);
632        assert_eq!(meta.uid(), 0);
633        assert_eq!(meta.gid(), 0);
634        assert_eq!(meta.rdev(), 0);
635        assert_eq!(meta.size(), 0);
636        assert_eq!(meta.atime(), 0);
637        assert_eq!(meta.atime_nsec(), 0);
638        assert_eq!(meta.mtime(), 0);
639        assert_eq!(meta.mtime_nsec(), 0);
640        assert_eq!(meta.ctime(), 0);
641        assert_eq!(meta.ctime_nsec(), 0);
642        assert_eq!(meta.blocks(), 0);
643        assert_eq!(meta.blksize(), 0);
644    }
645}