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