Implement frename
This commit is contained in:
+4
-10
@@ -520,16 +520,10 @@ impl<D: Disk> Filesystem for Fuse<D> {
|
||||
let new_name = new_name.to_str().expect("name is not utf-8");
|
||||
|
||||
// TODO: improve performance
|
||||
match self.fs.tx(|tx| {
|
||||
let node = tx.find_node(orig_parent_ptr, orig_name)?;
|
||||
tx.link_node(new_parent_ptr, new_name, node.ptr())?;
|
||||
tx.remove_node(
|
||||
orig_parent_ptr,
|
||||
orig_name,
|
||||
node.data().mode() & Node::MODE_TYPE,
|
||||
)?;
|
||||
Ok(())
|
||||
}) {
|
||||
match self
|
||||
.fs
|
||||
.tx(|tx| tx.rename_node(orig_parent_ptr, orig_name, new_parent_ptr, new_name))
|
||||
{
|
||||
Ok(()) => reply.ok(),
|
||||
Err(err) => reply.error(err.errno as i32),
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ use syscall::flag::{
|
||||
use crate::{Disk, Node, Transaction, TreePtr};
|
||||
|
||||
pub trait Resource<D: Disk> {
|
||||
fn parent_ptr_opt(&self) -> Option<TreePtr<Node>>;
|
||||
|
||||
fn node_ptr(&self) -> TreePtr<Node>;
|
||||
|
||||
fn uid(&self) -> u32;
|
||||
@@ -119,6 +121,7 @@ pub trait Resource<D: Disk> {
|
||||
|
||||
pub struct DirResource {
|
||||
path: String,
|
||||
parent_ptr_opt: Option<TreePtr<Node>>,
|
||||
node_ptr: TreePtr<Node>,
|
||||
data: Option<Vec<u8>>,
|
||||
seek: isize,
|
||||
@@ -128,12 +131,14 @@ pub struct DirResource {
|
||||
impl DirResource {
|
||||
pub fn new(
|
||||
path: String,
|
||||
parent_ptr_opt: Option<TreePtr<Node>>,
|
||||
node_ptr: TreePtr<Node>,
|
||||
data: Option<Vec<u8>>,
|
||||
uid: u32,
|
||||
) -> DirResource {
|
||||
DirResource {
|
||||
path,
|
||||
parent_ptr_opt,
|
||||
node_ptr,
|
||||
data,
|
||||
seek: 0,
|
||||
@@ -143,6 +148,10 @@ impl DirResource {
|
||||
}
|
||||
|
||||
impl<D: Disk> Resource<D> for DirResource {
|
||||
fn parent_ptr_opt(&self) -> Option<TreePtr<Node>> {
|
||||
self.parent_ptr_opt
|
||||
}
|
||||
|
||||
fn node_ptr(&self) -> TreePtr<Node> {
|
||||
self.node_ptr
|
||||
}
|
||||
@@ -154,6 +163,7 @@ impl<D: Disk> Resource<D> for DirResource {
|
||||
fn dup(&self) -> Result<Box<dyn Resource<D>>> {
|
||||
Ok(Box::new(DirResource {
|
||||
path: self.path.clone(),
|
||||
parent_ptr_opt: self.parent_ptr_opt,
|
||||
node_ptr: self.node_ptr,
|
||||
data: self.data.clone(),
|
||||
seek: self.seek,
|
||||
@@ -305,6 +315,7 @@ impl Drop for Fmap {
|
||||
|
||||
pub struct FileResource {
|
||||
path: String,
|
||||
parent_ptr_opt: Option<TreePtr<Node>>,
|
||||
node_ptr: TreePtr<Node>,
|
||||
flags: usize,
|
||||
seek: isize,
|
||||
@@ -313,9 +324,16 @@ pub struct FileResource {
|
||||
}
|
||||
|
||||
impl FileResource {
|
||||
pub fn new(path: String, node_ptr: TreePtr<Node>, flags: usize, uid: u32) -> FileResource {
|
||||
pub fn new(
|
||||
path: String,
|
||||
parent_ptr_opt: Option<TreePtr<Node>>,
|
||||
node_ptr: TreePtr<Node>,
|
||||
flags: usize,
|
||||
uid: u32,
|
||||
) -> FileResource {
|
||||
FileResource {
|
||||
path,
|
||||
parent_ptr_opt,
|
||||
node_ptr,
|
||||
flags,
|
||||
seek: 0,
|
||||
@@ -326,6 +344,10 @@ impl FileResource {
|
||||
}
|
||||
|
||||
impl<D: Disk> Resource<D> for FileResource {
|
||||
fn parent_ptr_opt(&self) -> Option<TreePtr<Node>> {
|
||||
self.parent_ptr_opt
|
||||
}
|
||||
|
||||
fn node_ptr(&self) -> TreePtr<Node> {
|
||||
self.node_ptr
|
||||
}
|
||||
@@ -337,6 +359,7 @@ impl<D: Disk> Resource<D> for FileResource {
|
||||
fn dup(&self) -> Result<Box<dyn Resource<D>>> {
|
||||
Ok(Box::new(FileResource {
|
||||
path: self.path.clone(),
|
||||
parent_ptr_opt: self.parent_ptr_opt,
|
||||
node_ptr: self.node_ptr,
|
||||
flags: self.flags,
|
||||
seek: self.seek,
|
||||
|
||||
+96
-74
@@ -5,8 +5,8 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use syscall::data::{Map, Stat, StatVfs, TimeSpec};
|
||||
use syscall::error::{
|
||||
Error, Result, EACCES, EBADF, EEXIST, EINVAL, EISDIR, ELOOP, ENOENT, ENOSYS, ENOTDIR,
|
||||
ENOTEMPTY, EPERM, EXDEV,
|
||||
Error, Result, EACCES, EBADF, EBUSY, EEXIST, EINVAL, EISDIR, ELOOP, ENOENT, ENOTDIR, ENOTEMPTY,
|
||||
EPERM, EXDEV,
|
||||
};
|
||||
use syscall::flag::{
|
||||
EventFlags, MODE_PERM, O_ACCMODE, O_CREAT, O_DIRECTORY, O_EXCL, O_NOFOLLOW, O_RDONLY, O_RDWR,
|
||||
@@ -222,6 +222,7 @@ impl<D: Disk> SchemeMut for FileScheme<D> {
|
||||
let node_opt = self
|
||||
.fs
|
||||
.tx(|tx| Self::path_nodes(scheme_name, tx, path, uid, gid, &mut nodes))?;
|
||||
let parent_ptr_opt = nodes.last().map(|x| x.0.ptr());
|
||||
let resource: Box<dyn Resource<D>> = match node_opt {
|
||||
Some((node, _node_name)) => {
|
||||
if flags & (O_CREAT | O_EXCL) == O_CREAT | O_EXCL {
|
||||
@@ -248,6 +249,7 @@ impl<D: Disk> SchemeMut for FileScheme<D> {
|
||||
|
||||
Box::new(DirResource::new(
|
||||
path.to_string(),
|
||||
parent_ptr_opt,
|
||||
node.ptr(),
|
||||
Some(data),
|
||||
uid,
|
||||
@@ -256,7 +258,13 @@ impl<D: Disk> SchemeMut for FileScheme<D> {
|
||||
// println!("{:X} & {:X}: EISDIR {}", flags, O_DIRECTORY, path);
|
||||
return Err(Error::new(EISDIR));
|
||||
} else {
|
||||
Box::new(DirResource::new(path.to_string(), node.ptr(), None, uid))
|
||||
Box::new(DirResource::new(
|
||||
path.to_string(),
|
||||
parent_ptr_opt,
|
||||
node.ptr(),
|
||||
None,
|
||||
uid,
|
||||
))
|
||||
}
|
||||
} else if node.data().is_symlink()
|
||||
&& !(flags & O_STAT == O_STAT && flags & O_NOFOLLOW == O_NOFOLLOW)
|
||||
@@ -313,7 +321,13 @@ impl<D: Disk> SchemeMut for FileScheme<D> {
|
||||
})?;
|
||||
}
|
||||
|
||||
Box::new(FileResource::new(path.to_string(), node_ptr, flags, uid))
|
||||
Box::new(FileResource::new(
|
||||
path.to_string(),
|
||||
parent_ptr_opt,
|
||||
node_ptr,
|
||||
flags,
|
||||
uid,
|
||||
))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
@@ -359,9 +373,21 @@ impl<D: Disk> SchemeMut for FileScheme<D> {
|
||||
})?;
|
||||
|
||||
if dir {
|
||||
Box::new(DirResource::new(path.to_string(), node_ptr, None, uid))
|
||||
Box::new(DirResource::new(
|
||||
path.to_string(),
|
||||
parent_ptr_opt,
|
||||
node_ptr,
|
||||
None,
|
||||
uid,
|
||||
))
|
||||
} else {
|
||||
Box::new(FileResource::new(path.to_string(), node_ptr, flags, uid))
|
||||
Box::new(FileResource::new(
|
||||
path.to_string(),
|
||||
parent_ptr_opt,
|
||||
node_ptr,
|
||||
flags,
|
||||
uid,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(EPERM));
|
||||
@@ -604,106 +630,102 @@ impl<D: Disk> SchemeMut for FileScheme<D> {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: this function has too much code, try to simplify it
|
||||
fn frename(&mut self, id: usize, url: &str, uid: u32, gid: u32) -> Result<usize> {
|
||||
unimplemented!();
|
||||
/*TODO: FRENAME
|
||||
let path = url.trim_matches('/');
|
||||
let new_path = url.trim_matches('/');
|
||||
|
||||
// println!("Frename {}, {} from {}, {}", id, path, uid, gid);
|
||||
// println!("Frename {}, {} from {}, {}", id, new_path, uid, gid);
|
||||
|
||||
let mut files = self.files.borrow_mut();
|
||||
if let Some(file) = files.get_mut(&id) {
|
||||
if let Some(file) = self.files.get_mut(&id) {
|
||||
//TODO: Check for EINVAL
|
||||
// The new pathname contained a path prefix of the old, or, more generally,
|
||||
// an attempt was made to make a directory a subdirectory of itself.
|
||||
|
||||
let mut last_part = String::new();
|
||||
for part in path.split('/') {
|
||||
let mut old_name = String::new();
|
||||
for part in file.path().split('/') {
|
||||
if !part.is_empty() {
|
||||
last_part = part.to_string();
|
||||
old_name = part.to_string();
|
||||
}
|
||||
}
|
||||
if last_part.is_empty() {
|
||||
if old_name.is_empty() {
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
|
||||
let mut fs = self.fs.borrow_mut();
|
||||
|
||||
let mut orig = fs.node(file.block())?;
|
||||
|
||||
if !orig.1.owner(uid) {
|
||||
// println!("orig not owned by caller {}", uid);
|
||||
return Err(Error::new(EACCES));
|
||||
let mut new_name = String::new();
|
||||
for part in new_path.split('/') {
|
||||
if !part.is_empty() {
|
||||
new_name = part.to_string();
|
||||
}
|
||||
}
|
||||
if new_name.is_empty() {
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
|
||||
let mut nodes = Vec::new();
|
||||
let node_opt = self.path_nodes(&mut fs, path, uid, gid, &mut nodes)?;
|
||||
let scheme_name = &self.name;
|
||||
self.fs.tx(|tx| {
|
||||
let orig_parent_ptr = match file.parent_ptr_opt() {
|
||||
Some(some) => some,
|
||||
None => {
|
||||
// println!("orig is root");
|
||||
return Err(Error::new(EBUSY));
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(parent) = nodes.last() {
|
||||
if !parent.1.owner(uid) {
|
||||
// println!("parent not owned by caller {}", uid);
|
||||
let mut orig_node = tx.read_tree(file.node_ptr())?;
|
||||
|
||||
if !orig_node.data().owner(uid) {
|
||||
// println!("orig_node not owned by caller {}", uid);
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
if let Some(ref node) = node_opt {
|
||||
if !node.data().owner(uid) {
|
||||
// println!("new dir not owned by caller {}", uid);
|
||||
let mut new_nodes = Vec::new();
|
||||
let new_node_opt =
|
||||
Self::path_nodes(scheme_name, tx, new_path, uid, gid, &mut new_nodes)?;
|
||||
|
||||
if let Some((ref new_parent, _)) = new_nodes.last() {
|
||||
if !new_parent.data().owner(uid) {
|
||||
// println!("new_parent not owned by caller {}", uid);
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
if node.data().is_dir() {
|
||||
if !orig.1.is_dir() {
|
||||
// println!("orig is file, new is dir");
|
||||
if let Some((ref new_node, _)) = new_node_opt {
|
||||
if !new_node.data().owner(uid) {
|
||||
// println!("new dir not owned by caller {}", uid);
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
let mut children = Vec::new();
|
||||
fs.child_nodes(node.ptr(), &mut children)?;
|
||||
if new_node.data().is_dir() {
|
||||
if !orig_node.data().is_dir() {
|
||||
// println!("orig_node is file, new is dir");
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
if !children.is_empty() {
|
||||
// println!("new dir not empty");
|
||||
return Err(Error::new(ENOTEMPTY));
|
||||
}
|
||||
} else {
|
||||
if orig.1.is_dir() {
|
||||
// println!("orig is dir, new is file");
|
||||
return Err(Error::new(ENOTDIR));
|
||||
let mut children = Vec::new();
|
||||
tx.child_nodes(new_node.ptr(), &mut children)?;
|
||||
|
||||
if !children.is_empty() {
|
||||
// println!("new dir not empty");
|
||||
return Err(Error::new(ENOTEMPTY));
|
||||
}
|
||||
} else {
|
||||
if orig_node.data().is_dir() {
|
||||
// println!("orig_node is dir, new is file");
|
||||
return Err(Error::new(ENOTDIR));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tx.rename_node(orig_parent_ptr, &old_name, new_parent.ptr(), &new_name)?;
|
||||
|
||||
file.set_path(new_path);
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(Error::new(EPERM))
|
||||
}
|
||||
|
||||
let orig_parent = orig.1.parent;
|
||||
|
||||
if parent.0 != orig_parent {
|
||||
fs.remove_blocks(orig.0, 1, orig_parent)?;
|
||||
}
|
||||
|
||||
if let Some(node) = node_opt {
|
||||
if node.0 != orig.0 {
|
||||
fs.node_set_len(node.0, 0)?;
|
||||
fs.remove_blocks(node.0, 1, parent.0)?;
|
||||
fs.write_at(node.0, &Node::default())?;
|
||||
fs.deallocate(node.0, BLOCK_SIZE)?;
|
||||
}
|
||||
}
|
||||
|
||||
orig.1.set_name(&last_part)?;
|
||||
orig.1.parent = parent.0;
|
||||
fs.write_at(orig.0, &orig.1)?;
|
||||
|
||||
if parent.0 != orig_parent {
|
||||
fs.insert_blocks(orig.0, BLOCK_SIZE, parent.0)?;
|
||||
}
|
||||
|
||||
file.set_path(path);
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(Error::new(EPERM))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<usize> {
|
||||
|
||||
+69
-22
@@ -507,30 +507,32 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
ctime_nsec: u32,
|
||||
) -> Result<TreeData<Node>> {
|
||||
if name.contains(':') {
|
||||
Err(Error::new(EINVAL))
|
||||
} else if self.find_node(parent_ptr, name).is_ok() {
|
||||
Err(Error::new(EEXIST))
|
||||
} else {
|
||||
unsafe {
|
||||
let parent = self.read_tree(parent_ptr)?;
|
||||
let node_block_data = BlockData::new(
|
||||
self.allocate()?,
|
||||
Node::new(
|
||||
mode,
|
||||
parent.data().uid(),
|
||||
parent.data().gid(),
|
||||
ctime,
|
||||
ctime_nsec,
|
||||
),
|
||||
);
|
||||
let node_block_ptr = self.write_block(node_block_data)?;
|
||||
let node_ptr = self.insert_tree(node_block_ptr)?;
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
self.link_node(parent_ptr, name, node_ptr)?;
|
||||
if self.find_node(parent_ptr, name).is_ok() {
|
||||
return Err(Error::new(EEXIST));
|
||||
}
|
||||
|
||||
//TODO: do not re-read node
|
||||
self.read_tree(node_ptr)
|
||||
}
|
||||
unsafe {
|
||||
let parent = self.read_tree(parent_ptr)?;
|
||||
let node_block_data = BlockData::new(
|
||||
self.allocate()?,
|
||||
Node::new(
|
||||
mode,
|
||||
parent.data().uid(),
|
||||
parent.data().gid(),
|
||||
ctime,
|
||||
ctime_nsec,
|
||||
),
|
||||
);
|
||||
let node_block_ptr = self.write_block(node_block_data)?;
|
||||
let node_ptr = self.insert_tree(node_block_ptr)?;
|
||||
|
||||
self.link_node(parent_ptr, name, node_ptr)?;
|
||||
|
||||
//TODO: do not re-read node
|
||||
self.read_tree(node_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,6 +542,14 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
name: &str,
|
||||
node_ptr: TreePtr<Node>,
|
||||
) -> Result<()> {
|
||||
if name.contains(':') {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
if self.find_node(parent_ptr, name).is_ok() {
|
||||
return Err(Error::new(EEXIST));
|
||||
}
|
||||
|
||||
let mut parent = self.read_tree(parent_ptr)?;
|
||||
|
||||
let mut node = self.read_tree(node_ptr)?;
|
||||
@@ -660,6 +670,43 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
|
||||
pub fn rename_node(
|
||||
&mut self,
|
||||
orig_parent_ptr: TreePtr<Node>,
|
||||
orig_name: &str,
|
||||
new_parent_ptr: TreePtr<Node>,
|
||||
new_name: &str,
|
||||
) -> Result<()> {
|
||||
let orig = self.find_node(orig_parent_ptr, orig_name)?;
|
||||
|
||||
//TODO: only allow ENOENT as an error?
|
||||
if let Ok(new) = self.find_node(new_parent_ptr, new_name) {
|
||||
// Move to same name, return
|
||||
if new.id() == orig.id() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Remove new name
|
||||
self.remove_node(
|
||||
new_parent_ptr,
|
||||
new_name,
|
||||
new.data().mode() & Node::MODE_TYPE,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Link original file to new name
|
||||
self.link_node(new_parent_ptr, new_name, orig.ptr())?;
|
||||
|
||||
// Remove original file
|
||||
self.remove_node(
|
||||
orig_parent_ptr,
|
||||
orig_name,
|
||||
orig.data().mode() & Node::MODE_TYPE,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn node_block_ptr(
|
||||
&mut self,
|
||||
node: &TreeData<Node>,
|
||||
|
||||
Reference in New Issue
Block a user