diff --git a/Cargo.lock b/Cargo.lock index 0da68764e6..da530d426e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,40 +6,10 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "bstr" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", -] - -[[package]] -name = "byteorder" -version = "1.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "memchr" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" - [[package]] name = "ramfs" version = "0.1.0" dependencies = [ - "bstr", "redox_syscall", ] @@ -50,12 +20,3 @@ source = "git+https://gitlab.redox-os.org/redox-os/syscall.git#8d0015be8693a81c2 dependencies = [ "bitflags", ] - -[[package]] -name = "regex-automata" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" -dependencies = [ - "byteorder", -] diff --git a/Cargo.toml b/Cargo.toml index f26c92450d..af4002d300 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,3 @@ edition = "2018" [dependencies] redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" } -bstr = "0.2" diff --git a/src/filesystem.rs b/src/filesystem.rs index 7b7ab61a7a..6a776f3906 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -1,18 +1,18 @@ use std::collections::BTreeMap; +use std::convert::TryInto; use std::ffi::OsStr; -use std::fs; +use std::{iter, fs, time}; use std::os::unix::io::AsRawFd; use std::os::unix::ffi::OsStrExt; -use bstr::ByteSlice; - -use syscall::{Error, Result, StatVfs, TimeSpec}; +use syscall::{Error, Result, StatVfs, TimeSpec, MODE_DIR}; use syscall::error::{EACCES, EIO, ENFILE, ENOENT}; use syscall::flag::{O_RDONLY, O_STAT}; use super::scheme::current_perm; +#[derive(Debug)] pub struct File { pub mode: u16, pub uid: u32, @@ -30,11 +30,13 @@ pub struct File { pub data: FileData, } +#[derive(Clone, Debug)] pub struct DirEntry { pub name: Vec, pub inode: usize, } +#[derive(Debug)] pub enum FileData { File(Vec), Directory(Vec), @@ -71,11 +73,30 @@ impl Filesystem { pub fn new() -> Result { Ok(Self { - files: BTreeMap::new(), + files: iter::once((Self::ROOT_INODE, Self::create_root_inode())).collect(), memory_file: fs::File::open("memory:").or(Err(Error::new(EIO)))?, last_inode_number: Self::ROOT_INODE, }) } + fn create_root_inode() -> File { + let cur_time = current_time(); + File { + atime: cur_time, + crtime: cur_time, + ctime: cur_time, + mtime: cur_time, + + mode: MODE_DIR | 0o755, + ino: Self::ROOT_INODE, + nlink: 1, + open_handles: 0, + + uid: 0, + gid: 0, + + data: FileData::Directory(Vec::new()), + } + } pub fn get_block_size(&self) -> Result { let mut statvfs = StatVfs::default(); syscall::fstatvfs(self.memory_file.as_raw_fd() as usize, &mut statvfs)?; @@ -89,13 +110,19 @@ impl Filesystem { self.last_inode_number = next; Ok(next) } - fn resolve_generic(&self, parts: Vec<&[u8]>, uid: u32, gid: u32) -> Result { + fn resolve_generic(&self, mut parts: Vec<&[u8]>, uid: u32, gid: u32) -> Result { let mut current_file = self.files.get(&Self::ROOT_INODE).ok_or(Error::new(ENOENT))?; + dbg!(); let mut current_inode = Self::ROOT_INODE; let mut i = 0; - while let Some(part) = parts.get(i) { + loop { + let part = match parts.get(i) { + Some(p) => p, + None => break, + }; + dbg!(current_inode, part); let dentries = match current_file.data { FileData::Directory(ref dentries) => dentries, FileData::File(_) => return Err(Error::new(ENOENT)), @@ -106,12 +133,15 @@ impl Filesystem { if part == b"." || part == b".." { parts.remove(i); } + + let part = parts.get(i).unwrap(); if part == b".." { if i > 0 { i -= 1; parts.remove(i); } } + let part = parts.get(i).unwrap(); let entry = dentries.iter().find(|dentry| &dentry.name == part).ok_or(Error::new(ENOENT))?; current_file = self.files.get(&entry.inode).ok_or(Error::new(EIO))?; @@ -121,10 +151,15 @@ impl Filesystem { } Ok(current_inode) } - pub fn resolve_except_last<'a>(&self, mut path_bytes: &'a [u8], uid: u32, gid: u32) -> Result<(usize, &'a [u8])> { + pub fn resolve_except_last<'a>(&self, mut path_bytes: &'a [u8], uid: u32, gid: u32) -> Result<(usize, Option<&'a [u8]>)> { if path_bytes.first() == Some(&b'/') { path_bytes = &path_bytes[1..] } - let parts = path_components_iter(path_bytes).collect::>(); - let last = parts.pop().ok_or(Error::new(ENOENT))?; + let mut parts = path_components_iter(path_bytes).collect::>(); + + let last = if parts.len() >= 1 { + Some(parts.pop().unwrap()) + } else { + None + }; Ok((self.resolve_generic(parts, uid, gid)?, last)) } @@ -139,3 +174,25 @@ pub fn path_components_iter(bytes: &[u8]) -> impl Iterator + '_ { let components_iter = bytes.split(|c| c == &b'/'); components_iter.filter(|item| !item.is_empty()) } +pub fn current_time() -> TimeSpec { + let sys_time = time::SystemTime::now(); + + let duration = match sys_time.duration_since(time::SystemTime::UNIX_EPOCH) { + Ok(d) => d, + Err(e) => { + eprintln!("Apparently the signed 32-bit integer has overflowed; the time is now before the Unix epoch..."); + + let negative_duration = e.duration(); + + return TimeSpec { + tv_sec: negative_duration.as_secs().try_into().unwrap_or(i64::min_value()), + tv_nsec: negative_duration.subsec_nanos().try_into().unwrap_or(i32::min_value()), + }; + } + }; + + TimeSpec { + tv_sec: duration.as_secs().try_into().unwrap_or(i64::max_value()), + tv_nsec: duration.subsec_nanos().try_into().unwrap_or(i32::max_value()), + } +} diff --git a/src/main.rs b/src/main.rs index 8879cd7985..dc73ff138c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use std::fs::File; use std::io::prelude::*; -use std::env; +use std::{env, io}; use syscall::{CloneFlags, Packet, SchemeMut}; @@ -13,26 +13,29 @@ use self::scheme::Scheme; fn main() { let scheme_name = env::args().nth(1).expect("Usage:\n\tramfs SCHEME_NAME"); - let mut socket = File::create(format!(":{}", scheme_name)).expect("ramfs: failed to create socket"); if unsafe { syscall::clone(CloneFlags::empty()) }.expect("ramfs: failed to fork") != 0 { return; } + let mut socket = File::create(format!(":{}", scheme_name)).expect("ramfs: failed to create socket"); let mut scheme = Scheme::new(scheme_name).expect("ramfs: failed to initialize scheme"); - loop { + 'packet_loop: loop { let mut packet = Packet::default(); + match socket.read(&mut packet) { - Ok(0) => break, + Ok(0) => break 'packet_loop, Ok(_) => (), + Err(error) if error.kind() == io::ErrorKind::Interrupted => continue 'packet_loop, Err(error) => panic!("ramfs: failed to read from socket: {:?}", error), } + scheme.handle(&mut packet); match socket.write(&packet) { - Ok(0) => break, Ok(_) => (), + Err(error) if error.kind() == io::ErrorKind::Interrupted => continue 'packet_loop, Err(error) => panic!("ramfs: failed to write to socket: {:?}", error), } } diff --git a/src/scheme.rs b/src/scheme.rs index 655ed207e7..7f17cfcb58 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -1,14 +1,14 @@ use std::collections::BTreeMap; use std::convert::{TryFrom, TryInto}; use std::os::unix::io::AsRawFd; -use std::{cmp, ops, time}; +use std::{cmp, ops}; use syscall::{Error, EventFlags, Map, Result, SchemeMut, Stat, StatVfs, TimeSpec}; -use syscall::flag::{O_CREAT, O_EXCL, O_RDWR, O_RDONLY, O_WRONLY, O_DIRECTORY, O_STAT}; -use syscall::{MODE_DIR, MODE_PERM, MODE_TYPE, SEEK_CUR, SEEK_END, SEEK_SET}; -use syscall::error::{EACCES, EBADF, EBADFD, EEXIST, EFBIG, EINVAL, EISDIR, EIO, ENOENT, ENOMEM, ENOSYS, ENOTDIR, ENOTEMPTY, EOPNOTSUPP, EOVERFLOW}; +use syscall::flag::{O_ACCMODE, O_CREAT, O_EXCL, O_RDWR, O_RDONLY, O_WRONLY, O_DIRECTORY, O_STAT}; +use syscall::{MODE_FILE, MODE_DIR, MODE_PERM, MODE_TYPE, SEEK_CUR, SEEK_END, SEEK_SET}; +use syscall::error::{EACCES, EBADF, EBADFD, EEXIST, EINVAL, EISDIR, EIO, ENOMEM, ENOSYS, ENOTDIR, ENOTEMPTY, EOVERFLOW}; -use crate::filesystem::{DirEntry, File, FileData, Filesystem}; +use crate::filesystem::{self, DirEntry, File, FileData, Filesystem}; #[derive(Clone)] struct Handle { @@ -44,10 +44,11 @@ impl Scheme { pub fn remove_dentry(&mut self, path: &[u8], uid: u32, gid: u32, directory: bool) -> Result { let removed_entry = { let (parent_dir_inode, name_to_delete) = self.filesystem.resolve_except_last(path, uid, gid)?; + let name_to_delete = name_to_delete.ok_or(Error::new(EINVAL))?; // can't remove root let parent = self.filesystem.files.get_mut(&parent_dir_inode).ok_or(Error::new(EIO))?; let mode = current_perm(parent, uid, gid); - if mode & 0o2 != 0 { + if mode & 0o2 == 0 { return Err(Error::new(EACCES)); } @@ -89,43 +90,27 @@ impl Scheme { } } -pub fn current_time() -> TimeSpec { - let sys_time = time::SystemTime::now(); - - let duration = match sys_time.duration_since(time::SystemTime::UNIX_EPOCH) { - Ok(d) => d, - Err(e) => { - eprintln!("Apparently the signed 32-bit integer has overflowed; the time is now before the Unix epoch..."); - - let negative_duration = e.duration(); - - return TimeSpec { - tv_sec: negative_duration.as_secs().try_into().unwrap_or(i64::min_value()), - tv_nsec: negative_duration.subsec_nanos().try_into().unwrap_or(i32::min_value()), - }; - } - }; - - TimeSpec { - tv_sec: duration.as_secs().try_into().unwrap_or(i64::max_value()), - tv_nsec: duration.subsec_nanos().try_into().unwrap_or(i32::max_value()), - } -} - impl SchemeMut for Scheme { fn open(&mut self, path: &[u8], flags: usize, uid: u32, gid: u32) -> Result { let handle = if flags & O_CREAT != 0 { if flags & O_EXCL != 0 && self.filesystem.resolve(path, 0, 0).is_ok() { - return Err(Error::new(EEXIST)) + return Err(Error::new(EEXIST)); } if flags & O_STAT != 0 { return Err(Error::new(EINVAL)) } let (parent_dir_inode, new_name) = self.filesystem.resolve_except_last(path, uid, gid)?; + let new_name = new_name.ok_or(Error::new(EINVAL))?; // cannot mkdir / - let current_time = current_time(); + let current_time = filesystem::current_time(); let new_inode_number = self.filesystem.next_inode_number()?; + + let mut mode = (flags & 0xFFFF) as u16; + let new_inode = if flags & O_DIRECTORY != 0 { + if mode & MODE_TYPE == 0 { mode |= MODE_DIR } + if mode & MODE_TYPE != MODE_DIR { return Err(Error::new(EINVAL)) } + File { atime: current_time, crtime: current_time, @@ -134,12 +119,15 @@ impl SchemeMut for Scheme { gid, uid, ino: new_inode_number, - mode: 0o755, + mode, nlink: 2, // parent entry, "." data: FileData::Directory(Vec::new()), open_handles: 1, } } else { + if mode & MODE_TYPE == 0 { mode |= MODE_FILE } + if mode & MODE_TYPE == MODE_DIR { return Err(Error::new(EINVAL)) } + File { atime: current_time, crtime: current_time, @@ -148,7 +136,7 @@ impl SchemeMut for Scheme { gid, uid, ino: new_inode_number, - mode: 0o644, + mode, nlink: 1, data: FileData::File(Vec::new()), open_handles: 1, @@ -168,14 +156,13 @@ impl SchemeMut for Scheme { Handle { inode: new_inode_number, offset: 0, - opened_as_read: flags & O_RDWR == O_RDONLY || flags & O_RDWR == O_RDWR, - opened_as_write: flags & O_RDWR == O_WRONLY || flags & O_RDWR == O_RDWR, + opened_as_read: flags & O_ACCMODE == O_RDONLY || flags & O_ACCMODE == O_RDWR, + opened_as_write: flags & O_ACCMODE == O_WRONLY || flags & O_ACCMODE == O_RDWR, current_perm, } } else { let inode = self.filesystem.resolve(path, uid, gid)?; - let file = self.filesystem.files.get_mut(&inode).ok_or(Error::new(ENOENT))?; - + let file = self.filesystem.files.get_mut(&inode).ok_or(Error::new(EIO))?; if flags & O_STAT == 0 && flags & O_DIRECTORY != 0 && file.mode & MODE_TYPE != MODE_DIR { return Err(Error::new(ENOTDIR)); } @@ -195,8 +182,8 @@ impl SchemeMut for Scheme { Handle { inode, offset: 0, - opened_as_read: flags & O_RDWR == O_RDONLY || flags & O_RDWR == O_RDWR, - opened_as_write: flags & O_RDWR == O_WRONLY || flags & O_RDWR == O_RDWR, + opened_as_read: flags & O_ACCMODE == O_RDONLY || flags & O_ACCMODE == O_RDWR, + opened_as_write: flags & O_ACCMODE == O_WRONLY || flags & O_ACCMODE == O_RDWR, current_perm, } }; @@ -212,13 +199,15 @@ impl SchemeMut for Scheme { let inode_num = self.filesystem.resolve(path, uid, gid)?; let inode = self.filesystem.files.get_mut(&inode_num).ok_or(Error::new(EIO))?; + let cur_mode = inode.mode & MODE_TYPE; + if inode.uid != uid && uid != 0 { return Err(Error::new(EACCES)); } if mode & MODE_TYPE != 0 { return Err(Error::new(EINVAL)); } - inode.mode = mode; + inode.mode = mode | cur_mode; Ok(0) } fn rmdir(&mut self, path: &[u8], uid: u32, gid: u32) -> Result { @@ -251,6 +240,7 @@ impl SchemeMut for Scheme { } let bytes_to_read = cmp::min(bytes.len(), buf.len() + handle.offset) - handle.offset; buf[..bytes_to_read].copy_from_slice(&bytes[handle.offset..handle.offset + bytes_to_read]); + handle.offset += bytes_to_read; Ok(bytes_to_read) } FileData::Directory(ref entries) => { @@ -269,14 +259,16 @@ impl SchemeMut for Scheme { continue; } - let bytes_to_read = cmp::min(entry_bytes.len() - bytes_to_skip, bytes_left_to_read); + let bytes_to_read = cmp::min(entry_bytes.len() + 1 - bytes_to_skip, bytes_left_to_read); - let entry_bytes = &entry_bytes[bytes_to_skip..bytes_to_skip + bytes_to_read]; + let entry_bytes = &entry_bytes[bytes_to_skip..bytes_to_skip + bytes_to_read - 1]; bytes_to_skip -= bytes_to_skip; - buf[handle.offset..handle.offset + bytes_to_read].copy_from_slice(&entry_bytes[..bytes_to_read]); + buf[bytes_read..bytes_read + bytes_to_read - 1].copy_from_slice(&entry_bytes[..bytes_to_read - 1]); + buf[bytes_read + bytes_to_read - 1] = b'\n'; bytes_left_to_read -= bytes_to_read; bytes_read += bytes_to_read; + handle.offset += bytes_read; } Ok(bytes_read) } @@ -308,6 +300,7 @@ impl SchemeMut for Scheme { // cast to isize, possibly making the offset negative let pos = pos as isize; + let old = handle.offset; handle.offset = match whence { SEEK_SET => cmp::max(0, pos) as usize, SEEK_CUR => cmp::max(0, pos + isize::try_from(handle.offset).or(Err(Error::new(EOVERFLOW)))?) as usize, @@ -320,11 +313,13 @@ impl SchemeMut for Scheme { let handle = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?; let file = self.filesystem.files.get_mut(&handle.inode).ok_or(Error::new(EBADFD))?; + let cur_type = file.mode & MODE_TYPE; + if mode & MODE_TYPE != 0 { return Err(Error::new(EINVAL)); } - file.mode = mode; + file.mode = mode | cur_type; Ok(0) } @@ -379,6 +374,8 @@ impl SchemeMut for Scheme { let block_size = self.filesystem.block_size(); let file = self.filesystem.files.get_mut(&handle.inode).ok_or(Error::new(EBADFD))?; + let size = file.data.size().try_into().or(Err(Error::new(EOVERFLOW)))?; + *stat = Stat { st_mode: file.mode, st_uid: file.uid, @@ -387,9 +384,9 @@ impl SchemeMut for Scheme { st_nlink: file.nlink.try_into().or(Err(Error::new(EOVERFLOW)))?, st_dev: 0, - st_size: file.data.size().try_into().or(Err(Error::new(EOVERFLOW)))?, + st_size: size, st_blksize: block_size, - st_blocks: div_round_up(stat.st_size, u64::from(stat.st_blksize)), + st_blocks: div_round_up(size, u64::from(block_size)), st_atime: file.atime.tv_sec.try_into().or(Err(Error::new(EOVERFLOW)))?, st_atime_nsec: file.atime.tv_nsec.try_into().or(Err(Error::new(EOVERFLOW)))?, @@ -483,11 +480,11 @@ pub fn current_perm(file: &crate::filesystem::File, uid: u32, gid: u32) -> u8 { } } fn check_permissions(flags: usize, single_mode: u8) -> Result<()> { - if flags & O_RDWR == O_RDONLY && single_mode & 0o4 == 0 { + if flags & O_ACCMODE == O_RDONLY && single_mode & 0o4 == 0 { return Err(Error::new(EACCES)); - } else if flags & O_RDWR == O_WRONLY && single_mode & 0o2 == 0 { + } else if flags & O_ACCMODE == O_WRONLY && single_mode & 0o2 == 0 { return Err(Error::new(EACCES)); - } else if flags & O_RDWR == O_RDWR && single_mode & 0o6 != 0o6 { + } else if flags & O_ACCMODE == O_RDWR && single_mode & 0o6 != 0o6 { return Err(Error::new(EACCES)); } Ok(())