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