From a6b6d35b30daeb1dd9376ed4b5400fbf8b8cf7ec Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 16 Oct 2025 21:26:49 +0200 Subject: [PATCH 1/2] set_flags: use get/set to only influence specific flags, fixes #9039 Linux platform only. (cherry picked from commit 9214197a2cd18796553f1d2cce6faf5ad7576a95) set_flags: if getting the flags fails, better give up than corrupting them. Thanks to Earnestly for the feedback on IRC. (cherry picked from commit 9c600a95715ec22a5dd6cfba9bb1bee8238fc938) --- src/borg/platform/linux.pyx | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/borg/platform/linux.pyx b/src/borg/platform/linux.pyx index 5ea9370cb..a76b36388 100644 --- a/src/borg/platform/linux.pyx +++ b/src/borg/platform/linux.pyx @@ -126,6 +126,8 @@ BSD_TO_LINUX_FLAGS = { stat.UF_APPEND: FS_APPEND_FL, stat.UF_COMPRESSED: FS_COMPR_FL, } +# must be a bitwise OR of all values in BSD_TO_LINUX_FLAGS. +LINUX_MASK = FS_NODUMP_FL | FS_IMMUTABLE_FL | FS_APPEND_FL | FS_COMPR_FL def set_flags(path, bsd_flags, fd=None): @@ -134,17 +136,34 @@ def set_flags(path, bsd_flags, fd=None): if stat.S_ISBLK(st.st_mode) or stat.S_ISCHR(st.st_mode) or stat.S_ISLNK(st.st_mode): # see comment in get_flags() return - cdef int flags = 0 + cdef int flags + cdef int mask = LINUX_MASK # 1 at positions we want to influence + cdef int new_flags = 0 for bsd_flag, linux_flag in BSD_TO_LINUX_FLAGS.items(): if bsd_flags & bsd_flag: - flags |= linux_flag + new_flags |= linux_flag + open_fd = fd is None if open_fd: fd = os.open(path, os.O_RDONLY|os.O_NONBLOCK|os.O_NOFOLLOW) try: + # Get current flags. + if ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1: + # If this fails, give up because it is either not supported by the fs + # or maybe not permitted? If we can't determine the current flags, + # we better not risk corrupting them by setflags, see the comment below. + return # give up silently + + # Replace only the bits we actually want to influence, keep others. + # We can't just set all flags to the archived value, because we might + # reset flags that are not controllable from userspace, see #9039. + flags = (flags & ~mask) | (new_flags & mask) + if ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1: error_number = errno.errno - if error_number != errno.EOPNOTSUPP: + # Usually we would only catch EOPNOTSUPP here, but Linux Kernel 6.17 + # has a bug where it returns ENOTTY instead of EOPNOTSUPP. + if error_number not in (errno.EOPNOTSUPP, errno.ENOTTY): raise OSError(error_number, strerror(error_number).decode(), path) finally: if open_fd: From aea883b700f29fbb9ddfdcb5330c79cf30a3c20a Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 17 Oct 2025 02:41:53 +0200 Subject: [PATCH 2/2] set_flags: remove compression flag This flag needs to be set BEFORE writing to the file. But "borg extract" sets the flags last (to support IMMUTABLE), thus the compression flag would not work as expected. (cherry picked from commit 56dda841623f90556b37798e85f9371ebe4a3de2) --- src/borg/platform/linux.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/borg/platform/linux.pyx b/src/borg/platform/linux.pyx index a76b36388..e3f0d3859 100644 --- a/src/borg/platform/linux.pyx +++ b/src/borg/platform/linux.pyx @@ -124,10 +124,9 @@ BSD_TO_LINUX_FLAGS = { stat.UF_NODUMP: FS_NODUMP_FL, stat.UF_IMMUTABLE: FS_IMMUTABLE_FL, stat.UF_APPEND: FS_APPEND_FL, - stat.UF_COMPRESSED: FS_COMPR_FL, } # must be a bitwise OR of all values in BSD_TO_LINUX_FLAGS. -LINUX_MASK = FS_NODUMP_FL | FS_IMMUTABLE_FL | FS_APPEND_FL | FS_COMPR_FL +LINUX_MASK = FS_NODUMP_FL | FS_IMMUTABLE_FL | FS_APPEND_FL def set_flags(path, bsd_flags, fd=None):