New upstream version 2017.6

This commit is contained in:
Simon McVittie committed 2017-05-24 19:00:16 +01:00
commit e3060b1b87
168 files changed
+5181 -3686

No files matched your search

+70 -9
View File
@@ -209,13 +209,12 @@ glnx_dirfd_iterator_next_dent_ensure_dtype (GLnxDirFdIterator *dfd_iter,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
struct dirent *ret_dent;
g_return_val_if_fail (out_dent, FALSE);
if (!glnx_dirfd_iterator_next_dent (dfd_iter, out_dent, cancellable, error))
goto out;
return FALSE;
ret_dent = *out_dent;
@@ -226,17 +225,12 @@ glnx_dirfd_iterator_next_dent_ensure_dtype (GLnxDirFdIterator *dfd_iter,
{
struct stat stbuf;
if (TEMP_FAILURE_RETRY (fstatat (dfd_iter->fd, ret_dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW)) != 0)
{
glnx_set_error_from_errno (error);
goto out;
}
return glnx_throw_errno (error);
ret_dent->d_type = IFTODT (stbuf.st_mode);
}
}
ret = TRUE;
out:
return ret;
return TRUE;
}
/**
@@ -348,3 +342,70 @@ glnx_mkdtempat (int dfd,
"mkstempat ran out of combinations to try.");
return FALSE;
}
/**
* glnx_mkdtempat_open:
* @dfd: Directory FD
* @tmpl: (type filename): template directory name, last 6 characters will be replaced
* @mode: permissions to create the temporary directory with
* @out_dfd: (out caller-allocates): Return location for an FD for the new
* temporary directory, or `-1` on error
* @error: Return location for a #GError, or %NULL
*
* Similar to glnx_mkdtempat(), except it will open the resulting temporary
* directory and return a directory FD to it.
*
* Returns: %TRUE on success, %FALSE otherwise
* Since: UNRELEASED
*/
gboolean
glnx_mkdtempat_open (int dfd,
gchar *tmpl,
int mode,
int *out_dfd,
GError **error)
{
/* FIXME: Ideally we could use openat(O_DIRECTORY | O_CREAT | O_EXCL) here
* to create and open the directory atomically, but that’s not supported by
* current kernel versions: http://www.openwall.com/lists/oss-security/2014/11/26/14
* (Tested on kernel 4.10.10-200.fc25.x86_64). For the moment, accept a
* TOCTTOU race here. */
*out_dfd = -1;
if (!glnx_mkdtempat (dfd, tmpl, mode, error))
return FALSE;
return glnx_opendirat (dfd, tmpl, FALSE, out_dfd, error);
}
/**
* glnx_mkdtempat_open_in_system:
* @tmpl: (type filename): template directory name, last 6 characters will be replaced
* @mode: permissions to create the temporary directory with
* @out_dfd: (out caller-allocates): Return location for an FD for the new
* temporary directory, or `-1` on error
* @error: Return location for a #GError, or %NULL
*
* Similar to glnx_mkdtempat_open(), except it will use the system temporary
* directory (from g_get_tmp_dir()) as the parent directory to @tmpl.
*
* Returns: %TRUE on success, %FALSE otherwise
* Since: UNRELEASED
*/
gboolean
glnx_mkdtempat_open_in_system (gchar *tmpl,
int mode,
int *out_dfd,
GError **error)
{
glnx_fd_close int tmp_dfd = -1;
*out_dfd = -1;
if (!glnx_opendirat (-1, g_get_tmp_dir (), TRUE, &tmp_dfd, error))
return FALSE;
return glnx_mkdtempat_open (tmp_dfd, tmpl, mode, out_dfd, error);
}
+11
View File
@@ -88,4 +88,15 @@ gboolean glnx_mkdtempat (int dfd,
int mode,
GError **error);
gboolean glnx_mkdtempat_open (int dfd,
gchar *tmpl,
int mode,
int *out_dfd,
GError **error);
gboolean glnx_mkdtempat_open_in_system (gchar *tmpl,
int mode,
int *out_dfd,
GError **error);
G_END_DECLS
+18 -6
View File
@@ -23,6 +23,22 @@
#include <glnx-backport-autocleanups.h>
#include <glnx-errors.h>
void
glnx_real_set_prefix_error_va (GError *error,
const char *format,
va_list args)
{
if (error == NULL)
return;
g_autofree char *old_msg = g_steal_pointer (&error->message);
g_autoptr(GString) buf = g_string_new ("");
g_string_append_vprintf (buf, format, args);
g_string_append (buf, ": ");
g_string_append (buf, old_msg);
error->message = g_string_free (g_steal_pointer (&buf), FALSE);
}
void
glnx_real_set_prefix_error_from_errno_va (GError **error,
gint errsv,
@@ -32,13 +48,9 @@ glnx_real_set_prefix_error_from_errno_va (GError **error,
if (!error)
return;
/* TODO - enhance GError to have a "set and take ownership" API */
g_autoptr(GString) buf = g_string_new ("");
g_string_append_vprintf (buf, format, args);
g_string_append (buf, ": ");
g_string_append (buf, g_strerror (errsv));
g_set_error_literal (error,
G_IO_ERROR,
g_io_error_from_errno (errsv),
buf->str);
g_strerror (errsv));
glnx_real_set_prefix_error_va (*error, format, args);
}
+45 -5
View File
@@ -30,7 +30,7 @@ G_BEGIN_DECLS
* This function returns %FALSE so it can be used conveniently in a single
* statement:
*
* ``
* ```
* if (strcmp (foo, "somevalue") != 0)
* return glnx_throw (error, "key must be somevalue, not '%s'", foo);
* ```
@@ -49,13 +49,47 @@ glnx_throw (GError **error, const char *fmt, ...)
return FALSE;
}
/* Like `glnx_throw ()`, but returns %NULL. */
#define glnx_null_throw(error, args...) \
({glnx_throw (error, args); NULL;})
/* Implementation detail of glnx_throw_prefix() */
void glnx_real_set_prefix_error_va (GError *error,
const char *format,
va_list args) G_GNUC_PRINTF (2,0);
/* Prepend to @error's message by `$prefix: ` where `$prefix` is computed via
* printf @fmt. Returns %FALSE so it can be used conveniently in a single
* statement:
*
* ```
* if (!function_that_fails (s, error))
* return glnx_throw_prefix (error, "while handling '%s'", s);
* ```
* */
static inline gboolean G_GNUC_PRINTF (2,3)
glnx_prefix_error (GError **error, const char *fmt, ...)
{
if (error == NULL)
return FALSE;
va_list args;
va_start (args, fmt);
glnx_real_set_prefix_error_va (*error, fmt, args);
va_end (args);
return FALSE;
}
/* Like `glnx_prefix_error ()`, but returns %NULL. */
#define glnx_prefix_error_null(error, args...) \
({glnx_prefix_error (error, args); NULL;})
/* Set @error using the value of `g_strerror (errno)`.
*
* This function returns %FALSE so it can be used conveniently in a single
* statement:
*
* ``
* ```
* if (unlinkat (fd, somepathname) < 0)
* return glnx_throw_errno (error);
* ```
@@ -79,6 +113,10 @@ glnx_throw_errno (GError **error)
return FALSE;
}
/* Like glnx_throw_errno(), but yields a NULL pointer. */
#define glnx_null_throw_errno(error) \
({glnx_throw_errno (error); NULL;})
/* Implementation detail of glnx_throw_errno_prefix() */
void glnx_real_set_prefix_error_from_errno_va (GError **error,
gint errsv,
@@ -108,6 +146,10 @@ glnx_throw_errno_prefix (GError **error, const char *fmt, ...)
return FALSE;
}
/* Like glnx_throw_errno_prefix(), but yields a NULL pointer. */
#define glnx_null_throw_errno_prefix(error, args...) \
({glnx_throw_errno_prefix (error, args); NULL;})
/* BEGIN LEGACY APIS */
#define glnx_set_error_from_errno(error) \
@@ -117,9 +159,7 @@ glnx_throw_errno_prefix (GError **error, const char *fmt, ...)
#define glnx_set_prefix_error_from_errno(error, format, args...) \
do { \
glnx_set_error_from_errno (error); \
g_prefix_error (error, format, args); \
glnx_throw_errno_prefix (error, format, args); \
} while (0);
G_END_DECLS
+183 -258
View File
@@ -102,10 +102,7 @@ rename_file_noreplace_at (int olddirfd, const char *oldpath,
return TRUE;
}
else
{
glnx_set_error_from_errno (error);
return FALSE;
}
return glnx_throw_errno (error);
}
return TRUE;
}
@@ -172,10 +169,7 @@ glnx_open_tmpfile_linkable_at (int dfd,
#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))
{
glnx_set_prefix_error_from_errno (error, "%s", "open(O_TMPFILE)");
return FALSE;
}
return glnx_throw_errno_prefix (error, "open(O_TMPFILE)");
if (fd != -1)
{
*out_fd = fd;
@@ -199,10 +193,7 @@ glnx_open_tmpfile_linkable_at (int dfd,
if (errno == EEXIST)
continue;
else
{
glnx_set_prefix_error_from_errno (error, "%s", "Creating temp file");
return FALSE;
}
return glnx_throw_errno_prefix (error, "Creating temp file");
}
else
{
@@ -246,9 +237,10 @@ glnx_link_tmpfile_at (int dfd,
*/
if (renameat (dfd, tmpfile_path, target_dfd, target) < 0)
{
int errsv = errno;
(void) unlinkat (dfd, tmpfile_path, 0);
glnx_set_error_from_errno (error);
return FALSE;
errno = errsv;
return glnx_throw_errno_prefix (error, "renameat");
}
}
else
@@ -293,10 +285,7 @@ glnx_link_tmpfile_at (int dfd,
if (errno == EEXIST)
continue;
else
{
glnx_set_error_from_errno (error);
return FALSE;
}
return glnx_throw_errno_prefix (error, "linkat");
}
else
break;
@@ -311,9 +300,10 @@ glnx_link_tmpfile_at (int dfd,
/* 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);
glnx_set_error_from_errno (error);
return FALSE;
errno = errsv;
return glnx_throw_errno_prefix (error, "renameat");
}
}
else
@@ -323,10 +313,7 @@ glnx_link_tmpfile_at (int dfd,
if (errno == EEXIST && mode == GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST)
;
else
{
glnx_set_error_from_errno (error);
return FALSE;
}
return glnx_throw_errno_prefix (error, "linkat");
}
}
@@ -341,49 +328,37 @@ glnx_fd_readall_malloc (int fd,
GCancellable *cancellable,
GError **error)
{
gboolean success = FALSE;
const guint maxreadlen = 4096;
int res;
struct stat stbuf;
guint8* buf = NULL;
if (TEMP_FAILURE_RETRY (fstat (fd, &stbuf)) < 0)
return glnx_null_throw_errno (error);
gsize buf_allocated;
gsize buf_size = 0;
gssize bytes_read;
do
res = fstat (fd, &stbuf);
while (G_UNLIKELY (res == -1 && errno == EINTR));
if (res == -1)
{
glnx_set_error_from_errno (error);
goto out;
}
if (S_ISREG (stbuf.st_mode) && stbuf.st_size > 0)
buf_allocated = stbuf.st_size;
else
buf_allocated = 16;
buf = g_malloc (buf_allocated);
g_autofree guint8* buf = g_malloc (buf_allocated);
gsize buf_size = 0;
while (TRUE)
{
gsize readlen = MIN (buf_allocated - buf_size, maxreadlen);
if (g_cancellable_set_error_if_cancelled (cancellable, error))
goto out;
if (g_cancellable_set_error_if_cancelled (cancellable, error))
return FALSE;
gssize bytes_read;
do
bytes_read = read (fd, buf + buf_size, readlen);
while (G_UNLIKELY (bytes_read == -1 && errno == EINTR));
if (G_UNLIKELY (bytes_read == -1))
{
glnx_set_error_from_errno (error);
goto out;
}
return glnx_null_throw_errno (error);
if (bytes_read == 0)
break;
buf_size += bytes_read;
if (buf_allocated - buf_size < maxreadlen)
buf = g_realloc (buf, buf_allocated *= 2);
@@ -396,15 +371,8 @@ glnx_fd_readall_malloc (int fd,
buf[buf_size] = '\0';
}
success = TRUE;
out:
if (success)
{
*out_len = buf_size;
return buf;
}
g_free (buf);
return NULL;
*out_len = buf_size;
return g_steal_pointer (&buf);
}
/**
@@ -423,13 +391,10 @@ glnx_fd_readall_bytes (int fd,
GCancellable *cancellable,
GError **error)
{
guint8 *buf;
gsize len;
buf = glnx_fd_readall_malloc (fd, &len, FALSE, cancellable, error);
guint8 *buf = glnx_fd_readall_malloc (fd, &len, FALSE, cancellable, error);
if (!buf)
return NULL;
return g_bytes_new_take (buf, len);
}
@@ -451,13 +416,10 @@ glnx_fd_readall_utf8 (int fd,
GCancellable *cancellable,
GError **error)
{
gboolean success = FALSE;
guint8 *buf;
gsize len;
buf = glnx_fd_readall_malloc (fd, &len, TRUE, cancellable, error);
g_autofree guint8 *buf = glnx_fd_readall_malloc (fd, &len, TRUE, cancellable, error);
if (!buf)
goto out;
return FALSE;
if (!g_utf8_validate ((char*)buf, len, NULL))
{
@@ -465,19 +427,12 @@ glnx_fd_readall_utf8 (int fd,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"Invalid UTF-8");
goto out;
return FALSE;
}
success = TRUE;
out:
if (success)
{
if (out_len)
*out_len = len;
return (char*)buf;
}
g_free (buf);
return NULL;
if (out_len)
*out_len = len;
return (char*)g_steal_pointer (&buf);
}
/**
@@ -501,36 +456,20 @@ glnx_file_get_contents_utf8_at (int dfd,
GCancellable *cancellable,
GError **error)
{
gboolean success = FALSE;
glnx_fd_close int fd = -1;
char *buf = NULL;
gsize len;
dfd = glnx_dirfd_canonicalize (dfd);
do
fd = openat (dfd, subpath, O_RDONLY | O_NOCTTY | O_CLOEXEC);
while (G_UNLIKELY (fd == -1 && errno == EINTR));
glnx_fd_close int fd = TEMP_FAILURE_RETRY (openat (dfd, subpath, O_RDONLY | O_NOCTTY | O_CLOEXEC));
if (G_UNLIKELY (fd == -1))
{
glnx_set_error_from_errno (error);
goto out;
}
return glnx_null_throw_errno_prefix (error, "open(%s)", subpath);
buf = glnx_fd_readall_utf8 (fd, &len, cancellable, error);
gsize len;
g_autofree char *buf = glnx_fd_readall_utf8 (fd, &len, cancellable, error);
if (G_UNLIKELY(!buf))
goto out;
success = TRUE;
out:
if (success)
{
if (out_len)
*out_len = len;
return buf;
}
g_free (buf);
return NULL;
return FALSE;
if (out_len)
*out_len = len;
return g_steal_pointer (&buf);
}
/**
@@ -555,25 +494,20 @@ glnx_readlinkat_malloc (int dfd,
for (;;)
{
char *c;
g_autofree char *c = NULL;
ssize_t n;
c = g_malloc (l);
n = TEMP_FAILURE_RETRY (readlinkat (dfd, subpath, c, l-1));
if (n < 0)
{
glnx_set_error_from_errno (error);
g_free (c);
return FALSE;
}
return glnx_null_throw_errno (error);
if ((size_t) n < l-1)
{
c[n] = 0;
return c;
return g_steal_pointer (&c);
}
g_free (c);
l *= 2;
}
@@ -590,158 +524,171 @@ copy_symlink_at (int src_dfd,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
g_autofree char *buf = NULL;
buf = glnx_readlinkat_malloc (src_dfd, src_subpath, cancellable, error);
g_autofree char *buf = glnx_readlinkat_malloc (src_dfd, src_subpath, cancellable, error);
if (!buf)
goto out;
return FALSE;
if (TEMP_FAILURE_RETRY (symlinkat (buf, dest_dfd, dest_subpath)) != 0)
{
glnx_set_error_from_errno (error);
goto out;
}
return glnx_throw_errno_prefix (error, "symlinkat");
if (!(copyflags & GLNX_FILE_COPY_NOXATTRS))
{
g_autoptr(GVariant) xattrs = NULL;
if (!glnx_dfd_name_get_all_xattrs (src_dfd, src_subpath, &xattrs,
cancellable, error))
goto out;
return FALSE;
if (!glnx_dfd_name_set_all_xattrs (dest_dfd, dest_subpath, xattrs,
cancellable, error))
goto out;
return FALSE;
}
if (TEMP_FAILURE_RETRY (fchownat (dest_dfd, dest_subpath,
src_stbuf->st_uid, src_stbuf->st_gid,
AT_SYMLINK_NOFOLLOW)) != 0)
{
glnx_set_error_from_errno (error);
goto out;
}
return glnx_throw_errno (error);
ret = TRUE;
out:
return ret;
return TRUE;
}
#define COPY_BUFFER_SIZE (16*1024)
/* From systemd */
/* Most of the code below is from systemd, but has been reindented to GNU style,
* and changed to use POSIX error conventions (return -1, set errno) to more
* conveniently fit in with the rest of libglnx.
*/
static int btrfs_reflink(int infd, int outfd) {
int r;
g_return_val_if_fail(infd >= 0, -1);
g_return_val_if_fail(outfd >= 0, -1);
r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
if (r < 0)
return -errno;
return 0;
return ioctl (outfd, BTRFS_IOC_CLONE, infd);
}
int glnx_loop_write(int fd, const void *buf, size_t nbytes) {
const uint8_t *p = buf;
/* Like write(), but loop until @nbytes are written, or an error
* occurs.
*
* On error, -1 is returned an @errno is set. NOTE: This is an
* API change from previous versions of this function.
*/
int
glnx_loop_write(int fd, const void *buf, size_t nbytes)
{
const uint8_t *p = buf;
g_return_val_if_fail(fd >= 0, -1);
g_return_val_if_fail(buf, -1);
g_return_val_if_fail(fd >= 0, -1);
g_return_val_if_fail(buf, -1);
errno = 0;
errno = 0;
while (nbytes > 0) {
ssize_t k;
while (nbytes > 0)
{
ssize_t k;
k = write(fd, p, nbytes);
if (k < 0) {
if (errno == EINTR)
continue;
k = write(fd, p, nbytes);
if (k < 0)
{
if (errno == EINTR)
continue;
return -errno;
}
if (k == 0) /* Can't really happen */
return -EIO;
p += k;
nbytes -= k;
return -1;
}
return 0;
if (k == 0) /* Can't really happen */
{
errno = EIO;
return -1;
}
p += k;
nbytes -= k;
}
return 0;
}
static int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
bool try_sendfile = true;
int r;
/* Read from @fdf until EOF, writing to @fdt. If @try_reflink is %TRUE,
* attempt to use any "reflink" functionality; see e.g. https://lwn.net/Articles/331808/
*
* The file descriptor @fdf must refer to a regular file.
*
* If provided, @max_bytes specifies the maximum number of bytes to read from @fdf.
* On error, this function returns `-1` and @errno will be set.
*/
int
glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes, gboolean try_reflink)
{
bool try_sendfile = true;
int r;
g_return_val_if_fail (fdf >= 0, -1);
g_return_val_if_fail (fdt >= 0, -1);
/* Try btrfs reflinks first. */
if (try_reflink && max_bytes == (off_t) -1) {
r = btrfs_reflink(fdf, fdt);
if (r >= 0)
return r;
}
for (;;) {
size_t m = COPY_BUFFER_SIZE;
ssize_t n;
if (max_bytes != (off_t) -1) {
if (max_bytes <= 0)
return -EFBIG;
if ((off_t) m > max_bytes)
m = (size_t) max_bytes;
}
/* First try sendfile(), unless we already tried */
if (try_sendfile) {
n = sendfile(fdt, fdf, NULL, m);
if (n < 0) {
if (errno != EINVAL && errno != ENOSYS)
return -errno;
try_sendfile = false;
/* use fallback below */
} else if (n == 0) /* EOF */
break;
else if (n > 0)
/* Succcess! */
goto next;
}
/* As a fallback just copy bits by hand */
{
char buf[m];
n = read(fdf, buf, m);
if (n < 0)
return -errno;
if (n == 0) /* EOF */
break;
r = glnx_loop_write(fdt, buf, (size_t) n);
if (r < 0)
return r;
}
next:
if (max_bytes != (off_t) -1) {
g_assert(max_bytes >= n);
max_bytes -= n;
}
}
g_return_val_if_fail (fdf >= 0, -1);
g_return_val_if_fail (fdt >= 0, -1);
g_return_val_if_fail (max_bytes >= -1, -1);
/* Try btrfs reflinks first. */
if (try_reflink && max_bytes == (off_t) -1)
{
r = btrfs_reflink(fdf, fdt);
if (r >= 0)
return 0;
/* Fall through */
}
while (TRUE)
{
size_t m = COPY_BUFFER_SIZE;
ssize_t n;
if (max_bytes != (off_t) -1)
{
if ((off_t) m > max_bytes)
m = (size_t) max_bytes;
}
/* First try sendfile(), unless we already tried */
if (try_sendfile)
{
n = sendfile (fdt, fdf, NULL, m);
if (n < 0)
{
if (errno != EINVAL && errno != ENOSYS)
return -1;
try_sendfile = false;
/* use fallback below */
}
else if (n == 0) /* EOF */
break;
else if (n > 0)
/* Succcess! */
goto next;
}
/* As a fallback just copy bits by hand */
{ char buf[m];
n = read (fdf, buf, m);
if (n < 0)
return -1;
if (n == 0) /* EOF */
break;
if (glnx_loop_write (fdt, buf, (size_t) n) < 0)
return -1;
}
next:
if (max_bytes != (off_t) -1)
{
g_assert(max_bytes >= n);
max_bytes -= n;
if (max_bytes == 0)
break;
}
}
return 0;
}
/**
@@ -829,10 +776,9 @@ glnx_file_copy_at (int src_dfd,
goto out;
}
r = copy_bytes (src_fd, dest_fd, (off_t) -1, TRUE);
r = glnx_regfile_copy_bytes (src_fd, dest_fd, (off_t) -1, TRUE);
if (r < 0)
{
errno = -r;
glnx_set_error_from_errno (error);
goto out;
}
@@ -921,7 +867,6 @@ glnx_file_replace_contents_at (int dfd,
return glnx_file_replace_contents_with_perms_at (dfd, subpath, buf, len,
(mode_t) -1, (uid_t) -1, (gid_t) -1,
flags, cancellable, error);
}
/**
@@ -953,8 +898,6 @@ glnx_file_replace_contents_with_perms_at (int dfd,
int r;
char *dnbuf = strdupa (subpath);
const char *dn = dirname (dnbuf);
g_autofree char *tmpfile_path = NULL;
glnx_fd_close int fd = -1;
dfd = glnx_dirfd_canonicalize (dfd);
@@ -964,6 +907,8 @@ 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;
if (!glnx_open_tmpfile_linkable_at (dfd, dn, O_WRONLY | O_CLOEXEC,
&fd, &tmpfile_path,
error))
@@ -979,30 +924,22 @@ glnx_file_replace_contents_with_perms_at (int dfd,
if (r != 0)
{
errno = r;
glnx_set_error_from_errno (error);
return FALSE;
return glnx_throw_errno_prefix (error, "fallocate");
}
}
if ((r = glnx_loop_write (fd, buf, len)) != 0)
{
errno = -r;
glnx_set_error_from_errno (error);
return FALSE;
}
if (glnx_loop_write (fd, buf, len) < 0)
return glnx_throw_errno (error);
if (!(flags & GLNX_FILE_REPLACE_NODATASYNC))
{
struct stat stbuf;
gboolean do_sync;
if (fstatat (dfd, subpath, &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
{
if (errno != ENOENT)
{
glnx_set_error_from_errno (error);
return FALSE;
}
return glnx_throw_errno (error);
do_sync = (flags & GLNX_FILE_REPLACE_DATASYNC_NEW) > 0;
}
else
@@ -1011,27 +948,18 @@ glnx_file_replace_contents_with_perms_at (int dfd,
if (do_sync)
{
if (fdatasync (fd) != 0)
{
glnx_set_error_from_errno (error);
return FALSE;
}
return glnx_throw_errno_prefix (error, "fdatasync");
}
}
if (uid != (uid_t) -1)
{
if (fchown (fd, uid, gid) != 0)
{
glnx_set_error_from_errno (error);
return FALSE;
}
return glnx_throw_errno (error);
}
if (fchmod (fd, mode) != 0)
{
glnx_set_error_from_errno (error);
return FALSE;
}
return glnx_throw_errno (error);
if (!glnx_link_tmpfile_at (dfd, GLNX_LINK_TMPFILE_REPLACE,
fd, tmpfile_path, dfd, subpath, error))
@@ -1060,10 +988,7 @@ glnx_stream_fstat (GFileDescriptorBased *stream,
int fd = g_file_descriptor_based_get_fd (stream);
if (fstat (fd, stbuf) == -1)
{
glnx_set_prefix_error_from_errno (error, "%s", "fstat");
return FALSE;
}
return glnx_throw_errno_prefix (error, "fstat");
return TRUE;
}
+54
View File
@@ -35,6 +35,8 @@
#include <libgen.h>
#undef basename
#include <glnx-errors.h>
G_BEGIN_DECLS
/* Irritatingly, g_basename() which is what we want
@@ -129,6 +131,9 @@ glnx_readlinkat_malloc (int dfd,
int
glnx_loop_write (int fd, const void *buf, size_t nbytes);
int
glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes, gboolean try_reflink);
typedef enum {
GLNX_FILE_COPY_OVERWRITE = (1 << 0),
GLNX_FILE_COPY_NOXATTRS = (1 << 1),
@@ -155,5 +160,54 @@ int glnx_renameat2_noreplace (int olddirfd, const char *oldpath,
int glnx_renameat2_exchange (int olddirfd, const char *oldpath,
int newdirfd, const char *newpath);
/**
* glnx_fstat:
* @fd: FD to stat
* @buf: (out caller-allocates): Return location for stat details
* @error: Return location for a #GError, or %NULL
*
* Wrapper around fstat() which adds #GError support and ensures that it retries
* on %EINTR.
*
* Returns: %TRUE on success, %FALSE otherwise
* Since: UNRELEASED
*/
static inline gboolean
glnx_fstat (int fd,
struct stat *buf,
GError **error)
{
if (TEMP_FAILURE_RETRY (fstat (fd, buf)) != 0)
return glnx_throw_errno (error);
return TRUE;
}
/**
* glnx_fstatat:
* @dfd: Directory FD to stat beneath
* @path: Path to stat beneath @dfd
* @buf: (out caller-allocates): Return location for stat details
* @flags: Flags to pass to fstatat()
* @error: Return location for a #GError, or %NULL
*
* Wrapper around fstatat() which adds #GError support and ensures that it
* retries on %EINTR.
*
* Returns: %TRUE on success, %FALSE otherwise
* Since: UNRELEASED
*/
static inline gboolean
glnx_fstatat (int dfd,
const gchar *path,
struct stat *buf,
int flags,
GError **error)
{
if (TEMP_FAILURE_RETRY (fstatat (dfd, path, buf, flags)) != 0)
return glnx_throw_errno (error);
return TRUE;
}
G_END_DECLS
+2 -2
View File
@@ -199,9 +199,9 @@ glnx_cleanup_close_fdp (int *fdp)
int fd, errsv;
g_assert (fdp);
fd = *fdp;
if (fd != -1)
if (fd >= 0)
{
errsv = errno;
(void) close (fd);
+7 -16
View File
@@ -61,7 +61,6 @@
*/
gboolean
glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_lock, GError **error) {
gboolean ret = FALSE;
glnx_fd_close int fd = -1;
g_autofree char *t = NULL;
int r;
@@ -89,10 +88,8 @@ glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_loc
struct stat st;
fd = openat(dfd, p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
if (fd < 0) {
glnx_set_error_from_errno(error);
goto out;
}
if (fd < 0)
return glnx_throw_errno(error);
/* Unfortunately, new locks are not in RHEL 7.1 glibc */
#ifdef F_OFD_SETLK
@@ -107,10 +104,8 @@ glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_loc
if (errno == EINVAL)
r = flock(fd, operation);
if (r < 0) {
glnx_set_error_from_errno(error);
goto out;
}
if (r < 0)
return glnx_throw_errno(error);
}
/* If we acquired the lock, let's check if the file
@@ -120,10 +115,8 @@ glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_loc
* hence try again. */
r = fstat(fd, &st);
if (r < 0) {
glnx_set_error_from_errno(error);
goto out;
}
if (r < 0)
return glnx_throw_errno(error);
if (st.st_nlink > 0)
break;
@@ -142,9 +135,7 @@ glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_loc
fd = -1;
t = NULL;
ret = TRUE;
out:
return ret;
return TRUE;
}
void glnx_release_lock_file(GLnxLockFile *f) {
+58 -55
View File
@@ -31,14 +31,12 @@ glnx_shutil_rm_rf_children (GLnxDirFdIterator *dfd_iter,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
struct dirent *dent;
while (TRUE)
{
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (dfd_iter, &dent, cancellable, error))
goto out;
return FALSE;
if (dent == NULL)
break;
@@ -48,33 +46,25 @@ glnx_shutil_rm_rf_children (GLnxDirFdIterator *dfd_iter,
if (!glnx_dirfd_iterator_init_at (dfd_iter->fd, dent->d_name, FALSE,
&child_dfd_iter, error))
goto out;
return FALSE;
if (!glnx_shutil_rm_rf_children (&child_dfd_iter, cancellable, error))
goto out;
return FALSE;
if (unlinkat (dfd_iter->fd, dent->d_name, AT_REMOVEDIR) == -1)
{
glnx_set_error_from_errno (error);
goto out;
}
return glnx_throw_errno_prefix (error, "unlinkat");
}
else
{
if (unlinkat (dfd_iter->fd, dent->d_name, 0) == -1)
{
if (errno != ENOENT)
{
glnx_set_error_from_errno (error);
goto out;
}
return glnx_throw_errno_prefix (error, "unlinkat");
}
}
}
ret = TRUE;
out:
return ret;
return TRUE;
}
/**
@@ -94,7 +84,6 @@ glnx_shutil_rm_rf_at (int dfd,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
glnx_fd_close int target_dfd = -1;
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
@@ -114,40 +103,28 @@ glnx_shutil_rm_rf_at (int dfd,
else if (errsv == ENOTDIR || errsv == ELOOP)
{
if (unlinkat (dfd, path, 0) != 0)
{
glnx_set_error_from_errno (error);
goto out;
}
return glnx_throw_errno_prefix (error, "unlinkat");
}
else
{
glnx_set_error_from_errno (error);
goto out;
}
return glnx_throw_errno_prefix (error, "open(%s)", path);
}
else
{
if (!glnx_dirfd_iterator_init_take_fd (target_dfd, &dfd_iter, error))
goto out;
return FALSE;
target_dfd = -1;
if (!glnx_shutil_rm_rf_children (&dfd_iter, cancellable, error))
goto out;
return FALSE;
if (unlinkat (dfd, path, AT_REMOVEDIR) == -1)
{
int errsv = errno;
if (errsv != ENOENT)
{
glnx_set_error_from_errno (error);
goto out;
}
if (errno != ENOENT)
return glnx_throw_errno_prefix (error, "unlinkat");
}
}
ret = TRUE;
out:
return ret;
return TRUE;
}
static gboolean
@@ -157,11 +134,10 @@ mkdir_p_at_internal (int dfd,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gboolean did_recurse = FALSE;
if (g_cancellable_set_error_if_cancelled (cancellable, error))
goto out;
return FALSE;
again:
if (mkdirat (dfd, path, mode) == -1)
@@ -179,7 +155,7 @@ mkdir_p_at_internal (int dfd,
if (!glnx_shutil_mkdir_p_at (dfd, path, mode,
cancellable, error))
goto out;
return FALSE;
/* Now restore it for another mkdir attempt */
*lastslash = '/';
@@ -194,15 +170,10 @@ mkdir_p_at_internal (int dfd,
*/
}
else
{
glnx_set_error_from_errno (error);
goto out;
}
return glnx_throw_errno_prefix (error, "mkdir(%s)", path);
}
ret = TRUE;
out:
return ret;
return TRUE;
}
/**
@@ -223,26 +194,58 @@ glnx_shutil_mkdir_p_at (int dfd,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
struct stat stbuf;
char *buf;
/* Fast path stat to see whether it already exists */
if (fstatat (dfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) == 0)
{
/* Note early return */
if (S_ISDIR (stbuf.st_mode))
{
ret = TRUE;
goto out;
}
return TRUE;
}
buf = strdupa (path);
if (!mkdir_p_at_internal (dfd, buf, mode, cancellable, error))
goto out;
return FALSE;
ret = TRUE;
out:
return ret;
return TRUE;
}
/**
* glnx_shutil_mkdir_p_at_open:
* @dfd: Directory fd
* @path: Directory path to be created
* @mode: Mode for newly created directories
* @out_dfd: (out caller-allocates): Return location for an FD to @dfd/@path,
* or `-1` on error
* @cancellable: (nullable): Cancellable, or %NULL
* @error: Return location for a #GError, or %NULL
*
* Similar to glnx_shutil_mkdir_p_at(), except it opens the resulting directory
* and returns a directory FD to it. Currently, this is not guaranteed to be
* race-free.
*
* Returns: %TRUE on success, %FALSE otherwise
* Since: UNRELEASED
*/
gboolean
glnx_shutil_mkdir_p_at_open (int dfd,
const char *path,
int mode,
int *out_dfd,
GCancellable *cancellable,
GError **error)
{
/* FIXME: It’s not possible to eliminate the race here until
* openat(O_DIRECTORY | O_CREAT) works (and returns a directory rather than a
* file). It appears to be not supported in current kernels. (Tested with
* 4.10.10-200.fc25.x86_64.) */
*out_dfd = -1;
if (!glnx_shutil_mkdir_p_at (dfd, path, mode, cancellable, error))
return FALSE;
return glnx_opendirat (dfd, path, TRUE, out_dfd, error);
}
+8
View File
@@ -37,4 +37,12 @@ glnx_shutil_mkdir_p_at (int dfd,
GCancellable *cancellable,
GError **error);
gboolean
glnx_shutil_mkdir_p_at_open (int dfd,
const char *path,
int mode,
int *out_dfd,
GCancellable *cancellable,
GError **error);
G_END_DECLS
+61
View File
@@ -33,6 +33,19 @@ test_error_throw (void)
g_assert (!glnx_throw (&error, "foo: %s %d", "hello", 42));
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
g_assert_cmpstr (error->message, ==, "foo: hello 42");
g_clear_error (&error);
gpointer dummy = glnx_null_throw (&error, "literal foo");
g_assert (dummy == NULL);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
g_assert_cmpstr (error->message, ==, "literal foo");
g_clear_error (&error);
gpointer dummy2 = glnx_null_throw (&error, "foo: %s %d", "hola", 24);
g_assert (dummy2 == NULL);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
g_assert_cmpstr (error->message, ==, "foo: hola 24");
g_clear_error (&error);
}
static void
@@ -47,6 +60,9 @@ test_error_errno (void)
{
g_assert (!glnx_throw_errno (&error));
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
g_assert (!glnx_prefix_error (&error, "myprefix"));
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
g_assert (g_str_has_prefix (error->message, "myprefix: "));
g_clear_error (&error);
}
else
@@ -55,8 +71,53 @@ test_error_errno (void)
fd = open (noent_path, O_RDONLY);
if (fd < 0)
{
gpointer dummy = glnx_null_throw_errno (&error);
g_assert (dummy == NULL);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
dummy = glnx_prefix_error_null (&error, "myprefix");
g_assert (dummy == NULL);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
g_assert (g_str_has_prefix (error->message, "myprefix: "));
g_clear_error (&error);
}
else
g_assert_cmpint (fd, ==, -1);
fd = open (noent_path, O_RDONLY);
if (fd < 0)
{
g_autofree char *expected_prefix = g_strdup_printf ("Failed to open %s", noent_path);
g_assert (!glnx_throw_errno_prefix (&error, "Failed to open %s", noent_path));
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
g_assert (g_str_has_prefix (error->message, expected_prefix));
g_clear_error (&error);
/* And test the legacy wrapper */
glnx_set_prefix_error_from_errno (&error, "Failed to open %s", noent_path);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
g_assert (g_str_has_prefix (error->message, expected_prefix));
g_clear_error (&error);
}
else
g_assert_cmpint (fd, ==, -1);
fd = open (noent_path, O_RDONLY);
if (fd < 0)
{
gpointer dummy = glnx_null_throw_errno_prefix (&error, "Failed to open file");
g_assert (dummy == NULL);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
g_assert (g_str_has_prefix (error->message, "Failed to open file"));
g_clear_error (&error);
}
else
g_assert_cmpint (fd, ==, -1);
fd = open (noent_path, O_RDONLY);
if (fd < 0)
{
gpointer dummy = glnx_null_throw_errno_prefix (&error, "Failed to open %s", noent_path);
g_assert (dummy == NULL);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
g_assert (g_str_has_prefix (error->message, glnx_strjoina ("Failed to open ", noent_path)));
g_clear_error (&error);
}
+15
View File
@@ -244,6 +244,21 @@ test_xattr_races (void)
if (!glnx_opendirat (AT_FDCWD, tmpdir, TRUE, &dfd, error))
goto out;
/* Support people building/testing on tmpfs https://github.com/flatpak/flatpak/issues/686 */
if (fsetxattr (dfd, "user.test", "novalue", strlen ("novalue"), 0) < 0)
{
if (errno == EOPNOTSUPP)
{
g_test_skip ("no xattr support");
return;
}
else
{
glnx_set_error_from_errno (error);
goto out;
}
}
for (guint i = 0; i < nprocs; i++)
{
struct XattrWorker *worker = &wdata[i];