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