LCOV - code coverage report
Current view: top level - tmpfiles - tmpfiles.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 388 1631 23.8 %
Date: 2019-08-22 15:41:25 Functions: 31 74 41.9 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <fcntl.h>
       5             : #include <fnmatch.h>
       6             : #include <getopt.h>
       7             : #include <glob.h>
       8             : #include <limits.h>
       9             : #include <linux/fs.h>
      10             : #include <stdbool.h>
      11             : #include <stddef.h>
      12             : #include <stdio.h>
      13             : #include <stdlib.h>
      14             : #include <string.h>
      15             : #include <sys/file.h>
      16             : #include <sys/stat.h>
      17             : #include <sys/xattr.h>
      18             : #include <sysexits.h>
      19             : #include <time.h>
      20             : #include <unistd.h>
      21             : 
      22             : #include "sd-path.h"
      23             : 
      24             : #include "acl-util.h"
      25             : #include "alloc-util.h"
      26             : #include "btrfs-util.h"
      27             : #include "capability-util.h"
      28             : #include "chattr-util.h"
      29             : #include "conf-files.h"
      30             : #include "copy.h"
      31             : #include "def.h"
      32             : #include "dirent-util.h"
      33             : #include "escape.h"
      34             : #include "fd-util.h"
      35             : #include "fileio.h"
      36             : #include "format-util.h"
      37             : #include "fs-util.h"
      38             : #include "glob-util.h"
      39             : #include "io-util.h"
      40             : #include "label.h"
      41             : #include "log.h"
      42             : #include "macro.h"
      43             : #include "main-func.h"
      44             : #include "missing.h"
      45             : #include "mkdir.h"
      46             : #include "mountpoint-util.h"
      47             : #include "pager.h"
      48             : #include "parse-util.h"
      49             : #include "path-lookup.h"
      50             : #include "path-util.h"
      51             : #include "pretty-print.h"
      52             : #include "rlimit-util.h"
      53             : #include "rm-rf.h"
      54             : #include "selinux-util.h"
      55             : #include "set.h"
      56             : #include "sort-util.h"
      57             : #include "specifier.h"
      58             : #include "stat-util.h"
      59             : #include "stdio-util.h"
      60             : #include "string-table.h"
      61             : #include "string-util.h"
      62             : #include "strv.h"
      63             : #include "umask-util.h"
      64             : #include "user-util.h"
      65             : 
      66             : /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
      67             :  * them in the file system. This is intended to be used to create
      68             :  * properly owned directories beneath /tmp, /var/tmp, /run, which are
      69             :  * volatile and hence need to be recreated on bootup. */
      70             : 
      71             : typedef enum OperationMask {
      72             :         OPERATION_CREATE = 1 << 0,
      73             :         OPERATION_REMOVE = 1 << 1,
      74             :         OPERATION_CLEAN  = 1 << 2,
      75             : } OperationMask;
      76             : 
      77             : typedef enum ItemType {
      78             :         /* These ones take file names */
      79             :         CREATE_FILE = 'f',
      80             :         TRUNCATE_FILE = 'F',
      81             :         CREATE_DIRECTORY = 'd',
      82             :         TRUNCATE_DIRECTORY = 'D',
      83             :         CREATE_SUBVOLUME = 'v',
      84             :         CREATE_SUBVOLUME_INHERIT_QUOTA = 'q',
      85             :         CREATE_SUBVOLUME_NEW_QUOTA = 'Q',
      86             :         CREATE_FIFO = 'p',
      87             :         CREATE_SYMLINK = 'L',
      88             :         CREATE_CHAR_DEVICE = 'c',
      89             :         CREATE_BLOCK_DEVICE = 'b',
      90             :         COPY_FILES = 'C',
      91             : 
      92             :         /* These ones take globs */
      93             :         WRITE_FILE = 'w',
      94             :         EMPTY_DIRECTORY = 'e',
      95             :         SET_XATTR = 't',
      96             :         RECURSIVE_SET_XATTR = 'T',
      97             :         SET_ACL = 'a',
      98             :         RECURSIVE_SET_ACL = 'A',
      99             :         SET_ATTRIBUTE = 'h',
     100             :         RECURSIVE_SET_ATTRIBUTE = 'H',
     101             :         IGNORE_PATH = 'x',
     102             :         IGNORE_DIRECTORY_PATH = 'X',
     103             :         REMOVE_PATH = 'r',
     104             :         RECURSIVE_REMOVE_PATH = 'R',
     105             :         RELABEL_PATH = 'z',
     106             :         RECURSIVE_RELABEL_PATH = 'Z',
     107             :         ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
     108             : } ItemType;
     109             : 
     110             : typedef struct Item {
     111             :         ItemType type;
     112             : 
     113             :         char *path;
     114             :         char *argument;
     115             :         char **xattrs;
     116             : #if HAVE_ACL
     117             :         acl_t acl_access;
     118             :         acl_t acl_default;
     119             : #endif
     120             :         uid_t uid;
     121             :         gid_t gid;
     122             :         mode_t mode;
     123             :         usec_t age;
     124             : 
     125             :         dev_t major_minor;
     126             :         unsigned attribute_value;
     127             :         unsigned attribute_mask;
     128             : 
     129             :         bool uid_set:1;
     130             :         bool gid_set:1;
     131             :         bool mode_set:1;
     132             :         bool age_set:1;
     133             :         bool mask_perms:1;
     134             :         bool attribute_set:1;
     135             : 
     136             :         bool keep_first_level:1;
     137             : 
     138             :         bool force:1;
     139             : 
     140             :         bool allow_failure:1;
     141             : 
     142             :         OperationMask done;
     143             : } Item;
     144             : 
     145             : typedef struct ItemArray {
     146             :         Item *items;
     147             :         size_t n_items;
     148             :         size_t allocated;
     149             : 
     150             :         struct ItemArray *parent;
     151             :         Set *children;
     152             : } ItemArray;
     153             : 
     154             : typedef enum DirectoryType {
     155             :         DIRECTORY_RUNTIME,
     156             :         DIRECTORY_STATE,
     157             :         DIRECTORY_CACHE,
     158             :         DIRECTORY_LOGS,
     159             :         _DIRECTORY_TYPE_MAX,
     160             : } DirectoryType;
     161             : 
     162             : static bool arg_cat_config = false;
     163             : static bool arg_user = false;
     164             : static OperationMask arg_operation = 0;
     165             : static bool arg_boot = false;
     166             : static PagerFlags arg_pager_flags = 0;
     167             : 
     168             : static char **arg_include_prefixes = NULL;
     169             : static char **arg_exclude_prefixes = NULL;
     170             : static char *arg_root = NULL;
     171             : static char *arg_replace = NULL;
     172             : 
     173             : #define MAX_DEPTH 256
     174             : 
     175             : static OrderedHashmap *items = NULL, *globs = NULL;
     176             : static Set *unix_sockets = NULL;
     177             : 
     178          86 : STATIC_DESTRUCTOR_REGISTER(items, ordered_hashmap_freep);
     179          86 : STATIC_DESTRUCTOR_REGISTER(globs, ordered_hashmap_freep);
     180          86 : STATIC_DESTRUCTOR_REGISTER(unix_sockets, set_free_freep);
     181          86 : STATIC_DESTRUCTOR_REGISTER(arg_include_prefixes, freep);
     182          86 : STATIC_DESTRUCTOR_REGISTER(arg_exclude_prefixes, freep);
     183          86 : STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
     184             : 
     185             : static int specifier_machine_id_safe(char specifier, const void *data, const void *userdata, char **ret);
     186             : static int specifier_directory(char specifier, const void *data, const void *userdata, char **ret);
     187             : 
     188             : static const Specifier specifier_table[] = {
     189             :         { 'm', specifier_machine_id_safe, NULL },
     190             :         { 'b', specifier_boot_id,         NULL },
     191             :         { 'H', specifier_host_name,       NULL },
     192             :         { 'v', specifier_kernel_release,  NULL },
     193             : 
     194             :         { 'g', specifier_group_name,      NULL },
     195             :         { 'G', specifier_group_id,        NULL },
     196             :         { 'U', specifier_user_id,         NULL },
     197             :         { 'u', specifier_user_name,       NULL },
     198             :         { 'h', specifier_user_home,       NULL },
     199             : 
     200             :         { 't', specifier_directory,       UINT_TO_PTR(DIRECTORY_RUNTIME) },
     201             :         { 'S', specifier_directory,       UINT_TO_PTR(DIRECTORY_STATE) },
     202             :         { 'C', specifier_directory,       UINT_TO_PTR(DIRECTORY_CACHE) },
     203             :         { 'L', specifier_directory,       UINT_TO_PTR(DIRECTORY_LOGS) },
     204             :         { 'T', specifier_tmp_dir,         NULL },
     205             :         { 'V', specifier_var_tmp_dir,     NULL },
     206             :         {}
     207             : };
     208             : 
     209           2 : static int specifier_machine_id_safe(char specifier, const void *data, const void *userdata, char **ret) {
     210             :         int r;
     211             : 
     212             :         /* If /etc/machine_id is missing or empty (e.g. in a chroot environment)
     213             :          * return a recognizable error so that the caller can skip the rule
     214             :          * gracefully. */
     215             : 
     216           2 :         r = specifier_machine_id(specifier, data, userdata, ret);
     217           2 :         if (IN_SET(r, -ENOENT, -ENOMEDIUM))
     218           0 :                 return -ENXIO;
     219             : 
     220           2 :         return r;
     221             : }
     222             : 
     223           6 : static int specifier_directory(char specifier, const void *data, const void *userdata, char **ret) {
     224             :         struct table_entry {
     225             :                 uint64_t type;
     226             :                 const char *suffix;
     227             :         };
     228             : 
     229             :         static const struct table_entry paths_system[] = {
     230             :                 [DIRECTORY_RUNTIME] = { SD_PATH_SYSTEM_RUNTIME            },
     231             :                 [DIRECTORY_STATE] =   { SD_PATH_SYSTEM_STATE_PRIVATE      },
     232             :                 [DIRECTORY_CACHE] =   { SD_PATH_SYSTEM_STATE_CACHE        },
     233             :                 [DIRECTORY_LOGS] =    { SD_PATH_SYSTEM_STATE_LOGS         },
     234             :         };
     235             : 
     236             :         static const struct table_entry paths_user[] = {
     237             :                 [DIRECTORY_RUNTIME] = { SD_PATH_USER_RUNTIME              },
     238             :                 [DIRECTORY_STATE] =   { SD_PATH_USER_CONFIGURATION        },
     239             :                 [DIRECTORY_CACHE] =   { SD_PATH_USER_STATE_CACHE          },
     240             :                 [DIRECTORY_LOGS] =    { SD_PATH_USER_CONFIGURATION, "log" },
     241             :         };
     242             : 
     243             :         unsigned i;
     244             :         const struct table_entry *paths;
     245             : 
     246             :         assert_cc(ELEMENTSOF(paths_system) == ELEMENTSOF(paths_user));
     247           6 :         paths = arg_user ? paths_user : paths_system;
     248             : 
     249           6 :         i = PTR_TO_UINT(data);
     250           6 :         assert(i < ELEMENTSOF(paths_system));
     251             : 
     252           6 :         return sd_path_home(paths[i].type, paths[i].suffix, ret);
     253             : }
     254             : 
     255           1 : static int log_unresolvable_specifier(const char *filename, unsigned line) {
     256             :         static bool notified = false;
     257             : 
     258             :         /* In system mode, this is called when /etc is not fully initialized (e.g.
     259             :          * in a chroot environment) where some specifiers are unresolvable. In user
     260             :          * mode, this is called when some variables are not defined. These cases are
     261             :          * not considered as an error so log at LOG_NOTICE only for the first time
     262             :          * and then downgrade this to LOG_DEBUG for the rest. */
     263             : 
     264           1 :         log_full(notified ? LOG_DEBUG : LOG_NOTICE,
     265             :                  "[%s:%u] Failed to resolve specifier: %s, skipping",
     266             :                  filename, line,
     267             :                  arg_user ? "Required $XDG_... variable not defined" : "uninitialized /etc detected");
     268             : 
     269           1 :         if (!notified)
     270           1 :                 log_notice("All rules containing unresolvable specifiers will be skipped.");
     271             : 
     272           1 :         notified = true;
     273           1 :         return 0;
     274             : }
     275             : 
     276          40 : static int user_config_paths(char*** ret) {
     277          40 :         _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
     278          40 :         _cleanup_free_ char *persistent_config = NULL, *runtime_config = NULL, *data_home = NULL;
     279          40 :         _cleanup_strv_free_ char **res = NULL;
     280             :         int r;
     281             : 
     282          40 :         r = xdg_user_dirs(&config_dirs, &data_dirs);
     283          40 :         if (r < 0)
     284           0 :                 return r;
     285             : 
     286          40 :         r = xdg_user_config_dir(&persistent_config, "/user-tmpfiles.d");
     287          40 :         if (r < 0 && r != -ENXIO)
     288           0 :                 return r;
     289             : 
     290          40 :         r = xdg_user_runtime_dir(&runtime_config, "/user-tmpfiles.d");
     291          40 :         if (r < 0 && r != -ENXIO)
     292           0 :                 return r;
     293             : 
     294          40 :         r = xdg_user_data_dir(&data_home, "/user-tmpfiles.d");
     295          40 :         if (r < 0 && r != -ENXIO)
     296           0 :                 return r;
     297             : 
     298          40 :         r = strv_extend_strv_concat(&res, config_dirs, "/user-tmpfiles.d");
     299          40 :         if (r < 0)
     300           0 :                 return r;
     301             : 
     302          40 :         r = strv_extend(&res, persistent_config);
     303          40 :         if (r < 0)
     304           0 :                 return r;
     305             : 
     306          40 :         r = strv_extend(&res, runtime_config);
     307          40 :         if (r < 0)
     308           0 :                 return r;
     309             : 
     310          40 :         r = strv_extend(&res, data_home);
     311          40 :         if (r < 0)
     312           0 :                 return r;
     313             : 
     314          40 :         r = strv_extend_strv_concat(&res, data_dirs, "/user-tmpfiles.d");
     315          40 :         if (r < 0)
     316           0 :                 return r;
     317             : 
     318          40 :         r = path_strv_make_absolute_cwd(res);
     319          40 :         if (r < 0)
     320           0 :                 return r;
     321             : 
     322          40 :         *ret = TAKE_PTR(res);
     323          40 :         return 0;
     324             : }
     325             : 
     326          27 : static bool needs_glob(ItemType t) {
     327          27 :         return IN_SET(t,
     328             :                       WRITE_FILE,
     329             :                       IGNORE_PATH,
     330             :                       IGNORE_DIRECTORY_PATH,
     331             :                       REMOVE_PATH,
     332             :                       RECURSIVE_REMOVE_PATH,
     333             :                       EMPTY_DIRECTORY,
     334             :                       ADJUST_MODE,
     335             :                       RELABEL_PATH,
     336             :                       RECURSIVE_RELABEL_PATH,
     337             :                       SET_XATTR,
     338             :                       RECURSIVE_SET_XATTR,
     339             :                       SET_ACL,
     340             :                       RECURSIVE_SET_ACL,
     341             :                       SET_ATTRIBUTE,
     342             :                       RECURSIVE_SET_ATTRIBUTE);
     343             : }
     344             : 
     345           0 : static bool takes_ownership(ItemType t) {
     346           0 :         return IN_SET(t,
     347             :                       CREATE_FILE,
     348             :                       TRUNCATE_FILE,
     349             :                       CREATE_DIRECTORY,
     350             :                       EMPTY_DIRECTORY,
     351             :                       TRUNCATE_DIRECTORY,
     352             :                       CREATE_SUBVOLUME,
     353             :                       CREATE_SUBVOLUME_INHERIT_QUOTA,
     354             :                       CREATE_SUBVOLUME_NEW_QUOTA,
     355             :                       CREATE_FIFO,
     356             :                       CREATE_SYMLINK,
     357             :                       CREATE_CHAR_DEVICE,
     358             :                       CREATE_BLOCK_DEVICE,
     359             :                       COPY_FILES,
     360             :                       WRITE_FILE,
     361             :                       IGNORE_PATH,
     362             :                       IGNORE_DIRECTORY_PATH,
     363             :                       REMOVE_PATH,
     364             :                       RECURSIVE_REMOVE_PATH);
     365             : }
     366             : 
     367           0 : static struct Item* find_glob(OrderedHashmap *h, const char *match) {
     368             :         ItemArray *j;
     369             :         Iterator i;
     370             : 
     371           0 :         ORDERED_HASHMAP_FOREACH(j, h, i) {
     372             :                 size_t n;
     373             : 
     374           0 :                 for (n = 0; n < j->n_items; n++) {
     375           0 :                         Item *item = j->items + n;
     376             : 
     377           0 :                         if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
     378           0 :                                 return item;
     379             :                 }
     380             :         }
     381             : 
     382           0 :         return NULL;
     383             : }
     384             : 
     385           0 : static int load_unix_sockets(void) {
     386           0 :         _cleanup_set_free_free_ Set *sockets = NULL;
     387           0 :         _cleanup_fclose_ FILE *f = NULL;
     388             :         int r;
     389             : 
     390           0 :         if (unix_sockets)
     391           0 :                 return 0;
     392             : 
     393             :         /* We maintain a cache of the sockets we found in /proc/net/unix to speed things up a little. */
     394             : 
     395           0 :         sockets = set_new(&path_hash_ops);
     396           0 :         if (!sockets)
     397           0 :                 return log_oom();
     398             : 
     399           0 :         f = fopen("/proc/net/unix", "re");
     400           0 :         if (!f)
     401           0 :                 return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
     402             :                                       "Failed to open /proc/net/unix, ignoring: %m");
     403             : 
     404             :         /* Skip header */
     405           0 :         r = read_line(f, LONG_LINE_MAX, NULL);
     406           0 :         if (r < 0)
     407           0 :                 return log_warning_errno(r, "Failed to skip /proc/net/unix header line: %m");
     408           0 :         if (r == 0)
     409           0 :                 return log_warning_errno(SYNTHETIC_ERRNO(EIO), "Premature end of file reading /proc/net/unix.");
     410             : 
     411           0 :         for (;;) {
     412           0 :                 _cleanup_free_ char *line = NULL, *s = NULL;
     413             :                 char *p;
     414             : 
     415           0 :                 r = read_line(f, LONG_LINE_MAX, &line);
     416           0 :                 if (r < 0)
     417           0 :                         return log_warning_errno(r, "Failed to read /proc/net/unix line, ignoring: %m");
     418           0 :                 if (r == 0) /* EOF */
     419           0 :                         break;
     420             : 
     421           0 :                 p = strchr(line, ':');
     422           0 :                 if (!p)
     423           0 :                         continue;
     424             : 
     425           0 :                 if (strlen(p) < 37)
     426           0 :                         continue;
     427             : 
     428           0 :                 p += 37;
     429           0 :                 p += strspn(p, WHITESPACE);
     430           0 :                 p += strcspn(p, WHITESPACE); /* skip one more word */
     431           0 :                 p += strspn(p, WHITESPACE);
     432             : 
     433           0 :                 if (*p != '/')
     434           0 :                         continue;
     435             : 
     436           0 :                 s = strdup(p);
     437           0 :                 if (!s)
     438           0 :                         return log_oom();
     439             : 
     440           0 :                 path_simplify(s, false);
     441             : 
     442           0 :                 r = set_consume(sockets, s);
     443           0 :                 if (r == -EEXIST)
     444           0 :                         continue;
     445           0 :                 if (r < 0)
     446           0 :                         return log_warning_errno(r, "Failed to add AF_UNIX socket to set, ignoring: %m");
     447             : 
     448           0 :                 TAKE_PTR(s);
     449             :         }
     450             : 
     451           0 :         unix_sockets = TAKE_PTR(sockets);
     452           0 :         return 1;
     453             : }
     454             : 
     455           0 : static bool unix_socket_alive(const char *fn) {
     456           0 :         assert(fn);
     457             : 
     458           0 :         if (load_unix_sockets() < 0)
     459           0 :                 return true;     /* We don't know, so assume yes */
     460             : 
     461           0 :         return !!set_get(unix_sockets, (char*) fn);
     462             : }
     463             : 
     464           0 : static DIR* xopendirat_nomod(int dirfd, const char *path) {
     465             :         DIR *dir;
     466             : 
     467           0 :         dir = xopendirat(dirfd, path, O_NOFOLLOW|O_NOATIME);
     468           0 :         if (dir)
     469           0 :                 return dir;
     470             : 
     471           0 :         log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path);
     472           0 :         if (errno != EPERM)
     473           0 :                 return NULL;
     474             : 
     475           0 :         dir = xopendirat(dirfd, path, O_NOFOLLOW);
     476           0 :         if (!dir)
     477           0 :                 log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path);
     478             : 
     479           0 :         return dir;
     480             : }
     481             : 
     482           0 : static DIR* opendir_nomod(const char *path) {
     483           0 :         return xopendirat_nomod(AT_FDCWD, path);
     484             : }
     485             : 
     486           0 : static int dir_cleanup(
     487             :                 Item *i,
     488             :                 const char *p,
     489             :                 DIR *d,
     490             :                 const struct stat *ds,
     491             :                 usec_t cutoff,
     492             :                 dev_t rootdev,
     493             :                 bool mountpoint,
     494             :                 int maxdepth,
     495             :                 bool keep_this_level) {
     496             : 
     497             :         struct dirent *dent;
     498           0 :         bool deleted = false;
     499           0 :         int r = 0;
     500             : 
     501           0 :         FOREACH_DIRENT_ALL(dent, d, break) {
     502             :                 struct stat s;
     503             :                 usec_t age;
     504           0 :                 _cleanup_free_ char *sub_path = NULL;
     505             : 
     506           0 :                 if (dot_or_dot_dot(dent->d_name))
     507           0 :                         continue;
     508             : 
     509           0 :                 if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
     510           0 :                         if (errno == ENOENT)
     511           0 :                                 continue;
     512             : 
     513             :                         /* FUSE, NFS mounts, SELinux might return EACCES */
     514           0 :                         r = log_full_errno(errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
     515             :                                            "stat(%s/%s) failed: %m", p, dent->d_name);
     516           0 :                         continue;
     517             :                 }
     518             : 
     519             :                 /* Stay on the same filesystem */
     520           0 :                 if (s.st_dev != rootdev) {
     521           0 :                         log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name);
     522           0 :                         continue;
     523             :                 }
     524             : 
     525             :                 /* Try to detect bind mounts of the same filesystem instance; they
     526             :                  * do not differ in device major/minors. This type of query is not
     527             :                  * supported on all kernels or filesystem types though. */
     528           0 :                 if (S_ISDIR(s.st_mode)) {
     529             :                         int q;
     530             : 
     531           0 :                         q = fd_is_mount_point(dirfd(d), dent->d_name, 0);
     532           0 :                         if (q < 0)
     533           0 :                                 log_debug_errno(q, "Failed to determine whether \"%s/%s\" is a mount point, ignoring: %m", p, dent->d_name);
     534           0 :                         else if (q > 0) {
     535           0 :                                 log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.", p, dent->d_name);
     536           0 :                                 continue;
     537             :                         }
     538             :                 }
     539             : 
     540           0 :                 sub_path = path_join(p, dent->d_name);
     541           0 :                 if (!sub_path) {
     542           0 :                         r = log_oom();
     543           0 :                         goto finish;
     544             :                 }
     545             : 
     546             :                 /* Is there an item configured for this path? */
     547           0 :                 if (ordered_hashmap_get(items, sub_path)) {
     548           0 :                         log_debug("Ignoring \"%s\": a separate entry exists.", sub_path);
     549           0 :                         continue;
     550             :                 }
     551             : 
     552           0 :                 if (find_glob(globs, sub_path)) {
     553           0 :                         log_debug("Ignoring \"%s\": a separate glob exists.", sub_path);
     554           0 :                         continue;
     555             :                 }
     556             : 
     557           0 :                 if (S_ISDIR(s.st_mode)) {
     558           0 :                         _cleanup_closedir_ DIR *sub_dir = NULL;
     559             : 
     560           0 :                         if (mountpoint &&
     561           0 :                             streq(dent->d_name, "lost+found") &&
     562           0 :                             s.st_uid == 0) {
     563           0 :                                 log_debug("Ignoring directory \"%s\".", sub_path);
     564           0 :                                 continue;
     565             :                         }
     566             : 
     567           0 :                         if (maxdepth <= 0)
     568           0 :                                 log_warning("Reached max depth on \"%s\".", sub_path);
     569             :                         else {
     570             :                                 int q;
     571             : 
     572           0 :                                 sub_dir = xopendirat_nomod(dirfd(d), dent->d_name);
     573           0 :                                 if (!sub_dir) {
     574           0 :                                         if (errno != ENOENT)
     575           0 :                                                 r = log_warning_errno(errno, "Opening directory \"%s\" failed, ignoring: %m", sub_path);
     576             : 
     577           0 :                                         continue;
     578             :                                 }
     579             : 
     580           0 :                                 if (flock(dirfd(sub_dir), LOCK_EX|LOCK_NB) < 0) {
     581           0 :                                         log_debug_errno(errno, "Couldn't acquire shared BSD lock on directory \"%s\", skipping: %m", p);
     582           0 :                                         continue;
     583             :                                 }
     584             : 
     585           0 :                                 q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
     586           0 :                                 if (q < 0)
     587           0 :                                         r = q;
     588             :                         }
     589             : 
     590             :                         /* Note: if you are wondering why we don't support the sticky bit for excluding
     591             :                          * directories from cleaning like we do it for other file system objects: well, the
     592             :                          * sticky bit already has a meaning for directories, so we don't want to overload
     593             :                          * that. */
     594             : 
     595           0 :                         if (keep_this_level) {
     596           0 :                                 log_debug("Keeping directory \"%s\".", sub_path);
     597           0 :                                 continue;
     598             :                         }
     599             : 
     600             :                         /* Ignore ctime, we change it when deleting */
     601           0 :                         age = timespec_load(&s.st_mtim);
     602           0 :                         if (age >= cutoff) {
     603             :                                 char a[FORMAT_TIMESTAMP_MAX];
     604             :                                 /* Follows spelling in stat(1). */
     605           0 :                                 log_debug("Directory \"%s\": modify time %s is too new.",
     606             :                                           sub_path,
     607             :                                           format_timestamp_us(a, sizeof(a), age));
     608           0 :                                 continue;
     609             :                         }
     610             : 
     611           0 :                         age = timespec_load(&s.st_atim);
     612           0 :                         if (age >= cutoff) {
     613             :                                 char a[FORMAT_TIMESTAMP_MAX];
     614           0 :                                 log_debug("Directory \"%s\": access time %s is too new.",
     615             :                                           sub_path,
     616             :                                           format_timestamp_us(a, sizeof(a), age));
     617           0 :                                 continue;
     618             :                         }
     619             : 
     620           0 :                         log_debug("Removing directory \"%s\".", sub_path);
     621           0 :                         if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0)
     622           0 :                                 if (!IN_SET(errno, ENOENT, ENOTEMPTY))
     623           0 :                                         r = log_warning_errno(errno, "Failed to remove directory \"%s\", ignoring: %m", sub_path);
     624             : 
     625             :                 } else {
     626             :                         /* Skip files for which the sticky bit is set. These are semantics we define, and are
     627             :                          * unknown elsewhere. See XDG_RUNTIME_DIR specification for details. */
     628           0 :                         if (s.st_mode & S_ISVTX) {
     629           0 :                                 log_debug("Skipping \"%s\": sticky bit set.", sub_path);
     630           0 :                                 continue;
     631             :                         }
     632             : 
     633           0 :                         if (mountpoint &&
     634           0 :                             S_ISREG(s.st_mode) &&
     635           0 :                             s.st_uid == 0 &&
     636           0 :                             STR_IN_SET(dent->d_name,
     637             :                                        ".journal",
     638             :                                        "aquota.user",
     639             :                                        "aquota.group")) {
     640           0 :                                 log_debug("Skipping \"%s\".", sub_path);
     641           0 :                                 continue;
     642             :                         }
     643             : 
     644             :                         /* Ignore sockets that are listed in /proc/net/unix */
     645           0 :                         if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) {
     646           0 :                                 log_debug("Skipping \"%s\": live socket.", sub_path);
     647           0 :                                 continue;
     648             :                         }
     649             : 
     650             :                         /* Ignore device nodes */
     651           0 :                         if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) {
     652           0 :                                 log_debug("Skipping \"%s\": a device.", sub_path);
     653           0 :                                 continue;
     654             :                         }
     655             : 
     656             :                         /* Keep files on this level around if this is requested */
     657           0 :                         if (keep_this_level) {
     658           0 :                                 log_debug("Keeping \"%s\".", sub_path);
     659           0 :                                 continue;
     660             :                         }
     661             : 
     662           0 :                         age = timespec_load(&s.st_mtim);
     663           0 :                         if (age >= cutoff) {
     664             :                                 char a[FORMAT_TIMESTAMP_MAX];
     665             :                                 /* Follows spelling in stat(1). */
     666           0 :                                 log_debug("File \"%s\": modify time %s is too new.",
     667             :                                           sub_path,
     668             :                                           format_timestamp_us(a, sizeof(a), age));
     669           0 :                                 continue;
     670             :                         }
     671             : 
     672           0 :                         age = timespec_load(&s.st_atim);
     673           0 :                         if (age >= cutoff) {
     674             :                                 char a[FORMAT_TIMESTAMP_MAX];
     675           0 :                                 log_debug("File \"%s\": access time %s is too new.",
     676             :                                           sub_path,
     677             :                                           format_timestamp_us(a, sizeof(a), age));
     678           0 :                                 continue;
     679             :                         }
     680             : 
     681           0 :                         age = timespec_load(&s.st_ctim);
     682           0 :                         if (age >= cutoff) {
     683             :                                 char a[FORMAT_TIMESTAMP_MAX];
     684           0 :                                 log_debug("File \"%s\": change time %s is too new.",
     685             :                                           sub_path,
     686             :                                           format_timestamp_us(a, sizeof(a), age));
     687           0 :                                 continue;
     688             :                         }
     689             : 
     690           0 :                         log_debug("Removing \"%s\".", sub_path);
     691           0 :                         if (unlinkat(dirfd(d), dent->d_name, 0) < 0)
     692           0 :                                 if (errno != ENOENT)
     693           0 :                                         r = log_warning_errno(errno, "Failed to remove \"%s\", ignoring: %m", sub_path);
     694             : 
     695           0 :                         deleted = true;
     696             :                 }
     697             :         }
     698             : 
     699           0 : finish:
     700           0 :         if (deleted) {
     701             :                 char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX];
     702             :                 usec_t age1, age2;
     703             : 
     704           0 :                 age1 = timespec_load(&ds->st_atim);
     705           0 :                 age2 = timespec_load(&ds->st_mtim);
     706             : 
     707           0 :                 log_debug("Restoring access and modification time on \"%s\": %s, %s",
     708             :                           p,
     709             :                           format_timestamp_us(a, sizeof(a), age1),
     710             :                           format_timestamp_us(b, sizeof(b), age2));
     711             : 
     712             :                 /* Restore original directory timestamps */
     713           0 :                 if (futimens(dirfd(d), (struct timespec[]) {
     714             :                                 ds->st_atim,
     715             :                                 ds->st_mtim }) < 0)
     716           0 :                         log_warning_errno(errno, "Failed to revert timestamps of '%s', ignoring: %m", p);
     717             :         }
     718             : 
     719           0 :         return r;
     720             : }
     721             : 
     722           0 : static bool dangerous_hardlinks(void) {
     723           0 :         _cleanup_free_ char *value = NULL;
     724             :         static int cached = -1;
     725             :         int r;
     726             : 
     727             :         /* Check whether the fs.protected_hardlinks sysctl is on. If we can't determine it we assume its off, as that's
     728             :          * what the upstream default is. */
     729             : 
     730           0 :         if (cached >= 0)
     731           0 :                 return cached;
     732             : 
     733           0 :         r = read_one_line_file("/proc/sys/fs/protected_hardlinks", &value);
     734           0 :         if (r < 0) {
     735           0 :                 log_debug_errno(r, "Failed to read fs.protected_hardlinks sysctl: %m");
     736           0 :                 return true;
     737             :         }
     738             : 
     739           0 :         r = parse_boolean(value);
     740           0 :         if (r < 0) {
     741           0 :                 log_debug_errno(r, "Failed to parse fs.protected_hardlinks sysctl: %m");
     742           0 :                 return true;
     743             :         }
     744             : 
     745           0 :         cached = r == 0;
     746           0 :         return cached;
     747             : }
     748             : 
     749           0 : static bool hardlink_vulnerable(const struct stat *st) {
     750           0 :         assert(st);
     751             : 
     752           0 :         return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks();
     753             : }
     754             : 
     755           0 : static mode_t process_mask_perms(mode_t mode, mode_t current) {
     756             : 
     757           0 :         if ((current & 0111) == 0)
     758           0 :                 mode &= ~0111;
     759           0 :         if ((current & 0222) == 0)
     760           0 :                 mode &= ~0222;
     761           0 :         if ((current & 0444) == 0)
     762           0 :                 mode &= ~0444;
     763           0 :         if (!S_ISDIR(current))
     764           0 :                 mode &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
     765             : 
     766           0 :         return mode;
     767             : }
     768             : 
     769          27 : static int fd_set_perms(Item *i, int fd, const char *path, const struct stat *st) {
     770             :         struct stat stbuf;
     771             :         mode_t new_mode;
     772             :         bool do_chown;
     773             : 
     774          27 :         assert(i);
     775          27 :         assert(fd);
     776          27 :         assert(path);
     777             : 
     778          27 :         if (!i->mode_set && !i->uid_set && !i->gid_set)
     779          27 :                 goto shortcut;
     780             : 
     781           0 :         if (!st) {
     782           0 :                 if (fstat(fd, &stbuf) < 0)
     783           0 :                         return log_error_errno(errno, "fstat(%s) failed: %m", path);
     784           0 :                 st = &stbuf;
     785             :         }
     786             : 
     787           0 :         if (hardlink_vulnerable(st))
     788           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
     789             :                                        "Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.",
     790             :                                        path);
     791             : 
     792             :         /* Do we need a chown()? */
     793           0 :         do_chown =
     794           0 :                 (i->uid_set && i->uid != st->st_uid) ||
     795           0 :                 (i->gid_set && i->gid != st->st_gid);
     796             : 
     797             :         /* Calculate the mode to apply */
     798           0 :         new_mode = i->mode_set ? (i->mask_perms ?
     799           0 :                                   process_mask_perms(i->mode, st->st_mode) :
     800           0 :                                   i->mode) :
     801           0 :                                  (st->st_mode & 07777);
     802             : 
     803           0 :         if (i->mode_set && do_chown) {
     804             :                 /* Before we issue the chmod() let's reduce the access mode to the common bits of the old and
     805             :                  * the new mode. That way there's no time window where the file exists under the old owner
     806             :                  * with more than the old access modes — and not under the new owner with more than the new
     807             :                  * access modes either. */
     808             : 
     809           0 :                 if (S_ISLNK(st->st_mode))
     810           0 :                         log_debug("Skipping temporary mode fix for symlink %s.", path);
     811             :                 else {
     812           0 :                         mode_t m = new_mode & st->st_mode; /* Mask new mode by old mode */
     813             : 
     814           0 :                         if (((m ^ st->st_mode) & 07777) == 0)
     815           0 :                                 log_debug("\"%s\" matches temporary mode %o already.", path, m);
     816             :                         else {
     817           0 :                                 log_debug("Temporarily changing \"%s\" to mode %o.", path, m);
     818           0 :                                 if (fchmod_opath(fd, m) < 0)
     819           0 :                                         return log_error_errno(errno, "fchmod() of %s failed: %m", path);
     820             :                         }
     821             :                 }
     822             :         }
     823             : 
     824           0 :         if (do_chown) {
     825           0 :                 log_debug("Changing \"%s\" to owner "UID_FMT":"GID_FMT,
     826             :                           path,
     827             :                           i->uid_set ? i->uid : UID_INVALID,
     828             :                           i->gid_set ? i->gid : GID_INVALID);
     829             : 
     830           0 :                 if (fchownat(fd,
     831             :                              "",
     832           0 :                              i->uid_set ? i->uid : UID_INVALID,
     833           0 :                              i->gid_set ? i->gid : GID_INVALID,
     834             :                              AT_EMPTY_PATH) < 0)
     835           0 :                         return log_error_errno(errno, "fchownat() of %s failed: %m", path);
     836             :         }
     837             : 
     838             :         /* Now, apply the final mode. We do this in two cases: when the user set a mode explicitly, or after a
     839             :          * chown(), since chown()'s mangle the access mode in regards to sgid/suid in some conditions. */
     840           0 :         if (i->mode_set || do_chown) {
     841           0 :                 if (S_ISLNK(st->st_mode))
     842           0 :                         log_debug("Skipping mode fix for symlink %s.", path);
     843             :                 else {
     844             :                        /* Check if the chmod() is unnecessary. Note that if we did a chown() before we always
     845             :                         * chmod() here again, since it might have mangled the bits. */
     846           0 :                         if (!do_chown && ((new_mode ^ st->st_mode) & 07777) == 0)
     847           0 :                                 log_debug("\"%s\" matches mode %o already.", path, new_mode);
     848             :                         else {
     849           0 :                                 log_debug("Changing \"%s\" to mode %o.", path, new_mode);
     850           0 :                                 if (fchmod_opath(fd, new_mode) < 0)
     851           0 :                                         return log_error_errno(errno, "fchmod() of %s failed: %m", path);
     852             :                         }
     853             :                 }
     854             :         }
     855             : 
     856           0 : shortcut:
     857          27 :         return label_fix(path, 0);
     858             : }
     859             : 
     860          27 : static int path_open_parent_safe(const char *path) {
     861          27 :         _cleanup_free_ char *dn = NULL;
     862             :         int fd;
     863             : 
     864          27 :         if (path_equal(path, "/") || !path_is_normalized(path))
     865           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     866             :                                        "Failed to open parent of '%s': invalid path.",
     867             :                                        path);
     868             : 
     869          27 :         dn = dirname_malloc(path);
     870          27 :         if (!dn)
     871           0 :                 return log_oom();
     872             : 
     873          27 :         fd = chase_symlinks(dn, arg_root, CHASE_OPEN|CHASE_SAFE|CHASE_WARN, NULL);
     874          27 :         if (fd < 0 && fd != -ENOLINK)
     875           0 :                 return log_error_errno(fd, "Failed to validate path %s: %m", path);
     876             : 
     877          27 :         return fd;
     878             : }
     879             : 
     880           0 : static int path_open_safe(const char *path) {
     881             :         int fd;
     882             : 
     883             :         /* path_open_safe() returns a file descriptor opened with O_PATH after
     884             :          * verifying that the path doesn't contain unsafe transitions, except
     885             :          * for its final component as the function does not follow symlink. */
     886             : 
     887           0 :         assert(path);
     888             : 
     889           0 :         if (!path_is_normalized(path))
     890           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     891             :                                        "Failed to open invalid path '%s'.",
     892             :                                        path);
     893             : 
     894           0 :         fd = chase_symlinks(path, arg_root, CHASE_OPEN|CHASE_SAFE|CHASE_WARN|CHASE_NOFOLLOW, NULL);
     895           0 :         if (fd < 0 && fd != -ENOLINK)
     896           0 :                 return log_error_errno(fd, "Failed to validate path %s: %m", path);
     897             : 
     898           0 :         return fd;
     899             : }
     900             : 
     901           0 : static int path_set_perms(Item *i, const char *path) {
     902           0 :         _cleanup_close_ int fd = -1;
     903             : 
     904           0 :         assert(i);
     905           0 :         assert(path);
     906             : 
     907           0 :         fd = path_open_safe(path);
     908           0 :         if (fd < 0)
     909           0 :                 return fd;
     910             : 
     911           0 :         return fd_set_perms(i, fd, path, NULL);
     912             : }
     913             : 
     914           0 : static int parse_xattrs_from_arg(Item *i) {
     915             :         const char *p;
     916             :         int r;
     917             : 
     918           0 :         assert(i);
     919           0 :         assert(i->argument);
     920             : 
     921           0 :         p = i->argument;
     922             : 
     923           0 :         for (;;) {
     924           0 :                 _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL;
     925             : 
     926           0 :                 r = extract_first_word(&p, &xattr, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
     927           0 :                 if (r < 0)
     928           0 :                         log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p);
     929           0 :                 if (r <= 0)
     930           0 :                         break;
     931             : 
     932           0 :                 r = split_pair(xattr, "=", &name, &value);
     933           0 :                 if (r < 0) {
     934           0 :                         log_warning_errno(r, "Failed to parse extended attribute, ignoring: %s", xattr);
     935           0 :                         continue;
     936             :                 }
     937             : 
     938           0 :                 if (isempty(name) || isempty(value)) {
     939           0 :                         log_warning("Malformed extended attribute found, ignoring: %s", xattr);
     940           0 :                         continue;
     941             :                 }
     942             : 
     943           0 :                 if (strv_push_pair(&i->xattrs, name, value) < 0)
     944           0 :                         return log_oom();
     945             : 
     946           0 :                 name = value = NULL;
     947             :         }
     948             : 
     949           0 :         return 0;
     950             : }
     951             : 
     952           0 : static int fd_set_xattrs(Item *i, int fd, const char *path, const struct stat *st) {
     953             :         char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
     954             :         char **name, **value;
     955             : 
     956           0 :         assert(i);
     957           0 :         assert(fd);
     958           0 :         assert(path);
     959             : 
     960           0 :         xsprintf(procfs_path, "/proc/self/fd/%i", fd);
     961             : 
     962           0 :         STRV_FOREACH_PAIR(name, value, i->xattrs) {
     963           0 :                 log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path);
     964           0 :                 if (setxattr(procfs_path, *name, *value, strlen(*value), 0) < 0)
     965           0 :                         return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m",
     966             :                                                *name, *value, path);
     967             :         }
     968           0 :         return 0;
     969             : }
     970             : 
     971           0 : static int path_set_xattrs(Item *i, const char *path) {
     972           0 :         _cleanup_close_ int fd = -1;
     973             : 
     974           0 :         assert(i);
     975           0 :         assert(path);
     976             : 
     977           0 :         fd = path_open_safe(path);
     978           0 :         if (fd < 0)
     979           0 :                 return fd;
     980             : 
     981           0 :         return fd_set_xattrs(i, fd, path, NULL);
     982             : }
     983             : 
     984           0 : static int parse_acls_from_arg(Item *item) {
     985             : #if HAVE_ACL
     986             :         int r;
     987             : 
     988           0 :         assert(item);
     989             : 
     990             :         /* If force (= modify) is set, we will not modify the acl
     991             :          * afterwards, so the mask can be added now if necessary. */
     992             : 
     993           0 :         r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force);
     994           0 :         if (r < 0)
     995           0 :                 log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring", item->argument);
     996             : #else
     997             :         log_warning_errno(SYNTHETIC_ERRNO(ENOSYS), "ACLs are not supported. Ignoring");
     998             : #endif
     999             : 
    1000           0 :         return 0;
    1001             : }
    1002             : 
    1003             : #if HAVE_ACL
    1004           0 : static int path_set_acl(const char *path, const char *pretty, acl_type_t type, acl_t acl, bool modify) {
    1005           0 :         _cleanup_(acl_free_charpp) char *t = NULL;
    1006           0 :         _cleanup_(acl_freep) acl_t dup = NULL;
    1007             :         int r;
    1008             : 
    1009             :         /* Returns 0 for success, positive error if already warned,
    1010             :          * negative error otherwise. */
    1011             : 
    1012           0 :         if (modify) {
    1013           0 :                 r = acls_for_file(path, type, acl, &dup);
    1014           0 :                 if (r < 0)
    1015           0 :                         return r;
    1016             : 
    1017           0 :                 r = calc_acl_mask_if_needed(&dup);
    1018           0 :                 if (r < 0)
    1019           0 :                         return r;
    1020             :         } else {
    1021           0 :                 dup = acl_dup(acl);
    1022           0 :                 if (!dup)
    1023           0 :                         return -errno;
    1024             : 
    1025             :                 /* the mask was already added earlier if needed */
    1026             :         }
    1027             : 
    1028           0 :         r = add_base_acls_if_needed(&dup, path);
    1029           0 :         if (r < 0)
    1030           0 :                 return r;
    1031             : 
    1032           0 :         t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE);
    1033           0 :         log_debug("Setting %s ACL %s on %s.",
    1034             :                   type == ACL_TYPE_ACCESS ? "access" : "default",
    1035             :                   strna(t), pretty);
    1036             : 
    1037           0 :         r = acl_set_file(path, type, dup);
    1038           0 :         if (r < 0)
    1039             :                 /* Return positive to indicate we already warned */
    1040           0 :                 return -log_error_errno(errno,
    1041             :                                         "Setting %s ACL \"%s\" on %s failed: %m",
    1042             :                                         type == ACL_TYPE_ACCESS ? "access" : "default",
    1043             :                                         strna(t), pretty);
    1044             : 
    1045           0 :         return 0;
    1046             : }
    1047             : #endif
    1048             : 
    1049           0 : static int fd_set_acls(Item *item, int fd, const char *path, const struct stat *st) {
    1050           0 :         int r = 0;
    1051             : #if HAVE_ACL
    1052             :         char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
    1053             :         struct stat stbuf;
    1054             : 
    1055           0 :         assert(item);
    1056           0 :         assert(fd);
    1057           0 :         assert(path);
    1058             : 
    1059           0 :         if (!st) {
    1060           0 :                 if (fstat(fd, &stbuf) < 0)
    1061           0 :                         return log_error_errno(errno, "fstat(%s) failed: %m", path);
    1062           0 :                 st = &stbuf;
    1063             :         }
    1064             : 
    1065           0 :         if (hardlink_vulnerable(st))
    1066           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
    1067             :                                        "Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.",
    1068             :                                        path);
    1069             : 
    1070           0 :         if (S_ISLNK(st->st_mode)) {
    1071           0 :                 log_debug("Skipping ACL fix for symlink %s.", path);
    1072           0 :                 return 0;
    1073             :         }
    1074             : 
    1075           0 :         xsprintf(procfs_path, "/proc/self/fd/%i", fd);
    1076             : 
    1077           0 :         if (item->acl_access)
    1078           0 :                 r = path_set_acl(procfs_path, path, ACL_TYPE_ACCESS, item->acl_access, item->force);
    1079             : 
    1080             :         /* set only default acls to folders */
    1081           0 :         if (r == 0 && item->acl_default && S_ISDIR(st->st_mode))
    1082           0 :                 r = path_set_acl(procfs_path, path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
    1083             : 
    1084           0 :         if (r > 0)
    1085           0 :                 return -r; /* already warned */
    1086           0 :         if (r == -EOPNOTSUPP) {
    1087           0 :                 log_debug_errno(r, "ACLs not supported by file system at %s", path);
    1088           0 :                 return 0;
    1089             :         }
    1090           0 :         if (r < 0)
    1091           0 :                 return log_error_errno(r, "ACL operation on \"%s\" failed: %m", path);
    1092             : #endif
    1093           0 :         return r;
    1094             : }
    1095             : 
    1096           0 : static int path_set_acls(Item *item, const char *path) {
    1097           0 :         int r = 0;
    1098             : #if HAVE_ACL
    1099           0 :         _cleanup_close_ int fd = -1;
    1100             : 
    1101           0 :         assert(item);
    1102           0 :         assert(path);
    1103             : 
    1104           0 :         fd = path_open_safe(path);
    1105           0 :         if (fd < 0)
    1106           0 :                 return fd;
    1107             : 
    1108           0 :         r = fd_set_acls(item, fd, path, NULL);
    1109             : #endif
    1110           0 :         return r;
    1111             : }
    1112             : 
    1113           0 : static int parse_attribute_from_arg(Item *item) {
    1114             : 
    1115             :         static const struct {
    1116             :                 char character;
    1117             :                 unsigned value;
    1118             :         } attributes[] = {
    1119             :                 { 'A', FS_NOATIME_FL },      /* do not update atime */
    1120             :                 { 'S', FS_SYNC_FL },         /* Synchronous updates */
    1121             :                 { 'D', FS_DIRSYNC_FL },      /* dirsync behaviour (directories only) */
    1122             :                 { 'a', FS_APPEND_FL },       /* writes to file may only append */
    1123             :                 { 'c', FS_COMPR_FL },        /* Compress file */
    1124             :                 { 'd', FS_NODUMP_FL },       /* do not dump file */
    1125             :                 { 'e', FS_EXTENT_FL },       /* Extents */
    1126             :                 { 'i', FS_IMMUTABLE_FL },    /* Immutable file */
    1127             :                 { 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */
    1128             :                 { 's', FS_SECRM_FL },        /* Secure deletion */
    1129             :                 { 'u', FS_UNRM_FL },         /* Undelete */
    1130             :                 { 't', FS_NOTAIL_FL },       /* file tail should not be merged */
    1131             :                 { 'T', FS_TOPDIR_FL },       /* Top of directory hierarchies */
    1132             :                 { 'C', FS_NOCOW_FL },        /* Do not cow file */
    1133             :                 { 'P', FS_PROJINHERIT_FL },  /* Inherit the quota project ID */
    1134             :         };
    1135             : 
    1136             :         enum {
    1137             :                 MODE_ADD,
    1138             :                 MODE_DEL,
    1139             :                 MODE_SET
    1140           0 :         } mode = MODE_ADD;
    1141             : 
    1142           0 :         unsigned value = 0, mask = 0;
    1143             :         const char *p;
    1144             : 
    1145           0 :         assert(item);
    1146             : 
    1147           0 :         p = item->argument;
    1148           0 :         if (p) {
    1149           0 :                 if (*p == '+') {
    1150           0 :                         mode = MODE_ADD;
    1151           0 :                         p++;
    1152           0 :                 } else if (*p == '-') {
    1153           0 :                         mode = MODE_DEL;
    1154           0 :                         p++;
    1155           0 :                 } else  if (*p == '=') {
    1156           0 :                         mode = MODE_SET;
    1157           0 :                         p++;
    1158             :                 }
    1159             :         }
    1160             : 
    1161           0 :         if (isempty(p) && mode != MODE_SET)
    1162           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    1163             :                                        "Setting file attribute on '%s' needs an attribute specification.",
    1164             :                                        item->path);
    1165             : 
    1166           0 :         for (; p && *p ; p++) {
    1167             :                 unsigned i, v;
    1168             : 
    1169           0 :                 for (i = 0; i < ELEMENTSOF(attributes); i++)
    1170           0 :                         if (*p == attributes[i].character)
    1171           0 :                                 break;
    1172             : 
    1173           0 :                 if (i >= ELEMENTSOF(attributes))
    1174           0 :                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    1175             :                                                "Unknown file attribute '%c' on '%s'.",
    1176             :                                                *p, item->path);
    1177             : 
    1178           0 :                 v = attributes[i].value;
    1179             : 
    1180           0 :                 SET_FLAG(value, v, IN_SET(mode, MODE_ADD, MODE_SET));
    1181             : 
    1182           0 :                 mask |= v;
    1183             :         }
    1184             : 
    1185           0 :         if (mode == MODE_SET)
    1186           0 :                 mask |= CHATTR_ALL_FL;
    1187             : 
    1188           0 :         assert(mask != 0);
    1189             : 
    1190           0 :         item->attribute_mask = mask;
    1191           0 :         item->attribute_value = value;
    1192           0 :         item->attribute_set = true;
    1193             : 
    1194           0 :         return 0;
    1195             : }
    1196             : 
    1197           0 : static int fd_set_attribute(Item *item, int fd, const char *path, const struct stat *st) {
    1198           0 :         _cleanup_close_ int procfs_fd = -1;
    1199             :         struct stat stbuf;
    1200             :         unsigned f;
    1201             :         int r;
    1202             : 
    1203           0 :         assert(item);
    1204           0 :         assert(fd);
    1205           0 :         assert(path);
    1206             : 
    1207           0 :         if (!item->attribute_set || item->attribute_mask == 0)
    1208           0 :                 return 0;
    1209             : 
    1210           0 :         if (!st) {
    1211           0 :                 if (fstat(fd, &stbuf) < 0)
    1212           0 :                         return log_error_errno(errno, "fstat(%s) failed: %m", path);
    1213           0 :                 st = &stbuf;
    1214             :         }
    1215             : 
    1216             :         /* Issuing the file attribute ioctls on device nodes is not
    1217             :          * safe, as that will be delivered to the drivers, not the
    1218             :          * file system containing the device node. */
    1219           0 :         if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode))
    1220           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    1221             :                                        "Setting file flags is only supported on regular files and directories, cannot set on '%s'.",
    1222             :                                        path);
    1223             : 
    1224           0 :         f = item->attribute_value & item->attribute_mask;
    1225             : 
    1226             :         /* Mask away directory-specific flags */
    1227           0 :         if (!S_ISDIR(st->st_mode))
    1228           0 :                 f &= ~FS_DIRSYNC_FL;
    1229             : 
    1230           0 :         procfs_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NOATIME);
    1231           0 :         if (procfs_fd < 0)
    1232           0 :                 return log_error_errno(procfs_fd, "Failed to re-open '%s': %m", path);
    1233             : 
    1234           0 :         r = chattr_fd(procfs_fd, f, item->attribute_mask, NULL);
    1235           0 :         if (r < 0)
    1236           0 :                 log_full_errno(IN_SET(r, -ENOTTY, -EOPNOTSUPP) ? LOG_DEBUG : LOG_WARNING,
    1237             :                                r,
    1238             :                                "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x, ignoring: %m",
    1239             :                                path, item->attribute_value, item->attribute_mask);
    1240             : 
    1241           0 :         return 0;
    1242             : }
    1243             : 
    1244           0 : static int path_set_attribute(Item *item, const char *path) {
    1245           0 :         _cleanup_close_ int fd = -1;
    1246             : 
    1247           0 :         if (!item->attribute_set || item->attribute_mask == 0)
    1248           0 :                 return 0;
    1249             : 
    1250           0 :         fd = path_open_safe(path);
    1251           0 :         if (fd < 0)
    1252           0 :                 return fd;
    1253             : 
    1254           0 :         return fd_set_attribute(item, fd, path, NULL);
    1255             : }
    1256             : 
    1257           0 : static int write_one_file(Item *i, const char *path) {
    1258           0 :         _cleanup_close_ int fd = -1, dir_fd = -1;
    1259             :         char *bn;
    1260             :         int r;
    1261             : 
    1262           0 :         assert(i);
    1263           0 :         assert(path);
    1264           0 :         assert(i->argument);
    1265           0 :         assert(i->type == WRITE_FILE);
    1266             : 
    1267             :         /* Validate the path and keep the fd on the directory for opening the
    1268             :          * file so we're sure that it can't be changed behind our back. */
    1269           0 :         dir_fd = path_open_parent_safe(path);
    1270           0 :         if (dir_fd < 0)
    1271           0 :                 return dir_fd;
    1272             : 
    1273           0 :         bn = basename(path);
    1274             : 
    1275             :         /* Follows symlinks */
    1276           0 :         fd = openat(dir_fd, bn, O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
    1277           0 :         if (fd < 0) {
    1278           0 :                 if (errno == ENOENT) {
    1279           0 :                         log_debug_errno(errno, "Not writing missing file \"%s\": %m", path);
    1280           0 :                         return 0;
    1281             :                 }
    1282           0 :                 return log_error_errno(errno, "Failed to open file \"%s\": %m", path);
    1283             :         }
    1284             : 
    1285             :         /* 'w' is allowed to write into any kind of files. */
    1286           0 :         log_debug("Writing to \"%s\".", path);
    1287             : 
    1288           0 :         r = loop_write(fd, i->argument, strlen(i->argument), false);
    1289           0 :         if (r < 0)
    1290           0 :                 return log_error_errno(r, "Failed to write file \"%s\": %m", path);
    1291             : 
    1292           0 :         return fd_set_perms(i, fd, path, NULL);
    1293             : }
    1294             : 
    1295          27 : static int create_file(Item *i, const char *path) {
    1296          27 :         _cleanup_close_ int fd = -1, dir_fd = -1;
    1297          27 :         struct stat stbuf, *st = NULL;
    1298          27 :         int r = 0;
    1299             :         char *bn;
    1300             : 
    1301          27 :         assert(i);
    1302          27 :         assert(path);
    1303          27 :         assert(i->type == CREATE_FILE);
    1304             : 
    1305             :         /* 'f' operates on regular files exclusively. */
    1306             : 
    1307             :         /* Validate the path and keep the fd on the directory for opening the
    1308             :          * file so we're sure that it can't be changed behind our back. */
    1309          27 :         dir_fd = path_open_parent_safe(path);
    1310          27 :         if (dir_fd < 0)
    1311           0 :                 return dir_fd;
    1312             : 
    1313          27 :         bn = basename(path);
    1314             : 
    1315          54 :         RUN_WITH_UMASK(0000) {
    1316          27 :                 mac_selinux_create_file_prepare(path, S_IFREG);
    1317          27 :                 fd = openat(dir_fd, bn, O_CREAT|O_EXCL|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
    1318          27 :                 mac_selinux_create_file_clear();
    1319             :         }
    1320             : 
    1321          27 :         if (fd < 0) {
    1322             :                 /* Even on a read-only filesystem, open(2) returns EEXIST if the
    1323             :                  * file already exists. It returns EROFS only if it needs to
    1324             :                  * create the file. */
    1325           0 :                 if (errno != EEXIST)
    1326           0 :                         return log_error_errno(errno, "Failed to create file %s: %m", path);
    1327             : 
    1328             :                 /* Re-open the file. At that point it must exist since open(2)
    1329             :                  * failed with EEXIST. We still need to check if the perms/mode
    1330             :                  * need to be changed. For read-only filesystems, we let
    1331             :                  * fd_set_perms() report the error if the perms need to be
    1332             :                  * modified. */
    1333           0 :                 fd = openat(dir_fd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH, i->mode);
    1334           0 :                 if (fd < 0)
    1335           0 :                         return log_error_errno(errno, "Failed to re-open file %s: %m", path);
    1336             : 
    1337           0 :                 if (fstat(fd, &stbuf) < 0)
    1338           0 :                         return log_error_errno(errno, "stat(%s) failed: %m", path);
    1339             : 
    1340           0 :                 if (!S_ISREG(stbuf.st_mode)) {
    1341           0 :                         log_error("%s exists and is not a regular file.", path);
    1342           0 :                         return -EEXIST;
    1343             :                 }
    1344             : 
    1345           0 :                 st = &stbuf;
    1346             :         } else {
    1347             : 
    1348          27 :                 log_debug("\"%s\" has been created.", path);
    1349             : 
    1350          27 :                 if (i->argument) {
    1351          27 :                         log_debug("Writing to \"%s\".", path);
    1352             : 
    1353          27 :                         r = loop_write(fd, i->argument, strlen(i->argument), false);
    1354          27 :                         if (r < 0)
    1355           0 :                                 return log_error_errno(r, "Failed to write file \"%s\": %m", path);
    1356             :                 }
    1357             :         }
    1358             : 
    1359          27 :         return fd_set_perms(i, fd, path, st);
    1360             : }
    1361             : 
    1362           0 : static int truncate_file(Item *i, const char *path) {
    1363           0 :         _cleanup_close_ int fd = -1, dir_fd = -1;
    1364           0 :         struct stat stbuf, *st = NULL;
    1365           0 :         bool erofs = false;
    1366           0 :         int r = 0;
    1367             :         char *bn;
    1368             : 
    1369           0 :         assert(i);
    1370           0 :         assert(path);
    1371           0 :         assert(i->type == TRUNCATE_FILE);
    1372             : 
    1373             :         /* We want to operate on regular file exclusively especially since
    1374             :          * O_TRUNC is unspecified if the file is neither a regular file nor a
    1375             :          * fifo nor a terminal device. Therefore we first open the file and make
    1376             :          * sure it's a regular one before truncating it. */
    1377             : 
    1378             :         /* Validate the path and keep the fd on the directory for opening the
    1379             :          * file so we're sure that it can't be changed behind our back. */
    1380           0 :         dir_fd = path_open_parent_safe(path);
    1381           0 :         if (dir_fd < 0)
    1382           0 :                 return dir_fd;
    1383             : 
    1384           0 :         bn = basename(path);
    1385             : 
    1386           0 :         RUN_WITH_UMASK(0000) {
    1387           0 :                 mac_selinux_create_file_prepare(path, S_IFREG);
    1388           0 :                 fd = openat(dir_fd, bn, O_CREAT|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
    1389           0 :                 mac_selinux_create_file_clear();
    1390             :         }
    1391             : 
    1392           0 :         if (fd < 0) {
    1393           0 :                 if (errno != EROFS)
    1394           0 :                         return log_error_errno(errno, "Failed to open/create file %s: %m", path);
    1395             : 
    1396             :                 /* On a read-only filesystem, we don't want to fail if the
    1397             :                  * target is already empty and the perms are set. So we still
    1398             :                  * proceed with the sanity checks and let the remaining
    1399             :                  * operations fail with EROFS if they try to modify the target
    1400             :                  * file. */
    1401             : 
    1402           0 :                 fd = openat(dir_fd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH, i->mode);
    1403           0 :                 if (fd < 0) {
    1404           0 :                         if (errno == ENOENT) {
    1405           0 :                                 log_error("Cannot create file %s on a read-only file system.", path);
    1406           0 :                                 return -EROFS;
    1407             :                         }
    1408             : 
    1409           0 :                         return log_error_errno(errno, "Failed to re-open file %s: %m", path);
    1410             :                 }
    1411             : 
    1412           0 :                 erofs = true;
    1413             :         }
    1414             : 
    1415           0 :         if (fstat(fd, &stbuf) < 0)
    1416           0 :                 return log_error_errno(errno, "stat(%s) failed: %m", path);
    1417             : 
    1418           0 :         if (!S_ISREG(stbuf.st_mode)) {
    1419           0 :                 log_error("%s exists and is not a regular file.", path);
    1420           0 :                 return -EEXIST;
    1421             :         }
    1422             : 
    1423           0 :         if (stbuf.st_size > 0) {
    1424           0 :                 if (ftruncate(fd, 0) < 0) {
    1425           0 :                         r = erofs ? -EROFS : -errno;
    1426           0 :                         return log_error_errno(r, "Failed to truncate file %s: %m", path);
    1427             :                 }
    1428             :         } else
    1429           0 :                 st = &stbuf;
    1430             : 
    1431           0 :         log_debug("\"%s\" has been created.", path);
    1432             : 
    1433           0 :         if (i->argument) {
    1434           0 :                 log_debug("Writing to \"%s\".", path);
    1435             : 
    1436           0 :                 r = loop_write(fd, i->argument, strlen(i->argument), false);
    1437           0 :                 if (r < 0) {
    1438           0 :                         r = erofs ? -EROFS : r;
    1439           0 :                         return log_error_errno(r, "Failed to write file %s: %m", path);
    1440             :                 }
    1441             :         }
    1442             : 
    1443           0 :         return fd_set_perms(i, fd, path, st);
    1444             : }
    1445             : 
    1446           0 : static int copy_files(Item *i) {
    1447           0 :         _cleanup_close_ int dfd = -1, fd = -1;
    1448             :         char *bn;
    1449             :         int r;
    1450             : 
    1451           0 :         log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path);
    1452             : 
    1453           0 :         bn = basename(i->path);
    1454             : 
    1455             :         /* Validate the path and use the returned directory fd for copying the
    1456             :          * target so we're sure that the path can't be changed behind our
    1457             :          * back. */
    1458           0 :         dfd = path_open_parent_safe(i->path);
    1459           0 :         if (dfd < 0)
    1460           0 :                 return dfd;
    1461             : 
    1462           0 :         r = copy_tree_at(AT_FDCWD, i->argument,
    1463             :                          dfd, bn,
    1464           0 :                          i->uid_set ? i->uid : UID_INVALID,
    1465           0 :                          i->gid_set ? i->gid : GID_INVALID,
    1466             :                          COPY_REFLINK | COPY_MERGE_EMPTY);
    1467           0 :         if (r < 0) {
    1468             :                 struct stat a, b;
    1469             : 
    1470             :                 /* If the target already exists on read-only filesystems, trying
    1471             :                  * to create the target will not fail with EEXIST but with
    1472             :                  * EROFS. */
    1473           0 :                 if (r == -EROFS && faccessat(dfd, bn, F_OK, AT_SYMLINK_NOFOLLOW) == 0)
    1474           0 :                         r = -EEXIST;
    1475             : 
    1476           0 :                 if (r != -EEXIST)
    1477           0 :                         return log_error_errno(r, "Failed to copy files to %s: %m", i->path);
    1478             : 
    1479           0 :                 if (stat(i->argument, &a) < 0)
    1480           0 :                         return log_error_errno(errno, "stat(%s) failed: %m", i->argument);
    1481             : 
    1482           0 :                 if (fstatat(dfd, bn, &b, AT_SYMLINK_NOFOLLOW) < 0)
    1483           0 :                         return log_error_errno(errno, "stat(%s) failed: %m", i->path);
    1484             : 
    1485           0 :                 if ((a.st_mode ^ b.st_mode) & S_IFMT) {
    1486           0 :                         log_debug("Can't copy to %s, file exists already and is of different type", i->path);
    1487           0 :                         return 0;
    1488             :                 }
    1489             :         }
    1490             : 
    1491           0 :         fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
    1492           0 :         if (fd < 0)
    1493           0 :                 return log_error_errno(errno, "Failed to openat(%s): %m", i->path);
    1494             : 
    1495           0 :         return fd_set_perms(i, fd, i->path, NULL);
    1496             : }
    1497             : 
    1498             : typedef enum {
    1499             :         CREATION_NORMAL,
    1500             :         CREATION_EXISTING,
    1501             :         CREATION_FORCE,
    1502             :         _CREATION_MODE_MAX,
    1503             :         _CREATION_MODE_INVALID = -1
    1504             : } CreationMode;
    1505             : 
    1506             : static const char *const creation_mode_verb_table[_CREATION_MODE_MAX] = {
    1507             :         [CREATION_NORMAL] = "Created",
    1508             :         [CREATION_EXISTING] = "Found existing",
    1509             :         [CREATION_FORCE] = "Created replacement",
    1510             : };
    1511             : 
    1512           0 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode);
    1513             : 
    1514           0 : static int create_directory_or_subvolume(const char *path, mode_t mode, bool subvol, CreationMode *creation) {
    1515           0 :         _cleanup_close_ int pfd = -1;
    1516             :         CreationMode c;
    1517             :         int r;
    1518             : 
    1519           0 :         assert(path);
    1520             : 
    1521           0 :         if (!creation)
    1522           0 :                 creation = &c;
    1523             : 
    1524           0 :         pfd = path_open_parent_safe(path);
    1525           0 :         if (pfd < 0)
    1526           0 :                 return pfd;
    1527             : 
    1528           0 :         if (subvol) {
    1529           0 :                 if (btrfs_is_subvol(empty_to_root(arg_root)) <= 0)
    1530             : 
    1531             :                         /* Don't create a subvolume unless the root directory is
    1532             :                          * one, too. We do this under the assumption that if the
    1533             :                          * root directory is just a plain directory (i.e. very
    1534             :                          * light-weight), we shouldn't try to split it up into
    1535             :                          * subvolumes (i.e. more heavy-weight). Thus, chroot()
    1536             :                          * environments and suchlike will get a full brtfs
    1537             :                          * subvolume set up below their tree only if they
    1538             :                          * specifically set up a btrfs subvolume for the root
    1539             :                          * dir too. */
    1540             : 
    1541           0 :                         subvol = false;
    1542             :                 else {
    1543           0 :                         RUN_WITH_UMASK((~mode) & 0777)
    1544           0 :                                 r = btrfs_subvol_make_fd(pfd, basename(path));
    1545             :                 }
    1546             :         } else
    1547           0 :                 r = 0;
    1548             : 
    1549           0 :         if (!subvol || r == -ENOTTY)
    1550           0 :                 RUN_WITH_UMASK(0000)
    1551           0 :                         r = mkdirat_label(pfd, basename(path), mode);
    1552             : 
    1553           0 :         if (r < 0) {
    1554             :                 int k;
    1555             : 
    1556           0 :                 if (!IN_SET(r, -EEXIST, -EROFS))
    1557           0 :                         return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", path);
    1558             : 
    1559           0 :                 k = is_dir_fd(pfd);
    1560           0 :                 if (k == -ENOENT && r == -EROFS)
    1561           0 :                         return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", path);
    1562           0 :                 if (k < 0)
    1563           0 :                         return log_error_errno(k, "Failed to check if %s exists: %m", path);
    1564           0 :                 if (!k) {
    1565           0 :                         log_warning("\"%s\" already exists and is not a directory.", path);
    1566           0 :                         return -EEXIST;
    1567             :                 }
    1568             : 
    1569           0 :                 *creation = CREATION_EXISTING;
    1570             :         } else
    1571           0 :                 *creation = CREATION_NORMAL;
    1572             : 
    1573           0 :         log_debug("%s directory \"%s\".", creation_mode_verb_to_string(*creation), path);
    1574             : 
    1575           0 :         r = openat(pfd, basename(path), O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
    1576           0 :         if (r < 0)
    1577           0 :                 return log_error_errno(errno, "Failed to open directory '%s': %m", basename(path));
    1578             : 
    1579           0 :         return r;
    1580             : }
    1581             : 
    1582           0 : static int create_directory(Item *i, const char *path) {
    1583           0 :         _cleanup_close_ int fd = -1;
    1584             : 
    1585           0 :         assert(i);
    1586           0 :         assert(IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY));
    1587             : 
    1588           0 :         fd = create_directory_or_subvolume(path, i->mode, false, NULL);
    1589           0 :         if (fd == -EEXIST)
    1590           0 :                 return 0;
    1591           0 :         if (fd < 0)
    1592           0 :                 return fd;
    1593             : 
    1594           0 :         return fd_set_perms(i, fd, path, NULL);
    1595             : }
    1596             : 
    1597           0 : static int create_subvolume(Item *i, const char *path) {
    1598           0 :         _cleanup_close_ int fd = -1;
    1599             :         CreationMode creation;
    1600           0 :         int r, q = 0;
    1601             : 
    1602           0 :         assert(i);
    1603           0 :         assert(IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA));
    1604             : 
    1605           0 :         fd = create_directory_or_subvolume(path, i->mode, true, &creation);
    1606           0 :         if (fd == -EEXIST)
    1607           0 :                 return 0;
    1608           0 :         if (fd < 0)
    1609           0 :                 return fd;
    1610             : 
    1611           0 :         if (creation == CREATION_NORMAL &&
    1612           0 :             IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
    1613           0 :                 r = btrfs_subvol_auto_qgroup_fd(fd, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
    1614           0 :                 if (r == -ENOTTY)
    1615           0 :                         log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);
    1616           0 :                 else if (r == -EROFS)
    1617           0 :                         log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path);
    1618           0 :                 else if (r == -ENOPROTOOPT)
    1619           0 :                         log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path);
    1620           0 :                 else if (r < 0)
    1621           0 :                         q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
    1622           0 :                 else if (r > 0)
    1623           0 :                         log_debug("Adjusted quota for subvolume \"%s\".", i->path);
    1624           0 :                 else if (r == 0)
    1625           0 :                         log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path);
    1626             :         }
    1627             : 
    1628           0 :         r = fd_set_perms(i, fd, path, NULL);
    1629           0 :         if (q < 0) /* prefer the quota change error from above */
    1630           0 :                 return q;
    1631             : 
    1632           0 :         return r;
    1633             : }
    1634             : 
    1635           0 : static int empty_directory(Item *i, const char *path) {
    1636             :         int r;
    1637             : 
    1638           0 :         assert(i);
    1639           0 :         assert(i->type == EMPTY_DIRECTORY);
    1640             : 
    1641           0 :         r = is_dir(path, false);
    1642           0 :         if (r == -ENOENT) {
    1643             :                 /* Option "e" operates only on existing objects. Do not
    1644             :                  * print errors about non-existent files or directories */
    1645           0 :                 log_debug("Skipping missing directory: %s", path);
    1646           0 :                 return 0;
    1647             :         }
    1648           0 :         if (r < 0)
    1649           0 :                 return log_error_errno(r, "is_dir() failed on path %s: %m", path);
    1650           0 :         if (r == 0)
    1651           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
    1652             :                                        "'%s' already exists and is not a directory.",
    1653             :                                        path);
    1654             : 
    1655           0 :         return path_set_perms(i, path);
    1656             : }
    1657             : 
    1658           0 : static int create_device(Item *i, mode_t file_type) {
    1659           0 :         _cleanup_close_ int dfd = -1, fd = -1;
    1660             :         CreationMode creation;
    1661             :         char *bn;
    1662             :         int r;
    1663             : 
    1664           0 :         assert(i);
    1665           0 :         assert(IN_SET(file_type, S_IFBLK, S_IFCHR));
    1666             : 
    1667           0 :         bn = basename(i->path);
    1668             : 
    1669             :         /* Validate the path and use the returned directory fd for copying the
    1670             :          * target so we're sure that the path can't be changed behind our
    1671             :          * back. */
    1672           0 :         dfd = path_open_parent_safe(i->path);
    1673           0 :         if (dfd < 0)
    1674           0 :                 return dfd;
    1675             : 
    1676           0 :         RUN_WITH_UMASK(0000) {
    1677           0 :                 mac_selinux_create_file_prepare(i->path, file_type);
    1678           0 :                 r = mknodat(dfd, bn, i->mode | file_type, i->major_minor);
    1679           0 :                 mac_selinux_create_file_clear();
    1680             :         }
    1681             : 
    1682           0 :         if (r < 0) {
    1683             :                 struct stat st;
    1684             : 
    1685           0 :                 if (errno == EPERM) {
    1686           0 :                         log_debug("We lack permissions, possibly because of cgroup configuration; "
    1687             :                                   "skipping creation of device node %s.", i->path);
    1688           0 :                         return 0;
    1689             :                 }
    1690             : 
    1691           0 :                 if (errno != EEXIST)
    1692           0 :                         return log_error_errno(errno, "Failed to create device node %s: %m", i->path);
    1693             : 
    1694           0 :                 if (fstatat(dfd, bn, &st, 0) < 0)
    1695           0 :                         return log_error_errno(errno, "stat(%s) failed: %m", i->path);
    1696             : 
    1697           0 :                 if ((st.st_mode & S_IFMT) != file_type) {
    1698             : 
    1699           0 :                         if (i->force) {
    1700             : 
    1701           0 :                                 RUN_WITH_UMASK(0000) {
    1702           0 :                                         mac_selinux_create_file_prepare(i->path, file_type);
    1703             :                                         /* FIXME: need to introduce mknodat_atomic() */
    1704           0 :                                         r = mknod_atomic(i->path, i->mode | file_type, i->major_minor);
    1705           0 :                                         mac_selinux_create_file_clear();
    1706             :                                 }
    1707             : 
    1708           0 :                                 if (r < 0)
    1709           0 :                                         return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
    1710           0 :                                 creation = CREATION_FORCE;
    1711             :                         } else {
    1712           0 :                                 log_debug("%s is not a device node.", i->path);
    1713           0 :                                 return 0;
    1714             :                         }
    1715             :                 } else
    1716           0 :                         creation = CREATION_EXISTING;
    1717             :         } else
    1718           0 :                 creation = CREATION_NORMAL;
    1719             : 
    1720           0 :         log_debug("%s %s device node \"%s\" %u:%u.",
    1721             :                   creation_mode_verb_to_string(creation),
    1722             :                   i->type == CREATE_BLOCK_DEVICE ? "block" : "char",
    1723             :                   i->path, major(i->mode), minor(i->mode));
    1724             : 
    1725           0 :         fd = openat(dfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
    1726           0 :         if (fd < 0)
    1727           0 :                 return log_error_errno(errno, "Failed to openat(%s): %m", i->path);
    1728             : 
    1729           0 :         return fd_set_perms(i, fd, i->path, NULL);
    1730             : }
    1731             : 
    1732           0 : static int create_fifo(Item *i, const char *path) {
    1733           0 :         _cleanup_close_ int pfd = -1, fd = -1;
    1734             :         CreationMode creation;
    1735             :         struct stat st;
    1736             :         char *bn;
    1737             :         int r;
    1738             : 
    1739           0 :         pfd = path_open_parent_safe(path);
    1740           0 :         if (pfd < 0)
    1741           0 :                 return pfd;
    1742             : 
    1743           0 :         bn = basename(path);
    1744             : 
    1745           0 :         RUN_WITH_UMASK(0000) {
    1746           0 :                 mac_selinux_create_file_prepare(path, S_IFIFO);
    1747           0 :                 r = mkfifoat(pfd, bn, i->mode);
    1748           0 :                 mac_selinux_create_file_clear();
    1749             :         }
    1750             : 
    1751           0 :         if (r < 0) {
    1752           0 :                 if (errno != EEXIST)
    1753           0 :                         return log_error_errno(errno, "Failed to create fifo %s: %m", path);
    1754             : 
    1755           0 :                 if (fstatat(pfd, bn, &st, AT_SYMLINK_NOFOLLOW) < 0)
    1756           0 :                         return log_error_errno(errno, "stat(%s) failed: %m", path);
    1757             : 
    1758           0 :                 if (!S_ISFIFO(st.st_mode)) {
    1759             : 
    1760           0 :                         if (i->force) {
    1761           0 :                                 RUN_WITH_UMASK(0000) {
    1762           0 :                                         mac_selinux_create_file_prepare(path, S_IFIFO);
    1763           0 :                                         r = mkfifoat_atomic(pfd, bn, i->mode);
    1764           0 :                                         mac_selinux_create_file_clear();
    1765             :                                 }
    1766             : 
    1767           0 :                                 if (r < 0)
    1768           0 :                                         return log_error_errno(r, "Failed to create fifo %s: %m", path);
    1769           0 :                                 creation = CREATION_FORCE;
    1770             :                         } else {
    1771           0 :                                 log_warning("\"%s\" already exists and is not a fifo.", path);
    1772           0 :                                 return 0;
    1773             :                         }
    1774             :                 } else
    1775           0 :                         creation = CREATION_EXISTING;
    1776             :         } else
    1777           0 :                 creation = CREATION_NORMAL;
    1778             : 
    1779           0 :         log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), path);
    1780             : 
    1781           0 :         fd = openat(pfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH);
    1782           0 :         if (fd < 0)
    1783           0 :                 return log_error_errno(errno, "Failed to openat(%s): %m", path);
    1784             : 
    1785           0 :         return fd_set_perms(i, fd, i->path, NULL);
    1786             : }
    1787             : 
    1788             : typedef int (*action_t)(Item *i, const char *path);
    1789             : typedef int (*fdaction_t)(Item *i, int fd, const char *path, const struct stat *st);
    1790             : 
    1791           0 : static int item_do(Item *i, int fd, const char *path, fdaction_t action) {
    1792             :         struct stat st;
    1793           0 :         int r = 0, q;
    1794             : 
    1795           0 :         assert(i);
    1796           0 :         assert(path);
    1797           0 :         assert(fd >= 0);
    1798             : 
    1799           0 :         if (fstat(fd, &st) < 0) {
    1800           0 :                 r = log_error_errno(errno, "fstat() on file failed: %m");
    1801           0 :                 goto finish;
    1802             :         }
    1803             : 
    1804             :         /* This returns the first error we run into, but nevertheless
    1805             :          * tries to go on */
    1806           0 :         r = action(i, fd, path, &st);
    1807             : 
    1808           0 :         if (S_ISDIR(st.st_mode)) {
    1809             :                 char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
    1810           0 :                 _cleanup_closedir_ DIR *d = NULL;
    1811             :                 struct dirent *de;
    1812             : 
    1813             :                 /* The passed 'fd' was opened with O_PATH. We need to convert
    1814             :                  * it into a 'regular' fd before reading the directory content. */
    1815           0 :                 xsprintf(procfs_path, "/proc/self/fd/%i", fd);
    1816             : 
    1817           0 :                 d = opendir(procfs_path);
    1818           0 :                 if (!d) {
    1819           0 :                         log_error_errno(errno, "Failed to opendir() '%s': %m", procfs_path);
    1820           0 :                         if (r == 0)
    1821           0 :                                 r = -errno;
    1822           0 :                         goto finish;
    1823             :                 }
    1824             : 
    1825           0 :                 FOREACH_DIRENT_ALL(de, d, q = -errno; goto finish) {
    1826             :                         int de_fd;
    1827             : 
    1828           0 :                         if (dot_or_dot_dot(de->d_name))
    1829           0 :                                 continue;
    1830             : 
    1831           0 :                         de_fd = openat(fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_PATH);
    1832           0 :                         if (de_fd < 0)
    1833           0 :                                 q = log_error_errno(errno, "Failed to open() file '%s': %m", de->d_name);
    1834             :                         else {
    1835           0 :                                 _cleanup_free_ char *de_path = NULL;
    1836             : 
    1837           0 :                                 de_path = path_join(path, de->d_name);
    1838           0 :                                 if (!de_path)
    1839           0 :                                         q = log_oom();
    1840             :                                 else
    1841             :                                         /* Pass ownership of dirent fd over */
    1842           0 :                                         q = item_do(i, de_fd, de_path, action);
    1843             :                         }
    1844             : 
    1845           0 :                         if (q < 0 && r == 0)
    1846           0 :                                 r = q;
    1847             :                 }
    1848             :         }
    1849           0 : finish:
    1850           0 :         safe_close(fd);
    1851           0 :         return r;
    1852             : }
    1853             : 
    1854           0 : static int glob_item(Item *i, action_t action) {
    1855           0 :         _cleanup_globfree_ glob_t g = {
    1856             :                 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
    1857             :         };
    1858           0 :         int r = 0, k;
    1859             :         char **fn;
    1860             : 
    1861           0 :         k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
    1862           0 :         if (k < 0 && k != -ENOENT)
    1863           0 :                 return log_error_errno(k, "glob(%s) failed: %m", i->path);
    1864             : 
    1865           0 :         STRV_FOREACH(fn, g.gl_pathv) {
    1866           0 :                 k = action(i, *fn);
    1867           0 :                 if (k < 0 && r == 0)
    1868           0 :                         r = k;
    1869             :         }
    1870             : 
    1871           0 :         return r;
    1872             : }
    1873             : 
    1874           0 : static int glob_item_recursively(Item *i, fdaction_t action) {
    1875           0 :         _cleanup_globfree_ glob_t g = {
    1876             :                 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
    1877             :         };
    1878           0 :         int r = 0, k;
    1879             :         char **fn;
    1880             : 
    1881           0 :         k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
    1882           0 :         if (k < 0 && k != -ENOENT)
    1883           0 :                 return log_error_errno(k, "glob(%s) failed: %m", i->path);
    1884             : 
    1885           0 :         STRV_FOREACH(fn, g.gl_pathv) {
    1886           0 :                 _cleanup_close_ int fd = -1;
    1887             : 
    1888             :                 /* Make sure we won't trigger/follow file object (such as
    1889             :                  * device nodes, automounts, ...) pointed out by 'fn' with
    1890             :                  * O_PATH. Note, when O_PATH is used, flags other than
    1891             :                  * O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW are ignored. */
    1892             : 
    1893           0 :                 fd = open(*fn, O_CLOEXEC|O_NOFOLLOW|O_PATH);
    1894           0 :                 if (fd < 0) {
    1895           0 :                         log_error_errno(errno, "Opening '%s' failed: %m", *fn);
    1896           0 :                         if (r == 0)
    1897           0 :                                 r = -errno;
    1898           0 :                         continue;
    1899             :                 }
    1900             : 
    1901           0 :                 k = item_do(i, fd, *fn, action);
    1902           0 :                 if (k < 0 && r == 0)
    1903           0 :                         r = k;
    1904             : 
    1905             :                 /* we passed fd ownership to the previous call */
    1906           0 :                 fd = -1;
    1907             :         }
    1908             : 
    1909           0 :         return r;
    1910             : }
    1911             : 
    1912          27 : static int create_item(Item *i) {
    1913             :         CreationMode creation;
    1914          27 :         int r = 0;
    1915             : 
    1916          27 :         assert(i);
    1917             : 
    1918          27 :         log_debug("Running create action for entry %c %s", (char) i->type, i->path);
    1919             : 
    1920          27 :         switch (i->type) {
    1921             : 
    1922           0 :         case IGNORE_PATH:
    1923             :         case IGNORE_DIRECTORY_PATH:
    1924             :         case REMOVE_PATH:
    1925             :         case RECURSIVE_REMOVE_PATH:
    1926           0 :                 return 0;
    1927             : 
    1928          27 :         case CREATE_FILE:
    1929          54 :                 RUN_WITH_UMASK(0000)
    1930          27 :                         (void) mkdir_parents_label(i->path, 0755);
    1931             : 
    1932          27 :                 r = create_file(i, i->path);
    1933          27 :                 if (r < 0)
    1934           0 :                         return r;
    1935          27 :                 break;
    1936             : 
    1937           0 :         case TRUNCATE_FILE:
    1938           0 :                 RUN_WITH_UMASK(0000)
    1939           0 :                         (void) mkdir_parents_label(i->path, 0755);
    1940             : 
    1941           0 :                 r = truncate_file(i, i->path);
    1942           0 :                 if (r < 0)
    1943           0 :                         return r;
    1944           0 :                 break;
    1945             : 
    1946           0 :         case COPY_FILES:
    1947           0 :                 RUN_WITH_UMASK(0000)
    1948           0 :                         (void) mkdir_parents_label(i->path, 0755);
    1949             : 
    1950           0 :                 r = copy_files(i);
    1951           0 :                 if (r < 0)
    1952           0 :                         return r;
    1953           0 :                 break;
    1954             : 
    1955           0 :         case WRITE_FILE:
    1956           0 :                 r = glob_item(i, write_one_file);
    1957           0 :                 if (r < 0)
    1958           0 :                         return r;
    1959             : 
    1960           0 :                 break;
    1961             : 
    1962           0 :         case CREATE_DIRECTORY:
    1963             :         case TRUNCATE_DIRECTORY:
    1964           0 :                 RUN_WITH_UMASK(0000)
    1965           0 :                         (void) mkdir_parents_label(i->path, 0755);
    1966             : 
    1967           0 :                 r = create_directory(i, i->path);
    1968           0 :                 if (r < 0)
    1969           0 :                         return r;
    1970           0 :                 break;
    1971             : 
    1972           0 :         case CREATE_SUBVOLUME:
    1973             :         case CREATE_SUBVOLUME_INHERIT_QUOTA:
    1974             :         case CREATE_SUBVOLUME_NEW_QUOTA:
    1975           0 :                 RUN_WITH_UMASK(0000)
    1976           0 :                         (void) mkdir_parents_label(i->path, 0755);
    1977             : 
    1978           0 :                 r = create_subvolume(i, i->path);
    1979           0 :                 if (r < 0)
    1980           0 :                         return r;
    1981           0 :                 break;
    1982             : 
    1983           0 :         case EMPTY_DIRECTORY:
    1984           0 :                 r = glob_item(i, empty_directory);
    1985           0 :                 if (r < 0)
    1986           0 :                         return r;
    1987           0 :                 break;
    1988             : 
    1989           0 :         case CREATE_FIFO:
    1990           0 :                 RUN_WITH_UMASK(0000)
    1991           0 :                         (void) mkdir_parents_label(i->path, 0755);
    1992             : 
    1993           0 :                 r = create_fifo(i, i->path);
    1994           0 :                 if (r < 0)
    1995           0 :                         return r;
    1996           0 :                 break;
    1997             : 
    1998           0 :         case CREATE_SYMLINK: {
    1999           0 :                 RUN_WITH_UMASK(0000)
    2000           0 :                         (void) mkdir_parents_label(i->path, 0755);
    2001             : 
    2002           0 :                 mac_selinux_create_file_prepare(i->path, S_IFLNK);
    2003           0 :                 r = symlink(i->argument, i->path);
    2004           0 :                 mac_selinux_create_file_clear();
    2005             : 
    2006           0 :                 if (r < 0) {
    2007           0 :                         _cleanup_free_ char *x = NULL;
    2008             : 
    2009           0 :                         if (errno != EEXIST)
    2010           0 :                                 return log_error_errno(errno, "symlink(%s, %s) failed: %m", i->argument, i->path);
    2011             : 
    2012           0 :                         r = readlink_malloc(i->path, &x);
    2013           0 :                         if (r < 0 || !streq(i->argument, x)) {
    2014             : 
    2015           0 :                                 if (i->force) {
    2016           0 :                                         mac_selinux_create_file_prepare(i->path, S_IFLNK);
    2017           0 :                                         r = symlink_atomic(i->argument, i->path);
    2018           0 :                                         mac_selinux_create_file_clear();
    2019             : 
    2020           0 :                                         if (IN_SET(r, -EISDIR, -EEXIST, -ENOTEMPTY)) {
    2021           0 :                                                 r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL);
    2022           0 :                                                 if (r < 0)
    2023           0 :                                                         return log_error_errno(r, "rm -fr %s failed: %m", i->path);
    2024             : 
    2025           0 :                                                 mac_selinux_create_file_prepare(i->path, S_IFLNK);
    2026           0 :                                                 r = symlink(i->argument, i->path) < 0 ? -errno : 0;
    2027           0 :                                                 mac_selinux_create_file_clear();
    2028             :                                         }
    2029           0 :                                         if (r < 0)
    2030           0 :                                                 return log_error_errno(r, "symlink(%s, %s) failed: %m", i->argument, i->path);
    2031             : 
    2032           0 :                                         creation = CREATION_FORCE;
    2033             :                                 } else {
    2034           0 :                                         log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path);
    2035           0 :                                         return 0;
    2036             :                                 }
    2037             :                         } else
    2038           0 :                                 creation = CREATION_EXISTING;
    2039             :                 } else
    2040             : 
    2041           0 :                         creation = CREATION_NORMAL;
    2042           0 :                 log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path);
    2043           0 :                 break;
    2044             :         }
    2045             : 
    2046           0 :         case CREATE_BLOCK_DEVICE:
    2047             :         case CREATE_CHAR_DEVICE:
    2048           0 :                 if (have_effective_cap(CAP_MKNOD) == 0) {
    2049             :                         /* In a container we lack CAP_MKNOD. We shouldn't attempt to create the device node in that
    2050             :                          * case to avoid noise, and we don't support virtualized devices in containers anyway. */
    2051             : 
    2052           0 :                         log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path);
    2053           0 :                         return 0;
    2054             :                 }
    2055             : 
    2056           0 :                 RUN_WITH_UMASK(0000)
    2057           0 :                         (void) mkdir_parents_label(i->path, 0755);
    2058             : 
    2059           0 :                 r = create_device(i, i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
    2060           0 :                 if (r < 0)
    2061           0 :                         return r;
    2062             : 
    2063           0 :                 break;
    2064             : 
    2065           0 :         case ADJUST_MODE:
    2066             :         case RELABEL_PATH:
    2067           0 :                 r = glob_item(i, path_set_perms);
    2068           0 :                 if (r < 0)
    2069           0 :                         return r;
    2070           0 :                 break;
    2071             : 
    2072           0 :         case RECURSIVE_RELABEL_PATH:
    2073           0 :                 r = glob_item_recursively(i, fd_set_perms);
    2074           0 :                 if (r < 0)
    2075           0 :                         return r;
    2076           0 :                 break;
    2077             : 
    2078           0 :         case SET_XATTR:
    2079           0 :                 r = glob_item(i, path_set_xattrs);
    2080           0 :                 if (r < 0)
    2081           0 :                         return r;
    2082           0 :                 break;
    2083             : 
    2084           0 :         case RECURSIVE_SET_XATTR:
    2085           0 :                 r = glob_item_recursively(i, fd_set_xattrs);
    2086           0 :                 if (r < 0)
    2087           0 :                         return r;
    2088           0 :                 break;
    2089             : 
    2090           0 :         case SET_ACL:
    2091           0 :                 r = glob_item(i, path_set_acls);
    2092           0 :                 if (r < 0)
    2093           0 :                         return r;
    2094           0 :                 break;
    2095             : 
    2096           0 :         case RECURSIVE_SET_ACL:
    2097           0 :                 r = glob_item_recursively(i, fd_set_acls);
    2098           0 :                 if (r < 0)
    2099           0 :                         return r;
    2100           0 :                 break;
    2101             : 
    2102           0 :         case SET_ATTRIBUTE:
    2103           0 :                 r = glob_item(i, path_set_attribute);
    2104           0 :                 if (r < 0)
    2105           0 :                         return r;
    2106           0 :                 break;
    2107             : 
    2108           0 :         case RECURSIVE_SET_ATTRIBUTE:
    2109           0 :                 r = glob_item_recursively(i, fd_set_attribute);
    2110           0 :                 if (r < 0)
    2111           0 :                         return r;
    2112           0 :                 break;
    2113             :         }
    2114             : 
    2115          27 :         return 0;
    2116             : }
    2117             : 
    2118           0 : static int remove_item_instance(Item *i, const char *instance) {
    2119             :         int r;
    2120             : 
    2121           0 :         assert(i);
    2122             : 
    2123           0 :         switch (i->type) {
    2124             : 
    2125           0 :         case REMOVE_PATH:
    2126           0 :                 if (remove(instance) < 0 && errno != ENOENT)
    2127           0 :                         return log_error_errno(errno, "rm(%s): %m", instance);
    2128             : 
    2129           0 :                 break;
    2130             : 
    2131           0 :         case RECURSIVE_REMOVE_PATH:
    2132             :                 /* FIXME: we probably should use dir_cleanup() here instead of rm_rf() so that 'x' is honoured. */
    2133           0 :                 log_debug("rm -rf \"%s\"", instance);
    2134           0 :                 r = rm_rf(instance, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
    2135           0 :                 if (r < 0 && r != -ENOENT)
    2136           0 :                         return log_error_errno(r, "rm_rf(%s): %m", instance);
    2137             : 
    2138           0 :                 break;
    2139             : 
    2140           0 :         default:
    2141           0 :                 assert_not_reached("wut?");
    2142             :         }
    2143             : 
    2144           0 :         return 0;
    2145             : }
    2146             : 
    2147           0 : static int remove_item(Item *i) {
    2148             :         int r;
    2149             : 
    2150           0 :         assert(i);
    2151             : 
    2152           0 :         log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
    2153             : 
    2154           0 :         switch (i->type) {
    2155             : 
    2156           0 :         case TRUNCATE_DIRECTORY:
    2157             :                 /* FIXME: we probably should use dir_cleanup() here instead of rm_rf() so that 'x' is honoured. */
    2158           0 :                 log_debug("rm -rf \"%s\"", i->path);
    2159           0 :                 r = rm_rf(i->path, REMOVE_PHYSICAL);
    2160           0 :                 if (r < 0 && r != -ENOENT)
    2161           0 :                         return log_error_errno(r, "rm_rf(%s): %m", i->path);
    2162             : 
    2163           0 :                 return 0;
    2164             : 
    2165           0 :         case REMOVE_PATH:
    2166             :         case RECURSIVE_REMOVE_PATH:
    2167           0 :                 return glob_item(i, remove_item_instance);
    2168             : 
    2169           0 :         default:
    2170           0 :                 return 0;
    2171             :         }
    2172             : }
    2173             : 
    2174           0 : static int clean_item_instance(Item *i, const char* instance) {
    2175           0 :         _cleanup_closedir_ DIR *d = NULL;
    2176             :         struct stat s, ps;
    2177             :         bool mountpoint;
    2178             :         usec_t cutoff, n;
    2179             :         char timestamp[FORMAT_TIMESTAMP_MAX];
    2180             : 
    2181           0 :         assert(i);
    2182             : 
    2183           0 :         if (!i->age_set)
    2184           0 :                 return 0;
    2185             : 
    2186           0 :         n = now(CLOCK_REALTIME);
    2187           0 :         if (n < i->age)
    2188           0 :                 return 0;
    2189             : 
    2190           0 :         cutoff = n - i->age;
    2191             : 
    2192           0 :         d = opendir_nomod(instance);
    2193           0 :         if (!d) {
    2194           0 :                 if (IN_SET(errno, ENOENT, ENOTDIR)) {
    2195           0 :                         log_debug_errno(errno, "Directory \"%s\": %m", instance);
    2196           0 :                         return 0;
    2197             :                 }
    2198             : 
    2199           0 :                 return log_error_errno(errno, "Failed to open directory %s: %m", instance);
    2200             :         }
    2201             : 
    2202           0 :         if (fstat(dirfd(d), &s) < 0)
    2203           0 :                 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
    2204             : 
    2205           0 :         if (!S_ISDIR(s.st_mode))
    2206           0 :                 return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR),
    2207             :                                        "%s is not a directory.", i->path);
    2208             : 
    2209           0 :         if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
    2210           0 :                 return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
    2211             : 
    2212           0 :         mountpoint = s.st_dev != ps.st_dev || s.st_ino == ps.st_ino;
    2213             : 
    2214           0 :         log_debug("Cleanup threshold for %s \"%s\" is %s",
    2215             :                   mountpoint ? "mount point" : "directory",
    2216             :                   instance,
    2217             :                   format_timestamp_us(timestamp, sizeof(timestamp), cutoff));
    2218             : 
    2219           0 :         return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint,
    2220           0 :                            MAX_DEPTH, i->keep_first_level);
    2221             : }
    2222             : 
    2223           0 : static int clean_item(Item *i) {
    2224           0 :         assert(i);
    2225             : 
    2226           0 :         log_debug("Running clean action for entry %c %s", (char) i->type, i->path);
    2227             : 
    2228           0 :         switch (i->type) {
    2229           0 :         case CREATE_DIRECTORY:
    2230             :         case CREATE_SUBVOLUME:
    2231             :         case CREATE_SUBVOLUME_INHERIT_QUOTA:
    2232             :         case CREATE_SUBVOLUME_NEW_QUOTA:
    2233             :         case TRUNCATE_DIRECTORY:
    2234             :         case IGNORE_PATH:
    2235             :         case COPY_FILES:
    2236           0 :                 clean_item_instance(i, i->path);
    2237           0 :                 return 0;
    2238           0 :         case EMPTY_DIRECTORY:
    2239             :         case IGNORE_DIRECTORY_PATH:
    2240           0 :                 return glob_item(i, clean_item_instance);
    2241           0 :         default:
    2242           0 :                 return 0;
    2243             :         }
    2244             : }
    2245             : 
    2246          27 : static int process_item(Item *i, OperationMask operation) {
    2247             :         OperationMask todo;
    2248             :         int r, q, p;
    2249             : 
    2250          27 :         assert(i);
    2251             : 
    2252          27 :         todo = operation & ~i->done;
    2253          27 :         if (todo == 0) /* Everything already done? */
    2254           0 :                 return 0;
    2255             : 
    2256          27 :         i->done |= operation;
    2257             : 
    2258          27 :         r = chase_symlinks(i->path, arg_root, CHASE_NO_AUTOFS|CHASE_WARN, NULL);
    2259          27 :         if (r == -EREMOTE) {
    2260           0 :                 log_notice_errno(r, "Skipping %s", i->path);
    2261           0 :                 return 0;
    2262             :         }
    2263          27 :         if (r < 0)
    2264          27 :                 log_debug_errno(r, "Failed to determine whether '%s' is below autofs, ignoring: %m", i->path);
    2265             : 
    2266          27 :         r = FLAGS_SET(operation, OPERATION_CREATE) ? create_item(i) : 0;
    2267             :         /* Failure can only be tolerated for create */
    2268          27 :         if (i->allow_failure)
    2269           0 :                 r = 0;
    2270             : 
    2271          27 :         q = FLAGS_SET(operation, OPERATION_REMOVE) ? remove_item(i) : 0;
    2272          27 :         p = FLAGS_SET(operation, OPERATION_CLEAN) ? clean_item(i) : 0;
    2273             : 
    2274          54 :         return r < 0 ? r :
    2275          27 :                 q < 0 ? q :
    2276             :                 p;
    2277             : }
    2278             : 
    2279          27 : static int process_item_array(ItemArray *array, OperationMask operation) {
    2280          27 :         int r = 0;
    2281             :         size_t n;
    2282             : 
    2283          27 :         assert(array);
    2284             : 
    2285             :         /* Create any parent first. */
    2286          27 :         if (FLAGS_SET(operation, OPERATION_CREATE) && array->parent)
    2287           0 :                 r = process_item_array(array->parent, operation & OPERATION_CREATE);
    2288             : 
    2289             :         /* Clean up all children first */
    2290          27 :         if ((operation & (OPERATION_REMOVE|OPERATION_CLEAN)) && !set_isempty(array->children)) {
    2291             :                 Iterator i;
    2292             :                 ItemArray *c;
    2293             : 
    2294           0 :                 SET_FOREACH(c, array->children, i) {
    2295             :                         int k;
    2296             : 
    2297           0 :                         k = process_item_array(c, operation & (OPERATION_REMOVE|OPERATION_CLEAN));
    2298           0 :                         if (k < 0 && r == 0)
    2299           0 :                                 r = k;
    2300             :                 }
    2301             :         }
    2302             : 
    2303          54 :         for (n = 0; n < array->n_items; n++) {
    2304             :                 int k;
    2305             : 
    2306          27 :                 k = process_item(array->items + n, operation);
    2307          27 :                 if (k < 0 && r == 0)
    2308           0 :                         r = k;
    2309             :         }
    2310             : 
    2311          27 :         return r;
    2312             : }
    2313             : 
    2314         109 : static void item_free_contents(Item *i) {
    2315         109 :         assert(i);
    2316         109 :         free(i->path);
    2317         109 :         free(i->argument);
    2318         109 :         strv_free(i->xattrs);
    2319             : 
    2320             : #if HAVE_ACL
    2321         109 :         acl_free(i->acl_access);
    2322         109 :         acl_free(i->acl_default);
    2323             : #endif
    2324         109 : }
    2325             : 
    2326          27 : static ItemArray* item_array_free(ItemArray *a) {
    2327             :         size_t n;
    2328             : 
    2329          27 :         if (!a)
    2330           0 :                 return NULL;
    2331             : 
    2332          54 :         for (n = 0; n < a->n_items; n++)
    2333          27 :                 item_free_contents(a->items + n);
    2334             : 
    2335          27 :         set_free(a->children);
    2336          27 :         free(a->items);
    2337          27 :         return mfree(a);
    2338             : }
    2339             : 
    2340           0 : static int item_compare(const Item *a, const Item *b) {
    2341             :         /* Make sure that the ownership taking item is put first, so
    2342             :          * that we first create the node, and then can adjust it */
    2343             : 
    2344           0 :         if (takes_ownership(a->type) && !takes_ownership(b->type))
    2345           0 :                 return -1;
    2346           0 :         if (!takes_ownership(a->type) && takes_ownership(b->type))
    2347           0 :                 return 1;
    2348             : 
    2349           0 :         return CMP(a->type, b->type);
    2350             : }
    2351             : 
    2352           0 : static bool item_compatible(Item *a, Item *b) {
    2353           0 :         assert(a);
    2354           0 :         assert(b);
    2355           0 :         assert(streq(a->path, b->path));
    2356             : 
    2357           0 :         if (takes_ownership(a->type) && takes_ownership(b->type))
    2358             :                 /* check if the items are the same */
    2359           0 :                 return  streq_ptr(a->argument, b->argument) &&
    2360             : 
    2361           0 :                         a->uid_set == b->uid_set &&
    2362           0 :                         a->uid == b->uid &&
    2363             : 
    2364           0 :                         a->gid_set == b->gid_set &&
    2365           0 :                         a->gid == b->gid &&
    2366             : 
    2367           0 :                         a->mode_set == b->mode_set &&
    2368           0 :                         a->mode == b->mode &&
    2369             : 
    2370           0 :                         a->age_set == b->age_set &&
    2371           0 :                         a->age == b->age &&
    2372             : 
    2373           0 :                         a->mask_perms == b->mask_perms &&
    2374             : 
    2375           0 :                         a->keep_first_level == b->keep_first_level &&
    2376             : 
    2377           0 :                         a->major_minor == b->major_minor;
    2378             : 
    2379           0 :         return true;
    2380             : }
    2381             : 
    2382          32 : static bool should_include_path(const char *path) {
    2383             :         char **prefix;
    2384             : 
    2385          32 :         STRV_FOREACH(prefix, arg_exclude_prefixes)
    2386           0 :                 if (path_startswith(path, *prefix)) {
    2387           0 :                         log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.",
    2388             :                                   path, *prefix);
    2389           0 :                         return false;
    2390             :                 }
    2391             : 
    2392          32 :         STRV_FOREACH(prefix, arg_include_prefixes)
    2393           0 :                 if (path_startswith(path, *prefix)) {
    2394           0 :                         log_debug("Entry \"%s\" matches include prefix \"%s\".", path, *prefix);
    2395           0 :                         return true;
    2396             :                 }
    2397             : 
    2398             :         /* no matches, so we should include this path only if we
    2399             :          * have no whitelist at all */
    2400          32 :         if (strv_isempty(arg_include_prefixes))
    2401          32 :                 return true;
    2402             : 
    2403           0 :         log_debug("Entry \"%s\" does not match any include prefix, skipping.", path);
    2404           0 :         return false;
    2405             : }
    2406             : 
    2407          32 : static int specifier_expansion_from_arg(Item *i) {
    2408          32 :         _cleanup_free_ char *unescaped = NULL, *resolved = NULL;
    2409             :         char **xattr;
    2410             :         int r;
    2411             : 
    2412          32 :         assert(i);
    2413             : 
    2414          32 :         if (!i->argument)
    2415           0 :                 return 0;
    2416             : 
    2417          32 :         switch (i->type) {
    2418          32 :         case COPY_FILES:
    2419             :         case CREATE_SYMLINK:
    2420             :         case CREATE_FILE:
    2421             :         case TRUNCATE_FILE:
    2422             :         case WRITE_FILE:
    2423          32 :                 r = cunescape(i->argument, 0, &unescaped);
    2424          32 :                 if (r < 0)
    2425           0 :                         return log_error_errno(r, "Failed to unescape parameter to write: %s", i->argument);
    2426             : 
    2427          32 :                 r = specifier_printf(unescaped, specifier_table, NULL, &resolved);
    2428          32 :                 if (r < 0)
    2429           5 :                         return r;
    2430             : 
    2431          27 :                 free_and_replace(i->argument, resolved);
    2432          27 :                 break;
    2433             : 
    2434           0 :         case SET_XATTR:
    2435             :         case RECURSIVE_SET_XATTR:
    2436           0 :                 assert(i->xattrs);
    2437             : 
    2438           0 :                 STRV_FOREACH (xattr, i->xattrs) {
    2439           0 :                         r = specifier_printf(*xattr, specifier_table, NULL, &resolved);
    2440           0 :                         if (r < 0)
    2441           0 :                                 return r;
    2442             : 
    2443           0 :                         free_and_replace(*xattr, resolved);
    2444             :                 }
    2445           0 :                 break;
    2446             : 
    2447           0 :         default:
    2448           0 :                 break;
    2449             :         }
    2450          27 :         return 0;
    2451             : }
    2452             : 
    2453          64 : static int patch_var_run(const char *fname, unsigned line, char **path) {
    2454             :         const char *k;
    2455             :         char *n;
    2456             : 
    2457          64 :         assert(path);
    2458          64 :         assert(*path);
    2459             : 
    2460             :         /* Optionally rewrites lines referencing /var/run/, to use /run/ instead. Why bother? tmpfiles merges lines in
    2461             :          * some cases and detects conflicts in others. If files/directories are specified through two equivalent lines
    2462             :          * this is problematic as neither case will be detected. Ideally we'd detect these cases by resolving symlinks
    2463             :          * early, but that's precisely not what we can do here as this code very likely is running very early on, at a
    2464             :          * time where the paths in question are not available yet, or even more importantly, our own tmpfiles rules
    2465             :          * might create the paths that are intermediary to the listed paths. We can't really cover the generic case,
    2466             :          * but the least we can do is cover the specific case of /var/run vs. /run, as /var/run is a legacy name for
    2467             :          * /run only, and we explicitly document that and require that on systemd systems the former is a symlink to
    2468             :          * the latter. Moreover files below this path are by far the primary usecase for tmpfiles.d/. */
    2469             : 
    2470          64 :         k = path_startswith(*path, "/var/run/");
    2471          64 :         if (isempty(k)) /* Don't complain about other paths than /var/run, and not about /var/run itself either. */
    2472          64 :                 return 0;
    2473             : 
    2474           0 :         n = path_join("/run", k);
    2475           0 :         if (!n)
    2476           0 :                 return log_oom();
    2477             : 
    2478             :         /* Also log about this briefly. We do so at LOG_NOTICE level, as we fixed up the situation automatically, hence
    2479             :          * there's no immediate need for action by the user. However, in the interest of making things less confusing
    2480             :          * to the user, let's still inform the user that these snippets should really be updated. */
    2481           0 :         log_syntax(NULL, LOG_NOTICE, fname, line, 0, "Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.", *path, n);
    2482             : 
    2483           0 :         free_and_replace(*path, n);
    2484             : 
    2485           0 :         return 0;
    2486             : }
    2487             : 
    2488          82 : static int parse_line(const char *fname, unsigned line, const char *buffer, bool *invalid_config) {
    2489             : 
    2490          82 :         _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
    2491          82 :         _cleanup_(item_free_contents) Item i = {};
    2492             :         ItemArray *existing;
    2493             :         OrderedHashmap *h;
    2494             :         int r, pos;
    2495          82 :         bool force = false, boot = false, allow_failure = false;
    2496             : 
    2497          82 :         assert(fname);
    2498          82 :         assert(line >= 1);
    2499          82 :         assert(buffer);
    2500             : 
    2501          82 :         r = extract_many_words(
    2502             :                         &buffer,
    2503             :                         NULL,
    2504             :                         EXTRACT_UNQUOTE,
    2505             :                         &action,
    2506             :                         &path,
    2507             :                         &mode,
    2508             :                         &user,
    2509             :                         &group,
    2510             :                         &age,
    2511             :                         NULL);
    2512          82 :         if (r < 0) {
    2513           2 :                 if (IN_SET(r, -EINVAL, -EBADSLT))
    2514             :                         /* invalid quoting and such or an unknown specifier */
    2515           2 :                         *invalid_config = true;
    2516           2 :                 return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line);
    2517          80 :         } else if (r < 2) {
    2518           4 :                 *invalid_config = true;
    2519           4 :                 log_error("[%s:%u] Syntax error.", fname, line);
    2520           4 :                 return -EIO;
    2521             :         }
    2522             : 
    2523          76 :         if (!empty_or_dash(buffer)) {
    2524          40 :                 i.argument = strdup(buffer);
    2525          40 :                 if (!i.argument)
    2526           0 :                         return log_oom();
    2527             :         }
    2528             : 
    2529          76 :         if (isempty(action)) {
    2530           0 :                 *invalid_config = true;
    2531           0 :                 log_error("[%s:%u] Command too short '%s'.", fname, line, action);
    2532           0 :                 return -EINVAL;
    2533             :         }
    2534             : 
    2535          88 :         for (pos = 1; action[pos]; pos++) {
    2536          20 :                 if (action[pos] == '!' && !boot)
    2537           6 :                         boot = true;
    2538          14 :                 else if (action[pos] == '+' && !force)
    2539           6 :                         force = true;
    2540           8 :                 else if (action[pos] == '-' && !allow_failure)
    2541           0 :                         allow_failure = true;
    2542             :                 else {
    2543           8 :                         *invalid_config = true;
    2544           8 :                         log_error("[%s:%u] Unknown modifiers in command '%s'",
    2545             :                                   fname, line, action);
    2546           8 :                         return -EINVAL;
    2547             :                 }
    2548             :         }
    2549             : 
    2550          68 :         if (boot && !arg_boot) {
    2551           0 :                 log_debug("Ignoring entry %s \"%s\" because --boot is not specified.",
    2552             :                           action, path);
    2553           0 :                 return 0;
    2554             :         }
    2555             : 
    2556          68 :         i.type = action[0];
    2557          68 :         i.force = force;
    2558          68 :         i.allow_failure = allow_failure;
    2559             : 
    2560          68 :         r = specifier_printf(path, specifier_table, NULL, &i.path);
    2561          68 :         if (r == -ENXIO)
    2562           0 :                 return log_unresolvable_specifier(fname, line);
    2563          68 :         if (r < 0) {
    2564           4 :                 if (IN_SET(r, -EINVAL, -EBADSLT))
    2565           4 :                         *invalid_config = true;
    2566           4 :                 return log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", fname, line, path);
    2567             :         }
    2568             : 
    2569          64 :         r = patch_var_run(fname, line, &i.path);
    2570          64 :         if (r < 0)
    2571           0 :                 return r;
    2572             : 
    2573          64 :         switch (i.type) {
    2574             : 
    2575           0 :         case CREATE_DIRECTORY:
    2576             :         case CREATE_SUBVOLUME:
    2577             :         case CREATE_SUBVOLUME_INHERIT_QUOTA:
    2578             :         case CREATE_SUBVOLUME_NEW_QUOTA:
    2579             :         case EMPTY_DIRECTORY:
    2580             :         case TRUNCATE_DIRECTORY:
    2581             :         case CREATE_FIFO:
    2582             :         case IGNORE_PATH:
    2583             :         case IGNORE_DIRECTORY_PATH:
    2584             :         case REMOVE_PATH:
    2585             :         case RECURSIVE_REMOVE_PATH:
    2586             :         case ADJUST_MODE:
    2587             :         case RELABEL_PATH:
    2588             :         case RECURSIVE_RELABEL_PATH:
    2589           0 :                 if (i.argument)
    2590           0 :                         log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname, line, i.type);
    2591             : 
    2592           0 :                 break;
    2593             : 
    2594          29 :         case CREATE_FILE:
    2595             :         case TRUNCATE_FILE:
    2596          29 :                 break;
    2597             : 
    2598           0 :         case CREATE_SYMLINK:
    2599           0 :                 if (!i.argument) {
    2600           0 :                         i.argument = path_join("/usr/share/factory", i.path);
    2601           0 :                         if (!i.argument)
    2602           0 :                                 return log_oom();
    2603             :                 }
    2604           0 :                 break;
    2605             : 
    2606           9 :         case WRITE_FILE:
    2607           9 :                 if (!i.argument) {
    2608           2 :                         *invalid_config = true;
    2609           2 :                         log_error("[%s:%u] Write file requires argument.", fname, line);
    2610           2 :                         return -EBADMSG;
    2611             :                 }
    2612           7 :                 break;
    2613             : 
    2614           4 :         case COPY_FILES:
    2615           4 :                 if (!i.argument) {
    2616           2 :                         i.argument = path_join(arg_root, "/usr/share/factory", i.path);
    2617           2 :                         if (!i.argument)
    2618           0 :                                 return log_oom();
    2619             : 
    2620           2 :                 } else if (!path_is_absolute(i.argument)) {
    2621           2 :                         *invalid_config = true;
    2622           2 :                         log_error("[%s:%u] Source path is not absolute.", fname, line);
    2623           2 :                         return -EBADMSG;
    2624             : 
    2625           0 :                 } else if (arg_root) {
    2626             :                         char *p;
    2627             : 
    2628           0 :                         p = path_join(arg_root, i.argument);
    2629           0 :                         if (!p)
    2630           0 :                                 return log_oom();
    2631           0 :                         free_and_replace(i.argument, p);
    2632             :                 }
    2633             : 
    2634           2 :                 path_simplify(i.argument, false);
    2635           2 :                 break;
    2636             : 
    2637           8 :         case CREATE_CHAR_DEVICE:
    2638             :         case CREATE_BLOCK_DEVICE:
    2639           8 :                 if (!i.argument) {
    2640           8 :                         *invalid_config = true;
    2641           8 :                         log_error("[%s:%u] Device file requires argument.", fname, line);
    2642           8 :                         return -EBADMSG;
    2643             :                 }
    2644             : 
    2645           0 :                 r = parse_dev(i.argument, &i.major_minor);
    2646           0 :                 if (r < 0) {
    2647           0 :                         *invalid_config = true;
    2648           0 :                         log_error_errno(r, "[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument);
    2649           0 :                         return -EBADMSG;
    2650             :                 }
    2651             : 
    2652           0 :                 break;
    2653             : 
    2654           4 :         case SET_XATTR:
    2655             :         case RECURSIVE_SET_XATTR:
    2656           4 :                 if (!i.argument) {
    2657           4 :                         *invalid_config = true;
    2658           4 :                         log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
    2659           4 :                         return -EBADMSG;
    2660             :                 }
    2661           0 :                 r = parse_xattrs_from_arg(&i);
    2662           0 :                 if (r < 0)
    2663           0 :                         return r;
    2664           0 :                 break;
    2665             : 
    2666           4 :         case SET_ACL:
    2667             :         case RECURSIVE_SET_ACL:
    2668           4 :                 if (!i.argument) {
    2669           4 :                         *invalid_config = true;
    2670           4 :                         log_error("[%s:%u] Set ACLs requires argument.", fname, line);
    2671           4 :                         return -EBADMSG;
    2672             :                 }
    2673           0 :                 r = parse_acls_from_arg(&i);
    2674           0 :                 if (r < 0)
    2675           0 :                         return r;
    2676           0 :                 break;
    2677             : 
    2678           4 :         case SET_ATTRIBUTE:
    2679             :         case RECURSIVE_SET_ATTRIBUTE:
    2680           4 :                 if (!i.argument) {
    2681           4 :                         *invalid_config = true;
    2682           4 :                         log_error("[%s:%u] Set file attribute requires argument.", fname, line);
    2683           4 :                         return -EBADMSG;
    2684             :                 }
    2685           0 :                 r = parse_attribute_from_arg(&i);
    2686           0 :                 if (IN_SET(r, -EINVAL, -EBADSLT))
    2687           0 :                         *invalid_config = true;
    2688           0 :                 if (r < 0)
    2689           0 :                         return r;
    2690           0 :                 break;
    2691             : 
    2692           2 :         default:
    2693           2 :                 log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type);
    2694           2 :                 *invalid_config = true;
    2695           2 :                 return -EBADMSG;
    2696             :         }
    2697             : 
    2698          38 :         if (!path_is_absolute(i.path)) {
    2699           6 :                 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path);
    2700           6 :                 *invalid_config = true;
    2701           6 :                 return -EBADMSG;
    2702             :         }
    2703             : 
    2704          32 :         path_simplify(i.path, false);
    2705             : 
    2706          32 :         if (!should_include_path(i.path))
    2707           0 :                 return 0;
    2708             : 
    2709          32 :         r = specifier_expansion_from_arg(&i);
    2710          32 :         if (r == -ENXIO)
    2711           1 :                 return log_unresolvable_specifier(fname, line);
    2712          31 :         if (r < 0) {
    2713           4 :                 if (IN_SET(r, -EINVAL, -EBADSLT))
    2714           4 :                         *invalid_config = true;
    2715           4 :                 return log_error_errno(r, "[%s:%u] Failed to substitute specifiers in argument: %m",
    2716             :                                        fname, line);
    2717             :         }
    2718             : 
    2719          27 :         if (arg_root) {
    2720             :                 char *p;
    2721             : 
    2722           0 :                 p = path_join(arg_root, i.path);
    2723           0 :                 if (!p)
    2724           0 :                         return log_oom();
    2725           0 :                 free_and_replace(i.path, p);
    2726             :         }
    2727             : 
    2728          27 :         if (!empty_or_dash(user)) {
    2729           0 :                 const char *u = user;
    2730             : 
    2731           0 :                 r = get_user_creds(&u, &i.uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
    2732           0 :                 if (r < 0) {
    2733           0 :                         *invalid_config = true;
    2734           0 :                         return log_error_errno(r, "[%s:%u] Unknown user '%s'.", fname, line, user);
    2735             :                 }
    2736             : 
    2737           0 :                 i.uid_set = true;
    2738             :         }
    2739             : 
    2740          27 :         if (!empty_or_dash(group)) {
    2741           0 :                 const char *g = group;
    2742             : 
    2743           0 :                 r = get_group_creds(&g, &i.gid, USER_CREDS_ALLOW_MISSING);
    2744           0 :                 if (r < 0) {
    2745           0 :                         *invalid_config = true;
    2746           0 :                         log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
    2747           0 :                         return r;
    2748             :                 }
    2749             : 
    2750           0 :                 i.gid_set = true;
    2751             :         }
    2752             : 
    2753          27 :         if (!empty_or_dash(mode)) {
    2754           0 :                 const char *mm = mode;
    2755             :                 unsigned m;
    2756             : 
    2757           0 :                 if (*mm == '~') {
    2758           0 :                         i.mask_perms = true;
    2759           0 :                         mm++;
    2760             :                 }
    2761             : 
    2762           0 :                 if (parse_mode(mm, &m) < 0) {
    2763           0 :                         *invalid_config = true;
    2764           0 :                         log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
    2765           0 :                         return -EBADMSG;
    2766             :                 }
    2767             : 
    2768           0 :                 i.mode = m;
    2769           0 :                 i.mode_set = true;
    2770             :         } else
    2771          27 :                 i.mode = IN_SET(i.type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644;
    2772             : 
    2773          27 :         if (!empty_or_dash(age)) {
    2774           0 :                 const char *a = age;
    2775             : 
    2776           0 :                 if (*a == '~') {
    2777           0 :                         i.keep_first_level = true;
    2778           0 :                         a++;
    2779             :                 }
    2780             : 
    2781           0 :                 if (parse_sec(a, &i.age) < 0) {
    2782           0 :                         *invalid_config = true;
    2783           0 :                         log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
    2784           0 :                         return -EBADMSG;
    2785             :                 }
    2786             : 
    2787           0 :                 i.age_set = true;
    2788             :         }
    2789             : 
    2790          27 :         h = needs_glob(i.type) ? globs : items;
    2791             : 
    2792          27 :         existing = ordered_hashmap_get(h, i.path);
    2793          27 :         if (existing) {
    2794             :                 size_t n;
    2795             : 
    2796           0 :                 for (n = 0; n < existing->n_items; n++) {
    2797           0 :                         if (!item_compatible(existing->items + n, &i)) {
    2798           0 :                                 log_notice("[%s:%u] Duplicate line for path \"%s\", ignoring.",
    2799             :                                            fname, line, i.path);
    2800           0 :                                 return 0;
    2801             :                         }
    2802             :                 }
    2803             :         } else {
    2804          27 :                 existing = new0(ItemArray, 1);
    2805          27 :                 if (!existing)
    2806           0 :                         return log_oom();
    2807             : 
    2808          27 :                 r = ordered_hashmap_put(h, i.path, existing);
    2809          27 :                 if (r < 0) {
    2810           0 :                         free(existing);
    2811           0 :                         return log_oom();
    2812             :                 }
    2813             :         }
    2814             : 
    2815          27 :         if (!GREEDY_REALLOC(existing->items, existing->allocated, existing->n_items + 1))
    2816           0 :                 return log_oom();
    2817             : 
    2818          27 :         existing->items[existing->n_items++] = i;
    2819          27 :         i = (struct Item) {};
    2820             : 
    2821             :         /* Sort item array, to enforce stable ordering of application */
    2822          27 :         typesafe_qsort(existing->items, existing->n_items, item_compare);
    2823             : 
    2824          27 :         return 0;
    2825             : }
    2826             : 
    2827           0 : static int cat_config(char **config_dirs, char **args) {
    2828           0 :         _cleanup_strv_free_ char **files = NULL;
    2829             :         int r;
    2830             : 
    2831           0 :         r = conf_files_list_with_replacement(arg_root, config_dirs, arg_replace, &files, NULL);
    2832           0 :         if (r < 0)
    2833           0 :                 return r;
    2834             : 
    2835           0 :         return cat_files(NULL, files, 0);
    2836             : }
    2837             : 
    2838           3 : static int help(void) {
    2839           3 :         _cleanup_free_ char *link = NULL;
    2840             :         int r;
    2841             : 
    2842           3 :         r = terminal_urlify_man("systemd-tmpfiles", "8", &link);
    2843           3 :         if (r < 0)
    2844           0 :                 return log_oom();
    2845             : 
    2846           3 :         printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
    2847             :                "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
    2848             :                "  -h --help                 Show this help\n"
    2849             :                "     --user                 Execute user configuration\n"
    2850             :                "     --version              Show package version\n"
    2851             :                "     --cat-config           Show configuration files\n"
    2852             :                "     --create               Create marked files/directories\n"
    2853             :                "     --clean                Clean up marked directories\n"
    2854             :                "     --remove               Remove marked files/directories\n"
    2855             :                "     --boot                 Execute actions only safe at boot\n"
    2856             :                "     --prefix=PATH          Only apply rules with the specified prefix\n"
    2857             :                "     --exclude-prefix=PATH  Ignore rules with the specified prefix\n"
    2858             :                "     --root=PATH            Operate on an alternate filesystem root\n"
    2859             :                "     --replace=PATH         Treat arguments as replacement for PATH\n"
    2860             :                "     --no-pager             Do not pipe output into a pager\n"
    2861             :                "\nSee the %s for details.\n"
    2862             :                , program_invocation_short_name
    2863             :                , link
    2864             :         );
    2865             : 
    2866           3 :         return 0;
    2867             : }
    2868             : 
    2869          86 : static int parse_argv(int argc, char *argv[]) {
    2870             : 
    2871             :         enum {
    2872             :                 ARG_VERSION = 0x100,
    2873             :                 ARG_CAT_CONFIG,
    2874             :                 ARG_USER,
    2875             :                 ARG_CREATE,
    2876             :                 ARG_CLEAN,
    2877             :                 ARG_REMOVE,
    2878             :                 ARG_BOOT,
    2879             :                 ARG_PREFIX,
    2880             :                 ARG_EXCLUDE_PREFIX,
    2881             :                 ARG_ROOT,
    2882             :                 ARG_REPLACE,
    2883             :                 ARG_NO_PAGER,
    2884             :         };
    2885             : 
    2886             :         static const struct option options[] = {
    2887             :                 { "help",           no_argument,         NULL, 'h'                },
    2888             :                 { "user",           no_argument,         NULL, ARG_USER           },
    2889             :                 { "version",        no_argument,         NULL, ARG_VERSION        },
    2890             :                 { "cat-config",     no_argument,         NULL, ARG_CAT_CONFIG     },
    2891             :                 { "create",         no_argument,         NULL, ARG_CREATE         },
    2892             :                 { "clean",          no_argument,         NULL, ARG_CLEAN          },
    2893             :                 { "remove",         no_argument,         NULL, ARG_REMOVE         },
    2894             :                 { "boot",           no_argument,         NULL, ARG_BOOT           },
    2895             :                 { "prefix",         required_argument,   NULL, ARG_PREFIX         },
    2896             :                 { "exclude-prefix", required_argument,   NULL, ARG_EXCLUDE_PREFIX },
    2897             :                 { "root",           required_argument,   NULL, ARG_ROOT           },
    2898             :                 { "replace",        required_argument,   NULL, ARG_REPLACE        },
    2899             :                 { "no-pager",       no_argument,         NULL, ARG_NO_PAGER       },
    2900             :                 {}
    2901             :         };
    2902             : 
    2903             :         int c, r;
    2904             : 
    2905          86 :         assert(argc >= 0);
    2906          86 :         assert(argv);
    2907             : 
    2908         208 :         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
    2909             : 
    2910         126 :                 switch (c) {
    2911             : 
    2912           3 :                 case 'h':
    2913           3 :                         return help();
    2914             : 
    2915           0 :                 case ARG_VERSION:
    2916           0 :                         return version();
    2917             : 
    2918           0 :                 case ARG_CAT_CONFIG:
    2919           0 :                         arg_cat_config = true;
    2920           0 :                         break;
    2921             : 
    2922          40 :                 case ARG_USER:
    2923          40 :                         arg_user = true;
    2924          40 :                         break;
    2925             : 
    2926          82 :                 case ARG_CREATE:
    2927          82 :                         arg_operation |= OPERATION_CREATE;
    2928          82 :                         break;
    2929             : 
    2930           0 :                 case ARG_CLEAN:
    2931           0 :                         arg_operation |= OPERATION_CLEAN;
    2932           0 :                         break;
    2933             : 
    2934           0 :                 case ARG_REMOVE:
    2935           0 :                         arg_operation |= OPERATION_REMOVE;
    2936           0 :                         break;
    2937             : 
    2938           0 :                 case ARG_BOOT:
    2939           0 :                         arg_boot = true;
    2940           0 :                         break;
    2941             : 
    2942           0 :                 case ARG_PREFIX:
    2943           0 :                         if (strv_push(&arg_include_prefixes, optarg) < 0)
    2944           0 :                                 return log_oom();
    2945           0 :                         break;
    2946             : 
    2947           0 :                 case ARG_EXCLUDE_PREFIX:
    2948           0 :                         if (strv_push(&arg_exclude_prefixes, optarg) < 0)
    2949           0 :                                 return log_oom();
    2950           0 :                         break;
    2951             : 
    2952           0 :                 case ARG_ROOT:
    2953           0 :                         r = parse_path_argument_and_warn(optarg, true, &arg_root);
    2954           0 :                         if (r < 0)
    2955           0 :                                 return r;
    2956           0 :                         break;
    2957             : 
    2958           0 :                 case ARG_REPLACE:
    2959           0 :                         if (!path_is_absolute(optarg) ||
    2960           0 :                             !endswith(optarg, ".conf"))
    2961           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2962             :                                                        "The argument to --replace= must an absolute path to a config file");
    2963             : 
    2964           0 :                         arg_replace = optarg;
    2965           0 :                         break;
    2966             : 
    2967           0 :                 case ARG_NO_PAGER:
    2968           0 :                         arg_pager_flags |= PAGER_DISABLE;
    2969           0 :                         break;
    2970             : 
    2971           1 :                 case '?':
    2972           1 :                         return -EINVAL;
    2973             : 
    2974           0 :                 default:
    2975           0 :                         assert_not_reached("Unhandled option");
    2976             :                 }
    2977             : 
    2978          82 :         if (arg_operation == 0 && !arg_cat_config)
    2979           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2980             :                                        "You need to specify at least one of --clean, --create or --remove.");
    2981             : 
    2982          82 :         if (arg_replace && arg_cat_config)
    2983           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2984             :                                        "Option --replace= is not supported with --cat-config");
    2985             : 
    2986          82 :         if (arg_replace && optind >= argc)
    2987           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2988             :                                        "When --replace= is given, some configuration items must be specified");
    2989             : 
    2990          82 :         return 1;
    2991             : }
    2992             : 
    2993          82 : static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
    2994          82 :         _cleanup_fclose_ FILE *_f = NULL;
    2995             :         Iterator iterator;
    2996          82 :         unsigned v = 0;
    2997             :         FILE *f;
    2998             :         Item *i;
    2999          82 :         int r = 0;
    3000             : 
    3001          82 :         assert(fn);
    3002             : 
    3003          82 :         if (streq(fn, "-")) {
    3004          82 :                 log_debug("Reading config from stdin…");
    3005          82 :                 fn = "<stdin>";
    3006          82 :                 f = stdin;
    3007             :         } else {
    3008           0 :                 r = search_and_fopen(fn, "re", arg_root, (const char**) config_dirs, &_f);
    3009           0 :                 if (r < 0) {
    3010           0 :                         if (ignore_enoent && r == -ENOENT) {
    3011           0 :                                 log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn);
    3012           0 :                                 return 0;
    3013             :                         }
    3014             : 
    3015           0 :                         return log_error_errno(r, "Failed to open '%s': %m", fn);
    3016             :                 }
    3017           0 :                 log_debug("Reading config file \"%s\"…", fn);
    3018           0 :                 f = _f;
    3019             :         }
    3020             : 
    3021          82 :         for (;;) {
    3022         164 :                 _cleanup_free_ char *line = NULL;
    3023         164 :                 bool invalid_line = false;
    3024             :                 char *l;
    3025             :                 int k;
    3026             : 
    3027         164 :                 k = read_line(f, LONG_LINE_MAX, &line);
    3028         164 :                 if (k < 0)
    3029           0 :                         return log_error_errno(k, "Failed to read '%s': %m", fn);
    3030         164 :                 if (k == 0)
    3031          82 :                         break;
    3032             : 
    3033          82 :                 v++;
    3034             : 
    3035          82 :                 l = strstrip(line);
    3036          82 :                 if (IN_SET(*l, 0, '#'))
    3037           0 :                         continue;
    3038             : 
    3039          82 :                 k = parse_line(fn, v, l, &invalid_line);
    3040          82 :                 if (k < 0) {
    3041          54 :                         if (invalid_line)
    3042             :                                 /* Allow reporting with a special code if the caller requested this */
    3043          54 :                                 *invalid_config = true;
    3044           0 :                         else if (r == 0)
    3045             :                                 /* The first error becomes our return value */
    3046           0 :                                 r = k;
    3047             :                 }
    3048             :         }
    3049             : 
    3050             :         /* we have to determine age parameter for each entry of type X */
    3051          82 :         ORDERED_HASHMAP_FOREACH(i, globs, iterator) {
    3052             :                 Iterator iter;
    3053           0 :                 Item *j, *candidate_item = NULL;
    3054             : 
    3055           0 :                 if (i->type != IGNORE_DIRECTORY_PATH)
    3056           0 :                         continue;
    3057             : 
    3058           0 :                 ORDERED_HASHMAP_FOREACH(j, items, iter) {
    3059           0 :                         if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA))
    3060           0 :                                 continue;
    3061             : 
    3062           0 :                         if (path_equal(j->path, i->path)) {
    3063           0 :                                 candidate_item = j;
    3064           0 :                                 break;
    3065             :                         }
    3066             : 
    3067           0 :                         if ((!candidate_item && path_startswith(i->path, j->path)) ||
    3068           0 :                             (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
    3069           0 :                                 candidate_item = j;
    3070             :                 }
    3071             : 
    3072           0 :                 if (candidate_item && candidate_item->age_set) {
    3073           0 :                         i->age = candidate_item->age;
    3074           0 :                         i->age_set = true;
    3075             :                 }
    3076             :         }
    3077             : 
    3078          82 :         if (ferror(f)) {
    3079           0 :                 log_error_errno(errno, "Failed to read from file %s: %m", fn);
    3080           0 :                 if (r == 0)
    3081           0 :                         r = -EIO;
    3082             :         }
    3083             : 
    3084          82 :         return r;
    3085             : }
    3086             : 
    3087          82 : static int parse_arguments(char **config_dirs, char **args, bool *invalid_config) {
    3088             :         char **arg;
    3089             :         int r;
    3090             : 
    3091         164 :         STRV_FOREACH(arg, args) {
    3092          82 :                 r = read_config_file(config_dirs, *arg, false, invalid_config);
    3093          82 :                 if (r < 0)
    3094           0 :                         return r;
    3095             :         }
    3096             : 
    3097          82 :         return 0;
    3098             : }
    3099             : 
    3100           0 : static int read_config_files(char **config_dirs, char **args, bool *invalid_config) {
    3101           0 :         _cleanup_strv_free_ char **files = NULL;
    3102           0 :         _cleanup_free_ char *p = NULL;
    3103             :         char **f;
    3104             :         int r;
    3105             : 
    3106           0 :         r = conf_files_list_with_replacement(arg_root, config_dirs, arg_replace, &files, &p);
    3107           0 :         if (r < 0)
    3108           0 :                 return r;
    3109             : 
    3110           0 :         STRV_FOREACH(f, files)
    3111           0 :                 if (p && path_equal(*f, p)) {
    3112           0 :                         log_debug("Parsing arguments at position \"%s\"…", *f);
    3113             : 
    3114           0 :                         r = parse_arguments(config_dirs, args, invalid_config);
    3115           0 :                         if (r < 0)
    3116           0 :                                 return r;
    3117             :                 } else
    3118             :                         /* Just warn, ignore result otherwise.
    3119             :                          * read_config_file() has some debug output, so no need to print anything. */
    3120           0 :                         (void) read_config_file(config_dirs, *f, true, invalid_config);
    3121             : 
    3122           0 :         return 0;
    3123             : }
    3124             : 
    3125          27 : static int link_parent(ItemArray *a) {
    3126             :         const char *path;
    3127             :         char *prefix;
    3128             :         int r;
    3129             : 
    3130          27 :         assert(a);
    3131             : 
    3132             :         /* Finds the closest "parent" item array for the specified item array. Then registers the specified item array
    3133             :          * as child of it, and fills the parent in, linking them both ways. This allows us to later create parents
    3134             :          * before their children, and clean up/remove children before their parents. */
    3135             : 
    3136          27 :         if (a->n_items <= 0)
    3137           0 :                 return 0;
    3138             : 
    3139          27 :         path = a->items[0].path;
    3140          27 :         prefix = newa(char, strlen(path) + 1);
    3141         108 :         PATH_FOREACH_PREFIX(prefix, path) {
    3142             :                 ItemArray *j;
    3143             : 
    3144          81 :                 j = ordered_hashmap_get(items, prefix);
    3145          81 :                 if (!j)
    3146          81 :                         j = ordered_hashmap_get(globs, prefix);
    3147          81 :                 if (j) {
    3148           0 :                         r = set_ensure_allocated(&j->children, NULL);
    3149           0 :                         if (r < 0)
    3150           0 :                                 return log_oom();
    3151             : 
    3152           0 :                         r = set_put(j->children, a);
    3153           0 :                         if (r < 0)
    3154           0 :                                 return log_oom();
    3155             : 
    3156           0 :                         a->parent = j;
    3157           0 :                         return 1;
    3158             :                 }
    3159             :         }
    3160             : 
    3161          27 :         return 0;
    3162             : }
    3163             : 
    3164          27 : DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_array_hash_ops, char, string_hash_func, string_compare_func,
    3165             :                                               ItemArray, item_array_free);
    3166             : 
    3167          86 : static int run(int argc, char *argv[]) {
    3168          86 :         _cleanup_strv_free_ char **config_dirs = NULL;
    3169          86 :         bool invalid_config = false;
    3170             :         Iterator iterator;
    3171             :         ItemArray *a;
    3172             :         enum {
    3173             :                 PHASE_REMOVE_AND_CLEAN,
    3174             :                 PHASE_CREATE,
    3175             :                 _PHASE_MAX
    3176             :         } phase;
    3177             :         int r, k;
    3178             : 
    3179          86 :         r = parse_argv(argc, argv);
    3180          86 :         if (r <= 0)
    3181           4 :                 return r;
    3182             : 
    3183          82 :         log_setup_service();
    3184             : 
    3185             :         /* Descending down file system trees might take a lot of fds */
    3186          82 :         (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
    3187             : 
    3188          82 :         if (arg_user) {
    3189          40 :                 r = user_config_paths(&config_dirs);
    3190          40 :                 if (r < 0)
    3191           0 :                         return log_error_errno(r, "Failed to initialize configuration directory list: %m");
    3192             :         } else {
    3193          42 :                 config_dirs = strv_split_nulstr(CONF_PATHS_NULSTR("tmpfiles.d"));
    3194          42 :                 if (!config_dirs)
    3195           0 :                         return log_oom();
    3196             :         }
    3197             : 
    3198          82 :         if (DEBUG_LOGGING) {
    3199           0 :                 _cleanup_free_ char *t = NULL;
    3200             : 
    3201           0 :                 t = strv_join(config_dirs, "\n\t");
    3202           0 :                 if (t)
    3203           0 :                         log_debug("Looking for configuration files in (higher priority first):\n\t%s", t);
    3204             :         }
    3205             : 
    3206          82 :         if (arg_cat_config) {
    3207           0 :                 (void) pager_open(arg_pager_flags);
    3208             : 
    3209           0 :                 return cat_config(config_dirs, argv + optind);
    3210             :         }
    3211             : 
    3212          82 :         umask(0022);
    3213             : 
    3214          82 :         mac_selinux_init();
    3215             : 
    3216          82 :         items = ordered_hashmap_new(&item_array_hash_ops);
    3217          82 :         globs = ordered_hashmap_new(&item_array_hash_ops);
    3218          82 :         if (!items || !globs)
    3219           0 :                 return log_oom();
    3220             : 
    3221             :         /* If command line arguments are specified along with --replace, read all
    3222             :          * configuration files and insert the positional arguments at the specified
    3223             :          * place. Otherwise, if command line arguments are specified, execute just
    3224             :          * them, and finally, without --replace= or any positional arguments, just
    3225             :          * read configuration and execute it.
    3226             :          */
    3227          82 :         if (arg_replace || optind >= argc)
    3228           0 :                 r = read_config_files(config_dirs, argv + optind, &invalid_config);
    3229             :         else
    3230          82 :                 r = parse_arguments(config_dirs, argv + optind, &invalid_config);
    3231          82 :         if (r < 0)
    3232           0 :                 return r;
    3233             : 
    3234             :         /* Let's now link up all child/parent relationships */
    3235         109 :         ORDERED_HASHMAP_FOREACH(a, items, iterator) {
    3236          27 :                 r = link_parent(a);
    3237          27 :                 if (r < 0)
    3238           0 :                         return r;
    3239             :         }
    3240          82 :         ORDERED_HASHMAP_FOREACH(a, globs, iterator) {
    3241           0 :                 r = link_parent(a);
    3242           0 :                 if (r < 0)
    3243           0 :                         return r;
    3244             :         }
    3245             : 
    3246             :         /* If multiple operations are requested, let's first run the remove/clean operations, and only then the create
    3247             :          * operations. i.e. that we first clean out the platform we then build on. */
    3248         246 :         for (phase = 0; phase < _PHASE_MAX; phase++) {
    3249             :                 OperationMask op;
    3250             : 
    3251         164 :                 if (phase == PHASE_REMOVE_AND_CLEAN)
    3252          82 :                         op = arg_operation & (OPERATION_REMOVE|OPERATION_CLEAN);
    3253          82 :                 else if (phase == PHASE_CREATE)
    3254          82 :                         op = arg_operation & OPERATION_CREATE;
    3255             :                 else
    3256           0 :                         assert_not_reached("unexpected phase");
    3257             : 
    3258         164 :                 if (op == 0) /* Nothing requested in this phase */
    3259          82 :                         continue;
    3260             : 
    3261             :                 /* The non-globbing ones usually create things, hence we apply them first */
    3262         109 :                 ORDERED_HASHMAP_FOREACH(a, items, iterator) {
    3263          27 :                         k = process_item_array(a, op);
    3264          27 :                         if (k < 0 && r >= 0)
    3265           0 :                                 r = k;
    3266             :                 }
    3267             : 
    3268             :                 /* The globbing ones usually alter things, hence we apply them second. */
    3269          82 :                 ORDERED_HASHMAP_FOREACH(a, globs, iterator) {
    3270           0 :                         k = process_item_array(a, op);
    3271           0 :                         if (k < 0 && r >= 0)
    3272           0 :                                 r = k;
    3273             :                 }
    3274             :         }
    3275             : 
    3276          82 :         if (ERRNO_IS_RESOURCE(-r))
    3277           0 :                 return r;
    3278          82 :         if (invalid_config)
    3279          54 :                 return EX_DATAERR;
    3280          28 :         if (r < 0)
    3281           0 :                 return EX_CANTCREAT;
    3282          28 :         return 0;
    3283             : }
    3284             : 
    3285          86 : DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);

Generated by: LCOV version 1.14