]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
copy: Copy nocow flag by default
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 23 Aug 2024 11:04:33 +0000 (13:04 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 4 Sep 2024 17:23:13 +0000 (19:23 +0200)
Unless otherwise requested, if we're going to copy a nocow file, make the
target file nocow as well.

Aside from keeping the performance characteristics of the cow or nocow file
intact, reflinking also only works from cow to cow or nocow to nocow files.
Reflinking from cow to nocow or nocow to cow files does not work and can
easily lead to unexpected copies for users, so by keeping the nocow bit
intact across copies by default we also make sure reflinks always work.

src/shared/copy.c

index abebe2d2f223eb214c5d26d2fade5f1c136c3485..8af1cb5ce5f3b6123441e96789811d67e2b04ac7 100644 (file)
@@ -765,6 +765,35 @@ static int memorize_hardlink(
         return 1;
 }
 
+static int prepare_nocow(int fdf, const char *from, int fdt, unsigned *chattr_mask, unsigned *chattr_flags) {
+        unsigned attrs = 0;
+        int r;
+
+        assert(fdf >= 0 || fdf == AT_FDCWD);
+        assert(fdt >= 0);
+        assert(!!chattr_mask == !!chattr_flags);
+
+        /* If caller explicitly requested NOCOW to be set or unset, let's not interfere. */
+        if (chattr_mask && FLAGS_SET(*chattr_mask, FS_NOCOW_FL))
+                return 0;
+
+        r = read_attr_at(fdf, from, &attrs);
+        if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r) && r != -ELOOP) /* If the source is a symlink we get ELOOP */
+                return r;
+
+        if (FLAGS_SET(attrs, FS_NOCOW_FL)) {
+                if (chattr_mask && chattr_flags) {
+                        *chattr_mask |= FS_NOCOW_FL;
+                        *chattr_flags |= FS_NOCOW_FL;
+                } else
+                        /* If the NOCOW flag is set on the source, make the copy NOCOW as well. If the source
+                         * is not NOCOW, don't do anything in particular with the copy. */
+                        (void) chattr_fd(fdt, FS_NOCOW_FL, FS_NOCOW_FL, /*previous=*/ NULL);
+        }
+
+        return 0;
+}
+
 static int fd_copy_tree_generic(
                 int df,
                 const char *from,
@@ -824,6 +853,10 @@ static int fd_copy_regular(
         if (fdt < 0)
                 return -errno;
 
+        r = prepare_nocow(fdf, /*from=*/ NULL, fdt, /*chattr_mask=*/ NULL, /*chattr_flags=*/ NULL);
+        if (r < 0)
+                return r;
+
         r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress, userdata);
         if (r < 0)
                 goto fail;
@@ -1452,6 +1485,10 @@ int copy_file_at_full(
                         goto fail;
         }
 
+        r = prepare_nocow(fdf, /*from=*/ NULL, fdt, &chattr_mask, &chattr_flags);
+        if (r < 0)
+                return r;
+
         if (chattr_mask != 0)
                 (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL);
 
@@ -1530,6 +1567,10 @@ int copy_file_atomic_at_full(
         if (fdt < 0)
                 return fdt;
 
+        r = prepare_nocow(dir_fdf, from, fdt, &chattr_mask, &chattr_flags);
+        if (r < 0)
+                return r;
+
         if (chattr_mask != 0)
                 (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL);