#include "string-util.h"
#include "tmpfile-util.h"
-#define READ_FULL_BYTES_MAX (4U*1024U*1024U)
+/* The maximum size of the file we'll read in one go. */
+#define READ_FULL_BYTES_MAX (4U*1024U*1024U - 1)
int fopen_unlocked(const char *path, const char *options, FILE **ret) {
assert(ret);
}
int take_fdopen_unlocked(int *fd, const char *options, FILE **ret) {
- int r;
+ int r;
assert(fd);
FILE *f,
const char *line,
WriteStringFileFlags flags,
- struct timespec *ts) {
+ const struct timespec *ts) {
bool needs_nl;
- int r, fd;
+ int r, fd = -1;
assert(f);
assert(line);
needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
- /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so
- * that the write goes out in one go, instead of two */
+ /* If STDIO buffering was disabled, then let's append the newline character to the string
+ * itself, so that the write goes out in one go, instead of two */
line = strjoina(line, "\n");
needs_nl = false;
return r;
if (ts) {
- struct timespec twice[2] = {*ts, *ts};
+ const struct timespec twice[2] = {*ts, *ts};
+ assert(fd >= 0);
if (futimens(fd, twice) < 0)
return -errno;
}
const char *fn,
const char *line,
WriteStringFileFlags flags,
- struct timespec *ts) {
+ const struct timespec *ts) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
const char *fn,
const char *line,
WriteStringFileFlags flags,
- struct timespec *ts) {
+ const struct timespec *ts) {
_cleanup_fclose_ FILE *f = NULL;
int q, r, fd;
/* OK, the operation failed, but let's see if the right
* contents in place already. If so, eat up the error. */
- q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
+ q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE));
if (q <= 0)
return r;
return 1;
}
-int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) {
+int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
_cleanup_free_ char *buf = NULL;
_cleanup_close_ int fd = -1;
- struct stat st;
size_t n, size;
int n_retries;
- char *p;
+ bool truncated = false;
assert(ret_contents);
- /* Virtual filesystems such as sysfs or procfs use kernfs, and kernfs can work
- * with two sorts of virtual files. One sort uses "seq_file", and the results of
- * the first read are buffered for the second read. The other sort uses "raw"
- * reads which always go direct to the device. In the latter case, the content of
- * the virtual file must be retrieved with a single read otherwise a second read
- * might get the new value instead of finding EOF immediately. That's the reason
- * why the usage of fread(3) is prohibited in this case as it always performs a
- * second call to read(2) looking for EOF. See issue 13585. */
+ /* Virtual filesystems such as sysfs or procfs use kernfs, and kernfs can work with two sorts of
+ * virtual files. One sort uses "seq_file", and the results of the first read are buffered for the
+ * second read. The other sort uses "raw" reads which always go direct to the device. In the latter
+ * case, the content of the virtual file must be retrieved with a single read otherwise a second read
+ * might get the new value instead of finding EOF immediately. That's the reason why the usage of
+ * fread(3) is prohibited in this case as it always performs a second call to read(2) looking for
+ * EOF. See issue #13585.
+ *
+ * max_size specifies a limit on the bytes read. If max_size is SIZE_MAX, the full file is read. If
+ * the the full file is too large to read, an error is returned. For other values of max_size,
+ * *partial contents* may be returned. (Though the read is still done using one syscall.)
+ * Returns 0 on partial success, 1 if untruncated contents were read. */
fd = open(filename, O_RDONLY|O_CLOEXEC);
if (fd < 0)
return -errno;
- /* Start size for files in /proc which usually report a file size of 0. */
- size = LINE_MAX / 2;
+ assert(max_size <= READ_FULL_BYTES_MAX || max_size == SIZE_MAX);
/* Limit the number of attempts to read the number of bytes returned by fstat(). */
n_retries = 3;
for (;;) {
- if (n_retries <= 0)
- return -EIO;
+ struct stat st;
if (fstat(fd, &st) < 0)
return -errno;
return -EBADF;
/* Be prepared for files from /proc which generally report a file size of 0. */
+ assert_cc(READ_FULL_BYTES_MAX < SSIZE_MAX);
if (st.st_size > 0) {
- size = st.st_size;
- n_retries--;
- } else
- size = size * 2;
+ if (st.st_size > SSIZE_MAX) /* Avoid overflow with 32-bit size_t and 64-bit off_t. */
+ return -EFBIG;
- if (size > READ_FULL_BYTES_MAX)
- return -E2BIG;
+ size = MIN((size_t) st.st_size, max_size);
+ if (size > READ_FULL_BYTES_MAX)
+ return -EFBIG;
- p = realloc(buf, size + 1);
- if (!p)
+ n_retries--;
+ } else {
+ size = MIN(READ_FULL_BYTES_MAX, max_size);
+ n_retries = 0;
+ }
+
+ buf = malloc(size + 1);
+ if (!buf)
return -ENOMEM;
- buf = TAKE_PTR(p);
+ /* Use a bigger allocation if we got it anyway, but not more than the limit. */
+ size = MIN3(malloc_usable_size(buf) - 1, max_size, READ_FULL_BYTES_MAX);
for (;;) {
ssize_t k;
* processing, let's try again either with a bigger guessed size or the new
* file size. */
+ if (n_retries <= 0) {
+ if (max_size == SIZE_MAX)
+ return st.st_size > 0 ? -EIO : -EFBIG;
+
+ /* Accept a short read, but truncate it appropropriately. */
+ n = MIN(n, max_size);
+ truncated = true;
+ break;
+ }
+
if (lseek(fd, 0, SEEK_SET) < 0)
return -errno;
+
+ buf = mfree(buf);
}
if (n < size) {
+ char *p;
+
+ /* Return rest of the buffer to libc */
p = realloc(buf, n + 1);
if (!p)
return -ENOMEM;
- buf = TAKE_PTR(p);
+ buf = p;
}
- if (!ret_size) {
- /* Safety check: if the caller doesn't want to know the size of what we
- * just read it will rely on the trailing NUL byte. But if there's an
- * embedded NUL byte, then we should refuse operation as otherwise
- * there'd be ambiguity about what we just read. */
-
- if (memchr(buf, 0, n))
- return -EBADMSG;
- } else
+ if (ret_size)
*ret_size = n;
+ else if (memchr(buf, 0, n))
+ /* Safety check: if the caller doesn't want to know the size of what we just read it will
+ * rely on the trailing NUL byte. But if there's an embedded NUL byte, then we should refuse
+ * operation as otherwise there'd be ambiguity about what we just read. */
+ return -EBADMSG;
buf[n] = 0;
*ret_contents = TAKE_PTR(buf);
- return 0;
+ return !truncated;
}
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;
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)) {
-
- /* Safety check */
- if (st.st_size > 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;
+ if (size == SIZE_MAX) {
+ 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)
+ 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;
}
buf = t;
- n = n_next;
+ /* Unless a size has been explicitly specified, try to read as much as fits into the memory
+ * we allocated (minus 1, to leave one byte for the safety NUL byte) */
+ n = size == SIZE_MAX ? malloc_usable_size(buf) - 1 : n_next;
errno = 0;
k = fread(buf + l, 1, n - l, f);
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 */
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) {
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 {
(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) {
return 0;
}
-static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
+static int search_and_fopen_internal(
+ const char *path,
+ const char *mode,
+ const char *root,
+ char **search,
+ FILE **ret,
+ char **ret_path) {
+
char **i;
assert(path);
assert(mode);
- assert(_f);
+ assert(ret);
if (!path_strv_resolve_uniq(search, root))
return -ENOMEM;
f = fopen(p, mode);
if (f) {
- *_f = f;
+ if (ret_path)
+ *ret_path = path_simplify(TAKE_PTR(p), true);
+
+ *ret = f;
return 0;
}
return -ENOENT;
}
-int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) {
+int search_and_fopen(
+ const char *filename,
+ const char *mode,
+ const char *root,
+ const char **search,
+ FILE **ret,
+ char **ret_path) {
+
_cleanup_strv_free_ char **copy = NULL;
- assert(path);
+ assert(filename);
assert(mode);
- assert(_f);
+ assert(ret);
- if (path_is_absolute(path)) {
- FILE *f;
+ if (path_is_absolute(filename)) {
+ _cleanup_fclose_ FILE *f = NULL;
- f = fopen(path, mode);
- if (f) {
- *_f = f;
- return 0;
+ f = fopen(filename, mode);
+ if (!f)
+ return -errno;
+
+ if (ret_path) {
+ char *p;
+
+ p = strdup(filename);
+ if (!p)
+ return -ENOMEM;
+
+ *ret_path = path_simplify(p, true);
}
- return -errno;
+ *ret = TAKE_PTR(f);
+ return 0;
}
copy = strv_copy((char**) search);
if (!copy)
return -ENOMEM;
- return search_and_fopen_internal(path, mode, root, copy, _f);
+ return search_and_fopen_internal(filename, mode, root, copy, ret, ret_path);
}
-int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) {
+int search_and_fopen_nulstr(
+ const char *filename,
+ const char *mode,
+ const char *root,
+ const char *search,
+ FILE **ret,
+ char **ret_path) {
+
_cleanup_strv_free_ char **s = NULL;
- if (path_is_absolute(path)) {
- FILE *f;
+ if (path_is_absolute(filename)) {
+ _cleanup_fclose_ FILE *f = NULL;
- f = fopen(path, mode);
- if (f) {
- *_f = f;
- return 0;
+ f = fopen(filename, mode);
+ if (!f)
+ return -errno;
+
+ if (ret_path) {
+ char *p;
+
+ p = strdup(filename);
+ if (!p)
+ return -ENOMEM;
+
+ *ret_path = path_simplify(p, true);
}
- return -errno;
+ *ret = TAKE_PTR(f);
+ return 0;
}
s = strv_split_nulstr(search);
if (!s)
return -ENOMEM;
- return search_and_fopen_internal(path, mode, root, s, _f);
+ return search_and_fopen_internal(filename, mode, root, s, ret, ret_path);
}
int chase_symlinks_and_fopen_unlocked(
_cleanup_close_ int fd = -1;
_cleanup_free_ char *final_path = NULL;
int mode_flags, r;
- FILE *f;
assert(path);
assert(open_flags);
if (fd < 0)
return fd;
- r = fdopen_unlocked(fd, open_flags, &f);
+ r = take_fdopen_unlocked(&fd, open_flags, ret_file);
if (r < 0)
return r;
- TAKE_FD(fd);
- *ret_file = f;
if (ret_path)
*ret_path = TAKE_PTR(final_path);
return 0;
return EOL_NONE;
}
-DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, funlockfile, NULL);
int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
size_t n = 0, allocated = 0, count = 0;
return 0;
}
-int sync_rights(int from, int to) {
- struct stat st;
-
- if (fstat(from, &st) < 0)
- return -errno;
-
- return fchmod_and_chown(to, st.st_mode & 07777, st.st_uid, st.st_gid);
-}
-
int rename_and_apply_smack_floor_label(const char *from, const char *to) {
int r = 0;
if (rename(from, to) < 0)