Make most of the scheme work, except for deletion.

This commit is contained in:
4lDO2
2020-04-04 10:26:43 +02:00
parent 931aa86ba8
commit 6b005a02f4
5 changed files with 121 additions and 104 deletions
Generated
-39
View File
@@ -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",
]
-1
View File
@@ -8,4 +8,3 @@ edition = "2018"
[dependencies]
redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" }
bstr = "0.2"
+67 -10
View File
@@ -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<u8>,
pub inode: usize,
}
#[derive(Debug)]
pub enum FileData {
File(Vec<u8>),
Directory(Vec<DirEntry>),
@@ -71,11 +73,30 @@ impl Filesystem {
pub fn new() -> Result<Self> {
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<u32> {
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<usize> {
fn resolve_generic(&self, mut parts: Vec<&[u8]>, uid: u32, gid: u32) -> Result<usize> {
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::<Vec<_>>();
let last = parts.pop().ok_or(Error::new(ENOENT))?;
let mut parts = path_components_iter(path_bytes).collect::<Vec<_>>();
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<Item = &[u8]> + '_ {
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()),
}
}
+8 -5
View File
@@ -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),
}
}
+46 -49
View File
@@ -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<usize> {
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<usize> {
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<usize> {
@@ -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(())