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