]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/rm-rf.c
Merge pull request #2068 from grawity/cgls-error-v2
[thirdparty/systemd.git] / src / basic / rm-rf.c
CommitLineData
c6878637
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2015 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
d9e2daaf 22#include "btrfs-util.h"
3ffd4af2 23#include "fd-util.h"
4349cd7c 24#include "mount-util.h"
07630cea 25#include "path-util.h"
3ffd4af2 26#include "rm-rf.h"
8fcde012 27#include "stat-util.h"
07630cea
LP
28#include "string-util.h"
29#include "util.h"
c6878637
LP
30
31int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
32 _cleanup_closedir_ DIR *d = NULL;
33 int ret = 0, r;
34
35 assert(fd >= 0);
36
37 /* This returns the first error we run into, but nevertheless
38 * tries to go on. This closes the passed fd. */
39
40 if (!(flags & REMOVE_PHYSICAL)) {
41
42 r = fd_is_temporary_fs(fd);
43 if (r < 0) {
44 safe_close(fd);
45 return r;
46 }
47
48 if (!r) {
49 /* We refuse to clean physical file systems
50 * with this call, unless explicitly
51 * requested. This is extra paranoia just to
52 * be sure we never ever remove non-state
53 * data */
54
55 log_error("Attempted to remove disk file system, and we can't allow that.");
56 safe_close(fd);
57 return -EPERM;
58 }
59 }
60
61 d = fdopendir(fd);
62 if (!d) {
63 safe_close(fd);
64 return errno == ENOENT ? 0 : -errno;
65 }
66
67 for (;;) {
68 struct dirent *de;
69 bool is_dir;
70 struct stat st;
71
72 errno = 0;
73 de = readdir(d);
74 if (!de) {
75 if (errno != 0 && ret == 0)
76 ret = -errno;
77 return ret;
78 }
79
80 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
81 continue;
82
9e9b663a
LP
83 if (de->d_type == DT_UNKNOWN ||
84 (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
c6878637
LP
85 if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
86 if (ret == 0 && errno != ENOENT)
87 ret = -errno;
88 continue;
89 }
90
91 is_dir = S_ISDIR(st.st_mode);
92 } else
93 is_dir = de->d_type == DT_DIR;
94
95 if (is_dir) {
96 int subdir_fd;
97
f25afeb6 98 /* if root_dev is set, remove subdirectories only if device is same */
c6878637
LP
99 if (root_dev && st.st_dev != root_dev->st_dev)
100 continue;
101
102 subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
103 if (subdir_fd < 0) {
104 if (ret == 0 && errno != ENOENT)
105 ret = -errno;
106 continue;
107 }
108
f25afeb6 109 /* Stop at mount points */
5d409034 110 r = fd_is_mount_point(fd, de->d_name, 0);
f25afeb6
LP
111 if (r < 0) {
112 if (ret == 0 && r != -ENOENT)
113 ret = r;
114
115 safe_close(subdir_fd);
116 continue;
117 }
118 if (r) {
119 safe_close(subdir_fd);
120 continue;
121 }
122
9e9b663a 123 if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) {
9e9b663a
LP
124
125 /* This could be a subvolume, try to remove it */
126
5bcd08db 127 r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
d9e2daaf
LP
128 if (r < 0) {
129 if (r != -ENOTTY && r != -EINVAL) {
9e9b663a 130 if (ret == 0)
d9e2daaf 131 ret = r;
9e9b663a
LP
132
133 safe_close(subdir_fd);
134 continue;
135 }
136
d9e2daaf
LP
137 /* ENOTTY, then it wasn't a
138 * btrfs subvolume, continue
139 * below. */
9e9b663a
LP
140 } else {
141 /* It was a subvolume, continue. */
142 safe_close(subdir_fd);
143 continue;
144 }
145 }
146
c6878637
LP
147 /* We pass REMOVE_PHYSICAL here, to avoid
148 * doing the fstatfs() to check the file
149 * system type again for each directory */
150 r = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev);
151 if (r < 0 && ret == 0)
152 ret = r;
153
154 if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
155 if (ret == 0 && errno != ENOENT)
156 ret = -errno;
157 }
158
159 } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
160
161 if (unlinkat(fd, de->d_name, 0) < 0) {
162 if (ret == 0 && errno != ENOENT)
163 ret = -errno;
164 }
165 }
166 }
167}
168
169int rm_rf(const char *path, RemoveFlags flags) {
170 int fd, r;
171 struct statfs s;
172
173 assert(path);
174
175 /* We refuse to clean the root file system with this
176 * call. This is extra paranoia to never cause a really
177 * seriously broken system. */
178 if (path_equal(path, "/")) {
179 log_error("Attempted to remove entire root file system, and we can't allow that.");
180 return -EPERM;
181 }
182
d9e2daaf
LP
183 if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) {
184 /* Try to remove as subvolume first */
5bcd08db 185 r = btrfs_subvol_remove(path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
d9e2daaf
LP
186 if (r >= 0)
187 return r;
188
636aabc2 189 if (r != -ENOTTY && r != -EINVAL && r != -ENOTDIR)
d9e2daaf
LP
190 return r;
191
192 /* Not btrfs or not a subvolume */
193 }
194
c6878637
LP
195 fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
196 if (fd < 0) {
197
198 if (errno != ENOTDIR && errno != ELOOP)
199 return -errno;
200
201 if (!(flags & REMOVE_PHYSICAL)) {
202 if (statfs(path, &s) < 0)
203 return -errno;
204
205 if (!is_temporary_fs(&s)) {
206 log_error("Attempted to remove disk file system, and we can't allow that.");
207 return -EPERM;
208 }
209 }
210
211 if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES))
212 if (unlink(path) < 0 && errno != ENOENT)
213 return -errno;
214
215 return 0;
216 }
217
218 r = rm_rf_children(fd, flags, NULL);
219
220 if (flags & REMOVE_ROOT) {
d9e2daaf
LP
221 if (rmdir(path) < 0) {
222 if (r == 0 && errno != ENOENT)
c6878637
LP
223 r = -errno;
224 }
225 }
226
227 return r;
228}