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

be more forgiving if follow_symlinks=False is not supported #1026

Merged
merged 3 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions src/maestral/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,6 @@ def __init__(

@staticmethod
def _check_system_compatibility() -> None:
if os.stat not in os.supports_follow_symlinks:
raise RuntimeError("Maestral requires lstat support")

if not (IS_MACOS or IS_LINUX):
raise RuntimeError("Only macOS and Linux are supported")

Expand Down
43 changes: 20 additions & 23 deletions src/maestral/utils/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,9 @@ def move(
also a folder.
:param raise_error: Whether to raise errors or return them.
:param preserve_dest_permissions: Whether to apply the permissions of the source
path to the destination path. Permissions will not be set recursively.
path to the destination path. Permissions will not be set recursively and may
will be set for symlinks if this is not supported by the platform, i.e., if
``os.chmod not in os.supports_follow_symlinks``.
:returns: Any caught exception during the move.
"""
err: Optional[OSError] = None
Expand All @@ -380,7 +382,7 @@ def move(
# save dest permissions
try:
orig_mode = os.lstat(dest_path).st_mode & 0o777
except FileNotFoundError:
except (FileNotFoundError, NotADirectoryError):
pass

try:
Expand All @@ -392,12 +394,11 @@ def move(
err = exc
else:
if orig_mode:
# reapply dest permissions
# Reapply dest permissions. If the dest is a symlink, only apply permissions
# if this is supported for symlinks by the platform.
try:
if os.chmod in os.supports_follow_symlinks:
os.chmod(dest_path, orig_mode, follow_symlinks=False)
else:
os.chmod(dest_path, orig_mode)
except OSError:
pass

Expand Down Expand Up @@ -530,36 +531,32 @@ def opener_no_symlink(path: _AnyPath, flags: int) -> int:
return os.open(path, flags=flags)


def _get_stats_no_symlink(path: _AnyPath) -> Optional[os.stat_result]:
try:
return os.lstat(path)
except (FileNotFoundError, NotADirectoryError):
return None


def exists(path: _AnyPath) -> bool:
"""Returns whether an item exists at the path. Returns True for symlinks."""
return _get_stats_no_symlink(path) is not None
try:
os.lstat(path)
except (FileNotFoundError, NotADirectoryError):
return False
else:
return True


def isfile(path: _AnyPath) -> bool:
"""Returns whether a file exists at the path. Returns True for symlinks."""
stat = _get_stats_no_symlink(path)

if stat is None:
return False
else:
try:
stat = os.lstat(path)
return not S_ISDIR(stat.st_mode)
except (FileNotFoundError, NotADirectoryError):
return False


def isdir(path: _AnyPath) -> bool:
"""Returns whether a folder exists at the path. Returns False for symlinks."""
stat = _get_stats_no_symlink(path)

if stat is None:
return False
else:
try:
stat = os.lstat(path)
return S_ISDIR(stat.st_mode)
except (FileNotFoundError, NotADirectoryError):
return False


def getsize(path: _AnyPath) -> int:
Expand Down
4 changes: 4 additions & 0 deletions tests/linked/integration/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,10 @@ def test_excluded_folder_cleared_on_deletion(m: Maestral) -> None:
assert_no_errors(m)


@pytest.mark.skipif(
os.chmod not in os.supports_follow_symlinks,
reason="chmod does not support follow_symlinks=False",
)
def test_unix_permissions(m: Maestral) -> None:
"""
Tests that a newly downloaded file is created with default permissions for our
Expand Down
Loading