@@ -1565,6 +1565,103 @@ def test_cleanup_with_symlink_to_a_directory(self):
1565
1565
"were deleted")
1566
1566
d2.cleanup()
1567
1567
1568
+ @os_helper.skip_unless_symlink
1569
+ def test_cleanup_with_symlink_modes(self):
1570
+ # cleanup() should not follow symlinks when fixing mode bits (#91133)
1571
+ with self.do_create(recurse=0) as d2:
1572
+ file1 = os.path.join(d2, 'file1')
1573
+ open(file1, 'wb').close()
1574
+ dir1 = os.path.join(d2, 'dir1')
1575
+ os.mkdir(dir1)
1576
+ for mode in range(8):
1577
+ mode <<= 6
1578
+ with self.subTest(mode=format(mode, '03o')):
1579
+ def test(target, target_is_directory):
1580
+ d1 = self.do_create(recurse=0)
1581
+ symlink = os.path.join(d1.name, 'symlink')
1582
+ os.symlink(target, symlink,
1583
+ target_is_directory=target_is_directory)
1584
+ try:
1585
+ os.chmod(symlink, mode, follow_symlinks=False)
1586
+ except NotImplementedError:
1587
+ pass
1588
+ try:
1589
+ os.chmod(symlink, mode)
1590
+ except FileNotFoundError:
1591
+ pass
1592
+ os.chmod(d1.name, mode)
1593
+ d1.cleanup()
1594
+ self.assertFalse(os.path.exists(d1.name))
1595
+
1596
+ with self.subTest('nonexisting file'):
1597
+ test('nonexisting', target_is_directory=False)
1598
+ with self.subTest('nonexisting dir'):
1599
+ test('nonexisting', target_is_directory=True)
1600
+
1601
+ with self.subTest('existing file'):
1602
+ os.chmod(file1, mode)
1603
+ old_mode = os.stat(file1).st_mode
1604
+ test(file1, target_is_directory=False)
1605
+ new_mode = os.stat(file1).st_mode
1606
+ self.assertEqual(new_mode, old_mode,
1607
+ '%03o != %03o' % (new_mode, old_mode))
1608
+
1609
+ with self.subTest('existing dir'):
1610
+ os.chmod(dir1, mode)
1611
+ old_mode = os.stat(dir1).st_mode
1612
+ test(dir1, target_is_directory=True)
1613
+ new_mode = os.stat(dir1).st_mode
1614
+ self.assertEqual(new_mode, old_mode,
1615
+ '%03o != %03o' % (new_mode, old_mode))
1616
+
1617
+ @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags')
1618
+ @os_helper.skip_unless_symlink
1619
+ def test_cleanup_with_symlink_flags(self):
1620
+ # cleanup() should not follow symlinks when fixing flags (#91133)
1621
+ flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK
1622
+ self.check_flags(flags)
1623
+
1624
+ with self.do_create(recurse=0) as d2:
1625
+ file1 = os.path.join(d2, 'file1')
1626
+ open(file1, 'wb').close()
1627
+ dir1 = os.path.join(d2, 'dir1')
1628
+ os.mkdir(dir1)
1629
+ def test(target, target_is_directory):
1630
+ d1 = self.do_create(recurse=0)
1631
+ symlink = os.path.join(d1.name, 'symlink')
1632
+ os.symlink(target, symlink,
1633
+ target_is_directory=target_is_directory)
1634
+ try:
1635
+ os.chflags(symlink, flags, follow_symlinks=False)
1636
+ except NotImplementedError:
1637
+ pass
1638
+ try:
1639
+ os.chflags(symlink, flags)
1640
+ except FileNotFoundError:
1641
+ pass
1642
+ os.chflags(d1.name, flags)
1643
+ d1.cleanup()
1644
+ self.assertFalse(os.path.exists(d1.name))
1645
+
1646
+ with self.subTest('nonexisting file'):
1647
+ test('nonexisting', target_is_directory=False)
1648
+ with self.subTest('nonexisting dir'):
1649
+ test('nonexisting', target_is_directory=True)
1650
+
1651
+ with self.subTest('existing file'):
1652
+ os.chflags(file1, flags)
1653
+ old_flags = os.stat(file1).st_flags
1654
+ test(file1, target_is_directory=False)
1655
+ new_flags = os.stat(file1).st_flags
1656
+ self.assertEqual(new_flags, old_flags)
1657
+
1658
+ with self.subTest('existing dir'):
1659
+ os.chflags(dir1, flags)
1660
+ old_flags = os.stat(dir1).st_flags
1661
+ test(dir1, target_is_directory=True)
1662
+ new_flags = os.stat(dir1).st_flags
1663
+ self.assertEqual(new_flags, old_flags)
1664
+
1568
1665
@support.cpython_only
1569
1666
def test_del_on_collection(self):
1570
1667
# A TemporaryDirectory is deleted when garbage collected
@@ -1737,10 +1834,7 @@ def test_modes(self):
1737
1834
d.cleanup()
1738
1835
self.assertFalse(os.path.exists(d.name))
1739
1836
1740
- @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags')
1741
- def test_flags(self):
1742
- flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK
1743
-
1837
+ def check_flags(self, flags):
1744
1838
# skip the test if these flags are not supported (ex: FreeBSD 13)
1745
1839
filename = os_helper.TESTFN
1746
1840
try:
@@ -1749,13 +1843,18 @@ def test_flags(self):
1749
1843
os.chflags(filename, flags)
1750
1844
except OSError as exc:
1751
1845
# "OSError: [Errno 45] Operation not supported"
1752
- self.skipTest(f"chflags() doesn't support "
1753
- f"UF_IMMUTABLE|UF_NOUNLINK : {exc}")
1846
+ self.skipTest(f"chflags() doesn't support flags "
1847
+ f"{flags:#b} : {exc}")
1754
1848
else:
1755
1849
os.chflags(filename, 0)
1756
1850
finally:
1757
1851
os_helper.unlink(filename)
1758
1852
1853
+ @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags')
1854
+ def test_flags(self):
1855
+ flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK
1856
+ self.check_flags(flags)
1857
+
1759
1858
d = self.do_create(recurse=3, dirs=2, files=2)
1760
1859
with d:
1761
1860
# Change files and directories flags recursively.
0 commit comments