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