|
rpm-build |
c487f7 |
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* This library is free software; you can redistribute it and/or
|
|
rpm-build |
c487f7 |
* modify it under the terms of the GNU Lesser General Public
|
|
rpm-build |
c487f7 |
* License as published by the Free Software Foundation; either
|
|
rpm-build |
c487f7 |
* version 2 of the License, or (at your option) any later version.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* This library is distributed in the hope that it will be useful,
|
|
rpm-build |
c487f7 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
rpm-build |
c487f7 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
rpm-build |
c487f7 |
* Lesser General Public License for more details.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* You should have received a copy of the GNU Lesser General Public
|
|
rpm-build |
c487f7 |
* License along with this library; if not, write to the
|
|
rpm-build |
c487f7 |
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
rpm-build |
c487f7 |
* Boston, MA 02111-1307, USA.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
#include "config.h"
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
#include <string.h>
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
#include <glnx-shutil.h>
|
|
rpm-build |
c487f7 |
#include <glnx-errors.h>
|
|
rpm-build |
c487f7 |
#include <glnx-local-alloc.h>
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
static gboolean
|
|
rpm-build |
c487f7 |
glnx_shutil_rm_rf_children (GLnxDirFdIterator *dfd_iter,
|
|
rpm-build |
c487f7 |
GCancellable *cancellable,
|
|
rpm-build |
c487f7 |
GError **error)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
struct dirent *dent;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
while (TRUE)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (dfd_iter, &dent, cancellable, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
if (dent == NULL)
|
|
rpm-build |
c487f7 |
break;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (dent->d_type == DT_DIR)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
g_auto(GLnxDirFdIterator) child_dfd_iter = { 0, };
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!glnx_dirfd_iterator_init_at (dfd_iter->fd, dent->d_name, FALSE,
|
|
rpm-build |
c487f7 |
&child_dfd_iter, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!glnx_shutil_rm_rf_children (&child_dfd_iter, cancellable, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (unlinkat (dfd_iter->fd, dent->d_name, AT_REMOVEDIR) == -1)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "unlinkat");
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (unlinkat (dfd_iter->fd, dent->d_name, 0) == -1)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (errno != ENOENT)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "unlinkat");
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/**
|
|
rpm-build |
c487f7 |
* glnx_shutil_rm_rf_at:
|
|
rpm-build |
c487f7 |
* @dfd: A directory file descriptor, or `AT_FDCWD` or `-1` for current
|
|
rpm-build |
c487f7 |
* @path: Path
|
|
rpm-build |
c487f7 |
* @cancellable: Cancellable
|
|
rpm-build |
c487f7 |
* @error: Error
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Recursively delete the filename referenced by the combination of
|
|
rpm-build |
c487f7 |
* the directory fd @dfd and @path; it may be a file or directory. No
|
|
rpm-build |
c487f7 |
* error is thrown if @path does not exist.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
gboolean
|
|
rpm-build |
c487f7 |
glnx_shutil_rm_rf_at (int dfd,
|
|
rpm-build |
c487f7 |
const char *path,
|
|
rpm-build |
c487f7 |
GCancellable *cancellable,
|
|
rpm-build |
c487f7 |
GError **error)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
dfd = glnx_dirfd_canonicalize (dfd);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* With O_NOFOLLOW first */
|
|
rpm-build |
c487f7 |
glnx_autofd int target_dfd =
|
|
rpm-build |
c487f7 |
openat (dfd, path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (target_dfd == -1)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
int errsv = errno;
|
|
rpm-build |
c487f7 |
if (errsv == ENOENT)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else if (errsv == ENOTDIR || errsv == ELOOP)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (unlinkat (dfd, path, 0) != 0)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "unlinkat");
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "open(%s)", path);
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
|
|
rpm-build |
c487f7 |
if (!glnx_dirfd_iterator_init_take_fd (&target_dfd, &dfd_iter, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!glnx_shutil_rm_rf_children (&dfd_iter, cancellable, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (unlinkat (dfd, path, AT_REMOVEDIR) == -1)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (errno != ENOENT)
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "unlinkat");
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
static gboolean
|
|
rpm-build |
c487f7 |
mkdir_p_at_internal (int dfd,
|
|
rpm-build |
c487f7 |
char *path,
|
|
rpm-build |
c487f7 |
int mode,
|
|
rpm-build |
c487f7 |
GCancellable *cancellable,
|
|
rpm-build |
c487f7 |
GError **error)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
gboolean did_recurse = FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
again:
|
|
rpm-build |
c487f7 |
if (mkdirat (dfd, path, mode) == -1)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
if (errno == ENOENT)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
char *lastslash;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
g_assert (!did_recurse);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
lastslash = strrchr (path, '/');
|
|
rpm-build |
c487f7 |
if (lastslash == NULL)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* This can happen if @dfd was deleted between being opened and
|
|
rpm-build |
c487f7 |
* passed to mkdir_p_at_internal(). */
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "mkdir(%s)", path);
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Note we can mutate the buffer as we dup'd it */
|
|
rpm-build |
c487f7 |
*lastslash = '\0';
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!glnx_shutil_mkdir_p_at (dfd, path, mode,
|
|
rpm-build |
c487f7 |
cancellable, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Now restore it for another mkdir attempt */
|
|
rpm-build |
c487f7 |
*lastslash = '/';
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
did_recurse = TRUE;
|
|
rpm-build |
c487f7 |
goto again;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else if (errno == EEXIST)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* Fall through; it may not have been a directory,
|
|
rpm-build |
c487f7 |
* but we'll find that out on the next call up.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
else
|
|
rpm-build |
c487f7 |
return glnx_throw_errno_prefix (error, "mkdir(%s)", path);
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/**
|
|
rpm-build |
c487f7 |
* glnx_shutil_mkdir_p_at:
|
|
rpm-build |
c487f7 |
* @dfd: Directory fd
|
|
rpm-build |
c487f7 |
* @path: Directory path to be created
|
|
rpm-build |
c487f7 |
* @mode: Mode for newly created directories
|
|
rpm-build |
c487f7 |
* @cancellable: Cancellable
|
|
rpm-build |
c487f7 |
* @error: Error
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Similar to g_mkdir_with_parents(), except operates relative to the
|
|
rpm-build |
c487f7 |
* directory fd @dfd.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* See also glnx_ensure_dir() for a non-recursive version.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* This will return %G_IO_ERROR_NOT_FOUND if @dfd has been deleted since being
|
|
rpm-build |
c487f7 |
* opened. It may return other errors from mkdirat() in other situations.
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
gboolean
|
|
rpm-build |
c487f7 |
glnx_shutil_mkdir_p_at (int dfd,
|
|
rpm-build |
c487f7 |
const char *path,
|
|
rpm-build |
c487f7 |
int mode,
|
|
rpm-build |
c487f7 |
GCancellable *cancellable,
|
|
rpm-build |
c487f7 |
GError **error)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
struct stat stbuf;
|
|
rpm-build |
c487f7 |
char *buf;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/* Fast path stat to see whether it already exists */
|
|
rpm-build |
c487f7 |
if (fstatat (dfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) == 0)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* Note early return */
|
|
rpm-build |
c487f7 |
if (S_ISDIR (stbuf.st_mode))
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
buf = strdupa (path);
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!mkdir_p_at_internal (dfd, buf, mode, cancellable, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
return TRUE;
|
|
rpm-build |
c487f7 |
}
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
/**
|
|
rpm-build |
c487f7 |
* glnx_shutil_mkdir_p_at_open:
|
|
rpm-build |
c487f7 |
* @dfd: Directory fd
|
|
rpm-build |
c487f7 |
* @path: Directory path to be created
|
|
rpm-build |
c487f7 |
* @mode: Mode for newly created directories
|
|
rpm-build |
c487f7 |
* @out_dfd: (out caller-allocates): Return location for an FD to @dfd/@path,
|
|
rpm-build |
c487f7 |
* or `-1` on error
|
|
rpm-build |
c487f7 |
* @cancellable: (nullable): Cancellable, or %NULL
|
|
rpm-build |
c487f7 |
* @error: Return location for a #GError, or %NULL
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Similar to glnx_shutil_mkdir_p_at(), except it opens the resulting directory
|
|
rpm-build |
c487f7 |
* and returns a directory FD to it. Currently, this is not guaranteed to be
|
|
rpm-build |
c487f7 |
* race-free.
|
|
rpm-build |
c487f7 |
*
|
|
rpm-build |
c487f7 |
* Returns: %TRUE on success, %FALSE otherwise
|
|
rpm-build |
c487f7 |
* Since: UNRELEASED
|
|
rpm-build |
c487f7 |
*/
|
|
rpm-build |
c487f7 |
gboolean
|
|
rpm-build |
c487f7 |
glnx_shutil_mkdir_p_at_open (int dfd,
|
|
rpm-build |
c487f7 |
const char *path,
|
|
rpm-build |
c487f7 |
int mode,
|
|
rpm-build |
c487f7 |
int *out_dfd,
|
|
rpm-build |
c487f7 |
GCancellable *cancellable,
|
|
rpm-build |
c487f7 |
GError **error)
|
|
rpm-build |
c487f7 |
{
|
|
rpm-build |
c487f7 |
/* FIXME: It’s not possible to eliminate the race here until
|
|
rpm-build |
c487f7 |
* openat(O_DIRECTORY | O_CREAT) works (and returns a directory rather than a
|
|
rpm-build |
c487f7 |
* file). It appears to be not supported in current kernels. (Tested with
|
|
rpm-build |
c487f7 |
* 4.10.10-200.fc25.x86_64.) */
|
|
rpm-build |
c487f7 |
*out_dfd = -1;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
if (!glnx_shutil_mkdir_p_at (dfd, path, mode, cancellable, error))
|
|
rpm-build |
c487f7 |
return FALSE;
|
|
rpm-build |
c487f7 |
|
|
rpm-build |
c487f7 |
return glnx_opendirat (dfd, path, TRUE, out_dfd, error);
|
|
rpm-build |
c487f7 |
}
|