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