diff options
Diffstat (limited to 'meta/recipes-core/systemd/systemd/0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch')
-rw-r--r-- | meta/recipes-core/systemd/systemd/0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch | 643 |
1 files changed, 643 insertions, 0 deletions
diff --git a/meta/recipes-core/systemd/systemd/0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch b/meta/recipes-core/systemd/systemd/0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch new file mode 100644 index 0000000000..108e4ad8b8 --- /dev/null +++ b/meta/recipes-core/systemd/systemd/0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch | |||
@@ -0,0 +1,643 @@ | |||
1 | From 33dc9a280f952f503e5493ee29f6815bef29d551 Mon Sep 17 00:00:00 2001 | ||
2 | From: Franck Bui <fbui@suse.com> | ||
3 | Date: Fri, 2 Mar 2018 17:19:32 +0100 | ||
4 | Subject: [PATCH] tmpfiles: don't resolve pathnames when traversing recursively | ||
5 | through directory trees | ||
6 | |||
7 | Otherwise we can be fooled if one path component is replaced underneath us. | ||
8 | |||
9 | The patch achieves that by always operating at file descriptor level (by using | ||
10 | *at() helpers) and by making sure we do not any path resolution when traversing | ||
11 | direcotry trees. | ||
12 | |||
13 | However this is not always possible, for instance when listing the content of a | ||
14 | directory or some operations don't provide the *at() helpers or others (such as | ||
15 | fchmodat()) don't have the AT_EMPTY_PATH flag. In such cases we operate on | ||
16 | /proc/self/fd/%i pseudo-symlink instead, which works the same for all kinds of | ||
17 | objects and requires no checking of type beforehand. | ||
18 | |||
19 | Also O_PATH flag is used when opening file objects in order to prevent | ||
20 | undesired behaviors: device nodes from reacting, automounts from | ||
21 | triggering, etc... | ||
22 | |||
23 | Fixes: CVE-2018-6954 | ||
24 | |||
25 | Origin: upstream, https://github.com/systemd/systemd/commit/936f6bdb803c432578e2cdcc5f93f3bfff93aff0 | ||
26 | Bug: https://github.com/systemd/systemd/issues/7986 | ||
27 | |||
28 | Patch from: | ||
29 | systemd_237-3ubuntu10.13.debian CVE-2018-6954.patch | ||
30 | |||
31 | https://usn.ubuntu.com/3816-1/ states that CVE-2018-6954 doesn't | ||
32 | affect Ubuntu 18.10 which uses the same version of systemd as thud | ||
33 | (239). | ||
34 | |||
35 | CVE: CVE-2018-6954 | ||
36 | Upstream-Status: Backport | ||
37 | |||
38 | Signed-off-by: George McCollister <george.mccollister@gmail.com> | ||
39 | --- | ||
40 | src/tmpfiles/tmpfiles.c | 363 +++++++++++++++++++++++++++++++----------------- | ||
41 | 1 file changed, 239 insertions(+), 124 deletions(-) | ||
42 | |||
43 | diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c | ||
44 | index 88cc543f09..613d418eb3 100644 | ||
45 | --- a/src/tmpfiles/tmpfiles.c | ||
46 | +++ b/src/tmpfiles/tmpfiles.c | ||
47 | @@ -792,94 +792,105 @@ static bool hardlink_vulnerable(struct stat *st) { | ||
48 | return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks(); | ||
49 | } | ||
50 | |||
51 | -static int path_set_perms(Item *i, const char *path) { | ||
52 | - char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
53 | - _cleanup_close_ int fd = -1; | ||
54 | - struct stat st; | ||
55 | +static int fd_set_perms(Item *i, int fd, const struct stat *st) { | ||
56 | + _cleanup_free_ char *path = NULL; | ||
57 | + int r; | ||
58 | |||
59 | assert(i); | ||
60 | - assert(path); | ||
61 | - | ||
62 | - if (!i->mode_set && !i->uid_set && !i->gid_set) | ||
63 | - goto shortcut; | ||
64 | - | ||
65 | - /* We open the file with O_PATH here, to make the operation | ||
66 | - * somewhat atomic. Also there's unfortunately no fchmodat() | ||
67 | - * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via | ||
68 | - * O_PATH. */ | ||
69 | - | ||
70 | - fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
71 | - if (fd < 0) { | ||
72 | - int level = LOG_ERR, r = -errno; | ||
73 | + assert(fd); | ||
74 | |||
75 | - /* Option "e" operates only on existing objects. Do not | ||
76 | - * print errors about non-existent files or directories */ | ||
77 | - if (i->type == EMPTY_DIRECTORY && errno == ENOENT) { | ||
78 | - level = LOG_DEBUG; | ||
79 | - r = 0; | ||
80 | - } | ||
81 | - | ||
82 | - log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path); | ||
83 | + r = fd_get_path(fd, &path); | ||
84 | + if (r < 0) | ||
85 | return r; | ||
86 | - } | ||
87 | |||
88 | - if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) | ||
89 | - return log_error_errno(errno, "Failed to fstat() file %s: %m", path); | ||
90 | + if (!i->mode_set && !i->uid_set && !i->gid_set) | ||
91 | + goto shortcut; | ||
92 | |||
93 | - if (hardlink_vulnerable(&st)) { | ||
94 | + if (hardlink_vulnerable(st)) { | ||
95 | log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); | ||
96 | return -EPERM; | ||
97 | } | ||
98 | |||
99 | - xsprintf(fn, "/proc/self/fd/%i", fd); | ||
100 | - | ||
101 | if (i->mode_set) { | ||
102 | - if (S_ISLNK(st.st_mode)) | ||
103 | + if (S_ISLNK(st->st_mode)) | ||
104 | log_debug("Skipping mode fix for symlink %s.", path); | ||
105 | else { | ||
106 | mode_t m = i->mode; | ||
107 | |||
108 | if (i->mask_perms) { | ||
109 | - if (!(st.st_mode & 0111)) | ||
110 | + if (!(st->st_mode & 0111)) | ||
111 | m &= ~0111; | ||
112 | - if (!(st.st_mode & 0222)) | ||
113 | + if (!(st->st_mode & 0222)) | ||
114 | m &= ~0222; | ||
115 | - if (!(st.st_mode & 0444)) | ||
116 | + if (!(st->st_mode & 0444)) | ||
117 | m &= ~0444; | ||
118 | - if (!S_ISDIR(st.st_mode)) | ||
119 | + if (!S_ISDIR(st->st_mode)) | ||
120 | m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */ | ||
121 | } | ||
122 | |||
123 | - if (m == (st.st_mode & 07777)) | ||
124 | - log_debug("\"%s\" has correct mode %o already.", path, st.st_mode); | ||
125 | + if (m == (st->st_mode & 07777)) | ||
126 | + log_debug("\"%s\" has correct mode %o already.", path, st->st_mode); | ||
127 | else { | ||
128 | + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
129 | + | ||
130 | log_debug("Changing \"%s\" to mode %o.", path, m); | ||
131 | |||
132 | - if (chmod(fn, m) < 0) | ||
133 | - return log_error_errno(errno, "chmod() of %s via %s failed: %m", path, fn); | ||
134 | + /* fchmodat() still doesn't have AT_EMPTY_PATH flag. */ | ||
135 | + xsprintf(procfs_path, "/proc/self/fd/%i", fd); | ||
136 | + | ||
137 | + if (chmod(procfs_path, m) < 0) | ||
138 | + return log_error_errno(errno, "chmod() of %s via %s failed: %m", path, procfs_path); | ||
139 | } | ||
140 | } | ||
141 | } | ||
142 | |||
143 | - if ((i->uid_set && i->uid != st.st_uid) || | ||
144 | - (i->gid_set && i->gid != st.st_gid)) { | ||
145 | + if ((i->uid_set && i->uid != st->st_uid) || | ||
146 | + (i->gid_set && i->gid != st->st_gid)) { | ||
147 | log_debug("Changing \"%s\" to owner "UID_FMT":"GID_FMT, | ||
148 | path, | ||
149 | i->uid_set ? i->uid : UID_INVALID, | ||
150 | i->gid_set ? i->gid : GID_INVALID); | ||
151 | |||
152 | - if (chown(fn, | ||
153 | - i->uid_set ? i->uid : UID_INVALID, | ||
154 | - i->gid_set ? i->gid : GID_INVALID) < 0) | ||
155 | - return log_error_errno(errno, "chown() of %s via %s failed: %m", path, fn); | ||
156 | + if (fchownat(fd, | ||
157 | + "", | ||
158 | + i->uid_set ? i->uid : UID_INVALID, | ||
159 | + i->gid_set ? i->gid : GID_INVALID, | ||
160 | + AT_EMPTY_PATH) < 0) | ||
161 | + return log_error_errno(errno, "fchownat() of %s failed: %m", path); | ||
162 | } | ||
163 | |||
164 | - fd = safe_close(fd); | ||
165 | - | ||
166 | shortcut: | ||
167 | return label_fix(path, false, false); | ||
168 | } | ||
169 | |||
170 | +static int path_set_perms(Item *i, const char *path) { | ||
171 | + _cleanup_close_ int fd = -1; | ||
172 | + struct stat st; | ||
173 | + | ||
174 | + assert(i); | ||
175 | + assert(path); | ||
176 | + | ||
177 | + fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
178 | + if (fd < 0) { | ||
179 | + int level = LOG_ERR, r = -errno; | ||
180 | + | ||
181 | + /* Option "e" operates only on existing objects. Do not | ||
182 | + * print errors about non-existent files or directories */ | ||
183 | + if (i->type == EMPTY_DIRECTORY && errno == ENOENT) { | ||
184 | + level = LOG_DEBUG; | ||
185 | + r = 0; | ||
186 | + } | ||
187 | + | ||
188 | + log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path); | ||
189 | + return r; | ||
190 | + } | ||
191 | + | ||
192 | + if (fstat(fd, &st) < 0) | ||
193 | + return log_error_errno(errno, "Failed to fstat() file %s: %m", path); | ||
194 | + | ||
195 | + return fd_set_perms(i, fd, &st); | ||
196 | +} | ||
197 | + | ||
198 | static int parse_xattrs_from_arg(Item *i) { | ||
199 | const char *p; | ||
200 | int r; | ||
201 | @@ -918,21 +929,43 @@ static int parse_xattrs_from_arg(Item *i) { | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | -static int path_set_xattrs(Item *i, const char *path) { | ||
206 | +static int fd_set_xattrs(Item *i, int fd, const struct stat *st) { | ||
207 | + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
208 | + _cleanup_free_ char *path = NULL; | ||
209 | char **name, **value; | ||
210 | + int r; | ||
211 | |||
212 | assert(i); | ||
213 | - assert(path); | ||
214 | + assert(fd); | ||
215 | + | ||
216 | + r = fd_get_path(fd, &path); | ||
217 | + if (r < 0) | ||
218 | + return r; | ||
219 | + | ||
220 | + xsprintf(procfs_path, "/proc/self/fd/%i", fd); | ||
221 | |||
222 | STRV_FOREACH_PAIR(name, value, i->xattrs) { | ||
223 | log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path); | ||
224 | - if (lsetxattr(path, *name, *value, strlen(*value), 0) < 0) | ||
225 | + if (setxattr(procfs_path, *name, *value, strlen(*value), 0) < 0) | ||
226 | return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m", | ||
227 | *name, *value, path); | ||
228 | } | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | +static int path_set_xattrs(Item *i, const char *path) { | ||
233 | + _cleanup_close_ int fd = -1; | ||
234 | + | ||
235 | + assert(i); | ||
236 | + assert(path); | ||
237 | + | ||
238 | + fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH); | ||
239 | + if (fd < 0) | ||
240 | + return log_error_errno(errno, "Cannot open '%s': %m", path); | ||
241 | + | ||
242 | + return fd_set_xattrs(i, fd, NULL); | ||
243 | +} | ||
244 | + | ||
245 | static int parse_acls_from_arg(Item *item) { | ||
246 | #if HAVE_ACL | ||
247 | int r; | ||
248 | @@ -998,52 +1031,71 @@ static int path_set_acl(const char *path, const char *pretty, acl_type_t type, a | ||
249 | } | ||
250 | #endif | ||
251 | |||
252 | -static int path_set_acls(Item *item, const char *path) { | ||
253 | +static int fd_set_acls(Item *item, int fd, const struct stat *st) { | ||
254 | int r = 0; | ||
255 | #if HAVE_ACL | ||
256 | - char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
257 | - _cleanup_close_ int fd = -1; | ||
258 | - struct stat st; | ||
259 | + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
260 | + _cleanup_free_ char *path = NULL; | ||
261 | |||
262 | assert(item); | ||
263 | - assert(path); | ||
264 | - | ||
265 | - fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
266 | - if (fd < 0) | ||
267 | - return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path); | ||
268 | + assert(fd); | ||
269 | + assert(st); | ||
270 | |||
271 | - if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) | ||
272 | - return log_error_errno(errno, "Failed to fstat() file %s: %m", path); | ||
273 | + r = fd_get_path(fd, &path); | ||
274 | + if (r < 0) | ||
275 | + return r; | ||
276 | |||
277 | - if (hardlink_vulnerable(&st)) { | ||
278 | + if (hardlink_vulnerable(st)) { | ||
279 | log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); | ||
280 | return -EPERM; | ||
281 | } | ||
282 | |||
283 | - if (S_ISLNK(st.st_mode)) { | ||
284 | + if (S_ISLNK(st->st_mode)) { | ||
285 | log_debug("Skipping ACL fix for symlink %s.", path); | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | - xsprintf(fn, "/proc/self/fd/%i", fd); | ||
290 | + xsprintf(procfs_path, "/proc/self/fd/%i", fd); | ||
291 | |||
292 | if (item->acl_access) | ||
293 | - r = path_set_acl(fn, path, ACL_TYPE_ACCESS, item->acl_access, item->force); | ||
294 | + r = path_set_acl(procfs_path, path, ACL_TYPE_ACCESS, item->acl_access, item->force); | ||
295 | |||
296 | if (r == 0 && item->acl_default) | ||
297 | - r = path_set_acl(fn, path, ACL_TYPE_DEFAULT, item->acl_default, item->force); | ||
298 | + r = path_set_acl(procfs_path, path, ACL_TYPE_DEFAULT, item->acl_default, item->force); | ||
299 | |||
300 | if (r > 0) | ||
301 | return -r; /* already warned */ | ||
302 | - else if (r == -EOPNOTSUPP) { | ||
303 | + if (r == -EOPNOTSUPP) { | ||
304 | log_debug_errno(r, "ACLs not supported by file system at %s", path); | ||
305 | return 0; | ||
306 | - } else if (r < 0) | ||
307 | - log_error_errno(r, "ACL operation on \"%s\" failed: %m", path); | ||
308 | + } | ||
309 | + if (r < 0) | ||
310 | + return log_error_errno(r, "ACL operation on \"%s\" failed: %m", path); | ||
311 | #endif | ||
312 | return r; | ||
313 | } | ||
314 | |||
315 | +static int path_set_acls(Item *item, const char *path) { | ||
316 | + int r = 0; | ||
317 | +#ifdef HAVE_ACL | ||
318 | + _cleanup_close_ int fd = -1; | ||
319 | + struct stat st; | ||
320 | + | ||
321 | + assert(item); | ||
322 | + assert(path); | ||
323 | + | ||
324 | + fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
325 | + if (fd < 0) | ||
326 | + return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path); | ||
327 | + | ||
328 | + if (fstat(fd, &st) < 0) | ||
329 | + return log_error_errno(errno, "Failed to fstat() file %s: %m", path); | ||
330 | + | ||
331 | + r = fd_set_acls(item, fd, &st); | ||
332 | + #endif | ||
333 | + return r; | ||
334 | + } | ||
335 | + | ||
336 | #define ATTRIBUTES_ALL \ | ||
337 | (FS_NOATIME_FL | \ | ||
338 | FS_SYNC_FL | \ | ||
339 | @@ -1143,30 +1195,24 @@ static int parse_attribute_from_arg(Item *item) { | ||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | -static int path_set_attribute(Item *item, const char *path) { | ||
344 | - _cleanup_close_ int fd = -1; | ||
345 | - struct stat st; | ||
346 | +static int fd_set_attribute(Item *item, int fd, const struct stat *st) { | ||
347 | + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
348 | + _cleanup_close_ int procfs_fd = -1; | ||
349 | + _cleanup_free_ char *path = NULL; | ||
350 | unsigned f; | ||
351 | int r; | ||
352 | |||
353 | if (!item->attribute_set || item->attribute_mask == 0) | ||
354 | return 0; | ||
355 | |||
356 | - fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOATIME|O_NOFOLLOW); | ||
357 | - if (fd < 0) { | ||
358 | - if (errno == ELOOP) | ||
359 | - return log_error_errno(errno, "Skipping file attributes adjustment on symlink %s.", path); | ||
360 | - | ||
361 | - return log_error_errno(errno, "Cannot open '%s': %m", path); | ||
362 | - } | ||
363 | - | ||
364 | - if (fstat(fd, &st) < 0) | ||
365 | - return log_error_errno(errno, "Cannot stat '%s': %m", path); | ||
366 | + r = fd_get_path(fd, &path); | ||
367 | + if (r < 0) | ||
368 | + return r; | ||
369 | |||
370 | /* Issuing the file attribute ioctls on device nodes is not | ||
371 | * safe, as that will be delivered to the drivers, not the | ||
372 | * file system containing the device node. */ | ||
373 | - if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) { | ||
374 | + if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) { | ||
375 | log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path); | ||
376 | return -EINVAL; | ||
377 | } | ||
378 | @@ -1174,10 +1220,16 @@ static int path_set_attribute(Item *item, const char *path) { | ||
379 | f = item->attribute_value & item->attribute_mask; | ||
380 | |||
381 | /* Mask away directory-specific flags */ | ||
382 | - if (!S_ISDIR(st.st_mode)) | ||
383 | + if (!S_ISDIR(st->st_mode)) | ||
384 | f &= ~FS_DIRSYNC_FL; | ||
385 | |||
386 | - r = chattr_fd(fd, f, item->attribute_mask); | ||
387 | + xsprintf(procfs_path, "/proc/self/fd/%i", fd); | ||
388 | + | ||
389 | + procfs_fd = open(procfs_path, O_RDONLY|O_CLOEXEC|O_NOATIME); | ||
390 | + if (procfs_fd < 0) | ||
391 | + return -errno; | ||
392 | + | ||
393 | + r = chattr_fd(procfs_fd, f, item->attribute_mask); | ||
394 | if (r < 0) | ||
395 | log_full_errno(IN_SET(r, -ENOTTY, -EOPNOTSUPP) ? LOG_DEBUG : LOG_WARNING, | ||
396 | r, | ||
397 | @@ -1187,6 +1239,23 @@ static int path_set_attribute(Item *item, const char *path) { | ||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | +static int path_set_attribute(Item *item, const char *path) { | ||
402 | + _cleanup_close_ int fd = -1; | ||
403 | + struct stat st; | ||
404 | + | ||
405 | + if (!item->attribute_set || item->attribute_mask == 0) | ||
406 | + return 0; | ||
407 | + | ||
408 | + fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH); | ||
409 | + if (fd < 0) | ||
410 | + return log_error_errno(errno, "Cannot open '%s': %m", path); | ||
411 | + | ||
412 | + if (fstat(fd, &st) < 0) | ||
413 | + return log_error_errno(errno, "Cannot stat '%s': %m", path); | ||
414 | + | ||
415 | + return fd_set_attribute(item, fd, &st); | ||
416 | +} | ||
417 | + | ||
418 | static int write_one_file(Item *i, const char *path) { | ||
419 | _cleanup_close_ int fd = -1; | ||
420 | int flags, r = 0; | ||
421 | @@ -1251,48 +1320,58 @@ done: | ||
422 | } | ||
423 | |||
424 | typedef int (*action_t)(Item *, const char *); | ||
425 | +typedef int (*fdaction_t)(Item *, int fd, const struct stat *st); | ||
426 | |||
427 | -static int item_do_children(Item *i, const char *path, action_t action) { | ||
428 | - _cleanup_closedir_ DIR *d; | ||
429 | - struct dirent *de; | ||
430 | - int r = 0; | ||
431 | +static int item_do(Item *i, int fd, const struct stat *st, fdaction_t action) { | ||
432 | + int r = 0, q; | ||
433 | |||
434 | assert(i); | ||
435 | - assert(path); | ||
436 | + assert(fd >= 0); | ||
437 | + assert(st); | ||
438 | |||
439 | /* This returns the first error we run into, but nevertheless | ||
440 | * tries to go on */ | ||
441 | + r = action(i, fd, st); | ||
442 | |||
443 | - d = opendir_nomod(path); | ||
444 | - if (!d) | ||
445 | - return IN_SET(errno, ENOENT, ENOTDIR, ELOOP) ? 0 : -errno; | ||
446 | + if (S_ISDIR(st->st_mode)) { | ||
447 | + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
448 | + _cleanup_closedir_ DIR *d = NULL; | ||
449 | + struct dirent *de; | ||
450 | |||
451 | - FOREACH_DIRENT_ALL(de, d, r = -errno) { | ||
452 | - _cleanup_free_ char *p = NULL; | ||
453 | - int q; | ||
454 | + /* The passed 'fd' was opened with O_PATH. We need to convert | ||
455 | + * it into a 'regular' fd before reading the directory content. */ | ||
456 | + xsprintf(procfs_path, "/proc/self/fd/%i", fd); | ||
457 | |||
458 | - if (dot_or_dot_dot(de->d_name)) | ||
459 | - continue; | ||
460 | + d = opendir(procfs_path); | ||
461 | + if (!d) { | ||
462 | + r = r ?: -errno; | ||
463 | + goto finish; | ||
464 | + } | ||
465 | |||
466 | - p = strjoin(path, "/", de->d_name); | ||
467 | - if (!p) | ||
468 | - return -ENOMEM; | ||
469 | + FOREACH_DIRENT_ALL(de, d, q = -errno; goto finish) { | ||
470 | + struct stat de_st; | ||
471 | + int de_fd; | ||
472 | + | ||
473 | + if (dot_or_dot_dot(de->d_name)) | ||
474 | + continue; | ||
475 | |||
476 | - q = action(i, p); | ||
477 | - if (q < 0 && q != -ENOENT && r == 0) | ||
478 | - r = q; | ||
479 | + de_fd = openat(fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
480 | + if (de_fd >= 0 && fstat(de_fd, &de_st) >= 0) | ||
481 | + /* pass ownership of dirent fd over */ | ||
482 | + q = item_do(i, de_fd, &de_st, action); | ||
483 | + else | ||
484 | + q = -errno; | ||
485 | |||
486 | - if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) { | ||
487 | - q = item_do_children(i, p, action); | ||
488 | if (q < 0 && r == 0) | ||
489 | r = q; | ||
490 | } | ||
491 | } | ||
492 | - | ||
493 | +finish: | ||
494 | + safe_close(fd); | ||
495 | return r; | ||
496 | } | ||
497 | |||
498 | -static int glob_item(Item *i, action_t action, bool recursive) { | ||
499 | +static int glob_item(Item *i, action_t action) { | ||
500 | _cleanup_globfree_ glob_t g = { | ||
501 | #ifdef GLOB_ALTDIRFUNC | ||
502 | .gl_opendir = (void *(*)(const char *)) opendir_nomod, | ||
503 | @@ -1309,12 +1388,48 @@ static int glob_item(Item *i, action_t action, bool recursive) { | ||
504 | k = action(i, *fn); | ||
505 | if (k < 0 && r == 0) | ||
506 | r = k; | ||
507 | + } | ||
508 | |||
509 | - if (recursive) { | ||
510 | - k = item_do_children(i, *fn, action); | ||
511 | - if (k < 0 && r == 0) | ||
512 | - r = k; | ||
513 | + return r; | ||
514 | +} | ||
515 | + | ||
516 | +static int glob_item_recursively(Item *i, fdaction_t action) { | ||
517 | + _cleanup_globfree_ glob_t g = { | ||
518 | + .gl_opendir = (void *(*)(const char *)) opendir_nomod, | ||
519 | + }; | ||
520 | + int r = 0, k; | ||
521 | + char **fn; | ||
522 | + | ||
523 | + k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g); | ||
524 | + if (k < 0 && k != -ENOENT) | ||
525 | + return log_error_errno(k, "glob(%s) failed: %m", i->path); | ||
526 | + | ||
527 | + STRV_FOREACH(fn, g.gl_pathv) { | ||
528 | + _cleanup_close_ int fd = -1; | ||
529 | + struct stat st; | ||
530 | + | ||
531 | + /* Make sure we won't trigger/follow file object (such as | ||
532 | + * device nodes, automounts, ...) pointed out by 'fn' with | ||
533 | + * O_PATH. Note, when O_PATH is used, flags other than | ||
534 | + * O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW are ignored. */ | ||
535 | + | ||
536 | + fd = open(*fn, O_CLOEXEC|O_NOFOLLOW|O_PATH); | ||
537 | + if (fd < 0) { | ||
538 | + r = r ?: -errno; | ||
539 | + continue; | ||
540 | + } | ||
541 | + | ||
542 | + if (fstat(fd, &st) < 0) { | ||
543 | + r = r ?: -errno; | ||
544 | + continue; | ||
545 | } | ||
546 | + | ||
547 | + k = item_do(i, fd, &st, action); | ||
548 | + if (k < 0 && r == 0) | ||
549 | + r = k; | ||
550 | + | ||
551 | + /* we passed fd ownership to the previous call */ | ||
552 | + fd = -1; | ||
553 | } | ||
554 | |||
555 | return r; | ||
556 | @@ -1403,7 +1518,7 @@ static int create_item(Item *i) { | ||
557 | break; | ||
558 | |||
559 | case WRITE_FILE: | ||
560 | - r = glob_item(i, write_one_file, false); | ||
561 | + r = glob_item(i, write_one_file); | ||
562 | if (r < 0) | ||
563 | return r; | ||
564 | |||
565 | @@ -1662,49 +1777,49 @@ static int create_item(Item *i) { | ||
566 | |||
567 | case ADJUST_MODE: | ||
568 | case RELABEL_PATH: | ||
569 | - r = glob_item(i, path_set_perms, false); | ||
570 | + r = glob_item(i, path_set_perms); | ||
571 | if (r < 0) | ||
572 | return r; | ||
573 | break; | ||
574 | |||
575 | case RECURSIVE_RELABEL_PATH: | ||
576 | - r = glob_item(i, path_set_perms, true); | ||
577 | + r = glob_item_recursively(i, fd_set_perms); | ||
578 | if (r < 0) | ||
579 | return r; | ||
580 | break; | ||
581 | |||
582 | case SET_XATTR: | ||
583 | - r = glob_item(i, path_set_xattrs, false); | ||
584 | + r = glob_item(i, path_set_xattrs); | ||
585 | if (r < 0) | ||
586 | return r; | ||
587 | break; | ||
588 | |||
589 | case RECURSIVE_SET_XATTR: | ||
590 | - r = glob_item(i, path_set_xattrs, true); | ||
591 | + r = glob_item_recursively(i, fd_set_xattrs); | ||
592 | if (r < 0) | ||
593 | return r; | ||
594 | break; | ||
595 | |||
596 | case SET_ACL: | ||
597 | - r = glob_item(i, path_set_acls, false); | ||
598 | + r = glob_item(i, path_set_acls); | ||
599 | if (r < 0) | ||
600 | return r; | ||
601 | break; | ||
602 | |||
603 | case RECURSIVE_SET_ACL: | ||
604 | - r = glob_item(i, path_set_acls, true); | ||
605 | + r = glob_item_recursively(i, fd_set_acls); | ||
606 | if (r < 0) | ||
607 | return r; | ||
608 | break; | ||
609 | |||
610 | case SET_ATTRIBUTE: | ||
611 | - r = glob_item(i, path_set_attribute, false); | ||
612 | + r = glob_item(i, path_set_attribute); | ||
613 | if (r < 0) | ||
614 | return r; | ||
615 | break; | ||
616 | |||
617 | case RECURSIVE_SET_ATTRIBUTE: | ||
618 | - r = glob_item(i, path_set_attribute, true); | ||
619 | + r = glob_item_recursively(i, fd_set_attribute); | ||
620 | if (r < 0) | ||
621 | return r; | ||
622 | break; | ||
623 | @@ -1754,7 +1869,7 @@ static int remove_item(Item *i) { | ||
624 | case REMOVE_PATH: | ||
625 | case TRUNCATE_DIRECTORY: | ||
626 | case RECURSIVE_REMOVE_PATH: | ||
627 | - return glob_item(i, remove_item_instance, false); | ||
628 | + return glob_item(i, remove_item_instance); | ||
629 | |||
630 | default: | ||
631 | return 0; | ||
632 | @@ -1828,7 +1943,7 @@ static int clean_item(Item *i) { | ||
633 | return 0; | ||
634 | case EMPTY_DIRECTORY: | ||
635 | case IGNORE_DIRECTORY_PATH: | ||
636 | - return glob_item(i, clean_item_instance, false); | ||
637 | + return glob_item(i, clean_item_instance); | ||
638 | default: | ||
639 | return 0; | ||
640 | } | ||
641 | -- | ||
642 | 2.11.0 | ||
643 | |||