cloud-hypervisor/vhost_user_fs/src/server.rs

1270 lines
41 KiB
Rust
Raw Normal View History

// 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 libc;
use vm_memory::ByteValued;
use crate::descriptor_utils::{Reader, Writer};
use crate::filesystem::{
Context, DirEntry, Entry, FileSystem, GetxattrReply, ListxattrReply, ZeroCopyReader,
ZeroCopyWriter,
};
use crate::fuse::*;
use crate::{Error, Result};
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<usize> {
self.0.read_to_at(f, count, off)
}
}
impl<'a> io::Read for ZCReader<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
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<usize> {
self.0.write_from_at(f, count, off)
}
}
impl<'a> io::Write for ZCWriter<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}
pub struct Server<F: FileSystem + Sync> {
fs: F,
}
impl<F: FileSystem + Sync> Server<F> {
pub fn new(fs: F) -> Server<F> {
Server { fs }
}
#[allow(clippy::cognitive_complexity)]
pub fn handle_message(&self, mut r: Reader, w: Writer) -> Result<usize> {
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 in_header.opcode {
x if x == Opcode::Lookup as u32 => self.lookup(in_header, r, w),
x if x == Opcode::Forget as u32 => self.forget(in_header, r), // No reply.
x if x == Opcode::Getattr as u32 => self.getattr(in_header, r, w),
x if x == Opcode::Setattr as u32 => self.setattr(in_header, r, w),
x if x == Opcode::Readlink as u32 => self.readlink(in_header, w),
x if x == Opcode::Symlink as u32 => self.symlink(in_header, r, w),
x if x == Opcode::Mknod as u32 => self.mknod(in_header, r, w),
x if x == Opcode::Mkdir as u32 => self.mkdir(in_header, r, w),
x if x == Opcode::Unlink as u32 => self.unlink(in_header, r, w),
x if x == Opcode::Rmdir as u32 => self.rmdir(in_header, r, w),
x if x == Opcode::Rename as u32 => self.rename(in_header, r, w),
x if x == Opcode::Link as u32 => self.link(in_header, r, w),
x if x == Opcode::Open as u32 => self.open(in_header, r, w),
x if x == Opcode::Read as u32 => self.read(in_header, r, w),
x if x == Opcode::Write as u32 => self.write(in_header, r, w),
x if x == Opcode::Statfs as u32 => self.statfs(in_header, w),
x if x == Opcode::Release as u32 => self.release(in_header, r, w),
x if x == Opcode::Fsync as u32 => self.fsync(in_header, r, w),
x if x == Opcode::Setxattr as u32 => self.setxattr(in_header, r, w),
x if x == Opcode::Getxattr as u32 => self.getxattr(in_header, r, w),
x if x == Opcode::Listxattr as u32 => self.listxattr(in_header, r, w),
x if x == Opcode::Removexattr as u32 => self.removexattr(in_header, r, w),
x if x == Opcode::Flush as u32 => self.flush(in_header, r, w),
x if x == Opcode::Init as u32 => self.init(in_header, r, w),
x if x == Opcode::Opendir as u32 => self.opendir(in_header, r, w),
x if x == Opcode::Readdir as u32 => self.readdir(in_header, r, w),
x if x == Opcode::Releasedir as u32 => self.releasedir(in_header, r, w),
x if x == Opcode::Fsyncdir as u32 => self.fsyncdir(in_header, r, w),
x if x == Opcode::Getlk as u32 => self.getlk(in_header, r, w),
x if x == Opcode::Setlk as u32 => self.setlk(in_header, r, w),
x if x == Opcode::Setlkw as u32 => self.setlkw(in_header, r, w),
x if x == Opcode::Access as u32 => self.access(in_header, r, w),
x if x == Opcode::Create as u32 => self.create(in_header, r, w),
x if x == Opcode::Interrupt as u32 => self.interrupt(in_header),
x if x == Opcode::Bmap as u32 => self.bmap(in_header, r, w),
x if x == Opcode::Destroy as u32 => self.destroy(),
x if x == Opcode::Ioctl as u32 => self.ioctl(in_header, r, w),
x if x == Opcode::Poll as u32 => self.poll(in_header, r, w),
x if x == Opcode::NotifyReply as u32 => self.notify_reply(in_header, r, w),
x if x == Opcode::BatchForget as u32 => self.batch_forget(in_header, r, w),
x if x == Opcode::Fallocate as u32 => self.fallocate(in_header, r, w),
x if x == Opcode::Readdirplus as u32 => self.readdirplus(in_header, r, w),
x if x == Opcode::Rename2 as u32 => self.rename2(in_header, r, w),
x if x == Opcode::Lseek as u32 => self.lseek(in_header, r, w),
_ => 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<usize> {
let namelen = (in_header.len as usize)
.checked_sub(size_of::<InHeader>())
.ok_or(Error::InvalidHeaderLength)?;
let mut buf = vec![0u8; namelen];
r.read_exact(&mut buf).map_err(Error::DecodeMessage)?;
let name = bytes_to_cstr(buf.as_ref())?;
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<usize> {
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<usize> {
let GetattrIn { flags, 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<usize> {
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<usize> {
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::<u8>, 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<usize> {
// 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::<InHeader>())
.ok_or(Error::InvalidHeaderLength)?;
let mut buf = vec![0; len];
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<usize> {
let MknodIn {
mode, rdev, umask, ..
} = r.read_obj().map_err(Error::DecodeMessage)?;
let namelen = (in_header.len as usize)
.checked_sub(size_of::<InHeader>())
.and_then(|l| l.checked_sub(size_of::<MknodIn>()))
.ok_or(Error::InvalidHeaderLength)?;
let mut name = vec![0; namelen];
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<usize> {
let MkdirIn { mode, umask } = r.read_obj().map_err(Error::DecodeMessage)?;
let namelen = (in_header.len as usize)
.checked_sub(size_of::<InHeader>())
.and_then(|l| l.checked_sub(size_of::<MkdirIn>()))
.ok_or(Error::InvalidHeaderLength)?;
let mut name = vec![0; namelen];
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<usize> {
let namelen = (in_header.len as usize)
.checked_sub(size_of::<InHeader>())
.ok_or(Error::InvalidHeaderLength)?;
let mut name = vec![0; namelen];
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::<u8>, 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<usize> {
let namelen = (in_header.len as usize)
.checked_sub(size_of::<InHeader>())
.ok_or(Error::InvalidHeaderLength)?;
let mut name = vec![0; namelen];
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::<u8>, 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<usize> {
let buflen = (in_header.len as usize)
.checked_sub(size_of::<InHeader>())
.and_then(|l| l.checked_sub(msg_size))
.ok_or(Error::InvalidHeaderLength)?;
let mut buf = vec![0; buflen];
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::<u8>, 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<usize> {
let RenameIn { newdir } = r.read_obj().map_err(Error::DecodeMessage)?;
self.do_rename(in_header, size_of::<RenameIn>(), newdir, 0, r, w)
}
fn rename2(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
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::<Rename2In>(), newdir, flags, r, w)
}
fn link(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
let LinkIn { oldnodeid } = r.read_obj().map_err(Error::DecodeMessage)?;
let namelen = (in_header.len as usize)
.checked_sub(size_of::<InHeader>())
.and_then(|l| l.checked_sub(size_of::<LinkIn>()))
.ok_or(Error::InvalidHeaderLength)?;
let mut name = vec![0; namelen];
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<usize> {
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<usize> {
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::<OutHeader>()).unwrap());
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::<OutHeader>() + 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<usize> {
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<usize> {
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<usize> {
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::<u8>, 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<usize> {
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::<u8>, 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<usize> {
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::<InHeader>())
.and_then(|l| l.checked_sub(size_of::<SetxattrIn>()))
.ok_or(Error::InvalidHeaderLength)?;
let mut buf = vec![0; len];
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::<u8>, 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<usize> {
let GetxattrIn { size, .. } = r.read_obj().map_err(Error::DecodeMessage)?;
let namelen = (in_header.len as usize)
.checked_sub(size_of::<InHeader>())
.and_then(|l| l.checked_sub(size_of::<GetxattrIn>()))
.ok_or(Error::InvalidHeaderLength)?;
let mut name = vec![0; namelen];
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::<u8>, 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<usize> {
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::<u8>, 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<usize> {
let namelen = (in_header.len as usize)
.checked_sub(size_of::<InHeader>())
.ok_or(Error::InvalidHeaderLength)?;
let mut buf = vec![0; namelen];
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::<u8>, 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<usize> {
let FlushIn { fh, 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::<u8>, 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<usize> {
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 < 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 enabled = capable & (want | supported);
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<usize> {
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<usize> {
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::<OutHeader>()).unwrap();
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::<OutHeader>() + 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<usize> {
self.do_readdir(in_header, r, w, false)
}
fn readdirplus(&self, in_header: InHeader, r: Reader, w: Writer) -> Result<usize> {
self.do_readdir(in_header, r, w, true)
}
fn releasedir(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
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::<u8>, 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<usize> {
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::<u8>, 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<usize> {
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<usize> {
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<usize> {
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<usize> {
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::<u8>, 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<usize> {
let CreateIn {
flags, mode, umask, ..
} = r.read_obj().map_err(Error::DecodeMessage)?;
let namelen = (in_header.len as usize)
.checked_sub(size_of::<InHeader>())
.and_then(|l| l.checked_sub(size_of::<CreateIn>()))
.ok_or(Error::InvalidHeaderLength)?;
let mut buf = vec![0; namelen];
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<usize> {
Ok(0)
}
fn bmap(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result<usize> {
if let Err(e) = self.fs.bmap() {
reply_error(e, in_header.unique, w)
} else {
Ok(0)
}
}
fn destroy(&self) -> Result<usize> {
// No reply to this function.
self.fs.destroy();
Ok(0)
}
fn ioctl(&self, in_header: InHeader, _r: Reader, w: Writer) -> Result<usize> {
if let Err(e) = self.fs.ioctl() {
reply_error(e, in_header.unique, w)
} else {
Ok(0)
}
}
fn poll(&self, in_header: InHeader, mut _r: Reader, w: Writer) -> Result<usize> {
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<usize> {
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<usize> {
let BatchForgetIn { count, .. } = r.read_obj().map_err(Error::DecodeMessage)?;
if let Some(size) = (count as usize).checked_mul(size_of::<ForgetOne>()) {
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::<ForgetOne>()
.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<usize> {
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::<u8>, 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<usize> {
if let Err(e) = self.fs.lseek() {
reply_error(e, in_header.unique, w)
} else {
Ok(0)
}
}
}
fn reply_ok<T: ByteValued>(
out: Option<T>,
data: Option<&[u8]>,
unique: u64,
mut w: Writer,
) -> Result<usize> {
let mut len = size_of::<OutHeader>();
if out.is_some() {
len += size_of::<T>();
}
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<usize> {
let header = OutHeader {
len: size_of::<OutHeader>() 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<Entry>,
) -> io::Result<usize> {
if d.name.len() > ::std::u32::MAX as usize {
return Err(io::Error::from_raw_os_error(libc::EOVERFLOW));
}
let dirent_len = size_of::<Dirent>()
.checked_add(d.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::<EntryOut>())
.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: d.name.len() as u32,
type_: d.type_,
};
cursor.write_all(dirent.as_slice())?;
cursor.write_all(d.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)
}
}