Skip to content

Commit

Permalink
Add support for O_DIRECTORY and O_NOFOLLOW flags
Browse files Browse the repository at this point in the history
- flags are considered in os.open if present
  • Loading branch information
mrbean-bremen committed Mar 12, 2024
1 parent c4edbcb commit 7ce713f
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ The released versions correspond to PyPI releases.
* fixed handling of `dirfd` in `os.symlink` (see [#968](../../issues/968))
* add missing `follow_symlink` argument to `os.link` (see [#973](../../issues/973))

### Enhancements
* added support for `O_NOFOLLOW` and `O_DIRECTORY` flags in `os.open`
(see [#972](../../issues/972) and [#974](../../issues/974))

## [Version 5.3.5](https://pypi.python.org/pypi/pyfakefs/5.3.5) (2024-01-30)
Fixes a regression.

Expand Down
12 changes: 12 additions & 0 deletions pyfakefs/fake_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,18 @@ def open(
else:
mode = 0o777 & ~self._umask()

has_directory_flag = (
hasattr(os, "O_DIRECTORY") and flags & os.O_DIRECTORY == os.O_DIRECTORY
)
if has_directory_flag and not self.filesystem.isdir(path):
raise OSError(errno.ENOTDIR, "path is not a directory", path)

has_follow_flag = (
hasattr(os, "O_NOFOLLOW") and flags & os.O_NOFOLLOW == os.O_NOFOLLOW
)
if has_follow_flag and self.filesystem.islink(path):
raise OSError(errno.ELOOP, "path is a symlink", path)

has_tmpfile_flag = (
hasattr(os, "O_TMPFILE") and flags & os.O_TMPFILE == os.O_TMPFILE
)
Expand Down
37 changes: 36 additions & 1 deletion pyfakefs/tests/fake_os_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,42 @@ def test_open_raises_with_trailing_separator_windows(self):
self.check_windows_only()
self.check_open_raises_with_trailing_separator(errno.EINVAL)

@unittest.skipIf(not hasattr(os, "O_DIRECTORY"), "opening directory not supported")
def test_open_raises_if_not_dir(self):
self.check_posix_only()
file_path = self.make_path("file.txt")
self.create_file(file_path, contents="foo")
with self.assertRaises(NotADirectoryError):
self.os.open(file_path, os.O_RDONLY | os.O_DIRECTORY)
dir_path = self.make_path("dir")
self.create_dir(dir_path)
with self.assertRaises(IsADirectoryError):
self.os.open(dir_path, os.O_RDWR | os.O_DIRECTORY)

@unittest.skipIf(not hasattr(os, "O_NOFOLLOW"), "NOFOLLOW attribute not supported")
def test_open_nofollow_symlink_raises(self):
self.skip_if_symlink_not_supported()
file_path = self.make_path("file.txt")
self.create_file(file_path, contents="foo")
link_path = self.make_path("link")
self.create_symlink(link_path, file_path)
with self.assertRaises(OSError) as cm:
self.os.open(link_path, os.O_RDONLY | os.O_NOFOLLOW)
assert cm.exception.errno == errno.ELOOP

@unittest.skipIf(not hasattr(os, "O_NOFOLLOW"), "NOFOLLOW attribute not supported")
def test_open_nofollow_symlink_as_parent_works(self):
self.skip_if_symlink_not_supported()
dir_path = self.make_path("dir")
self.create_dir(dir_path)
link_path = self.make_path("link")
self.create_symlink(link_path, dir_path)
file_path = self.os.path.join(link_path, "file.txt")
self.create_file(file_path, contents="foo")
fd = self.os.open(file_path, os.O_RDONLY | os.O_NOFOLLOW)
self.assertGreater(fd, 0)
self.os.close(fd)

def test_lexists_with_trailing_separator_linux_windows(self):
self.check_linux_and_windows()
self.skip_if_symlink_not_supported()
Expand Down Expand Up @@ -5298,7 +5334,6 @@ def test_scandir_stat_nlink(self):
self.assertEqual(1, self.os.stat(self.file_path).st_nlink)

@unittest.skipIf(not hasattr(os, "O_DIRECTORY"), "opening directory not supported")
@unittest.skipIf(sys.version_info < (3, 7), "fd not supported for scandir")
def test_scandir_with_fd(self):
# regression test for #723
temp_dir = self.make_path("tmp", "dir")
Expand Down

0 comments on commit 7ce713f

Please sign in to comment.