ext4_view/
file_type.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
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::inode::InodeMode;

#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct FileTypeError;

/// File type.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum FileType {
    /// Block device.
    BlockDevice,

    /// Character device.
    CharacterDevice,

    /// Directory.
    Directory,

    /// First-in first-out (FIFO) special file.
    Fifo,

    /// Regular file.
    Regular,

    /// Socket file.
    Socket,

    /// Symbolic link.
    Symlink,
}

impl FileType {
    pub(crate) fn from_dir_entry(val: u8) -> Result<Self, FileTypeError> {
        match val {
            1 => Ok(Self::Regular),
            2 => Ok(Self::Directory),
            3 => Ok(Self::CharacterDevice),
            4 => Ok(Self::BlockDevice),
            5 => Ok(Self::Fifo),
            6 => Ok(Self::Socket),
            7 => Ok(Self::Symlink),
            _ => Err(FileTypeError),
        }
    }

    /// Returns true if the file is a block device.
    pub fn is_block_dev(self) -> bool {
        self == Self::BlockDevice
    }

    /// Returns true if the file is a character device.
    pub fn is_char_dev(self) -> bool {
        self == Self::CharacterDevice
    }

    /// Returns true if the file is a directory.
    pub fn is_dir(self) -> bool {
        self == Self::Directory
    }

    /// Returns true if the file is a first-in first-out (FIFO) special file.
    pub fn is_fifo(self) -> bool {
        self == Self::Fifo
    }

    /// Returns true if the file is a regular file.
    pub fn is_regular_file(self) -> bool {
        self == Self::Regular
    }

    /// Returns true if the file is a socket.
    pub fn is_socket(self) -> bool {
        self == Self::Socket
    }

    /// Returns true if the file is a symlink.
    pub fn is_symlink(self) -> bool {
        self == Self::Symlink
    }
}

impl TryFrom<InodeMode> for FileType {
    type Error = FileTypeError;

    fn try_from(mode: InodeMode) -> Result<Self, Self::Error> {
        // Mask out the lower bits.
        let mode = InodeMode::from_bits_retain(mode.bits() & 0xf000);

        if mode == InodeMode::S_IFIFO {
            Ok(Self::Fifo)
        } else if mode == InodeMode::S_IFCHR {
            Ok(Self::CharacterDevice)
        } else if mode == InodeMode::S_IFDIR {
            Ok(Self::Directory)
        } else if mode == InodeMode::S_IFBLK {
            Ok(Self::BlockDevice)
        } else if mode == InodeMode::S_IFREG {
            Ok(Self::Regular)
        } else if mode == InodeMode::S_IFLNK {
            Ok(Self::Symlink)
        } else if mode == InodeMode::S_IFSOCK {
            Ok(Self::Socket)
        } else {
            Err(FileTypeError)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_file_type() {
        // Check each valid file type.
        assert!(FileType::try_from(InodeMode::S_IFIFO).unwrap().is_fifo());
        assert!(FileType::try_from(InodeMode::S_IFCHR)
            .unwrap()
            .is_char_dev());
        assert!(FileType::try_from(InodeMode::S_IFBLK)
            .unwrap()
            .is_block_dev());
        assert!(FileType::try_from(InodeMode::S_IFREG)
            .unwrap()
            .is_regular_file());
        assert!(FileType::try_from(InodeMode::S_IFLNK).unwrap().is_symlink());
        assert!(FileType::try_from(InodeMode::S_IFSOCK).unwrap().is_socket());

        // Check that other bits being set in the mode don't impact the
        // file type.
        assert!(FileType::try_from(InodeMode::S_IFREG | InodeMode::S_IXOTH)
            .unwrap()
            .is_regular_file());

        // Error, no file type set.
        assert_eq!(
            FileType::try_from(InodeMode::empty()).unwrap_err(),
            FileTypeError
        );
    }
}