• 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::{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