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