diff options
| author | Sebastien Bacher <seb128@ubuntu.com> | 2021-01-11 18:07:03 +0100 |
|---|---|---|
| committer | Sebastien Bacher <seb128@ubuntu.com> | 2021-01-11 18:15:57 +0100 |
| commit | 2567f558da3eb9911678227d4aeb15162e707d89 (patch) | |
| tree | 13c80b0e091c8ba585b14b966cda998f95f1936d /shared/nm-glib-aux/nm-io-utils.c | |
| parent | 02d99ffd62ede29cffcb45fcbe33d044b7406d3a (diff) | |
| parent | f54032b30e1ddd84f10a339e0caf493772e049a7 (diff) | |
Merge branch 'debian/master' into ubuntu/master
Diffstat (limited to 'shared/nm-glib-aux/nm-io-utils.c')
| -rw-r--r-- | shared/nm-glib-aux/nm-io-utils.c | 688 |
1 files changed, 355 insertions, 333 deletions
diff --git a/shared/nm-glib-aux/nm-io-utils.c b/shared/nm-glib-aux/nm-io-utils.c index 776c63e1..32f92156 100644 --- a/shared/nm-glib-aux/nm-io-utils.c +++ b/shared/nm-glib-aux/nm-io-utils.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: LGPL-2.1+ +/* SPDX-License-Identifier: LGPL-2.1+ */ /* * Copyright (C) 2018 Red Hat, Inc. */ @@ -18,39 +18,41 @@ /*****************************************************************************/ -_nm_printf (4, 5) -static int -_get_contents_error (GError **error, int errsv, int *out_errsv, const char *format, ...) +_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; + 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__); \ - }) +#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: @@ -86,147 +88,177 @@ _get_contents_error (GError **error, int errsv, int *out_errsv, const char *form * 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_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_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; } /** @@ -253,47 +285,47 @@ nm_utils_fd_get_contents (int fd, * 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) +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); + 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); } /*****************************************************************************/ @@ -303,92 +335,84 @@ nm_utils_file_get_contents (int dirfd, * 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) +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; + 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; } /** @@ -405,13 +429,13 @@ nm_utils_file_set_contents (const char *filename, * * Returns: 0 on success a negative errno on failure. */ int -nm_utils_file_stat (const char *filename, struct stat *out_st) +nm_utils_file_stat(const char *filename, struct stat *out_st) { - struct stat st; + struct stat st; - if (stat (filename, out_st ?: &st) != 0) - return -NM_ERRNO_NATIVE (errno); - return 0; + if (stat(filename, out_st ?: &st) != 0) + return -NM_ERRNO_NATIVE(errno); + return 0; } /** @@ -423,36 +447,34 @@ nm_utils_file_stat (const char *filename, struct stat *out_st) * 0 on EOF. * >0 on success, which is the number of bytes read. */ gssize -nm_utils_fd_read (int fd, NMStrBuf *out_string) +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; + 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; } |