Import directory iteration, errno handling, and shutil from libgsystem

This commit is contained in:
Colin Walters
2015-02-15 09:54:38 -05:00
parent 5ac3f4a119
commit 8e9a171ec4
10 changed files with 562 additions and 3 deletions

View File

@@ -20,6 +20,14 @@ EXTRA_DIST += $(libglnx_srcpath)/README $(libglnx_srcpath)/COPYING
libglnx_la_SOURCES = \
$(libglnx_srcpath)/glnx-backport-autocleanups.h \
$(libglnx_srcpath)/glnx-backport-autoptr.h \
$(libglnx_srcpath)/glnx-local-alloc.h \
$(libglnx_srcpath)/glnx-local-alloc.c \
$(libglnx_srcpath)/glnx-errors.h \
$(libglnx_srcpath)/glnx-errors.c \
$(libglnx_srcpath)/glnx-dirfd.h \
$(libglnx_srcpath)/glnx-dirfd.c \
$(libglnx_srcpath)/glnx-shutil.h \
$(libglnx_srcpath)/glnx-shutil.c \
$(libglnx_srcpath)/libglnx.h \
$(NULL)

View File

@@ -21,7 +21,7 @@
#include <glnx-backport-autoptr.h>
#if !GLIB_CHECK_VERSION(2, 44, 0)
#if !GLIB_CHECK_VERSION(2, 43, 3)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GAsyncQueue, g_async_queue_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBookmarkFile, g_bookmark_file_free)

View File

@@ -27,7 +27,7 @@
G_BEGIN_DECLS
#if !GLIB_CHECK_VERSION(2, 44, 0)
#if !GLIB_CHECK_VERSION(2, 43, 4)
#define _GLIB_AUTOPTR_FUNC_NAME(TypeName) glib_autoptr_cleanup_##TypeName
#define _GLIB_AUTOPTR_TYPENAME(TypeName) TypeName##_autoptr
@@ -127,6 +127,6 @@ static inline gpointer
#define g_steal_pointer(pp) \
(0 ? (*(pp)) : (g_steal_pointer) (pp))
#endif /* !GLIB_CHECK_VERSION(2, 44, 0) */
#endif /* !GLIB_CHECK_VERSION(2, 43, 3) */
G_END_DECLS

165
glnx-dirfd.c Normal file
View File

@@ -0,0 +1,165 @@
/* -*- 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.
*/
#include "config.h"
#include <string.h>
#include <glnx-dirfd.h>
#include <glnx-errors.h>
#include <glnx-local-alloc.h>
/**
* glnx_opendirat_with_errno:
* @dfd: File descriptor for origin directory
* @name: Pathname, relative to @dfd
* @follow: Whether or not to follow symbolic links
*
* Use openat() to open a directory, using a standard set of flags.
* This function sets errno.
*/
int
glnx_opendirat_with_errno (int dfd,
const char *path,
gboolean follow)
{
int flags = O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY;
if (!follow)
flags |= O_NOFOLLOW;
return openat (dfd, path, flags);
}
/**
* glnx_opendirat:
* @dfd: File descriptor for origin directory
* @path: Pathname, relative to @dfd
* @follow: Whether or not to follow symbolic links
* @error: Error
*
* Use openat() to open a directory, using a standard set of flags.
*/
gboolean
glnx_opendirat (int dfd,
const char *path,
gboolean follow,
int *out_fd,
GError **error)
{
int ret = glnx_opendirat_with_errno (dfd, path, follow);
if (ret == -1)
{
glnx_set_prefix_error_from_errno (error, "%s", "openat");
return FALSE;
}
*out_fd = ret;
return TRUE;
}
struct GLnxRealDirfdIterator
{
gboolean initialized;
int fd;
DIR *d;
};
typedef struct GLnxRealDirfdIterator GLnxRealDirfdIterator;
gboolean
glnx_dirfd_iterator_init_at (int dfd,
const char *path,
gboolean follow,
GLnxDirFdIterator *dfd_iter,
GError **error)
{
gboolean ret = FALSE;
glnx_fd_close int fd = -1;
if (!glnx_opendirat (dfd, path, follow, &fd, error))
goto out;
if (!glnx_dirfd_iterator_init_take_fd (fd, dfd_iter, error))
goto out;
fd = -1; /* Transfer ownership */
ret = TRUE;
out:
return ret;
}
gboolean
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);
if (!d)
{
glnx_set_prefix_error_from_errno (error, "%s", "fdopendir");
goto out;
}
real_dfd_iter->fd = dfd;
real_dfd_iter->d = d;
ret = TRUE;
out:
return ret;
}
gboolean
glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter,
struct dirent **out_dent,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter;
if (g_cancellable_set_error_if_cancelled (cancellable, error))
goto out;
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;
}
} while (*out_dent &&
(strcmp ((*out_dent)->d_name, ".") == 0 ||
strcmp ((*out_dent)->d_name, "..") == 0));
ret = TRUE;
out:
return ret;
}
void
glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter)
{
GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter;
/* fd is owned by dfd_iter */
(void) closedir (real_dfd_iter->d);
}

