]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
fileio: port read_file_full() to use connect_unix_path()
authorLennart Poettering <lennart@poettering.net>
Tue, 10 May 2022 14:18:35 +0000 (16:18 +0200)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 13 May 2022 20:01:38 +0000 (05:01 +0900)
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.

src/basic/fileio.c
src/test/test-fileio.c

index c2497ff85641aae0321484fe87f181aedd22bd13..2c4ba89a15022a6b2c9d22e8cb2b829607edad4c 100644 (file)
@@ -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;
index 3e98d94019c97a3ba5c58a6c64cf367307aabcf8..2cc71078032259a8dff7982c3841c3b3fafa9bad 100644 (file)
@@ -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));