Make scheme fully stateless.
Stateless in this case means the inode number is sufficient for describing an open file handle.
This commit is contained in:
Generated
+43
-4
@@ -2,12 +2,40 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.158"
|
||||
@@ -29,10 +57,12 @@ dependencies = [
|
||||
name = "ramfs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"libredox",
|
||||
"redox-daemon",
|
||||
"redox-scheme",
|
||||
"redox_syscall",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -47,9 +77,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox-scheme"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2148c23b7ed5bf1cc3919b4830e4bf2a674a5d3691c619317624ccd78d13a3ac"
|
||||
checksum = "98d040cfa6370d9c6b1a5987247272f19ea4722f1064e66ad2e077c152b82ea3"
|
||||
dependencies = [
|
||||
"libredox",
|
||||
"redox_syscall",
|
||||
@@ -57,9 +87,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.3"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
|
||||
checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
+4
-2
@@ -10,6 +10,8 @@ description = "In-memory filesystem for Redox, which is useful for early logging
|
||||
|
||||
[dependencies]
|
||||
redox-daemon = "0.1.2"
|
||||
redox_syscall = "0.5.1"
|
||||
redox-scheme = "0.2.1"
|
||||
redox_syscall = "0.5.3"
|
||||
redox-scheme = "0.2.2"
|
||||
libredox = "0.1.3"
|
||||
indexmap = "2.5.0"
|
||||
slab = "0.4.9"
|
||||
|
||||
+25
-57
@@ -1,14 +1,12 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::OsStr;
|
||||
use std::{fs, iter, time};
|
||||
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use syscall::error::{EACCES, EIO, ENFILE, ENOENT};
|
||||
use syscall::flag::{O_RDONLY, O_STAT};
|
||||
use syscall::{Error, Result, StatVfs, TimeSpec, MODE_DIR};
|
||||
use syscall::{Error, Result, TimeSpec, MODE_DIR};
|
||||
|
||||
use super::scheme::current_perm;
|
||||
|
||||
@@ -17,7 +15,6 @@ pub struct File {
|
||||
pub mode: u16,
|
||||
pub uid: u32,
|
||||
pub gid: u32,
|
||||
pub ino: usize,
|
||||
pub nlink: usize,
|
||||
|
||||
pub open_handles: usize,
|
||||
@@ -31,33 +28,18 @@ pub struct File {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DirEntry {
|
||||
pub name: Vec<u8>,
|
||||
pub inode: usize,
|
||||
}
|
||||
pub struct Inode(pub usize);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FileData {
|
||||
File(Vec<u8>),
|
||||
Directory(Vec<DirEntry>),
|
||||
Directory(IndexMap<String, Inode>),
|
||||
}
|
||||
impl FileData {
|
||||
pub fn size(&self) -> usize {
|
||||
match self {
|
||||
&Self::File(ref data) => data.len(),
|
||||
&Self::Directory(ref names) => names.iter().map(|dentry| dentry.name.len()).sum(),
|
||||
}
|
||||
}
|
||||
pub fn as_directory(&self) -> Option<&[DirEntry]> {
|
||||
match self {
|
||||
&Self::Directory(ref inner) => Some(inner),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn as_directory_mut(&mut self) -> Option<&mut Vec<DirEntry>> {
|
||||
match self {
|
||||
&mut Self::Directory(ref mut inner) => Some(inner),
|
||||
_ => None,
|
||||
&Self::Directory(_) => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,14 +69,13 @@ impl Filesystem {
|
||||
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()),
|
||||
data: FileData::Directory(IndexMap::new()),
|
||||
}
|
||||
}
|
||||
pub fn get_block_size(&self) -> Result<u32> {
|
||||
@@ -111,7 +92,7 @@ impl Filesystem {
|
||||
self.last_inode_number = next;
|
||||
Ok(next)
|
||||
}
|
||||
fn resolve_generic(&self, mut parts: Vec<&[u8]>, uid: u32, gid: u32) -> Result<usize> {
|
||||
fn resolve_generic(&self, mut parts: Vec<&str>, uid: u32, gid: u32) -> Result<usize> {
|
||||
let mut current_file = self
|
||||
.files
|
||||
.get(&Self::ROOT_INODE)
|
||||
@@ -121,9 +102,8 @@ impl Filesystem {
|
||||
let mut i = 0;
|
||||
|
||||
loop {
|
||||
let part = match parts.get(i) {
|
||||
Some(p) => p,
|
||||
None => break,
|
||||
let Some(&part) = parts.get(i) else {
|
||||
break;
|
||||
};
|
||||
let dentries = match current_file.data {
|
||||
FileData::Directory(ref dentries) => dentries,
|
||||
@@ -134,25 +114,19 @@ impl Filesystem {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
if part == b"." || part == b".." {
|
||||
if part == "." || part == ".." {
|
||||
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();
|
||||
if part == ".." && i > 0 {
|
||||
i -= 1;
|
||||
parts.remove(i);
|
||||
}
|
||||
let part = parts.get(i).unwrap();
|
||||
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))?;
|
||||
current_inode = entry.inode;
|
||||
current_inode = dentries.get(part).ok_or(Error::new(ENOENT))?.0;
|
||||
current_file = self.files.get(¤t_inode).ok_or(Error::new(EIO))?;
|
||||
|
||||
i += 1;
|
||||
}
|
||||
@@ -160,14 +134,11 @@ impl Filesystem {
|
||||
}
|
||||
pub fn resolve_except_last<'a>(
|
||||
&self,
|
||||
mut path_bytes: &'a [u8],
|
||||
path_bytes: &'a str,
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
) -> Result<(usize, Option<&'a [u8]>)> {
|
||||
if path_bytes.first() == Some(&b'/') {
|
||||
path_bytes = &path_bytes[1..]
|
||||
}
|
||||
let mut parts = path_components_iter(path_bytes).collect::<Vec<_>>();
|
||||
) -> Result<(usize, Option<&'a str>)> {
|
||||
let mut parts = path_components_iter(path_bytes.trim_start_matches('/')).collect::<Vec<_>>();
|
||||
|
||||
let last = if parts.len() >= 1 {
|
||||
Some(parts.pop().unwrap())
|
||||
@@ -177,17 +148,14 @@ impl Filesystem {
|
||||
|
||||
Ok((self.resolve_generic(parts, uid, gid)?, last))
|
||||
}
|
||||
pub fn resolve(&self, mut path_bytes: &[u8], uid: u32, gid: u32) -> Result<usize> {
|
||||
if path_bytes.first() == Some(&b'/') {
|
||||
path_bytes = &path_bytes[1..]
|
||||
}
|
||||
let parts = path_components_iter(path_bytes).collect::<Vec<_>>();
|
||||
pub fn resolve(&self, path: &str, uid: u32, gid: u32) -> Result<usize> {
|
||||
let parts = path_components_iter(path.trim_start_matches('/')).collect::<Vec<_>>();
|
||||
|
||||
self.resolve_generic(parts, uid, gid)
|
||||
}
|
||||
}
|
||||
pub fn path_components_iter(bytes: &[u8]) -> impl Iterator<Item = &[u8]> + '_ {
|
||||
let components_iter = bytes.split(|c| c == &b'/');
|
||||
pub fn path_components_iter(bytes: &str) -> impl Iterator<Item = &str> + '_ {
|
||||
let components_iter = bytes.split(|c| c == '/');
|
||||
components_iter.filter(|item| !item.is_empty())
|
||||
}
|
||||
pub fn current_time() -> TimeSpec {
|
||||
@@ -196,7 +164,7 @@ pub fn current_time() -> TimeSpec {
|
||||
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...");
|
||||
eprintln!("The time is apparently now before the Unix epoch...");
|
||||
|
||||
let negative_duration = e.duration();
|
||||
|
||||
|
||||
+2
-3
@@ -3,7 +3,7 @@ use std::{env, process};
|
||||
mod filesystem;
|
||||
mod scheme;
|
||||
|
||||
use redox_scheme::{RequestKind, SignalBehavior};
|
||||
use redox_scheme::{RequestKind, SignalBehavior, V2};
|
||||
|
||||
use self::scheme::Scheme;
|
||||
|
||||
@@ -11,8 +11,7 @@ fn main() {
|
||||
let scheme_name = env::args().nth(1).expect("Usage:\n\tramfs SCHEME_NAME");
|
||||
|
||||
redox_daemon::Daemon::new(move |daemon| {
|
||||
let socket = redox_scheme::Socket::<redox_scheme::V2>::create(&scheme_name)
|
||||
.expect("ramfs: failed to create socket");
|
||||
let socket = redox_scheme::Socket::<V2>::create(&scheme_name).expect("ramfs: failed to create socket");
|
||||
|
||||
let mut scheme = Scheme::new(scheme_name).expect("ramfs: failed to initialize scheme");
|
||||
|
||||
|
||||
+100
-227
@@ -1,8 +1,8 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::{cmp, ops};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use syscall::dirent::{DirEntry, DirentBuf, DirentKind};
|
||||
use syscall::error::{
|
||||
EACCES, EBADF, EBADFD, EEXIST, EINVAL, EIO, EISDIR, ENOMEM, ENOSYS, ENOTDIR, ENOTEMPTY,
|
||||
EOVERFLOW,
|
||||
@@ -11,30 +11,15 @@ use syscall::flag::{
|
||||
O_ACCMODE, O_CREAT, O_DIRECTORY, O_EXCL, O_RDONLY, O_RDWR, O_STAT, O_TRUNC, O_WRONLY,
|
||||
};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::{Error, EventFlags, Map, Result, Stat, StatVfs, TimeSpec};
|
||||
use syscall::{MODE_DIR, MODE_FILE, MODE_PERM, MODE_TYPE, SEEK_CUR, SEEK_END, SEEK_SET};
|
||||
use syscall::{Error, EventFlags, Result, Stat, StatVfs, TimeSpec, ENOENT};
|
||||
use syscall::{MODE_DIR, MODE_FILE, MODE_PERM, MODE_TYPE};
|
||||
|
||||
use redox_scheme::{CallerCtx, OpenResult, SchemeMut};
|
||||
|
||||
use crate::filesystem::{self, DirEntry, File, FileData, Filesystem};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Handle {
|
||||
inode: usize,
|
||||
|
||||
// TODO: fcntl_flags?
|
||||
opened_as_read: bool, // opened with O_RDONLY or O_RDWR
|
||||
opened_as_write: bool, // opened with O_WRONLY or O_RDWR
|
||||
|
||||
// the three first bits of mode >> 6 if uid matched, mode >> 3 if gid matched, otherwise mode
|
||||
// >> 0.
|
||||
current_perm: u8,
|
||||
}
|
||||
use crate::filesystem::{self, File, FileData, Filesystem, Inode};
|
||||
|
||||
pub struct Scheme {
|
||||
scheme_name: String,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
next_fd: usize,
|
||||
filesystem: Filesystem,
|
||||
}
|
||||
impl Scheme {
|
||||
@@ -42,21 +27,19 @@ impl Scheme {
|
||||
pub fn new(scheme_name: String) -> Result<Self> {
|
||||
Ok(Self {
|
||||
scheme_name,
|
||||
handles: BTreeMap::new(),
|
||||
filesystem: Filesystem::new()?,
|
||||
next_fd: 0,
|
||||
})
|
||||
}
|
||||
/// Remove a directory entry, where the entry can be both a file or a directory. Used by both
|
||||
/// `unlink` and `rmdir`.
|
||||
pub fn remove_dentry(
|
||||
&mut self,
|
||||
path: &[u8],
|
||||
path: &str,
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
directory: bool,
|
||||
) -> Result<usize> {
|
||||
let removed_entry = {
|
||||
let removed_inode = {
|
||||
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
|
||||
@@ -71,16 +54,11 @@ impl Scheme {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
let dentries = parent.data.as_directory_mut().ok_or(Error::new(EBADF))?;
|
||||
let FileData::Directory(ref mut dentries) = parent.data else {
|
||||
return Err(Error::new(ENOTDIR));
|
||||
};
|
||||
|
||||
let (position, entry_inode) = dentries
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, d)| d.name == name_to_delete)
|
||||
.unwrap();
|
||||
let entry_inode = entry_inode.inode;
|
||||
|
||||
let removed_entry = dentries.remove(position);
|
||||
let Inode(entry_inode) = dentries.shift_remove(name_to_delete).ok_or(Error::new(ENOENT))?;
|
||||
|
||||
if let Some(File {
|
||||
data: FileData::Directory(ref data),
|
||||
@@ -100,35 +78,35 @@ impl Scheme {
|
||||
parent.nlink -= 1; // '..' of subdirectory
|
||||
}
|
||||
|
||||
removed_entry
|
||||
entry_inode
|
||||
};
|
||||
|
||||
let removed_inode = self
|
||||
let removed_inode_info = self
|
||||
.filesystem
|
||||
.files
|
||||
.get_mut(&removed_entry.inode)
|
||||
.get_mut(&removed_inode)
|
||||
.ok_or(Error::new(EIO))?;
|
||||
|
||||
if let FileData::File(_) = removed_inode.data {
|
||||
if let FileData::File(_) = removed_inode_info.data {
|
||||
if directory {
|
||||
return Err(Error::new(EISDIR));
|
||||
}
|
||||
removed_inode.nlink -= 1; // only the parent entry
|
||||
removed_inode_info.nlink -= 1; // only the parent entry
|
||||
} else {
|
||||
if !directory {
|
||||
return Err(Error::new(ENOTDIR));
|
||||
}
|
||||
removed_inode.nlink -= 2; // both the parent entry and '.'
|
||||
removed_inode_info.nlink -= 2; // both the parent entry and '.'
|
||||
}
|
||||
|
||||
if removed_inode.nlink == 0 && removed_inode.open_handles == 0 {
|
||||
self.filesystem.files.remove(&removed_entry.inode);
|
||||
if removed_inode_info.nlink == 0 && removed_inode_info.open_handles == 0 {
|
||||
self.filesystem.files.remove(&removed_inode);
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn open_existing(&mut self, path: &[u8], flags: usize, uid: u32, gid: u32) -> Result<Handle> {
|
||||
fn open_existing(&mut self, path: &str, flags: usize, uid: u32, gid: u32) -> Result<Inode> {
|
||||
let inode = self.filesystem.resolve(path, uid, gid)?;
|
||||
let file = self
|
||||
.filesystem
|
||||
@@ -151,7 +129,6 @@ impl Scheme {
|
||||
let current_perm = current_perm(file, uid, gid);
|
||||
check_permissions(flags, current_perm)?;
|
||||
|
||||
let opened_as_read = flags & O_ACCMODE == O_RDONLY || flags & O_ACCMODE == O_RDWR;
|
||||
let opened_as_write = flags & O_ACCMODE == O_WRONLY || flags & O_ACCMODE == O_RDWR;
|
||||
|
||||
if flags & O_TRUNC == O_TRUNC && opened_as_write {
|
||||
@@ -166,24 +143,19 @@ impl Scheme {
|
||||
|
||||
file.open_handles += 1;
|
||||
|
||||
Ok(Handle {
|
||||
inode,
|
||||
opened_as_read,
|
||||
opened_as_write,
|
||||
current_perm,
|
||||
})
|
||||
Ok(Inode(inode))
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeMut for Scheme {
|
||||
fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result<OpenResult> {
|
||||
let exists = self.filesystem.resolve(path.as_bytes(), 0, 0).is_ok();
|
||||
let exists = self.filesystem.resolve(path, 0, 0).is_ok();
|
||||
if flags & O_CREAT != 0 && flags & O_EXCL != 0 && exists {
|
||||
return Err(Error::new(EEXIST));
|
||||
}
|
||||
|
||||
let handle = if flags & O_CREAT != 0 && exists {
|
||||
self.open_existing(path.as_bytes(), flags, ctx.uid, ctx.gid)?
|
||||
let inode = if flags & O_CREAT != 0 && exists {
|
||||
self.open_existing(path, flags, ctx.uid, ctx.gid)?.0
|
||||
} else if flags & O_CREAT != 0 {
|
||||
if flags & O_STAT != 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
@@ -191,7 +163,7 @@ impl SchemeMut for Scheme {
|
||||
|
||||
let (parent_dir_inode, new_name) =
|
||||
self.filesystem
|
||||
.resolve_except_last(path.as_bytes(), ctx.uid, ctx.gid)?;
|
||||
.resolve_except_last(path, ctx.uid, ctx.gid)?;
|
||||
let new_name = new_name.ok_or(Error::new(EINVAL))?; // cannot mkdir /
|
||||
|
||||
let current_time = filesystem::current_time();
|
||||
@@ -215,10 +187,9 @@ impl SchemeMut for Scheme {
|
||||
mtime: current_time,
|
||||
gid: ctx.gid,
|
||||
uid: ctx.uid,
|
||||
ino: new_inode_number,
|
||||
mode,
|
||||
nlink: 2, // parent entry, "."
|
||||
data: FileData::Directory(Vec::new()),
|
||||
data: FileData::Directory(IndexMap::new()),
|
||||
open_handles: 1,
|
||||
}
|
||||
} else {
|
||||
@@ -236,7 +207,6 @@ impl SchemeMut for Scheme {
|
||||
mtime: current_time,
|
||||
gid: ctx.gid,
|
||||
uid: ctx.uid,
|
||||
ino: new_inode_number,
|
||||
mode,
|
||||
nlink: 1,
|
||||
data: FileData::File(Vec::new()),
|
||||
@@ -255,63 +225,38 @@ impl SchemeMut for Scheme {
|
||||
.ok_or(Error::new(EIO))?;
|
||||
match parent_file.data {
|
||||
FileData::File(_) => return Err(Error::new(EIO)),
|
||||
FileData::Directory(ref mut entries) => entries.push(DirEntry {
|
||||
name: new_name.to_owned(),
|
||||
inode: new_inode_number,
|
||||
}),
|
||||
FileData::Directory(ref mut entries) => {
|
||||
entries.insert(new_name.to_owned(), Inode(new_inode_number));
|
||||
}
|
||||
}
|
||||
|
||||
Handle {
|
||||
inode: new_inode_number,
|
||||
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,
|
||||
}
|
||||
new_inode_number
|
||||
} else {
|
||||
self.open_existing(path.as_bytes(), flags, ctx.uid, ctx.gid)?
|
||||
self.open_existing(path, flags, ctx.uid, ctx.gid)?.0
|
||||
};
|
||||
|
||||
let fd = self.next_fd;
|
||||
self.next_fd += 1;
|
||||
|
||||
self.handles.insert(fd, handle);
|
||||
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: fd,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
})
|
||||
Ok(OpenResult::ThisScheme { number: inode, flags: NewFdFlags::POSITIONED })
|
||||
}
|
||||
fn rmdir(&mut self, path: &str, uid: u32, gid: u32) -> Result<usize> {
|
||||
self.remove_dentry(path.as_bytes(), uid, gid, true)
|
||||
self.remove_dentry(path, uid, gid, true)
|
||||
}
|
||||
fn unlink(&mut self, path: &str, uid: u32, gid: u32) -> Result<usize> {
|
||||
self.remove_dentry(path.as_bytes(), uid, gid, false)
|
||||
self.remove_dentry(path, uid, gid, false)
|
||||
}
|
||||
fn dup(&mut self, old_fd: usize, _buf: &[u8]) -> Result<usize> {
|
||||
let handle = self
|
||||
.handles
|
||||
.get_mut(&old_fd)
|
||||
.ok_or(Error::new(EBADF))?
|
||||
.clone();
|
||||
|
||||
let fd = self.next_fd;
|
||||
self.next_fd += 1;
|
||||
|
||||
self.handles.insert(fd, handle);
|
||||
Ok(fd)
|
||||
fn dup(&mut self, old_inode: usize, _buf: &[u8]) -> Result<usize> {
|
||||
Ok(old_inode)
|
||||
}
|
||||
fn read(&mut self, fd: usize, buf: &mut [u8], offset: u64, _fcntl_flags: u32) -> Result<usize> {
|
||||
let handle = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?;
|
||||
fn read(&mut self, inode: usize, buf: &mut [u8], offset: u64, fcntl_flags: u32) -> Result<usize> {
|
||||
let Ok(offset) = usize::try_from(offset) else {
|
||||
return Ok(0);
|
||||
};
|
||||
|
||||
let file = self
|
||||
.filesystem
|
||||
.files
|
||||
.get_mut(&handle.inode)
|
||||
.get_mut(&inode)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
|
||||
if !handle.opened_as_read {
|
||||
if !matches!((fcntl_flags as usize) & O_ACCMODE, O_RDONLY | O_RDWR) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
@@ -320,64 +265,47 @@ impl SchemeMut for Scheme {
|
||||
if file.mode & MODE_TYPE == MODE_DIR {
|
||||
return Err(Error::new(EBADFD));
|
||||
}
|
||||
|
||||
if offset >= bytes.len() {
|
||||
return Ok(0);
|
||||
}
|
||||
let src = buf.get(..offset).unwrap_or(&[]);
|
||||
let to_copy = src.len().min(buf.len());
|
||||
buf[..to_copy].copy_from_slice(&bytes[offset..][..to_copy]);
|
||||
Ok(to_copy)
|
||||
}
|
||||
FileData::Directory(ref entries) => {
|
||||
if file.mode & MODE_TYPE != MODE_DIR {
|
||||
return Err(Error::new(EBADFD));
|
||||
}
|
||||
// directories require the execute permission to be listed
|
||||
if handle.current_perm & 0o1 == 0 {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
let mut bytes_to_skip = offset;
|
||||
let mut bytes_left_to_read = buf.len();
|
||||
let mut bytes_read = 0;
|
||||
|
||||
for DirEntry {
|
||||
name: entry_bytes, ..
|
||||
} in entries
|
||||
{
|
||||
// skip the whole entry if it fits
|
||||
if bytes_to_skip >= entry_bytes.len() {
|
||||
bytes_to_skip -= entry_bytes.len();
|
||||
continue;
|
||||
}
|
||||
|
||||
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 - 1];
|
||||
bytes_to_skip -= bytes_to_skip;
|
||||
|
||||
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;
|
||||
}
|
||||
Ok(bytes_read)
|
||||
let src_bytes = bytes.get(offset..).unwrap_or(&[]);
|
||||
let bytes_to_read = src_bytes.len().min(buf.len());
|
||||
buf[..bytes_to_read].copy_from_slice(&src_bytes[..bytes_to_read]);
|
||||
Ok(bytes_to_read)
|
||||
}
|
||||
FileData::Directory(_) => return Err(Error::new(EISDIR)),
|
||||
}
|
||||
}
|
||||
fn write(&mut self, fd: usize, buf: &[u8], offset: u64, _fcntl_flags: u32) -> Result<usize> {
|
||||
let handle = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?;
|
||||
let Ok(offset) = usize::try_from(offset) else {
|
||||
return Ok(0);
|
||||
fn getdents<'buf>(&mut self, inode: usize, mut buf: DirentBuf<&'buf mut [u8]>, opaque_offset: u64) -> Result<DirentBuf<&'buf mut [u8]>> {
|
||||
let Ok(offset) = usize::try_from(opaque_offset) else {
|
||||
return Ok(buf);
|
||||
};
|
||||
let file = self
|
||||
.filesystem
|
||||
.files
|
||||
.get_mut(&handle.inode)
|
||||
.get_mut(&inode)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
|
||||
let FileData::Directory(ref dir) = file.data else {
|
||||
return Err(Error::new(ENOTDIR));
|
||||
};
|
||||
|
||||
for (i, (dent_name, Inode(dent_inode))) in dir.iter().enumerate().skip(offset) {
|
||||
buf.entry(DirEntry {
|
||||
inode: *dent_inode as u64,
|
||||
name: dent_name,
|
||||
kind: DirentKind::Unspecified,
|
||||
next_opaque_id: i as u64 + 1,
|
||||
})?;
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
fn write(&mut self, inode: usize, buf: &[u8], offset: u64, _fcntl_flags: u32) -> Result<usize> {
|
||||
let Ok(offset) = usize::try_from(offset) else {
|
||||
return Ok(0);
|
||||
};
|
||||
|
||||
let file = self
|
||||
.filesystem
|
||||
.files
|
||||
.get_mut(&inode)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
|
||||
if let &mut FileData::File(ref mut bytes) = &mut file.data {
|
||||
@@ -399,25 +327,11 @@ impl SchemeMut for Scheme {
|
||||
Err(Error::new(EISDIR))
|
||||
}
|
||||
}
|
||||
fn fsize(&mut self, fd: usize) -> Result<u64> {
|
||||
let handle = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?;
|
||||
fn fchmod(&mut self, inode: usize, mode: u16) -> Result<usize> {
|
||||
let file = self
|
||||
.filesystem
|
||||
.files
|
||||
.get_mut(&handle.inode)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
|
||||
file.data
|
||||
.size()
|
||||
.try_into()
|
||||
.map_err(|_| Error::new(EOVERFLOW))
|
||||
}
|
||||
fn fchmod(&mut self, fd: usize, mode: u16) -> Result<usize> {
|
||||
let handle = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?;
|
||||
let file = self
|
||||
.filesystem
|
||||
.files
|
||||
.get_mut(&handle.inode)
|
||||
.get_mut(&inode)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
|
||||
let cur_type = file.mode & MODE_TYPE;
|
||||
@@ -432,12 +346,11 @@ impl SchemeMut for Scheme {
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
fn fchown(&mut self, fd: usize, uid: u32, gid: u32) -> Result<usize> {
|
||||
let handle = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?;
|
||||
fn fchown(&mut self, inode: usize, uid: u32, gid: u32) -> Result<usize> {
|
||||
let file = self
|
||||
.filesystem
|
||||
.files
|
||||
.get_mut(&handle.inode)
|
||||
.get_mut(&inode)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
|
||||
file.uid = uid;
|
||||
@@ -445,52 +358,31 @@ impl SchemeMut for Scheme {
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
fn fcntl(&mut self, fd: usize, _cmd: usize, _arg: usize) -> Result<usize> {
|
||||
if !self.handles.contains_key(&fd) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
fn fcntl(&mut self, _inode: usize, _cmd: usize, _arg: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
fn fevent(&mut self, fd: usize, _flags: EventFlags) -> Result<EventFlags> {
|
||||
if !self.handles.contains_key(&fd) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
fn fevent(&mut self, _inode: usize, _flags: EventFlags) -> Result<EventFlags> {
|
||||
// TODO?
|
||||
Err(Error::new(ENOSYS))
|
||||
}
|
||||
fn mmap_prep(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
_offset: u64,
|
||||
_size: usize,
|
||||
_flags: syscall::MapFlags,
|
||||
) -> Result<usize> {
|
||||
if !self.handles.contains_key(&fd) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
fn mmap_prep(&mut self, _inode: usize, _offset: u64, _size: usize, _flags: syscall::MapFlags) -> Result<usize> {
|
||||
// TODO
|
||||
Err(Error::new(ENOSYS))
|
||||
}
|
||||
fn fpath(&mut self, fd: usize, _buf: &mut [u8]) -> Result<usize> {
|
||||
if !self.handles.contains_key(&fd) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
fn fpath(&mut self, _inode: usize, _buf: &mut [u8]) -> Result<usize> {
|
||||
// TODO
|
||||
Err(Error::new(ENOSYS))
|
||||
}
|
||||
fn frename(&mut self, fd: usize, _path: &str, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
if !self.handles.contains_key(&fd) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
fn frename(&mut self, _inode: usize, _path: &str, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
// TODO
|
||||
Err(Error::new(ENOSYS))
|
||||
}
|
||||
fn fstat(&mut self, fd: usize, stat: &mut Stat) -> Result<usize> {
|
||||
let handle = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?;
|
||||
fn fstat(&mut self, inode: usize, stat: &mut Stat) -> Result<usize> {
|
||||
let block_size = self.filesystem.block_size();
|
||||
let file = self
|
||||
.filesystem
|
||||
.files
|
||||
.get_mut(&handle.inode)
|
||||
.get_mut(&inode)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
|
||||
let size = file.data.size().try_into().or(Err(Error::new(EOVERFLOW)))?;
|
||||
@@ -499,13 +391,13 @@ impl SchemeMut for Scheme {
|
||||
st_mode: file.mode,
|
||||
st_uid: file.uid,
|
||||
st_gid: file.gid,
|
||||
st_ino: handle.inode.try_into().or(Err(Error::new(EOVERFLOW)))?,
|
||||
st_ino: inode.try_into().map_err(|_| Error::new(EOVERFLOW))?,
|
||||
st_nlink: file.nlink.try_into().or(Err(Error::new(EOVERFLOW)))?,
|
||||
st_dev: 0,
|
||||
|
||||
st_size: size,
|
||||
st_blksize: block_size,
|
||||
st_blocks: div_round_up(size, u64::from(block_size)),
|
||||
st_blocks: size.next_multiple_of(u64::from(block_size)),
|
||||
|
||||
st_atime: file
|
||||
.atime
|
||||
@@ -543,10 +435,7 @@ impl SchemeMut for Scheme {
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
fn fstatvfs(&mut self, fd: usize, stat: &mut StatVfs) -> Result<usize> {
|
||||
if !self.handles.contains_key(&fd) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
fn fstatvfs(&mut self, _inode: usize, stat: &mut StatVfs) -> Result<usize> {
|
||||
let abi_stat = libredox::call::fstatvfs(self.filesystem.memory_file.as_raw_fd() as usize)?;
|
||||
// TODO: From impl
|
||||
*stat = StatVfs {
|
||||
@@ -558,18 +447,14 @@ impl SchemeMut for Scheme {
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
fn fsync(&mut self, fd: usize) -> Result<usize> {
|
||||
if !self.handles.contains_key(&fd) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
fn fsync(&mut self, _inode: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
fn ftruncate(&mut self, fd: usize, size: usize) -> Result<usize> {
|
||||
let handle = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?;
|
||||
fn ftruncate(&mut self, inode: usize, size: usize) -> Result<usize> {
|
||||
let file = self
|
||||
.filesystem
|
||||
.files
|
||||
.get_mut(&handle.inode)
|
||||
.get_mut(&inode)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
|
||||
if file.mode & MODE_TYPE == MODE_DIR {
|
||||
@@ -589,12 +474,11 @@ impl SchemeMut for Scheme {
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
fn futimens(&mut self, fd: usize, times: &[TimeSpec]) -> Result<usize> {
|
||||
let handle = self.handles.get_mut(&fd).ok_or(Error::new(EBADF))?;
|
||||
fn futimens(&mut self, inode: usize, times: &[TimeSpec]) -> Result<usize> {
|
||||
let file = self
|
||||
.filesystem
|
||||
.files
|
||||
.get_mut(&handle.inode)
|
||||
.get_mut(&inode)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
|
||||
let new_atime = *times.get(0).ok_or(Error::new(EINVAL))?;
|
||||
@@ -605,32 +489,21 @@ impl SchemeMut for Scheme {
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
fn close(&mut self, fd: usize) -> Result<usize> {
|
||||
let inode_num = self.handles.remove(&fd).ok_or(Error::new(EBADF))?.inode;
|
||||
let inode = self
|
||||
fn close(&mut self, inode: usize) -> Result<usize> {
|
||||
let inode_info = self
|
||||
.filesystem
|
||||
.files
|
||||
.get_mut(&inode_num)
|
||||
.get_mut(&inode)
|
||||
.ok_or(Error::new(EIO))?;
|
||||
|
||||
inode.open_handles -= 1;
|
||||
inode_info.open_handles -= 1;
|
||||
|
||||
if inode.nlink == 0 && inode.open_handles == 0 {
|
||||
self.filesystem.files.remove(&inode_num);
|
||||
if inode_info.nlink == 0 && inode_info.open_handles == 0 {
|
||||
self.filesystem.files.remove(&inode);
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
fn div_round_up<T>(numer: T, denom: T) -> T
|
||||
where
|
||||
T: Copy
|
||||
+ ops::Add<T, Output = T>
|
||||
+ ops::Sub<T, Output = T>
|
||||
+ ops::Div<T, Output = T>
|
||||
+ From<u8>,
|
||||
{
|
||||
(numer + (denom - T::from(1u8))) / denom
|
||||
}
|
||||
pub fn current_perm(file: &crate::filesystem::File, uid: u32, gid: u32) -> u8 {
|
||||
let perm = file.mode & MODE_PERM;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user