Support create()
This commit is contained in:
parent
d7f0e7a141
commit
6a16534ec7
@ -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.
|
||||
|
@ -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 })
|
||||
|
@ -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,
|
||||
|
@ -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?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user