Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sync with purge keeps orphan destination directory having read-only file #49

Open
jokram opened this issue Mar 10, 2023 · 1 comment
Open

Comments

@jokram
Copy link

jokram commented Mar 10, 2023

Description

When calling sync with purge set to True and having a directory only in the destination with one read-only file then the sync removes the read-only file (OK), but the directory remains (Not OK).

Note that sometimes it works fine, but sometimes it fails. 😕
For cleaning up the read-only file there was another issue #25 implemented (and this works fine), but here the remaining empty directory is still in the destination.

Environment

  • Windows 10 Enterprise Build 10.0.19044
  • dirsync 2.2.5

Code to reproduce the error

Run the code several times, because sometimes it works, sometimes it gets an error!

import os
import shutil
import stat
from pathlib import Path
from dirsync import sync

test_dir = Path("D:/tmp/sync_test")
test_file = "test1.txt"
src_dir = test_dir / "src"
dst_dir = test_dir / "dst"
if src_dir.is_dir():
    shutil.rmtree(src_dir, onerror=lambda func, path, _: (os.chmod(path, stat.S_IWRITE), func(path)))
if dst_dir.is_dir():
    shutil.rmtree(dst_dir, onerror=lambda func, path, _: (os.chmod(path, stat.S_IWRITE), func(path)))
dst_dir.mkdir(parents=True, exist_ok=True)
# Build up a directory structure where all the files are read-only:
# +---[sub1]
#     +---test1.txt
# +---[sub2]
#     +---test1.txt
# +---[sub3]
#     +---test1.txt
for folder in ("sub1", "sub2", "sub3"):
    cur_dir = src_dir / folder
    cur_dir.mkdir(parents=True, exist_ok=True)
    open(cur_dir / test_file, 'w').close()
    # Set the file to read-only, because only then the error occurs
    os.chmod(cur_dir / test_file, stat.S_IREAD)
# The first sync works fine
print(f"Mirror {src_dir} -> {dst_dir}")
sync(src_dir, dst_dir, "sync", purge=True, verbose=False)
src_sub2 = src_dir / "sub2"
print("Remove the sub2 directory from the source")
shutil.rmtree(src_sub2, onerror=lambda func, path, _: (os.chmod(path, stat.S_IWRITE), func(path)))
# Now the sync (with purge set to True) should also delete the sub2 directory in the destination
print(f"Mirror {src_dir} -> {dst_dir} (should remove the sub2 directory)")
sync(src_dir, dst_dir, "sync", purge=True, verbose=False)
# Very strange: Often I'll get an error, but sometimes it seems to work
dst_sub2 = dst_dir / "sub2"
if dst_sub2.is_dir():
    print(f"*** ERROR: {dst_sub2} still exists in destination")
else:
    print("OK")
@jokram jokram changed the title sync with purge keeps orphan destination directory if it contains read-only files sync with purge keeps orphan destination directory if it contains read-only file Mar 10, 2023
@jokram jokram changed the title sync with purge keeps orphan destination directory if it contains read-only file sync with purge keeps orphan destination directory having read-only file Mar 10, 2023
@esn83
Copy link

esn83 commented Nov 26, 2024

This issue relates to syncer.py line 224 if os.path.isfile(fullf2): that only checks for files. Use this to also check for folders if os.path.isfile(fullf2) or os.path.isdir(fullf2):. The removal of the folder also fails with os.remove(fullf2) in line 230. Use this to successfully remove the dir shutil.rmtree(fullf2).

EDIT:
I ended up replacing line 224 to 230 in syncer.py with this

                    if os.path.isfile(fullf2) or os.path.isdir(fullf2):
                        try:
                            try:
                                if os.path.isfile(fullf2):
                                    os.remove(fullf2)
                                elif os.path.isdir(fullf2):
                                    shutil.rmtree(fullf2)
                            except PermissionError as e:
                                os.chmod(fullf2, stat.S_IWRITE)
                                if os.path.isfile(fullf2):
                                    os.remove(fullf2)
                                elif os.path.isdir(fullf2):
                                    shutil.rmtree(fullf2)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants