1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include "path-util.h"
24 // #include "btrfs-util.h"
27 int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
28 _cleanup_closedir_ DIR *d = NULL;
33 /* This returns the first error we run into, but nevertheless
34 * tries to go on. This closes the passed fd. */
36 if (!(flags & REMOVE_PHYSICAL)) {
38 r = fd_is_temporary_fs(fd);
45 /* We refuse to clean physical file systems
46 * with this call, unless explicitly
47 * requested. This is extra paranoia just to
48 * be sure we never ever remove non-state
51 log_error("Attempted to remove disk file system, and we can't allow that.");
60 return errno == ENOENT ? 0 : -errno;
71 if (errno != 0 && ret == 0)
76 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
79 if (de->d_type == DT_UNKNOWN ||
80 (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
81 if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
82 if (ret == 0 && errno != ENOENT)
87 is_dir = S_ISDIR(st.st_mode);
89 is_dir = de->d_type == DT_DIR;
94 /* if root_dev is set, remove subdirectories only if device is same */
95 if (root_dev && st.st_dev != root_dev->st_dev)
98 subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
100 if (ret == 0 && errno != ENOENT)
105 /* Stop at mount points */
106 r = fd_is_mount_point(fd, de->d_name, 0);
108 if (ret == 0 && r != -ENOENT)
111 safe_close(subdir_fd);
115 safe_close(subdir_fd);
120 if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) {
122 /* This could be a subvolume, try to remove it */
123 r = btrfs_subvol_remove_fd(fd, de->d_name, true);
125 if (r != -ENOTTY && r != -EINVAL) {
129 safe_close(subdir_fd);
133 /* ENOTTY, then it wasn't a
134 * btrfs subvolume, continue
137 /* It was a subvolume, continue. */
138 safe_close(subdir_fd);
144 /* We pass REMOVE_PHYSICAL here, to avoid
145 * doing the fstatfs() to check the file
146 * system type again for each directory */
147 r = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev);
148 if (r < 0 && ret == 0)
151 if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
152 if (ret == 0 && errno != ENOENT)
156 } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
158 if (unlinkat(fd, de->d_name, 0) < 0) {
159 if (ret == 0 && errno != ENOENT)
166 int rm_rf(const char *path, RemoveFlags flags) {
172 /* We refuse to clean the root file system with this
173 * call. This is extra paranoia to never cause a really
174 * seriously broken system. */
175 if (path_equal(path, "/")) {
176 log_error("Attempted to remove entire root file system, and we can't allow that.");
181 if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) {
182 /* Try to remove as subvolume first */
183 r = btrfs_subvol_remove(path, true);
187 if (r != -ENOTTY && r != -EINVAL && r != -ENOTDIR)
189 /* Not btrfs or not a subvolume */
193 fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
196 if (errno != ENOTDIR && errno != ELOOP)
199 if (!(flags & REMOVE_PHYSICAL)) {
200 if (statfs(path, &s) < 0)
203 if (!is_temporary_fs(&s)) {
204 log_error("Attempted to remove disk file system, and we can't allow that.");
209 if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES))
210 if (unlink(path) < 0 && errno != ENOENT)
216 r = rm_rf_children(fd, flags, NULL);
218 if (flags & REMOVE_ROOT) {
219 if (rmdir(path) < 0) {
220 if (r == 0 && errno != ENOENT)