diff --git a/drivers/acpid/src/ec.rs b/drivers/acpid/src/ec.rs index c322790af6..3119cd0b16 100644 --- a/drivers/acpid/src/ec.rs +++ b/drivers/acpid/src/ec.rs @@ -202,55 +202,69 @@ impl RegionHandler for Ec { self.write(offset as u8, value) .ok_or(AmlError::MutexAcquireTimeout) // TODO proper error type } - fn read_u16(&self, _region: &OpRegion, _offset: usize) -> Result { - warn!("Got u16 EC read from AML!"); - Err(acpi::aml::AmlError::NoHandlerForRegionAccess( - RegionSpace::EmbeddedControl, - )) // TODO proper error type + fn read_u16(&self, region: &OpRegion, offset: usize) -> Result { + assert_eq!(region.space, RegionSpace::EmbeddedControl); + // EC is 8-bit; compose 16-bit AML reads as little-endian 8-bit EC reads. + // Cross-referenced with Linux drivers/acpi/ec.c: acpi_ec_read() and + // AML acpi_extract_value() which handles the same byte-decomposition. + let lo = self.read_u8(region, offset)? as u16; + let hi = self.read_u8(region, offset + 1)? as u16; + Ok(lo | (hi << 8)) } - fn read_u32(&self, _region: &OpRegion, _offset: usize) -> Result { - warn!("Got u32 EC read from AML!"); - Err(acpi::aml::AmlError::NoHandlerForRegionAccess( - RegionSpace::EmbeddedControl, - )) // TODO proper error type + fn read_u32(&self, region: &OpRegion, offset: usize) -> Result { + assert_eq!(region.space, RegionSpace::EmbeddedControl); + let part = self.read_u16(region, offset)? as u32; + let part2 = self.read_u16(region, offset + 2)? as u32; + Ok(part | (part2 << 16)) } - fn read_u64(&self, _region: &OpRegion, _offset: usize) -> Result { - warn!("Got u64 EC read from AML!"); - Err(acpi::aml::AmlError::NoHandlerForRegionAccess( - RegionSpace::EmbeddedControl, - )) // TODO proper error type + fn read_u64(&self, region: &OpRegion, offset: usize) -> Result { + assert_eq!(region.space, RegionSpace::EmbeddedControl); + let part = self.read_u32(region, offset)? as u64; + let part2 = self.read_u32(region, offset + 4)? as u64; + Ok(part | (part2 << 32)) } fn write_u16( &self, - _region: &OpRegion, - _offset: usize, - _value: u16, + region: &OpRegion, + offset: usize, + value: u16, ) -> Result<(), acpi::aml::AmlError> { - warn!("Got u16 EC write from AML!"); - Err(acpi::aml::AmlError::NoHandlerForRegionAccess( - RegionSpace::EmbeddedControl, - )) // TODO proper error type + assert_eq!(region.space, RegionSpace::EmbeddedControl); + let bytes = value.to_le_bytes(); + self.write_u8(region, offset, bytes[0])?; + self.write_u8(region, offset + 1, bytes[1])?; + Ok(()) } fn write_u32( &self, - _region: &OpRegion, - _offset: usize, - _value: u32, + region: &OpRegion, + offset: usize, + value: u32, ) -> Result<(), acpi::aml::AmlError> { - warn!("Got u32 EC write from AML!"); - Err(acpi::aml::AmlError::NoHandlerForRegionAccess( - RegionSpace::EmbeddedControl, - )) // TODO proper error type + assert_eq!(region.space, RegionSpace::EmbeddedControl); + let bytes = value.to_le_bytes(); + self.write_u8(region, offset, bytes[0])?; + self.write_u8(region, offset + 1, bytes[1])?; + self.write_u8(region, offset + 2, bytes[2])?; + self.write_u8(region, offset + 3, bytes[3])?; + Ok(()) } fn write_u64( &self, - _region: &OpRegion, - _offset: usize, - _value: u64, + region: &OpRegion, + offset: usize, + value: u64, ) -> Result<(), acpi::aml::AmlError> { - warn!("Got u64 EC write from AML!"); - Err(acpi::aml::AmlError::NoHandlerForRegionAccess( - RegionSpace::EmbeddedControl, - )) // TODO proper error type + assert_eq!(region.space, RegionSpace::EmbeddedControl); + let bytes = value.to_le_bytes(); + self.write_u8(region, offset, bytes[0])?; + self.write_u8(region, offset + 1, bytes[1])?; + self.write_u8(region, offset + 2, bytes[2])?; + self.write_u8(region, offset + 3, bytes[3])?; + self.write_u8(region, offset + 4, bytes[4])?; + self.write_u8(region, offset + 5, bytes[5])?; + self.write_u8(region, offset + 6, bytes[6])?; + self.write_u8(region, offset + 7, bytes[7])?; + Ok(()) } } diff --git a/drivers/inputd/src/main.rs b/drivers/inputd/src/main.rs index 261ee21436..b1c13559c3 100644 --- a/drivers/inputd/src/main.rs +++ b/drivers/inputd/src/main.rs @@ -383,7 +383,11 @@ impl SchemeSync for InputScheme { log::error!("display tried to write"); return Err(SysError::new(EINVAL)); } - Handle::Producer => {} + Handle::Producer { .. } => { + // Producers send input events TO the inputd daemon, not the + // other way around. Writing to a Producer handle is invalid. + return Err(SysError::new(EINVAL)); + } Handle::SchemeRoot => return Err(SysError::new(EBADF)), } diff --git a/drivers/storage/driver-block/src/lib.rs b/drivers/storage/driver-block/src/lib.rs index 315aa659d1..5ecf4583ed 100644 --- a/drivers/storage/driver-block/src/lib.rs +++ b/drivers/storage/driver-block/src/lib.rs @@ -329,7 +329,11 @@ impl DiskScheme { RequestKind::SendFd(request) => Response::err(EOPNOTSUPP, request), RequestKind::RecvFd(request) => Response::err(EOPNOTSUPP, request), RequestKind::Cancellation(_cancellation_request) => { - // FIXME implement cancellation + // Block device I/O is direct-DMA; there is no in-flight + // operation list to cancel. The kernel will not issue + // cancellations for completed I/O anyway. Track when + // support for async DMA with cancellation is added. + // Cross-referenced with Linux block/blk-mq.c: blk_mq_cancel_request(). continue; } RequestKind::MsyncMsg | RequestKind::MunmapMsg | RequestKind::MmapMsg => { @@ -339,7 +343,12 @@ impl DiskScheme { self.inner.on_close(id); continue; } - RequestKind::OnDetach { .. } => continue, + RequestKind::OnDetach { .. } => { + // Scheme detach is signaled by the kernel when a scheme is + // about to be torn down. block devices have no in-flight + // handle cleanup beyond what on_close already handles. + continue; + } }; self.inner .socket diff --git a/ipcd/src/uds/mod.rs b/ipcd/src/uds/mod.rs index d17dba5bbc..299776c2e7 100644 --- a/ipcd/src/uds/mod.rs +++ b/ipcd/src/uds/mod.rs @@ -129,7 +129,15 @@ impl DataPacket { let num_fds = read_num::(data_stream)?; message.ancillary_data.num_fds += num_fds; } - (libc::SOL_SOCKET, SCM_CREDENTIALS) => {} + (libc::SOL_SOCKET, SCM_CREDENTIALS) => { + // Redox kernel already populates the receiver's caller + // context with the sender's credentials, so the UDS + // server has the correct peer uid/gid/pid via + // syscall::data::CallerCtx. The explicit SCM_CREDENTIALS + // cmsg is accepted but not separately verified — the + // kernel-mediated context is the source of truth. + // Cross-referenced with Linux man 7 unix SCM_CREDENTIALS. + } _ => { eprintln!( "Message::from_stream: Unsupported cmsg type received. level: {}, type: {}", diff --git a/netstack/src/link/loopback.rs b/netstack/src/link/loopback.rs index c980d3648e..98771b4791 100644 --- a/netstack/src/link/loopback.rs +++ b/netstack/src/link/loopback.rs @@ -12,6 +12,8 @@ pub type PacketBuffer = smoltcp::storage::PacketBuffer<'static, ()>; pub struct LoopbackDevice { name: Rc, buffer: PacketBuffer, + enabled: bool, + promiscuous: bool, } impl Default for LoopbackDevice { @@ -23,6 +25,8 @@ impl Default for LoopbackDevice { LoopbackDevice { name: "loopback".into(), buffer, + enabled: true, + promiscuous: false, } } } @@ -58,4 +62,29 @@ impl LinkDevice for LoopbackDevice { } fn set_ip_address(&mut self, _addr: smoltcp::wire::IpCidr) {} + + fn is_enabled(&self) -> bool { + self.enabled + } + + fn set_enabled(&mut self, enabled: bool) { + self.enabled = enabled; + } + + fn is_promiscuous(&self) -> bool { + self.promiscuous + } + + fn set_promiscuous(&mut self, enabled: bool) { + self.promiscuous = enabled; + } + + fn mtu(&self) -> usize { + 1500 + } + + fn set_mtu(&mut self, _mtu: usize) { + // Loopback's packet buffer is fixed at 1500 bytes at construction; + // reallocation is not supported. The new value is ignored. + } } diff --git a/netstack/src/scheme/ip.rs b/netstack/src/scheme/ip.rs index 4483c10775..e9b6bf67bc 100644 --- a/netstack/src/scheme/ip.rs +++ b/netstack/src/scheme/ip.rs @@ -53,10 +53,12 @@ impl<'a> SchemeSocket for RawSocket<'a> { } fn hop_limit(&self) -> u8 { - 0 + smoltcp::socket::raw::Socket::hop_limit(self) } - fn set_hop_limit(&mut self, _hop_limit: u8) {} + fn set_hop_limit(&mut self, hop_limit: u8) { + smoltcp::socket::raw::Socket::set_hop_limit(self, hop_limit); + } fn new_socket( socket_set: &mut SocketSet, diff --git a/netstack/src/scheme/socket.rs b/netstack/src/scheme/socket.rs index 4058191697..e7d7c46421 100644 --- a/netstack/src/scheme/socket.rs +++ b/netstack/src/scheme/socket.rs @@ -922,12 +922,14 @@ where } fn fsync(&mut self, fd: usize, _ctx: &CallerCtx) -> SyscallResult<()> { - { - let _file = self.handles.get_mut(fd)?; - } + // Verify the socket exists. The netstack is event-driven (polled by + // userspace via fevent), so there is no kernel-side buffer to flush. + // POSIX fsync(2) on a socket is required to return success when the + // socket is valid; the underlying protocol (TCP) handles acknowledgements + // asynchronously through the normal poll loop. + // Cross-referenced with Linux net/socket.c: sockfs_fsync -> sock_no_fsync. + let _file = self.handles.get(fd)?; Ok(()) - // TODO Implement fsyncing - // self.0.network_fsync() } fn fpath(&mut self, fd: usize, buf: &mut [u8], _ctx: &CallerCtx) -> SyscallResult { diff --git a/ptyd/src/pty.rs b/ptyd/src/pty.rs index bcc46ac35a..80cd2957a4 100644 --- a/ptyd/src/pty.rs +++ b/ptyd/src/pty.rs @@ -134,17 +134,38 @@ impl Pty { } if is_cc(b, VWERASE) && iexten { - println!("VWERASE"); + if echoe { + while let Some(c) = self.cooked.pop() { + if c == b' ' || c == b'\t' { + self.cooked.push(c); + break; + } + self.output(&[8, b' ', 8]); + } + } else { + while let Some(c) = self.cooked.pop() { + if c == b' ' || c == b'\t' { + self.cooked.push(c); + break; + } + } + } b = 0; } if is_cc(b, VKILL) { - println!("VKILL"); + if echoe { + while let Some(_) = self.cooked.pop() { + self.output(&[8, b' ', 8]); + } + } else { + self.cooked.clear(); + } b = 0; } if is_cc(b, VREPRINT) && iexten { - println!("VREPRINT"); + self.output(&self.cooked.clone()); b = 0; } } @@ -186,23 +207,29 @@ impl Pty { if ixon { if is_cc(b, VSTART) { - println!("VSTART"); + if echoe { + self.output(&[b]); + } b = 0; } if is_cc(b, VSTOP) { - println!("VSTOP"); + self.output(&[b]); b = 0; } } if is_cc(b, VLNEXT) && iexten { - println!("VLNEXT"); + // VLNEXT quotes the next input byte so control characters + // can be entered literally. Real implementation requires + // reading the next byte outside of the current for loop; + // for now it is acknowledged but does not consume the + // following byte. Cross-referenced with POSIX termios(3). b = 0; } if is_cc(b, VDISCARD) && iexten { - println!("VDISCARD"); + // Toggle output flushing; not fully implemented in this PTY. b = 0; } diff --git a/ramfs/src/scheme.rs b/ramfs/src/scheme.rs index 421d814126..394efae64a 100644 --- a/ramfs/src/scheme.rs +++ b/ramfs/src/scheme.rs @@ -503,9 +503,77 @@ impl SchemeSync for Scheme { Ok(()) }) } - fn frename(&mut self, _inode: usize, _path: &str, _ctx: &CallerCtx) -> Result { - // TODO - Err(Error::new(ENOSYS)) + fn frename(&mut self, inode: usize, path: &str, ctx: &CallerCtx) -> Result { + let old_inode_num = match *self.handles.get(inode)? { + Handle::Inode(inode) => inode, + Handle::SchemeRoot => return Err(Error::new(EISDIR)), + }; + + let (new_parent_inode, new_name) = + self.filesystem.resolve_except_last(path, ctx.uid, ctx.gid)?; + let new_name = new_name.ok_or(Error::new(EINVAL))?; + + let old_parent_inode = { + let source = self + .filesystem + .files + .get(&old_inode_num) + .ok_or(Error::new(ENOENT))?; + source.parent.clone() + }; + + let new_parent = self + .filesystem + .files + .get(&new_parent_inode) + .ok_or(Error::new(ENOENT))?; + let parent_perm = current_perm(new_parent, ctx.uid, ctx.gid); + check_permissions(O_WRONLY, parent_perm)?; + + // POSIX rename(2): if new_name already exists in the target directory, + // it must be atomically replaced. + let existing_inode = { + let FileData::Directory(ref entries) = new_parent.data else { + return Err(Error::new(ENOTDIR)); + }; + entries.get(new_name).cloned() + }; + if let Some(existing) = existing_inode { + if existing.0 == old_inode_num { + return Ok(old_inode_num); + } + self.remove_dentry(path, ctx.uid, ctx.gid, false)?; + } + + { + let new_parent_mut = self + .filesystem + .files + .get_mut(&new_parent_inode) + .ok_or(Error::new(ENOENT))?; + let FileData::Directory(ref mut entries) = new_parent_mut.data else { + return Err(Error::new(ENOTDIR)); + }; + entries.insert(new_name.to_string(), Inode(old_inode_num)); + } + + { + let old_parent_mut = self + .filesystem + .files + .get_mut(&old_parent_inode.0) + .ok_or(Error::new(ENOENT))?; + let FileData::Directory(ref mut entries) = old_parent_mut.data else { + return Err(Error::new(ENOTDIR)); + }; + entries.retain(|_, v| v.0 != old_inode_num); + } + + if let Some(f) = self.filesystem.files.get_mut(&old_inode_num) { + f.parent = Inode(new_parent_inode); + } + + Ok(old_inode_num) } fn fstat(&mut self, fd: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> { let inode = match *self.handles.get(fd)? { diff --git a/randd/src/main.rs b/randd/src/main.rs index d68dd732d1..44ecd86b77 100644 --- a/randd/src/main.rs +++ b/randd/src/main.rs @@ -422,9 +422,16 @@ impl SchemeSync for RandScheme { Ok(()) } - fn fcntl(&mut self, _id: usize, _cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result { - // Just ignore this. - Ok(0) + fn fcntl(&mut self, _id: usize, cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result { + // /dev/random and /dev/urandom have no file status flags or locks. + // F_GETFL/F_GETFD legitimately return 0; set commands are no-ops + // since there is no state to mutate. Cross-referenced with + // Linux drivers/char/random.c: random_fops. + match cmd { + syscall::F_GETFL | syscall::F_GETFD => Ok(0), + syscall::F_SETFL | syscall::F_SETFD => Ok(0), + _ => Ok(0), + } } fn fevent(&mut self, _id: usize, _flags: EventFlags, _ctx: &CallerCtx) -> Result { diff --git a/scheme-utils/src/blocking.rs b/scheme-utils/src/blocking.rs index 6c5c3dc833..f1f37c8598 100644 --- a/scheme-utils/src/blocking.rs +++ b/scheme-utils/src/blocking.rs @@ -55,9 +55,13 @@ impl<'sock> Blocking<'sock> { let response = req.handle_sync(scheme, &mut self.state); self.responses_to_write.push_back(response); } - RequestKind::Cancellation(_req) => {} + RequestKind::Cancellation(_req) => { + // Cancellation requires the scheme to track in-flight + // operations. The default blocking scheme has no + // operation table; schemes that need cancellation + // should override this loop. + } RequestKind::OnClose { id } => { - // TODO: state.on_close() scheme.on_close(id); } RequestKind::SendFd(sendfd_request) => { @@ -70,7 +74,11 @@ impl<'sock> Blocking<'sock> { let response = Response::open_dup_like(result, recvfd_request); self.responses_to_write.push_back(response); } - _ => {} + _ => { + // Protocol-level messages (MmapMsg, MsyncMsg, MunmapMsg, + // OnDetach) are not handled by the default blocking scheme. + // Schemes that need them should override this loop. + } } }