diff --git a/Doc/library/stat.rst b/Doc/library/stat.rst index 77538514598..c941d5557e3 100644 --- a/Doc/library/stat.rst +++ b/Doc/library/stat.rst @@ -350,6 +350,12 @@ The following flags can also be used in the *mode* argument of :func:`os.chmod`: The following flags can be used in the *flags* argument of :func:`os.chflags`: +.. data:: UF_SETTABLE + + All user settable flags. + + .. versionadded: 3.13 + .. data:: UF_NODUMP Do not dump the file. @@ -374,10 +380,44 @@ The following flags can be used in the *flags* argument of :func:`os.chflags`: The file is stored compressed (macOS 10.6+). +.. data:: UF_TRACKED + + Used for handling document IDs (macOS) + + .. versionadded: 3.13 + +.. data:: UF_DATAVAULT + + The file needs an entitlement for reading or writing (macOS 10.13+) + + .. versionadded: 3.13 + .. data:: UF_HIDDEN The file should not be displayed in a GUI (macOS 10.5+). +.. data:: SF_SETTABLE + + All super-user changeable flags + + .. versionadded: 3.13 + +.. data:: SF_SUPPORTED + + All super-user supported flags + + .. availability:: macOS + + .. versionadded: 3.13 + +.. data:: SF_SYNTHETIC + + All super-user read-only synthetic flags + + .. availability:: macOS + + .. versionadded: 3.13 + .. data:: SF_ARCHIVED The file may be archived. @@ -390,6 +430,12 @@ The following flags can be used in the *flags* argument of :func:`os.chflags`: The file may only be appended to. +.. data:: SF_RESTRICTED + + The file needs an entitlement to write to (macOS 10.13+) + + .. versionadded: 3.13 + .. data:: SF_NOUNLINK The file may not be renamed or deleted. @@ -398,6 +444,18 @@ The following flags can be used in the *flags* argument of :func:`os.chflags`: The file is a snapshot file. +.. data:: SF_FIRMLINK + + The file is a firmlink (macOS 10.15+) + + .. versionadded: 3.13 + +.. data:: SF_DATALESS + + The file is a dataless object (macOS 10.15+) + + .. versionadded: 3.13 + See the \*BSD or macOS systems man page :manpage:`chflags(2)` for more information. On Windows, the following file attribute constants are available for use when diff --git a/Lib/stat.py b/Lib/stat.py index 52cadbf04f6..9167ab18594 100644 --- a/Lib/stat.py +++ b/Lib/stat.py @@ -2,6 +2,7 @@ Suggested usage: from stat import * """ +import sys # Indices for stat struct members in the tuple returned by os.stat() @@ -110,19 +111,25 @@ S_IWOTH = 0o0002 # write by others S_IXOTH = 0o0001 # execute by others # Names for file flags - +UF_SETTABLE = 0x0000ffff # owner settable flags UF_NODUMP = 0x00000001 # do not dump file UF_IMMUTABLE = 0x00000002 # file may not be changed UF_APPEND = 0x00000004 # file may only be appended to UF_OPAQUE = 0x00000008 # directory is opaque when viewed through a union stack UF_NOUNLINK = 0x00000010 # file may not be renamed or deleted -UF_COMPRESSED = 0x00000020 # OS X: file is hfs-compressed -UF_HIDDEN = 0x00008000 # OS X: file should not be displayed +UF_COMPRESSED = 0x00000020 # macOS: file is compressed +UF_TRACKED = 0x00000040 # macOS: used for handling document IDs +UF_DATAVAULT = 0x00000080 # macOS: entitlement needed for I/O +UF_HIDDEN = 0x00008000 # macOS: file should not be displayed +SF_SETTABLE = 0xffff0000 # superuser settable flags SF_ARCHIVED = 0x00010000 # file may be archived SF_IMMUTABLE = 0x00020000 # file may not be changed SF_APPEND = 0x00040000 # file may only be appended to +SF_RESTRICTED = 0x00080000 # macOS: entitlement needed for writing SF_NOUNLINK = 0x00100000 # file may not be renamed or deleted SF_SNAPSHOT = 0x00200000 # file is a snapshot file +SF_FIRMLINK = 0x00800000 # macOS: file is a firmlink +SF_DATALESS = 0x40000000 # macOS: file is a dataless object _filemode_table = ( diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py index a0d0f61e5a1..d6b6dd6e741 100644 --- a/Lib/test/test_stat.py +++ b/Lib/test/test_stat.py @@ -15,8 +15,10 @@ class TestFilemode: statmod = None file_flags = {'SF_APPEND', 'SF_ARCHIVED', 'SF_IMMUTABLE', 'SF_NOUNLINK', - 'SF_SNAPSHOT', 'UF_APPEND', 'UF_COMPRESSED', 'UF_HIDDEN', - 'UF_IMMUTABLE', 'UF_NODUMP', 'UF_NOUNLINK', 'UF_OPAQUE'} + 'SF_SNAPSHOT', 'SF_SETTABLE', 'SF_RESTRICTED', 'SF_FIRMLINK', + 'SF_DATALESS', 'UF_APPEND', 'UF_COMPRESSED', 'UF_HIDDEN', + 'UF_IMMUTABLE', 'UF_NODUMP', 'UF_NOUNLINK', 'UF_OPAQUE', + 'UF_SETTABLE', 'UF_TRACKED', 'UF_DATAVAULT'} formats = {'S_IFBLK', 'S_IFCHR', 'S_IFDIR', 'S_IFIFO', 'S_IFLNK', 'S_IFREG', 'S_IFSOCK', 'S_IFDOOR', 'S_IFPORT', 'S_IFWHT'} @@ -239,6 +241,18 @@ class TestFilemode: self.assertTrue(callable(func)) self.assertEqual(func(0), 0) + def test_flags_consistent(self): + self.assertFalse(self.statmod.UF_SETTABLE & self.statmod.SF_SETTABLE) + + for flag in self.file_flags: + if flag.startswith("UF"): + self.assertTrue(getattr(self.statmod, flag) & self.statmod.UF_SETTABLE, f"{flag} not in UF_SETTABLE") + elif sys.platform == 'darwin' and self.statmod is c_stat and flag == 'SF_DATALESS': + self.assertTrue(self.statmod.SF_DATALESS & self.statmod.SF_SYNTHETIC, "SF_DATALESS not in SF_SYNTHETIC") + self.assertFalse(self.statmod.SF_DATALESS & self.statmod.SF_SETTABLE, "SF_DATALESS in SF_SETTABLE") + else: + self.assertTrue(getattr(self.statmod, flag) & self.statmod.SF_SETTABLE, f"{flag} notin SF_SETTABLE") + @unittest.skipUnless(sys.platform == "win32", "FILE_ATTRIBUTE_* constants are Win32 specific") def test_file_attribute_constants(self): @@ -247,6 +261,66 @@ class TestFilemode: modvalue = getattr(self.statmod, key) self.assertEqual(value, modvalue, key) + @unittest.skipUnless(sys.platform == "darwin", "macOS system check") + def test_macosx_attribute_values(self): + self.assertEqual(self.statmod.UF_SETTABLE, 0x0000ffff) + self.assertEqual(self.statmod.UF_NODUMP, 0x00000001) + self.assertEqual(self.statmod.UF_IMMUTABLE, 0x00000002) + self.assertEqual(self.statmod.UF_APPEND, 0x00000004) + self.assertEqual(self.statmod.UF_OPAQUE, 0x00000008) + self.assertEqual(self.statmod.UF_COMPRESSED, 0x00000020) + self.assertEqual(self.statmod.UF_TRACKED, 0x00000040) + self.assertEqual(self.statmod.UF_DATAVAULT, 0x00000080) + self.assertEqual(self.statmod.UF_HIDDEN, 0x00008000) + + if self.statmod is c_stat: + self.assertEqual(self.statmod.SF_SUPPORTED, 0x009f0000) + self.assertEqual(self.statmod.SF_SETTABLE, 0x3fff0000) + self.assertEqual(self.statmod.SF_SYNTHETIC, 0xc0000000) + else: + self.assertEqual(self.statmod.SF_SETTABLE, 0xffff0000) + self.assertEqual(self.statmod.SF_ARCHIVED, 0x00010000) + self.assertEqual(self.statmod.SF_IMMUTABLE, 0x00020000) + self.assertEqual(self.statmod.SF_APPEND, 0x00040000) + self.assertEqual(self.statmod.SF_RESTRICTED, 0x00080000) + self.assertEqual(self.statmod.SF_NOUNLINK, 0x00100000) + self.assertEqual(self.statmod.SF_FIRMLINK, 0x00800000) + self.assertEqual(self.statmod.SF_DATALESS, 0x40000000) + + self.assertFalse(isinstance(self.statmod.S_IFMT, int)) + self.assertEqual(self.statmod.S_IFIFO, 0o010000) + self.assertEqual(self.statmod.S_IFCHR, 0o020000) + self.assertEqual(self.statmod.S_IFDIR, 0o040000) + self.assertEqual(self.statmod.S_IFBLK, 0o060000) + self.assertEqual(self.statmod.S_IFREG, 0o100000) + self.assertEqual(self.statmod.S_IFLNK, 0o120000) + self.assertEqual(self.statmod.S_IFSOCK, 0o140000) + + if self.statmod is c_stat: + self.assertEqual(self.statmod.S_IFWHT, 0o160000) + + self.assertEqual(self.statmod.S_IRWXU, 0o000700) + self.assertEqual(self.statmod.S_IRUSR, 0o000400) + self.assertEqual(self.statmod.S_IWUSR, 0o000200) + self.assertEqual(self.statmod.S_IXUSR, 0o000100) + self.assertEqual(self.statmod.S_IRWXG, 0o000070) + self.assertEqual(self.statmod.S_IRGRP, 0o000040) + self.assertEqual(self.statmod.S_IWGRP, 0o000020) + self.assertEqual(self.statmod.S_IXGRP, 0o000010) + self.assertEqual(self.statmod.S_IRWXO, 0o000007) + self.assertEqual(self.statmod.S_IROTH, 0o000004) + self.assertEqual(self.statmod.S_IWOTH, 0o000002) + self.assertEqual(self.statmod.S_IXOTH, 0o000001) + self.assertEqual(self.statmod.S_ISUID, 0o004000) + self.assertEqual(self.statmod.S_ISGID, 0o002000) + self.assertEqual(self.statmod.S_ISVTX, 0o001000) + + self.assertFalse(hasattr(self.statmod, "S_ISTXT")) + self.assertEqual(self.statmod.S_IREAD, self.statmod.S_IRUSR) + self.assertEqual(self.statmod.S_IWRITE, self.statmod.S_IWUSR) + self.assertEqual(self.statmod.S_IEXEC, self.statmod.S_IXUSR) + + @unittest.skipIf(c_stat is None, 'need _stat extension') class TestFilemodeCStat(TestFilemode, unittest.TestCase): diff --git a/Misc/NEWS.d/next/macOS/2024-01-02-22-25-21.gh-issue-113666.xKZoBm.rst b/Misc/NEWS.d/next/macOS/2024-01-02-22-25-21.gh-issue-113666.xKZoBm.rst new file mode 100644 index 00000000000..4be0021eed3 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2024-01-02-22-25-21.gh-issue-113666.xKZoBm.rst @@ -0,0 +1,5 @@ +Add the following constants to module :mod:`stat`: ``UF_SETTABLE``, +``UF_TRACKED``, ``UF_DATAVAULT``, ``SF_SUPPORTED``, ``SF_SETTABLE``, +``SF_SYNTHETIC``, ``SF_RESTRICTED``, ``SF_FIRMLINK`` and ``SF_DATALESS``. +The values ``UF_SETTABLE``, ``SF_SUPPORTED``, ``SF_SETTABLE`` and +``SF_SYNTHETIC`` are only available on macOS. diff --git a/Modules/_stat.c b/Modules/_stat.c index 80f8a926689..b43e79453f5 100644 --- a/Modules/_stat.c +++ b/Modules/_stat.c @@ -202,6 +202,10 @@ typedef unsigned short mode_t; /* Names for file flags */ +#ifndef UF_SETTABLE +# define UF_SETTABLE 0x0000ffff +#endif + #ifndef UF_NODUMP # define UF_NODUMP 0x00000001 #endif @@ -226,10 +230,22 @@ typedef unsigned short mode_t; # define UF_COMPRESSED 0x00000020 #endif +#ifndef UF_TRACKED +# define UF_TRACKED 0x00000040 +#endif + +#ifndef UF_DATAVAULT +# define UF_DATAVAULT 0x00000080 +#endif + #ifndef UF_HIDDEN # define UF_HIDDEN 0x00008000 #endif +#ifndef SF_SETTABLE +# define SF_SETTABLE 0xffff0000 +#endif + #ifndef SF_ARCHIVED # define SF_ARCHIVED 0x00010000 #endif @@ -250,6 +266,30 @@ typedef unsigned short mode_t; # define SF_SNAPSHOT 0x00200000 #endif +#ifndef SF_FIRMLINK +# define SF_FIRMLINK 0x00800000 +#endif + +#ifndef SF_DATALESS +# define SF_DATALESS 0x40000000 +#endif + +#if defined(__APPLE__) && !defined(SF_SUPPORTED) + /* On older macOS versions the definition of SF_SUPPORTED is different + * from that on newer versions. + * + * Provide a consistent experience by redefining. + * + * None of bit bits set in the actual SF_SUPPORTED but not in this + * definition are defined on these versions of macOS. + */ +# undef SF_SETTABLE +# define SF_SUPPORTED 0x009f0000 +# define SF_SETTABLE 0x3fff0000 +# define SF_SYNTHETIC 0xc0000000 +#endif + + static mode_t _PyLong_AsMode_t(PyObject *op) { @@ -467,18 +507,29 @@ S_IWOTH: write by others\n\ S_IXOTH: execute by others\n\ \n" -"UF_NODUMP: do not dump file\n\ +"UF_SETTABLE: mask of owner changable flags\n\ +UF_NODUMP: do not dump file\n\ UF_IMMUTABLE: file may not be changed\n\ UF_APPEND: file may only be appended to\n\ UF_OPAQUE: directory is opaque when viewed through a union stack\n\ UF_NOUNLINK: file may not be renamed or deleted\n\ -UF_COMPRESSED: OS X: file is hfs-compressed\n\ -UF_HIDDEN: OS X: file should not be displayed\n\ +UF_COMPRESSED: macOS: file is hfs-compressed\n\ +UF_TRACKED: used for dealing with document IDs\n\ +UF_DATAVAULT: entitlement required for reading and writing\n\ +UF_HIDDEN: macOS: file should not be displayed\n\ +SF_SETTABLE: mask of super user changeable flags\n\ SF_ARCHIVED: file may be archived\n\ SF_IMMUTABLE: file may not be changed\n\ SF_APPEND: file may only be appended to\n\ +SF_RESTRICTED: entitlement required for writing\n\ SF_NOUNLINK: file may not be renamed or deleted\n\ SF_SNAPSHOT: file is a snapshot file\n\ +SF_FIRMLINK: file is a firmlink\n\ +SF_DATALESS: file is a dataless object\n\ +\n\ +On macOS:\n\ +SF_SUPPORTED: mask of super user supported flags\n\ +SF_SYNTHETIC: mask of read-only synthetic flags\n\ \n" "ST_MODE\n\ @@ -543,18 +594,32 @@ stat_exec(PyObject *module) ADD_INT_MACRO(module, S_IWOTH); ADD_INT_MACRO(module, S_IXOTH); + ADD_INT_MACRO(module, UF_SETTABLE); ADD_INT_MACRO(module, UF_NODUMP); ADD_INT_MACRO(module, UF_IMMUTABLE); ADD_INT_MACRO(module, UF_APPEND); ADD_INT_MACRO(module, UF_OPAQUE); ADD_INT_MACRO(module, UF_NOUNLINK); ADD_INT_MACRO(module, UF_COMPRESSED); + ADD_INT_MACRO(module, UF_TRACKED); + ADD_INT_MACRO(module, UF_DATAVAULT); ADD_INT_MACRO(module, UF_HIDDEN); + ADD_INT_MACRO(module, SF_SETTABLE); ADD_INT_MACRO(module, SF_ARCHIVED); ADD_INT_MACRO(module, SF_IMMUTABLE); ADD_INT_MACRO(module, SF_APPEND); ADD_INT_MACRO(module, SF_NOUNLINK); ADD_INT_MACRO(module, SF_SNAPSHOT); + ADD_INT_MACRO(module, SF_FIRMLINK); + ADD_INT_MACRO(module, SF_DATALESS); + +#ifdef SF_SUPPORTED + ADD_INT_MACRO(module, SF_SUPPORTED); +#endif +#ifdef SF_SYNTHETIC + ADD_INT_MACRO(module, SF_SYNTHETIC); +#endif + const char* st_constants[] = { "ST_MODE",