qcow_utils: add disk image expand function

This exports a new C API to resize a disk image.  The new function is
intended to only expand (increase in size) to avoid accidentally
truncating user data due to bugs elsewhere.

BUG=chromium:858815
TEST=build_test.py

Change-Id: I6f834209aba693618e0f51d920e7b73d4f2a9dfc
Signed-off-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1464384
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Zach Reizner <zachr@chromium.org>
This commit is contained in:
Daniel Verkamp 2019-02-07 16:52:08 -08:00 committed by chrome-bot
parent d39dd9af71
commit 348ccf1102
2 changed files with 68 additions and 1 deletions

View file

@ -13,6 +13,10 @@ extern "C" {
// Create a basic, empty qcow2 file that can grow to `virtual_size` at `path`.
int create_qcow_with_size(const char *path, uint64_t virtual_size);
// Attempt to resize the disk image at `path` to `virtual_size` bytes if
// the disk image is currently smaller than the requested size.
int expand_disk_image(const char *path, uint64_t virtual_size);
// Copy the source disk image from `src_fd` into `dst_fd` as a qcow2 image file.
// Returns 0 on success or a negated errno value on failure.
int convert_to_qcow2(int src_fd, int dst_fd);

View file

@ -6,16 +6,22 @@
extern crate libc;
extern crate qcow;
extern crate sys_util;
use libc::{EBADFD, EINVAL, EIO};
use libc::{EBADFD, EINVAL, EIO, ENOSYS};
use std::ffi::CStr;
use std::fs::{File, OpenOptions};
use std::io::{Seek, SeekFrom};
use std::mem::forget;
use std::os::raw::{c_char, c_int};
use std::os::unix::io::FromRawFd;
use std::panic::catch_unwind;
use qcow::{ImageType, QcowFile};
use sys_util::{flock, FileSetLen, FlockOperation};
trait DiskFile: FileSetLen + Seek {}
impl<D: FileSetLen + Seek> DiskFile for D {}
#[no_mangle]
pub unsafe extern "C" fn create_qcow_with_size(path: *const c_char, virtual_size: u64) -> c_int {
@ -46,6 +52,63 @@ pub unsafe extern "C" fn create_qcow_with_size(path: *const c_char, virtual_size
}
}
#[no_mangle]
pub unsafe extern "C" fn expand_disk_image(path: *const c_char, virtual_size: u64) -> c_int {
// NULL pointers are checked, but this will access any other invalid pointer passed from C
// code. It's the caller's responsibility to pass a valid pointer.
if path.is_null() {
return -EINVAL;
}
let c_str = CStr::from_ptr(path);
let file_path = match c_str.to_str() {
Ok(s) => s,
Err(_) => return -EINVAL,
};
let raw_image = match OpenOptions::new().read(true).write(true).open(file_path) {
Ok(f) => f,
Err(_) => return -EIO,
};
// Lock the disk image to prevent other processes from using it.
if let Err(_) = flock(&raw_image, FlockOperation::LockExclusive, true) {
return -EIO;
}
let image_type = match qcow::detect_image_type(&raw_image) {
Ok(t) => t,
Err(_) => return -EINVAL,
};
let mut disk_image: Box<DiskFile> = match image_type {
ImageType::Raw => Box::new(raw_image),
ImageType::Qcow2 => match QcowFile::from(raw_image) {
Ok(f) => Box::new(f),
Err(_) => return -EINVAL,
},
};
// For safety against accidentally shrinking the disk image due to a
// programming error elsewhere, verify that the new size is larger than
// the current size. This is safe against races due to the exclusive
// flock() taken above - this is only an advisory lock, but it is also
// acquired by other instances of this function as well as crosvm
// itself when running a VM, so this should be safe in all cases that
// can access a disk image in normal operation.
let current_size = match disk_image.seek(SeekFrom::End(0)) {
Ok(len) => len,
Err(_) => return -EIO,
};
if current_size >= virtual_size {
return 0;
}
match disk_image.set_len(virtual_size) {
Ok(_) => 0,
Err(_) => -ENOSYS,
}
}
#[no_mangle]
pub unsafe extern "C" fn convert_to_qcow2(src_fd: c_int, dst_fd: c_int) -> c_int {
// The caller is responsible for passing valid file descriptors.