// Copyright 2019 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use std::ffi::CStr; use std::fs::File; use std::io::{self, Read, Write}; use std::mem::size_of; use data_model::DataInit; use sys_util::error; use crate::virtio::fs::filesystem::{ Context, DirEntry, Entry, FileSystem, GetxattrReply, IoctlReply, ListxattrReply, ZeroCopyReader, ZeroCopyWriter, }; use crate::virtio::fs::fuse::*; use crate::virtio::fs::{Error, Result}; use crate::virtio::{Reader, Writer}; const MAX_BUFFER_SIZE: u32 = 1 << 20; const DIRENT_PADDING: [u8; 8] = [0; 8]; struct ZCReader<'a>(Reader<'a>); impl<'a> ZeroCopyReader for ZCReader<'a> { fn read_to(&mut self, f: &mut File, count: usize, off: u64) -> io::Result { self.0.read_to_at(f, count, off) } } impl<'a> io::Read for ZCReader<'a> { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } } struct ZCWriter<'a>(Writer<'a>); impl<'a> ZeroCopyWriter for ZCWriter<'a> { fn write_from(&mut self, f: &mut File, count: usize, off: u64) -> io::Result { self.0.write_from_at(f, count, off) } } impl<'a> io::Write for ZCWriter<'a> { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } fn flush(&mut self) -> io::Result<()> { self.0.flush() } } pub struct Server { fs: F, } impl Server { pub fn new(fs: F) -> Server { Server { fs } } pub fn handle_message(&self, mut r: Reader, w: Writer) -> Result { let in_header: InHeader = r.read_obj().map_err(Error::DecodeMessage)?; if in_header.len > MAX_BUFFER_SIZE { return reply_error( io::Error::from_raw_os_error(libc::ENOMEM), in_header.unique, w, ); } match Opcode::n(in_header.opcode) { Some(Opcode::Lookup) => self.lookup(in_header, r, w), Some(Opcode::Forget) => self.forget(in_header, r), // No reply. Some(Opcode::Getattr) => self.getattr(in_header, r, w), Some(Opcode::Setattr) => self.setattr(in_header, r, w), Some(Opcode::Readlink) => self.readlink(in_header, w), Some(Opcode::Symlink) => self.symlink(in_header, r, w), Some(Opcode::Mknod) => self.mknod(in_header, r, w), Some(Opcode::Mkdir) => self.mkdir(in_header, r, w), Some(Opcode::Unlink) => self.unlink(in_header, r, w), Some(Opcode::Rmdir) => self.rmdir(in_header, r, w), Some(Opcode::Rename) => self.rename(in_header, r, w), Some(Opcode::Link) => self.link(in_header, r, w), Some(Opcode::Open) => self.open(in_header, r, w), Some(Opcode::Read) => self.read(in_header, r, w), Some(Opcode::Write) => self.write(in_header, r, w), Some(Opcode::Statfs) => self.statfs(in_header, w), Some(Opcode::Release) => self.release(in_header, r, w), Some(Opcode::Fsync) => self.fsync(in_header, r, w), Some(Opcode::Setxattr) => self.setxattr(in_header, r, w), Some(Opcode::Getxattr) => self.getxattr(in_header, r, w), Some(Opcode::Listxattr) => self.listxattr(in_header, r, w), Some(Opcode::Removexattr) => self.removexattr(in_header, r, w), Some(Opcode::Flush) => self.flush(in_header, r, w), Some(Opcode::Init) => self.init(in_header, r, w), Some(Opcode::Opendir) => self.opendir(in_header, r, w), Some(Opcode::Readdir) => self.readdir(in_header, r, w), Some(Opcode::Releasedir) => self.releasedir(in_header, r, w), Some(Opcode::Fsyncdir) => self.fsyncdir(in_header, r, w), Some(Opcode::Getlk) => self.getlk(in_header, r, w), Some(Opcode::Setlk) => self.setlk(in_header, r, w), Some(Opcode::Setlkw) => self.setlkw(in_header, r, w), Some(Opcode::Access) => self.access(in_header, r, w), Some(Opcode::Create) => self.create(in_header, r, w), Some(Opcode::Interrupt) => self.interrupt(in_header), Some(Opcode::Bmap) => self.bmap(in_header, r, w), Some(Opcode::Destroy) => self.destroy(), Some(Opcode::Ioctl) => self.ioctl(in_header, r, w), Some(Opcode::Poll) => self.poll(in_header, r, w), Some(Opcode::NotifyReply) => self.notify_reply(in_header, r, w), Some(Opcode::BatchForget) => self.batch_forget(in_header, r, w), Some(Opcode::Fallocate) => self.fallocate(in_header, r, w), Some(Opcode::Readdirplus) => self.readdirplus(in_header, r, w), Some(Opcode::Rename2) => self.rename2(in_header, r, w), Some(Opcode::Lseek) => self.lseek(in_header, r, w), Some(Opcode::CopyFileRange) => self.copy_file_range(in_header, r, w), Some(Opcode::SetUpMapping) | Some(Opcode::RemoveMapping) | None => reply_error( io::Error::from_raw_os_error(libc::ENOSYS), in_header.unique, w, ), } } fn lookup(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let namelen = (in_header.len as usize) .checked_sub(size_of::()) .ok_or(Error::InvalidHeaderLength)?; let mut buf = Vec::with_capacity(namelen); buf.resize(namelen, 0); r.read_exact(&mut buf).map_err(Error::DecodeMessage)?; let name = bytes_to_cstr(&buf)?; match self .fs .lookup(Context::from(in_header), in_header.nodeid.into(), &name) { Ok(entry) => { let out = EntryOut::from(entry); reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn forget(&self, in_header: InHeader, mut r: Reader) -> Result { let ForgetIn { nlookup } = r.read_obj().map_err(Error::DecodeMessage)?; self.fs .forget(Context::from(in_header), in_header.nodeid.into(), nlookup); // There is no reply for forget messages. Ok(0) } fn getattr(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let GetattrIn { flags, dummy: _, fh, } = r.read_obj().map_err(Error::DecodeMessage)?; let handle = if (flags & GETATTR_FH) != 0 { Some(fh.into()) } else { None }; match self .fs .getattr(Context::from(in_header), in_header.nodeid.into(), handle) { Ok((st, timeout)) => { let out = AttrOut { attr_valid: timeout.as_secs(), attr_valid_nsec: timeout.subsec_nanos(), dummy: 0, attr: st.into(), }; reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn setattr(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let setattr_in: SetattrIn = r.read_obj().map_err(Error::DecodeMessage)?; let handle = if setattr_in.valid & FATTR_FH != 0 { Some(setattr_in.fh.into()) } else { None }; let valid = SetattrValid::from_bits_truncate(setattr_in.valid); let st: libc::stat64 = setattr_in.into(); match self.fs.setattr( Context::from(in_header), in_header.nodeid.into(), st, handle, valid, ) { Ok((st, timeout)) => { let out = AttrOut { attr_valid: timeout.as_secs(), attr_valid_nsec: timeout.subsec_nanos(), dummy: 0, attr: st.into(), }; reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn readlink(&self, in_header: InHeader, w: Writer) -> Result { match self .fs .readlink(Context::from(in_header), in_header.nodeid.into()) { Ok(linkname) => { // We need to disambiguate the option type here even though it is `None`. reply_ok(None::, Some(&linkname), in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn symlink(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { // Unfortunately the name and linkname are encoded one after another and // separated by a nul character. let len = (in_header.len as usize) .checked_sub(size_of::()) .ok_or(Error::InvalidHeaderLength)?; let mut buf = Vec::with_capacity(len); buf.resize(len, 0); r.read_exact(&mut buf).map_err(Error::DecodeMessage)?; // We want to include the '\0' byte in the first slice. let split_pos = buf .iter() .position(|c| *c == b'\0') .map(|p| p + 1) .ok_or(Error::MissingParameter)?; let (name, linkname) = buf.split_at(split_pos); match self.fs.symlink( Context::from(in_header), bytes_to_cstr(linkname)?, in_header.nodeid.into(), bytes_to_cstr(name)?, ) { Ok(entry) => { let out = EntryOut::from(entry); reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn mknod(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let MknodIn { mode, rdev, umask, .. } = r.read_obj().map_err(Error::DecodeMessage)?; let namelen = (in_header.len as usize) .checked_sub(size_of::()) .and_then(|l| l.checked_sub(size_of::())) .ok_or(Error::InvalidHeaderLength)?; let mut name = Vec::with_capacity(namelen); name.resize(namelen, 0); r.read_exact(&mut name).map_err(Error::DecodeMessage)?; match self.fs.mknod( Context::from(in_header), in_header.nodeid.into(), bytes_to_cstr(&name)?, mode, rdev, umask, ) { Ok(entry) => { let out = EntryOut::from(entry); reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn mkdir(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let MkdirIn { mode, umask } = r.read_obj().map_err(Error::DecodeMessage)?; let namelen = (in_header.len as usize) .checked_sub(size_of::()) .and_then(|l| l.checked_sub(size_of::())) .ok_or(Error::InvalidHeaderLength)?; let mut name = Vec::with_capacity(namelen); name.resize(namelen, 0); r.read_exact(&mut name).map_err(Error::DecodeMessage)?; match self.fs.mkdir( Context::from(in_header), in_header.nodeid.into(), bytes_to_cstr(&name)?, mode, umask, ) { Ok(entry) => { let out = EntryOut::from(entry); reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn unlink(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let namelen = (in_header.len as usize) .checked_sub(size_of::()) .ok_or(Error::InvalidHeaderLength)?; let mut name = Vec::with_capacity(namelen); name.resize(namelen, 0); r.read_exact(&mut name).map_err(Error::DecodeMessage)?; match self.fs.unlink( Context::from(in_header), in_header.nodeid.into(), bytes_to_cstr(&name)?, ) { Ok(()) => reply_ok(None::, None, in_header.unique, w), Err(e) => reply_error(e, in_header.unique, w), } } fn rmdir(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let namelen = (in_header.len as usize) .checked_sub(size_of::()) .ok_or(Error::InvalidHeaderLength)?; let mut name = Vec::with_capacity(namelen); name.resize(namelen, 0); r.read_exact(&mut name).map_err(Error::DecodeMessage)?; match self.fs.rmdir( Context::from(in_header), in_header.nodeid.into(), bytes_to_cstr(&name)?, ) { Ok(()) => reply_ok(None::, None, in_header.unique, w), Err(e) => reply_error(e, in_header.unique, w), } } fn do_rename( &self, in_header: InHeader, msg_size: usize, newdir: u64, flags: u32, mut r: Reader, w: Writer, ) -> Result { let buflen = (in_header.len as usize) .checked_sub(size_of::()) .and_then(|l| l.checked_sub(msg_size)) .ok_or(Error::InvalidHeaderLength)?; let mut buf = Vec::with_capacity(buflen); buf.resize(buflen, 0); r.read_exact(&mut buf).map_err(Error::DecodeMessage)?; // We want to include the '\0' byte in the first slice. let split_pos = buf .iter() .position(|c| *c == b'\0') .map(|p| p + 1) .ok_or(Error::MissingParameter)?; let (oldname, newname) = buf.split_at(split_pos); match self.fs.rename( Context::from(in_header), in_header.nodeid.into(), bytes_to_cstr(oldname)?, newdir.into(), bytes_to_cstr(newname)?, flags, ) { Ok(()) => reply_ok(None::, None, in_header.unique, w), Err(e) => reply_error(e, in_header.unique, w), } } fn rename(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let RenameIn { newdir } = r.read_obj().map_err(Error::DecodeMessage)?; self.do_rename(in_header, size_of::(), newdir, 0, r, w) } fn rename2(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let Rename2In { newdir, flags, .. } = r.read_obj().map_err(Error::DecodeMessage)?; let flags = flags & (libc::RENAME_EXCHANGE | libc::RENAME_NOREPLACE) as u32; self.do_rename(in_header, size_of::(), newdir, flags, r, w) } fn link(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let LinkIn { oldnodeid } = r.read_obj().map_err(Error::DecodeMessage)?; let namelen = (in_header.len as usize) .checked_sub(size_of::()) .and_then(|l| l.checked_sub(size_of::())) .ok_or(Error::InvalidHeaderLength)?; let mut name = Vec::with_capacity(namelen); name.resize(namelen, 0); r.read_exact(&mut name).map_err(Error::DecodeMessage)?; match self.fs.link( Context::from(in_header), oldnodeid.into(), in_header.nodeid.into(), bytes_to_cstr(&name)?, ) { Ok(entry) => { let out = EntryOut::from(entry); reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn open(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let OpenIn { flags, .. } = r.read_obj().map_err(Error::DecodeMessage)?; match self .fs .open(Context::from(in_header), in_header.nodeid.into(), flags) { Ok((handle, opts)) => { let out = OpenOut { fh: handle.map(Into::into).unwrap_or(0), open_flags: opts.bits(), ..Default::default() }; reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn read(&self, in_header: InHeader, mut r: Reader, mut w: Writer) -> Result { let ReadIn { fh, offset, size, read_flags, lock_owner, flags, .. } = r.read_obj().map_err(Error::DecodeMessage)?; if size > MAX_BUFFER_SIZE { return reply_error( io::Error::from_raw_os_error(libc::ENOMEM), in_header.unique, w, ); } let owner = if read_flags & READ_LOCKOWNER != 0 { Some(lock_owner) } else { None }; // Split the writer into 2 pieces: one for the `OutHeader` and the rest for the data. let data_writer = ZCWriter(w.split_at(size_of::())); match self.fs.read( Context::from(in_header), in_header.nodeid.into(), fh.into(), data_writer, size, offset, owner, flags, ) { Ok(count) => { // Don't use `reply_ok` because we need to set a custom size length for the // header. let out = OutHeader { len: (size_of::() + count) as u32, error: 0, unique: in_header.unique, }; w.write_all(out.as_slice()).map_err(Error::EncodeMessage)?; Ok(out.len as usize) } Err(e) => reply_error(e, in_header.unique, w), } } fn write(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let WriteIn { fh, offset, size, write_flags, lock_owner, flags, .. } = r.read_obj().map_err(Error::DecodeMessage)?; if size > MAX_BUFFER_SIZE { return reply_error( io::Error::from_raw_os_error(libc::ENOMEM), in_header.unique, w, ); } let owner = if write_flags & WRITE_LOCKOWNER != 0 { Some(lock_owner) } else { None }; let delayed_write = write_flags & WRITE_CACHE != 0; let data_reader = ZCReader(r); match self.fs.write( Context::from(in_header), in_header.nodeid.into(), fh.into(), data_reader, size, offset, owner, delayed_write, flags, ) { Ok(count) => { let out = WriteOut { size: count as u32, ..Default::default() }; reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn statfs(&self, in_header: InHeader, w: Writer) -> Result { match self .fs .statfs(Context::from(in_header), in_header.nodeid.into()) { Ok(st) => reply_ok(Some(Kstatfs::from(st)), None, in_header.unique, w), Err(e) => reply_error(e, in_header.unique, w), } } fn release(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let ReleaseIn { fh, flags, release_flags, lock_owner, } = r.read_obj().map_err(Error::DecodeMessage)?; let flush = release_flags & RELEASE_FLUSH != 0; let flock_release = release_flags & RELEASE_FLOCK_UNLOCK != 0; let lock_owner = if flush || flock_release { Some(lock_owner) } else { None }; match self.fs.release( Context::from(in_header), in_header.nodeid.into(), flags, fh.into(), flush, flock_release, lock_owner, ) { Ok(()) => reply_ok(None::, None, in_header.unique, w), Err(e) => reply_error(e, in_header.unique, w), } } fn fsync(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let FsyncIn { fh, fsync_flags, .. } = r.read_obj().map_err(Error::DecodeMessage)?; let datasync = fsync_flags & 0x1 != 0; match self.fs.fsync( Context::from(in_header), in_header.nodeid.into(), datasync, fh.into(), ) { Ok(()) => reply_ok(None::, None, in_header.unique, w), Err(e) => reply_error(e, in_header.unique, w), } } fn setxattr(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let SetxattrIn { size, flags } = r.read_obj().map_err(Error::DecodeMessage)?; // The name and value and encoded one after another and separated by a '\0' character. let len = (in_header.len as usize) .checked_sub(size_of::()) .and_then(|l| l.checked_sub(size_of::())) .ok_or(Error::InvalidHeaderLength)?; let mut buf = Vec::with_capacity(len); buf.resize(len, 0); r.read_exact(&mut buf).map_err(Error::DecodeMessage)?; // We want to include the '\0' byte in the first slice. let split_pos = buf .iter() .position(|c| *c == b'\0') .map(|p| p + 1) .ok_or(Error::MissingParameter)?; let (name, value) = buf.split_at(split_pos); if size != value.len() as u32 { return Err(Error::InvalidXattrSize((size, value.len()))); } match self.fs.setxattr( Context::from(in_header), in_header.nodeid.into(), bytes_to_cstr(name)?, value, flags, ) { Ok(()) => reply_ok(None::, None, in_header.unique, w), Err(e) => reply_error(e, in_header.unique, w), } } fn getxattr(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let GetxattrIn { size, .. } = r.read_obj().map_err(Error::DecodeMessage)?; let namelen = (in_header.len as usize) .checked_sub(size_of::()) .and_then(|l| l.checked_sub(size_of::())) .ok_or(Error::InvalidHeaderLength)?; let mut name = Vec::with_capacity(namelen); name.resize(namelen, 0); r.read_exact(&mut name).map_err(Error::DecodeMessage)?; if size > MAX_BUFFER_SIZE { return reply_error( io::Error::from_raw_os_error(libc::ENOMEM), in_header.unique, w, ); } match self.fs.getxattr( Context::from(in_header), in_header.nodeid.into(), bytes_to_cstr(&name)?, size, ) { Ok(GetxattrReply::Value(val)) => reply_ok(None::, Some(&val), in_header.unique, w), Ok(GetxattrReply::Count(count)) => { let out = GetxattrOut { size: count, ..Default::default() }; reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn listxattr(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let GetxattrIn { size, .. } = r.read_obj().map_err(Error::DecodeMessage)?; if size > MAX_BUFFER_SIZE { return reply_error( io::Error::from_raw_os_error(libc::ENOMEM), in_header.unique, w, ); } match self .fs .listxattr(Context::from(in_header), in_header.nodeid.into(), size) { Ok(ListxattrReply::Names(val)) => reply_ok(None::, Some(&val), in_header.unique, w), Ok(ListxattrReply::Count(count)) => { let out = GetxattrOut { size: count, ..Default::default() }; reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn removexattr(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let namelen = (in_header.len as usize) .checked_sub(size_of::()) .ok_or(Error::InvalidHeaderLength)?; let mut buf = Vec::with_capacity(namelen); buf.resize(namelen, 0); r.read_exact(&mut buf).map_err(Error::DecodeMessage)?; let name = bytes_to_cstr(&buf)?; match self .fs .removexattr(Context::from(in_header), in_header.nodeid.into(), name) { Ok(()) => reply_ok(None::, None, in_header.unique, w), Err(e) => reply_error(e, in_header.unique, w), } } fn flush(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let FlushIn { fh, unused: _, padding: _, lock_owner, } = r.read_obj().map_err(Error::DecodeMessage)?; match self.fs.flush( Context::from(in_header), in_header.nodeid.into(), fh.into(), lock_owner, ) { Ok(()) => reply_ok(None::, None, in_header.unique, w), Err(e) => reply_error(e, in_header.unique, w), } } fn init(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let InitIn { major, minor, max_readahead, flags, } = r.read_obj().map_err(Error::DecodeMessage)?; if major < KERNEL_VERSION { error!("Unsupported fuse protocol version: {}.{}", major, minor); return reply_error( io::Error::from_raw_os_error(libc::EPROTO), in_header.unique, w, ); } if major > KERNEL_VERSION { // Wait for the kernel to reply back with a 7.X version. let out = InitOut { major: KERNEL_VERSION, minor: KERNEL_MINOR_VERSION, ..Default::default() }; return reply_ok(Some(out), None, in_header.unique, w); } if minor < OLDEST_SUPPORTED_KERNEL_MINOR_VERSION { error!( "Unsupported fuse protocol minor version: {}.{}", major, minor ); return reply_error( io::Error::from_raw_os_error(libc::EPROTO), in_header.unique, w, ); } // These fuse features are supported by this server by default. let supported = FsOptions::ASYNC_READ | FsOptions::PARALLEL_DIROPS | FsOptions::BIG_WRITES | FsOptions::AUTO_INVAL_DATA | FsOptions::HANDLE_KILLPRIV | FsOptions::ASYNC_DIO | FsOptions::HAS_IOCTL_DIR | FsOptions::ATOMIC_O_TRUNC; let capable = FsOptions::from_bits_truncate(flags); match self.fs.init(capable) { Ok(want) => { let mut enabled = capable & (want | supported); // HANDLE_KILLPRIV doesn't work correctly when writeback caching is enabled so turn // it off. if enabled.contains(FsOptions::WRITEBACK_CACHE) { enabled.remove(FsOptions::HANDLE_KILLPRIV); } let out = InitOut { major: KERNEL_VERSION, minor: KERNEL_MINOR_VERSION, max_readahead, flags: enabled.bits(), max_background: ::std::u16::MAX, congestion_threshold: (::std::u16::MAX / 4) * 3, max_write: MAX_BUFFER_SIZE, time_gran: 1, // nanoseconds ..Default::default() }; reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn opendir(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let OpenIn { flags, .. } = r.read_obj().map_err(Error::DecodeMessage)?; match self .fs .opendir(Context::from(in_header), in_header.nodeid.into(), flags) { Ok((handle, opts)) => { let out = OpenOut { fh: handle.map(Into::into).unwrap_or(0), open_flags: opts.bits(), ..Default::default() }; reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } fn do_readdir( &self, in_header: InHeader, mut r: Reader, mut w: Writer, plus: bool, ) -> Result { let ReadIn { fh, offset, size, .. } = r.read_obj().map_err(Error::DecodeMessage)?; if size > MAX_BUFFER_SIZE { return reply_error( io::Error::from_raw_os_error(libc::ENOMEM), in_header.unique, w, ); } let available_bytes = w.available_bytes(); if available_bytes < size as usize { return reply_error( io::Error::from_raw_os_error(libc::ENOMEM), in_header.unique, w, ); } // Skip over enough bytes for the header. let mut cursor = w.split_at(size_of::()); let res = if plus { self.fs.readdirplus( Context::from(in_header), in_header.nodeid.into(), fh.into(), size, offset, |d, e| add_dirent(&mut cursor, size, d, Some(e)), ) } else { self.fs.readdir( Context::from(in_header), in_header.nodeid.into(), fh.into(), size, offset, |d| add_dirent(&mut cursor, size, d, None), ) }; if let Err(e) = res { reply_error(e, in_header.unique, w) } else { // Don't use `reply_ok` because we need to set a custom size length for the // header. let out = OutHeader { len: (size_of::() + cursor.bytes_written()) as u32, error: 0, unique: in_header.unique, }; w.write_all(out.as_slice()).map_err(Error::EncodeMessage)?; Ok(out.len as usize) } } fn readdir(&self, in_header: InHeader, r: Reader, w: Writer) -> Result { self.do_readdir(in_header, r, w, false) } fn readdirplus(&self, in_header: InHeader, r: Reader, w: Writer) -> Result { self.do_readdir(in_header, r, w, true) } fn releasedir(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let ReleaseIn { fh, flags, .. } = r.read_obj().map_err(Error::DecodeMessage)?; match self.fs.releasedir( Context::from(in_header), in_header.nodeid.into(), flags, fh.into(), ) { Ok(()) => reply_ok(None::, None, in_header.unique, w), Err(e) => reply_error(e, in_header.unique, w), } } fn fsyncdir(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let FsyncIn { fh, fsync_flags, .. } = r.read_obj().map_err(Error::DecodeMessage)?; let datasync = fsync_flags & 0x1 != 0; match self.fs.fsyncdir( Context::from(in_header), in_header.nodeid.into(), datasync, fh.into(), ) { Ok(()) => reply_ok(None::, None, in_header.unique, w), Err(e) => reply_error(e, in_header.unique, w), } } fn getlk(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result { if let Err(e) = self.fs.getlk() { reply_error(e, in_header.unique, w) } else { Ok(0) } } fn setlk(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result { if let Err(e) = self.fs.setlk() { reply_error(e, in_header.unique, w) } else { Ok(0) } } fn setlkw(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result { if let Err(e) = self.fs.setlkw() { reply_error(e, in_header.unique, w) } else { Ok(0) } } fn access(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let AccessIn { mask, .. } = r.read_obj().map_err(Error::DecodeMessage)?; match self .fs .access(Context::from(in_header), in_header.nodeid.into(), mask) { Ok(()) => reply_ok(None::, None, in_header.unique, w), Err(e) => reply_error(e, in_header.unique, w), } } fn create(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let CreateIn { flags, mode, umask, .. } = r.read_obj().map_err(Error::DecodeMessage)?; let namelen = (in_header.len as usize) .checked_sub(size_of::()) .and_then(|l| l.checked_sub(size_of::())) .ok_or(Error::InvalidHeaderLength)?; let mut buf = Vec::with_capacity(namelen); buf.resize(namelen, 0); r.read_exact(&mut buf).map_err(Error::DecodeMessage)?; let name = bytes_to_cstr(&buf)?; match self.fs.create( Context::from(in_header), in_header.nodeid.into(), name, mode, flags, umask, ) { Ok((entry, handle, opts)) => { let entry_out = EntryOut { nodeid: entry.inode, generation: entry.generation, entry_valid: entry.entry_timeout.as_secs(), attr_valid: entry.attr_timeout.as_secs(), entry_valid_nsec: entry.entry_timeout.subsec_nanos(), attr_valid_nsec: entry.attr_timeout.subsec_nanos(), attr: entry.attr.into(), }; let open_out = OpenOut { fh: handle.map(Into::into).unwrap_or(0), open_flags: opts.bits(), ..Default::default() }; // Kind of a hack to write both structs. reply_ok( Some(entry_out), Some(open_out.as_slice()), in_header.unique, w, ) } Err(e) => reply_error(e, in_header.unique, w), } } fn interrupt(&self, _in_header: InHeader) -> Result { Ok(0) } fn bmap(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result { if let Err(e) = self.fs.bmap() { reply_error(e, in_header.unique, w) } else { Ok(0) } } fn destroy(&self) -> Result { // No reply to this function. self.fs.destroy(); Ok(0) } fn ioctl(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let IoctlIn { fh, flags, cmd, arg, in_size, out_size, } = r.read_obj().map_err(Error::DecodeMessage)?; let res = self.fs.ioctl( in_header.into(), fh.into(), IoctlFlags::from_bits_truncate(flags), cmd, arg, in_size, out_size, r, ); match res { Ok(reply) => match reply { IoctlReply::Retry { input, output } => { retry_ioctl(in_header.unique, input, output, w) } IoctlReply::Done(res) => finish_ioctl(in_header.unique, res, w), }, Err(e) => reply_error(e, in_header.unique, w), } } fn poll(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result { if let Err(e) = self.fs.poll() { reply_error(e, in_header.unique, w) } else { Ok(0) } } fn notify_reply(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result { if let Err(e) = self.fs.notify_reply() { reply_error(e, in_header.unique, w) } else { Ok(0) } } fn batch_forget(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let BatchForgetIn { count, .. } = r.read_obj().map_err(Error::DecodeMessage)?; if let Some(size) = (count as usize).checked_mul(size_of::()) { if size > MAX_BUFFER_SIZE as usize { return reply_error( io::Error::from_raw_os_error(libc::ENOMEM), in_header.unique, w, ); } } else { return reply_error( io::Error::from_raw_os_error(libc::EOVERFLOW), in_header.unique, w, ); } let mut requests = Vec::with_capacity(count as usize); for _ in 0..count { requests.push( r.read_obj::() .map(|f| (f.nodeid.into(), f.nlookup)) .map_err(Error::DecodeMessage)?, ); } self.fs.batch_forget(Context::from(in_header), requests); // No reply for forget messages. Ok(0) } fn fallocate(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let FallocateIn { fh, offset, length, mode, .. } = r.read_obj().map_err(Error::DecodeMessage)?; match self.fs.fallocate( Context::from(in_header), in_header.nodeid.into(), fh.into(), mode, offset, length, ) { Ok(()) => reply_ok(None::, None, in_header.unique, w), Err(e) => reply_error(e, in_header.unique, w), } } fn lseek(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result { if let Err(e) = self.fs.lseek() { reply_error(e, in_header.unique, w) } else { Ok(0) } } fn copy_file_range(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let CopyFileRangeIn { fh_src, off_src, nodeid_dst, fh_dst, off_dst, len, flags, } = r.read_obj().map_err(Error::DecodeMessage)?; match self.fs.copy_file_range( Context::from(in_header), in_header.nodeid.into(), fh_src.into(), off_src, nodeid_dst.into(), fh_dst.into(), off_dst, len, flags, ) { Ok(count) => { let out = WriteOut { size: count as u32, ..Default::default() }; reply_ok(Some(out), None, in_header.unique, w) } Err(e) => reply_error(e, in_header.unique, w), } } } fn retry_ioctl( unique: u64, input: Vec, output: Vec, mut w: Writer, ) -> Result { // We don't need to check for overflow here because if adding these 2 values caused an overflow // we would have run out of memory before reaching this point. if input.len() + output.len() > IOCTL_MAX_IOV { return Err(Error::TooManyIovecs(( input.len() + output.len(), IOCTL_MAX_IOV, ))); } let len = size_of::() + size_of::() + (input.len() * size_of::()) + (output.len() * size_of::()); let header = OutHeader { len: len as u32, error: 0, unique, }; let out = IoctlOut { result: 0, flags: IoctlFlags::RETRY.bits(), in_iovs: input.len() as u32, out_iovs: output.len() as u32, }; w.write_obj(header).map_err(Error::EncodeMessage)?; w.write_obj(out).map_err(Error::EncodeMessage)?; for i in input.into_iter().chain(output.into_iter()) { w.write_obj(i).map_err(Error::EncodeMessage)?; } debug_assert_eq!(len, w.bytes_written()); Ok(w.bytes_written()) } fn finish_ioctl(unique: u64, res: io::Result>, w: Writer) -> Result { let (out, data) = match res { Ok(data) => { let out = IoctlOut { result: 0, ..Default::default() }; (out, Some(data)) } Err(e) => { let out = IoctlOut { result: -e.raw_os_error().unwrap_or(libc::EIO), ..Default::default() }; (out, None) } }; reply_ok(Some(out), data.as_ref().map(|d| &d[..]), unique, w) } fn reply_ok( out: Option, data: Option<&[u8]>, unique: u64, mut w: Writer, ) -> Result { let mut len = size_of::(); if out.is_some() { len += size_of::(); } if let Some(ref data) = data { len += data.len(); } let header = OutHeader { len: len as u32, error: 0, unique, }; w.write_all(header.as_slice()) .map_err(Error::EncodeMessage)?; if let Some(out) = out { w.write_all(out.as_slice()).map_err(Error::EncodeMessage)?; } if let Some(data) = data { w.write_all(data).map_err(Error::EncodeMessage)?; } debug_assert_eq!(len, w.bytes_written()); Ok(w.bytes_written()) } fn reply_error(e: io::Error, unique: u64, mut w: Writer) -> Result { let header = OutHeader { len: size_of::() as u32, error: -e.raw_os_error().unwrap_or(libc::EIO), unique, }; w.write_all(header.as_slice()) .map_err(Error::EncodeMessage)?; debug_assert_eq!(header.len as usize, w.bytes_written()); Ok(w.bytes_written()) } fn bytes_to_cstr(buf: &[u8]) -> Result<&CStr> { // Convert to a `CStr` first so that we can drop the '\0' byte at the end // and make sure there are no interior '\0' bytes. CStr::from_bytes_with_nul(buf).map_err(Error::InvalidCString) } fn add_dirent( cursor: &mut Writer, max: u32, d: DirEntry, entry: Option, ) -> io::Result { // Strip the trailing '\0'. let name = d.name.to_bytes(); if name.len() > ::std::u32::MAX as usize { return Err(io::Error::from_raw_os_error(libc::EOVERFLOW)); } let dirent_len = size_of::() .checked_add(name.len()) .ok_or_else(|| io::Error::from_raw_os_error(libc::EOVERFLOW))?; // Directory entries must be padded to 8-byte alignment. If adding 7 causes // an overflow then this dirent cannot be properly padded. let padded_dirent_len = dirent_len .checked_add(7) .map(|l| l & !7) .ok_or_else(|| io::Error::from_raw_os_error(libc::EOVERFLOW))?; let total_len = if entry.is_some() { padded_dirent_len .checked_add(size_of::()) .ok_or_else(|| io::Error::from_raw_os_error(libc::EOVERFLOW))? } else { padded_dirent_len }; if (max as usize).saturating_sub(cursor.bytes_written()) < total_len { Ok(0) } else { if let Some(entry) = entry { cursor.write_all(EntryOut::from(entry).as_slice())?; } let dirent = Dirent { ino: d.ino, off: d.offset, namelen: name.len() as u32, type_: d.type_, }; cursor.write_all(dirent.as_slice())?; cursor.write_all(name)?; // We know that `dirent_len` <= `padded_dirent_len` due to the check above // so there's no need for checked arithmetic. let padding = padded_dirent_len - dirent_len; if padding > 0 { cursor.write_all(&DIRENT_PADDING[..padding])?; } Ok(total_len) } }