Skip to content

Commit 9215b58

Browse files
jiangliubergwolf
authored andcommitted
ptfs: change return value of FileHandle::from_name_at()
Change return value of FileHandle::from_name_at() from io::Result<Self> to io::Result<Option<Self>>. Signed-off-by: Jiang Liu <[email protected]>
1 parent b5debef commit 9215b58

File tree

5 files changed

+71
-43
lines changed

5 files changed

+71
-43
lines changed

src/passthrough/file_handle.rs

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use std::sync::Arc;
1414
use vmm_sys_util::fam::{FamStruct, FamStructWrapper};
1515

1616
use super::mount_fd::{MountFd, MountFds, MountId};
17+
use super::util::enosys;
1718

1819
/// An arbitrary maximum size for CFileHandle::f_handle.
1920
///
@@ -174,7 +175,15 @@ extern "C" {
174175

175176
impl FileHandle {
176177
/// Create a file handle for the given file.
177-
pub fn from_name_at(dir_fd: &impl AsRawFd, path: &CStr) -> io::Result<Self> {
178+
///
179+
/// Return `Ok(None)` if no file handle can be generated for this file: Either because the
180+
/// filesystem does not support it, or because it would require a larger file handle than we
181+
/// can store. These are not intermittent failures, i.e. if this function returns `Ok(None)`
182+
/// for a specific file, it will always return `Ok(None)` for it. Conversely, if this function
183+
/// returns `Ok(Some)` at some point, it will never return `Ok(None)` later.
184+
///
185+
/// Return an `io::Error` for all other errors.
186+
pub fn from_name_at(dir_fd: &impl AsRawFd, path: &CStr) -> io::Result<Option<Self>> {
178187
let mut mount_id: libc::c_int = 0;
179188
let mut c_fh = CFileHandle::new(0);
180189

@@ -194,10 +203,14 @@ impl FileHandle {
194203
)
195204
};
196205
if ret == -1 {
197-
let e = io::Error::last_os_error();
198-
// unwrap is safe as e is obtained from last_os_error().
199-
if e.raw_os_error().unwrap() != libc::EOVERFLOW {
200-
return Err(e);
206+
let err = io::Error::last_os_error();
207+
match err.raw_os_error() {
208+
// Got the needed buffer size.
209+
Some(libc::EOVERFLOW) => {}
210+
// Filesystem does not support file handles
211+
Some(libc::EOPNOTSUPP) => return Ok(None),
212+
// Other error
213+
_ => return Err(err),
201214
}
202215
} else {
203216
return Err(io::Error::from(io::ErrorKind::InvalidData));
@@ -221,14 +234,14 @@ impl FileHandle {
221234
libc::AT_EMPTY_PATH,
222235
)
223236
};
224-
if ret == 0 {
225-
Ok(FileHandle {
226-
mnt_id: mount_id as u64,
227-
handle: c_fh,
228-
})
229-
} else {
230-
Err(io::Error::last_os_error())
237+
if ret == -1 {
238+
return Err(io::Error::last_os_error());
231239
}
240+
241+
Ok(Some(FileHandle {
242+
mnt_id: mount_id as MountId,
243+
handle: c_fh,
244+
}))
232245
}
233246
}
234247

@@ -243,7 +256,7 @@ impl Debug for OpenableFileHandle {
243256
write!(
244257
f,
245258
"Openable file handle: mountfd {}, type {}, len {}",
246-
self.mount_fd.file().as_raw_fd(),
259+
self.mount_fd.as_raw_fd(),
247260
fh.handle_type,
248261
fh.handle_bytes
249262
)
@@ -267,10 +280,12 @@ impl OpenableFileHandle {
267280
where
268281
F: FnOnce(RawFd, libc::c_int, u32) -> io::Result<File>,
269282
{
270-
let handle = FileHandle::from_name_at(dir_fd, path).map_err(|e| {
271-
error!("from_name_at failed error {:?}", e);
272-
e
273-
})?;
283+
let handle = FileHandle::from_name_at(dir_fd, path)
284+
.map_err(|e| {
285+
error!("from_name_at failed error {:?}", e);
286+
e
287+
})?
288+
.ok_or_else(enosys)?;
274289

275290
let mount_fd = mount_fds
276291
.get(handle.mnt_id, reopen_dir)
@@ -286,7 +301,7 @@ impl OpenableFileHandle {
286301
pub fn open(&self, flags: libc::c_int) -> io::Result<File> {
287302
let ret = unsafe {
288303
open_by_handle_at(
289-
self.mount_fd.file().as_raw_fd(),
304+
self.mount_fd.as_raw_fd(),
290305
self.handle.handle.wrapper.as_fam_struct_ptr(),
291306
flags,
292307
)
@@ -314,6 +329,7 @@ impl OpenableFileHandle {
314329
#[cfg(test)]
315330
mod tests {
316331
use super::*;
332+
use nix::unistd::getuid;
317333
use std::ffi::CString;
318334
use std::io::Read;
319335

@@ -412,8 +428,10 @@ mod tests {
412428
let dir = File::open(topdir).unwrap();
413429
let filename = CString::new("build.rs").unwrap();
414430

415-
let dir_handle = FileHandle::from_name_at(&dir, &CString::new("").unwrap()).unwrap();
416-
let file_handle = FileHandle::from_name_at(&dir, &filename).unwrap();
431+
let dir_handle = FileHandle::from_name_at(&dir, &CString::new("").unwrap())
432+
.unwrap()
433+
.unwrap();
434+
let file_handle = FileHandle::from_name_at(&dir, &filename).unwrap().unwrap();
417435

418436
assert_eq!(dir_handle.mnt_id, file_handle.mnt_id);
419437
assert_ne!(
@@ -428,6 +446,11 @@ mod tests {
428446

429447
#[test]
430448
fn test_openable_file_handle() {
449+
let uid = getuid();
450+
if !uid.is_root() {
451+
return;
452+
}
453+
431454
let topdir = env!("CARGO_MANIFEST_DIR");
432455
let dir = File::open(topdir).unwrap();
433456
let filename = CString::new("build.rs").unwrap();

src/passthrough/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
526526
// Be careful:
527527
// - readlink() does not append a terminating null byte to buf
528528
// - OsString instances are not NUL terminated
529-
return Ok(PathBuf::from(OsString::from_vec(buf)));
529+
Ok(PathBuf::from(OsString::from_vec(buf)))
530530
}
531531

532532
/// Get the file pathname corresponding to the Inode
@@ -621,8 +621,8 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
621621
// TODO: use statx(2) to query mntid when 5.8 kernel or later are widely used.
622622
let mnt_id = if use_mntid {
623623
match FileHandle::from_name_at(dir_fd, name) {
624-
Ok(h) => h.mnt_id,
625-
Err(_) => 0,
624+
Ok(Some(h)) => h.mnt_id,
625+
_ => 0,
626626
}
627627
} else {
628628
0

src/passthrough/mount_fd.rs

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,26 @@ use std::collections::{HashMap, HashSet};
55
use std::ffi::CString;
66
use std::fs::File;
77
use std::io::{self, Read, Seek};
8-
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
8+
use std::os::unix::io::{AsRawFd, RawFd};
99
use std::sync::{Arc, Mutex, RwLock, Weak};
1010

1111
use super::statx::statx;
12-
use super::util::einval;
12+
use super::util::{einval, is_safe_inode};
1313

1414
const MOUNT_INFO_FILE: &str = "/proc/self/mountinfo";
1515

16+
/// Type alias for mount id.
1617
pub type MountId = u64;
1718

1819
pub struct MountFd {
19-
map: Weak<RwLock<HashMap<MountId, Weak<MountFd>>>>,
20-
mount_id: MountId,
2120
file: File,
21+
mount_id: MountId,
22+
map: Weak<RwLock<HashMap<MountId, Weak<MountFd>>>>,
2223
}
2324

24-
impl MountFd {
25-
pub fn file(&self) -> &File {
26-
&self.file
25+
impl AsRawFd for MountFd {
26+
fn as_raw_fd(&self) -> RawFd {
27+
self.file.as_raw_fd()
2728
}
2829
}
2930

@@ -132,15 +133,12 @@ impl MountFds {
132133
.prefix(format!("Failed to open mount point \"{mount_point}\"")));
133134
}
134135

135-
// Safe because we have just opened this FD
136-
let mount_point_file = unsafe { File::from_raw_fd(mount_point_fd) };
137-
138136
// Check the mount point has the expected `mount_id`.
139-
let st_mode = self.validate_mount_id(mount_id, &mount_point_file, &mount_point)?;
137+
let st_mode = self.validate_mount_id(mount_id, &mount_point_fd, &mount_point)?;
140138

141139
// Ensure that we can safely reopen `mount_point_path` with `O_RDONLY`
142140
let file_type = st_mode & libc::S_IFMT;
143-
if file_type != libc::S_IFREG && file_type != libc::S_IFDIR {
141+
if !is_safe_inode(file_type) {
144142
return Err(self
145143
.error_for(mount_id, io::Error::from_raw_os_error(libc::EIO))
146144
.set_desc(format!(
@@ -150,7 +148,7 @@ impl MountFds {
150148

151149
// Now that we know that this is a regular file or directory, really open it
152150
let file = reopen_fd(
153-
mount_point_file.as_raw_fd(),
151+
mount_point_fd.as_raw_fd(),
154152
libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_CLOEXEC,
155153
st_mode,
156154
)
@@ -177,9 +175,9 @@ impl MountFds {
177175
file.as_raw_fd(),
178176
);
179177
let mount_fd = Arc::new(MountFd {
180-
map: Arc::downgrade(&self.map),
181-
mount_id,
182178
file,
179+
mount_id,
180+
map: Arc::downgrade(&self.map),
183181
});
184182
mount_fds_locked.insert(mount_id, Arc::downgrade(&mount_fd));
185183
mount_fd
@@ -193,10 +191,10 @@ impl MountFds {
193191
fn validate_mount_id(
194192
&self,
195193
mount_id: MountId,
196-
mount_point_file: &File,
194+
mount_point_fd: &impl AsRawFd,
197195
mount_point: &str,
198196
) -> MPRResult<libc::mode_t> {
199-
let stx = statx(mount_point_file, None).map_err(|e| {
197+
let stx = statx(mount_point_fd, None).map_err(|e| {
200198
self.error_for(mount_id, e)
201199
.prefix(format!("Failed to stat mount point \"{mount_point}\""))
202200
})?;
@@ -449,7 +447,7 @@ mod tests {
449447
let dir = File::open(topdir).unwrap();
450448
let filename = CString::new("build.rs").unwrap();
451449
let mount_fds = MountFds::new(None).unwrap();
452-
let handle = FileHandle::from_name_at(&dir, &filename).unwrap();
450+
let handle = FileHandle::from_name_at(&dir, &filename).unwrap().unwrap();
453451

454452
// Ensure that `MountFds::get()` works for new entry.
455453
let fd1 = mount_fds
@@ -466,7 +464,7 @@ mod tests {
466464
assert_eq!(mount_fds.map.read().unwrap().len(), 1);
467465

468466
// Ensure fd1 and fd2 are the same object.
469-
assert_eq!(fd1.file().as_raw_fd(), fd2.file().as_raw_fd());
467+
assert_eq!(fd1.as_raw_fd(), fd2.as_raw_fd());
470468

471469
drop(fd1);
472470
assert_eq!(Arc::strong_count(&fd2), 1);

src/passthrough/statx.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ impl SafeStatXAccess for statx_st {
8282
}
8383

8484
fn get_mount_id(dir: &impl AsRawFd, path: &CStr) -> Option<MountId> {
85-
FileHandle::from_name_at(dir, path).map(|v| v.mnt_id).ok()
85+
match FileHandle::from_name_at(dir, path) {
86+
Ok(Some(v)) => Some(v.mnt_id),
87+
_ => None,
88+
}
8689
}
8790

8891
// Only works on Linux, and libc::SYS_statx is only defined for these

src/passthrough/util.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ pub fn einval() -> io::Error {
216216
io::Error::from_raw_os_error(libc::EINVAL)
217217
}
218218

219+
pub fn enosys() -> io::Error {
220+
io::Error::from_raw_os_error(libc::ENOSYS)
221+
}
222+
219223
#[cfg(test)]
220224
mod tests {
221225
use super::*;

0 commit comments

Comments
 (0)