ext4_view/
file_type.rs

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use crate::inode::InodeMode;
10
11#[derive(Clone, Debug, Eq, PartialEq)]
12pub(crate) struct FileTypeError;
13
14/// File type.
15#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
16pub enum FileType {
17    /// Block device.
18    BlockDevice,
19
20    /// Character device.
21    CharacterDevice,
22
23    /// Directory.
24    Directory,
25
26    /// First-in first-out (FIFO) special file.
27    Fifo,
28
29    /// Regular file.
30    Regular,
31
32    /// Socket file.
33    Socket,
34
35    /// Symbolic link.
36    Symlink,
37}
38
39impl FileType {
40    pub(crate) fn from_dir_entry(val: u8) -> Result<Self, FileTypeError> {
41        match val {
42            1 => Ok(Self::Regular),
43            2 => Ok(Self::Directory),
44            3 => Ok(Self::CharacterDevice),
45            4 => Ok(Self::BlockDevice),
46            5 => Ok(Self::Fifo),
47            6 => Ok(Self::Socket),
48            7 => Ok(Self::Symlink),
49            _ => Err(FileTypeError),
50        }
51    }
52
53    /// Returns true if the file is a block device.
54    #[must_use]
55    pub fn is_block_dev(self) -> bool {
56        self == Self::BlockDevice
57    }
58
59    /// Returns true if the file is a character device.
60    #[must_use]
61    pub fn is_char_dev(self) -> bool {
62        self == Self::CharacterDevice
63    }
64
65    /// Returns true if the file is a directory.
66    #[must_use]
67    pub fn is_dir(self) -> bool {
68        self == Self::Directory
69    }
70
71    /// Returns true if the file is a first-in first-out (FIFO) special file.
72    #[must_use]
73    pub fn is_fifo(self) -> bool {
74        self == Self::Fifo
75    }
76
77    /// Returns true if the file is a regular file.
78    #[must_use]
79    pub fn is_regular_file(self) -> bool {
80        self == Self::Regular
81    }
82
83    /// Returns true if the file is a socket.
84    #[must_use]
85    pub fn is_socket(self) -> bool {
86        self == Self::Socket
87    }
88
89    /// Returns true if the file is a symlink.
90    #[must_use]
91    pub fn is_symlink(self) -> bool {
92        self == Self::Symlink
93    }
94}
95
96impl TryFrom<InodeMode> for FileType {
97    type Error = FileTypeError;
98
99    fn try_from(mode: InodeMode) -> Result<Self, Self::Error> {
100        // Mask out the lower bits.
101        let mode = InodeMode::from_bits_retain(mode.bits() & 0xf000);
102
103        if mode == InodeMode::S_IFIFO {
104            Ok(Self::Fifo)
105        } else if mode == InodeMode::S_IFCHR {
106            Ok(Self::CharacterDevice)
107        } else if mode == InodeMode::S_IFDIR {
108            Ok(Self::Directory)
109        } else if mode == InodeMode::S_IFBLK {
110            Ok(Self::BlockDevice)
111        } else if mode == InodeMode::S_IFREG {
112            Ok(Self::Regular)
113        } else if mode == InodeMode::S_IFLNK {
114            Ok(Self::Symlink)
115        } else if mode == InodeMode::S_IFSOCK {
116            Ok(Self::Socket)
117        } else {
118            Err(FileTypeError)
119        }
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn test_file_type() {
129        // Check each valid file type.
130        assert!(FileType::try_from(InodeMode::S_IFIFO).unwrap().is_fifo());
131        assert!(
132            FileType::try_from(InodeMode::S_IFCHR)
133                .unwrap()
134                .is_char_dev()
135        );
136        assert!(
137            FileType::try_from(InodeMode::S_IFBLK)
138                .unwrap()
139                .is_block_dev()
140        );
141        assert!(
142            FileType::try_from(InodeMode::S_IFREG)
143                .unwrap()
144                .is_regular_file()
145        );
146        assert!(FileType::try_from(InodeMode::S_IFLNK).unwrap().is_symlink());
147        assert!(FileType::try_from(InodeMode::S_IFSOCK).unwrap().is_socket());
148
149        // Check that other bits being set in the mode don't impact the
150        // file type.
151        assert!(
152            FileType::try_from(InodeMode::S_IFREG | InodeMode::S_IXOTH)
153                .unwrap()
154                .is_regular_file()
155        );
156
157        // Error, no file type set.
158        assert_eq!(
159            FileType::try_from(InodeMode::empty()).unwrap_err(),
160            FileTypeError
161        );
162    }
163}