rc_zip/parse/
mode.rs

1use std::fmt::{self, Write};
2
3/// Mode represents a file's mode and permission bits.
4/// The bits have the same definition on all systems,
5/// but not all bits apply to all systems.
6///
7/// It is modelled after Go's `os.FileMode`.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub struct Mode(pub u32);
10
11impl Mode {
12    /// d: is a directory
13    pub const DIR: Self = Self(1 << 31);
14    /// a: append-only
15    pub const APPEND: Self = Self(1 << 30);
16    /// l: exclusive use
17    pub const EXCLUSIVE: Self = Self(1 << 29);
18    /// T: temporary file; Plan 9 only
19    pub const TEMPORARY: Self = Self(1 << 28);
20    /// L: symbolic link
21    pub const SYMLINK: Self = Self(1 << 27);
22    /// D: device file
23    pub const DEVICE: Self = Self(1 << 26);
24    /// p: named pipe (FIFO)
25    pub const NAMED_PIPE: Self = Self(1 << 25);
26    /// S: Unix domain socket
27    pub const SOCKET: Self = Self(1 << 24);
28    /// u: setuid
29    pub const SETUID: Self = Self(1 << 23);
30    /// g: setgid
31    pub const SETGID: Self = Self(1 << 22);
32    /// c: Unix character device, when DEVICE is set
33    pub const CHAR_DEVICE: Self = Self(1 << 21);
34    /// t: sticky
35    pub const STICKY: Self = Self(1 << 20);
36    /// ?: non-regular file; nothing else is known
37    pub const IRREGULAR: Self = Self(1 << 19);
38}
39
40impl fmt::Display for Mode {
41    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
42        let mut empty = true;
43        let pairs = [
44            (Self::DIR, 'd'),
45            (Self::APPEND, 'a'),
46            (Self::EXCLUSIVE, 'l'),
47            (Self::TEMPORARY, 'T'),
48            (Self::SYMLINK, 'L'),
49            (Self::DEVICE, 'D'),
50            (Self::NAMED_PIPE, 'p'),
51            (Self::SOCKET, 'S'),
52            (Self::SETUID, 'u'),
53            (Self::SETGID, 'g'),
54            (Self::CHAR_DEVICE, 'c'),
55            (Self::STICKY, 't'),
56            (Self::IRREGULAR, '?'),
57        ];
58        for (flag, flag_char) in pairs {
59            if self.has(flag) {
60                f.write_char(flag_char)?;
61                empty = false;
62            }
63        }
64        if empty {
65            write!(f, "-")?;
66        }
67
68        let rwx = "rwxrwxrwx";
69        for (i, c) in rwx.char_indices() {
70            if self.has(Mode(1 << (9 - 1 - i))) {
71                write!(f, "{}", c)?;
72            } else {
73                write!(f, "-")?;
74            }
75        }
76
77        Ok(())
78    }
79}
80
81impl From<UnixMode> for Mode {
82    fn from(m: UnixMode) -> Self {
83        let mut mode = Mode(m.0 & 0o777);
84
85        match m & UnixMode::IFMT {
86            UnixMode::IFBLK => mode |= Mode::DEVICE,
87            UnixMode::IFCHR => mode |= Mode::DEVICE & Mode::CHAR_DEVICE,
88            UnixMode::IFDIR => mode |= Mode::DIR,
89            UnixMode::IFIFO => mode |= Mode::NAMED_PIPE,
90            UnixMode::IFLNK => mode |= Mode::SYMLINK,
91            UnixMode::IFREG => { /* nothing to do */ }
92            UnixMode::IFSOCK => mode |= Mode::SOCKET,
93            _ => {}
94        }
95
96        if m.has(UnixMode::ISGID) {
97            mode |= Mode::SETGID
98        }
99        if m.has(UnixMode::ISUID) {
100            mode |= Mode::SETUID
101        }
102        if m.has(UnixMode::ISVTX) {
103            mode |= Mode::STICKY
104        }
105
106        mode
107    }
108}
109
110impl From<MsdosMode> for Mode {
111    fn from(m: MsdosMode) -> Self {
112        let mut mode = if m.has(MsdosMode::DIR) {
113            Mode::DIR | Mode(0o777)
114        } else {
115            Mode(0o666)
116        };
117        if m.has(MsdosMode::READ_ONLY) {
118            mode &= Mode(0o222);
119        }
120
121        mode
122    }
123}
124
125impl From<u32> for Mode {
126    fn from(u: u32) -> Self {
127        Mode(u)
128    }
129}
130
131/// UnixMode represents the file mode and permission bits for Unix systems.
132#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
133pub struct UnixMode(pub u32);
134
135impl UnixMode {
136    /// bit mask for the file type bit fields
137    pub const IFMT: Self = Self(0xf000);
138
139    /// the file is a socket
140    pub const IFSOCK: Self = Self(0xc000);
141
142    /// the file is a symbolic link
143    pub const IFLNK: Self = Self(0xa000);
144
145    /// the file is a regular file
146    pub const IFREG: Self = Self(0x8000);
147
148    /// the file is a block device
149    pub const IFBLK: Self = Self(0x6000);
150
151    /// the file is a directory
152    pub const IFDIR: Self = Self(0x4000);
153
154    /// the file is a character device
155    pub const IFCHR: Self = Self(0x2000);
156
157    /// the file is a FIFO
158    pub const IFIFO: Self = Self(0x1000);
159
160    /// the file is set-user-ID
161    pub const ISUID: Self = Self(0x800);
162
163    /// the file is set-group-ID
164    pub const ISGID: Self = Self(0x400);
165
166    /// the file is sticky
167    pub const ISVTX: Self = Self(0x200);
168}
169
170impl From<u32> for UnixMode {
171    fn from(u: u32) -> Self {
172        UnixMode(u)
173    }
174}
175
176/// MsdosMode represents the file mode and permission bits for MS-DOS
177#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
178pub struct MsdosMode(pub u32);
179
180impl MsdosMode {
181    /// the file is a directory
182    pub const DIR: Self = Self(0x10);
183
184    /// the file is read-only
185    pub const READ_ONLY: Self = Self(0x01);
186}
187
188impl From<u32> for MsdosMode {
189    fn from(u: u32) -> Self {
190        MsdosMode(u)
191    }
192}
193
194macro_rules! derive_bitops {
195    ($T: ty) => {
196        impl std::ops::BitOr for $T {
197            type Output = Self;
198
199            fn bitor(self, rhs: Self) -> Self {
200                Self(self.0 | rhs.0)
201            }
202        }
203
204        impl std::ops::BitOrAssign for $T {
205            fn bitor_assign(&mut self, rhs: Self) {
206                self.0 |= rhs.0;
207            }
208        }
209
210        impl std::ops::BitAnd for $T {
211            type Output = Self;
212
213            fn bitand(self, rhs: Self) -> Self {
214                Self(self.0 & rhs.0)
215            }
216        }
217
218        impl std::ops::BitAndAssign for $T {
219            fn bitand_assign(&mut self, rhs: Self) {
220                self.0 &= rhs.0;
221            }
222        }
223
224        impl $T {
225            /// Check if the mode has the given bits set.
226            pub fn has(&self, rhs: Self) -> bool {
227                self.0 & rhs.0 != 0
228            }
229        }
230    };
231}
232
233derive_bitops!(Mode);
234derive_bitops!(UnixMode);
235derive_bitops!(MsdosMode);