disk/qcow/
qcow_raw_file.rs

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
5use std::fs::File;
6use std::io;
7use std::io::BufWriter;
8use std::io::Read;
9use std::io::Seek;
10use std::io::SeekFrom;
11use std::io::Write;
12use std::mem::size_of;
13use std::mem::size_of_val;
14
15use base::FileReadWriteAtVolatile;
16use base::VolatileSlice;
17use base::WriteZeroesAt;
18use zerocopy::IntoBytes;
19
20/// A qcow file. Allows reading/writing clusters and appending clusters.
21#[derive(Debug)]
22pub struct QcowRawFile {
23    file: File,
24    cluster_size: u64,
25    cluster_mask: u64,
26}
27
28impl QcowRawFile {
29    /// Creates a `QcowRawFile` from the given `File`, `None` is returned if `cluster_size` is not
30    /// a power of two.
31    pub fn from(file: File, cluster_size: u64) -> Option<Self> {
32        if cluster_size.count_ones() != 1 {
33            return None;
34        }
35        Some(QcowRawFile {
36            file,
37            cluster_size,
38            cluster_mask: cluster_size - 1,
39        })
40    }
41
42    /// Reads `count` 64 bit offsets and returns them as a vector.
43    /// `mask` optionally ands out some of the bits on the file.
44    pub fn read_pointer_table(
45        &mut self,
46        offset: u64,
47        count: u64,
48        mask: Option<u64>,
49    ) -> io::Result<Vec<u64>> {
50        let mut table = vec![0; count as usize];
51        self.file.seek(SeekFrom::Start(offset))?;
52        self.file.read_exact(table.as_mut_bytes())?;
53        let mask = mask.unwrap_or(u64::MAX);
54        for ptr in &mut table {
55            *ptr = u64::from_be(*ptr) & mask;
56        }
57        Ok(table)
58    }
59
60    /// Reads a cluster's worth of 64 bit offsets and returns them as a vector.
61    /// `mask` optionally ands out some of the bits on the file.
62    pub fn read_pointer_cluster(&mut self, offset: u64, mask: Option<u64>) -> io::Result<Vec<u64>> {
63        let count = self.cluster_size / size_of::<u64>() as u64;
64        self.read_pointer_table(offset, count, mask)
65    }
66
67    /// Writes `table` of u64 pointers to `offset` in the file.
68    /// `non_zero_flags` will be ORed with all non-zero values in `table`.
69    /// writing.
70    pub fn write_pointer_table(
71        &mut self,
72        offset: u64,
73        table: &[u64],
74        non_zero_flags: u64,
75    ) -> io::Result<()> {
76        self.file.seek(SeekFrom::Start(offset))?;
77        let mut buffer = BufWriter::with_capacity(size_of_val(table), &self.file);
78        for addr in table {
79            let val = if *addr == 0 {
80                0
81            } else {
82                *addr | non_zero_flags
83            };
84            buffer.write_all(&val.to_be_bytes())?;
85        }
86        buffer.flush()?;
87        Ok(())
88    }
89
90    /// Read a refcount block from the file and returns a Vec containing the block.
91    /// Always returns a cluster's worth of data.
92    pub fn read_refcount_block(&mut self, offset: u64) -> io::Result<Vec<u16>> {
93        let count = self.cluster_size / size_of::<u16>() as u64;
94        let mut table = vec![0; count as usize];
95        self.file.seek(SeekFrom::Start(offset))?;
96        self.file.read_exact(table.as_mut_bytes())?;
97        for refcount in &mut table {
98            *refcount = u16::from_be(*refcount);
99        }
100        Ok(table)
101    }
102
103    /// Writes a refcount block to the file.
104    pub fn write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()> {
105        self.file.seek(SeekFrom::Start(offset))?;
106        let mut buffer = BufWriter::with_capacity(size_of_val(table), &self.file);
107        for count in table {
108            buffer.write_all(&count.to_be_bytes())?;
109        }
110        buffer.flush()?;
111        Ok(())
112    }
113
114    /// Allocates a new cluster at the end of the current file, return the address.
115    pub fn add_cluster_end(&mut self, max_valid_cluster_offset: u64) -> io::Result<Option<u64>> {
116        // Determine where the new end of the file should be and set_len, which
117        // translates to truncate(2).
118        let file_end: u64 = self.file.seek(SeekFrom::End(0))?;
119        let new_cluster_address: u64 = (file_end + self.cluster_size - 1) & !self.cluster_mask;
120
121        if new_cluster_address > max_valid_cluster_offset {
122            return Ok(None);
123        }
124
125        self.file.set_len(new_cluster_address + self.cluster_size)?;
126
127        Ok(Some(new_cluster_address))
128    }
129
130    /// Returns a reference to the underlying file.
131    pub fn file(&self) -> &File {
132        &self.file
133    }
134
135    /// Returns a mutable reference to the underlying file.
136    pub fn file_mut(&mut self) -> &mut File {
137        &mut self.file
138    }
139
140    /// Returns the size of the file's clusters.
141    pub fn cluster_size(&self) -> u64 {
142        self.cluster_size
143    }
144
145    /// Returns the offset of `address` within a cluster.
146    pub fn cluster_offset(&self, address: u64) -> u64 {
147        address & self.cluster_mask
148    }
149
150    /// Zeros out a cluster in the file.
151    pub fn zero_cluster(&mut self, address: u64) -> io::Result<()> {
152        let cluster_size = self.cluster_size as usize;
153        self.file.write_zeroes_all_at(address, cluster_size)?;
154        Ok(())
155    }
156
157    /// Writes
158    pub fn write_cluster(&mut self, address: u64, mut initial_data: Vec<u8>) -> io::Result<()> {
159        if (initial_data.len() as u64) < self.cluster_size {
160            return Err(io::Error::new(
161                io::ErrorKind::InvalidInput,
162                "`initial_data` is too small",
163            ));
164        }
165        let volatile_slice = VolatileSlice::new(&mut initial_data[..self.cluster_size as usize]);
166        self.file.write_all_at_volatile(volatile_slice, address)
167    }
168}