]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/rm-rf.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2015 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <sys/statfs.h>
29 #include "btrfs-util.h"
30 #include "cgroup-util.h"
31 #include "dirent-util.h"
35 #include "mount-util.h"
36 #include "path-util.h"
38 #include "stat-util.h"
39 #include "string-util.h"
41 static bool is_physical_fs(const struct statfs
*sfs
) {
42 return !is_temporary_fs(sfs
) && !is_cgroup_fs(sfs
);
45 int rm_rf_children(int fd
, RemoveFlags flags
, struct stat
*root_dev
) {
46 _cleanup_closedir_
DIR *d
= NULL
;
53 /* This returns the first error we run into, but nevertheless
54 * tries to go on. This closes the passed fd. */
56 if (!(flags
& REMOVE_PHYSICAL
)) {
58 r
= fstatfs(fd
, &sfs
);
64 if (is_physical_fs(&sfs
)) {
65 /* We refuse to clean physical file systems
66 * with this call, unless explicitly
67 * requested. This is extra paranoia just to
68 * be sure we never ever remove non-state
71 log_error("Attempted to remove disk file system, and we can't allow that.");
80 return errno
== ENOENT
? 0 : -errno
;
83 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
87 if (dot_or_dot_dot(de
->d_name
))
90 if (de
->d_type
== DT_UNKNOWN
||
91 (de
->d_type
== DT_DIR
&& (root_dev
|| (flags
& REMOVE_SUBVOLUME
)))) {
92 if (fstatat(fd
, de
->d_name
, &st
, AT_SYMLINK_NOFOLLOW
) < 0) {
93 if (ret
== 0 && errno
!= ENOENT
)
98 is_dir
= S_ISDIR(st
.st_mode
);
100 is_dir
= de
->d_type
== DT_DIR
;
105 /* if root_dev is set, remove subdirectories only if device is same */
106 if (root_dev
&& st
.st_dev
!= root_dev
->st_dev
)
109 subdir_fd
= openat(fd
, de
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
111 if (ret
== 0 && errno
!= ENOENT
)
116 /* Stop at mount points */
117 r
= fd_is_mount_point(fd
, de
->d_name
, 0);
119 if (ret
== 0 && r
!= -ENOENT
)
122 safe_close(subdir_fd
);
126 safe_close(subdir_fd
);
130 if ((flags
& REMOVE_SUBVOLUME
) && st
.st_ino
== 256) {
132 /* This could be a subvolume, try to remove it */
134 r
= btrfs_subvol_remove_fd(fd
, de
->d_name
, BTRFS_REMOVE_RECURSIVE
|BTRFS_REMOVE_QUOTA
);
136 if (!IN_SET(r
, -ENOTTY
, -EINVAL
)) {
140 safe_close(subdir_fd
);
144 /* ENOTTY, then it wasn't a
145 * btrfs subvolume, continue
148 /* It was a subvolume, continue. */
149 safe_close(subdir_fd
);
154 /* We pass REMOVE_PHYSICAL here, to avoid
155 * doing the fstatfs() to check the file
156 * system type again for each directory */
157 r
= rm_rf_children(subdir_fd
, flags
| REMOVE_PHYSICAL
, root_dev
);
158 if (r
< 0 && ret
== 0)
161 if (unlinkat(fd
, de
->d_name
, AT_REMOVEDIR
) < 0) {
162 if (ret
== 0 && errno
!= ENOENT
)
166 } else if (!(flags
& REMOVE_ONLY_DIRECTORIES
)) {
168 if (unlinkat(fd
, de
->d_name
, 0) < 0) {
169 if (ret
== 0 && errno
!= ENOENT
)
177 int rm_rf(const char *path
, RemoveFlags flags
) {
183 /* We refuse to clean the root file system with this
184 * call. This is extra paranoia to never cause a really
185 * seriously broken system. */
186 if (path_equal_or_files_same(path
, "/", AT_SYMLINK_NOFOLLOW
)) {
187 log_error("Attempted to remove entire root file system, and we can't allow that.");
191 if ((flags
& (REMOVE_SUBVOLUME
|REMOVE_ROOT
|REMOVE_PHYSICAL
)) == (REMOVE_SUBVOLUME
|REMOVE_ROOT
|REMOVE_PHYSICAL
)) {
192 /* Try to remove as subvolume first */
193 r
= btrfs_subvol_remove(path
, BTRFS_REMOVE_RECURSIVE
|BTRFS_REMOVE_QUOTA
);
197 if (!IN_SET(r
, -ENOTTY
, -EINVAL
, -ENOTDIR
))
200 /* Not btrfs or not a subvolume */
203 fd
= open(path
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
206 if (!IN_SET(errno
, ENOTDIR
, ELOOP
))
209 if (!(flags
& REMOVE_PHYSICAL
)) {
210 if (statfs(path
, &s
) < 0)
213 if (is_physical_fs(&s
)) {
214 log_error("Attempted to remove disk file system, and we can't allow that.");
219 if ((flags
& REMOVE_ROOT
) && !(flags
& REMOVE_ONLY_DIRECTORIES
))
220 if (unlink(path
) < 0 && errno
!= ENOENT
)
226 r
= rm_rf_children(fd
, flags
, NULL
);
228 if (flags
& REMOVE_ROOT
) {
229 if (rmdir(path
) < 0) {
230 if (r
== 0 && errno
!= ENOENT
)