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