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