Skip to content
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
8 changes: 4 additions & 4 deletions fileglancer/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,16 +496,16 @@ def find_fsp_from_absolute_path(session: Session, absolute_path: str) -> Optiona
Returns:
Tuple of (FileSharePath, relative_subpath) if an exact match is found, None otherwise
"""
# Normalize the input path
normalized_path = os.path.normpath(absolute_path)
# Resolve symlinks in the input path (e.g., /var -> /private/var on macOS)
normalized_path = os.path.realpath(absolute_path)

# Get all file share paths
paths = get_file_share_paths(session)

for fsp in paths:
# Expand ~ to user's home directory before matching
# Expand ~ to user's home directory and resolve symlinks to match Filestore behavior
expanded_mount_path = os.path.expanduser(fsp.mount_path)
expanded_mount_path = os.path.normpath(expanded_mount_path)
expanded_mount_path = os.path.realpath(expanded_mount_path)

# Check if the normalized path starts with this mount path
if normalized_path.startswith(expanded_mount_path):
Expand Down
3 changes: 2 additions & 1 deletion fileglancer/filestore.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ def __init__(self, file_share_path: FileSharePath):
"""
# Expand ~/ to the user's home directory (within user context)
expanded_path = os.path.expanduser(file_share_path.mount_path)
self.root_path = os.path.abspath(expanded_path)
# Use realpath to resolve symlinks for consistent path operations (e.g., /var -> /private/var on macOS)
self.root_path = os.path.realpath(expanded_path)


def _check_path_in_root(self, path: Optional[str]) -> str:
Expand Down
38 changes: 38 additions & 0 deletions tests/test_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,41 @@ def test_find_fsp_from_absolute_path_boundary_check(db_session, temp_dir):
# This should not match because similar_path is not a subdirectory of temp_dir
assert result is None or result[0].mount_path != temp_dir


def test_find_fsp_from_absolute_path_with_symlink_resolution(db_session, temp_dir):
"""Test that find_fsp_from_absolute_path resolves symlinks correctly.
This addresses macOS symlink behavior. E.g., /var -> /private/var."""
# Create a file share path in temp_dir
fsp = FileSharePathDB(
name="test_mount",
zone="testzone",
group="testgroup",
storage="local",
mount_path=temp_dir,
mac_path=temp_dir,
windows_path=temp_dir,
linux_path=temp_dir
)
db_session.add(fsp)
db_session.commit()

# Create a subdirectory that we'll link to
target_dir = os.path.join(temp_dir, "target")
os.makedirs(target_dir, exist_ok=True)

# Create another temporary directory to hold the symlink
symlink_container = tempfile.mkdtemp()
try:
# Create a symlink pointing to the target directory
symlink_path = os.path.join(symlink_container, "link_to_target")
os.symlink(target_dir, symlink_path)

# When we resolve the symlink path, it should find the FSP
# This tests that the function uses realpath() to resolve symlinks
result = find_fsp_from_absolute_path(db_session, symlink_path)
assert result is not None, "Should find FSP through symlink resolution"
assert result[0].name == "test_mount"
assert result[1] == "target"
finally:
shutil.rmtree(symlink_container)

3 changes: 2 additions & 1 deletion tests/test_filestore.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ def test_unmounted_filestore():


def test_get_root_path(filestore, test_dir):
assert filestore.get_root_path() == test_dir
# Root path should be the canonicalized/resolved version of test_dir
assert filestore.get_root_path() == os.path.realpath(test_dir)


def test_get_root_info(filestore, test_dir):
Expand Down