Skip to content

Commit 72ef2a6

Browse files
committed
copy: introduce COPY_VERIFY_LINKED flag
If the flag is set, then copy_file() and friends check if the source file still exists when the copy operation finished.
1 parent cb0d5f7 commit 72ef2a6

File tree

3 files changed

+67
-2
lines changed

3 files changed

+67
-2
lines changed

src/shared/copy.c

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ int copy_bytes_full(
209209
r = reflink_range(fdf, foffset, fdt, toffset, max_bytes == UINT64_MAX ? 0 : max_bytes); /* partial reflink */
210210
if (r >= 0) {
211211
off_t t;
212+
int ret;
212213

213214
/* This worked, yay! Now — to be fully correct — let's adjust the file pointers */
214215
if (max_bytes == UINT64_MAX) {
@@ -227,7 +228,14 @@ int copy_bytes_full(
227228
if (t < 0)
228229
return -errno;
229230

230-
return 0; /* we copied the whole thing, hence hit EOF, return 0 */
231+
if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
232+
r = fd_verify_linked(fdf);
233+
if (r < 0)
234+
return r;
235+
}
236+
237+
/* We copied the whole thing, hence hit EOF, return 0. */
238+
ret = 0;
231239
} else {
232240
t = lseek(fdf, foffset + max_bytes, SEEK_SET);
233241
if (t < 0)
@@ -237,8 +245,18 @@ int copy_bytes_full(
237245
if (t < 0)
238246
return -errno;
239247

240-
return 1; /* we copied only some number of bytes, which worked, but this means we didn't hit EOF, return 1 */
248+
/* We copied only some number of bytes, which worked, but
249+
* this means we didn't hit EOF, return 1. */
250+
ret = 1;
251+
}
252+
253+
if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
254+
r = fd_verify_linked(fdf);
255+
if (r < 0)
256+
return r;
241257
}
258+
259+
return ret;
242260
}
243261
}
244262
}
@@ -484,6 +502,12 @@ int copy_bytes_full(
484502
copied_something = true;
485503
}
486504

505+
if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
506+
r = fd_verify_linked(fdf);
507+
if (r < 0)
508+
return r;
509+
}
510+
487511
if (copy_flags & COPY_TRUNCATE) {
488512
off_t off = lseek(fdt, 0, SEEK_CUR);
489513
if (off < 0)
@@ -799,6 +823,12 @@ static int fd_copy_regular(
799823
(void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
800824
(void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
801825

826+
if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
827+
r = fd_verify_linked(fdf);
828+
if (r < 0)
829+
return r;
830+
}
831+
802832
if (copy_flags & COPY_FSYNC) {
803833
if (fsync(fdt) < 0) {
804834
r = -errno;
@@ -1334,6 +1364,12 @@ int copy_file_fd_at_full(
13341364
(void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
13351365
}
13361366

1367+
if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
1368+
r = fd_verify_linked(fdf);
1369+
if (r < 0)
1370+
return r;
1371+
}
1372+
13371373
if (copy_flags & COPY_FSYNC_FULL) {
13381374
r = fsync_full(fdt);
13391375
if (r < 0)
@@ -1404,6 +1440,12 @@ int copy_file_at_full(
14041440
(void) copy_times(fdf, fdt, copy_flags);
14051441
(void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
14061442

1443+
if (FLAGS_SET(copy_flags, COPY_VERIFY_LINKED)) {
1444+
r = fd_verify_linked(fdf);
1445+
if (r < 0)
1446+
goto fail;
1447+
}
1448+
14071449
if (chattr_mask != 0)
14081450
(void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
14091451

src/shared/copy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ typedef enum CopyFlags {
3030
COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
3131
COPY_TRUNCATE = 1 << 16, /* Truncate to current file offset after copying */
3232
COPY_LOCK_BSD = 1 << 17, /* Return a BSD exclusively locked file descriptor referring to the copied image/directory. */
33+
COPY_VERIFY_LINKED = 1 << 18, /* Check the source file is still linked after copying. */
3334
} CopyFlags;
3435

3536
typedef enum DenyType {

src/test/test-copy.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,4 +566,26 @@ TEST(copy_lock) {
566566
fd = safe_close(fd);
567567
}
568568

569+
TEST(copy_verify_linked) {
570+
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
571+
_cleanup_close_ int tfd = -EBADF, fd_1 = -EBADF, fd_2 = -EBADF;
572+
573+
tfd = mkdtemp_open(NULL, O_PATH, &t);
574+
assert_se(tfd >= 0);
575+
576+
assert_se(write_string_file_at(tfd, "hoge", "bar bar", WRITE_STRING_FILE_CREATE) >= 0);
577+
578+
fd_1 = openat(tfd, "hoge", O_CLOEXEC | O_NOCTTY | O_RDONLY);
579+
assert_se(fd_1 >= 0);
580+
fd_2 = openat(tfd, "hoge", O_CLOEXEC | O_NOCTTY | O_RDONLY);
581+
assert_se(fd_2 >= 0);
582+
assert_se(unlinkat(tfd, "hoge", 0) >= 0);
583+
584+
assert_se(copy_file_at(fd_1, NULL, tfd, "to_1", 0, 0644, 0) >= 0);
585+
assert_se(read_file_at_and_streq(tfd, "to_1", "bar bar\n"));
586+
587+
assert_se(copy_file_at(fd_2, NULL, tfd, "to_2", O_EXCL, 0644, COPY_VERIFY_LINKED) == -EIDRM);
588+
assert_se(faccessat(tfd, "to_2", F_OK, AT_SYMLINK_NOFOLLOW) < 0 && errno == ENOENT);
589+
}
590+
569591
DEFINE_TEST_MAIN(LOG_DEBUG);

0 commit comments

Comments
 (0)