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