@@ -352,10 +352,6 @@ public static int dup(CodeContext/*!*/ context, int fd) {
352
352
353
353
StreamBox streams = fileManager . GetStreams ( fd ) ; // OSError if fd not valid
354
354
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
- }
359
355
int fd2 = UnixDup ( fd , - 1 , out Stream ? dupstream ) ;
360
356
if ( dupstream is not null ) {
361
357
return fileManager . Add ( fd2 , new ( dupstream ) ) ;
@@ -389,16 +385,14 @@ public static int dup2(CodeContext/*!*/ context, int fd, int fd2) {
389
385
close ( context , fd2 ) ;
390
386
}
391
387
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
393
389
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 ) ;
402
396
}
403
397
fd2 = UnixDup ( fd , fd2 , out Stream ? dupstream ) ; // closes fd2 atomically if reopened in the meantime
404
398
fileManager . Remove ( fd2 ) ;
@@ -410,6 +404,10 @@ public static int dup2(CodeContext/*!*/ context, int fd, int fd2) {
410
404
fileManager . AddRefStreams ( streams ) ;
411
405
return fileManager . Add ( fd2 , new ( streams ) ) ;
412
406
}
407
+ } else {
408
+ fileManager . EnsureRefStreams ( streams ) ;
409
+ fileManager . AddRefStreams ( streams ) ;
410
+ return fileManager . Add ( fd2 , new ( streams ) ) ;
413
411
}
414
412
}
415
413
@@ -877,8 +875,9 @@ public static object open(CodeContext/*!*/ context, [NotNone] string path, int f
877
875
FileMode fileMode = FileModeFromFlags ( flags ) ;
878
876
FileAccess access = FileAccessFromFlags ( flags ) ;
879
877
FileOptions options = FileOptionsFromFlags ( flags ) ;
880
- Stream s ;
881
- FileStream ? fs ;
878
+ Stream s ; // the stream opened to acces the file
879
+ FileStream ? fs ; // downcast of s if s is FileStream (this is always the case on POSIX)
880
+ Stream ? rs = null ; // secondary read stream if needed, otherwise same as s
882
881
if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) && IsNulFile ( path ) ) {
883
882
fs = null ;
884
883
s = Stream . Null ;
@@ -889,15 +888,28 @@ public static object open(CodeContext/*!*/ context, [NotNone] string path, int f
889
888
fs . Close ( ) ;
890
889
s = fs = new FileStream ( path , FileMode . Open , FileAccess . Read , FileShare . ReadWrite , DefaultBufferSize , options ) ;
891
890
} else if ( access == FileAccess . ReadWrite && fileMode == FileMode . Append ) {
891
+ // .NET doesn't allow Append w/ access != Write, so open the file w/ Write
892
+ // and a secondary stream w/ Read, then seek to the end.
892
893
s = fs = new FileStream ( path , FileMode . Append , FileAccess . Write , FileShare . ReadWrite , DefaultBufferSize , options ) ;
894
+ rs = new FileStream ( path , FileMode . Open , FileAccess . Read , FileShare . ReadWrite , DefaultBufferSize , options ) ;
895
+ rs . Seek ( 0L , SeekOrigin . End ) ;
893
896
} else {
894
897
s = fs = new FileStream ( path , fileMode , access , FileShare . ReadWrite , DefaultBufferSize , options ) ;
895
898
}
899
+ rs ??= s ;
896
900
897
- if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ) {
898
- return context . LanguageContext . FileManager . Add ( new ( s ) ) ;
901
+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) || RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ) {
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 ) ) ;
899
911
} else {
900
- return context . LanguageContext . FileManager . Add ( ( int ) fs ! . SafeFileHandle . DangerousGetHandle ( ) , new ( s ) ) ;
912
+ return context . LanguageContext . FileManager . Add ( new ( rs , s ) ) ;
901
913
}
902
914
} catch ( Exception e ) {
903
915
throw ToPythonException ( e , path ) ;
0 commit comments