1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#![warn(missing_docs)]

//! This crate can parse and read qcow2 virtual disks, as used by qemu and other emulators.
//!
//! [Qcow2](https://en.wikipedia.org/wiki/Qcow) is a flexible format for disk images, that only
//! allocates space as needed. It has many other interesting features.
//!
//! The following featuers are supported by this crate:
//!
//!  * Reading blocks of virtual disk data.
//!  * Reading data that is not aligned to block boundaries.
//!  * Parsing and validation of the header.
//!  * Reporting the names of any unsupported features, using the "feature name table" extension.
//!  * Basic caching of guest data locations, so nearby reads will be fast.
//!
//! These features are not yet supported, but should be easy to add:
//!
//! * Listing and reading snapshots.
//! * Reading version 2, currently only version 3 is supported.
//! * Reading compressed data.
//! * Backing file support, so you can chain qcow2 files together.
//! * Reporting information about images.
//!
//! These features are harder, or less interesting to me. Patches welcome!
//!
//! * Reading encrypted qcow2 files.
//! * Writing virtual disk data.
//! * Repairing the disk if refcounts are out of date.
//! * Compacting the virtual disk so it takes less space.
//! * Maintaining a "dirty bitmap" to make backups faster.
//! * Creating new qcow2 images.
//! * Creating new snapshots.
//! * Checking qcow2 images for inconsistencies.
//! * Merging images into their backing file.
//! * Resizing images.
//!
//! The repository for this crate is at https://github.com/vasi/qcow2-rs

extern crate byteorder;
extern crate lru_cache;
extern crate positioned_io;

mod error;
mod extension;
mod feature;
mod header;
mod int;
mod read;
pub use error::Error;
pub use read::Reader;

use std::fmt::{self, Debug, Formatter};
use std::result;
use std::sync::Mutex;

use byteorder::BigEndian;
use lru_cache::LruCache;
use positioned_io::{ReadAt, ByteIo};


const L2_CACHE_SIZE: usize = 32;

/// A qcow2 image.
///
/// # Examples
///
/// ```no_run
/// extern crate positioned_io;
/// extern crate qcow2;
///
/// # use std::fs::File;
/// use positioned_io::ReadAt;
/// use qcow2::Qcow2;
///
/// # fn foo() -> qcow2::Result<()> {
///
/// // Open a file.
/// let file = try!(File::open("image.qcow2"));
/// let qcow = try!(Qcow2::open(file));
///
/// // Read some data.
/// let reader = try!(qcow.reader());
/// let mut buf = vec![0, 4096];
/// try!(reader.read_exact_at(5 * 1024 * 1024, &mut buf));
///
/// # Ok(()) } fn main() { foo().unwrap(); }
/// ```
pub struct Qcow2<I>
    where I: ReadAt
{
    header: header::Header,
    io: ByteIo<I, BigEndian>,

    l2_cache: Mutex<LruCache<u64, u64>>,
}

/// The result type for operations on qcow2 images.
pub type Result<T> = std::result::Result<T, Error>;

impl<I> Qcow2<I>
    where I: ReadAt
{
    /// Open a source of data as a qcow2 image.
    ///
    /// Usually the data source `io` will be a file.
    pub fn open(io: I) -> Result<Self> {
        let io: ByteIo<_, BigEndian> = ByteIo::new(io);
        let mut q = Qcow2 {
            header: Default::default(),
            io: io,
            l2_cache: Mutex::new(LruCache::new(L2_CACHE_SIZE)),
        };
        try!(q.header.read(&mut q.io));
        Ok(q)
    }

    /// Get the size of each block of this qcow2 image.
    pub fn cluster_size(&self) -> u64 {
        self.header.cluster_size()
    }

    /// Get the size of the virtual image.
    ///
    /// This is likely to differ from the size of the qcow2 file itself, since the file can grow.
    pub fn guest_size(&self) -> u64 {
        self.header.guest_size()
    }
}

impl<I> Debug for Qcow2<I>
    where I: ReadAt
{
    fn fmt(&self, f: &mut Formatter) -> result::Result<(), fmt::Error> {
        f.debug_struct("Qcow2")
            .field("header", &self.header)
            .finish()
    }
}