summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2019-03-29 16:13:03 +0100
committerLennart Poettering <lennart@poettering.net>2019-07-11 12:18:51 +0200
commitc0228b4fa3e4e633afa5eb8417e6cd3e311cd250 (patch)
treec3d9590862e8b6ff68f3dd790f5f853a0e3ef666
parent6b39223cd3d5af24c0077207c633a47ae8f7d80d (diff)
downloadsystemd-c0228b4fa3e4e633afa5eb8417e6cd3e311cd250.tar.gz
rm-rf: introduce new flag REMOVE_MISSING_OK
This new flag suppresses error if the top-level path specified doesn't exist. This is useful since suppressing this on the caller side isn't easy, since ENOENT migh be propagate for some reason from further inside and we can't distuingish that. While we are at it, also be a bit more careful witht the various combinations of flags. (Note that in some cases rm_rf() was already ignoring ENOENT from unlink() or rmdir(), however that was pretty useless, since we always open() the top-level path with O_DIRECTORY and if that hit ENOENT we didn't ignore the failure).
-rw-r--r--src/basic/rm-rf.c51
-rw-r--r--src/basic/rm-rf.h9
2 files changed, 37 insertions, 23 deletions
diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
index b751933c83..796eb93c4b 100644
--- a/src/basic/rm-rf.c
+++ b/src/basic/rm-rf.c
@@ -161,9 +161,8 @@ int rm_rf(const char *path, RemoveFlags flags) {
if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME))
return -EINVAL;
- /* We refuse to clean the root file system with this
- * call. This is extra paranoia to never cause a really
- * seriously broken system. */
+ /* We refuse to clean the root file system with this call. This is extra paranoia to never cause a
+ * really seriously broken system. */
if (path_equal_or_files_same(path, "/", AT_SYMLINK_NOFOLLOW))
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
"Attempted to remove entire root file system (\"%s\"), and we can't allow that.",
@@ -175,6 +174,9 @@ int rm_rf(const char *path, RemoveFlags flags) {
if (r >= 0)
return r;
+ if (FLAGS_SET(flags, REMOVE_MISSING_OK) && r == -ENOENT)
+ return 0;
+
if (!IN_SET(r, -ENOTTY, -EINVAL, -ENOTDIR))
return r;
@@ -183,34 +185,45 @@ int rm_rf(const char *path, RemoveFlags flags) {
fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
if (fd < 0) {
+ if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT)
+ return 0;
+
if (!IN_SET(errno, ENOTDIR, ELOOP))
return -errno;
- if (!(flags & REMOVE_PHYSICAL)) {
- if (statfs(path, &s) < 0)
- return -errno;
+ if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES))
+ return 0;
- if (is_physical_fs(&s))
- return log_error_errno(SYNTHETIC_ERRNO(EPERM),
- "Attempted to remove files from a disk file system under \"%s\", refusing.",
- path);
- }
+ if (FLAGS_SET(flags, REMOVE_ROOT)) {
+
+ if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) {
+ if (statfs(path, &s) < 0)
+ return -errno;
+
+ if (is_physical_fs(&s))
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Attempted to remove files from a disk file system under \"%s\", refusing.",
+ path);
+ }
+
+ if (unlink(path) < 0) {
+ if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT)
+ return 0;
- if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES))
- if (unlink(path) < 0 && errno != ENOENT)
return -errno;
+ }
+ }
return 0;
}
r = rm_rf_children(fd, flags, NULL);
- if (flags & REMOVE_ROOT) {
- if (rmdir(path) < 0) {
- if (r == 0 && errno != ENOENT)
- r = -errno;
- }
- }
+ if (FLAGS_SET(flags, REMOVE_ROOT) &&
+ rmdir(path) < 0 &&
+ r >= 0 &&
+ (!FLAGS_SET(flags, REMOVE_MISSING_OK) || errno != ENOENT))
+ r = -errno;
return r;
}
diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h
index d42ebef434..40cbff21c0 100644
--- a/src/basic/rm-rf.h
+++ b/src/basic/rm-rf.h
@@ -6,10 +6,11 @@
#include "errno-util.h"
typedef enum RemoveFlags {
- REMOVE_ONLY_DIRECTORIES = 1 << 0,
- REMOVE_ROOT = 1 << 1,
- REMOVE_PHYSICAL = 1 << 2, /* if not set, only removes files on tmpfs, never physical file systems */
- REMOVE_SUBVOLUME = 1 << 3,
+ REMOVE_ONLY_DIRECTORIES = 1 << 0, /* Only remove empty directories, no files */
+ REMOVE_ROOT = 1 << 1, /* Remove the specified directory itself too, not just the contents of it */
+ REMOVE_PHYSICAL = 1 << 2, /* If not set, only removes files on tmpfs, never physical file systems */
+ REMOVE_SUBVOLUME = 1 << 3, /* Drop btrfs subvolumes in the tree too */
+ REMOVE_MISSING_OK = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */
} RemoveFlags;
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);