linux_stat/
lib.rs

1#![cfg(any(target_os = "linux", target_os = "android"))]
2#![cfg_attr(not(feature = "std"), no_std)]
3
4#[cfg(feature = "std")]
5#[doc(no_inline)]
6pub use std::os::unix::io::RawFd;
7#[cfg(all(not(feature = "std"), not(target_arch = "loongarch64")))]
8/// Raw file descriptor.
9pub type RawFd = cty::c_int;
10#[cfg(all(not(feature = "std"), target_arch = "loongarch64"))]
11/// Raw file descriptor.
12pub type RawFd = core::ffi::c_int;
13
14#[cfg(not(extern_cstr))]
15pub use core::ffi::CStr;
16#[cfg(extern_cstr)]
17pub use cstr_core::CStr;
18
19#[cfg(feature = "std")]
20pub use std::path::Path;
21#[cfg(not(feature = "std"))]
22pub use CStr as Path;
23
24pub use linux_syscalls::Errno;
25
26mod dev;
27pub mod raw;
28
29use core::fmt;
30
31use linux_syscalls::bitflags;
32
33pub use self::dev::*;
34
35/// Special file descriptor that represent the current directory.
36pub const CURRENT_DIRECTORY: RawFd = linux_raw_sys::general::AT_FDCWD;
37
38bitflags! {
39    /// Flags for `fstatat()`.
40    #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
41    pub enum StatAtFlags: u32 {
42        /// If pathname is an empty string, operate on the file referred to
43        /// by dirfd (which may have been obtained using the open(2) O_PATH
44        /// flag).  In this case, dirfd can refer to any type of file, not
45        /// just a directory, and the behavior of fstatat() is similar to
46        /// that of fstat().  If dirfd is AT_FDCWD, the call operates on the
47        /// current working directory.
48        EMPTY_PATH = linux_raw_sys::general::AT_EMPTY_PATH,
49        /// Don't automount the terminal ("basename") component of pathname.
50        /// Since Linux 3.1 this flag is ignored.  Since Linux 4.11 this
51        /// flag is implied.
52        NO_AUTOMOUNT = linux_raw_sys::general::AT_NO_AUTOMOUNT,
53        /// If pathname is a symbolic link, do not dereference it: instead
54        /// return information about the link itself, like lstat().  (By
55        /// default, fstatat() dereferences symbolic links, like stat().)
56        SYMLINK_NOFOLLOW = linux_raw_sys::general::AT_SYMLINK_NOFOLLOW,
57    }
58}
59
60bitflags! {
61    /// Entity (owner, group or other) permissions representation.
62    #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
63    pub enum ModePermission : u8 {
64        /// Read permission.
65        READ = 0o4,
66        /// Write permission.
67        WRITE = 0o2,
68        /// Exec permission.
69        EXEC = 0o1,
70    }
71}
72
73/// File permissions representation.
74#[repr(transparent)]
75#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
76pub struct Mode(pub(crate) u16);
77
78impl Mode {
79    /// Returns owner's file permissions.
80    #[inline]
81    pub const fn owner(&self) -> ModePermission {
82        ModePermission::from_bits(((self.0 >> 6) & 0o7) as u8)
83    }
84
85    /// Returns group's file permissions.
86    #[inline]
87    pub const fn group(&self) -> ModePermission {
88        ModePermission::from_bits(((self.0 >> 3) & 0o7) as u8)
89    }
90
91    /// Returns other's file permissions.
92    #[inline]
93    pub const fn other(&self) -> ModePermission {
94        ModePermission::from_bits((self.0 & 0o7) as u8)
95    }
96
97    /// Returns true if suid is set on file.
98    #[inline]
99    pub const fn suid(&self) -> bool {
100        const S_ISUID: u16 = linux_raw_sys::general::S_ISUID as u16;
101
102        self.0 & S_ISUID == S_ISUID
103    }
104
105    /// Returns true if sgid is set on file.
106    #[inline]
107    pub const fn sgid(&self) -> bool {
108        const S_ISGID: u16 = linux_raw_sys::general::S_ISGID as u16;
109
110        self.0 & S_ISGID == S_ISGID
111    }
112
113    /// Returns true if svtx is set on file.
114    #[inline]
115    pub const fn svtx(&self) -> bool {
116        const S_ISVTX: u16 = linux_raw_sys::general::S_ISVTX as u16;
117
118        self.0 & S_ISVTX == S_ISVTX
119    }
120
121    /// Returns [Mode] from a u16.
122    #[inline]
123    pub const fn from_u16(value: u16) -> Self {
124        Self(value)
125    }
126
127    /// Returns the underlining u16.
128    #[inline]
129    pub const fn as_u16(&self) -> u16 {
130        self.0
131    }
132}
133
134impl From<u16> for Mode {
135    #[inline]
136    fn from(value: u16) -> Self {
137        Self(value)
138    }
139}
140
141impl fmt::Debug for Mode {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        fn format_c(f: &mut fmt::Formatter<'_>, b: bool, c: char) -> fmt::Result {
144            if b {
145                fmt::Display::fmt(&c, f)
146            } else {
147                fmt::Display::fmt(&'-', f)
148            }
149        }
150
151        fn format_perm(
152            f: &mut fmt::Formatter<'_>,
153            p: ModePermission,
154            x: Option<char>,
155        ) -> fmt::Result {
156            format_c(f, p.contains(ModePermission::READ), 'r')?;
157            format_c(f, p.contains(ModePermission::WRITE), 'w')?;
158            if let Some(x) = x {
159                fmt::Display::fmt(&x, f)
160            } else {
161                format_c(f, p.contains(ModePermission::EXEC), 'x')
162            }
163        }
164
165        write!(f, "Mode(")?;
166        format_perm(f, self.owner(), if self.suid() { Some('s') } else { None })?;
167        format_perm(f, self.group(), if self.sgid() { Some('s') } else { None })?;
168        format_perm(f, self.other(), None)?;
169        write!(f, ")")
170    }
171}
172
173/// Timestamp representation.
174#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
175pub struct Timestamp {
176    secs: i64,
177    nsecs: u32,
178}
179
180impl Timestamp {
181    /// Returns the seconds part.
182    #[inline]
183    pub const fn seconds(&self) -> i64 {
184        self.secs
185    }
186
187    /// Alias for `Self::seconds()`
188    #[inline]
189    pub const fn secs(&self) -> i64 {
190        self.secs
191    }
192
193    /// Returns the nanoseconds part.
194    #[inline]
195    pub const fn nanoseconds(&self) -> u32 {
196        self.nsecs
197    }
198
199    /// Alias for `Self::nanoseconds()`
200    #[inline]
201    pub const fn nanosecs(&self) -> u32 {
202        self.nsecs
203    }
204
205    /// Alias for `Self::nanoseconds()`
206    #[inline]
207    pub const fn nsecs(&self) -> u32 {
208        self.nsecs
209    }
210}
211
212/// File type representation.
213#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
214pub enum FileType {
215    /// Block file type.
216    Block,
217    /// Character file type.
218    Character,
219    /// Directory file type.
220    Directory,
221    /// FIFO file type.
222    Fifo,
223    /// Link file type.
224    Link,
225    /// Regular file type.
226    Regular,
227    /// Socket file type.
228    Socket,
229    /// Unknown file type.
230    Unknown,
231}
232
233impl FileType {
234    pub fn as_u16(&self) -> u16 {
235        use linux_raw_sys::general::{
236            S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFREG, S_IFSOCK,
237        };
238
239        (match *self {
240            FileType::Socket => S_IFSOCK,
241            FileType::Link => S_IFLNK,
242            FileType::Regular => S_IFREG,
243            FileType::Block => S_IFBLK,
244            FileType::Directory => S_IFDIR,
245            FileType::Character => S_IFCHR,
246            FileType::Fifo => S_IFIFO,
247            FileType::Unknown => 0,
248        }) as u16
249    }
250}
251
252#[cfg(all(not(feature = "linux_4_11"), not(target_arch = "loongarch64")))]
253static mut HAS_STATX: core::sync::atomic::AtomicU8 = core::sync::atomic::AtomicU8::new(2);
254
255/// Unified Stat structure.
256#[cfg(any(feature = "linux_4_11", target_arch = "loongarch64"))]
257pub type Stat = crate::raw::Statx;
258
259/// Unified Stat structure.
260#[cfg(all(not(feature = "linux_4_11"), not(target_arch = "loongarch64")))]
261#[derive(Clone, Copy)]
262pub enum Stat {
263    Stat64(crate::raw::stat),
264    Statx(crate::raw::Statx),
265}
266
267#[cfg(all(not(feature = "linux_4_11"), not(target_arch = "loongarch64")))]
268macro_rules! with_stat {
269    ($outer:expr, |$name:ident| $($tt:tt)+) => {
270        match $outer {
271            $crate::Stat::Stat64($name) => $($tt)+,
272            $crate::Stat::Statx($name) => $($tt)+,
273        }
274    };
275}
276
277#[cfg(all(not(feature = "linux_4_11"), not(target_arch = "loongarch64")))]
278impl Stat {
279    /// Returns the "preferred" block size for efficient filesystem I/O.
280    /// (Writing to a file in smaller chunks may cause an inefficient
281    /// read-modify-rewrite.)
282    #[inline]
283    pub const fn block_size(&self) -> i32 {
284        with_stat!(self, |s| s.block_size())
285    }
286
287    /// Returns the number of hard links on a file.
288    #[inline]
289    pub const fn nlink(&self) -> u32 {
290        with_stat!(self, |s| s.nlink())
291    }
292
293    /// Returns the user ID of the owner of the file.
294    #[inline]
295    pub const fn uid(&self) -> u32 {
296        with_stat!(self, |s| s.uid())
297    }
298
299    /// Returns the ID of the group owner of the file.
300    #[inline]
301    pub const fn gid(&self) -> u32 {
302        with_stat!(self, |s| s.gid())
303    }
304
305    /// Returns the file mode.
306    #[inline]
307    pub const fn mode(&self) -> Mode {
308        with_stat!(self, |s| s.mode())
309    }
310
311    /// Returns the file type.
312    pub const fn file_type(&self) -> FileType {
313        with_stat!(self, |s| s.file_type())
314    }
315
316    /// Returns true if file type is socket.
317    #[inline]
318    pub const fn is_socket(&self) -> bool {
319        with_stat!(self, |s| s.is_socket())
320    }
321
322    /// Returns true if file type is link.
323    #[inline]
324    pub const fn is_link(&self) -> bool {
325        with_stat!(self, |s| s.is_link())
326    }
327
328    /// Returns true if file type is regular.
329    #[inline]
330    pub const fn is_regular(&self) -> bool {
331        with_stat!(self, |s| s.is_regular())
332    }
333
334    /// Returns true if file type is block.
335    #[inline]
336    pub const fn is_block(&self) -> bool {
337        with_stat!(self, |s| s.is_block())
338    }
339
340    /// Returns true if file type is directory.
341    #[inline]
342    pub const fn is_directory(&self) -> bool {
343        with_stat!(self, |s| s.is_directory())
344    }
345
346    /// Alias for `Self::is_directory()`.
347    #[inline]
348    pub const fn is_dir(&self) -> bool {
349        with_stat!(self, |s| s.is_dir())
350    }
351
352    /// Returns true if file type is character.
353    #[inline]
354    pub const fn is_character(&self) -> bool {
355        with_stat!(self, |s| s.is_character())
356    }
357
358    /// Alias for `Self::is_character()`.
359    #[inline]
360    pub const fn is_char(&self) -> bool {
361        with_stat!(self, |s| s.is_char())
362    }
363
364    /// Returns true if file type is FIFO.
365    #[inline]
366    pub const fn is_fifo(&self) -> bool {
367        with_stat!(self, |s| s.is_fifo())
368    }
369
370    /// Returns the inode number of the file.
371    #[inline]
372    pub const fn inode(&self) -> u64 {
373        with_stat!(self, |s| s.inode())
374    }
375
376    /// Returns the size of the file (if it is a regular file or a symbolic
377    /// link) in bytes. The size of a symbolic link is the length of
378    /// the pathname it contains, without a terminating null byte.
379    #[inline]
380    pub const fn size(&self) -> i64 {
381        with_stat!(self, |s| s.size())
382    }
383
384    /// Returns the number of blocks allocated to the file on the medium, in
385    /// 512-byte units. (This may be smaller than stx_size/512 when the
386    /// file has holes.)
387    #[inline]
388    pub const fn blocks(&self) -> i64 {
389        with_stat!(self, |s| s.blocks())
390    }
391
392    /// Returns the file's last access timestamp.
393    #[inline]
394    pub const fn atime(&self) -> Timestamp {
395        with_stat!(self, |s| s.atime())
396    }
397
398    /// Returns the file's last status change timestamp.
399    #[inline]
400    pub const fn ctime(&self) -> Timestamp {
401        with_stat!(self, |s| s.ctime())
402    }
403
404    /// Returns the file's last modification timestamp.
405    #[inline]
406    pub const fn mtime(&self) -> Timestamp {
407        with_stat!(self, |s| s.mtime())
408    }
409
410    /// Returns the major device that this file (inode) represents if the file
411    /// is of block or character device type
412    #[inline]
413    pub const fn rdev_major(&self) -> u32 {
414        with_stat!(self, |s| s.rdev_major())
415    }
416
417    /// Returns the minor device that this file (inode) represents if the file
418    /// is of block or character device type
419    #[inline]
420    pub const fn rdev_minor(&self) -> u32 {
421        with_stat!(self, |s| s.rdev_minor())
422    }
423
424    /// Returns the device that this file (inode) represents if the file is of
425    /// block or character device type
426    #[inline]
427    pub const fn rdev(&self) -> Dev {
428        with_stat!(self, |s| s.rdev())
429    }
430
431    /// Returns the major device on which this file (inode) resides.
432    #[inline]
433    pub const fn dev_major(&self) -> u32 {
434        with_stat!(self, |s| s.dev_major())
435    }
436
437    /// Returns the minor device on which this file (inode) resides.
438    #[inline]
439    pub const fn dev_minor(&self) -> u32 {
440        with_stat!(self, |s| s.dev_minor())
441    }
442
443    /// Returns the device on which this file (inode) resides.
444    #[inline]
445    pub const fn dev(&self) -> Dev {
446        with_stat!(self, |s| s.dev())
447    }
448}
449
450#[cfg(all(not(feature = "linux_4_11"), not(target_arch = "loongarch64")))]
451impl fmt::Debug for Stat {
452    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453        with_stat!(self, |s| s.debug(f, "Stat"))
454    }
455}
456
457/// Returns an empty path representation.
458#[inline]
459pub fn empty_path() -> &'static Path {
460    #[cfg(feature = "std")]
461    let empty: &Path = Path::new("");
462    #[cfg(not(feature = "std"))]
463    let empty: &Path = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") };
464    empty
465}
466
467#[cfg(feature = "std")]
468#[inline(always)]
469pub(crate) fn run_with_cstr<P, T, F>(path: P, f: F) -> Result<T, Errno>
470where
471    P: AsRef<Path>,
472    F: FnOnce(&CStr) -> Result<T, Errno>,
473{
474    use core::mem::MaybeUninit;
475    #[cfg(extern_cstr)]
476    use cstr_core::CString;
477    #[cfg(not(extern_cstr))]
478    use std::ffi::CString;
479    use std::os::unix::ffi::OsStrExt;
480
481    #[cfg(not(target_os = "espidf"))]
482    const MAX_STACK_ALLOCATION: usize = 384;
483    #[cfg(target_os = "espidf")]
484    const MAX_STACK_ALLOCATION: usize = 32;
485
486    let path = path.as_ref().as_os_str().as_bytes();
487
488    if path.is_empty() {
489        return f(unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") });
490    }
491
492    if path.last().map(|&c| c == 0).unwrap_or(false) {
493        return f(CStr::from_bytes_with_nul(path).map_err(|_| Errno::ENOENT)?);
494    }
495
496    if path.len() >= MAX_STACK_ALLOCATION {
497        return CString::new(path).map_or(Err(Errno::ENOENT), |path| f(&path));
498    }
499
500    let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
501    let buf_ptr = buf.as_mut_ptr() as *mut u8;
502
503    unsafe {
504        core::ptr::copy_nonoverlapping(path.as_ptr(), buf_ptr, path.len());
505        buf_ptr.add(path.len()).write(0);
506    }
507
508    CStr::from_bytes_with_nul(unsafe { core::slice::from_raw_parts(buf_ptr, path.len() + 1) })
509        .map_or(Err(Errno::ENOENT), f)
510}
511
512#[cfg(not(feature = "std"))]
513#[inline(always)]
514pub(crate) fn run_with_cstr<P, T, F>(path: P, f: F) -> Result<T, Errno>
515where
516    P: AsRef<Path>,
517    F: FnOnce(&CStr) -> Result<T, Errno>,
518{
519    f(path.as_ref())
520}
521
522/// If not feature `linux_4_11` try to call [crate::raw::statx] and fallback
523/// to [crate::raw::fstatat] if not available.
524///
525/// # Safety
526///
527/// This function is marked as unsafe because directory file descriptor
528/// (`dirfd`) cannot be checked.
529#[cfg(all(not(feature = "linux_4_11"), not(target_arch = "loongarch64")))]
530pub unsafe fn fstatat<P: AsRef<Path>>(
531    dirfd: RawFd,
532    path: P,
533    flags: StatAtFlags,
534) -> Result<Stat, Errno> {
535    let path = path.as_ref();
536
537    run_with_cstr(path, |path| fstatat_cstr(dirfd, path, flags))
538}
539
540/// If not feature `linux_4_11` try to call [crate::raw::statx] and fallback
541/// to [crate::raw::fstatat] if not available.
542/// Accept `path` as a [CStr].
543///
544/// # Safety
545///
546/// This function is marked as unsafe because directory file descriptor
547/// (`dirfd`) cannot be checked.
548#[cfg(all(not(feature = "linux_4_11"), not(target_arch = "loongarch64")))]
549pub unsafe fn fstatat_cstr(dirfd: RawFd, path: &CStr, flags: StatAtFlags) -> Result<Stat, Errno> {
550    use core::sync::atomic::Ordering;
551
552    match HAS_STATX.load(Ordering::Relaxed) {
553        0 => crate::raw::fstatat_cstr(dirfd, path, flags).map(Stat::Stat64),
554        1 => crate::raw::statx_cstr(dirfd, path, flags, crate::raw::StatXMask::BASIC_STATS)
555            .map(Stat::Statx),
556        _ => match crate::raw::statx_cstr(dirfd, path, flags, crate::raw::StatXMask::BASIC_STATS) {
557            Err(Errno::ENOSYS) => {
558                HAS_STATX.store(0, Ordering::Relaxed);
559                crate::raw::fstatat_cstr(dirfd, path, flags).map(Stat::Stat64)
560            }
561            other => {
562                HAS_STATX.store(1, Ordering::Relaxed);
563                other.map(Stat::Statx)
564            }
565        },
566    }
567}
568
569/// If not feature `linux_4_11` try to call [crate::raw::statx] and fallback
570/// to [crate::raw::fstatat] if not available.
571///
572/// # Safety
573///
574/// This function is marked as unsafe because directory file descriptor
575/// (`dirfd`) cannot be checked.
576#[cfg(any(feature = "linux_4_11", target_arch = "loongarch64"))]
577#[inline]
578pub unsafe fn fstatat<P: AsRef<Path>>(
579    dirfd: RawFd,
580    path: P,
581    flags: StatAtFlags,
582) -> Result<Stat, Errno> {
583    run_with_cstr(path, |path| fstatat_cstr(dirfd, path, flags))
584}
585
586/// If not feature `linux_4_11` try to call [crate::raw::statx] and fallback
587/// to [crate::raw::fstatat] if not available.
588/// Accept `path` as a [CStr].
589///
590/// # Safety
591///
592/// This function is marked as unsafe because directory file descriptor
593/// (`dirfd`) cannot be checked.
594#[cfg(any(feature = "linux_4_11", target_arch = "loongarch64"))]
595#[inline]
596pub unsafe fn fstatat_cstr(dirfd: RawFd, path: &CStr, flags: StatAtFlags) -> Result<Stat, Errno> {
597    raw::statx_cstr(dirfd, path, flags, crate::raw::StatXMask::empty())
598}
599
600/// Call [crate::fstatat] for `path` in the current directory
601/// following symlinks.
602#[inline]
603pub fn stat<P: AsRef<Path>>(path: P) -> Result<Stat, Errno> {
604    run_with_cstr(path, stat_cstr)
605}
606
607/// Call [crate::fstatat] for `path` in the current directory
608/// following symlinks. Accept `path` as as [CStr].
609#[inline]
610pub fn stat_cstr(path: &CStr) -> Result<Stat, Errno> {
611    unsafe { fstatat_cstr(CURRENT_DIRECTORY, path, StatAtFlags::empty()) }
612}
613
614/// Call [crate::fstatat] for `path` in the current directory
615/// not following symlinks.
616#[inline]
617pub fn lstat<P: AsRef<Path>>(path: P) -> Result<Stat, Errno> {
618    run_with_cstr(path, lstat_cstr)
619}
620
621/// Call [crate::fstatat] for `path` in the current directory
622/// not following symlinks. Accept `path` as a [CStr].
623#[inline]
624pub fn lstat_cstr(path: &CStr) -> Result<Stat, Errno> {
625    unsafe { fstatat_cstr(CURRENT_DIRECTORY, path, StatAtFlags::SYMLINK_NOFOLLOW) }
626}
627
628/// Call [crate::fstatat] on the `dirfd` directory file descriptor
629///
630/// # Safety
631///
632/// This function is marked as unsafe because directory file descriptor
633/// (`dirfd`) cannot be checked.
634#[inline]
635pub unsafe fn fstat(dirfd: RawFd) -> Result<Stat, Errno> {
636    if dirfd < 0 {
637        return Err(Errno::EBADF);
638    }
639
640    fstatat(dirfd, empty_path(), StatAtFlags::EMPTY_PATH)
641}
642
643#[cfg(test)]
644pub(crate) mod tests {
645    use super::*;
646
647    #[cfg(feature = "std")]
648    pub fn dev_null() -> &'static Path {
649        Path::new("/dev/null\0")
650    }
651
652    #[cfg(not(feature = "std"))]
653    pub fn dev_null() -> &'static CStr {
654        unsafe { CStr::from_bytes_with_nul_unchecked(b"/dev/null\0") }
655    }
656
657    pub fn retry<T, F: Fn() -> Result<T, Errno>>(f: F) -> Result<T, Errno> {
658        loop {
659            match f() {
660                Err(Errno::EINTR) => (),
661                other => return other,
662            }
663        }
664    }
665
666    #[cfg(target_os = "android")]
667    pub use libc::__errno as errno;
668    #[cfg(target_os = "linux")]
669    pub use libc::__errno_location as errno;
670
671    pub fn c_stat() -> Result<libc::stat64, Errno> {
672        unsafe {
673            let mut buf = core::mem::MaybeUninit::<libc::stat64>::uninit();
674            if libc::fstatat64(
675                libc::AT_FDCWD,
676                b"/dev/null\0".as_ptr() as *const _,
677                buf.as_mut_ptr(),
678                0,
679            ) == -1
680            {
681                return Err(Errno::new(*errno()));
682            }
683            Ok(buf.assume_init())
684        }
685    }
686
687    #[test]
688    #[allow(clippy::unnecessary_cast)]
689    fn stat_dev_null() {
690        linux_syscalls::init();
691
692        let c_stat = retry(c_stat);
693        assert!(c_stat.is_ok());
694        let c_stat = c_stat.unwrap();
695
696        let stat = retry(|| stat(dev_null()));
697        assert!(stat.is_ok());
698        let stat = stat.unwrap();
699
700        assert_eq!(stat.dev(), c_stat.st_dev);
701        assert_eq!(stat.inode(), c_stat.st_ino as u64);
702        assert_eq!(stat.nlink(), c_stat.st_nlink as u32);
703        assert_eq!(
704            stat.mode().as_u16() | stat.file_type().as_u16(),
705            c_stat.st_mode as u16
706        );
707        assert_eq!(stat.uid(), c_stat.st_uid as u32);
708        assert_eq!(stat.gid(), c_stat.st_gid as u32);
709        assert_eq!(stat.rdev(), c_stat.st_rdev);
710        assert_eq!(stat.size(), c_stat.st_size as i64);
711        assert_eq!(stat.block_size(), c_stat.st_blksize as i32);
712        assert_eq!(stat.blocks(), c_stat.st_blocks as i64);
713        assert_eq!(stat.atime().secs, c_stat.st_atime as i64);
714        assert_eq!(stat.atime().nsecs, c_stat.st_atime_nsec as u32);
715        assert_eq!(stat.mtime().secs, c_stat.st_mtime as i64);
716        assert_eq!(stat.mtime().nsecs, c_stat.st_mtime_nsec as u32);
717        assert_eq!(stat.ctime().secs, c_stat.st_ctime as i64);
718        assert_eq!(stat.ctime().nsecs, c_stat.st_ctime_nsec as u32);
719    }
720}