New upstream version 2017.8

This commit is contained in:
Simon McVittie committed 2017-07-18 22:18:02 +01:00
commit b6c0af922c
158 files changed
+12692 -2420

No files matched your search

+6 -2
View File
@@ -22,7 +22,7 @@ EXTRA_DIST += \
$(NULL)
libglnx_la_SOURCES = \
$(libglnx_srcpath)/glnx-alloca.h \
$(libglnx_srcpath)/glnx-macros.h \
$(libglnx_srcpath)/glnx-backport-autocleanups.h \
$(libglnx_srcpath)/glnx-backport-autoptr.h \
$(libglnx_srcpath)/glnx-backports.h \
@@ -52,7 +52,7 @@ libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags)
libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic
libglnx_la_LIBADD = $(libglnx_libs)
libglnx_tests = test-libglnx-xattrs test-libglnx-fdio test-libglnx-errors
libglnx_tests = test-libglnx-xattrs test-libglnx-fdio test-libglnx-errors test-libglnx-macros
TESTS += $(libglnx_tests)
check_PROGRAMS += $(libglnx_tests)
@@ -67,3 +67,7 @@ test_libglnx_fdio_LDADD = $(libglnx_libs) libglnx.la
test_libglnx_errors_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-errors.c
test_libglnx_errors_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags)
test_libglnx_errors_LDADD = $(libglnx_libs) libglnx.la
test_libglnx_macros_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-macros.c
test_libglnx_macros_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags)
test_libglnx_macros_LDADD = $(libglnx_libs) libglnx.la
+6 -2
View File
@@ -22,7 +22,7 @@ EXTRA_DIST += \
$(NULL)
libglnx_la_SOURCES = \
libglnx/glnx-alloca.h \
libglnx/glnx-macros.h \
libglnx/glnx-backport-autocleanups.h \
libglnx/glnx-backport-autoptr.h \
libglnx/glnx-backports.h \
@@ -52,7 +52,7 @@ libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags)
libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic
libglnx_la_LIBADD = $(libglnx_libs)
libglnx_tests = test-libglnx-xattrs test-libglnx-fdio test-libglnx-errors
libglnx_tests = test-libglnx-xattrs test-libglnx-fdio test-libglnx-errors test-libglnx-macros
TESTS += $(libglnx_tests)
check_PROGRAMS += $(libglnx_tests)
@@ -67,3 +67,7 @@ test_libglnx_fdio_LDADD = $(libglnx_libs) libglnx.la
test_libglnx_errors_SOURCES = libglnx/tests/test-libglnx-errors.c
test_libglnx_errors_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags)
test_libglnx_errors_LDADD = $(libglnx_libs) libglnx.la
test_libglnx_macros_SOURCES = libglnx/tests/test-libglnx-macros.c
test_libglnx_macros_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags)
test_libglnx_macros_LDADD = $(libglnx_libs) libglnx.la
-47
View File
@@ -1,47 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#pragma once
#include <stdlib.h>
#include <glib.h>
G_BEGIN_DECLS
/* Taken from https://github.com/systemd/systemd/src/basic/string-util.h
* at revision v228-666-gcf6c8c4
*/
#define glnx_strjoina(a, ...) \
({ \
const char *_appendees_[] = { a, __VA_ARGS__ }; \
char *_d_, *_p_; \
int _len_ = 0; \
unsigned _i_; \
for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \
_len_ += strlen(_appendees_[_i_]); \
_p_ = _d_ = alloca(_len_ + 1); \
for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \
_p_ = stpcpy(_p_, _appendees_[_i_]); \
*_p_ = 0; \
_d_; \
})
G_END_DECLS
+13 -32
View File
@@ -99,24 +99,19 @@ glnx_dirfd_iterator_init_at (int dfd,
GLnxDirFdIterator *out_dfd_iter,
GError **error)
{
gboolean ret = FALSE;
glnx_fd_close int fd = -1;
if (!glnx_opendirat (dfd, path, follow, &fd, error))
goto out;
return FALSE;
if (!glnx_dirfd_iterator_init_take_fd (fd, out_dfd_iter, error))
goto out;
fd = -1; /* Transfer ownership */
if (!glnx_dirfd_iterator_init_take_fd (&fd, out_dfd_iter, error))
return FALSE;
ret = TRUE;
out:
return ret;
return TRUE;
}
/**
* glnx_dirfd_iterator_init_take_fd:
* @dfd: File descriptor - ownership is taken
* @dfd: File descriptor - ownership is taken, and the value is set to -1
* @dfd_iter: A directory iterator
* @error: Error
*
@@ -124,28 +119,20 @@ glnx_dirfd_iterator_init_at (int dfd,
* iteration.
*/
gboolean
glnx_dirfd_iterator_init_take_fd (int dfd,
glnx_dirfd_iterator_init_take_fd (int *dfd,
GLnxDirFdIterator *dfd_iter,
GError **error)
{
gboolean ret = FALSE;
GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter;
DIR *d = NULL;
d = fdopendir (dfd);
DIR *d = fdopendir (*dfd);
if (!d)
{
glnx_set_prefix_error_from_errno (error, "%s", "fdopendir");
goto out;
}
return glnx_throw_errno_prefix (error, "fdopendir");
real_dfd_iter->fd = dfd;
real_dfd_iter->fd = glnx_steal_fd (dfd);
real_dfd_iter->d = d;
real_dfd_iter->initialized = TRUE;
ret = TRUE;
out:
return ret;
return TRUE;
}
/**
@@ -165,31 +152,25 @@ glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter;
g_return_val_if_fail (out_dent, FALSE);
g_return_val_if_fail (dfd_iter->initialized, FALSE);
if (g_cancellable_set_error_if_cancelled (cancellable, error))
goto out;
return FALSE;
do
{
errno = 0;
*out_dent = readdir (real_dfd_iter->d);
if (*out_dent == NULL && errno != 0)
{
glnx_set_prefix_error_from_errno (error, "%s", "fdopendir");
goto out;
}
return glnx_throw_errno_prefix (error, "readdir");
} while (*out_dent &&
(strcmp ((*out_dent)->d_name, ".") == 0 ||
strcmp ((*out_dent)->d_name, "..") == 0));
ret = TRUE;
out:
return ret;
return TRUE;
}
/**
+1 -1
View File
@@ -55,7 +55,7 @@ typedef struct GLnxDirFdIterator GLnxDirFdIterator;
gboolean glnx_dirfd_iterator_init_at (int dfd, const char *path,
gboolean follow,
GLnxDirFdIterator *dfd_iter, GError **error);
gboolean glnx_dirfd_iterator_init_take_fd (int dfd, GLnxDirFdIterator *dfd_iter, GError **error);
gboolean glnx_dirfd_iterator_init_take_fd (int *dfd, GLnxDirFdIterator *dfd_iter, GError **error);
gboolean glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter,
struct dirent **out_dent,
GCancellable *cancellable,
+98 -57
View File
@@ -37,7 +37,6 @@
#include <glnx-fdio.h>
#include <glnx-dirfd.h>
#include <glnx-alloca.h>
#include <glnx-errors.h>
#include <glnx-xattrs.h>
#include <glnx-backport-autoptr.h>
@@ -65,7 +64,7 @@ glnx_renameat2_noreplace (int olddirfd, const char *oldpath,
#ifndef ENABLE_WRPSEUDO_COMPAT
if (renameat2 (olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) < 0)
{
if (errno == EINVAL || errno == ENOSYS)
if (G_IN_SET(errno, EINVAL, ENOSYS))
{
/* Fall through */
}
@@ -119,7 +118,7 @@ glnx_renameat2_exchange (int olddirfd, const char *oldpath,
return 0;
else
{
if (errno == ENOSYS || errno == EINVAL)
if (G_IN_SET(errno, ENOSYS, EINVAL))
{
/* Fall through */
}
@@ -146,17 +145,51 @@ glnx_renameat2_exchange (int olddirfd, const char *oldpath,
return 0;
}
/* Deallocate a tmpfile, closing the fd and deleting the path, if any. This is
* normally called by default by the autocleanup attribute, but you can also
* invoke this directly.
*/
void
glnx_tmpfile_clear (GLnxTmpfile *tmpf)
{
/* Support being passed NULL so we work nicely in a GPtrArray */
if (!tmpf)
return;
if (!tmpf->initialized)
return;
if (tmpf->fd == -1)
return;
(void) close (tmpf->fd);
/* If ->path is set, we're likely aborting due to an error. Clean it up */
if (tmpf->path)
{
(void) unlinkat (tmpf->src_dfd, tmpf->path, 0);
g_free (tmpf->path);
}
tmpf->initialized = FALSE;
}
/* Allocate a temporary file, using Linux O_TMPFILE if available.
* 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
glnx_open_tmpfile_linkable_at (int dfd,
const char *subpath,
int flags,
int *out_fd,
char **out_path,
GLnxTmpfile *out_tmpf,
GError **error)
{
glnx_fd_close int fd = -1;
int count;
dfd = glnx_dirfd_canonicalize (dfd);
/* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
g_return_val_if_fail ((flags & O_EXCL) == 0, FALSE);
@@ -168,13 +201,14 @@ glnx_open_tmpfile_linkable_at (int dfd,
* in full. */
#if defined(O_TMPFILE) && !defined(DISABLE_OTMPFILE) && !defined(ENABLE_WRPSEUDO_COMPAT)
fd = openat (dfd, subpath, O_TMPFILE|flags, 0600);
if (fd == -1 && !(errno == ENOSYS || errno == EISDIR || errno == EOPNOTSUPP))
if (fd == -1 && !(G_IN_SET(errno, ENOSYS, EISDIR, EOPNOTSUPP)))
return glnx_throw_errno_prefix (error, "open(O_TMPFILE)");
if (fd != -1)
{
*out_fd = fd;
fd = -1;
*out_path = NULL;
out_tmpf->initialized = TRUE;
out_tmpf->src_dfd = dfd; /* Copied; caller must keep open */
out_tmpf->fd = glnx_steal_fd (&fd);
out_tmpf->path = NULL;
return TRUE;
}
/* Fallthrough */
@@ -182,7 +216,7 @@ glnx_open_tmpfile_linkable_at (int dfd,
{ g_autofree char *tmp = g_strconcat (subpath, "/tmp.XXXXXX", NULL);
const guint count_max = 100;
for (count = 0; count < count_max; count++)
{
glnx_gen_temp_name (tmp);
@@ -197,9 +231,10 @@ glnx_open_tmpfile_linkable_at (int dfd,
}
else
{
*out_fd = fd;
fd = -1;
*out_path = g_steal_pointer (&tmp);
out_tmpf->initialized = TRUE;
out_tmpf->src_dfd = dfd; /* Copied; caller must keep open */
out_tmpf->fd = glnx_steal_fd (&fd);
out_tmpf->path = g_steal_pointer (&tmp);
return TRUE;
}
}
@@ -209,11 +244,33 @@ glnx_open_tmpfile_linkable_at (int dfd,
return FALSE;
}
/* A variant of `glnx_open_tmpfile_linkable_at()` which doesn't support linking.
* Useful for true temporary storage. The fd will be allocated in /var/tmp to
* ensure maximum storage space.
*/
gboolean
glnx_link_tmpfile_at (int dfd,
glnx_open_anonymous_tmpfile (int flags,
GLnxTmpfile *out_tmpf,
GError **error)
{
if (!glnx_open_tmpfile_linkable_at (AT_FDCWD, "/var/tmp", flags, out_tmpf, error))
return FALSE;
if (out_tmpf->path)
{
(void) unlinkat (out_tmpf->src_dfd, out_tmpf->path, 0);
g_clear_pointer (&out_tmpf->path, g_free);
}
out_tmpf->anonymous = TRUE;
out_tmpf->src_dfd = -1;
return TRUE;
}
/* Use this after calling glnx_open_tmpfile_linkable_at() to give
* the file its final name (link into place).
*/
gboolean
glnx_link_tmpfile_at (GLnxTmpfile *tmpf,
GLnxLinkTmpfileReplaceMode mode,
int fd,
const char *tmpfile_path,
int target_dfd,
const char *target,
GError **error)
@@ -221,46 +278,42 @@ glnx_link_tmpfile_at (int dfd,
const gboolean replace = (mode == GLNX_LINK_TMPFILE_REPLACE);
const gboolean ignore_eexist = (mode == GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST);
g_return_val_if_fail (fd >= 0, FALSE);
g_return_val_if_fail (!tmpf->anonymous, FALSE);
g_return_val_if_fail (tmpf->fd >= 0, FALSE);
g_return_val_if_fail (tmpf->src_dfd == AT_FDCWD || tmpf->src_dfd >= 0, FALSE);
/* Unlike the original systemd code, this function also supports
* replacing existing files.
*/
/* We have `tmpfile_path` for old systems without O_TMPFILE. */
if (tmpfile_path)
if (tmpf->path)
{
if (replace)
{
/* We have a regular tempfile, we're overwriting - this is a
* simple renameat().
*/
if (renameat (dfd, tmpfile_path, target_dfd, target) < 0)
{
int errsv = errno;
(void) unlinkat (dfd, tmpfile_path, 0);
errno = errsv;
return glnx_throw_errno_prefix (error, "renameat");
}
if (renameat (tmpf->src_dfd, tmpf->path, target_dfd, target) < 0)
return glnx_throw_errno_prefix (error, "renameat");
}
else
{
/* We need to use renameat2(..., NOREPLACE) or emulate it */
if (!rename_file_noreplace_at (dfd, tmpfile_path, target_dfd, target,
if (!rename_file_noreplace_at (tmpf->src_dfd, tmpf->path, target_dfd, target,
ignore_eexist,
error))
{
(void) unlinkat (dfd, tmpfile_path, 0);
return FALSE;
}
return FALSE;
}
/* Now, clear the pointer so we don't try to unlink it */
g_clear_pointer (&tmpf->path, g_free);
}
else
{
/* This case we have O_TMPFILE, so our reference to it is via /proc/self/fd */
char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(tmpf->fd) + 1];
sprintf (proc_fd_path, "/proc/self/fd/%i", fd);
sprintf (proc_fd_path, "/proc/self/fd/%i", tmpf->fd);
if (replace)
{
@@ -294,16 +347,15 @@ glnx_link_tmpfile_at (int dfd,
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
"Exhausted %u attempts to create temporary file", count);
return FALSE;
}
if (renameat (target_dfd, tmpname_buf, target_dfd, target) < 0)
if (!glnx_renameat (target_dfd, tmpname_buf, target_dfd, target, error))
{
/* This is currently the only case where we need to have
* a cleanup unlinkat() still with O_TMPFILE.
*/
int errsv = errno;
(void) unlinkat (target_dfd, tmpname_buf, 0);
errno = errsv;
return glnx_throw_errno_prefix (error, "renameat");
return FALSE;
}
}
else
@@ -895,7 +947,6 @@ glnx_file_replace_contents_with_perms_at (int dfd,
GCancellable *cancellable,
GError **error)
{
int r;
char *dnbuf = strdupa (subpath);
const char *dn = dirname (dnbuf);
@@ -907,28 +958,18 @@ glnx_file_replace_contents_with_perms_at (int dfd,
if (mode == (mode_t) -1)
mode = 0644;
glnx_fd_close int fd = -1;
g_autofree char *tmpfile_path = NULL;
g_auto(GLnxTmpfile) tmpf = { 0, };
if (!glnx_open_tmpfile_linkable_at (dfd, dn, O_WRONLY | O_CLOEXEC,
&fd, &tmpfile_path,
error))
&tmpf, error))
return FALSE;
if (len == -1)
len = strlen ((char*)buf);
/* Note that posix_fallocate does *not* set errno but returns it. */
if (len > 0)
{
r = posix_fallocate (fd, 0, len);
if (r != 0)
{
errno = r;
return glnx_throw_errno_prefix (error, "fallocate");
}
}
if (!glnx_try_fallocate (tmpf.fd, 0, len, error))
return FALSE;
if (glnx_loop_write (fd, buf, len) < 0)
if (glnx_loop_write (tmpf.fd, buf, len) < 0)
return glnx_throw_errno (error);
if (!(flags & GLNX_FILE_REPLACE_NODATASYNC))
@@ -947,22 +988,22 @@ glnx_file_replace_contents_with_perms_at (int dfd,
if (do_sync)
{
if (fdatasync (fd) != 0)
if (fdatasync (tmpf.fd) != 0)
return glnx_throw_errno_prefix (error, "fdatasync");
}
}
if (uid != (uid_t) -1)
{
if (fchown (fd, uid, gid) != 0)
if (fchown (tmpf.fd, uid, gid) != 0)
return glnx_throw_errno (error);
}
if (fchmod (fd, mode) != 0)
if (fchmod (tmpf.fd, mode) != 0)
return glnx_throw_errno (error);
if (!glnx_link_tmpfile_at (dfd, GLNX_LINK_TMPFILE_REPLACE,
fd, tmpfile_path, dfd, subpath, error))
if (!glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_REPLACE,
dfd, subpath, error))
return FALSE;
return TRUE;
+86 -6
View File
@@ -27,6 +27,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <sys/xattr.h>
/* From systemd/src/shared/util.h */
/* When we include libgen.h because we need dirname() we immediately
@@ -35,6 +36,7 @@
#include <libgen.h>
#undef basename
#include <glnx-macros.h>
#include <glnx-errors.h>
G_BEGIN_DECLS
@@ -48,12 +50,26 @@ const char *glnx_basename (const char *path)
return (basename) (path);
}
typedef struct {
gboolean initialized;
gboolean anonymous;
int src_dfd;
int fd;
char *path;
} GLnxTmpfile;
void glnx_tmpfile_clear (GLnxTmpfile *tmpf);
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxTmpfile, glnx_tmpfile_clear)
gboolean
glnx_open_anonymous_tmpfile (int flags,
GLnxTmpfile *out_tmpf,
GError **error);
gboolean
glnx_open_tmpfile_linkable_at (int dfd,
const char *subpath,
int flags,
int *out_fd,
char **out_path,
GLnxTmpfile *out_tmpf,
GError **error);
typedef enum {
@@ -63,10 +79,8 @@ typedef enum {
} GLnxLinkTmpfileReplaceMode;
gboolean
glnx_link_tmpfile_at (int dfd,
glnx_link_tmpfile_at (GLnxTmpfile *tmpf,
GLnxLinkTmpfileReplaceMode flags,
int fd,
const char *tmpfile_path,
int target_dfd,
const char *target,
GError **error);
@@ -160,6 +174,38 @@ int glnx_renameat2_noreplace (int olddirfd, const char *oldpath,
int glnx_renameat2_exchange (int olddirfd, const char *oldpath,
int newdirfd, const char *newpath);
/**
* glnx_try_fallocate:
* @fd: File descriptor
* @size: Size
* @error: Error
*
* Wrapper for Linux fallocate(). Explicitly ignores a @size of zero.
* Also, will silently do nothing if the underlying filesystem doesn't
* support it. Use this instead of posix_fallocate(), since the glibc fallback
* is bad: https://sourceware.org/bugzilla/show_bug.cgi?id=18515
*/
static inline gboolean
glnx_try_fallocate (int fd,
off_t offset,
off_t size,
GError **error)
{
/* This is just nicer than throwing an error */
if (size == 0)
return TRUE;
if (fallocate (fd, 0, offset, size) < 0)
{
if (G_IN_SET(errno, ENOSYS, EOPNOTSUPP))
; /* Ignore */
else
return glnx_throw_errno_prefix (error, "fallocate");
}
return TRUE;
}
/**
* glnx_fstat:
* @fd: FD to stat
@@ -205,8 +251,42 @@ glnx_fstatat (int dfd,
GError **error)
{
if (TEMP_FAILURE_RETRY (fstatat (dfd, path, buf, flags)) != 0)
return glnx_throw_errno (error);
return glnx_throw_errno_prefix (error, "fstatat(%s)", path);
return TRUE;
}
/**
* glnx_renameat:
*
* Wrapper around renameat() which adds #GError support and ensures that it
* retries on %EINTR.
*/
static inline gboolean
glnx_renameat (int src_dfd,
const gchar *src_path,
int dest_dfd,
const gchar *dest_path,
GError **error)
{
if (TEMP_FAILURE_RETRY (renameat (src_dfd, src_path, dest_dfd, dest_path)) != 0)
return glnx_throw_errno_prefix (error, "renameat(%s, %s)", src_path, dest_path);
return TRUE;
}
/**
* glnx_unlinkat:
*
* Wrapper around unlinkat() which adds #GError support and ensures that it
* retries on %EINTR.
*/
static inline gboolean
glnx_unlinkat (int dfd,
const gchar *path,
int flags,
GError **error)
{
if (TEMP_FAILURE_RETRY (unlinkat (dfd, path, flags)) != 0)
return glnx_throw_errno_prefix (error, "unlinkat(%s)", path);
return TRUE;
}
+189
View File
@@ -0,0 +1,189 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2017 Colin Walters <walters@verbum.org>
* With original source from systemd:
* Copyright 2010 Lennart Poettering
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#pragma once
#include <stdlib.h>
#include <string.h>
#include <gio/gio.h>
G_BEGIN_DECLS
/* All of these are for C only. */
#ifndef __GI_SCANNER__
/* Taken from https://github.com/systemd/systemd/src/basic/string-util.h
* at revision v228-666-gcf6c8c4
*/
#define glnx_strjoina(a, ...) \
({ \
const char *_appendees_[] = { a, __VA_ARGS__ }; \
char *_d_, *_p_; \
int _len_ = 0; \
unsigned _i_; \
for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \
_len_ += strlen(_appendees_[_i_]); \
_p_ = _d_ = alloca(_len_ + 1); \
for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \
_p_ = stpcpy(_p_, _appendees_[_i_]); \
*_p_ = 0; \
_d_; \
})
#ifndef G_IN_SET
/* Infrastructure for `G_IN_SET`; this code is copied from
* systemd's macro.h - please treat that version as canonical
* and submit patches first to systemd.
*/
#define _G_INSET_CASE_F(X) case X:
#define _G_INSET_CASE_F_1(CASE, X) _G_INSET_CASE_F(X)
#define _G_INSET_CASE_F_2(CASE, X, ...) CASE(X) _G_INSET_CASE_F_1(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_3(CASE, X, ...) CASE(X) _G_INSET_CASE_F_2(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_4(CASE, X, ...) CASE(X) _G_INSET_CASE_F_3(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_5(CASE, X, ...) CASE(X) _G_INSET_CASE_F_4(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_6(CASE, X, ...) CASE(X) _G_INSET_CASE_F_5(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_7(CASE, X, ...) CASE(X) _G_INSET_CASE_F_6(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_8(CASE, X, ...) CASE(X) _G_INSET_CASE_F_7(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_9(CASE, X, ...) CASE(X) _G_INSET_CASE_F_8(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_10(CASE, X, ...) CASE(X) _G_INSET_CASE_F_9(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_11(CASE, X, ...) CASE(X) _G_INSET_CASE_F_10(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_12(CASE, X, ...) CASE(X) _G_INSET_CASE_F_11(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_13(CASE, X, ...) CASE(X) _G_INSET_CASE_F_12(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_14(CASE, X, ...) CASE(X) _G_INSET_CASE_F_13(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_15(CASE, X, ...) CASE(X) _G_INSET_CASE_F_14(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_16(CASE, X, ...) CASE(X) _G_INSET_CASE_F_15(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_17(CASE, X, ...) CASE(X) _G_INSET_CASE_F_16(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_18(CASE, X, ...) CASE(X) _G_INSET_CASE_F_17(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_19(CASE, X, ...) CASE(X) _G_INSET_CASE_F_18(CASE, __VA_ARGS__)
#define _G_INSET_CASE_F_20(CASE, X, ...) CASE(X) _G_INSET_CASE_F_19(CASE, __VA_ARGS__)
#define _G_INSET_GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME
#define _G_INSET_FOR_EACH_MAKE_CASE(...) \
_G_INSET_GET_CASE_F(__VA_ARGS__,_G_INSET_CASE_F_20,_G_INSET_CASE_F_19,_G_INSET_CASE_F_18,_G_INSET_CASE_F_17,_G_INSET_CASE_F_16,_G_INSET_CASE_F_15,_G_INSET_CASE_F_14,_G_INSET_CASE_F_13,_G_INSET_CASE_F_12,_G_INSET_CASE_F_11, \
_G_INSET_CASE_F_10,_G_INSET_CASE_F_9,_G_INSET_CASE_F_8,_G_INSET_CASE_F_7,_G_INSET_CASE_F_6,_G_INSET_CASE_F_5,_G_INSET_CASE_F_4,_G_INSET_CASE_F_3,_G_INSET_CASE_F_2,_G_INSET_CASE_F_1) \
(_G_INSET_CASE_F,__VA_ARGS__)
/* Note: claiming the name here even though it isn't upstream yet
* https://bugzilla.gnome.org/show_bug.cgi?id=783751
*/
/**
* G_IN_SET:
* @x: Integer (or smaller) sized value
* @...: Elements to compare
*
* It's quite common to test whether or not `char` values or Unix @errno (among) others
* are members of a small set. Normally one has to choose to either use `if (x == val || x == otherval ...)`
* or a `switch` statement. This macro is useful to reduce duplication in the first case,
* where one can write simply `if (G_IN_SET (x, val, otherval))`, and avoid the verbosity
* that the `switch` statement requires.
*/
#define G_IN_SET(x, ...) \
({ \
gboolean _g_inset_found = FALSE; \
/* If the build breaks in the line below, you need to extend the case macros */ \
static G_GNUC_UNUSED char _static_assert__macros_need_to_be_extended[20 - sizeof((int[]){__VA_ARGS__})/sizeof(int)]; \
switch(x) { \
_G_INSET_FOR_EACH_MAKE_CASE(__VA_ARGS__) \
_g_inset_found = TRUE; \
break; \
default: \
break; \
} \
_g_inset_found; \
})
#endif /* ifndef G_IN_SET */
#define _GLNX_CONCAT(a, b) a##b
#define _GLNX_CONCAT_INDIRECT(a, b) _GLNX_CONCAT(a, b)
#define _GLNX_MAKE_ANONYMOUS(a) _GLNX_CONCAT_INDIRECT(a, __COUNTER__)
#define _GLNX_HASH_TABLE_FOREACH_IMPL_KV(guard, ht, it, kt, k, vt, v) \
gboolean guard = TRUE; \
G_STATIC_ASSERT (sizeof (kt) == sizeof (void*)); \
G_STATIC_ASSERT (sizeof (vt) == sizeof (void*)); \
for (GHashTableIter it; \
guard && ({ g_hash_table_iter_init (&it, ht), TRUE; }); \
guard = FALSE) \
for (kt k; guard; guard = FALSE) \
for (vt v; g_hash_table_iter_next (&it, (gpointer)&k, (gpointer)&v);)
/* Cleaner method to iterate over a GHashTable. I.e. rather than
*
* gpointer k, v;
* GHashTableIter it;
* g_hash_table_iter_init (&it, table);
* while (g_hash_table_iter_next (&it, &k, &v))
* {
* const char *str = k;
* GPtrArray *arr = v;
* ...
* }
*
* you can simply do
*
* GLNX_HASH_TABLE_FOREACH_IT (table, it, const char*, str, GPtrArray*, arr)
* {
* ...
* }
*
* All variables are scoped within the loop. You may use the `it` variable as
* usual, e.g. to remove an element using g_hash_table_iter_remove(&it). There
* are shorter variants for the more common cases where you do not need access
* to the iterator or to keys/values:
*
* GLNX_HASH_TABLE_FOREACH (table, const char*, str) { ... }
* GLNX_HASH_TABLE_FOREACH_V (table, MyData*, data) { ... }
* GLNX_HASH_TABLE_FOREACH_KV (table, const char*, str, MyData*, data) { ... }
*
*/
#define GLNX_HASH_TABLE_FOREACH_IT(ht, it, kt, k, vt, v) \
_GLNX_HASH_TABLE_FOREACH_IMPL_KV( \
_GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_guard_), ht, it, kt, k, vt, v)
/* Variant of GLNX_HASH_TABLE_FOREACH without having to specify an iterator. An
* anonymous iterator will be created. */
#define GLNX_HASH_TABLE_FOREACH_KV(ht, kt, k, vt, v) \
_GLNX_HASH_TABLE_FOREACH_IMPL_KV( \
_GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_guard_), ht, \
_GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_it_), kt, k, vt, v)
/* Variant of GLNX_HASH_TABLE_FOREACH_KV which omits unpacking keys. */
#define GLNX_HASH_TABLE_FOREACH_V(ht, vt, v) \
_GLNX_HASH_TABLE_FOREACH_IMPL_KV( \
_GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_guard_), ht, \
_GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_it_), \
gpointer, _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_v_), \
vt, v)
/* Variant of GLNX_HASH_TABLE_FOREACH_KV which omits unpacking vals. */
#define GLNX_HASH_TABLE_FOREACH(ht, kt, k) \
_GLNX_HASH_TABLE_FOREACH_IMPL_KV( \
_GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_guard_), ht, \
_GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_it_), kt, k, \
gpointer, _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_v_))
#endif /* GI_SCANNER */
G_END_DECLS
+1 -2
View File
@@ -110,9 +110,8 @@ glnx_shutil_rm_rf_at (int dfd,
}
else
{
if (!glnx_dirfd_iterator_init_take_fd (target_dfd, &dfd_iter, error))
if (!glnx_dirfd_iterator_init_take_fd (&target_dfd, &dfd_iter, error))
return FALSE;
target_dfd = -1;
if (!glnx_shutil_rm_rf_children (&dfd_iter, cancellable, error))
return FALSE;
+35 -99
View File
@@ -23,6 +23,7 @@
#include <string.h>
#include <stdio.h>
#include <glnx-macros.h>
#include <glnx-xattrs.h>
#include <glnx-errors.h>
#include <glnx-local-alloc.h>
@@ -249,7 +250,7 @@ glnx_dfd_name_get_all_xattrs (int dfd,
GCancellable *cancellable,
GError **error)
{
if (dfd == AT_FDCWD || dfd == -1)
if (G_IN_SET(dfd, AT_FDCWD, -1))
{
return get_xattrs_impl (name, -1, out_xattrs, cancellable, error);
}
@@ -270,31 +271,22 @@ set_all_xattrs_for_path (const char *path,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
int i, n;
n = g_variant_n_children (xattrs);
for (i = 0; i < n; i++)
const guint n = g_variant_n_children (xattrs);
for (guint i = 0; i < n; i++)
{
const guint8* name;
g_autoptr(GVariant) value = NULL;
const guint8* value_data;
gsize value_len;
g_variant_get_child (xattrs, i, "(^&ay@ay)",
&name, &value);
value_data = g_variant_get_fixed_array (value, &value_len, 1);
gsize value_len;
const guint8* value_data = g_variant_get_fixed_array (value, &value_len, 1);
if (lsetxattr (path, (char*)name, (char*)value_data, value_len, 0) < 0)
{
glnx_set_prefix_error_from_errno (error, "%s", "lsetxattr");
goto out;
}
return glnx_throw_errno_prefix (error, "lsetxattr");
}
ret = TRUE;
out:
return ret;
return TRUE;
}
/**
@@ -315,7 +307,7 @@ glnx_dfd_name_set_all_xattrs (int dfd,
GCancellable *cancellable,
GError **error)
{
if (dfd == AT_FDCWD || dfd == -1)
if (G_IN_SET(dfd, AT_FDCWD, -1))
{
return set_all_xattrs_for_path (name, xattrs, cancellable, error);
}
@@ -347,35 +339,22 @@ glnx_fd_set_all_xattrs (int fd,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
int i, n;
n = g_variant_n_children (xattrs);
for (i = 0; i < n; i++)
const guint n = g_variant_n_children (xattrs);
for (guint i = 0; i < n; i++)
{
const guint8* name;
const guint8* value_data;
g_autoptr(GVariant) value = NULL;
gsize value_len;
int res;
g_variant_get_child (xattrs, i, "(^&ay@ay)",
&name, &value);
value_data = g_variant_get_fixed_array (value, &value_len, 1);
do
res = fsetxattr (fd, (char*)name, (char*)value_data, value_len, 0);
while (G_UNLIKELY (res == -1 && errno == EINTR));
if (G_UNLIKELY (res == -1))
{
glnx_set_prefix_error_from_errno (error, "%s", "fsetxattr");
goto out;
}
gsize value_len;
const guint8* value_data = g_variant_get_fixed_array (value, &value_len, 1);
if (TEMP_FAILURE_RETRY (fsetxattr (fd, (char*)name, (char*)value_data, value_len, 0)) < 0)
return glnx_throw_errno_prefix (error, "fsetxattr");
}
ret = TRUE;
out:
return ret;
return TRUE;
}
/**
@@ -395,35 +374,17 @@ glnx_lgetxattrat (int dfd,
GError **error)
{
char pathbuf[PATH_MAX];
GBytes *bytes = NULL;
ssize_t bytes_read, real_size;
guint8 *buf;
snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath);
do
bytes_read = lgetxattr (pathbuf, attribute, NULL, 0);
while (G_UNLIKELY (bytes_read < 0 && errno == EINTR));
if (G_UNLIKELY (bytes_read < 0))
{
glnx_set_error_from_errno (error);
goto out;
}
ssize_t bytes_read, real_size;
if (TEMP_FAILURE_RETRY (bytes_read = lgetxattr (pathbuf, attribute, NULL, 0)) < 0)
return glnx_null_throw_errno_prefix (error, "lgetxattr");
buf = g_malloc (bytes_read);
do
real_size = lgetxattr (pathbuf, attribute, buf, bytes_read);
while (G_UNLIKELY (real_size < 0 && errno == EINTR));
if (G_UNLIKELY (real_size < 0))
{
glnx_set_error_from_errno (error);
g_free (buf);
goto out;
}
g_autofree guint8 *buf = g_malloc (bytes_read);
if (TEMP_FAILURE_RETRY (real_size = lgetxattr (pathbuf, attribute, buf, bytes_read)) < 0)
return glnx_null_throw_errno_prefix (error, "lgetxattr");
bytes = g_bytes_new_take (buf, real_size);
out:
return bytes;
return g_bytes_new_take (g_steal_pointer (&buf), real_size);
}
/**
@@ -439,33 +400,16 @@ glnx_fgetxattr_bytes (int fd,
const char *attribute,
GError **error)
{
GBytes *bytes = NULL;
ssize_t bytes_read, real_size;
guint8 *buf;
do
bytes_read = fgetxattr (fd, attribute, NULL, 0);
while (G_UNLIKELY (bytes_read < 0 && errno == EINTR));
if (G_UNLIKELY (bytes_read < 0))
{
glnx_set_error_from_errno (error);
goto out;
}
if (TEMP_FAILURE_RETRY (bytes_read = fgetxattr (fd, attribute, NULL, 0)) < 0)
return glnx_null_throw_errno_prefix (error, "fgetxattr");
buf = g_malloc (bytes_read);
do
real_size = fgetxattr (fd, attribute, buf, bytes_read);
while (G_UNLIKELY (real_size < 0 && errno == EINTR));
if (G_UNLIKELY (real_size < 0))
{
glnx_set_error_from_errno (error);
g_free (buf);
goto out;
}
g_autofree guint8 *buf = g_malloc (bytes_read);
if (TEMP_FAILURE_RETRY (real_size = fgetxattr (fd, attribute, buf, bytes_read)) < 0)
return glnx_null_throw_errno_prefix (error, "fgetxattr");
bytes = g_bytes_new_take (buf, real_size);
out:
return bytes;
return g_bytes_new_take (g_steal_pointer (&buf), real_size);
}
/**
@@ -490,18 +434,10 @@ glnx_lsetxattrat (int dfd,
GError **error)
{
char pathbuf[PATH_MAX];
int res;
snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath);
do
res = lsetxattr (subpath, attribute, value, len, flags);
while (G_UNLIKELY (res == -1 && errno == EINTR));
if (G_UNLIKELY (res == -1))
{
glnx_set_error_from_errno (error);
return FALSE;
}
if (TEMP_FAILURE_RETRY (lsetxattr (subpath, attribute, value, len, flags)) < 0)
return glnx_throw_errno_prefix (error, "lsetxattr");
return TRUE;
}
+1 -1
View File
@@ -24,7 +24,7 @@
G_BEGIN_DECLS
#include <glnx-alloca.h>
#include <glnx-macros.h>
#include <glnx-local-alloc.h>
#include <glnx-backport-autocleanups.h>
#include <glnx-backports.h>
+2 -5
View File
@@ -80,11 +80,8 @@ test_renameat2_noreplace (void)
glnx_set_error_from_errno (error);
goto out;
}
if (fstatat (destfd, "bar", &stbuf, AT_SYMLINK_NOFOLLOW) < 0)
{
glnx_set_error_from_errno (error);
goto out;
}
if (!glnx_fstatat (destfd, "bar", &stbuf, AT_SYMLINK_NOFOLLOW, error))
goto out;
if (fstatat (srcfd, "foo", &stbuf, AT_SYMLINK_NOFOLLOW) == 0)
g_assert_not_reached ();
+109
View File
@@ -0,0 +1,109 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2017 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include "libglnx.h"
#include <glib.h>
#include <stdlib.h>
#include <gio/gio.h>
#include <string.h>
static void
test_inset (void)
{
g_assert (G_IN_SET (7, 7));
g_assert (G_IN_SET (7, 42, 7));
g_assert (G_IN_SET (7, 7,42,3,9));
g_assert (G_IN_SET (42, 7,42,3,9));
g_assert (G_IN_SET (3, 7,42,3,9));
g_assert (G_IN_SET (9, 7,42,3,9));
g_assert (!G_IN_SET (8, 7,42,3,9));
g_assert (!G_IN_SET (-1, 7,42,3,9));
g_assert (G_IN_SET ('x', 'a', 'x', 'c'));
g_assert (!G_IN_SET ('y', 'a', 'x', 'c'));
}
static void
test_hash_table_foreach (void)
{
/* use var names all different from the macro metavars to ensure proper
* substitution */
g_autoptr(GHashTable) table = g_hash_table_new (g_str_hash, g_str_equal);
const char *keys[] = {"key1", "key2"};
const char *vals[] = {"val1", "val2"};
g_hash_table_insert (table, (gpointer)keys[0], (gpointer)vals[0]);
g_hash_table_insert (table, (gpointer)keys[1], (gpointer)vals[1]);
guint i = 0;
GLNX_HASH_TABLE_FOREACH_IT (table, it, const char*, key, const char*, val)
{
g_assert_cmpstr (key, ==, keys[i]);
g_assert_cmpstr (val, ==, vals[i]);
i++;
}
g_assert_cmpuint (i, ==, 2);
i = 0;
GLNX_HASH_TABLE_FOREACH_IT (table, it, const char*, key, const char*, val)
{
g_hash_table_iter_remove (&it);
break;
}
g_assert_cmpuint (g_hash_table_size (table), ==, 1);
g_hash_table_insert (table, (gpointer)keys[1], (gpointer)vals[1]);
g_assert_cmpuint (g_hash_table_size (table), ==, 1);
g_hash_table_insert (table, (gpointer)keys[0], (gpointer)vals[0]);
g_assert_cmpuint (g_hash_table_size (table), ==, 2);
i = 0;
GLNX_HASH_TABLE_FOREACH_KV (table, const char*, key, const char*, val)
{
g_assert_cmpstr (key, ==, keys[i]);
g_assert_cmpstr (val, ==, vals[i]);
i++;
}
g_assert_cmpuint (i, ==, 2);
i = 0;
GLNX_HASH_TABLE_FOREACH (table, const char*, key)
{
g_assert_cmpstr (key, ==, keys[i]);
i++;
}
g_assert_cmpuint (i, ==, 2);
i = 0;
GLNX_HASH_TABLE_FOREACH_V (table, const char*, val)
{
g_assert_cmpstr (val, ==, vals[i]);
i++;
}
g_assert_cmpuint (i, ==, 2);
}
int main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/inset", test_inset);
g_test_add_func ("/hash_table_foreach", test_hash_table_foreach);
return g_test_run();
}