From: Lennart Poettering Date: Tue, 10 May 2022 14:18:35 +0000 (+0200) Subject: fileio: port read_file_full() to use connect_unix_path() X-Git-Tag: v252-rc1~951^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=28ae8da97240c0048f03ed516d64fe064a7fc161;p=thirdparty%2Fsystemd.git fileio: port read_file_full() to use connect_unix_path() This way we can connect correctly to any AF_UNIX socket in the file system, and even save some code. Yay! This also adds some test code for this, that ensures read_file_full() works correctly for AF_UNIX sockets that violate the 108 char limit. Supporting sockets like this kinda matters I think, for the simple reason that apps want to build socket paths via XDG_RUNTIME_DIR and suchlike, and we should be able to connect to them, even via non-normalized paths. --- diff --git a/src/basic/fileio.c b/src/basic/fileio.c index c2497ff8564..2c4ba89a150 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -763,8 +763,7 @@ int read_full_file_full( r = xfopenat(dir_fd, filename, "re", 0, &f); if (r < 0) { - _cleanup_close_ int dfd = -1, sk = -1; - union sockaddr_union sa; + _cleanup_close_ int sk = -1; /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */ if (r != -ENXIO) @@ -778,22 +777,6 @@ int read_full_file_full( if (offset != UINT64_MAX) return -ENXIO; - if (dir_fd == AT_FDCWD) - r = sockaddr_un_set_path(&sa.un, filename); - else { - /* If we shall operate relative to some directory, then let's use O_PATH first to - * open the socket inode, and then connect to it via /proc/self/fd/. We have to do - * this since there's not connectat() that takes a directory fd as first arg. */ - - dfd = openat(dir_fd, filename, O_PATH|O_CLOEXEC); - if (dfd < 0) - return -errno; - - r = sockaddr_un_set_path(&sa.un, FORMAT_PROC_FD_PATH(dfd)); - } - if (r < 0) - return r; - sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); if (sk < 0) return -errno; @@ -812,9 +795,11 @@ int read_full_file_full( return -errno; } - if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) - return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is - * not a socket after all */ + r = connect_unix_path(sk, dir_fd, filename); + if (IN_SET(r, -ENOTSOCK, -EINVAL)) /* propagate original error if this is not a socket after all */ + return -ENXIO; + if (r < 0) + return r; if (shutdown(sk, SHUT_WR) < 0) return -errno; diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 3e98d94019c..2cc71078032 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -881,7 +881,7 @@ TEST(read_full_file_socket) { _cleanup_close_ int listener = -1; _cleanup_free_ char *data = NULL, *clientname = NULL; union sockaddr_union sa; - const char *j; + const char *j, *jj; size_t size; pid_t pid; int r; @@ -897,6 +897,11 @@ TEST(read_full_file_socket) { assert_se(bind(listener, &sa.sa, SOCKADDR_UN_LEN(sa.un)) >= 0); assert_se(listen(listener, 1) >= 0); + /* Make sure the socket doesn't fit into a struct sockaddr_un, but we can still access it */ + jj = strjoina(z, "/a_very_long_patha_very_long_patha_very_long_patha_very_long_patha_very_long_patha_very_long_patha_very_long_patha_very_long_path"); + assert_se(strlen(jj) > sizeof_field(struct sockaddr_un, sun_path)); + assert_se(rename(j, jj) >= 0); + /* Bind the *client* socket to some randomized name, to verify that this works correctly. */ assert_se(asprintf(&clientname, "@%" PRIx64 "/test-bindname", random_u64()) >= 0); @@ -924,8 +929,8 @@ TEST(read_full_file_socket) { _exit(EXIT_SUCCESS); } - assert_se(read_full_file_full(AT_FDCWD, j, UINT64_MAX, SIZE_MAX, 0, NULL, &data, &size) == -ENXIO); - assert_se(read_full_file_full(AT_FDCWD, j, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_CONNECT_SOCKET, clientname, &data, &size) >= 0); + assert_se(read_full_file_full(AT_FDCWD, jj, UINT64_MAX, SIZE_MAX, 0, NULL, &data, &size) == -ENXIO); + assert_se(read_full_file_full(AT_FDCWD, jj, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_CONNECT_SOCKET, clientname, &data, &size) >= 0); assert_se(size == strlen(TEST_STR)); assert_se(streq(data, TEST_STR));