diff --git a/lib/galaxy/files/sources/posix.py b/lib/galaxy/files/sources/posix.py index 8768b5b5ca9c..6c9d744c682d 100644 --- a/lib/galaxy/files/sources/posix.py +++ b/lib/galaxy/files/sources/posix.py @@ -26,6 +26,7 @@ DEFAULT_ENFORCE_SYMLINK_SECURITY = True DEFAULT_DELETE_ON_REALIZE = False DEFAULT_ALLOW_SUBDIR_CREATION = True +DEFAULT_LINK_ONLY = False class PosixFilesSourceProperties(FilesSourceProperties, total=False): @@ -33,6 +34,7 @@ class PosixFilesSourceProperties(FilesSourceProperties, total=False): enforce_symlink_security: bool delete_on_realize: bool allow_subdir_creation: bool + link_only: bool class PosixFilesSource(BaseFilesSource): @@ -50,6 +52,7 @@ def __init__(self, **kwd: Unpack[PosixFilesSourceProperties]): self.root = props.get("root") if not self.root: self.writable = False + self.link_only = props.get("link_only", DEFAULT_LINK_ONLY) self.enforce_symlink_security = props.get("enforce_symlink_security", DEFAULT_ENFORCE_SYMLINK_SECURITY) self.delete_on_realize = props.get("delete_on_realize", DEFAULT_DELETE_ON_REALIZE) self.allow_subdir_creation = props.get("allow_subdir_creation", DEFAULT_ALLOW_SUBDIR_CREATION) @@ -103,7 +106,11 @@ def _realize_to( source_native_path = os.path.normpath(source_native_path) assert source_native_path.startswith(os.path.normpath(effective_root)) - if not self.delete_on_realize: + if self.link_only: + if os.path.exists(native_path) or os.path.islink(native_path): + os.remove(native_path) + os.symlink(source_native_path, native_path) + elif not self.delete_on_realize: shutil.copyfile(source_native_path, native_path) else: shutil.move(source_native_path, native_path) @@ -133,9 +140,14 @@ def _write_from( # Use a temporary name while writing so anything that consumes written files can detect when they've completed, # and identify interrupted writes - target_native_path_part = os.path.join(target_native_path_parent, f"_{target_native_path_name}.part") - shutil.copyfile(native_path, target_native_path_part) - os.rename(target_native_path_part, target_native_path) + if self.link_only: + if os.path.exists(native_path) or os.path.islink(native_path): + os.remove(native_path) + os.symlink(target_native_path, native_path) + else: + target_native_path_part = os.path.join(target_native_path_parent, f"_{target_native_path_name}.part") + shutil.copyfile(native_path, target_native_path_part) + os.rename(target_native_path_part, target_native_path) def _to_native_path(self, source_path: str, user_context: OptionalUserContext = None): source_path = os.path.normpath(source_path) @@ -182,6 +194,7 @@ def _serialization_props(self, user_context: OptionalUserContext = None) -> Posi "enforce_symlink_security": self.enforce_symlink_security, "delete_on_realize": self.delete_on_realize, "allow_subdir_creation": self.allow_subdir_creation, + "link_only": self.link_only, } @property