diff options
Diffstat (limited to 'shared/nm-glib-aux/nm-io-utils.c')
| -rw-r--r-- | shared/nm-glib-aux/nm-io-utils.c | 480 |
1 files changed, 0 insertions, 480 deletions
diff --git a/shared/nm-glib-aux/nm-io-utils.c b/shared/nm-glib-aux/nm-io-utils.c deleted file mode 100644 index 429591ad..00000000 --- a/shared/nm-glib-aux/nm-io-utils.c +++ /dev/null @@ -1,480 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-io-utils.h" - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> - -#include "nm-str-buf.h" -#include "nm-shared-utils.h" -#include "nm-secret-utils.h" -#include "nm-errno.h" - -/*****************************************************************************/ - -_nm_printf(4, 5) static int _get_contents_error(GError ** error, - int errsv, - int * out_errsv, - const char *format, - ...) -{ - nm_assert(NM_ERRNO_NATIVE(errsv)); - - if (error) { - gs_free char *msg = NULL; - va_list args; - char bstrerr[NM_STRERROR_BUFSIZE]; - - va_start(args, format); - msg = g_strdup_vprintf(format, args); - va_end(args); - g_set_error(error, - G_FILE_ERROR, - g_file_error_from_errno(errsv), - "%s: %s", - msg, - nm_strerror_native_r(errsv, bstrerr, sizeof(bstrerr))); - } - - nm_assert(errsv > 0); - NM_SET_OUT(out_errsv, errsv); - - return FALSE; -} -#define _get_contents_error_errno(error, out_errsv, ...) \ - ({ \ - int _errsv = (errno); \ - \ - _get_contents_error(error, _errsv, out_errsv, __VA_ARGS__); \ - }) - -/** - * nm_utils_fd_get_contents: - * @fd: open file descriptor to read. The fd will not be closed, - * but don't rely on its state afterwards. - * @close_fd: if %TRUE, @fd will be closed by the function. - * Passing %TRUE here might safe a syscall for dup(). - * @max_length: allocate at most @max_length bytes. If the - * file is larger, reading will fail. Set to zero to use - * a very large default. - * WARNING: @max_length is here to avoid a crash for huge/unlimited files. - * For example, stat(/sys/class/net/enp0s25/ifindex) gives a filesize of - * 4K, although the actual real is small. @max_length is the memory - * allocated in the process of reading the file, thus it must be at least - * the size reported by fstat. - * If you set it to 1K, read will fail because fstat() claims the - * file is larger. - * @flags: %NMUtilsFileGetContentsFlags for reading the file. - * @contents: the output buffer with the file read. It is always - * NUL terminated. The buffer is at most @max_length long, including - * the NUL byte. That is, it reads only files up to a length of - * @max_length - 1 bytes. - * @length: optional output argument of the read file size. - * @out_errsv: (allow-none) (out): on error, a positive errno. or zero. - * @error: - * - * - * A reimplementation of g_file_get_contents() with a few differences: - * - accepts an open fd, instead of a path name. This allows you to - * use openat(). - * - limits the maximum filesize to max_length. - * - * Returns: TRUE on success. - */ -gboolean -nm_utils_fd_get_contents(int fd, - gboolean close_fd, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char ** contents, - gsize * length, - int * out_errsv, - GError ** error) -{ - nm_auto_close int fd_keeper = close_fd ? fd : -1; - struct stat stat_buf; - gs_free char * str = NULL; - const bool do_bzero_mem = NM_FLAGS_HAS(flags, NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET); - int errsv; - - g_return_val_if_fail(fd >= 0, FALSE); - g_return_val_if_fail(contents && !*contents, FALSE); - g_return_val_if_fail(!error || !*error, FALSE); - - NM_SET_OUT(length, 0); - - if (fstat(fd, &stat_buf) < 0) - return _get_contents_error_errno(error, out_errsv, "failure during fstat"); - - if (!max_length) { - /* default to a very large size, but not extreme */ - max_length = 2 * 1024 * 1024; - } - - if (stat_buf.st_size > 0 && S_ISREG(stat_buf.st_mode)) { - const gsize n_stat = stat_buf.st_size; - ssize_t n_read; - - if (n_stat > max_length - 1) - return _get_contents_error(error, - EMSGSIZE, - out_errsv, - "file too large (%zu+1 bytes with maximum %zu bytes)", - n_stat, - max_length); - - str = g_try_malloc(n_stat + 1); - if (!str) - return _get_contents_error(error, - ENOMEM, - out_errsv, - "failure to allocate buffer of %zu+1 bytes", - n_stat); - - n_read = nm_utils_fd_read_loop(fd, str, n_stat, TRUE); - if (n_read < 0) { - if (do_bzero_mem) - nm_explicit_bzero(str, n_stat); - return _get_contents_error(error, - -n_read, - out_errsv, - "error reading %zu bytes from file descriptor", - n_stat); - } - str[n_read] = '\0'; - - if (n_read < n_stat) { - if (!(str = nm_secret_mem_try_realloc_take(str, do_bzero_mem, n_stat + 1, n_read + 1))) - return _get_contents_error(error, - ENOMEM, - out_errsv, - "failure to reallocate buffer with %zu bytes", - n_read + 1); - } - NM_SET_OUT(length, n_read); - } else { - nm_auto_fclose FILE *f = NULL; - char buf[4096]; - gsize n_have, n_alloc; - int fd2; - - if (fd_keeper >= 0) - fd2 = nm_steal_fd(&fd_keeper); - else { - fd2 = fcntl(fd, F_DUPFD_CLOEXEC, 0); - if (fd2 < 0) - return _get_contents_error_errno(error, out_errsv, "error during dup"); - } - - if (!(f = fdopen(fd2, "r"))) { - errsv = errno; - nm_close(fd2); - return _get_contents_error(error, errsv, out_errsv, "failure during fdopen"); - } - - n_have = 0; - n_alloc = 0; - - while (!feof(f)) { - gsize n_read; - - n_read = fread(buf, 1, sizeof(buf), f); - errsv = errno; - if (ferror(f)) { - if (do_bzero_mem) - nm_explicit_bzero(buf, sizeof(buf)); - return _get_contents_error(error, errsv, out_errsv, "error during fread"); - } - - if (n_have > G_MAXSIZE - 1 - n_read || n_have + n_read + 1 > max_length) { - if (do_bzero_mem) - nm_explicit_bzero(buf, sizeof(buf)); - return _get_contents_error( - error, - EMSGSIZE, - out_errsv, - "file stream too large (%zu+1 bytes with maximum %zu bytes)", - (n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read, - max_length); - } - - if (n_have + n_read + 1 >= n_alloc) { - gsize old_n_alloc = n_alloc; - - if (n_alloc != 0) { - nm_assert(str); - if (n_alloc >= max_length / 2) - n_alloc = max_length; - else - n_alloc *= 2; - } else { - nm_assert(!str); - n_alloc = NM_MIN(n_read + 1, sizeof(buf)); - } - - if (!(str = nm_secret_mem_try_realloc_take(str, - do_bzero_mem, - old_n_alloc, - n_alloc))) { - if (do_bzero_mem) - nm_explicit_bzero(buf, sizeof(buf)); - return _get_contents_error(error, - ENOMEM, - out_errsv, - "failure to allocate buffer of %zu bytes", - n_alloc); - } - } - - memcpy(str + n_have, buf, n_read); - n_have += n_read; - } - - if (do_bzero_mem) - nm_explicit_bzero(buf, sizeof(buf)); - - if (n_alloc == 0) - str = g_new0(char, 1); - else { - str[n_have] = '\0'; - if (n_have + 1 < n_alloc) { - if (!(str = nm_secret_mem_try_realloc_take(str, do_bzero_mem, n_alloc, n_have + 1))) - return _get_contents_error(error, - ENOMEM, - out_errsv, - "failure to truncate buffer to %zu bytes", - n_have + 1); - } - } - - NM_SET_OUT(length, n_have); - } - - *contents = g_steal_pointer(&str); - NM_SET_OUT(out_errsv, 0); - return TRUE; -} - -/** - * nm_utils_file_get_contents: - * @dirfd: optional file descriptor to use openat(). If negative, use plain open(). - * @filename: the filename to open. Possibly relative to @dirfd. - * @max_length: allocate at most @max_length bytes. - * WARNING: see nm_utils_fd_get_contents() hint about @max_length. - * @flags: %NMUtilsFileGetContentsFlags for reading the file. - * @contents: the output buffer with the file read. It is always - * NUL terminated. The buffer is at most @max_length long, including - * the NUL byte. That is, it reads only files up to a length of - * @max_length - 1 bytes. - * @length: optional output argument of the read file size. - * @out_errsv: (allow-none) (out): on error, a positive errno. or zero. - * @error: - * - * A reimplementation of g_file_get_contents() with a few differences: - * - accepts an @dirfd to open @filename relative to that path via openat(). - * - limits the maximum filesize to max_length. - * - uses O_CLOEXEC on internal file descriptor - * - optionally returns the native errno on failure. - * - * Returns: TRUE on success. - */ -gboolean -nm_utils_file_get_contents(int dirfd, - const char * filename, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char ** contents, - gsize * length, - int * out_errsv, - GError ** error) -{ - int fd; - - g_return_val_if_fail(filename && filename[0], FALSE); - g_return_val_if_fail(contents && !*contents, FALSE); - - NM_SET_OUT(length, 0); - - if (dirfd >= 0) { - fd = openat(dirfd, filename, O_RDONLY | O_CLOEXEC); - if (fd < 0) { - return _get_contents_error_errno(error, - out_errsv, - "Failed to open file \"%s\" with openat", - filename); - } - } else { - fd = open(filename, O_RDONLY | O_CLOEXEC); - if (fd < 0) { - return _get_contents_error_errno(error, - out_errsv, - "Failed to open file \"%s\"", - filename); - } - } - return nm_utils_fd_get_contents(fd, - TRUE, - max_length, - flags, - contents, - length, - out_errsv, - error); -} - -/*****************************************************************************/ - -/* - * Copied from GLib's g_file_set_contents() et al., but allows - * specifying a mode for the new file. - */ -gboolean -nm_utils_file_set_contents(const char *filename, - const char *contents, - gssize length, - mode_t mode, - int * out_errsv, - GError ** error) -{ - gs_free char *tmp_name = NULL; - struct stat statbuf; - int errsv; - gssize s; - int fd; - - g_return_val_if_fail(filename, FALSE); - g_return_val_if_fail(contents || !length, FALSE); - g_return_val_if_fail(!error || !*error, FALSE); - g_return_val_if_fail(length >= -1, FALSE); - - if (length == -1) - length = strlen(contents); - - tmp_name = g_strdup_printf("%s.XXXXXX", filename); - fd = g_mkstemp_full(tmp_name, O_RDWR | O_CLOEXEC, mode); - if (fd < 0) { - return _get_contents_error_errno(error, out_errsv, "failed to create file %s", tmp_name); - } - - while (length > 0) { - s = write(fd, contents, length); - if (s < 0) { - errsv = NM_ERRNO_NATIVE(errno); - if (errsv == EINTR) - continue; - - nm_close(fd); - unlink(tmp_name); - return _get_contents_error(error, - errsv, - out_errsv, - "failed to write to file %s", - tmp_name); - } - - g_assert(s <= length); - - contents += s; - length -= s; - } - - /* If the final destination exists and is > 0 bytes, we want to sync the - * newly written file to ensure the data is on disk when we rename over - * the destination. Otherwise, if we get a system crash we can lose both - * the new and the old file on some filesystems. (I.E. those that don't - * guarantee the data is written to the disk before the metadata.) - */ - if (lstat(filename, &statbuf) == 0 && statbuf.st_size > 0) { - if (fsync(fd) != 0) { - errsv = NM_ERRNO_NATIVE(errno); - nm_close(fd); - unlink(tmp_name); - return _get_contents_error(error, errsv, out_errsv, "failed to fsync %s", tmp_name); - } - } - - nm_close(fd); - - if (rename(tmp_name, filename)) { - errsv = NM_ERRNO_NATIVE(errno); - unlink(tmp_name); - return _get_contents_error(error, - errsv, - out_errsv, - "failed rename %s to %s", - tmp_name, - filename); - } - - return TRUE; -} - -/** - * nm_utils_file_stat: - * @filename: the filename to stat. - * @out_st: (allow-none) (out): if given, this will be passed to stat(). - * - * Just wraps stat() and gives the errno number as function result instead - * of setting the errno (though, errno is also set). It's only for convenience - * with - * - * if (nm_utils_file_stat (filename, NULL) == -ENOENT) { - * } - * - * Returns: 0 on success a negative errno on failure. */ -int -nm_utils_file_stat(const char *filename, struct stat *out_st) -{ - struct stat st; - - if (stat(filename, out_st ?: &st) != 0) - return -NM_ERRNO_NATIVE(errno); - return 0; -} - -/** - * nm_utils_fd_read: - * @fd: the fd to read from. - * @out_string: (out): output string where read bytes will be stored. - * - * Returns: <0 on failure, which is -(errno). - * 0 on EOF. - * >0 on success, which is the number of bytes read. */ -gssize -nm_utils_fd_read(int fd, NMStrBuf *out_string) -{ - gsize buf_available; - gssize n_read; - int errsv; - - g_return_val_if_fail(fd >= 0, -1); - g_return_val_if_fail(out_string, -1); - - /* If the buffer size is 0, we allocate NM_UTILS_GET_NEXT_REALLOC_SIZE_1000 (1000 bytes) - * the first time. Afterwards, the buffer grows exponentially. - * - * Note that with @buf_available, we always would read as much buffer as we actually - * have reserved. */ - nm_str_buf_maybe_expand(out_string, NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE); - - buf_available = out_string->allocated - out_string->len; - - n_read = read(fd, &((nm_str_buf_get_str_unsafe(out_string))[out_string->len]), buf_available); - if (n_read < 0) { - errsv = errno; - return -NM_ERRNO_NATIVE(errsv); - } - - if (n_read > 0) { - nm_assert((gsize) n_read <= buf_available); - nm_str_buf_set_size(out_string, out_string->len + (gsize) n_read, TRUE, FALSE); - } - - return n_read; -} |