Repository: pytest-dev/pyfakefs

Issue:
get_object_from_normpath check_exe_perm is examining the directory referenced by file_path instead of the parent directory
**Describe the bug**
Recent changes to get_object and get_object_from_normpath adds a check_exe_perm to True. When calling get_object on a directory created without exec permissions, there is a permissions error reading the directory object, even when setting check_read_perm=False. It seems that the check_exe_perm is always set to True.

I also wonder if the check_exe_perm is applied to the directory object itself referenced by the file_path instead of the parent directory.

**How To Reproduce**
self.fs.create_dir('/test_directory', perm_bits=0o644)
created_dir = self.fs.get_object('/test_directory')
PermissionError: [Errno 13] Permission denied in the fake filesystem: '/test_directory'

self.fs.create_dir('/test_directory', perm_bits=0o644)
created_dir = self.fs.get_object('/test_directory', check_read_perm=False)
PermissionError: [Errno 13] Permission denied in the fake filesystem: '/test_directory'

self.fs.create_dir('/', perm_bits=0o777)
self.fs.create_dir('/test_directory', perm_bits=0o644)
created_dir = self.fs.get_object('/test_directory', check_read_perm=False)
PermissionError: [Errno 13] Permission denied in the fake filesystem: '/test_directory'

self.fs.create_dir('/test_directory', perm_bits=0o644)
self.fs.get_object_from_normpath('/test_directory', check_read_perm=False, check_exe_perm=False)
 
**Your environment**
Linux-6.10.11-1rodete2-amd64-x86_64-with-glibc2.27
Python 3.11.8 (stable, redacted, redacted) [Clang 9999.0.0 (386af4a5c64ab75eaee2448dc38f2e34a40bfed0)]
pyfakefs 5.7.4
pytest 8.0.2

Hints from issue comments:
@mrbean-bremen: > When calling get_object on a directory created without exec permissions, there is a permissions error reading the directory object, even when setting check_read_perm=False. It seems that the check_exe_perm is always set to True.

This is correct. I'm not sure about the use case here, do you also want to be able to switch off exe permissions in `get_object`?
Actually, I added this for internal usage only (admittedly, this is not reflected in the documentation), I wasn't aware that this would be needed to be called by the user.

> I also wonder if the `check_exe_perm` is applied to the directory object itself referenced by the file_path instead of the parent directory.

