diff --git a/kernel/src/ipc/fd.rs b/kernel/src/ipc/fd.rs index ef3d3f7..ce44393 100644 --- a/kernel/src/ipc/fd.rs +++ b/kernel/src/ipc/fd.rs @@ -102,9 +102,20 @@ impl Default for FdTable { impl Clone for FdTable { fn clone(&self) -> Self { - FdTable { - fds: alloc::boxed::Box::new((*self.fds).clone()), + let cloned_fds = alloc::boxed::Box::new((*self.fds).clone()); + + // Increment pipe reference counts for all cloned pipe fds + for fd_opt in cloned_fds.iter() { + if let Some(fd_entry) = fd_opt { + match &fd_entry.kind { + FdKind::PipeRead(buffer) => buffer.lock().add_reader(), + FdKind::PipeWrite(buffer) => buffer.lock().add_writer(), + _ => {} + } + } } + + FdTable { fds: cloned_fds } } } @@ -179,8 +190,21 @@ impl FdTable { let fd_entry = self.fds[old_fd as usize].clone().ok_or(9)?; - // Close new_fd if it's open (silently ignore errors) - let _ = self.close(new_fd); + // If new_fd is open, close it and decrement pipe ref counts + if let Some(old_entry) = self.fds[new_fd as usize].take() { + match old_entry.kind { + FdKind::PipeRead(buffer) => buffer.lock().close_read(), + FdKind::PipeWrite(buffer) => buffer.lock().close_write(), + _ => {} + } + } + + // Increment pipe reference counts for the duplicated fd + match &fd_entry.kind { + FdKind::PipeRead(buffer) => buffer.lock().add_reader(), + FdKind::PipeWrite(buffer) => buffer.lock().add_writer(), + _ => {} + } self.fds[new_fd as usize] = Some(fd_entry); Ok(new_fd) @@ -196,6 +220,13 @@ impl FdTable { let fd_entry = self.fds[old_fd as usize].clone().ok_or(9)?; + // Increment pipe reference counts for the duplicated fd + match &fd_entry.kind { + FdKind::PipeRead(buffer) => buffer.lock().add_reader(), + FdKind::PipeWrite(buffer) => buffer.lock().add_writer(), + _ => {} + } + // Find lowest available slot for i in 0..MAX_FDS { if self.fds[i].is_none() { @@ -203,6 +234,13 @@ impl FdTable { return Ok(i as i32); } } + + // No slot found - need to decrement the counts we just added + match &fd_entry.kind { + FdKind::PipeRead(buffer) => buffer.lock().close_read(), + FdKind::PipeWrite(buffer) => buffer.lock().close_write(), + _ => {} + } Err(24) // EMFILE } } diff --git a/kernel/src/ipc/pipe.rs b/kernel/src/ipc/pipe.rs index 98feb19..0388bbe 100644 --- a/kernel/src/ipc/pipe.rs +++ b/kernel/src/ipc/pipe.rs @@ -130,6 +130,16 @@ impl PipeBuffer { } } + /// Add a reader (used when duplicating pipe read fds) + pub fn add_reader(&mut self) { + self.readers += 1; + } + + /// Add a writer (used when duplicating pipe write fds) + pub fn add_writer(&mut self) { + self.writers += 1; + } + /// Get the number of bytes available to read #[allow(dead_code)] pub fn available(&self) -> usize { diff --git a/kernel/src/syscall/pipe.rs b/kernel/src/syscall/pipe.rs index de4a3c5..4a9c975 100644 --- a/kernel/src/syscall/pipe.rs +++ b/kernel/src/syscall/pipe.rs @@ -148,15 +148,11 @@ pub fn sys_close(fd: i32) -> SyscallResult { FdKind::StdIo(_) => { log::debug!("sys_close: Closed stdio fd={}", fd); } - FdKind::UdpSocket(socket_ref) => { - // Unbind the socket if it was bound - let socket = socket_ref.lock(); - if let Some(port) = socket.local_port { - crate::socket::SOCKET_REGISTRY.unbind_udp(port); - log::debug!("sys_close: Closed UDP socket fd={}, unbound port {}", fd, port); - } else { - log::debug!("sys_close: Closed unbound UDP socket fd={}", fd); - } + FdKind::UdpSocket(_) => { + // Socket unbinding is handled by UdpSocket::Drop when the last + // Arc reference is released, allowing shared sockets (via dup/fork) + // to remain bound until all references are closed. + log::debug!("sys_close: Closed UDP socket fd={}", fd); } } SyscallResult::Ok(0)