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