New upstream version 2017.9
This commit is contained in:
commit
e5ac0cef85
134 files changed
+3986
-5324
No files matched your search
+1
-1
@@ -17,7 +17,7 @@ Why?
|
||||
----
|
||||
|
||||
There are multiple projects which have a hard dependency on Linux and
|
||||
GLib, such as NetworkManager, ostree, xdg-app, etc. It makes sense
|
||||
GLib, such as NetworkManager, ostree, flatpak, etc. It makes sense
|
||||
for them to be able to share Linux-specific APIs.
|
||||
|
||||
This module also contains some code taken from systemd, which has very
|
||||
|
||||
@@ -67,9 +67,7 @@ glnx_opendirat (int dfd,
|
||||
{
|
||||
int ret = glnx_opendirat_with_errno (dfd, path, follow);
|
||||
if (ret == -1)
|
||||
{
|
||||
return glnx_throw_errno_prefix (error, "openat");
|
||||
}
|
||||
return glnx_throw_errno_prefix (error, "opendir(%s)", path);
|
||||
*out_fd = ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <glnx-backport-autocleanups.h>
|
||||
#include <glnx-macros.h>
|
||||
#include <glnx-errors.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -83,6 +85,34 @@ char *glnx_fdrel_abspath (int dfd,
|
||||
|
||||
void glnx_gen_temp_name (gchar *tmpl);
|
||||
|
||||
/**
|
||||
* glnx_ensure_dir:
|
||||
* @dfd: directory fd
|
||||
* @path: Directory path
|
||||
* @mode: Mode
|
||||
* @error: Return location for a #GError, or %NULL
|
||||
*
|
||||
* Wrapper around mkdirat() which ignores adds #GError support, ensures that
|
||||
* it retries on %EINTR, and also ignores `EEXIST`.
|
||||
*
|
||||
* See also `glnx_shutil_mkdir_p_at()` for recursive handling.
|
||||
*
|
||||
* Returns: %TRUE on success, %FALSE otherwise
|
||||
*/
|
||||
static inline gboolean
|
||||
glnx_ensure_dir (int dfd,
|
||||
const char *path,
|
||||
mode_t mode,
|
||||
GError **error)
|
||||
{
|
||||
if (TEMP_FAILURE_RETRY (mkdirat (dfd, path, mode)) != 0)
|
||||
{
|
||||
if (G_UNLIKELY (errno != EEXIST))
|
||||
return glnx_throw_errno_prefix (error, "mkdirat(%s)", path);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean glnx_mkdtempat (int dfd,
|
||||
gchar *tmpl,
|
||||
int mode,
|
||||
|
||||
@@ -84,6 +84,38 @@ glnx_prefix_error (GError **error, const char *fmt, ...)
|
||||
#define glnx_prefix_error_null(error, args...) \
|
||||
({glnx_prefix_error (error, args); NULL;})
|
||||
|
||||
/**
|
||||
* GLNX_AUTO_PREFIX_ERROR:
|
||||
*
|
||||
* An autocleanup-based macro to automatically call `g_prefix_error()` (also with a colon+space `: `)
|
||||
* when it goes out of scope. This is useful when one wants error strings built up by the callee
|
||||
* function, not all callers.
|
||||
*
|
||||
* ```
|
||||
* gboolean start_http_request (..., GError **error)
|
||||
* {
|
||||
* GLNX_AUTO_PREFIX_ERROR ("HTTP request", error)
|
||||
*
|
||||
* if (!libhttp_request_start (..., error))
|
||||
* return FALSE;
|
||||
* ...
|
||||
* return TRUE;
|
||||
* ```
|
||||
*/
|
||||
typedef struct {
|
||||
const char *prefix;
|
||||
GError **error;
|
||||
} GLnxAutoErrorPrefix;
|
||||
static inline void
|
||||
glnx_cleanup_auto_prefix_error (GLnxAutoErrorPrefix *prefix)
|
||||
{
|
||||
if (prefix->error && *(prefix->error))
|
||||
g_prefix_error (prefix->error, "%s: ", prefix->prefix);
|
||||
}
|
||||
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxAutoErrorPrefix, glnx_cleanup_auto_prefix_error)
|
||||
#define GLNX_AUTO_PREFIX_ERROR(text, error) \
|
||||
G_GNUC_UNUSED g_auto(GLnxAutoErrorPrefix) _GLNX_MAKE_ANONYMOUS(_glnxautoprefixerror_) = { text, error }
|
||||
|
||||
/* Set @error using the value of `g_strerror (errno)`.
|
||||
*
|
||||
* This function returns %FALSE so it can be used conveniently in a single
|
||||
|
||||
+54
-40
@@ -53,6 +53,15 @@
|
||||
sizeof(type) <= 4 ? 10 : \
|
||||
sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
|
||||
|
||||
gboolean
|
||||
glnx_stdio_file_flush (FILE *f, GError **error)
|
||||
{
|
||||
if (fflush (f) != 0)
|
||||
return glnx_throw_errno_prefix (error, "fflush");
|
||||
if (ferror (f) != 0)
|
||||
return glnx_throw_errno_prefix (error, "ferror");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* An implementation of renameat2(..., RENAME_NOREPLACE)
|
||||
* with fallback to a non-atomic version.
|
||||
@@ -169,13 +178,12 @@ glnx_tmpfile_clear (GLnxTmpfile *tmpf)
|
||||
tmpf->initialized = FALSE;
|
||||
}
|
||||
|
||||
/* Allocate a temporary file, using Linux O_TMPFILE if available.
|
||||
/* Allocate a temporary file, using Linux O_TMPFILE if available. The file mode
|
||||
* will be 0600.
|
||||
*
|
||||
* The result will be stored in @out_tmpf, which is caller allocated
|
||||
* so you can store it on the stack in common scenarios.
|
||||
*
|
||||
* Note that with O_TMPFILE, the file mode will be `000`; you likely
|
||||
* want to chmod it before calling glnx_link_tmpfile_at().
|
||||
*
|
||||
* The directory fd @dfd must live at least as long as the output @out_tmpf.
|
||||
*/
|
||||
gboolean
|
||||
@@ -185,6 +193,7 @@ glnx_open_tmpfile_linkable_at (int dfd,
|
||||
GLnxTmpfile *out_tmpf,
|
||||
GError **error)
|
||||
{
|
||||
const guint mode = 0600;
|
||||
glnx_fd_close int fd = -1;
|
||||
int count;
|
||||
|
||||
@@ -200,11 +209,16 @@ glnx_open_tmpfile_linkable_at (int dfd,
|
||||
* link_tmpfile() below to rename the result after writing the file
|
||||
* in full. */
|
||||
#if defined(O_TMPFILE) && !defined(DISABLE_OTMPFILE) && !defined(ENABLE_WRPSEUDO_COMPAT)
|
||||
fd = openat (dfd, subpath, O_TMPFILE|flags, 0600);
|
||||
fd = openat (dfd, subpath, O_TMPFILE|flags, mode);
|
||||
if (fd == -1 && !(G_IN_SET(errno, ENOSYS, EISDIR, EOPNOTSUPP)))
|
||||
return glnx_throw_errno_prefix (error, "open(O_TMPFILE)");
|
||||
if (fd != -1)
|
||||
{
|
||||
/* Workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17523
|
||||
* See also https://github.com/ostreedev/ostree/issues/991
|
||||
*/
|
||||
if (fchmod (fd, mode) < 0)
|
||||
return glnx_throw_errno_prefix (error, "fchmod");
|
||||
out_tmpf->initialized = TRUE;
|
||||
out_tmpf->src_dfd = dfd; /* Copied; caller must keep open */
|
||||
out_tmpf->fd = glnx_steal_fd (&fd);
|
||||
@@ -221,7 +235,7 @@ glnx_open_tmpfile_linkable_at (int dfd,
|
||||
{
|
||||
glnx_gen_temp_name (tmp);
|
||||
|
||||
fd = openat (dfd, tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0600);
|
||||
fd = openat (dfd, tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, mode);
|
||||
if (fd < 0)
|
||||
{
|
||||
if (errno == EEXIST)
|
||||
@@ -373,6 +387,35 @@ glnx_link_tmpfile_at (GLnxTmpfile *tmpf,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* glnx_openat_rdonly:
|
||||
* @dfd: File descriptor for origin directory
|
||||
* @path: Pathname, relative to @dfd
|
||||
* @follow: Whether or not to follow symbolic links in the final component
|
||||
* @out_fd: (out): File descriptor
|
||||
* @error: Error
|
||||
*
|
||||
* Use openat() to open a file, with flags `O_RDONLY | O_CLOEXEC | O_NOCTTY`.
|
||||
* Like the other libglnx wrappers, will use `TEMP_FAILURE_RETRY` and
|
||||
* also includes @path in @error in case of failure.
|
||||
*/
|
||||
gboolean
|
||||
glnx_openat_rdonly (int dfd,
|
||||
const char *path,
|
||||
gboolean follow,
|
||||
int *out_fd,
|
||||
GError **error)
|
||||
{
|
||||
int flags = O_RDONLY | O_CLOEXEC | O_NOCTTY;
|
||||
if (!follow)
|
||||
flags |= O_NOFOLLOW;
|
||||
int fd = TEMP_FAILURE_RETRY (openat (dfd, path, flags));
|
||||
if (fd == -1)
|
||||
return glnx_throw_errno_prefix (error, "openat(%s)", path);
|
||||
*out_fd = fd;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static guint8*
|
||||
glnx_fd_readall_malloc (int fd,
|
||||
gsize *out_len,
|
||||
@@ -510,9 +553,9 @@ glnx_file_get_contents_utf8_at (int dfd,
|
||||
{
|
||||
dfd = glnx_dirfd_canonicalize (dfd);
|
||||
|
||||
glnx_fd_close int fd = TEMP_FAILURE_RETRY (openat (dfd, subpath, O_RDONLY | O_NOCTTY | O_CLOEXEC));
|
||||
if (G_UNLIKELY (fd == -1))
|
||||
return glnx_null_throw_errno_prefix (error, "open(%s)", subpath);
|
||||
glnx_fd_close int fd = -1;
|
||||
if (!glnx_openat_rdonly (dfd, subpath, TRUE, &fd, error))
|
||||
return NULL;
|
||||
|
||||
gsize len;
|
||||
g_autofree char *buf = glnx_fd_readall_utf8 (fd, &len, cancellable, error);
|
||||
@@ -808,12 +851,8 @@ glnx_file_copy_at (int src_dfd,
|
||||
goto out;
|
||||
}
|
||||
|
||||
src_fd = TEMP_FAILURE_RETRY (openat (src_dfd, src_subpath, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW));
|
||||
if (src_fd == -1)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
if (!glnx_openat_rdonly (src_dfd, src_subpath, FALSE, &src_fd, error))
|
||||
goto out;
|
||||
|
||||
dest_open_flags = O_WRONLY | O_CREAT | O_CLOEXEC | O_NOCTTY;
|
||||
if (!(copyflags & GLNX_FILE_COPY_OVERWRITE))
|
||||
@@ -1008,28 +1047,3 @@ glnx_file_replace_contents_with_perms_at (int dfd,
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* glnx_stream_fstat:
|
||||
* @stream: A stream containing a Unix file descriptor
|
||||
* @stbuf: Memory location to write stat buffer
|
||||
* @error:
|
||||
*
|
||||
* Some streams created via libgsystem are #GUnixInputStream; these do
|
||||
* not support e.g. g_file_input_stream_query_info(). This function
|
||||
* allows dropping to the raw unix fstat() call for these types of
|
||||
* streams, while still conveniently wrapped with the normal GLib
|
||||
* handling of @error.
|
||||
*/
|
||||
gboolean
|
||||
glnx_stream_fstat (GFileDescriptorBased *stream,
|
||||
struct stat *stbuf,
|
||||
GError **error)
|
||||
{
|
||||
int fd = g_file_descriptor_based_get_fd (stream);
|
||||
|
||||
if (fstat (fd, stbuf) == -1)
|
||||
return glnx_throw_errno_prefix (error, "fstat");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
+46
-6
@@ -50,6 +50,23 @@ const char *glnx_basename (const char *path)
|
||||
return (basename) (path);
|
||||
}
|
||||
|
||||
/* Utilities for standard FILE* */
|
||||
static inline void
|
||||
glnx_stdio_file_cleanup (void *filep)
|
||||
{
|
||||
FILE *f = filep;
|
||||
if (f)
|
||||
fclose (f);
|
||||
}
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FILE, glnx_stdio_file_cleanup)
|
||||
|
||||
/**
|
||||
* glnx_stdio_file_flush:
|
||||
* Call fflush() and check ferror().
|
||||
*/
|
||||
gboolean
|
||||
glnx_stdio_file_flush (FILE *f, GError **error);
|
||||
|
||||
typedef struct {
|
||||
gboolean initialized;
|
||||
gboolean anonymous;
|
||||
@@ -85,6 +102,13 @@ glnx_link_tmpfile_at (GLnxTmpfile *tmpf,
|
||||
const char *target,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
glnx_openat_rdonly (int dfd,
|
||||
const char *path,
|
||||
gboolean follow,
|
||||
int *out_fd,
|
||||
GError **error);
|
||||
|
||||
GBytes *
|
||||
glnx_fd_readall_bytes (int fd,
|
||||
GCancellable *cancellable,
|
||||
@@ -164,11 +188,6 @@ glnx_file_copy_at (int src_dfd,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
glnx_stream_fstat (GFileDescriptorBased *stream,
|
||||
struct stat *stbuf,
|
||||
GError **error);
|
||||
|
||||
int glnx_renameat2_noreplace (int olddirfd, const char *oldpath,
|
||||
int newdirfd, const char *newpath);
|
||||
int glnx_renameat2_exchange (int olddirfd, const char *oldpath,
|
||||
@@ -224,8 +243,29 @@ glnx_fstat (int fd,
|
||||
GError **error)
|
||||
{
|
||||
if (TEMP_FAILURE_RETRY (fstat (fd, buf)) != 0)
|
||||
return glnx_throw_errno (error);
|
||||
return glnx_throw_errno_prefix (error, "fstat");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* glnx_fchmod:
|
||||
* @fd: FD
|
||||
* @mode: Mode
|
||||
* @error: Return location for a #GError, or %NULL
|
||||
*
|
||||
* Wrapper around fchmod() which adds #GError support and ensures that it
|
||||
* retries on %EINTR.
|
||||
*
|
||||
* Returns: %TRUE on success, %FALSE otherwise
|
||||
* Since: UNRELEASED
|
||||
*/
|
||||
static inline gboolean
|
||||
glnx_fchmod (int fd,
|
||||
mode_t mode,
|
||||
GError **error)
|
||||
{
|
||||
if (TEMP_FAILURE_RETRY (fchmod (fd, mode)) != 0)
|
||||
return glnx_throw_errno_prefix (error, "fchmod");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -185,6 +185,8 @@ mkdir_p_at_internal (int dfd,
|
||||
*
|
||||
* Similar to g_mkdir_with_parents(), except operates relative to the
|
||||
* directory fd @dfd.
|
||||
*
|
||||
* See also glnx_ensure_dir() for a non-recursive version.
|
||||
*/
|
||||
gboolean
|
||||
glnx_shutil_mkdir_p_at (int dfd,
|
||||
|
||||
@@ -125,6 +125,48 @@ test_error_errno (void)
|
||||
g_assert_cmpint (fd, ==, -1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_error_auto_nothrow (GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR("foo", error);
|
||||
/* Side effect to avoid otherwise empty function */
|
||||
g_assert_no_error (*error);
|
||||
}
|
||||
|
||||
static void
|
||||
test_error_auto_throw (GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR("foo", error);
|
||||
(void) glnx_throw (error, "oops");
|
||||
}
|
||||
|
||||
static void
|
||||
test_error_auto_throw_recurse (GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR("foo", error);
|
||||
|
||||
if (TRUE)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR("bar", error);
|
||||
(void) glnx_throw (error, "oops");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_error_auto (void)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
test_error_auto_nothrow (&error);
|
||||
g_assert_no_error (error);
|
||||
test_error_auto_throw (&error);
|
||||
g_assert_nonnull (error);
|
||||
g_assert_cmpstr (error->message, ==, "foo: oops");
|
||||
g_clear_error (&error);
|
||||
test_error_auto_throw_recurse (&error);
|
||||
g_assert_nonnull (error);
|
||||
g_assert_cmpstr (error->message, ==, "foo: bar: oops");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
@@ -133,6 +175,7 @@ int main (int argc, char **argv)
|
||||
|
||||
g_test_add_func ("/error-throw", test_error_throw);
|
||||
g_test_add_func ("/error-errno", test_error_errno);
|
||||
g_test_add_func ("/error-auto", test_error_auto);
|
||||
|
||||
ret = g_test_run();
|
||||
|
||||
|
||||
@@ -137,12 +137,66 @@ test_renameat2_exchange (void)
|
||||
g_assert_no_error (local_error);
|
||||
}
|
||||
|
||||
static void
|
||||
test_tmpfile (void)
|
||||
{
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
GError **error = &local_error;
|
||||
g_auto(GLnxTmpfile) tmpf = { 0, };
|
||||
|
||||
if (!glnx_open_tmpfile_linkable_at (AT_FDCWD, ".", O_WRONLY|O_CLOEXEC, &tmpf, error))
|
||||
goto out;
|
||||
|
||||
if (glnx_loop_write (tmpf.fd, "foo", strlen ("foo")) < 0)
|
||||
{
|
||||
(void)glnx_throw_errno_prefix (error, "write");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_NOREPLACE, AT_FDCWD, "foo", error))
|
||||
goto out;
|
||||
|
||||
out:
|
||||
g_assert_no_error (local_error);
|
||||
}
|
||||
|
||||
static void
|
||||
test_stdio_file (void)
|
||||
{
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
GError **error = &local_error;
|
||||
g_auto(GLnxTmpfile) tmpf = { 0, };
|
||||
g_autoptr(FILE) f = NULL;
|
||||
|
||||
if (!glnx_open_anonymous_tmpfile (O_RDWR|O_CLOEXEC, &tmpf, error))
|
||||
goto out;
|
||||
f = fdopen (tmpf.fd, "w");
|
||||
if (!f)
|
||||
{
|
||||
(void)glnx_throw_errno_prefix (error, "fdopen");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fwrite ("hello", 1, strlen ("hello"), f) != strlen ("hello"))
|
||||
{
|
||||
(void)glnx_throw_errno_prefix (error, "fwrite");
|
||||
goto out;
|
||||
}
|
||||
if (!glnx_stdio_file_flush (f, error))
|
||||
goto out;
|
||||
|
||||
out:
|
||||
g_assert_no_error (local_error);
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/tmpfile", test_tmpfile);
|
||||
g_test_add_func ("/stdio-file", test_stdio_file);
|
||||
g_test_add_func ("/renameat2-noreplace", test_renameat2_noreplace);
|
||||
g_test_add_func ("/renameat2-exchange", test_renameat2_exchange);
|
||||
|
||||
|
||||
@@ -107,22 +107,17 @@ do_write_run (GLnxDirFdIterator *dfd_iter, GError **error)
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
g_autoptr(GVariant) current_xattrs = NULL;
|
||||
glnx_fd_close int fd = -1;
|
||||
|
||||
struct dirent *dent;
|
||||
if (!glnx_dirfd_iterator_next_dent (dfd_iter, &dent, NULL, error))
|
||||
return FALSE;
|
||||
if (!dent)
|
||||
break;
|
||||
|
||||
fd = openat (dfd_iter->fd, dent->d_name, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
return FALSE;
|
||||
}
|
||||
glnx_fd_close int fd = -1;
|
||||
if (!glnx_openat_rdonly (dfd_iter->fd, dent->d_name, FALSE, &fd, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) current_xattrs = NULL;
|
||||
if (!glnx_fd_get_all_xattrs (fd, ¤t_xattrs, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
@@ -156,22 +151,17 @@ do_read_run (GLnxDirFdIterator *dfd_iter,
|
||||
guint nattrs = 0;
|
||||
while (TRUE)
|
||||
{
|
||||
g_autoptr(GVariant) current_xattrs = NULL;
|
||||
glnx_fd_close int fd = -1;
|
||||
|
||||
struct dirent *dent;
|
||||
if (!glnx_dirfd_iterator_next_dent (dfd_iter, &dent, NULL, error))
|
||||
return FALSE;
|
||||
if (!dent)
|
||||
break;
|
||||
|
||||
fd = openat (dfd_iter->fd, dent->d_name, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
return FALSE;
|
||||
}
|
||||
glnx_fd_close int fd = -1;
|
||||
if (!glnx_openat_rdonly (dfd_iter->fd, dent->d_name, FALSE, &fd, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) current_xattrs = NULL;
|
||||
if (!glnx_fd_get_all_xattrs (fd, ¤t_xattrs, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
|
||||
Reference in new issue
Block a user