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