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
// This file is part of dpdk. It is subject to the license terms in the COPYRIGHT file found in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/dpdk/master/COPYRIGHT. No part of dpdk, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the COPYRIGHT file.
// Copyright © 2016-2017 The developers of dpdk. See the COPYRIGHT file in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/dpdk/master/COPYRIGHT.


/// Represents a mount.
#[derive(Debug)]
pub struct Mount
{
	/// eg eg `/dev/sda1`, `proc`, etc; not really that useful.
	pub source: CString,
	
	/// Mount point.
	pub mount_point: PathBuf,
	
	/// File system type.
	///
	/// eg `proc`, `sysfs`, `hugetlbs`, `ext4`; listed in second column of `/proc/filesystems`/
	pub file_system_type: FileSystemType,
	
	/// Mount options.
	///
	/// eg `nodev mode=0177`
	pub mount_options: HashMap<Box<[u8]>, Option<Box<[u8]>>>,
	
	/// Typically `0` (zero).
	dump_frequency_in_days: i32,
	
	/// Typically `0` (zero).
	pass_number_on_parallel_filesystem_type: i32,
}

impl Mount
{
	//noinspection SpellCheckingInspection
	/// Unmounts.
	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),
		}
	}
	
	/// Does this mount have this file system type?
	#[inline(always)]
	pub fn has_file_system_type(&self, file_system_type: &FileSystemType) -> bool
	{
		&self.file_system_type == file_system_type
	}
	
	//noinspection SpellCheckingInspection
	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 = FileSystemType::from_c_str(unsafe { CStr::from_ptr((*raw).mnt_type) });

		let mount_options_string = unsafe { CStr::from_ptr((*raw).mnt_opts) };
		
		let mut mount_options = HashMap::with_capacity(16);
		for mount_option_string in split(mount_options_string.to_bytes(), b',')
		{
			let mut split = splitn(mount_option_string, 2, b'=');
			let name = split.next().unwrap().to_vec().into_boxed_slice();
			let value_if_any = split.next().map(|value| value.to_vec().into_boxed_slice());
			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 },
		}
	}
	
	/// New instance for file systems which do not have a source (eg `hugetlbs`).
	pub fn new_where_source_is_file_system_type(mount_point: PathBuf, file_system_type: FileSystemType, mount_options: HashMap<Box<[u8]>, Option<Box<[u8]>>>) -> 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,
		}
	}
	
	//noinspection SpellCheckingInspection
	/// Mount.
	pub fn mount(&self, mount_flags: MountFlags) -> Result<(), io::Error>
	{
		use self::ErrorKind::*;
	
		fn to_mount_options_c_string(mount_options: &HashMap<Box<[u8]>, Option<Box<[u8]>>>) -> CString
		{
			let mut mount_options_cstring: Vec<u8> = Vec::with_capacity(64);
			let mut after_first = false;
			for (name, value_if_any) in mount_options
			{
				if after_first
				{
					mount_options_cstring.push(b',');
				}
				else
				{
					after_first = true;
				}
				mount_options_cstring.extend_from_slice(name);
				if let Some(ref value) = *value_if_any
				{
					mount_options_cstring.push(b'=');
					mount_options_cstring.extend_from_slice(value);
				}
			}
			CString::new(mount_options_cstring).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),
		}
	}
}