@@ -844,13 +844,65 @@ fn dup_fd_impl(state: &mut OpState, fd: i32) -> Result<ResourceId, FsError> {
844844#[ op2( fast) ]
845845#[ smi]
846846pub fn op_node_dup_fd (
847- _state : & mut OpState ,
848- #[ smi] _fd : i32 ,
847+ state : & mut OpState ,
848+ #[ smi] fd : i32 ,
849849) -> Result < ResourceId , FsError > {
850- Err ( FsError :: Io ( std:: io:: Error :: new (
851- std:: io:: ErrorKind :: Unsupported ,
852- "op_node_dup_fd is not supported on this platform" ,
853- ) ) )
850+ dup_fd_impl ( state, fd)
851+ }
852+
853+ #[ cfg( not( unix) ) ]
854+ fn dup_fd_impl ( state : & mut OpState , fd : i32 ) -> Result < ResourceId , FsError > {
855+ use std:: fs:: File as StdFile ;
856+ use std:: os:: windows:: io:: BorrowedHandle ;
857+ use std:: os:: windows:: io:: RawHandle ;
858+
859+ if fd < 0 {
860+ return Err ( FsError :: Io ( std:: io:: Error :: new (
861+ std:: io:: ErrorKind :: InvalidInput ,
862+ "Invalid file descriptor" ,
863+ ) ) ) ;
864+ }
865+
866+ // Convert CRT fd to Windows HANDLE.
867+ // get_osfhandle returns -1 for invalid fds, -2 for non-stream fds.
868+ let raw_handle = unsafe { libc:: get_osfhandle ( fd) } ;
869+ if raw_handle == -1 || raw_handle == -2 {
870+ // ERROR_INVALID_HANDLE (6) maps to EBADF via uvTranslateSysError
871+ return Err ( FsError :: Io ( std:: io:: Error :: from_raw_os_error ( 6 ) ) ) ;
872+ }
873+ let handle = raw_handle as RawHandle ;
874+
875+ // Check if this handle is managed by a non-fsFile internal Deno resource.
876+ // fsFile resources (opened via node:fs) are safe to dup.
877+ // Other managed resources (SQLite, internal pipes) are denied.
878+ // Unmanaged fds (child_process, native addons) are allowed through.
879+ for ( rid, name) in state. resource_table . names ( ) {
880+ if name == "fsFile" || name == "signal" {
881+ continue ;
882+ }
883+ if matches ! (
884+ state. resource_table. get_handle( rid) ,
885+ Ok ( deno_core:: ResourceHandle :: Fd ( existing_handle) ) if existing_handle == handle
886+ ) {
887+ return Err ( FsError :: Io ( std:: io:: Error :: new (
888+ std:: io:: ErrorKind :: PermissionDenied ,
889+ "File descriptor is managed by an internal Deno resource" ,
890+ ) ) ) ;
891+ }
892+ }
893+
894+ // Duplicate the handle so it's independently owned and closeable.
895+ // SAFETY: handle is valid (checked above via get_osfhandle).
896+ let borrowed = unsafe { BorrowedHandle :: borrow_raw ( handle) } ;
897+ let owned = borrowed. try_clone_to_owned ( ) . map_err ( FsError :: Io ) ?;
898+
899+ let std_file = StdFile :: from ( owned) ;
900+ let file: Rc < dyn deno_io:: fs:: File > =
901+ Rc :: new ( deno_io:: StdFileResourceInner :: file ( std_file, None ) ) ;
902+ let rid = state
903+ . resource_table
904+ . add ( FileResource :: new ( file, "fsFile" . to_string ( ) ) ) ;
905+ Ok ( rid)
854906}
855907
856908#[ derive( Debug , Serialize ) ]
0 commit comments