Skip to main content

yash_env/system/real/
file_system.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2024 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Extension to [`crate::system::file_system`] for the real system
18
19use super::super::{FileType, Gid, Mode, RawMode, Uid};
20use std::mem::MaybeUninit;
21
22impl FileType {
23    #[must_use]
24    pub(super) const fn from_raw(mode: RawMode) -> Self {
25        match mode & libc::S_IFMT {
26            libc::S_IFREG => Self::Regular,
27            libc::S_IFDIR => Self::Directory,
28            libc::S_IFLNK => Self::Symlink,
29            libc::S_IFIFO => Self::Fifo,
30            libc::S_IFBLK => Self::BlockDevice,
31            libc::S_IFCHR => Self::CharacterDevice,
32            libc::S_IFSOCK => Self::Socket,
33            _ => Self::Other,
34        }
35    }
36}
37
38/// Metadata of a file in the real file system
39///
40/// This is an implementation of the [`Stat` trait](super::super::Stat) for the
41/// [`RealSystem`](super::RealSystem).
42#[derive(Clone, Debug)]
43#[repr(transparent)]
44pub struct Stat(MaybeUninit<libc::stat>);
45// TODO: The auto-derived Debug implementation does not provide useful information.
46// Consider implementing a custom Debug that shows the contents.
47
48impl Stat {
49    /// Converts a raw `stat` structure to a `Stat` object.
50    ///
51    /// This function assumes the `stat` structure to be initialized by the
52    /// `stat` system call, but it is passed as `MaybeUninit` because of
53    /// possible padding or extension fields in the structure which may not be
54    /// initialized by the system call.
55    ///
56    /// # Safety
57    ///
58    /// The caller must ensure that the provided `stat` structure is properly
59    /// initialized by a system call like `stat`, `fstat`, or `lstat`.
60    #[must_use]
61    pub(super) const unsafe fn from_raw(stat: MaybeUninit<libc::stat>) -> Self {
62        Self(stat)
63    }
64}
65
66// The actual types of the fields in `libc::stat` may vary across platforms,
67// so some casts may be necessary.
68#[allow(clippy::unnecessary_cast)]
69impl super::super::Stat for Stat {
70    #[inline(always)]
71    fn dev(&self) -> u64 {
72        (unsafe { (*self.0.as_ptr()).st_dev }) as u64
73    }
74    #[inline(always)]
75    fn ino(&self) -> u64 {
76        (unsafe { (*self.0.as_ptr()).st_ino }) as u64
77    }
78    #[inline(always)]
79    fn mode(&self) -> Mode {
80        let raw_mode = unsafe { (*self.0.as_ptr()).st_mode };
81        Mode::from_bits_truncate(raw_mode)
82    }
83    #[inline(always)]
84    fn r#type(&self) -> FileType {
85        let raw_mode = unsafe { (*self.0.as_ptr()).st_mode };
86        FileType::from_raw(raw_mode)
87    }
88    #[inline(always)]
89    fn nlink(&self) -> u64 {
90        (unsafe { (*self.0.as_ptr()).st_nlink }) as u64
91    }
92    #[inline(always)]
93    fn uid(&self) -> Uid {
94        Uid(unsafe { (*self.0.as_ptr()).st_uid })
95    }
96    #[inline(always)]
97    fn gid(&self) -> Gid {
98        Gid(unsafe { (*self.0.as_ptr()).st_gid })
99    }
100    #[inline(always)]
101    fn size(&self) -> u64 {
102        (unsafe { (*self.0.as_ptr()).st_size }) as u64
103    }
104
105    fn is_regular_file(&self) -> bool {
106        let raw_mode = unsafe { (*self.0.as_ptr()).st_mode };
107        raw_mode & libc::S_IFMT == libc::S_IFREG
108    }
109    fn is_directory(&self) -> bool {
110        let raw_mode = unsafe { (*self.0.as_ptr()).st_mode };
111        raw_mode & libc::S_IFMT == libc::S_IFDIR
112    }
113    fn is_symlink(&self) -> bool {
114        let raw_mode = unsafe { (*self.0.as_ptr()).st_mode };
115        raw_mode & libc::S_IFMT == libc::S_IFLNK
116    }
117    fn is_fifo(&self) -> bool {
118        let raw_mode = unsafe { (*self.0.as_ptr()).st_mode };
119        raw_mode & libc::S_IFMT == libc::S_IFIFO
120    }
121    fn is_block_device(&self) -> bool {
122        let raw_mode = unsafe { (*self.0.as_ptr()).st_mode };
123        raw_mode & libc::S_IFMT == libc::S_IFBLK
124    }
125    fn is_character_device(&self) -> bool {
126        let raw_mode = unsafe { (*self.0.as_ptr()).st_mode };
127        raw_mode & libc::S_IFMT == libc::S_IFCHR
128    }
129    fn is_socket(&self) -> bool {
130        let raw_mode = unsafe { (*self.0.as_ptr()).st_mode };
131        raw_mode & libc::S_IFMT == libc::S_IFSOCK
132    }
133}