]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/tmpfiles/tmpfiles.c
core: skip the removal of cgroups in the TEST_RUN_MINIMAL mode (#8622)
[thirdparty/systemd.git] / src / tmpfiles / tmpfiles.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
5008d581
LP
2/***
3 This file is part of systemd.
4
3b63d2d3 5 Copyright 2010 Lennart Poettering, Kay Sievers
3f93da98 6 Copyright 2015 Zbigniew Jędrzejewski-Szmek
5008d581
LP
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
5008d581
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
5008d581 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
5008d581
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
5008d581 22#include <errno.h>
3f6fd1ba
LP
23#include <fcntl.h>
24#include <fnmatch.h>
25#include <getopt.h>
26#include <glob.h>
5008d581 27#include <limits.h>
3f6fd1ba
LP
28#include <linux/fs.h>
29#include <stdbool.h>
30#include <stddef.h>
3b63d2d3
LP
31#include <stdio.h>
32#include <stdlib.h>
3f6fd1ba 33#include <string.h>
505ef0e3 34#include <sys/stat.h>
ebf4e801 35#include <sys/xattr.h>
d9daae55 36#include <sysexits.h>
3f6fd1ba
LP
37#include <time.h>
38#include <unistd.h>
5008d581 39
5a8575ef
ZJS
40#include "sd-path.h"
41
3f6fd1ba 42#include "acl-util.h"
b5efdb8a 43#include "alloc-util.h"
3f6fd1ba 44#include "btrfs-util.h"
430f0182 45#include "capability-util.h"
c8b3094d 46#include "chattr-util.h"
3f6fd1ba
LP
47#include "conf-files.h"
48#include "copy.h"
a0f29c76 49#include "def.h"
8fb3f009 50#include "dirent-util.h"
4f5dd394 51#include "escape.h"
3ffd4af2 52#include "fd-util.h"
0d39fa9c 53#include "fileio.h"
f97b34a6 54#include "format-util.h"
f4f15635 55#include "fs-util.h"
7d50b32a 56#include "glob-util.h"
c004493c 57#include "io-util.h"
3f6fd1ba 58#include "label.h"
5008d581 59#include "log.h"
54693d9b 60#include "macro.h"
d39efe74 61#include "missing.h"
49e942b2 62#include "mkdir.h"
4e036b7a 63#include "mount-util.h"
6bedfcbb 64#include "parse-util.h"
f2b5ca0e 65#include "path-lookup.h"
9eb977db 66#include "path-util.h"
c6878637 67#include "rm-rf.h"
d7b8eec7 68#include "selinux-util.h"
3f6fd1ba
LP
69#include "set.h"
70#include "specifier.h"
8fcde012 71#include "stat-util.h"
15a5e950 72#include "stdio-util.h"
8b43440b 73#include "string-table.h"
07630cea 74#include "string-util.h"
3f6fd1ba 75#include "strv.h"
affb60b1 76#include "umask-util.h"
b1d4f8e1 77#include "user-util.h"
3f6fd1ba 78#include "util.h"
5008d581 79
01000479 80/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
5008d581 81 * them in the file system. This is intended to be used to create
db019b8d
KS
82 * properly owned directories beneath /tmp, /var/tmp, /run, which are
83 * volatile and hence need to be recreated on bootup. */
5008d581 84
66ccd038 85typedef enum ItemType {
b8bb3e8f 86 /* These ones take file names */
3b63d2d3
LP
87 CREATE_FILE = 'f',
88 TRUNCATE_FILE = 'F',
89 CREATE_DIRECTORY = 'd',
90 TRUNCATE_DIRECTORY = 'D',
d7b8eec7 91 CREATE_SUBVOLUME = 'v',
5fb13eb5
LP
92 CREATE_SUBVOLUME_INHERIT_QUOTA = 'q',
93 CREATE_SUBVOLUME_NEW_QUOTA = 'Q',
ee17ee7c 94 CREATE_FIFO = 'p',
468d726b
LP
95 CREATE_SYMLINK = 'L',
96 CREATE_CHAR_DEVICE = 'c',
97 CREATE_BLOCK_DEVICE = 'b',
849958d1 98 COPY_FILES = 'C',
b8bb3e8f
LP
99
100 /* These ones take globs */
17493fa5 101 WRITE_FILE = 'w',
df8dee85 102 EMPTY_DIRECTORY = 'e',
b705ab6a
ZJS
103 SET_XATTR = 't',
104 RECURSIVE_SET_XATTR = 'T',
105 SET_ACL = 'a',
106 RECURSIVE_SET_ACL = 'A',
17493fa5
LP
107 SET_ATTRIBUTE = 'h',
108 RECURSIVE_SET_ATTRIBUTE = 'H',
3b63d2d3 109 IGNORE_PATH = 'x',
78a92a5a 110 IGNORE_DIRECTORY_PATH = 'X',
3b63d2d3 111 REMOVE_PATH = 'r',
a8d88783 112 RECURSIVE_REMOVE_PATH = 'R',
777b87e7 113 RELABEL_PATH = 'z',
e73a03e0 114 RECURSIVE_RELABEL_PATH = 'Z',
17493fa5 115 ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
66ccd038 116} ItemType;
3b63d2d3
LP
117
118typedef struct Item {
66ccd038 119 ItemType type;
3b63d2d3
LP
120
121 char *path;
468d726b 122 char *argument;
ebf4e801 123 char **xattrs;
349cc4a5 124#if HAVE_ACL
f8eeeaf9
ZJS
125 acl_t acl_access;
126 acl_t acl_default;
127#endif
5008d581
LP
128 uid_t uid;
129 gid_t gid;
3b63d2d3
LP
130 mode_t mode;
131 usec_t age;
132
468d726b 133 dev_t major_minor;
88ec4dfa
LP
134 unsigned attribute_value;
135 unsigned attribute_mask;
468d726b 136
3b63d2d3
LP
137 bool uid_set:1;
138 bool gid_set:1;
139 bool mode_set:1;
140 bool age_set:1;
abef3f91 141 bool mask_perms:1;
88ec4dfa 142 bool attribute_set:1;
24f3a374
LP
143
144 bool keep_first_level:1;
1910cd0e 145
2e78fa79
LP
146 bool force:1;
147
1910cd0e 148 bool done:1;
3b63d2d3
LP
149} Item;
150
3f93da98
ZJS
151typedef struct ItemArray {
152 Item *items;
153 size_t count;
154 size_t size;
155} ItemArray;
156
5a8575ef
ZJS
157typedef enum DirectoryType {
158 DIRECTORY_RUNTIME = 0,
159 DIRECTORY_STATE,
160 DIRECTORY_CACHE,
161 DIRECTORY_LOGS,
162 _DIRECTORY_TYPE_MAX,
163} DirectoryType;
164
f2b5ca0e 165static bool arg_user = false;
3b63d2d3
LP
166static bool arg_create = false;
167static bool arg_clean = false;
168static bool arg_remove = false;
81815651 169static bool arg_boot = false;
3b63d2d3 170
7bc040fa
LP
171static char **arg_include_prefixes = NULL;
172static char **arg_exclude_prefixes = NULL;
cf9a4abd 173static char *arg_root = NULL;
a6d8474f 174static char *arg_replace = NULL;
fba6e687 175
3b63d2d3
LP
176#define MAX_DEPTH 256
177
ef43a391 178static OrderedHashmap *items = NULL, *globs = NULL;
7bc040fa
LP
179static Set *unix_sockets = NULL;
180
4cef1923 181static int specifier_machine_id_safe(char specifier, void *data, void *userdata, char **ret);
5a8575ef 182static int specifier_directory(char specifier, void *data, void *userdata, char **ret);
4cef1923 183
bd550f78 184static const Specifier specifier_table[] = {
4cef1923 185 { 'm', specifier_machine_id_safe, NULL },
5a8575ef
ZJS
186 { 'b', specifier_boot_id, NULL },
187 { 'H', specifier_host_name, NULL },
188 { 'v', specifier_kernel_release, NULL },
189
190 { 'U', specifier_user_id, NULL },
191 { 'u', specifier_user_name, NULL },
192 { 'h', specifier_user_home, NULL },
193 { 't', specifier_directory, UINT_TO_PTR(DIRECTORY_RUNTIME) },
194 { 'S', specifier_directory, UINT_TO_PTR(DIRECTORY_STATE) },
195 { 'C', specifier_directory, UINT_TO_PTR(DIRECTORY_CACHE) },
196 { 'L', specifier_directory, UINT_TO_PTR(DIRECTORY_LOGS) },
bd550f78
LP
197 {}
198};
199
4cef1923
FB
200static int specifier_machine_id_safe(char specifier, void *data, void *userdata, char **ret) {
201 int r;
202
d8dab757
FB
203 /* If /etc/machine_id is missing or empty (e.g. in a chroot environment)
204 * return a recognizable error so that the caller can skip the rule
4cef1923
FB
205 * gracefully. */
206
207 r = specifier_machine_id(specifier, data, userdata, ret);
d8dab757 208 if (IN_SET(r, -ENOENT, -ENOMEDIUM))
5a8575ef 209 return -ENXIO;
4cef1923
FB
210
211 return r;
212}
213
5a8575ef
ZJS
214static int specifier_directory(char specifier, void *data, void *userdata, char **ret) {
215 struct table_entry {
216 uint64_t type;
217 const char *suffix;
218 };
219
220 static const struct table_entry paths_system[] = {
221 [DIRECTORY_RUNTIME] = { SD_PATH_SYSTEM_RUNTIME },
222 [DIRECTORY_STATE] = { SD_PATH_SYSTEM_STATE_PRIVATE },
223 [DIRECTORY_CACHE] = { SD_PATH_SYSTEM_STATE_CACHE },
224 [DIRECTORY_LOGS] = { SD_PATH_SYSTEM_STATE_LOGS },
225 };
226
227 static const struct table_entry paths_user[] = {
228 [DIRECTORY_RUNTIME] = { SD_PATH_USER_RUNTIME },
229 [DIRECTORY_STATE] = { SD_PATH_USER_CONFIGURATION },
230 [DIRECTORY_CACHE] = { SD_PATH_USER_STATE_CACHE },
231 [DIRECTORY_LOGS] = { SD_PATH_USER_CONFIGURATION, "log" },
232 };
233
234 unsigned i;
235 const struct table_entry *paths;
236
237 assert_cc(ELEMENTSOF(paths_system) == ELEMENTSOF(paths_user));
238 paths = arg_user ? paths_user : paths_system;
239
240 i = PTR_TO_UINT(data);
241 assert(i < ELEMENTSOF(paths_system));
242
243 return sd_path_home(paths[i].type, paths[i].suffix, ret);
244}
245
4cef1923
FB
246static int log_unresolvable_specifier(const char *filename, unsigned line) {
247 static bool notified = false;
248
5a8575ef
ZJS
249 /* In system mode, this is called when /etc is not fully initialized (e.g.
250 * in a chroot environment) where some specifiers are unresolvable. In user
251 * mode, this is called when some variables are not defined. These cases are
252 * not considered as an error so log at LOG_NOTICE only for the first time
253 * and then downgrade this to LOG_DEBUG for the rest. */
4cef1923
FB
254
255 log_full(notified ? LOG_DEBUG : LOG_NOTICE,
5a8575ef
ZJS
256 "[%s:%u] Failed to resolve specifier: %s, skipping",
257 filename, line,
258 arg_user ? "Required $XDG_... variable not defined" : "uninitialized /etc detected");
4cef1923
FB
259
260 if (!notified)
261 log_notice("All rules containing unresolvable specifiers will be skipped.");
262
263 notified = true;
264 return 0;
265}
266
f2b5ca0e
ZJS
267static int user_config_paths(char*** ret) {
268 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
269 _cleanup_free_ char *persistent_config = NULL, *runtime_config = NULL, *data_home = NULL;
270 _cleanup_strv_free_ char **res = NULL;
271 int r;
272
273 r = xdg_user_dirs(&config_dirs, &data_dirs);
274 if (r < 0)
275 return r;
276
277 r = xdg_user_config_dir(&persistent_config, "/user-tmpfiles.d");
278 if (r < 0 && r != -ENXIO)
279 return r;
280
281 r = xdg_user_runtime_dir(&runtime_config, "/user-tmpfiles.d");
282 if (r < 0 && r != -ENXIO)
283 return r;
284
285 r = xdg_user_data_dir(&data_home, "/user-tmpfiles.d");
286 if (r < 0 && r != -ENXIO)
287 return r;
288
289 r = strv_extend_strv_concat(&res, config_dirs, "/user-tmpfiles.d");
290 if (r < 0)
291 return r;
292
293 r = strv_extend(&res, persistent_config);
294 if (r < 0)
295 return r;
296
297 r = strv_extend(&res, runtime_config);
298 if (r < 0)
299 return r;
300
301 r = strv_extend(&res, data_home);
302 if (r < 0)
303 return r;
304
305 r = strv_extend_strv_concat(&res, data_dirs, "/user-tmpfiles.d");
306 if (r < 0)
307 return r;
308
309 r = path_strv_make_absolute_cwd(res);
310 if (r < 0)
311 return r;
312
ae2a15bc 313 *ret = TAKE_PTR(res);
f2b5ca0e
ZJS
314 return 0;
315}
316
66ccd038 317static bool needs_glob(ItemType t) {
cde684a2
LP
318 return IN_SET(t,
319 WRITE_FILE,
320 IGNORE_PATH,
321 IGNORE_DIRECTORY_PATH,
322 REMOVE_PATH,
323 RECURSIVE_REMOVE_PATH,
df8dee85 324 EMPTY_DIRECTORY,
e73a03e0 325 ADJUST_MODE,
cde684a2 326 RELABEL_PATH,
b705ab6a
ZJS
327 RECURSIVE_RELABEL_PATH,
328 SET_XATTR,
329 RECURSIVE_SET_XATTR,
330 SET_ACL,
34f64536
LP
331 RECURSIVE_SET_ACL,
332 SET_ATTRIBUTE,
333 RECURSIVE_SET_ATTRIBUTE);
b8bb3e8f
LP
334}
335
3f93da98
ZJS
336static bool takes_ownership(ItemType t) {
337 return IN_SET(t,
338 CREATE_FILE,
339 TRUNCATE_FILE,
340 CREATE_DIRECTORY,
df8dee85 341 EMPTY_DIRECTORY,
3f93da98
ZJS
342 TRUNCATE_DIRECTORY,
343 CREATE_SUBVOLUME,
5fb13eb5
LP
344 CREATE_SUBVOLUME_INHERIT_QUOTA,
345 CREATE_SUBVOLUME_NEW_QUOTA,
3f93da98
ZJS
346 CREATE_FIFO,
347 CREATE_SYMLINK,
348 CREATE_CHAR_DEVICE,
349 CREATE_BLOCK_DEVICE,
350 COPY_FILES,
3f93da98
ZJS
351 WRITE_FILE,
352 IGNORE_PATH,
353 IGNORE_DIRECTORY_PATH,
354 REMOVE_PATH,
355 RECURSIVE_REMOVE_PATH);
356}
357
ef43a391 358static struct Item* find_glob(OrderedHashmap *h, const char *match) {
3f93da98 359 ItemArray *j;
b8bb3e8f
LP
360 Iterator i;
361
ef43a391 362 ORDERED_HASHMAP_FOREACH(j, h, i) {
3f93da98
ZJS
363 unsigned n;
364
365 for (n = 0; n < j->count; n++) {
366 Item *item = j->items + n;
367
368 if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
369 return item;
370 }
371 }
b8bb3e8f
LP
372
373 return NULL;
374}
375
17b90525 376static void load_unix_sockets(void) {
7fd1b19b 377 _cleanup_fclose_ FILE *f = NULL;
f1ff734f 378 int r;
17b90525
LP
379
380 if (unix_sockets)
381 return;
382
f1ff734f 383 /* We maintain a cache of the sockets we found in /proc/net/unix to speed things up a little. */
17b90525 384
548f6937
LP
385 unix_sockets = set_new(&path_hash_ops);
386 if (!unix_sockets) {
387 log_oom();
17b90525 388 return;
548f6937 389 }
17b90525 390
fdcad0c2 391 f = fopen("/proc/net/unix", "re");
f1ff734f
LP
392 if (!f) {
393 log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
394 "Failed to open /proc/net/unix, ignoring: %m");
395 goto fail;
396 }
17b90525 397
fdcad0c2 398 /* Skip header */
f1ff734f
LP
399 r = read_line(f, LONG_LINE_MAX, NULL);
400 if (r < 0) {
401 log_warning_errno(r, "Failed to skip /proc/net/unix header line: %m");
402 goto fail;
403 }
404 if (r == 0) {
405 log_warning("Premature end of file reading /proc/net/unix.");
17b90525 406 goto fail;
f1ff734f 407 }
17b90525
LP
408
409 for (;;) {
f1ff734f 410 _cleanup_free_ char *line = NULL;
17b90525 411 char *p, *s;
17b90525 412
f1ff734f
LP
413 r = read_line(f, LONG_LINE_MAX, &line);
414 if (r < 0) {
415 log_warning_errno(r, "Failed to read /proc/net/unix line, ignoring: %m");
416 goto fail;
417 }
418 if (r == 0) /* EOF */
17b90525
LP
419 break;
420
fdcad0c2
LP
421 p = strchr(line, ':');
422 if (!p)
423 continue;
424
425 if (strlen(p) < 37)
17b90525
LP
426 continue;
427
fdcad0c2 428 p += 37;
17b90525 429 p += strspn(p, WHITESPACE);
fdcad0c2 430 p += strcspn(p, WHITESPACE); /* skip one more word */
17b90525
LP
431 p += strspn(p, WHITESPACE);
432
433 if (*p != '/')
434 continue;
435
fdcad0c2 436 s = strdup(p);
f1ff734f
LP
437 if (!s) {
438 log_oom();
17b90525 439 goto fail;
f1ff734f 440 }
17b90525 441
4ff21d85
LP
442 path_kill_slashes(s);
443
f1ff734f
LP
444 r = set_consume(unix_sockets, s);
445 if (r < 0 && r != -EEXIST) {
446 log_warning_errno(r, "Failed to add AF_UNIX socket to set, ignoring: %m");
ef42202a 447 goto fail;
f1ff734f 448 }
17b90525
LP
449 }
450
451 return;
452
453fail:
f1ff734f 454 unix_sockets = set_free_free(unix_sockets);
17b90525
LP
455}
456
457static bool unix_socket_alive(const char *fn) {
458 assert(fn);
459
460 load_unix_sockets();
461
462 if (unix_sockets)
463 return !!set_get(unix_sockets, (char*) fn);
464
465 /* We don't know, so assume yes */
466 return true;
467}
468
99d680ac 469static int dir_is_mount_point(DIR *d, const char *subdir) {
cde684a2 470
99d680ac
KS
471 int mount_id_parent, mount_id;
472 int r_p, r;
473
cbfb8679 474 r_p = name_to_handle_at_loop(dirfd(d), ".", NULL, &mount_id_parent, 0);
99d680ac
KS
475 if (r_p < 0)
476 r_p = -errno;
477
cbfb8679 478 r = name_to_handle_at_loop(dirfd(d), subdir, NULL, &mount_id, 0);
99d680ac
KS
479 if (r < 0)
480 r = -errno;
481
482 /* got no handle; make no assumptions, return error */
483 if (r_p < 0 && r < 0)
484 return r_p;
485
486 /* got both handles; if they differ, it is a mount point */
487 if (r_p >= 0 && r >= 0)
488 return mount_id_parent != mount_id;
489
490 /* got only one handle; assume different mount points if one
491 * of both queries was not supported by the filesystem */
4c701096 492 if (IN_SET(r_p, -ENOSYS, -EOPNOTSUPP) || IN_SET(r, -ENOSYS, -EOPNOTSUPP))
99d680ac
KS
493 return true;
494
495 /* return error */
496 if (r_p < 0)
497 return r_p;
498 return r;
499}
500
df99a9ef
ZJS
501static DIR* xopendirat_nomod(int dirfd, const char *path) {
502 DIR *dir;
503
504 dir = xopendirat(dirfd, path, O_NOFOLLOW|O_NOATIME);
1532227a
LP
505 if (dir)
506 return dir;
507
508 log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path);
509 if (errno != EPERM)
510 return NULL;
511
512 dir = xopendirat(dirfd, path, O_NOFOLLOW);
513 if (!dir)
514 log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path);
df99a9ef
ZJS
515
516 return dir;
517}
518
519static DIR* opendir_nomod(const char *path) {
520 return xopendirat_nomod(AT_FDCWD, path);
521}
522
3b63d2d3 523static int dir_cleanup(
78a92a5a 524 Item *i,
3b63d2d3
LP
525 const char *p,
526 DIR *d,
527 const struct stat *ds,
528 usec_t cutoff,
529 dev_t rootdev,
530 bool mountpoint,
24f3a374 531 int maxdepth,
265ffa1e
LP
532 bool keep_this_level) {
533
3b63d2d3
LP
534 struct dirent *dent;
535 struct timespec times[2];
536 bool deleted = false;
3b63d2d3
LP
537 int r = 0;
538
8fb3f009 539 FOREACH_DIRENT_ALL(dent, d, break) {
3b63d2d3
LP
540 struct stat s;
541 usec_t age;
7fd1b19b 542 _cleanup_free_ char *sub_path = NULL;
3b63d2d3 543
49bfc877 544 if (dot_or_dot_dot(dent->d_name))
3b63d2d3 545 continue;
5008d581 546
3b63d2d3 547 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
ca2f4176
KS
548 if (errno == ENOENT)
549 continue;
5008d581 550
ca2f4176 551 /* FUSE, NFS mounts, SELinux might return EACCES */
ecc1709c
LP
552 r = log_full_errno(errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
553 "stat(%s/%s) failed: %m", p, dent->d_name);
3b63d2d3
LP
554 continue;
555 }
556
557 /* Stay on the same filesystem */
582deb84
ZJS
558 if (s.st_dev != rootdev) {
559 log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name);
3b63d2d3 560 continue;
582deb84 561 }
3b63d2d3 562
99d680ac
KS
563 /* Try to detect bind mounts of the same filesystem instance; they
564 * do not differ in device major/minors. This type of query is not
565 * supported on all kernels or filesystem types though. */
582deb84
ZJS
566 if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0) {
567 log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.",
568 p, dent->d_name);
99d680ac 569 continue;
582deb84 570 }
99d680ac 571
605405c6 572 sub_path = strjoin(p, "/", dent->d_name);
cde684a2 573 if (!sub_path) {
0d0f0c50 574 r = log_oom();
3b63d2d3
LP
575 goto finish;
576 }
577
578 /* Is there an item configured for this path? */
ef43a391 579 if (ordered_hashmap_get(items, sub_path)) {
582deb84 580 log_debug("Ignoring \"%s\": a separate entry exists.", sub_path);
3b63d2d3 581 continue;
582deb84 582 }
3b63d2d3 583
582deb84
ZJS
584 if (find_glob(globs, sub_path)) {
585 log_debug("Ignoring \"%s\": a separate glob exists.", sub_path);
b8bb3e8f 586 continue;
582deb84 587 }
b8bb3e8f 588
3b63d2d3
LP
589 if (S_ISDIR(s.st_mode)) {
590
591 if (mountpoint &&
592 streq(dent->d_name, "lost+found") &&
582deb84
ZJS
593 s.st_uid == 0) {
594 log_debug("Ignoring \"%s\".", sub_path);
3b63d2d3 595 continue;
582deb84 596 }
3b63d2d3
LP
597
598 if (maxdepth <= 0)
582deb84 599 log_warning("Reached max depth on \"%s\".", sub_path);
3b63d2d3 600 else {
7fd1b19b 601 _cleanup_closedir_ DIR *sub_dir;
3b63d2d3
LP
602 int q;
603
df99a9ef 604 sub_dir = xopendirat_nomod(dirfd(d), dent->d_name);
cde684a2 605 if (!sub_dir) {
582deb84
ZJS
606 if (errno != ENOENT)
607 r = log_error_errno(errno, "opendir(%s) failed: %m", sub_path);
3b63d2d3
LP
608
609 continue;
610 }
611
78a92a5a 612 q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
3b63d2d3
LP
613 if (q < 0)
614 r = q;
615 }
616
24f3a374
LP
617 /* Note: if you are wondering why we don't
618 * support the sticky bit for excluding
619 * directories from cleaning like we do it for
620 * other file system objects: well, the sticky
621 * bit already has a meaning for directories,
622 * so we don't want to overload that. */
623
582deb84
ZJS
624 if (keep_this_level) {
625 log_debug("Keeping \"%s\".", sub_path);
24f3a374 626 continue;
582deb84 627 }
24f3a374 628
3b63d2d3 629 /* Ignore ctime, we change it when deleting */
582deb84
ZJS
630 age = timespec_load(&s.st_mtim);
631 if (age >= cutoff) {
632 char a[FORMAT_TIMESTAMP_MAX];
633 /* Follows spelling in stat(1). */
634 log_debug("Directory \"%s\": modify time %s is too new.",
635 sub_path,
636 format_timestamp_us(a, sizeof(a), age));
3b63d2d3 637 continue;
582deb84
ZJS
638 }
639
640 age = timespec_load(&s.st_atim);
641 if (age >= cutoff) {
642 char a[FORMAT_TIMESTAMP_MAX];
643 log_debug("Directory \"%s\": access time %s is too new.",
644 sub_path,
645 format_timestamp_us(a, sizeof(a), age));
646 continue;
647 }
3b63d2d3 648
61253220
ZJS
649 log_debug("Removing directory \"%s\".", sub_path);
650 if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0)
920ce828
LP
651 if (!IN_SET(errno, ENOENT, ENOTEMPTY))
652 r = log_error_errno(errno, "rmdir(%s): %m", sub_path);
3b63d2d3
LP
653
654 } else {
9c73736d
LP
655 /* Skip files for which the sticky bit is
656 * set. These are semantics we define, and are
657 * unknown elsewhere. See XDG_RUNTIME_DIR
658 * specification for details. */
582deb84
ZJS
659 if (s.st_mode & S_ISVTX) {
660 log_debug("Skipping \"%s\": sticky bit set.", sub_path);
9c73736d 661 continue;
582deb84 662 }
9c73736d 663
582deb84 664 if (mountpoint && S_ISREG(s.st_mode))
1542c01b
ZJS
665 if (s.st_uid == 0 && STR_IN_SET(dent->d_name,
666 ".journal",
667 "aquota.user",
668 "aquota.group")) {
582deb84 669 log_debug("Skipping \"%s\".", sub_path);
3b63d2d3 670 continue;
582deb84 671 }
3b63d2d3 672
17b90525 673 /* Ignore sockets that are listed in /proc/net/unix */
582deb84
ZJS
674 if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) {
675 log_debug("Skipping \"%s\": live socket.", sub_path);
17b90525 676 continue;
582deb84 677 }
17b90525 678
78ab08eb 679 /* Ignore device nodes */
582deb84
ZJS
680 if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) {
681 log_debug("Skipping \"%s\": a device.", sub_path);
78ab08eb 682 continue;
582deb84 683 }
78ab08eb 684
24f3a374
LP
685 /* Keep files on this level around if this is
686 * requested */
582deb84
ZJS
687 if (keep_this_level) {
688 log_debug("Keeping \"%s\".", sub_path);
24f3a374 689 continue;
582deb84 690 }
24f3a374 691
582deb84
ZJS
692 age = timespec_load(&s.st_mtim);
693 if (age >= cutoff) {
694 char a[FORMAT_TIMESTAMP_MAX];
695 /* Follows spelling in stat(1). */
696 log_debug("File \"%s\": modify time %s is too new.",
697 sub_path,
698 format_timestamp_us(a, sizeof(a), age));
3b63d2d3 699 continue;
582deb84 700 }
3b63d2d3 701
582deb84
ZJS
702 age = timespec_load(&s.st_atim);
703 if (age >= cutoff) {
704 char a[FORMAT_TIMESTAMP_MAX];
705 log_debug("File \"%s\": access time %s is too new.",
706 sub_path,
707 format_timestamp_us(a, sizeof(a), age));
708 continue;
709 }
3b63d2d3 710
582deb84
ZJS
711 age = timespec_load(&s.st_ctim);
712 if (age >= cutoff) {
713 char a[FORMAT_TIMESTAMP_MAX];
714 log_debug("File \"%s\": change time %s is too new.",
715 sub_path,
716 format_timestamp_us(a, sizeof(a), age));
717 continue;
3b63d2d3
LP
718 }
719
582deb84
ZJS
720 log_debug("unlink \"%s\"", sub_path);
721
722 if (unlinkat(dirfd(d), dent->d_name, 0) < 0)
723 if (errno != ENOENT)
724 r = log_error_errno(errno, "unlink(%s): %m", sub_path);
725
3b63d2d3
LP
726 deleted = true;
727 }
728 }
729
730finish:
731 if (deleted) {
582deb84
ZJS
732 usec_t age1, age2;
733 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
734
3b63d2d3
LP
735 /* Restore original directory timestamps */
736 times[0] = ds->st_atim;
737 times[1] = ds->st_mtim;
738
582deb84
ZJS
739 age1 = timespec_load(&ds->st_atim);
740 age2 = timespec_load(&ds->st_mtim);
741 log_debug("Restoring access and modification time on \"%s\": %s, %s",
742 p,
743 format_timestamp_us(a, sizeof(a), age1),
744 format_timestamp_us(b, sizeof(b), age2));
3b63d2d3 745 if (futimens(dirfd(d), times) < 0)
56f64d95 746 log_error_errno(errno, "utimensat(%s): %m", p);
3b63d2d3
LP
747 }
748
3b63d2d3
LP
749 return r;
750}
751
5579f856
LP
752static bool dangerous_hardlinks(void) {
753 _cleanup_free_ char *value = NULL;
754 static int cached = -1;
755 int r;
756
757 /* Check whether the fs.protected_hardlinks sysctl is on. If we can't determine it we assume its off, as that's
758 * what the upstream default is. */
759
760 if (cached >= 0)
761 return cached;
762
763 r = read_one_line_file("/proc/sys/fs/protected_hardlinks", &value);
764 if (r < 0) {
765 log_debug_errno(r, "Failed to read fs.protected_hardlinks sysctl: %m");
766 return true;
767 }
768
769 r = parse_boolean(value);
770 if (r < 0) {
771 log_debug_errno(r, "Failed to parse fs.protected_hardlinks sysctl: %m");
772 return true;
773 }
774
775 cached = r == 0;
776 return cached;
777}
778
774f79b5 779static bool hardlink_vulnerable(const struct stat *st) {
5579f856
LP
780 assert(st);
781
782 return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks();
783}
784
936f6bdb
FB
785static int fd_set_perms(Item *i, int fd, const struct stat *st) {
786 _cleanup_free_ char *path = NULL;
787 int r;
1924a97d 788
cde684a2 789 assert(i);
936f6bdb 790 assert(fd);
780e2ee1 791
936f6bdb
FB
792 r = fd_get_path(fd, &path);
793 if (r < 0)
780e2ee1 794 return r;
48b8aaa8 795
936f6bdb
FB
796 if (!i->mode_set && !i->uid_set && !i->gid_set)
797 goto shortcut;
1924a97d 798
936f6bdb 799 if (hardlink_vulnerable(st)) {
5579f856
LP
800 log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path);
801 return -EPERM;
802 }
803
51207ca1 804 if (i->mode_set) {
936f6bdb 805 if (S_ISLNK(st->st_mode))
51207ca1
LP
806 log_debug("Skipping mode fix for symlink %s.", path);
807 else {
48b8aaa8
LP
808 mode_t m = i->mode;
809
810 if (i->mask_perms) {
936f6bdb 811 if (!(st->st_mode & 0111))
48b8aaa8 812 m &= ~0111;
936f6bdb 813 if (!(st->st_mode & 0222))
7d6884b6 814 m &= ~0222;
936f6bdb 815 if (!(st->st_mode & 0444))
48b8aaa8 816 m &= ~0444;
936f6bdb 817 if (!S_ISDIR(st->st_mode))
48b8aaa8
LP
818 m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
819 }
abef3f91 820
936f6bdb
FB
821 if (m == (st->st_mode & 07777))
822 log_debug("\"%s\" has correct mode %o already.", path, st->st_mode);
48b8aaa8 823 else {
34d26777 824 char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
936f6bdb 825
51207ca1
LP
826 log_debug("Changing \"%s\" to mode %o.", path, m);
827
936f6bdb
FB
828 /* fchmodat() still doesn't have AT_EMPTY_PATH flag. */
829 xsprintf(procfs_path, "/proc/self/fd/%i", fd);
830
831 if (chmod(procfs_path, m) < 0)
832 return log_error_errno(errno, "chmod() of %s via %s failed: %m", path, procfs_path);
48b8aaa8 833 }
062e01bb 834 }
51207ca1 835 }
062e01bb 836
936f6bdb
FB
837 if ((i->uid_set && i->uid != st->st_uid) ||
838 (i->gid_set && i->gid != st->st_gid)) {
51207ca1
LP
839 log_debug("Changing \"%s\" to owner "UID_FMT":"GID_FMT,
840 path,
841 i->uid_set ? i->uid : UID_INVALID,
842 i->gid_set ? i->gid : GID_INVALID);
843
936f6bdb
FB
844 if (fchownat(fd,
845 "",
846 i->uid_set ? i->uid : UID_INVALID,
847 i->gid_set ? i->gid : GID_INVALID,
848 AT_EMPTY_PATH) < 0)
849 return log_error_errno(errno, "fchownat() of %s failed: %m", path);
582deb84 850 }
062e01bb 851
59793c8f 852shortcut:
08c84981 853 return label_fix(path, 0);
062e01bb
MS
854}
855
936f6bdb
FB
856static int path_set_perms(Item *i, const char *path) {
857 _cleanup_close_ int fd = -1;
858 struct stat st;
859
860 assert(i);
861 assert(path);
862
863 fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
864 if (fd < 0) {
865 int level = LOG_ERR, r = -errno;
866
867 /* Option "e" operates only on existing objects. Do not
868 * print errors about non-existent files or directories */
869 if (i->type == EMPTY_DIRECTORY && errno == ENOENT) {
870 level = LOG_DEBUG;
871 r = 0;
872 }
873
874 log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path);
875 return r;
876 }
877
878 if (fstat(fd, &st) < 0)
879 return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
880
881 return fd_set_perms(i, fd, &st);
882}
883
bd550f78 884static int parse_xattrs_from_arg(Item *i) {
ebf4e801
MW
885 const char *p;
886 int r;
887
888 assert(i);
505ef0e3 889 assert(i->argument);
ebf4e801 890
ebf4e801
MW
891 p = i->argument;
892
4034a06d 893 for (;;) {
4cef1923 894 _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL;
4034a06d 895
12ba2c44 896 r = extract_first_word(&p, &xattr, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
4034a06d 897 if (r < 0)
bd550f78 898 log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p);
4034a06d
LP
899 if (r <= 0)
900 break;
505ef0e3 901
4cef1923 902 r = split_pair(xattr, "=", &name, &value);
ebf4e801 903 if (r < 0) {
4034a06d 904 log_warning_errno(r, "Failed to parse extended attribute, ignoring: %s", xattr);
ebf4e801
MW
905 continue;
906 }
505ef0e3 907
4034a06d 908 if (isempty(name) || isempty(value)) {
bd550f78 909 log_warning("Malformed extended attribute found, ignoring: %s", xattr);
ebf4e801
MW
910 continue;
911 }
505ef0e3 912
4034a06d 913 if (strv_push_pair(&i->xattrs, name, value) < 0)
ebf4e801 914 return log_oom();
505ef0e3 915
4034a06d 916 name = value = NULL;
ebf4e801
MW
917 }
918
4034a06d 919 return 0;
ebf4e801
MW
920}
921
936f6bdb 922static int fd_set_xattrs(Item *i, int fd, const struct stat *st) {
34d26777 923 char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
936f6bdb 924 _cleanup_free_ char *path = NULL;
ebf4e801 925 char **name, **value;
936f6bdb 926 int r;
ebf4e801
MW
927
928 assert(i);
936f6bdb
FB
929 assert(fd);
930
931 r = fd_get_path(fd, &path);
932 if (r < 0)
933 return r;
934
935 xsprintf(procfs_path, "/proc/self/fd/%i", fd);
ebf4e801 936
ebf4e801 937 STRV_FOREACH_PAIR(name, value, i->xattrs) {
bd550f78 938 log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path);
936f6bdb 939 if (setxattr(procfs_path, *name, *value, strlen(*value), 0) < 0)
25f027c5
ZJS
940 return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m",
941 *name, *value, path);
ebf4e801
MW
942 }
943 return 0;
944}
945
936f6bdb
FB
946static int path_set_xattrs(Item *i, const char *path) {
947 _cleanup_close_ int fd = -1;
948
949 assert(i);
950 assert(path);
951
952 fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH);
953 if (fd < 0)
954 return log_error_errno(errno, "Cannot open '%s': %m", path);
955
956 return fd_set_xattrs(i, fd, NULL);
957}
958
bd550f78 959static int parse_acls_from_arg(Item *item) {
349cc4a5 960#if HAVE_ACL
f8eeeaf9 961 int r;
f8eeeaf9
ZJS
962
963 assert(item);
964
50d9e46d
ZJS
965 /* If force (= modify) is set, we will not modify the acl
966 * afterwards, so the mask can be added now if necessary. */
bd550f78 967
50d9e46d 968 r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force);
f8eeeaf9 969 if (r < 0)
4034a06d 970 log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring", item->argument);
f8eeeaf9
ZJS
971#else
972 log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring");
973#endif
974
975 return 0;
976}
977
349cc4a5 978#if HAVE_ACL
48b8aaa8
LP
979static int path_set_acl(const char *path, const char *pretty, acl_type_t type, acl_t acl, bool modify) {
980 _cleanup_(acl_free_charpp) char *t = NULL;
dd4105b0 981 _cleanup_(acl_freep) acl_t dup = NULL;
50d9e46d
ZJS
982 int r;
983
d873e877
HPD
984 /* Returns 0 for success, positive error if already warned,
985 * negative error otherwise. */
986
50d9e46d 987 if (modify) {
dd4105b0 988 r = acls_for_file(path, type, acl, &dup);
50d9e46d
ZJS
989 if (r < 0)
990 return r;
50d9e46d 991
dd4105b0
ZJS
992 r = calc_acl_mask_if_needed(&dup);
993 if (r < 0)
994 return r;
995 } else {
996 dup = acl_dup(acl);
997 if (!dup)
998 return -errno;
999
1000 /* the mask was already added earlier if needed */
1001 }
1002
1003 r = add_base_acls_if_needed(&dup, path);
1004 if (r < 0)
1005 return r;
1006
582deb84 1007 t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE);
48b8aaa8 1008 log_debug("Setting %s ACL %s on %s.",
582deb84 1009 type == ACL_TYPE_ACCESS ? "access" : "default",
48b8aaa8 1010 strna(t), pretty);
50d9e46d 1011
582deb84
ZJS
1012 r = acl_set_file(path, type, dup);
1013 if (r < 0)
3ea40b78
LP
1014 /* Return positive to indicate we already warned */
1015 return -log_error_errno(errno,
1016 "Setting %s ACL \"%s\" on %s failed: %m",
1017 type == ACL_TYPE_ACCESS ? "access" : "default",
48b8aaa8 1018 strna(t), pretty);
d873e877 1019
582deb84 1020 return 0;
50d9e46d 1021}
35888b67 1022#endif
50d9e46d 1023
936f6bdb 1024static int fd_set_acls(Item *item, int fd, const struct stat *st) {
d873e877 1025 int r = 0;
349cc4a5 1026#if HAVE_ACL
34d26777 1027 char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
936f6bdb 1028 _cleanup_free_ char *path = NULL;
48b8aaa8 1029
f8eeeaf9 1030 assert(item);
936f6bdb
FB
1031 assert(fd);
1032 assert(st);
48b8aaa8 1033
936f6bdb
FB
1034 r = fd_get_path(fd, &path);
1035 if (r < 0)
1036 return r;
48b8aaa8 1037
936f6bdb 1038 if (hardlink_vulnerable(st)) {
5579f856
LP
1039 log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path);
1040 return -EPERM;
1041 }
1042
936f6bdb 1043 if (S_ISLNK(st->st_mode)) {
48b8aaa8
LP
1044 log_debug("Skipping ACL fix for symlink %s.", path);
1045 return 0;
1046 }
1047
936f6bdb 1048 xsprintf(procfs_path, "/proc/self/fd/%i", fd);
48b8aaa8 1049
d873e877 1050 if (item->acl_access)
936f6bdb 1051 r = path_set_acl(procfs_path, path, ACL_TYPE_ACCESS, item->acl_access, item->force);
f8eeeaf9 1052
d873e877 1053 if (r == 0 && item->acl_default)
936f6bdb 1054 r = path_set_acl(procfs_path, path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
f8eeeaf9 1055
d873e877
HPD
1056 if (r > 0)
1057 return -r; /* already warned */
936f6bdb 1058 if (r == -EOPNOTSUPP) {
d873e877
HPD
1059 log_debug_errno(r, "ACLs not supported by file system at %s", path);
1060 return 0;
936f6bdb
FB
1061 }
1062 if (r < 0)
1063 return log_error_errno(r, "ACL operation on \"%s\" failed: %m", path);
d873e877
HPD
1064#endif
1065 return r;
f8eeeaf9
ZJS
1066}
1067
936f6bdb
FB
1068static int path_set_acls(Item *item, const char *path) {
1069 int r = 0;
1070#ifdef HAVE_ACL
1071 _cleanup_close_ int fd = -1;
1072 struct stat st;
1073
1074 assert(item);
1075 assert(path);
1076
1077 fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
1078 if (fd < 0)
1079 return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path);
1080
1081 if (fstat(fd, &st) < 0)
1082 return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
1083
1084 r = fd_set_acls(item, fd, &st);
1085 #endif
1086 return r;
1087 }
1088
88ec4dfa
LP
1089#define ATTRIBUTES_ALL \
1090 (FS_NOATIME_FL | \
1091 FS_SYNC_FL | \
1092 FS_DIRSYNC_FL | \
1093 FS_APPEND_FL | \
1094 FS_COMPR_FL | \
1095 FS_NODUMP_FL | \
1096 FS_EXTENT_FL | \
1097 FS_IMMUTABLE_FL | \
1098 FS_JOURNAL_DATA_FL | \
1099 FS_SECRM_FL | \
1100 FS_UNRM_FL | \
1101 FS_NOTAIL_FL | \
1102 FS_TOPDIR_FL | \
1103 FS_NOCOW_FL)
1104
bd550f78 1105static int parse_attribute_from_arg(Item *item) {
88ec4dfa
LP
1106
1107 static const struct {
1108 char character;
1109 unsigned value;
1110 } attributes[] = {
1111 { 'A', FS_NOATIME_FL }, /* do not update atime */
1112 { 'S', FS_SYNC_FL }, /* Synchronous updates */
1113 { 'D', FS_DIRSYNC_FL }, /* dirsync behaviour (directories only) */
1114 { 'a', FS_APPEND_FL }, /* writes to file may only append */
1115 { 'c', FS_COMPR_FL }, /* Compress file */
1116 { 'd', FS_NODUMP_FL }, /* do not dump file */
8c35b2ca 1117 { 'e', FS_EXTENT_FL }, /* Extents */
88ec4dfa
LP
1118 { 'i', FS_IMMUTABLE_FL }, /* Immutable file */
1119 { 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */
1120 { 's', FS_SECRM_FL }, /* Secure deletion */
1121 { 'u', FS_UNRM_FL }, /* Undelete */
1122 { 't', FS_NOTAIL_FL }, /* file tail should not be merged */
13e785f7 1123 { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies */
88ec4dfa 1124 { 'C', FS_NOCOW_FL }, /* Do not cow file */
22c3a6ca 1125 };
88ec4dfa 1126
22c3a6ca
GB
1127 enum {
1128 MODE_ADD,
1129 MODE_DEL,
1130 MODE_SET
1131 } mode = MODE_ADD;
22c3a6ca 1132
88ec4dfa
LP
1133 unsigned value = 0, mask = 0;
1134 const char *p;
22c3a6ca 1135
88ec4dfa
LP
1136 assert(item);
1137
1138 p = item->argument;
1139 if (p) {
1140 if (*p == '+') {
1141 mode = MODE_ADD;
1142 p++;
1143 } else if (*p == '-') {
1144 mode = MODE_DEL;
1145 p++;
1146 } else if (*p == '=') {
1147 mode = MODE_SET;
1148 p++;
1149 }
22c3a6ca
GB
1150 }
1151
88ec4dfa
LP
1152 if (isempty(p) && mode != MODE_SET) {
1153 log_error("Setting file attribute on '%s' needs an attribute specification.", item->path);
22c3a6ca
GB
1154 return -EINVAL;
1155 }
88ec4dfa
LP
1156
1157 for (; p && *p ; p++) {
1158 unsigned i, v;
1159
1160 for (i = 0; i < ELEMENTSOF(attributes); i++)
1161 if (*p == attributes[i].character)
1162 break;
1163
1164 if (i >= ELEMENTSOF(attributes)) {
1165 log_error("Unknown file attribute '%c' on '%s'.", *p, item->path);
22c3a6ca
GB
1166 return -EINVAL;
1167 }
88ec4dfa
LP
1168
1169 v = attributes[i].value;
1170
3742095b 1171 SET_FLAG(value, v, IN_SET(mode, MODE_ADD, MODE_SET));
88ec4dfa
LP
1172
1173 mask |= v;
22c3a6ca
GB
1174 }
1175
1176 if (mode == MODE_SET)
88ec4dfa 1177 mask |= ATTRIBUTES_ALL;
22c3a6ca 1178
88ec4dfa 1179 assert(mask != 0);
22c3a6ca 1180
88ec4dfa
LP
1181 item->attribute_mask = mask;
1182 item->attribute_value = value;
1183 item->attribute_set = true;
22c3a6ca
GB
1184
1185 return 0;
22c3a6ca
GB
1186}
1187
936f6bdb 1188static int fd_set_attribute(Item *item, int fd, const struct stat *st) {
34d26777 1189 char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
936f6bdb
FB
1190 _cleanup_close_ int procfs_fd = -1;
1191 _cleanup_free_ char *path = NULL;
88ec4dfa
LP
1192 unsigned f;
1193 int r;
22c3a6ca 1194
88ec4dfa 1195 if (!item->attribute_set || item->attribute_mask == 0)
22c3a6ca 1196 return 0;
22c3a6ca 1197
936f6bdb
FB
1198 r = fd_get_path(fd, &path);
1199 if (r < 0)
1200 return r;
88ec4dfa
LP
1201
1202 /* Issuing the file attribute ioctls on device nodes is not
1203 * safe, as that will be delivered to the drivers, not the
1204 * file system containing the device node. */
936f6bdb 1205 if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) {
88ec4dfa
LP
1206 log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path);
1207 return -EINVAL;
1208 }
1209
1210 f = item->attribute_value & item->attribute_mask;
1211
1212 /* Mask away directory-specific flags */
936f6bdb 1213 if (!S_ISDIR(st->st_mode))
22c3a6ca 1214 f &= ~FS_DIRSYNC_FL;
88ec4dfa 1215
936f6bdb
FB
1216 xsprintf(procfs_path, "/proc/self/fd/%i", fd);
1217
1218 procfs_fd = open(procfs_path, O_RDONLY|O_CLOEXEC|O_NOATIME);
1219 if (procfs_fd < 0)
1220 return -errno;
1221
1222 r = chattr_fd(procfs_fd, f, item->attribute_mask);
22c3a6ca 1223 if (r < 0)
4c701096 1224 log_full_errno(IN_SET(r, -ENOTTY, -EOPNOTSUPP) ? LOG_DEBUG : LOG_WARNING,
ad75a97f
LP
1225 r,
1226 "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
1227 path, item->attribute_value, item->attribute_mask);
22c3a6ca
GB
1228
1229 return 0;
1230}
1231
936f6bdb
FB
1232static int path_set_attribute(Item *item, const char *path) {
1233 _cleanup_close_ int fd = -1;
1234 struct stat st;
1235
1236 if (!item->attribute_set || item->attribute_mask == 0)
1237 return 0;
1238
1239 fd = open(path, O_CLOEXEC|O_NOFOLLOW|O_PATH);
1240 if (fd < 0)
1241 return log_error_errno(errno, "Cannot open '%s': %m", path);
1242
1243 if (fstat(fd, &st) < 0)
1244 return log_error_errno(errno, "Cannot stat '%s': %m", path);
1245
1246 return fd_set_attribute(item, fd, &st);
1247}
1248
d4e9eb91 1249static int write_one_file(Item *i, const char *path) {
43ad6e31
LP
1250 _cleanup_close_ int fd = -1;
1251 int flags, r = 0;
d4e9eb91 1252 struct stat st;
d4e9eb91 1253
874f1947
LP
1254 assert(i);
1255 assert(path);
1256
49e87292 1257 flags = i->type == CREATE_FILE ? O_CREAT|O_EXCL|O_NOFOLLOW :
43ad6e31 1258 i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0;
d4e9eb91 1259
43ad6e31 1260 RUN_WITH_UMASK(0000) {
ecabcf8b 1261 mac_selinux_create_file_prepare(path, S_IFREG);
43ad6e31 1262 fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
ecabcf8b 1263 mac_selinux_create_file_clear();
5c0d398d 1264 }
d4e9eb91
DR
1265
1266 if (fd < 0) {
582deb84 1267 if (i->type == WRITE_FILE && errno == ENOENT) {
49e87292 1268 log_debug_errno(errno, "Not writing missing file \"%s\": %m", path);
d4e9eb91 1269 return 0;
582deb84 1270 }
49e87292
LP
1271 if (i->type == CREATE_FILE && errno == EEXIST) {
1272 log_debug_errno(errno, "Not writing to pre-existing file \"%s\": %m", path);
1273 goto done;
1274 }
d4e9eb91 1275
f44b28fd
MO
1276 r = -errno;
1277 if (!i->argument && errno == EROFS && stat(path, &st) == 0 &&
1278 (i->type == CREATE_FILE || st.st_size == 0))
1279 goto check_mode;
1280
1281 return log_error_errno(r, "Failed to create file %s: %m", path);
d4e9eb91
DR
1282 }
1283
1284 if (i->argument) {
4034a06d 1285 log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path);
d4e9eb91 1286
4cef1923 1287 r = loop_write(fd, i->argument, strlen(i->argument), false);
582deb84
ZJS
1288 if (r < 0)
1289 return log_error_errno(r, "Failed to write file \"%s\": %m", path);
1290 } else
1291 log_debug("\"%s\" has been created.", path);
d4e9eb91 1292
43ad6e31 1293 fd = safe_close(fd);
3612fbc1 1294
49e87292 1295done:
4a62c710
MS
1296 if (stat(path, &st) < 0)
1297 return log_error_errno(errno, "stat(%s) failed: %m", path);
d4e9eb91 1298
f44b28fd 1299 check_mode:
d4e9eb91
DR
1300 if (!S_ISREG(st.st_mode)) {
1301 log_error("%s is not a file.", path);
1302 return -EEXIST;
1303 }
1304
b705ab6a 1305 r = path_set_perms(i, path);
d4e9eb91
DR
1306 if (r < 0)
1307 return r;
1308
1309 return 0;
1310}
1311
081043cf 1312typedef int (*action_t)(Item *, const char *);
936f6bdb 1313typedef int (*fdaction_t)(Item *, int fd, const struct stat *st);
081043cf 1314
936f6bdb
FB
1315static int item_do(Item *i, int fd, const struct stat *st, fdaction_t action) {
1316 int r = 0, q;
e73a03e0
LP
1317
1318 assert(i);
936f6bdb
FB
1319 assert(fd >= 0);
1320 assert(st);
a8d88783
MS
1321
1322 /* This returns the first error we run into, but nevertheless
1323 * tries to go on */
936f6bdb 1324 r = action(i, fd, st);
a8d88783 1325
936f6bdb
FB
1326 if (S_ISDIR(st->st_mode)) {
1327 char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
1328 _cleanup_closedir_ DIR *d = NULL;
1329 struct dirent *de;
a8d88783 1330
936f6bdb
FB
1331 /* The passed 'fd' was opened with O_PATH. We need to convert
1332 * it into a 'regular' fd before reading the directory content. */
1333 xsprintf(procfs_path, "/proc/self/fd/%i", fd);
a8d88783 1334
936f6bdb
FB
1335 d = opendir(procfs_path);
1336 if (!d) {
1337 r = r ?: -errno;
1338 goto finish;
1339 }
a8d88783 1340
936f6bdb
FB
1341 FOREACH_DIRENT_ALL(de, d, q = -errno; goto finish) {
1342 struct stat de_st;
1343 int de_fd;
a8d88783 1344
936f6bdb
FB
1345 if (dot_or_dot_dot(de->d_name))
1346 continue;
1347
1348 de_fd = openat(fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_PATH);
1349 if (de_fd >= 0 && fstat(de_fd, &de_st) >= 0)
1350 /* pass ownership of dirent fd over */
1351 q = item_do(i, de_fd, &de_st, action);
1352 else
1353 q = -errno;
a8d88783 1354
e73a03e0
LP
1355 if (q < 0 && r == 0)
1356 r = q;
a8d88783 1357 }
a8d88783 1358 }
936f6bdb
FB
1359finish:
1360 safe_close(fd);
e73a03e0 1361 return r;
a8d88783
MS
1362}
1363
936f6bdb 1364static int glob_item(Item *i, action_t action) {
df99a9ef 1365 _cleanup_globfree_ glob_t g = {
ebf31a1f 1366 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
df99a9ef 1367 };
e73a03e0 1368 int r = 0, k;
99e68c0b
MS
1369 char **fn;
1370
84e72b5e
ZJS
1371 k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
1372 if (k < 0 && k != -ENOENT)
1373 return log_error_errno(k, "glob(%s) failed: %m", i->path);
99e68c0b 1374
c84a9488
ZJS
1375 STRV_FOREACH(fn, g.gl_pathv) {
1376 k = action(i, *fn);
e73a03e0 1377 if (k < 0 && r == 0)
99e68c0b 1378 r = k;
936f6bdb
FB
1379 }
1380
1381 return r;
1382}
081043cf 1383
936f6bdb
FB
1384static int glob_item_recursively(Item *i, fdaction_t action) {
1385 _cleanup_globfree_ glob_t g = {
1386 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
1387 };
1388 int r = 0, k;
1389 char **fn;
1390
1391 k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
1392 if (k < 0 && k != -ENOENT)
1393 return log_error_errno(k, "glob(%s) failed: %m", i->path);
1394
1395 STRV_FOREACH(fn, g.gl_pathv) {
1396 _cleanup_close_ int fd = -1;
1397 struct stat st;
1398
1399 /* Make sure we won't trigger/follow file object (such as
1400 * device nodes, automounts, ...) pointed out by 'fn' with
1401 * O_PATH. Note, when O_PATH is used, flags other than
1402 * O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW are ignored. */
1403
1404 fd = open(*fn, O_CLOEXEC|O_NOFOLLOW|O_PATH);
1405 if (fd < 0) {
1406 r = r ?: -errno;
1407 continue;
1408 }
1409
1410 if (fstat(fd, &st) < 0) {
1411 r = r ?: -errno;
1412 continue;
081043cf 1413 }
936f6bdb
FB
1414
1415 k = item_do(i, fd, &st, action);
1416 if (k < 0 && r == 0)
1417 r = k;
1418
1419 /* we passed fd ownership to the previous call */
1420 fd = -1;
c84a9488 1421 }
99e68c0b 1422
99e68c0b
MS
1423 return r;
1424}
1425
294929f8
ZJS
1426typedef enum {
1427 CREATION_NORMAL,
1428 CREATION_EXISTING,
1429 CREATION_FORCE,
7a7d5db7
LP
1430 _CREATION_MODE_MAX,
1431 _CREATION_MODE_INVALID = -1
294929f8
ZJS
1432} CreationMode;
1433
7a7d5db7
LP
1434static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = {
1435 [CREATION_NORMAL] = "Created",
1436 [CREATION_EXISTING] = "Found existing",
1437 [CREATION_FORCE] = "Created replacement",
1438};
1439
1440DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
294929f8 1441
3b63d2d3 1442static int create_item(Item *i) {
3b63d2d3 1443 struct stat st;
df28bc08 1444 int r = 0;
114a17d2 1445 int q = 0;
294929f8 1446 CreationMode creation;
5008d581 1447
3b63d2d3 1448 assert(i);
5008d581 1449
582deb84
ZJS
1450 log_debug("Running create action for entry %c %s", (char) i->type, i->path);
1451
3b63d2d3
LP
1452 switch (i->type) {
1453
1454 case IGNORE_PATH:
78a92a5a 1455 case IGNORE_DIRECTORY_PATH:
3b63d2d3
LP
1456 case REMOVE_PATH:
1457 case RECURSIVE_REMOVE_PATH:
1458 return 0;
5008d581 1459
3b63d2d3 1460 case CREATE_FILE:
31ed59c5 1461 case TRUNCATE_FILE:
7fa10748
LP
1462 RUN_WITH_UMASK(0000)
1463 (void) mkdir_parents_label(i->path, 0755);
1464
1845fdd9
DR
1465 r = write_one_file(i, i->path);
1466 if (r < 0)
1467 return r;
1468 break;
265ffa1e 1469
5c5ccf12 1470 case COPY_FILES: {
7fa10748
LP
1471
1472 RUN_WITH_UMASK(0000)
1473 (void) mkdir_parents_label(i->path, 0755);
1474
4cef1923 1475 log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path);
7fa10748
LP
1476 r = copy_tree(i->argument, i->path,
1477 i->uid_set ? i->uid : UID_INVALID,
1478 i->gid_set ? i->gid : GID_INVALID,
1479 COPY_REFLINK);
f44b28fd
MO
1480
1481 if (r == -EROFS && stat(i->path, &st) == 0)
1482 r = -EEXIST;
1483
849958d1 1484 if (r < 0) {
e156347e
LP
1485 struct stat a, b;
1486
8d3d7072
MS
1487 if (r != -EEXIST)
1488 return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
e156347e 1489
4cef1923
FB
1490 if (stat(i->argument, &a) < 0)
1491 return log_error_errno(errno, "stat(%s) failed: %m", i->argument);
e156347e 1492
4a62c710
MS
1493 if (stat(i->path, &b) < 0)
1494 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
e156347e
LP
1495
1496 if ((a.st_mode ^ b.st_mode) & S_IFMT) {
1497 log_debug("Can't copy to %s, file exists already and is of different type", i->path);
1498 return 0;
1499 }
849958d1
LP
1500 }
1501
b705ab6a 1502 r = path_set_perms(i, i->path);
849958d1
LP
1503 if (r < 0)
1504 return r;
1505
1506 break;
1507
d4e9eb91 1508 case WRITE_FILE:
936f6bdb 1509 r = glob_item(i, write_one_file);
f05bc3f7
MS
1510 if (r < 0)
1511 return r;
5008d581 1512
3b63d2d3
LP
1513 break;
1514
3b63d2d3 1515 case CREATE_DIRECTORY:
d7b8eec7
LP
1516 case TRUNCATE_DIRECTORY:
1517 case CREATE_SUBVOLUME:
5fb13eb5
LP
1518 case CREATE_SUBVOLUME_INHERIT_QUOTA:
1519 case CREATE_SUBVOLUME_NEW_QUOTA:
d7b8eec7 1520 RUN_WITH_UMASK(0000)
7fa10748 1521 (void) mkdir_parents_label(i->path, 0755);
d7b8eec7 1522
5fb13eb5 1523 if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) {
2904e949
LP
1524
1525 if (btrfs_is_subvol(isempty(arg_root) ? "/" : arg_root) <= 0)
1526
1527 /* Don't create a subvolume unless the
1528 * root directory is one, too. We do
1529 * this under the assumption that if
1530 * the root directory is just a plain
1531 * directory (i.e. very light-weight),
1532 * we shouldn't try to split it up
1533 * into subvolumes (i.e. more
1534 * heavy-weight). Thus, chroot()
1535 * environments and suchlike will get
1536 * a full brtfs subvolume set up below
1537 * their tree only if they
1538 * specifically set up a btrfs
1539 * subvolume for the root dir too. */
1540
1541 r = -ENOTTY;
1542 else {
1543 RUN_WITH_UMASK((~i->mode) & 0777)
1544 r = btrfs_subvol_make(i->path);
1545 }
5fb13eb5 1546 } else
d7b8eec7
LP
1547 r = 0;
1548
582deb84 1549 if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY)
d7b8eec7
LP
1550 RUN_WITH_UMASK(0000)
1551 r = mkdir_label(i->path, i->mode);
5008d581 1552
e156347e 1553 if (r < 0) {
7b135a73 1554 int k;
5008d581 1555
4c701096 1556 if (!IN_SET(r, -EEXIST, -EROFS))
7b135a73 1557 return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path);
5008d581 1558
7b135a73
LP
1559 k = is_dir(i->path, false);
1560 if (k == -ENOENT && r == -EROFS)
1561 return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", i->path);
1562 if (k < 0)
1563 return log_error_errno(k, "Failed to check if %s exists: %m", i->path);
1564 if (!k) {
1565 log_warning("\"%s\" already exists and is not a directory.", i->path);
e156347e
LP
1566 return 0;
1567 }
294929f8
ZJS
1568
1569 creation = CREATION_EXISTING;
1570 } else
1571 creation = CREATION_NORMAL;
7b135a73 1572
7a7d5db7 1573 log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path);
5008d581 1574
5fb13eb5
LP
1575 if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
1576 r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
114a17d2 1577 if (r == -ENOTTY)
022ffe4c 1578 log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);
114a17d2 1579 else if (r == -EROFS)
022ffe4c 1580 log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path);
114a17d2 1581 else if (r == -ENOPROTOOPT)
022ffe4c 1582 log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path);
114a17d2
EV
1583 else if (r < 0)
1584 q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
1585 else if (r > 0)
5fb13eb5 1586 log_debug("Adjusted quota for subvolume \"%s\".", i->path);
114a17d2 1587 else if (r == 0)
5fb13eb5
LP
1588 log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path);
1589 }
1590
4831981d 1591 _fallthrough_;
df8dee85 1592 case EMPTY_DIRECTORY:
b705ab6a 1593 r = path_set_perms(i, i->path);
114a17d2
EV
1594 if (q < 0)
1595 return q;
f05bc3f7
MS
1596 if (r < 0)
1597 return r;
3b63d2d3
LP
1598
1599 break;
ee17ee7c
LP
1600
1601 case CREATE_FIFO:
5c0d398d 1602 RUN_WITH_UMASK(0000) {
7fa10748
LP
1603 (void) mkdir_parents_label(i->path, 0755);
1604
ecabcf8b 1605 mac_selinux_create_file_prepare(i->path, S_IFIFO);
5c0d398d 1606 r = mkfifo(i->path, i->mode);
ecabcf8b 1607 mac_selinux_create_file_clear();
5c0d398d 1608 }
ee17ee7c 1609
1554afae 1610 if (r < 0) {
4a62c710
MS
1611 if (errno != EEXIST)
1612 return log_error_errno(errno, "Failed to create fifo %s: %m", i->path);
ee17ee7c 1613
a542c4dc 1614 if (lstat(i->path, &st) < 0)
4a62c710 1615 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
ee17ee7c 1616
1554afae
LP
1617 if (!S_ISFIFO(st.st_mode)) {
1618
1619 if (i->force) {
1554afae 1620 RUN_WITH_UMASK(0000) {
ecabcf8b 1621 mac_selinux_create_file_prepare(i->path, S_IFIFO);
1554afae 1622 r = mkfifo_atomic(i->path, i->mode);
ecabcf8b 1623 mac_selinux_create_file_clear();
1554afae
LP
1624 }
1625
f647962d
MS
1626 if (r < 0)
1627 return log_error_errno(r, "Failed to create fifo %s: %m", i->path);
294929f8 1628 creation = CREATION_FORCE;
1554afae 1629 } else {
a542c4dc 1630 log_warning("\"%s\" already exists and is not a fifo.", i->path);
1554afae
LP
1631 return 0;
1632 }
294929f8
ZJS
1633 } else
1634 creation = CREATION_EXISTING;
1635 } else
1636 creation = CREATION_NORMAL;
7a7d5db7 1637 log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path);
ee17ee7c 1638
b705ab6a 1639 r = path_set_perms(i, i->path);
f05bc3f7
MS
1640 if (r < 0)
1641 return r;
ee17ee7c
LP
1642
1643 break;
5c5ccf12 1644 }
a8d88783 1645
5c5ccf12 1646 case CREATE_SYMLINK: {
7fa10748
LP
1647 RUN_WITH_UMASK(0000)
1648 (void) mkdir_parents_label(i->path, 0755);
1649
ecabcf8b 1650 mac_selinux_create_file_prepare(i->path, S_IFLNK);
4cef1923 1651 r = symlink(i->argument, i->path);
ecabcf8b 1652 mac_selinux_create_file_clear();
e9a5ef7c 1653
468d726b 1654 if (r < 0) {
2e78fa79 1655 _cleanup_free_ char *x = NULL;
468d726b 1656
4a62c710 1657 if (errno != EEXIST)
4cef1923 1658 return log_error_errno(errno, "symlink(%s, %s) failed: %m", i->argument, i->path);
2e78fa79
LP
1659
1660 r = readlink_malloc(i->path, &x);
4cef1923 1661 if (r < 0 || !streq(i->argument, x)) {
2e78fa79
LP
1662
1663 if (i->force) {
ecabcf8b 1664 mac_selinux_create_file_prepare(i->path, S_IFLNK);
4cef1923 1665 r = symlink_atomic(i->argument, i->path);
ecabcf8b 1666 mac_selinux_create_file_clear();
2e78fa79 1667
b3f5897f
WD
1668 if (IN_SET(r, -EEXIST, -ENOTEMPTY)) {
1669 r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL);
1670 if (r < 0)
1671 return log_error_errno(r, "rm -fr %s failed: %m", i->path);
1672
1673 mac_selinux_create_file_prepare(i->path, S_IFLNK);
4cef1923 1674 r = symlink(i->argument, i->path) < 0 ? -errno : 0;
b3f5897f
WD
1675 mac_selinux_create_file_clear();
1676 }
f647962d 1677 if (r < 0)
4cef1923 1678 return log_error_errno(r, "symlink(%s, %s) failed: %m", i->argument, i->path);
a542c4dc 1679
294929f8 1680 creation = CREATION_FORCE;
1554afae 1681 } else {
582deb84 1682 log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path);
1554afae
LP
1683 return 0;
1684 }
294929f8
ZJS
1685 } else
1686 creation = CREATION_EXISTING;
1687 } else
a542c4dc 1688
294929f8 1689 creation = CREATION_NORMAL;
7a7d5db7 1690 log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path);
468d726b 1691 break;
5c5ccf12 1692 }
468d726b
LP
1693
1694 case CREATE_BLOCK_DEVICE:
1695 case CREATE_CHAR_DEVICE: {
cb7ed9df
LP
1696 mode_t file_type;
1697
1698 if (have_effective_cap(CAP_MKNOD) == 0) {
1699 /* In a container we lack CAP_MKNOD. We
ab06eef8 1700 shouldn't attempt to create the device node in
cb7ed9df
LP
1701 that case to avoid noise, and we don't support
1702 virtualized devices in containers anyway. */
1703
1704 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
1705 return 0;
1706 }
1707
7fa10748
LP
1708 RUN_WITH_UMASK(0000)
1709 (void) mkdir_parents_label(i->path, 0755);
1710
1554afae 1711 file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR;
468d726b 1712
5c0d398d 1713 RUN_WITH_UMASK(0000) {
ecabcf8b 1714 mac_selinux_create_file_prepare(i->path, file_type);
5c0d398d 1715 r = mknod(i->path, i->mode | file_type, i->major_minor);
ecabcf8b 1716 mac_selinux_create_file_clear();
5c0d398d 1717 }
468d726b 1718
6555ad8e
KS
1719 if (r < 0) {
1720 if (errno == EPERM) {
1721 log_debug("We lack permissions, possibly because of cgroup configuration; "
1722 "skipping creation of device node %s.", i->path);
1723 return 0;
1724 }
1725
4a62c710
MS
1726 if (errno != EEXIST)
1727 return log_error_errno(errno, "Failed to create device node %s: %m", i->path);
468d726b 1728
a542c4dc 1729 if (lstat(i->path, &st) < 0)
4a62c710 1730 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
468d726b 1731
1554afae
LP
1732 if ((st.st_mode & S_IFMT) != file_type) {
1733
1734 if (i->force) {
1735
1736 RUN_WITH_UMASK(0000) {
ecabcf8b 1737 mac_selinux_create_file_prepare(i->path, file_type);
1554afae 1738 r = mknod_atomic(i->path, i->mode | file_type, i->major_minor);
ecabcf8b 1739 mac_selinux_create_file_clear();
1554afae
LP
1740 }
1741
f647962d 1742 if (r < 0)
294929f8
ZJS
1743 return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
1744 creation = CREATION_FORCE;
1554afae
LP
1745 } else {
1746 log_debug("%s is not a device node.", i->path);
1747 return 0;
1748 }
294929f8
ZJS
1749 } else
1750 creation = CREATION_EXISTING;
1751 } else
1752 creation = CREATION_NORMAL;
a542c4dc 1753
294929f8 1754 log_debug("%s %s device node \"%s\" %u:%u.",
7a7d5db7 1755 creation_mode_verb_to_string(creation),
582deb84
ZJS
1756 i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
1757 i->path, major(i->mode), minor(i->mode));
468d726b 1758
b705ab6a 1759 r = path_set_perms(i, i->path);
468d726b
LP
1760 if (r < 0)
1761 return r;
1762
1763 break;
1764 }
1765
e73a03e0 1766 case ADJUST_MODE:
777b87e7 1767 case RELABEL_PATH:
936f6bdb 1768 r = glob_item(i, path_set_perms);
777b87e7 1769 if (r < 0)
96ca8194 1770 return r;
777b87e7
MS
1771 break;
1772
a8d88783 1773 case RECURSIVE_RELABEL_PATH:
936f6bdb 1774 r = glob_item_recursively(i, fd_set_perms);
a8d88783
MS
1775 if (r < 0)
1776 return r;
ebf4e801 1777 break;
e73a03e0 1778
ebf4e801 1779 case SET_XATTR:
936f6bdb 1780 r = glob_item(i, path_set_xattrs);
b705ab6a
ZJS
1781 if (r < 0)
1782 return r;
1783 break;
1784
1785 case RECURSIVE_SET_XATTR:
936f6bdb 1786 r = glob_item_recursively(i, fd_set_xattrs);
ebf4e801
MW
1787 if (r < 0)
1788 return r;
e73a03e0 1789 break;
f8eeeaf9
ZJS
1790
1791 case SET_ACL:
936f6bdb 1792 r = glob_item(i, path_set_acls);
f8eeeaf9
ZJS
1793 if (r < 0)
1794 return r;
b705ab6a
ZJS
1795 break;
1796
1797 case RECURSIVE_SET_ACL:
936f6bdb 1798 r = glob_item_recursively(i, fd_set_acls);
b705ab6a
ZJS
1799 if (r < 0)
1800 return r;
1801 break;
22c3a6ca 1802
88ec4dfa 1803 case SET_ATTRIBUTE:
936f6bdb 1804 r = glob_item(i, path_set_attribute);
22c3a6ca
GB
1805 if (r < 0)
1806 return r;
1807 break;
1808
88ec4dfa 1809 case RECURSIVE_SET_ATTRIBUTE:
936f6bdb 1810 r = glob_item_recursively(i, fd_set_attribute);
22c3a6ca
GB
1811 if (r < 0)
1812 return r;
1813 break;
3b63d2d3
LP
1814 }
1815
f05bc3f7 1816 return 0;
3b63d2d3
LP
1817}
1818
a0896123 1819static int remove_item_instance(Item *i, const char *instance) {
3b63d2d3
LP
1820 int r;
1821
1822 assert(i);
1823
1824 switch (i->type) {
1825
3b63d2d3 1826 case REMOVE_PATH:
4a62c710 1827 if (remove(instance) < 0 && errno != ENOENT)
3f93da98 1828 return log_error_errno(errno, "rm(%s): %m", instance);
3b63d2d3
LP
1829
1830 break;
1831
1832 case TRUNCATE_DIRECTORY:
1833 case RECURSIVE_REMOVE_PATH:
d139b24a
LP
1834 /* FIXME: we probably should use dir_cleanup() here
1835 * instead of rm_rf() so that 'x' is honoured. */
582deb84 1836 log_debug("rm -rf \"%s\"", instance);
1b26f09e 1837 r = rm_rf(instance, (i->type == RECURSIVE_REMOVE_PATH ? REMOVE_ROOT|REMOVE_SUBVOLUME : 0) | REMOVE_PHYSICAL);
f647962d
MS
1838 if (r < 0 && r != -ENOENT)
1839 return log_error_errno(r, "rm_rf(%s): %m", instance);
3b63d2d3
LP
1840
1841 break;
7fcb4b9b
ZJS
1842
1843 default:
1844 assert_not_reached("wut?");
3b63d2d3
LP
1845 }
1846
1847 return 0;
1848}
1849
a0896123 1850static int remove_item(Item *i) {
b8bb3e8f
LP
1851 assert(i);
1852
582deb84
ZJS
1853 log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
1854
b8bb3e8f
LP
1855 switch (i->type) {
1856
b8bb3e8f
LP
1857 case REMOVE_PATH:
1858 case TRUNCATE_DIRECTORY:
99e68c0b 1859 case RECURSIVE_REMOVE_PATH:
936f6bdb 1860 return glob_item(i, remove_item_instance);
b8bb3e8f 1861
df8dee85
ZJS
1862 default:
1863 return 0;
1864 }
b8bb3e8f
LP
1865}
1866
78a92a5a 1867static int clean_item_instance(Item *i, const char* instance) {
7fd1b19b 1868 _cleanup_closedir_ DIR *d = NULL;
78a92a5a
MS
1869 struct stat s, ps;
1870 bool mountpoint;
78a92a5a 1871 usec_t cutoff, n;
582deb84 1872 char timestamp[FORMAT_TIMESTAMP_MAX];
78a92a5a
MS
1873
1874 assert(i);
1875
1876 if (!i->age_set)
1877 return 0;
1878
1879 n = now(CLOCK_REALTIME);
1880 if (n < i->age)
1881 return 0;
1882
1883 cutoff = n - i->age;
1884
df99a9ef 1885 d = opendir_nomod(instance);
78a92a5a 1886 if (!d) {
d710aaf7 1887 if (IN_SET(errno, ENOENT, ENOTDIR)) {
582deb84 1888 log_debug_errno(errno, "Directory \"%s\": %m", instance);
78a92a5a 1889 return 0;
582deb84 1890 }
78a92a5a 1891
d710aaf7 1892 return log_error_errno(errno, "Failed to open directory %s: %m", instance);
78a92a5a
MS
1893 }
1894
4a62c710
MS
1895 if (fstat(dirfd(d), &s) < 0)
1896 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
78a92a5a
MS
1897
1898 if (!S_ISDIR(s.st_mode)) {
1899 log_error("%s is not a directory.", i->path);
19fbec19 1900 return -ENOTDIR;
78a92a5a
MS
1901 }
1902
4a62c710
MS
1903 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
1904 return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
78a92a5a 1905
2bb158c1 1906 mountpoint = s.st_dev != ps.st_dev || s.st_ino == ps.st_ino;
78a92a5a 1907
582deb84
ZJS
1908 log_debug("Cleanup threshold for %s \"%s\" is %s",
1909 mountpoint ? "mount point" : "directory",
1910 instance,
1911 format_timestamp_us(timestamp, sizeof(timestamp), cutoff));
1912
1913 return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
1914 MAX_DEPTH, i->keep_first_level);
78a92a5a
MS
1915}
1916
1917static int clean_item(Item *i) {
78a92a5a
MS
1918 assert(i);
1919
582deb84
ZJS
1920 log_debug("Running clean action for entry %c %s", (char) i->type, i->path);
1921
78a92a5a
MS
1922 switch (i->type) {
1923 case CREATE_DIRECTORY:
d7b8eec7 1924 case CREATE_SUBVOLUME:
5fb13eb5
LP
1925 case CREATE_SUBVOLUME_INHERIT_QUOTA:
1926 case CREATE_SUBVOLUME_NEW_QUOTA:
78a92a5a
MS
1927 case TRUNCATE_DIRECTORY:
1928 case IGNORE_PATH:
849958d1 1929 case COPY_FILES:
78a92a5a 1930 clean_item_instance(i, i->path);
df8dee85 1931 return 0;
65241c14 1932 case EMPTY_DIRECTORY:
78a92a5a 1933 case IGNORE_DIRECTORY_PATH:
936f6bdb 1934 return glob_item(i, clean_item_instance);
78a92a5a 1935 default:
df8dee85 1936 return 0;
78a92a5a 1937 }
78a92a5a
MS
1938}
1939
3f93da98
ZJS
1940static int process_item_array(ItemArray *array);
1941
3b63d2d3 1942static int process_item(Item *i) {
1e95893a 1943 int r, q, p, t = 0;
9348f0e6 1944 _cleanup_free_ char *prefix = NULL;
3b63d2d3
LP
1945
1946 assert(i);
1947
1910cd0e
LP
1948 if (i->done)
1949 return 0;
1950
1951 i->done = true;
1952
9348f0e6
ZJS
1953 prefix = malloc(strlen(i->path) + 1);
1954 if (!prefix)
1955 return log_oom();
1956
1910cd0e 1957 PATH_FOREACH_PREFIX(prefix, i->path) {
3f93da98 1958 ItemArray *j;
1910cd0e 1959
ef43a391 1960 j = ordered_hashmap_get(items, prefix);
1db50423
ZJS
1961 if (j) {
1962 int s;
1963
3f93da98 1964 s = process_item_array(j);
1db50423
ZJS
1965 if (s < 0 && t == 0)
1966 t = s;
1967 }
1910cd0e
LP
1968 }
1969
655f2da0
N
1970 if (chase_symlinks(i->path, NULL, CHASE_NO_AUTOFS, NULL) == -EREMOTE)
1971 return t;
1972
3b63d2d3 1973 r = arg_create ? create_item(i) : 0;
a0896123 1974 q = arg_remove ? remove_item(i) : 0;
3b63d2d3
LP
1975 p = arg_clean ? clean_item(i) : 0;
1976
1db50423
ZJS
1977 return t < 0 ? t :
1978 r < 0 ? r :
1979 q < 0 ? q :
1980 p;
3b63d2d3
LP
1981}
1982
3f93da98
ZJS
1983static int process_item_array(ItemArray *array) {
1984 unsigned n;
1985 int r = 0, k;
753615e8 1986
3f93da98
ZJS
1987 assert(array);
1988
1989 for (n = 0; n < array->count; n++) {
1990 k = process_item(array->items + n);
1991 if (k < 0 && r == 0)
1992 r = k;
1993 }
1994
1995 return r;
1996}
3b63d2d3 1997
3f93da98
ZJS
1998static void item_free_contents(Item *i) {
1999 assert(i);
3b63d2d3 2000 free(i->path);
468d726b 2001 free(i->argument);
ebf4e801 2002 strv_free(i->xattrs);
f8eeeaf9 2003
349cc4a5 2004#if HAVE_ACL
f8eeeaf9
ZJS
2005 acl_free(i->acl_access);
2006 acl_free(i->acl_default);
2007#endif
3b63d2d3
LP
2008}
2009
3f93da98
ZJS
2010static void item_array_free(ItemArray *a) {
2011 unsigned n;
2012
2013 if (!a)
2014 return;
2015
2016 for (n = 0; n < a->count; n++)
2017 item_free_contents(a->items + n);
2018 free(a->items);
2019 free(a);
2020}
e2f2fb78 2021
17493fa5
LP
2022static int item_compare(const void *a, const void *b) {
2023 const Item *x = a, *y = b;
2024
2025 /* Make sure that the ownership taking item is put first, so
2026 * that we first create the node, and then can adjust it */
2027
2028 if (takes_ownership(x->type) && !takes_ownership(y->type))
2029 return -1;
2030 if (!takes_ownership(x->type) && takes_ownership(y->type))
2031 return 1;
2032
2033 return (int) x->type - (int) y->type;
2034}
2035
3f93da98 2036static bool item_compatible(Item *a, Item *b) {
bfe95f35
LP
2037 assert(a);
2038 assert(b);
3f93da98 2039 assert(streq(a->path, b->path));
bfe95f35 2040
3f93da98
ZJS
2041 if (takes_ownership(a->type) && takes_ownership(b->type))
2042 /* check if the items are the same */
2043 return streq_ptr(a->argument, b->argument) &&
bfe95f35 2044
3f93da98
ZJS
2045 a->uid_set == b->uid_set &&
2046 a->uid == b->uid &&
bfe95f35 2047
3f93da98
ZJS
2048 a->gid_set == b->gid_set &&
2049 a->gid == b->gid &&
bfe95f35 2050
3f93da98
ZJS
2051 a->mode_set == b->mode_set &&
2052 a->mode == b->mode &&
bfe95f35 2053
3f93da98
ZJS
2054 a->age_set == b->age_set &&
2055 a->age == b->age &&
bfe95f35 2056
3f93da98 2057 a->mask_perms == b->mask_perms &&
bfe95f35 2058
3f93da98 2059 a->keep_first_level == b->keep_first_level &&
468d726b 2060
3f93da98 2061 a->major_minor == b->major_minor;
468d726b 2062
bfe95f35
LP
2063 return true;
2064}
2065
a2aced4a
DR
2066static bool should_include_path(const char *path) {
2067 char **prefix;
2068
abef3f91 2069 STRV_FOREACH(prefix, arg_exclude_prefixes)
582deb84
ZJS
2070 if (path_startswith(path, *prefix)) {
2071 log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
2072 path, *prefix);
5c795114 2073 return false;
582deb84 2074 }
a2aced4a 2075
abef3f91 2076 STRV_FOREACH(prefix, arg_include_prefixes)
582deb84
ZJS
2077 if (path_startswith(path, *prefix)) {
2078 log_debug("Entry \"%s\" matches include prefix \"%s\".", path, *prefix);
a2aced4a 2079 return true;
582deb84 2080 }
a2aced4a 2081
5c795114
DR
2082 /* no matches, so we should include this path only if we
2083 * have no whitelist at all */
7b943bb7 2084 if (strv_isempty(arg_include_prefixes))
582deb84
ZJS
2085 return true;
2086
2087 log_debug("Entry \"%s\" does not match any include prefix, skipping.", path);
2088 return false;
a2aced4a
DR
2089}
2090
4cef1923
FB
2091static int specifier_expansion_from_arg(Item *i) {
2092 _cleanup_free_ char *unescaped = NULL, *resolved = NULL;
2093 char **xattr;
2094 int r;
2095
2096 assert(i);
2097
2098 if (i->argument == NULL)
2099 return 0;
2100
2101 switch (i->type) {
2102 case COPY_FILES:
2103 case CREATE_SYMLINK:
2104 case CREATE_FILE:
2105 case TRUNCATE_FILE:
2106 case WRITE_FILE:
2107 r = cunescape(i->argument, 0, &unescaped);
2108 if (r < 0)
2109 return log_error_errno(r, "Failed to unescape parameter to write: %s", i->argument);
2110
2111 r = specifier_printf(unescaped, specifier_table, NULL, &resolved);
2112 if (r < 0)
2113 return r;
2114
2115 free_and_replace(i->argument, resolved);
2116 break;
2117
2118 case SET_XATTR:
2119 case RECURSIVE_SET_XATTR:
2120 assert(i->xattrs);
2121
2122 STRV_FOREACH (xattr, i->xattrs) {
2123 r = specifier_printf(*xattr, specifier_table, NULL, &resolved);
2124 if (r < 0)
2125 return r;
2126
2127 free_and_replace(*xattr, resolved);
2128 }
2129 break;
2130
2131 default:
2132 break;
2133 }
2134 return 0;
2135}
2136
d9daae55 2137static int parse_line(const char *fname, unsigned line, const char *buffer, bool *invalid_config) {
1731e34a 2138
cde684a2 2139 _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
3f93da98
ZJS
2140 _cleanup_(item_free_contents) Item i = {};
2141 ItemArray *existing;
ef43a391 2142 OrderedHashmap *h;
657cf7f4 2143 int r, pos;
5f255144 2144 bool force = false, boot = false;
3b63d2d3
LP
2145
2146 assert(fname);
2147 assert(line >= 1);
2148 assert(buffer);
2149
68685607 2150 r = extract_many_words(
4034a06d 2151 &buffer,
68685607 2152 NULL,
12ba2c44 2153 EXTRACT_QUOTES,
4034a06d
LP
2154 &action,
2155 &path,
2156 &mode,
2157 &user,
2158 &group,
2159 &age,
2160 NULL);
d9daae55 2161 if (r < 0) {
751223fe
ZJS
2162 if (IN_SET(r, -EINVAL, -EBADSLT))
2163 /* invalid quoting and such or an unknown specifier */
d9daae55 2164 *invalid_config = true;
657cf7f4 2165 return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line);
d9daae55
ZJS
2166 }
2167
657cf7f4 2168 else if (r < 2) {
d9daae55 2169 *invalid_config = true;
3b63d2d3 2170 log_error("[%s:%u] Syntax error.", fname, line);
7f2c1f4d 2171 return -EIO;
5008d581
LP
2172 }
2173
aa5f6817 2174 if (!isempty(buffer) && !streq(buffer, "-")) {
4034a06d
LP
2175 i.argument = strdup(buffer);
2176 if (!i.argument)
2177 return log_oom();
2178 }
2179
2e78fa79 2180 if (isempty(action)) {
d9daae55 2181 *invalid_config = true;
2e78fa79 2182 log_error("[%s:%u] Command too short '%s'.", fname, line, action);
c4708f13 2183 return -EINVAL;
2e78fa79
LP
2184 }
2185
5f255144
ZJS
2186 for (pos = 1; action[pos]; pos++) {
2187 if (action[pos] == '!' && !boot)
2188 boot = true;
2189 else if (action[pos] == '+' && !force)
2190 force = true;
2191 else {
d9daae55 2192 *invalid_config = true;
5f255144
ZJS
2193 log_error("[%s:%u] Unknown modifiers in command '%s'",
2194 fname, line, action);
2195 return -EINVAL;
2196 }
2e78fa79
LP
2197 }
2198
582deb84
ZJS
2199 if (boot && !arg_boot) {
2200 log_debug("Ignoring entry %s \"%s\" because --boot is not specified.",
2201 action, path);
c4708f13 2202 return 0;
582deb84 2203 }
c4708f13 2204
3f93da98
ZJS
2205 i.type = action[0];
2206 i.force = force;
2e78fa79 2207
3f93da98 2208 r = specifier_printf(path, specifier_table, NULL, &i.path);
5a8575ef 2209 if (r == -ENXIO)
4cef1923 2210 return log_unresolvable_specifier(fname, line);
d9daae55 2211 if (r < 0) {
751223fe
ZJS
2212 if (IN_SET(r, -EINVAL, -EBADSLT))
2213 *invalid_config = true;
4cef1923 2214 return log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", fname, line, path);
d9daae55 2215 }
1731e34a 2216
3f93da98 2217 switch (i.type) {
468d726b 2218
777b87e7 2219 case CREATE_DIRECTORY:
d7b8eec7 2220 case CREATE_SUBVOLUME:
5fb13eb5
LP
2221 case CREATE_SUBVOLUME_INHERIT_QUOTA:
2222 case CREATE_SUBVOLUME_NEW_QUOTA:
df8dee85 2223 case EMPTY_DIRECTORY:
777b87e7
MS
2224 case TRUNCATE_DIRECTORY:
2225 case CREATE_FIFO:
2226 case IGNORE_PATH:
78a92a5a 2227 case IGNORE_DIRECTORY_PATH:
777b87e7
MS
2228 case REMOVE_PATH:
2229 case RECURSIVE_REMOVE_PATH:
e73a03e0 2230 case ADJUST_MODE:
777b87e7
MS
2231 case RELABEL_PATH:
2232 case RECURSIVE_RELABEL_PATH:
c82500c6 2233 if (i.argument)
bd550f78 2234 log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname, line, i.type);
c82500c6
LP
2235
2236 break;
2237
2238 case CREATE_FILE:
2239 case TRUNCATE_FILE:
777b87e7 2240 break;
468d726b
LP
2241
2242 case CREATE_SYMLINK:
3f93da98
ZJS
2243 if (!i.argument) {
2244 i.argument = strappend("/usr/share/factory/", i.path);
2245 if (!i.argument)
2f3b873a 2246 return log_oom();
468d726b
LP
2247 }
2248 break;
2249
31ed59c5 2250 case WRITE_FILE:
3f93da98 2251 if (!i.argument) {
d9daae55 2252 *invalid_config = true;
31ed59c5 2253 log_error("[%s:%u] Write file requires argument.", fname, line);
7f2c1f4d 2254 return -EBADMSG;
31ed59c5
LP
2255 }
2256 break;
2257
849958d1 2258 case COPY_FILES:
3f93da98
ZJS
2259 if (!i.argument) {
2260 i.argument = strappend("/usr/share/factory/", i.path);
2261 if (!i.argument)
2f3b873a 2262 return log_oom();
3f93da98 2263 } else if (!path_is_absolute(i.argument)) {
d9daae55 2264 *invalid_config = true;
849958d1
LP
2265 log_error("[%s:%u] Source path is not absolute.", fname, line);
2266 return -EBADMSG;
2267 }
2268
3f93da98 2269 path_kill_slashes(i.argument);
849958d1
LP
2270 break;
2271
468d726b
LP
2272 case CREATE_CHAR_DEVICE:
2273 case CREATE_BLOCK_DEVICE: {
2274 unsigned major, minor;
2275
3f93da98 2276 if (!i.argument) {
d9daae55 2277 *invalid_config = true;
468d726b 2278 log_error("[%s:%u] Device file requires argument.", fname, line);
7f2c1f4d 2279 return -EBADMSG;
468d726b
LP
2280 }
2281
3f93da98 2282 if (sscanf(i.argument, "%u:%u", &major, &minor) != 2) {
d9daae55 2283 *invalid_config = true;
3f93da98 2284 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument);
7f2c1f4d 2285 return -EBADMSG;
468d726b
LP
2286 }
2287
3f93da98 2288 i.major_minor = makedev(major, minor);
468d726b
LP
2289 break;
2290 }
2291
ebf4e801 2292 case SET_XATTR:
b705ab6a 2293 case RECURSIVE_SET_XATTR:
3f93da98 2294 if (!i.argument) {
d9daae55 2295 *invalid_config = true;
ebf4e801
MW
2296 log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
2297 return -EBADMSG;
2298 }
bd550f78 2299 r = parse_xattrs_from_arg(&i);
ebf4e801
MW
2300 if (r < 0)
2301 return r;
2302 break;
2303
f8eeeaf9 2304 case SET_ACL:
b705ab6a 2305 case RECURSIVE_SET_ACL:
f8eeeaf9 2306 if (!i.argument) {
d9daae55 2307 *invalid_config = true;
f8eeeaf9
ZJS
2308 log_error("[%s:%u] Set ACLs requires argument.", fname, line);
2309 return -EBADMSG;
2310 }
bd550f78 2311 r = parse_acls_from_arg(&i);
f8eeeaf9
ZJS
2312 if (r < 0)
2313 return r;
2314 break;
2315
88ec4dfa
LP
2316 case SET_ATTRIBUTE:
2317 case RECURSIVE_SET_ATTRIBUTE:
22c3a6ca 2318 if (!i.argument) {
d9daae55 2319 *invalid_config = true;
88ec4dfa 2320 log_error("[%s:%u] Set file attribute requires argument.", fname, line);
22c3a6ca
GB
2321 return -EBADMSG;
2322 }
bd550f78 2323 r = parse_attribute_from_arg(&i);
751223fe 2324 if (IN_SET(r, -EINVAL, -EBADSLT))
d9daae55 2325 *invalid_config = true;
22c3a6ca
GB
2326 if (r < 0)
2327 return r;
2328 break;
2329
777b87e7 2330 default:
582deb84 2331 log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type);
d9daae55 2332 *invalid_config = true;
7f2c1f4d 2333 return -EBADMSG;
3b63d2d3 2334 }
468d726b 2335
3f93da98
ZJS
2336 if (!path_is_absolute(i.path)) {
2337 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path);
d9daae55 2338 *invalid_config = true;
7f2c1f4d 2339 return -EBADMSG;
3b63d2d3
LP
2340 }
2341
3f93da98 2342 path_kill_slashes(i.path);
3b63d2d3 2343
3f93da98 2344 if (!should_include_path(i.path))
7f2c1f4d 2345 return 0;
5008d581 2346
4cef1923 2347 r = specifier_expansion_from_arg(&i);
5a8575ef 2348 if (r == -ENXIO)
4cef1923 2349 return log_unresolvable_specifier(fname, line);
751223fe
ZJS
2350 if (r < 0) {
2351 if (IN_SET(r, -EINVAL, -EBADSLT))
2352 *invalid_config = true;
4cef1923
FB
2353 return log_error_errno(r, "[%s:%u] Failed to substitute specifiers in argument: %m",
2354 fname, line);
751223fe 2355 }
4cef1923 2356
cf9a4abd 2357 if (arg_root) {
cde684a2
LP
2358 char *p;
2359
1d13f648 2360 p = prefix_root(arg_root, i.path);
cf9a4abd
MM
2361 if (!p)
2362 return log_oom();
2363
3f93da98
ZJS
2364 free(i.path);
2365 i.path = p;
cf9a4abd
MM
2366 }
2367
90937fe3 2368 if (!isempty(user) && !streq(user, "-")) {
4b67834e
LP
2369 const char *u = user;
2370
3f93da98 2371 r = get_user_creds(&u, &i.uid, NULL, NULL, NULL);
4b67834e 2372 if (r < 0) {
d9daae55
ZJS
2373 *invalid_config = true;
2374 return log_error_errno(r, "[%s:%u] Unknown user '%s'.", fname, line, user);
3b63d2d3
LP
2375 }
2376
3f93da98 2377 i.uid_set = true;
3b63d2d3
LP
2378 }
2379
90937fe3 2380 if (!isempty(group) && !streq(group, "-")) {
4b67834e
LP
2381 const char *g = group;
2382
3f93da98 2383 r = get_group_creds(&g, &i.gid);
4b67834e 2384 if (r < 0) {
d9daae55 2385 *invalid_config = true;
3b63d2d3 2386 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
7f2c1f4d 2387 return r;
3b63d2d3
LP
2388 }
2389
3f93da98 2390 i.gid_set = true;
3b63d2d3
LP
2391 }
2392
90937fe3 2393 if (!isempty(mode) && !streq(mode, "-")) {
abef3f91 2394 const char *mm = mode;
3b63d2d3
LP
2395 unsigned m;
2396
abef3f91 2397 if (*mm == '~') {
3f93da98 2398 i.mask_perms = true;
abef3f91
LP
2399 mm++;
2400 }
2401
2ff7b0a5 2402 if (parse_mode(mm, &m) < 0) {
d9daae55 2403 *invalid_config = true;
3b63d2d3 2404 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
2ff7b0a5 2405 return -EBADMSG;
3b63d2d3
LP
2406 }
2407
3f93da98
ZJS
2408 i.mode = m;
2409 i.mode_set = true;
3b63d2d3 2410 } else
5fb13eb5 2411 i.mode = IN_SET(i.type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644;
3b63d2d3 2412
90937fe3 2413 if (!isempty(age) && !streq(age, "-")) {
24f3a374
LP
2414 const char *a = age;
2415
2416 if (*a == '~') {
3f93da98 2417 i.keep_first_level = true;
24f3a374
LP
2418 a++;
2419 }
2420
3f93da98 2421 if (parse_sec(a, &i.age) < 0) {
d9daae55 2422 *invalid_config = true;
3b63d2d3 2423 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
7f2c1f4d 2424 return -EBADMSG;
3b63d2d3
LP
2425 }
2426
3f93da98 2427 i.age_set = true;
3b63d2d3
LP
2428 }
2429
3f93da98 2430 h = needs_glob(i.type) ? globs : items;
bfe95f35 2431
ef43a391 2432 existing = ordered_hashmap_get(h, i.path);
468d726b 2433 if (existing) {
3f93da98
ZJS
2434 unsigned n;
2435
2436 for (n = 0; n < existing->count; n++) {
6487ada8 2437 if (!item_compatible(existing->items + n, &i)) {
e286dbaf
ZJS
2438 log_notice("[%s:%u] Duplicate line for path \"%s\", ignoring.",
2439 fname, line, i.path);
6487ada8
MP
2440 return 0;
2441 }
ebf4e801
MW
2442 }
2443 } else {
3f93da98 2444 existing = new0(ItemArray, 1);
07982ed1
LP
2445 if (!existing)
2446 return log_oom();
2447
ef43a391 2448 r = ordered_hashmap_put(h, i.path, existing);
3f93da98
ZJS
2449 if (r < 0)
2450 return log_oom();
bfe95f35
LP
2451 }
2452
3f93da98
ZJS
2453 if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1))
2454 return log_oom();
5008d581 2455
3f93da98 2456 memcpy(existing->items + existing->count++, &i, sizeof(i));
dd449aca
ZJS
2457
2458 /* Sort item array, to enforce stable ordering of application */
2459 qsort_safe(existing->items, existing->count, sizeof(Item), item_compare);
17493fa5 2460
3f93da98 2461 zero(i);
7f2c1f4d 2462 return 0;
5008d581
LP
2463}
2464
601185b4 2465static void help(void) {
522d4a49
LP
2466 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
2467 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
5c795114 2468 " -h --help Show this help\n"
f2b5ca0e 2469 " --user Execute user configuration\n"
eb9da376 2470 " --version Show package version\n"
5c795114
DR
2471 " --create Create marked files/directories\n"
2472 " --clean Clean up marked directories\n"
2473 " --remove Remove marked files/directories\n"
81815651 2474 " --boot Execute actions only safe at boot\n"
79ca888f
ZJS
2475 " --prefix=PATH Only apply rules with the specified prefix\n"
2476 " --exclude-prefix=PATH Ignore rules with the specified prefix\n"
d9daae55 2477 " --root=PATH Operate on an alternate filesystem root\n"
a6d8474f 2478 " --replace=PATH Treat arguments as replacement for PATH\n"
d9daae55 2479 , program_invocation_short_name);
3b63d2d3
LP
2480}
2481
2482static int parse_argv(int argc, char *argv[]) {
2483
2484 enum {
eb9da376 2485 ARG_VERSION = 0x100,
f2b5ca0e 2486 ARG_USER,
3b63d2d3
LP
2487 ARG_CREATE,
2488 ARG_CLEAN,
fba6e687 2489 ARG_REMOVE,
81815651 2490 ARG_BOOT,
5c795114
DR
2491 ARG_PREFIX,
2492 ARG_EXCLUDE_PREFIX,
cf9a4abd 2493 ARG_ROOT,
a6d8474f 2494 ARG_REPLACE,
3b63d2d3
LP
2495 };
2496
2497 static const struct option options[] = {
5c795114 2498 { "help", no_argument, NULL, 'h' },
f2b5ca0e 2499 { "user", no_argument, NULL, ARG_USER },
eb9da376 2500 { "version", no_argument, NULL, ARG_VERSION },
5c795114
DR
2501 { "create", no_argument, NULL, ARG_CREATE },
2502 { "clean", no_argument, NULL, ARG_CLEAN },
2503 { "remove", no_argument, NULL, ARG_REMOVE },
81815651 2504 { "boot", no_argument, NULL, ARG_BOOT },
5c795114
DR
2505 { "prefix", required_argument, NULL, ARG_PREFIX },
2506 { "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
cf9a4abd 2507 { "root", required_argument, NULL, ARG_ROOT },
a6d8474f 2508 { "replace", required_argument, NULL, ARG_REPLACE },
eb9da376 2509 {}
3b63d2d3
LP
2510 };
2511
0f474365 2512 int c, r;
3b63d2d3
LP
2513
2514 assert(argc >= 0);
2515 assert(argv);
2516
601185b4 2517 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
3b63d2d3
LP
2518
2519 switch (c) {
2520
2521 case 'h':
601185b4
ZJS
2522 help();
2523 return 0;
eb9da376
LP
2524
2525 case ARG_VERSION:
3f6fd1ba 2526 return version();
3b63d2d3 2527
f2b5ca0e
ZJS
2528 case ARG_USER:
2529 arg_user = true;
2530 break;
2531
3b63d2d3
LP
2532 case ARG_CREATE:
2533 arg_create = true;
2534 break;
2535
2536 case ARG_CLEAN:
2537 arg_clean = true;
2538 break;
2539
2540 case ARG_REMOVE:
2541 arg_remove = true;
2542 break;
2543
81815651
ZJS
2544 case ARG_BOOT:
2545 arg_boot = true;
c4708f13
ZJS
2546 break;
2547
fba6e687 2548 case ARG_PREFIX:
7bc040fa 2549 if (strv_push(&arg_include_prefixes, optarg) < 0)
a2aced4a 2550 return log_oom();
fba6e687
LP
2551 break;
2552
5c795114 2553 case ARG_EXCLUDE_PREFIX:
7bc040fa 2554 if (strv_push(&arg_exclude_prefixes, optarg) < 0)
5c795114
DR
2555 return log_oom();
2556 break;
2557
cf9a4abd 2558 case ARG_ROOT:
0f03c2a4 2559 r = parse_path_argument_and_warn(optarg, true, &arg_root);
0f474365 2560 if (r < 0)
0f03c2a4 2561 return r;
cf9a4abd
MM
2562 break;
2563
a6d8474f
ZJS
2564 case ARG_REPLACE:
2565 if (!path_is_absolute(optarg) ||
2566 !endswith(optarg, ".conf")) {
2567 log_error("The argument to --replace= must an absolute path to a config file");
2568 return -EINVAL;
2569 }
2570
2571 arg_replace = optarg;
2572 break;
2573
3b63d2d3
LP
2574 case '?':
2575 return -EINVAL;
2576
2577 default:
eb9da376 2578 assert_not_reached("Unhandled option");
3b63d2d3 2579 }
3b63d2d3
LP
2580
2581 if (!arg_clean && !arg_create && !arg_remove) {
35b8ca3a 2582 log_error("You need to specify at least one of --clean, --create or --remove.");
3b63d2d3
LP
2583 return -EINVAL;
2584 }
2585
a6d8474f
ZJS
2586 if (arg_replace && optind >= argc) {
2587 log_error("When --replace= is given, some configuration items must be specified");
2588 return -EINVAL;
2589 }
2590
3b63d2d3
LP
2591 return 1;
2592}
2593
a6d8474f 2594static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
f7ac1ed2
ZJS
2595 _cleanup_fclose_ FILE *_f = NULL;
2596 FILE *f;
1731e34a 2597 char line[LINE_MAX];
78a92a5a 2598 Iterator iterator;
1731e34a 2599 unsigned v = 0;
78a92a5a 2600 Item *i;
4e68ec18 2601 int r = 0;
fba6e687
LP
2602
2603 assert(fn);
2604
f7ac1ed2 2605 if (streq(fn, "-")) {
a6d8474f 2606 log_debug("Reading config from stdin…");
f7ac1ed2
ZJS
2607 fn = "<stdin>";
2608 f = stdin;
2609 } else {
a6d8474f 2610 r = search_and_fopen(fn, "re", arg_root, (const char**) config_dirs, &_f);
f7ac1ed2
ZJS
2611 if (r < 0) {
2612 if (ignore_enoent && r == -ENOENT) {
2613 log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn);
2614 return 0;
2615 }
fba6e687 2616
f7ac1ed2
ZJS
2617 return log_error_errno(r, "Failed to open '%s': %m", fn);
2618 }
a6d8474f 2619 log_debug("Reading config file \"%s\"…", fn);
f7ac1ed2 2620 f = _f;
fba6e687
LP
2621 }
2622
1731e34a
LP
2623 FOREACH_LINE(line, f, break) {
2624 char *l;
fba6e687 2625 int k;
d9daae55 2626 bool invalid_line = false;
fba6e687 2627
fba6e687
LP
2628 v++;
2629
2630 l = strstrip(line);
4c701096 2631 if (IN_SET(*l, 0, '#'))
fba6e687
LP
2632 continue;
2633
d9daae55
ZJS
2634 k = parse_line(fn, v, l, &invalid_line);
2635 if (k < 0) {
2636 if (invalid_line)
2637 /* Allow reporting with a special code if the caller requested this */
2638 *invalid_config = true;
2639 else if (r == 0)
2640 /* The first error becomes our return value */
2641 r = k;
2642 }
fba6e687
LP
2643 }
2644
78a92a5a 2645 /* we have to determine age parameter for each entry of type X */
ef43a391 2646 ORDERED_HASHMAP_FOREACH(i, globs, iterator) {
78a92a5a
MS
2647 Iterator iter;
2648 Item *j, *candidate_item = NULL;
2649
2650 if (i->type != IGNORE_DIRECTORY_PATH)
2651 continue;
2652
ef43a391 2653 ORDERED_HASHMAP_FOREACH(j, items, iter) {
5fb13eb5 2654 if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA))
78a92a5a
MS
2655 continue;
2656
2657 if (path_equal(j->path, i->path)) {
2658 candidate_item = j;
2659 break;
2660 }
2661
2662 if ((!candidate_item && path_startswith(i->path, j->path)) ||
2663 (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
2664 candidate_item = j;
2665 }
2666
9ed2a35e 2667 if (candidate_item && candidate_item->age_set) {
78a92a5a
MS
2668 i->age = candidate_item->age;
2669 i->age_set = true;
2670 }
2671 }
2672
fba6e687 2673 if (ferror(f)) {
56f64d95 2674 log_error_errno(errno, "Failed to read from file %s: %m", fn);
fba6e687
LP
2675 if (r == 0)
2676 r = -EIO;
2677 }
2678
fba6e687
LP
2679 return r;
2680}
2681
a6d8474f
ZJS
2682static int parse_arguments(char **config_dirs, char **args, bool *invalid_config) {
2683 char **arg;
2684 int r;
2685
2686 STRV_FOREACH(arg, args) {
2687 r = read_config_file(config_dirs, *arg, false, invalid_config);
2688 if (r < 0)
2689 return r;
2690 }
2691
2692 return 0;
2693}
2694
2695static int read_config_files(char **config_dirs, char **args, bool *invalid_config) {
2696 _cleanup_strv_free_ char **files = NULL;
2697 _cleanup_free_ char *p = NULL;
2698 char **f;
2699 int r;
2700
2701 r = conf_files_list_strv(&files, ".conf", arg_root, 0, (const char* const*) config_dirs);
2702 if (r < 0)
2703 return log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m");
2704
2705 if (arg_replace) {
2706 r = conf_files_insert(&files, arg_root, config_dirs, arg_replace);
2707 if (r < 0)
2708 return log_error_errno(r, "Failed to extend tmpfiles.d file list: %m");
2709
2710 p = path_join(arg_root, arg_replace, NULL);
2711 if (!p)
2712 return log_oom();
2713 }
2714
2715 STRV_FOREACH(f, files)
2716 if (p && path_equal(*f, p)) {
2717 log_debug("Parsing arguments at position \"%s\"…", *f);
2718
2719 r = parse_arguments(config_dirs, args, invalid_config);
2720 if (r < 0)
2721 return r;
2722 } else
2723 /* Just warn, ignore result otherwise.
2724 * read_config_file() has some debug output, so no need to print anything. */
2725 (void) read_config_file(config_dirs, *f, true, invalid_config);
2726
2727 return 0;
2728}
2729
5008d581 2730int main(int argc, char *argv[]) {
fabe5c0e 2731 int r, k;
3f93da98 2732 ItemArray *a;
3b63d2d3 2733 Iterator iterator;
f2b5ca0e 2734 _cleanup_strv_free_ char **config_dirs = NULL;
d9daae55 2735 bool invalid_config = false;
3b63d2d3 2736
fdcad0c2
LP
2737 r = parse_argv(argc, argv);
2738 if (r <= 0)
753615e8 2739 goto finish;
5008d581 2740
eb0ca9eb 2741 log_set_target(LOG_TARGET_AUTO);
5008d581
LP
2742 log_parse_environment();
2743 log_open();
2744
4c12626c
LP
2745 umask(0022);
2746
c3dacc8b 2747 mac_selinux_init();
5008d581 2748
ef43a391
LP
2749 items = ordered_hashmap_new(&string_hash_ops);
2750 globs = ordered_hashmap_new(&string_hash_ops);
b8bb3e8f
LP
2751
2752 if (!items || !globs) {
fabe5c0e 2753 r = log_oom();
3b63d2d3
LP
2754 goto finish;
2755 }
2756
fabe5c0e 2757 r = 0;
5008d581 2758
f2b5ca0e
ZJS
2759 if (arg_user) {
2760 r = user_config_paths(&config_dirs);
2761 if (r < 0) {
2762 log_error_errno(r, "Failed to initialize configuration directory list: %m");
2763 goto finish;
2764 }
2765 } else {
2766 config_dirs = strv_split_nulstr(CONF_PATHS_NULSTR("tmpfiles.d"));
2767 if (!config_dirs) {
2768 r = log_oom();
2769 goto finish;
2770 }
2771 }
2772
79c91cec 2773 if (DEBUG_LOGGING) {
f2b5ca0e
ZJS
2774 _cleanup_free_ char *t = NULL;
2775
2776 t = strv_join(config_dirs, "\n\t");
2777 if (t)
2778 log_debug("Looking for configuration files in (higher priority first:\n\t%s", t);
2779 }
2780
a6d8474f
ZJS
2781 /* If command line arguments are specified along with --replace, read all
2782 * configuration files and insert the positional arguments at the specified
2783 * place. Otherwise, if command line arguments are specified, execute just
2784 * them, and finally, without --replace= or any positional arguments, just
2785 * read configuration and execute it.
2786 */
2787 if (arg_replace || optind >= argc)
2788 r = read_config_files(config_dirs, argv + optind, &invalid_config);
2789 else
2790 r = parse_arguments(config_dirs, argv + optind, &invalid_config);
2791 if (r < 0)
2792 goto finish;
5008d581 2793
3b63d2d3 2794
5008d581 2795
17493fa5
LP
2796 /* The non-globbing ones usually create things, hence we apply
2797 * them first */
ef43a391 2798 ORDERED_HASHMAP_FOREACH(a, items, iterator) {
3f93da98 2799 k = process_item_array(a);
1db50423
ZJS
2800 if (k < 0 && r == 0)
2801 r = k;
2802 }
b8bb3e8f 2803
17493fa5
LP
2804 /* The globbing ones usually alter things, hence we apply them
2805 * second. */
ef43a391 2806 ORDERED_HASHMAP_FOREACH(a, globs, iterator) {
3f93da98 2807 k = process_item_array(a);
1db50423
ZJS
2808 if (k < 0 && r == 0)
2809 r = k;
2810 }
3b63d2d3 2811
5008d581 2812finish:
224b0e7a
ZJS
2813 ordered_hashmap_free_with_destructor(items, item_array_free);
2814 ordered_hashmap_free_with_destructor(globs, item_array_free);
5008d581 2815
7bc040fa
LP
2816 free(arg_include_prefixes);
2817 free(arg_exclude_prefixes);
cf9a4abd 2818 free(arg_root);
a2aced4a 2819
17b90525
LP
2820 set_free_free(unix_sockets);
2821
cc56fafe 2822 mac_selinux_finish();
29003cff 2823
d9daae55
ZJS
2824 if (r < 0)
2825 return EXIT_FAILURE;
2826 else if (invalid_config)
2827 return EX_DATAERR;
2828 else
2829 return EXIT_SUCCESS;
5008d581 2830}