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