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