RustDedicated/System.Core/Interop.cs
2025-05-25 05:29:54 +09:30

536 lines
14 KiB
C#

using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
internal static class Interop
{
internal enum Error
{
SUCCESS = 0,
E2BIG = 65537,
EACCES = 65538,
EADDRINUSE = 65539,
EADDRNOTAVAIL = 65540,
EAFNOSUPPORT = 65541,
EAGAIN = 65542,
EALREADY = 65543,
EBADF = 65544,
EBADMSG = 65545,
EBUSY = 65546,
ECANCELED = 65547,
ECHILD = 65548,
ECONNABORTED = 65549,
ECONNREFUSED = 65550,
ECONNRESET = 65551,
EDEADLK = 65552,
EDESTADDRREQ = 65553,
EDOM = 65554,
EDQUOT = 65555,
EEXIST = 65556,
EFAULT = 65557,
EFBIG = 65558,
EHOSTUNREACH = 65559,
EIDRM = 65560,
EILSEQ = 65561,
EINPROGRESS = 65562,
EINTR = 65563,
EINVAL = 65564,
EIO = 65565,
EISCONN = 65566,
EISDIR = 65567,
ELOOP = 65568,
EMFILE = 65569,
EMLINK = 65570,
EMSGSIZE = 65571,
EMULTIHOP = 65572,
ENAMETOOLONG = 65573,
ENETDOWN = 65574,
ENETRESET = 65575,
ENETUNREACH = 65576,
ENFILE = 65577,
ENOBUFS = 65578,
ENODEV = 65580,
ENOENT = 65581,
ENOEXEC = 65582,
ENOLCK = 65583,
ENOLINK = 65584,
ENOMEM = 65585,
ENOMSG = 65586,
ENOPROTOOPT = 65587,
ENOSPC = 65588,
ENOSYS = 65591,
ENOTCONN = 65592,
ENOTDIR = 65593,
ENOTEMPTY = 65594,
ENOTRECOVERABLE = 65595,
ENOTSOCK = 65596,
ENOTSUP = 65597,
ENOTTY = 65598,
ENXIO = 65599,
EOVERFLOW = 65600,
EOWNERDEAD = 65601,
EPERM = 65602,
EPIPE = 65603,
EPROTO = 65604,
EPROTONOSUPPORT = 65605,
EPROTOTYPE = 65606,
ERANGE = 65607,
EROFS = 65608,
ESPIPE = 65609,
ESRCH = 65610,
ESTALE = 65611,
ETIMEDOUT = 65613,
ETXTBSY = 65614,
EXDEV = 65615,
ESOCKTNOSUPPORT = 65630,
EPFNOSUPPORT = 65632,
ESHUTDOWN = 65644,
EHOSTDOWN = 65648,
ENODATA = 65649,
EOPNOTSUPP = 65597,
EWOULDBLOCK = 65542
}
internal struct ErrorInfo
{
private Error _error;
private int _rawErrno;
internal Error Error => _error;
internal int RawErrno
{
get
{
if (_rawErrno != -1)
{
return _rawErrno;
}
return _rawErrno = Sys.ConvertErrorPalToPlatform(_error);
}
}
internal ErrorInfo(int errno)
{
_error = Sys.ConvertErrorPlatformToPal(errno);
_rawErrno = errno;
}
internal ErrorInfo(Error error)
{
_error = error;
_rawErrno = -1;
}
internal string GetErrorMessage()
{
return Sys.StrError(RawErrno);
}
public override string ToString()
{
return $"RawErrno: {RawErrno} Error: {Error} GetErrorMessage: {GetErrorMessage()}";
}
}
internal static class Sys
{
internal enum LockOperations
{
LOCK_SH = 1,
LOCK_EX = 2,
LOCK_NB = 4,
LOCK_UN = 8
}
internal static class Fcntl
{
internal static readonly bool CanGetSetPipeSz = FcntlCanGetSetPipeSz() != 0;
[DllImport("System.Native", EntryPoint = "SystemNative_FcntlGetPipeSz", SetLastError = true)]
internal static extern int GetPipeSz(SafePipeHandle fd);
[DllImport("System.Native", EntryPoint = "SystemNative_FcntlSetPipeSz", SetLastError = true)]
internal static extern int SetPipeSz(SafePipeHandle fd, int size);
[DllImport("System.Native", EntryPoint = "SystemNative_FcntlCanGetSetPipeSz")]
private static extern int FcntlCanGetSetPipeSz();
[DllImport("System.Native", EntryPoint = "SystemNative_FcntlSetCloseOnExec", SetLastError = true)]
internal static extern int SetCloseOnExec(SafeHandle fd);
}
[Flags]
internal enum OpenFlags
{
O_RDONLY = 0,
O_WRONLY = 1,
O_RDWR = 2,
O_CLOEXEC = 0x10,
O_CREAT = 0x20,
O_EXCL = 0x40,
O_TRUNC = 0x80,
O_SYNC = 0x100
}
[Flags]
internal enum Permissions
{
Mask = 0x1FF,
S_IRWXU = 0x1C0,
S_IRUSR = 0x100,
S_IWUSR = 0x80,
S_IXUSR = 0x40,
S_IRWXG = 0x38,
S_IRGRP = 0x20,
S_IWGRP = 0x10,
S_IXGRP = 8,
S_IRWXO = 7,
S_IROTH = 4,
S_IWOTH = 2,
S_IXOTH = 1
}
[Flags]
internal enum PipeFlags
{
O_CLOEXEC = 0x10
}
[Flags]
internal enum PollEvents : short
{
POLLNONE = 0,
POLLIN = 1,
POLLPRI = 2,
POLLOUT = 4,
POLLERR = 8,
POLLHUP = 0x10,
POLLNVAL = 0x20
}
internal struct PollEvent
{
internal int FileDescriptor;
internal PollEvents Events;
internal PollEvents TriggeredEvents;
}
internal struct FileStatus
{
internal FileStatusFlags Flags;
internal int Mode;
internal uint Uid;
internal uint Gid;
internal long Size;
internal long ATime;
internal long ATimeNsec;
internal long MTime;
internal long MTimeNsec;
internal long CTime;
internal long CTimeNsec;
internal long BirthTime;
internal long BirthTimeNsec;
internal long Dev;
internal long Ino;
internal uint UserFlags;
}
internal static class FileTypes
{
internal const int S_IFMT = 61440;
internal const int S_IFIFO = 4096;
internal const int S_IFCHR = 8192;
internal const int S_IFDIR = 16384;
internal const int S_IFREG = 32768;
internal const int S_IFLNK = 40960;
internal const int S_IFSOCK = 49152;
}
[Flags]
internal enum FileStatusFlags
{
None = 0,
HasBirthTime = 1
}
internal const int ReadEndOfPipe = 0;
internal const int WriteEndOfPipe = 1;
internal static Error GetLastError()
{
return ConvertErrorPlatformToPal(Marshal.GetLastWin32Error());
}
internal static ErrorInfo GetLastErrorInfo()
{
return new ErrorInfo(Marshal.GetLastWin32Error());
}
internal unsafe static string StrError(int platformErrno)
{
int num = 1024;
byte* ptr = stackalloc byte[(int)(uint)num];
byte* ptr2 = StrErrorR(platformErrno, ptr, num);
if (ptr2 == null)
{
ptr2 = ptr;
}
return Marshal.PtrToStringAnsi((IntPtr)ptr2);
}
[DllImport("System.Native", EntryPoint = "SystemNative_ConvertErrorPlatformToPal")]
internal static extern Error ConvertErrorPlatformToPal(int platformErrno);
[DllImport("System.Native", EntryPoint = "SystemNative_ConvertErrorPalToPlatform")]
internal static extern int ConvertErrorPalToPlatform(Error error);
[DllImport("System.Native", EntryPoint = "SystemNative_StrErrorR")]
private unsafe static extern byte* StrErrorR(int platformErrno, byte* buffer, int bufferSize);
[DllImport("System.Native", EntryPoint = "SystemNative_Close", SetLastError = true)]
internal static extern int Close(IntPtr fd);
[DllImport("System.Native", EntryPoint = "SystemNative_FLock", SetLastError = true)]
internal static extern int FLock(SafeFileHandle fd, LockOperations operation);
[DllImport("System.Native", EntryPoint = "SystemNative_FLock", SetLastError = true)]
internal static extern int FLock(IntPtr fd, LockOperations operation);
[DllImport("System.Native", EntryPoint = "SystemNative_GetDomainSocketSizes")]
internal static extern void GetDomainSocketSizes(out int pathOffset, out int pathSize, out int addressSize);
[DllImport("System.Native", EntryPoint = "SystemNative_GetEUid")]
internal static extern uint GetEUid();
[DllImport("System.Native", EntryPoint = "SystemNative_GetHostName", SetLastError = true)]
private unsafe static extern int GetHostName(byte* name, int nameLength);
internal unsafe static string GetHostName()
{
byte* num = stackalloc byte[256];
int hostName = GetHostName(num, 256);
if (hostName != 0)
{
throw new InvalidOperationException($"gethostname returned {hostName}");
}
num[255] = 0;
return Marshal.PtrToStringAnsi((IntPtr)num);
}
[DllImport("System.Native", EntryPoint = "SystemNative_GetPeerID", SetLastError = true)]
internal static extern int GetPeerID(SafeHandle socket, out uint euid);
[DllImport("System.Native", EntryPoint = "SystemNative_GetPeerUserName", SetLastError = true)]
internal static extern string GetPeerUserName(SafeHandle socket);
[DllImport("System.Native", EntryPoint = "SystemNative_MkDir", SetLastError = true)]
internal static extern int MkDir(string path, int mode);
[DllImport("System.Native", EntryPoint = "SystemNative_Open", SetLastError = true)]
internal static extern SafeFileHandle Open(string filename, OpenFlags flags, int mode);
[DllImport("System.Native", EntryPoint = "SystemNative_Pipe", SetLastError = true)]
internal unsafe static extern int Pipe(int* pipefd, PipeFlags flags = (PipeFlags)0);
[DllImport("System.Native", EntryPoint = "SystemNative_Poll")]
internal unsafe static extern Error Poll(PollEvent* pollEvents, uint eventCount, int timeout, uint* triggered);
internal unsafe static Error Poll(SafeHandle fd, PollEvents events, int timeout, out PollEvents triggered)
{
bool success = false;
try
{
fd.DangerousAddRef(ref success);
PollEvent pollEvent = new PollEvent
{
FileDescriptor = fd.DangerousGetHandle().ToInt32(),
Events = events
};
uint num = default(uint);
Error result = Poll(&pollEvent, 1u, timeout, &num);
triggered = pollEvent.TriggeredEvents;
return result;
}
finally
{
if (success)
{
fd.DangerousRelease();
}
}
}
[DllImport("System.Native", EntryPoint = "SystemNative_Read", SetLastError = true)]
internal unsafe static extern int Read(SafePipeHandle fd, byte* buffer, int count);
[DllImport("System.Native", EntryPoint = "SystemNative_SetEUid")]
internal static extern int SetEUid(uint euid);
[DllImport("System.Native", EntryPoint = "SystemNative_FStat2", SetLastError = true)]
internal static extern int FStat(SafePipeHandle fd, out FileStatus output);
[DllImport("System.Native", EntryPoint = "SystemNative_FStat2", SetLastError = true)]
internal static extern int FStat(SafeFileHandle fd, out FileStatus output);
[DllImport("System.Native", EntryPoint = "SystemNative_Stat2", SetLastError = true)]
internal static extern int Stat(string path, out FileStatus output);
[DllImport("System.Native", EntryPoint = "SystemNative_LStat2", SetLastError = true)]
internal static extern int LStat(string path, out FileStatus output);
[DllImport("System.Native", EntryPoint = "SystemNative_Unlink", SetLastError = true)]
internal static extern int Unlink(string pathname);
[DllImport("System.Native", EntryPoint = "SystemNative_Write", SetLastError = true)]
internal unsafe static extern int Write(SafePipeHandle fd, byte* buffer, int bufferSize);
}
internal static class Libraries
{
internal const string SystemNative = "System.Native";
internal const string HttpNative = "System.Net.Http.Native";
internal const string NetSecurityNative = "System.Net.Security.Native";
internal const string CryptoNative = "System.Security.Cryptography.Native.OpenSsl";
internal const string CompressionNative = "System.IO.Compression.Native";
internal const string Libdl = "libdl";
}
private static void ThrowExceptionForIoErrno(ErrorInfo errorInfo, string path, bool isDirectory, Func<ErrorInfo, ErrorInfo> errorRewriter)
{
if (errorRewriter != null)
{
errorInfo = errorRewriter(errorInfo);
}
throw GetExceptionForIoErrno(errorInfo, path, isDirectory);
}
internal static void CheckIo(Error error, string path = null, bool isDirectory = false, Func<ErrorInfo, ErrorInfo> errorRewriter = null)
{
if (error != Error.SUCCESS)
{
ThrowExceptionForIoErrno(error.Info(), path, isDirectory, errorRewriter);
}
}
internal static long CheckIo(long result, string path = null, bool isDirectory = false, Func<ErrorInfo, ErrorInfo> errorRewriter = null)
{
if (result < 0)
{
ThrowExceptionForIoErrno(Sys.GetLastErrorInfo(), path, isDirectory, errorRewriter);
}
return result;
}
internal static int CheckIo(int result, string path = null, bool isDirectory = false, Func<ErrorInfo, ErrorInfo> errorRewriter = null)
{
CheckIo((long)result, path, isDirectory, errorRewriter);
return result;
}
internal static IntPtr CheckIo(IntPtr result, string path = null, bool isDirectory = false, Func<ErrorInfo, ErrorInfo> errorRewriter = null)
{
CheckIo((long)result, path, isDirectory, errorRewriter);
return result;
}
internal static TSafeHandle CheckIo<TSafeHandle>(TSafeHandle handle, string path = null, bool isDirectory = false, Func<ErrorInfo, ErrorInfo> errorRewriter = null) where TSafeHandle : SafeHandle
{
if (handle.IsInvalid)
{
ThrowExceptionForIoErrno(Sys.GetLastErrorInfo(), path, isDirectory, errorRewriter);
}
return handle;
}
internal static Exception GetExceptionForIoErrno(ErrorInfo errorInfo, string path = null, bool isDirectory = false)
{
switch (errorInfo.Error)
{
case Error.ENOENT:
if (isDirectory)
{
if (string.IsNullOrEmpty(path))
{
return new DirectoryNotFoundException("Could not find a part of the path.");
}
return new DirectoryNotFoundException(SR.Format("Could not find a part of the path '{0}'.", path));
}
if (string.IsNullOrEmpty(path))
{
return new FileNotFoundException("Unable to find the specified file.");
}
return new FileNotFoundException(SR.Format("Could not find file '{0}'.", path), path);
case Error.EACCES:
case Error.EBADF:
case Error.EPERM:
{
Exception iOException = GetIOException(errorInfo);
if (string.IsNullOrEmpty(path))
{
return new UnauthorizedAccessException("Access to the path is denied.", iOException);
}
return new UnauthorizedAccessException(SR.Format("Access to the path '{0}' is denied.", path), iOException);
}
case Error.ENAMETOOLONG:
if (string.IsNullOrEmpty(path))
{
return new PathTooLongException("The specified file name or path is too long, or a component of the specified path is too long.");
}
return new PathTooLongException(SR.Format("The path '{0}' is too long, or a component of the specified path is too long.", path));
case Error.EAGAIN:
if (string.IsNullOrEmpty(path))
{
return new IOException("The process cannot access the file because it is being used by another process.", errorInfo.RawErrno);
}
return new IOException(SR.Format("The process cannot access the file '{0}' because it is being used by another process.", path), errorInfo.RawErrno);
case Error.ECANCELED:
return new OperationCanceledException();
case Error.EFBIG:
return new ArgumentOutOfRangeException("value", "Specified file length was too large for the file system.");
case Error.EEXIST:
if (!string.IsNullOrEmpty(path))
{
return new IOException(SR.Format("The file '{0}' already exists.", path), errorInfo.RawErrno);
}
break;
}
return GetIOException(errorInfo);
}
internal static Exception GetIOException(ErrorInfo errorInfo)
{
return new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno);
}
}