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