|
|
@@ -0,0 +1,155 @@
|
|
|
+using System;
|
|
|
+using System.IO;
|
|
|
+using System.Runtime.InteropServices;
|
|
|
+using System.Runtime.Versioning;
|
|
|
+
|
|
|
+namespace Ryujinx.Common.SystemInterop
|
|
|
+{
|
|
|
+ [SupportedOSPlatform("linux")]
|
|
|
+ [SupportedOSPlatform("macos")]
|
|
|
+ public partial class UnixStream : Stream, IDisposable
|
|
|
+ {
|
|
|
+ private const int InvalidFd = -1;
|
|
|
+
|
|
|
+ private int _fd;
|
|
|
+
|
|
|
+ [LibraryImport("libc", SetLastError = true)]
|
|
|
+ private static partial long read(int fd, IntPtr buf, ulong count);
|
|
|
+
|
|
|
+ [LibraryImport("libc", SetLastError = true)]
|
|
|
+ private static partial long write(int fd, IntPtr buf, ulong count);
|
|
|
+
|
|
|
+ [LibraryImport("libc", SetLastError = true)]
|
|
|
+ private static partial int close(int fd);
|
|
|
+
|
|
|
+ public UnixStream(int fd)
|
|
|
+ {
|
|
|
+ if (InvalidFd == fd)
|
|
|
+ {
|
|
|
+ throw new ArgumentException("Invalid file descriptor");
|
|
|
+ }
|
|
|
+
|
|
|
+ _fd = fd;
|
|
|
+
|
|
|
+ CanRead = read(fd, IntPtr.Zero, 0) != -1;
|
|
|
+ CanWrite = write(fd, IntPtr.Zero, 0) != -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ ~UnixStream()
|
|
|
+ {
|
|
|
+ Close();
|
|
|
+ }
|
|
|
+
|
|
|
+ public override bool CanRead { get; }
|
|
|
+ public override bool CanWrite { get; }
|
|
|
+ public override bool CanSeek => false;
|
|
|
+
|
|
|
+ public override long Length => throw new NotSupportedException();
|
|
|
+
|
|
|
+ public override long Position
|
|
|
+ {
|
|
|
+ get => throw new NotSupportedException();
|
|
|
+ set => throw new NotSupportedException();
|
|
|
+ }
|
|
|
+
|
|
|
+ public override void Flush()
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ public override unsafe int Read([In, Out] byte[] buffer, int offset, int count)
|
|
|
+ {
|
|
|
+ if (offset < 0 || offset > (buffer.Length - count) || count < 0)
|
|
|
+ {
|
|
|
+ throw new ArgumentOutOfRangeException();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buffer.Length == 0)
|
|
|
+ {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ long r = 0;
|
|
|
+ fixed (byte* buf = &buffer[offset])
|
|
|
+ {
|
|
|
+ do
|
|
|
+ {
|
|
|
+ r = read(_fd, (IntPtr)buf, (ulong)count);
|
|
|
+ } while (ShouldRetry(r));
|
|
|
+ }
|
|
|
+
|
|
|
+ return (int)r;
|
|
|
+ }
|
|
|
+
|
|
|
+ public override unsafe void Write(byte[] buffer, int offset, int count)
|
|
|
+ {
|
|
|
+ if (offset < 0 || offset > (buffer.Length - count) || count < 0)
|
|
|
+ {
|
|
|
+ throw new ArgumentOutOfRangeException();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buffer.Length == 0)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ fixed (byte* buf = &buffer[offset])
|
|
|
+ {
|
|
|
+ long r = 0;
|
|
|
+ do {
|
|
|
+ r = write(_fd, (IntPtr)buf, (ulong)count);
|
|
|
+ } while (ShouldRetry(r));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public override long Seek(long offset, SeekOrigin origin)
|
|
|
+ {
|
|
|
+ throw new NotSupportedException();
|
|
|
+ }
|
|
|
+
|
|
|
+ public override void SetLength(long value)
|
|
|
+ {
|
|
|
+ throw new NotSupportedException();
|
|
|
+ }
|
|
|
+
|
|
|
+ public override void Close()
|
|
|
+ {
|
|
|
+ if (_fd == InvalidFd)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Flush();
|
|
|
+
|
|
|
+ int r;
|
|
|
+ do {
|
|
|
+ r = close(_fd);
|
|
|
+ } while (ShouldRetry(r));
|
|
|
+
|
|
|
+ _fd = InvalidFd;
|
|
|
+ }
|
|
|
+
|
|
|
+ void IDisposable.Dispose()
|
|
|
+ {
|
|
|
+ Close();
|
|
|
+ }
|
|
|
+
|
|
|
+ private bool ShouldRetry(long r)
|
|
|
+ {
|
|
|
+ if (r == -1)
|
|
|
+ {
|
|
|
+ const int eintr = 4;
|
|
|
+
|
|
|
+ int errno = Marshal.GetLastPInvokeError();
|
|
|
+
|
|
|
+ if (errno == eintr)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new SystemException($"Operation failed with error 0x{errno:X}");
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|