Merge pull request #34090 from DaanDeMeyer/cow-fix
Rework COW <=> NOCOW copying behavior
This commit is contained in:
+13
-4
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user