Skip to content
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

passthrough: reduce the memory footprint of file handle #102

Merged
merged 2 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions src/abi/fuse_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,13 +600,16 @@ impl Attr {
atimensec: st.st_atime_nsec as u32,
mtimensec: st.st_mtime_nsec as u32,
ctimensec: st.st_ctime_nsec as u32,
#[cfg(target_os = "linux")]
mode: st.st_mode,
#[cfg(target_os = "macos")]
mode: st.st_mode as u32,
nlink: st.st_nlink as u32,
uid: st.st_uid,
gid: st.st_gid,
rdev: st.st_rdev as u32,
blksize: st.st_blksize as u32,
flags: flags as u32,
flags,
#[cfg(target_os = "macos")]
crtime: 0,
#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -658,6 +661,22 @@ pub struct Kstatfs {
unsafe impl ByteValued for Kstatfs {}

impl From<statvfs64> for Kstatfs {
#[cfg(target_os = "linux")]
fn from(st: statvfs64) -> Self {
Kstatfs {
blocks: st.f_blocks,
bfree: st.f_bfree,
bavail: st.f_bavail,
files: st.f_files,
ffree: st.f_ffree,
bsize: st.f_bsize as u32,
namelen: st.f_namemax as u32,
frsize: st.f_frsize as u32,
..Default::default()
}
}

#[cfg(target_os = "macos")]
fn from(st: statvfs64) -> Self {
Kstatfs {
blocks: st.f_blocks as u64,
Expand Down Expand Up @@ -745,7 +764,7 @@ impl From<u32> for Opcode {
if op >= Opcode::MaxOpcode as u32 {
return Opcode::MaxOpcode;
}
unsafe { mem::transmute(op as u32) }
unsafe { mem::transmute(op) }
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/api/pseudo_fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,14 @@ impl PseudoFs {
..Default::default()
};
attr.ino = ino;
attr.mode = (libc::S_IFDIR | libc::S_IRWXU | libc::S_IRWXG | libc::S_IRWXO) as u32;
#[cfg(target_os = "linux")]
{
attr.mode = libc::S_IFDIR | libc::S_IRWXU | libc::S_IRWXG | libc::S_IRWXO;
}
#[cfg(target_os = "macos")]
{
attr.mode = (libc::S_IFDIR | libc::S_IRWXU | libc::S_IRWXG | libc::S_IRWXO) as u32;
}
let now = SystemTime::now();
attr.ctime = now
.duration_since(SystemTime::UNIX_EPOCH)
Expand Down
4 changes: 2 additions & 2 deletions src/api/server/sync_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,10 +284,10 @@ impl<F: FileSystem + Sync> Server<F> {

#[cfg(target_os = "linux")]
let flags =
flags & (libc::RENAME_EXCHANGE | libc::RENAME_NOREPLACE | libc::RENAME_WHITEOUT) as u32;
flags & (libc::RENAME_EXCHANGE | libc::RENAME_NOREPLACE | libc::RENAME_WHITEOUT);

#[cfg(target_os = "macos")]
let flags = flags & (libc::RENAME_EXCL | libc::RENAME_SWAP) as u32;
let flags = flags & (libc::RENAME_EXCL | libc::RENAME_SWAP);

self.do_rename(ctx, size_of::<Rename2In>(), newdir, flags)
}
Expand Down
2 changes: 1 addition & 1 deletion src/api/vfs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ impl Vfs {
/// Create a new vfs instance
pub fn new(opts: VfsOptions) -> Self {
Vfs {
next_super: AtomicU8::new((VFS_PSEUDO_FS_IDX + 1) as u8),
next_super: AtomicU8::new(VFS_PSEUDO_FS_IDX + 1),
mountpoints: ArcSwap::new(Arc::new(HashMap::new())),
superblocks: ArcSwap::new(Arc::new(vec![None; MAX_VFS_INDEX])),
root: PseudoFs::new(),
Expand Down
10 changes: 4 additions & 6 deletions src/common/file_buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,10 @@ impl<'a> FileVolatileSlice<'a> {

/// Return a subslice of this [FileVolatileSlice] starting at `offset`.
pub fn offset(&self, count: usize) -> Result<Self, Error> {
let new_addr = (self.addr as usize)
.checked_add(count)
.ok_or(Error::Overflow {
base: self.addr as usize,
offset: count,
})?;
let new_addr = self.addr.checked_add(count).ok_or(Error::Overflow {
base: self.addr,
offset: count,
})?;
let new_size = self
.size
.checked_sub(count)
Expand Down
143 changes: 108 additions & 35 deletions src/passthrough/file_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::fmt::{Debug, Formatter};
use std::fs::File;
use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::ptr;
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};

use crate::passthrough::PassthroughFs;
Expand All @@ -17,29 +18,33 @@ use crate::passthrough::PassthroughFs;
///
/// According to Linux ABI, struct file_handle has a flexible array member 'f_handle', but it's
/// hard-coded here for simplicity.
pub const MAX_HANDLE_SZ: usize = 128;
//pub const MAX_HANDLE_SZ: usize = 128;

#[derive(Clone, Copy)]
#[repr(C)]
pub(crate) struct CFileHandle {
struct CFileHandle {
// Size of f_handle [in, out]
pub(crate) handle_bytes: libc::c_uint,
handle_bytes: libc::c_uint,
// Handle type [out]
pub(crate) handle_type: libc::c_int,
handle_type: libc::c_int,
// File identifier (sized by caller) [out]
pub(crate) f_handle: [libc::c_char; MAX_HANDLE_SZ],
f_handle: *mut libc::c_char,
}

impl CFileHandle {
fn new() -> Self {
CFileHandle {
handle_bytes: MAX_HANDLE_SZ as libc::c_uint,
handle_bytes: 0,
handle_type: 0,
f_handle: [0; MAX_HANDLE_SZ],
f_handle: ptr::null_mut(),
}
}
}

// Safe because f_handle is readonly once FileHandle is initialized.
unsafe impl Send for CFileHandle {}
unsafe impl Sync for CFileHandle {}

impl Ord for CFileHandle {
fn cmp(&self, other: &Self) -> Ordering {
if self.handle_bytes != other.handle_bytes {
Expand All @@ -49,12 +54,8 @@ impl Ord for CFileHandle {
return self.handle_type.cmp(&other.handle_type);
}

self.f_handle
.iter()
.zip(other.f_handle.iter())
.map(|(x, y)| x.cmp(y))
.find(|&ord| ord != std::cmp::Ordering::Equal)
.unwrap_or(Ordering::Equal)
// f_handle is left to be compared by FileHandle's buf.
Ordering::Equal
}
}

Expand Down Expand Up @@ -83,10 +84,12 @@ impl Debug for CFileHandle {
}

/// Struct to maintain information for a file handle.
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)]
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
pub struct FileHandle {
pub(crate) mnt_id: u64,
pub(crate) handle: CFileHandle,
handle: CFileHandle,
// internal buffer for handle.f_handle
buf: Vec<libc::c_char>,
}

extern "C" {
Expand All @@ -113,6 +116,38 @@ impl FileHandle {
let mut mount_id: libc::c_int = 0;
let mut c_fh = CFileHandle::new();

// Per name_to_handle_at(2), the caller can discover the required size
// for the file_handle structure by making a call in which
// handle->handle_bytes is zero. In this case, the call fails with the
// error EOVERFLOW and handle->handle_bytes is set to indicate the
// required size; the caller can then use this information to allocate a
// structure of the correct size.
let ret = unsafe {
name_to_handle_at(
dir_fd,
path.as_ptr(),
&mut c_fh,
&mut mount_id,
libc::AT_EMPTY_PATH,
)
};
if ret == -1 {
let e = io::Error::last_os_error();
// unwrap is safe as e is obtained from last_os_error().
if e.raw_os_error().unwrap() != libc::EOVERFLOW {
return Err(e);
}
} else {
return Err(io::Error::from(io::ErrorKind::InvalidData));
}

let needed = c_fh.handle_bytes as usize;
let mut buf = vec![0; needed];

// get a unsafe pointer, FileHandle takes care of freeing the memory
// that 'f_handle' points to.
c_fh.f_handle = buf.as_mut_ptr();

let ret = unsafe {
name_to_handle_at(
dir_fd,
Expand All @@ -126,6 +161,7 @@ impl FileHandle {
Ok(FileHandle {
mnt_id: mount_id as u64,
handle: c_fh,
buf,
})
} else {
let e = io::Error::last_os_error();
Expand Down Expand Up @@ -296,43 +332,80 @@ mod tests {

#[test]
fn test_file_handle_derives() {
let mut h1 = CFileHandle {
let mut buf1 = vec![0; 128];
let h1 = CFileHandle {
handle_bytes: 128,
handle_type: 3,
f_handle: [0; MAX_HANDLE_SZ],
f_handle: buf1.as_mut_ptr(),
};
let mut fh1 = FileHandle {
mnt_id: 0,
handle: h1,
buf: buf1,
};

let mut buf2 = vec![0; 129];
let h2 = CFileHandle {
handle_bytes: 129,
handle_type: 3,
f_handle: [0; MAX_HANDLE_SZ],
f_handle: buf2.as_mut_ptr(),
};
let fh2 = FileHandle {
mnt_id: 0,
handle: h2,
buf: buf2,
};

let mut buf3 = vec![0; 128];
let h3 = CFileHandle {
handle_bytes: 128,
handle_type: 4,
f_handle: [0; MAX_HANDLE_SZ],
f_handle: buf3.as_mut_ptr(),
};
let fh3 = FileHandle {
mnt_id: 0,
handle: h3,
buf: buf3,
};

let mut buf4 = vec![1; 128];
let h4 = CFileHandle {
handle_bytes: 128,
handle_type: 4,
f_handle: [1; MAX_HANDLE_SZ],
handle_type: 3,
f_handle: buf4.as_mut_ptr(),
};
let mut h5 = CFileHandle {
let fh4 = FileHandle {
mnt_id: 0,
handle: h4,
buf: buf4,
};

let mut buf5 = vec![0; 128];
let h5 = CFileHandle {
handle_bytes: 128,
handle_type: 3,
f_handle: [0; MAX_HANDLE_SZ],
f_handle: buf5.as_mut_ptr(),
};
let mut fh5 = FileHandle {
mnt_id: 0,
handle: h5,
buf: buf5,
};

assert!(h1 < h2);
assert!(h1 != h2);
assert!(h1 < h3);
assert!(h1 != h3);
assert!(h1 < h4);
assert!(h1 != h4);

assert!(h1 == h5);
h1.f_handle[0] = 1;
assert!(h1 > h5);
h5.f_handle[0] = 1;
assert!(h1 == h5);
assert!(fh1 < fh2);
assert!(fh1 != fh2);
assert!(fh1 < fh3);
assert!(fh1 != fh3);
assert!(fh1 < fh4);
assert!(fh1 != fh4);

assert!(fh1 == fh5);
fh1.buf[0] = 1;
fh1.handle.f_handle = fh1.buf.as_mut_ptr();
assert!(fh1 > fh5);

fh5.buf[0] = 1;
fh5.handle.f_handle = fh5.buf.as_mut_ptr();
assert!(fh1 == fh5);
}
}
16 changes: 12 additions & 4 deletions src/passthrough/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl InodeStat {
}
}

#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug)]
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
enum InodeAltKey {
Ids {
ino: libc::ino64_t,
Expand Down Expand Up @@ -622,7 +622,7 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
fuse::ROOT_ID,
file_or_handle,
2,
ids_altkey,
ids_altkey.clone(),
st.get_stat().st_mode,
),
ids_altkey,
Expand Down Expand Up @@ -834,7 +834,9 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
// Note that this will always be `None` if `cfg.inode_file_handles` is false, but we only
// really need this alt key when we do not have an `O_PATH` fd open for every inode. So if
// `cfg.inode_file_handles` is false, we do not need this key anyway.
let handle_altkey = file_or_handle.handle().map(|h| InodeAltKey::Handle(*h));
let handle_altkey = file_or_handle
.handle()
.map(|h| InodeAltKey::Handle((*h).clone()));

Ok((file_or_handle, inode_stat, ids_altkey, handle_altkey))
}
Expand Down Expand Up @@ -937,7 +939,13 @@ impl<S: BitmapSlice + Send + Sync> PassthroughFs<S> {
InodeMap::insert_locked(
inodes.deref_mut(),
inode,
InodeData::new(inode, file_or_handle, 1, ids_altkey, st.get_stat().st_mode),
InodeData::new(
inode,
file_or_handle,
1,
ids_altkey.clone(),
st.get_stat().st_mode,
),
ids_altkey,
handle_altkey,
);
Expand Down
Loading