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