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