fileio: teach read_full_file_full() to read from offset/with maximum size
This commit is contained in:
parent
c61f46fe31
commit
986311c2da
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue