LCOV - code coverage report
Current view: top level - tmpfiles - tmpfiles.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 388 1631 23.8 %
Date: 2019-08-23 13:36:53 Functions: 31 74 41.9 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 241 1798 13.4 %

           Branch data     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                 :        344 : STATIC_DESTRUCTOR_REGISTER(items, ordered_hashmap_freep);
     179                 :        344 : STATIC_DESTRUCTOR_REGISTER(globs, ordered_hashmap_freep);
     180                 :        344 : STATIC_DESTRUCTOR_REGISTER(unix_sockets, set_free_freep);
     181                 :        344 : STATIC_DESTRUCTOR_REGISTER(arg_include_prefixes, freep);
     182                 :        344 : STATIC_DESTRUCTOR_REGISTER(arg_exclude_prefixes, freep);
     183                 :        344 : 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                 :          8 : 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                 :          8 :         r = specifier_machine_id(specifier, data, userdata, ret);
     217   [ -  +  -  + ]:          8 :         if (IN_SET(r, -ENOENT, -ENOMEDIUM))
     218                 :          0 :                 return -ENXIO;
     219                 :            : 
     220                 :          8 :         return r;
     221                 :            : }
     222                 :            : 
     223                 :         24 : 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         [ +  + ]:         24 :         paths = arg_user ? paths_user : paths_system;
     248                 :            : 
     249                 :         24 :         i = PTR_TO_UINT(data);
     250         [ -  + ]:         24 :         assert(i < ELEMENTSOF(paths_system));
     251                 :            : 
     252                 :         24 :         return sd_path_home(paths[i].type, paths[i].suffix, ret);
     253                 :            : }
     254                 :            : 
     255                 :          4 : 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   [ -  +  +  -  :          4 :         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         [ +  - ]:          4 :         if (!notified)
     270         [ +  - ]:          4 :                 log_notice("All rules containing unresolvable specifiers will be skipped.");
     271                 :            : 
     272                 :          4 :         notified = true;
     273                 :          4 :         return 0;
     274                 :            : }
     275                 :            : 
     276                 :        160 : static int user_config_paths(char*** ret) {
     277                 :        160 :         _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
     278                 :        160 :         _cleanup_free_ char *persistent_config = NULL, *runtime_config = NULL, *data_home = NULL;
     279                 :        160 :         _cleanup_strv_free_ char **res = NULL;
     280                 :            :         int r;
     281                 :            : 
     282                 :        160 :         r = xdg_user_dirs(&config_dirs, &data_dirs);
     283         [ -  + ]:        160 :         if (r < 0)
     284                 :          0 :                 return r;
     285                 :            : 
     286                 :        160 :         r = xdg_user_config_dir(&persistent_config, "/user-tmpfiles.d");
     287   [ -  +  #  # ]:        160 :         if (r < 0 && r != -ENXIO)
     288                 :          0 :                 return r;
     289                 :            : 
     290                 :        160 :         r = xdg_user_runtime_dir(&runtime_config, "/user-tmpfiles.d");
     291   [ +  +  -  + ]:        160 :         if (r < 0 && r != -ENXIO)
     292                 :          0 :                 return r;
     293                 :            : 
     294                 :        160 :         r = xdg_user_data_dir(&data_home, "/user-tmpfiles.d");
     295   [ -  +  #  # ]:        160 :         if (r < 0 && r != -ENXIO)
     296                 :          0 :                 return r;
     297                 :            : 
     298                 :        160 :         r = strv_extend_strv_concat(&res, config_dirs, "/user-tmpfiles.d");
     299         [ -  + ]:        160 :         if (r < 0)
     300                 :          0 :                 return r;
     301                 :            : 
     302                 :        160 :         r = strv_extend(&res, persistent_config);
     303         [ -  + ]:        160 :         if (r < 0)
     304                 :          0 :                 return r;
     305                 :            : 
     306                 :        160 :         r = strv_extend(&res, runtime_config);
     307         [ -  + ]:        160 :         if (r < 0)
     308                 :          0 :                 return r;
     309                 :            : 
     310                 :        160 :         r = strv_extend(&res, data_home);
     311         [ -  + ]:        160 :         if (r < 0)
     312                 :          0 :                 return r;
     313                 :            : 
     314                 :        160 :         r = strv_extend_strv_concat(&res, data_dirs, "/user-tmpfiles.d");
     315         [ -  + ]:        160 :         if (r < 0)
     316                 :          0 :                 return r;
     317                 :            : 
     318                 :        160 :         r = path_strv_make_absolute_cwd(res);
     319         [ -  + ]:        160 :         if (r < 0)
     320                 :          0 :                 return r;
     321                 :            : 
     322                 :        160 :         *ret = TAKE_PTR(res);
     323                 :        160 :         return 0;
     324                 :            : }
     325                 :            : 
     326                 :        108 : static bool needs_glob(ItemType t) {
     327         [ -  + ]:        108 :         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                 :        108 : 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         [ -  + ]:        108 :         assert(i);
     775         [ -  + ]:        108 :         assert(fd);
     776         [ -  + ]:        108 :         assert(path);
     777                 :            : 
     778   [ +  -  +  -  :        108 :         if (!i->mode_set && !i->uid_set && !i->gid_set)
                   +  - ]
     779                 :        108 :                 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                 :        108 :         return label_fix(path, 0);
     858                 :            : }
     859                 :            : 
     860                 :        108 : static int path_open_parent_safe(const char *path) {
     861                 :        108 :         _cleanup_free_ char *dn = NULL;
     862                 :            :         int fd;
     863                 :            : 
     864   [ +  -  -  + ]:        108 :         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                 :        108 :         dn = dirname_malloc(path);
     870         [ -  + ]:        108 :         if (!dn)
     871                 :          0 :                 return log_oom();
     872                 :            : 
     873                 :        108 :         fd = chase_symlinks(dn, arg_root, CHASE_OPEN|CHASE_SAFE|CHASE_WARN, NULL);
     874   [ -  +  #  # ]:        108 :         if (fd < 0 && fd != -ENOLINK)
     875         [ #  # ]:          0 :                 return log_error_errno(fd, "Failed to validate path %s: %m", path);
     876                 :            : 
     877                 :        108 :         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                 :        108 : static int create_file(Item *i, const char *path) {
    1296                 :        108 :         _cleanup_close_ int fd = -1, dir_fd = -1;
    1297                 :        108 :         struct stat stbuf, *st = NULL;
    1298                 :        108 :         int r = 0;
    1299                 :            :         char *bn;
    1300                 :            : 
    1301         [ -  + ]:        108 :         assert(i);
    1302         [ -  + ]:        108 :         assert(path);
    1303         [ -  + ]:        108 :         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                 :        108 :         dir_fd = path_open_parent_safe(path);
    1310         [ -  + ]:        108 :         if (dir_fd < 0)
    1311                 :          0 :                 return dir_fd;
    1312                 :            : 
    1313                 :        108 :         bn = basename(path);
    1314                 :            : 
    1315         [ +  + ]:        216 :         RUN_WITH_UMASK(0000) {
    1316                 :        108 :                 mac_selinux_create_file_prepare(path, S_IFREG);
    1317                 :        108 :                 fd = openat(dir_fd, bn, O_CREAT|O_EXCL|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode);
    1318                 :        108 :                 mac_selinux_create_file_clear();
    1319                 :            :         }
    1320                 :            : 
    1321         [ -  + ]:        108 :         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         [ -  + ]:        108 :                 log_debug("\"%s\" has been created.", path);
    1349                 :            : 
    1350         [ +  - ]:        108 :                 if (i->argument) {
    1351         [ -  + ]:        108 :                         log_debug("Writing to \"%s\".", path);
    1352                 :            : 
    1353                 :        108 :                         r = loop_write(fd, i->argument, strlen(i->argument), false);
    1354         [ -  + ]:        108 :                         if (r < 0)
    1355         [ #  # ]:          0 :                                 return log_error_errno(r, "Failed to write file \"%s\": %m", path);
    1356                 :            :                 }
    1357                 :            :         }
    1358                 :            : 
    1359                 :        108 :         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                 :        108 : static int create_item(Item *i) {
    1913                 :            :         CreationMode creation;
    1914                 :        108 :         int r = 0;
    1915                 :            : 
    1916         [ -  + ]:        108 :         assert(i);
    1917                 :            : 
    1918         [ -  + ]:        108 :         log_debug("Running create action for entry %c %s", (char) i->type, i->path);
    1919                 :            : 
    1920   [ -  +  -  -  :        108 :         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                 :        108 :         case CREATE_FILE:
    1929         [ +  + ]:        216 :                 RUN_WITH_UMASK(0000)
    1930                 :        108 :                         (void) mkdir_parents_label(i->path, 0755);
    1931                 :            : 
    1932                 :        108 :                 r = create_file(i, i->path);
    1933         [ -  + ]:        108 :                 if (r < 0)
    1934                 :          0 :                         return r;
    1935                 :        108 :                 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                 :        108 :         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                 :        108 : static int process_item(Item *i, OperationMask operation) {
    2247                 :            :         OperationMask todo;
    2248                 :            :         int r, q, p;
    2249                 :            : 
    2250         [ -  + ]:        108 :         assert(i);
    2251                 :            : 
    2252                 :        108 :         todo = operation & ~i->done;
    2253         [ -  + ]:        108 :         if (todo == 0) /* Everything already done? */
    2254                 :          0 :                 return 0;
    2255                 :            : 
    2256                 :        108 :         i->done |= operation;
    2257                 :            : 
    2258                 :        108 :         r = chase_symlinks(i->path, arg_root, CHASE_NO_AUTOFS|CHASE_WARN, NULL);
    2259         [ -  + ]:        108 :         if (r == -EREMOTE) {
    2260         [ #  # ]:          0 :                 log_notice_errno(r, "Skipping %s", i->path);
    2261                 :          0 :                 return 0;
    2262                 :            :         }
    2263         [ +  - ]:        108 :         if (r < 0)
    2264         [ -  + ]:        108 :                 log_debug_errno(r, "Failed to determine whether '%s' is below autofs, ignoring: %m", i->path);
    2265                 :            : 
    2266         [ +  - ]:        108 :         r = FLAGS_SET(operation, OPERATION_CREATE) ? create_item(i) : 0;
    2267                 :            :         /* Failure can only be tolerated for create */
    2268         [ -  + ]:        108 :         if (i->allow_failure)
    2269                 :          0 :                 r = 0;
    2270                 :            : 
    2271         [ -  + ]:        108 :         q = FLAGS_SET(operation, OPERATION_REMOVE) ? remove_item(i) : 0;
    2272         [ -  + ]:        108 :         p = FLAGS_SET(operation, OPERATION_CLEAN) ? clean_item(i) : 0;
    2273                 :            : 
    2274         [ +  - ]:        216 :         return r < 0 ? r :
    2275         [ -  + ]:        108 :                 q < 0 ? q :
    2276                 :            :                 p;
    2277                 :            : }
    2278                 :            : 
    2279                 :        108 : static int process_item_array(ItemArray *array, OperationMask operation) {
    2280                 :        108 :         int r = 0;
    2281                 :            :         size_t n;
    2282                 :            : 
    2283         [ -  + ]:        108 :         assert(array);
    2284                 :            : 
    2285                 :            :         /* Create any parent first. */
    2286   [ +  -  -  + ]:        108 :         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   [ -  +  #  # ]:        108 :         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         [ +  + ]:        216 :         for (n = 0; n < array->n_items; n++) {
    2304                 :            :                 int k;
    2305                 :            : 
    2306                 :        108 :                 k = process_item(array->items + n, operation);
    2307   [ -  +  #  # ]:        108 :                 if (k < 0 && r == 0)
    2308                 :          0 :                         r = k;
    2309                 :            :         }
    2310                 :            : 
    2311                 :        108 :         return r;
    2312                 :            : }
    2313                 :            : 
    2314                 :        436 : static void item_free_contents(Item *i) {
    2315         [ -  + ]:        436 :         assert(i);
    2316                 :        436 :         free(i->path);
    2317                 :        436 :         free(i->argument);
    2318                 :        436 :         strv_free(i->xattrs);
    2319                 :            : 
    2320                 :            : #if HAVE_ACL
    2321                 :        436 :         acl_free(i->acl_access);
    2322                 :        436 :         acl_free(i->acl_default);
    2323                 :            : #endif
    2324                 :        436 : }
    2325                 :            : 
    2326                 :        108 : static ItemArray* item_array_free(ItemArray *a) {
    2327                 :            :         size_t n;
    2328                 :            : 
    2329         [ -  + ]:        108 :         if (!a)
    2330                 :          0 :                 return NULL;
    2331                 :            : 
    2332         [ +  + ]:        216 :         for (n = 0; n < a->n_items; n++)
    2333                 :        108 :                 item_free_contents(a->items + n);
    2334                 :            : 
    2335                 :        108 :         set_free(a->children);
    2336                 :        108 :         free(a->items);
    2337                 :        108 :         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                 :        128 : static bool should_include_path(const char *path) {
    2383                 :            :         char **prefix;
    2384                 :            : 
    2385   [ -  +  #  # ]:        128 :         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   [ -  +  #  # ]:        128 :         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         [ +  - ]:        128 :         if (strv_isempty(arg_include_prefixes))
    2401                 :        128 :                 return true;
    2402                 :            : 
    2403         [ #  # ]:          0 :         log_debug("Entry \"%s\" does not match any include prefix, skipping.", path);
    2404                 :          0 :         return false;
    2405                 :            : }
    2406                 :            : 
    2407                 :        128 : static int specifier_expansion_from_arg(Item *i) {
    2408                 :        128 :         _cleanup_free_ char *unescaped = NULL, *resolved = NULL;
    2409                 :            :         char **xattr;
    2410                 :            :         int r;
    2411                 :            : 
    2412         [ -  + ]:        128 :         assert(i);
    2413                 :            : 
    2414         [ -  + ]:        128 :         if (!i->argument)
    2415                 :          0 :                 return 0;
    2416                 :            : 
    2417      [ +  -  - ]:        128 :         switch (i->type) {
    2418                 :        128 :         case COPY_FILES:
    2419                 :            :         case CREATE_SYMLINK:
    2420                 :            :         case CREATE_FILE:
    2421                 :            :         case TRUNCATE_FILE:
    2422                 :            :         case WRITE_FILE:
    2423                 :        128 :                 r = cunescape(i->argument, 0, &unescaped);
    2424         [ -  + ]:        128 :                 if (r < 0)
    2425         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to unescape parameter to write: %s", i->argument);
    2426                 :            : 
    2427                 :        128 :                 r = specifier_printf(unescaped, specifier_table, NULL, &resolved);
    2428         [ +  + ]:        128 :                 if (r < 0)
    2429                 :         20 :                         return r;
    2430                 :            : 
    2431                 :        108 :                 free_and_replace(i->argument, resolved);
    2432                 :        108 :                 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                 :        108 :         return 0;
    2451                 :            : }
    2452                 :            : 
    2453                 :        256 : static int patch_var_run(const char *fname, unsigned line, char **path) {
    2454                 :            :         const char *k;
    2455                 :            :         char *n;
    2456                 :            : 
    2457         [ -  + ]:        256 :         assert(path);
    2458         [ -  + ]:        256 :         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                 :        256 :         k = path_startswith(*path, "/var/run/");
    2471         [ +  - ]:        256 :         if (isempty(k)) /* Don't complain about other paths than /var/run, and not about /var/run itself either. */
    2472                 :        256 :                 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                 :        328 : static int parse_line(const char *fname, unsigned line, const char *buffer, bool *invalid_config) {
    2489                 :            : 
    2490                 :        328 :         _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
    2491                 :        328 :         _cleanup_(item_free_contents) Item i = {};
    2492                 :            :         ItemArray *existing;
    2493                 :            :         OrderedHashmap *h;
    2494                 :            :         int r, pos;
    2495                 :        328 :         bool force = false, boot = false, allow_failure = false;
    2496                 :            : 
    2497         [ -  + ]:        328 :         assert(fname);
    2498         [ -  + ]:        328 :         assert(line >= 1);
    2499         [ -  + ]:        328 :         assert(buffer);
    2500                 :            : 
    2501                 :        328 :         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         [ +  + ]:        328 :         if (r < 0) {
    2513   [ +  -  +  - ]:          8 :                 if (IN_SET(r, -EINVAL, -EBADSLT))
    2514                 :            :                         /* invalid quoting and such or an unknown specifier */
    2515                 :          8 :                         *invalid_config = true;
    2516         [ +  - ]:          8 :                 return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line);
    2517         [ +  + ]:        320 :         } else if (r < 2) {
    2518                 :         16 :                 *invalid_config = true;
    2519         [ +  - ]:         16 :                 log_error("[%s:%u] Syntax error.", fname, line);
    2520                 :         16 :                 return -EIO;
    2521                 :            :         }
    2522                 :            : 
    2523         [ +  + ]:        304 :         if (!empty_or_dash(buffer)) {
    2524                 :        160 :                 i.argument = strdup(buffer);
    2525         [ -  + ]:        160 :                 if (!i.argument)
    2526                 :          0 :                         return log_oom();
    2527                 :            :         }
    2528                 :            : 
    2529         [ -  + ]:        304 :         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         [ +  + ]:        352 :         for (pos = 1; action[pos]; pos++) {
    2536   [ +  +  +  + ]:         80 :                 if (action[pos] == '!' && !boot)
    2537                 :         24 :                         boot = true;
    2538   [ +  +  +  + ]:         56 :                 else if (action[pos] == '+' && !force)
    2539                 :         24 :                         force = true;
    2540   [ -  +  #  # ]:         32 :                 else if (action[pos] == '-' && !allow_failure)
    2541                 :          0 :                         allow_failure = true;
    2542                 :            :                 else {
    2543                 :         32 :                         *invalid_config = true;
    2544         [ +  - ]:         32 :                         log_error("[%s:%u] Unknown modifiers in command '%s'",
    2545                 :            :                                   fname, line, action);
    2546                 :         32 :                         return -EINVAL;
    2547                 :            :                 }
    2548                 :            :         }
    2549                 :            : 
    2550   [ -  +  #  # ]:        272 :         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                 :        272 :         i.type = action[0];
    2557                 :        272 :         i.force = force;
    2558                 :        272 :         i.allow_failure = allow_failure;
    2559                 :            : 
    2560                 :        272 :         r = specifier_printf(path, specifier_table, NULL, &i.path);
    2561         [ -  + ]:        272 :         if (r == -ENXIO)
    2562                 :          0 :                 return log_unresolvable_specifier(fname, line);
    2563         [ +  + ]:        272 :         if (r < 0) {
    2564   [ +  -  +  - ]:         16 :                 if (IN_SET(r, -EINVAL, -EBADSLT))
    2565                 :         16 :                         *invalid_config = true;
    2566         [ +  - ]:         16 :                 return log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", fname, line, path);
    2567                 :            :         }
    2568                 :            : 
    2569                 :        256 :         r = patch_var_run(fname, line, &i.path);
    2570         [ -  + ]:        256 :         if (r < 0)
    2571                 :          0 :                 return r;
    2572                 :            : 
    2573   [ -  +  -  +  :        256 :         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                 :        116 :         case CREATE_FILE:
    2595                 :            :         case TRUNCATE_FILE:
    2596                 :        116 :                 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                 :         36 :         case WRITE_FILE:
    2607         [ +  + ]:         36 :                 if (!i.argument) {
    2608                 :          8 :                         *invalid_config = true;
    2609         [ +  - ]:          8 :                         log_error("[%s:%u] Write file requires argument.", fname, line);
    2610                 :          8 :                         return -EBADMSG;
    2611                 :            :                 }
    2612                 :         28 :                 break;
    2613                 :            : 
    2614                 :         16 :         case COPY_FILES:
    2615         [ +  + ]:         16 :                 if (!i.argument) {
    2616                 :          8 :                         i.argument = path_join(arg_root, "/usr/share/factory", i.path);
    2617         [ -  + ]:          8 :                         if (!i.argument)
    2618                 :          0 :                                 return log_oom();
    2619                 :            : 
    2620         [ +  - ]:          8 :                 } else if (!path_is_absolute(i.argument)) {
    2621                 :          8 :                         *invalid_config = true;
    2622         [ +  - ]:          8 :                         log_error("[%s:%u] Source path is not absolute.", fname, line);
    2623                 :          8 :                         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                 :          8 :                 path_simplify(i.argument, false);
    2635                 :          8 :                 break;
    2636                 :            : 
    2637                 :         32 :         case CREATE_CHAR_DEVICE:
    2638                 :            :         case CREATE_BLOCK_DEVICE:
    2639         [ +  - ]:         32 :                 if (!i.argument) {
    2640                 :         32 :                         *invalid_config = true;
    2641         [ +  - ]:         32 :                         log_error("[%s:%u] Device file requires argument.", fname, line);
    2642                 :         32 :                         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                 :         16 :         case SET_XATTR:
    2655                 :            :         case RECURSIVE_SET_XATTR:
    2656         [ +  - ]:         16 :                 if (!i.argument) {
    2657                 :         16 :                         *invalid_config = true;
    2658         [ +  - ]:         16 :                         log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
    2659                 :         16 :                         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                 :         16 :         case SET_ACL:
    2667                 :            :         case RECURSIVE_SET_ACL:
    2668         [ +  - ]:         16 :                 if (!i.argument) {
    2669                 :         16 :                         *invalid_config = true;
    2670         [ +  - ]:         16 :                         log_error("[%s:%u] Set ACLs requires argument.", fname, line);
    2671                 :         16 :                         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                 :         16 :         case SET_ATTRIBUTE:
    2679                 :            :         case RECURSIVE_SET_ATTRIBUTE:
    2680         [ +  - ]:         16 :                 if (!i.argument) {
    2681                 :         16 :                         *invalid_config = true;
    2682         [ +  - ]:         16 :                         log_error("[%s:%u] Set file attribute requires argument.", fname, line);
    2683                 :         16 :                         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                 :          8 :         default:
    2693         [ +  - ]:          8 :                 log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type);
    2694                 :          8 :                 *invalid_config = true;
    2695                 :          8 :                 return -EBADMSG;
    2696                 :            :         }
    2697                 :            : 
    2698         [ +  + ]:        152 :         if (!path_is_absolute(i.path)) {
    2699         [ +  - ]:         24 :                 log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path);
    2700                 :         24 :                 *invalid_config = true;
    2701                 :         24 :                 return -EBADMSG;
    2702                 :            :         }
    2703                 :            : 
    2704                 :        128 :         path_simplify(i.path, false);
    2705                 :            : 
    2706         [ -  + ]:        128 :         if (!should_include_path(i.path))
    2707                 :          0 :                 return 0;
    2708                 :            : 
    2709                 :        128 :         r = specifier_expansion_from_arg(&i);
    2710         [ +  + ]:        128 :         if (r == -ENXIO)
    2711                 :          4 :                 return log_unresolvable_specifier(fname, line);
    2712         [ +  + ]:        124 :         if (r < 0) {
    2713   [ +  -  +  - ]:         16 :                 if (IN_SET(r, -EINVAL, -EBADSLT))
    2714                 :         16 :                         *invalid_config = true;
    2715         [ +  - ]:         16 :                 return log_error_errno(r, "[%s:%u] Failed to substitute specifiers in argument: %m",
    2716                 :            :                                        fname, line);
    2717                 :            :         }
    2718                 :            : 
    2719         [ -  + ]:        108 :         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         [ -  + ]:        108 :         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         [ -  + ]:        108 :         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         [ -  + ]:        108 :         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   [ -  +  -  + ]:        108 :                 i.mode = IN_SET(i.type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644;
    2772                 :            : 
    2773         [ -  + ]:        108 :         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         [ -  + ]:        108 :         h = needs_glob(i.type) ? globs : items;
    2791                 :            : 
    2792                 :        108 :         existing = ordered_hashmap_get(h, i.path);
    2793         [ -  + ]:        108 :         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                 :        108 :                 existing = new0(ItemArray, 1);
    2805         [ -  + ]:        108 :                 if (!existing)
    2806                 :          0 :                         return log_oom();
    2807                 :            : 
    2808                 :        108 :                 r = ordered_hashmap_put(h, i.path, existing);
    2809         [ -  + ]:        108 :                 if (r < 0) {
    2810                 :          0 :                         free(existing);
    2811                 :          0 :                         return log_oom();
    2812                 :            :                 }
    2813                 :            :         }
    2814                 :            : 
    2815         [ -  + ]:        108 :         if (!GREEDY_REALLOC(existing->items, existing->allocated, existing->n_items + 1))
    2816                 :          0 :                 return log_oom();
    2817                 :            : 
    2818                 :        108 :         existing->items[existing->n_items++] = i;
    2819                 :        108 :         i = (struct Item) {};
    2820                 :            : 
    2821                 :            :         /* Sort item array, to enforce stable ordering of application */
    2822                 :        108 :         typesafe_qsort(existing->items, existing->n_items, item_compare);
    2823                 :            : 
    2824                 :        108 :         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                 :         12 : static int help(void) {
    2839                 :         12 :         _cleanup_free_ char *link = NULL;
    2840                 :            :         int r;
    2841                 :            : 
    2842                 :         12 :         r = terminal_urlify_man("systemd-tmpfiles", "8", &link);
    2843         [ -  + ]:         12 :         if (r < 0)
    2844                 :          0 :                 return log_oom();
    2845                 :            : 
    2846                 :         12 :         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                 :         12 :         return 0;
    2867                 :            : }
    2868                 :            : 
    2869                 :        344 : 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         [ -  + ]:        344 :         assert(argc >= 0);
    2906         [ -  + ]:        344 :         assert(argv);
    2907                 :            : 
    2908         [ +  + ]:        832 :         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
    2909                 :            : 
    2910   [ +  -  -  +  :        504 :                 switch (c) {
          +  -  -  -  -  
          -  -  -  -  +  
                      - ]
    2911                 :            : 
    2912                 :         12 :                 case 'h':
    2913                 :         12 :                         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                 :        160 :                 case ARG_USER:
    2923                 :        160 :                         arg_user = true;
    2924                 :        160 :                         break;
    2925                 :            : 
    2926                 :        328 :                 case ARG_CREATE:
    2927                 :        328 :                         arg_operation |= OPERATION_CREATE;
    2928                 :        328 :                         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                 :          4 :                 case '?':
    2972                 :          4 :                         return -EINVAL;
    2973                 :            : 
    2974                 :          0 :                 default:
    2975                 :          0 :                         assert_not_reached("Unhandled option");
    2976                 :            :                 }
    2977                 :            : 
    2978   [ -  +  #  # ]:        328 :         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   [ -  +  #  # ]:        328 :         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   [ -  +  #  # ]:        328 :         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                 :        328 :         return 1;
    2991                 :            : }
    2992                 :            : 
    2993                 :        328 : static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
    2994                 :        328 :         _cleanup_fclose_ FILE *_f = NULL;
    2995                 :            :         Iterator iterator;
    2996                 :        328 :         unsigned v = 0;
    2997                 :            :         FILE *f;
    2998                 :            :         Item *i;
    2999                 :        328 :         int r = 0;
    3000                 :            : 
    3001         [ -  + ]:        328 :         assert(fn);
    3002                 :            : 
    3003         [ +  - ]:        328 :         if (streq(fn, "-")) {
    3004         [ -  + ]:        328 :                 log_debug("Reading config from stdin…");
    3005                 :        328 :                 fn = "<stdin>";
    3006                 :        328 :                 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                 :        328 :         for (;;) {
    3022   [ +  -  +  - ]:        656 :                 _cleanup_free_ char *line = NULL;
    3023                 :        656 :                 bool invalid_line = false;
    3024                 :            :                 char *l;
    3025                 :            :                 int k;
    3026                 :            : 
    3027                 :        656 :                 k = read_line(f, LONG_LINE_MAX, &line);
    3028         [ -  + ]:        656 :                 if (k < 0)
    3029         [ #  # ]:          0 :                         return log_error_errno(k, "Failed to read '%s': %m", fn);
    3030         [ +  + ]:        656 :                 if (k == 0)
    3031                 :        328 :                         break;
    3032                 :            : 
    3033                 :        328 :                 v++;
    3034                 :            : 
    3035                 :        328 :                 l = strstrip(line);
    3036   [ -  +  -  + ]:        328 :                 if (IN_SET(*l, 0, '#'))
    3037                 :          0 :                         continue;
    3038                 :            : 
    3039                 :        328 :                 k = parse_line(fn, v, l, &invalid_line);
    3040         [ +  + ]:        328 :                 if (k < 0) {
    3041         [ +  - ]:        216 :                         if (invalid_line)
    3042                 :            :                                 /* Allow reporting with a special code if the caller requested this */
    3043                 :        216 :                                 *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         [ -  + ]:        328 :         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         [ -  + ]:        328 :         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                 :        328 :         return r;
    3085                 :            : }
    3086                 :            : 
    3087                 :        328 : static int parse_arguments(char **config_dirs, char **args, bool *invalid_config) {
    3088                 :            :         char **arg;
    3089                 :            :         int r;
    3090                 :            : 
    3091   [ +  -  +  + ]:        656 :         STRV_FOREACH(arg, args) {
    3092                 :        328 :                 r = read_config_file(config_dirs, *arg, false, invalid_config);
    3093         [ -  + ]:        328 :                 if (r < 0)
    3094                 :          0 :                         return r;
    3095                 :            :         }
    3096                 :            : 
    3097                 :        328 :         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                 :        108 : static int link_parent(ItemArray *a) {
    3126                 :            :         const char *path;
    3127                 :            :         char *prefix;
    3128                 :            :         int r;
    3129                 :            : 
    3130         [ -  + ]:        108 :         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         [ -  + ]:        108 :         if (a->n_items <= 0)
    3137                 :          0 :                 return 0;
    3138                 :            : 
    3139                 :        108 :         path = a->items[0].path;
    3140   [ -  +  -  + ]:        108 :         prefix = newa(char, strlen(path) + 1);
    3141   [ +  -  +  + ]:        432 :         PATH_FOREACH_PREFIX(prefix, path) {
    3142                 :            :                 ItemArray *j;
    3143                 :            : 
    3144                 :        324 :                 j = ordered_hashmap_get(items, prefix);
    3145         [ +  - ]:        324 :                 if (!j)
    3146                 :        324 :                         j = ordered_hashmap_get(globs, prefix);
    3147         [ -  + ]:        324 :                 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                 :        108 :         return 0;
    3162                 :            : }
    3163                 :            : 
    3164                 :        108 : 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                 :        344 : static int run(int argc, char *argv[]) {
    3168                 :        344 :         _cleanup_strv_free_ char **config_dirs = NULL;
    3169                 :        344 :         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                 :        344 :         r = parse_argv(argc, argv);
    3180         [ +  + ]:        344 :         if (r <= 0)
    3181                 :         16 :                 return r;
    3182                 :            : 
    3183                 :        328 :         log_setup_service();
    3184                 :            : 
    3185                 :            :         /* Descending down file system trees might take a lot of fds */
    3186                 :        328 :         (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
    3187                 :            : 
    3188         [ +  + ]:        328 :         if (arg_user) {
    3189                 :        160 :                 r = user_config_paths(&config_dirs);
    3190         [ -  + ]:        160 :                 if (r < 0)
    3191         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to initialize configuration directory list: %m");
    3192                 :            :         } else {
    3193                 :        168 :                 config_dirs = strv_split_nulstr(CONF_PATHS_NULSTR("tmpfiles.d"));
    3194         [ -  + ]:        168 :                 if (!config_dirs)
    3195                 :          0 :                         return log_oom();
    3196                 :            :         }
    3197                 :            : 
    3198         [ -  + ]:        328 :         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         [ -  + ]:        328 :         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                 :        328 :         umask(0022);
    3213                 :            : 
    3214                 :        328 :         mac_selinux_init();
    3215                 :            : 
    3216                 :        328 :         items = ordered_hashmap_new(&item_array_hash_ops);
    3217                 :        328 :         globs = ordered_hashmap_new(&item_array_hash_ops);
    3218   [ +  -  -  + ]:        328 :         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   [ +  -  -  + ]:        328 :         if (arg_replace || optind >= argc)
    3228                 :          0 :                 r = read_config_files(config_dirs, argv + optind, &invalid_config);
    3229                 :            :         else
    3230                 :        328 :                 r = parse_arguments(config_dirs, argv + optind, &invalid_config);
    3231         [ -  + ]:        328 :         if (r < 0)
    3232                 :          0 :                 return r;
    3233                 :            : 
    3234                 :            :         /* Let's now link up all child/parent relationships */
    3235         [ +  + ]:        436 :         ORDERED_HASHMAP_FOREACH(a, items, iterator) {
    3236                 :        108 :                 r = link_parent(a);
    3237         [ -  + ]:        108 :                 if (r < 0)
    3238                 :          0 :                         return r;
    3239                 :            :         }
    3240         [ -  + ]:        328 :         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         [ +  + ]:        984 :         for (phase = 0; phase < _PHASE_MAX; phase++) {
    3249                 :            :                 OperationMask op;
    3250                 :            : 
    3251         [ +  + ]:        656 :                 if (phase == PHASE_REMOVE_AND_CLEAN)
    3252                 :        328 :                         op = arg_operation & (OPERATION_REMOVE|OPERATION_CLEAN);
    3253         [ +  - ]:        328 :                 else if (phase == PHASE_CREATE)
    3254                 :        328 :                         op = arg_operation & OPERATION_CREATE;
    3255                 :            :                 else
    3256                 :          0 :                         assert_not_reached("unexpected phase");
    3257                 :            : 
    3258         [ +  + ]:        656 :                 if (op == 0) /* Nothing requested in this phase */
    3259                 :        328 :                         continue;
    3260                 :            : 
    3261                 :            :                 /* The non-globbing ones usually create things, hence we apply them first */
    3262         [ +  + ]:        436 :                 ORDERED_HASHMAP_FOREACH(a, items, iterator) {
    3263                 :        108 :                         k = process_item_array(a, op);
    3264   [ -  +  #  # ]:        108 :                         if (k < 0 && r >= 0)
    3265                 :          0 :                                 r = k;
    3266                 :            :                 }
    3267                 :            : 
    3268                 :            :                 /* The globbing ones usually alter things, hence we apply them second. */
    3269         [ -  + ]:        328 :                 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         [ -  + ]:        328 :         if (ERRNO_IS_RESOURCE(-r))
    3277                 :          0 :                 return r;
    3278         [ +  + ]:        328 :         if (invalid_config)
    3279                 :        216 :                 return EX_DATAERR;
    3280         [ -  + ]:        112 :         if (r < 0)
    3281                 :          0 :                 return EX_CANTCREAT;
    3282                 :        112 :         return 0;
    3283                 :            : }
    3284                 :            : 
    3285         [ +  + ]:        344 : DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);

Generated by: LCOV version 1.14