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