monoio/fs/metadata/
mod.rs1mod unix;
2mod windows;
3
4use std::{os::unix::fs::MetadataExt, path::Path, time::SystemTime};
5
6use super::{file_type::FileType, permissions::Permissions};
7use crate::driver::op::Op;
8
9pub async fn metadata<P: AsRef<Path>>(path: P) -> std::io::Result<Metadata> {
42 #[cfg(target_os = "linux")]
43 let flags = libc::AT_STATX_SYNC_AS_STAT;
44
45 #[cfg(target_os = "linux")]
46 let op = Op::statx_using_path(path, flags)?;
47
48 #[cfg(any(target_os = "macos", target_os = "freebsd"))]
49 let op = Op::statx_using_path(path, true)?;
50
51 op.result().await.map(FileAttr::from).map(Metadata)
52}
53
54pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> std::io::Result<Metadata> {
82 #[cfg(target_os = "linux")]
83 let flags = libc::AT_STATX_SYNC_AS_STAT | libc::AT_SYMLINK_NOFOLLOW;
84
85 #[cfg(target_os = "linux")]
86 let op = Op::statx_using_path(path, flags)?;
87
88 #[cfg(any(target_os = "macos", target_os = "freebsd"))]
89 let op = Op::statx_using_path(path, false)?;
90
91 op.result().await.map(FileAttr::from).map(Metadata)
92}
93
94#[cfg(unix)]
95pub(crate) use unix::FileAttr;
96
97#[cfg(unix)]
104pub struct Metadata(pub(crate) FileAttr);
105
106impl Metadata {
107 pub fn is_dir(&self) -> bool {
123 self.0.stat.st_mode & libc::S_IFMT == libc::S_IFDIR
124 }
125
126 pub fn is_file(&self) -> bool {
142 self.0.stat.st_mode & libc::S_IFMT == libc::S_IFREG
143 }
144
145 pub fn is_symlink(&self) -> bool {
161 self.0.stat.st_mode & libc::S_IFMT == libc::S_IFLNK
162 }
163
164 #[allow(clippy::len_without_is_empty)]
180 pub fn len(&self) -> u64 {
181 self.0.size()
182 }
183
184 pub fn modified(&self) -> std::io::Result<SystemTime> {
199 #[cfg(not(musl_v1_2_3))]
200 {
201 let mtime = self.0.stat.st_mtime;
202 let mtime_nsec = self.0.stat.st_mtime_nsec as u32;
203
204 Ok(SystemTime::UNIX_EPOCH + std::time::Duration::new(mtime as u64, mtime_nsec))
205 }
206 #[cfg(musl_v1_2_3)]
207 {
208 let mtime = self.0.stat.st_mtim;
209 let mtime_nsec = mtime.tv_nsec as u32;
210
211 Ok(SystemTime::UNIX_EPOCH + std::time::Duration::new(mtime.tv_sec as u64, mtime_nsec))
212 }
213 }
214
215 pub fn accessed(&self) -> std::io::Result<SystemTime> {
231 #[cfg(not(musl_v1_2_3))]
232 {
233 let atime = self.0.stat.st_atime;
234 let atime_nsec = self.0.stat.st_atime_nsec as u32;
235
236 Ok(SystemTime::UNIX_EPOCH + std::time::Duration::new(atime as u64, atime_nsec))
237 }
238 #[cfg(musl_v1_2_3)]
239 {
240 let atime = self.0.stat.st_atim;
241 let atime_nsec = atime.tv_nsec as u32;
242
243 Ok(SystemTime::UNIX_EPOCH + std::time::Duration::new(atime.tv_sec as u64, atime_nsec))
244 }
245 }
246
247 #[cfg(target_os = "linux")]
263 pub fn created(&self) -> std::io::Result<SystemTime> {
264 if let Some(extra) = self.0.statx_extra_fields.as_ref() {
265 return if extra.stx_mask & libc::STATX_BTIME != 0 {
266 let btime = extra.stx_btime.tv_sec;
267 let btime_nsec = extra.stx_btime.tv_nsec;
268
269 Ok(SystemTime::UNIX_EPOCH + std::time::Duration::new(btime as u64, btime_nsec))
270 } else {
271 Err(std::io::Error::other("Creation time is not available"))
272 };
273 }
274
275 Err(std::io::Error::other("Creation time is not available"))
276 }
277
278 #[cfg(unix)]
294 pub fn permissions(&self) -> Permissions {
295 use super::permissions::Permissions;
296
297 Permissions(self.0.perm())
298 }
299
300 #[cfg(unix)]
316 pub fn file_type(&self) -> FileType {
317 self.0.file_type()
318 }
319}
320
321impl std::fmt::Debug for Metadata {
322 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
323 let mut debug = f.debug_struct("Metadata");
324 debug.field("permissions", &self.permissions());
326 debug.field("len", &self.len());
327 if let Ok(modified) = self.modified() {
328 debug.field("modified", &modified);
329 }
330 if let Ok(accessed) = self.accessed() {
331 debug.field("accessed", &accessed);
332 }
333 #[cfg(target_os = "linux")]
334 if let Ok(created) = self.created() {
335 debug.field("created", &created);
336 }
337 debug.finish_non_exhaustive()
338 }
339}
340
341#[cfg(all(target_os = "linux", not(target_pointer_width = "32")))]
342impl MetadataExt for Metadata {
343 fn dev(&self) -> u64 {
344 self.0.stat.st_dev
345 }
346
347 fn ino(&self) -> u64 {
348 self.0.stat.st_ino
349 }
350
351 fn mode(&self) -> u32 {
352 self.0.stat.st_mode
353 }
354
355 #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
356 fn nlink(&self) -> u64 {
357 self.0.stat.st_nlink.into()
358 }
359
360 #[allow(clippy::useless_conversion)]
362 #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
363 fn nlink(&self) -> u64 {
364 self.0.stat.st_nlink.into()
365 }
366
367 fn uid(&self) -> u32 {
368 self.0.stat.st_uid
369 }
370
371 fn gid(&self) -> u32 {
372 self.0.stat.st_gid
373 }
374
375 fn rdev(&self) -> u64 {
376 self.0.stat.st_rdev
377 }
378
379 fn size(&self) -> u64 {
380 self.0.stat.st_size as u64
381 }
382
383 fn atime(&self) -> i64 {
384 #[cfg(not(musl_v1_2_3))]
385 let atime = self.0.stat.st_atime;
386 #[cfg(musl_v1_2_3)]
387 let atime = self.0.stat.st_atim.tv_sec;
388
389 atime
390 }
391
392 fn atime_nsec(&self) -> i64 {
393 #[cfg(not(musl_v1_2_3))]
394 let atime_nsec = self.0.stat.st_atime_nsec;
395 #[cfg(musl_v1_2_3)]
396 let atime_nsec = self.0.stat.st_atim.tv_nsec;
397
398 atime_nsec
399 }
400
401 fn mtime(&self) -> i64 {
402 #[cfg(not(musl_v1_2_3))]
403 let mtime = self.0.stat.st_mtime;
404 #[cfg(musl_v1_2_3)]
405 let mtime = self.0.stat.st_mtim.tv_sec;
406
407 mtime
408 }
409
410 fn mtime_nsec(&self) -> i64 {
411 #[cfg(not(musl_v1_2_3))]
412 let mtime_nsec = self.0.stat.st_mtime_nsec;
413 #[cfg(musl_v1_2_3)]
414 let mtime_nsec = self.0.stat.st_mtim.tv_nsec;
415
416 mtime_nsec
417 }
418
419 fn ctime(&self) -> i64 {
420 #[cfg(not(musl_v1_2_3))]
421 let ctime = self.0.stat.st_ctime;
422 #[cfg(musl_v1_2_3)]
423 let ctime = self.0.stat.st_ctim.tv_sec;
424
425 ctime
426 }
427
428 fn ctime_nsec(&self) -> i64 {
429 #[cfg(not(musl_v1_2_3))]
430 let ctime_nsec = self.0.stat.st_ctime_nsec;
431 #[cfg(musl_v1_2_3)]
432 let ctime_nsec = self.0.stat.st_ctim.tv_nsec;
433
434 ctime_nsec
435 }
436
437 fn blksize(&self) -> u64 {
438 self.0.stat.st_blksize as u64
439 }
440
441 fn blocks(&self) -> u64 {
442 self.0.stat.st_blocks as u64
443 }
444}
445
446#[cfg(all(
447 any(target_os = "macos", target_os = "freebsd"),
448 not(target_pointer_width = "32")
449))]
450impl MetadataExt for Metadata {
451 fn dev(&self) -> u64 {
452 self.0.stat.st_dev as u64
453 }
454
455 fn ino(&self) -> u64 {
456 self.0.stat.st_ino.into()
457 }
458
459 fn mode(&self) -> u32 {
460 self.0.stat.st_mode as u32
461 }
462
463 fn nlink(&self) -> u64 {
464 self.0.stat.st_nlink.into()
465 }
466
467 fn uid(&self) -> u32 {
468 self.0.stat.st_uid
469 }
470
471 fn gid(&self) -> u32 {
472 self.0.stat.st_gid
473 }
474
475 fn rdev(&self) -> u64 {
476 self.0.stat.st_rdev as u64
477 }
478
479 fn size(&self) -> u64 {
480 self.0.stat.st_size as u64
481 }
482
483 fn atime(&self) -> i64 {
484 self.0.stat.st_atime
485 }
486
487 fn atime_nsec(&self) -> i64 {
488 self.0.stat.st_atime_nsec
489 }
490
491 fn mtime(&self) -> i64 {
492 self.0.stat.st_mtime
493 }
494
495 fn mtime_nsec(&self) -> i64 {
496 self.0.stat.st_mtime_nsec
497 }
498
499 fn ctime(&self) -> i64 {
500 self.0.stat.st_ctime
501 }
502
503 fn ctime_nsec(&self) -> i64 {
504 self.0.stat.st_ctime_nsec
505 }
506
507 fn blksize(&self) -> u64 {
508 self.0.stat.st_blksize as u64
509 }
510
511 fn blocks(&self) -> u64 {
512 self.0.stat.st_blocks as u64
513 }
514}
515
516#[cfg(all(unix, target_pointer_width = "32"))]
517impl MetadataExt for Metadata {
518 fn dev(&self) -> u64 {
519 self.0.stat.st_dev.into()
520 }
521
522 fn ino(&self) -> u64 {
523 self.0.stat.st_ino.into()
524 }
525
526 fn mode(&self) -> u32 {
527 self.0.stat.st_mode
528 }
529
530 fn nlink(&self) -> u64 {
531 self.0.stat.st_nlink.into()
532 }
533
534 fn uid(&self) -> u32 {
535 self.0.stat.st_uid
536 }
537
538 fn gid(&self) -> u32 {
539 self.0.stat.st_gid
540 }
541
542 fn rdev(&self) -> u64 {
543 self.0.stat.st_rdev.into()
544 }
545
546 fn size(&self) -> u64 {
547 self.0.stat.st_size as u64
548 }
549
550 fn atime(&self) -> i64 {
551 #[cfg(not(musl_v1_2_3))]
552 let atime = self.0.stat.st_atime;
553 #[cfg(musl_v1_2_3)]
554 let atime = self.0.stat.st_atim.tv_sec;
555
556 atime.into()
557 }
558
559 fn atime_nsec(&self) -> i64 {
560 #[cfg(not(musl_v1_2_3))]
561 let atime_nsec = self.0.stat.st_atime_nsec;
562 #[cfg(musl_v1_2_3)]
563 let atime_nsec = self.0.stat.st_atim.tv_nsec;
564
565 atime_nsec.into()
566 }
567
568 fn mtime(&self) -> i64 {
569 #[cfg(not(musl_v1_2_3))]
570 let mtime = self.0.stat.st_mtime;
571 #[cfg(musl_v1_2_3)]
572 let mtime = self.0.stat.st_mtim.tv_sec;
573
574 mtime.into()
575 }
576
577 fn mtime_nsec(&self) -> i64 {
578 #[cfg(not(musl_v1_2_3))]
579 let mtime_nsec = self.0.stat.st_mtime_nsec;
580 #[cfg(musl_v1_2_3)]
581 let mtime_nsec = self.0.stat.st_mtim.tv_nsec;
582
583 mtime_nsec.into()
584 }
585
586 fn ctime(&self) -> i64 {
587 #[cfg(not(musl_v1_2_3))]
588 let ctime = self.0.stat.st_ctime;
589 #[cfg(musl_v1_2_3)]
590 let ctime = self.0.stat.st_ctim.tv_sec;
591
592 ctime.into()
593 }
594
595 fn ctime_nsec(&self) -> i64 {
596 #[cfg(not(musl_v1_2_3))]
597 let ctime_nsec = self.0.stat.st_ctime_nsec;
598 #[cfg(musl_v1_2_3)]
599 let ctime_nsec = self.0.stat.st_ctim.tv_nsec;
600
601 ctime_nsec.into()
602 }
603
604 fn blksize(&self) -> u64 {
605 self.0.stat.st_blksize as u64
606 }
607
608 fn blocks(&self) -> u64 {
609 self.0.stat.st_blocks as u64
610 }
611}