]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/tmpfiles/tmpfiles.c
e574e5ac26d013780845a85e6b1a331f8ff4fdbe
[thirdparty/systemd.git] / src / tmpfiles / tmpfiles.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering, Kay Sievers
7 Copyright 2015 Zbigniew Jędrzejewski-Szmek
8
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 ***/
22
23 #include <dirent.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <fnmatch.h>
27 #include <getopt.h>
28 #include <glob.h>
29 #include <limits.h>
30 #include <linux/fs.h>
31 #include <stdbool.h>
32 #include <stddef.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include <sys/xattr.h>
38 #include <time.h>
39 #include <unistd.h>
40
41 #include "acl-util.h"
42 #include "btrfs-util.h"
43 #include "capability.h"
44 #include "conf-files.h"
45 #include "copy.h"
46 #include "escape.h"
47 #include "fd-util.h"
48 #include "formats-util.h"
49 #include "label.h"
50 #include "log.h"
51 #include "macro.h"
52 #include "missing.h"
53 #include "mkdir.h"
54 #include "path-util.h"
55 #include "rm-rf.h"
56 #include "selinux-util.h"
57 #include "set.h"
58 #include "specifier.h"
59 #include "string-util.h"
60 #include "strv.h"
61 #include "util.h"
62
63 /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
64 * them in the file system. This is intended to be used to create
65 * properly owned directories beneath /tmp, /var/tmp, /run, which are
66 * volatile and hence need to be recreated on bootup. */
67
68 typedef enum ItemType {
69 /* These ones take file names */
70 CREATE_FILE = 'f',
71 TRUNCATE_FILE = 'F',
72 CREATE_DIRECTORY = 'd',
73 TRUNCATE_DIRECTORY = 'D',
74 CREATE_SUBVOLUME = 'v',
75 CREATE_SUBVOLUME_INHERIT_QUOTA = 'q',
76 CREATE_SUBVOLUME_NEW_QUOTA = 'Q',
77 CREATE_FIFO = 'p',
78 CREATE_SYMLINK = 'L',
79 CREATE_CHAR_DEVICE = 'c',
80 CREATE_BLOCK_DEVICE = 'b',
81 COPY_FILES = 'C',
82
83 /* These ones take globs */
84 WRITE_FILE = 'w',
85 SET_XATTR = 't',
86 RECURSIVE_SET_XATTR = 'T',
87 SET_ACL = 'a',
88 RECURSIVE_SET_ACL = 'A',
89 SET_ATTRIBUTE = 'h',
90 RECURSIVE_SET_ATTRIBUTE = 'H',
91 IGNORE_PATH = 'x',
92 IGNORE_DIRECTORY_PATH = 'X',
93 REMOVE_PATH = 'r',
94 RECURSIVE_REMOVE_PATH = 'R',
95 RELABEL_PATH = 'z',
96 RECURSIVE_RELABEL_PATH = 'Z',
97 ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
98 } ItemType;
99
100 typedef struct Item {
101 ItemType type;
102
103 char *path;
104 char *argument;
105 char **xattrs;
106 #ifdef HAVE_ACL
107 acl_t acl_access;
108 acl_t acl_default;
109 #endif
110 uid_t uid;
111 gid_t gid;
112 mode_t mode;
113 usec_t age;
114
115 dev_t major_minor;
116 unsigned attribute_value;
117 unsigned attribute_mask;
118
119 bool uid_set:1;
120 bool gid_set:1;
121 bool mode_set:1;
122 bool age_set:1;
123 bool mask_perms:1;
124 bool attribute_set:1;
125
126 bool keep_first_level:1;
127
128 bool force:1;
129
130 bool done:1;
131 } Item;
132
133 typedef struct ItemArray {
134 Item *items;
135 size_t count;
136 size_t size;
137 } ItemArray;
138
139 static bool arg_create = false;
140 static bool arg_clean = false;
141 static bool arg_remove = false;
142 static bool arg_boot = false;
143
144 static char **arg_include_prefixes = NULL;
145 static char **arg_exclude_prefixes = NULL;
146 static char *arg_root = NULL;
147
148 static const char conf_file_dirs[] = CONF_DIRS_NULSTR("tmpfiles");
149
150 #define MAX_DEPTH 256
151
152 static OrderedHashmap *items = NULL, *globs = NULL;
153 static Set *unix_sockets = NULL;
154
155 static const Specifier specifier_table[] = {
156 { 'm', specifier_machine_id, NULL },
157 { 'b', specifier_boot_id, NULL },
158 { 'H', specifier_host_name, NULL },
159 { 'v', specifier_kernel_release, NULL },
160 {}
161 };
162
163 static bool needs_glob(ItemType t) {
164 return IN_SET(t,
165 WRITE_FILE,
166 IGNORE_PATH,
167 IGNORE_DIRECTORY_PATH,
168 REMOVE_PATH,
169 RECURSIVE_REMOVE_PATH,
170 ADJUST_MODE,
171 RELABEL_PATH,
172 RECURSIVE_RELABEL_PATH,
173 SET_XATTR,
174 RECURSIVE_SET_XATTR,
175 SET_ACL,
176 RECURSIVE_SET_ACL,
177 SET_ATTRIBUTE,
178 RECURSIVE_SET_ATTRIBUTE);
179 }
180
181 static bool takes_ownership(ItemType t) {
182 return IN_SET(t,
183 CREATE_FILE,
184 TRUNCATE_FILE,
185 CREATE_DIRECTORY,
186 TRUNCATE_DIRECTORY,
187 CREATE_SUBVOLUME,
188 CREATE_SUBVOLUME_INHERIT_QUOTA,
189 CREATE_SUBVOLUME_NEW_QUOTA,
190 CREATE_FIFO,
191 CREATE_SYMLINK,
192 CREATE_CHAR_DEVICE,
193 CREATE_BLOCK_DEVICE,
194 COPY_FILES,
195 WRITE_FILE,
196 IGNORE_PATH,
197 IGNORE_DIRECTORY_PATH,
198 REMOVE_PATH,
199 RECURSIVE_REMOVE_PATH);
200 }
201
202 static struct Item* find_glob(OrderedHashmap *h, const char *match) {
203 ItemArray *j;
204 Iterator i;
205
206 ORDERED_HASHMAP_FOREACH(j, h, i) {
207 unsigned n;
208
209 for (n = 0; n < j->count; n++) {
210 Item *item = j->items + n;
211
212 if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
213 return item;
214 }
215 }
216
217 return NULL;
218 }
219
220 static void load_unix_sockets(void) {
221 _cleanup_fclose_ FILE *f = NULL;
222 char line[LINE_MAX];
223
224 if (unix_sockets)
225 return;
226
227 /* We maintain a cache of the sockets we found in
228 * /proc/net/unix to speed things up a little. */
229
230 unix_sockets = set_new(&string_hash_ops);
231 if (!unix_sockets)
232 return;
233
234 f = fopen("/proc/net/unix", "re");
235 if (!f)
236 return;
237
238 /* Skip header */
239 if (!fgets(line, sizeof(line), f))
240 goto fail;
241
242 for (;;) {
243 char *p, *s;
244 int k;
245
246 if (!fgets(line, sizeof(line), f))
247 break;
248
249 truncate_nl(line);
250
251 p = strchr(line, ':');
252 if (!p)
253 continue;
254
255 if (strlen(p) < 37)
256 continue;
257
258 p += 37;
259 p += strspn(p, WHITESPACE);
260 p += strcspn(p, WHITESPACE); /* skip one more word */
261 p += strspn(p, WHITESPACE);
262
263 if (*p != '/')
264 continue;
265
266 s = strdup(p);
267 if (!s)
268 goto fail;
269
270 path_kill_slashes(s);
271
272 k = set_consume(unix_sockets, s);
273 if (k < 0 && k != -EEXIST)
274 goto fail;
275 }
276
277 return;
278
279 fail:
280 set_free_free(unix_sockets);
281 unix_sockets = NULL;
282 }
283
284 static bool unix_socket_alive(const char *fn) {
285 assert(fn);
286
287 load_unix_sockets();
288
289 if (unix_sockets)
290 return !!set_get(unix_sockets, (char*) fn);
291
292 /* We don't know, so assume yes */
293 return true;
294 }
295
296 static int dir_is_mount_point(DIR *d, const char *subdir) {
297
298 union file_handle_union h = FILE_HANDLE_INIT;
299 int mount_id_parent, mount_id;
300 int r_p, r;
301
302 r_p = name_to_handle_at(dirfd(d), ".", &h.handle, &mount_id_parent, 0);
303 if (r_p < 0)
304 r_p = -errno;
305
306 h.handle.handle_bytes = MAX_HANDLE_SZ;
307 r = name_to_handle_at(dirfd(d), subdir, &h.handle, &mount_id, 0);
308 if (r < 0)
309 r = -errno;
310
311 /* got no handle; make no assumptions, return error */
312 if (r_p < 0 && r < 0)
313 return r_p;
314
315 /* got both handles; if they differ, it is a mount point */
316 if (r_p >= 0 && r >= 0)
317 return mount_id_parent != mount_id;
318
319 /* got only one handle; assume different mount points if one
320 * of both queries was not supported by the filesystem */
321 if (r_p == -ENOSYS || r_p == -EOPNOTSUPP || r == -ENOSYS || r == -EOPNOTSUPP)
322 return true;
323
324 /* return error */
325 if (r_p < 0)
326 return r_p;
327 return r;
328 }
329
330 static DIR* xopendirat_nomod(int dirfd, const char *path) {
331 DIR *dir;
332
333 dir = xopendirat(dirfd, path, O_NOFOLLOW|O_NOATIME);
334 if (dir)
335 return dir;
336
337 log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path);
338 if (errno != EPERM)
339 return NULL;
340
341 dir = xopendirat(dirfd, path, O_NOFOLLOW);
342 if (!dir)
343 log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path);
344
345 return dir;
346 }
347
348 static DIR* opendir_nomod(const char *path) {
349 return xopendirat_nomod(AT_FDCWD, path);
350 }
351
352 static int dir_cleanup(
353 Item *i,
354 const char *p,
355 DIR *d,
356 const struct stat *ds,
357 usec_t cutoff,
358 dev_t rootdev,
359 bool mountpoint,
360 int maxdepth,
361 bool keep_this_level) {
362
363 struct dirent *dent;
364 struct timespec times[2];
365 bool deleted = false;
366 int r = 0;
367
368 while ((dent = readdir(d))) {
369 struct stat s;
370 usec_t age;
371 _cleanup_free_ char *sub_path = NULL;
372
373 if (STR_IN_SET(dent->d_name, ".", ".."))
374 continue;
375
376 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
377 if (errno == ENOENT)
378 continue;
379
380 /* FUSE, NFS mounts, SELinux might return EACCES */
381 if (errno == EACCES)
382 log_debug_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name);
383 else
384 log_error_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name);
385 r = -errno;
386 continue;
387 }
388
389 /* Stay on the same filesystem */
390 if (s.st_dev != rootdev) {
391 log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name);
392 continue;
393 }
394
395 /* Try to detect bind mounts of the same filesystem instance; they
396 * do not differ in device major/minors. This type of query is not
397 * supported on all kernels or filesystem types though. */
398 if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0) {
399 log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.",
400 p, dent->d_name);
401 continue;
402 }
403
404 /* Do not delete read-only files owned by root */
405 if (s.st_uid == 0 && !(s.st_mode & S_IWUSR)) {
406 log_debug("Ignoring \"%s/%s\": read-only and owner by root.", p, dent->d_name);
407 continue;
408 }
409
410 sub_path = strjoin(p, "/", dent->d_name, NULL);
411 if (!sub_path) {
412 r = log_oom();
413 goto finish;
414 }
415
416 /* Is there an item configured for this path? */
417 if (ordered_hashmap_get(items, sub_path)) {
418 log_debug("Ignoring \"%s\": a separate entry exists.", sub_path);
419 continue;
420 }
421
422 if (find_glob(globs, sub_path)) {
423 log_debug("Ignoring \"%s\": a separate glob exists.", sub_path);
424 continue;
425 }
426
427 if (S_ISDIR(s.st_mode)) {
428
429 if (mountpoint &&
430 streq(dent->d_name, "lost+found") &&
431 s.st_uid == 0) {
432 log_debug("Ignoring \"%s\".", sub_path);
433 continue;
434 }
435
436 if (maxdepth <= 0)
437 log_warning("Reached max depth on \"%s\".", sub_path);
438 else {
439 _cleanup_closedir_ DIR *sub_dir;
440 int q;
441
442 sub_dir = xopendirat_nomod(dirfd(d), dent->d_name);
443 if (!sub_dir) {
444 if (errno != ENOENT)
445 r = log_error_errno(errno, "opendir(%s) failed: %m", sub_path);
446
447 continue;
448 }
449
450 q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
451 if (q < 0)
452 r = q;
453 }
454
455 /* Note: if you are wondering why we don't
456 * support the sticky bit for excluding
457 * directories from cleaning like we do it for
458 * other file system objects: well, the sticky
459 * bit already has a meaning for directories,
460 * so we don't want to overload that. */
461
462 if (keep_this_level) {
463 log_debug("Keeping \"%s\".", sub_path);
464 continue;
465 }
466
467 /* Ignore ctime, we change it when deleting */
468 age = timespec_load(&s.st_mtim);
469 if (age >= cutoff) {
470 char a[FORMAT_TIMESTAMP_MAX];
471 /* Follows spelling in stat(1). */
472 log_debug("Directory \"%s\": modify time %s is too new.",
473 sub_path,
474 format_timestamp_us(a, sizeof(a), age));
475 continue;
476 }
477
478 age = timespec_load(&s.st_atim);
479 if (age >= cutoff) {
480 char a[FORMAT_TIMESTAMP_MAX];
481 log_debug("Directory \"%s\": access time %s is too new.",
482 sub_path,
483 format_timestamp_us(a, sizeof(a), age));
484 continue;
485 }
486
487 log_debug("Removing directory \"%s\".", sub_path);
488 if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0)
489 if (errno != ENOENT && errno != ENOTEMPTY) {
490 log_error_errno(errno, "rmdir(%s): %m", sub_path);
491 r = -errno;
492 }
493
494 } else {
495 /* Skip files for which the sticky bit is
496 * set. These are semantics we define, and are
497 * unknown elsewhere. See XDG_RUNTIME_DIR
498 * specification for details. */
499 if (s.st_mode & S_ISVTX) {
500 log_debug("Skipping \"%s\": sticky bit set.", sub_path);
501 continue;
502 }
503
504 if (mountpoint && S_ISREG(s.st_mode))
505 if (s.st_uid == 0 && STR_IN_SET(dent->d_name,
506 ".journal",
507 "aquota.user",
508 "aquota.group")) {
509 log_debug("Skipping \"%s\".", sub_path);
510 continue;
511 }
512
513 /* Ignore sockets that are listed in /proc/net/unix */
514 if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) {
515 log_debug("Skipping \"%s\": live socket.", sub_path);
516 continue;
517 }
518
519 /* Ignore device nodes */
520 if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) {
521 log_debug("Skipping \"%s\": a device.", sub_path);
522 continue;
523 }
524
525 /* Keep files on this level around if this is
526 * requested */
527 if (keep_this_level) {
528 log_debug("Keeping \"%s\".", sub_path);
529 continue;
530 }
531
532 age = timespec_load(&s.st_mtim);
533 if (age >= cutoff) {
534 char a[FORMAT_TIMESTAMP_MAX];
535 /* Follows spelling in stat(1). */
536 log_debug("File \"%s\": modify time %s is too new.",
537 sub_path,
538 format_timestamp_us(a, sizeof(a), age));
539 continue;
540 }
541
542 age = timespec_load(&s.st_atim);
543 if (age >= cutoff) {
544 char a[FORMAT_TIMESTAMP_MAX];
545 log_debug("File \"%s\": access time %s is too new.",
546 sub_path,
547 format_timestamp_us(a, sizeof(a), age));
548 continue;
549 }
550
551 age = timespec_load(&s.st_ctim);
552 if (age >= cutoff) {
553 char a[FORMAT_TIMESTAMP_MAX];
554 log_debug("File \"%s\": change time %s is too new.",
555 sub_path,
556 format_timestamp_us(a, sizeof(a), age));
557 continue;
558 }
559
560 log_debug("unlink \"%s\"", sub_path);
561
562 if (unlinkat(dirfd(d), dent->d_name, 0) < 0)
563 if (errno != ENOENT)
564 r = log_error_errno(errno, "unlink(%s): %m", sub_path);
565
566 deleted = true;
567 }
568 }
569
570 finish:
571 if (deleted) {
572 usec_t age1, age2;
573 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
574
575 /* Restore original directory timestamps */
576 times[0] = ds->st_atim;
577 times[1] = ds->st_mtim;
578
579 age1 = timespec_load(&ds->st_atim);
580 age2 = timespec_load(&ds->st_mtim);
581 log_debug("Restoring access and modification time on \"%s\": %s, %s",
582 p,
583 format_timestamp_us(a, sizeof(a), age1),
584 format_timestamp_us(b, sizeof(b), age2));
585 if (futimens(dirfd(d), times) < 0)
586 log_error_errno(errno, "utimensat(%s): %m", p);
587 }
588
589 return r;
590 }
591
592 static int path_set_perms(Item *i, const char *path) {
593 _cleanup_close_ int fd = -1;
594 struct stat st;
595
596 assert(i);
597 assert(path);
598
599 /* We open the file with O_PATH here, to make the operation
600 * somewhat atomic. Also there's unfortunately no fchmodat()
601 * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via
602 * O_PATH. */
603
604 fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME);
605 if (fd < 0)
606 return log_error_errno(errno, "Adjusting owner and mode for %s failed: %m", path);
607
608 if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
609 return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
610
611 if (S_ISLNK(st.st_mode))
612 log_debug("Skipping mode an owner fix for symlink %s.", path);
613 else {
614 char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
615 xsprintf(fn, "/proc/self/fd/%i", fd);
616
617 /* not using i->path directly because it may be a glob */
618 if (i->mode_set) {
619 mode_t m = i->mode;
620
621 if (i->mask_perms) {
622 if (!(st.st_mode & 0111))
623 m &= ~0111;
624 if (!(st.st_mode & 0222))
625 m &= ~0222;
626 if (!(st.st_mode & 0444))
627 m &= ~0444;
628 if (!S_ISDIR(st.st_mode))
629 m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
630 }
631
632 if (m == (st.st_mode & 07777))
633 log_debug("\"%s\" has right mode %o", path, st.st_mode);
634 else {
635 log_debug("chmod \"%s\" to mode %o", path, m);
636 if (chmod(fn, m) < 0)
637 return log_error_errno(errno, "chmod(%s) failed: %m", path);
638 }
639 }
640
641 if ((i->uid != st.st_uid || i->gid != st.st_gid) &&
642 (i->uid_set || i->gid_set)) {
643 log_debug("chown \"%s\" to "UID_FMT"."GID_FMT,
644 path,
645 i->uid_set ? i->uid : UID_INVALID,
646 i->gid_set ? i->gid : GID_INVALID);
647 if (chown(fn,
648 i->uid_set ? i->uid : UID_INVALID,
649 i->gid_set ? i->gid : GID_INVALID) < 0)
650 return log_error_errno(errno, "chown(%s) failed: %m", path);
651 }
652 }
653
654 fd = safe_close(fd);
655
656 return label_fix(path, false, false);
657 }
658
659 static int parse_xattrs_from_arg(Item *i) {
660 const char *p;
661 int r;
662
663 assert(i);
664 assert(i->argument);
665
666 p = i->argument;
667
668 for (;;) {
669 _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL, *xattr_replaced = NULL;
670
671 r = extract_first_word(&p, &xattr, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
672 if (r < 0)
673 log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p);
674 if (r <= 0)
675 break;
676
677 r = specifier_printf(xattr, specifier_table, NULL, &xattr_replaced);
678 if (r < 0)
679 return log_error_errno(r, "Failed to replace specifiers in extended attribute '%s': %m", xattr);
680
681 r = split_pair(xattr_replaced, "=", &name, &value);
682 if (r < 0) {
683 log_warning_errno(r, "Failed to parse extended attribute, ignoring: %s", xattr);
684 continue;
685 }
686
687 if (isempty(name) || isempty(value)) {
688 log_warning("Malformed extended attribute found, ignoring: %s", xattr);
689 continue;
690 }
691
692 if (strv_push_pair(&i->xattrs, name, value) < 0)
693 return log_oom();
694
695 name = value = NULL;
696 }
697
698 return 0;
699 }
700
701 static int path_set_xattrs(Item *i, const char *path) {
702 char **name, **value;
703
704 assert(i);
705 assert(path);
706
707 STRV_FOREACH_PAIR(name, value, i->xattrs) {
708 int n;
709
710 n = strlen(*value);
711 log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path);
712 if (lsetxattr(path, *name, *value, n, 0) < 0) {
713 log_error("Setting extended attribute %s=%s on %s failed: %m", *name, *value, path);
714 return -errno;
715 }
716 }
717 return 0;
718 }
719
720 static int parse_acls_from_arg(Item *item) {
721 #ifdef HAVE_ACL
722 int r;
723
724 assert(item);
725
726 /* If force (= modify) is set, we will not modify the acl
727 * afterwards, so the mask can be added now if necessary. */
728
729 r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force);
730 if (r < 0)
731 log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring", item->argument);
732 #else
733 log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring");
734 #endif
735
736 return 0;
737 }
738
739 #ifdef HAVE_ACL
740 static int path_set_acl(const char *path, const char *pretty, acl_type_t type, acl_t acl, bool modify) {
741 _cleanup_(acl_free_charpp) char *t = NULL;
742 _cleanup_(acl_freep) acl_t dup = NULL;
743 int r;
744
745 /* Returns 0 for success, positive error if already warned,
746 * negative error otherwise. */
747
748 if (modify) {
749 r = acls_for_file(path, type, acl, &dup);
750 if (r < 0)
751 return r;
752
753 r = calc_acl_mask_if_needed(&dup);
754 if (r < 0)
755 return r;
756 } else {
757 dup = acl_dup(acl);
758 if (!dup)
759 return -errno;
760
761 /* the mask was already added earlier if needed */
762 }
763
764 r = add_base_acls_if_needed(&dup, path);
765 if (r < 0)
766 return r;
767
768 t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE);
769 log_debug("Setting %s ACL %s on %s.",
770 type == ACL_TYPE_ACCESS ? "access" : "default",
771 strna(t), pretty);
772
773 r = acl_set_file(path, type, dup);
774 if (r < 0)
775 /* Return positive to indicate we already warned */
776 return -log_error_errno(errno,
777 "Setting %s ACL \"%s\" on %s failed: %m",
778 type == ACL_TYPE_ACCESS ? "access" : "default",
779 strna(t), pretty);
780
781 return 0;
782 }
783 #endif
784
785 static int path_set_acls(Item *item, const char *path) {
786 int r = 0;
787 #ifdef HAVE_ACL
788 char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
789 _cleanup_close_ int fd = -1;
790 struct stat st;
791
792 assert(item);
793 assert(path);
794
795 fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME);
796 if (fd < 0)
797 return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path);
798
799 if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
800 return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
801
802 if (S_ISLNK(st.st_mode)) {
803 log_debug("Skipping ACL fix for symlink %s.", path);
804 return 0;
805 }
806
807 xsprintf(fn, "/proc/self/fd/%i", fd);
808
809 if (item->acl_access)
810 r = path_set_acl(fn, path, ACL_TYPE_ACCESS, item->acl_access, item->force);
811
812 if (r == 0 && item->acl_default)
813 r = path_set_acl(fn, path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
814
815 if (r > 0)
816 return -r; /* already warned */
817 else if (r == -EOPNOTSUPP) {
818 log_debug_errno(r, "ACLs not supported by file system at %s", path);
819 return 0;
820 } else if (r < 0)
821 log_error_errno(r, "ACL operation on \"%s\" failed: %m", path);
822 #endif
823 return r;
824 }
825
826 #define ATTRIBUTES_ALL \
827 (FS_NOATIME_FL | \
828 FS_SYNC_FL | \
829 FS_DIRSYNC_FL | \
830 FS_APPEND_FL | \
831 FS_COMPR_FL | \
832 FS_NODUMP_FL | \
833 FS_EXTENT_FL | \
834 FS_IMMUTABLE_FL | \
835 FS_JOURNAL_DATA_FL | \
836 FS_SECRM_FL | \
837 FS_UNRM_FL | \
838 FS_NOTAIL_FL | \
839 FS_TOPDIR_FL | \
840 FS_NOCOW_FL)
841
842 static int parse_attribute_from_arg(Item *item) {
843
844 static const struct {
845 char character;
846 unsigned value;
847 } attributes[] = {
848 { 'A', FS_NOATIME_FL }, /* do not update atime */
849 { 'S', FS_SYNC_FL }, /* Synchronous updates */
850 { 'D', FS_DIRSYNC_FL }, /* dirsync behaviour (directories only) */
851 { 'a', FS_APPEND_FL }, /* writes to file may only append */
852 { 'c', FS_COMPR_FL }, /* Compress file */
853 { 'd', FS_NODUMP_FL }, /* do not dump file */
854 { 'e', FS_EXTENT_FL }, /* Top of directory hierarchies*/
855 { 'i', FS_IMMUTABLE_FL }, /* Immutable file */
856 { 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */
857 { 's', FS_SECRM_FL }, /* Secure deletion */
858 { 'u', FS_UNRM_FL }, /* Undelete */
859 { 't', FS_NOTAIL_FL }, /* file tail should not be merged */
860 { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies*/
861 { 'C', FS_NOCOW_FL }, /* Do not cow file */
862 };
863
864 enum {
865 MODE_ADD,
866 MODE_DEL,
867 MODE_SET
868 } mode = MODE_ADD;
869
870 unsigned value = 0, mask = 0;
871 const char *p;
872
873 assert(item);
874
875 p = item->argument;
876 if (p) {
877 if (*p == '+') {
878 mode = MODE_ADD;
879 p++;
880 } else if (*p == '-') {
881 mode = MODE_DEL;
882 p++;
883 } else if (*p == '=') {
884 mode = MODE_SET;
885 p++;
886 }
887 }
888
889 if (isempty(p) && mode != MODE_SET) {
890 log_error("Setting file attribute on '%s' needs an attribute specification.", item->path);
891 return -EINVAL;
892 }
893
894 for (; p && *p ; p++) {
895 unsigned i, v;
896
897 for (i = 0; i < ELEMENTSOF(attributes); i++)
898 if (*p == attributes[i].character)
899 break;
900
901 if (i >= ELEMENTSOF(attributes)) {
902 log_error("Unknown file attribute '%c' on '%s'.", *p, item->path);
903 return -EINVAL;
904 }
905
906 v = attributes[i].value;
907
908 if (mode == MODE_ADD || mode == MODE_SET)
909 value |= v;
910 else
911 value &= ~v;
912
913 mask |= v;
914 }
915
916 if (mode == MODE_SET)
917 mask |= ATTRIBUTES_ALL;
918
919 assert(mask != 0);
920
921 item->attribute_mask = mask;
922 item->attribute_value = value;
923 item->attribute_set = true;
924
925 return 0;
926 }
927
928 static int path_set_attribute(Item *item, const char *path) {
929 _cleanup_close_ int fd = -1;
930 struct stat st;
931 unsigned f;
932 int r;
933
934 if (!item->attribute_set || item->attribute_mask == 0)
935 return 0;
936
937 fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOATIME|O_NOFOLLOW);
938 if (fd < 0) {
939 if (errno == ELOOP)
940 return log_error_errno(errno, "Skipping file attributes adjustment on symlink %s.", path);
941
942 return log_error_errno(errno, "Cannot open '%s': %m", path);
943 }
944
945 if (fstat(fd, &st) < 0)
946 return log_error_errno(errno, "Cannot stat '%s': %m", path);
947
948 /* Issuing the file attribute ioctls on device nodes is not
949 * safe, as that will be delivered to the drivers, not the
950 * file system containing the device node. */
951 if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) {
952 log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path);
953 return -EINVAL;
954 }
955
956 f = item->attribute_value & item->attribute_mask;
957
958 /* Mask away directory-specific flags */
959 if (!S_ISDIR(st.st_mode))
960 f &= ~FS_DIRSYNC_FL;
961
962 r = chattr_fd(fd, f, item->attribute_mask);
963 if (r < 0)
964 log_full_errno(r == -ENOTTY ? LOG_DEBUG : LOG_WARNING,
965 r,
966 "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
967 path, item->attribute_value, item->attribute_mask);
968
969 return 0;
970 }
971
972 static int write_one_file(Item *i, const char *path) {
973 _cleanup_close_ int fd = -1;
974 int flags, r = 0;
975 struct stat st;
976
977 assert(i);
978 assert(path);
979
980 flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW :
981 i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0;
982
983 RUN_WITH_UMASK(0000) {
984 mac_selinux_create_file_prepare(path, S_IFREG);
985 fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
986 mac_selinux_create_file_clear();
987 }
988
989 if (fd < 0) {
990 if (i->type == WRITE_FILE && errno == ENOENT) {
991 log_debug_errno(errno, "Not writing \"%s\": %m", path);
992 return 0;
993 }
994
995 r = -errno;
996 if (!i->argument && errno == EROFS && stat(path, &st) == 0 &&
997 (i->type == CREATE_FILE || st.st_size == 0))
998 goto check_mode;
999
1000 return log_error_errno(r, "Failed to create file %s: %m", path);
1001 }
1002
1003 if (i->argument) {
1004 _cleanup_free_ char *unescaped = NULL, *replaced = NULL;
1005
1006 log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path);
1007
1008 r = cunescape(i->argument, 0, &unescaped);
1009 if (r < 0)
1010 return log_error_errno(r, "Failed to unescape parameter to write: %s", i->argument);
1011
1012 r = specifier_printf(unescaped, specifier_table, NULL, &replaced);
1013 if (r < 0)
1014 return log_error_errno(r, "Failed to replace specifiers in parameter to write '%s': %m", unescaped);
1015
1016 r = loop_write(fd, replaced, strlen(replaced), false);
1017 if (r < 0)
1018 return log_error_errno(r, "Failed to write file \"%s\": %m", path);
1019 } else
1020 log_debug("\"%s\" has been created.", path);
1021
1022 fd = safe_close(fd);
1023
1024 if (stat(path, &st) < 0)
1025 return log_error_errno(errno, "stat(%s) failed: %m", path);
1026
1027 check_mode:
1028 if (!S_ISREG(st.st_mode)) {
1029 log_error("%s is not a file.", path);
1030 return -EEXIST;
1031 }
1032
1033 r = path_set_perms(i, path);
1034 if (r < 0)
1035 return r;
1036
1037 return 0;
1038 }
1039
1040 typedef int (*action_t)(Item *, const char *);
1041
1042 static int item_do_children(Item *i, const char *path, action_t action) {
1043 _cleanup_closedir_ DIR *d;
1044 int r = 0;
1045
1046 assert(i);
1047 assert(path);
1048
1049 /* This returns the first error we run into, but nevertheless
1050 * tries to go on */
1051
1052 d = opendir_nomod(path);
1053 if (!d)
1054 return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
1055
1056 for (;;) {
1057 _cleanup_free_ char *p = NULL;
1058 struct dirent *de;
1059 int q;
1060
1061 errno = 0;
1062 de = readdir(d);
1063 if (!de) {
1064 if (errno != 0 && r == 0)
1065 r = -errno;
1066
1067 break;
1068 }
1069
1070 if (STR_IN_SET(de->d_name, ".", ".."))
1071 continue;
1072
1073 p = strjoin(path, "/", de->d_name, NULL);
1074 if (!p)
1075 return -ENOMEM;
1076
1077 q = action(i, p);
1078 if (q < 0 && q != -ENOENT && r == 0)
1079 r = q;
1080
1081 if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) {
1082 q = item_do_children(i, p, action);
1083 if (q < 0 && r == 0)
1084 r = q;
1085 }
1086 }
1087
1088 return r;
1089 }
1090
1091 static int glob_item(Item *i, action_t action, bool recursive) {
1092 _cleanup_globfree_ glob_t g = {
1093 .gl_closedir = (void (*)(void *)) closedir,
1094 .gl_readdir = (struct dirent *(*)(void *)) readdir,
1095 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
1096 .gl_lstat = lstat,
1097 .gl_stat = stat,
1098 };
1099 int r = 0, k;
1100 char **fn;
1101
1102 errno = 0;
1103 k = glob(i->path, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g);
1104 if (k != 0 && k != GLOB_NOMATCH)
1105 return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path);
1106
1107 STRV_FOREACH(fn, g.gl_pathv) {
1108 k = action(i, *fn);
1109 if (k < 0 && r == 0)
1110 r = k;
1111
1112 if (recursive) {
1113 k = item_do_children(i, *fn, action);
1114 if (k < 0 && r == 0)
1115 r = k;
1116 }
1117 }
1118
1119 return r;
1120 }
1121
1122 typedef enum {
1123 CREATION_NORMAL,
1124 CREATION_EXISTING,
1125 CREATION_FORCE,
1126 _CREATION_MODE_MAX,
1127 _CREATION_MODE_INVALID = -1
1128 } CreationMode;
1129
1130 static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = {
1131 [CREATION_NORMAL] = "Created",
1132 [CREATION_EXISTING] = "Found existing",
1133 [CREATION_FORCE] = "Created replacement",
1134 };
1135
1136 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
1137
1138 static int create_item(Item *i) {
1139 _cleanup_free_ char *resolved = NULL;
1140 struct stat st;
1141 int r = 0;
1142 CreationMode creation;
1143
1144 assert(i);
1145
1146 log_debug("Running create action for entry %c %s", (char) i->type, i->path);
1147
1148 switch (i->type) {
1149
1150 case IGNORE_PATH:
1151 case IGNORE_DIRECTORY_PATH:
1152 case REMOVE_PATH:
1153 case RECURSIVE_REMOVE_PATH:
1154 return 0;
1155
1156 case CREATE_FILE:
1157 case TRUNCATE_FILE:
1158 r = write_one_file(i, i->path);
1159 if (r < 0)
1160 return r;
1161 break;
1162
1163 case COPY_FILES: {
1164 r = specifier_printf(i->argument, specifier_table, NULL, &resolved);
1165 if (r < 0)
1166 return log_error_errno(r, "Failed to substitute specifiers in copy source %s: %m", i->argument);
1167
1168 log_debug("Copying tree \"%s\" to \"%s\".", resolved, i->path);
1169 r = copy_tree(resolved, i->path, false);
1170
1171 if (r == -EROFS && stat(i->path, &st) == 0)
1172 r = -EEXIST;
1173
1174 if (r < 0) {
1175 struct stat a, b;
1176
1177 if (r != -EEXIST)
1178 return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
1179
1180 if (stat(resolved, &a) < 0)
1181 return log_error_errno(errno, "stat(%s) failed: %m", resolved);
1182
1183 if (stat(i->path, &b) < 0)
1184 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
1185
1186 if ((a.st_mode ^ b.st_mode) & S_IFMT) {
1187 log_debug("Can't copy to %s, file exists already and is of different type", i->path);
1188 return 0;
1189 }
1190 }
1191
1192 r = path_set_perms(i, i->path);
1193 if (r < 0)
1194 return r;
1195
1196 break;
1197
1198 case WRITE_FILE:
1199 r = glob_item(i, write_one_file, false);
1200 if (r < 0)
1201 return r;
1202
1203 break;
1204
1205 case CREATE_DIRECTORY:
1206 case TRUNCATE_DIRECTORY:
1207 case CREATE_SUBVOLUME:
1208 case CREATE_SUBVOLUME_INHERIT_QUOTA:
1209 case CREATE_SUBVOLUME_NEW_QUOTA:
1210
1211 RUN_WITH_UMASK(0000)
1212 mkdir_parents_label(i->path, 0755);
1213
1214 if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) {
1215 RUN_WITH_UMASK((~i->mode) & 0777)
1216 r = btrfs_subvol_make(i->path);
1217 } else
1218 r = 0;
1219
1220 if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY)
1221 RUN_WITH_UMASK(0000)
1222 r = mkdir_label(i->path, i->mode);
1223
1224 if (r < 0) {
1225 int k;
1226
1227 if (r != -EEXIST && r != -EROFS)
1228 return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path);
1229
1230 k = is_dir(i->path, false);
1231 if (k == -ENOENT && r == -EROFS)
1232 return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", i->path);
1233 if (k < 0)
1234 return log_error_errno(k, "Failed to check if %s exists: %m", i->path);
1235 if (!k) {
1236 log_warning("\"%s\" already exists and is not a directory.", i->path);
1237 return 0;
1238 }
1239
1240 creation = CREATION_EXISTING;
1241 } else
1242 creation = CREATION_NORMAL;
1243
1244 log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path);
1245
1246 if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
1247 r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
1248 if (r == -ENOTTY) {
1249 log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of unsupported file system or because directory is not a subvolume: %m", i->path);
1250 return 0;
1251 }
1252 if (r == -EROFS) {
1253 log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of read-only file system: %m", i->path);
1254 return 0;
1255 }
1256 if (r < 0)
1257 return log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
1258 if (r > 0)
1259 log_debug("Adjusted quota for subvolume \"%s\".", i->path);
1260 if (r == 0)
1261 log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path);
1262 }
1263
1264 r = path_set_perms(i, i->path);
1265 if (r < 0)
1266 return r;
1267
1268 break;
1269
1270 case CREATE_FIFO:
1271
1272 RUN_WITH_UMASK(0000) {
1273 mac_selinux_create_file_prepare(i->path, S_IFIFO);
1274 r = mkfifo(i->path, i->mode);
1275 mac_selinux_create_file_clear();
1276 }
1277
1278 if (r < 0) {
1279 if (errno != EEXIST)
1280 return log_error_errno(errno, "Failed to create fifo %s: %m", i->path);
1281
1282 if (lstat(i->path, &st) < 0)
1283 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
1284
1285 if (!S_ISFIFO(st.st_mode)) {
1286
1287 if (i->force) {
1288 RUN_WITH_UMASK(0000) {
1289 mac_selinux_create_file_prepare(i->path, S_IFIFO);
1290 r = mkfifo_atomic(i->path, i->mode);
1291 mac_selinux_create_file_clear();
1292 }
1293
1294 if (r < 0)
1295 return log_error_errno(r, "Failed to create fifo %s: %m", i->path);
1296 creation = CREATION_FORCE;
1297 } else {
1298 log_warning("\"%s\" already exists and is not a fifo.", i->path);
1299 return 0;
1300 }
1301 } else
1302 creation = CREATION_EXISTING;
1303 } else
1304 creation = CREATION_NORMAL;
1305 log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path);
1306
1307 r = path_set_perms(i, i->path);
1308 if (r < 0)
1309 return r;
1310
1311 break;
1312 }
1313
1314 case CREATE_SYMLINK: {
1315 r = specifier_printf(i->argument, specifier_table, NULL, &resolved);
1316 if (r < 0)
1317 return log_error_errno(r, "Failed to substitute specifiers in symlink target %s: %m", i->argument);
1318
1319 mac_selinux_create_file_prepare(i->path, S_IFLNK);
1320 r = symlink(resolved, i->path);
1321 mac_selinux_create_file_clear();
1322
1323 if (r < 0) {
1324 _cleanup_free_ char *x = NULL;
1325
1326 if (errno != EEXIST)
1327 return log_error_errno(errno, "symlink(%s, %s) failed: %m", resolved, i->path);
1328
1329 r = readlink_malloc(i->path, &x);
1330 if (r < 0 || !streq(resolved, x)) {
1331
1332 if (i->force) {
1333 mac_selinux_create_file_prepare(i->path, S_IFLNK);
1334 r = symlink_atomic(resolved, i->path);
1335 mac_selinux_create_file_clear();
1336
1337 if (r < 0)
1338 return log_error_errno(r, "symlink(%s, %s) failed: %m", resolved, i->path);
1339
1340 creation = CREATION_FORCE;
1341 } else {
1342 log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path);
1343 return 0;
1344 }
1345 } else
1346 creation = CREATION_EXISTING;
1347 } else
1348
1349 creation = CREATION_NORMAL;
1350 log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path);
1351 break;
1352 }
1353
1354 case CREATE_BLOCK_DEVICE:
1355 case CREATE_CHAR_DEVICE: {
1356 mode_t file_type;
1357
1358 if (have_effective_cap(CAP_MKNOD) == 0) {
1359 /* In a container we lack CAP_MKNOD. We
1360 shouldn't attempt to create the device node in
1361 that case to avoid noise, and we don't support
1362 virtualized devices in containers anyway. */
1363
1364 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
1365 return 0;
1366 }
1367
1368 file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR;
1369
1370 RUN_WITH_UMASK(0000) {
1371 mac_selinux_create_file_prepare(i->path, file_type);
1372 r = mknod(i->path, i->mode | file_type, i->major_minor);
1373 mac_selinux_create_file_clear();
1374 }
1375
1376 if (r < 0) {
1377 if (errno == EPERM) {
1378 log_debug("We lack permissions, possibly because of cgroup configuration; "
1379 "skipping creation of device node %s.", i->path);
1380 return 0;
1381 }
1382
1383 if (errno != EEXIST)
1384 return log_error_errno(errno, "Failed to create device node %s: %m", i->path);
1385
1386 if (lstat(i->path, &st) < 0)
1387 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
1388
1389 if ((st.st_mode & S_IFMT) != file_type) {
1390
1391 if (i->force) {
1392
1393 RUN_WITH_UMASK(0000) {
1394 mac_selinux_create_file_prepare(i->path, file_type);
1395 r = mknod_atomic(i->path, i->mode | file_type, i->major_minor);
1396 mac_selinux_create_file_clear();
1397 }
1398
1399 if (r < 0)
1400 return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
1401 creation = CREATION_FORCE;
1402 } else {
1403 log_debug("%s is not a device node.", i->path);
1404 return 0;
1405 }
1406 } else
1407 creation = CREATION_EXISTING;
1408 } else
1409 creation = CREATION_NORMAL;
1410
1411 log_debug("%s %s device node \"%s\" %u:%u.",
1412 creation_mode_verb_to_string(creation),
1413 i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
1414 i->path, major(i->mode), minor(i->mode));
1415
1416 r = path_set_perms(i, i->path);
1417 if (r < 0)
1418 return r;
1419
1420 break;
1421 }
1422
1423 case ADJUST_MODE:
1424 case RELABEL_PATH:
1425 r = glob_item(i, path_set_perms, false);
1426 if (r < 0)
1427 return r;
1428 break;
1429
1430 case RECURSIVE_RELABEL_PATH:
1431 r = glob_item(i, path_set_perms, true);
1432 if (r < 0)
1433 return r;
1434 break;
1435
1436 case SET_XATTR:
1437 r = glob_item(i, path_set_xattrs, false);
1438 if (r < 0)
1439 return r;
1440 break;
1441
1442 case RECURSIVE_SET_XATTR:
1443 r = glob_item(i, path_set_xattrs, true);
1444 if (r < 0)
1445 return r;
1446 break;
1447
1448 case SET_ACL:
1449 r = glob_item(i, path_set_acls, false);
1450 if (r < 0)
1451 return r;
1452 break;
1453
1454 case RECURSIVE_SET_ACL:
1455 r = glob_item(i, path_set_acls, true);
1456 if (r < 0)
1457 return r;
1458 break;
1459
1460 case SET_ATTRIBUTE:
1461 r = glob_item(i, path_set_attribute, false);
1462 if (r < 0)
1463 return r;
1464 break;
1465
1466 case RECURSIVE_SET_ATTRIBUTE:
1467 r = glob_item(i, path_set_attribute, true);
1468 if (r < 0)
1469 return r;
1470 break;
1471 }
1472
1473 return 0;
1474 }
1475
1476 static int remove_item_instance(Item *i, const char *instance) {
1477 int r;
1478
1479 assert(i);
1480
1481 switch (i->type) {
1482
1483 case REMOVE_PATH:
1484 if (remove(instance) < 0 && errno != ENOENT)
1485 return log_error_errno(errno, "rm(%s): %m", instance);
1486
1487 break;
1488
1489 case TRUNCATE_DIRECTORY:
1490 case RECURSIVE_REMOVE_PATH:
1491 /* FIXME: we probably should use dir_cleanup() here
1492 * instead of rm_rf() so that 'x' is honoured. */
1493 log_debug("rm -rf \"%s\"", instance);
1494 r = rm_rf(instance, (i->type == RECURSIVE_REMOVE_PATH ? REMOVE_ROOT|REMOVE_SUBVOLUME : 0) | REMOVE_PHYSICAL);
1495 if (r < 0 && r != -ENOENT)
1496 return log_error_errno(r, "rm_rf(%s): %m", instance);
1497
1498 break;
1499
1500 default:
1501 assert_not_reached("wut?");
1502 }
1503
1504 return 0;
1505 }
1506
1507 static int remove_item(Item *i) {
1508 int r = 0;
1509
1510 assert(i);
1511
1512 log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
1513
1514 switch (i->type) {
1515
1516 case CREATE_FILE:
1517 case TRUNCATE_FILE:
1518 case CREATE_DIRECTORY:
1519 case CREATE_SUBVOLUME:
1520 case CREATE_SUBVOLUME_INHERIT_QUOTA:
1521 case CREATE_SUBVOLUME_NEW_QUOTA:
1522 case CREATE_FIFO:
1523 case CREATE_SYMLINK:
1524 case CREATE_CHAR_DEVICE:
1525 case CREATE_BLOCK_DEVICE:
1526 case IGNORE_PATH:
1527 case IGNORE_DIRECTORY_PATH:
1528 case ADJUST_MODE:
1529 case RELABEL_PATH:
1530 case RECURSIVE_RELABEL_PATH:
1531 case WRITE_FILE:
1532 case COPY_FILES:
1533 case SET_XATTR:
1534 case RECURSIVE_SET_XATTR:
1535 case SET_ACL:
1536 case RECURSIVE_SET_ACL:
1537 case SET_ATTRIBUTE:
1538 case RECURSIVE_SET_ATTRIBUTE:
1539 break;
1540
1541 case REMOVE_PATH:
1542 case TRUNCATE_DIRECTORY:
1543 case RECURSIVE_REMOVE_PATH:
1544 r = glob_item(i, remove_item_instance, false);
1545 break;
1546 }
1547
1548 return r;
1549 }
1550
1551 static int clean_item_instance(Item *i, const char* instance) {
1552 _cleanup_closedir_ DIR *d = NULL;
1553 struct stat s, ps;
1554 bool mountpoint;
1555 usec_t cutoff, n;
1556 char timestamp[FORMAT_TIMESTAMP_MAX];
1557
1558 assert(i);
1559
1560 if (!i->age_set)
1561 return 0;
1562
1563 n = now(CLOCK_REALTIME);
1564 if (n < i->age)
1565 return 0;
1566
1567 cutoff = n - i->age;
1568
1569 d = opendir_nomod(instance);
1570 if (!d) {
1571 if (errno == ENOENT || errno == ENOTDIR) {
1572 log_debug_errno(errno, "Directory \"%s\": %m", instance);
1573 return 0;
1574 }
1575
1576 log_error_errno(errno, "Failed to open directory %s: %m", instance);
1577 return -errno;
1578 }
1579
1580 if (fstat(dirfd(d), &s) < 0)
1581 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
1582
1583 if (!S_ISDIR(s.st_mode)) {
1584 log_error("%s is not a directory.", i->path);
1585 return -ENOTDIR;
1586 }
1587
1588 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
1589 return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
1590
1591 mountpoint = s.st_dev != ps.st_dev || s.st_ino == ps.st_ino;
1592
1593 log_debug("Cleanup threshold for %s \"%s\" is %s",
1594 mountpoint ? "mount point" : "directory",
1595 instance,
1596 format_timestamp_us(timestamp, sizeof(timestamp), cutoff));
1597
1598 return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
1599 MAX_DEPTH, i->keep_first_level);
1600 }
1601
1602 static int clean_item(Item *i) {
1603 int r = 0;
1604
1605 assert(i);
1606
1607 log_debug("Running clean action for entry %c %s", (char) i->type, i->path);
1608
1609 switch (i->type) {
1610 case CREATE_DIRECTORY:
1611 case CREATE_SUBVOLUME:
1612 case CREATE_SUBVOLUME_INHERIT_QUOTA:
1613 case CREATE_SUBVOLUME_NEW_QUOTA:
1614 case TRUNCATE_DIRECTORY:
1615 case IGNORE_PATH:
1616 case COPY_FILES:
1617 clean_item_instance(i, i->path);
1618 break;
1619 case IGNORE_DIRECTORY_PATH:
1620 r = glob_item(i, clean_item_instance, false);
1621 break;
1622 default:
1623 break;
1624 }
1625
1626 return r;
1627 }
1628
1629 static int process_item_array(ItemArray *array);
1630
1631 static int process_item(Item *i) {
1632 int r, q, p, t = 0;
1633 _cleanup_free_ char *prefix = NULL;
1634
1635 assert(i);
1636
1637 if (i->done)
1638 return 0;
1639
1640 i->done = true;
1641
1642 prefix = malloc(strlen(i->path) + 1);
1643 if (!prefix)
1644 return log_oom();
1645
1646 PATH_FOREACH_PREFIX(prefix, i->path) {
1647 ItemArray *j;
1648
1649 j = ordered_hashmap_get(items, prefix);
1650 if (j) {
1651 int s;
1652
1653 s = process_item_array(j);
1654 if (s < 0 && t == 0)
1655 t = s;
1656 }
1657 }
1658
1659 r = arg_create ? create_item(i) : 0;
1660 q = arg_remove ? remove_item(i) : 0;
1661 p = arg_clean ? clean_item(i) : 0;
1662
1663 return t < 0 ? t :
1664 r < 0 ? r :
1665 q < 0 ? q :
1666 p;
1667 }
1668
1669 static int process_item_array(ItemArray *array) {
1670 unsigned n;
1671 int r = 0, k;
1672
1673 assert(array);
1674
1675 for (n = 0; n < array->count; n++) {
1676 k = process_item(array->items + n);
1677 if (k < 0 && r == 0)
1678 r = k;
1679 }
1680
1681 return r;
1682 }
1683
1684 static void item_free_contents(Item *i) {
1685 assert(i);
1686 free(i->path);
1687 free(i->argument);
1688 strv_free(i->xattrs);
1689
1690 #ifdef HAVE_ACL
1691 acl_free(i->acl_access);
1692 acl_free(i->acl_default);
1693 #endif
1694 }
1695
1696 static void item_array_free(ItemArray *a) {
1697 unsigned n;
1698
1699 if (!a)
1700 return;
1701
1702 for (n = 0; n < a->count; n++)
1703 item_free_contents(a->items + n);
1704 free(a->items);
1705 free(a);
1706 }
1707
1708 static int item_compare(const void *a, const void *b) {
1709 const Item *x = a, *y = b;
1710
1711 /* Make sure that the ownership taking item is put first, so
1712 * that we first create the node, and then can adjust it */
1713
1714 if (takes_ownership(x->type) && !takes_ownership(y->type))
1715 return -1;
1716 if (!takes_ownership(x->type) && takes_ownership(y->type))
1717 return 1;
1718
1719 return (int) x->type - (int) y->type;
1720 }
1721
1722 static bool item_compatible(Item *a, Item *b) {
1723 assert(a);
1724 assert(b);
1725 assert(streq(a->path, b->path));
1726
1727 if (takes_ownership(a->type) && takes_ownership(b->type))
1728 /* check if the items are the same */
1729 return streq_ptr(a->argument, b->argument) &&
1730
1731 a->uid_set == b->uid_set &&
1732 a->uid == b->uid &&
1733
1734 a->gid_set == b->gid_set &&
1735 a->gid == b->gid &&
1736
1737 a->mode_set == b->mode_set &&
1738 a->mode == b->mode &&
1739
1740 a->age_set == b->age_set &&
1741 a->age == b->age &&
1742
1743 a->mask_perms == b->mask_perms &&
1744
1745 a->keep_first_level == b->keep_first_level &&
1746
1747 a->major_minor == b->major_minor;
1748
1749 return true;
1750 }
1751
1752 static bool should_include_path(const char *path) {
1753 char **prefix;
1754
1755 STRV_FOREACH(prefix, arg_exclude_prefixes)
1756 if (path_startswith(path, *prefix)) {
1757 log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
1758 path, *prefix);
1759 return false;
1760 }
1761
1762 STRV_FOREACH(prefix, arg_include_prefixes)
1763 if (path_startswith(path, *prefix)) {
1764 log_debug("Entry \"%s\" matches include prefix \"%s\".", path, *prefix);
1765 return true;
1766 }
1767
1768 /* no matches, so we should include this path only if we
1769 * have no whitelist at all */
1770 if (strv_length(arg_include_prefixes) == 0)
1771 return true;
1772
1773 log_debug("Entry \"%s\" does not match any include prefix, skipping.", path);
1774 return false;
1775 }
1776
1777 static int parse_line(const char *fname, unsigned line, const char *buffer) {
1778
1779 _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
1780 _cleanup_(item_free_contents) Item i = {};
1781 ItemArray *existing;
1782 OrderedHashmap *h;
1783 int r, pos;
1784 bool force = false, boot = false;
1785
1786 assert(fname);
1787 assert(line >= 1);
1788 assert(buffer);
1789
1790 r = extract_many_words(
1791 &buffer,
1792 NULL,
1793 EXTRACT_QUOTES,
1794 &action,
1795 &path,
1796 &mode,
1797 &user,
1798 &group,
1799 &age,
1800 NULL);
1801 if (r < 0)
1802 return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line);
1803 else if (r < 2) {
1804 log_error("[%s:%u] Syntax error.", fname, line);
1805 return -EIO;
1806 }
1807
1808 if (!isempty(buffer) && !streq(buffer, "-")) {
1809 i.argument = strdup(buffer);
1810 if (!i.argument)
1811 return log_oom();
1812 }
1813
1814 if (isempty(action)) {
1815 log_error("[%s:%u] Command too short '%s'.", fname, line, action);
1816 return -EINVAL;
1817 }
1818
1819 for (pos = 1; action[pos]; pos++) {
1820 if (action[pos] == '!' && !boot)
1821 boot = true;
1822 else if (action[pos] == '+' && !force)
1823 force = true;
1824 else {
1825 log_error("[%s:%u] Unknown modifiers in command '%s'",
1826 fname, line, action);
1827 return -EINVAL;
1828 }
1829 }
1830
1831 if (boot && !arg_boot) {
1832 log_debug("Ignoring entry %s \"%s\" because --boot is not specified.",
1833 action, path);
1834 return 0;
1835 }
1836
1837 i.type = action[0];
1838 i.force = force;
1839
1840 r = specifier_printf(path, specifier_table, NULL, &i.path);
1841 if (r < 0) {
1842 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path);
1843 return r;
1844 }
1845
1846 switch (i.type) {
1847
1848 case CREATE_DIRECTORY:
1849 case CREATE_SUBVOLUME:
1850 case CREATE_SUBVOLUME_INHERIT_QUOTA:
1851 case CREATE_SUBVOLUME_NEW_QUOTA:
1852 case TRUNCATE_DIRECTORY:
1853 case CREATE_FIFO:
1854 case IGNORE_PATH:
1855 case IGNORE_DIRECTORY_PATH:
1856 case REMOVE_PATH:
1857 case RECURSIVE_REMOVE_PATH:
1858 case ADJUST_MODE:
1859 case RELABEL_PATH:
1860 case RECURSIVE_RELABEL_PATH:
1861 if (i.argument)
1862 log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname, line, i.type);
1863
1864 break;
1865
1866 case CREATE_FILE:
1867 case TRUNCATE_FILE:
1868 break;
1869
1870 case CREATE_SYMLINK:
1871 if (!i.argument) {
1872 i.argument = strappend("/usr/share/factory/", i.path);
1873 if (!i.argument)
1874 return log_oom();
1875 }
1876 break;
1877
1878 case WRITE_FILE:
1879 if (!i.argument) {
1880 log_error("[%s:%u] Write file requires argument.", fname, line);
1881 return -EBADMSG;
1882 }
1883 break;
1884
1885 case COPY_FILES:
1886 if (!i.argument) {
1887 i.argument = strappend("/usr/share/factory/", i.path);
1888 if (!i.argument)
1889 return log_oom();
1890 } else if (!path_is_absolute(i.argument)) {
1891 log_error("[%s:%u] Source path is not absolute.", fname, line);
1892 return -EBADMSG;
1893 }
1894
1895 path_kill_slashes(i.argument);
1896 break;
1897
1898 case CREATE_CHAR_DEVICE:
1899 case CREATE_BLOCK_DEVICE: {
1900 unsigned major, minor;
1901
1902 if (!i.argument) {
1903 log_error("[%s:%u] Device file requires argument.", fname, line);
1904 return -EBADMSG;
1905 }
1906
1907 if (sscanf(i.argument, "%u:%u", &major, &minor) != 2) {
1908 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument);
1909 return -EBADMSG;
1910 }
1911
1912 i.major_minor = makedev(major, minor);
1913 break;
1914 }
1915
1916 case SET_XATTR:
1917 case RECURSIVE_SET_XATTR:
1918 if (!i.argument) {
1919 log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
1920 return -EBADMSG;
1921 }
1922 r = parse_xattrs_from_arg(&i);
1923 if (r < 0)
1924 return r;
1925 break;
1926
1927 case SET_ACL:
1928 case RECURSIVE_SET_ACL:
1929 if (!i.argument) {
1930 log_error("[%s:%u] Set ACLs requires argument.", fname, line);
1931 return -EBADMSG;
1932 }
1933 r = parse_acls_from_arg(&i);
1934 if (r < 0)
1935 return r;
1936 break;
1937
1938 case SET_ATTRIBUTE:
1939 case RECURSIVE_SET_ATTRIBUTE:
1940 if (!i.argument) {
1941 log_error("[%s:%u] Set file attribute requires argument.", fname, line);
1942 return -EBADMSG;
1943 }
1944 r = parse_attribute_from_arg(&i);
1945 if (r < 0)
1946 return r;
1947 break;
1948
1949 default:
1950 log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type);
1951 return -EBADMSG;
1952 }
1953
1954 if (!path_is_absolute(i.path)) {
1955 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path);
1956 return -EBADMSG;
1957 }
1958
1959 path_kill_slashes(i.path);
1960
1961 if (!should_include_path(i.path))
1962 return 0;
1963
1964 if (arg_root) {
1965 char *p;
1966
1967 p = prefix_root(arg_root, i.path);
1968 if (!p)
1969 return log_oom();
1970
1971 free(i.path);
1972 i.path = p;
1973 }
1974
1975 if (!isempty(user) && !streq(user, "-")) {
1976 const char *u = user;
1977
1978 r = get_user_creds(&u, &i.uid, NULL, NULL, NULL);
1979 if (r < 0) {
1980 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
1981 return r;
1982 }
1983
1984 i.uid_set = true;
1985 }
1986
1987 if (!isempty(group) && !streq(group, "-")) {
1988 const char *g = group;
1989
1990 r = get_group_creds(&g, &i.gid);
1991 if (r < 0) {
1992 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
1993 return r;
1994 }
1995
1996 i.gid_set = true;
1997 }
1998
1999 if (!isempty(mode) && !streq(mode, "-")) {
2000 const char *mm = mode;
2001 unsigned m;
2002
2003 if (*mm == '~') {
2004 i.mask_perms = true;
2005 mm++;
2006 }
2007
2008 if (parse_mode(mm, &m) < 0) {
2009 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
2010 return -EBADMSG;
2011 }
2012
2013 i.mode = m;
2014 i.mode_set = true;
2015 } else
2016 i.mode = IN_SET(i.type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644;
2017
2018 if (!isempty(age) && !streq(age, "-")) {
2019 const char *a = age;
2020
2021 if (*a == '~') {
2022 i.keep_first_level = true;
2023 a++;
2024 }
2025
2026 if (parse_sec(a, &i.age) < 0) {
2027 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
2028 return -EBADMSG;
2029 }
2030
2031 i.age_set = true;
2032 }
2033
2034 h = needs_glob(i.type) ? globs : items;
2035
2036 existing = ordered_hashmap_get(h, i.path);
2037 if (existing) {
2038 unsigned n;
2039
2040 for (n = 0; n < existing->count; n++) {
2041 if (!item_compatible(existing->items + n, &i)) {
2042 log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.",
2043 fname, line, i.path);
2044 return 0;
2045 }
2046 }
2047 } else {
2048 existing = new0(ItemArray, 1);
2049 r = ordered_hashmap_put(h, i.path, existing);
2050 if (r < 0)
2051 return log_oom();
2052 }
2053
2054 if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1))
2055 return log_oom();
2056
2057 memcpy(existing->items + existing->count++, &i, sizeof(i));
2058
2059 /* Sort item array, to enforce stable ordering of application */
2060 qsort_safe(existing->items, existing->count, sizeof(Item), item_compare);
2061
2062 zero(i);
2063 return 0;
2064 }
2065
2066 static void help(void) {
2067 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
2068 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
2069 " -h --help Show this help\n"
2070 " --version Show package version\n"
2071 " --create Create marked files/directories\n"
2072 " --clean Clean up marked directories\n"
2073 " --remove Remove marked files/directories\n"
2074 " --boot Execute actions only safe at boot\n"
2075 " --prefix=PATH Only apply rules with the specified prefix\n"
2076 " --exclude-prefix=PATH Ignore rules with the specified prefix\n"
2077 " --root=PATH Operate on an alternate filesystem root\n",
2078 program_invocation_short_name);
2079 }
2080
2081 static int parse_argv(int argc, char *argv[]) {
2082
2083 enum {
2084 ARG_VERSION = 0x100,
2085 ARG_CREATE,
2086 ARG_CLEAN,
2087 ARG_REMOVE,
2088 ARG_BOOT,
2089 ARG_PREFIX,
2090 ARG_EXCLUDE_PREFIX,
2091 ARG_ROOT,
2092 };
2093
2094 static const struct option options[] = {
2095 { "help", no_argument, NULL, 'h' },
2096 { "version", no_argument, NULL, ARG_VERSION },
2097 { "create", no_argument, NULL, ARG_CREATE },
2098 { "clean", no_argument, NULL, ARG_CLEAN },
2099 { "remove", no_argument, NULL, ARG_REMOVE },
2100 { "boot", no_argument, NULL, ARG_BOOT },
2101 { "prefix", required_argument, NULL, ARG_PREFIX },
2102 { "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
2103 { "root", required_argument, NULL, ARG_ROOT },
2104 {}
2105 };
2106
2107 int c, r;
2108
2109 assert(argc >= 0);
2110 assert(argv);
2111
2112 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
2113
2114 switch (c) {
2115
2116 case 'h':
2117 help();
2118 return 0;
2119
2120 case ARG_VERSION:
2121 return version();
2122
2123 case ARG_CREATE:
2124 arg_create = true;
2125 break;
2126
2127 case ARG_CLEAN:
2128 arg_clean = true;
2129 break;
2130
2131 case ARG_REMOVE:
2132 arg_remove = true;
2133 break;
2134
2135 case ARG_BOOT:
2136 arg_boot = true;
2137 break;
2138
2139 case ARG_PREFIX:
2140 if (strv_push(&arg_include_prefixes, optarg) < 0)
2141 return log_oom();
2142 break;
2143
2144 case ARG_EXCLUDE_PREFIX:
2145 if (strv_push(&arg_exclude_prefixes, optarg) < 0)
2146 return log_oom();
2147 break;
2148
2149 case ARG_ROOT:
2150 r = parse_path_argument_and_warn(optarg, true, &arg_root);
2151 if (r < 0)
2152 return r;
2153 break;
2154
2155 case '?':
2156 return -EINVAL;
2157
2158 default:
2159 assert_not_reached("Unhandled option");
2160 }
2161
2162 if (!arg_clean && !arg_create && !arg_remove) {
2163 log_error("You need to specify at least one of --clean, --create or --remove.");
2164 return -EINVAL;
2165 }
2166
2167 return 1;
2168 }
2169
2170 static int read_config_file(const char *fn, bool ignore_enoent) {
2171 _cleanup_fclose_ FILE *f = NULL;
2172 char line[LINE_MAX];
2173 Iterator iterator;
2174 unsigned v = 0;
2175 Item *i;
2176 int r;
2177
2178 assert(fn);
2179
2180 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
2181 if (r < 0) {
2182 if (ignore_enoent && r == -ENOENT) {
2183 log_debug_errno(r, "Failed to open \"%s\": %m", fn);
2184 return 0;
2185 }
2186
2187 return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
2188 }
2189 log_debug("Reading config file \"%s\".", fn);
2190
2191 FOREACH_LINE(line, f, break) {
2192 char *l;
2193 int k;
2194
2195 v++;
2196
2197 l = strstrip(line);
2198 if (*l == '#' || *l == 0)
2199 continue;
2200
2201 k = parse_line(fn, v, l);
2202 if (k < 0 && r == 0)
2203 r = k;
2204 }
2205
2206 /* we have to determine age parameter for each entry of type X */
2207 ORDERED_HASHMAP_FOREACH(i, globs, iterator) {
2208 Iterator iter;
2209 Item *j, *candidate_item = NULL;
2210
2211 if (i->type != IGNORE_DIRECTORY_PATH)
2212 continue;
2213
2214 ORDERED_HASHMAP_FOREACH(j, items, iter) {
2215 if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA))
2216 continue;
2217
2218 if (path_equal(j->path, i->path)) {
2219 candidate_item = j;
2220 break;
2221 }
2222
2223 if ((!candidate_item && path_startswith(i->path, j->path)) ||
2224 (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
2225 candidate_item = j;
2226 }
2227
2228 if (candidate_item && candidate_item->age_set) {
2229 i->age = candidate_item->age;
2230 i->age_set = true;
2231 }
2232 }
2233
2234 if (ferror(f)) {
2235 log_error_errno(errno, "Failed to read from file %s: %m", fn);
2236 if (r == 0)
2237 r = -EIO;
2238 }
2239
2240 return r;
2241 }
2242
2243 int main(int argc, char *argv[]) {
2244 int r, k;
2245 ItemArray *a;
2246 Iterator iterator;
2247
2248 r = parse_argv(argc, argv);
2249 if (r <= 0)
2250 goto finish;
2251
2252 log_set_target(LOG_TARGET_AUTO);
2253 log_parse_environment();
2254 log_open();
2255
2256 umask(0022);
2257
2258 mac_selinux_init(NULL);
2259
2260 items = ordered_hashmap_new(&string_hash_ops);
2261 globs = ordered_hashmap_new(&string_hash_ops);
2262
2263 if (!items || !globs) {
2264 r = log_oom();
2265 goto finish;
2266 }
2267
2268 r = 0;
2269
2270 if (optind < argc) {
2271 int j;
2272
2273 for (j = optind; j < argc; j++) {
2274 k = read_config_file(argv[j], false);
2275 if (k < 0 && r == 0)
2276 r = k;
2277 }
2278
2279 } else {
2280 _cleanup_strv_free_ char **files = NULL;
2281 char **f;
2282
2283 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
2284 if (r < 0) {
2285 log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m");
2286 goto finish;
2287 }
2288
2289 STRV_FOREACH(f, files) {
2290 k = read_config_file(*f, true);
2291 if (k < 0 && r == 0)
2292 r = k;
2293 }
2294 }
2295
2296 /* The non-globbing ones usually create things, hence we apply
2297 * them first */
2298 ORDERED_HASHMAP_FOREACH(a, items, iterator) {
2299 k = process_item_array(a);
2300 if (k < 0 && r == 0)
2301 r = k;
2302 }
2303
2304 /* The globbing ones usually alter things, hence we apply them
2305 * second. */
2306 ORDERED_HASHMAP_FOREACH(a, globs, iterator) {
2307 k = process_item_array(a);
2308 if (k < 0 && r == 0)
2309 r = k;
2310 }
2311
2312 finish:
2313 while ((a = ordered_hashmap_steal_first(items)))
2314 item_array_free(a);
2315
2316 while ((a = ordered_hashmap_steal_first(globs)))
2317 item_array_free(a);
2318
2319 ordered_hashmap_free(items);
2320 ordered_hashmap_free(globs);
2321
2322 free(arg_include_prefixes);
2323 free(arg_exclude_prefixes);
2324 free(arg_root);
2325
2326 set_free_free(unix_sockets);
2327
2328 mac_selinux_finish();
2329
2330 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2331 }