feat: Add UDS bind and connect integration

This commit is contained in:
Ibuki Omatsu
2025-07-22 00:32:18 +00:00
committed by Jeremy Soller
parent dc25344b68
commit 63217aa69a
6 changed files with 198 additions and 33 deletions
Generated
+7 -7
View File
@@ -303,9 +303,9 @@ checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "libredox"
version = "0.1.4"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638"
checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0"
dependencies = [
"bitflags",
"libc",
@@ -417,9 +417,9 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717"
[[package]]
name = "redox-scheme"
version = "0.6.2"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c00025a04f76fdcf72c15f10c7a12d9f2fdde93e539be9a57d5d632c4158a9e"
checksum = "4da6a0251965958189cdfd5ebb66f99754db4aa165394300aa2b958525d94b64"
dependencies = [
"libredox",
"redox_syscall",
@@ -427,9 +427,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.13"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec"
dependencies = [
"bitflags",
]
@@ -442,7 +442,7 @@ checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
[[package]]
name = "redoxfs"
version = "0.7.0"
version = "0.7.1"
dependencies = [
"aes",
"argon2",
+5 -5
View File
@@ -2,7 +2,7 @@
name = "redoxfs"
description = "The Redox Filesystem"
repository = "https://gitlab.redox-os.org/redox-os/redoxfs"
version = "0.7.0"
version = "0.7.1"
license-file = "LICENSE"
readme = "README.md"
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
@@ -38,12 +38,12 @@ endian-num = "0.1"
env_logger = { version = "0.11", optional = true }
getrandom = { version = "0.2.5", optional = true }
libc = "0.2"
libredox = { version = "0.1.3", optional = true }
log = { version = "0.4.14", default-features = false, optional = true}
libredox = { version = "0.1.6", features = ["call"], optional = true }
log = { version = "0.4.14", default-features = false, optional = true }
range-tree = { version = "0.1", optional = true }
redox-path = "0.3.0"
redox-scheme = { version = "0.6.2", optional = true }
redox_syscall = { version = "0.5.13" }
redox-scheme = { version = "0.7.0", optional = true }
redox_syscall = { version = "0.5.15", optional = true }
seahash = { version = "4.1.0", default-features = false }
termion = { version = "4", optional = true }
uuid = { version = "1.4", default-features = false }
+18 -8
View File
@@ -1,4 +1,4 @@
use redox_scheme::{RequestKind, SignalBehavior, Socket};
use redox_scheme::{scheme::SchemeSync, RequestKind, Response, SignalBehavior, Socket};
use std::io;
use std::path::Path;
use std::sync::atomic::Ordering;
@@ -22,17 +22,27 @@ where
let mounted_path = format!("/scheme/{}", mountpoint.display());
let res = callback(Path::new(&mounted_path));
let mut scheme = FileScheme::new(format!("{}", mountpoint.display()), filesystem);
let mut scheme = FileScheme::new(format!("{}", mountpoint.display()), filesystem, &socket);
while IS_UMT.load(Ordering::SeqCst) == 0 {
let req = match socket.next_request(SignalBehavior::Restart)? {
None => break,
Some(req) => {
if let RequestKind::Call(r) = req.kind() {
r
} else {
// TODO: Redoxfs does not yet support asynchronous file IO. It might still make
// sense to implement cancellation for huge buffers, e.g. dd bs=1G
continue;
match req.kind() {
RequestKind::Call(r) => r,
RequestKind::SendFd(sendfd_request) => {
let result = scheme.on_sendfd(&sendfd_request);
let response = Response::new(result, sendfd_request);
if !socket.write_response(response, SignalBehavior::Restart)? {
break;
}
continue;
}
_ => {
// TODO: Redoxfs does not yet support asynchronous file IO. It might still make
// sense to implement cancellation for huge buffers, e.g. dd bs=1G
continue;
}
}
}
};
+153 -10
View File
@@ -3,18 +3,20 @@ use std::str;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
use redox_scheme::{scheme::SchemeSync, CallerCtx, OpenResult};
use redox_scheme::{scheme::SchemeSync, CallerCtx, OpenResult, SendFdRequest, Socket};
use syscall::data::{Stat, StatVfs, TimeSpec};
use syscall::dirent::DirentBuf;
use syscall::error::{
Error, Result, EACCES, EBADF, EBUSY, EEXIST, EINVAL, EISDIR, ELOOP, ENOENT, ENOTDIR, ENOTEMPTY,
EPERM, EXDEV,
EOPNOTSUPP, EPERM, EXDEV,
};
use syscall::flag::{
EventFlags, MapFlags, O_ACCMODE, O_CREAT, O_DIRECTORY, O_EXCL, O_NOFOLLOW, O_RDONLY, O_RDWR,
O_STAT, O_SYMLINK, O_TRUNC, O_WRONLY,
};
use syscall::schemev2::NewFdFlags;
use syscall::FobtainFdFlags;
use syscall::FsCall;
use syscall::MunmapFlags;
use redox_path::{
@@ -26,22 +28,28 @@ use crate::{Disk, FileSystem, Node, Transaction, TreeData, TreePtr, BLOCK_SIZE};
use super::resource::{DirResource, Entry, FileResource, Resource};
pub struct FileScheme<D: Disk> {
pub struct FileScheme<'sock, D: Disk> {
name: String,
pub(crate) fs: FileSystem<D>,
socket: &'sock Socket,
next_id: AtomicUsize,
files: BTreeMap<usize, Box<dyn Resource<D>>>,
fmap: super::resource::Fmaps,
// Map of file id to other scheme's file descriptor.
other_scheme_fd_map: BTreeMap<u32, usize>,
}
impl<D: Disk> FileScheme<D> {
pub fn new(name: String, fs: FileSystem<D>) -> FileScheme<D> {
impl<'sock, D: Disk> FileScheme<'sock, D> {
pub fn new(name: String, fs: FileSystem<D>, socket: &'sock Socket) -> FileScheme<'sock, D> {
FileScheme {
name,
fs,
socket,
next_id: AtomicUsize::new(1),
files: BTreeMap::new(),
fmap: BTreeMap::new(),
other_scheme_fd_map: BTreeMap::new(),
}
}
@@ -110,6 +118,17 @@ impl<D: Disk> FileScheme<D> {
Err(Error::new(ELOOP))
}
fn handle_connect(&mut self, id: usize, payload: &mut [u8]) -> Result<usize> {
let resource = self.files.get(&id).ok_or(Error::new(EBADF))?;
let inode_id = resource.node_ptr().id();
let target_fd = self
.other_scheme_fd_map
.get(&inode_id)
.ok_or(Error::new(EBADF))?;
let len = libredox::call::get_socket_token(*target_fd, payload)?;
return Ok(len);
}
fn open_internal(
&mut self,
start_ptr: TreePtr<Node>,
@@ -383,7 +402,7 @@ fn dirname(path: &str) -> Option<String> {
canonicalize_using_cwd(Some(path), "..")
}
impl<D: Disk> SchemeSync for FileScheme<D> {
impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> {
fn open(&mut self, url: &str, flags: usize, ctx: &CallerCtx) -> Result<OpenResult> {
self.open_internal(TreePtr::root(), url, flags, ctx)
}
@@ -454,7 +473,7 @@ impl<D: Disk> SchemeSync for FileScheme<D> {
// println!("Unlink '{}'", path);
let scheme_name = &self.name;
self.fs.tx(|tx| {
let unlink_result = self.fs.tx(|tx| {
let mut nodes = Vec::new();
let Some((child, child_name)) =
@@ -480,15 +499,23 @@ impl<D: Disk> SchemeSync for FileScheme<D> {
if child.data().is_symlink() {
tx.remove_node(parent.ptr(), &child_name, Node::MODE_SYMLINK)
.and(Ok(()))
} else if child.data().is_sock() {
tx.remove_node(parent.ptr(), &child_name, Node::MODE_SOCK)
} else {
tx.remove_node(parent.ptr(), &child_name, Node::MODE_FILE)
.and(Ok(()))
}
} else {
Err(Error::new(EISDIR))
}
})
});
let Some(node_id) = unlink_result? else {
return Ok(());
};
if let Some(fd) = self.other_scheme_fd_map.remove(&node_id) {
let _ = syscall::close(fd);
}
Ok(())
}
/* Resource operations */
@@ -910,4 +937,120 @@ impl<D: Disk> SchemeSync for FileScheme<D> {
// TODO: If open_fds reaches zero and there are no hardlinks (directory entries) to any
// particular inode, remove that inode here.
}
fn on_sendfd(&mut self, sendfd_request: &SendFdRequest) -> Result<usize> {
let ctx = sendfd_request.caller();
let uid = ctx.uid;
let gid = ctx.gid;
let parent_resource = self
.files
.get(&sendfd_request.id())
.ok_or(Error::new(EBADF))?;
let mut new_fd = usize::MAX;
if let Err(e) =
sendfd_request.obtain_fd(&self.socket, FobtainFdFlags::empty(), Err(&mut new_fd))
{
return Err(e);
}
let parent_resource_ptr = parent_resource.node_ptr();
let parent_node = self.fs.tx(|tx| tx.read_tree(parent_resource_ptr))?;
if !parent_node.data().is_dir() {
return Err(Error::new(ENOTDIR));
}
if !parent_node.data().permission(uid, gid, Node::MODE_WRITE) {
return Err(Error::new(EACCES));
}
let parent_path = parent_resource.path();
// TODO: Move the PATH_MAX definition to a more appropriate place.
const PATH_MAX: usize = 4096;
let mut url_buf = [0u8; PATH_MAX];
let url_len = syscall::fpath(new_fd, &mut url_buf)?;
let url_str = str::from_utf8(&url_buf[..url_len]).map_err(|_| Error::new(EINVAL))?;
let redox_path = RedoxPath::from_absolute(url_str).ok_or(Error::new(EINVAL))?;
let (_, path) = redox_path.as_parts().ok_or(Error::new(EINVAL))?;
let mut last_part = String::new();
for part in path.as_ref().split('/') {
if !part.is_empty() {
last_part = part.to_string();
}
}
let (resource, node_id): (Box<dyn Resource<D>>, u32) = if !last_part.is_empty() {
let mut stat = Stat::default();
syscall::fstat(new_fd, &mut stat)?;
let mode_type = stat.st_mode & Node::MODE_TYPE;
let flags = 0o777;
let node_ptr = self.fs.tx(|tx| {
if tx.find_node(parent_resource_ptr, &last_part).is_ok() {
// If the file already exists, we cannot create it again
return Err(Error::new(EEXIST));
}
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let mut node = tx.create_node(
parent_resource_ptr,
&last_part,
mode_type | (flags as u16 & Node::MODE_PERM),
ctime.as_secs(),
ctime.subsec_nanos(),
)?;
let node_ptr = node.ptr();
if node.data().uid() != uid || node.data().gid() != gid {
node.data_mut().set_uid(uid);
node.data_mut().set_gid(gid);
tx.sync_tree(node)?;
}
Ok(node_ptr)
})?;
let file_path = format!("{parent_path}/{last_part}");
let node_id = node_ptr.id();
(
Box::new(FileResource::new(
file_path,
Some(parent_resource_ptr),
node_ptr,
flags,
uid,
)),
node_id,
)
} else {
return Err(Error::new(EINVAL));
};
self.fmap
.entry(resource.node_ptr().id())
.or_insert_with(Default::default)
.open_fds += 1;
let id = self.next_id.fetch_add(1, Ordering::Relaxed);
self.files.insert(id, resource);
self.other_scheme_fd_map.insert(node_id, new_fd);
Ok(new_fd)
}
fn call(
&mut self,
id: usize,
payload: &mut [u8],
metadata: &[u64],
ctx: &CallerCtx,
) -> Result<usize> {
let Some(verb) = FsCall::try_from_raw(metadata[0] as usize) else {
return Err(Error::new(EINVAL));
};
match verb {
FsCall::Connect => self.handle_connect(id, payload),
_ => Err(Error::new(EOPNOTSUPP)),
}
}
}
+5
View File
@@ -190,6 +190,7 @@ impl Node {
pub const MODE_FILE: u16 = 0x8000;
pub const MODE_DIR: u16 = 0x4000;
pub const MODE_SYMLINK: u16 = 0xA000;
pub const MODE_SOCK: u16 = 0xC000;
/// Mask for node permission bits
pub const MODE_PERM: u16 = 0x0FFF;
@@ -310,6 +311,10 @@ impl Node {
self.mode() & Self::MODE_TYPE == Self::MODE_SYMLINK
}
pub fn is_sock(&self) -> bool {
self.mode() & Self::MODE_SOCK == Self::MODE_SOCK
}
/// Tests if UID is the owner of that file, only true when uid=0 or when the UID stored in metadata is equal to the UID you supply
pub fn owner(&self, uid: u32) -> bool {
uid == 0 || self.uid() == uid
+10 -3
View File
@@ -993,7 +993,12 @@ impl<'a, D: Disk> Transaction<'a, D> {
Ok(None)
}
pub fn remove_node(&mut self, parent_ptr: TreePtr<Node>, name: &str, mode: u16) -> Result<()> {
pub fn remove_node(
&mut self,
parent_ptr: TreePtr<Node>,
name: &str,
mode: u16,
) -> Result<Option<u32>> {
#[cfg(feature = "log")]
log::debug!(
"REMOVE_NODE: name: {}, mode: {:x}, parent_ptr: {:?}",
@@ -1052,6 +1057,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
}
let links = node.data().links();
let node_id = node.id();
let remove_node = if links > 1 {
node.data_mut().set_links(links - 1);
false
@@ -1102,12 +1108,13 @@ impl<'a, D: Disk> Transaction<'a, D> {
unsafe {
self.deallocate(node_addr);
}
Ok(Some(node_id))
} else {
// Sync both parent and node at the same time
self.sync_trees(&[parent, node])?;
Ok(None)
}
Ok(())
}
fn remove_node_inner(