diff options
-rw-r--r-- | src/basic/fileio.c | 30 | ||||
-rw-r--r-- | src/test/test-fileio.c | 20 |
2 files changed, 29 insertions, 21 deletions
diff --git a/src/basic/fileio.c b/src/basic/fileio.c index a158ab080d..df30870a1a 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -385,20 +385,10 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re if (fd < 0) return -errno; - /* Start size for files in /proc/ which usually report a file size of 0. (Files in /sys/ report a - * file size of 4K, which is probably OK for sizing our initial buffer, and sysfs attributes can't be - * larger anyway.) - * - * It's one less than 4k, so that the malloc() below allocates exactly 4k. */ - size = 4095; - /* Limit the number of attempts to read the number of bytes returned by fstat(). */ n_retries = 3; for (;;) { - if (n_retries <= 0) - return -EIO; - if (fstat(fd, &st) < 0) return -errno; @@ -409,24 +399,20 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re assert_cc(READ_FULL_BYTES_MAX < SSIZE_MAX); if (st.st_size > 0) { if (st.st_size > READ_FULL_BYTES_MAX) - return -E2BIG; + return -EFBIG; size = st.st_size; n_retries--; } else { - /* Double the buffer size */ - if (size >= READ_FULL_BYTES_MAX) - return -E2BIG; - if (size > READ_FULL_BYTES_MAX / 2 - 1) - size = READ_FULL_BYTES_MAX; /* clamp to max */ - else - size = size * 2 + 1; /* Stay always one less than page size, so we malloc evenly */ + size = READ_FULL_BYTES_MAX; + n_retries = 0; } buf = malloc(size + 1); if (!buf) return -ENOMEM; - size = malloc_usable_size(buf) - 1; /* Use a bigger allocation if we got it anyway */ + /* Use a bigger allocation if we got it anyway, but not more than the limit. */ + size = MIN(malloc_usable_size(buf) - 1, READ_FULL_BYTES_MAX); for (;;) { ssize_t k; @@ -453,6 +439,9 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re * processing, let's try again either with a bigger guessed size or the new * file size. */ + if (n_retries <= 0) + return st.st_size > 0 ? -EIO : -EFBIG; + if (lseek(fd, 0, SEEK_SET) < 0) return -errno; @@ -466,8 +455,7 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re p = realloc(buf, n + 1); if (!p) return -ENOMEM; - - buf = TAKE_PTR(p); + buf = p; } if (ret_size) diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 659b690082..c5c8116e96 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -9,6 +9,7 @@ #include "ctype.h" #include "env-file.h" #include "env-util.h" +#include "errno-util.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" @@ -964,6 +965,24 @@ static void test_read_full_file_offset_size(void) { rbuf = mfree(rbuf); } +static void test_read_full_virtual_file(void) { + const char *filename; + int r; + + FOREACH_STRING(filename, + "/proc/1/cmdline", + "/etc/nsswitch.conf", + "/sys/kernel/uevent_seqnum") { + + _cleanup_free_ char *buf = NULL; + size_t size = 0; + + r = read_full_virtual_file(filename, &buf, &size); + log_info_errno(r, "read_full_virtual_file(\"%s\"): %m (%zu bytes)", filename, size); + assert_se(r == 0 || ERRNO_IS_PRIVILEGE(r) || r == -ENOENT); + } +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); @@ -991,6 +1010,7 @@ int main(int argc, char *argv[]) { test_read_nul_string(); test_read_full_file_socket(); test_read_full_file_offset_size(); + test_read_full_virtual_file(); return 0; } |