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

feat(ofs): rename2 lseek copy_file_range getattr API support #4395

Merged
merged 10 commits into from
Mar 27, 2024
Merged
Changes from 9 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
171 changes: 163 additions & 8 deletions bin/ofs/src/fuse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ use std::ffi::OsString;
use std::num::NonZeroU32;
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::RwLock;
use std::time::Duration;
use std::time::SystemTime;

use bytes::Bytes;

use fuse3::path::prelude::*;
use fuse3::Errno;
use fuse3::Result;
Expand All @@ -46,6 +48,7 @@ struct OpenedFile {
is_read: bool,
is_write: bool,
is_append: bool,
offset: u64,
}

#[derive(Debug, Clone, Copy)]
Expand All @@ -72,7 +75,7 @@ pub(super) struct Fuse {
op: Operator,
gid: u32,
uid: u32,
opened_files: Slab<OpenedFile>,
opened_files: Slab<RwLock<OpenedFile>>,
}

impl Fuse {
Expand Down Expand Up @@ -107,13 +110,18 @@ impl Fuse {

// Get opened file and check given path
fn get_opened_file(&self, key: FileKey, path: Option<&OsStr>) -> Result<OpenedFile> {
let file = self
let file = match self
.opened_files
.get(key.0)
.as_ref()
.ok_or(Errno::from(libc::ENOENT))?
.deref()
.clone();
.read()
{
Ok(file) => file.clone(),
Err(_) => Err(Errno::from(libc::EBADF))?,
};

if matches!(path, Some(path) if path != file.path) {
log::trace!(
"get_opened_file: path not match: path={:?}, file={:?}",
Expand All @@ -125,6 +133,37 @@ impl Fuse {

Ok(file)
}

// Set opened file offset
fn set_opened_file_offset(
&self,
key: FileKey,
path: Option<&OsStr>,
offset: u64,
) -> Result<()> {
let binding = self.opened_files.get(key.0);
let mut file = match binding
.as_ref()
.ok_or(Errno::from(libc::ENOENT))?
.deref()
.write()
{
Ok(file) => file,
Err(_) => Err(Errno::from(libc::EBADF))?,
};
if matches!(path, Some(path) if path != file.path) {
log::trace!(
"set_opened_file: path not match: path={:?}, file={:?}",
path,
file.path
);
Err(Errno::from(libc::EBADF))?;
}

file.offset = offset;

Ok(())
}
}

impl PathFilesystem for Fuse {
Expand Down Expand Up @@ -170,6 +209,7 @@ impl PathFilesystem for Fuse {
self.opened_files
.get(FileKey::try_from(fh).ok()?.0)
.as_ref()
.and_then(|f| f.read().ok())
.map(|f| f.path.clone())
});

Expand Down Expand Up @@ -208,7 +248,8 @@ impl PathFilesystem for Fuse {
fh,
set_attr
);
Err(libc::EOPNOTSUPP.into())

self.getattr(_req, path, fh, 0).await
}

async fn symlink(
Expand Down Expand Up @@ -313,6 +354,10 @@ impl PathFilesystem for Fuse {
name
);

if !self.op.info().full_capability().rename {
return Err(Errno::from(libc::EACCES))?;
oowl marked this conversation as resolved.
Show resolved Hide resolved
}

let origin_path = PathBuf::from(origin_parent).join(origin_name);
let path = PathBuf::from(parent).join(name);

Expand Down Expand Up @@ -369,12 +414,13 @@ impl PathFilesystem for Fuse {

let key = self
.opened_files
.insert(OpenedFile {
.insert(RwLock::new(OpenedFile {
path: path.into(),
is_read,
is_write,
is_append: flags & libc::O_APPEND as u32 != 0,
})
offset: 0,
}))
.ok_or(Errno::from(libc::EBUSY))?;

Ok(ReplyCreated {
Expand Down Expand Up @@ -407,6 +453,7 @@ impl PathFilesystem for Fuse {
let file = self
.opened_files
.take(FileKey::try_from(fh)?.0)
.map(|f| f.read().unwrap().clone())
.ok_or(Errno::from(libc::EBADF))?;
if matches!(path, Some(ref p) if p != &file.path) {
Err(Errno::from(libc::EBADF))?;
Expand All @@ -422,12 +469,13 @@ impl PathFilesystem for Fuse {

let key = self
.opened_files
.insert(OpenedFile {
.insert(RwLock::new(OpenedFile {
path: path.into(),
is_read,
is_write,
is_append: flags & libc::O_APPEND as u32 != 0,
})
offset: 0,
}))
.ok_or(Errno::from(libc::EBUSY))?;

Ok(ReplyOpen {
Expand Down Expand Up @@ -465,6 +513,8 @@ impl PathFilesystem for Fuse {
.await
.map_err(opendal_error2errno)?;

self.set_opened_file_offset(FileKey::try_from(fh)?, path, offset + data.len() as u64)?;

Ok(ReplyData { data: data.into() })
}

Expand Down Expand Up @@ -505,6 +555,8 @@ impl PathFilesystem for Fuse {
.await
.map_err(opendal_error2errno)?;

self.set_opened_file_offset(FileKey::try_from(fh)?, path, offset + data.len() as u64)?;

Ok(ReplyWrite {
written: data.len() as _,
})
Expand Down Expand Up @@ -638,6 +690,109 @@ impl PathFilesystem for Fuse {
entries: relative_paths.chain(children).skip(offset as usize).boxed(),
})
}
async fn rename2(
&self,
req: Request,
origin_parent: &OsStr,
origin_name: &OsStr,
parent: &OsStr,
name: &OsStr,
_flags: u32,
) -> Result<()> {
log::debug!(
"rename2(origin_parent={:?}, origin_name={:?}, parent={:?}, name={:?})",
origin_parent,
origin_name,
parent,
name
);
self.rename(req, origin_parent, origin_name, parent, name)
Xuanwo marked this conversation as resolved.
Show resolved Hide resolved
.await
}

async fn lseek(
&self,
_req: Request,
path: Option<&OsStr>,
fh: u64,
offset: u64,
whence: u32,
) -> Result<ReplyLSeek> {
log::debug!(
"lseek(path={:?}, fh={}, offset={}, whence={})",
path,
fh,
offset,
whence
);

let whence = whence as i32;

let file = self.get_opened_file(FileKey::try_from(fh)?, path)?;

if !file.is_read && !file.is_write {
Err(Errno::from(libc::EACCES))?;
}

let offset = if whence == libc::SEEK_SET {
offset
} else if whence == libc::SEEK_CUR {
file.offset + offset
} else if whence == libc::SEEK_END {
let metadata = self
.op
.stat(&path.unwrap().to_string_lossy())
.await
.map_err(opendal_error2errno)?;
let content_size = metadata.content_length();

if content_size >= offset as _ {
content_size as u64 - offset
} else {
0
}
} else {
return Err(libc::ENOSYS.into());
};

Ok(ReplyLSeek { offset })
}

async fn copy_file_range(
Xuanwo marked this conversation as resolved.
Show resolved Hide resolved
&self,
req: Request,
from_path: Option<&OsStr>,
fh_in: u64,
offset_in: u64,
to_path: Option<&OsStr>,
fh_out: u64,
offset_out: u64,
length: u64,
flags: u64,
) -> Result<ReplyCopyFileRange> {
log::debug!(
"copy_file_range(from_path={:?}, fh_in={}, offset_in={}, to_path={:?}, fh_out={}, offset_out={}, length={}, flags={})",
from_path,
fh_in,
offset_in,
to_path,
fh_out,
offset_out,
length,
flags
);
let data = self
.read(req, from_path, fh_in, offset_in, length as _)
.await?;

let ReplyWrite { written } = self
.write(req, to_path, fh_out, offset_out, &data.data, 0, flags as _)
.await?;

Ok(ReplyCopyFileRange {
copied: u64::from(written),
})
}
}

const fn entry_mode2file_type(mode: EntryMode) -> FileType {
Expand Down