1use crate::win32;
2use derive_try_from_primitive::TryFromPrimitive;
3use std::{
4 cmp::min,
5 convert::TryFrom,
6 io::{self, Cursor, Read, Seek, SeekFrom},
7 mem::size_of,
8 ptr::null_mut,
9};
10use winapi::{
11 ctypes::c_void,
12 shared::ntdef::LARGE_INTEGER,
13 um::{
14 fileapi::{self as fs, OPEN_EXISTING},
15 handleapi::{CloseHandle, INVALID_HANDLE_VALUE},
16 ioapiset::DeviceIoControl,
17 winbase::{FILE_BEGIN, FILE_CURRENT, FILE_FLAG_NO_BUFFERING},
18 winioctl::{
19 DISK_GEOMETRY, IOCTL_DISK_GET_DRIVE_GEOMETRY, IOCTL_DISK_GET_PARTITION_INFO_EX,
20 PARTITION_INFORMATION_EX, PARTITION_STYLE_GPT, PARTITION_STYLE_MBR,
21 PARTITION_STYLE_RAW,
22 },
23 winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, HANDLE},
24 },
25};
26
27#[derive(Debug)]
32pub struct PhysicalDrive {
33 handle: HANDLE,
34 pub geometry: DiskGeometry,
36}
37
38#[derive(Debug)]
42pub struct BufferedPhysicalDrive {
43 drive: PhysicalDrive,
44 current_sector: Cursor<Vec<u8>>,
45 current_sector_num: u64,
46 pub geometry: DiskGeometry,
48}
49
50#[derive(Debug)]
55pub struct HarddiskVolume {
56 handle: HANDLE,
57 pub geometry: DiskGeometry,
59 pub partition_info: PartitionInfo,
61}
62
63#[derive(Debug)]
67pub struct BufferedHarddiskVolume {
68 volume: HarddiskVolume,
69 current_sector: Cursor<Vec<u8>>,
70 current_sector_num: u64,
71 pub geometry: DiskGeometry,
73 pub partition_info: PartitionInfo,
75}
76
77#[derive(Copy, Clone, Debug)]
79pub struct DiskGeometry {
80 pub cylinders: i64,
81 pub media_type: u32,
82 pub tracks_per_cylinder: u32,
83 pub sectors_per_track: u32,
84 pub bytes_per_sector: u32,
85}
86
87#[repr(u32)]
89#[derive(Copy, Clone, Debug, TryFromPrimitive)]
90pub enum PartitionStyle {
91 Gpt = PARTITION_STYLE_GPT,
92 Mbr = PARTITION_STYLE_MBR,
93 Raw = PARTITION_STYLE_RAW,
94}
95
96#[derive(Copy, Clone, Debug)]
98pub struct PartitionInfo {
99 pub partition_style: PartitionStyle,
100 pub starting_offset: u64,
101 pub partition_length: u64,
102 pub partition_number: u32,
103}
104
105impl PhysicalDrive {
106 pub fn open(drive_num: u8) -> Result<Self, String> {
115 let path = format!("\\\\.\\PhysicalDrive{}", drive_num);
116 let handle = open_handle(&path)?;
117 Ok(PhysicalDrive {
118 handle,
119 geometry: geometry(&handle)?,
120 })
121 }
122
123 pub fn size(&self) -> u64 {
125 self.geometry.size()
126 }
127}
128
129impl HarddiskVolume {
130 pub fn open(volume_num: u8) -> Result<Self, String> {
139 let path = format!("\\\\?\\HarddiskVolume{}", volume_num);
140 let handle = open_handle(&path)?;
141 Ok(HarddiskVolume {
142 handle,
143 geometry: geometry(&handle)?,
144 partition_info: partition_info(&handle)?,
145 })
146 }
147
148 pub fn size(&self) -> u64 {
150 self.partition_info.partition_length
151 }
152}
153
154impl_physical!(PhysicalDrive, HarddiskVolume);
155
156impl BufferedPhysicalDrive {
157 pub fn open(drive_num: u8) -> Result<Self, String> {
166 let mut drive = PhysicalDrive::open(drive_num)?;
167 let mut sector = vec![0; drive.geometry.bytes_per_sector as usize];
168 match drive.read(&mut sector) {
169 Ok(_) => (),
170 Err(e) => return Err(format!("{}", e)),
171 }
172 let geo = drive.geometry;
173 Ok(BufferedPhysicalDrive {
174 drive,
175 current_sector: Cursor::new(sector),
176 current_sector_num: 0,
177 geometry: geo,
178 })
179 }
180
181 pub fn size(&self) -> u64 {
183 self.geometry.size()
184 }
185}
186
187impl BufferedHarddiskVolume {
188 pub fn open(volume_num: u8) -> Result<Self, String> {
197 let mut volume = HarddiskVolume::open(volume_num)?;
198 let mut sector = vec![0; volume.geometry.bytes_per_sector as usize];
199 match volume.read(&mut sector) {
200 Ok(_) => (),
201 Err(e) => return Err(format!("{}", e)),
202 }
203 let geo = volume.geometry;
204 let info = volume.partition_info;
205 Ok(BufferedHarddiskVolume {
206 volume,
207 current_sector: Cursor::new(sector),
208 current_sector_num: 0,
209 geometry: geo,
210 partition_info: info,
211 })
212 }
213
214 pub fn size(&self) -> u64 {
216 self.partition_info.partition_length
217 }
218}
219
220impl_buffered!(
221 (BufferedPhysicalDrive, drive),
222 (BufferedHarddiskVolume, volume)
223);
224
225impl DiskGeometry {
226 pub fn size(&self) -> u64 {
228 self.sectors() * self.bytes_per_sector as u64
229 }
230
231 pub fn sectors(&self) -> u64 {
233 self.cylinders as u64 * self.tracks_per_cylinder as u64 * self.sectors_per_track as u64
234 }
235}
236
237impl From<DISK_GEOMETRY> for DiskGeometry {
238 fn from(geo: DISK_GEOMETRY) -> Self {
239 DiskGeometry {
240 cylinders: unsafe { *geo.Cylinders.QuadPart() },
241 media_type: geo.MediaType,
242 tracks_per_cylinder: geo.TracksPerCylinder,
243 sectors_per_track: geo.SectorsPerTrack,
244 bytes_per_sector: geo.BytesPerSector,
245 }
246 }
247}
248
249impl From<PARTITION_INFORMATION_EX> for PartitionInfo {
250 fn from(info: PARTITION_INFORMATION_EX) -> Self {
251 PartitionInfo {
252 partition_style: PartitionStyle::try_from(info.PartitionStyle).unwrap(),
253 starting_offset: unsafe { *info.StartingOffset.QuadPart() } as _,
254 partition_length: unsafe { *info.PartitionLength.QuadPart() } as _,
255 partition_number: info.PartitionNumber,
256 }
257 }
258}
259
260fn open_handle(path: &str) -> Result<HANDLE, String> {
261 let path = win32::win32_string(&path);
262 let handle = unsafe {
263 fs::CreateFileW(
264 path.as_ptr(),
265 GENERIC_READ,
266 FILE_SHARE_READ | FILE_SHARE_WRITE,
267 null_mut(),
268 OPEN_EXISTING,
269 FILE_FLAG_NO_BUFFERING,
270 null_mut(),
271 )
272 };
273 if handle == INVALID_HANDLE_VALUE {
274 let err = win32::last_error();
275 Err(match err {
276 2 => "could not open handle because the device was not found".to_string(),
277 5 => "could not open handle because access was denied - do you have administrator privileges?".to_string(),
278 _ => format!("got invalid handle: error code {:#08x}", err)
279 })
280 } else {
281 Ok(handle)
282 }
283}
284
285fn geometry(drive: &HANDLE) -> Result<DiskGeometry, String> {
288 let mut geo = Default::default();
289 let mut bytes_returned = 0u32;
290 let geo_ptr: *mut DISK_GEOMETRY = &mut geo;
291 let r = unsafe {
292 DeviceIoControl(
293 *drive,
294 IOCTL_DISK_GET_DRIVE_GEOMETRY,
295 null_mut(),
296 0,
297 geo_ptr as *mut c_void,
298 size_of::<DISK_GEOMETRY>() as u32,
299 &mut bytes_returned,
300 null_mut(),
301 )
302 };
303 if r == 0 {
304 Err(format!(
305 "could not get geometry: error code {:#08x}",
306 win32::last_error()
307 ))
308 } else {
309 Ok(DiskGeometry::from(geo))
310 }
311}
312
313fn partition_info(partition: &HANDLE) -> Result<PartitionInfo, String> {
316 let mut info = Default::default();
317 let mut bytes_returned = 0u32;
318 let info_ptr: *mut PARTITION_INFORMATION_EX = &mut info;
319 let r = unsafe {
320 DeviceIoControl(
321 *partition,
322 IOCTL_DISK_GET_PARTITION_INFO_EX,
323 null_mut(),
324 0,
325 info_ptr as *mut c_void,
326 size_of::<PARTITION_INFORMATION_EX>() as u32,
327 &mut bytes_returned,
328 null_mut(),
329 )
330 };
331 if r == 0 {
332 Err(format!(
333 "could not get partition info: error code {:#08x}",
334 win32::last_error()
335 ))
336 } else {
337 Ok(PartitionInfo::from(info))
338 }
339}