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