diff options
Diffstat (limited to 'devices/src/virtio/fs/passthrough.rs')
-rw-r--r-- | devices/src/virtio/fs/passthrough.rs | 108 |
1 files changed, 105 insertions, 3 deletions
diff --git a/devices/src/virtio/fs/passthrough.rs b/devices/src/virtio/fs/passthrough.rs index 7f6e3bb..bdc1c6f 100644 --- a/devices/src/virtio/fs/passthrough.rs +++ b/devices/src/virtio/fs/passthrough.rs @@ -17,11 +17,11 @@ use std::time::Duration; use data_model::DataInit; use libc; use sync::Mutex; -use sys_util::error; +use sys_util::{error, ioctl_ior_nr, ioctl_iow_nr, ioctl_with_mut_ptr, ioctl_with_ptr}; use crate::virtio::fs::filesystem::{ - Context, DirEntry, Entry, FileSystem, FsOptions, GetxattrReply, ListxattrReply, OpenOptions, - SetattrValid, ZeroCopyReader, ZeroCopyWriter, + Context, DirEntry, Entry, FileSystem, FsOptions, GetxattrReply, IoctlFlags, IoctlIovec, + IoctlReply, ListxattrReply, OpenOptions, SetattrValid, ZeroCopyReader, ZeroCopyWriter, }; use crate::virtio::fs::fuse; use crate::virtio::fs::multikey::MultikeyBTreeMap; @@ -32,6 +32,22 @@ const EMPTY_CSTR: &[u8] = b"\0"; const ROOT_CSTR: &[u8] = b"/\0"; const PROC_CSTR: &[u8] = b"/proc\0"; +const FSCRYPT_KEY_DESCRIPTOR_SIZE: usize = 8; + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +struct fscrypt_policy_v1 { + _version: u8, + _contents_encryption_mode: u8, + _filenames_encryption_mode: u8, + _flags: u8, + _master_key_descriptor: [u8; FSCRYPT_KEY_DESCRIPTOR_SIZE], +} +unsafe impl DataInit for fscrypt_policy_v1 {} + +ioctl_ior_nr!(FS_IOC_SET_ENCRYPTION_POLICY, 0x66, 19, fscrypt_policy_v1); +ioctl_iow_nr!(FS_IOC_GET_ENCRYPTION_POLICY, 0x66, 21, fscrypt_policy_v1); + type Inode = u64; type Handle = u64; @@ -629,6 +645,50 @@ impl PassthroughFs { Err(io::Error::last_os_error()) } } + + fn get_encryption_policy(&self, handle: Handle) -> io::Result<IoctlReply> { + let data = self + .handles + .read() + .unwrap() + .get(&handle) + .map(Arc::clone) + .ok_or_else(ebadf)?; + + let mut buf = MaybeUninit::<fscrypt_policy_v1>::zeroed(); + let file = data.file.lock(); + + // Safe because the kernel will only write to `buf` and we check the return value. + let res = + unsafe { ioctl_with_mut_ptr(&*file, FS_IOC_GET_ENCRYPTION_POLICY(), buf.as_mut_ptr()) }; + if res < 0 { + Ok(IoctlReply::Done(Err(io::Error::last_os_error()))) + } else { + // Safe because the kernel guarantees that the policy is now initialized. + let policy = unsafe { buf.assume_init() }; + Ok(IoctlReply::Done(Ok(policy.as_slice().to_vec()))) + } + } + + fn set_encryption_policy<R: io::Read>(&self, handle: Handle, r: R) -> io::Result<IoctlReply> { + let data = self + .handles + .read() + .unwrap() + .get(&handle) + .map(Arc::clone) + .ok_or_else(ebadf)?; + + let policy = fscrypt_policy_v1::from_reader(r)?; + let file = data.file.lock(); + // Safe because the kernel will only read from `policy` and we check the return value. + let res = unsafe { ioctl_with_ptr(&*file, FS_IOC_SET_ENCRYPTION_POLICY(), &policy) }; + if res < 0 { + Ok(IoctlReply::Done(Err(io::Error::last_os_error()))) + } else { + Ok(IoctlReply::Done(Ok(Vec::new()))) + } + } } fn forget_one( @@ -1587,4 +1647,46 @@ impl FileSystem for PassthroughFs { Err(io::Error::last_os_error()) } } + + fn ioctl<R: io::Read>( + &self, + _ctx: Context, + handle: Handle, + _flags: IoctlFlags, + cmd: u32, + arg: u64, + in_size: u32, + out_size: u32, + r: R, + ) -> io::Result<IoctlReply> { + // Normally, we wouldn't need to retry the FS_IOC_GET_ENCRYPTION_POLICY and + // FS_IOC_SET_ENCRYPTION_POLICY ioctls. Unfortunately, the I/O directions for both of them + // are encoded backwards so they can only be handled as unrestricted fuse ioctls. + if cmd == FS_IOC_GET_ENCRYPTION_POLICY() as u32 { + if out_size < size_of::<fscrypt_policy_v1>() as u32 { + let input = Vec::new(); + let output = vec![IoctlIovec { + base: arg, + len: size_of::<fscrypt_policy_v1>() as u64, + }]; + Ok(IoctlReply::Retry { input, output }) + } else { + self.get_encryption_policy(handle) + } + } else if cmd == FS_IOC_SET_ENCRYPTION_POLICY() as u32 { + if in_size < size_of::<fscrypt_policy_v1>() as u32 { + let input = vec![IoctlIovec { + base: arg, + len: size_of::<fscrypt_policy_v1>() as u64, + }]; + let output = Vec::new(); + Ok(IoctlReply::Retry { input, output }) + } else { + self.set_encryption_policy(handle, r) + } + } else { + // Did you know that a file/directory is not a TTY? + Err(io::Error::from_raw_os_error(libc::ENOTTY)) + } + } } |