New upstream version 2017.6
This commit is contained in:
commit
e3060b1b87
168 files changed
+5181
-3686
No files matched your search
+70
-9
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
Reference in new issue
Block a user