]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/tmpfiles/tmpfiles.c
tmpfiles: use qsort_safe
[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) {
1924a97d
MO
586 struct stat st;
587 bool st_valid;
588
cde684a2
LP
589 assert(i);
590 assert(path);
591
1924a97d
MO
592 st_valid = stat(path, &st) == 0;
593
062e01bb 594 /* not using i->path directly because it may be a glob */
abef3f91
LP
595 if (i->mode_set) {
596 mode_t m = i->mode;
597
1924a97d
MO
598 if (i->mask_perms && st_valid) {
599 if (!(st.st_mode & 0111))
600 m &= ~0111;
601 if (!(st.st_mode & 0222))
602 m &= ~0222;
603 if (!(st.st_mode & 0444))
604 m &= ~0444;
605 if (!S_ISDIR(st.st_mode))
606 m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
abef3f91
LP
607 }
608
582deb84
ZJS
609 if (st_valid && m == (st.st_mode & 07777))
610 log_debug("\"%s\" has right mode %o", path, st.st_mode);
611 else {
612 log_debug("chmod \"%s\" to mode %o", path, m);
4a62c710
MS
613 if (chmod(path, m) < 0)
614 return log_error_errno(errno, "chmod(%s) failed: %m", path);
062e01bb 615 }
abef3f91 616 }
062e01bb 617
582deb84
ZJS
618 if ((!st_valid || i->uid != st.st_uid || i->gid != st.st_gid) &&
619 (i->uid_set || i->gid_set)) {
620 log_debug("chown \"%s\" to "UID_FMT"."GID_FMT,
621 path,
622 i->uid_set ? i->uid : UID_INVALID,
623 i->gid_set ? i->gid : GID_INVALID);
062e01bb 624 if (chown(path,
fed1e721 625 i->uid_set ? i->uid : UID_INVALID,
505ef0e3 626 i->gid_set ? i->gid : GID_INVALID) < 0)
062e01bb 627
505ef0e3 628 return log_error_errno(errno, "chown(%s) failed: %m", path);
582deb84 629 }
062e01bb 630
9855d6c7 631 return label_fix(path, false, false);
062e01bb
MS
632}
633
bd550f78 634static int parse_xattrs_from_arg(Item *i) {
ebf4e801
MW
635 const char *p;
636 int r;
637
638 assert(i);
505ef0e3 639 assert(i->argument);
ebf4e801 640
ebf4e801
MW
641 p = i->argument;
642
4034a06d 643 for (;;) {
bd550f78 644 _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL, *xattr_replaced = NULL;
4034a06d
LP
645
646 r = unquote_first_word(&p, &xattr, UNQUOTE_CUNESCAPE);
647 if (r < 0)
bd550f78 648 log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p);
4034a06d
LP
649 if (r <= 0)
650 break;
505ef0e3 651
bd550f78
LP
652 r = specifier_printf(xattr, specifier_table, NULL, &xattr_replaced);
653 if (r < 0)
654 return log_error_errno(r, "Failed to replace specifiers in extended attribute '%s': %m", xattr);
655
656 r = split_pair(xattr_replaced, "=", &name, &value);
ebf4e801 657 if (r < 0) {
4034a06d 658 log_warning_errno(r, "Failed to parse extended attribute, ignoring: %s", xattr);
ebf4e801
MW
659 continue;
660 }
505ef0e3 661
4034a06d 662 if (isempty(name) || isempty(value)) {
bd550f78 663 log_warning("Malformed extended attribute found, ignoring: %s", xattr);
ebf4e801
MW
664 continue;
665 }
505ef0e3 666
4034a06d 667 if (strv_push_pair(&i->xattrs, name, value) < 0)
ebf4e801 668 return log_oom();
505ef0e3 669
4034a06d 670 name = value = NULL;
ebf4e801
MW
671 }
672
4034a06d 673 return 0;
ebf4e801
MW
674}
675
b705ab6a 676static int path_set_xattrs(Item *i, const char *path) {
ebf4e801
MW
677 char **name, **value;
678
679 assert(i);
680 assert(path);
681
ebf4e801
MW
682 STRV_FOREACH_PAIR(name, value, i->xattrs) {
683 int n;
505ef0e3 684
ebf4e801 685 n = strlen(*value);
bd550f78 686 log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path);
ebf4e801 687 if (lsetxattr(path, *name, *value, n, 0) < 0) {
bd550f78 688 log_error("Setting extended attribute %s=%s on %s failed: %m", *name, *value, path);
ebf4e801
MW
689 return -errno;
690 }
691 }
692 return 0;
693}
694
bd550f78 695static int parse_acls_from_arg(Item *item) {
f8eeeaf9
ZJS
696#ifdef HAVE_ACL
697 int r;
f8eeeaf9
ZJS
698
699 assert(item);
700
50d9e46d
ZJS
701 /* If force (= modify) is set, we will not modify the acl
702 * afterwards, so the mask can be added now if necessary. */
bd550f78 703
50d9e46d 704 r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force);
f8eeeaf9 705 if (r < 0)
4034a06d 706 log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring", item->argument);
f8eeeaf9
ZJS
707#else
708 log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring");
709#endif
710
711 return 0;
712}
713
35888b67 714#ifdef HAVE_ACL
50d9e46d 715static int path_set_acl(const char *path, acl_type_t type, acl_t acl, bool modify) {
dd4105b0 716 _cleanup_(acl_freep) acl_t dup = NULL;
50d9e46d 717 int r;
582deb84 718 _cleanup_(acl_free_charpp) char *t = NULL;
50d9e46d 719
d873e877
HPD
720 /* Returns 0 for success, positive error if already warned,
721 * negative error otherwise. */
722
50d9e46d 723 if (modify) {
dd4105b0 724 r = acls_for_file(path, type, acl, &dup);
50d9e46d
ZJS
725 if (r < 0)
726 return r;
50d9e46d 727
dd4105b0
ZJS
728 r = calc_acl_mask_if_needed(&dup);
729 if (r < 0)
730 return r;
731 } else {
732 dup = acl_dup(acl);
733 if (!dup)
734 return -errno;
735
736 /* the mask was already added earlier if needed */
737 }
738
739 r = add_base_acls_if_needed(&dup, path);
740 if (r < 0)
741 return r;
742
582deb84
ZJS
743 t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE);
744 log_debug("\"%s\": setting %s ACL \"%s\"", path,
745 type == ACL_TYPE_ACCESS ? "access" : "default",
746 strna(t));
50d9e46d 747
582deb84
ZJS
748 r = acl_set_file(path, type, dup);
749 if (r < 0)
3ea40b78
LP
750 /* Return positive to indicate we already warned */
751 return -log_error_errno(errno,
752 "Setting %s ACL \"%s\" on %s failed: %m",
753 type == ACL_TYPE_ACCESS ? "access" : "default",
754 strna(t), path);
d873e877 755
582deb84 756 return 0;
50d9e46d 757}
35888b67 758#endif
50d9e46d 759
b705ab6a 760static int path_set_acls(Item *item, const char *path) {
d873e877 761 int r = 0;
f8eeeaf9 762#ifdef HAVE_ACL
f8eeeaf9
ZJS
763 assert(item);
764 assert(path);
765
d873e877 766 if (item->acl_access)
50d9e46d 767 r = path_set_acl(path, ACL_TYPE_ACCESS, item->acl_access, item->force);
f8eeeaf9 768
d873e877 769 if (r == 0 && item->acl_default)
50d9e46d 770 r = path_set_acl(path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
f8eeeaf9 771
d873e877
HPD
772 if (r > 0)
773 return -r; /* already warned */
15411c0c 774 else if (r == -EOPNOTSUPP) {
d873e877
HPD
775 log_debug_errno(r, "ACLs not supported by file system at %s", path);
776 return 0;
777 } else if (r < 0)
778 log_error_errno(r, "ACL operation on \"%s\" failed: %m", path);
779#endif
780 return r;
f8eeeaf9
ZJS
781}
782
88ec4dfa
LP
783#define ATTRIBUTES_ALL \
784 (FS_NOATIME_FL | \
785 FS_SYNC_FL | \
786 FS_DIRSYNC_FL | \
787 FS_APPEND_FL | \
788 FS_COMPR_FL | \
789 FS_NODUMP_FL | \
790 FS_EXTENT_FL | \
791 FS_IMMUTABLE_FL | \
792 FS_JOURNAL_DATA_FL | \
793 FS_SECRM_FL | \
794 FS_UNRM_FL | \
795 FS_NOTAIL_FL | \
796 FS_TOPDIR_FL | \
797 FS_NOCOW_FL)
798
bd550f78 799static int parse_attribute_from_arg(Item *item) {
88ec4dfa
LP
800
801 static const struct {
802 char character;
803 unsigned value;
804 } attributes[] = {
805 { 'A', FS_NOATIME_FL }, /* do not update atime */
806 { 'S', FS_SYNC_FL }, /* Synchronous updates */
807 { 'D', FS_DIRSYNC_FL }, /* dirsync behaviour (directories only) */
808 { 'a', FS_APPEND_FL }, /* writes to file may only append */
809 { 'c', FS_COMPR_FL }, /* Compress file */
810 { 'd', FS_NODUMP_FL }, /* do not dump file */
811 { 'e', FS_EXTENT_FL }, /* Top of directory hierarchies*/
812 { 'i', FS_IMMUTABLE_FL }, /* Immutable file */
813 { 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */
814 { 's', FS_SECRM_FL }, /* Secure deletion */
815 { 'u', FS_UNRM_FL }, /* Undelete */
816 { 't', FS_NOTAIL_FL }, /* file tail should not be merged */
817 { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies*/
818 { 'C', FS_NOCOW_FL }, /* Do not cow file */
22c3a6ca 819 };
88ec4dfa 820
22c3a6ca
GB
821 enum {
822 MODE_ADD,
823 MODE_DEL,
824 MODE_SET
825 } mode = MODE_ADD;
22c3a6ca 826
88ec4dfa
LP
827 unsigned value = 0, mask = 0;
828 const char *p;
22c3a6ca 829
88ec4dfa
LP
830 assert(item);
831
832 p = item->argument;
833 if (p) {
834 if (*p == '+') {
835 mode = MODE_ADD;
836 p++;
837 } else if (*p == '-') {
838 mode = MODE_DEL;
839 p++;
840 } else if (*p == '=') {
841 mode = MODE_SET;
842 p++;
843 }
22c3a6ca
GB
844 }
845
88ec4dfa
LP
846 if (isempty(p) && mode != MODE_SET) {
847 log_error("Setting file attribute on '%s' needs an attribute specification.", item->path);
22c3a6ca
GB
848 return -EINVAL;
849 }
88ec4dfa
LP
850
851 for (; p && *p ; p++) {
852 unsigned i, v;
853
854 for (i = 0; i < ELEMENTSOF(attributes); i++)
855 if (*p == attributes[i].character)
856 break;
857
858 if (i >= ELEMENTSOF(attributes)) {
859 log_error("Unknown file attribute '%c' on '%s'.", *p, item->path);
22c3a6ca
GB
860 return -EINVAL;
861 }
88ec4dfa
LP
862
863 v = attributes[i].value;
864
22c3a6ca 865 if (mode == MODE_ADD || mode == MODE_SET)
88ec4dfa 866 value |= v;
22c3a6ca 867 else
88ec4dfa
LP
868 value &= ~v;
869
870 mask |= v;
22c3a6ca
GB
871 }
872
873 if (mode == MODE_SET)
88ec4dfa 874 mask |= ATTRIBUTES_ALL;
22c3a6ca 875
88ec4dfa 876 assert(mask != 0);
22c3a6ca 877
88ec4dfa
LP
878 item->attribute_mask = mask;
879 item->attribute_value = value;
880 item->attribute_set = true;
22c3a6ca
GB
881
882 return 0;
22c3a6ca
GB
883}
884
88ec4dfa 885static int path_set_attribute(Item *item, const char *path) {
22c3a6ca 886 _cleanup_close_ int fd = -1;
22c3a6ca 887 struct stat st;
88ec4dfa
LP
888 unsigned f;
889 int r;
22c3a6ca 890
88ec4dfa 891 if (!item->attribute_set || item->attribute_mask == 0)
22c3a6ca 892 return 0;
22c3a6ca
GB
893
894 fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC);
22c3a6ca 895 if (fd < 0)
88ec4dfa 896 return log_error_errno(errno, "Cannot open '%s': %m", path);
22c3a6ca 897
88ec4dfa
LP
898 if (fstat(fd, &st) < 0)
899 return log_error_errno(errno, "Cannot stat '%s': %m", path);
900
901 /* Issuing the file attribute ioctls on device nodes is not
902 * safe, as that will be delivered to the drivers, not the
903 * file system containing the device node. */
904 if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) {
905 log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path);
906 return -EINVAL;
907 }
908
909 f = item->attribute_value & item->attribute_mask;
910
911 /* Mask away directory-specific flags */
22c3a6ca
GB
912 if (!S_ISDIR(st.st_mode))
913 f &= ~FS_DIRSYNC_FL;
88ec4dfa
LP
914
915 r = chattr_fd(fd, f, item->attribute_mask);
22c3a6ca 916 if (r < 0)
88ec4dfa
LP
917 return log_error_errno(r,
918 "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
919 path, item->attribute_value, item->attribute_mask);
22c3a6ca
GB
920
921 return 0;
922}
923
d4e9eb91 924static int write_one_file(Item *i, const char *path) {
43ad6e31
LP
925 _cleanup_close_ int fd = -1;
926 int flags, r = 0;
d4e9eb91 927 struct stat st;
d4e9eb91 928
874f1947
LP
929 assert(i);
930 assert(path);
931
43ad6e31
LP
932 flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW :
933 i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0;
d4e9eb91 934
43ad6e31 935 RUN_WITH_UMASK(0000) {
ecabcf8b 936 mac_selinux_create_file_prepare(path, S_IFREG);
43ad6e31 937 fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
ecabcf8b 938 mac_selinux_create_file_clear();
5c0d398d 939 }
d4e9eb91
DR
940
941 if (fd < 0) {
582deb84
ZJS
942 if (i->type == WRITE_FILE && errno == ENOENT) {
943 log_debug_errno(errno, "Not writing \"%s\": %m", path);
d4e9eb91 944 return 0;
582deb84 945 }
d4e9eb91 946
56f64d95 947 log_error_errno(errno, "Failed to create file %s: %m", path);
d4e9eb91
DR
948 return -errno;
949 }
950
951 if (i->argument) {
bd550f78 952 _cleanup_free_ char *unescaped = NULL, *replaced = NULL;
582deb84 953
4034a06d 954 log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path);
d4e9eb91 955
527b7a42
LP
956 r = cunescape(i->argument, 0, &unescaped);
957 if (r < 0)
958 return log_error_errno(r, "Failed to unescape parameter to write: %s", i->argument);
d4e9eb91 959
bd550f78
LP
960 r = specifier_printf(unescaped, specifier_table, NULL, &replaced);
961 if (r < 0)
962 return log_error_errno(r, "Failed to replace specifiers in parameter to write '%s': %m", unescaped);
963
964 r = loop_write(fd, replaced, strlen(replaced), false);
582deb84
ZJS
965 if (r < 0)
966 return log_error_errno(r, "Failed to write file \"%s\": %m", path);
967 } else
968 log_debug("\"%s\" has been created.", path);
d4e9eb91 969
43ad6e31 970 fd = safe_close(fd);
3612fbc1 971
4a62c710
MS
972 if (stat(path, &st) < 0)
973 return log_error_errno(errno, "stat(%s) failed: %m", path);
d4e9eb91
DR
974
975 if (!S_ISREG(st.st_mode)) {
976 log_error("%s is not a file.", path);
977 return -EEXIST;
978 }
979
b705ab6a 980 r = path_set_perms(i, path);
d4e9eb91
DR
981 if (r < 0)
982 return r;
983
984 return 0;
985}
986
081043cf
ZJS
987typedef int (*action_t)(Item *, const char *);
988
989static int item_do_children(Item *i, const char *path, action_t action) {
7fd1b19b 990 _cleanup_closedir_ DIR *d;
e73a03e0
LP
991 int r = 0;
992
993 assert(i);
994 assert(path);
a8d88783
MS
995
996 /* This returns the first error we run into, but nevertheless
997 * tries to go on */
998
df99a9ef
ZJS
999 d = opendir_nomod(path);
1000 if (!d)
e73a03e0 1001 return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
a8d88783
MS
1002
1003 for (;;) {
e73a03e0 1004 _cleanup_free_ char *p = NULL;
7d5e9c0f 1005 struct dirent *de;
e73a03e0 1006 int q;
a8d88783 1007
d78096b3
FW
1008 errno = 0;
1009 de = readdir(d);
e73a03e0
LP
1010 if (!de) {
1011 if (errno != 0 && r == 0)
1012 r = -errno;
a8d88783 1013
a8d88783 1014 break;
e73a03e0 1015 }
a8d88783 1016
7fcb4b9b 1017 if (STR_IN_SET(de->d_name, ".", ".."))
a8d88783
MS
1018 continue;
1019
e73a03e0
LP
1020 p = strjoin(path, "/", de->d_name, NULL);
1021 if (!p)
1022 return -ENOMEM;
a8d88783 1023
081043cf 1024 q = action(i, p);
e73a03e0
LP
1025 if (q < 0 && q != -ENOENT && r == 0)
1026 r = q;
a8d88783 1027
e73a03e0 1028 if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) {
081043cf 1029 q = item_do_children(i, p, action);
e73a03e0
LP
1030 if (q < 0 && r == 0)
1031 r = q;
a8d88783 1032 }
a8d88783
MS
1033 }
1034
e73a03e0 1035 return r;
a8d88783
MS
1036}
1037
081043cf 1038static int glob_item(Item *i, action_t action, bool recursive) {
df99a9ef 1039 _cleanup_globfree_ glob_t g = {
ebf31a1f
ZJS
1040 .gl_closedir = (void (*)(void *)) closedir,
1041 .gl_readdir = (struct dirent *(*)(void *)) readdir,
1042 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
df99a9ef
ZJS
1043 .gl_lstat = lstat,
1044 .gl_stat = stat,
1045 };
e73a03e0 1046 int r = 0, k;
99e68c0b
MS
1047 char **fn;
1048
99e68c0b 1049 errno = 0;
df99a9ef 1050 k = glob(i->path, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g);
081043cf
ZJS
1051 if (k != 0 && k != GLOB_NOMATCH)
1052 return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path);
99e68c0b 1053
c84a9488
ZJS
1054 STRV_FOREACH(fn, g.gl_pathv) {
1055 k = action(i, *fn);
e73a03e0 1056 if (k < 0 && r == 0)
99e68c0b 1057 r = k;
081043cf
ZJS
1058
1059 if (recursive) {
1060 k = item_do_children(i, *fn, action);
1061 if (k < 0 && r == 0)
1062 r = k;
1063 }
c84a9488 1064 }
99e68c0b 1065
99e68c0b
MS
1066 return r;
1067}
1068
294929f8
ZJS
1069typedef enum {
1070 CREATION_NORMAL,
1071 CREATION_EXISTING,
1072 CREATION_FORCE,
7a7d5db7
LP
1073 _CREATION_MODE_MAX,
1074 _CREATION_MODE_INVALID = -1
294929f8
ZJS
1075} CreationMode;
1076
7a7d5db7
LP
1077static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = {
1078 [CREATION_NORMAL] = "Created",
1079 [CREATION_EXISTING] = "Found existing",
1080 [CREATION_FORCE] = "Created replacement",
1081};
1082
1083DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
294929f8 1084
3b63d2d3 1085static int create_item(Item *i) {
75c2a9fd 1086 _cleanup_free_ char *resolved = NULL;
3b63d2d3 1087 struct stat st;
df28bc08 1088 int r = 0;
294929f8 1089 CreationMode creation;
5008d581 1090
3b63d2d3 1091 assert(i);
5008d581 1092
582deb84
ZJS
1093 log_debug("Running create action for entry %c %s", (char) i->type, i->path);
1094
3b63d2d3
LP
1095 switch (i->type) {
1096
1097 case IGNORE_PATH:
78a92a5a 1098 case IGNORE_DIRECTORY_PATH:
3b63d2d3
LP
1099 case REMOVE_PATH:
1100 case RECURSIVE_REMOVE_PATH:
1101 return 0;
5008d581 1102
3b63d2d3 1103 case CREATE_FILE:
31ed59c5 1104 case TRUNCATE_FILE:
1845fdd9
DR
1105 r = write_one_file(i, i->path);
1106 if (r < 0)
1107 return r;
1108 break;
265ffa1e 1109
5c5ccf12 1110 case COPY_FILES: {
5c5ccf12
LP
1111 r = specifier_printf(i->argument, specifier_table, NULL, &resolved);
1112 if (r < 0)
1113 return log_error_errno(r, "Failed to substitute specifiers in copy source %s: %m", i->argument);
1114
1115 log_debug("Copying tree \"%s\" to \"%s\".", resolved, i->path);
1116 r = copy_tree(resolved, i->path, false);
849958d1 1117 if (r < 0) {
e156347e
LP
1118 struct stat a, b;
1119
8d3d7072
MS
1120 if (r != -EEXIST)
1121 return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
e156347e 1122
5c5ccf12
LP
1123 if (stat(resolved, &a) < 0)
1124 return log_error_errno(errno, "stat(%s) failed: %m", resolved);
e156347e 1125
4a62c710
MS
1126 if (stat(i->path, &b) < 0)
1127 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
e156347e
LP
1128
1129 if ((a.st_mode ^ b.st_mode) & S_IFMT) {
1130 log_debug("Can't copy to %s, file exists already and is of different type", i->path);
1131 return 0;
1132 }
849958d1
LP
1133 }
1134
b705ab6a 1135 r = path_set_perms(i, i->path);
849958d1
LP
1136 if (r < 0)
1137 return r;
1138
1139 break;
1140
d4e9eb91 1141 case WRITE_FILE:
081043cf 1142 r = glob_item(i, write_one_file, false);
f05bc3f7
MS
1143 if (r < 0)
1144 return r;
5008d581 1145
3b63d2d3
LP
1146 break;
1147
3b63d2d3 1148 case CREATE_DIRECTORY:
d7b8eec7
LP
1149 case TRUNCATE_DIRECTORY:
1150 case CREATE_SUBVOLUME:
5008d581 1151
d7b8eec7 1152 RUN_WITH_UMASK(0000)
5c0d398d 1153 mkdir_parents_label(i->path, 0755);
d7b8eec7 1154
582deb84
ZJS
1155 if (i->type == CREATE_SUBVOLUME)
1156 RUN_WITH_UMASK((~i->mode) & 0777) {
d7b8eec7 1157 r = btrfs_subvol_make(i->path);
582deb84
ZJS
1158 log_debug_errno(r, "Creating subvolume \"%s\": %m", i->path);
1159 }
1160 else
d7b8eec7
LP
1161 r = 0;
1162
582deb84 1163 if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY)
d7b8eec7
LP
1164 RUN_WITH_UMASK(0000)
1165 r = mkdir_label(i->path, i->mode);
5008d581 1166
e156347e 1167 if (r < 0) {
f647962d 1168 if (r != -EEXIST)
582deb84 1169 return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path);
5008d581 1170
4a62c710
MS
1171 if (stat(i->path, &st) < 0)
1172 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
5008d581 1173
e156347e 1174 if (!S_ISDIR(st.st_mode)) {
582deb84 1175 log_debug("\"%s\" already exists and is not a directory.", i->path);
e156347e
LP
1176 return 0;
1177 }
294929f8
ZJS
1178
1179 creation = CREATION_EXISTING;
1180 } else
1181 creation = CREATION_NORMAL;
7a7d5db7 1182 log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path);
5008d581 1183
b705ab6a 1184 r = path_set_perms(i, i->path);
f05bc3f7
MS
1185 if (r < 0)
1186 return r;
3b63d2d3
LP
1187
1188 break;
ee17ee7c
LP
1189
1190 case CREATE_FIFO:
1191
5c0d398d 1192 RUN_WITH_UMASK(0000) {
ecabcf8b 1193 mac_selinux_create_file_prepare(i->path, S_IFIFO);
5c0d398d 1194 r = mkfifo(i->path, i->mode);
ecabcf8b 1195 mac_selinux_create_file_clear();
5c0d398d 1196 }
ee17ee7c 1197
1554afae 1198 if (r < 0) {
4a62c710
MS
1199 if (errno != EEXIST)
1200 return log_error_errno(errno, "Failed to create fifo %s: %m", i->path);
ee17ee7c 1201
4a62c710
MS
1202 if (stat(i->path, &st) < 0)
1203 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
ee17ee7c 1204
1554afae
LP
1205 if (!S_ISFIFO(st.st_mode)) {
1206
1207 if (i->force) {
1208
1209 RUN_WITH_UMASK(0000) {
ecabcf8b 1210 mac_selinux_create_file_prepare(i->path, S_IFIFO);
1554afae 1211 r = mkfifo_atomic(i->path, i->mode);
ecabcf8b 1212 mac_selinux_create_file_clear();
1554afae
LP
1213 }
1214
f647962d
MS
1215 if (r < 0)
1216 return log_error_errno(r, "Failed to create fifo %s: %m", i->path);
294929f8 1217 creation = CREATION_FORCE;
1554afae
LP
1218 } else {
1219 log_debug("%s is not a fifo.", i->path);
1220 return 0;
1221 }
294929f8
ZJS
1222 } else
1223 creation = CREATION_EXISTING;
1224 } else
1225 creation = CREATION_NORMAL;
7a7d5db7 1226 log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path);
ee17ee7c 1227
b705ab6a 1228 r = path_set_perms(i, i->path);
f05bc3f7
MS
1229 if (r < 0)
1230 return r;
ee17ee7c
LP
1231
1232 break;
5c5ccf12 1233 }
a8d88783 1234
5c5ccf12 1235 case CREATE_SYMLINK: {
5c5ccf12
LP
1236 r = specifier_printf(i->argument, specifier_table, NULL, &resolved);
1237 if (r < 0)
1238 return log_error_errno(r, "Failed to substitute specifiers in symlink target %s: %m", i->argument);
468d726b 1239
ecabcf8b 1240 mac_selinux_create_file_prepare(i->path, S_IFLNK);
5c5ccf12 1241 r = symlink(resolved, i->path);
ecabcf8b 1242 mac_selinux_create_file_clear();
e9a5ef7c 1243
468d726b 1244 if (r < 0) {
2e78fa79 1245 _cleanup_free_ char *x = NULL;
468d726b 1246
4a62c710 1247 if (errno != EEXIST)
5c5ccf12 1248 return log_error_errno(errno, "symlink(%s, %s) failed: %m", resolved, i->path);
2e78fa79
LP
1249
1250 r = readlink_malloc(i->path, &x);
5c5ccf12 1251 if (r < 0 || !streq(resolved, x)) {
2e78fa79
LP
1252
1253 if (i->force) {
ecabcf8b 1254 mac_selinux_create_file_prepare(i->path, S_IFLNK);
5c5ccf12 1255 r = symlink_atomic(resolved, i->path);
ecabcf8b 1256 mac_selinux_create_file_clear();
2e78fa79 1257
f647962d 1258 if (r < 0)
5c5ccf12 1259 return log_error_errno(r, "symlink(%s, %s) failed: %m", resolved, i->path);
294929f8 1260 creation = CREATION_FORCE;
1554afae 1261 } else {
582deb84 1262 log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path);
1554afae
LP
1263 return 0;
1264 }
294929f8
ZJS
1265 } else
1266 creation = CREATION_EXISTING;
1267 } else
1268 creation = CREATION_NORMAL;
7a7d5db7 1269 log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path);
468d726b 1270
468d726b 1271 break;
5c5ccf12 1272 }
468d726b
LP
1273
1274 case CREATE_BLOCK_DEVICE:
1275 case CREATE_CHAR_DEVICE: {
cb7ed9df
LP
1276 mode_t file_type;
1277
1278 if (have_effective_cap(CAP_MKNOD) == 0) {
1279 /* In a container we lack CAP_MKNOD. We
ab06eef8 1280 shouldn't attempt to create the device node in
cb7ed9df
LP
1281 that case to avoid noise, and we don't support
1282 virtualized devices in containers anyway. */
1283
1284 log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
1285 return 0;
1286 }
1287
1554afae 1288 file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR;
468d726b 1289
5c0d398d 1290 RUN_WITH_UMASK(0000) {
ecabcf8b 1291 mac_selinux_create_file_prepare(i->path, file_type);
5c0d398d 1292 r = mknod(i->path, i->mode | file_type, i->major_minor);
ecabcf8b 1293 mac_selinux_create_file_clear();
5c0d398d 1294 }
468d726b 1295
6555ad8e
KS
1296 if (r < 0) {
1297 if (errno == EPERM) {
1298 log_debug("We lack permissions, possibly because of cgroup configuration; "
1299 "skipping creation of device node %s.", i->path);
1300 return 0;
1301 }
1302
4a62c710
MS
1303 if (errno != EEXIST)
1304 return log_error_errno(errno, "Failed to create device node %s: %m", i->path);
468d726b 1305
4a62c710
MS
1306 if (stat(i->path, &st) < 0)
1307 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
468d726b 1308
1554afae
LP
1309 if ((st.st_mode & S_IFMT) != file_type) {
1310
1311 if (i->force) {
1312
1313 RUN_WITH_UMASK(0000) {
ecabcf8b 1314 mac_selinux_create_file_prepare(i->path, file_type);
1554afae 1315 r = mknod_atomic(i->path, i->mode | file_type, i->major_minor);
ecabcf8b 1316 mac_selinux_create_file_clear();
1554afae
LP
1317 }
1318
f647962d 1319 if (r < 0)
294929f8
ZJS
1320 return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
1321 creation = CREATION_FORCE;
1554afae
LP
1322 } else {
1323 log_debug("%s is not a device node.", i->path);
1324 return 0;
1325 }
294929f8
ZJS
1326 } else
1327 creation = CREATION_EXISTING;
1328 } else
1329 creation = CREATION_NORMAL;
1330 log_debug("%s %s device node \"%s\" %u:%u.",
7a7d5db7 1331 creation_mode_verb_to_string(creation),
582deb84
ZJS
1332 i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
1333 i->path, major(i->mode), minor(i->mode));
468d726b 1334
b705ab6a 1335 r = path_set_perms(i, i->path);
468d726b
LP
1336 if (r < 0)
1337 return r;
1338
1339 break;
1340 }
1341
e73a03e0 1342 case ADJUST_MODE:
777b87e7 1343 case RELABEL_PATH:
b705ab6a 1344 r = glob_item(i, path_set_perms, false);
777b87e7 1345 if (r < 0)
96ca8194 1346 return r;
777b87e7
MS
1347 break;
1348
a8d88783 1349 case RECURSIVE_RELABEL_PATH:
b705ab6a 1350 r = glob_item(i, path_set_perms, true);
a8d88783
MS
1351 if (r < 0)
1352 return r;
ebf4e801 1353 break;
e73a03e0 1354
ebf4e801 1355 case SET_XATTR:
b705ab6a
ZJS
1356 r = glob_item(i, path_set_xattrs, false);
1357 if (r < 0)
1358 return r;
1359 break;
1360
1361 case RECURSIVE_SET_XATTR:
1362 r = glob_item(i, path_set_xattrs, true);
ebf4e801
MW
1363 if (r < 0)
1364 return r;
e73a03e0 1365 break;
f8eeeaf9
ZJS
1366
1367 case SET_ACL:
b705ab6a 1368 r = glob_item(i, path_set_acls, false);
f8eeeaf9
ZJS
1369 if (r < 0)
1370 return r;
b705ab6a
ZJS
1371 break;
1372
1373 case RECURSIVE_SET_ACL:
1374 r = glob_item(i, path_set_acls, true);
1375 if (r < 0)
1376 return r;
1377 break;
22c3a6ca 1378
88ec4dfa
LP
1379 case SET_ATTRIBUTE:
1380 r = glob_item(i, path_set_attribute, false);
22c3a6ca
GB
1381 if (r < 0)
1382 return r;
1383 break;
1384
88ec4dfa
LP
1385 case RECURSIVE_SET_ATTRIBUTE:
1386 r = glob_item(i, path_set_attribute, true);
22c3a6ca
GB
1387 if (r < 0)
1388 return r;
1389 break;
3b63d2d3
LP
1390 }
1391
f05bc3f7 1392 return 0;
3b63d2d3
LP
1393}
1394
a0896123 1395static int remove_item_instance(Item *i, const char *instance) {
3b63d2d3
LP
1396 int r;
1397
1398 assert(i);
1399
1400 switch (i->type) {
1401
3b63d2d3 1402 case REMOVE_PATH:
4a62c710 1403 if (remove(instance) < 0 && errno != ENOENT)
3f93da98 1404 return log_error_errno(errno, "rm(%s): %m", instance);
3b63d2d3
LP
1405
1406 break;
1407
1408 case TRUNCATE_DIRECTORY:
1409 case RECURSIVE_REMOVE_PATH:
d139b24a
LP
1410 /* FIXME: we probably should use dir_cleanup() here
1411 * instead of rm_rf() so that 'x' is honoured. */
582deb84 1412 log_debug("rm -rf \"%s\"", instance);
c6878637 1413 r = rm_rf(instance, (i->type == RECURSIVE_REMOVE_PATH ? REMOVE_ROOT : 0) | REMOVE_PHYSICAL);
f647962d
MS
1414 if (r < 0 && r != -ENOENT)
1415 return log_error_errno(r, "rm_rf(%s): %m", instance);
3b63d2d3
LP
1416
1417 break;
7fcb4b9b
ZJS
1418
1419 default:
1420 assert_not_reached("wut?");
3b63d2d3
LP
1421 }
1422
1423 return 0;
1424}
1425
a0896123 1426static int remove_item(Item *i) {
99e68c0b
MS
1427 int r = 0;
1428
b8bb3e8f
LP
1429 assert(i);
1430
582deb84
ZJS
1431 log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
1432
b8bb3e8f
LP
1433 switch (i->type) {
1434
1435 case CREATE_FILE:
1436 case TRUNCATE_FILE:
1437 case CREATE_DIRECTORY:
d7b8eec7 1438 case CREATE_SUBVOLUME:
ee17ee7c 1439 case CREATE_FIFO:
468d726b
LP
1440 case CREATE_SYMLINK:
1441 case CREATE_CHAR_DEVICE:
1442 case CREATE_BLOCK_DEVICE:
b8bb3e8f 1443 case IGNORE_PATH:
78a92a5a 1444 case IGNORE_DIRECTORY_PATH:
e73a03e0 1445 case ADJUST_MODE:
777b87e7 1446 case RELABEL_PATH:
a8d88783 1447 case RECURSIVE_RELABEL_PATH:
31ed59c5 1448 case WRITE_FILE:
849958d1 1449 case COPY_FILES:
ebf4e801 1450 case SET_XATTR:
b705ab6a 1451 case RECURSIVE_SET_XATTR:
f8eeeaf9 1452 case SET_ACL:
b705ab6a 1453 case RECURSIVE_SET_ACL:
88ec4dfa
LP
1454 case SET_ATTRIBUTE:
1455 case RECURSIVE_SET_ATTRIBUTE:
b8bb3e8f
LP
1456 break;
1457
1458 case REMOVE_PATH:
1459 case TRUNCATE_DIRECTORY:
99e68c0b 1460 case RECURSIVE_REMOVE_PATH:
081043cf 1461 r = glob_item(i, remove_item_instance, false);
99e68c0b 1462 break;
b8bb3e8f
LP
1463 }
1464
99e68c0b 1465 return r;
b8bb3e8f
LP
1466}
1467
78a92a5a 1468static int clean_item_instance(Item *i, const char* instance) {
7fd1b19b 1469 _cleanup_closedir_ DIR *d = NULL;
78a92a5a
MS
1470 struct stat s, ps;
1471 bool mountpoint;
78a92a5a 1472 usec_t cutoff, n;
582deb84 1473 char timestamp[FORMAT_TIMESTAMP_MAX];
78a92a5a
MS
1474
1475 assert(i);
1476
1477 if (!i->age_set)
1478 return 0;
1479
1480 n = now(CLOCK_REALTIME);
1481 if (n < i->age)
1482 return 0;
1483
1484 cutoff = n - i->age;
1485
df99a9ef 1486 d = opendir_nomod(instance);
78a92a5a 1487 if (!d) {
582deb84
ZJS
1488 if (errno == ENOENT || errno == ENOTDIR) {
1489 log_debug_errno(errno, "Directory \"%s\": %m", instance);
78a92a5a 1490 return 0;
582deb84 1491 }
78a92a5a 1492
582deb84 1493 log_error_errno(errno, "Failed to open directory %s: %m", instance);
78a92a5a
MS
1494 return -errno;
1495 }
1496
4a62c710
MS
1497 if (fstat(dirfd(d), &s) < 0)
1498 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
78a92a5a
MS
1499
1500 if (!S_ISDIR(s.st_mode)) {
1501 log_error("%s is not a directory.", i->path);
19fbec19 1502 return -ENOTDIR;
78a92a5a
MS
1503 }
1504
4a62c710
MS
1505 if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
1506 return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
78a92a5a
MS
1507
1508 mountpoint = s.st_dev != ps.st_dev ||
1509 (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
1510
582deb84
ZJS
1511 log_debug("Cleanup threshold for %s \"%s\" is %s",
1512 mountpoint ? "mount point" : "directory",
1513 instance,
1514 format_timestamp_us(timestamp, sizeof(timestamp), cutoff));
1515
1516 return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
1517 MAX_DEPTH, i->keep_first_level);
78a92a5a
MS
1518}
1519
1520static int clean_item(Item *i) {
1521 int r = 0;
1522
1523 assert(i);
1524
582deb84
ZJS
1525 log_debug("Running clean action for entry %c %s", (char) i->type, i->path);
1526
78a92a5a
MS
1527 switch (i->type) {
1528 case CREATE_DIRECTORY:
d7b8eec7 1529 case CREATE_SUBVOLUME:
78a92a5a
MS
1530 case TRUNCATE_DIRECTORY:
1531 case IGNORE_PATH:
849958d1 1532 case COPY_FILES:
78a92a5a
MS
1533 clean_item_instance(i, i->path);
1534 break;
1535 case IGNORE_DIRECTORY_PATH:
081043cf 1536 r = glob_item(i, clean_item_instance, false);
78a92a5a
MS
1537 break;
1538 default:
1539 break;
1540 }
1541
1542 return r;
1543}
1544
3f93da98
ZJS
1545static int process_item_array(ItemArray *array);
1546
3b63d2d3 1547static int process_item(Item *i) {
1e95893a 1548 int r, q, p, t = 0;
9348f0e6 1549 _cleanup_free_ char *prefix = NULL;
3b63d2d3
LP
1550
1551 assert(i);
1552
1910cd0e
LP
1553 if (i->done)
1554 return 0;
1555
1556 i->done = true;
1557
9348f0e6
ZJS
1558 prefix = malloc(strlen(i->path) + 1);
1559 if (!prefix)
1560 return log_oom();
1561
1910cd0e 1562 PATH_FOREACH_PREFIX(prefix, i->path) {
3f93da98 1563 ItemArray *j;
1910cd0e
LP
1564
1565 j = hashmap_get(items, prefix);
1db50423
ZJS
1566 if (j) {
1567 int s;
1568
3f93da98 1569 s = process_item_array(j);
1db50423
ZJS
1570 if (s < 0 && t == 0)
1571 t = s;
1572 }
1910cd0e
LP
1573 }
1574
3b63d2d3 1575 r = arg_create ? create_item(i) : 0;
a0896123 1576 q = arg_remove ? remove_item(i) : 0;
3b63d2d3
LP
1577 p = arg_clean ? clean_item(i) : 0;
1578
1db50423
ZJS
1579 return t < 0 ? t :
1580 r < 0 ? r :
1581 q < 0 ? q :
1582 p;
3b63d2d3
LP
1583}
1584
3f93da98
ZJS
1585static int process_item_array(ItemArray *array) {
1586 unsigned n;
1587 int r = 0, k;
753615e8 1588
3f93da98
ZJS
1589 assert(array);
1590
1591 for (n = 0; n < array->count; n++) {
1592 k = process_item(array->items + n);
1593 if (k < 0 && r == 0)
1594 r = k;
1595 }
1596
1597 return r;
1598}
3b63d2d3 1599
3f93da98
ZJS
1600static void item_free_contents(Item *i) {
1601 assert(i);
3b63d2d3 1602 free(i->path);
468d726b 1603 free(i->argument);
ebf4e801 1604 strv_free(i->xattrs);
f8eeeaf9
ZJS
1605
1606#ifdef HAVE_ACL
1607 acl_free(i->acl_access);
1608 acl_free(i->acl_default);
1609#endif
3b63d2d3
LP
1610}
1611
3f93da98
ZJS
1612static void item_array_free(ItemArray *a) {
1613 unsigned n;
1614
1615 if (!a)
1616 return;
1617
1618 for (n = 0; n < a->count; n++)
1619 item_free_contents(a->items + n);
1620 free(a->items);
1621 free(a);
1622}
e2f2fb78 1623
17493fa5
LP
1624static int item_compare(const void *a, const void *b) {
1625 const Item *x = a, *y = b;
1626
1627 /* Make sure that the ownership taking item is put first, so
1628 * that we first create the node, and then can adjust it */
1629
1630 if (takes_ownership(x->type) && !takes_ownership(y->type))
1631 return -1;
1632 if (!takes_ownership(x->type) && takes_ownership(y->type))
1633 return 1;
1634
1635 return (int) x->type - (int) y->type;
1636}
1637
3f93da98 1638static bool item_compatible(Item *a, Item *b) {
bfe95f35
LP
1639 assert(a);
1640 assert(b);
3f93da98 1641 assert(streq(a->path, b->path));
bfe95f35 1642
3f93da98
ZJS
1643 if (takes_ownership(a->type) && takes_ownership(b->type))
1644 /* check if the items are the same */
1645 return streq_ptr(a->argument, b->argument) &&
bfe95f35 1646
3f93da98
ZJS
1647 a->uid_set == b->uid_set &&
1648 a->uid == b->uid &&
bfe95f35 1649
3f93da98
ZJS
1650 a->gid_set == b->gid_set &&
1651 a->gid == b->gid &&
bfe95f35 1652
3f93da98
ZJS
1653 a->mode_set == b->mode_set &&
1654 a->mode == b->mode &&
bfe95f35 1655
3f93da98
ZJS
1656 a->age_set == b->age_set &&
1657 a->age == b->age &&
bfe95f35 1658
3f93da98 1659 a->mask_perms == b->mask_perms &&
bfe95f35 1660
3f93da98 1661 a->keep_first_level == b->keep_first_level &&
468d726b 1662
3f93da98 1663 a->major_minor == b->major_minor;
468d726b 1664
bfe95f35
LP
1665 return true;
1666}
1667
a2aced4a
DR
1668static bool should_include_path(const char *path) {
1669 char **prefix;
1670
abef3f91 1671 STRV_FOREACH(prefix, arg_exclude_prefixes)
582deb84
ZJS
1672 if (path_startswith(path, *prefix)) {
1673 log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
1674 path, *prefix);
5c795114 1675 return false;
582deb84 1676 }
a2aced4a 1677
abef3f91 1678 STRV_FOREACH(prefix, arg_include_prefixes)
582deb84
ZJS
1679 if (path_startswith(path, *prefix)) {
1680 log_debug("Entry \"%s\" matches include prefix \"%s\".", path, *prefix);
a2aced4a 1681 return true;
582deb84 1682 }
a2aced4a 1683
5c795114
DR
1684 /* no matches, so we should include this path only if we
1685 * have no whitelist at all */
582deb84
ZJS
1686 if (strv_length(arg_include_prefixes) == 0)
1687 return true;
1688
1689 log_debug("Entry \"%s\" does not match any include prefix, skipping.", path);
1690 return false;
a2aced4a
DR
1691}
1692
fba6e687 1693static int parse_line(const char *fname, unsigned line, const char *buffer) {
1731e34a 1694
cde684a2 1695 _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
3f93da98
ZJS
1696 _cleanup_(item_free_contents) Item i = {};
1697 ItemArray *existing;
bfe95f35 1698 Hashmap *h;
657cf7f4 1699 int r, pos;
5f255144 1700 bool force = false, boot = false;
3b63d2d3
LP
1701
1702 assert(fname);
1703 assert(line >= 1);
1704 assert(buffer);
1705
4034a06d
LP
1706 r = unquote_many_words(
1707 &buffer,
1708 0,
1709 &action,
1710 &path,
1711 &mode,
1712 &user,
1713 &group,
1714 &age,
1715 NULL);
657cf7f4 1716 if (r < 0)
1717 return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line);
1718 else if (r < 2) {
3b63d2d3 1719 log_error("[%s:%u] Syntax error.", fname, line);
7f2c1f4d 1720 return -EIO;
5008d581
LP
1721 }
1722
4034a06d
LP
1723 if (!isempty(buffer)) {
1724 i.argument = strdup(buffer);
1725 if (!i.argument)
1726 return log_oom();
1727 }
1728
2e78fa79
LP
1729 if (isempty(action)) {
1730 log_error("[%s:%u] Command too short '%s'.", fname, line, action);
c4708f13 1731 return -EINVAL;
2e78fa79
LP
1732 }
1733
5f255144
ZJS
1734 for (pos = 1; action[pos]; pos++) {
1735 if (action[pos] == '!' && !boot)
1736 boot = true;
1737 else if (action[pos] == '+' && !force)
1738 force = true;
1739 else {
1740 log_error("[%s:%u] Unknown modifiers in command '%s'",
1741 fname, line, action);
1742 return -EINVAL;
1743 }
2e78fa79
LP
1744 }
1745
582deb84
ZJS
1746 if (boot && !arg_boot) {
1747 log_debug("Ignoring entry %s \"%s\" because --boot is not specified.",
1748 action, path);
c4708f13 1749 return 0;
582deb84 1750 }
c4708f13 1751
3f93da98
ZJS
1752 i.type = action[0];
1753 i.force = force;
2e78fa79 1754
3f93da98 1755 r = specifier_printf(path, specifier_table, NULL, &i.path);
1731e34a
LP
1756 if (r < 0) {
1757 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path);
1758 return r;
1759 }
1760
3f93da98 1761 switch (i.type) {
468d726b 1762
777b87e7 1763 case CREATE_DIRECTORY:
d7b8eec7 1764 case CREATE_SUBVOLUME:
777b87e7
MS
1765 case TRUNCATE_DIRECTORY:
1766 case CREATE_FIFO:
1767 case IGNORE_PATH:
78a92a5a 1768 case IGNORE_DIRECTORY_PATH:
777b87e7
MS
1769 case REMOVE_PATH:
1770 case RECURSIVE_REMOVE_PATH:
e73a03e0 1771 case ADJUST_MODE:
777b87e7
MS
1772 case RELABEL_PATH:
1773 case RECURSIVE_RELABEL_PATH:
c82500c6 1774 if (i.argument)
bd550f78 1775 log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname, line, i.type);
c82500c6
LP
1776
1777 break;
1778
1779 case CREATE_FILE:
1780 case TRUNCATE_FILE:
777b87e7 1781 break;
468d726b
LP
1782
1783 case CREATE_SYMLINK:
3f93da98
ZJS
1784 if (!i.argument) {
1785 i.argument = strappend("/usr/share/factory/", i.path);
1786 if (!i.argument)
2f3b873a 1787 return log_oom();
468d726b
LP
1788 }
1789 break;
1790
31ed59c5 1791 case WRITE_FILE:
3f93da98 1792 if (!i.argument) {
31ed59c5 1793 log_error("[%s:%u] Write file requires argument.", fname, line);
7f2c1f4d 1794 return -EBADMSG;
31ed59c5
LP
1795 }
1796 break;
1797
849958d1 1798 case COPY_FILES:
3f93da98
ZJS
1799 if (!i.argument) {
1800 i.argument = strappend("/usr/share/factory/", i.path);
1801 if (!i.argument)
2f3b873a 1802 return log_oom();
3f93da98 1803 } else if (!path_is_absolute(i.argument)) {
849958d1
LP
1804 log_error("[%s:%u] Source path is not absolute.", fname, line);
1805 return -EBADMSG;
1806 }
1807
3f93da98 1808 path_kill_slashes(i.argument);
849958d1
LP
1809 break;
1810
468d726b
LP
1811 case CREATE_CHAR_DEVICE:
1812 case CREATE_BLOCK_DEVICE: {
1813 unsigned major, minor;
1814
3f93da98 1815 if (!i.argument) {
468d726b 1816 log_error("[%s:%u] Device file requires argument.", fname, line);
7f2c1f4d 1817 return -EBADMSG;
468d726b
LP
1818 }
1819
3f93da98
ZJS
1820 if (sscanf(i.argument, "%u:%u", &major, &minor) != 2) {
1821 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument);
7f2c1f4d 1822 return -EBADMSG;
468d726b
LP
1823 }
1824
3f93da98 1825 i.major_minor = makedev(major, minor);
468d726b
LP
1826 break;
1827 }
1828
ebf4e801 1829 case SET_XATTR:
b705ab6a 1830 case RECURSIVE_SET_XATTR:
3f93da98 1831 if (!i.argument) {
ebf4e801
MW
1832 log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
1833 return -EBADMSG;
1834 }
bd550f78 1835 r = parse_xattrs_from_arg(&i);
ebf4e801
MW
1836 if (r < 0)
1837 return r;
1838 break;
1839
f8eeeaf9 1840 case SET_ACL:
b705ab6a 1841 case RECURSIVE_SET_ACL:
f8eeeaf9
ZJS
1842 if (!i.argument) {
1843 log_error("[%s:%u] Set ACLs requires argument.", fname, line);
1844 return -EBADMSG;
1845 }
bd550f78 1846 r = parse_acls_from_arg(&i);
f8eeeaf9
ZJS
1847 if (r < 0)
1848 return r;
1849 break;
1850
88ec4dfa
LP
1851 case SET_ATTRIBUTE:
1852 case RECURSIVE_SET_ATTRIBUTE:
22c3a6ca 1853 if (!i.argument) {
88ec4dfa 1854 log_error("[%s:%u] Set file attribute requires argument.", fname, line);
22c3a6ca
GB
1855 return -EBADMSG;
1856 }
bd550f78 1857 r = parse_attribute_from_arg(&i);
22c3a6ca
GB
1858 if (r < 0)
1859 return r;
1860 break;
1861
777b87e7 1862 default:
582deb84 1863 log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type);
7f2c1f4d 1864 return -EBADMSG;
3b63d2d3 1865 }
468d726b 1866
3f93da98
ZJS
1867 if (!path_is_absolute(i.path)) {
1868 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path);
7f2c1f4d 1869 return -EBADMSG;
3b63d2d3
LP
1870 }
1871
3f93da98 1872 path_kill_slashes(i.path);
3b63d2d3 1873
3f93da98 1874 if (!should_include_path(i.path))
7f2c1f4d 1875 return 0;
5008d581 1876
cf9a4abd 1877 if (arg_root) {
cde684a2
LP
1878 char *p;
1879
3f93da98 1880 p = strappend(arg_root, i.path);
cf9a4abd
MM
1881 if (!p)
1882 return log_oom();
1883
3f93da98
ZJS
1884 free(i.path);
1885 i.path = p;
cf9a4abd
MM
1886 }
1887
90937fe3 1888 if (!isempty(user) && !streq(user, "-")) {
4b67834e
LP
1889 const char *u = user;
1890
3f93da98 1891 r = get_user_creds(&u, &i.uid, NULL, NULL, NULL);
4b67834e 1892 if (r < 0) {
3b63d2d3 1893 log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
7f2c1f4d 1894 return r;
3b63d2d3
LP
1895 }
1896
3f93da98 1897 i.uid_set = true;
3b63d2d3
LP
1898 }
1899
90937fe3 1900 if (!isempty(group) && !streq(group, "-")) {
4b67834e
LP
1901 const char *g = group;
1902
3f93da98 1903 r = get_group_creds(&g, &i.gid);
4b67834e 1904 if (r < 0) {
3b63d2d3 1905 log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
7f2c1f4d 1906 return r;
3b63d2d3
LP
1907 }
1908
3f93da98 1909 i.gid_set = true;
3b63d2d3
LP
1910 }
1911
90937fe3 1912 if (!isempty(mode) && !streq(mode, "-")) {
abef3f91 1913 const char *mm = mode;
3b63d2d3
LP
1914 unsigned m;
1915
abef3f91 1916 if (*mm == '~') {
3f93da98 1917 i.mask_perms = true;
abef3f91
LP
1918 mm++;
1919 }
1920
2ff7b0a5 1921 if (parse_mode(mm, &m) < 0) {
3b63d2d3 1922 log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
2ff7b0a5 1923 return -EBADMSG;
3b63d2d3
LP
1924 }
1925
3f93da98
ZJS
1926 i.mode = m;
1927 i.mode_set = true;
3b63d2d3 1928 } else
3f93da98
ZJS
1929 i.mode = IN_SET(i.type, CREATE_DIRECTORY, CREATE_SUBVOLUME, TRUNCATE_DIRECTORY)
1930 ? 0755 : 0644;
3b63d2d3 1931
90937fe3 1932 if (!isempty(age) && !streq(age, "-")) {
24f3a374
LP
1933 const char *a = age;
1934
1935 if (*a == '~') {
3f93da98 1936 i.keep_first_level = true;
24f3a374
LP
1937 a++;
1938 }
1939
3f93da98 1940 if (parse_sec(a, &i.age) < 0) {
3b63d2d3 1941 log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
7f2c1f4d 1942 return -EBADMSG;
3b63d2d3
LP
1943 }
1944
3f93da98 1945 i.age_set = true;
3b63d2d3
LP
1946 }
1947
3f93da98 1948 h = needs_glob(i.type) ? globs : items;
bfe95f35 1949
3f93da98 1950 existing = hashmap_get(h, i.path);
468d726b 1951 if (existing) {
3f93da98
ZJS
1952 unsigned n;
1953
1954 for (n = 0; n < existing->count; n++) {
6487ada8 1955 if (!item_compatible(existing->items + n, &i)) {
505ef0e3 1956 log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.",
3f93da98 1957 fname, line, i.path);
6487ada8
MP
1958 return 0;
1959 }
ebf4e801
MW
1960 }
1961 } else {
3f93da98
ZJS
1962 existing = new0(ItemArray, 1);
1963 r = hashmap_put(h, i.path, existing);
1964 if (r < 0)
1965 return log_oom();
bfe95f35
LP
1966 }
1967
3f93da98
ZJS
1968 if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1))
1969 return log_oom();
5008d581 1970
3f93da98 1971 memcpy(existing->items + existing->count++, &i, sizeof(i));
dd449aca
ZJS
1972
1973 /* Sort item array, to enforce stable ordering of application */
1974 qsort_safe(existing->items, existing->count, sizeof(Item), item_compare);
17493fa5 1975
3f93da98 1976 zero(i);
7f2c1f4d 1977 return 0;
5008d581
LP
1978}
1979
601185b4 1980static void help(void) {
522d4a49
LP
1981 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1982 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
5c795114 1983 " -h --help Show this help\n"
eb9da376 1984 " --version Show package version\n"
5c795114
DR
1985 " --create Create marked files/directories\n"
1986 " --clean Clean up marked directories\n"
1987 " --remove Remove marked files/directories\n"
81815651 1988 " --boot Execute actions only safe at boot\n"
79ca888f
ZJS
1989 " --prefix=PATH Only apply rules with the specified prefix\n"
1990 " --exclude-prefix=PATH Ignore rules with the specified prefix\n"
cf9a4abd 1991 " --root=PATH Operate on an alternate filesystem root\n",
3b63d2d3 1992 program_invocation_short_name);
3b63d2d3
LP
1993}
1994
1995static int parse_argv(int argc, char *argv[]) {
1996
1997 enum {
eb9da376 1998 ARG_VERSION = 0x100,
3b63d2d3
LP
1999 ARG_CREATE,
2000 ARG_CLEAN,
fba6e687 2001 ARG_REMOVE,
81815651 2002 ARG_BOOT,
5c795114
DR
2003 ARG_PREFIX,
2004 ARG_EXCLUDE_PREFIX,
cf9a4abd 2005 ARG_ROOT,
3b63d2d3
LP
2006 };
2007
2008 static const struct option options[] = {
5c795114 2009 { "help", no_argument, NULL, 'h' },
eb9da376 2010 { "version", no_argument, NULL, ARG_VERSION },
5c795114
DR
2011 { "create", no_argument, NULL, ARG_CREATE },
2012 { "clean", no_argument, NULL, ARG_CLEAN },
2013 { "remove", no_argument, NULL, ARG_REMOVE },
81815651 2014 { "boot", no_argument, NULL, ARG_BOOT },
5c795114
DR
2015 { "prefix", required_argument, NULL, ARG_PREFIX },
2016 { "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
cf9a4abd 2017 { "root", required_argument, NULL, ARG_ROOT },
eb9da376 2018 {}
3b63d2d3
LP
2019 };
2020
2021 int c;
2022
2023 assert(argc >= 0);
2024 assert(argv);
2025
601185b4 2026 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
3b63d2d3
LP
2027
2028 switch (c) {
2029
2030 case 'h':
601185b4
ZJS
2031 help();
2032 return 0;
eb9da376
LP
2033
2034 case ARG_VERSION:
2035 puts(PACKAGE_STRING);
2036 puts(SYSTEMD_FEATURES);
3b63d2d3
LP
2037 return 0;
2038
2039 case ARG_CREATE:
2040 arg_create = true;
2041 break;
2042
2043 case ARG_CLEAN:
2044 arg_clean = true;
2045 break;
2046
2047 case ARG_REMOVE:
2048 arg_remove = true;
2049 break;
2050
81815651
ZJS
2051 case ARG_BOOT:
2052 arg_boot = true;
c4708f13
ZJS
2053 break;
2054
fba6e687 2055 case ARG_PREFIX:
7bc040fa 2056 if (strv_push(&arg_include_prefixes, optarg) < 0)
a2aced4a 2057 return log_oom();
fba6e687
LP
2058 break;
2059
5c795114 2060 case ARG_EXCLUDE_PREFIX:
7bc040fa 2061 if (strv_push(&arg_exclude_prefixes, optarg) < 0)
5c795114
DR
2062 return log_oom();
2063 break;
2064
cf9a4abd 2065 case ARG_ROOT:
753615e8 2066 free(arg_root);
cf9a4abd
MM
2067 arg_root = path_make_absolute_cwd(optarg);
2068 if (!arg_root)
2069 return log_oom();
753615e8 2070
cf9a4abd
MM
2071 path_kill_slashes(arg_root);
2072 break;
2073
3b63d2d3
LP
2074 case '?':
2075 return -EINVAL;
2076
2077 default:
eb9da376 2078 assert_not_reached("Unhandled option");
3b63d2d3 2079 }
3b63d2d3
LP
2080
2081 if (!arg_clean && !arg_create && !arg_remove) {
35b8ca3a 2082 log_error("You need to specify at least one of --clean, --create or --remove.");
3b63d2d3
LP
2083 return -EINVAL;
2084 }
2085
2086 return 1;
2087}
2088
fba6e687 2089static int read_config_file(const char *fn, bool ignore_enoent) {
1731e34a
LP
2090 _cleanup_fclose_ FILE *f = NULL;
2091 char line[LINE_MAX];
78a92a5a 2092 Iterator iterator;
1731e34a 2093 unsigned v = 0;
78a92a5a 2094 Item *i;
1731e34a 2095 int r;
fba6e687
LP
2096
2097 assert(fn);
2098
cf9a4abd 2099 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
fabe5c0e 2100 if (r < 0) {
582deb84
ZJS
2101 if (ignore_enoent && r == -ENOENT) {
2102 log_debug_errno(r, "Failed to open \"%s\": %m", fn);
fba6e687 2103 return 0;
582deb84 2104 }
fba6e687 2105
8d3d7072 2106 return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
fba6e687 2107 }
582deb84 2108 log_debug("Reading config file \"%s\".", fn);
fba6e687 2109
1731e34a
LP
2110 FOREACH_LINE(line, f, break) {
2111 char *l;
fba6e687
LP
2112 int k;
2113
fba6e687
LP
2114 v++;
2115
2116 l = strstrip(line);
2117 if (*l == '#' || *l == 0)
2118 continue;
2119
1731e34a
LP
2120 k = parse_line(fn, v, l);
2121 if (k < 0 && r == 0)
2122 r = k;
fba6e687
LP
2123 }
2124
78a92a5a
MS
2125 /* we have to determine age parameter for each entry of type X */
2126 HASHMAP_FOREACH(i, globs, iterator) {
2127 Iterator iter;
2128 Item *j, *candidate_item = NULL;
2129
2130 if (i->type != IGNORE_DIRECTORY_PATH)
2131 continue;
2132
2133 HASHMAP_FOREACH(j, items, iter) {
d7b8eec7 2134 if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY && j->type != CREATE_SUBVOLUME)
78a92a5a
MS
2135 continue;
2136
2137 if (path_equal(j->path, i->path)) {
2138 candidate_item = j;
2139 break;
2140 }
2141
2142 if ((!candidate_item && path_startswith(i->path, j->path)) ||
2143 (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
2144 candidate_item = j;
2145 }
2146
9ed2a35e 2147 if (candidate_item && candidate_item->age_set) {
78a92a5a
MS
2148 i->age = candidate_item->age;
2149 i->age_set = true;
2150 }
2151 }
2152
fba6e687 2153 if (ferror(f)) {
56f64d95 2154 log_error_errno(errno, "Failed to read from file %s: %m", fn);
fba6e687
LP
2155 if (r == 0)
2156 r = -EIO;
2157 }
2158
fba6e687
LP
2159 return r;
2160}
2161
5008d581 2162int main(int argc, char *argv[]) {
fabe5c0e 2163 int r, k;
3f93da98 2164 ItemArray *a;
3b63d2d3
LP
2165 Iterator iterator;
2166
fdcad0c2
LP
2167 r = parse_argv(argc, argv);
2168 if (r <= 0)
753615e8 2169 goto finish;
5008d581 2170
eb0ca9eb 2171 log_set_target(LOG_TARGET_AUTO);
5008d581
LP
2172 log_parse_environment();
2173 log_open();
2174
4c12626c
LP
2175 umask(0022);
2176
cc56fafe 2177 mac_selinux_init(NULL);
5008d581 2178
d5099efc
MS
2179 items = hashmap_new(&string_hash_ops);
2180 globs = hashmap_new(&string_hash_ops);
b8bb3e8f
LP
2181
2182 if (!items || !globs) {
fabe5c0e 2183 r = log_oom();
3b63d2d3
LP
2184 goto finish;
2185 }
2186
fabe5c0e 2187 r = 0;
5008d581 2188
fba6e687
LP
2189 if (optind < argc) {
2190 int j;
5008d581 2191
9125670f 2192 for (j = optind; j < argc; j++) {
fabe5c0e
LP
2193 k = read_config_file(argv[j], false);
2194 if (k < 0 && r == 0)
2195 r = k;
9125670f 2196 }
5008d581 2197
fba6e687 2198 } else {
fabe5c0e
LP
2199 _cleanup_strv_free_ char **files = NULL;
2200 char **f;
5008d581 2201
cf9a4abd 2202 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
44143309 2203 if (r < 0) {
da927ba9 2204 log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m");
44143309
KS
2205 goto finish;
2206 }
3b63d2d3 2207
772f8371 2208 STRV_FOREACH(f, files) {
fabe5c0e
LP
2209 k = read_config_file(*f, true);
2210 if (k < 0 && r == 0)
2211 r = k;
5008d581 2212 }
772f8371 2213 }
5008d581 2214
17493fa5
LP
2215 /* The non-globbing ones usually create things, hence we apply
2216 * them first */
2217 HASHMAP_FOREACH(a, items, iterator) {
3f93da98 2218 k = process_item_array(a);
1db50423
ZJS
2219 if (k < 0 && r == 0)
2220 r = k;
2221 }
b8bb3e8f 2222
17493fa5
LP
2223 /* The globbing ones usually alter things, hence we apply them
2224 * second. */
2225 HASHMAP_FOREACH(a, globs, iterator) {
3f93da98 2226 k = process_item_array(a);
1db50423
ZJS
2227 if (k < 0 && r == 0)
2228 r = k;
2229 }
3b63d2d3 2230
5008d581 2231finish:
3f93da98
ZJS
2232 while ((a = hashmap_steal_first(items)))
2233 item_array_free(a);
3b63d2d3 2234
3f93da98
ZJS
2235 while ((a = hashmap_steal_first(globs)))
2236 item_array_free(a);
17b90525 2237
3b63d2d3 2238 hashmap_free(items);
b8bb3e8f 2239 hashmap_free(globs);
5008d581 2240
7bc040fa
LP
2241 free(arg_include_prefixes);
2242 free(arg_exclude_prefixes);
cf9a4abd 2243 free(arg_root);
a2aced4a 2244
17b90525
LP
2245 set_free_free(unix_sockets);
2246
cc56fafe 2247 mac_selinux_finish();
29003cff 2248
fabe5c0e 2249 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
5008d581 2250}