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