From f2caab6119b37d16340142208a76e51ddc9e8e6b Mon Sep 17 00:00:00 2001 From: ed Date: Sat, 11 Oct 2025 23:43:09 +0000 Subject: [PATCH] dedup: explicit reflink/ficlone on python<3.14 --- copyparty/authsrv.py | 2 -- copyparty/up2k.py | 28 +++++++++++++++++++++++----- copyparty/util.py | 2 ++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index 3e6b92e2..979fd042 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -2868,8 +2868,6 @@ class AuthSrv(object): if have_reflink: t = "WARNING: Reflink-based dedup was requested, but %s. This will not work; files will be full copies instead." - if sys.version_info < (3, 14): - self.log(t % "your python version is not new enough", 1) if not sys.platform.startswith("linux"): self.log(t % "your OS is not Linux", 1) diff --git a/copyparty/up2k.py b/copyparty/up2k.py index b6032f1e..58bfb445 100644 --- a/copyparty/up2k.py +++ b/copyparty/up2k.py @@ -10,6 +10,7 @@ import re import shutil import stat import subprocess as sp +import sys import tempfile import threading import time @@ -27,6 +28,7 @@ from .mtag import MParser, MTag from .util import ( E_FS_CRIT, E_FS_MEH, + HAVE_FICLONE, HAVE_SQLITE3, SYMTIME, VF_CAREFUL, @@ -88,6 +90,10 @@ if True: # pylint: disable=using-constant-test if TYPE_CHECKING: from .svchub import SvcHub +USE_FICLONE = HAVE_FICLONE and sys.version_info < (3, 14) +if USE_FICLONE: + import fcntl + zsg = "avif,avifs,bmp,gif,heic,heics,heif,heifs,ico,j2p,j2k,jp2,jpeg,jpg,jpx,png,tga,tif,tiff,webp" ICV_EXTS = set(zsg.split(",")) @@ -3530,11 +3536,26 @@ class Up2k(object): linked = False try: - if "reflink" in flags: - raise Exception("reflink") + if rm and bos.path.exists(dst): + wunlink(self.log, dst, flags) + if not is_mv and not flags.get("dedup"): raise Exception("dedup is disabled in config") + if "reflink" in flags: + if not USE_FICLONE: + raise Exception("reflink") # python 3.14 or newer; no need + try: + with open(fsenc(src), "rb") as fi, open(fsenc(dst), "wb") as fo: + fcntl.ioctl(fo.fileno(), fcntl.FICLONE, fi.fileno()) + except: + if bos.path.exists(dst): + wunlink(self.log, dst, flags) + raise + if lmod: + bos.utime_c(self.log, dst, int(lmod), False) + return + lsrc = src ldst = dst fs1 = bos.stat(os.path.dirname(src)).st_dev @@ -3561,9 +3582,6 @@ class Up2k(object): lsrc = lsrc.replace("/", "\\") ldst = ldst.replace("/", "\\") - if rm and bos.path.exists(dst): - wunlink(self.log, dst, flags) - try: if "hardlink" in flags: os.link(fsenc(absreal(src)), fsenc(dst)) diff --git a/copyparty/util.py b/copyparty/util.py index d8011ddf..4f026a56 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -129,8 +129,10 @@ try: import fcntl HAVE_FCNTL = True + HAVE_FICLONE = hasattr(fcntl, "FICLONE") except: HAVE_FCNTL = False + HAVE_FICLONE = False try: import ctypes