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