diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am
index 6340063c32a2903c7cd29e4a4640ca17bc7aaf97..462000133ae64631fc72217fa7a3649dbff8ec67 100644
--- a/Makefile-libglnx.am
+++ b/Makefile-libglnx.am
@@ -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)
 
diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h
index 05ac49ee9875318beaaf9c82d40b69457f296696..8fd9b9c1020e1f8fe95eda973d098da8040fe208 100644
--- a/glnx-backport-autocleanups.h
+++ b/glnx-backport-autocleanups.h
@@ -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)
diff --git a/glnx-backport-autoptr.h b/glnx-backport-autoptr.h
index de611b7fd7eb976c329bbf6e6661d8c8f4763bf1..fd5f5d0133b2451927de4065c4666c7bb07c7720 100644
--- a/glnx-backport-autoptr.h
+++ b/glnx-backport-autoptr.h
@@ -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
diff --git a/glnx-dirfd.c b/glnx-dirfd.c
new file mode 100644
index 0000000000000000000000000000000000000000..8bc288b0351fae2ef07510f28dd162f860c6c480
--- /dev/null
+++ b/glnx-dirfd.c
@@ -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);
+}
diff --git a/glnx-dirfd.h b/glnx-dirfd.h
new file mode 100644
index 0000000000000000000000000000000000000000..ccb6aa2f30e2c5d68d840d1b98407edba943a76a
--- /dev/null
+++ b/glnx-dirfd.h
@@ -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
diff --git a/glnx-errors.c b/glnx-errors.c
new file mode 100644
index 0000000000000000000000000000000000000000..3cbda7e41ff20ff4d216f1c42592e8dc31399dd4
--- /dev/null
+++ b/glnx-errors.c
@@ -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;
+    }
+}
diff --git a/glnx-errors.h b/glnx-errors.h
new file mode 100644
index 0000000000000000000000000000000000000000..e51cd7e8a25bb528d3c1b684947879eddebefe1d
--- /dev/null
+++ b/glnx-errors.h
@@ -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
diff --git a/glnx-shutil.c b/glnx-shutil.c
new file mode 100644
index 0000000000000000000000000000000000000000..9fcb65a0617f2649cdec95492c30b3f93f6137f0
--- /dev/null
+++ b/glnx-shutil.c
@@ -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;
+}
diff --git a/glnx-shutil.h b/glnx-shutil.h
new file mode 100644
index 0000000000000000000000000000000000000000..0c53fd86de0289b137644206d235134a6e28e272
--- /dev/null
+++ b/glnx-shutil.h
@@ -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
diff --git a/libglnx.h b/libglnx.h
index 12881eb5f7c4164e620fb5d90d8bf7b27e82e461..294b90447392159f2099d27c8744afae82042ed8 100644
--- a/libglnx.h
+++ b/libglnx.h
@@ -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