From 170b73834b8e89bf13d38b057a2c988db4aa06e2 Mon Sep 17 00:00:00 2001 From: Maks Snegov Date: Thu, 10 Jun 2021 22:19:26 +0300 Subject: [PATCH] Add hardlinking with cp -al --- spqr/curateipsum/fs.py | 66 +++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/spqr/curateipsum/fs.py b/spqr/curateipsum/fs.py index 599600a..ccf2940 100644 --- a/spqr/curateipsum/fs.py +++ b/spqr/curateipsum/fs.py @@ -4,7 +4,6 @@ Module with filesystem-related functions. import logging import os -import pathlib import subprocess from typing import Iterable @@ -82,6 +81,47 @@ def rsync(src_dir, dst_dir=None): _lg.info("Updating %s", src_entry) +def _hardlink_dir_ext(src, dst): + """ + Make hardlink for a directory using cp -al. Both src and dst should exist. + :param src: absolute path to source directory. + :param dst: absolute path to target directory. + :return: None + """ + res = subprocess.run(["cp", "-v", "-a", "-l", f"{src}/*", dst]) + return res + + +def _recursive_hardlink(src, dst): + """ + Do hardlink directory recursively using python only. + Both src and dst directories should exist. + :param src: absolute path to source directory. + :param dst: absolute path to target directory. + :return: None + """ + with os.scandir(src) as it: + ent: os.DirEntry + for ent in it: + ent_dst_path = os.path.join(dst, ent.name) + if ent.is_dir(follow_symlinks=False): + _lg.debug(f"Copying directory: {ent.path} -> {ent_dst_path}") + os.mkdir(ent_dst_path) + ent_stat = ent.stat(follow_symlinks=False) + os.chown(ent_dst_path, ent_stat.st_uid, ent_stat.st_gid) + os.chmod(ent_dst_path, ent_stat.st_mode) + + # process directory children + _recursive_hardlink(ent.path, ent_dst_path) + continue + if ent.is_file(follow_symlinks=False) or ent.is_symlink(): + _lg.debug(f"Hardlink file: {ent.path} -> {ent_dst_path}") + os.link(ent.path, ent_dst_path, follow_symlinks=False) + continue + # something that is not a file, symlink or directory + raise NotImplementedError(ent.path) + + def hardlink_dir(src_dir, dst_dir): """ Make hardlink for a directory with all its content. @@ -93,28 +133,6 @@ def hardlink_dir(src_dir, dst_dir): src_abs = os.path.abspath(src_dir) dst_abs = os.path.abspath(dst_dir) - def recursive_hardlink(src, dst): - with os.scandir(src) as it: - ent: os.DirEntry - for ent in it: - ent_dst_path = os.path.join(dst, ent.name) - if ent.is_dir(follow_symlinks=False): - _lg.debug(f"Copying directory: {ent.path} -> {ent_dst_path}") - os.mkdir(ent_dst_path) - ent_stat = ent.stat(follow_symlinks=False) - os.chown(ent_dst_path, ent_stat.st_uid, ent_stat.st_gid) - os.chmod(ent_dst_path, ent_stat.st_mode) - - # process directory children - recursive_hardlink(ent.path, ent_dst_path) - continue - if ent.is_file(follow_symlinks=False) or ent.is_symlink(): - _lg.debug(f"Hardlink file: {ent.path} -> {ent_dst_path}") - os.link(ent.path, ent_dst_path, follow_symlinks=False) - continue - # something that is not a file, symlink or directory - raise NotImplementedError(ent.path) - if not os.path.isdir(src_abs): _lg.error(f"Error reading source directory: {src_dir}") raise RuntimeError(f"Error reading source directory: {src_dir}") @@ -125,5 +143,5 @@ def hardlink_dir(src_dir, dst_dir): _lg.debug(f"Creating directory: {dst_abs}") os.mkdir(dst_abs) - recursive_hardlink(src_abs, dst_abs) + _recursive_hardlink(src_abs, dst_abs) return