Fix copying symlinks in Linux

This commit is contained in:
Maks Snegov 2021-10-23 21:07:14 +03:00
parent 1b6badf375
commit f01d24f1bd
2 changed files with 26 additions and 8 deletions

View File

@ -112,9 +112,12 @@ def copy_direntry(entry: os.DirEntry, dst_path):
copy_file(entry.path, dst_path) copy_file(entry.path, dst_path)
src_stat = entry.stat(follow_symlinks=False) src_stat = entry.stat(follow_symlinks=False)
os.chown(dst_path, src_stat.st_uid, src_stat.st_gid, follow_symlinks=False) if not entry.is_symlink() or os.chown in os.supports_follow_symlinks:
os.chmod(dst_path, src_stat.st_mode, follow_symlinks=False) os.chown(dst_path, src_stat.st_uid, src_stat.st_gid, follow_symlinks=False)
os.utime(dst_path, (src_stat.st_atime, src_stat.st_mtime), follow_symlinks=False) if not entry.is_symlink() or os.chmod in os.supports_follow_symlinks:
os.chmod(dst_path, src_stat.st_mode, follow_symlinks=False)
if not entry.is_symlink() or os.utime in os.supports_follow_symlinks:
os.utime(dst_path, (src_stat.st_atime, src_stat.st_mtime), follow_symlinks=False)
def update_direntry(src_entry: os.DirEntry, dst_entry: os.DirEntry): def update_direntry(src_entry: os.DirEntry, dst_entry: os.DirEntry):

View File

@ -20,17 +20,26 @@ class CommonFSTestCase(unittest.TestCase):
self.tmp_dir_dst.cleanup() self.tmp_dir_dst.cleanup()
@staticmethod @staticmethod
def create_file(parent_dir, prefix=None): def create_file(parent_dir: str, prefix: str = None) -> str:
"""
Create file with random name in parent_dir.
Returns absolute path to created file.
"""
fd, path = tempfile.mkstemp(prefix=prefix, dir=parent_dir) fd, path = tempfile.mkstemp(prefix=prefix, dir=parent_dir)
with open(fd, "w") as f: with open(fd, "w") as f:
f.write(string.printable) f.write(string.printable)
return path return path
@staticmethod @staticmethod
def create_dir(parent_dir, prefix=None): def create_dir(parent_dir: str, prefix: str = None) -> str:
"""
Create directory with random name in parent_dir.
Returns absolute path to created directory.
"""
return tempfile.mkdtemp(prefix=prefix, dir=parent_dir) return tempfile.mkdtemp(prefix=prefix, dir=parent_dir)
def relpath(self, full_path): def relpath(self, full_path: str) -> str:
""" Get relative path for entity in src/dst dirs. """
if full_path.startswith(self.src_dir): if full_path.startswith(self.src_dir):
p_dir = self.src_dir p_dir = self.src_dir
elif full_path.startswith(self.dst_dir): elif full_path.startswith(self.dst_dir):
@ -118,7 +127,8 @@ class TestHardlinkDir(CommonFSTestCase):
# TODO not finished # TODO not finished
class TestRsync(CommonFSTestCase): class TestRsync(CommonFSTestCase):
@staticmethod @staticmethod
def check_identical_file(file1, file2): def check_identical_file(file1: str, file2: str):
""" Check that files are identical. Fails test, if not. """
st1 = os.lstat(file1) st1 = os.lstat(file1)
st2 = os.lstat(file2) st2 = os.lstat(file2)
@ -135,7 +145,7 @@ class TestRsync(CommonFSTestCase):
assert not os.path.lexists(dst_fpath) assert not os.path.lexists(dst_fpath)
def test_dst_has_excess_symlink(self): def test_dst_has_excess_symlink(self):
dst_lpath = os.path.join(self.dst_dir, 'broken_symlink') dst_lpath = os.path.join(self.dst_dir, 'nonexisting_file')
os.symlink('broken_symlink', dst_lpath) os.symlink('broken_symlink', dst_lpath)
fs.rsync(self.src_dir, self.dst_dir) fs.rsync(self.src_dir, self.dst_dir)
@ -239,3 +249,8 @@ class TestRsync(CommonFSTestCase):
fs.rsync(self.src_dir, self.dst_dir) fs.rsync(self.src_dir, self.dst_dir)
assert os.path.lexists(dst_fpath) assert os.path.lexists(dst_fpath)
self.check_identical_file(src_fpath, dst_fpath) self.check_identical_file(src_fpath, dst_fpath)
# TODO add tests for changing ownership
# TODO add tests for changing permissions
# TODO add tests for changing times (?)
# TODO add tests for symlink behaviour