8000 remove directory mode check from makedirs (closes #21082) · python/cpython@ee5f1c1 · GitHub
[go: up one dir, main page]

Skip to content

Commit ee5f1c1

Browse files
committed
remove directory mode check from makedirs (closes #21082)
1 parent b4be376 commit ee5f1c1

File tree

4 files changed

+20
-32
lines changed

4 files changed

+20
-32
lines changed

Doc/library/os.rst

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,11 +1188,8 @@ Files and Directories
11881188
The default *mode* is ``0o777`` (octal). On some systems, *mode* is
11891189
ignored. Where it is used, the current umask value is first masked out.
11901190

1191-
If *exists_ok* is ``False`` (the default), an :exc:`OSError` is raised if
1192-
the target directory already exists. If *exists_ok* is ``True`` an
1193-
:exc:`OSError` is still raised if the umask-masked *mode* is different from
1194-
the existing mode, on systems where the mode is used. :exc:`OSError` will
1195-
also be raised if the directory creation fails.
1191+
If *exist_ok* is ``False`` (the default), an :exc:`OSError` is raised if the
1192+
target directory already exists.
11961193

11971194
.. note::
11981195

@@ -1204,6 +1201,13 @@ Files and Directories
12041201
.. versionadded:: 3.2
12051202
The *exist_ok* parameter.
12061203

1204+
.. versionchanged:: 3.2.5
1205+
1206+
Before Python 3.2.5, if *exist_ok* was ``True`` and the directory existed,
1207+
:func:`makedirs` would still raise an error if *mode* did not match the
1208+
mode of the existing directory. Since this behavior was impossible to
1209+
implement safely, it was removed in Python 3.2.6. See :issue:`21082`.
1210+
12071211

12081212
.. function:: pathconf(path, name)
12091213

Lib/os.py

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,6 @@ def _get_exports_list(module):
114114
SEEK_CUR = 1
115115
SEEK_END = 2
116116

117-
118-
def _get_masked_mode(mode):
119-
mask = umask(0)
120-
umask(mask)
121-
return mode & ~mask
122-
123117
#'
124118

125119
# Super directory utilities.
@@ -128,11 +122,10 @@ def _get_masked_mode(mode):
128122
def makedirs(name, mode=0o777, exist_ok=False):
129123
"""makedirs(path [, mode=0o777][, exist_ok=False])
130124
131-
Super-mkdir; create a leaf directory and all intermediate ones.
132-
Works like mkdir, except that any intermediate path segment (not
133-
just the rightmost) will be created if it does not exist. If the
134-
target directory with the same mode as we specified already exists,
135-
raises an OSError if exist_ok is False, otherwise no exception is
125+
Super-mkdir; create a leaf directory and all intermediate ones. Works like
126+
mkdir, except that any intermediate path segment (not just the rightmost)
127+
will be created if it does not exist. If the target directory already
128+
exists, raise an OSError if exist_ok is False. Otherwise no exception is
136129
raised. This is recursive.
137130
138131
"""
@@ -154,18 +147,7 @@ def makedirs(name, mode=0o777, exist_ok=False):
154147
try:
155148
mkdir(name, mode)
156149
except OSError as e:
157-
import stat as st
158-
dir_exists = path.isdir(name)
159-
expected_mode = _get_masked_mode(mode)
160-
if dir_exists:
161-
# S_ISGID is automatically copied by the OS from parent to child
162-
# directories on mkdir. Don't consider it being set to be a mode
163-
# mismatch as mkdir does not unset it when not specified in mode.
164-
actual_mode = st.S_IMODE(lstat(name).st_mode) & ~st.S_ISGID
165-
else:
166-
actual_mode = -1
167-
if not (e.errno == errno.EEXIST and exist_ok and dir_exists and
168-
actual_mode == expected_mode):
150+
if not exist_ok or e.errno != errno.EEXIST or not path.isdir(name):
169151
raise
170152

171153
def removedirs(name):

Lib/test/test_os.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ def test_exist_ok_existing_directory(self):
579579
os.makedirs(path, mode)
580580
self.assertRaises(OSError, os.makedirs, path, mode)
581581
self.assertRaises(OSError, os.makedirs, path, mode, exist_ok=False)
582-
self.assertRaises(OSError, os.makedirs, path, 0o776, exist_ok=True)
582+
os.makedirs(path, 0o776, exist_ok=True)
583583
os.makedirs(path, mode=mode, exist_ok=True)
584584
finally:
585585
os.umask(old_mask)
@@ -606,9 +606,8 @@ def test_exist_ok_s_isgid_directory(self):
606606
os.makedirs(path, mode, exist_ok=True)
607607
# remove the bit.
608608
os.chmod(path, stat.S_IMODE(os.lstat(path).st_mode) & ~S_ISGID)
609-
with self.assertRaises(OSError):
610-
# Should fail when the bit is not already set when demanded.
611-
os.makedirs(path, mode | S_ISGID, exist_ok=True)
609+
# May work even when the bit is not already set when demanded.
610+
os.makedirs(path, mode | S_ISGID, exist_ok=True)
612611
finally:
613612
os.umask(old_mask)
614613

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.2.6?
1010
Library
1111
-------
1212

13+
- Issue #21082: In os.makedirs, do not set the process-wide umask. Note this
14+
changes behavior of makedirs when exist_ok=True.
15+
1316
- Issue #20246: Fix buffer overflow in socket.recvfrom_into.
1417

1518
- Issue #12226: HTTPS is now used by default when connecting to PyPI.

0 commit comments

Comments
 (0)
0