diff options
author | George McCollister <george.mccollister@gmail.com> | 2019-02-25 10:37:12 -0600 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2019-03-24 16:49:54 +0000 |
commit | 4c55db6d5c6f93e5fd851ed2d0c8ebc5ec043cab (patch) | |
tree | 42fc8e334fd3ecc0be5a5cbf106677e54870ccb5 /meta | |
parent | d4e0f9252852ebfe5677d2bf0e6d70aacd7a984a (diff) | |
download | poky-4c55db6d5c6f93e5fd851ed2d0c8ebc5ec043cab.tar.gz |
systemd: fix CVE-2018-6954
Apply patches to fix CVE-2018-6954
NVD description from https://nvd.nist.gov/vuln/detail/CVE-2018-6954
systemd-tmpfiles in systemd through 237 mishandles symlinks present in
non-terminal path components, which allows local users to obtain
ownership of arbitrary files via vectors involving creation of a
directory and a file under that directory, and later replacing that
directory with a symlink. This occurs even if the fs.protected_symlinks
sysctl is turned on.
Patches from systemd_237-3ubuntu10.13.debian.
These patches shouldn't be required on newer OE releases since they use
systemd v239 or higher.
(From OE-Core rev: 607350d98aa4c65b71fe1f10900e205fad81d1ec)
Signed-off-by: George McCollister <george.mccollister@gmail.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta')
3 files changed, 2473 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 | |||
diff --git a/meta/recipes-core/systemd/systemd/0002-Make-tmpfiles-safe.patch b/meta/recipes-core/systemd/systemd/0002-Make-tmpfiles-safe.patch new file mode 100644 index 0000000000..80d27c141b --- /dev/null +++ b/meta/recipes-core/systemd/systemd/0002-Make-tmpfiles-safe.patch | |||
@@ -0,0 +1,1828 @@ | |||
1 | From fb95c890cf5116e698347c6a7bb3daeeb2d28cf9 Mon Sep 17 00:00:00 2001 | ||
2 | From: George McCollister <george.mccollister@gmail.com> | ||
3 | Date: Thu, 21 Feb 2019 18:04:37 -0600 | ||
4 | Subject: [PATCH] Make tmpfiles safe | ||
5 | |||
6 | In addition to backporting the changesets in #8822, this also backports | ||
7 | e04fc13 (test: add tests for systemd-tmpfiles), as well as empty_to_root() | ||
8 | from v239. | ||
9 | |||
10 | Origin: upstream, https://github.com/systemd/systemd/pull/8822/commits | ||
11 | Bug: https://github.com/systemd/systemd/issues/7986 | ||
12 | |||
13 | Patch from: | ||
14 | systemd_237-3ubuntu10.13.debian CVE-2018-6954_2.patch | ||
15 | |||
16 | https://usn.ubuntu.com/3816-1/ states that CVE-2018-6954 doesn't | ||
17 | affect Ubuntu 18.10 which uses the same version of systemd as thud | ||
18 | (239). | ||
19 | |||
20 | CVE: CVE-2018-6954 | ||
21 | Upstream-Status: Backport | ||
22 | |||
23 | Signed-off-by: George McCollister <george.mccollister@gmail.com> | ||
24 | --- | ||
25 | src/basic/btrfs-util.c | 26 +- | ||
26 | src/basic/btrfs-util.h | 1 + | ||
27 | src/basic/fileio.c | 5 +- | ||
28 | src/basic/fs-util.c | 27 +- | ||
29 | src/basic/fs-util.h | 2 + | ||
30 | src/basic/label.h | 1 + | ||
31 | src/basic/mkdir-label.c | 17 + | ||
32 | src/basic/mkdir.c | 6 + | ||
33 | src/basic/mkdir.h | 1 + | ||
34 | src/basic/path-util.c | 5 +- | ||
35 | src/basic/path-util.h | 4 + | ||
36 | src/basic/selinux-util.c | 84 +++-- | ||
37 | src/basic/selinux-util.h | 1 + | ||
38 | src/basic/smack-util.c | 119 +++++-- | ||
39 | src/basic/smack-util.h | 1 + | ||
40 | src/basic/stat-util.c | 11 + | ||
41 | src/basic/stat-util.h | 1 + | ||
42 | src/test/test-fs-util.c | 25 ++ | ||
43 | src/tmpfiles/tmpfiles.c | 902 ++++++++++++++++++++++++++++++++--------------- | ||
44 | 19 files changed, 882 insertions(+), 357 deletions(-) | ||
45 | |||
46 | diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c | ||
47 | index 19d385ab7c..26b088f52b 100644 | ||
48 | --- a/src/basic/btrfs-util.c | ||
49 | +++ b/src/basic/btrfs-util.c | ||
50 | @@ -150,8 +150,25 @@ int btrfs_is_subvol(const char *path) { | ||
51 | return btrfs_is_subvol_fd(fd); | ||
52 | } | ||
53 | |||
54 | -int btrfs_subvol_make(const char *path) { | ||
55 | +int btrfs_subvol_make_fd(int fd, const char *subvolume) { | ||
56 | struct btrfs_ioctl_vol_args args = {}; | ||
57 | + int r; | ||
58 | + | ||
59 | + assert(subvolume); | ||
60 | + | ||
61 | + r = validate_subvolume_name(subvolume); | ||
62 | + if (r < 0) | ||
63 | + return r; | ||
64 | + | ||
65 | + strncpy(args.name, subvolume, sizeof(args.name)-1); | ||
66 | + | ||
67 | + if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0) | ||
68 | + return -errno; | ||
69 | + | ||
70 | + return 0; | ||
71 | +} | ||
72 | + | ||
73 | +int btrfs_subvol_make(const char *path) { | ||
74 | _cleanup_close_ int fd = -1; | ||
75 | const char *subvolume; | ||
76 | int r; | ||
77 | @@ -166,12 +183,7 @@ int btrfs_subvol_make(const char *path) { | ||
78 | if (fd < 0) | ||
79 | return fd; | ||
80 | |||
81 | - strncpy(args.name, subvolume, sizeof(args.name)-1); | ||
82 | - | ||
83 | - if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0) | ||
84 | - return -errno; | ||
85 | - | ||
86 | - return 0; | ||
87 | + return btrfs_subvol_make_fd(fd, subvolume); | ||
88 | } | ||
89 | |||
90 | int btrfs_subvol_set_read_only_fd(int fd, bool b) { | ||
91 | diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h | ||
92 | index 952b3c26da..e92687bc57 100644 | ||
93 | --- a/src/basic/btrfs-util.h | ||
94 | +++ b/src/basic/btrfs-util.h | ||
95 | @@ -84,6 +84,7 @@ int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only); | ||
96 | int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only); | ||
97 | |||
98 | int btrfs_subvol_make(const char *path); | ||
99 | +int btrfs_subvol_make_fd(int fd, const char *subvolume); | ||
100 | |||
101 | int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags); | ||
102 | int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags); | ||
103 | diff --git a/src/basic/fileio.c b/src/basic/fileio.c | ||
104 | index 26d6174664..1c7e23332f 100644 | ||
105 | --- a/src/basic/fileio.c | ||
106 | +++ b/src/basic/fileio.c | ||
107 | @@ -1304,7 +1304,10 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { | ||
108 | if (!t) | ||
109 | return -ENOMEM; | ||
110 | |||
111 | - x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra); | ||
112 | + if (isempty(p)) | ||
113 | + x = stpcpy(stpcpy(t, ".#"), extra); | ||
114 | + else | ||
115 | + x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra); | ||
116 | |||
117 | u = random_u64(); | ||
118 | for (i = 0; i < 16; i++) { | ||
119 | diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c | ||
120 | index a8e50d4c78..c96a8813ea 100644 | ||
121 | --- a/src/basic/fs-util.c | ||
122 | +++ b/src/basic/fs-util.c | ||
123 | @@ -465,6 +465,31 @@ int mkfifo_atomic(const char *path, mode_t mode) { | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | +int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) { | ||
128 | + _cleanup_free_ char *t = NULL; | ||
129 | + int r; | ||
130 | + | ||
131 | + assert(path); | ||
132 | + | ||
133 | + if (path_is_absolute(path)) | ||
134 | + return mkfifo_atomic(path, mode); | ||
135 | + | ||
136 | + /* We're only interested in the (random) filename. */ | ||
137 | + r = tempfn_random_child("", NULL, &t); | ||
138 | + if (r < 0) | ||
139 | + return r; | ||
140 | + | ||
141 | + if (mkfifoat(dirfd, t, mode) < 0) | ||
142 | + return -errno; | ||
143 | + | ||
144 | + if (renameat(dirfd, t, dirfd, path) < 0) { | ||
145 | + unlink_noerrno(t); | ||
146 | + return -errno; | ||
147 | + } | ||
148 | + | ||
149 | + return 0; | ||
150 | +} | ||
151 | + | ||
152 | int get_files_in_directory(const char *path, char ***list) { | ||
153 | _cleanup_closedir_ DIR *d = NULL; | ||
154 | struct dirent *de; | ||
155 | @@ -808,7 +833,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, | ||
156 | fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) | ||
157 | return -EREMOTE; | ||
158 | |||
159 | - if (S_ISLNK(st.st_mode)) { | ||
160 | + if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { | ||
161 | char *joined; | ||
162 | |||
163 | _cleanup_free_ char *destination = NULL; | ||
164 | diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h | ||
165 | index 9c4b02eccd..121345e74d 100644 | ||
166 | --- a/src/basic/fs-util.h | ||
167 | +++ b/src/basic/fs-util.h | ||
168 | @@ -80,6 +80,7 @@ int symlink_idempotent(const char *from, const char *to); | ||
169 | int symlink_atomic(const char *from, const char *to); | ||
170 | int mknod_atomic(const char *path, mode_t mode, dev_t dev); | ||
171 | int mkfifo_atomic(const char *path, mode_t mode); | ||
172 | +int mkfifoat_atomic(int dir_fd, const char *path, mode_t mode); | ||
173 | |||
174 | int get_files_in_directory(const char *path, char ***list); | ||
175 | |||
176 | @@ -106,6 +107,7 @@ enum { | ||
177 | CHASE_NO_AUTOFS = 1U << 2, /* If set, return -EREMOTE if autofs mount point found */ | ||
178 | CHASE_SAFE = 1U << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */ | ||
179 | CHASE_OPEN = 1U << 4, /* If set, return an O_PATH object to the final component */ | ||
180 | + CHASE_NOFOLLOW = 1U << 7, /* Only valid with CHASE_OPEN: when the path's right-most component refers to symlink return O_PATH fd of the symlink, rather than following it. */ | ||
181 | }; | ||
182 | |||
183 | int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret); | ||
184 | diff --git a/src/basic/label.h b/src/basic/label.h | ||
185 | index d73dacec4f..3ecfed72c6 100644 | ||
186 | --- a/src/basic/label.h | ||
187 | +++ b/src/basic/label.h | ||
188 | @@ -26,6 +26,7 @@ | ||
189 | int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs); | ||
190 | |||
191 | int mkdir_label(const char *path, mode_t mode); | ||
192 | +int mkdirat_label(int dirfd, const char *path, mode_t mode); | ||
193 | int symlink_label(const char *old_path, const char *new_path); | ||
194 | |||
195 | int btrfs_subvol_make_label(const char *path); | ||
196 | diff --git a/src/basic/mkdir-label.c b/src/basic/mkdir-label.c | ||
197 | index 6f3a46f467..3c1a227bfa 100644 | ||
198 | --- a/src/basic/mkdir-label.c | ||
199 | +++ b/src/basic/mkdir-label.c | ||
200 | @@ -47,6 +47,23 @@ int mkdir_label(const char *path, mode_t mode) { | ||
201 | return mac_smack_fix(path, false, false); | ||
202 | } | ||
203 | |||
204 | +int mkdirat_label(int dirfd, const char *path, mode_t mode) { | ||
205 | + int r; | ||
206 | + | ||
207 | + assert(path); | ||
208 | + | ||
209 | + r = mac_selinux_create_file_prepare_at(dirfd, path, S_IFDIR); | ||
210 | + if (r < 0) | ||
211 | + return r; | ||
212 | + | ||
213 | + r = mkdirat_errno_wrapper(dirfd, path, mode); | ||
214 | + mac_selinux_create_file_clear(); | ||
215 | + if (r < 0) | ||
216 | + return r; | ||
217 | + | ||
218 | + return mac_smack_fix_at(dirfd, path, false, false); | ||
219 | +} | ||
220 | + | ||
221 | int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink) { | ||
222 | return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir_label); | ||
223 | } | ||
224 | diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c | ||
225 | index d51518a5a7..418945ad4a 100644 | ||
226 | --- a/src/basic/mkdir.c | ||
227 | +++ b/src/basic/mkdir.c | ||
228 | @@ -77,6 +77,12 @@ int mkdir_errno_wrapper(const char *pathname, mode_t mode) { | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | +int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode) { | ||
233 | + if (mkdirat(dirfd, pathname, mode) < 0) | ||
234 | + return -errno; | ||
235 | + return 0; | ||
236 | +} | ||
237 | + | ||
238 | int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink) { | ||
239 | return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir_errno_wrapper); | ||
240 | } | ||
241 | diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h | ||
242 | index d6c2d579a3..3ec6f3ed2d 100644 | ||
243 | --- a/src/basic/mkdir.h | ||
244 | +++ b/src/basic/mkdir.h | ||
245 | @@ -24,6 +24,7 @@ | ||
246 | #include <sys/types.h> | ||
247 | |||
248 | int mkdir_errno_wrapper(const char *pathname, mode_t mode); | ||
249 | +int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode); | ||
250 | int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink); | ||
251 | int mkdir_parents(const char *path, mode_t mode); | ||
252 | int mkdir_p(const char *path, mode_t mode); | ||
253 | diff --git a/src/basic/path-util.c b/src/basic/path-util.c | ||
254 | index df94629385..84404f7ee1 100644 | ||
255 | --- a/src/basic/path-util.c | ||
256 | +++ b/src/basic/path-util.c | ||
257 | @@ -127,10 +127,7 @@ int path_make_absolute_cwd(const char *p, char **ret) { | ||
258 | if (r < 0) | ||
259 | return r; | ||
260 | |||
261 | - if (endswith(cwd, "/")) | ||
262 | - c = strjoin(cwd, p); | ||
263 | - else | ||
264 | - c = strjoin(cwd, "/", p); | ||
265 | + c = path_join(NULL, cwd, p); | ||
266 | } | ||
267 | if (!c) | ||
268 | return -ENOMEM; | ||
269 | diff --git a/src/basic/path-util.h b/src/basic/path-util.h | ||
270 | index 89c285e076..1094baca12 100644 | ||
271 | --- a/src/basic/path-util.h | ||
272 | +++ b/src/basic/path-util.h | ||
273 | @@ -156,3 +156,7 @@ static inline const char *skip_dev_prefix(const char *p) { | ||
274 | |||
275 | return e ?: p; | ||
276 | } | ||
277 | +static inline const char *empty_to_root(const char *path) { | ||
278 | + return isempty(path) ? "/" : path; | ||
279 | +} | ||
280 | + | ||
281 | diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c | ||
282 | index 0c6e99b1d7..bdef7d148b 100644 | ||
283 | --- a/src/basic/selinux-util.c | ||
284 | +++ b/src/basic/selinux-util.c | ||
285 | @@ -34,6 +34,7 @@ | ||
286 | #endif | ||
287 | |||
288 | #include "alloc-util.h" | ||
289 | +#include "fd-util.h" | ||
290 | #include "log.h" | ||
291 | #include "macro.h" | ||
292 | #include "path-util.h" | ||
293 | @@ -311,48 +312,89 @@ char* mac_selinux_free(char *label) { | ||
294 | return NULL; | ||
295 | } | ||
296 | |||
297 | -int mac_selinux_create_file_prepare(const char *path, mode_t mode) { | ||
298 | - | ||
299 | #if HAVE_SELINUX | ||
300 | +static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) { | ||
301 | _cleanup_freecon_ char *filecon = NULL; | ||
302 | + _cleanup_free_ char *path = NULL; | ||
303 | int r; | ||
304 | |||
305 | - assert(path); | ||
306 | - | ||
307 | - if (!label_hnd) | ||
308 | - return 0; | ||
309 | - | ||
310 | - if (path_is_absolute(path)) | ||
311 | - r = selabel_lookup_raw(label_hnd, &filecon, path, mode); | ||
312 | - else { | ||
313 | - _cleanup_free_ char *newpath = NULL; | ||
314 | - | ||
315 | - r = path_make_absolute_cwd(path, &newpath); | ||
316 | - if (r < 0) | ||
317 | - return r; | ||
318 | - | ||
319 | - r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode); | ||
320 | - } | ||
321 | + assert(abspath); | ||
322 | + assert(path_is_absolute(abspath)); | ||
323 | |||
324 | + r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode); | ||
325 | if (r < 0) { | ||
326 | /* No context specified by the policy? Proceed without setting it. */ | ||
327 | if (errno == ENOENT) | ||
328 | return 0; | ||
329 | |||
330 | - log_enforcing("Failed to determine SELinux security context for %s: %m", path); | ||
331 | + log_enforcing("Failed to determine SELinux security context for %s: %m", abspath); | ||
332 | } else { | ||
333 | if (setfscreatecon_raw(filecon) >= 0) | ||
334 | return 0; /* Success! */ | ||
335 | |||
336 | - log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path); | ||
337 | + log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, abspath); | ||
338 | } | ||
339 | |||
340 | if (security_getenforce() > 0) | ||
341 | return -errno; | ||
342 | |||
343 | -#endif | ||
344 | return 0; | ||
345 | } | ||
346 | +#endif | ||
347 | + | ||
348 | +int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) { | ||
349 | + int r = 0; | ||
350 | + | ||
351 | +#if HAVE_SELINUX | ||
352 | + _cleanup_free_ char *abspath = NULL; | ||
353 | + _cleanup_close_ int fd = -1; | ||
354 | + | ||
355 | + assert(path); | ||
356 | + | ||
357 | + if (!label_hnd) | ||
358 | + return 0; | ||
359 | + | ||
360 | + if (!path_is_absolute(path)) { | ||
361 | + _cleanup_free_ char *p = NULL; | ||
362 | + | ||
363 | + if (dirfd == AT_FDCWD) | ||
364 | + r = safe_getcwd(&p); | ||
365 | + else | ||
366 | + r = fd_get_path(dirfd, &p); | ||
367 | + if (r < 0) | ||
368 | + return r; | ||
369 | + | ||
370 | + abspath = path_join(NULL, p, path); | ||
371 | + if (!abspath) | ||
372 | + return -ENOMEM; | ||
373 | + | ||
374 | + path = abspath; | ||
375 | + } | ||
376 | + | ||
377 | + r = selinux_create_file_prepare_abspath(path, mode); | ||
378 | +#endif | ||
379 | + return r; | ||
380 | +} | ||
381 | + | ||
382 | +int mac_selinux_create_file_prepare(const char *path, mode_t mode) { | ||
383 | + int r = 0; | ||
384 | + | ||
385 | +#if HAVE_SELINUX | ||
386 | + _cleanup_free_ char *abspath = NULL; | ||
387 | + | ||
388 | + assert(path); | ||
389 | + | ||
390 | + if (!label_hnd) | ||
391 | + return 0; | ||
392 | + | ||
393 | + r = path_make_absolute_cwd(path, &abspath); | ||
394 | + if (r < 0) | ||
395 | + return r; | ||
396 | + | ||
397 | + r = selinux_create_file_prepare_abspath(abspath, mode); | ||
398 | +#endif | ||
399 | + return r; | ||
400 | +} | ||
401 | |||
402 | void mac_selinux_create_file_clear(void) { | ||
403 | |||
404 | diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h | ||
405 | index 9780dca81e..84a8bf9729 100644 | ||
406 | --- a/src/basic/selinux-util.h | ||
407 | +++ b/src/basic/selinux-util.h | ||
408 | @@ -41,6 +41,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * | ||
409 | char* mac_selinux_free(char *label); | ||
410 | |||
411 | int mac_selinux_create_file_prepare(const char *path, mode_t mode); | ||
412 | +int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode); | ||
413 | void mac_selinux_create_file_clear(void); | ||
414 | |||
415 | int mac_selinux_create_socket_prepare(const char *label); | ||
416 | diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c | ||
417 | index f0018f013f..ea0af3e45f 100644 | ||
418 | --- a/src/basic/smack-util.c | ||
419 | +++ b/src/basic/smack-util.c | ||
420 | @@ -21,18 +21,21 @@ | ||
421 | ***/ | ||
422 | |||
423 | #include <errno.h> | ||
424 | +#include <fcntl.h> | ||
425 | #include <string.h> | ||
426 | #include <sys/stat.h> | ||
427 | #include <sys/xattr.h> | ||
428 | #include <unistd.h> | ||
429 | |||
430 | #include "alloc-util.h" | ||
431 | +#include "fd-util.h" | ||
432 | #include "fileio.h" | ||
433 | #include "log.h" | ||
434 | #include "macro.h" | ||
435 | #include "path-util.h" | ||
436 | #include "process-util.h" | ||
437 | #include "smack-util.h" | ||
438 | +#include "stdio-util.h" | ||
439 | #include "string-table.h" | ||
440 | #include "xattr-util.h" | ||
441 | |||
442 | @@ -134,59 +137,111 @@ int mac_smack_apply_pid(pid_t pid, const char *label) { | ||
443 | return r; | ||
444 | } | ||
445 | |||
446 | -int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { | ||
447 | +static int smack_fix_fd(int fd , const char *abspath, bool ignore_erofs) { | ||
448 | + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
449 | + const char *label; | ||
450 | struct stat st; | ||
451 | int r; | ||
452 | |||
453 | - assert(path); | ||
454 | + /* The caller should have done the sanity checks. */ | ||
455 | + assert(abspath); | ||
456 | + assert(path_is_absolute(abspath)); | ||
457 | |||
458 | - if (!mac_smack_use()) | ||
459 | + /* Path must be in /dev. */ | ||
460 | + if (!path_startswith(abspath, "/dev")) | ||
461 | return 0; | ||
462 | |||
463 | + if (fstat(fd, &st) < 0) | ||
464 | + return -errno; | ||
465 | + | ||
466 | /* | ||
467 | - * Path must be in /dev and must exist | ||
468 | + * Label directories and character devices "*". | ||
469 | + * Label symlinks "_". | ||
470 | + * Don't change anything else. | ||
471 | */ | ||
472 | - if (!path_startswith(path, "/dev")) | ||
473 | + | ||
474 | + if (S_ISDIR(st.st_mode)) | ||
475 | + label = SMACK_STAR_LABEL; | ||
476 | + else if (S_ISLNK(st.st_mode)) | ||
477 | + label = SMACK_FLOOR_LABEL; | ||
478 | + else if (S_ISCHR(st.st_mode)) | ||
479 | + label = SMACK_STAR_LABEL; | ||
480 | + else | ||
481 | return 0; | ||
482 | |||
483 | - r = lstat(path, &st); | ||
484 | - if (r >= 0) { | ||
485 | - const char *label; | ||
486 | - | ||
487 | - /* | ||
488 | - * Label directories and character devices "*". | ||
489 | - * Label symlinks "_". | ||
490 | - * Don't change anything else. | ||
491 | - */ | ||
492 | - | ||
493 | - if (S_ISDIR(st.st_mode)) | ||
494 | - label = SMACK_STAR_LABEL; | ||
495 | - else if (S_ISLNK(st.st_mode)) | ||
496 | - label = SMACK_FLOOR_LABEL; | ||
497 | - else if (S_ISCHR(st.st_mode)) | ||
498 | - label = SMACK_STAR_LABEL; | ||
499 | - else | ||
500 | - return 0; | ||
501 | + xsprintf(procfs_path, "/proc/self/fd/%i", fd); | ||
502 | + if (setxattr(procfs_path, "security.SMACK64", label, strlen(label), 0) < 0) { | ||
503 | + _cleanup_free_ char *old_label = NULL; | ||
504 | |||
505 | - r = lsetxattr(path, "security.SMACK64", label, strlen(label), 0); | ||
506 | + r = -errno; | ||
507 | |||
508 | /* If the FS doesn't support labels, then exit without warning */ | ||
509 | - if (r < 0 && errno == EOPNOTSUPP) | ||
510 | + if (r == -EOPNOTSUPP) | ||
511 | + return 0; | ||
512 | + | ||
513 | + /* It the FS is read-only and we were told to ignore failures caused by that, suppress error */ | ||
514 | + if (r == -EROFS && ignore_erofs) | ||
515 | + return 0; | ||
516 | + | ||
517 | + /* If the old label is identical to the new one, suppress any kind of error */ | ||
518 | + if (getxattr_malloc(procfs_path, "security.SMACK64", &old_label, false) >= 0 && | ||
519 | + streq(old_label, label)) | ||
520 | return 0; | ||
521 | + | ||
522 | + return log_debug_errno(r, "Unable to fix SMACK label of %s: %m", abspath); | ||
523 | } | ||
524 | |||
525 | - if (r < 0) { | ||
526 | - /* Ignore ENOENT in some cases */ | ||
527 | + return r; | ||
528 | +} | ||
529 | + | ||
530 | +int mac_smack_fix_at(int dirfd, const char *path, bool ignore_enoent, bool ignore_erofs) { | ||
531 | + _cleanup_free_ char *p = NULL; | ||
532 | + _cleanup_close_ int fd = -1; | ||
533 | + int r; | ||
534 | + | ||
535 | + assert(path); | ||
536 | + | ||
537 | + if (!mac_smack_use()) | ||
538 | + return 0; | ||
539 | + | ||
540 | + fd = openat(dirfd, path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
541 | + if (fd < 0) { | ||
542 | if (ignore_enoent && errno == ENOENT) | ||
543 | return 0; | ||
544 | |||
545 | - if (ignore_erofs && errno == EROFS) | ||
546 | + return -errno; | ||
547 | + } | ||
548 | + | ||
549 | + r = fd_get_path(fd, &p); | ||
550 | + if (r < 0) | ||
551 | + return r; | ||
552 | + | ||
553 | + return smack_fix_fd(fd, p, ignore_erofs); | ||
554 | +} | ||
555 | + | ||
556 | +int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { | ||
557 | + _cleanup_free_ char *abspath = NULL; | ||
558 | + _cleanup_close_ int fd = -1; | ||
559 | + int r; | ||
560 | + | ||
561 | + assert(path); | ||
562 | + | ||
563 | + if (!mac_smack_use()) | ||
564 | + return 0; | ||
565 | + | ||
566 | + r = path_make_absolute_cwd(path, &abspath); | ||
567 | + if (r < 0) | ||
568 | + return r; | ||
569 | + | ||
570 | + fd = open(abspath, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
571 | + if (fd < 0) { | ||
572 | + if (ignore_enoent && errno == ENOENT) | ||
573 | return 0; | ||
574 | |||
575 | - r = log_debug_errno(errno, "Unable to fix SMACK label of %s: %m", path); | ||
576 | + return -errno; | ||
577 | } | ||
578 | |||
579 | - return r; | ||
580 | + return smack_fix_fd(fd, abspath, ignore_erofs); | ||
581 | } | ||
582 | |||
583 | int mac_smack_copy(const char *dest, const char *src) { | ||
584 | @@ -236,6 +291,10 @@ int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { | ||
585 | return 0; | ||
586 | } | ||
587 | |||
588 | +int mac_smack_fix_at(int dirfd, const char *path, bool ignore_enoent, bool ignore_erofs) { | ||
589 | + return 0; | ||
590 | +} | ||
591 | + | ||
592 | int mac_smack_copy(const char *dest, const char *src) { | ||
593 | return 0; | ||
594 | } | ||
595 | diff --git a/src/basic/smack-util.h b/src/basic/smack-util.h | ||
596 | index e4d46d7736..0c214bbbc0 100644 | ||
597 | --- a/src/basic/smack-util.h | ||
598 | +++ b/src/basic/smack-util.h | ||
599 | @@ -44,6 +44,7 @@ typedef enum SmackAttr { | ||
600 | bool mac_smack_use(void); | ||
601 | |||
602 | int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs); | ||
603 | +int mac_smack_fix_at(int dirfd, const char *path, bool ignore_enoent, bool ignore_erofs); | ||
604 | |||
605 | const char* smack_attr_to_string(SmackAttr i) _const_; | ||
606 | SmackAttr smack_attr_from_string(const char *s) _pure_; | ||
607 | diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c | ||
608 | index 3a54103f1b..801889ae5b 100644 | ||
609 | --- a/src/basic/stat-util.c | ||
610 | +++ b/src/basic/stat-util.c | ||
611 | @@ -63,6 +63,17 @@ int is_dir(const char* path, bool follow) { | ||
612 | return !!S_ISDIR(st.st_mode); | ||
613 | } | ||
614 | |||
615 | +int is_dir_fd(int fd) { | ||
616 | + struct stat st; | ||
617 | + int r; | ||
618 | + | ||
619 | + r = fstat(fd, &st); | ||
620 | + if (r < 0) | ||
621 | + return -errno; | ||
622 | + | ||
623 | + return !!S_ISDIR(st.st_mode); | ||
624 | +} | ||
625 | + | ||
626 | int is_device_node(const char *path) { | ||
627 | struct stat info; | ||
628 | |||
629 | diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h | ||
630 | index d8d3c20496..7ea68abfa3 100644 | ||
631 | --- a/src/basic/stat-util.h | ||
632 | +++ b/src/basic/stat-util.h | ||
633 | @@ -31,6 +31,7 @@ | ||
634 | |||
635 | int is_symlink(const char *path); | ||
636 | int is_dir(const char *path, bool follow); | ||
637 | +int is_dir_fd(int fd); | ||
638 | int is_device_node(const char *path); | ||
639 | |||
640 | int dir_is_empty(const char *path); | ||
641 | diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c | ||
642 | index 9f3a500080..a76d6d0f8b 100644 | ||
643 | --- a/src/test/test-fs-util.c | ||
644 | +++ b/src/test/test-fs-util.c | ||
645 | @@ -40,6 +40,7 @@ static void test_chase_symlinks(void) { | ||
646 | _cleanup_free_ char *result = NULL; | ||
647 | char temp[] = "/tmp/test-chase.XXXXXX"; | ||
648 | const char *top, *p, *pslash, *q, *qslash; | ||
649 | + struct stat st; | ||
650 | int r, pfd; | ||
651 | |||
652 | assert_se(mkdtemp(temp)); | ||
653 | @@ -288,6 +289,30 @@ static void test_chase_symlinks(void) { | ||
654 | assert_se(sd_id128_equal(a, b)); | ||
655 | } | ||
656 | |||
657 | + /* Test CHASE_NOFOLLOW */ | ||
658 | + | ||
659 | + p = strjoina(temp, "/target"); | ||
660 | + q = strjoina(temp, "/symlink"); | ||
661 | + assert_se(symlink(p, q) >= 0); | ||
662 | + pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result); | ||
663 | + assert_se(pfd > 0); | ||
664 | + assert_se(path_equal(result, q)); | ||
665 | + assert_se(fstat(pfd, &st) >= 0); | ||
666 | + assert_se(S_ISLNK(st.st_mode)); | ||
667 | + result = mfree(result); | ||
668 | + | ||
669 | + /* s1 -> s2 -> nonexistent */ | ||
670 | + q = strjoina(temp, "/s1"); | ||
671 | + assert_se(symlink("s2", q) >= 0); | ||
672 | + p = strjoina(temp, "/s2"); | ||
673 | + assert_se(symlink("nonexistent", p) >= 0); | ||
674 | + pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result); | ||
675 | + assert_se(pfd > 0); | ||
676 | + assert_se(path_equal(result, q)); | ||
677 | + assert_se(fstat(pfd, &st) >= 0); | ||
678 | + assert_se(S_ISLNK(st.st_mode)); | ||
679 | + result = mfree(result); | ||
680 | + | ||
681 | assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); | ||
682 | } | ||
683 | |||
684 | diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c | ||
685 | index 613d418eb3..d59ccbaa39 100644 | ||
686 | --- a/src/tmpfiles/tmpfiles.c | ||
687 | +++ b/src/tmpfiles/tmpfiles.c | ||
688 | @@ -794,6 +794,7 @@ static bool hardlink_vulnerable(struct stat *st) { | ||
689 | |||
690 | static int fd_set_perms(Item *i, int fd, const struct stat *st) { | ||
691 | _cleanup_free_ char *path = NULL; | ||
692 | + struct stat stbuf; | ||
693 | int r; | ||
694 | |||
695 | assert(i); | ||
696 | @@ -806,6 +807,12 @@ static int fd_set_perms(Item *i, int fd, const struct stat *st) { | ||
697 | if (!i->mode_set && !i->uid_set && !i->gid_set) | ||
698 | goto shortcut; | ||
699 | |||
700 | + if (!st) { | ||
701 | + if (fstat(fd, &stbuf) < 0) | ||
702 | + return log_error_errno(errno, "fstat(%s) failed: %m", path); | ||
703 | + st = &stbuf; | ||
704 | + } | ||
705 | + | ||
706 | if (hardlink_vulnerable(st)) { | ||
707 | log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); | ||
708 | return -EPERM; | ||
709 | @@ -863,32 +870,62 @@ shortcut: | ||
710 | return label_fix(path, false, false); | ||
711 | } | ||
712 | |||
713 | -static int path_set_perms(Item *i, const char *path) { | ||
714 | - _cleanup_close_ int fd = -1; | ||
715 | - struct stat st; | ||
716 | +static int path_open_parent_safe(const char *path) { | ||
717 | + _cleanup_free_ char *dn = NULL; | ||
718 | + int fd; | ||
719 | |||
720 | - assert(i); | ||
721 | - assert(path); | ||
722 | + if (path_equal(path, "/") || !path_is_normalized(path)) { | ||
723 | + log_error("Failed to open parent of '%s': invalid path.", path); | ||
724 | + return -EINVAL; | ||
725 | + } | ||
726 | |||
727 | - fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
728 | - if (fd < 0) { | ||
729 | - int level = LOG_ERR, r = -errno; | ||
730 | + dn = dirname_malloc(path); | ||
731 | + if (!dn) | ||
732 | + return log_oom(); | ||
733 | |||
734 | - /* Option "e" operates only on existing objects. Do not | ||
735 | - * print errors about non-existent files or directories */ | ||
736 | - if (i->type == EMPTY_DIRECTORY && errno == ENOENT) { | ||
737 | - level = LOG_DEBUG; | ||
738 | - r = 0; | ||
739 | - } | ||
740 | + fd = chase_symlinks(dn, NULL, CHASE_OPEN|CHASE_SAFE, NULL); | ||
741 | + if (fd == -EPERM) | ||
742 | + return log_error_errno(fd, "Unsafe symlinks encountered in %s, refusing.", path); | ||
743 | + if (fd < 0) | ||
744 | + return log_error_errno(fd, "Failed to validate path %s: %m", path); | ||
745 | |||
746 | - log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path); | ||
747 | - return r; | ||
748 | + return fd; | ||
749 | +} | ||
750 | + | ||
751 | +static int path_open_safe(const char *path) { | ||
752 | + int fd; | ||
753 | + | ||
754 | + /* path_open_safe() returns a file descriptor opened with O_PATH after | ||
755 | + * verifying that the path doesn't contain unsafe transitions, except | ||
756 | + * for its final component as the function does not follow symlink. */ | ||
757 | + | ||
758 | + assert(path); | ||
759 | + | ||
760 | + if (!path_is_normalized(path)) { | ||
761 | + log_error("Failed to open invalid path '%s'.", path); | ||
762 | + return -EINVAL; | ||
763 | } | ||
764 | |||
765 | - if (fstat(fd, &st) < 0) | ||
766 | - return log_error_errno(errno, "Failed to fstat() file %s: %m", path); | ||
767 | + fd = chase_symlinks(path, NULL, CHASE_OPEN|CHASE_SAFE|CHASE_NOFOLLOW, NULL); | ||
768 | + if (fd == -EPERM) | ||
769 | + return log_error_errno(fd, "Unsafe symlinks encountered in %s, refusing.", path); | ||
770 | + if (fd < 0) | ||
771 | + return log_error_errno(fd, "Failed to validate path %s: %m", path); | ||
772 | + | ||
773 | + return fd; | ||
774 | +} | ||
775 | + | ||
776 | +static int path_set_perms(Item *i, const char *path) { | ||
777 | + _cleanup_close_ int fd = -1; | ||
778 | + | ||
779 | + assert(i); | ||
780 | + assert(path); | ||
781 | |||
782 | - return fd_set_perms(i, fd, &st); | ||
783 | + fd = path_open_safe(path); | ||
784 | + if (fd < 0) | ||
785 | + return fd; | ||
786 | + | ||
787 | + return fd_set_perms(i, fd, NULL); | ||
788 | } | ||
789 | |||
790 | static int parse_xattrs_from_arg(Item *i) { | ||
791 | @@ -959,9 +996,9 @@ static int path_set_xattrs(Item *i, const char *path) { | ||
792 | assert(i); | ||
793 | assert(path); | ||
794 | |||
795 | - fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH); | ||
796 | + fd = path_open_safe(path); | ||
797 | if (fd < 0) | ||
798 | - return log_error_errno(errno, "Cannot open '%s': %m", path); | ||
799 | + return fd; | ||
800 | |||
801 | return fd_set_xattrs(i, fd, NULL); | ||
802 | } | ||
803 | @@ -1036,15 +1073,21 @@ static int fd_set_acls(Item *item, int fd, const struct stat *st) { | ||
804 | #if HAVE_ACL | ||
805 | char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
806 | _cleanup_free_ char *path = NULL; | ||
807 | + struct stat stbuf; | ||
808 | |||
809 | assert(item); | ||
810 | assert(fd); | ||
811 | - assert(st); | ||
812 | |||
813 | r = fd_get_path(fd, &path); | ||
814 | if (r < 0) | ||
815 | return r; | ||
816 | |||
817 | + if (!st) { | ||
818 | + if (fstat(fd, &stbuf) < 0) | ||
819 | + return log_error_errno(errno, "fstat(%s) failed: %m", path); | ||
820 | + st = &stbuf; | ||
821 | + } | ||
822 | + | ||
823 | if (hardlink_vulnerable(st)) { | ||
824 | log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); | ||
825 | return -EPERM; | ||
826 | @@ -1079,19 +1122,15 @@ static int path_set_acls(Item *item, const char *path) { | ||
827 | int r = 0; | ||
828 | #ifdef HAVE_ACL | ||
829 | _cleanup_close_ int fd = -1; | ||
830 | - struct stat st; | ||
831 | |||
832 | assert(item); | ||
833 | assert(path); | ||
834 | |||
835 | - fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
836 | + fd = path_open_safe(path); | ||
837 | if (fd < 0) | ||
838 | - return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path); | ||
839 | + return fd; | ||
840 | |||
841 | - if (fstat(fd, &st) < 0) | ||
842 | - return log_error_errno(errno, "Failed to fstat() file %s: %m", path); | ||
843 | - | ||
844 | - r = fd_set_acls(item, fd, &st); | ||
845 | + r = fd_set_acls(item, fd, NULL); | ||
846 | #endif | ||
847 | return r; | ||
848 | } | ||
849 | @@ -1199,6 +1238,7 @@ static int fd_set_attribute(Item *item, int fd, const struct stat *st) { | ||
850 | char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
851 | _cleanup_close_ int procfs_fd = -1; | ||
852 | _cleanup_free_ char *path = NULL; | ||
853 | + struct stat stbuf; | ||
854 | unsigned f; | ||
855 | int r; | ||
856 | |||
857 | @@ -1209,6 +1249,12 @@ static int fd_set_attribute(Item *item, int fd, const struct stat *st) { | ||
858 | if (r < 0) | ||
859 | return r; | ||
860 | |||
861 | + if (!st) { | ||
862 | + if (fstat(fd, &stbuf) < 0) | ||
863 | + return log_error_errno(errno, "fstat(%s) failed: %m", path); | ||
864 | + st = &stbuf; | ||
865 | + } | ||
866 | + | ||
867 | /* Issuing the file attribute ioctls on device nodes is not | ||
868 | * safe, as that will be delivered to the drivers, not the | ||
869 | * file system containing the device node. */ | ||
870 | @@ -1241,99 +1287,558 @@ static int fd_set_attribute(Item *item, int fd, const struct stat *st) { | ||
871 | |||
872 | static int path_set_attribute(Item *item, const char *path) { | ||
873 | _cleanup_close_ int fd = -1; | ||
874 | - struct stat st; | ||
875 | |||
876 | if (!item->attribute_set || item->attribute_mask == 0) | ||
877 | return 0; | ||
878 | |||
879 | - fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH); | ||
880 | + fd = path_open_safe(path); | ||
881 | if (fd < 0) | ||
882 | - return log_error_errno(errno, "Cannot open '%s': %m", path); | ||
883 | - | ||
884 | - if (fstat(fd, &st) < 0) | ||
885 | - return log_error_errno(errno, "Cannot stat '%s': %m", path); | ||
886 | + return fd; | ||
887 | |||
888 | - return fd_set_attribute(item, fd, &st); | ||
889 | + return fd_set_attribute(item, fd, NULL); | ||
890 | } | ||
891 | |||
892 | static int write_one_file(Item *i, const char *path) { | ||
893 | - _cleanup_close_ int fd = -1; | ||
894 | - int flags, r = 0; | ||
895 | - struct stat st; | ||
896 | + _cleanup_close_ int fd = -1, dir_fd = -1; | ||
897 | + char *bn; | ||
898 | + int r; | ||
899 | + | ||
900 | + assert(i); | ||
901 | + assert(path); | ||
902 | + assert(i->argument); | ||
903 | + assert(i->type == WRITE_FILE); | ||
904 | + | ||
905 | + /* Validate the path and keep the fd on the directory for opening the | ||
906 | + * file so we're sure that it can't be changed behind our back. */ | ||
907 | + dir_fd = path_open_parent_safe(path); | ||
908 | + if (dir_fd < 0) | ||
909 | + return dir_fd; | ||
910 | + | ||
911 | + bn = basename(path); | ||
912 | + | ||
913 | + /* Follows symlinks */ | ||
914 | + fd = openat(dir_fd, bn, O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode); | ||
915 | + if (fd < 0) { | ||
916 | + if (errno == ENOENT) { | ||
917 | + log_debug_errno(errno, "Not writing missing file \"%s\": %m", path); | ||
918 | + return 0; | ||
919 | + } | ||
920 | + return log_error_errno(errno, "Failed to open file \"%s\": %m", path); | ||
921 | + } | ||
922 | + | ||
923 | + /* 'w' is allowed to write into any kind of files. */ | ||
924 | + log_debug("Writing to \"%s\".", path); | ||
925 | + | ||
926 | + r = loop_write(fd, i->argument, strlen(i->argument), false); | ||
927 | + if (r < 0) | ||
928 | + return log_error_errno(r, "Failed to write file \"%s\": %m", path); | ||
929 | + | ||
930 | + return fd_set_perms(i, fd, NULL); | ||
931 | +} | ||
932 | + | ||
933 | +static int create_file(Item *i, const char *path) { | ||
934 | + _cleanup_close_ int fd = -1, dir_fd = -1; | ||
935 | + struct stat stbuf, *st = NULL; | ||
936 | + int r = 0; | ||
937 | + char *bn; | ||
938 | |||
939 | assert(i); | ||
940 | assert(path); | ||
941 | + assert(i->type == CREATE_FILE); | ||
942 | + | ||
943 | + /* 'f' operates on regular files exclusively. */ | ||
944 | |||
945 | - flags = i->type == CREATE_FILE ? O_CREAT|O_EXCL|O_NOFOLLOW : | ||
946 | - i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0; | ||
947 | + /* Validate the path and keep the fd on the directory for opening the | ||
948 | + * file so we're sure that it can't be changed behind our back. */ | ||
949 | + dir_fd = path_open_parent_safe(path); | ||
950 | + if (dir_fd < 0) | ||
951 | + return dir_fd; | ||
952 | + | ||
953 | + bn = basename(path); | ||
954 | |||
955 | RUN_WITH_UMASK(0000) { | ||
956 | mac_selinux_create_file_prepare(path, S_IFREG); | ||
957 | - fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode); | ||
958 | + fd = openat(dir_fd, bn, O_CREAT|O_EXCL|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode); | ||
959 | mac_selinux_create_file_clear(); | ||
960 | } | ||
961 | |||
962 | if (fd < 0) { | ||
963 | - if (i->type == WRITE_FILE && errno == ENOENT) { | ||
964 | - log_debug_errno(errno, "Not writing missing file \"%s\": %m", path); | ||
965 | - return 0; | ||
966 | + /* Even on a read-only filesystem, open(2) returns EEXIST if the | ||
967 | + * file already exists. It returns EROFS only if it needs to | ||
968 | + * create the file. */ | ||
969 | + if (errno != EEXIST) | ||
970 | + return log_error_errno(errno, "Failed to create file %s: %m", path); | ||
971 | + | ||
972 | + /* Re-open the file. At that point it must exist since open(2) | ||
973 | + * failed with EEXIST. We still need to check if the perms/mode | ||
974 | + * need to be changed. For read-only filesystems, we let | ||
975 | + * fd_set_perms() report the error if the perms need to be | ||
976 | + * modified. */ | ||
977 | + fd = openat(dir_fd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH, i->mode); | ||
978 | + if (fd < 0) | ||
979 | + return log_error_errno(errno, "Failed to re-open file %s: %m", path); | ||
980 | + | ||
981 | + if (fstat(fd, &stbuf) < 0) | ||
982 | + return log_error_errno(errno, "stat(%s) failed: %m", path); | ||
983 | + | ||
984 | + if (!S_ISREG(stbuf.st_mode)) { | ||
985 | + log_error("%s exists and is not a regular file.", path); | ||
986 | + return -EEXIST; | ||
987 | } | ||
988 | - if (i->type == CREATE_FILE && errno == EEXIST) { | ||
989 | - log_debug_errno(errno, "Not writing to pre-existing file \"%s\": %m", path); | ||
990 | - goto done; | ||
991 | + | ||
992 | + st = &stbuf; | ||
993 | + } else { | ||
994 | + | ||
995 | + log_debug("\"%s\" has been created.", path); | ||
996 | + | ||
997 | + if (i->argument) { | ||
998 | + log_debug("Writing to \"%s\".", path); | ||
999 | + | ||
1000 | + r = loop_write(fd, i->argument, strlen(i->argument), false); | ||
1001 | + if (r < 0) | ||
1002 | + return log_error_errno(r, "Failed to write file \"%s\": %m", path); | ||
1003 | } | ||
1004 | + } | ||
1005 | |||
1006 | - r = -errno; | ||
1007 | - if (!i->argument && errno == EROFS && stat(path, &st) == 0 && | ||
1008 | - (i->type == CREATE_FILE || st.st_size == 0)) | ||
1009 | - goto check_mode; | ||
1010 | + return fd_set_perms(i, fd, st); | ||
1011 | +} | ||
1012 | |||
1013 | - return log_error_errno(r, "Failed to create file %s: %m", path); | ||
1014 | +static int truncate_file(Item *i, const char *path) { | ||
1015 | + _cleanup_close_ int fd = -1, dir_fd = -1; | ||
1016 | + struct stat stbuf, *st = NULL; | ||
1017 | + bool erofs = false; | ||
1018 | + int r = 0; | ||
1019 | + char *bn; | ||
1020 | + | ||
1021 | + assert(i); | ||
1022 | + assert(path); | ||
1023 | + assert(i->type == TRUNCATE_FILE); | ||
1024 | + | ||
1025 | + /* We want to operate on regular file exclusively especially since | ||
1026 | + * O_TRUNC is unspecified if the file is neither a regular file nor a | ||
1027 | + * fifo nor a terminal device. Therefore we first open the file and make | ||
1028 | + * sure it's a regular one before truncating it. */ | ||
1029 | + | ||
1030 | + /* Validate the path and keep the fd on the directory for opening the | ||
1031 | + * file so we're sure that it can't be changed behind our back. */ | ||
1032 | + dir_fd = path_open_parent_safe(path); | ||
1033 | + if (dir_fd < 0) | ||
1034 | + return dir_fd; | ||
1035 | + | ||
1036 | + bn = basename(path); | ||
1037 | + | ||
1038 | + RUN_WITH_UMASK(0000) { | ||
1039 | + mac_selinux_create_file_prepare(path, S_IFREG); | ||
1040 | + fd = openat(dir_fd, bn, O_CREAT|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode); | ||
1041 | + mac_selinux_create_file_clear(); | ||
1042 | } | ||
1043 | |||
1044 | - if (i->argument) { | ||
1045 | - log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path); | ||
1046 | + if (fd < 0) { | ||
1047 | + if (errno != EROFS) | ||
1048 | + return log_error_errno(errno, "Failed to open/create file %s: %m", path); | ||
1049 | |||
1050 | - r = loop_write(fd, i->argument, strlen(i->argument), false); | ||
1051 | - if (r < 0) | ||
1052 | - return log_error_errno(r, "Failed to write file \"%s\": %m", path); | ||
1053 | - } else | ||
1054 | - log_debug("\"%s\" has been created.", path); | ||
1055 | + /* On a read-only filesystem, we don't want to fail if the | ||
1056 | + * target is already empty and the perms are set. So we still | ||
1057 | + * proceed with the sanity checks and let the remaining | ||
1058 | + * operations fail with EROFS if they try to modify the target | ||
1059 | + * file. */ | ||
1060 | + | ||
1061 | + fd = openat(dir_fd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH, i->mode); | ||
1062 | + if (fd < 0) { | ||
1063 | + if (errno == ENOENT) { | ||
1064 | + log_error("Cannot create file %s on a read-only file system.", path); | ||
1065 | + return -EROFS; | ||
1066 | + } | ||
1067 | |||
1068 | - fd = safe_close(fd); | ||
1069 | + return log_error_errno(errno, "Failed to re-open file %s: %m", path); | ||
1070 | + } | ||
1071 | |||
1072 | -done: | ||
1073 | - if (stat(path, &st) < 0) | ||
1074 | + erofs = true; | ||
1075 | + } | ||
1076 | + | ||
1077 | + if (fstat(fd, &stbuf) < 0) | ||
1078 | return log_error_errno(errno, "stat(%s) failed: %m", path); | ||
1079 | |||
1080 | - check_mode: | ||
1081 | - if (!S_ISREG(st.st_mode)) { | ||
1082 | - log_error("%s is not a file.", path); | ||
1083 | + if (!S_ISREG(stbuf.st_mode)) { | ||
1084 | + log_error("%s exists and is not a regular file.", path); | ||
1085 | return -EEXIST; | ||
1086 | } | ||
1087 | |||
1088 | - r = path_set_perms(i, path); | ||
1089 | + if (stbuf.st_size > 0) { | ||
1090 | + if (ftruncate(fd, 0) < 0) { | ||
1091 | + r = erofs ? -EROFS : -errno; | ||
1092 | + return log_error_errno(r, "Failed to truncate file %s: %m", path); | ||
1093 | + } | ||
1094 | + } else | ||
1095 | + st = &stbuf; | ||
1096 | + | ||
1097 | + log_debug("\"%s\" has been created.", path); | ||
1098 | + | ||
1099 | + if (i->argument) { | ||
1100 | + log_debug("Writing to \"%s\".", path); | ||
1101 | + | ||
1102 | + r = loop_write(fd, i->argument, strlen(i->argument), false); | ||
1103 | + if (r < 0) { | ||
1104 | + r = erofs ? -EROFS : r; | ||
1105 | + return log_error_errno(r, "Failed to write file %s: %m", path); | ||
1106 | + } | ||
1107 | + } | ||
1108 | + | ||
1109 | + return fd_set_perms(i, fd, st); | ||
1110 | +} | ||
1111 | + | ||
1112 | +static int copy_files(Item *i) { | ||
1113 | + _cleanup_close_ int dfd = -1, fd = -1; | ||
1114 | + char *bn; | ||
1115 | + int r; | ||
1116 | + | ||
1117 | + log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path); | ||
1118 | + | ||
1119 | + bn = basename(i->path); | ||
1120 | + | ||
1121 | + /* Validate the path and use the returned directory fd for copying the | ||
1122 | + * target so we're sure that the path can't be changed behind our | ||
1123 | + * back. */ | ||
1124 | + dfd = path_open_parent_safe(i->path); | ||
1125 | + if (dfd < 0) | ||
1126 | + return dfd; | ||
1127 | + | ||
1128 | + r = copy_tree_at(AT_FDCWD, i->argument, | ||
1129 | + dfd, bn, | ||
1130 | + i->uid_set ? i->uid : UID_INVALID, | ||
1131 | + i->gid_set ? i->gid : GID_INVALID, | ||
1132 | + COPY_REFLINK); | ||
1133 | + if (r < 0) { | ||
1134 | + struct stat a, b; | ||
1135 | + | ||
1136 | + /* If the target already exists on read-only filesystems, trying | ||
1137 | + * to create the target will not fail with EEXIST but with | ||
1138 | + * EROFS. */ | ||
1139 | + if (r == -EROFS && faccessat(dfd, bn, F_OK, AT_SYMLINK_NOFOLLOW) == 0) | ||
1140 | + r = -EEXIST; | ||
1141 | + | ||
1142 | + if (r != -EEXIST) | ||
1143 | + return log_error_errno(r, "Failed to copy files to %s: %m", i->path); | ||
1144 | + | ||
1145 | + if (stat(i->argument, &a) < 0) | ||
1146 | + return log_error_errno(errno, "stat(%s) failed: %m", i->argument); | ||
1147 | + | ||
1148 | + if (fstatat(dfd, bn, &b, AT_SYMLINK_NOFOLLOW) < 0) | ||
1149 | + return log_error_errno(errno, "stat(%s) failed: %m", i->path); | ||
1150 | + | ||
1151 | + if ((a.st_mode ^ b.st_mode) & S_IFMT) { | ||
1152 | + log_debug("Can't copy to %s, file exists already and is of different type", i->path); | ||
1153 | + return 0; | ||
1154 | + } | ||
1155 | + } | ||
1156 | + | ||
1157 | + fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
1158 | + if (fd < 0) | ||
1159 | + return log_error_errno(errno, "Failed to openat(%s): %m", i->path); | ||
1160 | + | ||
1161 | + return fd_set_perms(i, fd, NULL); | ||
1162 | +} | ||
1163 | + | ||
1164 | +typedef enum { | ||
1165 | + CREATION_NORMAL, | ||
1166 | + CREATION_EXISTING, | ||
1167 | + CREATION_FORCE, | ||
1168 | + _CREATION_MODE_MAX, | ||
1169 | + _CREATION_MODE_INVALID = -1 | ||
1170 | +} CreationMode; | ||
1171 | + | ||
1172 | +static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = { | ||
1173 | + [CREATION_NORMAL] = "Created", | ||
1174 | + [CREATION_EXISTING] = "Found existing", | ||
1175 | + [CREATION_FORCE] = "Created replacement", | ||
1176 | +}; | ||
1177 | + | ||
1178 | +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode); | ||
1179 | + | ||
1180 | +static int create_directory_or_subvolume(const char *path, mode_t mode, bool subvol) { | ||
1181 | + _cleanup_close_ int pfd = -1; | ||
1182 | + CreationMode creation; | ||
1183 | + int r; | ||
1184 | + | ||
1185 | + assert(path); | ||
1186 | + | ||
1187 | + pfd = path_open_parent_safe(path); | ||
1188 | + if (pfd < 0) | ||
1189 | + return pfd; | ||
1190 | + | ||
1191 | + if (subvol) { | ||
1192 | + if (btrfs_is_subvol(empty_to_root(arg_root)) <= 0) | ||
1193 | + | ||
1194 | + /* Don't create a subvolume unless the root directory is | ||
1195 | + * one, too. We do this under the assumption that if the | ||
1196 | + * root directory is just a plain directory (i.e. very | ||
1197 | + * light-weight), we shouldn't try to split it up into | ||
1198 | + * subvolumes (i.e. more heavy-weight). Thus, chroot() | ||
1199 | + * environments and suchlike will get a full brtfs | ||
1200 | + * subvolume set up below their tree only if they | ||
1201 | + * specifically set up a btrfs subvolume for the root | ||
1202 | + * dir too. */ | ||
1203 | + | ||
1204 | + subvol = false; | ||
1205 | + else { | ||
1206 | + RUN_WITH_UMASK((~mode) & 0777) | ||
1207 | + r = btrfs_subvol_make_fd(pfd, basename(path)); | ||
1208 | + } | ||
1209 | + } else | ||
1210 | + r = 0; | ||
1211 | + | ||
1212 | + if (!subvol || r == -ENOTTY) | ||
1213 | + RUN_WITH_UMASK(0000) | ||
1214 | + r = mkdirat_label(pfd, basename(path), mode); | ||
1215 | + | ||
1216 | + if (r < 0) { | ||
1217 | + int k; | ||
1218 | + | ||
1219 | + if (!IN_SET(r, -EEXIST, -EROFS)) | ||
1220 | + return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", path); | ||
1221 | + | ||
1222 | + k = is_dir_fd(pfd); | ||
1223 | + if (k == -ENOENT && r == -EROFS) | ||
1224 | + return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", path); | ||
1225 | + if (k < 0) | ||
1226 | + return log_error_errno(k, "Failed to check if %s exists: %m", path); | ||
1227 | + if (!k) { | ||
1228 | + log_warning("\"%s\" already exists and is not a directory.", path); | ||
1229 | + return -EEXIST; | ||
1230 | + } | ||
1231 | + | ||
1232 | + creation = CREATION_EXISTING; | ||
1233 | + } else | ||
1234 | + creation = CREATION_NORMAL; | ||
1235 | + | ||
1236 | + log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), path); | ||
1237 | + | ||
1238 | + r = openat(pfd, basename(path), O_NOCTTY|O_CLOEXEC|O_DIRECTORY); | ||
1239 | if (r < 0) | ||
1240 | - return r; | ||
1241 | + return -errno; | ||
1242 | + return r; | ||
1243 | +} | ||
1244 | |||
1245 | - return 0; | ||
1246 | +static int create_directory(Item *i, const char *path) { | ||
1247 | + _cleanup_close_ int fd = -1; | ||
1248 | + | ||
1249 | + assert(i); | ||
1250 | + assert(IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY)); | ||
1251 | + | ||
1252 | + fd = create_directory_or_subvolume(path, i->mode, false); | ||
1253 | + if (fd == -EEXIST) | ||
1254 | + return 0; | ||
1255 | + if (fd < 0) | ||
1256 | + return fd; | ||
1257 | + | ||
1258 | + return fd_set_perms(i, fd, NULL); | ||
1259 | +} | ||
1260 | + | ||
1261 | +static int create_subvolume(Item *i, const char *path) { | ||
1262 | + _cleanup_close_ int fd = -1; | ||
1263 | + int r, q = 0; | ||
1264 | + | ||
1265 | + assert(i); | ||
1266 | + assert(IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)); | ||
1267 | + | ||
1268 | + fd = create_directory_or_subvolume(path, i->mode, true); | ||
1269 | + if (fd == -EEXIST) | ||
1270 | + return 0; | ||
1271 | + if (fd < 0) | ||
1272 | + return fd; | ||
1273 | + | ||
1274 | + if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) { | ||
1275 | + r = btrfs_subvol_auto_qgroup_fd(fd, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA); | ||
1276 | + if (r == -ENOTTY) | ||
1277 | + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path); | ||
1278 | + else if (r == -EROFS) | ||
1279 | + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path); | ||
1280 | + else if (r == -ENOPROTOOPT) | ||
1281 | + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path); | ||
1282 | + else if (r < 0) | ||
1283 | + q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); | ||
1284 | + else if (r > 0) | ||
1285 | + log_debug("Adjusted quota for subvolume \"%s\".", i->path); | ||
1286 | + else if (r == 0) | ||
1287 | + log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path); | ||
1288 | + } | ||
1289 | + | ||
1290 | + r = fd_set_perms(i, fd, NULL); | ||
1291 | + if (q < 0) | ||
1292 | + return q; | ||
1293 | + | ||
1294 | + return r; | ||
1295 | +} | ||
1296 | + | ||
1297 | +static int empty_directory(Item *i, const char *path) { | ||
1298 | + int r; | ||
1299 | + | ||
1300 | + assert(i); | ||
1301 | + assert(i->type == EMPTY_DIRECTORY); | ||
1302 | + | ||
1303 | + r = is_dir(path, false); | ||
1304 | + if (r == -ENOENT) { | ||
1305 | + /* Option "e" operates only on existing objects. Do not | ||
1306 | + * print errors about non-existent files or directories */ | ||
1307 | + log_debug("Skipping missing directory: %s", path); | ||
1308 | + return 0; | ||
1309 | + } | ||
1310 | + if (r < 0) | ||
1311 | + return log_error_errno(r, "is_dir() failed on path %s: %m", path); | ||
1312 | + | ||
1313 | + return path_set_perms(i, path); | ||
1314 | +} | ||
1315 | + | ||
1316 | +static int create_device(Item *i, mode_t file_type) { | ||
1317 | + _cleanup_close_ int dfd = -1, fd = -1; | ||
1318 | + CreationMode creation; | ||
1319 | + char *bn; | ||
1320 | + int r; | ||
1321 | + | ||
1322 | + assert(i); | ||
1323 | + assert(IN_SET(file_type, S_IFBLK, S_IFCHR)); | ||
1324 | + | ||
1325 | + bn = basename(i->path); | ||
1326 | + | ||
1327 | + /* Validate the path and use the returned directory fd for copying the | ||
1328 | + * target so we're sure that the path can't be changed behind our | ||
1329 | + * back. */ | ||
1330 | + dfd = path_open_parent_safe(i->path); | ||
1331 | + if (dfd < 0) | ||
1332 | + return dfd; | ||
1333 | + | ||
1334 | + RUN_WITH_UMASK(0000) { | ||
1335 | + mac_selinux_create_file_prepare(i->path, file_type); | ||
1336 | + r = mknodat(dfd, bn, i->mode | file_type, i->major_minor); | ||
1337 | + mac_selinux_create_file_clear(); | ||
1338 | + } | ||
1339 | + | ||
1340 | + if (r < 0) { | ||
1341 | + struct stat st; | ||
1342 | + | ||
1343 | + if (errno == EPERM) { | ||
1344 | + log_debug("We lack permissions, possibly because of cgroup configuration; " | ||
1345 | + "skipping creation of device node %s.", i->path); | ||
1346 | + return 0; | ||
1347 | + } | ||
1348 | + | ||
1349 | + if (errno != EEXIST) | ||
1350 | + return log_error_errno(errno, "Failed to create device node %s: %m", i->path); | ||
1351 | + | ||
1352 | + if (fstatat(dfd, bn, &st, 0) < 0) | ||
1353 | + return log_error_errno(errno, "stat(%s) failed: %m", i->path); | ||
1354 | + | ||
1355 | + if ((st.st_mode & S_IFMT) != file_type) { | ||
1356 | + | ||
1357 | + if (i->force) { | ||
1358 | + | ||
1359 | + RUN_WITH_UMASK(0000) { | ||
1360 | + mac_selinux_create_file_prepare(i->path, file_type); | ||
1361 | + /* FIXME: need to introduce mknodat_atomic() */ | ||
1362 | + r = mknod_atomic(i->path, i->mode | file_type, i->major_minor); | ||
1363 | + mac_selinux_create_file_clear(); | ||
1364 | + } | ||
1365 | + | ||
1366 | + if (r < 0) | ||
1367 | + return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path); | ||
1368 | + creation = CREATION_FORCE; | ||
1369 | + } else { | ||
1370 | + log_debug("%s is not a device node.", i->path); | ||
1371 | + return 0; | ||
1372 | + } | ||
1373 | + } else | ||
1374 | + creation = CREATION_EXISTING; | ||
1375 | + } else | ||
1376 | + creation = CREATION_NORMAL; | ||
1377 | + | ||
1378 | + log_debug("%s %s device node \"%s\" %u:%u.", | ||
1379 | + creation_mode_verb_to_string(creation), | ||
1380 | + i->type == CREATE_BLOCK_DEVICE ? "block" : "char", | ||
1381 | + i->path, major(i->mode), minor(i->mode)); | ||
1382 | + | ||
1383 | + fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
1384 | + if (fd < 0) | ||
1385 | + return log_error_errno(errno, "Failed to openat(%s): %m", i->path); | ||
1386 | + | ||
1387 | + return fd_set_perms(i, fd, NULL); | ||
1388 | +} | ||
1389 | + | ||
1390 | +static int create_fifo(Item *i, const char *path) { | ||
1391 | + _cleanup_close_ int pfd = -1, fd = -1; | ||
1392 | + CreationMode creation; | ||
1393 | + struct stat st; | ||
1394 | + char *bn; | ||
1395 | + int r; | ||
1396 | + | ||
1397 | + pfd = path_open_parent_safe(path); | ||
1398 | + if (pfd < 0) | ||
1399 | + return pfd; | ||
1400 | + | ||
1401 | + bn = basename(path); | ||
1402 | + | ||
1403 | + RUN_WITH_UMASK(0000) { | ||
1404 | + mac_selinux_create_file_prepare(path, S_IFIFO); | ||
1405 | + r = mkfifoat(pfd, bn, i->mode); | ||
1406 | + mac_selinux_create_file_clear(); | ||
1407 | + } | ||
1408 | + | ||
1409 | + if (r < 0) { | ||
1410 | + if (errno != EEXIST) | ||
1411 | + return log_error_errno(errno, "Failed to create fifo %s: %m", path); | ||
1412 | + | ||
1413 | + if (fstatat(pfd, bn, &st, AT_SYMLINK_NOFOLLOW) < 0) | ||
1414 | + return log_error_errno(errno, "stat(%s) failed: %m", path); | ||
1415 | + | ||
1416 | + if (!S_ISFIFO(st.st_mode)) { | ||
1417 | + | ||
1418 | + if (i->force) { | ||
1419 | + RUN_WITH_UMASK(0000) { | ||
1420 | + mac_selinux_create_file_prepare(path, S_IFIFO); | ||
1421 | + r = mkfifoat_atomic(pfd, bn, i->mode); | ||
1422 | + mac_selinux_create_file_clear(); | ||
1423 | + } | ||
1424 | + | ||
1425 | + if (r < 0) | ||
1426 | + return log_error_errno(r, "Failed to create fifo %s: %m", path); | ||
1427 | + creation = CREATION_FORCE; | ||
1428 | + } else { | ||
1429 | + log_warning("\"%s\" already exists and is not a fifo.", path); | ||
1430 | + return 0; | ||
1431 | + } | ||
1432 | + } else | ||
1433 | + creation = CREATION_EXISTING; | ||
1434 | + } else | ||
1435 | + creation = CREATION_NORMAL; | ||
1436 | + | ||
1437 | + log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), path); | ||
1438 | + | ||
1439 | + fd = openat(pfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
1440 | + if (fd < 0) | ||
1441 | + return log_error_errno(fd, "Failed to openat(%s): %m", path); | ||
1442 | + | ||
1443 | + return fd_set_perms(i, fd, NULL); | ||
1444 | } | ||
1445 | |||
1446 | typedef int (*action_t)(Item *, const char *); | ||
1447 | typedef int (*fdaction_t)(Item *, int fd, const struct stat *st); | ||
1448 | |||
1449 | -static int item_do(Item *i, int fd, const struct stat *st, fdaction_t action) { | ||
1450 | +static int item_do(Item *i, int fd, fdaction_t action) { | ||
1451 | + struct stat st; | ||
1452 | int r = 0, q; | ||
1453 | |||
1454 | assert(i); | ||
1455 | assert(fd >= 0); | ||
1456 | - assert(st); | ||
1457 | + | ||
1458 | + if (fstat(fd, &st) < 0) { | ||
1459 | + r = -errno; | ||
1460 | + goto finish; | ||
1461 | + } | ||
1462 | |||
1463 | /* This returns the first error we run into, but nevertheless | ||
1464 | * tries to go on */ | ||
1465 | - r = action(i, fd, st); | ||
1466 | + r = action(i, fd, &st); | ||
1467 | |||
1468 | - if (S_ISDIR(st->st_mode)) { | ||
1469 | + if (S_ISDIR(st.st_mode)) { | ||
1470 | char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; | ||
1471 | _cleanup_closedir_ DIR *d = NULL; | ||
1472 | struct dirent *de; | ||
1473 | @@ -1349,16 +1854,15 @@ static int item_do(Item *i, int fd, const struct stat *st, fdaction_t action) { | ||
1474 | } | ||
1475 | |||
1476 | FOREACH_DIRENT_ALL(de, d, q = -errno; goto finish) { | ||
1477 | - struct stat de_st; | ||
1478 | int de_fd; | ||
1479 | |||
1480 | if (dot_or_dot_dot(de->d_name)) | ||
1481 | continue; | ||
1482 | |||
1483 | de_fd = openat(fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_PATH); | ||
1484 | - if (de_fd >= 0 && fstat(de_fd, &de_st) >= 0) | ||
1485 | + if (de_fd >= 0) | ||
1486 | /* pass ownership of dirent fd over */ | ||
1487 | - q = item_do(i, de_fd, &de_st, action); | ||
1488 | + q = item_do(i, de_fd, action); | ||
1489 | else | ||
1490 | q = -errno; | ||
1491 | |||
1492 | @@ -1406,7 +1910,6 @@ static int glob_item_recursively(Item *i, fdaction_t action) { | ||
1493 | |||
1494 | STRV_FOREACH(fn, g.gl_pathv) { | ||
1495 | _cleanup_close_ int fd = -1; | ||
1496 | - struct stat st; | ||
1497 | |||
1498 | /* Make sure we won't trigger/follow file object (such as | ||
1499 | * device nodes, automounts, ...) pointed out by 'fn' with | ||
1500 | @@ -1419,12 +1922,7 @@ static int glob_item_recursively(Item *i, fdaction_t action) { | ||
1501 | continue; | ||
1502 | } | ||
1503 | |||
1504 | - if (fstat(fd, &st) < 0) { | ||
1505 | - r = r ?: -errno; | ||
1506 | - continue; | ||
1507 | - } | ||
1508 | - | ||
1509 | - k = item_do(i, fd, &st, action); | ||
1510 | + k = item_do(i, fd, action); | ||
1511 | if (k < 0 && r == 0) | ||
1512 | r = k; | ||
1513 | |||
1514 | @@ -1435,27 +1933,9 @@ static int glob_item_recursively(Item *i, fdaction_t action) { | ||
1515 | return r; | ||
1516 | } | ||
1517 | |||
1518 | -typedef enum { | ||
1519 | - CREATION_NORMAL, | ||
1520 | - CREATION_EXISTING, | ||
1521 | - CREATION_FORCE, | ||
1522 | - _CREATION_MODE_MAX, | ||
1523 | - _CREATION_MODE_INVALID = -1 | ||
1524 | -} CreationMode; | ||
1525 | - | ||
1526 | -static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = { | ||
1527 | - [CREATION_NORMAL] = "Created", | ||
1528 | - [CREATION_EXISTING] = "Found existing", | ||
1529 | - [CREATION_FORCE] = "Created replacement", | ||
1530 | -}; | ||
1531 | - | ||
1532 | -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode); | ||
1533 | - | ||
1534 | static int create_item(Item *i) { | ||
1535 | - struct stat st; | ||
1536 | - int r = 0; | ||
1537 | - int q = 0; | ||
1538 | CreationMode creation; | ||
1539 | + int r = 0; | ||
1540 | |||
1541 | assert(i); | ||
1542 | |||
1543 | @@ -1470,51 +1950,31 @@ static int create_item(Item *i) { | ||
1544 | return 0; | ||
1545 | |||
1546 | case CREATE_FILE: | ||
1547 | - case TRUNCATE_FILE: | ||
1548 | RUN_WITH_UMASK(0000) | ||
1549 | (void) mkdir_parents_label(i->path, 0755); | ||
1550 | |||
1551 | - r = write_one_file(i, i->path); | ||
1552 | + r = create_file(i, i->path); | ||
1553 | if (r < 0) | ||
1554 | return r; | ||
1555 | break; | ||
1556 | |||
1557 | - case COPY_FILES: { | ||
1558 | - | ||
1559 | + case TRUNCATE_FILE: | ||
1560 | RUN_WITH_UMASK(0000) | ||
1561 | (void) mkdir_parents_label(i->path, 0755); | ||
1562 | |||
1563 | - log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path); | ||
1564 | - r = copy_tree(i->argument, i->path, | ||
1565 | - i->uid_set ? i->uid : UID_INVALID, | ||
1566 | - i->gid_set ? i->gid : GID_INVALID, | ||
1567 | - COPY_REFLINK); | ||
1568 | - | ||
1569 | - if (r == -EROFS && stat(i->path, &st) == 0) | ||
1570 | - r = -EEXIST; | ||
1571 | - | ||
1572 | - if (r < 0) { | ||
1573 | - struct stat a, b; | ||
1574 | - | ||
1575 | - if (r != -EEXIST) | ||
1576 | - return log_error_errno(r, "Failed to copy files to %s: %m", i->path); | ||
1577 | - | ||
1578 | - if (stat(i->argument, &a) < 0) | ||
1579 | - return log_error_errno(errno, "stat(%s) failed: %m", i->argument); | ||
1580 | + r = truncate_file(i, i->path); | ||
1581 | + if (r < 0) | ||
1582 | + return r; | ||
1583 | + break; | ||
1584 | |||
1585 | - if (stat(i->path, &b) < 0) | ||
1586 | - return log_error_errno(errno, "stat(%s) failed: %m", i->path); | ||
1587 | + case COPY_FILES: { | ||
1588 | |||
1589 | - if ((a.st_mode ^ b.st_mode) & S_IFMT) { | ||
1590 | - log_debug("Can't copy to %s, file exists already and is of different type", i->path); | ||
1591 | - return 0; | ||
1592 | - } | ||
1593 | - } | ||
1594 | + RUN_WITH_UMASK(0000) | ||
1595 | + (void) mkdir_parents_label(i->path, 0755); | ||
1596 | |||
1597 | - r = path_set_perms(i, i->path); | ||
1598 | + r = copy_files(i); | ||
1599 | if (r < 0) | ||
1600 | return r; | ||
1601 | - | ||
1602 | break; | ||
1603 | |||
1604 | case WRITE_FILE: | ||
1605 | @@ -1526,132 +1986,39 @@ static int create_item(Item *i) { | ||
1606 | |||
1607 | case CREATE_DIRECTORY: | ||
1608 | case TRUNCATE_DIRECTORY: | ||
1609 | + RUN_WITH_UMASK(0000) | ||
1610 | + (void) mkdir_parents_label(i->path, 0755); | ||
1611 | + | ||
1612 | + r = create_directory(i, i->path); | ||
1613 | + if (r < 0) | ||
1614 | + return r; | ||
1615 | + break; | ||
1616 | + | ||
1617 | case CREATE_SUBVOLUME: | ||
1618 | case CREATE_SUBVOLUME_INHERIT_QUOTA: | ||
1619 | case CREATE_SUBVOLUME_NEW_QUOTA: | ||
1620 | RUN_WITH_UMASK(0000) | ||
1621 | (void) mkdir_parents_label(i->path, 0755); | ||
1622 | |||
1623 | - if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) { | ||
1624 | - | ||
1625 | - if (btrfs_is_subvol(isempty(arg_root) ? "/" : arg_root) <= 0) | ||
1626 | - | ||
1627 | - /* Don't create a subvolume unless the | ||
1628 | - * root directory is one, too. We do | ||
1629 | - * this under the assumption that if | ||
1630 | - * the root directory is just a plain | ||
1631 | - * directory (i.e. very light-weight), | ||
1632 | - * we shouldn't try to split it up | ||
1633 | - * into subvolumes (i.e. more | ||
1634 | - * heavy-weight). Thus, chroot() | ||
1635 | - * environments and suchlike will get | ||
1636 | - * a full brtfs subvolume set up below | ||
1637 | - * their tree only if they | ||
1638 | - * specifically set up a btrfs | ||
1639 | - * subvolume for the root dir too. */ | ||
1640 | - | ||
1641 | - r = -ENOTTY; | ||
1642 | - else { | ||
1643 | - RUN_WITH_UMASK((~i->mode) & 0777) | ||
1644 | - r = btrfs_subvol_make(i->path); | ||
1645 | - } | ||
1646 | - } else | ||
1647 | - r = 0; | ||
1648 | - | ||
1649 | - if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY) | ||
1650 | - RUN_WITH_UMASK(0000) | ||
1651 | - r = mkdir_label(i->path, i->mode); | ||
1652 | - | ||
1653 | - if (r < 0) { | ||
1654 | - int k; | ||
1655 | - | ||
1656 | - if (!IN_SET(r, -EEXIST, -EROFS)) | ||
1657 | - return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path); | ||
1658 | - | ||
1659 | - k = is_dir(i->path, false); | ||
1660 | - if (k == -ENOENT && r == -EROFS) | ||
1661 | - return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", i->path); | ||
1662 | - if (k < 0) | ||
1663 | - return log_error_errno(k, "Failed to check if %s exists: %m", i->path); | ||
1664 | - if (!k) { | ||
1665 | - log_warning("\"%s\" already exists and is not a directory.", i->path); | ||
1666 | - return 0; | ||
1667 | - } | ||
1668 | - | ||
1669 | - creation = CREATION_EXISTING; | ||
1670 | - } else | ||
1671 | - creation = CREATION_NORMAL; | ||
1672 | - | ||
1673 | - log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path); | ||
1674 | - | ||
1675 | - if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) { | ||
1676 | - r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA); | ||
1677 | - if (r == -ENOTTY) | ||
1678 | - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path); | ||
1679 | - else if (r == -EROFS) | ||
1680 | - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path); | ||
1681 | - else if (r == -ENOPROTOOPT) | ||
1682 | - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path); | ||
1683 | - else if (r < 0) | ||
1684 | - q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); | ||
1685 | - else if (r > 0) | ||
1686 | - log_debug("Adjusted quota for subvolume \"%s\".", i->path); | ||
1687 | - else if (r == 0) | ||
1688 | - log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path); | ||
1689 | - } | ||
1690 | + r = create_subvolume(i, i->path); | ||
1691 | + if (r < 0) | ||
1692 | + return r; | ||
1693 | + break; | ||
1694 | |||
1695 | - _fallthrough_; | ||
1696 | case EMPTY_DIRECTORY: | ||
1697 | - r = path_set_perms(i, i->path); | ||
1698 | - if (q < 0) | ||
1699 | - return q; | ||
1700 | + r = empty_directory(i, i->path); | ||
1701 | if (r < 0) | ||
1702 | return r; | ||
1703 | |||
1704 | break; | ||
1705 | |||
1706 | case CREATE_FIFO: | ||
1707 | - RUN_WITH_UMASK(0000) { | ||
1708 | + RUN_WITH_UMASK(0000) | ||
1709 | (void) mkdir_parents_label(i->path, 0755); | ||
1710 | |||
1711 | - mac_selinux_create_file_prepare(i->path, S_IFIFO); | ||
1712 | - r = mkfifo(i->path, i->mode); | ||
1713 | - mac_selinux_create_file_clear(); | ||
1714 | - } | ||
1715 | - | ||
1716 | - if (r < 0) { | ||
1717 | - if (errno != EEXIST) | ||
1718 | - return log_error_errno(errno, "Failed to create fifo %s: %m", i->path); | ||
1719 | - | ||
1720 | - if (lstat(i->path, &st) < 0) | ||
1721 | - return log_error_errno(errno, "stat(%s) failed: %m", i->path); | ||
1722 | - | ||
1723 | - if (!S_ISFIFO(st.st_mode)) { | ||
1724 | - | ||
1725 | - if (i->force) { | ||
1726 | - RUN_WITH_UMASK(0000) { | ||
1727 | - mac_selinux_create_file_prepare(i->path, S_IFIFO); | ||
1728 | - r = mkfifo_atomic(i->path, i->mode); | ||
1729 | - mac_selinux_create_file_clear(); | ||
1730 | - } | ||
1731 | - | ||
1732 | - if (r < 0) | ||
1733 | - return log_error_errno(r, "Failed to create fifo %s: %m", i->path); | ||
1734 | - creation = CREATION_FORCE; | ||
1735 | - } else { | ||
1736 | - log_warning("\"%s\" already exists and is not a fifo.", i->path); | ||
1737 | - return 0; | ||
1738 | - } | ||
1739 | - } else | ||
1740 | - creation = CREATION_EXISTING; | ||
1741 | - } else | ||
1742 | - creation = CREATION_NORMAL; | ||
1743 | - log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path); | ||
1744 | - | ||
1745 | - r = path_set_perms(i, i->path); | ||
1746 | + r = create_fifo(i, i->path); | ||
1747 | if (r < 0) | ||
1748 | return r; | ||
1749 | - | ||
1750 | break; | ||
1751 | } | ||
1752 | |||
1753 | @@ -1704,9 +2071,7 @@ static int create_item(Item *i) { | ||
1754 | } | ||
1755 | |||
1756 | case CREATE_BLOCK_DEVICE: | ||
1757 | - case CREATE_CHAR_DEVICE: { | ||
1758 | - mode_t file_type; | ||
1759 | - | ||
1760 | + case CREATE_CHAR_DEVICE: | ||
1761 | if (have_effective_cap(CAP_MKNOD) == 0) { | ||
1762 | /* In a container we lack CAP_MKNOD. We | ||
1763 | shouldn't attempt to create the device node in | ||
1764 | @@ -1720,60 +2085,11 @@ static int create_item(Item *i) { | ||
1765 | RUN_WITH_UMASK(0000) | ||
1766 | (void) mkdir_parents_label(i->path, 0755); | ||
1767 | |||
1768 | - file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR; | ||
1769 | - | ||
1770 | - RUN_WITH_UMASK(0000) { | ||
1771 | - mac_selinux_create_file_prepare(i->path, file_type); | ||
1772 | - r = mknod(i->path, i->mode | file_type, i->major_minor); | ||
1773 | - mac_selinux_create_file_clear(); | ||
1774 | - } | ||
1775 | - | ||
1776 | - if (r < 0) { | ||
1777 | - if (errno == EPERM) { | ||
1778 | - log_debug("We lack permissions, possibly because of cgroup configuration; " | ||
1779 | - "skipping creation of device node %s.", i->path); | ||
1780 | - return 0; | ||
1781 | - } | ||
1782 | - | ||
1783 | - if (errno != EEXIST) | ||
1784 | - return log_error_errno(errno, "Failed to create device node %s: %m", i->path); | ||
1785 | - | ||
1786 | - if (lstat(i->path, &st) < 0) | ||
1787 | - return log_error_errno(errno, "stat(%s) failed: %m", i->path); | ||
1788 | - | ||
1789 | - if ((st.st_mode & S_IFMT) != file_type) { | ||
1790 | - | ||
1791 | - if (i->force) { | ||
1792 | - | ||
1793 | - RUN_WITH_UMASK(0000) { | ||
1794 | - mac_selinux_create_file_prepare(i->path, file_type); | ||
1795 | - r = mknod_atomic(i->path, i->mode | file_type, i->major_minor); | ||
1796 | - mac_selinux_create_file_clear(); | ||
1797 | - } | ||
1798 | - | ||
1799 | - if (r < 0) | ||
1800 | - return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path); | ||
1801 | - creation = CREATION_FORCE; | ||
1802 | - } else { | ||
1803 | - log_debug("%s is not a device node.", i->path); | ||
1804 | - return 0; | ||
1805 | - } | ||
1806 | - } else | ||
1807 | - creation = CREATION_EXISTING; | ||
1808 | - } else | ||
1809 | - creation = CREATION_NORMAL; | ||
1810 | - | ||
1811 | - log_debug("%s %s device node \"%s\" %u:%u.", | ||
1812 | - creation_mode_verb_to_string(creation), | ||
1813 | - i->type == CREATE_BLOCK_DEVICE ? "block" : "char", | ||
1814 | - i->path, major(i->mode), minor(i->mode)); | ||
1815 | - | ||
1816 | - r = path_set_perms(i, i->path); | ||
1817 | + r = create_device(i, i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR); | ||
1818 | if (r < 0) | ||
1819 | return r; | ||
1820 | |||
1821 | break; | ||
1822 | - } | ||
1823 | |||
1824 | case ADJUST_MODE: | ||
1825 | case RELABEL_PATH: | ||
1826 | -- | ||
1827 | 2.11.0 | ||
1828 | |||
diff --git a/meta/recipes-core/systemd/systemd_237.bb b/meta/recipes-core/systemd/systemd_237.bb index 96f419a7f9..bc33fbebdc 100644 --- a/meta/recipes-core/systemd/systemd_237.bb +++ b/meta/recipes-core/systemd/systemd_237.bb | |||
@@ -61,6 +61,8 @@ SRC_URI += "file://touchscreen.rules \ | |||
61 | file://0025-journald-set-a-limit-on-the-number-of-fields-1k.patch \ | 61 | file://0025-journald-set-a-limit-on-the-number-of-fields-1k.patch \ |
62 | file://0026-journal-remote-set-a-limit-on-the-number-of-fields-i.patch \ | 62 | file://0026-journal-remote-set-a-limit-on-the-number-of-fields-i.patch \ |
63 | file://0027-journal-fix-out-of-bounds-read-CVE-2018-16866.patch \ | 63 | file://0027-journal-fix-out-of-bounds-read-CVE-2018-16866.patch \ |
64 | file://0001-tmpfiles-don-t-resolve-pathnames-when-traversing-rec.patch \ | ||
65 | file://0002-Make-tmpfiles-safe.patch \ | ||
64 | " | 66 | " |
65 | SRC_URI_append_qemuall = " file://0001-core-device.c-Change-the-default-device-timeout-to-2.patch" | 67 | SRC_URI_append_qemuall = " file://0001-core-device.c-Change-the-default-device-timeout-to-2.patch" |
66 | 68 | ||