]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/rm-rf.c
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/>.
28 #include <sys/statfs.h>
31 #include "btrfs-util.h"
35 #include "mount-util.h"
36 #include "path-util.h"
38 #include "stat-util.h"
39 #include "string-util.h"
41 int rm_rf_children(int fd
, RemoveFlags flags
, struct stat
*root_dev
) {
42 _cleanup_closedir_
DIR *d
= NULL
;
47 /* This returns the first error we run into, but nevertheless
48 * tries to go on. This closes the passed fd. */
50 if (!(flags
& REMOVE_PHYSICAL
)) {
52 r
= fd_is_temporary_fs(fd
);
59 /* We refuse to clean physical file systems
60 * with this call, unless explicitly
61 * requested. This is extra paranoia just to
62 * be sure we never ever remove non-state
65 log_error("Attempted to remove disk file system, and we can't allow that.");
74 return errno
== ENOENT
? 0 : -errno
;
85 if (errno
> 0 && ret
== 0)
90 if (streq(de
->d_name
, ".") || streq(de
->d_name
, ".."))
93 if (de
->d_type
== DT_UNKNOWN
||
94 (de
->d_type
== DT_DIR
&& (root_dev
|| (flags
& REMOVE_SUBVOLUME
)))) {
95 if (fstatat(fd
, de
->d_name
, &st
, AT_SYMLINK_NOFOLLOW
) < 0) {
96 if (ret
== 0 && errno
!= ENOENT
)
101 is_dir
= S_ISDIR(st
.st_mode
);
103 is_dir
= de
->d_type
== DT_DIR
;
108 /* if root_dev is set, remove subdirectories only if device is same */
109 if (root_dev
&& st
.st_dev
!= root_dev
->st_dev
)
112 subdir_fd
= openat(fd
, de
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
114 if (ret
== 0 && errno
!= ENOENT
)
119 /* Stop at mount points */
120 r
= fd_is_mount_point(fd
, de
->d_name
, 0);
122 if (ret
== 0 && r
!= -ENOENT
)
125 safe_close(subdir_fd
);
129 safe_close(subdir_fd
);
133 if ((flags
& REMOVE_SUBVOLUME
) && st
.st_ino
== 256) {
135 /* This could be a subvolume, try to remove it */
137 r
= btrfs_subvol_remove_fd(fd
, de
->d_name
, BTRFS_REMOVE_RECURSIVE
|BTRFS_REMOVE_QUOTA
);
139 if (r
!= -ENOTTY
&& r
!= -EINVAL
) {
143 safe_close(subdir_fd
);
147 /* ENOTTY, then it wasn't a
148 * btrfs subvolume, continue
151 /* It was a subvolume, continue. */
152 safe_close(subdir_fd
);
157 /* We pass REMOVE_PHYSICAL here, to avoid
158 * doing the fstatfs() to check the file
159 * system type again for each directory */
160 r
= rm_rf_children(subdir_fd
, flags
| REMOVE_PHYSICAL
, root_dev
);
161 if (r
< 0 && ret
== 0)
164 if (unlinkat(fd
, de
->d_name
, AT_REMOVEDIR
) < 0) {
165 if (ret
== 0 && errno
!= ENOENT
)
169 } else if (!(flags
& REMOVE_ONLY_DIRECTORIES
)) {
171 if (unlinkat(fd
, de
->d_name
, 0) < 0) {
172 if (ret
== 0 && errno
!= ENOENT
)
179 int rm_rf(const char *path
, RemoveFlags flags
) {
185 /* We refuse to clean the root file system with this
186 * call. This is extra paranoia to never cause a really
187 * seriously broken system. */
188 if (path_equal(path
, "/")) {
189 log_error("Attempted to remove entire root file system, and we can't allow that.");
193 if ((flags
& (REMOVE_SUBVOLUME
|REMOVE_ROOT
|REMOVE_PHYSICAL
)) == (REMOVE_SUBVOLUME
|REMOVE_ROOT
|REMOVE_PHYSICAL
)) {
194 /* Try to remove as subvolume first */
195 r
= btrfs_subvol_remove(path
, BTRFS_REMOVE_RECURSIVE
|BTRFS_REMOVE_QUOTA
);
199 if (r
!= -ENOTTY
&& r
!= -EINVAL
&& r
!= -ENOTDIR
)
202 /* Not btrfs or not a subvolume */
205 fd
= open(path
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
208 if (errno
!= ENOTDIR
&& errno
!= ELOOP
)
211 if (!(flags
& REMOVE_PHYSICAL
)) {
212 if (statfs(path
, &s
) < 0)
215 if (!is_temporary_fs(&s
)) {
216 log_error("Attempted to remove disk file system, and we can't allow that.");
221 if ((flags
& REMOVE_ROOT
) && !(flags
& REMOVE_ONLY_DIRECTORIES
))
222 if (unlink(path
) < 0 && errno
!= ENOENT
)
228 r
= rm_rf_children(fd
, flags
, NULL
);
230 if (flags
& REMOVE_ROOT
) {
231 if (rmdir(path
) < 0) {
232 if (r
== 0 && errno
!= ENOENT
)