]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/rm-rf.c
2 This file is part of systemd.
4 Copyright 2015 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include <sys/statfs.h>
28 #include "btrfs-util.h"
29 #include "cgroup-util.h"
30 #include "dirent-util.h"
34 #include "mount-util.h"
35 #include "path-util.h"
37 #include "stat-util.h"
38 #include "string-util.h"
40 static bool is_physical_fs(const struct statfs
*sfs
) {
41 return !is_temporary_fs(sfs
) && !is_cgroup_fs(sfs
);
44 int rm_rf_children(int fd
, RemoveFlags flags
, struct stat
*root_dev
) {
45 _cleanup_closedir_
DIR *d
= NULL
;
52 /* This returns the first error we run into, but nevertheless
53 * tries to go on. This closes the passed fd. */
55 if (!(flags
& REMOVE_PHYSICAL
)) {
57 r
= fstatfs(fd
, &sfs
);
63 if (is_physical_fs(&sfs
)) {
64 /* We refuse to clean physical file systems
65 * with this call, unless explicitly
66 * requested. This is extra paranoia just to
67 * be sure we never ever remove non-state
70 log_error("Attempted to remove disk file system, and we can't allow that.");
79 return errno
== ENOENT
? 0 : -errno
;
82 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
86 if (dot_or_dot_dot(de
->d_name
))
89 if (de
->d_type
== DT_UNKNOWN
||
90 (de
->d_type
== DT_DIR
&& (root_dev
|| (flags
& REMOVE_SUBVOLUME
)))) {
91 if (fstatat(fd
, de
->d_name
, &st
, AT_SYMLINK_NOFOLLOW
) < 0) {
92 if (ret
== 0 && errno
!= ENOENT
)
97 is_dir
= S_ISDIR(st
.st_mode
);
99 is_dir
= de
->d_type
== DT_DIR
;
104 /* if root_dev is set, remove subdirectories only if device is same */
105 if (root_dev
&& st
.st_dev
!= root_dev
->st_dev
)
108 subdir_fd
= openat(fd
, de
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
110 if (ret
== 0 && errno
!= ENOENT
)
115 /* Stop at mount points */
116 r
= fd_is_mount_point(fd
, de
->d_name
, 0);
118 if (ret
== 0 && r
!= -ENOENT
)
121 safe_close(subdir_fd
);
125 safe_close(subdir_fd
);
129 if ((flags
& REMOVE_SUBVOLUME
) && st
.st_ino
== 256) {
131 /* This could be a subvolume, try to remove it */
133 r
= btrfs_subvol_remove_fd(fd
, de
->d_name
, BTRFS_REMOVE_RECURSIVE
|BTRFS_REMOVE_QUOTA
);
135 if (r
!= -ENOTTY
&& r
!= -EINVAL
) {
139 safe_close(subdir_fd
);
143 /* ENOTTY, then it wasn't a
144 * btrfs subvolume, continue
147 /* It was a subvolume, continue. */
148 safe_close(subdir_fd
);
153 /* We pass REMOVE_PHYSICAL here, to avoid
154 * doing the fstatfs() to check the file
155 * system type again for each directory */
156 r
= rm_rf_children(subdir_fd
, flags
| REMOVE_PHYSICAL
, root_dev
);
157 if (r
< 0 && ret
== 0)
160 if (unlinkat(fd
, de
->d_name
, AT_REMOVEDIR
) < 0) {
161 if (ret
== 0 && errno
!= ENOENT
)
165 } else if (!(flags
& REMOVE_ONLY_DIRECTORIES
)) {
167 if (unlinkat(fd
, de
->d_name
, 0) < 0) {
168 if (ret
== 0 && errno
!= ENOENT
)
176 int rm_rf(const char *path
, RemoveFlags flags
) {
182 /* We refuse to clean the root file system with this
183 * call. This is extra paranoia to never cause a really
184 * seriously broken system. */
185 if (path_equal_or_files_same(path
, "/", AT_SYMLINK_NOFOLLOW
)) {
186 log_error("Attempted to remove entire root file system, and we can't allow that.");
190 if ((flags
& (REMOVE_SUBVOLUME
|REMOVE_ROOT
|REMOVE_PHYSICAL
)) == (REMOVE_SUBVOLUME
|REMOVE_ROOT
|REMOVE_PHYSICAL
)) {
191 /* Try to remove as subvolume first */
192 r
= btrfs_subvol_remove(path
, BTRFS_REMOVE_RECURSIVE
|BTRFS_REMOVE_QUOTA
);
196 if (r
!= -ENOTTY
&& r
!= -EINVAL
&& r
!= -ENOTDIR
)
199 /* Not btrfs or not a subvolume */
202 fd
= open(path
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
205 if (errno
!= ENOTDIR
&& errno
!= ELOOP
)
208 if (!(flags
& REMOVE_PHYSICAL
)) {
209 if (statfs(path
, &s
) < 0)
212 if (is_physical_fs(&s
)) {
213 log_error("Attempted to remove disk file system, and we can't allow that.");
218 if ((flags
& REMOVE_ROOT
) && !(flags
& REMOVE_ONLY_DIRECTORIES
))
219 if (unlink(path
) < 0 && errno
!= ENOENT
)
225 r
= rm_rf_children(fd
, flags
, NULL
);
227 if (flags
& REMOVE_ROOT
) {
228 if (rmdir(path
) < 0) {
229 if (r
== 0 && errno
!= ENOENT
)