-
Notifications
You must be signed in to change notification settings - Fork 617
block: Add query_device_size() and fix size handling for block devices #7902
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b2f6ecf
3dd2d8a
0dee6e1
b3ae2de
a8f8d9a
4972f9b
6892334
2b2ed6b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,7 @@ use std::fmt::{self, Debug}; | |
| use std::fs::{File, OpenOptions}; | ||
| use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; | ||
| use std::os::linux::fs::MetadataExt; | ||
| use std::os::unix::fs::FileTypeExt; | ||
| use std::os::unix::io::AsRawFd; | ||
| use std::path::Path; | ||
| use std::str::FromStr; | ||
|
|
@@ -61,7 +62,7 @@ use vm_memory::{ | |
| }; | ||
| use vm_virtio::{AccessPlatform, Translatable}; | ||
| use vmm_sys_util::eventfd::EventFd; | ||
| use vmm_sys_util::{aio, ioctl_io_nr}; | ||
| use vmm_sys_util::{aio, ioctl_io_nr, ioctl_ior_nr}; | ||
|
|
||
| use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult}; | ||
| use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp}; | ||
|
|
@@ -1193,6 +1194,36 @@ ioctl_io_nr!(BLKSSZGET, 0x12, 104); | |
| ioctl_io_nr!(BLKPBSZGET, 0x12, 123); | ||
| ioctl_io_nr!(BLKIOMIN, 0x12, 120); | ||
| ioctl_io_nr!(BLKIOOPT, 0x12, 121); | ||
| ioctl_ior_nr!(BLKGETSIZE64, 0x12, 114, u64); | ||
|
|
||
| /// Returns `(logical_size, physical_size)` in bytes for regular files and block devices. | ||
| /// | ||
| /// For regular files, logical size is `st_size` and physical size is | ||
| /// `st_blocks * 512` (actual host allocation). For block devices both | ||
| /// values equal the `BLKGETSIZE64` result. | ||
| pub fn query_device_size(file: &File) -> io::Result<(u64, u64)> { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if you can do a very simple unit test for that using
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gotcha. I first thought it's actually covered through existing tests, but this topic is far more complicated than it seemed to be. I've added some more sophisticated unit tests just for this new function, including the Thanks |
||
| let m = file.metadata()?; | ||
| if m.is_file() { | ||
| // st_blocks is always in 512-byte units on Linux | ||
| Ok((m.len(), m.st_blocks() * 512)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. regarding
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, this is Thanks
russell-islam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } else if m.file_type().is_block_device() { | ||
| let mut size: u64 = 0; | ||
| // SAFETY: BLKGETSIZE64 reads the device size into a u64 pointer. | ||
| let ret = unsafe { libc::ioctl(file.as_raw_fd(), BLKGETSIZE64() as _, &mut size) }; | ||
| if ret != 0 { | ||
| return Err(io::Error::last_os_error()); | ||
| } | ||
| Ok((size, size)) | ||
russell-islam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } else { | ||
| Err(io::Error::new( | ||
| io::ErrorKind::InvalidInput, | ||
| format!( | ||
| "disk image must be a regular file or block device, is: {:?}", | ||
| m.file_type() | ||
| ), | ||
| )) | ||
| } | ||
| } | ||
|
|
||
| #[derive(Copy, Clone)] | ||
| enum BlockSize { | ||
|
|
@@ -1442,4 +1473,68 @@ mod unit_tests { | |
| // SAFETY: buf was allocated with this layout via alloc_zeroed. | ||
| unsafe { dealloc(buf, layout) }; | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_query_device_size_regular_file() { | ||
| let temp_file = TempFile::new().unwrap(); | ||
| let mut f = temp_file.into_file(); | ||
| // 5 sectors + 13 extra bytes - not page aligned, not sectoraligned | ||
| f.write_all(&[0xAB; 5 * 512 + 13]).unwrap(); | ||
| f.sync_all().unwrap(); | ||
|
|
||
| let (logical, physical) = query_device_size(&f).unwrap(); | ||
| assert_eq!(logical, 5 * 512 + 13); | ||
| assert!(physical > 0); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_query_device_size_sparse_file_punch_hole() { | ||
| let temp_file = TempFile::new().unwrap(); | ||
| let f = temp_file.as_file(); | ||
| // Allocate 1 MiB | ||
| let size: i64 = 1 << 20; | ||
| f.set_len(size as u64).unwrap(); | ||
| // SAFETY: fd is valid, range is within file size. | ||
| let ret = unsafe { | ||
| libc::fallocate( | ||
| f.as_raw_fd(), | ||
| 0, // allocate | ||
| 0, | ||
| size, | ||
| ) | ||
| }; | ||
| assert_eq!(ret, 0, "fallocate failed: {}", io::Error::last_os_error()); | ||
| f.sync_all().unwrap(); | ||
|
|
||
| let (log_before, phys_before) = query_device_size(f).unwrap(); | ||
| assert_eq!(log_before, size as u64); | ||
| assert_eq!(phys_before, size as u64); | ||
|
|
||
| // Punch a hole in the middle 512 KiB | ||
| // SAFETY: fd is valid, range is within file size. | ||
| let ret = unsafe { | ||
| libc::fallocate( | ||
| f.as_raw_fd(), | ||
| libc::FALLOC_FL_PUNCH_HOLE | libc::FALLOC_FL_KEEP_SIZE, | ||
| size / 4, | ||
| size / 2, | ||
| ) | ||
| }; | ||
| assert_eq!(ret, 0, "punch hole failed: {}", io::Error::last_os_error()); | ||
| f.sync_all().unwrap(); | ||
|
|
||
| let (logical, physical) = query_device_size(f).unwrap(); | ||
| assert_eq!(logical, size as u64, "logical size must not change"); | ||
| assert!( | ||
| physical < logical, | ||
| "physical ({physical}) should be less than logical ({logical}) after punch hole" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_query_device_size_rejects_char_device() { | ||
| let f = std::fs::File::open("/dev/zero").unwrap(); | ||
| let err = query_device_size(&f).unwrap_err(); | ||
| assert_eq!(err.kind(), io::ErrorKind::InvalidInput); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.