]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
fileio: teach read_full_file_full() to read from offset/with maximum size
authorLennart Poettering <lennart@poettering.net>
Wed, 4 Nov 2020 19:25:06 +0000 (20:25 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 1 Dec 2020 13:17:47 +0000 (14:17 +0100)
12 files changed:
src/basic/fileio.c
src/basic/fileio.h
src/core/execute.c
src/journal-remote/journal-gatewayd.c
src/journal-remote/journal-remote-main.c
src/network/netdev/macsec.c
src/network/netdev/wireguard.c
src/nspawn/nspawn.c
src/partition/repart.c
src/shared/json.c
src/test/test-fileio.c
src/veritysetup/veritysetup.c

index 973756c891f1510fb3ca0b853d7397440d2097dd..f4708bc05f0fee1d2e592e12c3f8d2e45ea09c3e 100644 (file)
@@ -472,12 +472,13 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
 int read_full_stream_full(
                 FILE *f,
                 const char *filename,
+                uint64_t offset,
+                size_t size,
                 ReadFullFileFlags flags,
                 char **ret_contents,
                 size_t *ret_size) {
 
         _cleanup_free_ char *buf = NULL;
-        struct stat st;
         size_t n, n_next, l;
         int fd, r;
 
@@ -485,32 +486,45 @@ int read_full_stream_full(
         assert(ret_contents);
         assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX));
 
-        n_next = LINE_MAX; /* Start size */
+        if (offset != UINT64_MAX && offset > LONG_MAX)
+                return -ERANGE;
+
+        n_next = size != SIZE_MAX ? size : LINE_MAX; /* Start size */
 
         fd = fileno(f);
-        if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen()), let's
-                        * optimize our buffering */
+        if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see
+                        * fmemopen()), let's optimize our buffering */
+                struct stat st;
 
                 if (fstat(fd, &st) < 0)
                         return -errno;
 
                 if (S_ISREG(st.st_mode)) {
-
-                        /* Safety check */
-                        if (st.st_size > READ_FULL_BYTES_MAX)
-                                return -E2BIG;
-
-                        /* Start with the right file size. Note that we increase the size
-                         * to read here by one, so that the first read attempt already
-                         * makes us notice the EOF. */
-                        if (st.st_size > 0)
-                                n_next = st.st_size + 1;
+                        if (size == SIZE_MAX) {
+                                uint64_t rsize =
+                                        LESS_BY((uint64_t) st.st_size, offset == UINT64_MAX ? 0 : offset);
+
+                                /* Safety check */
+                                if (rsize > READ_FULL_BYTES_MAX)
+                                        return -E2BIG;
+
+                                /* Start with the right file size. Note that we increase the size to read
+                                 * here by one, so that the first read attempt already makes us notice the
+                                 * EOF. If the reported size of the file is zero, we avoid this logic
+                                 * however, since quite likely it might be a virtual file in procfs that all
+                                 * report a zero file size. */
+                                if (st.st_size > 0)
+                                        n_next = rsize + 1;
+                        }
 
                         if (flags & READ_FULL_FILE_WARN_WORLD_READABLE)
                                 (void) warn_file_is_world_accessible(filename, &st, NULL, 0);
                 }
         }
 
