diff options
Diffstat (limited to 'meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch')
-rw-r--r-- | meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch b/meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch new file mode 100644 index 0000000000..f80e6433c6 --- /dev/null +++ b/meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch | |||
@@ -0,0 +1,318 @@ | |||
1 | Backport of the following upstream commit: | ||
2 | From 96906b22417c65d70933976e0ee920c70c9113a4 Mon Sep 17 00:00:00 2001 | ||
3 | From: Lennart Poettering <lennart@poettering.net> | ||
4 | Date: Tue, 26 Jan 2021 16:30:06 +0100 | ||
5 | Subject: [PATCH] rm-rf: refactor rm_rf_children(), split out body of directory | ||
6 | iteration loop | ||
7 | |||
8 | This splits out rm_rf_children_inner() as body of the loop. We can use | ||
9 | that to implement rm_rf_child() for deleting one specific entry in a | ||
10 | directory. | ||
11 | |||
12 | Upstream-Status: Backport [http://archive.ubuntu.com/ubuntu/pool/main/s/systemd/systemd_245.4-4ubuntu3.15.debian.tar.xz] | ||
13 | Signed-off-by: Purushottam Choudhary <Purushottam.Choudhary@kpit.com> | ||
14 | --- | ||
15 | src/basic/rm-rf.c | 223 ++++++++++++++++++++++++++------------------- | ||
16 | src/basic/rm-rf.h | 3 +- | ||
17 | 2 files changed, 131 insertions(+), 95 deletions(-) | ||
18 | |||
19 | --- a/src/basic/rm-rf.c | ||
20 | +++ b/src/basic/rm-rf.c | ||
21 | @@ -19,138 +19,153 @@ | ||
22 | #include "stat-util.h" | ||
23 | #include "string-util.h" | ||
24 | |||
25 | +/* We treat tmpfs/ramfs + cgroupfs as non-physical file sytems. cgroupfs is similar to tmpfs in a way after | ||
26 | + * all: we can create arbitrary directory hierarchies in it, and hence can also use rm_rf() on it to remove | ||
27 | + * those again. */ | ||
28 | static bool is_physical_fs(const struct statfs *sfs) { | ||
29 | return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs); | ||
30 | } | ||
31 | |||
32 | -int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { | ||
33 | +static int rm_rf_children_inner( | ||
34 | + int fd, | ||
35 | + const char *fname, | ||
36 | + int is_dir, | ||
37 | + RemoveFlags flags, | ||
38 | + const struct stat *root_dev) { | ||
39 | + | ||
40 | + struct stat st; | ||
41 | + int r; | ||
42 | + | ||
43 | + assert(fd >= 0); | ||
44 | + assert(fname); | ||
45 | + | ||
46 | + if (is_dir < 0 || (is_dir > 0 && (root_dev || (flags & REMOVE_SUBVOLUME)))) { | ||
47 | + | ||
48 | + r = fstatat(fd, fname, &st, AT_SYMLINK_NOFOLLOW); | ||
49 | + if (r < 0) | ||
50 | + return r; | ||
51 | + | ||
52 | + is_dir = S_ISDIR(st.st_mode); | ||
53 | + } | ||
54 | + | ||
55 | + if (is_dir) { | ||
56 | + _cleanup_close_ int subdir_fd = -1; | ||
57 | + int q; | ||
58 | + | ||
59 | + /* if root_dev is set, remove subdirectories only if device is same */ | ||
60 | + if (root_dev && st.st_dev != root_dev->st_dev) | ||
61 | + return 0; | ||
62 | + | ||
63 | + /* Stop at mount points */ | ||
64 | + r = fd_is_mount_point(fd, fname, 0); | ||
65 | + if (r < 0) | ||
66 | + return r; | ||
67 | + if (r > 0) | ||
68 | + return 0; | ||
69 | + | ||
70 | + if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { | ||
71 | + | ||
72 | + /* This could be a subvolume, try to remove it */ | ||
73 | + | ||
74 | + r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); | ||
75 | + if (r < 0) { | ||
76 | + if (!IN_SET(r, -ENOTTY, -EINVAL)) | ||
77 | + return r; | ||
78 | + | ||
79 | + /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */ | ||
80 | + } else | ||
81 | + /* It was a subvolume, done. */ | ||
82 | + return 1; | ||
83 | + } | ||
84 | + | ||
85 | + subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); | ||
86 | + if (subdir_fd < 0) | ||
87 | + return -errno; | ||
88 | + | ||
89 | + /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file system type | ||
90 | + * again for each directory */ | ||
91 | + q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); | ||
92 | + | ||
93 | + r = unlinkat(fd, fname, AT_REMOVEDIR); | ||
94 | + if (r < 0) | ||
95 | + return r; | ||
96 | + if (q < 0) | ||
97 | + return q; | ||
98 | + | ||
99 | + return 1; | ||
100 | + | ||
101 | + } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { | ||
102 | + r = unlinkat(fd, fname, 0); | ||
103 | + if (r < 0) | ||
104 | + return r; | ||
105 | + | ||
106 | + return 1; | ||
107 | + } | ||
108 | + | ||
109 | + return 0; | ||
110 | +} | ||
111 | + | ||
112 | +int rm_rf_children( | ||
113 | + int fd, | ||
114 | + RemoveFlags flags, | ||
115 | + const struct stat *root_dev) { | ||
116 | + | ||
117 | _cleanup_closedir_ DIR *d = NULL; | ||
118 | struct dirent *de; | ||
119 | int ret = 0, r; | ||
120 | - struct statfs sfs; | ||
121 | |||
122 | assert(fd >= 0); | ||
123 | |||
124 | /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed | ||
125 | - * fd, in all cases, including on failure.. */ | ||
126 | + * fd, in all cases, including on failure. */ | ||
127 | + | ||
128 | + d = fdopendir(fd); | ||
129 | + if (!d) { | ||
130 | + safe_close(fd); | ||
131 | + return -errno; | ||
132 | + } | ||
133 | |||
134 | if (!(flags & REMOVE_PHYSICAL)) { | ||
135 | + struct statfs sfs; | ||
136 | |||
137 | - r = fstatfs(fd, &sfs); | ||
138 | - if (r < 0) { | ||
139 | - safe_close(fd); | ||
140 | + if (fstatfs(dirfd(d), &sfs) < 0) | ||
141 | return -errno; | ||
142 | } | ||
143 | |||
144 | if (is_physical_fs(&sfs)) { | ||
145 | - /* We refuse to clean physical file systems with this call, | ||
146 | - * unless explicitly requested. This is extra paranoia just | ||
147 | - * to be sure we never ever remove non-state data. */ | ||
148 | + /* We refuse to clean physical file systems with this call, unless explicitly | ||
149 | + * requested. This is extra paranoia just to be sure we never ever remove non-state | ||
150 | + * data. */ | ||
151 | + | ||
152 | _cleanup_free_ char *path = NULL; | ||
153 | |||
154 | (void) fd_get_path(fd, &path); | ||
155 | - log_error("Attempted to remove disk file system under \"%s\", and we can't allow that.", | ||
156 | - strna(path)); | ||
157 | - | ||
158 | - safe_close(fd); | ||
159 | - return -EPERM; | ||
160 | + return log_error_errno(SYNTHETIC_ERRNO(EPERM), | ||
161 | + "Attempted to remove disk file system under \"%s\", and we can't allow that.", | ||
162 | + strna(path)); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | - d = fdopendir(fd); | ||
167 | - if (!d) { | ||
168 | - safe_close(fd); | ||
169 | - return errno == ENOENT ? 0 : -errno; | ||
170 | - } | ||
171 | - | ||
172 | FOREACH_DIRENT_ALL(de, d, return -errno) { | ||
173 | - bool is_dir; | ||
174 | - struct stat st; | ||
175 | + int is_dir; | ||
176 | |||
177 | if (dot_or_dot_dot(de->d_name)) | ||
178 | continue; | ||
179 | |||
180 | - if (de->d_type == DT_UNKNOWN || | ||
181 | - (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) { | ||
182 | - if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { | ||
183 | - if (ret == 0 && errno != ENOENT) | ||
184 | - ret = -errno; | ||
185 | - continue; | ||
186 | - } | ||
187 | - | ||
188 | - is_dir = S_ISDIR(st.st_mode); | ||
189 | - } else | ||
190 | - is_dir = de->d_type == DT_DIR; | ||
191 | - | ||
192 | - if (is_dir) { | ||
193 | - _cleanup_close_ int subdir_fd = -1; | ||
194 | - | ||
195 | - /* if root_dev is set, remove subdirectories only if device is same */ | ||
196 | - if (root_dev && st.st_dev != root_dev->st_dev) | ||
197 | - continue; | ||
198 | - | ||
199 | - subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); | ||
200 | - if (subdir_fd < 0) { | ||
201 | - if (ret == 0 && errno != ENOENT) | ||
202 | - ret = -errno; | ||
203 | - continue; | ||
204 | - } | ||
205 | - | ||
206 | - /* Stop at mount points */ | ||
207 | - r = fd_is_mount_point(fd, de->d_name, 0); | ||
208 | - if (r < 0) { | ||
209 | - if (ret == 0 && r != -ENOENT) | ||
210 | - ret = r; | ||
211 | - | ||
212 | - continue; | ||
213 | - } | ||
214 | - if (r > 0) | ||
215 | - continue; | ||
216 | - | ||
217 | - if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { | ||
218 | - | ||
219 | - /* This could be a subvolume, try to remove it */ | ||
220 | - | ||
221 | - r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); | ||
222 | - if (r < 0) { | ||
223 | - if (!IN_SET(r, -ENOTTY, -EINVAL)) { | ||
224 | - if (ret == 0) | ||
225 | - ret = r; | ||
226 | - | ||
227 | - continue; | ||
228 | - } | ||
229 | - | ||
230 | - /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */ | ||
231 | - } else | ||
232 | - /* It was a subvolume, continue. */ | ||
233 | - continue; | ||
234 | - } | ||
235 | - | ||
236 | - /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file | ||
237 | - * system type again for each directory */ | ||
238 | - r = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); | ||
239 | - if (r < 0 && ret == 0) | ||
240 | - ret = r; | ||
241 | - | ||
242 | - if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { | ||
243 | - if (ret == 0 && errno != ENOENT) | ||
244 | - ret = -errno; | ||
245 | - } | ||
246 | - | ||
247 | - } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { | ||
248 | - | ||
249 | - if (unlinkat(fd, de->d_name, 0) < 0) { | ||
250 | - if (ret == 0 && errno != ENOENT) | ||
251 | - ret = -errno; | ||
252 | - } | ||
253 | - } | ||
254 | + is_dir = | ||
255 | + de->d_type == DT_UNKNOWN ? -1 : | ||
256 | + de->d_type == DT_DIR; | ||
257 | + | ||
258 | + r = rm_rf_children_inner(dirfd(d), de->d_name, is_dir, flags, root_dev); | ||
259 | + if (r < 0 && r != -ENOENT && ret == 0) | ||
260 | + ret = r; | ||
261 | } | ||
262 | + | ||
263 | return ret; | ||
264 | } | ||
265 | |||
266 | int rm_rf(const char *path, RemoveFlags flags) { | ||
267 | int fd, r; | ||
268 | - struct statfs s; | ||
269 | |||
270 | assert(path); | ||
271 | |||
272 | @@ -195,9 +210,10 @@ | ||
273 | if (FLAGS_SET(flags, REMOVE_ROOT)) { | ||
274 | |||
275 | if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) { | ||
276 | + struct statfs s; | ||
277 | + | ||
278 | if (statfs(path, &s) < 0) | ||
279 | return -errno; | ||
280 | - | ||
281 | if (is_physical_fs(&s)) | ||
282 | return log_error_errno(SYNTHETIC_ERRNO(EPERM), | ||
283 | "Attempted to remove files from a disk file system under \"%s\", refusing.", | ||
284 | @@ -225,3 +241,22 @@ | ||
285 | |||
286 | return r; | ||
287 | } | ||
288 | + | ||
289 | +int rm_rf_child(int fd, const char *name, RemoveFlags flags) { | ||
290 | + | ||
291 | + /* Removes one specific child of the specified directory */ | ||
292 | + | ||
293 | + if (fd < 0) | ||
294 | + return -EBADF; | ||
295 | + | ||
296 | + if (!filename_is_valid(name)) | ||
297 | + return -EINVAL; | ||
298 | + | ||
299 | + if ((flags & (REMOVE_ROOT|REMOVE_MISSING_OK)) != 0) /* Doesn't really make sense here, we are not supposed to remove 'fd' anyway */ | ||
300 | + return -EINVAL; | ||
301 | + | ||
302 | + if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME)) | ||
303 | + return -EINVAL; | ||
304 | + | ||
305 | + return rm_rf_children_inner(fd, name, -1, flags, NULL); | ||
306 | +} | ||
307 | --- a/src/basic/rm-rf.h | ||
308 | +++ b/src/basic/rm-rf.h | ||
309 | @@ -13,7 +13,8 @@ | ||
310 | REMOVE_MISSING_OK = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */ | ||
311 | } RemoveFlags; | ||
312 | |||
313 | -int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev); | ||
314 | +int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev); | ||
315 | +int rm_rf_child(int fd, const char *name, RemoveFlags flags); | ||
316 | int rm_rf(const char *path, RemoveFlags flags); | ||
317 | |||
318 | /* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */ | ||