@@ -352,10 +352,6 @@ public static int dup(CodeContext/*!*/ context, int fd) {
352352
353353 StreamBox streams = fileManager . GetStreams ( fd ) ; // OSError if fd not valid
354354 if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) || RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ) {
355- if ( ! streams . IsSingleStream && fd is 1 or 2 ) {
356- // If there is a separate write stream, dupping over stout or sderr uses write stream's file descriptor
357- fd = streams . WriteStream is FileStream fs ? fs . SafeFileHandle . DangerousGetHandle ( ) . ToInt32 ( ) : fd ;
358- }
359355 int fd2 = UnixDup ( fd , - 1 , out Stream ? dupstream ) ;
360356 if ( dupstream is not null ) {
361357 return fileManager . Add ( fd2 , new ( dupstream ) ) ;
@@ -389,16 +385,14 @@ public static int dup2(CodeContext/*!*/ context, int fd, int fd2) {
389385 close ( context , fd2 ) ;
390386 }
391387
392- // TODO: race condition: `open` or `dup` on another thread may occupy fd2 (simulated descriptors only)
388+ // TODO: race condition: `open` or `dup` on another thread may occupy fd2
393389
394- if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ) {
395- fileManager . EnsureRefStreams ( streams ) ;
396- fileManager . AddRefStreams ( streams ) ;
397- return fileManager . Add ( fd2 , new ( streams ) ) ;
398- } else {
399- if ( ! streams . IsSingleStream && fd is 1 or 2 ) {
400- // If there is a separate write stream, dupping over stout or sderr uses write stream's file descriptor
401- fd = streams . WriteStream is FileStream fs ? fs . SafeFileHandle . DangerousGetHandle ( ) . ToInt32 ( ) : fd ;
390+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) || RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ) {
391+ if ( ! streams . IsSingleStream && fd2 is 0 && streams . ReadStream is FileStream fs ) {
392+ // If there is a separate read stream, dupping over stdin uses read stream's file descriptor as source
393+ long pos = fs . Position ;
394+ fd = fs . SafeFileHandle . DangerousGetHandle ( ) . ToInt32 ( ) ;
395+ fs . Seek ( pos , SeekOrigin . Begin ) ;
402396 }
403397 fd2 = UnixDup ( fd , fd2 , out Stream ? dupstream ) ; // closes fd2 atomically if reopened in the meantime
404398 fileManager . Remove ( fd2 ) ;
@@ -410,6 +404,10 @@ public static int dup2(CodeContext/*!*/ context, int fd, int fd2) {
410404 fileManager . AddRefStreams ( streams ) ;
411405 return fileManager . Add ( fd2 , new ( streams ) ) ;
412406 }
407+ } else {
408+ fileManager . EnsureRefStreams ( streams ) ;
409+ fileManager . AddRefStreams ( streams ) ;
410+ return fileManager . Add ( fd2 , new ( streams ) ) ;
413411 }
414412 }
415413
@@ -901,7 +899,15 @@ public static object open(CodeContext/*!*/ context, [NotNone] string path, int f
901899 rs ??= s ;
902900
903901 if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) || RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ) {
904- return context . LanguageContext . FileManager . Add ( fs ! . SafeFileHandle . DangerousGetHandle ( ) . ToInt32 ( ) , new ( rs , s ) ) ;
902+ int fd = fs ! . SafeFileHandle . DangerousGetHandle ( ) . ToInt32 ( ) ;
903+ // accessing SafeFileHandle may reset file position
904+ if ( fileMode == FileMode . Append ) {
905+ fs . Seek ( 0L , SeekOrigin . End ) ;
906+ }
907+ if ( ! ReferenceEquals ( fs , rs ) ) {
908+ rs . Seek ( fs . Position , SeekOrigin . Begin ) ;
909+ }
910+ return context . LanguageContext . FileManager . Add ( fd , new ( rs , s ) ) ;
905911 } else {
906912 return context . LanguageContext . FileManager . Add ( new ( rs , s ) ) ;
907913 }
0 commit comments