File 0001-set_flags-use-get-set-to-only-influence-specific-fla.patch of Package borgbackup

From: Thomas Waldmann <tw@waldmann-edv.de>
Date: Thu, 16 Oct 2025 21:26:49 +0200
Subject: set_flags: use get/set to only influence specific flags, fixes #9039
References: bsc#1251048
Git-repo: https://github.com/ThomasWaldmann/borg#fix-set_flags-1.4
Git-commit: 9214197a2cd18796553f1d2cce6faf5ad7576a95
Patch-mainline: Queued in subsystem maintainer repository

Linux platform only.

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
---
 src/borg/platform/linux.pyx | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/src/borg/platform/linux.pyx b/src/borg/platform/linux.pyx
index 35ad1bde5ba0..7a913791fc50 100644
--- a/src/borg/platform/linux.pyx
+++ b/src/borg/platform/linux.pyx
@@ -128,6 +128,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):
@@ -136,17 +138,31 @@ 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 this fails, fall back to 0 so we can still attempt to set.
+        if ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1:
+            flags = 0
+
+        # 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:
-- 
2.51.0
openSUSE Build Service is sponsored by