+        if (offset != UINT64_MAX && fseek(f, offset, SEEK_SET) < 0)
+                return -errno;
+
         n = l = 0;
         for (;;) {
                 char *t;
@@ -547,6 +561,11 @@ int read_full_stream_full(
                 if (feof(f))
                         break;
 
+                if (size != SIZE_MAX) { /* If we got asked to read some specific size, we already sized the buffer right, hence leave */
+                        assert(l == size);
+                        break;
+                }
+
                 assert(k > 0); /* we can't have read zero bytes because that would have been EOF */
 
                 /* Safety check */
@@ -605,15 +624,18 @@ finalize:
 int read_full_file_full(
                 int dir_fd,
                 const char *filename,
+                uint64_t offset,
+                size_t size,
                 ReadFullFileFlags flags,
                 const char *bind_name,
-                char **contents, size_t *size) {
+                char **ret_contents,
+                size_t *ret_size) {
 
         _cleanup_fclose_ FILE *f = NULL;
         int r;
 
         assert(filename);
-        assert(contents);
+        assert(ret_contents);
 
         r = xfopenat(dir_fd, filename, "re", 0, &f);
         if (r < 0) {
@@ -628,6 +650,10 @@ int read_full_file_full(
                 if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET))
                         return -ENXIO;
 
+                /* Seeking is not supported on AF_UNIX sockets */
+                if (offset != UINT64_MAX)
+                        return -ESPIPE;
+
                 if (dir_fd == AT_FDCWD)
                         r = sockaddr_un_set_path(&sa.un, filename);
                 else {
@@ -681,7 +707,7 @@ int read_full_file_full(
 
         (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
 
-        return read_full_stream_full(f, filename, flags, contents, size);
+        return read_full_stream_full(f, filename, offset, size, flags, ret_contents, ret_size);
 }
 
 int executable_is_script(const char *path, char **interpreter) {
index 0886354cbcf8a9348b6cb245fa28dfab461ab9c8..498e88035483cfa5716dcb8f521613bafb4a2f88 100644 (file)
@@ -60,14 +60,14 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin
 int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4);
 
 int read_one_line_file(const char *filename, char **line);
-int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, const char *bind_name, char **contents, size_t *size);
-static inline int read_full_file(const char *filename, char **contents, size_t *size) {
-        return read_full_file_full(AT_FDCWD, filename, 0, NULL, contents, size);
+int read_full_file_full(int dir_fd, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, const char *bind_name, char **ret_contents, size_t *ret_size);
+static inline int read_full_file(const char *filename, char **ret_contents, size_t *ret_size) {
+        return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size);
 }
 int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size);
-int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size);
-static inline int read_full_stream(FILE *f, char **contents, size_t *size) {
-        return read_full_stream_full(f, NULL, 0, contents, size);
+int read_full_stream_full(FILE *f, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, char **ret_contents, size_t *ret_size);
+static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_size) {
+        return read_full_stream_full(f, NULL, UINT64_MAX, SIZE_MAX, 0, ret_contents, ret_size);
 }
 
 int verify_file(const char *fn, const char *blob, bool accept_extra_nl);
index f1f37441910fe38235940cd82827ee00e5c3b3d2..56793c5a43d8ef9ff7c25e4f1ab6d0939f990c2f 100644 (file)
@@ -2576,7 +2576,7 @@ static int acquire_credentials(
 
 
                 if (source)
-                        r = read_full_file_full(AT_FDCWD, source, flags, bindname, &data, &size);
+                        r = read_full_file_full(AT_FDCWD, source, UINT64_MAX, SIZE_MAX, flags, bindname, &data, &size);
                 else
                         r = -ENOENT;
                 if (r == -ENOENT &&
index 362c61203a3da9edc1f1bb4ee4124fb57e9ea28d..534de51b0f60a7345fc32c98836983b9cf31db9e 100644 (file)
@@ -898,7 +898,7 @@ static int parse_argv(int argc, char *argv[]) {
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                        "Key file specified twice");
                         r = read_full_file_full(
-                                        AT_FDCWD, optarg,
+                                        AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
                                         READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
                                         NULL,
                                         &arg_key_pem, NULL);
@@ -911,7 +911,11 @@ static int parse_argv(int argc, char *argv[]) {
                         if (arg_cert_pem)
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                        "Certificate file specified twice");
-                        r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, NULL, &arg_cert_pem, NULL);
+                        r = read_full_file_full(
+                                        AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
+                                        READ_FULL_FILE_CONNECT_SOCKET,
+                                        NULL,
+                                        &arg_cert_pem, NULL);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to read certificate file: %m");
                         assert(arg_cert_pem);
@@ -922,7 +926,11 @@ static int parse_argv(int argc, char *argv[]) {
                         if (arg_trust_pem)
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                        "CA certificate file specified twice");
-                        r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, NULL, &arg_trust_pem, NULL);
+                        r = read_full_file_full(
+                                        AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
+                                        READ_FULL_FILE_CONNECT_SOCKET,
+                                        NULL,
+                                        &arg_trust_pem, NULL);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to read CA certificate file: %m");
                         assert(arg_trust_pem);
index a78896c14fd38702f10448b7e60ab81d8f5e82b3..af8cde7eef4060354bc86eb32394e6f46c5f1200 100644 (file)
@@ -1079,7 +1079,7 @@ static int load_certificates(char **key, char **cert, char **trust) {
         int r;
 
         r = read_full_file_full(
-                        AT_FDCWD, arg_key ?: PRIV_KEY_FILE,
+                        AT_FDCWD, arg_key ?: PRIV_KEY_FILE, UINT64_MAX, SIZE_MAX,
                         READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
                         NULL,
                         key, NULL);
@@ -1087,7 +1087,11 @@ static int load_certificates(char **key, char **cert, char **trust) {
                 return log_error_errno(r, "Failed to read key from file '%s': %m",
                                        arg_key ?: PRIV_KEY_FILE);
 
-        r = read_full_file_full(AT_FDCWD, arg_cert ?: CERT_FILE, READ_FULL_FILE_CONNECT_SOCKET, NULL, cert, NULL);
+        r = read_full_file_full(
+                        AT_FDCWD, arg_cert ?: CERT_FILE, UINT64_MAX, SIZE_MAX,
+                        READ_FULL_FILE_CONNECT_SOCKET,
+                        NULL,
+                        cert, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to read certificate from file '%s': %m",
                                        arg_cert ?: CERT_FILE);
@@ -1095,7 +1099,11 @@ static int load_certificates(char **key, char **cert, char **trust) {
         if (arg_trust_all)
                 log_info("Certificate checking disabled.");
         else {
-                r = read_full_file_full(AT_FDCWD, arg_trust ?: TRUST_FILE, READ_FULL_FILE_CONNECT_SOCKET, NULL, trust, NULL);
+                r = read_full_file_full(
+                                AT_FDCWD, arg_trust ?: TRUST_FILE, UINT64_MAX, SIZE_MAX,
+                                READ_FULL_FILE_CONNECT_SOCKET,
+                                NULL,
+                                trust, NULL);
                 if (r < 0)
                         return log_error_errno(r, "Failed to read CA certificate file '%s': %m",
                                                arg_trust ?: TRUST_FILE);
index 313277ca167f67e4c611a70a9241043c2fb11b40..27fc2fd5e59de8e08298b95083d0445bf9725075 100644 (file)
@@ -986,7 +986,7 @@ static int macsec_read_key_file(NetDev *netdev, SecurityAssociation *sa) {
         (void) warn_file_is_world_accessible(sa->key_file, NULL, NULL, 0);
 
         r = read_full_file_full(
-                        AT_FDCWD, sa->key_file,
+                        AT_FDCWD, sa->key_file, UINT64_MAX, SIZE_MAX,
                         READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
                         NULL, (char **) &key, &key_len);
         if (r < 0)
index 416e9b92d187a4380398deab533dcf5f9ff7b173..76444bdd7cecab7903c2aed35fb0d171c4b3f65d 100644 (file)
@@ -869,7 +869,7 @@ static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_
         (void) warn_file_is_world_accessible(filename, NULL, NULL, 0);
 
         r = read_full_file_full(
-                        AT_FDCWD, filename,
+                        AT_FDCWD, filename, UINT64_MAX, SIZE_MAX,
                         READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
                         NULL, &key, &key_len);
         if (r < 0)
index 0842731c186676344ef6256ade669c8f7841da71..ad2f572869cc3553f14ed7b56a9febc4dd3e0cb9 100644 (file)
@@ -1589,7 +1589,10 @@ static int parse_argv(int argc, char *argv[]) {
                                         return log_oom();
                         }
 
-                        r = read_full_file_full(AT_FDCWD, j ?: p, flags, NULL, &data, &size);
+                        r = read_full_file_full(AT_FDCWD, j ?: p, UINT64_MAX, SIZE_MAX,
+                                                flags,
+                                                NULL,
+                                                &data, &size);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to read credential '%s': %m", j ?: p);
 
index 58cacab2442967536cfd80408ab385ac2118bd92..ddb9476cec94ed4e8f1868d90e884e295a13a8fb 100644 (file)
@@ -3622,7 +3622,7 @@ static int parse_argv(int argc, char *argv[]) {
                         size_t n = 0;
 
                         r = read_full_file_full(
-                                        AT_FDCWD, optarg,
+                                        AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
                                         READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
                                         NULL,
                                         &k, &n);
index ddf6dcb6637381a119913ea561049cdf973321b6..28fe482749d10c82bd8dca97b4d01fb30227b9b4 100644 (file)
@@ -3195,7 +3195,7 @@ int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags fla
         if (f)
                 r = read_full_stream(f, &text, NULL);
         else if (path)
-                r = read_full_file_full(dir_fd, path, 0, NULL, &text, NULL);
+                r = read_full_file_full(dir_fd, path, UINT64_MAX, SIZE_MAX, 0, NULL, &text, NULL);
         else
                 return -EINVAL;
         if (r < 0)
index 431aea07ef03370197231b0a6345ee9948075fdf..a5834eba364a0812ed89a9e4b31950eb2bc19cd7 100644 (file)
@@ -911,8 +911,8 @@ static void test_read_full_file_socket(void) {
                 _exit(EXIT_SUCCESS);
         }
 
-        assert_se(read_full_file_full(AT_FDCWD, j, 0, NULL, &data, &size) == -ENXIO);
-        assert_se(read_full_file_full(AT_FDCWD, j, READ_FULL_FILE_CONNECT_SOCKET, clientname, &data, &size) >= 0);
+        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(size == strlen(TEST_STR));
         assert_se(streq(data, TEST_STR));
 
@@ -920,6 +920,50 @@ static void test_read_full_file_socket(void) {
 #undef TEST_STR
 }
 
+static void test_read_full_file_offset_size(void) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_(unlink_and_freep) char *fn = NULL;
+        _cleanup_free_ char *rbuf = NULL;
+        size_t rbuf_size;
+        uint8_t buf[4711];
+
+        random_bytes(buf, sizeof(buf));
+
+        assert_se(tempfn_random_child(NULL, NULL, &fn) >= 0);
+        assert_se(f = fopen(fn, "we"));
+        assert_se(fwrite(buf, 1, sizeof(buf), f) == sizeof(buf));
+        assert_se(fflush_and_check(f) >= 0);
+
+        assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, SIZE_MAX, 0, NULL, &rbuf, &rbuf_size) >= 0);
+        assert_se(rbuf_size == sizeof(buf));
+        assert_se(memcmp(buf, rbuf, rbuf_size) == 0);
+        rbuf = mfree(rbuf);
+
+        assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, 128, 0, NULL, &rbuf, &rbuf_size) >= 0);
+        assert_se(rbuf_size == 128);
+        assert_se(memcmp(buf, rbuf, rbuf_size) == 0);
+        rbuf = mfree(rbuf);
+
+        assert_se(read_full_file_full(AT_FDCWD, fn, 1234, SIZE_MAX, 0, NULL, &rbuf, &rbuf_size) >= 0);
+        assert_se(rbuf_size == sizeof(buf) - 1234);
+        assert_se(memcmp(buf + 1234, rbuf, rbuf_size) == 0);
+        rbuf = mfree(rbuf);
+
+        assert_se(read_full_file_full(AT_FDCWD, fn, 2345, 777, 0, NULL, &rbuf, &rbuf_size) >= 0);
+        assert_se(rbuf_size == 777);
+        assert_se(memcmp(buf + 2345, rbuf, rbuf_size) == 0);
+        rbuf = mfree(rbuf);
+
+        assert_se(read_full_file_full(AT_FDCWD, fn, 4700, 20, 0, NULL, &rbuf, &rbuf_size) >= 0);
+        assert_se(rbuf_size == 11);
+        assert_se(memcmp(buf + 4700, rbuf, rbuf_size) == 0);
+        rbuf = mfree(rbuf);
+
+        assert_se(read_full_file_full(AT_FDCWD, fn, 10000, 99, 0, NULL, &rbuf, &rbuf_size) >= 0);
+        assert_se(rbuf_size == 0);
+        rbuf = mfree(rbuf);
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_DEBUG);
 
@@ -946,6 +990,7 @@ int main(int argc, char *argv[]) {
         test_read_line4();
         test_read_nul_string();
         test_read_full_file_socket();
+        test_read_full_file_offset_size();
 
         return 0;
 }
index 558e9510ff0b457ab8a0f0dfb90b2a06b0262889..9b8bca11f2cb5faa7f91487895f251fb23156206 100644 (file)
@@ -100,7 +100,11 @@ static int run(int argc, char *argv[]) {
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to parse root hash signature '%s': %m", argv[6]);
                         } else {
-                                r = read_full_file_full(AT_FDCWD, argv[6], READ_FULL_FILE_CONNECT_SOCKET, NULL, &hash_sig, &hash_sig_size);
+                                r = read_full_file_full(
+                                                AT_FDCWD, argv[6], UINT64_MAX, SIZE_MAX,
+                                                READ_FULL_FILE_CONNECT_SOCKET,
+                                                NULL,
+                                                &hash_sig, &hash_sig_size);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to read root hash signature: %m");
                         }