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