windows_drives/
macros.rs

1macro_rules! impl_physical {
2    ( $($type:ident),+ ) => {
3        $(
4            impl Read for $type {
5                fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
6                    let sector_size = self.geometry.bytes_per_sector;
7                    if buf.len() < sector_size as usize {
8                        return Err(io::Error::new(
9                            io::ErrorKind::InvalidInput,
10                            format!("Could not read: buffer too small, must be at least {} bytes", sector_size)
11                        ));
12                    } else if (buf.len() % sector_size as usize) != 0 {
13                        return Err(io::Error::new(
14                            io::ErrorKind::InvalidInput,
15                            format!(
16                                "Could not read: buffer size must be a multiple of {} but is {}",
17                                sector_size, buf.len()
18                            )
19                        ));
20                    }
21
22                    let mut bytes_read = 0u32;
23                    let res = unsafe {
24                        fs::ReadFile(
25                            self.handle,
26                            buf.as_mut_ptr() as *mut c_void,
27                            (buf.len() as u32 / sector_size) * sector_size,
28                            &mut bytes_read,
29                            null_mut()
30                        )
31                    };
32                    if res == 0 {
33                        Err(io::Error::new(
34                            io::ErrorKind::Other,
35                            format!(
36                                "Could not read: error code {:#08x}",
37                                win32::last_error()
38                            )
39                        ))
40                    } else {
41                        Ok(bytes_read as usize)
42                    }
43                }
44            }
45
46            impl Seek for $type {
47                fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
48                    fn check_pos(s: &$type, p: u64) -> io::Result<()> {
49                        if p > i64::MAX as u64 {
50                            Err(io::Error::new(
51                                io::ErrorKind::InvalidInput,
52                                format!("can only seek to pos <= {}", i64::MAX)
53                            ))
54                        } else if (p % s.geometry.bytes_per_sector as u64) != 0 {
55                            Err(io::Error::new(
56                                io::ErrorKind::InvalidInput,
57                                format!("can only seek to multiples of {}", s.geometry.bytes_per_sector)
58                            ))
59                        } else {
60                            Ok(())
61                        }
62                    }
63
64                    let mut dist = LARGE_INTEGER::default();
65                    let mut new_pos = LARGE_INTEGER::default();
66
67                    let res = match pos {
68                        SeekFrom::Start(p) => {
69                            check_pos(self, p)?;
70                            unsafe {
71                                *dist.QuadPart_mut() = p as i64;
72                                fs::SetFilePointerEx(self.handle, dist, &mut new_pos, FILE_BEGIN)
73                            }
74                        },
75                        SeekFrom::Current(p) => {
76                            unsafe {
77                                *dist.QuadPart_mut() = p;
78                                fs::SetFilePointerEx(self.handle, dist, &mut new_pos, FILE_CURRENT)
79                            }
80                        },
81                        SeekFrom::End(p) => {
82                            // SetFilePointerEx seems to always fail when using FILE_END, therefore roll our own
83                            let p = min(0, p); // limit p to non-positive values (for p > 0 we seek to the end)
84                            return self.seek(SeekFrom::Start((self.size() as i128 + p as i128) as u64));
85                        }
86                    };
87
88                    if res == 0 {
89                        Err(io::Error::new(
90                            io::ErrorKind::Other,
91                            format!("Could not seek: error code {} (did you respect sector boundaries?)", win32::last_error())
92                        ))
93                    } else {
94                        Ok(unsafe { *new_pos.QuadPart() } as u64)
95                    }
96                }
97            }
98
99            impl Drop for $type {
100                fn drop(&mut self) {
101                    unsafe {
102                        CloseHandle(self.handle);
103                    }
104                }
105            }
106        )+
107    }
108}
109
110macro_rules! impl_buffered {
111    ( $(($type:ident, $handle:ident)),+ ) => {
112        $(
113            impl $type {
114                // updates the internal state so that current sector is the one with the given number
115                // performs boundary checks (returns an error if sector_num too small, goes to last
116                // sector if sector_num too big)
117                fn go_to_sector(&mut self, sector_num: u64) -> io::Result<()> {
118                    if sector_num == self.current_sector_num {
119                        return Ok(());
120                    }
121                    let sector_num = min(sector_num, self.geometry.sectors());
122                    let sector_size = self.$handle.geometry.bytes_per_sector as u64;
123                    let sector_pos = sector_num * sector_size;
124                    self.$handle.seek(SeekFrom::Start(sector_pos))?;
125
126                    // last sector may not be of full length or length zero if max_pos % sector_size == 0
127                    let max_pos = self.size();
128                    let next_sector_len = min(sector_size, max_pos - sector_pos);
129                    let mut sector = vec![0; next_sector_len as usize];
130                    self.$handle.read_exact(&mut sector)?;
131
132                    self.current_sector = Cursor::new(sector);
133                    self.current_sector_num = sector_num;
134
135                    Ok(())
136                }
137            }
138
139            impl Read for $type {
140                fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
141                    if self.current_sector.position() == self.geometry.bytes_per_sector as u64 {
142                        self.go_to_sector(self.current_sector_num + 1)?;
143                    }
144
145                    self.current_sector.read(buf)
146                }
147            }
148
149            impl Seek for $type {
150                fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
151                    match pos {
152                        SeekFrom::Start(p) => {
153                            let sector = p / self.geometry.bytes_per_sector as u64;
154                            self.go_to_sector(sector)?;
155                            self.current_sector.seek(SeekFrom::Start(p % self.geometry.bytes_per_sector as u64))?;
156                        },
157                        SeekFrom::End(p) => {
158                            let p = min(0, p); // limit p to non-positive values (for p > 0 we seek to the end)
159                            let end = self.size() as i128;
160                            let sector = (end + p as i128) / self.geometry.bytes_per_sector as i128;
161                            self.go_to_sector(sector as u64)?;
162
163                            let target_pos = (end + p as i128) - sector * self.geometry.bytes_per_sector as i128;
164                            self.current_sector.seek(SeekFrom::Start(target_pos as u64))?;
165                        },
166                        SeekFrom::Current(p) => {
167                            let current = (self.current_sector_num * self.geometry.bytes_per_sector as u64 + self.current_sector.position()) as i128;
168                            let sector = (current + p as i128) / self.geometry.bytes_per_sector as i128;
169                            self.go_to_sector(sector as u64)?;
170
171                            let target_pos = (current + p as i128) - sector * self.geometry.bytes_per_sector as i128;
172                            self.current_sector.seek(SeekFrom::Start(target_pos as u64))?;
173                        }
174                    }
175
176                    Ok(self.current_sector_num * self.geometry.bytes_per_sector as u64 + self.current_sector.position())
177                }
178            }
179        )+
180    }
181}