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
use std::fmt;
use std::str::FromStr;
use sys_mount::FilesystemType as MountFS;

/// Describes a file system format, such as ext4 or fat32.
#[derive(Debug, PartialEq, Copy, Clone, Hash)]
pub enum FileSystem {
    Btrfs,
    Exfat,
    Ext2,
    Ext3,
    Ext4,
    F2fs,
    Fat16,
    Fat32,
    Ntfs,
    Swap,
    Xfs,
    Luks,
    Lvm,
}

/// Indicates that a partition is either too small or too large.
#[derive(Debug)]
pub enum PartitionSizeError {
    TooSmall(u64, u64),
    TooLarge(u64, u64),
}

impl FileSystem {
    /// Check if a given size, in bytes, is valid for this file system.
    ///
    /// # Possible Values
    /// - `Ok(())` indicates a valid partition size.
    /// - `Err(PartitionSizeError::TooSmall)` for a partition that is too small.
    /// - `Err(PartitionSizeError::TooLarge)` for a partition that is too large.
    pub fn validate_size(self, size: u64) -> Result<(), PartitionSizeError> {
        const MIB: u64 = 1024 * 1024;
        const GIB: u64 = MIB * 1024;
        const TIB: u64 = GIB * 1024;

        const FAT16_MIN: u64 = 16 * MIB;
        const FAT16_MAX: u64 = (4096 - 1) * MIB;
        const FAT32_MIN: u64 = 33 * MIB;
        const FAT32_MAX: u64 = 2 * TIB;
        const EXT4_MAX: u64 = 16 * TIB;
        const BTRFS_MIN: u64 = 250 * MIB;

        match self {
            FileSystem::Btrfs if size < BTRFS_MIN => {
                Err(PartitionSizeError::TooSmall(size, BTRFS_MIN))
            }
            FileSystem::Fat16 if size < FAT16_MIN => {
                Err(PartitionSizeError::TooSmall(size, FAT16_MIN))
            }
            FileSystem::Fat16 if size > FAT16_MAX => {
                Err(PartitionSizeError::TooLarge(size, FAT16_MAX))
            }
            FileSystem::Fat32 if size < FAT32_MIN => {
                Err(PartitionSizeError::TooSmall(size, FAT32_MIN))
            }
            FileSystem::Fat32 if size > FAT32_MAX => {
                Err(PartitionSizeError::TooLarge(size, FAT32_MAX))
            }
            FileSystem::Ext4 if size > EXT4_MAX => {
                Err(PartitionSizeError::TooLarge(size, EXT4_MAX))
            }
            _ => Ok(()),
        }
    }
}

impl FromStr for FileSystem {
    type Err = &'static str;
    fn from_str(string: &str) -> Result<Self, Self::Err> {
        let type_ = match string.to_lowercase().as_str() {
            "btrfs" => FileSystem::Btrfs,
            "exfat" => FileSystem::Exfat,
            "ext2" => FileSystem::Ext2,
            "ext3" => FileSystem::Ext3,
            "ext4" => FileSystem::Ext4,
            "f2fs" => FileSystem::F2fs,
            "fat16" => FileSystem::Fat16,
            "fat32" => FileSystem::Fat32,
            "swap" | "linux-swap(v1)" => FileSystem::Swap,
            "ntfs" => FileSystem::Ntfs,
            "xfs" => FileSystem::Xfs,
            "lvm" | "lvm2_member" => FileSystem::Lvm,
            "luks" | "crypto_luks" => FileSystem::Luks,
            _ => return Err("invalid file system name"),
        };
        Ok(type_)
    }
}

impl Into<&'static str> for FileSystem {
    fn into(self) -> &'static str {
        match self {
            FileSystem::Btrfs => "btrfs",
            FileSystem::Exfat => "exfat",
            FileSystem::Ext2 => "ext2",
            FileSystem::Ext3 => "ext3",
            FileSystem::Ext4 => "ext4",
            FileSystem::F2fs => "f2fs",
            FileSystem::Fat16 => "fat16",
            FileSystem::Fat32 => "fat32",
            FileSystem::Ntfs => "ntfs",
            FileSystem::Swap => "linux-swap(v1)",
            FileSystem::Xfs => "xfs",
            FileSystem::Lvm => "lvm",
            FileSystem::Luks => "luks",
        }
    }
}

impl fmt::Display for FileSystem {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let str: &'static str = (*self).into();
        f.write_str(str)
    }
}

/// Enable integration with the `sys_mount` crate, if it is used.
impl From<FileSystem> for MountFS<'static> {
    fn from(fs: FileSystem) -> Self {
        MountFS::Manual(match fs {
            FileSystem::Fat16 | FileSystem::Fat32 => "vfat",
            fs => fs.into(),
        })
    }
}