1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Exported interface to basic qcow functionality to be used from C.
6
7 use libc::{EBADFD, EINVAL, EIO, ENOSYS};
8 use std::ffi::CStr;
9 use std::fs::{File, OpenOptions};
10 use std::io::{Seek, SeekFrom};
11 use std::mem::forget;
12 use std::os::raw::{c_char, c_int};
13 use std::os::unix::io::FromRawFd;
14 use std::panic::catch_unwind;
15
16 use qcow::{ImageType, QcowFile};
17 use sys_util::{flock, FileSetLen, FlockOperation};
18
19 trait DiskFile: FileSetLen + Seek {}
20 impl<D: FileSetLen + Seek> DiskFile for D {}
21
22 #[no_mangle]
create_qcow_with_size(path: *const c_char, virtual_size: u64) -> c_int23 pub unsafe extern "C" fn create_qcow_with_size(path: *const c_char, virtual_size: u64) -> c_int {
24 // NULL pointers are checked, but this will access any other invalid pointer passed from C
25 // code. It's the caller's responsibility to pass a valid pointer.
26 if path.is_null() {
27 return -EINVAL;
28 }
29 let c_str = CStr::from_ptr(path);
30 let file_path = match c_str.to_str() {
31 Ok(s) => s,
32 Err(_) => return -EINVAL,
33 };
34
35 let file = match OpenOptions::new()
36 .create(true)
37 .read(true)
38 .write(true)
39 .open(file_path)
40 {
41 Ok(f) => f,
42 Err(_) => return -1,
43 };
44
45 match QcowFile::new(file, virtual_size) {
46 Ok(_) => 0,
47 Err(_) => -1,
48 }
49 }
50
51 #[no_mangle]
expand_disk_image(path: *const c_char, virtual_size: u64) -> c_int52 pub unsafe extern "C" fn expand_disk_image(path: *const c_char, virtual_size: u64) -> c_int {
53 // NULL pointers are checked, but this will access any other invalid pointer passed from C
54 // code. It's the caller's responsibility to pass a valid pointer.
55 if path.is_null() {
56 return -EINVAL;
57 }
58 let c_str = CStr::from_ptr(path);
59 let file_path = match c_str.to_str() {
60 Ok(s) => s,
61 Err(_) => return -EINVAL,
62 };
63
64 let raw_image = match OpenOptions::new().read(true).write(true).open(file_path) {
65 Ok(f) => f,
66 Err(_) => return -EIO,
67 };
68
69 // Lock the disk image to prevent other processes from using it.
70 if let Err(_) = flock(&raw_image, FlockOperation::LockExclusive, true) {
71 return -EIO;
72 }
73
74 let image_type = match qcow::detect_image_type(&raw_image) {
75 Ok(t) => t,
76 Err(_) => return -EINVAL,
77 };
78
79 let mut disk_image: Box<DiskFile> = match image_type {
80 ImageType::Raw => Box::new(raw_image),
81 ImageType::Qcow2 => match QcowFile::from(raw_image) {
82 Ok(f) => Box::new(f),
83 Err(_) => return -EINVAL,
84 },
85 };
86
87 // For safety against accidentally shrinking the disk image due to a
88 // programming error elsewhere, verify that the new size is larger than
89 // the current size. This is safe against races due to the exclusive
90 // flock() taken above - this is only an advisory lock, but it is also
91 // acquired by other instances of this function as well as crosvm
92 // itself when running a VM, so this should be safe in all cases that
93 // can access a disk image in normal operation.
94 let current_size = match disk_image.seek(SeekFrom::End(0)) {
95 Ok(len) => len,
96 Err(_) => return -EIO,
97 };
98 if current_size >= virtual_size {
99 return 0;
100 }
101
102 match disk_image.set_len(virtual_size) {
103 Ok(_) => 0,
104 Err(_) => -ENOSYS,
105 }
106 }
107
108 #[no_mangle]
convert_to_qcow2(src_fd: c_int, dst_fd: c_int) -> c_int109 pub unsafe extern "C" fn convert_to_qcow2(src_fd: c_int, dst_fd: c_int) -> c_int {
110 // The caller is responsible for passing valid file descriptors.
111 // The caller retains ownership of the file descriptors.
112 let src_file = File::from_raw_fd(src_fd);
113 let src_file_owned = src_file.try_clone();
114 forget(src_file);
115
116 let dst_file = File::from_raw_fd(dst_fd);
117 let dst_file_owned = dst_file.try_clone();
118 forget(dst_file);
119
120 match (src_file_owned, dst_file_owned) {
121 (Ok(src_file), Ok(dst_file)) => {
122 catch_unwind(
123 || match qcow::convert(src_file, dst_file, ImageType::Qcow2) {
124 Ok(_) => 0,
125 Err(_) => -EIO,
126 },
127 )
128 .unwrap_or(-EIO)
129 }
130 _ => -EBADFD,
131 }
132 }
133
134 #[no_mangle]
convert_to_raw(src_fd: c_int, dst_fd: c_int) -> c_int135 pub unsafe extern "C" fn convert_to_raw(src_fd: c_int, dst_fd: c_int) -> c_int {
136 // The caller is responsible for passing valid file descriptors.
137 // The caller retains ownership of the file descriptors.
138 let src_file = File::from_raw_fd(src_fd);
139 let src_file_owned = src_file.try_clone();
140 forget(src_file);
141
142 let dst_file = File::from_raw_fd(dst_fd);
143 let dst_file_owned = dst_file.try_clone();
144 forget(dst_file);
145
146 match (src_file_owned, dst_file_owned) {
147 (Ok(src_file), Ok(dst_file)) => {
148 catch_unwind(|| match qcow::convert(src_file, dst_file, ImageType::Raw) {
149 Ok(_) => 0,
150 Err(_) => -EIO,
151 })
152 .unwrap_or(-EIO)
153 }
154 _ => -EBADFD,
155 }
156 }
157