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