From 5b37b822d9bb14b5ff7853f552bc7ee0420eff57 Mon Sep 17 00:00:00 2001 From: Pavel Koneski Date: Wed, 21 Oct 2026 14:53:41 -0700 Subject: [PATCH] Implement umask on POSIX --- src/core/IronPython.Modules/nt.cs | 4 ++++ src/core/IronPython.Modules/posix.cs | 6 ++++++ tests/suite/modules/system_related/test_os.py | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/src/core/IronPython.Modules/nt.cs b/src/core/IronPython.Modules/nt.cs index 225fc2109..f5aa8aca5 100644 --- a/src/core/IronPython.Modules/nt.cs +++ b/src/core/IronPython.Modules/nt.cs @@ -1898,6 +1898,10 @@ public static object urandom(object? n) private static readonly object _umaskKey = new object(); public static int umask(CodeContext/*!*/ context, int mask) { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { + return umaskUnix(mask & 0x1FF); + } + mask &= 0x180; object oldMask = context.LanguageContext.GetSetModuleState(_umaskKey, mask); if (oldMask == null) { diff --git a/src/core/IronPython.Modules/posix.cs b/src/core/IronPython.Modules/posix.cs index fe802fcfa..c59b33d36 100644 --- a/src/core/IronPython.Modules/posix.cs +++ b/src/core/IronPython.Modules/posix.cs @@ -123,6 +123,12 @@ private static void chmodUnix(string path, int mode) { } + [SupportedOSPlatform("linux")] + [SupportedOSPlatform("macos")] + private static int umaskUnix(int mask) + => (int)NativeConvert.FromFilePermissions(Syscall.umask(NativeConvert.ToFilePermissions((uint)mask))); + + [SupportedOSPlatform("linux")] [SupportedOSPlatform("macos")] private static void linkUnix(string src, string dst) { diff --git a/tests/suite/modules/system_related/test_os.py b/tests/suite/modules/system_related/test_os.py index 1f77237d6..18e18b81a 100644 --- a/tests/suite/modules/system_related/test_os.py +++ b/tests/suite/modules/system_related/test_os.py @@ -3,6 +3,7 @@ # See the LICENSE file in the project root for more information. import os +import unittest from iptest import IronPythonTestCase, is_osx, is_linux, is_windows, run_test @@ -55,4 +56,21 @@ def test_open_abplus(self): finally: os.close(fd) + @unittest.skipIf(is_windows, "umask is emulated on Windows") + def test_umask(self): + old = os.umask(0o022) + try: + self.assertEqual(os.umask(0o077), 0o022) + self.assertEqual(os.umask(0o022), 0o077) + + os.umask(0o027) + try: + fd = os.open(self.temp_file, os.O_CREAT | os.O_WRONLY, 0o666) + os.close(fd) + self.assertEqual(os.stat(self.temp_file).st_mode & 0o777, 0o640) + finally: + self.delete_files(self.temp_file) + finally: + os.umask(old) + run_test(__name__)