Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Behavior for directories with 0o000 permissions does not match native filesystems #719

Closed
jessecureton-aurora opened this issue Sep 23, 2022 · 3 comments · Fixed by #726
Closed
Labels

Comments

@jessecureton-aurora
Copy link

Describe the bug
The behavior of file operations on directories with 0o000 permissions does not match the behavior of native filesystems for at least rmdir and chmod operations. The owner of a file/dir with 0o000 perms should always be able to remove or chmod them again, but pyfakefs does not respect this.

I've attached both a pytest test suite and bash script that demonstrates these operations and confirmed the behavior is the same on Ubuntu 18.04 and MacOS 12.5.1.

How To Reproduce
Here's a set of Pytest cases that show the operations passing natively and failing when run in pyfakefs. You can also directly run the python test file without using pytest to get an annotated output of the test.

Python test case
import pyfakefs
import pytest

from pathlib import Path
from stat import S_IMODE

def get_path_perms(p):
    return S_IMODE(p.stat().st_mode)

def do_mkdir():
    path = Path("/tmp/test-chmod")

    print("Creating test directory set setting permissions to 000")
    path.mkdir(mode=0o000)
    print(f"{path.resolve()} has permissions {oct(get_path_perms(path))}")
    assert get_path_perms(path) == 0o000
    print()

    print("Listing test dir - expect to get permissions denied")
    with pytest.raises(PermissionError):
        try:
            list(path.iterdir())
        except PermissionError as e:
            print(f"Got PermissionError: {e}")
            raise
    print()

    print("Chmod test dir to 600 - expect to pass and print new permissions")
    path.chmod(0o600)
    print(f"{path.resolve()} has permissions {oct(get_path_perms(path))}")
    # Should run without exception but fails under pyfakefs
    list(path.iterdir())
    assert get_path_perms(path) == 0o600
    print()

    print("Set permissions back to 000")
    path.chmod(0o000)
    print(f"{path.resolve()} has permissions {oct(get_path_perms(path))}")
    assert get_path_perms(path) == 0o000
    print()

    print("Remove test directory while permissions 000 - expect to pass and show no such directory")
    path.rmdir()
    print(f"{path.resolve()} exists? {path.exists()}")
    assert not path.exists()
    print()

def test_do_mkdir_native():
    do_mkdir()

def test_do_mkdir_fakefs(fs):
    do_mkdir()

if __name__ == "__main__":
    do_mkdir()
Full python test output Notice that the `test_do_mkdir_native` test case passes, but the `test_do_mkdir_fakefs` test case fails.
jesse.cureton@jesse:~/debugging_logs/20220923_pyfakefs_bug_report$ ls -la
total 20
drwxrwxr-x 2 jesse.cureton jesse.cureton 4096 Sep 23 14:39 .
drwxrwxr-x 4 jesse.cureton jesse.cureton 4096 Sep 23 13:43 ..
-rwxrwxr-x 1 jesse.cureton jesse.cureton  608 Sep 23 13:55 bash_test.sh
-rw-rw-r-- 1 jesse.cureton jesse.cureton 1539 Sep 23 14:38 python_test.py
jesse.cureton@jesse:~/debugging_logs/20220923_pyfakefs_bug_report$ pytest-3
===================================================================================== test session starts ======================================================================================
platform linux -- Python 3.6.9, pytest-3.6.4, py-1.5.2, pluggy-0.6.0
rootdir: /home/jesse.cureton/debugging_logs/20220923_pyfakefs_bug_report, inifile:
plugins: pyfakefs-4.6.3
collected 2 items

python_test.py .F                                                                                                                                                                        [100%]

=========================================================================================== FAILURES ===========================================================================================
_____________________________________________________________________________________ test_do_mkdir_fakefs _____________________________________________________________________________________

fs = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f2ed2c85400>

    def test_do_mkdir_fakefs(fs):
>       do_mkdir()

/home/jesse.cureton/debugging_logs/20220923_pyfakefs_bug_report/python_test.py:52:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/home/jesse.cureton/debugging_logs/20220923_pyfakefs_bug_report/python_test.py:29: in do_mkdir
    path.chmod(0o600)
/usr/lib/python3.6/pathlib.py:1266: in chmod
    ???
/home/jesse.cureton/.local/lib/python3.6/site-packages/pyfakefs/fake_pathlib.py:124: in chmod
    return pathobj.filesystem.chmod(str(pathobj), *args, **kwargs)
/home/jesse.cureton/.local/lib/python3.6/site-packages/pyfakefs/fake_filesystem.py:1415: in chmod
    file_object = self.resolve(path, follow_symlinks, allow_fd=True)
/home/jesse.cureton/.local/lib/python3.6/site-packages/pyfakefs/fake_filesystem.py:2292: in resolve
    file_path, check_read_perm), check_read_perm)