60
glnx-dirfd.h Normal file
View File

@@ -0,0 +1,60 @@
/* -*- 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 <glnx-backport-autoptr.h>
#include <limits.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
G_BEGIN_DECLS
struct GLnxDirFdIterator {
gboolean initialized;
int fd;
gpointer padding_data[4];
};
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_next_dent (GLnxDirFdIterator *dfd_iter,
struct dirent **out_dent,
GCancellable *cancellable,
GError **error);
void glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter);
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxDirFdIterator, glnx_dirfd_iterator_clear)
int glnx_opendirat_with_errno (int dfd,
const char *path,
gboolean follow);
gboolean glnx_opendirat (int dfd,
const char *path,
gboolean follow,
int *out_fd,
GError **error);
G_END_DECLS

53
glnx-errors.c Normal file
View File

@@ -0,0 +1,53 @@
/* -*- 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.
*/
#include "config.h"
#include <glnx-backport-autoptr.h>
#include <glnx-errors.h>
void
glnx_real_set_prefix_error_from_errno (GError **error,
gint errsv,
const char *format,
...)
{
if (!error)
return;
else
{
GString *buf = g_string_new ("");
va_list args;
va_start (args, format);
g_string_append_vprintf (buf, format, args);
va_end (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_string_free (buf, TRUE);
errno = errsv;
}
}

49
glnx-errors.h Normal file
View File

@@ -0,0 +1,49 @@
/* -*- 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 <glnx-backport-autoptr.h>
#include <errno.h>
G_BEGIN_DECLS
#define glnx_set_error_from_errno(error) \
do { \
int errsv = errno; \
g_set_error_literal (error, G_IO_ERROR, \
g_io_error_from_errno (errsv), \
g_strerror (errsv)); \
errno = errsv; \
} while (0);
#define glnx_set_prefix_error_from_errno(error, format, args...) \
do { \
int errsv = errno; \
glnx_real_set_prefix_error_from_errno (error, errsv, format, args); \
errno = errsv; \
} while (0);
void glnx_real_set_prefix_error_from_errno (GError **error,
gint errsv,
const char *format,
...) G_GNUC_PRINTF (3,4);
G_END_DECLS

188
glnx-shutil.c Normal file
View File

@@ -0,0 +1,188 @@
/* -*- 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.
*/
#include "config.h"
#include <string.h>
#include <glnx-shutil.h>
#include <glnx-errors.h>
#include <glnx-local-alloc.h>
static unsigned char
struct_stat_to_dt (struct stat *stbuf)
{
if (S_ISDIR (stbuf->st_mode))
return DT_DIR;
if (S_ISREG (stbuf->st_mode))
return DT_REG;
if (S_ISCHR (stbuf->st_mode))
return DT_CHR;
if (S_ISBLK (stbuf->st_mode))
return DT_BLK;
if (S_ISFIFO (stbuf->st_mode))
return DT_FIFO;
if (S_ISLNK (stbuf->st_mode))
return DT_LNK;
if (S_ISSOCK (stbuf->st_mode))
return DT_SOCK;
return DT_UNKNOWN;
}
static gboolean
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 (dfd_iter, &dent, cancellable, error))
goto out;
if (dent == NULL)
break;
if (dent->d_type == DT_UNKNOWN)
{
struct stat stbuf;
if (fstatat (dfd_iter->fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1)
{
if (errno == ENOENT)
continue;
else
{
glnx_set_error_from_errno (error);
goto out;
}
}
dent->d_type = struct_stat_to_dt (&stbuf);
/* Assume unknown types are just treated like regular files */
if (dent->d_type == DT_UNKNOWN)
dent->d_type = DT_REG;
}
if (dent->d_type == DT_DIR)
{
g_auto(GLnxDirFdIterator) child_dfd_iter = { 0, };
if (!glnx_dirfd_iterator_init_at (dfd_iter->fd, dent->d_name, FALSE,
&child_dfd_iter, error))
goto out;
if (!glnx_shutil_rm_rf_children (&child_dfd_iter, cancellable, error))
goto out;
if (unlinkat (dfd_iter->fd, dent->d_name, AT_REMOVEDIR) == -1)
{
glnx_set_error_from_errno (error);
goto out;
}
}
else
{
if (unlinkat (dfd_iter->fd, dent->d_name, 0) == -1)
{
if (errno != ENOENT)
{
glnx_set_error_from_errno (error);
goto out;
}
}
}
}
ret = TRUE;
out:
return ret;
}
/**
* glnx_shutil_rm_rf_at:
* @dfd: A directory file descriptor, or -1 for current
* @path: Path
* @cancellable: Cancellable
* @error: Error
*
* Recursively delete the filename referenced by the combination of
* the directory fd @dfd and @path; it may be a file or directory. No
* error is thrown if @path does not exist.
*/
gboolean
glnx_shutil_rm_rf_at (int dfd,
const char *path,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
glnx_fd_close int target_dfd = -1;
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
/* With O_NOFOLLOW first */
target_dfd = openat (dfd, path,
O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
if (target_dfd == -1)
{
int errsv = errno;
if (errsv == ENOENT)
{
;
}
else if (errsv == ENOTDIR || errsv == ELOOP)
{
if (unlinkat (dfd, path, 0) != 0)
{
glnx_set_error_from_errno (error);
goto out;
}
}
else
{
glnx_set_error_from_errno (error);
goto out;
}
}
else
{
if (!glnx_dirfd_iterator_init_take_fd (target_dfd, &dfd_iter, error))
goto out;
target_dfd = -1;
if (!glnx_shutil_rm_rf_children (&dfd_iter, cancellable, error))
goto out;
if (unlinkat (dfd, path, AT_REMOVEDIR) == -1)
{
int errsv = errno;
if (errsv != ENOENT)
{
glnx_set_error_from_errno (error);
goto out;
}
}
}
ret = TRUE;
out:
return ret;
}

33
glnx-shutil.h Normal file
View File

@@ -0,0 +1,33 @@
/* -*- 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 <glnx-dirfd.h>
G_BEGIN_DECLS
gboolean
glnx_shutil_rm_rf_at (int dfd,
const char *path,
GCancellable *cancellable,
GError **error);
G_END_DECLS

View File

@@ -27,5 +27,8 @@ G_BEGIN_DECLS
#include <glnx-local-alloc.h>
#include <glnx-backport-autoptr.h>
#include <glnx-backport-autocleanups.h>
#include <glnx-errors.h>
#include <glnx-dirfd.h>
#include <glnx-shutil.h>
G_END_DECLS