• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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