Skip to content

Commit 17fb9a6

Browse files
committed
ptfs: close a race window in read()/write()
If error happens between File::from_raw_fd() and ManuallyDrop::new(), the underlying fd will be closed unexpectedly. Fix it by introducing a helper to ensure atomicity. Signed-off-by: Jiang Liu <[email protected]>
1 parent 314ea71 commit 17fb9a6

File tree

1 file changed

+14
-20
lines changed

1 file changed

+14
-20
lines changed

src/passthrough/sync_io.rs

+14-20
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
4646
fn ensure_file_flags<'a>(
4747
&self,
4848
data: &'a Arc<HandleData>,
49-
file: &File,
49+
fd: &impl AsRawFd,
5050
mut flags: u32,
5151
) -> io::Result<FileFlagGuard<'a, u32>> {
5252
let guard = data.open_flags.read().unwrap();
@@ -63,7 +63,7 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
6363
} else {
6464
flags = *guard & !libc::O_DIRECT as u32;
6565
}
66-
let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETFL, flags) };
66+
let ret = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFL, flags) };
6767
if ret != 0 {
6868
return Err(io::Error::last_os_error());
6969
}
@@ -134,8 +134,7 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
134134

135135
let (front, back) = rem.split_at(size_of::<LinuxDirent64>());
136136

137-
let dirent64 = LinuxDirent64::from_slice(front)
138-
.expect("fuse: unable to get LinuxDirent64 from slice");
137+
let dirent64 = LinuxDirent64::from_slice(front).ok_or_else(einval)?;
139138

140139
let namelen = dirent64.d_reclen as usize - size_of::<LinuxDirent64>();
141140
debug_assert!(
@@ -677,16 +676,14 @@ impl<S: BitmapSlice + Send + Sync> FileSystem for PassthroughFs<S> {
677676
flags: u32,
678677
) -> io::Result<usize> {
679678
let data = self.get_data(handle, inode, libc::O_RDONLY)?;
679+
let fd = data.borrow_fd();
680+
681+
self.ensure_file_flags(&data, &fd, flags)?;
680682

681683
// Manually implement File::try_clone() by borrowing fd of data.file instead of dup().
682684
// It's safe because the `data` variable's lifetime spans the whole function,
683685
// so data.file won't be closed.
684-
let f = unsafe { File::from_raw_fd(data.borrow_fd().as_raw_fd()) };
685-
686-
self.ensure_file_flags(&data, &f, flags)?;
687-
688-
let mut f = ManuallyDrop::new(f);
689-
686+
let mut f = unsafe { ManuallyDrop::new(File::from_raw_fd(fd.as_raw_fd())) };
690687
w.write_from(&mut *f, size as usize, offset)
691688
}
692689

@@ -704,21 +701,14 @@ impl<S: BitmapSlice + Send + Sync> FileSystem for PassthroughFs<S> {
704701
fuse_flags: u32,
705702
) -> io::Result<usize> {
706703
let data = self.get_data(handle, inode, libc::O_RDWR)?;
704+
let fd = data.borrow_fd();
707705

708-
// Manually implement File::try_clone() by borrowing fd of data.file instead of dup().
709-
// It's safe because the `data` variable's lifetime spans the whole function,
710-
// so data.file won't be closed.
711-
let f = unsafe { File::from_raw_fd(data.borrow_fd().as_raw_fd()) };
712-
713-
self.ensure_file_flags(&data, &f, flags)?;
714-
706+
self.ensure_file_flags(&data, &fd, flags)?;
715707
if self.seal_size.load(Ordering::Relaxed) {
716-
let st = stat_fd(&f, None)?;
708+
let st = stat_fd(&fd, None)?;
717709
self.seal_size_check(Opcode::Write, st.st_size as u64, offset, size as u64, 0)?;
718710
}
719711

720-
let mut f = ManuallyDrop::new(f);
721-
722712
// Cap restored when _killpriv is dropped
723713
let _killpriv =
724714
if self.killpriv_v2.load(Ordering::Relaxed) && (fuse_flags & WRITE_KILL_PRIV != 0) {
@@ -727,6 +717,10 @@ impl<S: BitmapSlice + Send + Sync> FileSystem for PassthroughFs<S> {
727717
None
728718
};
729719

720+
// Manually implement File::try_clone() by borrowing fd of data.file instead of dup().
721+
// It's safe because the `data` variable's lifetime spans the whole function,
722+
// so data.file won't be closed.
723+
let mut f = unsafe { ManuallyDrop::new(File::from_raw_fd(fd.as_raw_fd())) };
730724
r.read_to(&mut *f, size as usize, offset)
731725
}
732726

0 commit comments

Comments
 (0)