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