/home/jesse.cureton/.local/lib/python3.6/site-packages/pyfakefs/fake_filesystem.py:2239: in get_object_from_normpath
    self.raise_os_error(errno.EACCES, target.path)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7f2ed2c85400>, err_no = 13, filename = '/tmp/test-chmod', winerror = None

    def raise_os_error(self, err_no: int,
                       filename: Optional[AnyString] = None,
                       winerror: Optional[int] = None) -> NoReturn:
        """Raises OSError.
            The error message is constructed from the given error code and shall
            start with the error string issued in the real system.
            Note: this is not true under Windows if winerror is given - in this
            case a localized message specific to winerror will be shown in the
            real file system.

            Args:
                err_no: A numeric error code from the C variable errno.
                filename: The name of the affected file, if any.
                winerror: Windows only - the specific Windows error code.
            """
        message = os.strerror(err_no) + ' in the fake filesystem'
        if (winerror is not None and sys.platform == 'win32' and
                self.is_windows_fs):
            raise OSError(err_no, message, filename, winerror)
>       raise OSError(err_no, message, filename)
E       PermissionError: [Errno 13] Permission denied in the fake filesystem: '/tmp/test-chmod'

/home/jesse.cureton/.local/lib/python3.6/site-packages/pyfakefs/fake_filesystem.py:1122: PermissionError
------------------------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------------------------
Creating test directory set setting permissions to 000
/tmp/test-chmod has permissions 0o0

Listing test dir - expect to get permissions denied
Got PermissionError: [Errno 13] Permission denied in the fake filesystem: '/tmp/test-chmod'

Chmod test dir to 600 - expect to pass and print new permissions
============================================================================== 1 failed, 1 passed in 0.32 seconds ==============================================================================

As a bonus, here's a Bash script that runs the same test to confirm that this does behave as expected. I've confirmed this on both MacOS and Linux.

Bash test case
#!/bin/bash

set +euo pipefail

cd /tmp

echo "Creating test directory set setting permissions to 000"
mkdir test-chmod
chmod 000 test-chmod
ls -la . | grep test-chmod
echo

echo "Listing test dir - expect to get permissions denied"
ls -la test-chmod/
echo

echo "Chmod test dir to 600 - expect to pass and print new permissions"
chmod 600 test-chmod
ls -la . | grep test-chmod
echo

echo "Set permissions back to 000"
chmod 000 test-chmod
ls -la . | grep test-chmod
echo

echo "Remove test directory while permissions 000 - expect to pass and show no such directory"
rmdir test-chmod
ls -la test-chmod
echo

Your environment
On MacOS:

jesse.cureton@Jesses-MacBook-Pro:/tmp$ python3 -c "import platform; print(platform.platform())"
macOS-12.5.1-x86_64-i386-64bit
jesse.cureton@Jesses-MacBook-Pro:/tmp$ python3 -c "import sys; print('Python', sys.version)"
Python 3.9.9 (main, Nov 21 2021, 03:23:42) 
[Clang 13.0.0 (clang-1300.0.29.3)]
jesse.cureton@Jesses-MacBook-Pro:/tmp$ python3 -c "from pyfakefs.fake_filesystem import __version__; print('pyfakefs', __version__)"
pyfakefs 4.7.0

On Ubuntu:

jesse.cureton@jesse:~/debugging_logs/20220923_pyfakefs_bug_report$ python3 -c "import platform; print(platform.platform())"
Linux-5.4.0-1083-aws-x86_64-with-Ubuntu-18.04-bionic
jesse.cureton@jesse:~/debugging_logs/20220923_pyfakefs_bug_report$ python3 -c "import sys; print('Python', sys.version)"
Python 3.6.9 (default, Jun 29 2022, 11:45:57)
[GCC 8.4.0]
jesse.cureton@jesse:~/debugging_logs/20220923_pyfakefs_bug_report$ python3 -c "from pyfakefs.fake_filesystem import __version__; print('pyfakefs', __version__)"
pyfakefs 4.6.3
@jessecureton-aurora jessecureton-aurora changed the title Behavior for directories with 0o000 permissions does not match native filesystems Behavior for directories with 0o000 permissions does not match native filesystems Sep 23, 2022
@mrbean-bremen
Copy link
Member

Thanks for the report and the detailed description! I will have a look at the problem.

mrbean-bremen added a commit to mrbean-bremen/pyfakefs that referenced this issue Oct 3, 2022
- rmdir and chmod work without permission
  if the file user ID is the current user ID
- better handle owner/group/other permission bits
- fixes pytest-dev#719
@mrbean-bremen
Copy link
Member

@jessecureton-aurora - should be fixed in master now, please check if it works for you.

github-actions bot pushed a commit that referenced this issue Oct 4, 2022
…ssion if the file user ID is the current user ID - better handle owner/group/other permission bits - fixes #719
@jessecureton-aurora
Copy link
Author

@mrbean-bremen Thanks for getting this fixed!! Yes, I just confirmed that this passes my test cases locally - all seems well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants