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