2020-12-23 22:21:36 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2019-09-25 13:13:40 +02:00
|
|
|
/*
|
2019-10-01 09:20:35 +02:00
|
|
|
* Copyright (C) 2018 Red Hat, Inc.
|
2018-08-30 15:00:39 +02:00
|
|
|
*/
|
|
|
|
|
|
2021-02-04 08:48:06 +01:00
|
|
|
#include "nm-glib-aux/nm-default-glib-i18n-lib.h"
|
2018-08-30 15:00:39 +02:00
|
|
|
|
|
|
|
|
#include "nm-io-utils.h"
|
|
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
cli/polkit: make parsing polkit-agent-helper-1 protocol more conforming
- in io_watch_have_data(), ensure that we handle incomplete lines
that don't yet have a newline by waiting for more data. That means,
if the current content of the in_buffer does not have a newline, we
wait longer.
- in io_watch_have_data(), implement (and ignore) certain commands
instead of failing the request.
- in io_watch_have_data(), no longer g_compress() the entire line.
"polkitagenthelper-pam.c" never backslash escapes the command, it
only escapes the arguments. Of course, there should be no difference
in practice, except that we don't want to handle escape sequences
in the commands.
- in io_watch_have_data(), compare SUCCESS/FAILURE literally.
"polkitagenthelper-pam.c" never appends any trailing garbage to these
commands, and we shouldn't handle that (although "polkitagentsession.c"
does).
- when io_watch_have_data() completes with success, we cannot destroy
AuthRequest right away. It probably still has data pending that we first
need to write to the polkit helper. Wait longer, and let io_watch_can_write()
complete the request.
- ensure we always answer the GDBusMethodInvocation. Otherwise, it gets
leaked.
- use NMStrBuf instead of GString.
2020-04-06 10:54:35 +02:00
|
|
|
#include "nm-str-buf.h"
|
2018-08-30 15:13:52 +02:00
|
|
|
#include "nm-shared-utils.h"
|
|
|
|
|
#include "nm-secret-utils.h"
|
2019-01-31 16:53:45 +01:00
|
|
|
#include "nm-errno.h"
|
2018-08-30 15:13:52 +02:00
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
_nm_printf(4, 5) static int _get_contents_error(GError ** error,
|
|
|
|
|
int errsv,
|
|
|
|
|
int * out_errsv,
|
|
|
|
|
const char *format,
|
|
|
|
|
...)
|
2018-08-30 15:13:52 +02:00
|
|
|
{
|
2019-01-31 16:53:45 +01:00
|
|
|
nm_assert(NM_ERRNO_NATIVE(errsv));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (error) {
|
2019-02-01 08:35:14 +01:00
|
|
|
gs_free char *msg = NULL;
|
2018-08-30 15:13:52 +02:00
|
|
|
va_list args;
|
2019-02-01 08:35:14 +01:00
|
|
|
char bstrerr[NM_STRERROR_BUFSIZE];
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
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",
|
2019-02-01 08:35:14 +01:00
|
|
|
msg,
|
|
|
|
|
nm_strerror_native_r(errsv, bstrerr, sizeof(bstrerr)));
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
nm_assert(errsv > 0);
|
|
|
|
|
NM_SET_OUT(out_errsv, errsv);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return FALSE;
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
#define _get_contents_error_errno(error, out_errsv, ...) \
|
2019-01-31 13:29:21 +01:00
|
|
|
({ \
|
|
|
|
|
int _errsv = (errno); \
|
|
|
|
|
\
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
_get_contents_error(error, _errsv, out_errsv, __VA_ARGS__); \
|
2019-01-31 13:29:21 +01:00
|
|
|
})
|
2018-08-30 15:13:52 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
* @out_errsv: (allow-none) (out): on error, a positive errno. or zero.
|
|
|
|
|
* @error:
|
|
|
|
|
*
|
2018-08-30 15:13:52 +02:00
|
|
|
*
|
|
|
|
|
* 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().
|
2018-09-30 11:30:18 -03:00
|
|
|
* - limits the maximum filesize to max_length.
|
2018-08-30 15:13:52 +02:00
|
|
|
*
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
* Returns: TRUE on success.
|
2018-08-30 15:13:52 +02:00
|
|
|
*/
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
gboolean
|
2018-08-30 15:13:52 +02:00
|
|
|
nm_utils_fd_get_contents(int fd,
|
|
|
|
|
gboolean close_fd,
|
|
|
|
|
gsize max_length,
|
|
|
|
|
NMUtilsFileGetContentsFlags flags,
|
|
|
|
|
char ** contents,
|
|
|
|
|
gsize * length,
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
int * out_errsv,
|
2018-08-30 15:13:52 +02:00
|
|
|
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);
|
2019-01-31 13:29:21 +01:00
|
|
|
int errsv;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
g_return_val_if_fail(fd >= 0, FALSE);
|
|
|
|
|
g_return_val_if_fail(contents && !*contents, FALSE);
|
|
|
|
|
g_return_val_if_fail(!error || !*error, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
NM_SET_OUT(length, 0);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (fstat(fd, &stat_buf) < 0)
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error_errno(error, out_errsv, "failure during fstat");
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (!max_length) {
|
|
|
|
|
/* default to a very large size, but not extreme */
|
|
|
|
|
max_length = 2 * 1024 * 1024;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (stat_buf.st_size > 0 && S_ISREG(stat_buf.st_mode)) {
|
|
|
|
|
const gsize n_stat = stat_buf.st_size;
|
|
|
|
|
ssize_t n_read;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (n_stat > max_length - 1)
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error(error,
|
|
|
|
|
EMSGSIZE,
|
|
|
|
|
out_errsv,
|
|
|
|
|
"file too large (%zu+1 bytes with maximum %zu bytes)",
|
|
|
|
|
n_stat,
|
|
|
|
|
max_length);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
str = g_try_malloc(n_stat + 1);
|
|
|
|
|
if (!str)
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error(error,
|
|
|
|
|
ENOMEM,
|
|
|
|
|
out_errsv,
|
|
|
|
|
"failure to allocate buffer of %zu+1 bytes",
|
|
|
|
|
n_stat);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
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);
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error(error,
|
|
|
|
|
-n_read,
|
|
|
|
|
out_errsv,
|
|
|
|
|
"error reading %zu bytes from file descriptor",
|
|
|
|
|
n_stat);
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
|
|
|
|
str[n_read] = '\0';
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (n_read < n_stat) {
|
2020-02-25 10:45:05 +01:00
|
|
|
if (!(str = nm_secret_mem_try_realloc_take(str, do_bzero_mem, n_stat + 1, n_read + 1)))
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error(error,
|
|
|
|
|
ENOMEM,
|
|
|
|
|
out_errsv,
|
|
|
|
|
"failure to reallocate buffer with %zu bytes",
|
|
|
|
|
n_read + 1);
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
|
|
|
|
NM_SET_OUT(length, n_read);
|
|
|
|
|
} else {
|
|
|
|
|
nm_auto_fclose FILE *f = NULL;
|
|
|
|
|
char buf[4096];
|
|
|
|
|
gsize n_have, n_alloc;
|
|
|
|
|
int fd2;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (fd_keeper >= 0)
|
|
|
|
|
fd2 = nm_steal_fd(&fd_keeper);
|
|
|
|
|
else {
|
|
|
|
|
fd2 = fcntl(fd, F_DUPFD_CLOEXEC, 0);
|
|
|
|
|
if (fd2 < 0)
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error_errno(error, out_errsv, "error during dup");
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (!(f = fdopen(fd2, "r"))) {
|
2019-01-31 13:29:21 +01:00
|
|
|
errsv = errno;
|
2018-08-30 15:13:52 +02:00
|
|
|
nm_close(fd2);
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error(error, errsv, out_errsv, "failure during fdopen");
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
n_have = 0;
|
|
|
|
|
n_alloc = 0;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
while (!feof(f)) {
|
|
|
|
|
gsize n_read;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
n_read = fread(buf, 1, sizeof(buf), f);
|
|
|
|
|
errsv = errno;
|
|
|
|
|
if (ferror(f)) {
|
|
|
|
|
if (do_bzero_mem)
|
|
|
|
|
nm_explicit_bzero(buf, sizeof(buf));
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error(error, errsv, out_errsv, "error during fread");
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
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));
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error(
|
|
|
|
|
error,
|
|
|
|
|
EMSGSIZE,
|
|
|
|
|
out_errsv,
|
|
|
|
|
"file stream too large (%zu+1 bytes with maximum %zu bytes)",
|
2018-08-30 15:13:52 +02:00
|
|
|
(n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read,
|
|
|
|
|
max_length);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (n_have + n_read + 1 >= n_alloc) {
|
|
|
|
|
gsize old_n_alloc = n_alloc;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
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));
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-02-25 10:45:05 +01:00
|
|
|
if (!(str = nm_secret_mem_try_realloc_take(str,
|
|
|
|
|
do_bzero_mem,
|
|
|
|
|
old_n_alloc,
|
|
|
|
|
n_alloc))) {
|
2018-08-30 15:13:52 +02:00
|
|
|
if (do_bzero_mem)
|
|
|
|
|
nm_explicit_bzero(buf, sizeof(buf));
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error(error,
|
|
|
|
|
ENOMEM,
|
|
|
|
|
out_errsv,
|
|
|
|
|
"failure to allocate buffer of %zu bytes",
|
|
|
|
|
n_alloc);
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
|
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
memcpy(str + n_have, buf, n_read);
|
|
|
|
|
n_have += n_read;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (do_bzero_mem)
|
|
|
|
|
nm_explicit_bzero(buf, sizeof(buf));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (n_alloc == 0)
|
|
|
|
|
str = g_new0(char, 1);
|
|
|
|
|
else {
|
|
|
|
|
str[n_have] = '\0';
|
|
|
|
|
if (n_have + 1 < n_alloc) {
|
2020-02-25 10:45:05 +01:00
|
|
|
if (!(str = nm_secret_mem_try_realloc_take(str, do_bzero_mem, n_alloc, n_have + 1)))
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error(error,
|
|
|
|
|
ENOMEM,
|
|
|
|
|
out_errsv,
|
|
|
|
|
"failure to truncate buffer to %zu bytes",
|
|
|
|
|
n_have + 1);
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
|
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
NM_SET_OUT(length, n_have);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
*contents = g_steal_pointer(&str);
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
NM_SET_OUT(out_errsv, 0);
|
|
|
|
|
return TRUE;
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
2019-02-28 14:22:35 +01:00
|
|
|
* the NUL byte. That is, it reads only files up to a length of
|
|
|
|
|
* @max_length - 1 bytes.
|
2018-08-30 15:13:52 +02:00
|
|
|
* @length: optional output argument of the read file size.
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
* @out_errsv: (allow-none) (out): on error, a positive errno. or zero.
|
|
|
|
|
* @error:
|
2018-08-30 15:13:52 +02:00
|
|
|
*
|
|
|
|
|
* A reimplementation of g_file_get_contents() with a few differences:
|
|
|
|
|
* - accepts an @dirfd to open @filename relative to that path via openat().
|
2018-09-30 11:30:18 -03:00
|
|
|
* - limits the maximum filesize to max_length.
|
2018-08-30 15:13:52 +02:00
|
|
|
* - uses O_CLOEXEC on internal file descriptor
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
* - optionally returns the native errno on failure.
|
2018-08-30 15:13:52 +02:00
|
|
|
*
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
* Returns: TRUE on success.
|
2018-08-30 15:13:52 +02:00
|
|
|
*/
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
gboolean
|
2018-08-30 15:13:52 +02:00
|
|
|
nm_utils_file_get_contents(int dirfd,
|
|
|
|
|
const char * filename,
|
|
|
|
|
gsize max_length,
|
|
|
|
|
NMUtilsFileGetContentsFlags flags,
|
|
|
|
|
char ** contents,
|
|
|
|
|
gsize * length,
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
int * out_errsv,
|
2018-08-30 15:13:52 +02:00
|
|
|
GError ** error)
|
|
|
|
|
{
|
|
|
|
|
int fd;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
g_return_val_if_fail(filename && filename[0], FALSE);
|
|
|
|
|
g_return_val_if_fail(contents && !*contents, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
NM_SET_OUT(length, 0);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (dirfd >= 0) {
|
|
|
|
|
fd = openat(dirfd, filename, O_RDONLY | O_CLOEXEC);
|
|
|
|
|
if (fd < 0) {
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error_errno(error,
|
|
|
|
|
out_errsv,
|
|
|
|
|
"Failed to open file \"%s\" with openat",
|
|
|
|
|
filename);
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
fd = open(filename, O_RDONLY | O_CLOEXEC);
|
|
|
|
|
if (fd < 0) {
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error_errno(error,
|
|
|
|
|
out_errsv,
|
|
|
|
|
"Failed to open file \"%s\"",
|
|
|
|
|
filename);
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nm_utils_fd_get_contents(fd,
|
|
|
|
|
TRUE,
|
|
|
|
|
max_length,
|
|
|
|
|
flags,
|
|
|
|
|
contents,
|
|
|
|
|
length,
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
out_errsv,
|
2018-08-30 15:13:52 +02:00
|
|
|
error);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-30 15:00:39 +02:00
|
|
|
/*****************************************************************************/
|
2018-08-30 15:13:52 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Copied from GLib's g_file_set_contents() et al., but allows
|
|
|
|
|
* specifying a mode for the new file.
|
|
|
|
|
*/
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
gboolean
|
2018-08-30 15:13:52 +02:00
|
|
|
nm_utils_file_set_contents(const char *filename,
|
|
|
|
|
const char *contents,
|
|
|
|
|
gssize length,
|
|
|
|
|
mode_t mode,
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
int * out_errsv,
|
2018-08-30 15:13:52 +02:00
|
|
|
GError ** error)
|
|
|
|
|
{
|
|
|
|
|
gs_free char *tmp_name = NULL;
|
|
|
|
|
struct stat statbuf;
|
|
|
|
|
int errsv;
|
|
|
|
|
gssize s;
|
|
|
|
|
int fd;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
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);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (length == -1)
|
|
|
|
|
length = strlen(contents);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
tmp_name = g_strdup_printf("%s.XXXXXX", filename);
|
2018-09-12 16:31:59 +02:00
|
|
|
fd = g_mkstemp_full(tmp_name, O_RDWR | O_CLOEXEC, mode);
|
2018-08-30 15:13:52 +02:00
|
|
|
if (fd < 0) {
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error_errno(error, out_errsv, "failed to create file %s", tmp_name);
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
while (length > 0) {
|
|
|
|
|
s = write(fd, contents, length);
|
|
|
|
|
if (s < 0) {
|
2019-08-01 10:59:36 +02:00
|
|
|
errsv = NM_ERRNO_NATIVE(errno);
|
2018-08-30 15:13:52 +02:00
|
|
|
if (errsv == EINTR)
|
|
|
|
|
continue;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
nm_close(fd);
|
|
|
|
|
unlink(tmp_name);
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error(error,
|
|
|
|
|
errsv,
|
|
|
|
|
out_errsv,
|
|
|
|
|
"failed to write to file %s",
|
|
|
|
|
tmp_name);
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
g_assert(s <= length);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
contents += s;
|
|
|
|
|
length -= s;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
/* 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
|
2020-07-01 17:20:40 -04:00
|
|
|
* the destination. Otherwise, if we get a system crash we can lose both
|
2018-08-30 15:13:52 +02:00
|
|
|
* 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) {
|
2019-01-31 13:29:21 +01:00
|
|
|
if (fsync(fd) != 0) {
|
2019-08-01 10:59:36 +02:00
|
|
|
errsv = NM_ERRNO_NATIVE(errno);
|
2019-01-31 13:29:21 +01:00
|
|
|
nm_close(fd);
|
|
|
|
|
unlink(tmp_name);
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error(error, errsv, out_errsv, "failed to fsync %s", tmp_name);
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
2019-01-31 13:29:21 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
nm_close(fd);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-30 15:13:52 +02:00
|
|
|
if (rename(tmp_name, filename)) {
|
2019-08-01 10:59:36 +02:00
|
|
|
errsv = NM_ERRNO_NATIVE(errno);
|
2018-08-30 15:13:52 +02:00
|
|
|
unlink(tmp_name);
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return _get_contents_error(error,
|
|
|
|
|
errsv,
|
|
|
|
|
out_errsv,
|
|
|
|
|
"failed rename %s to %s",
|
|
|
|
|
tmp_name,
|
|
|
|
|
filename);
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
return TRUE;
|
2018-08-30 15:13:52 +02:00
|
|
|
}
|
2019-06-24 09:00:15 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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;
|
|
|
|
|
}
|
2019-12-19 11:30:03 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_fd_read:
|
|
|
|
|
* @fd: the fd to read from.
|
|
|
|
|
* @out_string: (out): output string where read bytes will be stored.
|
|
|
|
|
*
|
cli/polkit: make parsing polkit-agent-helper-1 protocol more conforming
- in io_watch_have_data(), ensure that we handle incomplete lines
that don't yet have a newline by waiting for more data. That means,
if the current content of the in_buffer does not have a newline, we
wait longer.
- in io_watch_have_data(), implement (and ignore) certain commands
instead of failing the request.
- in io_watch_have_data(), no longer g_compress() the entire line.
"polkitagenthelper-pam.c" never backslash escapes the command, it
only escapes the arguments. Of course, there should be no difference
in practice, except that we don't want to handle escape sequences
in the commands.
- in io_watch_have_data(), compare SUCCESS/FAILURE literally.
"polkitagenthelper-pam.c" never appends any trailing garbage to these
commands, and we shouldn't handle that (although "polkitagentsession.c"
does).
- when io_watch_have_data() completes with success, we cannot destroy
AuthRequest right away. It probably still has data pending that we first
need to write to the polkit helper. Wait longer, and let io_watch_can_write()
complete the request.
- ensure we always answer the GDBusMethodInvocation. Otherwise, it gets
leaked.
- use NMStrBuf instead of GString.
2020-04-06 10:54:35 +02:00
|
|
|
* 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)
|
2019-12-19 11:30:03 +01:00
|
|
|
{
|
cli/polkit: make parsing polkit-agent-helper-1 protocol more conforming
- in io_watch_have_data(), ensure that we handle incomplete lines
that don't yet have a newline by waiting for more data. That means,
if the current content of the in_buffer does not have a newline, we
wait longer.
- in io_watch_have_data(), implement (and ignore) certain commands
instead of failing the request.
- in io_watch_have_data(), no longer g_compress() the entire line.
"polkitagenthelper-pam.c" never backslash escapes the command, it
only escapes the arguments. Of course, there should be no difference
in practice, except that we don't want to handle escape sequences
in the commands.
- in io_watch_have_data(), compare SUCCESS/FAILURE literally.
"polkitagenthelper-pam.c" never appends any trailing garbage to these
commands, and we shouldn't handle that (although "polkitagentsession.c"
does).
- when io_watch_have_data() completes with success, we cannot destroy
AuthRequest right away. It probably still has data pending that we first
need to write to the polkit helper. Wait longer, and let io_watch_can_write()
complete the request.
- ensure we always answer the GDBusMethodInvocation. Otherwise, it gets
leaked.
- use NMStrBuf instead of GString.
2020-04-06 10:54:35 +02:00
|
|
|
gsize buf_available;
|
|
|
|
|
gssize n_read;
|
|
|
|
|
int errsv;
|
2019-12-19 11:30:03 +01:00
|
|
|
|
|
|
|
|
g_return_val_if_fail(fd >= 0, -1);
|
|
|
|
|
g_return_val_if_fail(out_string, -1);
|
|
|
|
|
|
cli/polkit: make parsing polkit-agent-helper-1 protocol more conforming
- in io_watch_have_data(), ensure that we handle incomplete lines
that don't yet have a newline by waiting for more data. That means,
if the current content of the in_buffer does not have a newline, we
wait longer.
- in io_watch_have_data(), implement (and ignore) certain commands
instead of failing the request.
- in io_watch_have_data(), no longer g_compress() the entire line.
"polkitagenthelper-pam.c" never backslash escapes the command, it
only escapes the arguments. Of course, there should be no difference
in practice, except that we don't want to handle escape sequences
in the commands.
- in io_watch_have_data(), compare SUCCESS/FAILURE literally.
"polkitagenthelper-pam.c" never appends any trailing garbage to these
commands, and we shouldn't handle that (although "polkitagentsession.c"
does).
- when io_watch_have_data() completes with success, we cannot destroy
AuthRequest right away. It probably still has data pending that we first
need to write to the polkit helper. Wait longer, and let io_watch_can_write()
complete the request.
- ensure we always answer the GDBusMethodInvocation. Otherwise, it gets
leaked.
- use NMStrBuf instead of GString.
2020-04-06 10:54:35 +02:00
|
|
|
/* 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);
|
|
|
|
|
}
|
2020-04-06 12:54:30 +02:00
|
|
|
|
cli/polkit: make parsing polkit-agent-helper-1 protocol more conforming
- in io_watch_have_data(), ensure that we handle incomplete lines
that don't yet have a newline by waiting for more data. That means,
if the current content of the in_buffer does not have a newline, we
wait longer.
- in io_watch_have_data(), implement (and ignore) certain commands
instead of failing the request.
- in io_watch_have_data(), no longer g_compress() the entire line.
"polkitagenthelper-pam.c" never backslash escapes the command, it
only escapes the arguments. Of course, there should be no difference
in practice, except that we don't want to handle escape sequences
in the commands.
- in io_watch_have_data(), compare SUCCESS/FAILURE literally.
"polkitagenthelper-pam.c" never appends any trailing garbage to these
commands, and we shouldn't handle that (although "polkitagentsession.c"
does).
- when io_watch_have_data() completes with success, we cannot destroy
AuthRequest right away. It probably still has data pending that we first
need to write to the polkit helper. Wait longer, and let io_watch_can_write()
complete the request.
- ensure we always answer the GDBusMethodInvocation. Otherwise, it gets
leaked.
- use NMStrBuf instead of GString.
2020-04-06 10:54:35 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2020-04-06 12:54:30 +02:00
|
|
|
|
2019-12-19 11:30:03 +01:00
|
|
|
return n_read;
|
|
|
|
|
}
|