diff --git a/Cargo.toml b/Cargo.toml index 853435157c..09ee5d6059 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,10 @@ authors = ["Jeremy Soller "] name = "redoxfs" path = "src/lib.rs" +[[bin]] +name = "redoxfs" +path = "scheme/main.rs" + [[bin]] name = "redoxfs-fuse" path = "fuse/main.rs" @@ -20,6 +24,9 @@ name = "redoxfs-utility" path = "utility/main.rs" [dependencies] -redox-system = {path = "../system/"} +spin = "*" +syscall = {path = "../../syscall/"} + +[target.'cfg(unix)'.dependencies] fuse = "0.2" time = "*" diff --git a/fuse/image.rs b/fuse/image.rs index ae8bf6f88a..cb1356aadb 100644 --- a/fuse/image.rs +++ b/fuse/image.rs @@ -2,8 +2,7 @@ use std::fs::{File, OpenOptions}; use std::io::{Read, Write, Seek, SeekFrom}; use redoxfs::Disk; - -use system::error::{Error, Result, EIO}; +use syscall::error::{Error, Result, EIO}; macro_rules! try_disk { ($expr:expr) => (match $expr { diff --git a/fuse/main.rs b/fuse/main.rs index 99f6b76ae8..849e1718a2 100644 --- a/fuse/main.rs +++ b/fuse/main.rs @@ -2,7 +2,7 @@ extern crate fuse; extern crate redoxfs; -extern crate system; +extern crate syscall; extern crate time; use image::Image; @@ -21,30 +21,34 @@ struct RedoxFS { fs: redoxfs::FileSystem, } +fn node_attr(node: &(u64, redoxfs::Node)) -> FileAttr { + FileAttr { + ino: node.0, + size: node.1.extents[0].length, + blocks: (node.1.extents[0].length + 511)/512, + atime: CREATE_TIME, + mtime: CREATE_TIME, + ctime: CREATE_TIME, + crtime: CREATE_TIME, + kind: if node.1.is_dir() { + FileType::Directory + } else { + FileType::RegularFile + }, + perm: node.1.mode & redoxfs::Node::MODE_PERM, + nlink: 1, + uid: node.1.uid, + gid: node.1.gid, + rdev: 0, + flags: 0, + } +} + impl Filesystem for RedoxFS { fn lookup(&mut self, _req: &Request, parent_block: u64, name: &Path, reply: ReplyEntry) { match self.fs.find_node(name.to_str().unwrap(), parent_block) { Ok(node) => { - reply.entry(&TTL, &FileAttr { - ino: node.0, - size: node.1.extents[0].length, - blocks: (node.1.extents[0].length + 511)/512, - atime: CREATE_TIME, - mtime: CREATE_TIME, - ctime: CREATE_TIME, - crtime: CREATE_TIME, - kind: if node.1.is_dir() { - FileType::Directory - } else { - FileType::RegularFile - }, - perm: 0o777, - nlink: 1, - uid: 0, - gid: 0, - rdev: 0, - flags: 0, - }, 0); + reply.entry(&TTL, &node_attr(&node), 0); }, Err(err) => { reply.error(err.errno as i32); @@ -55,26 +59,7 @@ impl Filesystem for RedoxFS { fn getattr(&mut self, _req: &Request, block: u64, reply: ReplyAttr) { match self.fs.node(block) { Ok(node) => { - reply.attr(&TTL, &FileAttr { - ino: node.0, - size: node.1.extents[0].length, - blocks: (node.1.extents[0].length + 511)/512, - atime: CREATE_TIME, - mtime: CREATE_TIME, - ctime: CREATE_TIME, - crtime: CREATE_TIME, - kind: if node.1.is_dir() { - FileType::Directory - } else { - FileType::RegularFile - }, - perm: 0o777, - nlink: 1, - uid: 0, - gid: 0, - rdev: 0, - flags: 0, - }); + reply.attr(&TTL, &node_attr(&node)); }, Err(err) => { reply.error(err.errno as i32); @@ -82,39 +67,70 @@ impl Filesystem for RedoxFS { } } - fn setattr(&mut self, _req: &Request, block: u64, _mode: Option, - _uid: Option, _gid: Option, size: Option, + fn setattr(&mut self, _req: &Request, block: u64, mode: Option, + uid: Option, gid: Option, size: Option, _atime: Option, _mtime: Option, _fh: Option, _crtime: Option, _chgtime: Option, _bkuptime: Option, _flags: Option, reply: ReplyAttr) { - if let Some(truncate_size) = size { - if let Err(err) = self.fs.node_set_len(block, truncate_size) { + if let Some(mode) = mode { + match self.fs.node(block) { + Ok(mut node) => if node.1.mode & redoxfs::Node::MODE_PERM != mode as u16 & redoxfs::Node::MODE_PERM { + // println!("Chmod {:?}:{:o}:{:o}", node.1.name(), node.1.mode, mode); + node.1.mode = (node.1.mode & redoxfs::Node::MODE_TYPE) | (mode as u16 & redoxfs::Node::MODE_PERM); + if let Err(err) = self.fs.write_at(node.0, &node.1) { + reply.error(err.errno as i32); + return; + } + }, + Err(err) => { + reply.error(err.errno as i32); + return; + } + } + } + + if let Some(uid) = uid { + match self.fs.node(block) { + Ok(mut node) => if node.1.uid != uid { + node.1.uid = uid; + if let Err(err) = self.fs.write_at(node.0, &node.1) { + reply.error(err.errno as i32); + return; + } + }, + Err(err) => { + reply.error(err.errno as i32); + return; + } + } + } + + if let Some(gid) = gid { + match self.fs.node(block) { + Ok(mut node) => if node.1.gid != gid { + node.1.gid = gid; + if let Err(err) = self.fs.write_at(node.0, &node.1) { + reply.error(err.errno as i32); + return; + } + }, + Err(err) => { + reply.error(err.errno as i32); + return; + } + } + } + + if let Some(size) = size { + if let Err(err) = self.fs.node_set_len(block, size) { reply.error(err.errno as i32); return; } } + match self.fs.node(block) { Ok(node) => { - reply.attr(&TTL, &FileAttr { - ino: node.0, - size: node.1.extents[0].length, - blocks: (node.1.extents[0].length + 511)/512, - atime: CREATE_TIME, - mtime: CREATE_TIME, - ctime: CREATE_TIME, - crtime: CREATE_TIME, - kind: if node.1.is_dir() { - FileType::Directory - } else { - FileType::RegularFile - }, - perm: 0o777, - nlink: 1, - uid: 0, - gid: 0, - rdev: 0, - flags: 0, - }); + reply.attr(&TTL, &node_attr(&node)); }, Err(err) => { reply.error(err.errno as i32); @@ -180,29 +196,11 @@ impl Filesystem for RedoxFS { } } - fn create(&mut self, _req: &Request, parent_block: u64, name: &Path, _mode: u32, flags: u32, reply: ReplyCreate) { - match self.fs.create_node(redoxfs::Node::MODE_FILE, name.to_str().unwrap(), parent_block) { + fn create(&mut self, _req: &Request, parent_block: u64, name: &Path, mode: u32, flags: u32, reply: ReplyCreate) { + match self.fs.create_node(redoxfs::Node::MODE_FILE | (mode as u16 & redoxfs::Node::MODE_PERM), name.to_str().unwrap(), parent_block) { Ok(node) => { - reply.created(&TTL, &FileAttr { - ino: node.0, - size: node.1.extents[0].length, - blocks: (node.1.extents[0].length + 511)/512, - atime: CREATE_TIME, - mtime: CREATE_TIME, - ctime: CREATE_TIME, - crtime: CREATE_TIME, - kind: if node.1.is_dir() { - FileType::Directory - } else { - FileType::RegularFile - }, - perm: 0o777, - nlink: 1, - uid: 0, - gid: 0, - rdev: 0, - flags: 0, - }, 0, 0, flags); + // println!("Create {:?}:{:o}:{:o}", node.1.name(), node.1.mode, mode); + reply.created(&TTL, &node_attr(&node), 0, 0, flags); }, Err(error) => { reply.error(error.errno as i32); @@ -210,29 +208,11 @@ impl Filesystem for RedoxFS { } } - fn mkdir(&mut self, _req: &Request, parent_block: u64, name: &Path, _mode: u32, reply: ReplyEntry) { - match self.fs.create_node(redoxfs::Node::MODE_DIR, name.to_str().unwrap(), parent_block) { + fn mkdir(&mut self, _req: &Request, parent_block: u64, name: &Path, mode: u32, reply: ReplyEntry) { + match self.fs.create_node(redoxfs::Node::MODE_DIR | (mode as u16 & redoxfs::Node::MODE_PERM), name.to_str().unwrap(), parent_block) { Ok(node) => { - reply.entry(&TTL, &FileAttr { - ino: node.0, - size: node.1.extents[0].length, - blocks: (node.1.extents[0].length + 511)/512, - atime: CREATE_TIME, - mtime: CREATE_TIME, - ctime: CREATE_TIME, - crtime: CREATE_TIME, - kind: if node.1.is_dir() { - FileType::Directory - } else { - FileType::RegularFile - }, - perm: 0o777, - nlink: 1, - uid: 0, - gid: 0, - rdev: 0, - flags: 0, - }, 0); + // println!("Mkdir {:?}:{:o}:{:o}", node.1.name(), node.1.mode, mode); + reply.entry(&TTL, &node_attr(&node), 0); }, Err(error) => { reply.error(error.errno as i32); diff --git a/scheme/cache/mod.rs b/scheme/cache/mod.rs index 0798592a06..3df653b21d 100644 --- a/scheme/cache/mod.rs +++ b/scheme/cache/mod.rs @@ -2,7 +2,7 @@ use std::{cmp, ptr}; use redoxfs::Disk; -use system::error::Result; +use syscall::error::Result; use self::lru_cache::LruCache; diff --git a/scheme/image.rs b/scheme/image.rs index 169727095b..b589c5cb69 100644 --- a/scheme/image.rs +++ b/scheme/image.rs @@ -3,7 +3,7 @@ use std::io::{Read, Write, Seek, SeekFrom}; use redoxfs::Disk; -use system::error::{Error, Result, EIO}; +use syscall::error::{Error, Result, EIO}; macro_rules! try_disk { ($expr:expr) => (match $expr { diff --git a/scheme/main.rs b/scheme/main.rs index e82005a9a9..944223807e 100644 --- a/scheme/main.rs +++ b/scheme/main.rs @@ -1,16 +1,15 @@ #![deny(warnings)] extern crate redoxfs; - -extern crate system; +extern crate spin; +extern crate syscall; use std::env; use std::fs::File; use std::io::{Read, Write}; -use std::mem::size_of; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::thread; -use std::time::Duration; +use spin::Mutex; use cache::Cache; use image::Image; @@ -18,7 +17,7 @@ use scheme::FileScheme; use redoxfs::FileSystem; -use system::scheme::{Packet, Scheme}; +use syscall::{Packet, Scheme}; pub mod cache; pub mod image; @@ -43,15 +42,14 @@ fn main() { Ok(mut socket) => { println!("redoxfs: mounted filesystem {} on file:", path); - *status_daemon.lock().unwrap() = Status::Running; + *status_daemon.lock() = Status::Running; - let mut scheme = FileScheme::new(fs); + let scheme = FileScheme::new("file", fs); loop { let mut packet = Packet::default(); - while socket.read(&mut packet).unwrap() == size_of::() { - scheme.handle(&mut packet); - socket.write(&packet).unwrap(); - } + socket.read(&mut packet).unwrap(); + scheme.handle(&mut packet); + socket.write(&packet).unwrap(); } }, Err(err) => println!("redoxfs: failed to create file scheme: {}", err) @@ -61,17 +59,17 @@ fn main() { Err(err) => println!("redoxfs: failed to open image {}: {}", path, err) } - *status_daemon.lock().unwrap() = Status::Stopping; + *status_daemon.lock() = Status::Stopping; }); 'waiting: loop { - match *status_mutex.lock().unwrap() { + match *status_mutex.lock() { Status::Starting => (), Status::Running => break 'waiting, Status::Stopping => break 'waiting, } - thread::sleep(Duration::new(0, 30000000)); + thread::yield_now(); } } else { println!("redoxfs: no disk image provided"); diff --git a/scheme/resource.rs b/scheme/resource.rs index 9f4071302e..1276515419 100644 --- a/scheme/resource.rs +++ b/scheme/resource.rs @@ -2,30 +2,32 @@ use redoxfs::FileSystem; use std::cmp::{min, max}; -use system::error::{Error, Result, EINVAL}; -use system::syscall::{Stat, SEEK_SET, SEEK_CUR, SEEK_END, MODE_DIR, MODE_FILE}; +use syscall::error::{Error, Result, EINVAL}; +use syscall::{Stat, SEEK_SET, SEEK_CUR, SEEK_END}; pub trait Resource { fn dup(&self) -> Result>; fn read(&mut self, buf: &mut [u8], fs: &mut FileSystem) -> Result; fn write(&mut self, buf: &[u8], fs: &mut FileSystem) -> Result; - fn seek(&mut self, offset: usize, whence: usize) -> Result; + fn seek(&mut self, offset: usize, whence: usize, fs: &mut FileSystem) -> Result; fn path(&self, buf: &mut [u8]) -> Result; - fn stat(&self, _stat: &mut Stat) -> Result; + fn stat(&self, _stat: &mut Stat, fs: &mut FileSystem) -> Result; fn sync(&mut self) -> Result; fn truncate(&mut self, len: usize, fs: &mut FileSystem) -> Result; } pub struct DirResource { path: String, + block: u64, data: Vec, seek: usize, } impl DirResource { - pub fn new(path: &str, data: Vec) -> DirResource { + pub fn new(path: String, block: u64, data: Vec) -> DirResource { DirResource { - path: path.to_string(), + path: path, + block: block, data: data, seek: 0, } @@ -36,6 +38,7 @@ impl Resource for DirResource { fn dup(&self) -> Result> { Ok(Box::new(DirResource { path: self.path.clone(), + block: self.block, data: self.data.clone(), seek: self.seek })) @@ -55,29 +58,37 @@ impl Resource for DirResource { Err(Error::new(EINVAL)) } - fn seek(&mut self, offset: usize, whence: usize) -> Result { - match whence { - SEEK_SET => self.seek = min(0, max(self.data.len() as isize, offset as isize)) as usize, - SEEK_CUR => self.seek = min(0, max(self.data.len() as isize, self.seek as isize + offset as isize)) as usize, - SEEK_END => self.seek = min(0, max(self.data.len() as isize, self.data.len() as isize + offset as isize)) as usize, + fn seek(&mut self, offset: usize, whence: usize, _fs: &mut FileSystem) -> Result { + self.seek = match whence { + SEEK_SET => min(0, max(self.data.len() as isize, offset as isize)) as usize, + SEEK_CUR => min(0, max(self.data.len() as isize, self.seek as isize + offset as isize)) as usize, + SEEK_END => min(0, max(self.data.len() as isize, self.data.len() as isize + offset as isize)) as usize, _ => return Err(Error::new(EINVAL)) - } + }; + Ok(self.seek) } fn path(&self, buf: &mut [u8]) -> Result { - let mut i = 0; let path = self.path.as_bytes(); + + let mut i = 0; while i < buf.len() && i < path.len() { buf[i] = path[i]; i += 1; } + Ok(i) } - fn stat(&self, stat: &mut Stat) -> Result { - stat.st_mode = MODE_DIR; - stat.st_size = self.data.len() as u32; + fn stat(&self, stat: &mut Stat, fs: &mut FileSystem) -> Result { + let node = try!(fs.node(self.block)); + + stat.st_mode = node.1.mode; + stat.st_uid = node.1.uid; + stat.st_gid = node.1.gid; + stat.st_size = try!(fs.node_len(self.block)); + Ok(0) } @@ -94,16 +105,14 @@ pub struct FileResource { path: String, block: u64, seek: u64, - size: u64, } impl FileResource { - pub fn new(path: &str, block: u64, size: u64) -> FileResource { + pub fn new(path: String, block: u64) -> FileResource { FileResource { - path: path.to_string(), + path: path, block: block, seek: 0, - size: size, } } } @@ -114,7 +123,6 @@ impl Resource for FileResource { path: self.path.clone(), block: self.block, seek: self.seek, - size: self.size })) } @@ -127,36 +135,42 @@ impl Resource for FileResource { fn write(&mut self, buf: &[u8], fs: &mut FileSystem) -> Result { let count = try!(fs.write_node(self.block, self.seek, buf)); self.seek += count as u64; - if self.seek > self.size { - self.size = self.seek; - } Ok(count) } - fn seek(&mut self, offset: usize, whence: usize) -> Result { - match whence { - SEEK_SET => self.seek = min(0, max(self.size as i64, offset as i64)) as u64, - SEEK_CUR => self.seek = min(0, max(self.size as i64, self.seek as i64 + offset as i64)) as u64, - SEEK_END => self.seek = min(0, max(self.size as i64, self.size as i64 + offset as i64)) as u64, + fn seek(&mut self, offset: usize, whence: usize, fs: &mut FileSystem) -> Result { + let size = try!(fs.node_len(self.block)); + + self.seek = match whence { + SEEK_SET => min(0, max(size as i64, offset as i64)) as u64, + SEEK_CUR => min(0, max(size as i64, self.seek as i64 + offset as i64)) as u64, + SEEK_END => min(0, max(size as i64, size as i64 + offset as i64)) as u64, _ => return Err(Error::new(EINVAL)) - } + }; Ok(self.seek as usize) } fn path(&self, buf: &mut [u8]) -> Result { - let mut i = 0; let path = self.path.as_bytes(); + + let mut i = 0; while i < buf.len() && i < path.len() { buf[i] = path[i]; i += 1; } + Ok(i) } - fn stat(&self, stat: &mut Stat) -> Result { - stat.st_mode = MODE_FILE; - stat.st_size = self.size as u32; + fn stat(&self, stat: &mut Stat, fs: &mut FileSystem) -> Result { + let node = try!(fs.node(self.block)); + + stat.st_mode = node.1.mode; + stat.st_uid = node.1.uid; + stat.st_gid = node.1.gid; + stat.st_size = try!(fs.node_len(self.block)); + Ok(0) } @@ -165,11 +179,8 @@ impl Resource for FileResource { } fn truncate(&mut self, len: usize, fs: &mut FileSystem) -> Result { - if let Err(err) = fs.node_set_len(self.block, len as u64) { - Err(err) - } else { - self.size = len as u64; - Ok(0) - } + try!(fs.node_set_len(self.block, len as u64)); + + Ok(0) } } diff --git a/scheme/scheme.rs b/scheme/scheme.rs index 7deb198b79..6f64dc10e8 100644 --- a/scheme/scheme.rs +++ b/scheme/scheme.rs @@ -1,60 +1,87 @@ use resource::{Resource, DirResource, FileResource}; use redoxfs::{FileSystem, Node}; - +use spin::Mutex; +use std::cell::RefCell; use std::collections::BTreeMap; +use std::str; +use std::sync::atomic::{AtomicUsize, Ordering}; -use system::error::{Error, Result, EEXIST, EISDIR, ENOTDIR, EPERM, ENOENT, EBADF}; -use system::scheme::Scheme; -use system::syscall::{Stat, O_CREAT, O_TRUNC}; +use syscall::error::{Error, Result, EACCES, EEXIST, EISDIR, ENOTDIR, EPERM, ENOENT, EBADF}; +use syscall::scheme::Scheme; +use syscall::{Stat, O_CREAT, O_TRUNC}; pub struct FileScheme { - fs: FileSystem, - next_id: isize, - files: BTreeMap> + name: &'static str, + fs: RefCell, + next_id: AtomicUsize, + files: Mutex>> } impl FileScheme { - pub fn new(fs: FileSystem) -> FileScheme { + pub fn new(name: &'static str, fs: FileSystem) -> FileScheme { FileScheme { - fs: fs, - next_id: 1, - files: BTreeMap::new() + name: name, + fs: RefCell::new(fs), + next_id: AtomicUsize::new(1), + files: Mutex::new(BTreeMap::new()) } } +} - fn open_inner(&mut self, url: &str, flags: usize) -> Result> { - let path = url.split(':').nth(1).unwrap_or("").trim_matches('/'); +impl Scheme for FileScheme { + fn open(&self, url: &[u8], flags: usize, uid: u32, gid: u32) -> Result { + let path = str::from_utf8(url).unwrap_or("").trim_matches('/'); - // println!("Open '{}' {:X}", path, flags); + //println!("Open '{}' {:X}", path, flags); + + let mut fs = self.fs.borrow_mut(); let mut nodes = Vec::new(); - let node_result = self.fs.path_nodes(path, &mut nodes); + let node_result = fs.path_nodes(path, &mut nodes); + for node in nodes.iter() { + if ! node.1.permission(uid, gid, Node::MODE_EXEC) { + // println!("dir not executable {:o}", node.1.mode); + return Err(Error::new(EACCES)); + } + } - match node_result { + let resource: Box = match node_result { Ok(node) => if node.1.is_dir() { + if ! node.1.permission(uid, gid, Node::MODE_READ) { + // println!("dir not readable {:o}", node.1.mode); + return Err(Error::new(EACCES)); + } + let mut data = Vec::new(); let mut children = Vec::new(); - try!(self.fs.child_nodes(&mut children, node.0)); + try!(fs.child_nodes(&mut children, node.0)); for child in children.iter() { if let Ok(name) = child.1.name() { if ! data.is_empty() { data.push(b'\n'); } data.extend_from_slice(&name.as_bytes()); - if child.1.is_dir() { - data.push(b'/'); - } } } - return Ok(Box::new(DirResource::new(url, data))); + + Box::new(DirResource::new(path.to_string(), node.0, data)) } else { - if flags & O_TRUNC == O_TRUNC { - // println!("Truncate {}", path); - try!(self.fs.node_set_len(node.0, 0)); + if ! node.1.permission(uid, gid, Node::MODE_READ) { + // println!("file not readable {:o}", node.1.mode); + return Err(Error::new(EACCES)); } - let size = try!(self.fs.node_len(node.0)); - return Ok(Box::new(FileResource::new(url, node.0, size))); + + if flags & O_TRUNC == O_TRUNC { + if ! node.1.permission(uid, gid, Node::MODE_WRITE) { + // println!("file not writable {:o}", node.1.mode); + return Err(Error::new(EACCES)); + } + + try!(fs.node_set_len(node.0, 0)); + } + + Box::new(FileResource::new(path.to_string(), node.0)) }, Err(err) => if err.errno == ENOENT && flags & O_CREAT == O_CREAT { let mut last_part = String::new(); @@ -65,8 +92,16 @@ impl FileScheme { } if ! last_part.is_empty() { if let Some(parent) = nodes.last() { - let node = try!(self.fs.create_node(Node::MODE_FILE, &last_part, parent.0)); - return Ok(Box::new(FileResource::new(url, node.0, 0))); + if ! parent.1.permission(uid, gid, Node::MODE_WRITE) { + // println!("dir not writable {:o}", parent.1.mode); + return Err(Error::new(EACCES)); + } + + let mut node = try!(fs.create_node(Node::MODE_FILE | (flags as u16 & Node::MODE_PERM), &last_part, parent.0)); + node.1.uid = uid; + node.1.gid = gid; + try!(fs.write_at(node.0, &node.1)); + Box::new(FileResource::new(path.to_string(), node.0)) } else { return Err(Error::new(EPERM)); } @@ -76,32 +111,31 @@ impl FileScheme { } else { return Err(err); } - } - } -} + }; -impl Scheme for FileScheme { - fn open(&mut self, url: &str, flags: usize) -> Result { - let resource = try!(self.open_inner(url, flags)); - - let id = self.next_id as usize; - self.next_id += 1; - if self.next_id < 0 { - self.next_id = 1; - } - - self.files.insert(id, resource); + let id = self.next_id.fetch_add(1, Ordering::SeqCst); + self.files.lock().insert(id, resource); Ok(id) } - fn mkdir(&mut self, url: &str, _mode: usize) -> Result { - let path = url.split(':').nth(1).unwrap_or("").trim_matches('/'); + fn mkdir(&self, url: &[u8], mode: u16, uid: u32, gid: u32) -> Result { + let path = str::from_utf8(url).unwrap_or("").trim_matches('/'); // println!("Mkdir '{}'", path); + let mut fs = self.fs.borrow_mut(); + let mut nodes = Vec::new(); - match self.fs.path_nodes(path, &mut nodes) { + let node_result = fs.path_nodes(path, &mut nodes); + for node in nodes.iter() { + if ! node.1.permission(uid, gid, Node::MODE_EXEC) { + // println!("dir not executable {:o}", node.1.mode); + return Err(Error::new(EACCES)); + } + } + + match node_result { Ok(_node) => Err(Error::new(EEXIST)), Err(err) => if err.errno == ENOENT { let mut last_part = String::new(); @@ -112,7 +146,16 @@ impl Scheme for FileScheme { } if ! last_part.is_empty() { if let Some(parent) = nodes.last() { - self.fs.create_node(Node::MODE_DIR, &last_part, parent.0).and(Ok(0)) + if ! parent.1.permission(uid, gid, Node::MODE_WRITE) { + // println!("dir not writable {:o}", parent.1.mode); + return Err(Error::new(EACCES)); + } + + let mut node = try!(fs.create_node(Node::MODE_DIR | (mode & Node::MODE_PERM), &last_part, parent.0)); + node.1.uid = uid; + node.1.gid = gid; + try!(fs.write_at(node.0, &node.1)); + Ok(0) } else { Err(Error::new(EPERM)) } @@ -125,17 +168,36 @@ impl Scheme for FileScheme { } } - fn rmdir(&mut self, url: &str) -> Result { - let path = url.split(':').nth(1).unwrap_or("").trim_matches('/'); + fn rmdir(&self, url: &[u8], uid: u32, gid: u32) -> Result { + let path = str::from_utf8(url).unwrap_or("").trim_matches('/'); // println!("Rmdir '{}'", path); + let mut fs = self.fs.borrow_mut(); + let mut nodes = Vec::new(); - let child = try!(self.fs.path_nodes(path, &mut nodes)); + let child = try!(fs.path_nodes(path, &mut nodes)); + for node in nodes.iter() { + if ! node.1.permission(uid, gid, Node::MODE_EXEC) { + // println!("dir not executable {:o}", node.1.mode); + return Err(Error::new(EACCES)); + } + } + if let Some(parent) = nodes.last() { + if ! parent.1.permission(uid, gid, Node::MODE_WRITE) { + // println!("dir not writable {:o}", parent.1.mode); + return Err(Error::new(EACCES)); + } + if child.1.is_dir() { + if ! child.1.permission(uid, gid, Node::MODE_WRITE) { + // println!("dir not writable {:o}", parent.1.mode); + return Err(Error::new(EACCES)); + } + if let Ok(child_name) = child.1.name() { - self.fs.remove_node(Node::MODE_DIR, child_name, parent.0).and(Ok(0)) + fs.remove_node(Node::MODE_DIR, child_name, parent.0).and(Ok(0)) } else { Err(Error::new(ENOENT)) } @@ -147,22 +209,36 @@ impl Scheme for FileScheme { } } - fn stat(&mut self, url: &str, stat: &mut Stat) -> Result { - let resource = try!(self.open_inner(url, 0)); - resource.stat(stat) - } - - fn unlink(&mut self, url: &str) -> Result { - let path = url.split(':').nth(1).unwrap_or("").trim_matches('/'); + fn unlink(&self, url: &[u8], uid: u32, gid: u32) -> Result { + let path = str::from_utf8(url).unwrap_or("").trim_matches('/'); // println!("Unlink '{}'", path); + let mut fs = self.fs.borrow_mut(); + let mut nodes = Vec::new(); - let child = try!(self.fs.path_nodes(path, &mut nodes)); + let child = try!(fs.path_nodes(path, &mut nodes)); + for node in nodes.iter() { + if ! node.1.permission(uid, gid, Node::MODE_EXEC) { + // println!("dir not executable {:o}", node.1.mode); + return Err(Error::new(EACCES)); + } + } + if let Some(parent) = nodes.last() { + if ! parent.1.permission(uid, gid, Node::MODE_WRITE) { + // println!("dir not writable {:o}", parent.1.mode); + return Err(Error::new(EACCES)); + } + if ! child.1.is_dir() { + if ! child.1.permission(uid, gid, Node::MODE_WRITE) { + // println!("file not writable {:o}", parent.1.mode); + return Err(Error::new(EACCES)); + } + if let Ok(child_name) = child.1.name() { - self.fs.remove_node(Node::MODE_FILE, child_name, parent.0).and(Ok(0)) + fs.remove_node(Node::MODE_FILE, child_name, parent.0).and(Ok(0)) } else { Err(Error::new(ENOENT)) } @@ -176,45 +252,48 @@ impl Scheme for FileScheme { /* Resource operations */ #[allow(unused_variables)] - fn dup(&mut self, old_id: usize) -> Result { + fn dup(&self, old_id: usize, _buf: &[u8]) -> Result { // println!("Dup {}", old_id); - let resource = try!(try!(self.files.get(&old_id).ok_or(Error::new(EBADF))).dup()); + let mut files = self.files.lock(); + let resource = if let Some(old_resource) = files.get(&old_id) { + try!(old_resource.dup()) + } else { + return Err(Error::new(EBADF)); + }; - let id = self.next_id as usize; - self.next_id += 1; - if self.next_id < 0 { - self.next_id = 1; - } - - self.files.insert(id, resource); + let id = self.next_id.fetch_add(1, Ordering::SeqCst); + files.insert(id, resource); Ok(id) } #[allow(unused_variables)] - fn read(&mut self, id: usize, buf: &mut [u8]) -> Result { + fn read(&self, id: usize, buf: &mut [u8]) -> Result { // println!("Read {}, {:X} {}", id, buf.as_ptr() as usize, buf.len()); - if let Some(mut file) = self.files.get_mut(&id) { - file.read(buf, &mut self.fs) + let mut files = self.files.lock(); + if let Some(mut file) = files.get_mut(&id) { + file.read(buf, &mut self.fs.borrow_mut()) } else { Err(Error::new(EBADF)) } } - fn write(&mut self, id: usize, buf: &[u8]) -> Result { + fn write(&self, id: usize, buf: &[u8]) -> Result { // println!("Write {}, {:X} {}", id, buf.as_ptr() as usize, buf.len()); - if let Some(mut file) = self.files.get_mut(&id) { - file.write(buf, &mut self.fs) + let mut files = self.files.lock(); + if let Some(mut file) = files.get_mut(&id) { + file.write(buf, &mut self.fs.borrow_mut()) } else { Err(Error::new(EBADF)) } } - fn seek(&mut self, id: usize, pos: usize, whence: usize) -> Result { + fn seek(&self, id: usize, pos: usize, whence: usize) -> Result { // println!("Seek {}, {} {}", id, pos, whence); - if let Some(mut file) = self.files.get_mut(&id) { - file.seek(pos, whence) + let mut files = self.files.lock(); + if let Some(mut file) = files.get_mut(&id) { + file.seek(pos, whence, &mut self.fs.borrow_mut()) } else { Err(Error::new(EBADF)) } @@ -222,8 +301,21 @@ impl Scheme for FileScheme { fn fpath(&self, id: usize, buf: &mut [u8]) -> Result { // println!("Fpath {}, {:X} {}", id, buf.as_ptr() as usize, buf.len()); - if let Some(file) = self.files.get(&id) { - file.path(buf) + let files = self.files.lock(); + if let Some(file) = files.get(&id) { + let name = self.name.as_bytes(); + + let mut i = 0; + while i < buf.len() && i < name.len() { + buf[i] = name[i]; + i += 1; + } + if i < buf.len() { + buf[i] = b':'; + i += 1; + } + + file.path(&mut buf[i..]).map(|count| i + count) } else { Err(Error::new(EBADF)) } @@ -231,34 +323,38 @@ impl Scheme for FileScheme { fn fstat(&self, id: usize, stat: &mut Stat) -> Result { // println!("Fstat {}, {:X}", id, stat as *mut Stat as usize); - if let Some(file) = self.files.get(&id) { - file.stat(stat) + let files = self.files.lock(); + if let Some(file) = files.get(&id) { + file.stat(stat, &mut self.fs.borrow_mut()) } else { Err(Error::new(EBADF)) } } - fn fsync(&mut self, id: usize) -> Result { + fn fsync(&self, id: usize) -> Result { // println!("Fsync {}", id); - if let Some(mut file) = self.files.get_mut(&id) { + let mut files = self.files.lock(); + if let Some(mut file) = files.get_mut(&id) { file.sync() } else { Err(Error::new(EBADF)) } } - fn ftruncate(&mut self, id: usize, len: usize) -> Result { + fn ftruncate(&self, id: usize, len: usize) -> Result { // println!("Ftruncate {}, {}", id, len); - if let Some(mut file) = self.files.get_mut(&id) { - file.truncate(len, &mut self.fs) + let mut files = self.files.lock(); + if let Some(mut file) = files.get_mut(&id) { + file.truncate(len, &mut self.fs.borrow_mut()) } else { Err(Error::new(EBADF)) } } - fn close(&mut self, id: usize) -> Result { + fn close(&self, id: usize) -> Result { // println!("Close {}", id); - if self.files.remove(&id).is_some() { + let mut files = self.files.lock(); + if files.remove(&id).is_some() { Ok(0) } else { Err(Error::new(EBADF)) diff --git a/src/disk.rs b/src/disk.rs index 75a9e58ae8..e345ee82cf 100644 --- a/src/disk.rs +++ b/src/disk.rs @@ -1,4 +1,4 @@ -use system::error::Result; +use syscall::error::Result; /// A disk pub trait Disk { diff --git a/src/filesystem.rs b/src/filesystem.rs index bd2b96c8bf..19f8be25a8 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -1,6 +1,6 @@ use std::cmp::min; -use system::error::{Result, Error, EEXIST, EISDIR, ENOENT, ENOSPC, ENOTDIR, ENOTEMPTY}; +use syscall::error::{Result, Error, EEXIST, EISDIR, ENOENT, ENOSPC, ENOTDIR, ENOTEMPTY}; use super::{Disk, ExNode, Extent, Header, Node}; @@ -14,7 +14,7 @@ pub struct FileSystem { impl FileSystem { /// Open a file system on a disk pub fn open(mut disk: Box) -> Result { - for block in 0..8192 { + for block in 0..65536 { let mut header = (0, Header::default()); try!(disk.read_at(block + header.0, &mut header.1)); @@ -45,7 +45,7 @@ impl FileSystem { free.1.extents[0] = Extent::new(4, size - 4 * 512); try!(disk.write_at(free.0, &free.1)); - let root = (1, Node::new(Node::MODE_DIR, "root", 0)); + let root = (1, Node::new(Node::MODE_DIR | 0o755, "root", 0)); try!(disk.write_at(root.0, &root.1)); let header = (0, Header::new(size, root.0, free.0)); @@ -61,11 +61,11 @@ impl FileSystem { } } - fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result { + pub fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result { self.disk.read_at(self.block + block, buffer) } - fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result { + pub fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result { self.disk.write_at(self.block + block, buffer) } diff --git a/src/lib.rs b/src/lib.rs index fcf352966f..f4106c3994 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ #![deny(warnings)] -extern crate system; +extern crate syscall; pub use self::disk::Disk; pub use self::ex_node::ExNode; diff --git a/src/node.rs b/src/node.rs index 57e4c1192d..56a1b975e3 100644 --- a/src/node.rs +++ b/src/node.rs @@ -6,9 +6,9 @@ use super::Extent; #[repr(packed)] pub struct Node { pub mode: u16, - pub user: u16, - pub group: u16, - pub name: [u8; 250], + pub uid: u32, + pub gid: u32, + pub name: [u8; 246], pub parent: u64, pub next: u64, pub extents: [Extent; 15], @@ -20,13 +20,16 @@ impl Node { pub const MODE_DIR: u16 = 0x4000; pub const MODE_PERM: u16 = 0x0FFF; + pub const MODE_EXEC: u16 = 0o1; + pub const MODE_WRITE: u16 = 0o2; + pub const MODE_READ: u16 = 0o4; pub fn default() -> Node { Node { mode: 0, - user: 0, - group: 0, - name: [0; 250], + uid: 0, + gid: 0, + name: [0; 246], parent: 0, next: 0, extents: [Extent::default(); 15], @@ -34,15 +37,15 @@ impl Node { } pub fn new(mode: u16, name: &str, parent: u64) -> Node { - let mut bytes = [0; 250]; + let mut bytes = [0; 246]; for (mut b, c) in bytes.iter_mut().zip(name.bytes()) { *b = c; } Node { mode: mode, - user: 0, - group: 0, + uid: 0, + gid: 0, name: bytes, parent: parent, next: 0, @@ -71,6 +74,20 @@ impl Node { self.mode & Node::MODE_TYPE == Node::MODE_FILE } + pub fn permission(&self, uid: u32, gid: u32, op: u16) -> bool { + let mut perm = self.mode & 0o7; + if self.uid == uid { + perm |= (self.mode >> 6) & 0o7; + } + if self.gid == gid || gid == 0 { + perm |= (self.mode >> 3) & 0o7; + } + if uid == 0 { + perm |= 0o7; + } + perm & op == op + } + pub fn size(&self) -> u64 { self.extents.iter().fold(0, |size, extent| size + extent.length) } @@ -81,8 +98,8 @@ impl fmt::Debug for Node { let extents: Vec<&Extent> = self.extents.iter().filter(|extent| -> bool { extent.length > 0 }).collect(); f.debug_struct("Node") .field("mode", &self.mode) - .field("user", &self.user) - .field("group", &self.group) + .field("uid", &self.uid) + .field("gid", &self.gid) .field("name", &self.name()) .field("next", &self.next) .field("extents", &extents) diff --git a/utility/image.rs b/utility/image.rs index ae8bf6f88a..cb1356aadb 100644 --- a/utility/image.rs +++ b/utility/image.rs @@ -2,8 +2,7 @@ use std::fs::{File, OpenOptions}; use std::io::{Read, Write, Seek, SeekFrom}; use redoxfs::Disk; - -use system::error::{Error, Result, EIO}; +use syscall::error::{Error, Result, EIO}; macro_rules! try_disk { ($expr:expr) => (match $expr { diff --git a/utility/main.rs b/utility/main.rs index b8c0b471e2..3331893981 100644 --- a/utility/main.rs +++ b/utility/main.rs @@ -1,8 +1,7 @@ #![deny(warnings)] extern crate redoxfs; - -extern crate system; +extern crate syscall; use std::env; use std::io::{self, Write}; @@ -218,9 +217,8 @@ fn main() { }, Err(err) => println!("redoxfs: failed to open image {}: {}", path, err) } - }else{ - //Create a 256 MB disk image - let size = 256 * 1024 * 1024; + }else if let Some(size_str) = args.next() { + let size = size_str.parse::().expect("redoxfs: size is not a valid number") * 1024 * 1024; match Image::create(&path, size) { Ok(disk) => match FileSystem::create(Box::new(disk)) { Ok(filesystem) => { @@ -231,6 +229,8 @@ fn main() { }, Err(err) => println!("redoxfs: failed to create image {}: {}", path, err) } + } else { + println!("redoxfs: no size provided"); } } else { println!("redoxfs: no disk image provided");