LCOV - code coverage report
Current view: top level - shared - machine-image.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 1 662 0.2 %
Date: 2019-08-23 13:36:53 Functions: 2 27 7.4 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 4 708 0.6 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <dirent.h>
       4                 :            : #include <errno.h>
       5                 :            : #include <fcntl.h>
       6                 :            : #include <stdio.h>
       7                 :            : #include <stdlib.h>
       8                 :            : #include <string.h>
       9                 :            : #include <sys/file.h>
      10                 :            : #include <sys/ioctl.h>
      11                 :            : #include <sys/stat.h>
      12                 :            : #include <unistd.h>
      13                 :            : #include <linux/fs.h>
      14                 :            : 
      15                 :            : #include "alloc-util.h"
      16                 :            : #include "btrfs-util.h"
      17                 :            : #include "chattr-util.h"
      18                 :            : #include "copy.h"
      19                 :            : #include "dirent-util.h"
      20                 :            : #include "dissect-image.h"
      21                 :            : #include "env-file.h"
      22                 :            : #include "env-util.h"
      23                 :            : #include "fd-util.h"
      24                 :            : #include "fs-util.h"
      25                 :            : #include "hashmap.h"
      26                 :            : #include "hostname-util.h"
      27                 :            : #include "id128-util.h"
      28                 :            : #include "lockfile-util.h"
      29                 :            : #include "log.h"
      30                 :            : #include "loop-util.h"
      31                 :            : #include "machine-image.h"
      32                 :            : #include "macro.h"
      33                 :            : #include "mkdir.h"
      34                 :            : #include "nulstr-util.h"
      35                 :            : #include "os-util.h"
      36                 :            : #include "path-util.h"
      37                 :            : #include "rm-rf.h"
      38                 :            : #include "string-table.h"
      39                 :            : #include "string-util.h"
      40                 :            : #include "strv.h"
      41                 :            : #include "time-util.h"
      42                 :            : #include "utf8.h"
      43                 :            : #include "xattr-util.h"
      44                 :            : 
      45                 :            : static const char* const image_search_path[_IMAGE_CLASS_MAX] = {
      46                 :            :         [IMAGE_MACHINE] =  "/etc/machines\0"              /* only place symlinks here */
      47                 :            :                            "/run/machines\0"              /* and here too */
      48                 :            :                            "/var/lib/machines\0"          /* the main place for images */
      49                 :            :                            "/var/lib/container\0"         /* legacy */
      50                 :            :                            "/usr/local/lib/machines\0"
      51                 :            :                            "/usr/lib/machines\0",
      52                 :            : 
      53                 :            :         [IMAGE_PORTABLE] = "/etc/portables\0"             /* only place symlinks here */
      54                 :            :                            "/run/portables\0"             /* and here too */
      55                 :            :                            "/var/lib/portables\0"         /* the main place for images */
      56                 :            :                            "/usr/local/lib/portables\0"
      57                 :            :                            "/usr/lib/portables\0",
      58                 :            : };
      59                 :            : 
      60                 :          0 : static Image *image_free(Image *i) {
      61         [ #  # ]:          0 :         assert(i);
      62                 :            : 
      63                 :          0 :         free(i->name);
      64                 :          0 :         free(i->path);
      65                 :            : 
      66                 :          0 :         free(i->hostname);
      67                 :          0 :         strv_free(i->machine_info);
      68                 :          0 :         strv_free(i->os_release);
      69                 :            : 
      70                 :          0 :         return mfree(i);
      71                 :            : }
      72                 :            : 
      73   [ #  #  #  #  :          0 : DEFINE_TRIVIAL_REF_UNREF_FUNC(Image, image, image_free);
                   #  # ]
      74                 :          0 : DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(image_hash_ops, char, string_hash_func, string_compare_func,
      75                 :            :                                       Image, image_unref);
      76                 :            : 
      77                 :          0 : static char **image_settings_path(Image *image) {
      78                 :          0 :         _cleanup_strv_free_ char **l = NULL;
      79                 :            :         const char *fn, *s;
      80                 :          0 :         unsigned i = 0;
      81                 :            : 
      82         [ #  # ]:          0 :         assert(image);
      83                 :            : 
      84                 :          0 :         l = new0(char*, 4);
      85         [ #  # ]:          0 :         if (!l)
      86                 :          0 :                 return NULL;
      87                 :            : 
      88   [ #  #  #  #  :          0 :         fn = strjoina(image->name, ".nspawn");
          #  #  #  #  #  
                #  #  # ]
      89                 :            : 
      90         [ #  # ]:          0 :         FOREACH_STRING(s, "/etc/systemd/nspawn", "/run/systemd/nspawn") {
      91                 :          0 :                 l[i] = path_join(s, fn);
      92         [ #  # ]:          0 :                 if (!l[i])
      93                 :          0 :                         return NULL;
      94                 :            : 
      95                 :          0 :                 i++;
      96                 :            :         }
      97                 :            : 
      98                 :          0 :         l[i] = file_in_same_dir(image->path, fn);
      99         [ #  # ]:          0 :         if (!l[i])
     100                 :          0 :                 return NULL;
     101                 :            : 
     102                 :          0 :         return TAKE_PTR(l);
     103                 :            : }
     104                 :            : 
     105                 :          0 : static char *image_roothash_path(Image *image) {
     106                 :            :         const char *fn;
     107                 :            : 
     108         [ #  # ]:          0 :         assert(image);
     109                 :            : 
     110   [ #  #  #  #  :          0 :         fn = strjoina(image->name, ".roothash");
          #  #  #  #  #  
                #  #  # ]
     111                 :            : 
     112                 :          0 :         return file_in_same_dir(image->path, fn);
     113                 :            : }
     114                 :            : 
     115                 :          0 : static int image_new(
     116                 :            :                 ImageType t,
     117                 :            :                 const char *pretty,
     118                 :            :                 const char *path,
     119                 :            :                 const char *filename,
     120                 :            :                 bool read_only,
     121                 :            :                 usec_t crtime,
     122                 :            :                 usec_t mtime,
     123                 :            :                 Image **ret) {
     124                 :            : 
     125                 :          0 :         _cleanup_(image_unrefp) Image *i = NULL;
     126                 :            : 
     127         [ #  # ]:          0 :         assert(t >= 0);
     128         [ #  # ]:          0 :         assert(t < _IMAGE_TYPE_MAX);
     129         [ #  # ]:          0 :         assert(pretty);
     130         [ #  # ]:          0 :         assert(filename);
     131         [ #  # ]:          0 :         assert(ret);
     132                 :            : 
     133                 :          0 :         i = new0(Image, 1);
     134         [ #  # ]:          0 :         if (!i)
     135                 :          0 :                 return -ENOMEM;
     136                 :            : 
     137                 :          0 :         i->n_ref = 1;
     138                 :          0 :         i->type = t;
     139                 :          0 :         i->read_only = read_only;
     140                 :          0 :         i->crtime = crtime;
     141                 :          0 :         i->mtime = mtime;
     142                 :          0 :         i->usage = i->usage_exclusive = (uint64_t) -1;
     143                 :          0 :         i->limit = i->limit_exclusive = (uint64_t) -1;
     144                 :            : 
     145                 :          0 :         i->name = strdup(pretty);
     146         [ #  # ]:          0 :         if (!i->name)
     147                 :          0 :                 return -ENOMEM;
     148                 :            : 
     149                 :          0 :         i->path = path_join(path, filename);
     150         [ #  # ]:          0 :         if (!i->path)
     151                 :          0 :                 return -ENOMEM;
     152                 :            : 
     153                 :          0 :         path_simplify(i->path, false);
     154                 :            : 
     155                 :          0 :         *ret = TAKE_PTR(i);
     156                 :            : 
     157                 :          0 :         return 0;
     158                 :            : }
     159                 :            : 
     160                 :          0 : static int extract_pretty(const char *path, const char *suffix, char **ret) {
     161                 :          0 :         _cleanup_free_ char *name = NULL;
     162                 :            :         const char *p;
     163                 :            :         size_t n;
     164                 :            : 
     165         [ #  # ]:          0 :         assert(path);
     166         [ #  # ]:          0 :         assert(ret);
     167                 :            : 
     168                 :          0 :         p = last_path_component(path);
     169                 :          0 :         n = strcspn(p, "/");
     170                 :            : 
     171                 :          0 :         name = strndup(p, n);
     172         [ #  # ]:          0 :         if (!name)
     173                 :          0 :                 return -ENOMEM;
     174                 :            : 
     175         [ #  # ]:          0 :         if (suffix) {
     176                 :            :                 char *e;
     177                 :            : 
     178                 :          0 :                 e = endswith(name, suffix);
     179         [ #  # ]:          0 :                 if (!e)
     180                 :          0 :                         return -EINVAL;
     181                 :            : 
     182                 :          0 :                 *e = 0;
     183                 :            :         }
     184                 :            : 
     185         [ #  # ]:          0 :         if (!image_name_is_valid(name))
     186                 :          0 :                 return -EINVAL;
     187                 :            : 
     188                 :          0 :         *ret = TAKE_PTR(name);
     189                 :          0 :         return 0;
     190                 :            : }
     191                 :            : 
     192                 :          0 : static int image_make(
     193                 :            :                 const char *pretty,
     194                 :            :                 int dfd,
     195                 :            :                 const char *path,
     196                 :            :                 const char *filename,
     197                 :            :                 const struct stat *st,
     198                 :            :                 Image **ret) {
     199                 :            : 
     200                 :          0 :         _cleanup_free_ char *pretty_buffer = NULL, *parent = NULL;
     201                 :            :         struct stat stbuf;
     202                 :            :         bool read_only;
     203                 :            :         int r;
     204                 :            : 
     205   [ #  #  #  # ]:          0 :         assert(dfd >= 0 || dfd == AT_FDCWD);
     206   [ #  #  #  # ]:          0 :         assert(path || dfd == AT_FDCWD);
     207         [ #  # ]:          0 :         assert(filename);
     208                 :            : 
     209                 :            :         /* We explicitly *do* follow symlinks here, since we want to allow symlinking trees, raw files and block
     210                 :            :          * devices into /var/lib/machines/, and treat them normally.
     211                 :            :          *
     212                 :            :          * This function returns -ENOENT if we can't find the image after all, and -EMEDIUMTYPE if it's not a file we
     213                 :            :          * recognize. */
     214                 :            : 
     215         [ #  # ]:          0 :         if (!st) {
     216         [ #  # ]:          0 :                 if (fstatat(dfd, filename, &stbuf, 0) < 0)
     217                 :          0 :                         return -errno;
     218                 :            : 
     219                 :          0 :                 st = &stbuf;
     220                 :            :         }
     221                 :            : 
     222         [ #  # ]:          0 :         if (!path) {
     223         [ #  # ]:          0 :                 if (dfd == AT_FDCWD)
     224                 :          0 :                         (void) safe_getcwd(&parent);
     225                 :            :                 else
     226                 :          0 :                         (void) fd_get_path(dfd, &parent);
     227                 :            :         }
     228                 :            : 
     229                 :          0 :         read_only =
     230   [ #  #  #  #  :          0 :                 (path && path_startswith(path, "/usr")) ||
                   #  # ]
     231         [ #  # ]:          0 :                 (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);
     232                 :            : 
     233         [ #  # ]:          0 :         if (S_ISDIR(st->st_mode)) {
     234                 :          0 :                 _cleanup_close_ int fd = -1;
     235                 :          0 :                 unsigned file_attr = 0;
     236                 :            : 
     237         [ #  # ]:          0 :                 if (!ret)
     238                 :          0 :                         return 0;
     239                 :            : 
     240         [ #  # ]:          0 :                 if (!pretty) {
     241                 :          0 :                         r = extract_pretty(filename, NULL, &pretty_buffer);
     242         [ #  # ]:          0 :                         if (r < 0)
     243                 :          0 :                                 return r;
     244                 :            : 
     245                 :          0 :                         pretty = pretty_buffer;
     246                 :            :                 }
     247                 :            : 
     248                 :          0 :                 fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
     249         [ #  # ]:          0 :                 if (fd < 0)
     250                 :          0 :                         return -errno;
     251                 :            : 
     252                 :            :                 /* btrfs subvolumes have inode 256 */
     253         [ #  # ]:          0 :                 if (st->st_ino == 256) {
     254                 :            : 
     255                 :          0 :                         r = btrfs_is_filesystem(fd);
     256         [ #  # ]:          0 :                         if (r < 0)
     257                 :          0 :                                 return r;
     258         [ #  # ]:          0 :                         if (r) {
     259                 :            :                                 BtrfsSubvolInfo info;
     260                 :            : 
     261                 :            :                                 /* It's a btrfs subvolume */
     262                 :            : 
     263                 :          0 :                                 r = btrfs_subvol_get_info_fd(fd, 0, &info);
     264         [ #  # ]:          0 :                                 if (r < 0)
     265                 :          0 :                                         return r;
     266                 :            : 
     267                 :          0 :                                 r = image_new(IMAGE_SUBVOLUME,
     268                 :            :                                               pretty,
     269                 :            :                                               path,
     270                 :            :                                               filename,
     271   [ #  #  #  # ]:          0 :                                               info.read_only || read_only,
     272                 :            :                                               info.otime,
     273                 :            :                                               0,
     274                 :            :                                               ret);
     275         [ #  # ]:          0 :                                 if (r < 0)
     276                 :          0 :                                         return r;
     277                 :            : 
     278         [ #  # ]:          0 :                                 if (btrfs_quota_scan_ongoing(fd) == 0) {
     279                 :            :                                         BtrfsQuotaInfo quota;
     280                 :            : 
     281                 :          0 :                                         r = btrfs_subvol_get_subtree_quota_fd(fd, 0, &quota);
     282         [ #  # ]:          0 :                                         if (r >= 0) {
     283                 :          0 :                                                 (*ret)->usage = quota.referenced;
     284                 :          0 :                                                 (*ret)->usage_exclusive = quota.exclusive;
     285                 :            : 
     286                 :          0 :                                                 (*ret)->limit = quota.referenced_max;
     287                 :          0 :                                                 (*ret)->limit_exclusive = quota.exclusive_max;
     288                 :            :                                         }
     289                 :            :                                 }
     290                 :            : 
     291                 :          0 :                                 return 0;
     292                 :            :                         }
     293                 :            :                 }
     294                 :            : 
     295                 :            :                 /* If the IMMUTABLE bit is set, we consider the
     296                 :            :                  * directory read-only. Since the ioctl is not
     297                 :            :                  * supported everywhere we ignore failures. */
     298                 :          0 :                 (void) read_attr_fd(fd, &file_attr);
     299                 :            : 
     300                 :            :                 /* It's just a normal directory. */
     301                 :          0 :                 r = image_new(IMAGE_DIRECTORY,
     302                 :            :                               pretty,
     303                 :            :                               path,
     304                 :            :                               filename,
     305   [ #  #  #  # ]:          0 :                               read_only || (file_attr & FS_IMMUTABLE_FL),
     306                 :            :                               0,
     307                 :            :                               0,
     308                 :            :                               ret);
     309         [ #  # ]:          0 :                 if (r < 0)
     310                 :          0 :                         return r;
     311                 :            : 
     312                 :          0 :                 return 0;
     313                 :            : 
     314   [ #  #  #  # ]:          0 :         } else if (S_ISREG(st->st_mode) && endswith(filename, ".raw")) {
     315                 :          0 :                 usec_t crtime = 0;
     316                 :            : 
     317                 :            :                 /* It's a RAW disk image */
     318                 :            : 
     319         [ #  # ]:          0 :                 if (!ret)
     320                 :          0 :                         return 0;
     321                 :            : 
     322                 :          0 :                 (void) fd_getcrtime_at(dfd, filename, &crtime, 0);
     323                 :            : 
     324         [ #  # ]:          0 :                 if (!pretty) {
     325                 :          0 :                         r = extract_pretty(filename, ".raw", &pretty_buffer);
     326         [ #  # ]:          0 :                         if (r < 0)
     327                 :          0 :                                 return r;
     328                 :            : 
     329                 :          0 :                         pretty = pretty_buffer;
     330                 :            :                 }
     331                 :            : 
     332                 :          0 :                 r = image_new(IMAGE_RAW,
     333                 :            :                               pretty,
     334                 :            :                               path,
     335                 :            :                               filename,
     336   [ #  #  #  # ]:          0 :                               !(st->st_mode & 0222) || read_only,
     337                 :            :                               crtime,
     338                 :            :                               timespec_load(&st->st_mtim),
     339                 :            :                               ret);
     340         [ #  # ]:          0 :                 if (r < 0)
     341                 :          0 :                         return r;
     342                 :            : 
     343                 :          0 :                 (*ret)->usage = (*ret)->usage_exclusive = st->st_blocks * 512;
     344                 :          0 :                 (*ret)->limit = (*ret)->limit_exclusive = st->st_size;
     345                 :            : 
     346                 :          0 :                 return 0;
     347                 :            : 
     348         [ #  # ]:          0 :         } else if (S_ISBLK(st->st_mode)) {
     349                 :          0 :                 _cleanup_close_ int block_fd = -1;
     350                 :          0 :                 uint64_t size = UINT64_MAX;
     351                 :            : 
     352                 :            :                 /* A block device */
     353                 :            : 
     354         [ #  # ]:          0 :                 if (!ret)
     355                 :          0 :                         return 0;
     356                 :            : 
     357         [ #  # ]:          0 :                 if (!pretty) {
     358                 :          0 :                         r = extract_pretty(filename, NULL, &pretty_buffer);
     359         [ #  # ]:          0 :                         if (r < 0)
     360                 :          0 :                                 return r;
     361                 :            : 
     362                 :          0 :                         pretty = pretty_buffer;
     363                 :            :                 }
     364                 :            : 
     365                 :          0 :                 block_fd = openat(dfd, filename, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
     366         [ #  # ]:          0 :                 if (block_fd < 0)
     367   [ #  #  #  # ]:          0 :                         log_debug_errno(errno, "Failed to open block device %s/%s, ignoring: %m", path ?: strnull(parent), filename);
     368                 :            :                 else {
     369                 :            :                         /* Refresh stat data after opening the node */
     370         [ #  # ]:          0 :                         if (fstat(block_fd, &stbuf) < 0)
     371                 :          0 :                                 return -errno;
     372                 :          0 :                         st = &stbuf;
     373                 :            : 
     374         [ #  # ]:          0 :                         if (!S_ISBLK(st->st_mode)) /* Verify that what we opened is actually what we think it is */
     375                 :          0 :                                 return -ENOTTY;
     376                 :            : 
     377         [ #  # ]:          0 :                         if (!read_only) {
     378                 :          0 :                                 int state = 0;
     379                 :            : 
     380         [ #  # ]:          0 :                                 if (ioctl(block_fd, BLKROGET, &state) < 0)
     381   [ #  #  #  # ]:          0 :                                         log_debug_errno(errno, "Failed to issue BLKROGET on device %s/%s, ignoring: %m", path ?: strnull(parent), filename);
     382         [ #  # ]:          0 :                                 else if (state)
     383                 :          0 :                                         read_only = true;
     384                 :            :                         }
     385                 :            : 
     386         [ #  # ]:          0 :                         if (ioctl(block_fd, BLKGETSIZE64, &size) < 0)
     387   [ #  #  #  # ]:          0 :                                 log_debug_errno(errno, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path ?: strnull(parent), filename);
     388                 :            : 
     389                 :          0 :                         block_fd = safe_close(block_fd);
     390                 :            :                 }
     391                 :            : 
     392                 :          0 :                 r = image_new(IMAGE_BLOCK,
     393                 :            :                               pretty,
     394                 :            :                               path,
     395                 :            :                               filename,
     396   [ #  #  #  # ]:          0 :                               !(st->st_mode & 0222) || read_only,
     397                 :            :                               0,
     398                 :            :                               0,
     399                 :            :                               ret);
     400         [ #  # ]:          0 :                 if (r < 0)
     401                 :          0 :                         return r;
     402                 :            : 
     403   [ #  #  #  # ]:          0 :                 if (!IN_SET(size, 0, UINT64_MAX))
     404                 :          0 :                         (*ret)->usage = (*ret)->usage_exclusive = (*ret)->limit = (*ret)->limit_exclusive = size;
     405                 :            : 
     406                 :          0 :                 return 0;
     407                 :            :         }
     408                 :            : 
     409                 :          0 :         return -EMEDIUMTYPE;
     410                 :            : }
     411                 :            : 
     412                 :          0 : int image_find(ImageClass class, const char *name, Image **ret) {
     413                 :            :         const char *path;
     414                 :            :         int r;
     415                 :            : 
     416         [ #  # ]:          0 :         assert(class >= 0);
     417         [ #  # ]:          0 :         assert(class < _IMAGE_CLASS_MAX);
     418         [ #  # ]:          0 :         assert(name);
     419                 :            : 
     420                 :            :         /* There are no images with invalid names */
     421         [ #  # ]:          0 :         if (!image_name_is_valid(name))
     422                 :          0 :                 return -ENOENT;
     423                 :            : 
     424   [ #  #  #  # ]:          0 :         NULSTR_FOREACH(path, image_search_path[class]) {
     425         [ #  # ]:          0 :                 _cleanup_closedir_ DIR *d = NULL;
     426                 :            :                 struct stat st;
     427                 :            : 
     428                 :          0 :                 d = opendir(path);
     429         [ #  # ]:          0 :                 if (!d) {
     430         [ #  # ]:          0 :                         if (errno == ENOENT)
     431                 :          0 :                                 continue;
     432                 :            : 
     433                 :          0 :                         return -errno;
     434                 :            :                 }
     435                 :            : 
     436                 :            :                 /* As mentioned above, we follow symlinks on this fstatat(), because we want to permit people to
     437                 :            :                  * symlink block devices into the search path */
     438         [ #  # ]:          0 :                 if (fstatat(dirfd(d), name, &st, 0) < 0) {
     439      [ #  #  # ]:          0 :                         _cleanup_free_ char *raw = NULL;
     440                 :            : 
     441         [ #  # ]:          0 :                         if (errno != ENOENT)
     442                 :          0 :                                 return -errno;
     443                 :            : 
     444                 :          0 :                         raw = strjoin(name, ".raw");
     445         [ #  # ]:          0 :                         if (!raw)
     446                 :          0 :                                 return -ENOMEM;
     447                 :            : 
     448         [ #  # ]:          0 :                         if (fstatat(dirfd(d), raw, &st, 0) < 0) {
     449                 :            : 
     450         [ #  # ]:          0 :                                 if (errno == ENOENT)
     451                 :          0 :                                         continue;
     452                 :            : 
     453                 :          0 :                                 return -errno;
     454                 :            :                         }
     455                 :            : 
     456         [ #  # ]:          0 :                         if (!S_ISREG(st.st_mode))
     457                 :          0 :                                 continue;
     458                 :            : 
     459                 :          0 :                         r = image_make(name, dirfd(d), path, raw, &st, ret);
     460                 :            : 
     461                 :            :                 } else {
     462   [ #  #  #  # ]:          0 :                         if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode))
     463                 :          0 :                                 continue;
     464                 :            : 
     465                 :          0 :                         r = image_make(name, dirfd(d), path, name, &st, ret);
     466                 :            :                 }
     467   [ #  #  #  # ]:          0 :                 if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
     468                 :          0 :                         continue;
     469         [ #  # ]:          0 :                 if (r < 0)
     470                 :          0 :                         return r;
     471                 :            : 
     472         [ #  # ]:          0 :                 if (ret)
     473                 :          0 :                         (*ret)->discoverable = true;
     474                 :            : 
     475                 :          0 :                 return 1;
     476                 :            :         }
     477                 :            : 
     478   [ #  #  #  # ]:          0 :         if (class == IMAGE_MACHINE && streq(name, ".host")) {
     479                 :          0 :                 r = image_make(".host", AT_FDCWD, NULL, "/", NULL, ret);
     480         [ #  # ]:          0 :                 if (r < 0)
     481                 :          0 :                         return r;
     482                 :            : 
     483         [ #  # ]:          0 :                 if (ret)
     484                 :          0 :                         (*ret)->discoverable = true;
     485                 :            : 
     486                 :          0 :                 return r;
     487                 :            :         }
     488                 :            : 
     489                 :          0 :         return -ENOENT;
     490                 :            : };
     491                 :            : 
     492                 :          0 : int image_from_path(const char *path, Image **ret) {
     493                 :            : 
     494                 :            :         /* Note that we don't set the 'discoverable' field of the returned object, because we don't check here whether
     495                 :            :          * the image is in the image search path. And if it is we don't know if the path we used is actually not
     496                 :            :          * overridden by another, different image earlier in the search path */
     497                 :            : 
     498         [ #  # ]:          0 :         if (path_equal(path, "/"))
     499                 :          0 :                 return image_make(".host", AT_FDCWD, NULL, "/", NULL, ret);
     500                 :            : 
     501                 :          0 :         return image_make(NULL, AT_FDCWD, NULL, path, NULL, ret);
     502                 :            : }
     503                 :            : 
     504                 :          0 : int image_find_harder(ImageClass class, const char *name_or_path, Image **ret) {
     505         [ #  # ]:          0 :         if (image_name_is_valid(name_or_path))
     506                 :          0 :                 return image_find(class, name_or_path, ret);
     507                 :            : 
     508                 :          0 :         return image_from_path(name_or_path, ret);
     509                 :            : }
     510                 :            : 
     511                 :          0 : int image_discover(ImageClass class, Hashmap *h) {
     512                 :            :         const char *path;
     513                 :            :         int r;
     514                 :            : 
     515         [ #  # ]:          0 :         assert(class >= 0);
     516         [ #  # ]:          0 :         assert(class < _IMAGE_CLASS_MAX);
     517         [ #  # ]:          0 :         assert(h);
     518                 :            : 
     519   [ #  #  #  # ]:          0 :         NULSTR_FOREACH(path, image_search_path[class]) {
     520      [ #  #  # ]:          0 :                 _cleanup_closedir_ DIR *d = NULL;
     521                 :            :                 struct dirent *de;
     522                 :            : 
     523                 :          0 :                 d = opendir(path);
     524         [ #  # ]:          0 :                 if (!d) {
     525         [ #  # ]:          0 :                         if (errno == ENOENT)
     526                 :          0 :                                 continue;
     527                 :            : 
     528                 :          0 :                         return -errno;
     529                 :            :                 }
     530                 :            : 
     531   [ #  #  #  # ]:          0 :                 FOREACH_DIRENT_ALL(de, d, return -errno) {
     532      [ #  #  # ]:          0 :                         _cleanup_(image_unrefp) Image *image = NULL;
     533      [ #  #  # ]:          0 :                         _cleanup_free_ char *truncated = NULL;
     534                 :            :                         const char *pretty;
     535                 :            :                         struct stat st;
     536                 :            : 
     537         [ #  # ]:          0 :                         if (dot_or_dot_dot(de->d_name))
     538                 :          0 :                                 continue;
     539                 :            : 
     540                 :            :                         /* As mentioned above, we follow symlinks on this fstatat(), because we want to permit people
     541                 :            :                          * to symlink block devices into the search path */
     542         [ #  # ]:          0 :                         if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) {
     543         [ #  # ]:          0 :                                 if (errno == ENOENT)
     544                 :          0 :                                         continue;
     545                 :            : 
     546                 :          0 :                                 return -errno;
     547                 :            :                         }
     548                 :            : 
     549         [ #  # ]:          0 :                         if (S_ISREG(st.st_mode)) {
     550                 :            :                                 const char *e;
     551                 :            : 
     552                 :          0 :                                 e = endswith(de->d_name, ".raw");
     553         [ #  # ]:          0 :                                 if (!e)
     554                 :          0 :                                         continue;
     555                 :            : 
     556                 :          0 :                                 truncated = strndup(de->d_name, e - de->d_name);
     557         [ #  # ]:          0 :                                 if (!truncated)
     558                 :          0 :                                         return -ENOMEM;
     559                 :            : 
     560                 :          0 :                                 pretty = truncated;
     561   [ #  #  #  # ]:          0 :                         } else if (S_ISDIR(st.st_mode) || S_ISBLK(st.st_mode))
     562                 :          0 :                                 pretty = de->d_name;
     563                 :            :                         else
     564                 :          0 :                                 continue;
     565                 :            : 
     566         [ #  # ]:          0 :                         if (!image_name_is_valid(pretty))
     567                 :          0 :                                 continue;
     568                 :            : 
     569         [ #  # ]:          0 :                         if (hashmap_contains(h, pretty))
     570                 :          0 :                                 continue;
     571                 :            : 
     572                 :          0 :                         r = image_make(pretty, dirfd(d), path, de->d_name, &st, &image);
     573   [ #  #  #  # ]:          0 :                         if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
     574                 :          0 :                                 continue;
     575         [ #  # ]:          0 :                         if (r < 0)
     576                 :          0 :                                 return r;
     577                 :            : 
     578                 :          0 :                         image->discoverable = true;
     579                 :            : 
     580                 :          0 :                         r = hashmap_put(h, image->name, image);
     581         [ #  # ]:          0 :                         if (r < 0)
     582                 :          0 :                                 return r;
     583                 :            : 
     584                 :          0 :                         image = NULL;
     585                 :            :                 }
     586                 :            :         }
     587                 :            : 
     588   [ #  #  #  # ]:          0 :         if (class == IMAGE_MACHINE && !hashmap_contains(h, ".host")) {
     589         [ #  # ]:          0 :                 _cleanup_(image_unrefp) Image *image = NULL;
     590                 :            : 
     591                 :          0 :                 r = image_make(".host", AT_FDCWD, NULL, "/", NULL, &image);
     592         [ #  # ]:          0 :                 if (r < 0)
     593                 :          0 :                         return r;
     594                 :            : 
     595                 :          0 :                 image->discoverable = true;
     596                 :            : 
     597                 :          0 :                 r = hashmap_put(h, image->name, image);
     598         [ #  # ]:          0 :                 if (r < 0)
     599                 :          0 :                         return r;
     600                 :            : 
     601                 :          0 :                 image = NULL;
     602                 :            :         }
     603                 :            : 
     604                 :          0 :         return 0;
     605                 :            : }
     606                 :            : 
     607                 :          0 : int image_remove(Image *i) {
     608                 :          0 :         _cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
     609                 :          0 :         _cleanup_strv_free_ char **settings = NULL;
     610                 :          0 :         _cleanup_free_ char *roothash = NULL;
     611                 :            :         char **j;
     612                 :            :         int r;
     613                 :            : 
     614         [ #  # ]:          0 :         assert(i);
     615                 :            : 
     616   [ #  #  #  # ]:          0 :         if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
     617                 :          0 :                 return -EROFS;
     618                 :            : 
     619                 :          0 :         settings = image_settings_path(i);
     620         [ #  # ]:          0 :         if (!settings)
     621                 :          0 :                 return -ENOMEM;
     622                 :            : 
     623                 :          0 :         roothash = image_roothash_path(i);
     624         [ #  # ]:          0 :         if (!roothash)
     625                 :          0 :                 return -ENOMEM;
     626                 :            : 
     627                 :            :         /* Make sure we don't interfere with a running nspawn */
     628                 :          0 :         r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
     629         [ #  # ]:          0 :         if (r < 0)
     630                 :          0 :                 return r;
     631                 :            : 
     632   [ #  #  #  #  :          0 :         switch (i->type) {
                      # ]
     633                 :            : 
     634                 :          0 :         case IMAGE_SUBVOLUME:
     635                 :            : 
     636                 :            :                 /* Let's unlink first, maybe it is a symlink? If that works we are happy. Otherwise, let's get out the
     637                 :            :                  * big guns */
     638         [ #  # ]:          0 :                 if (unlink(i->path) < 0) {
     639                 :          0 :                         r = btrfs_subvol_remove(i->path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
     640         [ #  # ]:          0 :                         if (r < 0)
     641                 :          0 :                                 return r;
     642                 :            :                 }
     643                 :            : 
     644                 :          0 :                 break;
     645                 :            : 
     646                 :          0 :         case IMAGE_DIRECTORY:
     647                 :            :                 /* Allow deletion of read-only directories */
     648                 :          0 :                 (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL, NULL);
     649                 :          0 :                 r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
     650         [ #  # ]:          0 :                 if (r < 0)
     651                 :          0 :                         return r;
     652                 :            : 
     653                 :          0 :                 break;
     654                 :            : 
     655                 :          0 :         case IMAGE_BLOCK:
     656                 :            : 
     657                 :            :                 /* If this is inside of /dev, then it's a real block device, hence let's not touch the device node
     658                 :            :                  * itself (but let's remove the stuff stored alongside it). If it's anywhere else, let's try to unlink
     659                 :            :                  * the thing (it's most likely a symlink after all). */
     660                 :            : 
     661         [ #  # ]:          0 :                 if (path_startswith(i->path, "/dev"))
     662                 :          0 :                         break;
     663                 :            : 
     664                 :            :                 _fallthrough_;
     665                 :            :         case IMAGE_RAW:
     666         [ #  # ]:          0 :                 if (unlink(i->path) < 0)
     667                 :          0 :                         return -errno;
     668                 :          0 :                 break;
     669                 :            : 
     670                 :          0 :         default:
     671                 :          0 :                 return -EOPNOTSUPP;
     672                 :            :         }
     673                 :            : 
     674   [ #  #  #  # ]:          0 :         STRV_FOREACH(j, settings) {
     675   [ #  #  #  # ]:          0 :                 if (unlink(*j) < 0 && errno != ENOENT)
     676         [ #  # ]:          0 :                         log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j);
     677                 :            :         }
     678                 :            : 
     679   [ #  #  #  # ]:          0 :         if (unlink(roothash) < 0 && errno != ENOENT)
     680         [ #  # ]:          0 :                 log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", roothash);
     681                 :            : 
     682                 :          0 :         return 0;
     683                 :            : }
     684                 :            : 
     685                 :          0 : static int rename_auxiliary_file(const char *path, const char *new_name, const char *suffix) {
     686                 :          0 :         _cleanup_free_ char *rs = NULL;
     687                 :            :         const char *fn;
     688                 :            : 
     689   [ #  #  #  #  :          0 :         fn = strjoina(new_name, suffix);
          #  #  #  #  #  
                #  #  # ]
     690                 :            : 
     691                 :          0 :         rs = file_in_same_dir(path, fn);
     692         [ #  # ]:          0 :         if (!rs)
     693                 :          0 :                 return -ENOMEM;
     694                 :            : 
     695                 :          0 :         return rename_noreplace(AT_FDCWD, path, AT_FDCWD, rs);
     696                 :            : }
     697                 :            : 
     698                 :          0 : int image_rename(Image *i, const char *new_name) {
     699                 :          0 :         _cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT, name_lock = LOCK_FILE_INIT;
     700                 :          0 :         _cleanup_free_ char *new_path = NULL, *nn = NULL, *roothash = NULL;
     701                 :          0 :         _cleanup_strv_free_ char **settings = NULL;
     702                 :          0 :         unsigned file_attr = 0;
     703                 :            :         char **j;
     704                 :            :         int r;
     705                 :            : 
     706         [ #  # ]:          0 :         assert(i);
     707                 :            : 
     708         [ #  # ]:          0 :         if (!image_name_is_valid(new_name))
     709                 :          0 :                 return -EINVAL;
     710                 :            : 
     711   [ #  #  #  # ]:          0 :         if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
     712                 :          0 :                 return -EROFS;
     713                 :            : 
     714                 :          0 :         settings = image_settings_path(i);
     715         [ #  # ]:          0 :         if (!settings)
     716                 :          0 :                 return -ENOMEM;
     717                 :            : 
     718                 :          0 :         roothash = image_roothash_path(i);
     719         [ #  # ]:          0 :         if (!roothash)
     720                 :          0 :                 return -ENOMEM;
     721                 :            : 
     722                 :            :         /* Make sure we don't interfere with a running nspawn */
     723                 :          0 :         r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
     724         [ #  # ]:          0 :         if (r < 0)
     725                 :          0 :                 return r;
     726                 :            : 
     727                 :            :         /* Make sure nobody takes the new name, between the time we
     728                 :            :          * checked it is currently unused in all search paths, and the
     729                 :            :          * time we take possession of it */
     730                 :          0 :         r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock);
     731         [ #  # ]:          0 :         if (r < 0)
     732                 :          0 :                 return r;
     733                 :            : 
     734                 :          0 :         r = image_find(IMAGE_MACHINE, new_name, NULL);
     735         [ #  # ]:          0 :         if (r >= 0)
     736                 :          0 :                 return -EEXIST;
     737         [ #  # ]:          0 :         if (r != -ENOENT)
     738                 :          0 :                 return r;
     739                 :            : 
     740   [ #  #  #  #  :          0 :         switch (i->type) {
                      # ]
     741                 :            : 
     742                 :          0 :         case IMAGE_DIRECTORY:
     743                 :            :                 /* Turn of the immutable bit while we rename the image, so that we can rename it */
     744                 :          0 :                 (void) read_attr_path(i->path, &file_attr);
     745                 :            : 
     746         [ #  # ]:          0 :                 if (file_attr & FS_IMMUTABLE_FL)
     747                 :          0 :                         (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL, NULL);
     748                 :            : 
     749                 :            :                 _fallthrough_;
     750                 :            :         case IMAGE_SUBVOLUME:
     751                 :          0 :                 new_path = file_in_same_dir(i->path, new_name);
     752                 :          0 :                 break;
     753                 :            : 
     754                 :          0 :         case IMAGE_BLOCK:
     755                 :            : 
     756                 :            :                 /* Refuse renaming raw block devices in /dev, the names are picked by udev after all. */
     757         [ #  # ]:          0 :                 if (path_startswith(i->path, "/dev"))
     758                 :          0 :                         return -EROFS;
     759                 :            : 
     760                 :          0 :                 new_path = file_in_same_dir(i->path, new_name);
     761                 :          0 :                 break;
     762                 :            : 
     763                 :          0 :         case IMAGE_RAW: {
     764                 :            :                 const char *fn;
     765                 :            : 
     766   [ #  #  #  #  :          0 :                 fn = strjoina(new_name, ".raw");
          #  #  #  #  #  
                #  #  # ]
     767                 :          0 :                 new_path = file_in_same_dir(i->path, fn);
     768                 :          0 :                 break;
     769                 :            :         }
     770                 :            : 
     771                 :          0 :         default:
     772                 :          0 :                 return -EOPNOTSUPP;
     773                 :            :         }
     774                 :            : 
     775         [ #  # ]:          0 :         if (!new_path)
     776                 :          0 :                 return -ENOMEM;
     777                 :            : 
     778                 :          0 :         nn = strdup(new_name);
     779         [ #  # ]:          0 :         if (!nn)
     780                 :          0 :                 return -ENOMEM;
     781                 :            : 
     782                 :          0 :         r = rename_noreplace(AT_FDCWD, i->path, AT_FDCWD, new_path);
     783         [ #  # ]:          0 :         if (r < 0)
     784                 :          0 :                 return r;
     785                 :            : 
     786                 :            :         /* Restore the immutable bit, if it was set before */
     787         [ #  # ]:          0 :         if (file_attr & FS_IMMUTABLE_FL)
     788                 :          0 :                 (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
     789                 :            : 
     790                 :          0 :         free_and_replace(i->path, new_path);
     791                 :          0 :         free_and_replace(i->name, nn);
     792                 :            : 
     793   [ #  #  #  # ]:          0 :         STRV_FOREACH(j, settings) {
     794                 :          0 :                 r = rename_auxiliary_file(*j, new_name, ".nspawn");
     795   [ #  #  #  # ]:          0 :                 if (r < 0 && r != -ENOENT)
     796         [ #  # ]:          0 :                         log_debug_errno(r, "Failed to rename settings file %s, ignoring: %m", *j);
     797                 :            :         }
     798                 :            : 
     799                 :          0 :         r = rename_auxiliary_file(roothash, new_name, ".roothash");
     800   [ #  #  #  # ]:          0 :         if (r < 0 && r != -ENOENT)
     801         [ #  # ]:          0 :                 log_debug_errno(r, "Failed to rename roothash file %s, ignoring: %m", roothash);
     802                 :            : 
     803                 :          0 :         return 0;
     804                 :            : }
     805                 :            : 
     806                 :          0 : static int clone_auxiliary_file(const char *path, const char *new_name, const char *suffix) {
     807                 :          0 :         _cleanup_free_ char *rs = NULL;
     808                 :            :         const char *fn;
     809                 :            : 
     810   [ #  #  #  #  :          0 :         fn = strjoina(new_name, suffix);
          #  #  #  #  #  
                #  #  # ]
     811                 :            : 
     812                 :          0 :         rs = file_in_same_dir(path, fn);
     813         [ #  # ]:          0 :         if (!rs)
     814                 :          0 :                 return -ENOMEM;
     815                 :            : 
     816                 :          0 :         return copy_file_atomic(path, rs, 0664, 0, 0, COPY_REFLINK);
     817                 :            : }
     818                 :            : 
     819                 :          0 : int image_clone(Image *i, const char *new_name, bool read_only) {
     820                 :          0 :         _cleanup_(release_lock_file) LockFile name_lock = LOCK_FILE_INIT;
     821                 :          0 :         _cleanup_strv_free_ char **settings = NULL;
     822                 :          0 :         _cleanup_free_ char *roothash = NULL;
     823                 :            :         const char *new_path;
     824                 :            :         char **j;
     825                 :            :         int r;
     826                 :            : 
     827         [ #  # ]:          0 :         assert(i);
     828                 :            : 
     829         [ #  # ]:          0 :         if (!image_name_is_valid(new_name))
     830                 :          0 :                 return -EINVAL;
     831                 :            : 
     832                 :          0 :         settings = image_settings_path(i);
     833         [ #  # ]:          0 :         if (!settings)
     834                 :          0 :                 return -ENOMEM;
     835                 :            : 
     836                 :          0 :         roothash = image_roothash_path(i);
     837         [ #  # ]:          0 :         if (!roothash)
     838                 :          0 :                 return -ENOMEM;
     839                 :            : 
     840                 :            :         /* Make sure nobody takes the new name, between the time we
     841                 :            :          * checked it is currently unused in all search paths, and the
     842                 :            :          * time we take possession of it */
     843                 :          0 :         r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock);
     844         [ #  # ]:          0 :         if (r < 0)
     845                 :          0 :                 return r;
     846                 :            : 
     847                 :          0 :         r = image_find(IMAGE_MACHINE, new_name, NULL);
     848         [ #  # ]:          0 :         if (r >= 0)
     849                 :          0 :                 return -EEXIST;
     850         [ #  # ]:          0 :         if (r != -ENOENT)
     851                 :          0 :                 return r;
     852                 :            : 
     853      [ #  #  # ]:          0 :         switch (i->type) {
     854                 :            : 
     855                 :          0 :         case IMAGE_SUBVOLUME:
     856                 :            :         case IMAGE_DIRECTORY:
     857                 :            :                 /* If we can we'll always try to create a new btrfs subvolume here, even if the source is a plain
     858                 :            :                  * directory. */
     859                 :            : 
     860   [ #  #  #  #  :          0 :                 new_path = strjoina("/var/lib/machines/", new_name);
          #  #  #  #  #  
                #  #  # ]
     861                 :            : 
     862         [ #  # ]:          0 :                 r = btrfs_subvol_snapshot(i->path, new_path,
     863                 :            :                                           (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) |
     864                 :            :                                           BTRFS_SNAPSHOT_FALLBACK_COPY |
     865                 :            :                                           BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
     866                 :            :                                           BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE |
     867                 :            :                                           BTRFS_SNAPSHOT_RECURSIVE |
     868                 :            :                                           BTRFS_SNAPSHOT_QUOTA);
     869         [ #  # ]:          0 :                 if (r >= 0)
     870                 :            :                         /* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */
     871                 :          0 :                         (void) btrfs_subvol_auto_qgroup(new_path, 0, true);
     872                 :            : 
     873                 :          0 :                 break;
     874                 :            : 
     875                 :          0 :         case IMAGE_RAW:
     876   [ #  #  #  #  :          0 :                 new_path = strjoina("/var/lib/machines/", new_name, ".raw");
          #  #  #  #  #  
                #  #  # ]
     877                 :            : 
     878         [ #  # ]:          0 :                 r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, FS_NOCOW_FL, FS_NOCOW_FL, COPY_REFLINK|COPY_CRTIME);
     879                 :          0 :                 break;
     880                 :            : 
     881                 :          0 :         case IMAGE_BLOCK:
     882                 :            :         default:
     883                 :          0 :                 return -EOPNOTSUPP;
     884                 :            :         }
     885                 :            : 
     886         [ #  # ]:          0 :         if (r < 0)
     887                 :          0 :                 return r;
     888                 :            : 
     889   [ #  #  #  # ]:          0 :         STRV_FOREACH(j, settings) {
     890                 :          0 :                 r = clone_auxiliary_file(*j, new_name, ".nspawn");
     891   [ #  #  #  # ]:          0 :                 if (r < 0 && r != -ENOENT)
     892         [ #  # ]:          0 :                         log_debug_errno(r, "Failed to clone settings %s, ignoring: %m", *j);
     893                 :            :         }
     894                 :            : 
     895                 :          0 :         r = clone_auxiliary_file(roothash, new_name, ".roothash");
     896   [ #  #  #  # ]:          0 :         if (r < 0 && r != -ENOENT)
     897         [ #  # ]:          0 :                 log_debug_errno(r, "Failed to clone root hash file %s, ignoring: %m", roothash);
     898                 :            : 
     899                 :          0 :         return 0;
     900                 :            : }
     901                 :            : 
     902                 :          0 : int image_read_only(Image *i, bool b) {
     903                 :          0 :         _cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
     904                 :            :         int r;
     905                 :            : 
     906         [ #  # ]:          0 :         assert(i);
     907                 :            : 
     908   [ #  #  #  # ]:          0 :         if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
     909                 :          0 :                 return -EROFS;
     910                 :            : 
     911                 :            :         /* Make sure we don't interfere with a running nspawn */
     912                 :          0 :         r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
     913         [ #  # ]:          0 :         if (r < 0)
     914                 :          0 :                 return r;
     915                 :            : 
     916   [ #  #  #  #  :          0 :         switch (i->type) {
                      # ]
     917                 :            : 
     918                 :          0 :         case IMAGE_SUBVOLUME:
     919                 :            : 
     920                 :            :                 /* Note that we set the flag only on the top-level
     921                 :            :                  * subvolume of the image. */
     922                 :            : 
     923                 :          0 :                 r = btrfs_subvol_set_read_only(i->path, b);
     924         [ #  # ]:          0 :                 if (r < 0)
     925                 :          0 :                         return r;
     926                 :            : 
     927                 :          0 :                 break;
     928                 :            : 
     929                 :          0 :         case IMAGE_DIRECTORY:
     930                 :            :                 /* For simple directory trees we cannot use the access
     931                 :            :                    mode of the top-level directory, since it has an
     932                 :            :                    effect on the container itself.  However, we can
     933                 :            :                    use the "immutable" flag, to at least make the
     934                 :            :                    top-level directory read-only. It's not as good as
     935                 :            :                    a read-only subvolume, but at least something, and
     936                 :            :                    we can read the value back. */
     937                 :            : 
     938         [ #  # ]:          0 :                 r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL, NULL);
     939         [ #  # ]:          0 :                 if (r < 0)
     940                 :          0 :                         return r;
     941                 :            : 
     942                 :          0 :                 break;
     943                 :            : 
     944                 :          0 :         case IMAGE_RAW: {
     945                 :            :                 struct stat st;
     946                 :            : 
     947         [ #  # ]:          0 :                 if (stat(i->path, &st) < 0)
     948                 :          0 :                         return -errno;
     949                 :            : 
     950   [ #  #  #  # ]:          0 :                 if (chmod(i->path, (st.st_mode & 0444) | (b ? 0000 : 0200)) < 0)
     951                 :          0 :                         return -errno;
     952                 :            : 
     953                 :            :                 /* If the images is now read-only, it's a good time to
     954                 :            :                  * defrag it, given that no write patterns will
     955                 :            :                  * fragment it again. */
     956         [ #  # ]:          0 :                 if (b)
     957                 :          0 :                         (void) btrfs_defrag(i->path);
     958                 :          0 :                 break;
     959                 :            :         }
     960                 :            : 
     961                 :          0 :         case IMAGE_BLOCK: {
     962         [ #  # ]:          0 :                 _cleanup_close_ int fd = -1;
     963                 :            :                 struct stat st;
     964                 :          0 :                 int state = b;
     965                 :            : 
     966                 :          0 :                 fd = open(i->path, O_CLOEXEC|O_RDONLY|O_NONBLOCK|O_NOCTTY);
     967         [ #  # ]:          0 :                 if (fd < 0)
     968                 :          0 :                         return -errno;
     969                 :            : 
     970         [ #  # ]:          0 :                 if (fstat(fd, &st) < 0)
     971                 :          0 :                         return -errno;
     972         [ #  # ]:          0 :                 if (!S_ISBLK(st.st_mode))
     973                 :          0 :                         return -ENOTTY;
     974                 :            : 
     975         [ #  # ]:          0 :                 if (ioctl(fd, BLKROSET, &state) < 0)
     976                 :          0 :                         return -errno;
     977                 :            : 
     978                 :          0 :                 break;
     979                 :            :         }
     980                 :            : 
     981                 :          0 :         default:
     982                 :          0 :                 return -EOPNOTSUPP;
     983                 :            :         }
     984                 :            : 
     985                 :          0 :         return 0;
     986                 :            : }
     987                 :            : 
     988                 :          0 : int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local) {
     989                 :          0 :         _cleanup_free_ char *p = NULL;
     990                 :          0 :         LockFile t = LOCK_FILE_INIT;
     991                 :            :         struct stat st;
     992                 :            :         bool exclusive;
     993                 :            :         int r;
     994                 :            : 
     995         [ #  # ]:          0 :         assert(path);
     996         [ #  # ]:          0 :         assert(global);
     997         [ #  # ]:          0 :         assert(local);
     998                 :            : 
     999                 :            :         /* Locks an image path. This actually creates two locks: one "local" one, next to the image path
    1000                 :            :          * itself, which might be shared via NFS. And another "global" one, in /run, that uses the
    1001                 :            :          * device/inode number. This has the benefit that we can even lock a tree that is a mount point,
    1002                 :            :          * correctly. */
    1003                 :            : 
    1004         [ #  # ]:          0 :         if (!path_is_absolute(path))
    1005                 :          0 :                 return -EINVAL;
    1006                 :            : 
    1007      [ #  #  # ]:          0 :         switch (operation & (LOCK_SH|LOCK_EX)) {
    1008                 :          0 :         case LOCK_SH:
    1009                 :          0 :                 exclusive = false;
    1010                 :          0 :                 break;
    1011                 :          0 :         case LOCK_EX:
    1012                 :          0 :                 exclusive = true;
    1013                 :          0 :                 break;
    1014                 :          0 :         default:
    1015                 :          0 :                 return -EINVAL;
    1016                 :            :         }
    1017                 :            : 
    1018         [ #  # ]:          0 :         if (getenv_bool("SYSTEMD_NSPAWN_LOCK") == 0) {
    1019                 :          0 :                 *local = *global = (LockFile) LOCK_FILE_INIT;
    1020                 :          0 :                 return 0;
    1021                 :            :         }
    1022                 :            : 
    1023                 :            :         /* Prohibit taking exclusive locks on the host image. We can't allow this, since we ourselves are
    1024                 :            :          * running off it after all, and we don't want any images to manipulate the host image. We make an
    1025                 :            :          * exception for shared locks however: we allow those (and make them NOPs since there's no point in
    1026                 :            :          * taking them if there can't be exclusive locks). Strictly speaking these are questionable as well,
    1027                 :            :          * since it means changes made to the host might propagate to the container as they happen (and a
    1028                 :            :          * shared lock kinda suggests that no changes happen at all while it is in place), but it's too
    1029                 :            :          * useful not to allow read-only containers off the host root, hence let's support this, and trust
    1030                 :            :          * the user to do the right thing with this. */
    1031         [ #  # ]:          0 :         if (path_equal(path, "/")) {
    1032         [ #  # ]:          0 :                 if (exclusive)
    1033                 :          0 :                         return -EBUSY;
    1034                 :            : 
    1035                 :          0 :                 *local = *global = (LockFile) LOCK_FILE_INIT;
    1036                 :          0 :                 return 0;
    1037                 :            :         }
    1038                 :            : 
    1039         [ #  # ]:          0 :         if (stat(path, &st) >= 0) {
    1040         [ #  # ]:          0 :                 if (S_ISBLK(st.st_mode))
    1041                 :          0 :                         r = asprintf(&p, "/run/systemd/nspawn/locks/block-%u:%u", major(st.st_rdev), minor(st.st_rdev));
    1042   [ #  #  #  # ]:          0 :                 else if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))
    1043                 :          0 :                         r = asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino);
    1044                 :            :                 else
    1045                 :          0 :                         return -ENOTTY;
    1046                 :            : 
    1047         [ #  # ]:          0 :                 if (r < 0)
    1048                 :          0 :                         return -ENOMEM;
    1049                 :            :         }
    1050                 :            : 
    1051                 :            :         /* For block devices we don't need the "local" lock, as the major/minor lock above should be
    1052                 :            :          * sufficient, since block devices are host local anyway. */
    1053         [ #  # ]:          0 :         if (!path_startswith(path, "/dev/")) {
    1054                 :          0 :                 r = make_lock_file_for(path, operation, &t);
    1055         [ #  # ]:          0 :                 if (r < 0) {
    1056   [ #  #  #  # ]:          0 :                         if (!exclusive && r == -EROFS)
    1057         [ #  # ]:          0 :                                 log_debug_errno(r, "Failed to create shared lock for '%s', ignoring: %m", path);
    1058                 :            :                         else
    1059                 :          0 :                                 return r;
    1060                 :            :                 }
    1061                 :            :         }
    1062                 :            : 
    1063         [ #  # ]:          0 :         if (p) {
    1064                 :          0 :                 (void) mkdir_p("/run/systemd/nspawn/locks", 0700);
    1065                 :            : 
    1066                 :          0 :                 r = make_lock_file(p, operation, global);
    1067         [ #  # ]:          0 :                 if (r < 0) {
    1068                 :          0 :                         release_lock_file(&t);
    1069                 :          0 :                         return r;
    1070                 :            :                 }
    1071                 :            :         } else
    1072                 :          0 :                 *global = (LockFile) LOCK_FILE_INIT;
    1073                 :            : 
    1074                 :          0 :         *local = t;
    1075                 :          0 :         return 0;
    1076                 :            : }
    1077                 :            : 
    1078                 :          0 : int image_set_limit(Image *i, uint64_t referenced_max) {
    1079         [ #  # ]:          0 :         assert(i);
    1080                 :            : 
    1081   [ #  #  #  # ]:          0 :         if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
    1082                 :          0 :                 return -EROFS;
    1083                 :            : 
    1084         [ #  # ]:          0 :         if (i->type != IMAGE_SUBVOLUME)
    1085                 :          0 :                 return -EOPNOTSUPP;
    1086                 :            : 
    1087                 :            :         /* We set the quota both for the subvolume as well as for the
    1088                 :            :          * subtree. The latter is mostly for historical reasons, since
    1089                 :            :          * we didn't use to have a concept of subtree quota, and hence
    1090                 :            :          * only modified the subvolume quota. */
    1091                 :            : 
    1092                 :          0 :         (void) btrfs_qgroup_set_limit(i->path, 0, referenced_max);
    1093                 :          0 :         (void) btrfs_subvol_auto_qgroup(i->path, 0, true);
    1094                 :          0 :         return btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max);
    1095                 :            : }
    1096                 :            : 
    1097                 :          0 : int image_read_metadata(Image *i) {
    1098                 :          0 :         _cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
    1099                 :            :         int r;
    1100                 :            : 
    1101         [ #  # ]:          0 :         assert(i);
    1102                 :            : 
    1103                 :          0 :         r = image_path_lock(i->path, LOCK_SH|LOCK_NB, &global_lock, &local_lock);
    1104         [ #  # ]:          0 :         if (r < 0)
    1105                 :          0 :                 return r;
    1106                 :            : 
    1107      [ #  #  # ]:          0 :         switch (i->type) {
    1108                 :            : 
    1109                 :          0 :         case IMAGE_SUBVOLUME:
    1110                 :            :         case IMAGE_DIRECTORY: {
    1111                 :          0 :                 _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL;
    1112                 :          0 :                 sd_id128_t machine_id = SD_ID128_NULL;
    1113                 :          0 :                 _cleanup_free_ char *hostname = NULL;
    1114                 :          0 :                 _cleanup_free_ char *path = NULL;
    1115                 :            : 
    1116                 :          0 :                 r = chase_symlinks("/etc/hostname", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path);
    1117   [ #  #  #  # ]:          0 :                 if (r < 0 && r != -ENOENT)
    1118         [ #  # ]:          0 :                         log_debug_errno(r, "Failed to chase /etc/hostname in image %s: %m", i->name);
    1119         [ #  # ]:          0 :                 else if (r >= 0) {
    1120                 :          0 :                         r = read_etc_hostname(path, &hostname);
    1121         [ #  # ]:          0 :                         if (r < 0)
    1122         [ #  # ]:          0 :                                 log_debug_errno(errno, "Failed to read /etc/hostname of image %s: %m", i->name);
    1123                 :            :                 }
    1124                 :            : 
    1125                 :          0 :                 path = mfree(path);
    1126                 :            : 
    1127                 :          0 :                 r = chase_symlinks("/etc/machine-id", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path);
    1128   [ #  #  #  # ]:          0 :                 if (r < 0 && r != -ENOENT)
    1129         [ #  # ]:          0 :                         log_debug_errno(r, "Failed to chase /etc/machine-id in image %s: %m", i->name);
    1130         [ #  # ]:          0 :                 else if (r >= 0) {
    1131                 :          0 :                         _cleanup_close_ int fd = -1;
    1132                 :            : 
    1133                 :          0 :                         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
    1134         [ #  # ]:          0 :                         if (fd < 0)
    1135         [ #  # ]:          0 :                                 log_debug_errno(errno, "Failed to open %s: %m", path);
    1136                 :            :                         else {
    1137                 :          0 :                                 r = id128_read_fd(fd, ID128_PLAIN, &machine_id);
    1138         [ #  # ]:          0 :                                 if (r < 0)
    1139         [ #  # ]:          0 :                                         log_debug_errno(r, "Image %s contains invalid machine ID.", i->name);
    1140                 :            :                         }
    1141                 :            :                 }
    1142                 :            : 
    1143                 :          0 :                 path = mfree(path);
    1144                 :            : 
    1145                 :          0 :                 r = chase_symlinks("/etc/machine-info", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path);
    1146   [ #  #  #  # ]:          0 :                 if (r < 0 && r != -ENOENT)
    1147         [ #  # ]:          0 :                         log_debug_errno(r, "Failed to chase /etc/machine-info in image %s: %m", i->name);
    1148         [ #  # ]:          0 :                 else if (r >= 0) {
    1149                 :          0 :                         r = load_env_file_pairs(NULL, path, &machine_info);
    1150         [ #  # ]:          0 :                         if (r < 0)
    1151         [ #  # ]:          0 :                                 log_debug_errno(r, "Failed to parse machine-info data of %s: %m", i->name);
    1152                 :            :                 }
    1153                 :            : 
    1154                 :          0 :                 r = load_os_release_pairs(i->path, &os_release);
    1155         [ #  # ]:          0 :                 if (r < 0)
    1156         [ #  # ]:          0 :                         log_debug_errno(r, "Failed to read os-release in image, ignoring: %m");
    1157                 :            : 
    1158                 :          0 :                 free_and_replace(i->hostname, hostname);
    1159                 :          0 :                 i->machine_id = machine_id;
    1160                 :          0 :                 strv_free_and_replace(i->machine_info, machine_info);
    1161                 :          0 :                 strv_free_and_replace(i->os_release, os_release);
    1162                 :            : 
    1163                 :          0 :                 break;
    1164                 :            :         }
    1165                 :            : 
    1166                 :          0 :         case IMAGE_RAW:
    1167                 :            :         case IMAGE_BLOCK: {
    1168         [ #  # ]:          0 :                 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
    1169         [ #  # ]:          0 :                 _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
    1170                 :            : 
    1171                 :          0 :                 r = loop_device_make_by_path(i->path, O_RDONLY, &d);
    1172         [ #  # ]:          0 :                 if (r < 0)
    1173                 :          0 :                         return r;
    1174                 :            : 
    1175                 :          0 :                 r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m);
    1176         [ #  # ]:          0 :                 if (r < 0)
    1177                 :          0 :                         return r;
    1178                 :            : 
    1179                 :          0 :                 r = dissected_image_acquire_metadata(m);
    1180         [ #  # ]:          0 :                 if (r < 0)
    1181                 :          0 :                         return r;
    1182                 :            : 
    1183                 :          0 :                 free_and_replace(i->hostname, m->hostname);
    1184                 :          0 :                 i->machine_id = m->machine_id;
    1185                 :          0 :                 strv_free_and_replace(i->machine_info, m->machine_info);
    1186                 :          0 :                 strv_free_and_replace(i->os_release, m->os_release);
    1187                 :            : 
    1188                 :          0 :                 break;
    1189                 :            :         }
    1190                 :            : 
    1191                 :          0 :         default:
    1192                 :          0 :                 return -EOPNOTSUPP;
    1193                 :            :         }
    1194                 :            : 
    1195                 :          0 :         i->metadata_valid = true;
    1196                 :            : 
    1197                 :          0 :         return 0;
    1198                 :            : }
    1199                 :            : 
    1200                 :          0 : int image_name_lock(const char *name, int operation, LockFile *ret) {
    1201         [ #  # ]:          0 :         assert(name);
    1202         [ #  # ]:          0 :         assert(ret);
    1203                 :            : 
    1204                 :            :         /* Locks an image name, regardless of the precise path used. */
    1205                 :            : 
    1206         [ #  # ]:          0 :         if (!image_name_is_valid(name))
    1207                 :          0 :                 return -EINVAL;
    1208                 :            : 
    1209         [ #  # ]:          0 :         if (getenv_bool("SYSTEMD_NSPAWN_LOCK") == 0) {
    1210                 :          0 :                 *ret = (LockFile) LOCK_FILE_INIT;
    1211                 :          0 :                 return 0;
    1212                 :            :         }
    1213                 :            : 
    1214         [ #  # ]:          0 :         if (streq(name, ".host"))
    1215                 :          0 :                 return -EBUSY;
    1216                 :            : 
    1217   [ #  #  #  #  :          0 :         const char *p = strjoina("/run/systemd/nspawn/locks/name-", name);
          #  #  #  #  #  
                #  #  # ]
    1218                 :          0 :         (void) mkdir_p("/run/systemd/nspawn/locks", 0700);
    1219                 :          0 :         return make_lock_file(p, operation, ret);
    1220                 :            : }
    1221                 :            : 
    1222                 :          0 : bool image_name_is_valid(const char *s) {
    1223         [ #  # ]:          0 :         if (!filename_is_valid(s))
    1224                 :          0 :                 return false;
    1225                 :            : 
    1226         [ #  # ]:          0 :         if (string_has_cc(s, NULL))
    1227                 :          0 :                 return false;
    1228                 :            : 
    1229         [ #  # ]:          0 :         if (!utf8_is_valid(s))
    1230                 :          0 :                 return false;
    1231                 :            : 
    1232                 :            :         /* Temporary files for atomically creating new files */
    1233         [ #  # ]:          0 :         if (startswith(s, ".#"))
    1234                 :          0 :                 return false;
    1235                 :            : 
    1236                 :          0 :         return true;
    1237                 :            : }
    1238                 :            : 
    1239                 :          0 : bool image_in_search_path(ImageClass class, const char *image) {
    1240                 :            :         const char *path;
    1241                 :            : 
    1242         [ #  # ]:          0 :         assert(image);
    1243                 :            : 
    1244   [ #  #  #  # ]:          0 :         NULSTR_FOREACH(path, image_search_path[class]) {
    1245                 :            :                 const char *p;
    1246                 :            :                 size_t k;
    1247                 :            : 
    1248                 :          0 :                 p = path_startswith(image, path);
    1249         [ #  # ]:          0 :                 if (!p)
    1250                 :          0 :                         continue;
    1251                 :            : 
    1252                 :            :                 /* Make sure there's a filename following */
    1253                 :          0 :                 k = strcspn(p, "/");
    1254         [ #  # ]:          0 :                 if (k == 0)
    1255                 :          0 :                         continue;
    1256                 :            : 
    1257                 :          0 :                 p += k;
    1258                 :            : 
    1259                 :            :                 /* Accept trailing slashes */
    1260         [ #  # ]:          0 :                 if (p[strspn(p, "/")] == 0)
    1261                 :          0 :                         return true;
    1262                 :            : 
    1263                 :            :         }
    1264                 :            : 
    1265                 :          0 :         return false;
    1266                 :            : }
    1267                 :            : 
    1268                 :            : static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
    1269                 :            :         [IMAGE_DIRECTORY] = "directory",
    1270                 :            :         [IMAGE_SUBVOLUME] = "subvolume",
    1271                 :            :         [IMAGE_RAW] = "raw",
    1272                 :            :         [IMAGE_BLOCK] = "block",
    1273                 :            : };
    1274                 :            : 
    1275   [ +  +  +  + ]:         48 : DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);

Generated by: LCOV version 1.14