• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::{
6     fs::File,
7     io::{
8         Error, ErrorKind, Seek, SeekFrom, {self},
9     },
10 };
11 
12 #[path = "win/punch_hole.rs"]
13 mod punch_hole;
14 
15 #[path = "win/write_zeros.rs"]
16 mod write_zeros;
17 
18 use super::write_zeroes::punch_hole::execute_punch_hole;
19 
20 /// A trait for deallocating space in a file.
21 pub trait PunchHole {
22     /// Replace a range of bytes with a hole.
punch_hole(&mut self, offset: u64, length: u64) -> io::Result<()>23     fn punch_hole(&mut self, offset: u64, length: u64) -> io::Result<()>;
24 }
25 
26 impl PunchHole for File {
punch_hole(&mut self, offset: u64, length: u64) -> io::Result<()>27     fn punch_hole(&mut self, offset: u64, length: u64) -> io::Result<()> {
28         execute_punch_hole(self, offset, length)
29     }
30 }
31 
32 /// A trait for writing zeroes to a stream.
33 pub trait WriteZeroes {
34     /// Write up to `length` bytes of zeroes to the stream, returning how many bytes were written.
write_zeroes(&mut self, length: usize) -> io::Result<usize>35     fn write_zeroes(&mut self, length: usize) -> io::Result<usize>;
36 
37     /// Write zeroes to the stream until `length` bytes have been written.
38     ///
39     /// This method will continuously call `write_zeroes` until the requested
40     /// `length` is satisfied or an error is encountered.
write_zeroes_all(&mut self, mut length: usize) -> io::Result<()>41     fn write_zeroes_all(&mut self, mut length: usize) -> io::Result<()> {
42         while length > 0 {
43             match self.write_zeroes(length) {
44                 Ok(0) => return Err(Error::from(ErrorKind::WriteZero)),
45                 Ok(bytes_written) => {
46                     length = length
47                         .checked_sub(bytes_written)
48                         .ok_or_else(|| Error::from(ErrorKind::Other))?
49                 }
50                 Err(e) => {
51                     if e.kind() != ErrorKind::Interrupted {
52                         return Err(e);
53                     }
54                 }
55             }
56         }
57         Ok(())
58     }
59 }
60 
61 /// A trait for writing zeroes to an arbitrary position in a file.
62 pub trait WriteZeroesAt {
63     /// Write up to `length` bytes of zeroes starting at `offset`, returning how many bytes were
64     /// written.
write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result<usize>65     fn write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result<usize>;
66 
67     /// Write zeroes starting at `offset` until `length` bytes have been written.
68     ///
69     /// This method will continuously call `write_zeroes_at` until the requested
70     /// `length` is satisfied or an error is encountered.
write_zeroes_all_at(&mut self, mut offset: u64, mut length: usize) -> io::Result<()>71     fn write_zeroes_all_at(&mut self, mut offset: u64, mut length: usize) -> io::Result<()> {
72         while length > 0 {
73             match self.write_zeroes_at(offset, length) {
74                 Ok(0) => return Err(Error::from(ErrorKind::WriteZero)),
75                 Ok(bytes_written) => {
76                     length = length
77                         .checked_sub(bytes_written)
78                         .ok_or_else(|| Error::from(ErrorKind::Other))?;
79                     offset = offset
80                         .checked_add(bytes_written as u64)
81                         .ok_or_else(|| Error::from(ErrorKind::Other))?;
82                 }
83                 Err(e) => {
84                     if e.kind() != ErrorKind::Interrupted {
85                         return Err(e);
86                     }
87                 }
88             }
89         }
90         Ok(())
91     }
92 }
93 
94 impl<T: WriteZeroesAt + Seek> WriteZeroes for T {
write_zeroes(&mut self, length: usize) -> io::Result<usize>95     fn write_zeroes(&mut self, length: usize) -> io::Result<usize> {
96         let offset = self.seek(SeekFrom::Current(0))?;
97         let nwritten = self.write_zeroes_at(offset, length)?;
98         // Advance the seek cursor as if we had done a real write().
99         self.seek(SeekFrom::Current(nwritten as i64))?;
100         Ok(length)
101     }
102 }
103 
104 #[cfg(test)]
105 mod tests {
106     use super::*;
107     use std::{
108         fs::OpenOptions,
109         io::{Read, Seek, SeekFrom, Write},
110     };
111     use tempfile::TempDir;
112 
113     #[test]
simple_test()114     fn simple_test() {
115         let tempdir = TempDir::new().unwrap();
116         let mut path = tempdir.path().to_owned();
117         path.push("file");
118         let mut f = OpenOptions::new()
119             .read(true)
120             .write(true)
121             .create(true)
122             .open(&path)
123             .unwrap();
124         f.set_len(16384).unwrap();
125 
126         // Write buffer of non-zero bytes to offset 1234
127         let orig_data = [0x55u8; 5678];
128         f.seek(SeekFrom::Start(1234)).unwrap();
129         f.write_all(&orig_data).unwrap();
130 
131         // Read back the data plus some overlap on each side
132         let mut readback = [0u8; 16384];
133         f.seek(SeekFrom::Start(0)).unwrap();
134         f.read_exact(&mut readback).unwrap();
135         // Bytes before the write should still be 0
136         for read in &readback[0..1234] {
137             assert_eq!(*read, 0);
138         }
139         // Bytes that were just written should be 0x55
140         for read in &readback[1234..(1234 + 5678)] {
141             assert_eq!(*read, 0x55);
142         }
143         // Bytes after the written area should still be 0
144         for read in &readback[(1234 + 5678)..] {
145             assert_eq!(*read, 0);
146         }
147 
148         // Overwrite some of the data with zeroes
149         f.seek(SeekFrom::Start(2345)).unwrap();
150         f.write_zeroes_all(4321).expect("write_zeroes failed");
151         // Verify seek position after write_zeroes_all()
152         assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 2345 + 4321);
153 
154         // Read back the data and verify that it is now zero
155         f.seek(SeekFrom::Start(0)).unwrap();
156         f.read_exact(&mut readback).unwrap();
157         // Bytes before the write should still be 0
158         for read in &readback[0..1234] {
159             assert_eq!(*read, 0);
160         }
161         // Original data should still exist before the write_zeroes region
162         for read in &readback[1234..2345] {
163             assert_eq!(*read, 0x55);
164         }
165         // The write_zeroes region should now be zero
166         for read in &readback[2345..(2345 + 4321)] {
167             assert_eq!(*read, 0);
168         }
169         // Original data should still exist after the write_zeroes region
170         for read in &readback[(2345 + 4321)..(1234 + 5678)] {
171             assert_eq!(*read, 0x55);
172         }
173         // The rest of the file should still be 0
174         for read in &readback[(1234 + 5678)..] {
175             assert_eq!(*read, 0);
176         }
177     }
178 
179     #[test]
large_write_zeroes()180     fn large_write_zeroes() {
181         let tempdir = TempDir::new().unwrap();
182         let mut path = tempdir.path().to_owned();
183         path.push("file");
184         let mut f = OpenOptions::new()
185             .read(true)
186             .write(true)
187             .create(true)
188             .open(&path)
189             .unwrap();
190         f.set_len(16384).unwrap();
191 
192         // Write buffer of non-zero bytes
193         let orig_data = [0x55u8; 0x20000];
194         f.seek(SeekFrom::Start(0)).unwrap();
195         f.write_all(&orig_data).unwrap();
196 
197         // Overwrite some of the data with zeroes
198         f.seek(SeekFrom::Start(0)).unwrap();
199         f.write_zeroes_all(0x10001).expect("write_zeroes failed");
200         // Verify seek position after write_zeroes_all()
201         assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 0x10001);
202 
203         // Read back the data and verify that it is now zero
204         let mut readback = [0u8; 0x20000];
205         f.seek(SeekFrom::Start(0)).unwrap();
206         f.read_exact(&mut readback).unwrap();
207         // The write_zeroes region should now be zero
208         for read in &readback[0..0x10001] {
209             assert_eq!(*read, 0);
210         }
211         // Original data should still exist after the write_zeroes region
212         for read in &readback[0x10001..0x20000] {
213             assert_eq!(*read, 0x55);
214         }
215     }
216 }
217