Skip to content

Commit

Permalink
Isolate POSIX-specific native calls in PythonNT into separate file (#…
Browse files Browse the repository at this point in the history
…1870)

* Isolate POSIX-specific native calls in PythonNT into separate file

* Simplify error creation

* Clean up some error usage in mmap

* Normalize macos platform name

* Fix some mmap errors on POSIX

* Move more functions to posix.cs

* Update after review
  • Loading branch information
BCSharp authored Jan 13, 2025
1 parent 9082851 commit 1cd8e71
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 201 deletions.
3 changes: 3 additions & 0 deletions Src/IronPython.Modules/_overlapped.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

using IronPython.Runtime;

[assembly: PythonModule("_overlapped", typeof(IronPython.Modules.PythonOverlapped), PlatformsAttribute.PlatformFamily.Windows)]
namespace IronPython.Modules {

[SupportedOSPlatform("windows")]
public static class PythonOverlapped {
public const int ERROR_NETNAME_DELETED = 64;
public const int ERROR_SEM_TIMEOUT = 121;
Expand Down
2 changes: 2 additions & 0 deletions Src/IronPython.Modules/_winapi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;

using IronPython.Runtime;
using IronPython.Runtime.Operations;

[assembly: PythonModule("_winapi", typeof(IronPython.Modules.PythonWinApi), PlatformsAttribute.PlatformFamily.Windows)]
namespace IronPython.Modules {
[SupportedOSPlatform("windows")]
public static class PythonWinApi {
#region Public API

Expand Down
74 changes: 28 additions & 46 deletions Src/IronPython.Modules/mmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#if FEATURE_MMAP

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
Expand Down Expand Up @@ -205,12 +204,12 @@ public static class MmapModule {

public static readonly string __doc__ = null;

private static string FormatError(int errorCode) {
return new Win32Exception(errorCode).Message;
}

private static Exception WindowsError(int code) {
return PythonExceptions.CreateThrowable(PythonExceptions.OSError, code, FormatError(code));
private static Exception WindowsError(int winerror) {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
return PythonNT.GetWin32Error(winerror);
} else {
return PythonNT.GetOsError(PythonExceptions._OSError.WinErrorToErrno(winerror));
}
}

public static PythonType error => PythonExceptions.OSError;
Expand All @@ -234,7 +233,7 @@ public MmapUnix(CodeContext/*!*/ context, int fileno, long length, int flags = M
private static MemoryMappedFileAccess ToMmapFileAccess(int flags, int prot, int access) {
if (access == ACCESS_DEFAULT) {
if ((flags & (MAP_PRIVATE | MAP_SHARED)) == 0) {
throw PythonOps.OSError(PythonErrno.EINVAL, "Invalid argument");
throw PythonNT.GetOsError(PythonErrno.EINVAL);
}
if ((prot & PROT_WRITE) != 0) {
prot |= PROT_READ;
Expand Down Expand Up @@ -339,13 +338,13 @@ public MmapDefault(CodeContext/*!*/ context, int fileno, long length, string tag
#if NET8_0_OR_GREATER
// On .NET 8.0+ we can create a MemoryMappedFile directly from a file descriptor
stream.Flush();
CheckFileAccessAndSize(stream);
fileno = Dup(fileno);
CheckFileAccessAndSize(stream, isWindows: false);
fileno = PythonNT.dupUnix(fileno, closeOnExec: true);
_handle = new SafeFileHandle((IntPtr)fileno, ownsHandle: true);
_file = MemoryMappedFile.CreateFromFile(_handle, _mapName, stream.Length, _fileAccess, HandleInheritability.None, leaveOpen: true);
#else
// On .NET 6.0 on POSIX we need to create a FileStream from the file descriptor
fileno = Dup(fileno);
fileno = PythonNT.dupUnix(fileno, closeOnExec: true);
_handle = new SafeFileHandle((IntPtr)fileno, ownsHandle: true);
FileAccess fa = stream.CanWrite ? stream.CanRead ? FileAccess.ReadWrite : FileAccess.Write : FileAccess.Read;
// This FileStream constructor may or may not work on Mono, but on Mono streams.ReadStream is FileStream
Expand All @@ -356,7 +355,7 @@ public MmapDefault(CodeContext/*!*/ context, int fileno, long length, string tag
}
// otherwise leaves _file as null and _sourceStream as null
} else {
throw PythonOps.OSError(PythonExceptions._OSError.ERROR_INVALID_BLOCK, "Bad file descriptor");
throw PythonNT.GetOsError(PythonErrno.EBADF);
}

if (_file is null) {
Expand All @@ -369,7 +368,7 @@ public MmapDefault(CodeContext/*!*/ context, int fileno, long length, string tag
length = _sourceStream.Length - _offset;
}

CheckFileAccessAndSize(_sourceStream);
CheckFileAccessAndSize(_sourceStream, RuntimeInformation.IsOSPlatform(OSPlatform.Windows));

long capacity = checked(_offset + length);

Expand Down Expand Up @@ -402,7 +401,7 @@ public MmapDefault(CodeContext/*!*/ context, int fileno, long length, string tag
}
_position = 0L;

void CheckFileAccessAndSize(Stream stream) {
void CheckFileAccessAndSize(Stream stream, bool isWindows) {
bool isValid = _fileAccess switch {
MemoryMappedFileAccess.Read => stream.CanRead,
MemoryMappedFileAccess.ReadWrite => stream.CanRead && stream.CanWrite,
Expand All @@ -414,10 +413,10 @@ void CheckFileAccessAndSize(Stream stream) {

try {
if (!isValid) {
throw PythonOps.OSError(PythonExceptions._OSError.ERROR_ACCESS_DENIED, "Invalid access mode");
throw WindowsError(PythonExceptions._OSError.ERROR_ACCESS_DENIED);
}

if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
if (!isWindows) {
// Unix map does not support increasing size on open
if (length != 0 && _offset + length > stream.Length) {
throw PythonOps.ValueError("mmap length is greater than file size");
Expand All @@ -437,29 +436,6 @@ void CheckFileAccessAndSize(Stream stream) {
} // end of constructor


// TODO: Move to PythonNT - POSIX
private static int Dup(int fd) {
int fd2 = Mono.Unix.Native.Syscall.dup(fd);
if (fd2 == -1) throw PythonNT.GetLastUnixError();

try {
// set close-on-exec flag
int flags = Mono.Unix.Native.Syscall.fcntl(fd2, Mono.Unix.Native.FcntlCommand.F_GETFD);
if (flags == -1) throw PythonNT.GetLastUnixError();

const int FD_CLOEXEC = 1; // TODO: Move to module fcntl
flags |= FD_CLOEXEC;
flags = Mono.Unix.Native.Syscall.fcntl(fd2, Mono.Unix.Native.FcntlCommand.F_SETFD, flags);
if (flags == -1) throw PythonNT.GetLastUnixError();
} catch {
Mono.Unix.Native.Syscall.close(fd2);
throw;
}

return fd2;
}


public object __len__() {
using (new MmapLocker(this)) {
return ReturnLong(_view.Capacity);
Expand Down Expand Up @@ -825,12 +801,20 @@ public void resize(long newsize) {
throw PythonOps.TypeError("mmap can't resize a readonly or copy-on-write memory map.");
}

if (newsize < 0) {
throw PythonOps.ValueError("new size out of range");
}

if (_handle is not null
&& (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux))) {
// resize on Posix platforms
try {
if (_handle.IsInvalid) {
throw PythonOps.OSError(PythonErrno.EBADF, "Bad file descriptor");
throw PythonNT.GetOsError(PythonErrno.EBADF);
}
if (newsize == 0) {
// resizing to an empty mapped region is not allowed
throw PythonNT.GetOsError(PythonErrno.EINVAL);
}
_view.Flush();
_view.Dispose();
Expand Down Expand Up @@ -1132,20 +1116,18 @@ internal Bytes GetSearchString() {
}
}

[SupportedOSPlatform("linux"), SupportedOSPlatform("macos")]
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("macos")]
private static long GetFileSizeUnix(SafeFileHandle handle) {
long size;
if (handle.IsInvalid) {
throw PythonOps.OSError(PythonExceptions._OSError.ERROR_INVALID_HANDLE, "Invalid file handle");
throw PythonNT.GetOsError(PythonErrno.EBADF);
}

if (Mono.Unix.Native.Syscall.fstat((int)handle.DangerousGetHandle(), out Mono.Unix.Native.Stat status) == 0) {
size = status.st_size;
} else {
Mono.Unix.Native.Errno errno = Mono.Unix.Native.Stdlib.GetLastError();
string msg = Mono.Unix.UnixMarshal.GetErrorDescription(errno);
int error = Mono.Unix.Native.NativeConvert.FromErrno(errno);
throw PythonOps.OSError(error, msg);
throw PythonNT.GetLastUnixError();
}

return size;
Expand Down
Loading

0 comments on commit 1cd8e71

Please sign in to comment.