• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The ChromiumOS Authors
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::fs::File;
6 use std::io;
7 use std::io::Error;
8 use std::io::ErrorKind;
9 
10 /// A trait for deallocating space in a file.
11 pub trait PunchHole {
12     /// Replace a range of bytes with a hole.
punch_hole(&self, offset: u64, length: u64) -> io::Result<()>13     fn punch_hole(&self, offset: u64, length: u64) -> io::Result<()>;
14 }
15 
16 impl PunchHole for File {
punch_hole(&self, offset: u64, length: u64) -> io::Result<()>17     fn punch_hole(&self, offset: u64, length: u64) -> io::Result<()> {
18         crate::platform::file_punch_hole(self, offset, length)
19     }
20 }
21 
22 /// A trait for deallocating space in a file of a mutable reference
23 pub trait PunchHoleMut {
24     /// Replace a range of bytes with a hole.
punch_hole_mut(&mut self, offset: u64, length: u64) -> io::Result<()>25     fn punch_hole_mut(&mut self, offset: u64, length: u64) -> io::Result<()>;
26 }
27 
28 impl<T: PunchHole> PunchHoleMut for T {
punch_hole_mut(&mut self, offset: u64, length: u64) -> io::Result<()>29     fn punch_hole_mut(&mut self, offset: u64, length: u64) -> io::Result<()> {
30         self.punch_hole(offset, length)
31     }
32 }
33 
34 /// A trait for writing zeroes to an arbitrary position in a file.
35 pub trait WriteZeroesAt {
36     /// Write up to `length` bytes of zeroes starting at `offset`, returning how many bytes were
37     /// written.
write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result<usize>38     fn write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result<usize>;
39 
40     /// Write zeroes starting at `offset` until `length` bytes have been written.
41     ///
42     /// This method will continuously call `write_zeroes_at` until the requested
43     /// `length` is satisfied or an error is encountered.
write_zeroes_all_at(&mut self, mut offset: u64, mut length: usize) -> io::Result<()>44     fn write_zeroes_all_at(&mut self, mut offset: u64, mut length: usize) -> io::Result<()> {
45         while length > 0 {
46             match self.write_zeroes_at(offset, length) {
47                 Ok(0) => return Err(Error::from(ErrorKind::WriteZero)),
48                 Ok(bytes_written) => {
49                     length = length
50                         .checked_sub(bytes_written)
51                         .ok_or_else(|| Error::from(ErrorKind::Other))?;
52                     offset = offset
53                         .checked_add(bytes_written as u64)
54                         .ok_or_else(|| Error::from(ErrorKind::Other))?;
55                 }
56                 Err(e) => {
57                     if e.kind() != ErrorKind::Interrupted {
58                         return Err(e);
59                     }
60                 }
61             }
62         }
63         Ok(())
64     }
65 }
66 
67 impl WriteZeroesAt for File {
write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result<usize>68     fn write_zeroes_at(&mut self, offset: u64, length: usize) -> io::Result<usize> {
69         crate::platform::file_write_zeroes_at(self, offset, length)
70     }
71 }
72 
73 #[cfg(test)]
74 mod tests {
75     use std::io::Read;
76     use std::io::Seek;
77     use std::io::SeekFrom;
78     use std::io::Write;
79 
80     use tempfile::tempfile;
81 
82     use super::*;
83 
84     #[test]
simple_test()85     fn simple_test() {
86         let mut f = tempfile().unwrap();
87 
88         // Extend and fill the file with zero bytes.
89         // This is not using set_len() because that fails on wine.
90         let init_data = [0x00u8; 16384];
91         f.write_all(&init_data).unwrap();
92 
93         // Write buffer of non-zero bytes to offset 1234
94         let orig_data = [0x55u8; 5678];
95         f.seek(SeekFrom::Start(1234)).unwrap();
96         f.write_all(&orig_data).unwrap();
97 
98         // Read back the data plus some overlap on each side
99         let mut readback = [0u8; 16384];
100         f.seek(SeekFrom::Start(0)).unwrap();
101         f.read_exact(&mut readback).unwrap();
102         // Bytes before the write should still be 0
103         for read in &readback[0..1234] {
104             assert_eq!(*read, 0);
105         }
106         // Bytes that were just written should be 0x55
107         for read in &readback[1234..(1234 + 5678)] {
108             assert_eq!(*read, 0x55);
109         }
110         // Bytes after the written area should still be 0
111         for read in &readback[(1234 + 5678)..] {
112             assert_eq!(*read, 0);
113         }
114 
115         // Overwrite some of the data with zeroes
116         f.write_zeroes_all_at(2345, 4321)
117             .expect("write_zeroes failed");
118 
119         // Read back the data and verify that it is now zero
120         f.seek(SeekFrom::Start(0)).unwrap();
121         f.read_exact(&mut readback).unwrap();
122         // Bytes before the write should still be 0
123         for read in &readback[0..1234] {
124             assert_eq!(*read, 0);
125         }
126         // Original data should still exist before the write_zeroes region
127         for read in &readback[1234..2345] {
128             assert_eq!(*read, 0x55);
129         }
130         // The write_zeroes region should now be zero
131         for read in &readback[2345..(2345 + 4321)] {
132             assert_eq!(*read, 0);
133         }
134         // Original data should still exist after the write_zeroes region
135         for read in &readback[(2345 + 4321)..(1234 + 5678)] {
136             assert_eq!(*read, 0x55);
137         }
138         // The rest of the file should still be 0
139         for read in &readback[(1234 + 5678)..] {
140             assert_eq!(*read, 0);
141         }
142     }
143 
144     #[test]
large_write_zeroes()145     fn large_write_zeroes() {
146         let mut f = tempfile().unwrap();
147 
148         // Extend and fill the file with zero bytes.
149         // This is not using set_len() because that fails on wine.
150         let init_data = [0x00u8; 16384];
151         f.write_all(&init_data).unwrap();
152 
153         // Write buffer of non-zero bytes
154         let orig_data = [0x55u8; 0x20000];
155         f.seek(SeekFrom::Start(0)).unwrap();
156         f.write_all(&orig_data).unwrap();
157 
158         // Overwrite some of the data with zeroes
159         f.write_zeroes_all_at(0, 0x10001)
160             .expect("write_zeroes failed");
161 
162         // Read back the data and verify that it is now zero
163         let mut readback = [0u8; 0x20000];
164         f.seek(SeekFrom::Start(0)).unwrap();
165         f.read_exact(&mut readback).unwrap();
166         // The write_zeroes region should now be zero
167         for read in &readback[0..0x10001] {
168             assert_eq!(*read, 0);
169         }
170         // Original data should still exist after the write_zeroes region
171         for read in &readback[0x10001..0x20000] {
172             assert_eq!(*read, 0x55);
173         }
174     }
175 }
176