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