yash_env/system/
file_system.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// This file is part of yash, an extended POSIX shell.
// Copyright (C) 2021 WATANABE Yuki
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

//! Items about file systems

use super::{Gid, Result, Uid};
use crate::str::UnixStr;
use bitflags::bitflags;
use std::fmt::Debug;
use yash_syntax::syntax::Fd;

#[cfg(unix)]
const RAW_AT_FDCWD: i32 = nix::libc::AT_FDCWD;
#[cfg(not(unix))]
const RAW_AT_FDCWD: i32 = -100;

/// Sentinel for the current working directory
///
/// This value can be passed to system calls named "*at" such as
/// [`fstatat`](super::System::fstatat).
pub const AT_FDCWD: Fd = Fd(RAW_AT_FDCWD);

/// Metadata of a file contained in a directory
///
/// `DirEntry` objects are enumerated by a [`Dir`] implementor.
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub struct DirEntry<'a> {
    /// Filename
    pub name: &'a UnixStr,
}

/// Trait for enumerating directory entries
///
/// An implementor of `Dir` may retain a file descriptor (or any other resource
/// alike) to access the underlying system and obtain entry information. The
/// file descriptor is released when the implementor object is dropped.
pub trait Dir: Debug {
    /// Returns the next directory entry.
    fn next(&mut self) -> Result<Option<DirEntry>>;
}

#[cfg(unix)]
type RawModeDef = nix::libc::mode_t;
#[cfg(not(unix))]
type RawModeDef = u32;

/// Raw file permission bits type
///
/// This is a type alias for the raw file permission bits type `mode_t` declared
/// in the [`libc`] crate. The exact representation of this type is
/// platform-dependent while POSIX requires the type to be an integer. On
/// non-Unix platforms, this type is hard-coded to `u32`.
///
/// File permission bits are usually wrapped in the [`Mode`] type for better type
/// safety, so this type is not used directly in most cases.
///
/// [`libc`]: nix::libc
pub type RawMode = RawModeDef;

/// File permission bits
///
/// This type implements the new type pattern for the raw file permission bits
/// type [`RawMode`]. The advantage of using this type is that it is more
/// type-safe than using the raw integer value directly.
///
/// This type only defines the permission bits and does not include the file
/// type bits (e.g., regular file, directory, symbolic link, etc.). The file
/// types are represented by the [`FileType`] enum.
#[derive(Copy, Clone, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct Mode(RawMode);

bitflags! {
    impl Mode: RawMode {
        /// User read permission (`0o400`)
        const USER_READ = 0o400;
        /// User write permission (`0o200`)
        const USER_WRITE = 0o200;
        /// User execute permission (`0o100`)
        const USER_EXEC = 0o100;
        /// User read, write, and execute permissions (`0o700`)
        const USER_ALL = 0o700;
        /// Group read permission (`0o040`)
        const GROUP_READ = 0o040;
        /// Group write permission (`0o020`)
        const GROUP_WRITE = 0o020;
        /// Group execute permission (`0o010`)
        const GROUP_EXEC = 0o010;
        /// Group read, write, and execute permissions (`0o070`)
        const GROUP_ALL = 0o070;
        /// Other read permission (`0o004`)
        const OTHER_READ = 0o004;
        /// Other write permission (`0o002`)
        const OTHER_WRITE = 0o002;
        /// Other execute permission (`0o001`)
        const OTHER_EXEC = 0o001;
        /// Other read, write, and execute permissions (`0o007`)
        const OTHER_ALL = 0o007;
        /// All read permission (`0o444`)
        const ALL_READ = 0o444;
        /// All write permission (`0o222`)
        const ALL_WRITE = 0o222;
        /// All execute permission (`0o111`)
        const ALL_EXEC = 0o111;
        /// All combinations of (user, group, other) × (read, write, execute)
        ///
        /// Note that this is equivalent to `Mode::USER_ALL | Mode::GROUP_ALL |
        /// Mode::OTHER_ALL` and does not include the sticky bit, the
        /// set-user-ID bit, or the set-group-ID bit.
        const ALL_9 = 0o777;
        /// Set-user-ID bit (`0o4000`)
        const SET_USER_ID = 0o4000;
        /// Set-group-ID bit (`0o2000`)
        const SET_GROUP_ID = 0o2000;
        /// Sticky bit (`0o1000`)
        const STICKY = 0o1000;
    }
}

impl Debug for Mode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Mode({:#o})", self.0)
    }
}

/// The default mode is `0o644`, not `0o000`.
impl Default for Mode {
    fn default() -> Mode {
        Mode(0o644)
    }
}

/// File type
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum FileType {
    /// Regular file
    Regular,
    /// Directory
    Directory,
    /// Symbolic link
    Symlink,
    /// Pipe
    Fifo,
    /// Block special device file
    BlockDevice,
    /// Character special device file
    CharacterDevice,
    /// Socket
    Socket,
    /// Other file type, including unknown file types
    Other,
}

/// File status
///
/// This type is a collection of file status information. It is similar to the
/// `stat` structure defined in the POSIX standard, but it is simplified and
/// does not include all fields of the `stat` structure.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub struct Stat {
    /// Device ID
    pub dev: u64,
    /// Inode number
    pub ino: u64,
    /// Access permissions
    ///
    /// Note that this field does not include the file type bits.
    /// The file type is stored in the `type` field.
    pub mode: Mode,
    /// File type
    pub r#type: FileType,
    /// Number of hard links
    pub nlink: u64,
    /// User ID of the file owner
    pub uid: Uid,
    /// Group ID of the file owner
    pub gid: Gid,
    /// Length of the file in bytes
    pub size: u64,
    // TODO: atime, mtime, ctime, (birthtime)
}

impl Stat {
    /// Returns the device ID and inode number as a tuple
    ///
    /// This method is useful for testing whether two `Stat` objects refer to
    /// the same file.
    #[inline]
    #[must_use]
    pub const fn identity(&self) -> (u64, u64) {
        (self.dev, self.ino)
    }
}