From 4960ce43ff8c42a8c92219d88cbcdb82ae7a1ec0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 9 Mar 2018 22:45:08 +0100 Subject: fd-util: add new helper call fd_duplicate_data_fd() This call creates an fd from another fd containing the same data. Specifically, repeated read() on the returned fd should return the same data as the original fd. This call is useful when we want to copy data out of disk images and suchlike, and want to be pass fds with the data around without having to keep the disk image continously mounted. The implementation tries to be somewhat smart and tries to prefer memfds/pipes over files in /tmp or /var/tmp based on the size of the data, but has appropropriate fallbacks in place. --- src/test/test-fd-util.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) (limited to 'src/test/test-fd-util.c') diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c index 432fa5d403..fc3c463385 100644 --- a/src/test/test-fd-util.c +++ b/src/test/test-fd-util.c @@ -228,6 +228,93 @@ static void test_rearrange_stdio(void) { } } +static void assert_equal_fd(int fd1, int fd2) { + + for (;;) { + uint8_t a[4096], b[4096]; + ssize_t x, y; + + x = read(fd1, a, sizeof(a)); + assert(x >= 0); + + y = read(fd2, b, sizeof(b)); + assert(y >= 0); + + assert(x == y); + + if (x == 0) + break; + + assert(memcmp(a, b, x) == 0); + } +} + +static void test_fd_duplicate_data_fd(void) { + _cleanup_close_ int fd1 = -1, fd2 = -1; + _cleanup_(close_pairp) int sfd[2] = { -1, -1 }; + _cleanup_(sigkill_waitp) pid_t pid = -1; + uint64_t i, j; + int r; + + fd1 = open("/etc/fstab", O_RDONLY|O_CLOEXEC); + if (fd1 >= 0) { + + fd2 = fd_duplicate_data_fd(fd1); + assert_se(fd2 >= 0); + + assert_se(lseek(fd1, 0, SEEK_SET) == 0); + assert_equal_fd(fd1, fd2); + } + + fd1 = safe_close(fd1); + fd2 = safe_close(fd2); + + fd1 = acquire_data_fd("hallo", 6, 0); + assert_se(fd1 >= 0); + + fd2 = fd_duplicate_data_fd(fd1); + assert_se(fd2 >= 0); + + safe_close(fd1); + fd1 = acquire_data_fd("hallo", 6, 0); + assert_se(fd1 >= 0); + + assert_equal_fd(fd1, fd2); + + fd1 = safe_close(fd1); + fd2 = safe_close(fd2); + + assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sfd) >= 0); + + r = safe_fork("(sd-pipe)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + assert_se(r >= 0); + + if (r == 0) { + /* child */ + + sfd[0] = safe_close(sfd[0]); + + for (i = 0; i < 1536*1024 / sizeof(uint64_t); i++) + assert_se(write(sfd[1], &i, sizeof(i)) == sizeof(i)); + + sfd[1] = safe_close(sfd[1]); + + _exit(EXIT_SUCCESS); + } + + sfd[1] = safe_close(sfd[1]); + + fd2 = fd_duplicate_data_fd(sfd[0]); + assert_se(fd2 >= 0); + + for (i = 0; i < 1536*1024 / sizeof(uint64_t); i++) { + assert_se(read(fd2, &j, sizeof(j)) == sizeof(j)); + assert_se(i == j); + } + + assert_se(read(fd2, &j, sizeof(j)) == 0); +} + int main(int argc, char *argv[]) { test_close_many(); test_close_nointr(); @@ -236,6 +323,7 @@ int main(int argc, char *argv[]) { test_acquire_data_fd(); test_fd_move_above_stdio(); test_rearrange_stdio(); + test_fd_duplicate_data_fd(); return 0; } -- cgit v1.2.1