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