@@ -844,13 +844,67 @@ 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+ // SAFETY: get_osfhandle is safe to call with any fd value; it returns
869+ // -1 for invalid fds rather than causing undefined behavior.
870+ let raw_handle = unsafe { libc:: get_osfhandle ( fd) } ;
871+ if raw_handle == -1 || raw_handle == -2 {
872+ // ERROR_INVALID_HANDLE (6) maps to EBADF via uvTranslateSysError
873+ return Err ( FsError :: Io ( std:: io:: Error :: from_raw_os_error ( 6 ) ) ) ;
874+ }
875+ let handle = raw_handle as RawHandle ;
876+
877+ // Check if this handle is managed by a non-fsFile internal Deno resource.
878+ // fsFile resources (opened via node:fs) are safe to dup.
879+ // Other managed resources (SQLite, internal pipes) are denied.
880+ // Unmanaged fds (child_process, native addons) are allowed through.
881+ for ( rid, name) in state. resource_table . names ( ) {
882+ if name == "fsFile" || name == "signal" {
883+ continue ;
884+ }
885+ if matches ! (
886+ state. resource_table. get_handle( rid) ,
887+ Ok ( deno_core:: ResourceHandle :: Fd ( existing_handle) ) if existing_handle == handle
888+ ) {
889+ return Err ( FsError :: Io ( std:: io:: Error :: new (
890+ std:: io:: ErrorKind :: PermissionDenied ,
891+ "File descriptor is managed by an internal Deno resource" ,
892+ ) ) ) ;
893+ }
894+ }
895+
896+ // Duplicate the handle so it's independently owned and closeable.
897+ // SAFETY: handle is valid (checked above via get_osfhandle).
898+ let borrowed = unsafe { BorrowedHandle :: borrow_raw ( handle) } ;
899+ let owned = borrowed. try_clone_to_owned ( ) . map_err ( FsError :: Io ) ?;
900+
901+ let std_file = StdFile :: from ( owned) ;
902+ let file: Rc < dyn deno_io:: fs:: File > =
903+ Rc :: new ( deno_io:: StdFileResourceInner :: file ( std_file, None ) ) ;
904+ let rid = state
905+ . resource_table
906+ . add ( FileResource :: new ( file, "fsFile" . to_string ( ) ) ) ;
907+ Ok ( rid)
854908}
855909
856910#[ derive( Debug , Serialize ) ]
0 commit comments