summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/basic/fileio.c30
-rw-r--r--src/test/test-fileio.c20
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;
}