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::{EINVAL, EIO, ENOSYS};
8 use std::ffi::CStr;
9 use std::fs::OpenOptions;
10 use std::os::raw::{c_char, c_int};
11
12 use base::{flock, FlockOperation};
13 use disk::{DiskFile, ImageType, QcowFile};
14
15 #[no_mangle]
create_qcow_with_size(path: *const c_char, virtual_size: u64) -> c_int16 pub unsafe extern "C" fn create_qcow_with_size(path: *const c_char, virtual_size: u64) -> c_int {
17 // NULL pointers are checked, but this will access any other invalid pointer passed from C
18 // code. It's the caller's responsibility to pass a valid pointer.
19 if path.is_null() {
20 return -EINVAL;
21 }
22 let c_str = CStr::from_ptr(path);
23 let file_path = match c_str.to_str() {
24 Ok(s) => s,
25 Err(_) => return -EINVAL,
26 };
27
28 let file = match OpenOptions::new()
29 .create(true)
30 .read(true)
31 .write(true)
32 .open(file_path)
33 {
34 Ok(f) => f,
35 Err(_) => return -1,
36 };
37
38 match QcowFile::new(file, virtual_size) {
39 Ok(_) => 0,
40 Err(_) => -1,
41 }
42 }
43
44 #[no_mangle]
expand_disk_image(path: *const c_char, virtual_size: u64) -> c_int45 pub unsafe extern "C" fn expand_disk_image(path: *const c_char, virtual_size: u64) -> c_int {
46 // NULL pointers are checked, but this will access any other invalid pointer passed from C
47 // code. It's the caller's responsibility to pass a valid pointer.
48 if path.is_null() {
49 return -EINVAL;
50 }
51 let c_str = CStr::from_ptr(path);
52 let file_path = match c_str.to_str() {
53 Ok(s) => s,
54 Err(_) => return -EINVAL,
55 };
56
57 let raw_image = match OpenOptions::new().read(true).write(true).open(file_path) {
58 Ok(f) => f,
59 Err(_) => return -EIO,
60 };
61
62 // Lock the disk image to prevent other processes from using it.
63 if let Err(_) = flock(&raw_image, FlockOperation::LockExclusive, true) {
64 return -EIO;
65 }
66
67 let image_type = match disk::detect_image_type(&raw_image) {
68 Ok(t) => t,
69 Err(_) => return -EINVAL,
70 };
71
72 let disk_image: Box<dyn DiskFile> = match image_type {
73 ImageType::Raw => Box::new(raw_image),
74 ImageType::Qcow2 => match QcowFile::from(raw_image) {
75 Ok(f) => Box::new(f),
76 Err(_) => return -EINVAL,
77 },
78 _ => return -EINVAL,
79 };
80
81 // For safety against accidentally shrinking the disk image due to a
82 // programming error elsewhere, verify that the new size is larger than
83 // the current size. This is safe against races due to the exclusive
84 // flock() taken above - this is only an advisory lock, but it is also
85 // acquired by other instances of this function as well as crosvm
86 // itself when running a VM, so this should be safe in all cases that
87 // can access a disk image in normal operation.
88 let current_size = match disk_image.get_len() {
89 Ok(len) => len,
90 Err(_) => return -EIO,
91 };
92 if current_size >= virtual_size {
93 return 0;
94 }
95
96 match disk_image.set_len(virtual_size) {
97 Ok(_) => 0,
98 Err(_) => -ENOSYS,
99 }
100 }
101