summaryrefslogtreecommitdiff
path: root/libmisc/remove_tree.c
diff options
context:
space:
mode:
authorBalint Reczey <balint@balintreczey.hu>2022-08-20 18:17:29 +0200
committerBalint Reczey <balint@balintreczey.hu>2022-08-20 18:17:29 +0200
commit69ca70f20204c63dca3ae4f85dd9a37188dcfc0b (patch)
treefd3a6ed4ed4551b63d0f9ed74690c8fbcaf2f7df /libmisc/remove_tree.c
parent8d8547f65fc0474fa335d04201ee9c2ccb9aa773 (diff)
parent675b462b64b213647d0f5c56b1e8440be5890c8a (diff)
downloadshadow-69ca70f20204c63dca3ae4f85dd9a37188dcfc0b.tar.gz
Update upstream source from tag 'upstream/4.12.2+dfsg1'
Update to upstream version '4.12.2+dfsg1' with Debian dir 52044f35b7f4073e1929e253a646847ef319cac4
Diffstat (limited to 'libmisc/remove_tree.c')
-rw-r--r--libmisc/remove_tree.c87
1 files changed, 39 insertions, 48 deletions
diff --git a/libmisc/remove_tree.c b/libmisc/remove_tree.c
index c3aa8131..3d76b95e 100644
--- a/libmisc/remove_tree.c
+++ b/libmisc/remove_tree.c
@@ -11,6 +11,7 @@
#ident "$Id$"
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -21,90 +22,80 @@
#include "prototypes.h"
#include "defines.h"
-/*
- * remove_tree - delete a directory tree
- *
- * remove_tree() walks a directory tree and deletes all the files
- * and directories.
- * At the end, it deletes the root directory itself.
- */
-
-int remove_tree (const char *root, bool remove_root)
+static int remove_tree_at (int at_fd, const char *path, bool remove_root)
{
- char *new_name = NULL;
- int err = 0;
- struct DIRECT *ent;
- struct stat sb;
DIR *dir;
+ const struct dirent *ent;
+ int dir_fd, rc = 0;
- /*
- * Open the source directory and read each entry. Every file
- * entry in the directory is copied with the UID and GID set
- * to the provided values. As an added security feature only
- * regular files (and directories ...) are copied, and no file
- * is made set-ID.
- */
- dir = opendir (root);
- if (NULL == dir) {
+ dir_fd = openat (at_fd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (dir_fd < 0) {
+ return -1;
+ }
+
+ dir = fdopendir (dir_fd);
+ if (!dir) {
+ (void) close (dir_fd);
return -1;
}
+ /*
+ * Open the source directory and delete each entry.
+ */
while ((ent = readdir (dir))) {
- size_t new_len = strlen (root) + strlen (ent->d_name) + 2;
+ struct stat ent_sb;
/*
* Skip the "." and ".." entries
*/
-
if (strcmp (ent->d_name, ".") == 0 ||
strcmp (ent->d_name, "..") == 0) {
continue;
}
- /*
- * Make the filename for the current entry.
- */
-
- free (new_name);
- new_name = (char *) malloc (new_len);
- if (NULL == new_name) {
- err = -1;
+ rc = fstatat (dirfd(dir), ent->d_name, &ent_sb, AT_SYMLINK_NOFOLLOW);
+ if (rc < 0) {
break;
}
- (void) snprintf (new_name, new_len, "%s/%s", root, ent->d_name);
- if (LSTAT (new_name, &sb) == -1) {
- continue;
- }
- if (S_ISDIR (sb.st_mode)) {
+ if (S_ISDIR (ent_sb.st_mode)) {
/*
* Recursively delete this directory.
*/
- if (remove_tree (new_name, true) != 0) {
- err = -1;
+ if (remove_tree_at (dirfd(dir), ent->d_name, true) != 0) {
+ rc = -1;
break;
}
} else {
/*
* Delete the file.
*/
- if (unlink (new_name) != 0) {
- err = -1;
+ if (unlinkat (dirfd(dir), ent->d_name, 0) != 0) {
+ rc = -1;
break;
}
}
}
- if (NULL != new_name) {
- free (new_name);
- }
+
(void) closedir (dir);
- if (remove_root && (0 == err)) {
- if (rmdir (root) != 0) {
- err = -1;
+ if (remove_root && (0 == rc)) {
+ if (unlinkat (at_fd, path, AT_REMOVEDIR) != 0) {
+ rc = -1;
}
}
- return err;
+ return rc;
}
+/*
+ * remove_tree - delete a directory tree
+ *
+ * remove_tree() walks a directory tree and deletes all the files
+ * and directories.
+ * At the end, it deletes the root directory itself.
+ */
+int remove_tree (const char *root, bool remove_root)
+{
+ return remove_tree_at (AT_FDCWD, root, remove_root);
+}