]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/tmpfiles/tmpfiles.c
hexdecoct: make unbase64mem and unhexmem always use SIZE_MAX
[thirdparty/systemd.git] / src / tmpfiles / tmpfiles.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <fnmatch.h>
6 #include <getopt.h>
7 #include <limits.h>
8 #include <linux/fs.h>
9 #include <stdbool.h>
10 #include <stddef.h>
11 #include <stdlib.h>
12 #include <sys/file.h>
13 #include <sys/xattr.h>
14 #include <sysexits.h>
15 #include <time.h>
16 #include <unistd.h>
17
18 #include "sd-path.h"
19
20 #include "acl-util.h"
21 #include "alloc-util.h"
22 #include "btrfs-util.h"
23 #include "build.h"
24 #include "capability-util.h"
25 #include "chase.h"
26 #include "chattr-util.h"
27 #include "conf-files.h"
28 #include "constants.h"
29 #include "copy.h"
30 #include "creds-util.h"
31 #include "devnum-util.h"
32 #include "dirent-util.h"
33 #include "dissect-image.h"
34 #include "env-util.h"
35 #include "errno-util.h"
36 #include "escape.h"
37 #include "fd-util.h"
38 #include "fileio.h"
39 #include "format-util.h"
40 #include "fs-util.h"
41 #include "glob-util.h"
42 #include "hexdecoct.h"
43 #include "io-util.h"
44 #include "label-util.h"
45 #include "log.h"
46 #include "macro.h"
47 #include "main-func.h"
48 #include "missing_syscall.h"
49 #include "mkdir-label.h"
50 #include "mount-util.h"
51 #include "mountpoint-util.h"
52 #include "nulstr-util.h"
53 #include "offline-passwd.h"
54 #include "pager.h"
55 #include "parse-argument.h"
56 #include "parse-util.h"
57 #include "path-lookup.h"
58 #include "path-util.h"
59 #include "pretty-print.h"
60 #include "rlimit-util.h"
61 #include "rm-rf.h"
62 #include "selinux-util.h"
63 #include "set.h"
64 #include "sort-util.h"
65 #include "specifier.h"
66 #include "stat-util.h"
67 #include "stdio-util.h"
68 #include "string-table.h"
69 #include "string-util.h"
70 #include "strv.h"
71 #include "terminal-util.h"
72 #include "umask-util.h"
73 #include "user-util.h"
74 #include "virt.h"
75
76 /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
77 * them in the file system. This is intended to be used to create
78 * properly owned directories beneath /tmp, /var/tmp, /run, which are
79 * volatile and hence need to be recreated on bootup. */
80
81 typedef enum OperationMask {
82 OPERATION_CREATE = 1 << 0,
83 OPERATION_REMOVE = 1 << 1,
84 OPERATION_CLEAN = 1 << 2,
85 OPERATION_PURGE = 1 << 3,
86 } OperationMask;
87
88 typedef enum ItemType {
89 /* These ones take file names */
90 CREATE_FILE = 'f',
91 TRUNCATE_FILE = 'F', /* deprecated: use f+ */
92 CREATE_DIRECTORY = 'd',
93 TRUNCATE_DIRECTORY = 'D',
94 CREATE_SUBVOLUME = 'v',
95 CREATE_SUBVOLUME_INHERIT_QUOTA = 'q',
96 CREATE_SUBVOLUME_NEW_QUOTA = 'Q',
97 CREATE_FIFO = 'p',
98 CREATE_SYMLINK = 'L',
99 CREATE_CHAR_DEVICE = 'c',
100 CREATE_BLOCK_DEVICE = 'b',
101 COPY_FILES = 'C',
102
103 /* These ones take globs */
104 WRITE_FILE = 'w',
105 EMPTY_DIRECTORY = 'e',
106 SET_XATTR = 't',
107 RECURSIVE_SET_XATTR = 'T',
108 SET_ACL = 'a',
109 RECURSIVE_SET_ACL = 'A',
110 SET_ATTRIBUTE = 'h',
111 RECURSIVE_SET_ATTRIBUTE = 'H',
112 IGNORE_PATH = 'x',
113 IGNORE_DIRECTORY_PATH = 'X',
114 REMOVE_PATH = 'r',
115 RECURSIVE_REMOVE_PATH = 'R',
116 RELABEL_PATH = 'z',
117 RECURSIVE_RELABEL_PATH = 'Z',
118 ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
119 } ItemType;
120
121 typedef enum AgeBy {
122 AGE_BY_ATIME = 1 << 0,
123 AGE_BY_BTIME = 1 << 1,
124 AGE_BY_CTIME = 1 << 2,
125 AGE_BY_MTIME = 1 << 3,
126
127 /* All file timestamp types are checked by default. */
128 AGE_BY_DEFAULT_FILE = AGE_BY_ATIME | AGE_BY_BTIME | AGE_BY_CTIME | AGE_BY_MTIME,
129 AGE_BY_DEFAULT_DIR = AGE_BY_ATIME | AGE_BY_BTIME | AGE_BY_MTIME,
130 } AgeBy;
131
132 typedef struct Item {
133 ItemType type;
134
135 char *path;
136 char *argument;
137 void *binary_argument; /* set if binary data, in which case it takes precedence over 'argument' */
138 size_t binary_argument_size;
139 char **xattrs;
140 #if HAVE_ACL
141 acl_t acl_access;
142 acl_t acl_access_exec;
143 acl_t acl_default;
144 #endif
145 uid_t uid;
146 gid_t gid;
147 mode_t mode;
148 usec_t age;
149 AgeBy age_by_file, age_by_dir;
150
151 dev_t major_minor;
152 unsigned attribute_value;
153 unsigned attribute_mask;
154
155 bool uid_set:1;
156 bool gid_set:1;
157 bool mode_set:1;
158 bool uid_only_create:1;
159 bool gid_only_create:1;
160 bool mode_only_create:1;
161 bool age_set:1;
162 bool mask_perms:1;
163 bool attribute_set:1;
164
165 bool keep_first_level:1;
166
167 bool append_or_force:1;
168
169 bool allow_failure:1;
170
171 bool try_replace:1;
172
173 OperationMask done;
174 } Item;
175
176 typedef struct ItemArray {
177 Item *items;
178 size_t n_items;
179
180 struct ItemArray *parent;
181 Set *children;
182 } ItemArray;
183
184 typedef enum DirectoryType {
185 DIRECTORY_RUNTIME,
186 DIRECTORY_STATE,
187 DIRECTORY_CACHE,
188 DIRECTORY_LOGS,
189 _DIRECTORY_TYPE_MAX,
190 } DirectoryType;
191
192 typedef enum {
193 CREATION_NORMAL,
194 CREATION_EXISTING,
195 CREATION_FORCE,
196 _CREATION_MODE_MAX,
197 _CREATION_MODE_INVALID = -EINVAL,
198 } CreationMode;
199
200 static CatFlags arg_cat_flags = CAT_CONFIG_OFF;
201 static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
202 static OperationMask arg_operation = 0;
203 static bool arg_boot = false;
204 static bool arg_graceful = false;
205 static PagerFlags arg_pager_flags = 0;
206
207 static char **arg_include_prefixes = NULL;
208 static char **arg_exclude_prefixes = NULL;
209 static char *arg_root = NULL;
210 static char *arg_image = NULL;
211 static char *arg_replace = NULL;
212 static ImagePolicy *arg_image_policy = NULL;
213
214 #define MAX_DEPTH 256
215
216 typedef struct Context {
217 OrderedHashmap *items, *globs;
218 Set *unix_sockets;
219 } Context;
220
221 STATIC_DESTRUCTOR_REGISTER(arg_include_prefixes, strv_freep);
222 STATIC_DESTRUCTOR_REGISTER(arg_exclude_prefixes, strv_freep);
223 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
224 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
225 STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
226
227 static const char *const creation_mode_verb_table[_CREATION_MODE_MAX] = {
228 [CREATION_NORMAL] = "Created",
229 [CREATION_EXISTING] = "Found existing",
230 [CREATION_FORCE] = "Created replacement",
231 };
232
233 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
234
235 static void context_done(Context *c) {
236 assert(c);
237
238 ordered_hashmap_free(c->items);
239 ordered_hashmap_free(c->globs);
240
241 set_free(c->unix_sockets);
242 }
243
244 /* Different kinds of errors that mean that information is not available in the environment. */
245 static bool ERRNO_IS_NOINFO(int r) {
246 return IN_SET(abs(r),
247 EUNATCH, /* os-release or machine-id missing */
248 ENOMEDIUM, /* machine-id or another file empty */
249 ENOPKG, /* machine-id is uninitialized */
250 ENXIO); /* env var is unset */
251 }
252
253 static int specifier_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
254 struct table_entry {
255 uint64_t type;
256 const char *suffix;
257 };
258
259 static const struct table_entry paths_system[] = {
260 [DIRECTORY_RUNTIME] = { SD_PATH_SYSTEM_RUNTIME },
261 [DIRECTORY_STATE] = { SD_PATH_SYSTEM_STATE_PRIVATE },
262 [DIRECTORY_CACHE] = { SD_PATH_SYSTEM_STATE_CACHE },
263 [DIRECTORY_LOGS] = { SD_PATH_SYSTEM_STATE_LOGS },
264 };
265
266 static const struct table_entry paths_user[] = {
267 [DIRECTORY_RUNTIME] = { SD_PATH_USER_RUNTIME },
268 [DIRECTORY_STATE] = { SD_PATH_USER_STATE_PRIVATE },
269 [DIRECTORY_CACHE] = { SD_PATH_USER_STATE_CACHE },
270 [DIRECTORY_LOGS] = { SD_PATH_USER_STATE_PRIVATE, "log" },
271 };
272
273 const struct table_entry *paths;
274 _cleanup_free_ char *p = NULL;
275 unsigned i;
276 int r;
277
278 assert_cc(ELEMENTSOF(paths_system) == ELEMENTSOF(paths_user));
279 paths = arg_runtime_scope == RUNTIME_SCOPE_USER ? paths_user : paths_system;
280
281 i = PTR_TO_UINT(data);
282 assert(i < ELEMENTSOF(paths_system));
283
284 r = sd_path_lookup(paths[i].type, paths[i].suffix, &p);
285 if (r < 0)
286 return r;
287
288 if (arg_root) {
289 _cleanup_free_ char *j = NULL;
290
291 j = path_join(arg_root, p);
292 if (!j)
293 return -ENOMEM;
294
295 *ret = TAKE_PTR(j);
296 } else
297 *ret = TAKE_PTR(p);
298
299 return 0;
300 }
301
302 static int log_unresolvable_specifier(const char *filename, unsigned line) {
303 static bool notified = false;
304
305 /* In system mode, this is called when /etc is not fully initialized and some specifiers are
306 * unresolvable. In user mode, this is called when some variables are not defined. These cases are
307 * not considered a fatal error, so log at LOG_NOTICE only for the first time and then downgrade this
308 * to LOG_DEBUG for the rest.
309 *
310 * If we're running in a chroot (--root was used or sd_booted() reports that systemd is not running),
311 * always use LOG_DEBUG. We may be called to initialize a chroot before booting and there is no
312 * expectation that machine-id and other files will be populated.
313 */
314
315 int log_level = notified || arg_root || running_in_chroot() > 0 ?
316 LOG_DEBUG : LOG_NOTICE;
317
318 log_syntax(NULL,
319 log_level,
320 filename, line, 0,
321 "Failed to resolve specifier: %s, skipping.",
322 arg_runtime_scope == RUNTIME_SCOPE_USER ? "Required $XDG_... variable not defined" : "uninitialized /etc/ detected");
323
324 if (!notified)
325 log_full(log_level,
326 "All rules containing unresolvable specifiers will be skipped.");
327
328 notified = true;
329 return 0;
330 }
331
332 static int user_config_paths(char*** ret) {
333 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
334 _cleanup_free_ char *persistent_config = NULL, *runtime_config = NULL, *data_home = NULL;
335 _cleanup_strv_free_ char **res = NULL;
336 int r;
337
338 r = xdg_user_dirs(&config_dirs, &data_dirs);
339 if (r < 0)
340 return r;
341
342 r = xdg_user_config_dir(&persistent_config, "/user-tmpfiles.d");
343 if (r < 0 && !ERRNO_IS_NOINFO(r))
344 return r;
345
346 r = xdg_user_runtime_dir(&runtime_config, "/user-tmpfiles.d");
347 if (r < 0 && !ERRNO_IS_NOINFO(r))
348 return r;
349
350 r = xdg_user_data_dir(&data_home, "/user-tmpfiles.d");
351 if (r < 0 && !ERRNO_IS_NOINFO(r))
352 return r;
353
354 r = strv_extend_strv_concat(&res, config_dirs, "/user-tmpfiles.d");
355 if (r < 0)
356 return r;
357
358 r = strv_extend(&res, persistent_config);
359 if (r < 0)
360 return r;
361
362 r = strv_extend(&res, runtime_config);
363 if (r < 0)
364 return r;
365
366 r = strv_extend(&res, data_home);
367 if (r < 0)
368 return r;
369
370 r = strv_extend_strv_concat(&res, data_dirs, "/user-tmpfiles.d");
371 if (r < 0)
372 return r;
373
374 r = path_strv_make_absolute_cwd(res);
375 if (r < 0)
376 return r;
377
378 *ret = TAKE_PTR(res);
379 return 0;
380 }
381
382 static bool needs_purge(ItemType t) {
383 return IN_SET(t,
384 COPY_FILES,
385 TRUNCATE_FILE,
386 CREATE_FILE,
387 WRITE_FILE,
388 EMPTY_DIRECTORY,
389 CREATE_SUBVOLUME,
390 CREATE_SUBVOLUME_INHERIT_QUOTA,
391 CREATE_SUBVOLUME_NEW_QUOTA,
392 CREATE_CHAR_DEVICE,
393 CREATE_BLOCK_DEVICE,
394 CREATE_SYMLINK,
395 CREATE_FIFO,
396 CREATE_DIRECTORY,
397 TRUNCATE_DIRECTORY);
398 }
399
400 static bool needs_glob(ItemType t) {
401 return IN_SET(t,
402 WRITE_FILE,
403 EMPTY_DIRECTORY,
404 SET_XATTR,
405 RECURSIVE_SET_XATTR,
406 SET_ACL,
407 RECURSIVE_SET_ACL,
408 SET_ATTRIBUTE,
409 RECURSIVE_SET_ATTRIBUTE,
410 IGNORE_PATH,
411 IGNORE_DIRECTORY_PATH,
412 REMOVE_PATH,
413 RECURSIVE_REMOVE_PATH,
414 RELABEL_PATH,
415 RECURSIVE_RELABEL_PATH,
416 ADJUST_MODE);
417 }
418
419 static bool takes_ownership(ItemType t) {
420 return IN_SET(t,
421 CREATE_FILE,
422 TRUNCATE_FILE,
423 CREATE_DIRECTORY,
424 TRUNCATE_DIRECTORY,
425 CREATE_SUBVOLUME,
426 CREATE_SUBVOLUME_INHERIT_QUOTA,
427 CREATE_SUBVOLUME_NEW_QUOTA,
428 CREATE_FIFO,
429 CREATE_SYMLINK,
430 CREATE_CHAR_DEVICE,
431 CREATE_BLOCK_DEVICE,
432 COPY_FILES,
433 WRITE_FILE,
434 EMPTY_DIRECTORY,
435 IGNORE_PATH,
436 IGNORE_DIRECTORY_PATH,
437 REMOVE_PATH,
438 RECURSIVE_REMOVE_PATH);
439 }
440
441 static struct Item* find_glob(OrderedHashmap *h, const char *match) {
442 ItemArray *j;
443
444 ORDERED_HASHMAP_FOREACH(j, h) {
445 size_t n;
446
447 for (n = 0; n < j->n_items; n++) {
448 Item *item = j->items + n;
449
450 if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
451 return item;
452 }
453 }
454
455 return NULL;
456 }
457
458 static int load_unix_sockets(Context *c) {
459 _cleanup_set_free_ Set *sockets = NULL;
460 _cleanup_fclose_ FILE *f = NULL;
461 int r;
462
463 if (c->unix_sockets)
464 return 0;
465
466 /* We maintain a cache of the sockets we found in /proc/net/unix to speed things up a little. */
467
468 f = fopen("/proc/net/unix", "re");
469 if (!f)
470 return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
471 "Failed to open /proc/net/unix, ignoring: %m");
472
473 /* Skip header */
474 r = read_line(f, LONG_LINE_MAX, NULL);
475 if (r < 0)
476 return log_warning_errno(r, "Failed to skip /proc/net/unix header line: %m");
477 if (r == 0)
478 return log_warning_errno(SYNTHETIC_ERRNO(EIO), "Premature end of file reading /proc/net/unix.");
479
480 for (;;) {
481 _cleanup_free_ char *line = NULL;
482 char *p;
483
484 r = read_line(f, LONG_LINE_MAX, &line);
485 if (r < 0)
486 return log_warning_errno(r, "Failed to read /proc/net/unix line, ignoring: %m");
487 if (r == 0) /* EOF */
488 break;
489
490 p = strchr(line, ':');
491 if (!p)
492 continue;
493
494 if (strlen(p) < 37)
495 continue;
496
497 p += 37;
498 p += strspn(p, WHITESPACE);
499 p += strcspn(p, WHITESPACE); /* skip one more word */
500 p += strspn(p, WHITESPACE);
501
502 if (!path_is_absolute(p))
503 continue;
504
505 r = set_put_strdup_full(&sockets, &path_hash_ops_free, p);
506 if (r < 0)
507 return log_warning_errno(r, "Failed to add AF_UNIX socket to set, ignoring: %m");
508 }
509
510 c->unix_sockets = TAKE_PTR(sockets);
511 return 1;
512 }
513
514 static bool unix_socket_alive(Context *c, const char *fn) {
515 assert(c);
516 assert(fn);
517
518 if (load_unix_sockets(c) < 0)
519 return true; /* We don't know, so assume yes */
520
521 return set_contains(c->unix_sockets, fn);
522 }
523
524 /* Accessors for the argument in binary format */
525 static const void* item_binary_argument(const Item *i) {
526 assert(i);
527 return i->binary_argument ?: i->argument;
528 }
529
530 static size_t item_binary_argument_size(const Item *i) {
531 assert(i);
532 return i->binary_argument ? i->binary_argument_size : strlen_ptr(i->argument);
533 }
534
535 static DIR* xopendirat_nomod(int dirfd, const char *path) {
536 DIR *dir;
537
538 dir = xopendirat(dirfd, path, O_NOFOLLOW|O_NOATIME);
539 if (dir)
540 return dir;
541
542 if (!IN_SET(errno, ENOENT, ELOOP))
543 log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path);
544
545 if (errno != EPERM)
546 return NULL;
547
548 dir = xopendirat(dirfd, path, O_NOFOLLOW);
549 if (!dir)
550 log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path);
551
552 return dir;
553 }
554
555 static DIR* opendir_nomod(const char *path) {
556 return xopendirat_nomod(AT_FDCWD, path);
557 }
558
559 static bool needs_cleanup(
560 nsec_t atime,
561 nsec_t btime,
562 nsec_t ctime,
563 nsec_t mtime,
564 nsec_t cutoff,
565 const char *sub_path,
566 AgeBy age_by,
567 bool is_dir) {
568
569 if (FLAGS_SET(age_by, AGE_BY_MTIME) && mtime != NSEC_INFINITY && mtime >= cutoff) {
570 /* Follows spelling in stat(1). */
571 log_debug("%s \"%s\": modify time %s is too new.",
572 is_dir ? "Directory" : "File",
573 sub_path,
574 FORMAT_TIMESTAMP_STYLE(mtime / NSEC_PER_USEC, TIMESTAMP_US));
575
576 return false;
577 }
578
579 if (FLAGS_SET(age_by, AGE_BY_ATIME) && atime != NSEC_INFINITY && atime >= cutoff) {
580 log_debug("%s \"%s\": access time %s is too new.",
581 is_dir ? "Directory" : "File",
582 sub_path,
583 FORMAT_TIMESTAMP_STYLE(atime / NSEC_PER_USEC, TIMESTAMP_US));
584
585 return false;
586 }
587
588 /*
589 * Note: Unless explicitly specified by the user, "ctime" is ignored
590 * by default for directories, because we change it when deleting.
591 */
592 if (FLAGS_SET(age_by, AGE_BY_CTIME) && ctime != NSEC_INFINITY && ctime >= cutoff) {
593 log_debug("%s \"%s\": change time %s is too new.",
594 is_dir ? "Directory" : "File",
595 sub_path,
596 FORMAT_TIMESTAMP_STYLE(ctime / NSEC_PER_USEC, TIMESTAMP_US));
597
598 return false;
599 }
600
601 if (FLAGS_SET(age_by, AGE_BY_BTIME) && btime != NSEC_INFINITY && btime >= cutoff) {
602 log_debug("%s \"%s\": birth time %s is too new.",
603 is_dir ? "Directory" : "File",
604 sub_path,
605 FORMAT_TIMESTAMP_STYLE(btime / NSEC_PER_USEC, TIMESTAMP_US));
606
607 return false;
608 }
609
610 return true;
611 }
612
613 static int dir_cleanup(
614 Context *c,
615 Item *i,
616 const char *p,
617 DIR *d,
618 nsec_t self_atime_nsec,
619 nsec_t self_mtime_nsec,
620 nsec_t cutoff_nsec,
621 dev_t rootdev_major,
622 dev_t rootdev_minor,
623 bool mountpoint,
624 int maxdepth,
625 bool keep_this_level,
626 AgeBy age_by_file,
627 AgeBy age_by_dir) {
628
629 bool deleted = false;
630 int r = 0;
631
632 assert(c);
633 assert(i);
634 assert(d);
635
636 FOREACH_DIRENT_ALL(de, d, break) {
637 _cleanup_free_ char *sub_path = NULL;
638 nsec_t atime_nsec, mtime_nsec, ctime_nsec, btime_nsec;
639
640 if (dot_or_dot_dot(de->d_name))
641 continue;
642
643 /* If statx() is supported, use it. It's preferable over fstatat() since it tells us
644 * explicitly where we are looking at a mount point, for free as side information. Determining
645 * the same information without statx() is hard, see the complexity of path_is_mount_point(),
646 * and also much slower as it requires a number of syscalls instead of just one. Hence, when
647 * we have modern statx() we use it instead of fstat() and do proper mount point checks,
648 * while on older kernels's well do traditional st_dev based detection of mount points.
649 *
650 * Using statx() for detecting mount points also has the benefit that we handle weird file
651 * systems such as overlayfs better where each file is originating from a different
652 * st_dev. */
653
654 STRUCT_STATX_DEFINE(sx);
655
656 r = statx_fallback(
657 dirfd(d), de->d_name,
658 AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT,
659 STATX_TYPE|STATX_MODE|STATX_UID|STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_BTIME,
660 &sx);
661 if (r == -ENOENT)
662 continue;
663 if (r < 0) {
664 /* FUSE, NFS mounts, SELinux might return EACCES */
665 r = log_full_errno(r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
666 "statx(%s/%s) failed: %m", p, de->d_name);
667 continue;
668 }
669
670 if (FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT)) {
671 /* Yay, we have the mount point API, use it */
672 if (FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT)) {
673 log_debug("Ignoring \"%s/%s\": different mount points.", p, de->d_name);
674 continue;
675 }
676 } else {
677 /* So we might have statx() but the STATX_ATTR_MOUNT_ROOT flag is not supported, fall
678 * back to traditional stx_dev checking. */
679 if (sx.stx_dev_major != rootdev_major ||
680 sx.stx_dev_minor != rootdev_minor) {
681 log_debug("Ignoring \"%s/%s\": different filesystem.", p, de->d_name);
682 continue;
683 }
684
685 /* Try to detect bind mounts of the same filesystem instance; they do not differ in device
686 * major/minors. This type of query is not supported on all kernels or filesystem types
687 * though. */
688 if (S_ISDIR(sx.stx_mode)) {
689 int q;
690
691 q = fd_is_mount_point(dirfd(d), de->d_name, 0);
692 if (q < 0)
693 log_debug_errno(q, "Failed to determine whether \"%s/%s\" is a mount point, ignoring: %m", p, de->d_name);
694 else if (q > 0) {
695 log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.", p, de->d_name);
696 continue;
697 }
698 }
699 }
700
701 atime_nsec = FLAGS_SET(sx.stx_mask, STATX_ATIME) ? statx_timestamp_load_nsec(&sx.stx_atime) : 0;
702 mtime_nsec = FLAGS_SET(sx.stx_mask, STATX_MTIME) ? statx_timestamp_load_nsec(&sx.stx_mtime) : 0;
703 ctime_nsec = FLAGS_SET(sx.stx_mask, STATX_CTIME) ? statx_timestamp_load_nsec(&sx.stx_ctime) : 0;
704 btime_nsec = FLAGS_SET(sx.stx_mask, STATX_BTIME) ? statx_timestamp_load_nsec(&sx.stx_btime) : 0;
705
706 sub_path = path_join(p, de->d_name);
707 if (!sub_path) {
708 r = log_oom();
709 goto finish;
710 }
711
712 /* Is there an item configured for this path? */
713 if (ordered_hashmap_get(c->items, sub_path)) {
714 log_debug("Ignoring \"%s\": a separate entry exists.", sub_path);
715 continue;
716 }
717
718 if (find_glob(c->globs, sub_path)) {
719 log_debug("Ignoring \"%s\": a separate glob exists.", sub_path);
720 continue;
721 }
722
723 if (S_ISDIR(sx.stx_mode)) {
724 _cleanup_closedir_ DIR *sub_dir = NULL;
725
726 if (mountpoint &&
727 streq(de->d_name, "lost+found") &&
728 sx.stx_uid == 0) {
729 log_debug("Ignoring directory \"%s\".", sub_path);
730 continue;
731 }
732
733 if (maxdepth <= 0)
734 log_warning("Reached max depth on \"%s\".", sub_path);
735 else {
736 int q;
737
738 sub_dir = xopendirat_nomod(dirfd(d), de->d_name);
739 if (!sub_dir) {
740 if (errno != ENOENT)
741 r = log_warning_errno(errno, "Opening directory \"%s\" failed, ignoring: %m", sub_path);
742
743 continue;
744 }
745
746 if (flock(dirfd(sub_dir), LOCK_EX|LOCK_NB) < 0) {
747 log_debug_errno(errno, "Couldn't acquire shared BSD lock on directory \"%s\", skipping: %m", sub_path);
748 continue;
749 }
750
751 q = dir_cleanup(c, i,
752 sub_path, sub_dir,
753 atime_nsec, mtime_nsec, cutoff_nsec,
754 rootdev_major, rootdev_minor,
755 false, maxdepth-1, false,
756 age_by_file, age_by_dir);
757 if (q < 0)
758 r = q;
759 }
760
761 /* Note: if you are wondering why we don't support the sticky bit for excluding
762 * directories from cleaning like we do it for other file system objects: well, the
763 * sticky bit already has a meaning for directories, so we don't want to overload
764 * that. */
765
766 if (keep_this_level) {
767 log_debug("Keeping directory \"%s\".", sub_path);
768 continue;
769 }
770
771 /*
772 * Check the file timestamps of an entry against the
773 * given cutoff time; delete if it is older.
774 */
775 if (!needs_cleanup(atime_nsec, btime_nsec, ctime_nsec, mtime_nsec,
776 cutoff_nsec, sub_path, age_by_dir, true))
777 continue;
778
779 log_debug("Removing directory \"%s\".", sub_path);
780 if (unlinkat(dirfd(d), de->d_name, AT_REMOVEDIR) < 0)
781 if (!IN_SET(errno, ENOENT, ENOTEMPTY))
782 r = log_warning_errno(errno, "Failed to remove directory \"%s\", ignoring: %m", sub_path);
783
784 } else {
785 _cleanup_close_ int fd = -EBADF;
786
787 /* Skip files for which the sticky bit is set. These are semantics we define, and are
788 * unknown elsewhere. See XDG_RUNTIME_DIR specification for details. */
789 if (sx.stx_mode & S_ISVTX) {
790 log_debug("Skipping \"%s\": sticky bit set.", sub_path);
791 continue;
792 }
793
794 if (mountpoint &&
795 S_ISREG(sx.stx_mode) &&
796 sx.stx_uid == 0 &&
797 STR_IN_SET(de->d_name,
798 ".journal",
799 "aquota.user",
800 "aquota.group")) {
801 log_debug("Skipping \"%s\".", sub_path);
802 continue;
803 }
804
805 /* Ignore sockets that are listed in /proc/net/unix */
806 if (S_ISSOCK(sx.stx_mode) && unix_socket_alive(c, sub_path)) {
807 log_debug("Skipping \"%s\": live socket.", sub_path);
808 continue;
809 }
810
811 /* Ignore device nodes */
812 if (S_ISCHR(sx.stx_mode) || S_ISBLK(sx.stx_mode)) {
813 log_debug("Skipping \"%s\": a device.", sub_path);
814 continue;
815 }
816
817 /* Keep files on this level around if this is requested */
818 if (keep_this_level) {
819 log_debug("Keeping \"%s\".", sub_path);
820 continue;
821 }
822
823 if (!needs_cleanup(atime_nsec, btime_nsec, ctime_nsec, mtime_nsec,
824 cutoff_nsec, sub_path, age_by_file, false))
825 continue;
826
827 fd = xopenat(dirfd(d),
828 de->d_name,
829 O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME,
830 /* xopen_flags = */ 0,
831 /* mode = */ 0);
832 if (fd < 0 && !IN_SET(fd, -ENOENT, -ELOOP))
833 log_warning_errno(fd, "Opening file \"%s\" failed, ignoring: %m", sub_path);
834 if (fd >= 0 && flock(fd, LOCK_EX|LOCK_NB) < 0 && errno == EAGAIN) {
835 log_debug_errno(errno, "Couldn't acquire shared BSD lock on file \"%s\", skipping: %m", sub_path);
836 continue;
837 }
838
839 log_debug("Removing \"%s\".", sub_path);
840 if (unlinkat(dirfd(d), de->d_name, 0) < 0)
841 if (errno != ENOENT)
842 r = log_warning_errno(errno, "Failed to remove \"%s\", ignoring: %m", sub_path);
843
844 deleted = true;
845 }
846 }
847
848 finish:
849 if (deleted) {
850 struct timespec ts[2];
851
852 log_debug("Restoring access and modification time on \"%s\": %s, %s",
853 p,
854 FORMAT_TIMESTAMP_STYLE(self_atime_nsec / NSEC_PER_USEC, TIMESTAMP_US),
855 FORMAT_TIMESTAMP_STYLE(self_mtime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
856
857 timespec_store_nsec(ts + 0, self_atime_nsec);
858 timespec_store_nsec(ts + 1, self_mtime_nsec);
859
860 /* Restore original directory timestamps */
861 if (futimens(dirfd(d), ts) < 0)
862 log_warning_errno(errno, "Failed to revert timestamps of '%s', ignoring: %m", p);
863 }
864
865 return r;
866 }
867
868 static bool dangerous_hardlinks(void) {
869 _cleanup_free_ char *value = NULL;
870 static int cached = -1;
871 int r;
872
873 /* Check whether the fs.protected_hardlinks sysctl is on. If we can't determine it we assume its off, as that's
874 * what the upstream default is. */
875
876 if (cached >= 0)
877 return cached;
878
879 r = read_one_line_file("/proc/sys/fs/protected_hardlinks", &value);
880 if (r < 0) {
881 log_debug_errno(r, "Failed to read fs.protected_hardlinks sysctl: %m");
882 return true;
883 }
884
885 r = parse_boolean(value);
886 if (r < 0) {
887 log_debug_errno(r, "Failed to parse fs.protected_hardlinks sysctl: %m");
888 return true;
889 }
890
891 cached = r == 0;
892 return cached;
893 }
894
895 static bool hardlink_vulnerable(const struct stat *st) {
896 assert(st);
897
898 return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks();
899 }
900
901 static mode_t process_mask_perms(mode_t mode, mode_t current) {
902
903 if ((current & 0111) == 0)
904 mode &= ~0111;
905 if ((current & 0222) == 0)
906 mode &= ~0222;
907 if ((current & 0444) == 0)
908 mode &= ~0444;
909 if (!S_ISDIR(current))
910 mode &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
911
912 return mode;
913 }
914
915 static int fd_set_perms(
916 Context *c,
917 Item *i,
918 int fd,
919 const char *path,
920 const struct stat *st,
921 CreationMode creation) {
922
923 bool do_chown, do_chmod;
924 struct stat stbuf;
925 mode_t new_mode;
926 uid_t new_uid;
927 gid_t new_gid;
928 int r;
929
930 assert(c);
931 assert(i);
932 assert(fd >= 0);
933 assert(path);
934
935 if (!i->mode_set && !i->uid_set && !i->gid_set)
936 goto shortcut;
937
938 if (!st) {
939 if (fstat(fd, &stbuf) < 0)
940 return log_error_errno(errno, "fstat(%s) failed: %m", path);
941 st = &stbuf;
942 }
943
944 if (hardlink_vulnerable(st))
945 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
946 "Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.",
947 path);
948 new_uid = i->uid_set && (creation != CREATION_EXISTING || !i->uid_only_create) ? i->uid : st->st_uid;
949 new_gid = i->gid_set && (creation != CREATION_EXISTING || !i->gid_only_create) ? i->gid : st->st_gid;
950
951 /* Do we need a chown()? */
952 do_chown = (new_uid != st->st_uid) || (new_gid != st->st_gid);
953
954 /* Calculate the mode to apply */
955 new_mode = i->mode_set && (creation != CREATION_EXISTING || !i->mode_only_create) ?
956 (i->mask_perms ? process_mask_perms(i->mode, st->st_mode) : i->mode) :
957 (st->st_mode & 07777);
958
959 do_chmod = ((new_mode ^ st->st_mode) & 07777) != 0;
960
961 if (do_chmod && do_chown) {
962 /* Before we issue the chmod() let's reduce the access mode to the common bits of the old and
963 * the new mode. That way there's no time window where the file exists under the old owner
964 * with more than the old access modes — and not under the new owner with more than the new
965 * access modes either. */
966
967 if (S_ISLNK(st->st_mode))
968 log_debug("Skipping temporary mode fix for symlink %s.", path);
969 else {
970 mode_t m = new_mode & st->st_mode; /* Mask new mode by old mode */
971
972 if (((m ^ st->st_mode) & 07777) == 0)
973 log_debug("\"%s\" matches temporary mode %o already.", path, m);
974 else {
975 log_debug("Temporarily changing \"%s\" to mode %o.", path, m);
976 r = fchmod_opath(fd, m);
977 if (r < 0)
978 return log_error_errno(r, "fchmod() of %s failed: %m", path);
979 }
980 }
981 }
982
983 if (do_chown) {
984 log_debug("Changing \"%s\" to owner "UID_FMT":"GID_FMT, path, new_uid, new_gid);
985
986 if (fchownat(fd, "",
987 new_uid != st->st_uid ? new_uid : UID_INVALID,
988 new_gid != st->st_gid ? new_gid : GID_INVALID,
989 AT_EMPTY_PATH) < 0)
990 return log_error_errno(errno, "fchownat() of %s failed: %m", path);
991 }
992
993 /* Now, apply the final mode. We do this in two cases: when the user set a mode explicitly, or after a
994 * chown(), since chown()'s mangle the access mode in regards to sgid/suid in some conditions. */
995 if (do_chmod || do_chown) {
996 if (S_ISLNK(st->st_mode))
997 log_debug("Skipping mode fix for symlink %s.", path);
998 else {
999 log_debug("Changing \"%s\" to mode %o.", path, new_mode);
1000 r = fchmod_opath(fd, new_mode);
1001 if (r < 0)
1002 return log_error_errno(r, "fchmod() of %s failed: %m", path);
1003 }
1004 }
1005
1006 shortcut:
1007 return label_fix_full(fd, /* inode_path= */ NULL, /* label_path= */ path, 0);
1008 }
1009
1010 static int path_open_parent_safe(const char *path, bool allow_failure) {
1011 _cleanup_free_ char *dn = NULL;
1012 int r, fd;
1013
1014 if (!path_is_normalized(path))
1015 return log_full_errno(allow_failure ? LOG_INFO : LOG_ERR,
1016 SYNTHETIC_ERRNO(EINVAL),
1017 "Failed to open parent of '%s': path not normalized%s.",
1018 path,
1019 allow_failure ? ", ignoring" : "");
1020
1021 r = path_extract_directory(path, &dn);
1022 if (r < 0)
1023 return log_full_errno(allow_failure ? LOG_INFO : LOG_ERR,
1024 r,
1025 "Unable to determine parent directory of '%s'%s: %m",
1026 path,
1027 allow_failure ? ", ignoring" : "");
1028
1029 r = chase(dn, arg_root, allow_failure ? CHASE_SAFE : CHASE_SAFE|CHASE_WARN, NULL, &fd);
1030 if (r == -ENOLINK) /* Unsafe symlink: already covered by CHASE_WARN */
1031 return r;
1032 if (r < 0)
1033 return log_full_errno(allow_failure ? LOG_INFO : LOG_ERR,
1034 r,
1035 "Failed to open path '%s'%s: %m",
1036 dn,
1037 allow_failure ? ", ignoring" : "");
1038
1039 return fd;
1040 }
1041
1042 static int path_open_safe(const char *path) {
1043 int r, fd;
1044
1045 /* path_open_safe() returns a file descriptor opened with O_PATH after
1046 * verifying that the path doesn't contain unsafe transitions, except
1047 * for its final component as the function does not follow symlink. */
1048
1049 assert(path);
1050
1051 if (!path_is_normalized(path))
1052 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to open invalid path '%s'.", path);
1053
1054 r = chase(path, arg_root, CHASE_SAFE|CHASE_WARN|CHASE_NOFOLLOW, NULL, &fd);
1055 if (r == -ENOLINK)
1056 return r; /* Unsafe symlink: already covered by CHASE_WARN */
1057 if (r < 0)
1058 return log_error_errno(r, "Failed to open path %s: %m", path);
1059
1060 return fd;
1061 }
1062
1063 static int path_set_perms(
1064 Context *c,
1065 Item *i,
1066 const char *path,
1067 CreationMode creation) {
1068
1069 _cleanup_close_ int fd = -EBADF;
1070
1071 assert(c);
1072 assert(i);
1073 assert(path);
1074
1075 fd = path_open_safe(path);
1076 if (fd < 0)
1077 return fd;
1078
1079 return fd_set_perms(c, i, fd, path, /* st= */ NULL, creation);
1080 }
1081
1082 static int parse_xattrs_from_arg(Item *i) {
1083 const char *p;
1084 int r;
1085
1086 assert(i);
1087
1088 assert_se(p = i->argument);
1089 for (;;) {
1090 _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL;
1091
1092 r = extract_first_word(&p, &xattr, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
1093 if (r < 0)
1094 log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p);
1095 if (r <= 0)
1096 break;
1097
1098 r = split_pair(xattr, "=", &name, &value);
1099 if (r < 0) {
1100 log_warning_errno(r, "Failed to parse extended attribute, ignoring: %s", xattr);
1101 continue;
1102 }
1103
1104 if (isempty(name) || isempty(value)) {
1105 log_warning("Malformed extended attribute found, ignoring: %s", xattr);
1106 continue;
1107 }
1108
1109 if (strv_push_pair(&i->xattrs, name, value) < 0)
1110 return log_oom();
1111
1112 name = value = NULL;
1113 }
1114
1115 return 0;
1116 }
1117
1118 static int fd_set_xattrs(
1119 Context *c,
1120 Item *i,
1121 int fd,
1122 const char *path,
1123 const struct stat *st,
1124 CreationMode creation) {
1125
1126 assert(c);
1127 assert(i);
1128 assert(fd >= 0);
1129 assert(path);
1130
1131 STRV_FOREACH_PAIR(name, value, i->xattrs) {
1132 log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path);
1133 if (setxattr(FORMAT_PROC_FD_PATH(fd), *name, *value, strlen(*value), 0) < 0)
1134 return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m",
1135 *name, *value, path);
1136 }
1137 return 0;
1138 }
1139
1140 static int path_set_xattrs(
1141 Context *c,
1142 Item *i,
1143 const char *path,
1144 CreationMode creation) {
1145
1146 _cleanup_close_ int fd = -EBADF;
1147
1148 assert(c);
1149 assert(i);
1150 assert(path);
1151
1152 fd = path_open_safe(path);
1153 if (fd < 0)
1154 return fd;
1155
1156 return fd_set_xattrs(c, i, fd, path, /* st = */ NULL, creation);
1157 }
1158
1159 static int parse_acls_from_arg(Item *item) {
1160 #if HAVE_ACL
1161 int r;
1162
1163 assert(item);
1164
1165 /* If append_or_force (= modify) is set, we will not modify the acl
1166 * afterwards, so the mask can be added now if necessary. */
1167
1168 r = parse_acl(item->argument, &item->acl_access, &item->acl_access_exec,
1169 &item->acl_default, !item->append_or_force);
1170 if (r < 0)
1171 log_full_errno(arg_graceful && IN_SET(r, -EINVAL, -ENOENT, -ESRCH) ? LOG_DEBUG : LOG_WARNING,
1172 r, "Failed to parse ACL \"%s\", ignoring: %m", item->argument);
1173 #else
1174 log_warning("ACLs are not supported, ignoring.");
1175 #endif
1176
1177 return 0;
1178 }
1179
1180 #if HAVE_ACL
1181 static int parse_acl_cond_exec(
1182 const char *path,
1183 acl_t access, /* could be empty (NULL) */
1184 acl_t cond_exec,
1185 const struct stat *st,
1186 bool append,
1187 acl_t *ret) {
1188
1189 _cleanup_(acl_freep) acl_t parsed = NULL;
1190 acl_entry_t entry;
1191 acl_permset_t permset;
1192 bool has_exec;
1193 int r;
1194
1195 assert(path);
1196 assert(ret);
1197 assert(st);
1198
1199 parsed = access ? acl_dup(access) : acl_init(0);
1200 if (!parsed)
1201 return -errno;
1202
1203 /* Since we substitute 'X' with 'x' in parse_acl(), we just need to copy the entries over
1204 * for directories */
1205 if (S_ISDIR(st->st_mode)) {
1206 for (r = acl_get_entry(cond_exec, ACL_FIRST_ENTRY, &entry);
1207 r > 0;
1208 r = acl_get_entry(cond_exec, ACL_NEXT_ENTRY, &entry)) {
1209
1210 acl_entry_t parsed_entry;
1211
1212 if (acl_create_entry(&parsed, &parsed_entry) < 0)
1213 return -errno;
1214
1215 if (acl_copy_entry(parsed_entry, entry) < 0)
1216 return -errno;
1217 }
1218 if (r < 0)
1219 return -errno;
1220
1221 goto finish;
1222 }
1223
1224 has_exec = st->st_mode & S_IXUSR;
1225
1226 if (!has_exec && append) {
1227 _cleanup_(acl_freep) acl_t old = NULL;
1228
1229 old = acl_get_file(path, ACL_TYPE_ACCESS);
1230 if (!old)
1231 return -errno;
1232
1233 for (r = acl_get_entry(old, ACL_FIRST_ENTRY, &entry);
1234 r > 0;
1235 r = acl_get_entry(old, ACL_NEXT_ENTRY, &entry)) {
1236
1237 if (acl_get_permset(entry, &permset) < 0)
1238 return -errno;
1239
1240 r = acl_get_perm(permset, ACL_EXECUTE);
1241 if (r < 0)
1242 return -errno;
1243 if (r > 0) {
1244 has_exec = true;
1245 break;
1246 }
1247 }
1248 if (r < 0)
1249 return -errno;
1250 }
1251
1252 /* Check if we're about to set the execute bit in acl_access */
1253 if (!has_exec && access) {
1254 for (r = acl_get_entry(access, ACL_FIRST_ENTRY, &entry);
1255 r > 0;
1256 r = acl_get_entry(access, ACL_NEXT_ENTRY, &entry)) {
1257
1258 if (acl_get_permset(entry, &permset) < 0)
1259 return -errno;
1260
1261 r = acl_get_perm(permset, ACL_EXECUTE);
1262 if (r < 0)
1263 return -errno;
1264 if (r > 0) {
1265 has_exec = true;
1266 break;
1267 }
1268 }
1269 if (r < 0)
1270 return -errno;
1271 }
1272
1273 for (r = acl_get_entry(cond_exec, ACL_FIRST_ENTRY, &entry);
1274 r > 0;
1275 r = acl_get_entry(cond_exec, ACL_NEXT_ENTRY, &entry)) {
1276
1277 acl_entry_t parsed_entry;
1278
1279 if (acl_create_entry(&parsed, &parsed_entry) < 0)
1280 return -errno;
1281
1282 if (acl_copy_entry(parsed_entry, entry) < 0)
1283 return -errno;
1284
1285 if (!has_exec) {
1286 if (acl_get_permset(parsed_entry, &permset) < 0)
1287 return -errno;
1288
1289 if (acl_delete_perm(permset, ACL_EXECUTE) < 0)
1290 return -errno;
1291 }
1292 }
1293 if (r < 0)
1294 return -errno;
1295
1296 finish:
1297 if (!append) { /* want_mask = true */
1298 r = calc_acl_mask_if_needed(&parsed);
1299 if (r < 0)
1300 return r;
1301 }
1302
1303 *ret = TAKE_PTR(parsed);
1304
1305 return 0;
1306 }
1307
1308 static int path_set_acl(
1309 Context *c,
1310 const char *path,
1311 const char *pretty,
1312 acl_type_t type,
1313 acl_t acl,
1314 bool modify) {
1315
1316 _cleanup_(acl_free_charpp) char *t = NULL;
1317 _cleanup_(acl_freep) acl_t dup = NULL;
1318 int r;
1319
1320 assert(c);
1321
1322 /* Returns 0 for success, positive error if already warned, negative error otherwise. */
1323
1324 if (modify) {
1325 r = acls_for_file(path, type, acl, &dup);
1326 if (r < 0)
1327 return r;
1328
1329 r = calc_acl_mask_if_needed(&dup);
1330 if (r < 0)
1331 return r;
1332 } else {
1333 dup = acl_dup(acl);
1334 if (!dup)
1335 return -errno;
1336
1337 /* the mask was already added earlier if needed */
1338 }
1339
1340 r = add_base_acls_if_needed(&dup, path);
1341 if (r < 0)
1342 return r;
1343
1344 t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE);
1345 log_debug("Setting %s ACL %s on %s.",
1346 type == ACL_TYPE_ACCESS ? "access" : "default",
1347 strna(t), pretty);
1348
1349 r = acl_set_file(path, type, dup);
1350 if (r < 0) {
1351 if (ERRNO_IS_NOT_SUPPORTED(errno))
1352 /* No error if filesystem doesn't support ACLs. Return negative. */
1353 return -errno;
1354 else
1355 /* Return positive to indicate we already warned */
1356 return -log_error_errno(errno,
1357 "Setting %s ACL \"%s\" on %s failed: %m",
1358 type == ACL_TYPE_ACCESS ? "access" : "default",
1359 strna(t), pretty);
1360 }
1361 return 0;
1362 }
1363 #endif
1364
1365 static int fd_set_acls(
1366 Context *c,
1367 Item *item,
1368 int fd,
1369 const char *path,
1370 const struct stat *st,
1371 CreationMode creation) {
1372
1373 int r = 0;
1374 #if HAVE_ACL
1375 _cleanup_(acl_freep) acl_t access_with_exec_parsed = NULL;
1376 struct stat stbuf;
1377
1378 assert(c);
1379 assert(item);
1380 assert(fd >= 0);
1381 assert(path);
1382
1383 if (!st) {
1384 if (fstat(fd, &stbuf) < 0)
1385 return log_error_errno(errno, "fstat(%s) failed: %m", path);
1386 st = &stbuf;
1387 }
1388
1389 if (hardlink_vulnerable(st))
1390 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
1391 "Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.",
1392 path);
1393
1394 if (S_ISLNK(st->st_mode)) {
1395 log_debug("Skipping ACL fix for symlink %s.", path);
1396 return 0;
1397 }
1398
1399 if (item->acl_access_exec) {
1400 r = parse_acl_cond_exec(FORMAT_PROC_FD_PATH(fd),
1401 item->acl_access,
1402 item->acl_access_exec,
1403 st,
1404 item->append_or_force,
1405 &access_with_exec_parsed);
1406 if (r < 0)
1407 return log_error_errno(r, "Failed to parse conditionalized execute bit for \"%s\": %m", path);
1408
1409 r = path_set_acl(c, FORMAT_PROC_FD_PATH(fd), path, ACL_TYPE_ACCESS, access_with_exec_parsed, item->append_or_force);
1410 } else if (item->acl_access)
1411 r = path_set_acl(c, FORMAT_PROC_FD_PATH(fd), path, ACL_TYPE_ACCESS, item->acl_access, item->append_or_force);
1412
1413 /* set only default acls to folders */
1414 if (r == 0 && item->acl_default && S_ISDIR(st->st_mode))
1415 r = path_set_acl(c, FORMAT_PROC_FD_PATH(fd), path, ACL_TYPE_DEFAULT, item->acl_default, item->append_or_force);
1416
1417 if (ERRNO_IS_NOT_SUPPORTED(r)) {
1418 log_debug_errno(r, "ACLs not supported by file system at %s", path);
1419 return 0;
1420 }
1421
1422 if (r > 0)
1423 return -r; /* already warned in path_set_acl */
1424
1425 /* The above procfs paths don't work if /proc is not mounted. */
1426 if (r == -ENOENT && proc_mounted() == 0)
1427 r = -ENOSYS;
1428
1429 if (r < 0)
1430 return log_error_errno(r, "ACL operation on \"%s\" failed: %m", path);
1431 #endif
1432 return r;
1433 }
1434
1435 static int path_set_acls(
1436 Context *c,
1437 Item *item,
1438 const char *path,
1439 CreationMode creation) {
1440
1441 int r = 0;
1442 #if HAVE_ACL
1443 _cleanup_close_ int fd = -EBADF;
1444
1445 assert(c);
1446 assert(item);
1447 assert(path);
1448
1449 fd = path_open_safe(path);
1450 if (fd < 0)
1451 return fd;
1452
1453 r = fd_set_acls(c, item, fd, path, /* st= */ NULL, creation);
1454 #endif
1455 return r;
1456 }
1457
1458 static int parse_attribute_from_arg(Item *item) {
1459
1460 static const struct {
1461 char character;
1462 unsigned value;
1463 } attributes[] = {
1464 { 'A', FS_NOATIME_FL }, /* do not update atime */
1465 { 'S', FS_SYNC_FL }, /* Synchronous updates */
1466 { 'D', FS_DIRSYNC_FL }, /* dirsync behaviour (directories only) */
1467 { 'a', FS_APPEND_FL }, /* writes to file may only append */
1468 { 'c', FS_COMPR_FL }, /* Compress file */
1469 { 'd', FS_NODUMP_FL }, /* do not dump file */
1470 { 'e', FS_EXTENT_FL }, /* Extents */
1471 { 'i', FS_IMMUTABLE_FL }, /* Immutable file */
1472 { 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */
1473 { 's', FS_SECRM_FL }, /* Secure deletion */
1474 { 'u', FS_UNRM_FL }, /* Undelete */
1475 { 't', FS_NOTAIL_FL }, /* file tail should not be merged */
1476 { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies */
1477 { 'C', FS_NOCOW_FL }, /* Do not cow file */
1478 { 'P', FS_PROJINHERIT_FL }, /* Inherit the quota project ID */
1479 };
1480
1481 enum {
1482 MODE_ADD,
1483 MODE_DEL,
1484 MODE_SET
1485 } mode = MODE_ADD;
1486
1487 unsigned value = 0, mask = 0;
1488 const char *p;
1489
1490 assert(item);
1491
1492 p = item->argument;
1493 if (p) {
1494 if (*p == '+') {
1495 mode = MODE_ADD;
1496 p++;
1497 } else if (*p == '-') {
1498 mode = MODE_DEL;
1499 p++;
1500 } else if (*p == '=') {
1501 mode = MODE_SET;
1502 p++;
1503 }
1504 }
1505
1506 if (isempty(p) && mode != MODE_SET)
1507 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1508 "Setting file attribute on '%s' needs an attribute specification.",
1509 item->path);
1510
1511 for (; p && *p ; p++) {
1512 unsigned i, v;
1513
1514 for (i = 0; i < ELEMENTSOF(attributes); i++)
1515 if (*p == attributes[i].character)
1516 break;
1517
1518 if (i >= ELEMENTSOF(attributes))
1519 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1520 "Unknown file attribute '%c' on '%s'.",
1521 *p, item->path);
1522
1523 v = attributes[i].value;
1524
1525 SET_FLAG(value, v, IN_SET(mode, MODE_ADD, MODE_SET));
1526
1527 mask |= v;
1528 }
1529
1530 if (mode == MODE_SET)
1531 mask |= CHATTR_ALL_FL;
1532
1533 assert(mask != 0);
1534
1535 item->attribute_mask = mask;
1536 item->attribute_value = value;
1537 item->attribute_set = true;
1538
1539 return 0;
1540 }
1541
1542 static int fd_set_attribute(
1543 Context *c,
1544 Item *item,
1545 int fd,
1546 const char *path,
1547 const struct stat *st,
1548 CreationMode creation) {
1549
1550 _cleanup_close_ int procfs_fd = -EBADF;
1551 struct stat stbuf;
1552 unsigned f;
1553 int r;
1554
1555 assert(c);
1556 assert(item);
1557 assert(fd >= 0);
1558 assert(path);
1559
1560 if (!item->attribute_set || item->attribute_mask == 0)
1561 return 0;
1562
1563 if (!st) {
1564 if (fstat(fd, &stbuf) < 0)
1565 return log_error_errno(errno, "fstat(%s) failed: %m", path);
1566 st = &stbuf;
1567 }
1568
1569 /* Issuing the file attribute ioctls on device nodes is not safe, as that will be delivered to the
1570 * drivers, not the file system containing the device node. */
1571 if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode))
1572 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1573 "Setting file flags is only supported on regular files and directories, cannot set on '%s'.",
1574 path);
1575
1576 f = item->attribute_value & item->attribute_mask;
1577
1578 /* Mask away directory-specific flags */
1579 if (!S_ISDIR(st->st_mode))
1580 f &= ~FS_DIRSYNC_FL;
1581
1582 procfs_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NOATIME);
1583 if (procfs_fd < 0)
1584 return log_error_errno(procfs_fd, "Failed to re-open '%s': %m", path);
1585
1586 unsigned previous, current;
1587 r = chattr_full(procfs_fd, NULL, f, item->attribute_mask, &previous, &current, CHATTR_FALLBACK_BITWISE);
1588 if (r == -ENOANO)
1589 log_warning("Cannot set file attributes for '%s', maybe due to incompatibility in specified attributes, "
1590 "previous=0x%08x, current=0x%08x, expected=0x%08x, ignoring.",
1591 path, previous, current, (previous & ~item->attribute_mask) | (f & item->attribute_mask));
1592 else if (r < 0)
1593 log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING, r,
1594 "Cannot set file attributes for '%s', value=0x%08x, mask=0x%08x, ignoring: %m",
1595 path, item->attribute_value, item->attribute_mask);
1596
1597 return 0;
1598 }
1599
1600 static int path_set_attribute(
1601 Context *c,
1602 Item *item,
1603 const char *path,
1604 CreationMode creation) {
1605
1606 _cleanup_close_ int fd = -EBADF;
1607
1608 assert(c);
1609 assert(item);
1610
1611 if (!item->attribute_set || item->attribute_mask == 0)
1612 return 0;
1613
1614 fd = path_open_safe(path);
1615 if (fd < 0)
1616 return fd;
1617
1618 return fd_set_attribute(c, item, fd, path, /* st= */ NULL, creation);
1619 }
1620
1621 static int write_argument_data(Item *i, int fd, const char *path) {
1622 int r;
1623
1624 assert(i);
1625 assert(fd >= 0);
1626 assert(path);
1627
1628 if (item_binary_argument_size(i) == 0)
1629 return 0;
1630
1631 assert(item_binary_argument(i));
1632
1633 log_debug("Writing to \"%s\".", path);
1634
1635 r = loop_write(fd, item_binary_argument(i), item_binary_argument_size(i));
1636 if (r < 0)
1637 return log_error_errno(r, "Failed to write file \"%s\": %m", path);
1638
1639 return 0;
1640 }
1641
1642 static int write_one_file(Context *c, Item *i, const char *path, CreationMode creation) {
1643 _cleanup_close_ int fd = -EBADF, dir_fd = -EBADF;
1644 _cleanup_free_ char *bn = NULL;
1645 int r;
1646
1647 assert(c);
1648 assert(i);
1649 assert(path);
1650 assert(i->type == WRITE_FILE);
1651
1652 r = path_extract_filename(path, &bn);
1653 if (r < 0)
1654 return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
1655 if (r == O_DIRECTORY)
1656 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Cannot open path '%s' for writing, is a directory.", path);
1657
1658 /* Validate the path and keep the fd on the directory for opening the file so we're sure that it
1659 * can't be changed behind our back. */
1660 dir_fd = path_open_parent_safe(path, i->allow_failure);
1661 if (dir_fd < 0)
1662 return dir_fd;
1663
1664 /* Follows symlinks */
1665 fd = openat(dir_fd, bn,
1666 O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY|(i->append_or_force ? O_APPEND : 0),
1667 i->mode);
1668 if (fd < 0) {
1669 if (errno == ENOENT) {
1670 log_debug_errno(errno, "Not writing missing file \"%s\": %m", path);
1671 return 0;
1672 }
1673
1674 if (i->allow_failure)
1675 return log_debug_errno(errno, "Failed to open file \"%s\", ignoring: %m", path);
1676
1677 return log_error_errno(errno, "Failed to open file \"%s\": %m", path);
1678 }
1679
1680 /* 'w' is allowed to write into any kind of files. */
1681
1682 r = write_argument_data(i, fd, path);
1683 if (r < 0)
1684 return r;
1685
1686 return fd_set_perms(c, i, fd, path, NULL, creation);
1687 }
1688
1689 static int create_file(
1690 Context *c,
1691 Item *i,
1692 const char *path) {
1693
1694 _cleanup_close_ int fd = -EBADF, dir_fd = -EBADF;
1695 _cleanup_free_ char *bn = NULL;
1696 struct stat stbuf, *st = NULL;
1697 CreationMode creation;
1698 int r = 0;
1699
1700 assert(c);
1701 assert(i);
1702 assert(path);
1703 assert(i->type == CREATE_FILE);
1704
1705 /* 'f' operates on regular files exclusively. */
1706
1707 r = path_extract_filename(path, &bn);
1708 if (r < 0)
1709 return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
1710 if (r == O_DIRECTORY)
1711 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Cannot open path '%s' for writing, is a directory.", path);
1712
1713 /* Validate the path and keep the fd on the directory for opening the file so we're sure that it
1714 * can't be changed behind our back. */
1715 dir_fd = path_open_parent_safe(path, i->allow_failure);
1716 if (dir_fd < 0)
1717 return dir_fd;
1718
1719 WITH_UMASK(0000) {
1720 mac_selinux_create_file_prepare(path, S_IFREG);
1721 fd = RET_NERRNO(openat(dir_fd, bn, O_CREAT|O_EXCL|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode));
1722 mac_selinux_create_file_clear();
1723 }
1724
1725 if (fd < 0) {
1726 /* Even on a read-only filesystem, open(2) returns EEXIST if the file already exists. It
1727 * returns EROFS only if it needs to create the file. */
1728 if (fd != -EEXIST)
1729 return log_error_errno(fd, "Failed to create file %s: %m", path);
1730
1731 /* Re-open the file. At that point it must exist since open(2) failed with EEXIST. We still
1732 * need to check if the perms/mode need to be changed. For read-only filesystems, we let
1733 * fd_set_perms() report the error if the perms need to be modified. */
1734 fd = openat(dir_fd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH, i->mode);
1735 if (fd < 0)
1736 return log_error_errno(errno, "Failed to re-open file %s: %m", path);
1737
1738 if (fstat(fd, &stbuf) < 0)
1739 return log_error_errno(errno, "stat(%s) failed: %m", path);
1740
1741 if (!S_ISREG(stbuf.st_mode))
1742 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
1743 "%s exists and is not a regular file.",
1744 path);
1745
1746 st = &stbuf;
1747 creation = CREATION_EXISTING;
1748 } else {
1749 r = write_argument_data(i, fd, path);
1750 if (r < 0)
1751 return r;
1752
1753 creation = CREATION_NORMAL;
1754 }
1755
1756 return fd_set_perms(c, i, fd, path, st, creation);
1757 }
1758
1759 static int truncate_file(
1760 Context *c,
1761 Item *i,
1762 const char *path) {
1763
1764 _cleanup_close_ int fd = -EBADF, dir_fd = -EBADF;
1765 _cleanup_free_ char *bn = NULL;
1766 struct stat stbuf, *st = NULL;
1767 CreationMode creation;
1768 bool erofs = false;
1769 int r = 0;
1770
1771 assert(c);
1772 assert(i);
1773 assert(path);
1774 assert(i->type == TRUNCATE_FILE || (i->type == CREATE_FILE && i->append_or_force));
1775
1776 /* We want to operate on regular file exclusively especially since O_TRUNC is unspecified if the file
1777 * is neither a regular file nor a fifo nor a terminal device. Therefore we first open the file and
1778 * make sure it's a regular one before truncating it. */
1779
1780 r = path_extract_filename(path, &bn);
1781 if (r < 0)
1782 return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
1783 if (r == O_DIRECTORY)
1784 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Cannot open path '%s' for truncation, is a directory.", path);
1785
1786 /* Validate the path and keep the fd on the directory for opening the file so we're sure that it
1787 * can't be changed behind our back. */
1788 dir_fd = path_open_parent_safe(path, i->allow_failure);
1789 if (dir_fd < 0)
1790 return dir_fd;
1791
1792 creation = CREATION_EXISTING;
1793 fd = RET_NERRNO(openat(dir_fd, bn, O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode));
1794 if (fd == -ENOENT) {
1795 creation = CREATION_NORMAL; /* Didn't work without O_CREATE, try again with */
1796
1797 WITH_UMASK(0000) {
1798 mac_selinux_create_file_prepare(path, S_IFREG);
1799 fd = RET_NERRNO(openat(dir_fd, bn, O_CREAT|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode));
1800 mac_selinux_create_file_clear();
1801 }
1802 }
1803
1804 if (fd < 0) {
1805 if (fd != -EROFS)
1806 return log_error_errno(fd, "Failed to open/create file %s: %m", path);
1807
1808 /* On a read-only filesystem, we don't want to fail if the target is already empty and the
1809 * perms are set. So we still proceed with the sanity checks and let the remaining operations
1810 * fail with EROFS if they try to modify the target file. */
1811
1812 fd = openat(dir_fd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH, i->mode);
1813 if (fd < 0) {
1814 if (errno == ENOENT)
1815 return log_error_errno(SYNTHETIC_ERRNO(EROFS),
1816 "Cannot create file %s on a read-only file system.",
1817 path);
1818
1819 return log_error_errno(errno, "Failed to re-open file %s: %m", path);
1820 }
1821
1822 erofs = true;
1823 creation = CREATION_EXISTING;
1824 }
1825
1826 if (fstat(fd, &stbuf) < 0)
1827 return log_error_errno(errno, "stat(%s) failed: %m", path);
1828
1829 if (!S_ISREG(stbuf.st_mode))
1830 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
1831 "%s exists and is not a regular file.",
1832 path);
1833
1834 if (stbuf.st_size > 0) {
1835 if (ftruncate(fd, 0) < 0) {
1836 r = erofs ? -EROFS : -errno;
1837 return log_error_errno(r, "Failed to truncate file %s: %m", path);
1838 }
1839 } else
1840 st = &stbuf;
1841
1842 log_debug("\"%s\" has been created.", path);
1843
1844 if (item_binary_argument(i)) {
1845 r = write_argument_data(i, fd, path);
1846 if (r < 0)
1847 return r;
1848 }
1849
1850 return fd_set_perms(c, i, fd, path, st, creation);
1851 }
1852
1853 static int copy_files(Context *c, Item *i) {
1854 _cleanup_close_ int dfd = -EBADF, fd = -EBADF;
1855 _cleanup_free_ char *bn = NULL;
1856 struct stat st, a;
1857 int r;
1858
1859 log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path);
1860
1861 r = path_extract_filename(i->path, &bn);
1862 if (r < 0)
1863 return log_error_errno(r, "Failed to extract filename from path '%s': %m", i->path);
1864
1865 /* Validate the path and use the returned directory fd for copying the target so we're sure that the
1866 * path can't be changed behind our back. */
1867 dfd = path_open_parent_safe(i->path, i->allow_failure);
1868 if (dfd < 0)
1869 return dfd;
1870
1871 r = copy_tree_at(AT_FDCWD, i->argument,
1872 dfd, bn,
1873 i->uid_set ? i->uid : UID_INVALID,
1874 i->gid_set ? i->gid : GID_INVALID,
1875 COPY_REFLINK | ((i->append_or_force) ? COPY_MERGE : COPY_MERGE_EMPTY) | COPY_MAC_CREATE | COPY_HARDLINKS,
1876 NULL, NULL);
1877
1878 fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
1879 if (fd < 0) {
1880 if (r < 0) /* Look at original error first */
1881 return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
1882
1883 return log_error_errno(errno, "Failed to openat(%s): %m", i->path);
1884 }
1885
1886 if (fstat(fd, &st) < 0)
1887 return log_error_errno(errno, "Failed to fstat(%s): %m", i->path);
1888
1889 if (stat(i->argument, &a) < 0)
1890 return log_error_errno(errno, "Failed to stat(%s): %m", i->argument);
1891
1892 if (((st.st_mode ^ a.st_mode) & S_IFMT) != 0) {
1893 log_debug("Can't copy to %s, file exists already and is of different type", i->path);
1894 return 0;
1895 }
1896
1897 return fd_set_perms(c, i, fd, i->path, &st, _CREATION_MODE_INVALID);
1898 }
1899
1900 static int create_directory_or_subvolume(
1901 const char *path,
1902 mode_t mode,
1903 bool subvol,
1904 bool allow_failure,
1905 struct stat *ret_st,
1906 CreationMode *ret_creation) {
1907
1908 _cleanup_free_ char *bn = NULL;
1909 _cleanup_close_ int pfd = -EBADF;
1910 CreationMode creation;
1911 struct stat st;
1912 int r, fd;
1913
1914 assert(path);
1915
1916 r = path_extract_filename(path, &bn);
1917 if (r < 0)
1918 return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
1919
1920 pfd = path_open_parent_safe(path, allow_failure);
1921 if (pfd < 0)
1922 return pfd;
1923
1924 if (subvol) {
1925 r = getenv_bool("SYSTEMD_TMPFILES_FORCE_SUBVOL");
1926 if (r < 0) {
1927 if (r != -ENXIO) /* env var is unset */
1928 log_warning_errno(r, "Cannot parse value of $SYSTEMD_TMPFILES_FORCE_SUBVOL, ignoring.");
1929 r = btrfs_is_subvol(empty_to_root(arg_root)) > 0;
1930 }
1931 if (r == 0)
1932 /* Don't create a subvolume unless the root directory is one, too. We do this under
1933 * the assumption that if the root directory is just a plain directory (i.e. very
1934 * light-weight), we shouldn't try to split it up into subvolumes (i.e. more
1935 * heavy-weight). Thus, chroot() environments and suchlike will get a full brtfs
1936 * subvolume set up below their tree only if they specifically set up a btrfs
1937 * subvolume for the root dir too. */
1938
1939 subvol = false;
1940 else {
1941 WITH_UMASK((~mode) & 0777)
1942 r = btrfs_subvol_make(pfd, bn);
1943 }
1944 } else
1945 r = 0;
1946
1947 if (!subvol || ERRNO_IS_NEG_NOT_SUPPORTED(r))
1948 WITH_UMASK(0000)
1949 r = mkdirat_label(pfd, bn, mode);
1950
1951 creation = r >= 0 ? CREATION_NORMAL : CREATION_EXISTING;
1952
1953 fd = openat(pfd, bn, O_NOFOLLOW|O_CLOEXEC|O_DIRECTORY|O_PATH);
1954 if (fd < 0) {
1955 /* We couldn't open it because it is not actually a directory? */
1956 if (errno == ENOTDIR)
1957 return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "\"%s\" already exists and is not a directory.", path);
1958
1959 /* Then look at the original error */
1960 if (r < 0)
1961 return log_full_errno(allow_failure ? LOG_INFO : LOG_ERR,
1962 r,
1963 "Failed to create directory or subvolume \"%s\"%s: %m",
1964 path,
1965 allow_failure ? ", ignoring" : "");
1966
1967 return log_error_errno(errno, "Failed to open directory/subvolume we just created '%s': %m", path);
1968 }
1969
1970 if (fstat(fd, &st) < 0)
1971 return log_error_errno(errno, "Failed to fstat(%s): %m", path);
1972
1973 assert(S_ISDIR(st.st_mode)); /* we used O_DIRECTORY above */
1974
1975 log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), path);
1976
1977 if (ret_st)
1978 *ret_st = st;
1979 if (ret_creation)
1980 *ret_creation = creation;
1981
1982 return fd;
1983 }
1984
1985 static int create_directory(
1986 Context *c,
1987 Item *i,
1988 const char *path) {
1989
1990 _cleanup_close_ int fd = -EBADF;
1991 CreationMode creation;
1992 struct stat st;
1993
1994 assert(c);
1995 assert(i);
1996 assert(IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY));
1997
1998 fd = create_directory_or_subvolume(path, i->mode, /* subvol= */ false, i->allow_failure, &st, &creation);
1999 if (fd == -EEXIST)
2000 return 0;
2001 if (fd < 0)
2002 return fd;
2003
2004 return fd_set_perms(c, i, fd, path, &st, creation);
2005 }
2006
2007 static int create_subvolume(
2008 Context *c,
2009 Item *i,
2010 const char *path) {
2011
2012 _cleanup_close_ int fd = -EBADF;
2013 CreationMode creation;
2014 struct stat st;
2015 int r, q = 0;
2016
2017 assert(c);
2018 assert(i);
2019 assert(IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA));
2020
2021 fd = create_directory_or_subvolume(path, i->mode, /* subvol = */ true, i->allow_failure, &st, &creation);
2022 if (fd == -EEXIST)
2023 return 0;
2024 if (fd < 0)
2025 return fd;
2026
2027 if (creation == CREATION_NORMAL &&
2028 IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
2029 r = btrfs_subvol_auto_qgroup_fd(fd, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
2030 if (r == -ENOTTY)
2031 log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);
2032 else if (r == -EROFS)
2033 log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path);
2034 else if (r == -ENOTCONN)
2035 log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path);
2036 else if (r < 0)
2037 q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
2038 else if (r > 0)
2039 log_debug("Adjusted quota for subvolume \"%s\".", i->path);
2040 else if (r == 0)
2041 log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path);
2042 }
2043
2044 r = fd_set_perms(c, i, fd, path, &st, creation);
2045 if (q < 0) /* prefer the quota change error from above */
2046 return q;
2047
2048 return r;
2049 }
2050
2051 static int empty_directory(
2052 Context *c,
2053 Item *i,
2054 const char *path,
2055 CreationMode creation) {
2056
2057 _cleanup_close_ int fd = -EBADF;
2058 struct stat st;
2059 int r;
2060
2061 assert(c);
2062 assert(i);
2063 assert(i->type == EMPTY_DIRECTORY);
2064
2065 r = chase(path, arg_root, CHASE_SAFE|CHASE_WARN, NULL, &fd);
2066 if (r == -ENOLINK) /* Unsafe symlink: already covered by CHASE_WARN */
2067 return r;
2068 if (r == -ENOENT) {
2069 /* Option "e" operates only on existing objects. Do not print errors about non-existent files
2070 * or directories */
2071 log_debug_errno(r, "Skipping missing directory: %s", path);
2072 return 0;
2073 }
2074 if (r < 0)
2075 return log_error_errno(r, "Failed to open directory '%s': %m", path);
2076
2077 if (fstat(fd, &st) < 0)
2078 return log_error_errno(errno, "Failed to fstat(%s): %m", path);
2079 if (!S_ISDIR(st.st_mode)) {
2080 log_warning("'%s' already exists and is not a directory.", path);
2081 return 0;
2082 }
2083
2084 return fd_set_perms(c, i, fd, path, &st, creation);
2085 }
2086
2087 static int create_device(
2088 Context *c,
2089 Item *i,
2090 mode_t file_type) {
2091
2092 _cleanup_close_ int dfd = -EBADF, fd = -EBADF;
2093 _cleanup_free_ char *bn = NULL;
2094 CreationMode creation;
2095 struct stat st;
2096 int r;
2097
2098 assert(c);
2099 assert(i);
2100 assert(IN_SET(i->type, CREATE_BLOCK_DEVICE, CREATE_CHAR_DEVICE));
2101 assert(IN_SET(file_type, S_IFBLK, S_IFCHR));
2102
2103 r = path_extract_filename(i->path, &bn);
2104 if (r < 0)
2105 return log_error_errno(r, "Failed to extract filename from path '%s': %m", i->path);
2106 if (r == O_DIRECTORY)
2107 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Cannot open path '%s' for creating device node, is a directory.", i->path);
2108
2109 /* Validate the path and use the returned directory fd for copying the target so we're sure that the
2110 * path can't be changed behind our back. */
2111 dfd = path_open_parent_safe(i->path, i->allow_failure);
2112 if (dfd < 0)
2113 return dfd;
2114
2115 WITH_UMASK(0000) {
2116 mac_selinux_create_file_prepare(i->path, file_type);
2117 r = RET_NERRNO(mknodat(dfd, bn, i->mode | file_type, i->major_minor));
2118 mac_selinux_create_file_clear();
2119 }
2120 creation = r >= 0 ? CREATION_NORMAL : CREATION_EXISTING;
2121
2122 /* Try to open the inode via O_PATH, regardless if we could create it or not. Maybe everything is in
2123 * order anyway and we hence can ignore the error to create the device node */
2124 fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
2125 if (fd < 0) {
2126 /* OK, so opening the inode failed, let's look at the original error then. */
2127
2128 if (r < 0) {
2129 if (ERRNO_IS_PRIVILEGE(r))
2130 goto handle_privilege;
2131
2132 return log_error_errno(r, "Failed to create device node '%s': %m", i->path);
2133 }
2134
2135 return log_error_errno(errno, "Failed to open device node '%s' we just created: %m", i->path);
2136 }
2137
2138 if (fstat(fd, &st) < 0)
2139 return log_error_errno(errno, "Failed to fstat(%s): %m", i->path);
2140
2141 if (((st.st_mode ^ file_type) & S_IFMT) != 0) {
2142
2143 if (i->append_or_force) {
2144 fd = safe_close(fd);
2145
2146 WITH_UMASK(0000) {
2147 mac_selinux_create_file_prepare(i->path, file_type);
2148 r = mknodat_atomic(dfd, bn, i->mode | file_type, i->major_minor);
2149 mac_selinux_create_file_clear();
2150 }
2151 if (ERRNO_IS_PRIVILEGE(r))
2152 goto handle_privilege;
2153 if (IN_SET(r, -EISDIR, -EEXIST, -ENOTEMPTY)) {
2154 r = rm_rf_child(dfd, bn, REMOVE_PHYSICAL);
2155 if (r < 0)
2156 return log_error_errno(r, "rm -rf %s failed: %m", i->path);
2157
2158 mac_selinux_create_file_prepare(i->path, file_type);
2159 r = RET_NERRNO(mknodat(dfd, bn, i->mode | file_type, i->major_minor));
2160 mac_selinux_create_file_clear();
2161 }
2162 if (r < 0)
2163 return log_error_errno(r, "Failed to create device node '%s': %m", i->path);
2164
2165 fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
2166 if (fd < 0)
2167 return log_error_errno(errno, "Failed to open device node we just created '%s': %m", i->path);
2168
2169 /* Validate type before change ownership below */
2170 if (fstat(fd, &st) < 0)
2171 return log_error_errno(errno, "Failed to fstat(%s): %m", i->path);
2172
2173 if (((st.st_mode ^ file_type) & S_IFMT) != 0)
2174 return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Device node we just created is not a device node, refusing.");
2175
2176 creation = CREATION_FORCE;
2177 } else {
2178 log_warning("\"%s\" already exists and is not a device node.", i->path);
2179 return 0;
2180 }
2181 }
2182
2183 log_debug("%s %s device node \"%s\" %u:%u.",
2184 creation_mode_verb_to_string(creation),
2185 i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
2186 i->path, major(i->mode), minor(i->mode));
2187
2188 return fd_set_perms(c, i, fd, i->path, &st, creation);
2189
2190 handle_privilege:
2191 log_debug_errno(r,
2192 "We lack permissions, possibly because of cgroup configuration; "
2193 "skipping creation of device node '%s'.", i->path);
2194 return 0;
2195 }
2196
2197 static int create_fifo(Context *c, Item *i) {
2198 _cleanup_close_ int pfd = -EBADF, fd = -EBADF;
2199 _cleanup_free_ char *bn = NULL;
2200 CreationMode creation;
2201 struct stat st;
2202 int r;
2203
2204 assert(c);
2205 assert(i);
2206 assert(i->type == CREATE_FIFO);
2207
2208 r = path_extract_filename(i->path, &bn);
2209 if (r < 0)
2210 return log_error_errno(r, "Failed to extract filename from path '%s': %m", i->path);
2211 if (r == O_DIRECTORY)
2212 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Cannot open path '%s' for creating FIFO, is a directory.", i->path);
2213
2214 pfd = path_open_parent_safe(i->path, i->allow_failure);
2215 if (pfd < 0)
2216 return pfd;
2217
2218 WITH_UMASK(0000) {
2219 mac_selinux_create_file_prepare(i->path, S_IFIFO);
2220 r = RET_NERRNO(mkfifoat(pfd, bn, i->mode));
2221 mac_selinux_create_file_clear();
2222 }
2223
2224 creation = r >= 0 ? CREATION_NORMAL : CREATION_EXISTING;
2225
2226 /* Open the inode via O_PATH, regardless if we managed to create it or not. Maybe it is already the FIFO we want */
2227 fd = openat(pfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
2228 if (fd < 0) {
2229 if (r < 0)
2230 return log_error_errno(r, "Failed to create FIFO %s: %m", i->path); /* original error! */
2231
2232 return log_error_errno(errno, "Failed to open FIFO we just created %s: %m", i->path);
2233 }
2234
2235 if (fstat(fd, &st) < 0)
2236 return log_error_errno(errno, "Failed to fstat(%s): %m", i->path);
2237
2238 if (!S_ISFIFO(st.st_mode)) {
2239
2240 if (i->append_or_force) {
2241 fd = safe_close(fd);
2242
2243 WITH_UMASK(0000) {
2244 mac_selinux_create_file_prepare(i->path, S_IFIFO);
2245 r = mkfifoat_atomic(pfd, bn, i->mode);
2246 mac_selinux_create_file_clear();
2247 }
2248 if (IN_SET(r, -EISDIR, -EEXIST, -ENOTEMPTY)) {
2249 r = rm_rf_child(pfd, bn, REMOVE_PHYSICAL);
2250 if (r < 0)
2251 return log_error_errno(r, "rm -rf %s failed: %m", i->path);
2252
2253 mac_selinux_create_file_prepare(i->path, S_IFIFO);
2254 r = RET_NERRNO(mkfifoat(pfd, bn, i->mode));
2255 mac_selinux_create_file_clear();
2256 }
2257 if (r < 0)
2258 return log_error_errno(r, "Failed to create FIFO %s: %m", i->path);
2259
2260 fd = openat(pfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
2261 if (fd < 0)
2262 return log_error_errno(errno, "Failed to open FIFO we just created '%s': %m", i->path);
2263
2264 /* Validate type before change ownership below */
2265 if (fstat(fd, &st) < 0)
2266 return log_error_errno(errno, "Failed to fstat(%s): %m", i->path);
2267
2268 if (!S_ISFIFO(st.st_mode))
2269 return log_error_errno(SYNTHETIC_ERRNO(EBADF), "FIFO inode we just created is not a FIFO, refusing.");
2270
2271 creation = CREATION_FORCE;
2272 } else {
2273 log_warning("\"%s\" already exists and is not a FIFO.", i->path);
2274 return 0;
2275 }
2276 }
2277
2278 log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path);
2279
2280 return fd_set_perms(c, i, fd, i->path, &st, creation);
2281 }
2282
2283 static int create_symlink(Context *c, Item *i) {
2284 _cleanup_close_ int pfd = -EBADF, fd = -EBADF;
2285 _cleanup_free_ char *bn = NULL;
2286 CreationMode creation;
2287 struct stat st;
2288 bool good = false;
2289 int r;
2290
2291 assert(c);
2292 assert(i);
2293
2294 r = path_extract_filename(i->path, &bn);
2295 if (r < 0)
2296 return log_error_errno(r, "Failed to extract filename from path '%s': %m", i->path);
2297 if (r == O_DIRECTORY)
2298 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Cannot open path '%s' for creating FIFO, is a directory.", i->path);
2299
2300 pfd = path_open_parent_safe(i->path, i->allow_failure);
2301 if (pfd < 0)
2302 return pfd;
2303
2304 mac_selinux_create_file_prepare(i->path, S_IFLNK);
2305 r = RET_NERRNO(symlinkat(i->argument, pfd, bn));
2306 mac_selinux_create_file_clear();
2307
2308 creation = r >= 0 ? CREATION_NORMAL : CREATION_EXISTING;
2309
2310 fd = openat(pfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
2311 if (fd < 0) {
2312 if (r < 0)
2313 return log_error_errno(r, "Failed to create symlink '%s': %m", i->path); /* original error! */
2314
2315 return log_error_errno(errno, "Failed to open symlink we just created '%s': %m", i->path);
2316 }
2317
2318 if (fstat(fd, &st) < 0)
2319 return log_error_errno(errno, "Failed to fstat(%s): %m", i->path);
2320
2321 if (S_ISLNK(st.st_mode)) {
2322 _cleanup_free_ char *x = NULL;
2323
2324 r = readlinkat_malloc(fd, "", &x);
2325 if (r < 0)
2326 return log_error_errno(r, "readlinkat(%s) failed: %m", i->path);
2327
2328 good = streq(x, i->argument);
2329 } else
2330 good = false;
2331
2332 if (!good) {
2333 if (!i->append_or_force) {
2334 log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path);
2335 return 0;
2336 }
2337
2338 fd = safe_close(fd);
2339
2340 mac_selinux_create_file_prepare(i->path, S_IFLNK);
2341 r = symlinkat_atomic_full(i->argument, pfd, bn, /* make_relative= */ false);
2342 mac_selinux_create_file_clear();
2343 if (IN_SET(r, -EISDIR, -EEXIST, -ENOTEMPTY)) {
2344 r = rm_rf_child(pfd, bn, REMOVE_PHYSICAL);
2345 if (r < 0)
2346 return log_error_errno(r, "rm -rf %s failed: %m", i->path);
2347
2348 mac_selinux_create_file_prepare(i->path, S_IFLNK);
2349 r = RET_NERRNO(symlinkat(i->argument, pfd, i->path));
2350 mac_selinux_create_file_clear();
2351 }
2352 if (r < 0)
2353 return log_error_errno(r, "symlink(%s, %s) failed: %m", i->argument, i->path);
2354
2355 fd = openat(pfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
2356 if (fd < 0)
2357 return log_error_errno(errno, "Failed to open symlink we just created '%s': %m", i->path);
2358
2359 /* Validate type before change ownership below */
2360 if (fstat(fd, &st) < 0)
2361 return log_error_errno(errno, "Failed to fstat(%s): %m", i->path);
2362
2363 if (!S_ISLNK(st.st_mode))
2364 return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Symlink we just created is not a symlink, refusing.");
2365
2366 creation = CREATION_FORCE;
2367 }
2368
2369 log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path);
2370 return fd_set_perms(c, i, fd, i->path, &st, creation);
2371 }
2372
2373 typedef int (*action_t)(Context *c, Item *i, const char *path, CreationMode creation);
2374 typedef int (*fdaction_t)(Context *c, Item *i, int fd, const char *path, const struct stat *st, CreationMode creation);
2375
2376 static int item_do(
2377 Context *c,
2378 Item *i,
2379 int fd,
2380 const char *path,
2381 CreationMode creation,
2382 fdaction_t action) {
2383
2384 struct stat st;
2385 int r = 0, q;
2386
2387 assert(c);
2388 assert(i);
2389 assert(path);
2390 assert(fd >= 0);
2391
2392 if (fstat(fd, &st) < 0) {
2393 r = log_error_errno(errno, "fstat() on file failed: %m");
2394 goto finish;
2395 }
2396
2397 /* This returns the first error we run into, but nevertheless tries to go on */
2398 r = action(c, i, fd, path, &st, creation);
2399
2400 if (S_ISDIR(st.st_mode)) {
2401 _cleanup_closedir_ DIR *d = NULL;
2402
2403 /* The passed 'fd' was opened with O_PATH. We need to convert it into a 'regular' fd before
2404 * reading the directory content. */
2405 d = opendir(FORMAT_PROC_FD_PATH(fd));
2406 if (!d) {
2407 log_error_errno(errno, "Failed to opendir() '%s': %m", FORMAT_PROC_FD_PATH(fd));
2408 if (r == 0)
2409 r = -errno;
2410 goto finish;
2411 }
2412
2413 FOREACH_DIRENT_ALL(de, d, q = -errno; goto finish) {
2414 int de_fd;
2415
2416 if (dot_or_dot_dot(de->d_name))
2417 continue;
2418
2419 de_fd = openat(fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_PATH);
2420 if (de_fd < 0)
2421 q = log_error_errno(errno, "Failed to open() file '%s': %m", de->d_name);
2422 else {
2423 _cleanup_free_ char *de_path = NULL;
2424
2425 de_path = path_join(path, de->d_name);
2426 if (!de_path)
2427 q = log_oom();
2428 else
2429 /* Pass ownership of dirent fd over */
2430 q = item_do(c, i, de_fd, de_path, CREATION_EXISTING, action);
2431 }
2432
2433 if (q < 0 && r == 0)
2434 r = q;
2435 }
2436 }
2437 finish:
2438 safe_close(fd);
2439 return r;
2440 }
2441
2442 static int glob_item(Context *c, Item *i, action_t action) {
2443 _cleanup_globfree_ glob_t g = {
2444 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
2445 };
2446 int r = 0, k;
2447
2448 assert(c);
2449 assert(i);
2450
2451 k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
2452 if (k < 0 && k != -ENOENT)
2453 return log_error_errno(k, "glob(%s) failed: %m", i->path);
2454
2455 STRV_FOREACH(fn, g.gl_pathv) {
2456 /* We pass CREATION_EXISTING here, since if we are globbing for it, it always has to exist */
2457 k = action(c, i, *fn, CREATION_EXISTING);
2458 if (k < 0 && r == 0)
2459 r = k;
2460 }
2461
2462 return r;
2463 }
2464
2465 static int glob_item_recursively(
2466 Context *c,
2467 Item *i,
2468 fdaction_t action) {
2469
2470 _cleanup_globfree_ glob_t g = {
2471 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
2472 };
2473 int r = 0, k;
2474
2475 k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
2476 if (k < 0 && k != -ENOENT)
2477 return log_error_errno(k, "glob(%s) failed: %m", i->path);
2478
2479 STRV_FOREACH(fn, g.gl_pathv) {
2480 _cleanup_close_ int fd = -EBADF;
2481
2482 /* Make sure we won't trigger/follow file object (such as
2483 * device nodes, automounts, ...) pointed out by 'fn' with
2484 * O_PATH. Note, when O_PATH is used, flags other than
2485 * O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW are ignored. */
2486
2487 fd = open(*fn, O_CLOEXEC|O_NOFOLLOW|O_PATH);
2488 if (fd < 0) {
2489 log_error_errno(errno, "Opening '%s' failed: %m", *fn);
2490 if (r == 0)
2491 r = -errno;
2492 continue;
2493 }
2494
2495 k = item_do(c, i, fd, *fn, CREATION_EXISTING, action);
2496 if (k < 0 && r == 0)
2497 r = k;
2498
2499 /* we passed fd ownership to the previous call */
2500 fd = -EBADF;
2501 }
2502
2503 return r;
2504 }
2505
2506 static int rm_if_wrong_type_safe(
2507 mode_t mode,
2508 int parent_fd,
2509 const struct stat *parent_st, /* Only used if follow_links below is true. */
2510 const char *name,
2511 int flags) {
2512 _cleanup_free_ char *parent_name = NULL;
2513 bool follow_links = !FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW);
2514 struct stat st;
2515 int r;
2516
2517 assert(name);
2518 assert((mode & ~S_IFMT) == 0);
2519 assert(!follow_links || parent_st);
2520 assert((flags & ~AT_SYMLINK_NOFOLLOW) == 0);
2521
2522 if (!filename_is_valid(name))
2523 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "\"%s\" is not a valid filename.", name);
2524
2525 r = fstatat_harder(parent_fd, name, &st, flags, REMOVE_CHMOD | REMOVE_CHMOD_RESTORE);
2526 if (r < 0) {
2527 (void) fd_get_path(parent_fd, &parent_name);
2528 return log_full_errno(r == -ENOENT? LOG_DEBUG : LOG_ERR, r,
2529 "Failed to stat \"%s\" at \"%s\": %m", name, strna(parent_name));
2530 }
2531
2532 /* Fail before removing anything if this is an unsafe transition. */
2533 if (follow_links && unsafe_transition(parent_st, &st)) {
2534 (void) fd_get_path(parent_fd, &parent_name);
2535 return log_error_errno(SYNTHETIC_ERRNO(ENOLINK),
2536 "Unsafe transition from \"%s\" to \"%s\".", parent_name, name);
2537 }
2538
2539 if ((st.st_mode & S_IFMT) == mode)
2540 return 0;
2541
2542 (void) fd_get_path(parent_fd, &parent_name);
2543 log_notice("Wrong file type 0o%o; rm -rf \"%s/%s\"", st.st_mode & S_IFMT, strna(parent_name), name);
2544
2545 /* If the target of the symlink was the wrong type, the link needs to be removed instead of the
2546 * target, so make sure it is identified as a link and not a directory. */
2547 if (follow_links) {
2548 r = fstatat_harder(parent_fd, name, &st, AT_SYMLINK_NOFOLLOW, REMOVE_CHMOD | REMOVE_CHMOD_RESTORE);
2549 if (r < 0)
2550 return log_error_errno(r, "Failed to stat \"%s\" at \"%s\": %m", name, strna(parent_name));
2551 }
2552
2553 /* Do not remove mount points. */
2554 r = fd_is_mount_point(parent_fd, name, follow_links ? AT_SYMLINK_FOLLOW : 0);
2555 if (r < 0)
2556 (void) log_warning_errno(r, "Failed to check if \"%s/%s\" is a mount point: %m; Continuing",
2557 strna(parent_name), name);
2558 else if (r > 0)
2559 return log_error_errno(SYNTHETIC_ERRNO(EBUSY),
2560 "Not removing \"%s/%s\" because it is a mount point.", strna(parent_name), name);
2561
2562 if ((st.st_mode & S_IFMT) == S_IFDIR) {
2563 _cleanup_close_ int child_fd = -EBADF;
2564
2565 child_fd = openat(parent_fd, name, O_NOCTTY | O_CLOEXEC | O_DIRECTORY);
2566 if (child_fd < 0)
2567 return log_error_errno(errno, "Failed to open \"%s\" at \"%s\": %m", name, strna(parent_name));
2568
2569 r = rm_rf_children(TAKE_FD(child_fd), REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL, &st);
2570 if (r < 0)
2571 return log_error_errno(r, "Failed to remove contents of \"%s\" at \"%s\": %m", name, strna(parent_name));
2572
2573 r = unlinkat_harder(parent_fd, name, AT_REMOVEDIR, REMOVE_CHMOD | REMOVE_CHMOD_RESTORE);
2574 } else
2575 r = unlinkat_harder(parent_fd, name, 0, REMOVE_CHMOD | REMOVE_CHMOD_RESTORE);
2576 if (r < 0)
2577 return log_error_errno(r, "Failed to remove \"%s\" at \"%s\": %m", name, strna(parent_name));
2578
2579 /* This is covered by the log_notice "Wrong file type..." It is logged earlier because it gives
2580 * context to other error messages that might follow. */
2581 return -ENOENT;
2582 }
2583
2584 /* If child_mode is non-zero, rm_if_wrong_type_safe will be executed for the last path component. */
2585 static int mkdir_parents_rm_if_wrong_type(mode_t child_mode, const char *path) {
2586 _cleanup_close_ int parent_fd = -EBADF;
2587 struct stat parent_st;
2588 size_t path_len;
2589 int r;
2590
2591 assert(path);
2592 assert((child_mode & ~S_IFMT) == 0);
2593
2594 path_len = strlen(path);
2595
2596 if (!is_path(path))
2597 /* rm_if_wrong_type_safe already logs errors. */
2598 return child_mode != 0 ? rm_if_wrong_type_safe(child_mode, AT_FDCWD, NULL, path, AT_SYMLINK_NOFOLLOW) : 0;
2599
2600 if (child_mode != 0 && endswith(path, "/"))
2601 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2602 "Trailing path separators are only allowed if child_mode is not set; got \"%s\"", path);
2603
2604 /* Get the parent_fd and stat. */
2605 parent_fd = openat(AT_FDCWD, path_is_absolute(path) ? "/" : ".", O_NOCTTY | O_CLOEXEC | O_DIRECTORY);
2606 if (parent_fd < 0)
2607 return log_error_errno(errno, "Failed to open root: %m");
2608
2609 if (fstat(parent_fd, &parent_st) < 0)
2610 return log_error_errno(errno, "Failed to stat root: %m");
2611
2612 /* Check every parent directory in the path, except the last component */
2613 for (const char *e = path;;) {
2614 _cleanup_close_ int next_fd = -EBADF;
2615 char t[path_len + 1];
2616 const char *s;
2617
2618 /* Find the start of the next path component. */
2619 s = e + strspn(e, "/");
2620 /* Find the end of the next path component. */
2621 e = s + strcspn(s, "/");
2622
2623 /* Copy the path component to t so it can be a null terminated string. */
2624 *((char*) mempcpy(t, s, e - s)) = 0;
2625
2626 /* Is this the last component? If so, then check the type */
2627 if (*e == 0)
2628 return child_mode != 0 ? rm_if_wrong_type_safe(child_mode, parent_fd, &parent_st, t, AT_SYMLINK_NOFOLLOW) : 0;
2629
2630 r = rm_if_wrong_type_safe(S_IFDIR, parent_fd, &parent_st, t, 0);
2631 /* Remove dangling symlinks. */
2632 if (r == -ENOENT)
2633 r = rm_if_wrong_type_safe(S_IFDIR, parent_fd, &parent_st, t, AT_SYMLINK_NOFOLLOW);
2634 if (r == -ENOENT) {
2635 WITH_UMASK(0000)
2636 r = mkdirat_label(parent_fd, t, 0755);
2637 if (r < 0) {
2638 _cleanup_free_ char *parent_name = NULL;
2639
2640 (void) fd_get_path(parent_fd, &parent_name);
2641 return log_error_errno(r, "Failed to mkdir \"%s\" at \"%s\": %m", t, strnull(parent_name));
2642 }
2643 } else if (r < 0)
2644 /* rm_if_wrong_type_safe already logs errors. */
2645 return r;
2646
2647 next_fd = RET_NERRNO(openat(parent_fd, t, O_NOCTTY | O_CLOEXEC | O_DIRECTORY));
2648 if (next_fd < 0) {
2649 _cleanup_free_ char *parent_name = NULL;
2650
2651 (void) fd_get_path(parent_fd, &parent_name);
2652 return log_error_errno(next_fd, "Failed to open \"%s\" at \"%s\": %m", t, strnull(parent_name));
2653 }
2654 r = RET_NERRNO(fstat(next_fd, &parent_st));
2655 if (r < 0) {
2656 _cleanup_free_ char *parent_name = NULL;
2657
2658 (void) fd_get_path(parent_fd, &parent_name);
2659 return log_error_errno(r, "Failed to stat \"%s\" at \"%s\": %m", t, strnull(parent_name));
2660 }
2661
2662 close_and_replace(parent_fd, next_fd);
2663 }
2664 }
2665
2666 static int mkdir_parents_item(Item *i, mode_t child_mode) {
2667 int r;
2668 if (i->try_replace) {
2669 r = mkdir_parents_rm_if_wrong_type(child_mode, i->path);
2670 if (r < 0 && r != -ENOENT)
2671 return r;
2672 } else
2673 WITH_UMASK(0000)
2674 (void) mkdir_parents_label(i->path, 0755);
2675
2676 return 0;
2677 }
2678
2679 static int create_item(Context *c, Item *i) {
2680 int r;
2681
2682 assert(c);
2683 assert(i);
2684
2685 log_debug("Running create action for entry %c %s", (char) i->type, i->path);
2686
2687 switch (i->type) {
2688
2689 case IGNORE_PATH:
2690 case IGNORE_DIRECTORY_PATH:
2691 case REMOVE_PATH:
2692 case RECURSIVE_REMOVE_PATH:
2693 return 0;
2694
2695 case TRUNCATE_FILE:
2696 case CREATE_FILE:
2697 r = mkdir_parents_item(i, S_IFREG);
2698 if (r < 0)
2699 return r;
2700
2701 if ((i->type == CREATE_FILE && i->append_or_force) || i->type == TRUNCATE_FILE)
2702 r = truncate_file(c, i, i->path);
2703 else
2704 r = create_file(c, i, i->path);
2705 if (r < 0)
2706 return r;
2707 break;
2708
2709 case COPY_FILES:
2710 r = mkdir_parents_item(i, 0);
2711 if (r < 0)
2712 return r;
2713
2714 r = copy_files(c, i);
2715 if (r < 0)
2716 return r;
2717 break;
2718
2719 case WRITE_FILE:
2720 r = glob_item(c, i, write_one_file);
2721 if (r < 0)
2722 return r;
2723
2724 break;
2725
2726 case CREATE_DIRECTORY:
2727 case TRUNCATE_DIRECTORY:
2728 r = mkdir_parents_item(i, S_IFDIR);
2729 if (r < 0)
2730 return r;
2731
2732 r = create_directory(c, i, i->path);
2733 if (r < 0)
2734 return r;
2735 break;
2736
2737 case CREATE_SUBVOLUME:
2738 case CREATE_SUBVOLUME_INHERIT_QUOTA:
2739 case CREATE_SUBVOLUME_NEW_QUOTA:
2740 r = mkdir_parents_item(i, S_IFDIR);
2741 if (r < 0)
2742 return r;
2743
2744 r = create_subvolume(c, i, i->path);
2745 if (r < 0)
2746 return r;
2747 break;
2748
2749 case EMPTY_DIRECTORY:
2750 r = glob_item(c, i, empty_directory);
2751 if (r < 0)
2752 return r;
2753 break;
2754
2755 case CREATE_FIFO:
2756 r = mkdir_parents_item(i, S_IFIFO);
2757 if (r < 0)
2758 return r;
2759
2760 r = create_fifo(c, i);
2761 if (r < 0)
2762 return r;
2763 break;
2764
2765 case CREATE_SYMLINK:
2766 r = mkdir_parents_item(i, S_IFLNK);
2767 if (r < 0)
2768 return r;
2769
2770 r = create_symlink(c, i);
2771 if (r < 0)
2772 return r;
2773
2774 break;
2775
2776 case CREATE_BLOCK_DEVICE:
2777 case CREATE_CHAR_DEVICE:
2778 if (have_effective_cap(CAP_MKNOD) <= 0) {
2779 /* In a container we lack CAP_MKNOD. We shouldn't attempt to create the device node in that
2780 * case to avoid noise, and we don't support virtualized devices in containers anyway. */
2781
2782 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
2783 return 0;
2784 }
2785
2786 r = mkdir_parents_item(i, i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
2787 if (r < 0)
2788 return r;
2789
2790 r = create_device(c, i, i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
2791 if (r < 0)
2792 return r;
2793
2794 break;
2795
2796 case ADJUST_MODE:
2797 case RELABEL_PATH:
2798 r = glob_item(c, i, path_set_perms);
2799 if (r < 0)
2800 return r;
2801 break;
2802
2803 case RECURSIVE_RELABEL_PATH:
2804 r = glob_item_recursively(c, i, fd_set_perms);
2805 if (r < 0)
2806 return r;
2807 break;
2808
2809 case SET_XATTR:
2810 r = glob_item(c, i, path_set_xattrs);
2811 if (r < 0)
2812 return r;
2813 break;
2814
2815 case RECURSIVE_SET_XATTR:
2816 r = glob_item_recursively(c, i, fd_set_xattrs);
2817 if (r < 0)
2818 return r;
2819 break;
2820
2821 case SET_ACL:
2822 r = glob_item(c, i, path_set_acls);
2823 if (r < 0)
2824 return r;
2825 break;
2826
2827 case RECURSIVE_SET_ACL:
2828 r = glob_item_recursively(c, i, fd_set_acls);
2829 if (r < 0)
2830 return r;
2831 break;
2832
2833 case SET_ATTRIBUTE:
2834 r = glob_item(c, i, path_set_attribute);
2835 if (r < 0)
2836 return r;
2837 break;
2838
2839 case RECURSIVE_SET_ATTRIBUTE:
2840 r = glob_item_recursively(c, i, fd_set_attribute);
2841 if (r < 0)
2842 return r;
2843 break;
2844 }
2845
2846 return 0;
2847 }
2848
2849 static int purge_item_instance(Context *c, Item *i, const char *instance, CreationMode creation) {
2850 int r;
2851
2852 /* FIXME: we probably should use dir_cleanup() here instead of rm_rf() so that 'x' is honoured. */
2853 log_debug("rm -rf \"%s\"", instance);
2854 r = rm_rf(instance, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
2855 if (r < 0 && r != -ENOENT)
2856 return log_error_errno(r, "rm_rf(%s): %m", instance);
2857
2858 return 0;
2859 }
2860
2861 static int purge_item(Context *c, Item *i) {
2862
2863 assert(i);
2864
2865 if (!needs_purge(i->type))
2866 return 0;
2867
2868 log_debug("Running purge owned action for entry %c %s", (char) i->type, i->path);
2869
2870 if (needs_glob(i->type))
2871 return glob_item(c, i, purge_item_instance);
2872
2873 return purge_item_instance(c, i, i->path, CREATION_EXISTING);
2874 }
2875
2876 static int remove_item_instance(
2877 Context *c,
2878 Item *i,
2879 const char *instance,
2880 CreationMode creation) {
2881
2882 int r;
2883
2884 assert(c);
2885 assert(i);
2886
2887 switch (i->type) {
2888
2889 case REMOVE_PATH:
2890 if (remove(instance) < 0 && errno != ENOENT)
2891 return log_error_errno(errno, "rm(%s): %m", instance);
2892
2893 break;
2894
2895 case RECURSIVE_REMOVE_PATH:
2896 /* FIXME: we probably should use dir_cleanup() here instead of rm_rf() so that 'x' is honoured. */
2897 log_debug("rm -rf \"%s\"", instance);
2898 r = rm_rf(instance, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
2899 if (r < 0 && r != -ENOENT)
2900 return log_error_errno(r, "rm_rf(%s): %m", instance);
2901
2902 break;
2903
2904 default:
2905 assert_not_reached();
2906 }
2907
2908 return 0;
2909 }
2910
2911 static int remove_item(Context *c, Item *i) {
2912 int r;
2913
2914 assert(c);
2915 assert(i);
2916
2917 log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
2918
2919 switch (i->type) {
2920
2921 case TRUNCATE_DIRECTORY:
2922 /* FIXME: we probably should use dir_cleanup() here instead of rm_rf() so that 'x' is honoured. */
2923 log_debug("rm -rf \"%s\"", i->path);
2924 r = rm_rf(i->path, REMOVE_PHYSICAL);
2925 if (r < 0 && r != -ENOENT)
2926 return log_error_errno(r, "rm_rf(%s): %m", i->path);
2927
2928 return 0;
2929
2930 case REMOVE_PATH:
2931 case RECURSIVE_REMOVE_PATH:
2932 return glob_item(c, i, remove_item_instance);
2933
2934 default:
2935 return 0;
2936 }
2937 }
2938
2939 static char *age_by_to_string(AgeBy ab, bool is_dir) {
2940 static const char ab_map[] = { 'a', 'b', 'c', 'm' };
2941 size_t j = 0;
2942 char *ret;
2943
2944 ret = new(char, ELEMENTSOF(ab_map) + 1);
2945 if (!ret)
2946 return NULL;
2947
2948 for (size_t i = 0; i < ELEMENTSOF(ab_map); i++)
2949 if (FLAGS_SET(ab, 1U << i))
2950 ret[j++] = is_dir ? ascii_toupper(ab_map[i]) : ab_map[i];
2951
2952 ret[j] = 0;
2953 return ret;
2954 }
2955
2956 static int clean_item_instance(
2957 Context *c,
2958 Item *i,
2959 const char* instance,
2960 CreationMode creation) {
2961
2962 _cleanup_closedir_ DIR *d = NULL;
2963 STRUCT_STATX_DEFINE(sx);
2964 int mountpoint, r;
2965 usec_t cutoff, n;
2966
2967 assert(i);
2968
2969 if (!i->age_set)
2970 return 0;
2971
2972 n = now(CLOCK_REALTIME);
2973 if (n < i->age)
2974 return 0;
2975
2976 cutoff = n - i->age;
2977
2978 d = opendir_nomod(instance);
2979 if (!d) {
2980 if (IN_SET(errno, ENOENT, ENOTDIR)) {
2981 log_debug_errno(errno, "Directory \"%s\": %m", instance);
2982 return 0;
2983 }
2984
2985 return log_error_errno(errno, "Failed to open directory %s: %m", instance);
2986 }
2987
2988 r = statx_fallback(dirfd(d), "", AT_EMPTY_PATH, STATX_MODE|STATX_INO|STATX_ATIME|STATX_MTIME, &sx);
2989 if (r < 0)
2990 return log_error_errno(r, "statx(%s) failed: %m", instance);
2991
2992 if (FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT))
2993 mountpoint = FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT);
2994 else {
2995 struct stat ps;
2996
2997 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
2998 return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
2999
3000 mountpoint =
3001 sx.stx_dev_major != major(ps.st_dev) ||
3002 sx.stx_dev_minor != minor(ps.st_dev) ||
3003 sx.stx_ino != ps.st_ino;
3004 }
3005
3006 if (DEBUG_LOGGING) {
3007 _cleanup_free_ char *ab_f = NULL, *ab_d = NULL;
3008
3009 ab_f = age_by_to_string(i->age_by_file, false);
3010 if (!ab_f)
3011 return log_oom();
3012
3013 ab_d = age_by_to_string(i->age_by_dir, true);
3014 if (!ab_d)
3015 return log_oom();
3016
3017 log_debug("Cleanup threshold for %s \"%s\" is %s; age-by: %s%s",
3018 mountpoint ? "mount point" : "directory",
3019 instance,
3020 FORMAT_TIMESTAMP_STYLE(cutoff, TIMESTAMP_US),
3021 ab_f, ab_d);
3022 }
3023
3024 return dir_cleanup(c, i, instance, d,
3025 statx_timestamp_load_nsec(&sx.stx_atime),
3026 statx_timestamp_load_nsec(&sx.stx_mtime),
3027 cutoff * NSEC_PER_USEC,
3028 sx.stx_dev_major, sx.stx_dev_minor, mountpoint,
3029 MAX_DEPTH, i->keep_first_level,
3030 i->age_by_file, i->age_by_dir);
3031 }
3032
3033 static int clean_item(Context *c, Item *i) {
3034 assert(c);
3035 assert(i);
3036
3037 log_debug("Running clean action for entry %c %s", (char) i->type, i->path);
3038
3039 switch (i->type) {
3040
3041 case CREATE_DIRECTORY:
3042 case TRUNCATE_DIRECTORY:
3043 case CREATE_SUBVOLUME:
3044 case CREATE_SUBVOLUME_INHERIT_QUOTA:
3045 case CREATE_SUBVOLUME_NEW_QUOTA:
3046 case COPY_FILES:
3047 clean_item_instance(c, i, i->path, CREATION_EXISTING);
3048 return 0;
3049
3050 case EMPTY_DIRECTORY:
3051 case IGNORE_PATH:
3052 case IGNORE_DIRECTORY_PATH:
3053 return glob_item(c, i, clean_item_instance);
3054
3055 default:
3056 return 0;
3057 }
3058 }
3059
3060 static int process_item(
3061 Context *c,
3062 Item *i,
3063 OperationMask operation) {
3064
3065 OperationMask todo;
3066 _cleanup_free_ char *_path = NULL;
3067 const char *path;
3068 int r;
3069
3070 assert(c);
3071 assert(i);
3072
3073 todo = operation & ~i->done;
3074 if (todo == 0) /* Everything already done? */
3075 return 0;
3076
3077 i->done |= operation;
3078
3079 path = i->path;
3080 if (string_is_glob(path)) {
3081 /* We can't easily check whether a glob matches any autofs path, so let's do the check only
3082 * for the non-glob part. */
3083
3084 r = glob_non_glob_prefix(path, &_path);
3085 if (r < 0 && r != -ENOENT)
3086 return log_debug_errno(r, "Failed to deglob path: %m");
3087 if (r >= 0)
3088 path = _path;
3089 }
3090
3091 r = chase(path, arg_root, CHASE_NO_AUTOFS|CHASE_NONEXISTENT|CHASE_WARN, NULL, NULL);
3092 if (r == -EREMOTE) {
3093 log_notice_errno(r, "Skipping %s", i->path); /* We log the configured path, to not confuse the user. */
3094 return 0;
3095 }
3096 if (r < 0)
3097 log_debug_errno(r, "Failed to determine whether '%s' is below autofs, ignoring: %m", i->path);
3098
3099 r = FLAGS_SET(operation, OPERATION_CREATE) ? create_item(c, i) : 0;
3100 /* Failure can only be tolerated for create */
3101 if (i->allow_failure)
3102 r = 0;
3103
3104 RET_GATHER(r, FLAGS_SET(operation, OPERATION_REMOVE) ? remove_item(c, i) : 0);
3105 RET_GATHER(r, FLAGS_SET(operation, OPERATION_CLEAN) ? clean_item(c, i) : 0);
3106 RET_GATHER(r, FLAGS_SET(operation, OPERATION_PURGE) ? purge_item(c, i) : 0);
3107
3108 return r;
3109 }
3110
3111 static int process_item_array(
3112 Context *c,
3113 ItemArray *array,
3114 OperationMask operation) {
3115
3116 int r = 0;
3117 size_t n;
3118
3119 assert(c);
3120 assert(array);
3121
3122 /* Create any parent first. */
3123 if (FLAGS_SET(operation, OPERATION_CREATE) && array->parent)
3124 r = process_item_array(c, array->parent, operation & OPERATION_CREATE);
3125
3126 /* Clean up all children first */
3127 if ((operation & (OPERATION_REMOVE|OPERATION_CLEAN|OPERATION_PURGE)) && !set_isempty(array->children)) {
3128 ItemArray *cc;
3129
3130 SET_FOREACH(cc, array->children) {
3131 int k;
3132
3133 k = process_item_array(c, cc, operation & (OPERATION_REMOVE|OPERATION_CLEAN|OPERATION_PURGE));
3134 if (k < 0 && r == 0)
3135 r = k;
3136 }
3137 }
3138
3139 for (n = 0; n < array->n_items; n++) {
3140 int k;
3141
3142 k = process_item(c, array->items + n, operation);
3143 if (k < 0 && r == 0)
3144 r = k;
3145 }
3146
3147 return r;
3148 }
3149
3150 static void item_free_contents(Item *i) {
3151 assert(i);
3152 free(i->path);
3153 free(i->argument);
3154 free(i->binary_argument);
3155 strv_free(i->xattrs);
3156
3157 #if HAVE_ACL
3158 if (i->acl_access)
3159 acl_free(i->acl_access);
3160
3161 if (i->acl_access_exec)
3162 acl_free(i->acl_access_exec);
3163
3164 if (i->acl_default)
3165 acl_free(i->acl_default);
3166 #endif
3167 }
3168
3169 static ItemArray* item_array_free(ItemArray *a) {
3170 size_t n;
3171
3172 if (!a)
3173 return NULL;
3174
3175 for (n = 0; n < a->n_items; n++)
3176 item_free_contents(a->items + n);
3177
3178 set_free(a->children);
3179 free(a->items);
3180 return mfree(a);
3181 }
3182
3183 static int item_compare(const Item *a, const Item *b) {
3184 /* Make sure that the ownership taking item is put first, so
3185 * that we first create the node, and then can adjust it */
3186
3187 if (takes_ownership(a->type) && !takes_ownership(b->type))
3188 return -1;
3189 if (!takes_ownership(a->type) && takes_ownership(b->type))
3190 return 1;
3191
3192 return CMP(a->type, b->type);
3193 }
3194
3195 static bool item_compatible(const Item *a, const Item *b) {
3196 assert(a);
3197 assert(b);
3198 assert(streq(a->path, b->path));
3199
3200 if (takes_ownership(a->type) && takes_ownership(b->type))
3201 /* check if the items are the same */
3202 return memcmp_nn(item_binary_argument(a), item_binary_argument_size(a),
3203 item_binary_argument(b), item_binary_argument_size(b)) == 0 &&
3204
3205 a->uid_set == b->uid_set &&
3206 a->uid == b->uid &&
3207 a->uid_only_create == b->uid_only_create &&
3208
3209 a->gid_set == b->gid_set &&
3210 a->gid == b->gid &&
3211 a->gid_only_create == b->gid_only_create &&
3212
3213 a->mode_set == b->mode_set &&
3214 a->mode == b->mode &&
3215 a->mode_only_create == b->mode_only_create &&
3216
3217 a->age_set == b->age_set &&
3218 a->age == b->age &&
3219
3220 a->age_by_file == b->age_by_file &&
3221 a->age_by_dir == b->age_by_dir &&
3222
3223 a->mask_perms == b->mask_perms &&
3224
3225 a->keep_first_level == b->keep_first_level &&
3226
3227 a->major_minor == b->major_minor;
3228
3229 return true;
3230 }
3231
3232 static bool should_include_path(const char *path) {
3233 STRV_FOREACH(prefix, arg_exclude_prefixes)
3234 if (path_startswith(path, *prefix)) {
3235 log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
3236 path, *prefix);
3237 return false;
3238 }
3239
3240 STRV_FOREACH(prefix, arg_include_prefixes)
3241 if (path_startswith(path, *prefix)) {
3242 log_debug("Entry \"%s\" matches include prefix \"%s\".", path, *prefix);
3243 return true;
3244 }
3245
3246 /* no matches, so we should include this path only if we have no allow list at all */
3247 if (strv_isempty(arg_include_prefixes))
3248 return true;
3249
3250 log_debug("Entry \"%s\" does not match any include prefix, skipping.", path);
3251 return false;
3252 }
3253
3254 static int specifier_expansion_from_arg(const Specifier *specifier_table, Item *i) {
3255 int r;
3256
3257 assert(i);
3258
3259 if (!i->argument)
3260 return 0;
3261
3262 switch (i->type) {
3263 case COPY_FILES:
3264 case CREATE_SYMLINK:
3265 case CREATE_FILE:
3266 case TRUNCATE_FILE:
3267 case WRITE_FILE: {
3268 _cleanup_free_ char *unescaped = NULL, *resolved = NULL;
3269 ssize_t l;
3270
3271 l = cunescape(i->argument, 0, &unescaped);
3272 if (l < 0)
3273 return log_error_errno(l, "Failed to unescape parameter to write: %s", i->argument);
3274
3275 r = specifier_printf(unescaped, PATH_MAX-1, specifier_table, arg_root, NULL, &resolved);
3276 if (r < 0)
3277 return r;
3278
3279 return free_and_replace(i->argument, resolved);
3280 }
3281 case SET_XATTR:
3282 case RECURSIVE_SET_XATTR:
3283 STRV_FOREACH(xattr, i->xattrs) {
3284 _cleanup_free_ char *resolved = NULL;
3285
3286 r = specifier_printf(*xattr, SIZE_MAX, specifier_table, arg_root, NULL, &resolved);
3287 if (r < 0)
3288 return r;
3289
3290 free_and_replace(*xattr, resolved);
3291 }
3292 return 0;
3293
3294 default:
3295 return 0;
3296 }
3297 }
3298
3299 static int patch_var_run(const char *fname, unsigned line, char **path) {
3300 const char *k;
3301 char *n;
3302
3303 assert(path);
3304 assert(*path);
3305
3306 /* Optionally rewrites lines referencing /var/run/, to use /run/ instead. Why bother? tmpfiles merges lines in
3307 * some cases and detects conflicts in others. If files/directories are specified through two equivalent lines
3308 * this is problematic as neither case will be detected. Ideally we'd detect these cases by resolving symlinks
3309 * early, but that's precisely not what we can do here as this code very likely is running very early on, at a
3310 * time where the paths in question are not available yet, or even more importantly, our own tmpfiles rules
3311 * might create the paths that are intermediary to the listed paths. We can't really cover the generic case,
3312 * but the least we can do is cover the specific case of /var/run vs. /run, as /var/run is a legacy name for
3313 * /run only, and we explicitly document that and require that on systemd systems the former is a symlink to
3314 * the latter. Moreover files below this path are by far the primary use case for tmpfiles.d/. */
3315
3316 k = path_startswith(*path, "/var/run/");
3317 if (isempty(k)) /* Don't complain about other paths than /var/run, and not about /var/run itself either. */
3318 return 0;
3319
3320 n = path_join("/run", k);
3321 if (!n)
3322 return log_oom();
3323
3324 /* Also log about this briefly. We do so at LOG_NOTICE level, as we fixed up the situation automatically, hence
3325 * there's no immediate need for action by the user. However, in the interest of making things less confusing
3326 * to the user, let's still inform the user that these snippets should really be updated. */
3327 log_syntax(NULL, LOG_NOTICE, fname, line, 0,
3328 "Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.",
3329 *path, n);
3330
3331 free_and_replace(*path, n);
3332
3333 return 0;
3334 }
3335
3336 static int find_uid(const char *user, uid_t *ret_uid, Hashmap **cache) {
3337 int r;
3338
3339 assert(user);
3340 assert(ret_uid);
3341
3342 /* First: parse as numeric UID string */
3343 r = parse_uid(user, ret_uid);
3344 if (r >= 0)
3345 return r;
3346
3347 /* Second: pass to NSS if we are running "online" */
3348 if (!arg_root)
3349 return get_user_creds(&user, ret_uid, NULL, NULL, NULL, 0);
3350
3351 /* Third, synthesize "root" unconditionally */
3352 if (streq(user, "root")) {
3353 *ret_uid = 0;
3354 return 0;
3355 }
3356
3357 /* Fourth: use fgetpwent() to read /etc/passwd directly, if we are "offline" */
3358 return name_to_uid_offline(arg_root, user, ret_uid, cache);
3359 }
3360
3361 static int find_gid(const char *group, gid_t *ret_gid, Hashmap **cache) {
3362 int r;
3363
3364 assert(group);
3365 assert(ret_gid);
3366
3367 /* First: parse as numeric GID string */
3368 r = parse_gid(group, ret_gid);
3369 if (r >= 0)
3370 return r;
3371
3372 /* Second: pass to NSS if we are running "online" */
3373 if (!arg_root)
3374 return get_group_creds(&group, ret_gid, 0);
3375
3376 /* Third, synthesize "root" unconditionally */
3377 if (streq(group, "root")) {
3378 *ret_gid = 0;
3379 return 0;
3380 }
3381
3382 /* Fourth: use fgetgrent() to read /etc/group directly, if we are "offline" */
3383 return name_to_gid_offline(arg_root, group, ret_gid, cache);
3384 }
3385
3386 static int parse_age_by_from_arg(const char *age_by_str, Item *item) {
3387 AgeBy ab_f = 0, ab_d = 0;
3388
3389 static const struct {
3390 char age_by_chr;
3391 AgeBy age_by_flag;
3392 } age_by_types[] = {
3393 { 'a', AGE_BY_ATIME },
3394 { 'b', AGE_BY_BTIME },
3395 { 'c', AGE_BY_CTIME },
3396 { 'm', AGE_BY_MTIME },
3397 };
3398
3399 assert(age_by_str);
3400 assert(item);
3401
3402 if (isempty(age_by_str))
3403 return -EINVAL;
3404
3405 for (const char *s = age_by_str; *s != 0; s++) {
3406 size_t i;
3407
3408 /* Ignore whitespace. */
3409 if (strchr(WHITESPACE, *s))
3410 continue;
3411
3412 for (i = 0; i < ELEMENTSOF(age_by_types); i++) {
3413 /* Check lower-case for files, upper-case for directories. */
3414 if (*s == age_by_types[i].age_by_chr) {
3415 ab_f |= age_by_types[i].age_by_flag;
3416 break;
3417 } else if (*s == ascii_toupper(age_by_types[i].age_by_chr)) {
3418 ab_d |= age_by_types[i].age_by_flag;
3419 break;
3420 }
3421 }
3422
3423 /* Invalid character. */
3424 if (i >= ELEMENTSOF(age_by_types))
3425 return -EINVAL;
3426 }
3427
3428 /* No match. */
3429 if (ab_f == 0 && ab_d == 0)
3430 return -EINVAL;
3431
3432 item->age_by_file = ab_f > 0 ? ab_f : AGE_BY_DEFAULT_FILE;
3433 item->age_by_dir = ab_d > 0 ? ab_d : AGE_BY_DEFAULT_DIR;
3434
3435 return 0;
3436 }
3437
3438 static bool is_duplicated_item(ItemArray *existing, const Item *i) {
3439
3440 assert(existing);
3441 assert(i);
3442
3443 for (size_t n = 0; n < existing->n_items; n++) {
3444 const Item *e = existing->items + n;
3445
3446 if (item_compatible(e, i))
3447 continue;
3448
3449 /* Only multiple 'w+' lines for the same path are allowed. */
3450 if (e->type != WRITE_FILE || !e->append_or_force ||
3451 i->type != WRITE_FILE || !i->append_or_force)
3452 return true;
3453 }
3454
3455 return false;
3456 }
3457
3458 static int parse_line(
3459 Context *c,
3460 const char *fname,
3461 unsigned line,
3462 const char *buffer,
3463 bool *invalid_config,
3464 Hashmap **uid_cache,
3465 Hashmap **gid_cache) {
3466
3467 _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
3468 _cleanup_(item_free_contents) Item i = {
3469 /* The "age-by" argument considers all file timestamp types by default. */
3470 .age_by_file = AGE_BY_DEFAULT_FILE,
3471 .age_by_dir = AGE_BY_DEFAULT_DIR,
3472 };
3473 ItemArray *existing;
3474 OrderedHashmap *h;
3475 int r, pos;
3476 bool append_or_force = false, boot = false, allow_failure = false, try_replace = false,
3477 unbase64 = false, from_cred = false, missing_user_or_group = false;
3478
3479 assert(c);
3480 assert(fname);
3481 assert(line >= 1);
3482 assert(buffer);
3483
3484 const Specifier specifier_table[] = {
3485 { 'a', specifier_architecture, NULL },
3486 { 'b', specifier_boot_id, NULL },
3487 { 'B', specifier_os_build_id, NULL },
3488 { 'H', specifier_hostname, NULL },
3489 { 'l', specifier_short_hostname, NULL },
3490 { 'm', specifier_machine_id, NULL },
3491 { 'o', specifier_os_id, NULL },
3492 { 'v', specifier_kernel_release, NULL },
3493 { 'w', specifier_os_version_id, NULL },
3494 { 'W', specifier_os_variant_id, NULL },
3495
3496 { 'h', specifier_user_home, NULL },
3497
3498 { 'C', specifier_directory, UINT_TO_PTR(DIRECTORY_CACHE) },
3499 { 'L', specifier_directory, UINT_TO_PTR(DIRECTORY_LOGS) },
3500 { 'S', specifier_directory, UINT_TO_PTR(DIRECTORY_STATE) },
3501 { 't', specifier_directory, UINT_TO_PTR(DIRECTORY_RUNTIME) },
3502
3503 COMMON_CREDS_SPECIFIERS(arg_runtime_scope),
3504 COMMON_TMP_SPECIFIERS,
3505 {}
3506 };
3507
3508 r = extract_many_words(
3509 &buffer,
3510 NULL,
3511 EXTRACT_UNQUOTE | EXTRACT_CUNESCAPE,
3512 &action,
3513 &path,
3514 &mode,
3515 &user,
3516 &group,
3517 &age,
3518 NULL);
3519 if (r < 0) {
3520 if (IN_SET(r, -EINVAL, -EBADSLT))
3521 /* invalid quoting and such or an unknown specifier */
3522 *invalid_config = true;
3523 return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to parse line: %m");
3524 } else if (r < 2) {
3525 *invalid_config = true;
3526 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Syntax error.");
3527 }
3528
3529 if (!empty_or_dash(buffer)) {
3530 i.argument = strdup(buffer);
3531 if (!i.argument)
3532 return log_oom();
3533 }
3534
3535 if (isempty(action)) {
3536 *invalid_config = true;
3537 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Command too short '%s'.", action);
3538 }
3539
3540 for (pos = 1; action[pos]; pos++) {
3541 if (action[pos] == '!' && !boot)
3542 boot = true;
3543 else if (action[pos] == '+' && !append_or_force)
3544 append_or_force = true;
3545 else if (action[pos] == '-' && !allow_failure)
3546 allow_failure = true;
3547 else if (action[pos] == '=' && !try_replace)
3548 try_replace = true;
3549 else if (action[pos] == '~' && !unbase64)
3550 unbase64 = true;
3551 else if (action[pos] == '^' && !from_cred)
3552 from_cred = true;
3553 else {
3554 *invalid_config = true;
3555 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Unknown modifiers in command '%s'", action);
3556 }
3557 }
3558
3559 if (boot && !arg_boot) {
3560 log_syntax(NULL, LOG_DEBUG, fname, line, 0, "Ignoring entry %s \"%s\" because --boot is not specified.", action, path);
3561 return 0;
3562 }
3563
3564 i.type = action[0];
3565 i.append_or_force = append_or_force;
3566 i.allow_failure = allow_failure;
3567 i.try_replace = try_replace;
3568
3569 r = specifier_printf(path, PATH_MAX-1, specifier_table, arg_root, NULL, &i.path);
3570 if (ERRNO_IS_NOINFO(r))
3571 return log_unresolvable_specifier(fname, line);
3572 if (r < 0) {
3573 if (IN_SET(r, -EINVAL, -EBADSLT))
3574 *invalid_config = true;
3575 return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to replace specifiers in '%s': %m", path);
3576 }
3577
3578 r = patch_var_run(fname, line, &i.path);
3579 if (r < 0)
3580 return r;
3581
3582 if (!path_is_absolute(i.path)) {
3583 *invalid_config = true;
3584 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
3585 "Path '%s' not absolute.", i.path);
3586 }
3587
3588 path_simplify(i.path);
3589
3590 switch (i.type) {
3591
3592 case CREATE_DIRECTORY:
3593 case CREATE_SUBVOLUME:
3594 case CREATE_SUBVOLUME_INHERIT_QUOTA:
3595 case CREATE_SUBVOLUME_NEW_QUOTA:
3596 case EMPTY_DIRECTORY:
3597 case TRUNCATE_DIRECTORY:
3598 case CREATE_FIFO:
3599 case IGNORE_PATH:
3600 case IGNORE_DIRECTORY_PATH:
3601 case REMOVE_PATH:
3602 case RECURSIVE_REMOVE_PATH:
3603 case ADJUST_MODE:
3604 case RELABEL_PATH:
3605 case RECURSIVE_RELABEL_PATH:
3606 if (i.argument)
3607 log_syntax(NULL,
3608 LOG_WARNING,
3609 fname,
3610 line,
3611 0,
3612 "%c lines don't take argument fields, ignoring.",
3613 (char) i.type);
3614
3615 break;
3616
3617 case CREATE_FILE:
3618 case TRUNCATE_FILE:
3619 break;
3620
3621 case CREATE_SYMLINK:
3622 if (unbase64) {
3623 *invalid_config = true;
3624 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for symlink targets.");
3625 }
3626 break;
3627
3628 case WRITE_FILE:
3629 if (!i.argument) {
3630 *invalid_config = true;
3631 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Write file requires argument.");
3632 }
3633 break;
3634
3635 case COPY_FILES:
3636 if (unbase64) {
3637 *invalid_config = true;
3638 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for copy sources.");
3639 }
3640 break;
3641
3642 case CREATE_CHAR_DEVICE:
3643 case CREATE_BLOCK_DEVICE:
3644 if (unbase64) {
3645 *invalid_config = true;
3646 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for device node creation.");
3647 }
3648
3649 if (!i.argument) {
3650 *invalid_config = true;
3651 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Device file requires argument.");
3652 }
3653
3654 r = parse_devnum(i.argument, &i.major_minor);
3655 if (r < 0) {
3656 *invalid_config = true;
3657 return log_syntax(NULL, LOG_ERR, fname, line, r, "Can't parse device file major/minor '%s'.", i.argument);
3658 }
3659
3660 break;
3661
3662 case SET_XATTR:
3663 case RECURSIVE_SET_XATTR:
3664 if (unbase64) {
3665 *invalid_config = true;
3666 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for extended attributes.");
3667 }
3668 if (!i.argument) {
3669 *invalid_config = true;
3670 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
3671 "Set extended attribute requires argument.");
3672 }
3673 r = parse_xattrs_from_arg(&i);
3674 if (r < 0)
3675 return r;
3676 break;
3677
3678 case SET_ACL:
3679 case RECURSIVE_SET_ACL:
3680 if (unbase64) {
3681 *invalid_config = true;
3682 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for ACLs.");
3683 }
3684 if (!i.argument) {
3685 *invalid_config = true;
3686 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
3687 "Set ACLs requires argument.");
3688 }
3689 r = parse_acls_from_arg(&i);
3690 if (r < 0)
3691 return r;
3692 break;
3693
3694 case SET_ATTRIBUTE:
3695 case RECURSIVE_SET_ATTRIBUTE:
3696 if (unbase64) {
3697 *invalid_config = true;
3698 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for file attributes.");
3699 }
3700 if (!i.argument) {
3701 *invalid_config = true;
3702 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
3703 "Set file attribute requires argument.");
3704 }
3705 r = parse_attribute_from_arg(&i);
3706 if (IN_SET(r, -EINVAL, -EBADSLT))
3707 *invalid_config = true;
3708 if (r < 0)
3709 return r;
3710 break;
3711
3712 default:
3713 *invalid_config = true;
3714 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
3715 "Unknown command type '%c'.", (char) i.type);
3716 }
3717
3718 if (!should_include_path(i.path))
3719 return 0;
3720
3721 if (!unbase64) {
3722 /* Do specifier expansion except if base64 mode is enabled */
3723 r = specifier_expansion_from_arg(specifier_table, &i);
3724 if (ERRNO_IS_NOINFO(r))
3725 return log_unresolvable_specifier(fname, line);
3726 if (r < 0) {
3727 if (IN_SET(r, -EINVAL, -EBADSLT))
3728 *invalid_config = true;
3729 return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to substitute specifiers in argument: %m");
3730 }
3731 }
3732
3733 switch (i.type) {
3734 case CREATE_SYMLINK:
3735 if (!i.argument) {
3736 i.argument = path_join("/usr/share/factory", i.path);
3737 if (!i.argument)
3738 return log_oom();
3739 }
3740 break;
3741
3742 case COPY_FILES:
3743 if (!i.argument) {
3744 i.argument = path_join("/usr/share/factory", i.path);
3745 if (!i.argument)
3746 return log_oom();
3747 } else if (!path_is_absolute(i.argument)) {
3748 *invalid_config = true;
3749 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Source path '%s' is not absolute.", i.argument);
3750
3751 }
3752
3753 if (!empty_or_root(arg_root)) {
3754 char *p;
3755
3756 p = path_join(arg_root, i.argument);
3757 if (!p)
3758 return log_oom();
3759 free_and_replace(i.argument, p);
3760 }
3761
3762 path_simplify(i.argument);
3763
3764 if (laccess(i.argument, F_OK) == -ENOENT) {
3765 /* Silently skip over lines where the source file is missing. */
3766 log_syntax(NULL, LOG_DEBUG, fname, line, 0, "Copy source path '%s' does not exist, skipping line.", i.argument);
3767 return 0;
3768 }
3769
3770 break;
3771
3772 default:
3773 break;
3774 }
3775
3776 if (from_cred) {
3777 if (!i.argument)
3778 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL), "Reading from credential requested, but no credential name specified.");
3779 if (!credential_name_valid(i.argument))
3780 return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL), "Credential name not valid: %s", i.argument);
3781
3782 r = read_credential(i.argument, &i.binary_argument, &i.binary_argument_size);
3783 if (IN_SET(r, -ENXIO, -ENOENT)) {
3784 /* Silently skip over lines that have no credentials passed */
3785 log_syntax(NULL, LOG_DEBUG, fname, line, 0,
3786 "Credential '%s' not specified, skipping line.", i.argument);
3787 return 0;
3788 }
3789 if (r < 0)
3790 return log_error_errno(r, "Failed to read credential '%s': %m", i.argument);
3791 }
3792
3793 /* If base64 decoding is requested, do so now */
3794 if (unbase64 && item_binary_argument(&i)) {
3795 _cleanup_free_ void *data = NULL;
3796 size_t data_size = 0;
3797
3798 r = unbase64mem_full(item_binary_argument(&i), item_binary_argument_size(&i), /* secure = */ false,
3799 &data, &data_size);
3800 if (r < 0)
3801 return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to base64 decode specified argument '%s': %m", i.argument);
3802
3803 free_and_replace(i.binary_argument, data);
3804 i.binary_argument_size = data_size;
3805 }
3806
3807 if (!empty_or_root(arg_root)) {
3808 char *p;
3809
3810 p = path_join(arg_root, i.path);
3811 if (!p)
3812 return log_oom();
3813 free_and_replace(i.path, p);
3814 }
3815
3816 if (!empty_or_dash(user)) {
3817 const char *u;
3818
3819 u = startswith(user, ":");
3820 if (u)
3821 i.uid_only_create = true;
3822 else
3823 u = user;
3824
3825 r = find_uid(u, &i.uid, uid_cache);
3826 if (r == -ESRCH && arg_graceful) {
3827 log_syntax(NULL, LOG_DEBUG, fname, line, r,
3828 "%s: user '%s' not found, not adjusting ownership.", i.path, u);
3829 missing_user_or_group = true;
3830 } else if (r < 0) {
3831 *invalid_config = true;
3832 return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to resolve user '%s': %m", u);
3833 } else
3834 i.uid_set = true;
3835 }
3836
3837 if (!empty_or_dash(group)) {
3838 const char *g;
3839
3840 g = startswith(group, ":");
3841 if (g)
3842 i.gid_only_create = true;
3843 else
3844 g = group;
3845
3846 r = find_gid(g, &i.gid, gid_cache);
3847 if (r == -ESRCH && arg_graceful) {
3848 log_syntax(NULL, LOG_DEBUG, fname, line, r,
3849 "%s: group '%s' not found, not adjusting ownership.", i.path, g);
3850 missing_user_or_group = true;
3851 } else if (r < 0) {
3852 *invalid_config = true;
3853 return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to resolve group '%s': %m", g);
3854 } else
3855 i.gid_set = true;
3856 }
3857
3858 if (!empty_or_dash(mode)) {
3859 const char *mm;
3860 unsigned m;
3861
3862 for (mm = mode;; mm++) {
3863 if (*mm == '~')
3864 i.mask_perms = true;
3865 else if (*mm == ':')
3866 i.mode_only_create = true;
3867 else
3868 break;
3869 }
3870
3871 r = parse_mode(mm, &m);
3872 if (r < 0) {
3873 *invalid_config = true;
3874 return log_syntax(NULL, LOG_ERR, fname, line, r, "Invalid mode '%s'.", mode);
3875 }
3876
3877 i.mode = m;
3878 i.mode_set = true;
3879 } else
3880 i.mode = IN_SET(i.type,
3881 CREATE_DIRECTORY,
3882 TRUNCATE_DIRECTORY,
3883 CREATE_SUBVOLUME,
3884 CREATE_SUBVOLUME_INHERIT_QUOTA,
3885 CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644;
3886
3887 if (missing_user_or_group && (i.mode & ~0777) != 0) {
3888 /* Refuse any special bits for nodes where we couldn't resolve the ownership properly. */
3889 mode_t adjusted = i.mode & 0777;
3890 log_syntax(NULL, LOG_INFO, fname, line, 0,
3891 "Changing mode 0%o to 0%o because of changed ownership.", i.mode, adjusted);
3892 i.mode = adjusted;
3893 }
3894
3895 if (!empty_or_dash(age)) {
3896 const char *a = age;
3897 _cleanup_free_ char *seconds = NULL, *age_by = NULL;
3898
3899 if (*a == '~') {
3900 i.keep_first_level = true;
3901 a++;
3902 }
3903
3904 /* Format: "age-by:age"; where age-by is "[abcmABCM]+". */
3905 r = split_pair(a, ":", &age_by, &seconds);
3906 if (r == -ENOMEM)
3907 return log_oom();
3908 if (r < 0 && r != -EINVAL)
3909 return log_error_errno(r, "Failed to parse age-by for '%s': %m", age);
3910 if (r >= 0) {
3911 /* We found a ":", parse the "age-by" part. */
3912 r = parse_age_by_from_arg(age_by, &i);
3913 if (r == -ENOMEM)
3914 return log_oom();
3915 if (r < 0) {
3916 *invalid_config = true;
3917 return log_syntax(NULL, LOG_ERR, fname, line, r, "Invalid age-by '%s'.", age_by);
3918 }
3919
3920 /* For parsing the "age" part, after the ":". */
3921 a = seconds;
3922 }
3923
3924 r = parse_sec(a, &i.age);
3925 if (r < 0) {
3926 *invalid_config = true;
3927 return log_syntax(NULL, LOG_ERR, fname, line, r, "Invalid age '%s'.", a);
3928 }
3929
3930 i.age_set = true;
3931 }
3932
3933 h = needs_glob(i.type) ? c->globs : c->items;
3934
3935 existing = ordered_hashmap_get(h, i.path);
3936 if (existing) {
3937 if (is_duplicated_item(existing, &i)) {
3938 log_syntax(NULL, LOG_NOTICE, fname, line, 0,
3939 "Duplicate line for path \"%s\", ignoring.", i.path);
3940 return 0;
3941 }
3942 } else {
3943 existing = new0(ItemArray, 1);
3944 if (!existing)
3945 return log_oom();
3946
3947 r = ordered_hashmap_put(h, i.path, existing);
3948 if (r < 0) {
3949 free(existing);
3950 return log_oom();
3951 }
3952 }
3953
3954 if (!GREEDY_REALLOC(existing->items, existing->n_items + 1))
3955 return log_oom();
3956
3957 existing->items[existing->n_items++] = TAKE_STRUCT(i);
3958
3959 /* Sort item array, to enforce stable ordering of application */
3960 typesafe_qsort(existing->items, existing->n_items, item_compare);
3961
3962 return 0;
3963 }
3964
3965 static int cat_config(char **config_dirs, char **args) {
3966 _cleanup_strv_free_ char **files = NULL;
3967 int r;
3968
3969 r = conf_files_list_with_replacement(arg_root, config_dirs, arg_replace, &files, NULL);
3970 if (r < 0)
3971 return r;
3972
3973 pager_open(arg_pager_flags);
3974
3975 return cat_files(NULL, files, arg_cat_flags);
3976 }
3977
3978 static int exclude_default_prefixes(void) {
3979 int r;
3980
3981 /* Provide an easy way to exclude virtual/memory file systems from what we do here. Useful in
3982 * combination with --root= where we probably don't want to apply stuff to these dirs as they are
3983 * likely over-mounted if the root directory is actually used, and it wouldbe less than ideal to have
3984 * all kinds of files created/adjusted underneath these mount points. */
3985
3986 r = strv_extend_strv(
3987 &arg_exclude_prefixes,
3988 STRV_MAKE("/dev",
3989 "/proc",
3990 "/run",
3991 "/sys"),
3992 true);
3993 if (r < 0)
3994 return log_oom();
3995
3996 return 0;
3997 }
3998
3999 static int help(void) {
4000 _cleanup_free_ char *link = NULL;
4001 int r;
4002
4003 r = terminal_urlify_man("systemd-tmpfiles", "8", &link);
4004 if (r < 0)
4005 return log_oom();
4006
4007 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n"
4008 "\n%sCreates, deletes and cleans up volatile and temporary files and directories.%s\n\n"
4009 " -h --help Show this help\n"
4010 " --user Execute user configuration\n"
4011 " --version Show package version\n"
4012 " --cat-config Show configuration files\n"
4013 " --tldr Show non-comment parts of configuration\n"
4014 " --create Create marked files/directories\n"
4015 " --clean Clean up marked directories\n"
4016 " --remove Remove marked files/directories\n"
4017 " --boot Execute actions only safe at boot\n"
4018 " --graceful Quietly ignore unknown users or groups\n"
4019 " --purge Delete all files owned by the configuration files\n"
4020 " --prefix=PATH Only apply rules with the specified prefix\n"
4021 " --exclude-prefix=PATH Ignore rules with the specified prefix\n"
4022 " -E Ignore rules prefixed with /dev, /proc, /run, /sys\n"
4023 " --root=PATH Operate on an alternate filesystem root\n"
4024 " --image=PATH Operate on disk image as filesystem root\n"
4025 " --image-policy=POLICY Specify disk image dissection policy\n"
4026 " --replace=PATH Treat arguments as replacement for PATH\n"
4027 " --no-pager Do not pipe output into a pager\n"
4028 "\nSee the %s for details.\n",
4029 program_invocation_short_name,
4030 ansi_highlight(),
4031 ansi_normal(),
4032 link);
4033
4034 return 0;
4035 }
4036
4037 static int parse_argv(int argc, char *argv[]) {
4038
4039 enum {
4040 ARG_VERSION = 0x100,
4041 ARG_CAT_CONFIG,
4042 ARG_TLDR,
4043 ARG_USER,
4044 ARG_CREATE,
4045 ARG_CLEAN,
4046 ARG_REMOVE,
4047 ARG_PURGE,
4048 ARG_BOOT,
4049 ARG_GRACEFUL,
4050 ARG_PREFIX,
4051 ARG_EXCLUDE_PREFIX,
4052 ARG_ROOT,
4053 ARG_IMAGE,
4054 ARG_IMAGE_POLICY,
4055 ARG_REPLACE,
4056 ARG_NO_PAGER,
4057 };
4058
4059 static const struct option options[] = {
4060 { "help", no_argument, NULL, 'h' },
4061 { "user", no_argument, NULL, ARG_USER },
4062 { "version", no_argument, NULL, ARG_VERSION },
4063 { "cat-config", no_argument, NULL, ARG_CAT_CONFIG },
4064 { "tldr", no_argument, NULL, ARG_TLDR },
4065 { "create", no_argument, NULL, ARG_CREATE },
4066 { "clean", no_argument, NULL, ARG_CLEAN },
4067 { "remove", no_argument, NULL, ARG_REMOVE },
4068 { "purge", no_argument, NULL, ARG_PURGE },
4069 { "boot", no_argument, NULL, ARG_BOOT },
4070 { "graceful", no_argument, NULL, ARG_GRACEFUL },
4071 { "prefix", required_argument, NULL, ARG_PREFIX },
4072 { "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
4073 { "root", required_argument, NULL, ARG_ROOT },
4074 { "image", required_argument, NULL, ARG_IMAGE },
4075 { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
4076 { "replace", required_argument, NULL, ARG_REPLACE },
4077 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
4078 {}
4079 };
4080
4081 int c, r;
4082
4083 assert(argc >= 0);
4084 assert(argv);
4085
4086 while ((c = getopt_long(argc, argv, "hE", options, NULL)) >= 0)
4087
4088 switch (c) {
4089
4090 case 'h':
4091 return help();
4092
4093 case ARG_VERSION:
4094 return version();
4095
4096 case ARG_CAT_CONFIG:
4097 arg_cat_flags = CAT_CONFIG_ON;
4098 break;
4099
4100 case ARG_TLDR:
4101 arg_cat_flags = CAT_TLDR;
4102 break;
4103
4104 case ARG_USER:
4105 arg_runtime_scope = RUNTIME_SCOPE_USER;
4106 break;
4107
4108 case ARG_CREATE:
4109 arg_operation |= OPERATION_CREATE;
4110 break;
4111
4112 case ARG_CLEAN:
4113 arg_operation |= OPERATION_CLEAN;
4114 break;
4115
4116 case ARG_REMOVE:
4117 arg_operation |= OPERATION_REMOVE;
4118 break;
4119
4120 case ARG_BOOT:
4121 arg_boot = true;
4122 break;
4123
4124 case ARG_PURGE:
4125 arg_operation |= OPERATION_PURGE;
4126 break;
4127
4128 case ARG_GRACEFUL:
4129 arg_graceful = true;
4130 break;
4131
4132 case ARG_PREFIX:
4133 if (strv_extend(&arg_include_prefixes, optarg) < 0)
4134 return log_oom();
4135 break;
4136
4137 case ARG_EXCLUDE_PREFIX:
4138 if (strv_extend(&arg_exclude_prefixes, optarg) < 0)
4139 return log_oom();
4140 break;
4141
4142 case ARG_ROOT:
4143 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_root);
4144 if (r < 0)
4145 return r;
4146 break;
4147
4148 case ARG_IMAGE:
4149 #ifdef STANDALONE
4150 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
4151 "This systemd-tmpfiles version is compiled without support for --image=.");
4152 #else
4153 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
4154 if (r < 0)
4155 return r;
4156 #endif
4157 /* Imply -E here since it makes little sense to create files persistently in the /run mountpoint of a disk image */
4158 _fallthrough_;
4159
4160 case 'E':
4161 r = exclude_default_prefixes();
4162 if (r < 0)
4163 return r;
4164
4165 break;
4166
4167 case ARG_IMAGE_POLICY:
4168 r = parse_image_policy_argument(optarg, &arg_image_policy);
4169 if (r < 0)
4170 return r;
4171 break;
4172
4173 case ARG_REPLACE:
4174 if (!path_is_absolute(optarg) ||
4175 !endswith(optarg, ".conf"))
4176 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
4177 "The argument to --replace= must an absolute path to a config file");
4178
4179 arg_replace = optarg;
4180 break;
4181
4182 case ARG_NO_PAGER:
4183 arg_pager_flags |= PAGER_DISABLE;
4184 break;
4185
4186 case '?':
4187 return -EINVAL;
4188
4189 default:
4190 assert_not_reached();
4191 }
4192
4193 if (arg_operation == 0 && arg_cat_flags == CAT_CONFIG_OFF)
4194 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
4195 "You need to specify at least one of --clean, --create, --remove, or --purge.");
4196
4197 if (arg_replace && arg_cat_flags != CAT_CONFIG_OFF)
4198 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
4199 "Option --replace= is not supported with --cat-config/--tldr.");
4200
4201 if (arg_replace && optind >= argc)
4202 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
4203 "When --replace= is given, some configuration items must be specified.");
4204
4205 if (arg_root && arg_runtime_scope == RUNTIME_SCOPE_USER)
4206 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
4207 "Combination of --user and --root= is not supported.");
4208
4209 if (arg_image && arg_root)
4210 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
4211
4212 return 1;
4213 }
4214
4215 static int read_config_file(
4216 Context *c,
4217 char **config_dirs,
4218 const char *fn,
4219 bool ignore_enoent,
4220 bool *invalid_config) {
4221
4222 _cleanup_hashmap_free_ Hashmap *uid_cache = NULL, *gid_cache = NULL;
4223 _cleanup_fclose_ FILE *_f = NULL;
4224 _cleanup_free_ char *pp = NULL;
4225 unsigned v = 0;
4226 FILE *f;
4227 ItemArray *ia;
4228 int r = 0;
4229
4230 assert(c);
4231 assert(fn);
4232
4233 if (streq(fn, "-")) {
4234 log_debug("Reading config from stdin%s", special_glyph(SPECIAL_GLYPH_ELLIPSIS));
4235 fn = "<stdin>";
4236 f = stdin;
4237 } else {
4238 r = search_and_fopen(fn, "re", arg_root, (const char**) config_dirs, &_f, &pp);
4239 if (r < 0) {
4240 if (ignore_enoent && r == -ENOENT) {
4241 log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn);
4242 return 0;
4243 }
4244
4245 return log_error_errno(r, "Failed to open '%s': %m", fn);
4246 }
4247
4248 log_debug("Reading config file \"%s\"%s", pp, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
4249 fn = pp;
4250 f = _f;
4251 }
4252
4253 for (;;) {
4254 _cleanup_free_ char *line = NULL;
4255 bool invalid_line = false;
4256 int k;
4257
4258 k = read_stripped_line(f, LONG_LINE_MAX, &line);
4259 if (k < 0)
4260 return log_error_errno(k, "Failed to read '%s': %m", fn);
4261 if (k == 0)
4262 break;
4263
4264 v++;
4265
4266 if (IN_SET(line[0], 0, '#'))
4267 continue;
4268
4269 k = parse_line(c, fn, v, line, &invalid_line, &uid_cache, &gid_cache);
4270 if (k < 0) {
4271 if (invalid_line)
4272 /* Allow reporting with a special code if the caller requested this */
4273 *invalid_config = true;
4274 else if (r == 0)
4275 /* The first error becomes our return value */
4276 r = k;
4277 }
4278 }
4279
4280 /* we have to determine age parameter for each entry of type X */
4281 ORDERED_HASHMAP_FOREACH(ia, c->globs)
4282 for (size_t ni = 0; ni < ia->n_items; ni++) {
4283 ItemArray *ja;
4284 Item *i = ia->items + ni, *candidate_item = NULL;
4285
4286 if (i->type != IGNORE_DIRECTORY_PATH)
4287 continue;
4288
4289 ORDERED_HASHMAP_FOREACH(ja, c->items)
4290 for (size_t nj = 0; nj < ja->n_items; nj++) {
4291 Item *j = ja->items + nj;
4292
4293 if (!IN_SET(j->type, CREATE_DIRECTORY,
4294 TRUNCATE_DIRECTORY,
4295 CREATE_SUBVOLUME,
4296 CREATE_SUBVOLUME_INHERIT_QUOTA,
4297 CREATE_SUBVOLUME_NEW_QUOTA))
4298 continue;
4299
4300 if (path_equal(j->path, i->path)) {
4301 candidate_item = j;
4302 break;
4303 }
4304
4305 if (candidate_item
4306 ? (path_startswith(j->path, candidate_item->path) && fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)
4307 : path_startswith(i->path, j->path) != NULL)
4308 candidate_item = j;
4309 }
4310
4311 if (candidate_item && candidate_item->age_set) {
4312 i->age = candidate_item->age;
4313 i->age_set = true;
4314 }
4315 }
4316
4317 if (ferror(f)) {
4318 log_error_errno(errno, "Failed to read from file %s: %m", fn);
4319 if (r == 0)
4320 r = -EIO;
4321 }
4322
4323 return r;
4324 }
4325
4326 static int parse_arguments(
4327 Context *c,
4328 char **config_dirs,
4329 char **args,
4330 bool *invalid_config) {
4331 int r;
4332
4333 assert(c);
4334
4335 STRV_FOREACH(arg, args) {
4336 r = read_config_file(c, config_dirs, *arg, false, invalid_config);
4337 if (r < 0)
4338 return r;
4339 }
4340
4341 return 0;
4342 }
4343
4344 static int read_config_files(
4345 Context *c,
4346 char **config_dirs,
4347 char **args,
4348 bool *invalid_config) {
4349
4350 _cleanup_strv_free_ char **files = NULL;
4351 _cleanup_free_ char *p = NULL;
4352 int r;
4353
4354 assert(c);
4355
4356 r = conf_files_list_with_replacement(arg_root, config_dirs, arg_replace, &files, &p);
4357 if (r < 0)
4358 return r;
4359
4360 STRV_FOREACH(f, files)
4361 if (p && path_equal(*f, p)) {
4362 log_debug("Parsing arguments at position \"%s\"%s", *f, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
4363
4364 r = parse_arguments(c, config_dirs, args, invalid_config);
4365 if (r < 0)
4366 return r;
4367 } else
4368 /* Just warn, ignore result otherwise.
4369 * read_config_file() has some debug output, so no need to print anything. */
4370 (void) read_config_file(c, config_dirs, *f, true, invalid_config);
4371
4372 return 0;
4373 }
4374
4375 static int read_credential_lines(Context *c, bool *invalid_config) {
4376
4377 _cleanup_free_ char *j = NULL;
4378 const char *d;
4379 int r;
4380
4381 assert(c);
4382
4383 r = get_credentials_dir(&d);
4384 if (r == -ENXIO)
4385 return 0;
4386 if (r < 0)
4387 return log_error_errno(r, "Failed to get credentials directory: %m");
4388
4389 j = path_join(d, "tmpfiles.extra");
4390 if (!j)
4391 return log_oom();
4392
4393 (void) read_config_file(c, /* config_dirs= */ NULL, j, /* ignore_enoent= */ true, invalid_config);
4394 return 0;
4395 }
4396
4397 static int link_parent(Context *c, ItemArray *a) {
4398 const char *path;
4399 char *prefix;
4400 int r;
4401
4402 assert(c);
4403 assert(a);
4404
4405 /* Finds the closest "parent" item array for the specified item array. Then registers the specified item array
4406 * as child of it, and fills the parent in, linking them both ways. This allows us to later create parents
4407 * before their children, and clean up/remove children before their parents. */
4408
4409 if (a->n_items <= 0)
4410 return 0;
4411
4412 path = a->items[0].path;
4413 prefix = newa(char, strlen(path) + 1);
4414 PATH_FOREACH_PREFIX(prefix, path) {
4415 ItemArray *j;
4416
4417 j = ordered_hashmap_get(c->items, prefix);
4418 if (!j)
4419 j = ordered_hashmap_get(c->globs, prefix);
4420 if (j) {
4421 r = set_ensure_put(&j->children, NULL, a);
4422 if (r < 0)
4423 return log_oom();
4424
4425 a->parent = j;
4426 return 1;
4427 }
4428 }
4429
4430 return 0;
4431 }
4432
4433 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_array_hash_ops, char, string_hash_func, string_compare_func,
4434 ItemArray, item_array_free);
4435
4436 static int run(int argc, char *argv[]) {
4437 #ifndef STANDALONE
4438 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
4439 _cleanup_(umount_and_freep) char *mounted_dir = NULL;
4440 #endif
4441 _cleanup_strv_free_ char **config_dirs = NULL;
4442 _cleanup_(context_done) Context c = {};
4443 bool invalid_config = false;
4444 ItemArray *a;
4445 enum {
4446 PHASE_PURGE,
4447 PHASE_REMOVE_AND_CLEAN,
4448 PHASE_CREATE,
4449 _PHASE_MAX
4450 } phase;
4451 int r, k;
4452
4453 r = parse_argv(argc, argv);
4454 if (r <= 0)
4455 return r;
4456
4457 log_setup();
4458
4459 /* We require /proc/ for a lot of our operations, i.e. for adjusting access modes, for anything
4460 * SELinux related, for recursive operation, for xattr, acl and chattr handling, for btrfs stuff and
4461 * a lot more. It's probably the majority of invocations where /proc/ is required. Since people
4462 * apparently invoke it without anyway and are surprised about the failures, let's catch this early
4463 * and output a nice and friendly warning. */
4464 if (proc_mounted() == 0)
4465 return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
4466 "/proc/ is not mounted, but required for successful operation of systemd-tmpfiles. "
4467 "Please mount /proc/. Alternatively, consider using the --root= or --image= switches.");
4468
4469 /* Descending down file system trees might take a lot of fds */
4470 (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
4471
4472 switch (arg_runtime_scope) {
4473
4474 case RUNTIME_SCOPE_USER:
4475 r = user_config_paths(&config_dirs);
4476 if (r < 0)
4477 return log_error_errno(r, "Failed to initialize configuration directory list: %m");
4478 break;
4479
4480 case RUNTIME_SCOPE_SYSTEM:
4481 config_dirs = strv_split_nulstr(CONF_PATHS_NULSTR("tmpfiles.d"));
4482 if (!config_dirs)
4483 return log_oom();
4484 break;
4485
4486 default:
4487 assert_not_reached();
4488 }
4489
4490 if (DEBUG_LOGGING) {
4491 _cleanup_free_ char *t = NULL;
4492
4493 STRV_FOREACH(i, config_dirs) {
4494 _cleanup_free_ char *j = NULL;
4495
4496 j = path_join(arg_root, *i);
4497 if (!j)
4498 return log_oom();
4499
4500 if (!strextend(&t, "\n\t", j))
4501 return log_oom();
4502 }
4503
4504 log_debug("Looking for configuration files in (higher priority first):%s", t);
4505 }
4506
4507 if (arg_cat_flags != CAT_CONFIG_OFF)
4508 return cat_config(config_dirs, argv + optind);
4509
4510 umask(0022);
4511
4512 r = mac_init();
4513 if (r < 0)
4514 return r;
4515
4516 #ifndef STANDALONE
4517 if (arg_image) {
4518 assert(!arg_root);
4519
4520 r = mount_image_privately_interactively(
4521 arg_image,
4522 arg_image_policy,
4523 DISSECT_IMAGE_GENERIC_ROOT |
4524 DISSECT_IMAGE_REQUIRE_ROOT |
4525 DISSECT_IMAGE_VALIDATE_OS |
4526 DISSECT_IMAGE_RELAX_VAR_CHECK |
4527 DISSECT_IMAGE_FSCK |
4528 DISSECT_IMAGE_GROWFS,
4529 &mounted_dir,
4530 /* ret_dir_fd= */ NULL,
4531 &loop_device);
4532 if (r < 0)
4533 return r;
4534
4535 arg_root = strdup(mounted_dir);
4536 if (!arg_root)
4537 return log_oom();
4538 }
4539 #else
4540 assert(!arg_image);
4541 #endif
4542
4543 c.items = ordered_hashmap_new(&item_array_hash_ops);
4544 c.globs = ordered_hashmap_new(&item_array_hash_ops);
4545 if (!c.items || !c.globs)
4546 return log_oom();
4547
4548 /* If command line arguments are specified along with --replace, read all
4549 * configuration files and insert the positional arguments at the specified
4550 * place. Otherwise, if command line arguments are specified, execute just
4551 * them, and finally, without --replace= or any positional arguments, just
4552 * read configuration and execute it.
4553 */
4554 if (arg_replace || optind >= argc)
4555 r = read_config_files(&c, config_dirs, argv + optind, &invalid_config);
4556 else
4557 r = parse_arguments(&c, config_dirs, argv + optind, &invalid_config);
4558 if (r < 0)
4559 return r;
4560
4561 r = read_credential_lines(&c, &invalid_config);
4562 if (r < 0)
4563 return r;
4564
4565 /* Let's now link up all child/parent relationships */
4566 ORDERED_HASHMAP_FOREACH(a, c.items) {
4567 r = link_parent(&c, a);
4568 if (r < 0)
4569 return r;
4570 }
4571 ORDERED_HASHMAP_FOREACH(a, c.globs) {
4572 r = link_parent(&c, a);
4573 if (r < 0)
4574 return r;
4575 }
4576
4577 /* If multiple operations are requested, let's first run the remove/clean operations, and only then the create
4578 * operations. i.e. that we first clean out the platform we then build on. */
4579 for (phase = 0; phase < _PHASE_MAX; phase++) {
4580 OperationMask op;
4581
4582 if (phase == PHASE_PURGE)
4583 op = arg_operation & OPERATION_PURGE;
4584 else if (phase == PHASE_REMOVE_AND_CLEAN)
4585 op = arg_operation & (OPERATION_REMOVE|OPERATION_CLEAN);
4586 else if (phase == PHASE_CREATE)
4587 op = arg_operation & OPERATION_CREATE;
4588 else
4589 assert_not_reached();
4590
4591 if (op == 0) /* Nothing requested in this phase */
4592 continue;
4593
4594 /* The non-globbing ones usually create things, hence we apply them first */
4595 ORDERED_HASHMAP_FOREACH(a, c.items) {
4596 k = process_item_array(&c, a, op);
4597 if (k < 0 && r >= 0)
4598 r = k;
4599 }
4600
4601 /* The globbing ones usually alter things, hence we apply them second. */
4602 ORDERED_HASHMAP_FOREACH(a, c.globs) {
4603 k = process_item_array(&c, a, op);
4604 if (k < 0 && r >= 0)
4605 r = k;
4606 }
4607 }
4608
4609 if (ERRNO_IS_RESOURCE(r))
4610 return r;
4611 if (invalid_config)
4612 return EX_DATAERR;
4613 if (r < 0)
4614 return EX_CANTCREAT;
4615 return 0;
4616 }
4617
4618 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);