fileio: teach read_full_file_full() to read from offset/with maximum size

This commit is contained in:
Lennart Poettering
2020-12-01 14:17:47 +01:00
parent c61f46fe31
commit 986311c2da
12 changed files with 130 additions and 36 deletions
+41 -15
View 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)) {
if (size == SIZE_MAX) {
uint64_t rsize =
LESS_BY((uint64_t) st.st_size, offset == UINT64_MAX ? 0 : offset);
/* Safety check */
if (st.st_size > READ_FULL_BYTES_MAX)
return -E2BIG;
/* 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 (st.st_size > 0)
n_next = st.st_size + 1;
/* 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) {
+6 -6
View 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);