1#![no_std]
2
3extern crate alloc;
4use alloc::format;
5use syscall::dirent::DirentBuf;
6
7use core::str;
8
9use libredox::flag;
10use syscall::schemev2::{Cqe, CqeOpcode, NewFdFlags, Opcode, Sqe};
11use syscall::{
12 Error, EventFlags, FobtainFdFlags, MapFlags, MunmapFlags, Packet, Result, SendFdFlags, Stat,
13 StatVfs, TimeSpec, EBADF, EINTR, EINVAL, ENOENT, EOPNOTSUPP,
14};
15
16pub use self::scheme::Scheme;
17pub use self::scheme_block::SchemeBlock;
18
19mod scheme;
20mod scheme_block;
21
22pub struct CallerCtx {
23 pub pid: usize,
24 pub uid: u32,
25 pub gid: u32,
26}
27
28pub enum OpenResult {
29 ThisScheme { number: usize, flags: NewFdFlags },
30 OtherScheme { fd: usize },
31}
32
33pub(crate) fn convert_to_this_scheme(r: Result<usize>) -> Result<OpenResult> {
35 r.map(|number| OpenResult::ThisScheme {
36 number,
37 flags: NewFdFlags::empty(),
38 })
39}
40pub(crate) fn convert_to_this_scheme_block(r: Result<Option<usize>>) -> Result<Option<OpenResult>> {
41 r.map(|o| {
42 o.map(|number| OpenResult::ThisScheme {
43 number,
44 flags: NewFdFlags::empty(),
45 })
46 })
47}
48
49impl CallerCtx {
50 pub fn from_packet(packet: &Packet) -> Self {
51 Self {
52 pid: packet.pid,
53 uid: packet.uid,
54 gid: packet.gid,
55 }
56 }
57}
58
59use core::mem::size_of;
60
61#[repr(transparent)]
62#[derive(Clone, Copy, Debug, Default)]
63pub struct Request {
64 sqe: Sqe,
65}
66
67#[derive(Clone, Copy, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)]
68pub struct Id(u32);
69
70#[derive(Debug)]
71pub struct CancellationRequest {
72 pub id: Id,
73}
74
75#[repr(transparent)]
76#[derive(Clone, Copy, Debug)]
77pub struct CallRequest {
78 inner: Request,
79}
80
81pub struct SendFdRequest {
82 inner: Request,
83}
84
85pub enum RequestKind {
86 Call(CallRequest),
87 Cancellation(CancellationRequest),
88 SendFd(SendFdRequest),
89 MsyncMsg,
90 MunmapMsg,
91 MmapMsg,
92 OnClose { id: usize },
93}
94
95impl CallRequest {
96 #[inline]
97 pub fn request(&self) -> Request {
98 self.inner
99 }
100}
101
102impl CallRequest {
103 pub fn handle_scheme(self, scheme: &mut impl Scheme) -> Response {
104 let Some(opcode) = Opcode::try_from_raw(self.inner.sqe.opcode) else {
105 return Response::new(&self, Err(Error::new(EOPNOTSUPP)));
106 };
107 let args = self.inner.sqe.args;
108
109 let hack_uid = args[5] as u32;
110 let hack_gid = (args[5] >> 32) as u32;
111 let ctx = CallerCtx {
112 pid: self.inner.sqe.caller as usize,
113 uid: hack_uid,
114 gid: hack_gid,
115 };
116
117 let [a, b, c, d, e, _f] = args.map(|a| a as usize);
118 let result = unsafe {
119 use core::{slice, str};
120 match opcode {
121 Opcode::Open => match scheme.xopen(
122 str::from_utf8_unchecked(slice::from_raw_parts(a as *const u8, b)),
123 c,
124 &ctx,
125 ) {
126 Ok(OpenResult::ThisScheme { number, flags }) => {
127 return Response(Cqe {
128 tag: self.inner.sqe.tag,
129 extra_raw: [flags.bits(), 0, 0],
130 flags: CqeOpcode::RespondRegular as u8,
131 result: number as u64,
132 })
133 }
134 Err(err) => Err(err),
135 Ok(OpenResult::OtherScheme { fd }) => {
136 return Response(Cqe {
137 tag: self.inner.sqe.tag,
138 extra_raw: [0_u8; 3],
139 flags: CqeOpcode::RespondWithFd as u8,
140 result: fd as u64,
141 })
142 }
143 },
144 Opcode::Rmdir => scheme.rmdir(
145 str::from_utf8_unchecked(slice::from_raw_parts(a as *const u8, b)),
146 hack_uid,
147 hack_gid,
148 ),
149 Opcode::Unlink => scheme.unlink(
150 str::from_utf8_unchecked(slice::from_raw_parts(a as *const u8, b)),
151 hack_uid,
152 hack_gid,
153 ),
154 Opcode::Dup => match scheme.xdup(a, slice::from_raw_parts(b as *const u8, c), &ctx)
155 {
156 Ok(OpenResult::ThisScheme { number, flags }) => {
157 return Response(Cqe {
158 tag: self.inner.sqe.tag,
159 extra_raw: [flags.bits(), 0, 0],
160 flags: CqeOpcode::RespondRegular as u8,
161 result: number as u64,
162 })
163 }
164 Err(err) => Err(err),
165 Ok(OpenResult::OtherScheme { fd }) => {
166 return Response(Cqe {
167 tag: self.inner.sqe.tag,
168 extra_raw: [0_u8; 3],
169 flags: CqeOpcode::RespondWithFd as u8,
170 result: fd as u64,
171 })
172 }
173 },
174 Opcode::Read => scheme.read(
175 a,
176 slice::from_raw_parts_mut(b as *mut u8, c),
177 args[3],
178 args[4] as u32,
179 ),
180 Opcode::Write => scheme.write(
181 a,
182 slice::from_raw_parts(b as *const u8, c),
183 args[3],
184 args[4] as u32,
185 ),
186 Opcode::Getdents => {
187 DirentBuf::new(slice::from_raw_parts_mut(b as *mut u8, c), d as u16)
188 .map_or(Err(Error::new(EINVAL)), |buf| {
189 scheme.getdents(a, buf, e as u64)
190 })
191 .map(|b| b.finalize())
192 }
193
194 Opcode::Fsize => scheme.fsize(a).map(|o| o as usize),
196 Opcode::Fchmod => scheme.fchmod(a, b as u16),
197 Opcode::Fchown => scheme.fchown(a, b as u32, c as u32),
198 Opcode::Fcntl => scheme.fcntl(a, b, c),
199 Opcode::Fevent => scheme
200 .fevent(a, EventFlags::from_bits_retain(b))
201 .map(|fl| fl.bits()),
202 Opcode::Fpath => scheme.fpath(a, slice::from_raw_parts_mut(b as *mut u8, c)),
203 Opcode::Frename => scheme.frename(
204 a,
205 str::from_utf8_unchecked(slice::from_raw_parts(b as *const u8, c)),
206 hack_uid,
207 hack_gid,
208 ),
209 Opcode::Fstat => {
210 assert!(c >= size_of::<Stat>());
211 scheme.fstat(a, &mut *(b as *mut Stat))
212 }
213 Opcode::Fstatvfs => {
214 assert!(c >= size_of::<StatVfs>());
215 scheme.fstatvfs(a, &mut *(b as *mut StatVfs))
216 }
217 Opcode::Fsync => scheme.fsync(a),
218 Opcode::Ftruncate => scheme.ftruncate(a, b),
219 Opcode::Futimens => {
220 assert!(c <= 2 * core::mem::size_of::<TimeSpec>());
221 scheme.futimens(
222 a,
223 slice::from_raw_parts(
224 b as *const TimeSpec,
225 c / core::mem::size_of::<TimeSpec>(),
226 ),
227 )
228 }
229
230 Opcode::MmapPrep => scheme.mmap_prep(a, args[3], b, MapFlags::from_bits_retain(c)),
231 Opcode::Munmap => scheme.munmap(a, args[3], b, MunmapFlags::from_bits_retain(c)),
232
233 _ => return Response::new(&self, Err(Error::new(EOPNOTSUPP))),
234 }
235 };
236 Response::new(&self, result)
237 }
238 pub fn handle_scheme_block(self, scheme: &mut impl SchemeBlock) -> Option<Response> {
240 let Some(opcode) = Opcode::try_from_raw(self.inner.sqe.opcode) else {
241 return Some(Response::new(&self, Err(Error::new(EOPNOTSUPP))));
242 };
243 let args = self.inner.sqe.args;
244
245 let hack_uid = args[5] as u32;
246 let hack_gid = (args[5] >> 32) as u32;
247 let ctx = CallerCtx {
248 pid: self.inner.sqe.caller as usize,
249 uid: hack_uid,
250 gid: hack_gid,
251 };
252
253 let [a, b, c, d, e, _f] = args.map(|a| a as usize);
254 let result = unsafe {
255 use core::{slice, str};
256 match opcode {
257 Opcode::Open => match scheme
258 .xopen(
259 str::from_utf8_unchecked(slice::from_raw_parts(a as *const u8, b)),
260 c,
261 &ctx,
262 )
263 .transpose()?
264 {
265 Ok(OpenResult::ThisScheme { number, flags }) => {
266 return Some(Response(Cqe {
267 tag: self.inner.sqe.tag,
268 extra_raw: [flags.bits(), 0, 0],
269 flags: CqeOpcode::RespondRegular as u8,
270 result: number as u64,
271 }))
272 }
273 Err(err) => Err(err),
274 Ok(OpenResult::OtherScheme { fd }) => {
275 return Some(Response(Cqe {
276 tag: self.inner.sqe.tag,
277 extra_raw: [0_u8; 3],
278 flags: CqeOpcode::RespondWithFd as u8,
279 result: fd as u64,
280 }))
281 }
282 },
283 Opcode::Rmdir => scheme
284 .rmdir(
285 str::from_utf8_unchecked(slice::from_raw_parts(a as *const u8, b)),
286 hack_uid,
287 hack_gid,
288 )
289 .transpose()?,
290 Opcode::Unlink => scheme
291 .unlink(
292 str::from_utf8_unchecked(slice::from_raw_parts(a as *const u8, b)),
293 hack_uid,
294 hack_gid,
295 )
296 .transpose()?,
297 Opcode::Dup => match scheme
298 .xdup(a, slice::from_raw_parts(b as *const u8, c), &ctx)
299 .transpose()?
300 {
301 Ok(OpenResult::ThisScheme { number, flags }) => {
302 return Some(Response(Cqe {
303 tag: self.inner.sqe.tag,
304 extra_raw: [flags.bits(), 0, 0],
305 flags: CqeOpcode::RespondRegular as u8,
306 result: number as u64,
307 }))
308 }
309 Err(err) => Err(err),
310 Ok(OpenResult::OtherScheme { fd }) => {
311 return Some(Response(Cqe {
312 tag: self.inner.sqe.tag,
313 extra_raw: [0_u8; 3],
314 flags: CqeOpcode::RespondWithFd as u8,
315 result: fd as u64,
316 }))
317 }
318 },
319 Opcode::Read => scheme
320 .read(
321 a,
322 slice::from_raw_parts_mut(b as *mut u8, c),
323 args[3],
324 args[4] as u32,
325 )
326 .transpose()?,
327 Opcode::Write => scheme
328 .write(
329 a,
330 slice::from_raw_parts(b as *const u8, c),
331 args[3],
332 args[4] as u32,
333 )
334 .transpose()?,
335
336 Opcode::Getdents => {
337 DirentBuf::new(slice::from_raw_parts_mut(b as *mut u8, c), d as u16)
338 .map_or(Err(Error::new(EINVAL)), |buf| {
339 scheme.getdents(a, buf, e as u64)
340 })
341 .map(|b| b.map(|b| b.finalize()))
342 .transpose()?
343 }
344
345 Opcode::Fsize => scheme.fsize(a).transpose()?.map(|o| o as usize),
347 Opcode::Fchmod => scheme.fchmod(a, b as u16).transpose()?,
348 Opcode::Fchown => scheme.fchown(a, b as u32, c as u32).transpose()?,
349 Opcode::Fcntl => scheme.fcntl(a, b, c).transpose()?,
350 Opcode::Fevent => scheme
351 .fevent(a, EventFlags::from_bits_retain(b))
352 .transpose()?
353 .map(|fl| fl.bits()),
354 Opcode::Fpath => scheme
355 .fpath(a, slice::from_raw_parts_mut(b as *mut u8, c))
356 .transpose()?,
357 Opcode::Frename => scheme
358 .frename(
359 a,
360 str::from_utf8_unchecked(slice::from_raw_parts(b as *const u8, c)),
361 hack_uid,
362 hack_gid,
363 )
364 .transpose()?,
365 Opcode::Fstat => {
366 assert!(c >= size_of::<Stat>());
367 scheme.fstat(a, &mut *(b as *mut Stat)).transpose()?
368 }
369 Opcode::Fstatvfs => {
370 assert!(c >= size_of::<StatVfs>());
371 scheme.fstatvfs(a, &mut *(b as *mut StatVfs)).transpose()?
372 }
373 Opcode::Fsync => scheme.fsync(a).transpose()?,
374 Opcode::Ftruncate => scheme.ftruncate(a, b).transpose()?,
375 Opcode::Futimens => {
376 assert!(c <= 2 * core::mem::size_of::<TimeSpec>());
377 scheme
378 .futimens(
379 a,
380 slice::from_raw_parts(
381 b as *const TimeSpec,
382 c / core::mem::size_of::<TimeSpec>(),
383 ),
384 )
385 .transpose()?
386 }
387
388 Opcode::MmapPrep => scheme
389 .mmap_prep(a, args[3], b, MapFlags::from_bits_retain(c))
390 .transpose()?,
391 Opcode::Munmap => scheme
392 .munmap(a, args[3], b, MunmapFlags::from_bits_retain(c))
393 .transpose()?,
394
395 _ => return Some(Response::new(&self, Err(Error::new(EOPNOTSUPP)))),
396 }
397 };
398 Some(Response::new(&self, result))
399 }
400}
401
402impl SendFdRequest {
403 #[inline]
404 pub fn request(&self) -> Request {
405 self.inner
406 }
407
408 pub fn id(&self) -> usize {
409 self.inner.sqe.args[0] as usize
410 }
411
412 pub fn flags(&self) -> SendFdFlags {
413 SendFdFlags::from_bits_retain(self.inner.sqe.args[1] as usize)
414 }
415
416 pub fn obtain_fd(
417 &self,
418 socket: &Socket,
419 flags: FobtainFdFlags,
420 dst_fd_or_ptr: Result<usize, &mut usize>,
421 ) -> Result<()> {
422 assert!(!flags.contains(FobtainFdFlags::MANUAL_FD));
423 match dst_fd_or_ptr {
424 Ok(dst_fd) => {
425 socket.inner.write(&Cqe {
426 flags: CqeOpcode::ObtainFd as u8,
427 extra_raw: usize::to_ne_bytes((flags | FobtainFdFlags::MANUAL_FD).bits())[..3]
428 .try_into()
429 .unwrap(),
430 tag: self.inner.request_id().0,
431 result: dst_fd as u64,
432 })?;
433 }
434 Err(ptr) => {
435 socket.inner.write(&Cqe {
436 flags: CqeOpcode::ObtainFd as u8,
437 extra_raw: usize::to_ne_bytes(flags.bits())[..3].try_into().unwrap(),
438 tag: self.inner.request_id().0,
439 result: ptr as *mut usize as u64,
440 })?;
441 }
442 }
443 Ok(())
444 }
445}
446
447impl Request {
448 #[inline]
449 pub fn context_id(&self) -> usize {
450 self.sqe.caller as usize
451 }
452 #[inline]
453 pub fn request_id(&self) -> Id {
454 Id(self.sqe.tag)
455 }
456 pub fn kind(self) -> RequestKind {
457 match Opcode::try_from_raw(self.sqe.opcode) {
458 Some(Opcode::Cancel) => RequestKind::Cancellation(CancellationRequest {
459 id: Id(self.sqe.tag),
460 }),
461 Some(Opcode::Sendfd) => RequestKind::SendFd(SendFdRequest {
462 inner: Request { sqe: self.sqe },
463 }),
464 Some(Opcode::Msync) => RequestKind::MsyncMsg,
465 Some(Opcode::RequestMmap) => RequestKind::MmapMsg,
467 Some(Opcode::CloseMsg) => RequestKind::OnClose {
468 id: self.sqe.args[0] as usize,
469 },
470
471 _ => RequestKind::Call(CallRequest {
472 inner: Request { sqe: self.sqe },
473 }),
474 }
475 }
476}
477
478pub struct Socket {
479 inner: libredox::Fd,
480}
481
482impl Socket {
483 fn create_inner(name: &str, nonblock: bool) -> Result<Self> {
484 let mut flags = flag::O_FSYNC | 0x0020_0000 ;
485
486 if nonblock {
487 flags |= flag::O_NONBLOCK;
488 }
489
490 let fd = libredox::Fd::open(
491 &format!(":{name}"),
492 flag::O_CLOEXEC | flag::O_CREAT | flags,
493 0,
494 )?;
495 Ok(Self { inner: fd })
496 }
497 pub fn create(name: impl AsRef<str>) -> Result<Self> {
498 Self::create_inner(name.as_ref(), false)
499 }
500 pub fn nonblock(name: impl AsRef<str>) -> Result<Self> {
501 Self::create_inner(name.as_ref(), true)
502 }
503 pub fn read_requests(&self, buf: &mut [Request], behavior: SignalBehavior) -> Result<usize> {
504 read_requests(self.inner.raw(), buf, behavior)
505 }
506 pub fn next_request(&self, behavior: SignalBehavior) -> Result<Option<Request>> {
507 let mut buf = [Request::default()];
508 Ok(if self.read_requests(&mut buf, behavior)? > 0 {
509 Some(buf[0])
510 } else {
511 None
512 })
513 }
514 pub fn write_responses(&self, buf: &[Response], behavior: SignalBehavior) -> Result<usize> {
515 write_responses(self.inner.raw(), buf, behavior)
516 }
517 pub fn write_response(&self, resp: Response, behavior: SignalBehavior) -> Result<bool> {
518 Ok(self.write_responses(&[resp], behavior)? > 0)
519 }
520 pub fn post_fevent(&self, id: usize, flags: usize) -> Result<()> {
521 self.inner.write(&Cqe {
522 flags: CqeOpcode::SendFevent as u8,
523 extra_raw: [0_u8; 3],
524 tag: flags as u32,
525 result: id as u64,
526 })?;
527 Ok(())
528 }
529 pub fn inner(&self) -> &libredox::Fd {
530 &self.inner
531 }
532}
533
534#[repr(transparent)]
535#[derive(Clone, Copy, Default)]
536pub struct Response(Cqe);
537
538impl Response {
539 pub fn new(req: &CallRequest, status: Result<usize>) -> Self {
540 Self(Cqe {
541 flags: CqeOpcode::RespondRegular as u8,
542 extra_raw: [0_u8; 3],
543 result: Error::mux(status) as u64,
544 tag: req.inner.sqe.tag,
545 })
546 }
547
548 pub fn for_sendfd(req: &SendFdRequest, status: Result<usize>) -> Self {
549 Self(Cqe {
550 flags: CqeOpcode::RespondRegular as u8,
551 extra_raw: [0_u8; 3],
552 result: Error::mux(status) as u64,
553 tag: req.inner.sqe.tag,
554 })
555 }
556}
557
558pub enum SignalBehavior {
559 Interrupt,
560 Restart,
561}
562
563#[inline]
565pub fn read_requests(
566 socket: usize,
567 buf: &mut [Request],
568 behavior: SignalBehavior,
569) -> Result<usize> {
570 let len = buf.len().checked_mul(size_of::<Request>()).unwrap();
571
572 let bytes_read = loop {
573 match libredox::call::read(socket, unsafe {
574 core::slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), len)
575 }) {
576 Ok(n) => break n,
577 Err(error) if error.errno() == EINTR => match behavior {
578 SignalBehavior::Restart => continue,
579 SignalBehavior::Interrupt => return Err(error.into()),
580 },
581 Err(err) => return Err(err.into()),
582 }
583 };
584
585 debug_assert_eq!(bytes_read % size_of::<Request>(), 0);
586
587 Ok(bytes_read / size_of::<Request>())
588}
589
590#[inline]
591pub fn write_responses(socket: usize, buf: &[Response], behavior: SignalBehavior) -> Result<usize> {
592 let bytes = unsafe {
593 core::slice::from_raw_parts(
594 buf.as_ptr().cast(),
595 buf.len().checked_mul(size_of::<Response>()).unwrap(),
596 )
597 };
598
599 let bytes_written = loop {
600 match libredox::call::write(socket, bytes) {
601 Ok(n) => break n,
602 Err(error) if error.errno() == EINTR => match behavior {
603 SignalBehavior::Restart => continue,
604 SignalBehavior::Interrupt => return Err(error.into()),
605 },
606 Err(err) => return Err(err.into()),
607 }
608 };
609 debug_assert_eq!(bytes_written % size_of::<Response>(), 0);
610 Ok(bytes_written / size_of::<Response>())
611}