mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-22 13:33:56 +03:00
fileio: optionally allow interpreting file size as limit
This commit is contained in:
parent
c1631ee124
commit
7b0da71d49
@ -522,18 +522,17 @@ int read_full_stream_full(
|
||||
size_t *ret_size) {
|
||||
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
size_t n, n_next, l;
|
||||
size_t n, n_next = 0, l;
|
||||
int fd, r;
|
||||
|
||||
assert(f);
|
||||
assert(ret_contents);
|
||||
assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX));
|
||||
assert(size != SIZE_MAX || !FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER));
|
||||
|
||||
if (offset != UINT64_MAX && offset > LONG_MAX)
|
||||
if (offset != UINT64_MAX && offset > LONG_MAX) /* fseek() can only deal with "long" offsets */
|
||||
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 */
|
||||
@ -543,20 +542,20 @@ int read_full_stream_full(
|
||||
return -errno;
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
if (size == SIZE_MAX) {
|
||||
|
||||
/* Try to start with the right file size if we shall read the file in full. 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 &&
|
||||
(size == SIZE_MAX || FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER))) {
|
||||
|
||||
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)
|
||||
if (rsize < SIZE_MAX) /* overflow check */
|
||||
n_next = rsize + 1;
|
||||
}
|
||||
|
||||
@ -565,6 +564,17 @@ int read_full_stream_full(
|
||||
}
|
||||
}
|
||||
|
||||
/* If we don't know how much to read, figure it out now. If we shall read a part of the file, then
|
||||
* allocate the requested size. If we shall load the full file start with LINE_MAX. Note that if
|
||||
* READ_FULL_FILE_FAIL_WHEN_LARGER we consider the specified size a safety limit, and thus also start
|
||||
* with LINE_MAX, under assumption the file is most likely much shorter. */
|
||||
if (n_next == 0)
|
||||
n_next = size != SIZE_MAX && !FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) ? size : LINE_MAX;
|
||||
|
||||
/* Never read more than we need to determine that our own limit is hit */
|
||||
if (n_next > READ_FULL_BYTES_MAX)
|
||||
n_next = READ_FULL_BYTES_MAX + 1;
|
||||
|
||||
if (offset != UINT64_MAX && fseek(f, offset, SEEK_SET) < 0)
|
||||
return -errno;
|
||||
|
||||
@ -573,6 +583,11 @@ int read_full_stream_full(
|
||||
char *t;
|
||||
size_t k;
|
||||
|
||||
/* If we shall fail when reading overly large data, then read exactly one byte more than the
|
||||
* specified size at max, since that'll tell us if there's anymore data beyond the limit*/
|
||||
if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && n_next > size)
|
||||
n_next = size + 1;
|
||||
|
||||
if (flags & READ_FULL_FILE_SECURE) {
|
||||
t = malloc(n_next + 1);
|
||||
if (!t) {
|
||||
@ -606,14 +621,18 @@ 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 */
|
||||
if (size != SIZE_MAX && !FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER)) { /* 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 */
|
||||
if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && l > size) {
|
||||
r = -E2BIG;
|
||||
goto finalize;
|
||||
}
|
||||
|
||||
if (n >= READ_FULL_BYTES_MAX) {
|
||||
r = -E2BIG;
|
||||
goto finalize;
|
||||
|
@ -39,6 +39,7 @@ typedef enum {
|
||||
READ_FULL_FILE_UNHEX = 1 << 2, /* hex decode what we read */
|
||||
READ_FULL_FILE_WARN_WORLD_READABLE = 1 << 3, /* if regular file, log at LOG_WARNING level if access mode above 0700 */
|
||||
READ_FULL_FILE_CONNECT_SOCKET = 1 << 4, /* if socket inode, connect to it and read off it */
|
||||
READ_FULL_FILE_FAIL_WHEN_LARGER = 1 << 5, /* fail loading if file is larger than specified size */
|
||||
} ReadFullFileFlags;
|
||||
|
||||
int fopen_unlocked(const char *path, const char *options, FILE **ret);
|
||||
|
@ -999,6 +999,25 @@ static void test_read_full_file_offset_size(void) {
|
||||
assert_se(memcmp(buf, rbuf, rbuf_size) == 0);
|
||||
rbuf = mfree(rbuf);
|
||||
|
||||
assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, 128, READ_FULL_FILE_FAIL_WHEN_LARGER, NULL, &rbuf, &rbuf_size) == -E2BIG);
|
||||
assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, sizeof(buf)-1, READ_FULL_FILE_FAIL_WHEN_LARGER, NULL, &rbuf, &rbuf_size) == -E2BIG);
|
||||
assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, sizeof(buf), READ_FULL_FILE_FAIL_WHEN_LARGER, 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, 47, 128, READ_FULL_FILE_FAIL_WHEN_LARGER, NULL, &rbuf, &rbuf_size) == -E2BIG);
|
||||
assert_se(read_full_file_full(AT_FDCWD, fn, 47, sizeof(buf)-47-1, READ_FULL_FILE_FAIL_WHEN_LARGER, NULL, &rbuf, &rbuf_size) == -E2BIG);
|
||||
assert_se(read_full_file_full(AT_FDCWD, fn, 47, sizeof(buf)-47, READ_FULL_FILE_FAIL_WHEN_LARGER, NULL, &rbuf, &rbuf_size) >= 0);
|
||||
assert_se(rbuf_size == sizeof(buf)-47);
|
||||
assert_se(memcmp(buf+47, rbuf, rbuf_size) == 0);
|
||||
rbuf = mfree(rbuf);
|
||||
|
||||
assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, sizeof(buf)+1, READ_FULL_FILE_FAIL_WHEN_LARGER, 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, 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);
|
||||
|
Loading…
Reference in New Issue
Block a user