Skip to content

Commit 3de4150

Browse files
Lencerfacatangiu
authored andcommitted
ioctls: Add open_with_cloexec_at and new_with_path
These two functions are the same as `open_with_cloexec` and `new` except they allow users to specify the path to the KVM device file. Signed-off-by: Changyuan Lyu <[email protected]>
1 parent 071626b commit 3de4150

File tree

2 files changed

+83
-2
lines changed

2 files changed

+83
-2
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# Unreleased
2+
3+
## Added
4+
- [[#213](https://github.com/rust-vmm/kvm-ioctls/pull/213)] Add `Kvm::new_with_path()`
5+
and `Kvm::open_with_cloexec_at()` to allowing using kvm device file other than
6+
`/dev/kvm`.
7+
18
# v0.12.0
29

310
## Added

src/ioctls/system.rs

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Use of this source code is governed by a BSD-style license that can be
66
// found in the THIRD-PARTY file.
77
use libc::{open, O_CLOEXEC, O_RDWR};
8+
use std::ffi::CStr;
89
use std::fs::File;
910
use std::os::raw::{c_char, c_ulong};
1011
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
@@ -46,6 +47,32 @@ impl Kvm {
4647
Ok(unsafe { Self::from_raw_fd(fd) })
4748
}
4849

50+
/// Opens the KVM device at `kvm_path` and returns a `Kvm` object on success.
51+
///
52+
/// # Arguments
53+
///
54+
/// * `kvm_path`: path to the KVM device. Usually it is `/dev/kvm`.
55+
///
56+
/// # Example
57+
///
58+
/// ```
59+
/// use kvm_ioctls::Kvm;
60+
/// use std::ffi::CString;
61+
/// let kvm_path = CString::new("/dev/kvm").unwrap();
62+
/// let kvm = Kvm::new_with_path(&kvm_path).unwrap();
63+
/// ```
64+
#[allow(clippy::new_ret_no_self)]
65+
pub fn new_with_path<P>(kvm_path: P) -> Result<Self>
66+
where
67+
P: AsRef<CStr>,
68+
{
69+
// Open `kvm_path` using `O_CLOEXEC` flag.
70+
let fd = Self::open_with_cloexec_at(kvm_path, true)?;
71+
// SAFETY: Safe because we verify that the fd is valid in `open_with_cloexec_at`
72+
// and we own the fd.
73+
Ok(unsafe { Self::from_raw_fd(fd) })
74+
}
75+
4976
/// Opens `/dev/kvm` and returns the fd number on success.
5077
///
5178
/// One usecase for this method is opening `/dev/kvm` before exec-ing into a
@@ -68,9 +95,39 @@ impl Kvm {
6895
/// let kvm = unsafe { Kvm::from_raw_fd(kvm_fd) };
6996
/// ```
7097
pub fn open_with_cloexec(close_on_exec: bool) -> Result<RawFd> {
98+
// SAFETY: Safe because we give a constant nul-terminated string.
99+
let kvm_path = unsafe { CStr::from_bytes_with_nul_unchecked(b"/dev/kvm\0") };
100+
Self::open_with_cloexec_at(kvm_path, close_on_exec)
101+
}
102+
103+
/// Opens the KVM device at `kvm_path` and returns the fd number on success.
104+
/// Same as [open_with_cloexec()](struct.Kvm.html#method.open_with_cloexec)
105+
/// except this method opens `kvm_path` instead of `/dev/kvm`.
106+
///
107+
/// # Arguments
108+
///
109+
/// * `kvm_path`: path to the KVM device. Usually it is `/dev/kvm`.
110+
/// * `close_on_exec`: If true opens `kvm_path` using the `O_CLOEXEC` flag.
111+
///
112+
/// # Example
113+
///
114+
/// ```
115+
/// # use kvm_ioctls::Kvm;
116+
/// # use std::ffi::CString;
117+
/// # use std::os::unix::io::FromRawFd;
118+
/// let kvm_path = CString::new("/dev/kvm").unwrap();
119+
/// let kvm_fd = Kvm::open_with_cloexec_at(kvm_path, false).unwrap();
120+
/// // The `kvm_fd` can now be passed to another process where we can use
121+
/// // `from_raw_fd` for creating a `Kvm` object:
122+
/// let kvm = unsafe { Kvm::from_raw_fd(kvm_fd) };
123+
/// ```
124+
pub fn open_with_cloexec_at<P>(path: P, close_on_exec: bool) -> Result<RawFd>
125+
where
126+
P: AsRef<CStr>,
127+
{
71128
let open_flags = O_RDWR | if close_on_exec { O_CLOEXEC } else { 0 };
72-
// SAFETY: Safe because we give a constant nul-terminated string and verify the result.
73-
let ret = unsafe { open("/dev/kvm\0".as_ptr() as *const c_char, open_flags) };
129+
// SAFETY: Safe because we verify the result.
130+
let ret = unsafe { open(path.as_ref().as_ptr() as *const c_char, open_flags) };
74131
if ret < 0 {
75132
Err(errno::Error::last())
76133
} else {
@@ -583,6 +640,12 @@ mod tests {
583640
Kvm::new().unwrap();
584641
}
585642

643+
#[test]
644+
fn test_kvm_new_with_path() {
645+
let kvm_path = unsafe { CStr::from_bytes_with_nul_unchecked(b"/dev/kvm\0") };
646+
Kvm::new_with_path(kvm_path).unwrap();
647+
}
648+
586649
#[test]
587650
fn test_open_with_cloexec() {
588651
let fd = Kvm::open_with_cloexec(false).unwrap();
@@ -593,6 +656,17 @@ mod tests {
593656
assert_eq!(flags & FD_CLOEXEC, FD_CLOEXEC);
594657
}
595658

659+
#[test]
660+
fn test_open_with_cloexec_at() {
661+
let kvm_path = std::ffi::CString::new("/dev/kvm").unwrap();
662+
let fd = Kvm::open_with_cloexec_at(&kvm_path, false).unwrap();
663+
let flags = unsafe { fcntl(fd, F_GETFD, 0) };
664+
assert_eq!(flags & FD_CLOEXEC, 0);
665+
let fd = Kvm::open_with_cloexec_at(&kvm_path, true).unwrap();
666+
let flags = unsafe { fcntl(fd, F_GETFD, 0) };
667+
assert_eq!(flags & FD_CLOEXEC, FD_CLOEXEC);
668+
}
669+
596670
#[test]
597671
fn test_kvm_api_version() {
598672
let kvm = Kvm::new().unwrap();

0 commit comments

Comments
 (0)