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