Support create()

This commit is contained in:
Olivier 'reivilibre' 2022-01-24 08:47:35 +00:00
parent d7f0e7a141
commit 6a16534ec7
5 changed files with 168 additions and 5 deletions

View File

@ -1,7 +1,7 @@
use fuser::{
Filesystem, KernelConfig, ReplyAttr, ReplyBmap, ReplyCreate, ReplyData, ReplyDirectory,
ReplyDirectoryPlus, ReplyEmpty, ReplyEntry, ReplyIoctl, ReplyLock, ReplyLseek, ReplyOpen,
ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow,
FileAttr, Filesystem, KernelConfig, ReplyAttr, ReplyBmap, ReplyCreate, ReplyData,
ReplyDirectory, ReplyDirectoryPlus, ReplyEmpty, ReplyEntry, ReplyIoctl, ReplyLock, ReplyLseek,
ReplyOpen, ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow,
};
use libc::{ENOSYS, EPERM, O_APPEND, O_RDONLY, O_RDWR, O_WRONLY};
use log::{debug, error, warn};
@ -200,6 +200,8 @@ impl Filesystem for OliveFilesystem {
rdev: u32,
reply: ReplyEntry,
) {
// TODO Support this as a create + release.
// Modern Linuxes will call create() rather than mknod()+open() so this may even be optional.
debug!(
"[Not Implemented] mknod(parent: {:#x?}, name: {:?}, mode: {}, \
umask: {:#x?}, rdev: {})",
@ -381,7 +383,7 @@ impl Filesystem for OliveFilesystem {
Ok(())
},
"opendir",
"open",
);
}
@ -817,7 +819,75 @@ impl Filesystem for OliveFilesystem {
flags: {:#x?})",
parent, name, mode, umask, flags
);
reply.error(ENOSYS);
let name = if let Some(name) = name.to_str() {
name
} else {
// If we can't decode the filename, pretend it doesn't exist.
reply.error(ENOENT);
return;
}
.to_owned();
let requester = self.requester.clone();
// TODO This needs more careful consideration before using to write files
// How does truncation work?
let mut open_mode = OpenMode {
read: false,
write: false,
append: false,
};
if test_bits(flags, O_APPEND) {
open_mode.append = true;
}
if test_bits(flags, O_RDWR) {
open_mode.read = true;
open_mode.write = true;
} else if test_bits(flags, O_WRONLY) {
open_mode.write = true;
} else if test_bits(flags, O_RDONLY) {
open_mode.read = true;
};
self.spawn_with_error_handler(
async move {
let parent_vnode = VnodeId(
parent
.try_into()
.context("Converting u64 inode to u32 VnodeId.")?,
);
match requester.create(parent_vnode, open_mode, name).await? {
DataResponse::Success((file_handle, metadata)) => {
// Use the direct I/O flag so that we can be in control of when EOF happens
// rather than letting the kernel assume the getattr size is valid
// and caching up to that point.
// We might wind up wanting to do our own buffering...
let file_attr: FileAttr = metadata.into();
// TODO generation should be generated client-side
reply.created(
&Duration::from_secs(5),
&file_attr,
0,
file_handle as u64,
FOPEN_DIRECT_IO,
);
}
DataResponse::Error { code, message } => {
warn!(
"create(parent: {:#x?}, flags: {:#x?}) failed: {:?}",
parent_vnode, flags, message
);
reply.error(code as c_int);
}
}
Ok(())
},
"create",
);
}
/// Test for a POSIX file lock.

View File

@ -196,6 +196,21 @@ impl Requester {
.await
}
pub async fn create(
&self,
dir_vnode: VnodeId,
mode: OpenMode,
name: String,
) -> anyhow::Result<DataResponse<(u32, FileMetadata)>> {
self.internal
.command(&DataCommand::CreateFile {
dir_vnode,
mode,
name,
})
.await
}
pub async fn open(&self, vnode: VnodeId, mode: OpenMode) -> anyhow::Result<DataResponse<u32>> {
self.internal
.command(&DataCommand::OpenFile { vnode, mode })

View File

@ -54,6 +54,11 @@ pub enum DataCommand {
dir_vnode: VnodeId,
name: String,
},
CreateFile {
dir_vnode: VnodeId,
mode: OpenMode,
name: String,
},
OpenFile {
vnode: VnodeId,
mode: OpenMode,

View File

@ -62,6 +62,14 @@ pub async fn handle_command_stream(
)
.await?;
}
DataCommand::CreateFile {
dir_vnode,
mode,
name,
} => {
send_bare_message(&mut tx, &file_access.create(dir_vnode, mode, name).await?)
.await?;
}
}
}

View File

@ -297,6 +297,71 @@ impl FileAccess {
Ok(DataResponse::Success(()))
}
pub async fn create(
&self,
dir_vnode: VnodeId,
mode: OpenMode,
name: String,
) -> anyhow::Result<DataResponse<(u32, FileMetadata)>> {
match self.resolve_vnode(dir_vnode).await {
Ok(dir_path) => {
let lookup_path = dir_path.join(name);
// TODO check the security of this: make sure you can't escape the root
match lookup_path.absolutize_virtually(&self.client_info.root) {
Ok(the_path) => {
// We'll allocate the Vnode after we know the target doesn't already exist. Fill in 0 for now.
let mut open_options = OpenOptions::new();
// Only create new files
open_options.create_new(true);
if mode.read {
open_options.read(true);
}
if mode.write {
open_options.write(true);
}
if mode.append {
open_options.append(true);
}
// TODO truncate?
match open_options.open(&the_path).await {
Ok(file) => {
// TODO offset & append files: how does that work?
let handle = Arc::new(RwLock::new(FileHandle { file, offset: 0 }));
let file_handle: u32 = self
.client_state
.file_handles
.write()
.await
.insert(handle)
.try_into()
.unwrap();
let vnode = self.allocate_vnode(the_path.to_path_buf()).await?;
let metadata = self.read_metadata(&the_path, vnode).await?;
Ok(DataResponse::Success((file_handle, metadata)))
}
Err(error) => Ok(io_error_to_response(
error,
EFAULT,
&format!("create {:?}", the_path),
)),
}
}
Err(error) => Ok(DataResponse::Error {
code: ENOENT,
message: format!("Can't make absolute. {:?} ({:?})", error, lookup_path),
}),
}
}
Err(response) => Ok(response),
}
}
pub async fn lookup(
&self,
dir_vnode: VnodeId,