Skip to content

Commit

Permalink
feat(ofs): rename2 lseek copy_file_range getattr API support (#4395)
Browse files Browse the repository at this point in the history
* feat(ofs): support lseek and copy_file_range rename2

* feat(ofs): support getattr

* feat(ofs): fix code

* feat(ofs): fix code

* feat(ofs): fix code

* feat(ofs): fix code

* feat(ofs): fix code

* feat(ofs): fix code

* feat(ofs): fix code

* feat(ofs): fix code
  • Loading branch information
oowl authored Mar 27, 2024
1 parent 7ccc8a8 commit b6cdd68
Showing 1 changed file with 163 additions and 8 deletions.
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::ENOTSUP))?;
}

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)
.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(
&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

0 comments on commit b6cdd68

Please sign in to comment.