xref: /aosp_15_r20/external/crosvm/base/src/write_zeroes.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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