]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
copy: introduce COPY_VERIFY_LINKED flag
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 15 Feb 2024 10:37:43 +0000 (19:37 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 15 Feb 2024 18:48:18 +0000 (03:48 +0900)
If the flag is set, then copy_file() and friends check if the source
file still exists when the copy operation finished.

src/shared/copy.c
src/shared/copy.h
src/test/test-copy.c

index ce815c58a2127dcfd91895b161ef1334dab2dd73..2374fa3b62f9f705526c48ed34a32f62428cb451 100644 (file)
@@ -209,6 +209,7 @@ int copy_bytes_full(
                                         r = reflink_range(fdf, foffset, fdt, toffset, max_bytes == UINT64_MAX ? 0 : max_bytes); /* partial reflink */
                                 if (r >= 0) {
                                         off_t t;
+                                        int ret;
 
                                         /* This worked, yay! Now — to be fully correct — let's adjust the file pointers */
                                         if (max_bytes == UINT64_MAX) {
@@ -227,7 +228,14 @@ int copy_bytes_full(
                                                 if (t < 0)
                                                         return -errno;
 
-                                                return 0; /* we copied the whole thing, hence hit EOF, return 0 */
+                                                if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
+                                                        r = fd_verify_linked(fdf);
+                                                        if (r < 0)
+                                                                return r;
+                                                }
+
+                                                /* We copied the whole thing, hence hit EOF, return 0. */
+                                                ret = 0;
                                         } else {
                                                 t = lseek(fdf, foffset + max_bytes, SEEK_SET);
                                                 if (t < 0)
@@ -237,8 +245,18 @@ int copy_bytes_full(
                                                 if (t < 0)
                                                         return -errno;
 
-                                                return 1; /* we copied only some number of bytes, which worked, but this means we didn't hit EOF, return 1 */
+                                                /* We copied only some number of bytes, which worked, but
+                                                 * this means we didn't hit EOF, return 1. */
+                                                ret = 1;
+                                        }
+
+                                        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
+                                                r = fd_verify_linked(fdf);
+                                                if (r < 0)
+                                                        return r;
                                         }
+
+                                        return ret;
                                 }
                         }
                 }
@@ -484,6 +502,12 @@ int copy_bytes_full(
                 copied_something = true;
         }
 
+        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
+                r = fd_verify_linked(fdf);
+                if (r < 0)
+                        return r;
+        }
+
         if (copy_flags & COPY_TRUNCATE) {
                 off_t off = lseek(fdt, 0, SEEK_CUR);
                 if (off < 0)
@@ -799,6 +823,12 @@ 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 (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
+                r = fd_verify_linked(fdf);
+                if (r < 0)
+                        return r;
+        }
+
         if (copy_flags & COPY_FSYNC) {
                 if (fsync(fdt) < 0) {
                         r = -errno;
@@ -1334,6 +1364,12 @@ int copy_file_fd_at_full(
                 (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
         }
 
+        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
+                r = fd_verify_linked(fdf);
+                if (r < 0)
+                        return r;
+        }
+
         if (copy_flags & COPY_FSYNC_FULL) {
                 r = fsync_full(fdt);
                 if (r < 0)
@@ -1404,6 +1440,12 @@ int copy_file_at_full(
         (void) copy_times(fdf, fdt, copy_flags);
         (void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
 
+        if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
+                r = fd_verify_linked(fdf);
+                if (r < 0)
+                        goto fail;
+        }
+
         if (chattr_mask != 0)
                 (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
 
index d842edd2c8a88208c2ca525ab03af43d92ccce95..b8fb28a09e48983b810aacc57429cb8e8d9f91fa 100644 (file)
@@ -30,6 +30,7 @@ typedef enum CopyFlags {
         COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
         COPY_TRUNCATE      = 1 << 16, /* Truncate to current file offset after copying */
         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. */
 } CopyFlags;
 
 typedef enum DenyType {
index 41c7884825b4552a9245335349780e1d25e1d372..111c001de6b45405b40af6c130103ccfeca3aa63 100644 (file)
@@ -566,4 +566,26 @@ TEST(copy_lock) {
         fd = safe_close(fd);
 }
 
+TEST(copy_verify_linked) {
+        _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
+        _cleanup_close_ int tfd = -EBADF, fd_1 = -EBADF, fd_2 = -EBADF;
+
+        tfd = mkdtemp_open(NULL, O_PATH, &t);
+        assert_se(tfd >= 0);
+
+        assert_se(write_string_file_at(tfd, "hoge", "bar bar", WRITE_STRING_FILE_CREATE) >= 0);
+
+        fd_1 = openat(tfd, "hoge", O_CLOEXEC | O_NOCTTY | O_RDONLY);
+        assert_se(fd_1 >= 0);
+        fd_2 = openat(tfd, "hoge", O_CLOEXEC | O_NOCTTY | O_RDONLY);
+        assert_se(fd_2 >= 0);
+        assert_se(unlinkat(tfd, "hoge", 0) >= 0);
+
+        assert_se(copy_file_at(fd_1, NULL, tfd, "to_1", 0, 0644, 0) >= 0);
+        assert_se(read_file_at_and_streq(tfd, "to_1", "bar bar\n"));
+
+        assert_se(copy_file_at(fd_2, NULL, tfd, "to_2", O_EXCL, 0644, COPY_VERIFY_LINKED) == -EIDRM);
+        assert_se(faccessat(tfd, "to_2", F_OK, AT_SYMLINK_NOFOLLOW) < 0 && errno == ENOENT);
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);