1use crate::dev::{Qcow2Dev, Qcow2DevParams};
2use crate::error::Qcow2Result;
3use crate::helpers::Qcow2IoBuf;
4use crate::meta::Qcow2Header;
5use crate::ops::*;
6#[cfg(not(target_os = "windows"))]
7use crate::sync_io::Qcow2IoSync;
8#[cfg(target_os = "linux")]
9use crate::uring::Qcow2IoUring;
10use async_recursion::async_recursion;
11use std::io::Write;
12use std::path::{Path, PathBuf};
13
14#[macro_export]
15macro_rules! qcow2_default_params {
16 ($ro: expr, $dio: expr) => {
17 Qcow2DevParams::new(9, None, None, $ro, $dio)
18 };
19}
20
21const DEF_HEADER_SIZE: usize = 4096;
23
24const MAX_HEADER_SIZE: usize = 65536;
26
27pub fn qcow2_alloc_dev_sync<T: Qcow2IoOps>(
30 path: &Path,
31 io: T,
32 params: &Qcow2DevParams,
33) -> Qcow2Result<(Qcow2Dev<T>, Option<PathBuf>)> {
34 fn read_header(path: &Path, bytes: usize) -> Qcow2Result<Qcow2IoBuf<u8>> {
35 use std::io::Read;
36 let mut buf = Qcow2IoBuf::<u8>::new(bytes);
37 let mut file = std::fs::File::open(path).unwrap();
38 let _ = file.read(&mut buf).unwrap();
39 Ok(buf)
40 }
41
42 let buf = read_header(path, DEF_HEADER_SIZE)?;
43 let header = match Qcow2Header::from_buf(&buf) {
44 Ok(h) => h,
45 Err(_) => {
46 let buf = read_header(path, MAX_HEADER_SIZE)?;
47 Qcow2Header::from_buf(&buf)?
48 }
49 };
50 let back_path = header.backing_filename().map(|s| PathBuf::from(s.clone()));
51
52 Ok((
53 Qcow2Dev::new(path, header, params, io).expect("new dev failed"),
54 back_path,
55 ))
56}
57
58pub async fn qcow2_alloc_dev<T: Qcow2IoOps>(
61 path: &Path,
62 io: T,
63 params: &Qcow2DevParams,
64) -> Qcow2Result<(Qcow2Dev<T>, Option<PathBuf>)> {
65 async fn read_header<T: Qcow2IoOps>(io: &T, bytes: usize) -> Qcow2Result<Qcow2IoBuf<u8>> {
66 let mut buf = Qcow2IoBuf::<u8>::new(bytes);
67 let _ = io.read_to(0, &mut buf).await?;
68 Ok(buf)
69 }
70 let buf = read_header(&io, DEF_HEADER_SIZE).await?;
71 let header = match Qcow2Header::from_buf(&buf) {
72 Ok(h) => h,
73 Err(_) => {
74 let buf = read_header(&io, MAX_HEADER_SIZE).await?;
75 Qcow2Header::from_buf(&buf)?
76 }
77 };
78 let back_path = header.backing_filename().map(|s| PathBuf::from(s.clone()));
79
80 Ok((
81 Qcow2Dev::new(path, header, params, io).expect("new dev failed"),
82 back_path,
83 ))
84}
85
86#[macro_export]
90macro_rules! qcow2_setup_dev_fn {
91 ($type:ty, $fn_name: ident) => {
92 #[async_recursion(?Send)]
93 pub async fn $fn_name(
94 path: &Path,
95 params: &Qcow2DevParams,
96 ) -> Qcow2Result<Qcow2Dev<$type>> {
97 let io = <$type>::new(path, params.is_read_only(), params.is_direct_io()).await;
98 let (mut dev, backing) = qcow2_alloc_dev(&path, io, params).await?;
99 match backing {
100 Some(back_path) => {
101 let p = params.clone();
102 p.mark_backing_dev(Some(true));
103 let bdev = $fn_name(&back_path.as_path(), &p).await?;
104 dev.set_backing_dev(Box::new(bdev));
105 }
106 _ => {}
107 };
108
109 dev.__qcow2_prep_io().await?;
110 Ok(dev)
111 }
112 };
113}
114
115#[cfg(target_os = "linux")]
116qcow2_setup_dev_fn!(Qcow2IoUring, qcow2_setup_dev_uring);
117qcow2_setup_dev_fn!(crate::tokio_io::Qcow2IoTokio, qcow2_setup_dev_tokio);
118
119#[macro_export]
124macro_rules! qcow2_setup_dev_fn_sync {
125 ($type:ty, $fn_name: ident) => {
126 pub fn $fn_name(path: &Path, params: &Qcow2DevParams) -> Qcow2Result<Qcow2Dev<$type>> {
127 let io = <$type>::new(path, params.is_read_only(), params.is_direct_io());
128 let (mut dev, backing) = qcow2_alloc_dev_sync(&path, io, params)?;
129 match backing {
130 Some(back_path) => {
131 let p = params.clone();
132 p.mark_backing_dev(Some(true));
133 let bdev = $fn_name(&back_path.as_path(), &p)?;
134 dev.set_backing_dev(Box::new(bdev));
135 }
136 _ => {}
137 };
138
139 Ok(dev)
140 }
141 };
142}
143
144#[cfg(not(target_os = "windows"))]
145qcow2_setup_dev_fn_sync!(Qcow2IoSync, qcow2_setup_dev_sync);
146
147fn make_qcow2_buf(cluster_bits: usize, refcount_order: u8, size: u64) -> Vec<u8> {
148 let bs_shift = 9_u8;
149 let bs = 1 << bs_shift;
150 let (rc_t, rc_b, _) =
151 Qcow2Header::calculate_meta_params(size, cluster_bits, refcount_order, bs);
152 let clusters = 1 + rc_t.1 + rc_b.1;
153 let img_size = ((clusters as usize) << cluster_bits) + 512;
154 let mut buf = vec![0u8; img_size];
155
156 Qcow2Header::format_qcow2(&mut buf, size, cluster_bits, refcount_order, bs).unwrap();
157
158 buf
159}
160
161pub fn make_temp_qcow2_img(
162 size: u64,
163 cluster_bits: usize,
164 refcount_order: u8,
165) -> tempfile::NamedTempFile {
166 let tmpfile = tempfile::NamedTempFile::new().unwrap();
167 let mut file = std::fs::File::create(tmpfile.path()).unwrap();
168
169 let buf = make_qcow2_buf(cluster_bits, refcount_order, size);
170 file.write_all(&buf).unwrap();
171
172 tmpfile
173}