]>
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/>.
26 #include <sys/statfs.h>
29 #include "btrfs-util.h"
30 #include "cgroup-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
;
51 /* This returns the first error we run into, but nevertheless
52 * tries to go on. This closes the passed fd. */
54 if (!(flags
& REMOVE_PHYSICAL
)) {
56 r
= fstatfs(fd
, &sfs
);
62 if (is_physical_fs(&sfs
)) {
63 /* We refuse to clean physical file systems
64 * with this call, unless explicitly
65 * requested. This is extra paranoia just to
66 * be sure we never ever remove non-state
69 log_error("Attempted to remove disk file system, and we can't allow that.");
78 return errno
== ENOENT
? 0 : -errno
;
89 if (errno
> 0 && ret
== 0)
94 if (streq(de
->d_name
, ".") || streq(de
->d_name
, ".."))
97 if (de
->d_type
== DT_UNKNOWN
||
98 (de
->d_type
== DT_DIR
&& (root_dev
|| (flags
& REMOVE_SUBVOLUME
)))) {
99 if (fstatat(fd
, de
->d_name
, &st
, AT_SYMLINK_NOFOLLOW
) < 0) {
100 if (ret
== 0 && errno
!= ENOENT
)
105 is_dir
= S_ISDIR(st
.st_mode
);
107 is_dir
= de
->d_type
== DT_DIR
;
112 /* if root_dev is set, remove subdirectories only if device is same */
113 if (root_dev
&& st
.st_dev
!= root_dev
->st_dev
)
116 subdir_fd
= openat(fd
, de
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
118 if (ret
== 0 && errno
!= ENOENT
)
123 /* Stop at mount points */
124 r
= fd_is_mount_point(fd
, de
->d_name
, 0);
126 if (ret
== 0 && r
!= -ENOENT
)
129 safe_close(subdir_fd
);
133 safe_close(subdir_fd
);
137 if ((flags
& REMOVE_SUBVOLUME
) && st
.st_ino
== 256) {
139 /* This could be a subvolume, try to remove it */
141 r
= btrfs_subvol_remove_fd(fd
, de
->d_name
, BTRFS_REMOVE_RECURSIVE
|BTRFS_REMOVE_QUOTA
);
143 if (r
!= -ENOTTY
&& r
!= -EINVAL
) {
147 safe_close(subdir_fd
);
151 /* ENOTTY, then it wasn't a
152 * btrfs subvolume, continue
155 /* It was a subvolume, continue. */
156 safe_close(subdir_fd
);
161 /* We pass REMOVE_PHYSICAL here, to avoid
162 * doing the fstatfs() to check the file
163 * system type again for each directory */
164 r
= rm_rf_children(subdir_fd
, flags
| REMOVE_PHYSICAL
, root_dev
);
165 if (r
< 0 && ret
== 0)
168 if (unlinkat(fd
, de
->d_name
, AT_REMOVEDIR
) < 0) {
169 if (ret
== 0 && errno
!= ENOENT
)
173 } else if (!(flags
& REMOVE_ONLY_DIRECTORIES
)) {
175 if (unlinkat(fd
, de
->d_name
, 0) < 0) {
176 if (ret
== 0 && errno
!= ENOENT
)
183 int rm_rf(const char *path
, RemoveFlags flags
) {
189 /* We refuse to clean the root file system with this
190 * call. This is extra paranoia to never cause a really
191 * seriously broken system. */
192 if (path_equal(path
, "/")) {
193 log_error("Attempted to remove entire root file system, and we can't allow that.");
197 if ((flags
& (REMOVE_SUBVOLUME
|REMOVE_ROOT
|REMOVE_PHYSICAL
)) == (REMOVE_SUBVOLUME
|REMOVE_ROOT
|REMOVE_PHYSICAL
)) {
198 /* Try to remove as subvolume first */
199 r
= btrfs_subvol_remove(path
, BTRFS_REMOVE_RECURSIVE
|BTRFS_REMOVE_QUOTA
);
203 if (r
!= -ENOTTY
&& r
!= -EINVAL
&& r
!= -ENOTDIR
)
206 /* Not btrfs or not a subvolume */
209 fd
= open(path
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
212 if (errno
!= ENOTDIR
&& errno
!= ELOOP
)
215 if (!(flags
& REMOVE_PHYSICAL
)) {
216 if (statfs(path
, &s
) < 0)
219 if (is_physical_fs(&s
)) {
220 log_error("Attempted to remove disk file system, and we can't allow that.");
225 if ((flags
& REMOVE_ROOT
) && !(flags
& REMOVE_ONLY_DIRECTORIES
))
226 if (unlink(path
) < 0 && errno
!= ENOENT
)
232 r
= rm_rf_children(fd
, flags
, NULL
);
234 if (flags
& REMOVE_ROOT
) {
235 if (rmdir(path
) < 0) {
236 if (r
== 0 && errno
!= ENOENT
)