]>
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
13 #include <sys/statfs.h>
16 #include "btrfs-util.h"
17 #include "cgroup-util.h"
18 #include "dirent-util.h"
22 #include "mount-util.h"
23 #include "path-util.h"
25 #include "stat-util.h"
26 #include "string-util.h"
28 static bool is_physical_fs(const struct statfs
*sfs
) {
29 return !is_temporary_fs(sfs
) && !is_cgroup_fs(sfs
);
32 int rm_rf_children(int fd
, RemoveFlags flags
, struct stat
*root_dev
) {
33 _cleanup_closedir_
DIR *d
= NULL
;
40 /* This returns the first error we run into, but nevertheless
41 * tries to go on. This closes the passed fd. */
43 if (!(flags
& REMOVE_PHYSICAL
)) {
45 r
= fstatfs(fd
, &sfs
);
51 if (is_physical_fs(&sfs
)) {
52 /* We refuse to clean physical file systems
53 * with this call, unless explicitly
54 * requested. This is extra paranoia just to
55 * be sure we never ever remove non-state
58 log_error("Attempted to remove disk file system, and we can't allow that.");
67 return errno
== ENOENT
? 0 : -errno
;
70 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
74 if (dot_or_dot_dot(de
->d_name
))
77 if (de
->d_type
== DT_UNKNOWN
||
78 (de
->d_type
== DT_DIR
&& (root_dev
|| (flags
& REMOVE_SUBVOLUME
)))) {
79 if (fstatat(fd
, de
->d_name
, &st
, AT_SYMLINK_NOFOLLOW
) < 0) {
80 if (ret
== 0 && errno
!= ENOENT
)
85 is_dir
= S_ISDIR(st
.st_mode
);
87 is_dir
= de
->d_type
== DT_DIR
;
92 /* if root_dev is set, remove subdirectories only if device is same */
93 if (root_dev
&& st
.st_dev
!= root_dev
->st_dev
)
96 subdir_fd
= openat(fd
, de
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
98 if (ret
== 0 && errno
!= ENOENT
)
103 /* Stop at mount points */
104 r
= fd_is_mount_point(fd
, de
->d_name
, 0);
106 if (ret
== 0 && r
!= -ENOENT
)
109 safe_close(subdir_fd
);
113 safe_close(subdir_fd
);
117 if ((flags
& REMOVE_SUBVOLUME
) && st
.st_ino
== 256) {
119 /* This could be a subvolume, try to remove it */
121 r
= btrfs_subvol_remove_fd(fd
, de
->d_name
, BTRFS_REMOVE_RECURSIVE
|BTRFS_REMOVE_QUOTA
);
123 if (!IN_SET(r
, -ENOTTY
, -EINVAL
)) {
127 safe_close(subdir_fd
);
131 /* ENOTTY, then it wasn't a
132 * btrfs subvolume, continue
135 /* It was a subvolume, continue. */
136 safe_close(subdir_fd
);
141 /* We pass REMOVE_PHYSICAL here, to avoid
142 * doing the fstatfs() to check the file
143 * system type again for each directory */
144 r
= rm_rf_children(subdir_fd
, flags
| REMOVE_PHYSICAL
, root_dev
);
145 if (r
< 0 && ret
== 0)
148 if (unlinkat(fd
, de
->d_name
, AT_REMOVEDIR
) < 0) {
149 if (ret
== 0 && errno
!= ENOENT
)
153 } else if (!(flags
& REMOVE_ONLY_DIRECTORIES
)) {
155 if (unlinkat(fd
, de
->d_name
, 0) < 0) {
156 if (ret
== 0 && errno
!= ENOENT
)
164 int rm_rf(const char *path
, RemoveFlags flags
) {
170 /* We refuse to clean the root file system with this
171 * call. This is extra paranoia to never cause a really
172 * seriously broken system. */
173 if (path_equal_or_files_same(path
, "/", AT_SYMLINK_NOFOLLOW
)) {
174 log_error("Attempted to remove entire root file system, and we can't allow that.");
178 if ((flags
& (REMOVE_SUBVOLUME
|REMOVE_ROOT
|REMOVE_PHYSICAL
)) == (REMOVE_SUBVOLUME
|REMOVE_ROOT
|REMOVE_PHYSICAL
)) {
179 /* Try to remove as subvolume first */
180 r
= btrfs_subvol_remove(path
, BTRFS_REMOVE_RECURSIVE
|BTRFS_REMOVE_QUOTA
);
184 if (!IN_SET(r
, -ENOTTY
, -EINVAL
, -ENOTDIR
))
187 /* Not btrfs or not a subvolume */
190 fd
= open(path
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
193 if (!IN_SET(errno
, ENOTDIR
, ELOOP
))
196 if (!(flags
& REMOVE_PHYSICAL
)) {
197 if (statfs(path
, &s
) < 0)
200 if (is_physical_fs(&s
)) {
201 log_error("Attempted to remove disk file system, and we can't allow that.");
206 if ((flags
& REMOVE_ROOT
) && !(flags
& REMOVE_ONLY_DIRECTORIES
))
207 if (unlink(path
) < 0 && errno
!= ENOENT
)
213 r
= rm_rf_children(fd
, flags
, NULL
);
215 if (flags
& REMOVE_ROOT
) {
216 if (rmdir(path
) < 0) {
217 if (r
== 0 && errno
!= ENOENT
)