diff --git a/fileglancer/database.py b/fileglancer/database.py index e53d2dfa..735b3bc0 100644 --- a/fileglancer/database.py +++ b/fileglancer/database.py @@ -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): diff --git a/fileglancer/filestore.py b/fileglancer/filestore.py index 0a6f7001..90ca4a48 100644 --- a/fileglancer/filestore.py +++ b/fileglancer/filestore.py @@ -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: diff --git a/tests/test_database.py b/tests/test_database.py index ef5e021b..ac6a8a17 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -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) + diff --git a/tests/test_filestore.py b/tests/test_filestore.py index 07113ae1..a7933106 100644 --- a/tests/test_filestore.py +++ b/tests/test_filestore.py @@ -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):