diff --git a/src/mount/fuse.rs b/src/mount/fuse.rs index a6e788a7d0..cde92c9870 100644 --- a/src/mount/fuse.rs +++ b/src/mount/fuse.rs @@ -520,16 +520,10 @@ impl Filesystem for Fuse { 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), } diff --git a/src/mount/redox/resource.rs b/src/mount/redox/resource.rs index 2f18ff65a6..d2a8430ee1 100644 --- a/src/mount/redox/resource.rs +++ b/src/mount/redox/resource.rs @@ -13,6 +13,8 @@ use syscall::flag::{ use crate::{Disk, Node, Transaction, TreePtr}; pub trait Resource { + fn parent_ptr_opt(&self) -> Option>; + fn node_ptr(&self) -> TreePtr; fn uid(&self) -> u32; @@ -119,6 +121,7 @@ pub trait Resource { pub struct DirResource { path: String, + parent_ptr_opt: Option>, node_ptr: TreePtr, data: Option>, seek: isize, @@ -128,12 +131,14 @@ pub struct DirResource { impl DirResource { pub fn new( path: String, + parent_ptr_opt: Option>, node_ptr: TreePtr, data: Option>, uid: u32, ) -> DirResource { DirResource { path, + parent_ptr_opt, node_ptr, data, seek: 0, @@ -143,6 +148,10 @@ impl DirResource { } impl Resource for DirResource { + fn parent_ptr_opt(&self) -> Option> { + self.parent_ptr_opt + } + fn node_ptr(&self) -> TreePtr { self.node_ptr } @@ -154,6 +163,7 @@ impl Resource for DirResource { fn dup(&self) -> Result>> { 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>, node_ptr: TreePtr, flags: usize, seek: isize, @@ -313,9 +324,16 @@ pub struct FileResource { } impl FileResource { - pub fn new(path: String, node_ptr: TreePtr, flags: usize, uid: u32) -> FileResource { + pub fn new( + path: String, + parent_ptr_opt: Option>, + node_ptr: TreePtr, + flags: usize, + uid: u32, + ) -> FileResource { FileResource { path, + parent_ptr_opt, node_ptr, flags, seek: 0, @@ -326,6 +344,10 @@ impl FileResource { } impl Resource for FileResource { + fn parent_ptr_opt(&self) -> Option> { + self.parent_ptr_opt + } + fn node_ptr(&self) -> TreePtr { self.node_ptr } @@ -337,6 +359,7 @@ impl Resource for FileResource { fn dup(&self) -> Result>> { 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, diff --git a/src/mount/redox/scheme.rs b/src/mount/redox/scheme.rs index a5f2d5623c..cd52fbaeb7 100644 --- a/src/mount/redox/scheme.rs +++ b/src/mount/redox/scheme.rs @@ -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 SchemeMut for FileScheme { 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> = match node_opt { Some((node, _node_name)) => { if flags & (O_CREAT | O_EXCL) == O_CREAT | O_EXCL { @@ -248,6 +249,7 @@ impl SchemeMut for FileScheme { Box::new(DirResource::new( path.to_string(), + parent_ptr_opt, node.ptr(), Some(data), uid, @@ -256,7 +258,13 @@ impl SchemeMut for FileScheme { // 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 SchemeMut for FileScheme { })?; } - 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 SchemeMut for FileScheme { })?; 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 SchemeMut for FileScheme { } } + //TODO: this function has too much code, try to simplify it fn frename(&mut self, id: usize, url: &str, uid: u32, gid: u32) -> Result { - 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 { diff --git a/src/transaction.rs b/src/transaction.rs index 609090690d..994c6f780c 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -507,30 +507,32 @@ impl<'a, D: Disk> Transaction<'a, D> { ctime_nsec: u32, ) -> Result> { 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, ) -> 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, + orig_name: &str, + new_parent_ptr: TreePtr, + 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,