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