Skip to content

Commit fcc8db8

Browse files
committed
Isolate POSIX-specific native calls in PythonNT into separate file
1 parent 9082851 commit fcc8db8

File tree

3 files changed

+222
-150
lines changed

3 files changed

+222
-150
lines changed

Src/IronPython.Modules/mmap.cs

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -339,13 +339,13 @@ public MmapDefault(CodeContext/*!*/ context, int fileno, long length, string tag
339339
#if NET8_0_OR_GREATER
340340
// On .NET 8.0+ we can create a MemoryMappedFile directly from a file descriptor
341341
stream.Flush();
342-
CheckFileAccessAndSize(stream);
343-
fileno = Dup(fileno);
342+
CheckFileAccessAndSize(stream, isWindows: false);
343+
fileno = PythonNT.dupUnix(fileno, closeOnExec: true);
344344
_handle = new SafeFileHandle((IntPtr)fileno, ownsHandle: true);
345345
_file = MemoryMappedFile.CreateFromFile(_handle, _mapName, stream.Length, _fileAccess, HandleInheritability.None, leaveOpen: true);
346346
#else
347347
// On .NET 6.0 on POSIX we need to create a FileStream from the file descriptor
348-
fileno = Dup(fileno);
348+
fileno = PythonNT.dupUnix(fileno, closeOnExec: true);
349349
_handle = new SafeFileHandle((IntPtr)fileno, ownsHandle: true);
350350
FileAccess fa = stream.CanWrite ? stream.CanRead ? FileAccess.ReadWrite : FileAccess.Write : FileAccess.Read;
351351
// This FileStream constructor may or may not work on Mono, but on Mono streams.ReadStream is FileStream
@@ -369,7 +369,7 @@ public MmapDefault(CodeContext/*!*/ context, int fileno, long length, string tag
369369
length = _sourceStream.Length - _offset;
370370
}
371371

372-
CheckFileAccessAndSize(_sourceStream);
372+
CheckFileAccessAndSize(_sourceStream, RuntimeInformation.IsOSPlatform(OSPlatform.Windows));
373373

374374
long capacity = checked(_offset + length);
375375

@@ -402,7 +402,7 @@ public MmapDefault(CodeContext/*!*/ context, int fileno, long length, string tag
402402
}
403403
_position = 0L;
404404

405-
void CheckFileAccessAndSize(Stream stream) {
405+
void CheckFileAccessAndSize(Stream stream, bool isWindows) {
406406
bool isValid = _fileAccess switch {
407407
MemoryMappedFileAccess.Read => stream.CanRead,
408408
MemoryMappedFileAccess.ReadWrite => stream.CanRead && stream.CanWrite,
@@ -417,7 +417,7 @@ void CheckFileAccessAndSize(Stream stream) {
417417
throw PythonOps.OSError(PythonExceptions._OSError.ERROR_ACCESS_DENIED, "Invalid access mode");
418418
}
419419

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

439439

440-
// TODO: Move to PythonNT - POSIX
441-
private static int Dup(int fd) {
442-
int fd2 = Mono.Unix.Native.Syscall.dup(fd);
443-
if (fd2 == -1) throw PythonNT.GetLastUnixError();
444-
445-
try {
446-
// set close-on-exec flag
447-
int flags = Mono.Unix.Native.Syscall.fcntl(fd2, Mono.Unix.Native.FcntlCommand.F_GETFD);
448-
if (flags == -1) throw PythonNT.GetLastUnixError();
449-
450-
const int FD_CLOEXEC = 1; // TODO: Move to module fcntl
451-
flags |= FD_CLOEXEC;
452-
flags = Mono.Unix.Native.Syscall.fcntl(fd2, Mono.Unix.Native.FcntlCommand.F_SETFD, flags);
453-
if (flags == -1) throw PythonNT.GetLastUnixError();
454-
} catch {
455-
Mono.Unix.Native.Syscall.close(fd2);
456-
throw;
457-
}
458-
459-
return fd2;
460-
}
461-
462-
463440
public object __len__() {
464441
using (new MmapLocker(this)) {
465442
return ReturnLong(_view.Capacity);

Src/IronPython.Modules/nt.cs

Lines changed: 13 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
[assembly: PythonModule("nt", typeof(IronPython.Modules.PythonNT))]
3838
namespace IronPython.Modules {
39-
public static class PythonNT {
39+
public static partial class PythonNT {
4040
public const string __doc__ = "Provides low-level operating system access for files, the environment, etc...";
4141

4242
/* TODO: missing functions/classes:
@@ -301,12 +301,6 @@ public static void chdir(CodeContext context, [NotNone] Bytes path)
301301
public static void chdir(CodeContext context, object? path)
302302
=> chdir(ConvertToFsString(context, path, nameof(path)));
303303

304-
// Isolate Mono.Unix from the rest of the method so that we don't try to load the Mono.Unix assembly on Windows.
305-
private static void chmodUnix(string path, int mode) {
306-
if (Mono.Unix.Native.Syscall.chmod(path, Mono.Unix.Native.NativeConvert.ToFilePermissions((uint)mode)) == 0) return;
307-
throw GetLastUnixError(path);
308-
}
309-
310304
[Documentation("chmod(path, mode, *, dir_fd=None, follow_symlinks=True)")]
311305
public static void chmod([NotNone] string path, int mode, [ParamDictionary, NotNone] IDictionary<string, object> kwargs) {
312306
foreach (var key in kwargs.Keys) {
@@ -371,7 +365,7 @@ public static int dup(CodeContext/*!*/ context, int fd) {
371365

372366
StreamBox streams = fileManager.GetStreams(fd); // OSError if fd not valid
373367
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
374-
int fd2 = UnixDup(fd, -1, out Stream? dupstream);
368+
int fd2 = DuplicateStreamDescriptorUnix(fd, -1, out Stream? dupstream);
375369
if (dupstream is not null) {
376370
return fileManager.Add(fd2, new(dupstream));
377371
} else {
@@ -413,7 +407,7 @@ public static int dup2(CodeContext/*!*/ context, int fd, int fd2) {
413407
fd = fs.SafeFileHandle.DangerousGetHandle().ToInt32();
414408
fs.Seek(pos, SeekOrigin.Begin);
415409
}
416-
fd2 = UnixDup(fd, fd2, out Stream? dupstream); // closes fd2 atomically if reopened in the meantime
410+
fd2 = DuplicateStreamDescriptorUnix(fd, fd2, out Stream? dupstream); // closes fd2 atomically if reopened in the meantime
417411
fileManager.Remove(fd2);
418412
if (dupstream is not null) {
419413
return fileManager.Add(fd2, new(dupstream));
@@ -431,41 +425,6 @@ public static int dup2(CodeContext/*!*/ context, int fd, int fd2) {
431425
}
432426

433427

434-
[SupportedOSPlatform("linux"), SupportedOSPlatform("osx")]
435-
private static int UnixDup(int fd, int fd2, out Stream? stream) {
436-
int res = fd2 < 0 ? Mono.Unix.Native.Syscall.dup(fd) : Mono.Unix.Native.Syscall.dup2(fd, fd2);
437-
if (res < 0) throw GetLastUnixError();
438-
if (ClrModule.IsMono) {
439-
// Elaborate workaround on Mono to avoid UnixStream as out
440-
stream = new Mono.Unix.UnixStream(res, ownsHandle: false);
441-
FileAccess fileAccess = stream.CanWrite ? stream.CanRead ? FileAccess.ReadWrite : FileAccess.Write : FileAccess.Read;
442-
stream.Dispose();
443-
try {
444-
// FileStream on Mono created with a file descriptor might not work: https://github.com/mono/mono/issues/12783
445-
// Test if it does, without closing the handle if it doesn't
446-
var sfh = new SafeFileHandle((IntPtr)res, ownsHandle: false);
447-
stream = new FileStream(sfh, fileAccess);
448-
// No exception? Great! We can use FileStream.
449-
stream.Dispose();
450-
sfh.Dispose();
451-
stream = null; // Create outside of try block
452-
} catch (IOException) {
453-
// Fall back to UnixStream
454-
stream = new Mono.Unix.UnixStream(res, ownsHandle: true);
455-
}
456-
if (stream is null) {
457-
// FileStream is safe
458-
var sfh = new SafeFileHandle((IntPtr)res, ownsHandle: true);
459-
stream = new FileStream(sfh, fileAccess);
460-
}
461-
} else {
462-
// normal case
463-
stream = new PosixFileStream(res);
464-
}
465-
return res;
466-
}
467-
468-
469428
#if FEATURE_PROCESS
470429
/// <summary>
471430
/// single instance of environment dictionary is shared between multiple runtimes because the environment
@@ -563,11 +522,6 @@ static void linkWindows(string src, string dst) {
563522
if (!CreateHardLink(dst, src, IntPtr.Zero))
564523
throw GetLastWin32Error(src, dst);
565524
}
566-
567-
static void linkUnix(string src, string dst) {
568-
if (Mono.Unix.Native.Syscall.link(src, dst) == 0) return;
569-
throw GetLastUnixError(src, dst);
570-
}
571525
}
572526

573527
public static bool isatty(CodeContext context, int fd) {
@@ -779,11 +733,6 @@ public static void symlink([NotNone] string src, [NotNone] string dst, [ParamDic
779733
} else {
780734
throw new NotImplementedException();
781735
}
782-
783-
static void symlinkUnix(string src, string dst) {
784-
if (Mono.Unix.Native.Syscall.symlink(src, dst) == 0) return;
785-
throw GetLastUnixError(src, dst);
786-
}
787736
}
788737

789738
[Documentation("")]
@@ -1012,17 +961,14 @@ public static PythonTuple pipe(CodeContext context) {
1012961
manager.Add(new(inPipe)),
1013962
manager.Add(new(outPipe))
1014963
);
1015-
} else {
964+
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
1016965
var pipeStreams = CreatePipeStreamsUnix();
1017966
return PythonTuple.MakeTuple(
1018967
manager.Add(pipeStreams.Item1, new(pipeStreams.Item2)),
1019968
manager.Add(pipeStreams.Item3, new(pipeStreams.Item4))
1020969
);
1021-
}
1022-
1023-
static Tuple<int, Stream, int, Stream> CreatePipeStreamsUnix() {
1024-
Mono.Unix.UnixPipes pipes = Mono.Unix.UnixPipes.CreatePipes();
1025-
return Tuple.Create<int, Stream, int, Stream>(pipes.Reading.Handle, pipes.Reading, pipes.Writing.Handle, pipes.Writing);
970+
} else {
971+
throw new PlatformNotSupportedException();
1026972
}
1027973
}
1028974
#endif
@@ -1094,11 +1040,6 @@ public static void rename(CodeContext context, object? src, object? dst, [ParamD
10941040
[DllImport("kernel32.dll", EntryPoint = "MoveFileExW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)]
10951041
private static extern bool MoveFileEx(string src, string dst, uint flags);
10961042

1097-
private static void renameUnix(string src, string dst) {
1098-
if (Mono.Unix.Native.Syscall.rename(src, dst) == 0) return;
1099-
throw GetLastUnixError(src, dst);
1100-
}
1101-
11021043
[Documentation("replace(src, dst, *, src_dir_fd=None, dst_dir_fd=None)")]
11031044
public static void replace([NotNone] string src, [NotNone] string dst, [ParamDictionary, NotNone] IDictionary<string, object> kwargs) {
11041045
foreach (var key in kwargs.Keys) {
@@ -1467,21 +1408,6 @@ private static bool HasExecutableExtension(string path) {
14671408
return (extension == ".exe" || extension == ".dll" || extension == ".com" || extension == ".bat");
14681409
}
14691410

1470-
// Isolate Mono.Unix from the rest of the method so that we don't try to load the Mono.Unix assembly on Windows.
1471-
private static object statUnix(string path) {
1472-
if (Mono.Unix.Native.Syscall.stat(path, out Mono.Unix.Native.Stat buf) == 0) {
1473-
return new stat_result(buf);
1474-
}
1475-
return LightExceptions.Throw(GetLastUnixError(path));
1476-
}
1477-
1478-
private static object fstatUnix(int fd) {
1479-
if (Mono.Unix.Native.Syscall.fstat(fd, out Mono.Unix.Native.Stat buf) == 0) {
1480-
return new stat_result(buf);
1481-
}
1482-
return LightExceptions.Throw(GetLastUnixError());
1483-
}
1484-
14851411
private const int OPEN_EXISTING = 3;
14861412
private const int FILE_ATTRIBUTE_NORMAL = 0x00000080;
14871413
private const int FILE_READ_ATTRIBUTES = 0x0080;
@@ -1616,9 +1542,12 @@ public static string strerror(int code) {
16161542
const int bufsize = 0x1FF;
16171543
var buffer = new StringBuilder(bufsize);
16181544

1619-
int result = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
1620-
Interop.Ucrtbase.strerror(code, buffer) :
1621-
strerror_r(code, buffer);
1545+
int result = -1;
1546+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
1547+
result = Interop.Ucrtbase.strerror(code, buffer);
1548+
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
1549+
result = strerror_r(code, buffer);
1550+
}
16221551

16231552
if (result == 0) {
16241553
var msg = buffer.ToString();
@@ -1630,12 +1559,6 @@ public static string strerror(int code) {
16301559
return "Unknown error " + code;
16311560
}
16321561

1633-
#if FEATURE_NATIVE
1634-
// Isolate Mono.Unix from the rest of the method so that we don't try to load the Mono.Unix assembly on Windows.
1635-
private static int strerror_r(int code, StringBuilder buffer)
1636-
=> Mono.Unix.Native.Syscall.strerror_r(Mono.Unix.Native.NativeConvert.ToErrno(code), buffer);
1637-
#endif
1638-
16391562
#if FEATURE_PROCESS
16401563
[Documentation("system(command) -> int\nExecute the command (a string) in a subshell.")]
16411564
public static int system([NotNone] string command) {
@@ -1724,18 +1647,6 @@ public static void ftruncate(CodeContext context, int fd, BigInteger length) {
17241647
}
17251648

17261649

1727-
[SupportedOSPlatform("linux"), SupportedOSPlatform("osx")]
1728-
internal static void ftruncateUnix(int fd, long length) {
1729-
int result;
1730-
Mono.Unix.Native.Errno errno;
1731-
do {
1732-
result = Mono.Unix.Native.Syscall.ftruncate(fd, length);
1733-
} while (Mono.Unix.UnixMarshal.ShouldRetrySyscall(result, out errno));
1734-
1735-
if (errno != 0)
1736-
throw GetOsError(Mono.Unix.Native.NativeConvert.FromErrno(errno));
1737-
}
1738-
17391650

17401651
#if FEATURE_FILESYSTEM
17411652
public static object times() {
@@ -1841,18 +1752,6 @@ public static int umask(CodeContext/*!*/ context, object? mask)
18411752

18421753
#if FEATURE_FILESYSTEM
18431754

1844-
private static void utimeUnix(string path, long atime_ns, long utime_ns) {
1845-
var atime = new Mono.Unix.Native.Timespec();
1846-
atime.tv_sec = atime_ns / 1_000_000_000;
1847-
atime.tv_nsec = atime_ns % 1_000_000_000;
1848-
var utime = new Mono.Unix.Native.Timespec();
1849-
utime.tv_sec = utime_ns / 1_000_000_000;
1850-
utime.tv_nsec = utime_ns % 1_000_000_000;
1851-
1852-
if (Mono.Unix.Native.Syscall.utimensat(Mono.Unix.Native.Syscall.AT_FDCWD, path, new[] { atime, utime }, 0) == 0) return;
1853-
throw GetLastUnixError(path);
1854-
}
1855-
18561755
[Documentation("utime(path, times=None, *[, ns], dir_fd=None, follow_symlinks=True)")]
18571756
public static void utime([NotNone] string path, [ParamDictionary, NotNone] IDictionary<string, object> kwargs, [NotNone] params object[] args) {
18581757
var numArgs = args.Length;
@@ -1979,8 +1878,7 @@ public static int write(CodeContext/*!*/ context, int fd, [NotNone] IBufferProto
19791878
public static void kill(CodeContext/*!*/ context, int pid, int sig) {
19801879
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
19811880
RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
1982-
if (Mono.Unix.Native.Syscall.kill(pid, Mono.Unix.Native.NativeConvert.ToSignum(sig)) == 0) return;
1983-
throw GetLastUnixError();
1881+
killUnix(pid, sig);
19841882
} else {
19851883
if (PythonSignal.NativeSignal.GenerateConsoleCtrlEvent((uint)sig, (uint)pid)) return;
19861884

@@ -2414,12 +2312,6 @@ private static Exception DirectoryExistsError(string? filename) {
24142312
return GetOsError(PythonErrno.EEXIST, filename);
24152313
}
24162314

2417-
#if FEATURE_NATIVE
2418-
2419-
internal static Exception GetLastUnixError(string? filename = null, string? filename2 = null)
2420-
=> GetOsError(Mono.Unix.Native.NativeConvert.FromErrno(Mono.Unix.Native.Syscall.GetLastError()), filename, filename2);
2421-
2422-
#endif
24232315

24242316
private static Exception GetOsError(int error, string? filename = null, string? filename2 = null)
24252317
=> PythonOps.OSError(error, strerror(error), filename, null, filename2);

0 commit comments

Comments
 (0)