copy: Copy nocow flag by default

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.
This commit is contained in:
Daan De Meyer
2024-09-04 19:23:13 +02:00
parent 07862c9fc2
commit 8af3b12fe0
+41
View 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);