This seems indeed to be a bug, I will check this (if you don't want to make a PR yourself, of course - there are quite a few issues surfacing here all of a sudden).@davidlbaird: > This is correct. I'm not sure about the use case here, do you also want to be able to switch off exe permissions in `get_object`? Actually, I added this for internal usage only (admittedly, this is not reflected in the documentation), I wasn't aware that this would be needed to be called by the user.

I think that the intent of check_read_perm=False is to ignore any file object permissions when getting the file object. If so, it would make sense to ignore any execute bit permissions, too.

As an aside, originally, the `FakeFilesystem` methods were intended to provide unit test access to the file system without regard to the permissions or other file system restrictions applied to the `FakeOsModule` and other emulations of standard library interfaces to the file system. The methods like `'get_object` were intended as a unit test backdoor into the file system.@mrbean-bremen: > I think that the intent of check_read_perm=False is to ignore any file object permissions when getting the file object

As I wrote, this was thought for internal purposes only, and changing the meaning of the flag would probably break stuff. The easiest way would be to also add the other flag (for exec permissions) in `get_object` as is done in `get_object_from_normpath`. The other possibility would be to change `get_object` to ignore permssions as before and let it call an internal method that then uses the permissions.

> As an aside, originally, the FakeFilesystem methods were intended to provide unit test access to the file system without regard to the permissions

A few years and hundreds of issues later, all requesting that the fake fs behave exactly as the real fs, this got a bit lost.

Regardless, if you see such use cases that are still needed, it would help to write feature requests (or better yet PRs) that would help with that. 
@davidlbaird: I wrote a unit test in `fake_filesystem_vs_real_test.py` to demonstrate the error for a directory that is not readable or writable, and has differing behaviors between the "real" and "fake" `os.isdir` calls.

```
def test_directory_permissions(self):
        self._create_test_file("d", "a")
        self._create_test_file("f", "a/write")
        self._create_test_file("d", "b")
        self._create_test_file("f", "b/write")
        self._create_test_file("d", "b/stat_dir")
        for path, mode in [("b", 0o555), ("b/stat_dir", 0o111)]:
          real_path, fake_path = self._paths(path)
          os.chmod(real_path, mode)
          self.fake_os.chmod(fake_path, mode)

        try:
          self.assertFileHandleOpenBehaviorsMatch("a/write", "w")
          self.assertFileHandleOpenBehaviorsMatch("b/write", "w")
          self.assertAllOsBehaviorsMatch("b/stat_dir")
        finally:
          for path in ["b", "b/stat_dir"]:
            real_path, fake_path = self._paths(path)
            os.chmod(real_path, 0o777)
            self.fake_os.chmod(fake_path, 0o777)
```

```
AssertionError: Behaviors do not match for b/stat_dir:
    isdir({path}): real return True, fake returned False
    stat.size({path}): real return None, fake returned 0
    lstat.size({path}): real return None, fake returned 0
self = <pyfakefs.tests.fake_filesystem_vs_real_test.FakeFilesystemVsRealTest testMethod=test_directory_permissions>

    def test_directory_permissions(self):
        self._create_test_file("d", "a")
        self._create_test_file("f", "a/write")
        self._create_test_file("d", "b")
        self._create_test_file("f", "b/write")
        self._create_test_file("d", "b/stat_dir")
        for path, mode in [("b", 0o555), ("b/stat_dir", 0o111)]:
          real_path, fake_path = self._paths(path)
          os.chmod(real_path, mode)
          self.fake_os.chmod(fake_path, mode)
    
        try:
          self.assertFileHandleOpenBehaviorsMatch("a/write", "w")
          self.assertFileHandleOpenBehaviorsMatch("b/write", "w")
>         self.assertAllOsBehaviorsMatch("b/stat_dir")

tests/fake_filesystem_vs_real_test.py:738: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/fake_filesystem_vs_real_test.py:412: in assertAllOsBehaviorsMatch
    self.fail(
E   AssertionError: Behaviors do not match for b/stat_dir:
E       isdir({path}): real return True, fake returned False
E       stat.size({path}): real return None, fake returned 0
E       lstat.size({path}): real return None, fake returned 0
```

I suggest adding more tests for differing permissions updates.
@davidlbaird: I updated the subject of this bug to only focus on the error in get_object_from_normpath on directory objects.@mrbean-bremen: Thanks - I will have a closer look somewhere in the next days, probably at the weekend.@mrbean-bremen: This may take a while - indeed the permissions are often checked on the wrong object. However, fixing this is a bit more involved as I had hoped. Still, it is an important issue and I will try to handle this ASAP.
 
It is also a good idea to add more tests to `FakeFilesystemVsRealTest` - while most other tests are also run in both the real and the fake filesystem if configured (e.g. if `TEST_REAL_FS` is set), the direct comparison has its advantages.
@mrbean-bremen: @davidlbaird - for now, I've fixed only the concrete problems, and changed the behavior of `get_object()` back to what it was before - please check if this is ok for you (in main, will probably make a new release soon).
I intend to add more tests to `FakeFilesystemVsRealTest`, but it would be helpful if you could also continue to do so (in new issues), as you know best how pyfakefs is used at your company.@mrbean-bremen: I'm closing this as fixed - will handle other problems in separate issues or PRs.

Solution patch:
diff --git a/CHANGES.md b/CHANGES.md
index 3ac40e4..88a3bc9 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -16,6 +16,8 @@ The released versions correspond to PyPI releases.
 
 ### Changes
 * added some preliminary support for Python 3.14
+* change behavior of `FakeFilesystem.get_object()` to ignore permissions as it has been
+  before version 5.4.0 (see [#1122](../../issues/1122))
 
 ### Fixes
 * fixed a problem with flushing if writing over the buffer end
@@ -23,6 +25,7 @@ The released versions correspond to PyPI releases.
 * fixed a regression that could break tests under Posix in Python 3.12
   (see [#1126](../../issues/1126))
 * fixed behavior for `os.access` for symlinks under Windows
+* fixes permission problem on querying file properties (see [#1122](../../issues/1122))
 
 ## [Version 5.7.4](https://pypi.python.org/pypi/pyfakefs/5.7.4) (2025-01-14)
 Minor bugfix release.
diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py
index 53d5265..08f45dd 100644
--- a/pyfakefs/fake_filesystem.py
+++ b/pyfakefs/fake_filesystem.py
@@ -745,7 +745,7 @@ class FakeFilesystem:
             # make sure stat raises if a parent dir is not readable
             parent_dir = file_object.parent_dir
             if parent_dir:
-                self.get_object(parent_dir.path, check_read_perm=False)  # type: ignore[arg-type]
+                self._get_object(parent_dir.path, check_read_perm=False)  # type: ignore[arg-type]
 
         self.raise_for_filepath_ending_with_separator(
             entry_path, file_object, follow_symlinks
@@ -1775,15 +1775,22 @@ class FakeFilesystem:
             return True
         return target.has_permission(permission)
 
-    def get_object(self, file_path: AnyPath, check_read_perm: bool = True) -> FakeFile:
+    def _get_object(
+        self,
+        file_path: AnyPath,
+        check_read_perm: bool = True,
+        check_exe_perm: bool = True,
+    ) -> FakeFile:
         """Search for the specified filesystem object within the fake
-        filesystem.
+        filesystem. By default, consider read and execute permissions.
 
         Args:
             file_path: Specifies the target
                 :py:class:`FakeFile<pyfakefs.fake_file.FakeFile>` object to retrieve.
             check_read_perm: If True, raises OSError if a parent directory
                 does not have read permission
+            check_exe_perm: If True, raises OSError if a parent directory
+                does not have execute (e.g. search) permission
 
         Returns:
             The :py:class:`FakeFile<pyfakefs.fake_file.FakeFile>` object corresponding
@@ -1794,7 +1801,24 @@ class FakeFilesystem:
         """
         path = make_string_path(file_path)
         path = self.absnormpath(self._original_path(path))
-        return self.get_object_from_normpath(path, check_read_perm)
+        return self.get_object_from_normpath(path, check_read_perm, check_exe_perm)
+
+    def get_object(self, file_path: AnyPath) -> FakeFile:
+        """Search for the specified filesystem object within the fake
+        filesystem and return it. Ignore any read permissions.
+
+        Args:
+            file_path: Specifies the target
+                :py:class:`FakeFile<pyfakefs.fake_file.FakeFile>` object to retrieve.
+
+        Returns:
+            The :py:class:`FakeFile<pyfakefs.fake_file.FakeFile>` object corresponding
+            to `file_path`.
+
+        Raises:
+            OSError: if the object is not found.
+        """
+        return self._get_object(file_path, check_read_perm=False, check_exe_perm=False)
 
     def resolve(
         self,
@@ -2051,7 +2075,7 @@ class FakeFilesystem:
         old_object: FakeFile,
         ends_with_sep: bool,
     ) -> Optional[AnyStr]:
-        new_object = self.get_object(new_file_path)
+        new_object = self._get_object(new_file_path)
         if old_file_path == new_file_path:
             if not S_ISLNK(new_object.st_mode) and ends_with_sep:
                 error = errno.EINVAL if self.is_windows_fs else errno.ENOTDIR
@@ -2418,7 +2442,7 @@ class FakeFilesystem:
             )
         else:
             self._create_fake_from_real_dir(source_path_str, target_path_str, read_only)
-        return cast(FakeDirectory, self.get_object(target_path_str))
+        return cast(FakeDirectory, self._get_object(target_path_str))
 
     def _create_fake_from_real_dir(self, source_path_str, target_path_str, read_only):
         if not self.exists(target_path_str):
@@ -2456,11 +2480,11 @@ class FakeFilesystem:
                     self.add_real_symlink(src_entry_path, target_entry_path)
                 elif os.path.isfile(src_entry_path):
                     self.add_real_file(src_entry_path, read_only, target_entry_path)
-            return self.get_object(target_path_str)
+            return self._get_object(target_path_str)
 
         parent_path = os.path.split(target_path_str)[0]
         if self.exists(parent_path):
-            parent_dir = self.get_object(parent_path)
+            parent_dir = self._get_object(parent_path)
         else:
             parent_dir = self.create_dir(parent_path)
         new_dir = FakeDirectoryFromRealDirectory(
@@ -2913,7 +2937,6 @@ class FakeFilesystem:
         path: AnyPath,
         st_flag: int,
         follow_symlinks: bool = True,
-        check_read_perm: bool = True,
     ) -> bool:
         """Helper function to implement isdir(), islink(), etc.
 
@@ -2922,8 +2945,8 @@ class FakeFilesystem:
         Args:
             path: Path to file to stat and test
             st_flag: The stat.S_I* flag checked for the file's st_mode
-            check_read_perm: If True (default) False is returned for
-                existing but unreadable file paths.
+            follow_symlinks: If `False` and path points to a symlink,
+                the link itself is checked instead of the linked object.
 
         Returns:
             (boolean) `True` if the st_flag is set in path's st_mode.
@@ -2935,9 +2958,7 @@ class FakeFilesystem:
             raise TypeError
         file_path = make_string_path(path)
         try:
-            obj = self.resolve(
-                file_path, follow_symlinks, check_read_perm=check_read_perm
-            )
+            obj = self.resolve(file_path, follow_symlinks, check_read_perm=False)
             if obj:
                 self.raise_for_filepath_ending_with_separator(
                     file_path, obj, macos_handling=not follow_symlinks
@@ -2952,6 +2973,8 @@ class FakeFilesystem:
 
         Args:
             path: Path to filesystem object.
+            follow_symlinks: If `False` and path points to a symlink,
+                the link itself is checked instead of the linked object.
 
         Returns:
             `True` if path points to a directory (following symlinks).
@@ -2966,6 +2989,8 @@ class FakeFilesystem:
 
         Args:
             path: Path to filesystem object.
+            follow_symlinks: If `False` and path points to a symlink,
+                the link itself is checked instead of the linked object.
 
         Returns:
             `True` if path points to a regular file (following symlinks).
@@ -2973,7 +2998,7 @@ class FakeFilesystem:
         Raises:
             TypeError: if path is None.
         """
-        return self._is_of_type(path, S_IFREG, follow_symlinks, check_read_perm=False)
+        return self._is_of_type(path, S_IFREG, follow_symlinks)
 
     def islink(self, path: AnyPath) -> bool:
         """Determine if path identifies a symbolic link.
@@ -3121,6 +3146,8 @@ class FakeFilesystem:
         Raises:
             OSError: if the target is not a directory.
         """
+        if target_directory is None:
+            return []
         target_directory = self.resolve_path(target_directory, allow_fd=True)
         directory = self.confirmdir(target_directory, check_exe_perm=False)
         directory_contents = list(directory.entries.keys())
diff --git a/pyfakefs/fake_os.py b/pyfakefs/fake_os.py
index 59a7690..b446026 100644
--- a/pyfakefs/fake_os.py
+++ b/pyfakefs/fake_os.py
@@ -453,8 +453,20 @@ class FakeOsModule:
         except OSError as exc:
             if self.filesystem.is_macos and exc.errno == errno.EBADF:
                 raise OSError(errno.ENOTDIR, "Not a directory: " + str(path))
+            elif (
+                self.filesystem.is_windows_fs
+                and exc.errno == errno.ENOENT
+                and path == ""
+            ):
+                raise OSError(errno.EINVAL, "Invalid argument:  + str(path)")
+            raise
+        try:
+            self.filesystem.confirmdir(path)
+        except OSError as exc:
+            if exc.errno == errno.EACCES:
+                # no access rights to the parent directory - do nothing
+                return
             raise
-        self.filesystem.confirmdir(path)
         directory = self.filesystem.resolve(path)
         # A full implementation would check permissions all the way
         # up the tree.
@@ -1058,7 +1070,9 @@ class FakeOsModule:
                 return False
             raise
         if is_root():
-            mode &= ~os.W_OK
+            if mode & os.X_OK:
+                return stat_result.st_mode & 0o111 != 0
+            return True
         return (mode & ((stat_result.st_mode >> 6) & 7)) == mode
 
     def fchmod(
diff --git a/pyfakefs/fake_scandir.py b/pyfakefs/fake_scandir.py
index 308dff6..ae69601 100644
--- a/pyfakefs/fake_scandir.py
+++ b/pyfakefs/fake_scandir.py
@@ -133,7 +133,10 @@ class ScanDirIter:
                 self.filesystem.get_open_file(path).get_object().path
             )
             self.path = ""
+            self.entry_iter = iter(tuple())
         else:
+            if path is None:
+                path = "."
             path = make_string_path(path)
             self.abspath = self.filesystem.absnormpath(path)
             self.path = to_string(path)


Test patch:
diff --git a/pyfakefs/tests/fake_filesystem_vs_real_test.py b/pyfakefs/tests/fake_filesystem_vs_real_test.py
index 7ca4281..a829938 100644
--- a/pyfakefs/tests/fake_filesystem_vs_real_test.py
+++ b/pyfakefs/tests/fake_filesystem_vs_real_test.py
@@ -41,12 +41,10 @@ def _get_errno(raised_error):
             pass
 
 
-class TestCase(unittest.TestCase):
+class FakeFilesystemVsRealTest(unittest.TestCase):
     is_windows = sys.platform.startswith("win")
-    _FAKE_FS_BASE = "C:\\fakefs" if is_windows else "/fakefs"
+    fake_base = "C:\\fakefs" if is_windows else "/fakefs"
 
-
-class FakeFilesystemVsRealTest(TestCase):
     def _paths(self, path):
         """For a given path, return paths in the real and fake filesystems."""
         if not path:
@@ -106,7 +104,6 @@ class FakeFilesystemVsRealTest(TestCase):
         if os.path.isdir(self.real_base):
             shutil.rmtree(self.real_base)
         os.mkdir(self.real_base)
-        self.fake_base = self._FAKE_FS_BASE
 
         # Make sure we can write to the physical testing temp directory.
         self.assertTrue(os.access(self.real_base, os.W_OK))
@@ -167,7 +164,7 @@ class FakeFilesystemVsRealTest(TestCase):
                 to method.
             real: built-in system library or method from the built-in system
                 library which takes a path as an arg and returns some value.
-            fake: fake_filsystem object or method from a fake_filesystem class
+            fake: fake_filesystem object or method from a fake_filesystem class
                 which takes a path as an arg and returns some value.
             method_returns_path: True if the method returns a path, and thus we
                 must compensate for expected difference between real and fake.
@@ -302,7 +299,7 @@ class FakeFilesystemVsRealTest(TestCase):
     def diff_open_method_behavior(
         self, method_name, path, mode, data, method_returns_data=True
     ):
-        """Invoke an open method in both real and fkae contexts and compare.
+        """Invoke an open method in both real and fake contexts and compare.
 
         Args:
             method_name: Name of method being tested.
@@ -335,7 +332,7 @@ class FakeFilesystemVsRealTest(TestCase):
 
         For a given method name (from the os.path module) and a path, compare
         the behavior of the system provided module against the
-        fake_filesytem module.
+        fake_filesystem module.
         We expect results and/or Exceptions raised to be identical.
 
         Args:
@@ -374,24 +371,35 @@ class FakeFilesystemVsRealTest(TestCase):
         if diff:
             self.fail(diff)
 
-    def assertAllOsBehaviorsMatch(self, path):
+    def assertAllOsBehaviorsMatch(self, path, excludes=None):
+        excludes = excludes or []
         path = sep(path)
         os_method_names = ["readlink"]
         os_method_names_no_args = ["getcwd"]
-        os_path_method_names = [
+        os_path_method_names = {
             "isabs",
             "isdir",
             "islink",
             "lexists",
             "isfile",
             "exists",
-        ]
+        } - set(excludes)
 
         wrapped_methods = [
             ["access", self._access_real, self._access_fake],
-            ["stat.size", self._stat_size_real, self._stat_size_fake],
+            [
+                "stat.size",
+                lambda p: self._stat_result_real(p, "st_size", False),
+                lambda p: self._stat_result_fake(p, "st_size", False),
+            ],
+            [
+                "stat.mode",
+                lambda p: self._stat_result_real(p, "st_mode"),
+                lambda p: self._stat_result_fake(p, "st_mode"),
+            ],
             ["lstat.size", self._lstat_size_real, self._lstat_size_fake],
         ]
+        wrapped_methods = [m for m in wrapped_methods if m[0] not in excludes]
 
         differences = []
         for method_name in os_method_names:
@@ -492,19 +500,18 @@ class FakeFilesystemVsRealTest(TestCase):
     def _access_fake(self, path):
         return self.fake_os.access(path, os.R_OK)
 
-    def _stat_size_real(self, path):
+    def _stat_result_real(self, path, prop, support_dir=True):
         real_path, unused_fake_path = self._paths(path)
         # fake_filesystem.py does not implement stat().st_size for directories
-        if os.path.isdir(real_path):
+        if not support_dir and os.path.isdir(real_path):
             return None
-        return os.stat(real_path).st_size
+        return getattr(os.stat(real_path), prop)
 
-    def _stat_size_fake(self, path):
-        unused_real_path, fake_path = self._paths(path)
-        # fake_filesystem.py does not implement stat().st_size for directories
-        if self.fake_os.path.isdir(fake_path):
+    def _stat_result_fake(self, path, prop, support_dir=True):
+        _, fake_path = self._paths(path)
+        if not support_dir and self.fake_os.path.isdir(fake_path):
             return None
-        return self.fake_os.stat(fake_path).st_size
+        return getattr(self.fake_os.stat(fake_path), prop)
 
     def _lstat_size_real(self, path):
         real_path, unused_fake_path = self._paths(path)
@@ -544,9 +551,9 @@ class FakeFilesystemVsRealTest(TestCase):
         self.assertAllOsBehaviorsMatch("")
 
     def test_root_path(self):
-        self.assertAllOsBehaviorsMatch("/")
+        self.assertAllOsBehaviorsMatch("/", excludes=["stat.mode"])
 
-    def test_non_existant_file(self):
+    def test_non_existent_file(self):
         self.assertAllOsBehaviorsMatch("foo")
 
     def test_empty_file(self):
@@ -690,7 +697,7 @@ class FakeFilesystemVsRealTest(TestCase):
         self._create_test_file("f", "a/sibling_of_b/target", "contents")
         self.assertAllOsBehaviorsMatch("a/b/../broken/../target")
 
-    def test_getmtime_nonexistant_path(self):
+    def test_getmtime_nonexistent_path(self):
         self.assertOsPathMethodBehaviorMatches("getmtime", "no/such/path")
 
     def test_builtin_open_modes(self):
@@ -724,6 +731,27 @@ class FakeFilesystemVsRealTest(TestCase):
         self.assertFileHandleOpenBehaviorsMatch("write", "w", encoding="utf-8")
         self.assertFileHandleOpenBehaviorsMatch("append", "a", encoding="utf-8")
 
+    def test_directory_permissions(self):
+        self._create_test_file("d", "a")
+        self._create_test_file("f", "a/write")
+        self._create_test_file("d", "b")
+        self._create_test_file("f", "b/write")
+        self._create_test_file("d", "b/stat_dir")
+        for path, mode in [("b", 0o555), ("b/stat_dir", 0o111)]:
+            real_path, fake_path = self._paths(path)
+            os.chmod(real_path, mode)
+            self.fake_os.chmod(fake_path, mode)
+
+        try:
+            self.assertFileHandleOpenBehaviorsMatch("a/write", "w")
+            self.assertFileHandleOpenBehaviorsMatch("b/write", "w")
+            self.assertAllOsBehaviorsMatch("b/stat_dir")
+        finally:
+            for path in ["b", "b/stat_dir"]:
+                real_path, fake_path = self._paths(path)
+                os.chmod(real_path, 0o777)
+                self.fake_os.chmod(fake_path, 0o777)
+
 
 def main(_):
     unittest.main()
diff --git a/pyfakefs/tests/fake_os_test.py b/pyfakefs/tests/fake_os_test.py
index 9a0904c..e026af8 100644
--- a/pyfakefs/tests/fake_os_test.py
+++ b/pyfakefs/tests/fake_os_test.py
@@ -1461,7 +1461,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         new_file = self.filesystem.get_object(new_file_path)
         self.assertNotEqual(new_file.st_mtime, old_file.st_mtime)
         self.os.rename(old_file_path, new_file_path)
-        new_file = self.filesystem.get_object(new_file_path, check_read_perm=False)
+        new_file = self.filesystem.get_object(new_file_path)
         self.assertEqual(new_file.st_mtime, old_file.st_mtime)
         self.assertEqual(new_file.st_mode, old_file.st_mode)
         self.assertEqual(new_file.st_uid, old_file.st_uid)
@@ -5440,6 +5440,11 @@ class FakeScandirTest(FakeOsModuleTestBase):
                 self.dir_entries[5].inode(),
             )
 
+    def test_scandir_none(self):
+        result1 = [entry.path for entry in self.scandir(None)]
+        result2 = [entry.path for entry in self.scandir(".")]
+        self.assertEqual(result1, result2)
+
     def test_scandir_stat_nlink(self):
         # regression test for #350
         stat_nlink = self.os.stat(self.file_path).st_nlink