Merge pull request #34090 from DaanDeMeyer/cow-fix

Rework COW <=> NOCOW copying behavior
This commit is contained in:
Yu Watanabe
2024-09-05 10:19:49 +09:00
committed by GitHub
6 changed files with 74 additions and 42 deletions
+13 -4
View File
@@ -149,14 +149,23 @@ int read_attr_fd(int fd, unsigned *ret) {
}
int read_attr_at(int dir_fd, const char *path, unsigned *ret) {
_cleanup_close_ int fd = -EBADF;
_cleanup_close_ int fd_close = -EBADF;
int fd;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(ret);
fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd < 0)
return fd;
if (isempty(path)) {
fd = fd_reopen_condition(dir_fd, O_RDONLY|O_CLOEXEC, O_PATH, &fd_close); /* drop O_PATH if it is set */
if (fd < 0)
return fd;
} else {
fd_close = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd_close < 0)
return fd_close;
fd = fd_close;
}
return read_attr_fd(fd, ret);
}
+2 -7
View File
@@ -2881,13 +2881,8 @@ static int setup_ephemeral(
if (*root_image) {
log_debug("Making ephemeral copy of %s to %s", *root_image, new_root);
fd = copy_file(*root_image,
new_root,
O_EXCL,
0600,
COPY_LOCK_BSD|
COPY_REFLINK|
COPY_CRTIME);
fd = copy_file(*root_image, new_root, O_EXCL, 0600,
COPY_LOCK_BSD|COPY_REFLINK|COPY_CRTIME|COPY_NOCOW_AFTER);
if (fd < 0)
return log_debug_errno(fd, "Failed to copy image %s to %s: %m",
*root_image, new_root);
+2 -1
View File
@@ -36,6 +36,7 @@
#include "capability-util.h"
#include "cgroup-util.h"
#include "chase.h"
#include "chattr-util.h"
#include "common-signal.h"
#include "copy.h"
#include "cpu-set-util.h"
@@ -6073,7 +6074,7 @@ static int run(int argc, char *argv[]) {
{
BLOCK_SIGNALS(SIGINT);
r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600,
COPY_REFLINK|COPY_CRTIME|COPY_SIGINT);
COPY_REFLINK|COPY_CRTIME|COPY_SIGINT|COPY_NOCOW_AFTER);
}
if (r == -EINTR) {
log_error_errno(r, "Interrupted while copying image file to %s, removed again.", np);
+47 -29
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,
@@ -798,7 +827,6 @@ static int fd_copy_regular(
void *userdata) {
_cleanup_close_ int fdf = -EBADF, fdt = -EBADF;
unsigned attrs = 0;
int r, q;
assert(st);
@@ -814,10 +842,6 @@ static int fd_copy_regular(
if (fdf < 0)
return fdf;
r = read_attr_fd(fdf, &attrs);
if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r) && r != -ELOOP)
return r;
if (copy_flags & COPY_MAC_CREATE) {
r = mac_selinux_create_file_prepare_at(dt, to, S_IFREG);
if (r < 0)
@@ -829,8 +853,9 @@ static int fd_copy_regular(
if (fdt < 0)
return -errno;
if (attrs != 0)
(void) chattr_full(fdt, NULL, attrs, CHATTR_EARLY_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE);
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)
@@ -847,9 +872,6 @@ static int fd_copy_regular(
(void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
(void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
if (attrs != 0)
(void) chattr_full(fdt, NULL, attrs, ~CHATTR_EARLY_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE);
if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
r = fd_verify_linked(fdf);
if (r < 0)
@@ -1430,7 +1452,6 @@ int copy_file_at_full(
_cleanup_close_ int fdf = -EBADF, fdt = -EBADF;
struct stat st;
unsigned attrs = 0;
int r;
assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
@@ -1448,10 +1469,6 @@ int copy_file_at_full(
if (r < 0)
return r;
r = read_attr_at(dir_fdf, from, &attrs);
if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r) && r != -ELOOP)
return r;
WITH_UMASK(0000) {
fdt = xopenat_lock_full(dir_fdt, to,
flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
@@ -1468,10 +1485,12 @@ int copy_file_at_full(
goto fail;
}
attrs = (attrs & ~chattr_mask) | (chattr_flags & chattr_mask);
r = prepare_nocow(fdf, /*from=*/ NULL, fdt, &chattr_mask, &chattr_flags);
if (r < 0)
return r;
if (attrs != 0)
(void) chattr_full(fdt, NULL, attrs, CHATTR_EARLY_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE);
if (chattr_mask != 0)
(void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL);
r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags & ~COPY_LOCK_BSD, NULL, NULL, progress_bytes, userdata);
if (r < 0)
@@ -1486,8 +1505,9 @@ int copy_file_at_full(
goto fail;
}
if (attrs != 0)
(void) chattr_full(fdt, NULL, attrs, ~CHATTR_EARLY_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE);
unsigned nocow = FLAGS_SET(copy_flags, COPY_NOCOW_AFTER) ? FS_NOCOW_FL : 0;
if ((chattr_mask | nocow) != 0)
(void) chattr_fd(fdt, chattr_flags | nocow, (chattr_mask & ~CHATTR_EARLY_FL) | nocow, NULL);
if (copy_flags & (COPY_FSYNC|COPY_FSYNC_FULL)) {
if (fsync(fdt) < 0) {
@@ -1532,7 +1552,6 @@ int copy_file_atomic_at_full(
_cleanup_(unlink_and_freep) char *t = NULL;
_cleanup_close_ int fdt = -EBADF;
unsigned attrs = 0;
int r;
assert(to);
@@ -1549,14 +1568,12 @@ int copy_file_atomic_at_full(
if (fdt < 0)
return fdt;
r = read_attr_at(dir_fdf, from, &attrs);
if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r) && r != -ELOOP)
r = prepare_nocow(dir_fdf, from, fdt, &chattr_mask, &chattr_flags);
if (r < 0)
return r;
attrs = (attrs & ~chattr_mask) | (chattr_flags & chattr_mask);
if (attrs != 0)
(void) chattr_full(fdt, NULL, attrs, CHATTR_EARLY_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE);
if (chattr_mask != 0)
(void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL);
r = copy_file_fd_at_full(dir_fdf, from, fdt, copy_flags, progress_bytes, userdata);
if (r < 0)
@@ -1577,8 +1594,9 @@ int copy_file_atomic_at_full(
t = mfree(t);
if (attrs != 0)
(void) chattr_full(fdt, NULL, attrs, ~CHATTR_EARLY_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE);
unsigned nocow = FLAGS_SET(copy_flags, COPY_NOCOW_AFTER) ? FS_NOCOW_FL : 0;
if ((chattr_mask | nocow) != 0)
(void) chattr_fd(fdt, chattr_flags | nocow, (chattr_mask & ~CHATTR_EARLY_FL) | nocow, NULL);
r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
if (r < 0)
+8
View File
@@ -32,6 +32,14 @@ typedef enum CopyFlags {
COPY_LOCK_BSD = 1 << 17, /* Return a BSD exclusively locked file descriptor referring to the copied image/directory. */
COPY_VERIFY_LINKED = 1 << 18, /* Check the source file is still linked after copying. */
COPY_RESTORE_DIRECTORY_TIMESTAMPS = 1 << 19, /* Make sure existing directory timestamps don't change during copying. */
/* A root image might be subject to lots of random writes so we provide a flag to try to disable COW
* on a copied file which tends to not perform well in combination with lots of random writes.
*
* Note: btrfs actually isn't impressed by us setting the flag after making the copy, but this at
* least makes the intention clear. We don't want to unconditionally set the flag before doing the
* copy because reflinking from COW to NOCOW files is not supported.
*/
COPY_NOCOW_AFTER = 1 << 20,
} CopyFlags;
typedef enum DenyType {
+2 -1
View File
@@ -1205,7 +1205,8 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
case IMAGE_RAW:
new_path = strjoina("/var/lib/machines/", new_name, ".raw");
r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, COPY_REFLINK|COPY_CRTIME);
r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644,
COPY_REFLINK|COPY_CRTIME|COPY_NOCOW_AFTER);
break;
case IMAGE_BLOCK: