mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 17:12:13 -06:00
ensure nested symlinks are not broken during deletes;
when moving/deleting a file, all symlinked dupes are verified to ensure this action does not break any symlinks, however it did this by checking the realpath of each link. This was not good enough, since the deleted file may be a part of a series of nested symlinks this situation occurs because the deduper tries to keep relative symlinks as close as possible, only traversing into parent/sibling folders as required, which can lead to several levels of nested links
This commit is contained in:
parent
e70ecd98ef
commit
9672b8c9b3
|
@ -43,6 +43,10 @@ def open(p: str, *a, **ka) -> int:
|
||||||
return os.open(fsenc(p), *a, **ka)
|
return os.open(fsenc(p), *a, **ka)
|
||||||
|
|
||||||
|
|
||||||
|
def readlink(p: str) -> str:
|
||||||
|
return fsdec(os.readlink(fsenc(p)))
|
||||||
|
|
||||||
|
|
||||||
def rename(src: str, dst: str) -> None:
|
def rename(src: str, dst: str) -> None:
|
||||||
return os.rename(fsenc(src), fsenc(dst))
|
return os.rename(fsenc(src), fsenc(dst))
|
||||||
|
|
||||||
|
|
|
@ -3617,6 +3617,8 @@ class Up2k(object):
|
||||||
except:
|
except:
|
||||||
self.log("relink: not found: [{}]".format(ap))
|
self.log("relink: not found: [{}]".format(ap))
|
||||||
|
|
||||||
|
# self.log("full:\n" + "\n".join(" {:90}: {}".format(*x) for x in full.items()))
|
||||||
|
# self.log("links:\n" + "\n".join(" {:90}: {}".format(*x) for x in links.items()))
|
||||||
if not dabs and not full and links:
|
if not dabs and not full and links:
|
||||||
# deleting final remaining full copy; swap it with a symlink
|
# deleting final remaining full copy; swap it with a symlink
|
||||||
slabs = list(sorted(links.keys()))[0]
|
slabs = list(sorted(links.keys()))[0]
|
||||||
|
@ -3634,12 +3636,45 @@ class Up2k(object):
|
||||||
dabs = list(sorted(full.keys()))[0]
|
dabs = list(sorted(full.keys()))[0]
|
||||||
|
|
||||||
for alink, parts in links.items():
|
for alink, parts in links.items():
|
||||||
lmod = None
|
lmod = 0.0
|
||||||
try:
|
try:
|
||||||
if alink != sabs and absreal(alink) != sabs:
|
faulty = False
|
||||||
continue
|
ldst = alink
|
||||||
|
try:
|
||||||
|
for n in range(40): # MAXSYMLINKS
|
||||||
|
zs = bos.readlink(ldst)
|
||||||
|
ldst = os.path.join(os.path.dirname(ldst), zs)
|
||||||
|
ldst = bos.path.abspath(ldst)
|
||||||
|
if not bos.path.islink(ldst):
|
||||||
|
break
|
||||||
|
|
||||||
self.log("relinking [{}] to [{}]".format(alink, dabs))
|
if ldst == sabs:
|
||||||
|
t = "relink because level %d would break:"
|
||||||
|
self.log(t % (n,), 6)
|
||||||
|
faulty = True
|
||||||
|
except Exception as ex:
|
||||||
|
self.log("relink because walk failed: %s; %r" % (ex, ex), 3)
|
||||||
|
faulty = True
|
||||||
|
|
||||||
|
zs = absreal(alink)
|
||||||
|
if ldst != zs:
|
||||||
|
t = "relink because computed != actual destination:\n %s\n %s"
|
||||||
|
self.log(t % (ldst, zs), 3)
|
||||||
|
ldst = zs
|
||||||
|
faulty = True
|
||||||
|
|
||||||
|
if bos.path.islink(ldst):
|
||||||
|
raise Exception("broken symlink: %s" % (alink,))
|
||||||
|
|
||||||
|
if alink != sabs and ldst != sabs and not faulty:
|
||||||
|
continue # original symlink OK; leave it be
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
t = "relink because symlink verification failed: %s; %r"
|
||||||
|
self.log(t % (ex, ex), 3)
|
||||||
|
|
||||||
|
self.log("relinking [%s] to [%s]" % (alink, dabs))
|
||||||
|
try:
|
||||||
lmod = bos.path.getmtime(alink, False)
|
lmod = bos.path.getmtime(alink, False)
|
||||||
bos.unlink(alink)
|
bos.unlink(alink)
|
||||||
except:
|
except:
|
||||||
|
|
Loading…
Reference in a new issue