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
#[derive(Debug)]
pub struct Mount
{
pub source: CString,
pub mount_point: PathBuf,
pub file_system_type: FileSystemType,
pub mount_options: HashMap<String, Option<String>>,
dump_frequency_in_days: i32,
pass_number_on_parallel_filesystem_type: i32,
}
impl Mount
{
pub fn unmount(mount_point: &Path, unmount_flags: UnmountFlags) -> Result<(), io::Error>
{
use self::ErrorKind::*;
let target = mount_point.to_c_string();
match unsafe { umount2(target.as_ptr(), unmount_flags.bits()) }
{
0 => Ok(()),
-1 => match errno().0
{
E::EAGAIN =>
{
if unmount_flags.contains(UnmountFlags::Expire)
{
Ok(())
}
else
{
panic!("umount() set an illegal errno of EAGAIN when unmount flags did not contain MNT_EXPIRE");
}
},
E::EBUSY => Err(io::Error::new(TimedOut, "Busy")),
E::EPERM => Err(io::Error::new(PermissionDenied, "permission denied")),
E::ENOENT => Err(io::Error::new(NotFound, "Mount path had an empty or non-existent component")),
E::EINVAL => Err(io::Error::new(InvalidData, "One of many possible failures (EINVAL)")),
E::ENOMEM => panic!("Out of memory (ENOMEM)"),
E::ENAMETOOLONG => panic!("mount_point path name is too long"),
E::EFAULT => panic!("Invalid data"),
illegal @ _ => panic!("umount() set an illegal errno '{}'", illegal),
},
illegal @ _ => panic!("umount() returned an illegal result '{}'", illegal),
}
}
#[inline(always)]
pub fn has_file_system_type(&self, file_system_type: &FileSystemType) -> bool
{
&self.file_system_type == file_system_type
}
fn from_mntent(raw: *mut mntent) -> Self
{
debug_assert!(!unsafe {(*raw).mnt_fsname }.is_null(), "null");
let source = unsafe { CStr::from_ptr((*raw).mnt_fsname) }.to_owned();
let mount_point = (unsafe { c_string_pointer_to_path_buf((*raw).mnt_dir) }).expect("mnt_dir was empty").expect("mnt_dir was null");
let file_system_type =
{
let string = unsafe { c_string_pointer_to_string_with_replacements_if_any((*raw).mnt_type) };
FileSystemType::from_string(string.expect("mnt_type was null"))
};
let mount_options_string = (unsafe { c_string_pointer_to_string_with_replacements_if_any((*raw).mnt_opts) }).expect("mnt_opts was null");
let mut mount_options = HashMap::with_capacity(16);
for mount_option_string in mount_options_string.split(',')
{
let mut split = mount_option_string.splitn(2, '=');
let name = split.next().unwrap().to_owned();
let value_if_any = split.next().map(|value| value.to_owned());
assert!(mount_options.insert(name, value_if_any).is_none(), "Duplicate key in mount options for mount_point '{:?}'", mount_point);
}
Self
{
source,
mount_point,
file_system_type,
mount_options,
dump_frequency_in_days: unsafe { (*raw).mnt_freq },
pass_number_on_parallel_filesystem_type: unsafe { (*raw).mnt_passno },
}
}
pub fn new_where_source_is_file_system_type(mount_point: PathBuf, file_system_type: FileSystemType, mount_options: HashMap<String, Option<String>>) -> Self
{
Self
{
source: file_system_type.to_c_string(),
mount_point,
file_system_type,
mount_options,
dump_frequency_in_days: 0,
pass_number_on_parallel_filesystem_type: 0,
}
}
pub fn mount(&self, mount_flags: MountFlags) -> Result<(), io::Error>
{
use self::ErrorKind::*;
fn to_mount_options_c_string(mount_options: &HashMap<String, Option<String>>) -> CString
{
let mut mount_options_string = String::with_capacity(64);
let mut after_first = false;
for (name, value_if_any) in mount_options
{
if after_first
{
mount_options_string.push(',');
}
else
{
after_first = true;
}
mount_options_string.push_str(name);
if let Some(ref value) = *value_if_any
{
mount_options_string.push('=');
mount_options_string.push_str(value);
}
}
CString::new(mount_options_string).expect("mount_options should not contain interior ASCII NULs")
}
let target = self.mount_point.to_c_string();
let file_system_type = self.file_system_type.to_c_string();
let data = to_mount_options_c_string(&self.mount_options);
match unsafe { mount(self.source.as_ptr(), target.as_ptr(), file_system_type.as_ptr(), mount_flags.bits(), data.as_ptr() as *mut c_void) }
{
0 => Ok(()),
-1 => match errno().0
{
E::EACCES => Err(io::Error::new(NotFound, "Component of mount path to mount does not exist")),
E::ENOENT => Err(io::Error::new(NotFound, "Mount path had an empty or non-existent component")),
E::ENOTDIR => Err(io::Error::new(NotFound, "target or source is not a directory")),
E::ELOOP => Err(io::Error::new(NotFound, "Loops - target is a descendant of source, or too many links in mount path")),
E::EPERM => Err(io::Error::new(PermissionDenied, "permission denied")),
E::EBUSY => Err(io::Error::new(TimedOut, "Busy")),
E::EINVAL => Err(io::Error::new(InvalidData, "One of many possible failures (EINVAL)")),
E::EMFILE => panic!("Out of memory (EMFILE)"),
E::ENOMEM => panic!("Out of memory (ENOMEM)"),
E::ENODEV => panic!("File system type not supported by Linux Kernel (check /proc/filesystem first)"),
E::ENOTBLK => panic!("Specified block device wasn't"),
E::ENXIO => panic!("Block device major number is out of range"),
E::ENAMETOOLONG => panic!("Mount path name is too long"),
E::EFAULT => panic!("Invalid data"),
illegal @ _ => panic!("mount() set an illegal errno '{}'", illegal),
},
illegal @ _ => panic!("mount() returned an illegal result '{}'", illegal),
}
}
}