Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public sealed partial class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid

// not using bool? as it's not thread safe
private volatile NullableBool _canSeek = NullableBool.Undefined;
private volatile NullableBool _supportsRandomAccess = NullableBool.Undefined;
private volatile NullableBool _isRegularFile = NullableBool.Undefined;
private bool _deleteOnClose;
private bool _isLocked;

Expand All @@ -34,20 +34,7 @@ private SafeFileHandle(bool ownsHandle)

internal bool CanSeek => !IsClosed && GetCanSeek();

internal bool SupportsRandomAccess
{
get
{
NullableBool supportsRandomAccess = _supportsRandomAccess;
if (supportsRandomAccess == NullableBool.Undefined)
{
_supportsRandomAccess = supportsRandomAccess = GetCanSeek() ? NullableBool.True : NullableBool.False;
}

return supportsRandomAccess == NullableBool.True;
}
set => _supportsRandomAccess = value ? NullableBool.True : NullableBool.False;
}
internal bool IsRegularFile => GetIsRegularFile();

internal ThreadPoolBoundHandle? ThreadPoolBinding => null;

Expand Down Expand Up @@ -112,8 +99,13 @@ private static SafeFileHandle Open(string path, Interop.Sys.OpenFlags flags, int
// and for regular files (most common case)
// avoid one extra sys call for determining whether file can be seeked
handle._canSeek = NullableBool.True;
handle._isRegularFile = NullableBool.True;
Debug.Assert(Interop.Sys.LSeek(handle, 0, Interop.Sys.SeekWhence.SEEK_CUR) >= 0);
}
else
{
handle._isRegularFile = NullableBool.False;
}
}

return handle;
Expand Down Expand Up @@ -405,6 +397,18 @@ private bool GetCanSeek()
return canSeek == NullableBool.True;
}

private bool GetIsRegularFile()
{
NullableBool isRegularFile = _isRegularFile;
if (isRegularFile == NullableBool.Undefined)
{
_isRegularFile = isRegularFile = (Interop.Sys.FStat(this, out Interop.Sys.FileStatus status) == 0
&& (status.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFREG) ? NullableBool.True : NullableBool.False;
}

return isRegularFile == NullableBool.True;
}

private enum NullableBool
{
Undefined = 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,9 @@ internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span<byte> buffer
// The Windows implementation uses ReadFile, which ignores the offset if the handle
// isn't seekable. We do the same manually with PRead vs Read, in order to enable
// the function to be used by FileStream for all the same situations.
int result;
if (handle.SupportsRandomAccess)
{
// Try pread for seekable files.
result = Interop.Sys.PRead(handle, bufPtr, buffer.Length, fileOffset);
if (result == -1)
{
// We need to fallback to the non-offset version for certain file types
// e.g: character devices (such as /dev/tty), pipes, and sockets.
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();

if (errorInfo.Error == Interop.Error.ENXIO ||
errorInfo.Error == Interop.Error.ESPIPE)
{
handle.SupportsRandomAccess = false;
result = Interop.Sys.Read(handle, bufPtr, buffer.Length);
}
}
}
else
{
result = Interop.Sys.Read(handle, bufPtr, buffer.Length);
}

int result = handle.IsRegularFile ?
Interop.Sys.PRead(handle, bufPtr, buffer.Length, fileOffset) :
Interop.Sys.Read(handle, bufPtr, buffer.Length);
FileStreamHelpers.CheckFileCall(result, handle.Path);
return result;
}
Expand Down Expand Up @@ -111,29 +90,9 @@ internal static unsafe void WriteAtOffset(SafeFileHandle handle, ReadOnlySpan<by
// The Windows implementation uses WriteFile, which ignores the offset if the handle
// isn't seekable. We do the same manually with PWrite vs Write, in order to enable
// the function to be used by FileStream for all the same situations.
int bytesToWrite = GetNumberOfBytesToWrite(buffer.Length);
int bytesWritten;
if (handle.SupportsRandomAccess)
{
bytesWritten = Interop.Sys.PWrite(handle, bufPtr, bytesToWrite, fileOffset);
if (bytesWritten == -1)
{
// We need to fallback to the non-offset version for certain file types
// e.g: character devices (such as /dev/tty), pipes, and sockets.
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();

if (errorInfo.Error == Interop.Error.ENXIO ||
errorInfo.Error == Interop.Error.ESPIPE)
{
handle.SupportsRandomAccess = false;
bytesWritten = Interop.Sys.Write(handle, bufPtr, bytesToWrite);
}
}
}
else
{
bytesWritten = Interop.Sys.Write(handle, bufPtr, bytesToWrite);
}
int bytesWritten = handle.IsRegularFile ?
Interop.Sys.PWrite(handle, bufPtr, GetNumberOfBytesToWrite(buffer.Length), fileOffset) :
Interop.Sys.Write(handle, bufPtr, GetNumberOfBytesToWrite(buffer.Length));

FileStreamHelpers.CheckFileCall(bytesWritten, handle.Path);
if (bytesWritten == buffer.Length)
Expand Down