summaryrefslogtreecommitdiff
path: root/src/libnm-systemd-shared/src/basic/fileio.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-systemd-shared/src/basic/fileio.c')
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.c272
1 files changed, 189 insertions, 83 deletions
diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c
index 908a030911..7ab29816fe 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.c
+++ b/src/libnm-systemd-shared/src/basic/fileio.c
@@ -30,10 +30,13 @@
#include "stdio-util.h"
#include "string-util.h"
#include "sync-util.h"
+#include "terminal-util.h"
#include "tmpfile-util.h"
/* The maximum size of the file we'll read in one go in read_full_file() (64M). */
#define READ_FULL_BYTES_MAX (64U*1024U*1024U - 1U)
+/* Used when a size is specified for read_full_file() with READ_FULL_FILE_UNBASE64 or _UNHEX */
+#define READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY 3
/* The maximum size of virtual files (i.e. procfs, sysfs, and other virtual "API" files) we'll read in one go
* in read_virtual_file(). Note that this limit is different (and much lower) than the READ_FULL_BYTES_MAX
@@ -200,6 +203,19 @@ int write_string_stream_ts(
return 0;
}
+static mode_t write_string_file_flags_to_mode(WriteStringFileFlags flags) {
+
+ /* We support three different modes, that are the ones that really make sense for text files like this:
+ *
+ * → 0600 (i.e. root-only)
+ * → 0444 (i.e. read-only)
+ * → 0644 (i.e. writable for root, readable for everyone else)
+ */
+
+ return FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 :
+ FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0444) ? 0444 : 0644;
+}
+
static int write_string_file_atomic_at(
int dir_fd,
const char *fn,
@@ -225,7 +241,7 @@ static int write_string_file_atomic_at(
if (r < 0)
goto fail;
- r = fchmod_umask(fileno(f), FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0644);
+ r = fchmod_umask(fileno(f), write_string_file_flags_to_mode(flags));
if (r < 0)
goto fail;
@@ -288,7 +304,7 @@ int write_string_file_ts_at(
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
- (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
+ write_string_file_flags_to_mode(flags));
if (fd < 0) {
r = -errno;
goto fail;
@@ -576,7 +592,7 @@ int read_full_stream_full(
size_t *ret_size) {
_cleanup_free_ char *buf = NULL;
- size_t n, n_next = 0, l;
+ size_t n, n_next = 0, l, expected_decoded_size = size;
int fd, r;
assert(f);
@@ -587,6 +603,13 @@ int read_full_stream_full(
if (offset != UINT64_MAX && offset > LONG_MAX) /* fseek() can only deal with "long" offsets */
return -ERANGE;
+ if ((flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) != 0) {
+ if (size <= SIZE_MAX / READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY)
+ size *= READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY;
+ else
+ size = SIZE_MAX;
+ }
+
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 */
@@ -711,6 +734,11 @@ int read_full_stream_full(
explicit_bzero_safe(buf, n);
free_and_replace(buf, decoded);
n = l = decoded_size;
+
+ if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && l > expected_decoded_size) {
+ r = -E2BIG;
+ goto finalize;
+ }
}
if (!ret_size) {
@@ -1057,7 +1085,9 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) {
if (mode_flags < 0)
return mode_flags;
- copy_fd = fd_reopen(fd, mode_flags);
+ /* Flags returned by fopen_mode_to_flags might contain O_CREAT, but it doesn't make sense for fd_reopen
+ * since we're working on an existing fd anyway. Let's drop it here to avoid triggering assertion. */
+ copy_fd = fd_reopen(fd, mode_flags & ~O_CREAT);
if (copy_fd < 0)
return copy_fd;
@@ -1069,123 +1099,171 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) {
return 0;
}
-static int search_and_fopen_internal(
+static int search_and_open_internal(
const char *path,
- const char *mode,
+ int mode, /* if ret_fd is NULL this is an [FRWX]_OK mode for access(), otherwise an open mode for open() */
const char *root,
char **search,
- FILE **ret,
+ int *ret_fd,
char **ret_path) {
+ int r;
+
+ assert(!ret_fd || !FLAGS_SET(mode, O_CREAT)); /* We don't support O_CREAT for this */
assert(path);
- assert(mode);
- assert(ret);
+
+ if (path_is_absolute(path)) {
+ _cleanup_close_ int fd = -EBADF;
+
+ if (ret_fd)
+ /* We only specify 0777 here to appease static analyzers, it's never used since we
+ * don't support O_CREAT here */
+ r = fd = RET_NERRNO(open(path, mode, 0777));
+ else
+ r = RET_NERRNO(access(path, mode));
+ if (r < 0)
+ return r;
+
+ if (ret_path) {
+ r = path_simplify_alloc(path, ret_path);
+ if (r < 0)
+ return r;
+ }
+
+ if (ret_fd)
+ *ret_fd = TAKE_FD(fd);
+
+ return 0;
+ }
if (!path_strv_resolve_uniq(search, root))
return -ENOMEM;
STRV_FOREACH(i, search) {
+ _cleanup_close_ int fd = -EBADF;
_cleanup_free_ char *p = NULL;
- FILE *f;
p = path_join(root, *i, path);
if (!p)
return -ENOMEM;
- f = fopen(p, mode);
- if (f) {
+ if (ret_fd)
+ /* as above, 0777 is static analyzer appeasement */
+ r = fd = RET_NERRNO(open(p, mode, 0777));
+ else
+ r = RET_NERRNO(access(p, F_OK));
+ if (r >= 0) {
if (ret_path)
*ret_path = path_simplify(TAKE_PTR(p));
- *ret = f;
+ if (ret_fd)
+ *ret_fd = TAKE_FD(fd);
+
return 0;
}
-
- if (errno != ENOENT)
- return -errno;
+ if (r != -ENOENT)
+ return r;
}
return -ENOENT;
}
-int search_and_fopen(
- const char *filename,
- const char *mode,
+int search_and_open(
+ const char *path,
+ int mode,
const char *root,
- const char **search,
- FILE **ret,
+ char **search,
+ int *ret_fd,
char **ret_path) {
_cleanup_strv_free_ char **copy = NULL;
- assert(filename);
- assert(mode);
- assert(ret);
+ assert(path);
- if (path_is_absolute(filename)) {
- _cleanup_fclose_ FILE *f = NULL;
+ copy = strv_copy((char**) search);
+ if (!copy)
+ return -ENOMEM;
+
+ return search_and_open_internal(path, mode, root, copy, ret_fd, ret_path);
+}
+
+static int search_and_fopen_internal(
+ const char *path,
+ const char *mode,
+ const char *root,
+ char **search,
+ FILE **ret_file,
+ char **ret_path) {
+
+ _cleanup_free_ char *found_path = NULL;
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ assert(path);
+ assert(mode || !ret_file);
+
+ r = search_and_open(
+ path,
+ mode ? fopen_mode_to_flags(mode) : 0,
+ root,
+ search,
+ ret_file ? &fd : NULL,
+ ret_path ? &found_path : NULL);
+ if (r < 0)
+ return r;
- f = fopen(filename, mode);
+ if (ret_file) {
+ FILE *f = take_fdopen(&fd, mode);
if (!f)
return -errno;
- if (ret_path) {
- char *p;
+ *ret_file = f;
+ }
- p = strdup(filename);
- if (!p)
- return -ENOMEM;
+ if (ret_path)
+ *ret_path = TAKE_PTR(found_path);
- *ret_path = path_simplify(p);
- }
+ return 0;
+}
- *ret = TAKE_PTR(f);
- return 0;
- }
+int search_and_fopen(
+ const char *path,
+ const char *mode,
+ const char *root,
+ const char **search,
+ FILE **ret_file,
+ char **ret_path) {
+
+ _cleanup_strv_free_ char **copy = NULL;
+
+ assert(path);
+ assert(mode || !ret_file);
copy = strv_copy((char**) search);
if (!copy)
return -ENOMEM;
- return search_and_fopen_internal(filename, mode, root, copy, ret, ret_path);
+ return search_and_fopen_internal(path, mode, root, copy, ret_file, ret_path);
}
int search_and_fopen_nulstr(
- const char *filename,
+ const char *path,
const char *mode,
const char *root,
const char *search,
- FILE **ret,
+ FILE **ret_file,
char **ret_path) {
- _cleanup_strv_free_ char **s = NULL;
-
- if (path_is_absolute(filename)) {
- _cleanup_fclose_ FILE *f = NULL;
-
- 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);
- }
+ _cleanup_strv_free_ char **l = NULL;
- *ret = TAKE_PTR(f);
- return 0;
- }
+ assert(path);
+ assert(mode || !ret_file);
- s = strv_split_nulstr(search);
- if (!s)
+ l = strv_split_nulstr(search);
+ if (!l)
return -ENOMEM;
- return search_and_fopen_internal(filename, mode, root, s, ret, ret_path);
+ return search_and_fopen_internal(path, mode, root, l, ret_file, ret_path);
}
#endif /* NM_IGNORED */
@@ -1259,33 +1337,31 @@ int read_timestamp_file(const char *fn, usec_t *ret) {
}
#endif /* NM_IGNORED */
-int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) {
- int r;
-
+int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space) {
assert(s);
+ assert(space);
- /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter
- * when specified shall initially point to a boolean variable initialized to false. It is set to true after the
- * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each
- * element, but not before the first one. */
+ /* Outputs the specified string with fputs(), but optionally prefixes it with a separator.
+ * The *space parameter when specified shall initially point to a boolean variable initialized
+ * to false. It is set to true after the first invocation. This call is supposed to be use in loops,
+ * where a separator shall be inserted between each element, but not before the first one. */
if (!f)
f = stdout;
- if (space) {
- if (!separator)
- separator = " ";
+ if (!separator)
+ separator = " ";
- if (*space) {
- r = fputs(separator, f);
- if (r < 0)
- return r;
- }
+ if (*space)
+ if (fputs(separator, f) < 0)
+ return -EIO;
- *space = true;
- }
+ *space = true;
+
+ if (fputs(s, f) < 0)
+ return -EIO;
- return fputs(s, f);
+ return 0;
}
#if 0 /* NM_IGNORED */
@@ -1406,7 +1482,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
* and don't call isatty() on an invalid fd */
flags |= READ_LINE_NOT_A_TTY;
else
- flags |= isatty(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
+ flags |= isatty_safe(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
}
if (FLAGS_SET(flags, READ_LINE_IS_A_TTY))
break;
@@ -1437,6 +1513,36 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
return (int) count;
}
+int read_stripped_line(FILE *f, size_t limit, char **ret) {
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ assert(f);
+
+ r = read_line(f, limit, ret ? &s : NULL);
+ if (r < 0)
+ return r;
+
+ if (ret) {
+ const char *p;
+
+ p = strstrip(s);
+ if (p == s)
+ *ret = TAKE_PTR(s);
+ else {
+ char *copy;
+
+ copy = strdup(p);
+ if (!copy)
+ return -ENOMEM;
+
+ *ret = copy;
+ }
+ }
+
+ return r;
+}
+
int safe_fgetc(FILE *f, char *ret) {
int k;