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