LCOV - code coverage report
Current view: top level - nspawn - nspawn-oci.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 959 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 69 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 1035 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <linux/oom.h>
       4                 :            : #if HAVE_SECCOMP
       5                 :            : #include <seccomp.h>
       6                 :            : #endif
       7                 :            : 
       8                 :            : #include "bus-util.h"
       9                 :            : #include "cap-list.h"
      10                 :            : #include "cpu-set-util.h"
      11                 :            : #include "env-util.h"
      12                 :            : #include "format-util.h"
      13                 :            : #include "fs-util.h"
      14                 :            : #include "hostname-util.h"
      15                 :            : #include "json.h"
      16                 :            : #include "missing_sched.h"
      17                 :            : #include "nspawn-oci.h"
      18                 :            : #include "path-util.h"
      19                 :            : #include "rlimit-util.h"
      20                 :            : #if HAVE_SECCOMP
      21                 :            : #include "seccomp-util.h"
      22                 :            : #endif
      23                 :            : #include "stat-util.h"
      24                 :            : #include "stdio-util.h"
      25                 :            : #include "string-util.h"
      26                 :            : #include "strv.h"
      27                 :            : #include "user-util.h"
      28                 :            : 
      29                 :            : /* TODO:
      30                 :            :  * OCI runtime tool implementation
      31                 :            :  * hooks
      32                 :            :  *
      33                 :            :  * Spec issues:
      34                 :            :  *
      35                 :            :  * How is RLIM_INFINITY supposed to be encoded?
      36                 :            :  * configured effective caps is bullshit, as execv() corrupts it anyway
      37                 :            :  * pipes bind mounted is *very* different from pipes newly created, comments regarding bind mount or not are bogus
      38                 :            :  * annotation values structured? or string?
      39                 :            :  * configurable file system namespace path, but then also root path? wtf?
      40                 :            :  * apply sysctl inside of the container? or outside?
      41                 :            :  * how is unlimited pids tasks limit to be encoded?
      42                 :            :  * what are the defaults for caps if not specified?
      43                 :            :  * what are the default uid/gid mappings if one is missing but the other set, or when user ns is on but no namespace configured
      44                 :            :  * the source field of "mounts" is really weird, as it cannot realistically be relative to the bundle, since we never know if that's what the fs wants
      45                 :            :  * spec contradicts itself on the mount "type" field, as the example uses "bind" as type, but it's not listed in /proc/filesystem, and is something made up by /bin/mount
      46                 :            :  * if type of mount is left out, what shall be assumed? "bind"?
      47                 :            :  * readonly mounts is entirely redundant?
      48                 :            :  * should escaping be applied when joining mount options with ","?
      49                 :            :  * devices cgroup support is bogus, "allow" and "deny" on the kernel level is about adding/removing entries, not about access
      50                 :            :  * spec needs to say that "rwm" devices cgroup combination can't be the empty string
      51                 :            :  * cgrouspv1 crap: kernel, kernelTCP, swapiness, disableOOMKiller, swap, devices, leafWeight
      52                 :            :  * general: it shouldn't leak lower level abstractions this obviously
      53                 :            :  * unmanagable cgroups stuff: realtimeRuntime/realtimePeriod
      54                 :            :  * needs to say what happense when some option is not specified, i.e. which defautls apply
      55                 :            :  * no architecture? no personality?
      56                 :            :  * seccomp example and logic is simply broken: there's no constant "SCMP_ACT_ERRNO".
      57                 :            :  * spec should say what to do with unknown props
      58                 :            :  * /bin/mount regarding NFS and FUSE required?
      59                 :            :  * what does terminal=false mean?
      60                 :            :  * sysctl inside or outside? whitelisting?
      61                 :            :  * swapiness typo -> swappiness
      62                 :            :  *
      63                 :            :  * Unsupported:
      64                 :            :  *
      65                 :            :  * apparmorProfile
      66                 :            :  * selinuxLabel + mountLabel
      67                 :            :  * hugepageLimits
      68                 :            :  * network
      69                 :            :  * rdma
      70                 :            :  * intelRdt
      71                 :            :  * swappiness, disableOOMKiller, kernel, kernelTCP, leafWeight (because it's dead, cgroupsv2 can't do it and hence systemd neither)
      72                 :            :  *
      73                 :            :  * Non-slice cgroup paths
      74                 :            :  * Propagation that is not slave + shared
      75                 :            :  * more than one uid/gid mapping, mappings with a container base != 0, or non-matching uid/gid mappings
      76                 :            :  * device cgroups access = false items that are not catchall
      77                 :            :  * device cgroups matches where minor is specified, but major isn't. similar where major is specified but char/block is not. also, any match that only has a type set that has less than "rwm" set. also, any entry that has none of rwm set.
      78                 :            :  *
      79                 :            :  */
      80                 :            : 
      81                 :          0 : static int oci_unexpected(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
      82         [ #  # ]:          0 :         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
      83                 :            :                         "Unexpected OCI element '%s' of type '%s'.", name, json_variant_type_to_string(json_variant_type(v)));
      84                 :            : }
      85                 :            : 
      86                 :          0 : static int oci_unsupported(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
      87         [ #  # ]:          0 :         return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
      88                 :            :                         "Unsupported OCI element '%s' of type '%s'.", name, json_variant_type_to_string(json_variant_type(v)));
      89                 :            : }
      90                 :            : 
      91                 :          0 : static int oci_terminal(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
      92                 :          0 :         Settings *s = userdata;
      93                 :            : 
      94                 :            :         /* If not specified, or set to true, we'll default to either an interactive or a read-only
      95                 :            :          * console. If specified as false, we'll forcibly move to "pipe" mode though. */
      96         [ #  # ]:          0 :         s->console_mode = json_variant_boolean(v) ? _CONSOLE_MODE_INVALID : CONSOLE_PIPE;
      97                 :          0 :         return 0;
      98                 :            : }
      99                 :            : 
     100                 :          0 : static int oci_console_dimension(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
     101                 :          0 :         unsigned *u = userdata;
     102                 :            :         uintmax_t k;
     103                 :            : 
     104         [ #  # ]:          0 :         assert(u);
     105                 :            : 
     106                 :          0 :         k = json_variant_unsigned(variant);
     107         [ #  # ]:          0 :         if (k == 0)
     108         [ #  # ]:          0 :                 return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
     109                 :            :                                 "Console size field '%s' is too small.", strna(name));
     110         [ #  # ]:          0 :         if (k > USHRT_MAX) /* TIOCSWINSZ's struct winsize uses "unsigned short" for width and height */
     111         [ #  # ]:          0 :                 return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
     112                 :            :                                 "Console size field '%s' is too large.", strna(name));
     113                 :            : 
     114                 :          0 :         *u = (unsigned) k;
     115                 :          0 :         return 0;
     116                 :            : }
     117                 :            : 
     118                 :          0 : static int oci_console_size(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     119                 :            : 
     120                 :            :         static const JsonDispatch table[] = {
     121                 :            :                 { "height", JSON_VARIANT_UNSIGNED, oci_console_dimension, offsetof(Settings, console_height), JSON_MANDATORY },
     122                 :            :                 { "width",  JSON_VARIANT_UNSIGNED, oci_console_dimension, offsetof(Settings, console_width),  JSON_MANDATORY },
     123                 :            :                 {}
     124                 :            :         };
     125                 :            : 
     126                 :          0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
     127                 :            : }
     128                 :            : 
     129                 :          0 : static int oci_absolute_path(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     130                 :          0 :         char **p = userdata;
     131                 :            :         const char *n;
     132                 :            : 
     133         [ #  # ]:          0 :         assert(p);
     134                 :            : 
     135                 :          0 :         n = json_variant_string(v);
     136                 :            : 
     137         [ #  # ]:          0 :         if (!path_is_absolute(n))
     138         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     139                 :            :                                 "Path in JSON field '%s' is not absolute: %s", strna(name), n);
     140                 :            : 
     141                 :          0 :         return free_and_strdup_warn(p, n);
     142                 :            : }
     143                 :            : 
     144                 :          0 : static int oci_env(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     145                 :          0 :         char ***l = userdata;
     146                 :            :         JsonVariant *e;
     147                 :            :         int r;
     148                 :            : 
     149         [ #  # ]:          0 :         assert(l);
     150                 :            : 
     151   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
     152                 :            :                 const char *n;
     153                 :            : 
     154         [ #  # ]:          0 :                 if (!json_variant_is_string(e))
     155         [ #  # ]:          0 :                         return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
     156                 :            :                                         "Environment array contains non-string.");
     157                 :            : 
     158         [ #  # ]:          0 :                 assert_se(n = json_variant_string(e));
     159                 :            : 
     160         [ #  # ]:          0 :                 if (!env_assignment_is_valid(n))
     161         [ #  # ]:          0 :                         return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
     162                 :            :                                         "Environment assignment not valid: %s", n);
     163                 :            : 
     164                 :          0 :                 r = strv_extend(l, n);
     165         [ #  # ]:          0 :                 if (r < 0)
     166                 :          0 :                         return log_oom();
     167                 :            :         }
     168                 :            : 
     169                 :          0 :         return 0;
     170                 :            : }
     171                 :            : 
     172                 :          0 : static int oci_args(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     173                 :          0 :         _cleanup_strv_free_ char **l = NULL;
     174                 :          0 :         char ***value = userdata;
     175                 :            :         JsonVariant *e;
     176                 :            :         int r;
     177                 :            : 
     178         [ #  # ]:          0 :         assert(value);
     179                 :            : 
     180   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
     181                 :            :                 const char *n;
     182                 :            : 
     183         [ #  # ]:          0 :                 if (!json_variant_is_string(e))
     184         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     185                 :            :                                         "Argument is not a string.");
     186                 :            : 
     187         [ #  # ]:          0 :                 assert_se(n = json_variant_string(e));
     188                 :            : 
     189                 :          0 :                 r = strv_extend(&l, n);
     190         [ #  # ]:          0 :                 if (r < 0)
     191                 :          0 :                         return log_oom();
     192                 :            :         }
     193                 :            : 
     194         [ #  # ]:          0 :         if (strv_isempty(l))
     195         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     196                 :            :                                 "Argument list empty, refusing.");
     197                 :            : 
     198         [ #  # ]:          0 :         if (isempty(l[0]))
     199         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     200                 :            :                                 "Executable name is empty, refusing.");
     201                 :            : 
     202                 :          0 :         return strv_free_and_replace(*value, l);
     203                 :            : }
     204                 :            : 
     205                 :          0 : static int oci_rlimit_type(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     206                 :            :         const char *z;
     207                 :          0 :         int t, *type = userdata;
     208                 :            : 
     209         [ #  # ]:          0 :         assert_se(type);
     210                 :            : 
     211                 :          0 :         z = startswith(json_variant_string(v), "RLIMIT_");
     212         [ #  # ]:          0 :         if (!z)
     213         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     214                 :            :                                 "rlimit entry's name does not begin with 'RLIMIT_', refusing: %s",
     215                 :            :                                 json_variant_string(v));
     216                 :            : 
     217                 :          0 :         t = rlimit_from_string(z);
     218         [ #  # ]:          0 :         if (t < 0)
     219         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     220                 :            :                                 "rlimit name unknown: %s", json_variant_string(v));
     221                 :            : 
     222                 :          0 :         *type = t;
     223                 :          0 :         return 0;
     224                 :            : }
     225                 :            : 
     226                 :          0 : static int oci_rlimit_value(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     227                 :          0 :         rlim_t z, *value = userdata;
     228                 :            : 
     229         [ #  # ]:          0 :         assert(value);
     230                 :            : 
     231         [ #  # ]:          0 :         if (json_variant_is_negative(v))
     232                 :          0 :                 z = RLIM_INFINITY;
     233                 :            :         else {
     234         [ #  # ]:          0 :                 if (!json_variant_is_unsigned(v))
     235         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
     236                 :            :                                         "rlimits limit not unsigned, refusing.");
     237                 :            : 
     238                 :          0 :                 z = (rlim_t) json_variant_unsigned(v);
     239                 :            : 
     240         [ #  # ]:          0 :                 if ((uintmax_t) z != json_variant_unsigned(v))
     241         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     242                 :            :                                         "rlimits limit out of range, refusing.");
     243                 :            :         }
     244                 :            : 
     245                 :          0 :         *value = z;
     246                 :          0 :         return 0;
     247                 :            : }
     248                 :            : 
     249                 :          0 : static int oci_rlimits(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     250                 :            : 
     251                 :          0 :         Settings *s = userdata;
     252                 :            :         JsonVariant *e;
     253                 :            :         int r;
     254                 :            : 
     255         [ #  # ]:          0 :         assert(s);
     256                 :            : 
     257   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
     258                 :            : 
     259                 :            :                 struct rlimit_data {
     260                 :            :                         int type;
     261                 :            :                         rlim_t soft;
     262                 :            :                         rlim_t hard;
     263                 :          0 :                 } data = {
     264                 :            :                         .type = -1,
     265                 :            :                         .soft = RLIM_INFINITY,
     266                 :            :                         .hard = RLIM_INFINITY,
     267                 :            :                 };
     268                 :            : 
     269                 :            :                 static const JsonDispatch table[] = {
     270                 :            :                         { "soft", JSON_VARIANT_NUMBER, oci_rlimit_value, offsetof(struct rlimit_data, soft), JSON_MANDATORY },
     271                 :            :                         { "hard", JSON_VARIANT_NUMBER, oci_rlimit_value, offsetof(struct rlimit_data, hard), JSON_MANDATORY },
     272                 :            :                         { "type", JSON_VARIANT_STRING, oci_rlimit_type,  offsetof(struct rlimit_data, type), JSON_MANDATORY },
     273                 :            :                         {}
     274                 :            :                 };
     275                 :            : 
     276                 :          0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &data);
     277         [ #  # ]:          0 :                 if (r < 0)
     278                 :          0 :                         return r;
     279                 :            : 
     280         [ #  # ]:          0 :                 assert(data.type >= 0);
     281         [ #  # ]:          0 :                 assert(data.type < _RLIMIT_MAX);
     282                 :            : 
     283         [ #  # ]:          0 :                 if (s->rlimit[data.type])
     284         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     285                 :            :                                         "rlimits array contains duplicate entry, refusing.");
     286                 :            : 
     287                 :          0 :                 s->rlimit[data.type] = new(struct rlimit, 1);
     288         [ #  # ]:          0 :                 if (!s->rlimit[data.type])
     289                 :          0 :                         return log_oom();
     290                 :            : 
     291                 :          0 :                 *s->rlimit[data.type] = (struct rlimit) {
     292                 :          0 :                         .rlim_cur = data.soft,
     293                 :          0 :                         .rlim_max = data.hard,
     294                 :            :                 };
     295                 :            : 
     296                 :            :         }
     297                 :          0 :         return 0;
     298                 :            : }
     299                 :            : 
     300                 :          0 : static int oci_capability_array(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     301                 :          0 :         uint64_t *mask = userdata, m = 0;
     302                 :            :         JsonVariant *e;
     303                 :            : 
     304   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
     305                 :            :                 const char *n;
     306                 :            :                 int cap;
     307                 :            : 
     308         [ #  # ]:          0 :                 if (!json_variant_is_string(e))
     309         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     310                 :            :                                         "Entry in capabilities array is not a string.");
     311                 :            : 
     312         [ #  # ]:          0 :                 assert_se(n = json_variant_string(e));
     313                 :            : 
     314                 :          0 :                 cap = capability_from_name(n);
     315         [ #  # ]:          0 :                 if (cap < 0)
     316         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     317                 :            :                                         "Unknown capability: %s", n);
     318                 :            : 
     319                 :          0 :                 m |= UINT64_C(1) << cap;
     320                 :            :         }
     321                 :            : 
     322         [ #  # ]:          0 :         if (*mask == (uint64_t) -1)
     323                 :          0 :                 *mask = m;
     324                 :            :         else
     325                 :          0 :                 *mask |= m;
     326                 :            : 
     327                 :          0 :         return 0;
     328                 :            : }
     329                 :            : 
     330                 :          0 : static int oci_capabilities(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     331                 :            : 
     332                 :            :         static const JsonDispatch table[] = {
     333                 :            :                 { "effective",   JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, effective)   },
     334                 :            :                 { "bounding",    JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, bounding)    },
     335                 :            :                 { "inheritable", JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, inheritable) },
     336                 :            :                 { "permitted",   JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, permitted)   },
     337                 :            :                 { "ambient",     JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, ambient)     },
     338                 :            :                 {}
     339                 :            :         };
     340                 :            : 
     341                 :          0 :         Settings *s = userdata;
     342                 :            :         int r;
     343                 :            : 
     344         [ #  # ]:          0 :         assert(s);
     345                 :            : 
     346                 :          0 :         r = json_dispatch(v, table, oci_unexpected, flags, &s->full_capabilities);
     347         [ #  # ]:          0 :         if (r < 0)
     348                 :          0 :                 return r;
     349                 :            : 
     350         [ #  # ]:          0 :         if (s->full_capabilities.bounding != (uint64_t) -1) {
     351                 :          0 :                 s->capability = s->full_capabilities.bounding;
     352                 :          0 :                 s->drop_capability = ~s->full_capabilities.bounding;
     353                 :            :         }
     354                 :            : 
     355                 :          0 :         return 0;
     356                 :            : }
     357                 :            : 
     358                 :          0 : static int oci_oom_score_adj(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     359                 :          0 :         Settings *s = userdata;
     360                 :            :         intmax_t k;
     361                 :            : 
     362         [ #  # ]:          0 :         assert(s);
     363                 :            : 
     364                 :          0 :         k = json_variant_integer(v);
     365   [ #  #  #  # ]:          0 :         if (k < OOM_SCORE_ADJ_MIN || k > OOM_SCORE_ADJ_MAX)
     366         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     367                 :            :                                 "oomScoreAdj value out of range: %ji", k);
     368                 :            : 
     369                 :          0 :         s->oom_score_adjust = (int) k;
     370                 :          0 :         s->oom_score_adjust_set = true;
     371                 :            : 
     372                 :          0 :         return 0;
     373                 :            : }
     374                 :            : 
     375                 :          0 : static int oci_uid_gid(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     376                 :          0 :         uid_t *uid = userdata, u;
     377                 :            :         uintmax_t k;
     378                 :            : 
     379         [ #  # ]:          0 :         assert(uid);
     380                 :            :         assert_cc(sizeof(uid_t) == sizeof(gid_t));
     381                 :            : 
     382                 :          0 :         k = json_variant_unsigned(v);
     383                 :          0 :         u = (uid_t) k;
     384         [ #  # ]:          0 :         if ((uintmax_t) u != k)
     385         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     386                 :            :                                 "UID/GID out of range: %ji", k);
     387                 :            : 
     388         [ #  # ]:          0 :         if (!uid_is_valid(u))
     389         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     390                 :            :                                 "UID/GID is not valid: " UID_FMT, u);
     391                 :            : 
     392                 :          0 :         *uid = u;
     393                 :          0 :         return 0;
     394                 :            : }
     395                 :            : 
     396                 :          0 : static int oci_supplementary_gids(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     397                 :          0 :         Settings *s = userdata;
     398                 :            :         JsonVariant *e;
     399                 :            :         int r;
     400                 :            : 
     401         [ #  # ]:          0 :         assert(s);
     402                 :            : 
     403   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
     404                 :            :                 gid_t gid, *a;
     405                 :            : 
     406         [ #  # ]:          0 :                 if (!json_variant_is_unsigned(e))
     407         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     408                 :            :                                         "Supplementary GID entry is not a UID.");
     409                 :            : 
     410                 :          0 :                 r = oci_uid_gid(name, e, flags, &gid);
     411         [ #  # ]:          0 :                 if (r < 0)
     412                 :          0 :                         return r;
     413                 :            : 
     414                 :          0 :                 a = reallocarray(s->supplementary_gids, s->n_supplementary_gids + 1, sizeof(gid_t));
     415         [ #  # ]:          0 :                 if (!a)
     416                 :          0 :                         return log_oom();
     417                 :            : 
     418                 :          0 :                 s->supplementary_gids = a;
     419                 :          0 :                 s->supplementary_gids[s->n_supplementary_gids++] = gid;
     420                 :            :         }
     421                 :            : 
     422                 :          0 :         return 0;
     423                 :            : }
     424                 :            : 
     425                 :          0 : static int oci_user(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     426                 :            :         static const JsonDispatch table[] = {
     427                 :            :                 { "uid",            JSON_VARIANT_UNSIGNED, oci_uid_gid,            offsetof(Settings, uid), JSON_MANDATORY },
     428                 :            :                 { "gid",            JSON_VARIANT_UNSIGNED, oci_uid_gid,            offsetof(Settings, gid), JSON_MANDATORY },
     429                 :            :                 { "additionalGids", JSON_VARIANT_ARRAY,    oci_supplementary_gids, 0,                       0              },
     430                 :            :                 {}
     431                 :            :         };
     432                 :            : 
     433                 :          0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
     434                 :            : }
     435                 :            : 
     436                 :          0 : static int oci_process(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     437                 :            : 
     438                 :            :         static const JsonDispatch table[] = {
     439                 :            :                 { "terminal",        JSON_VARIANT_BOOLEAN, oci_terminal,          0,                                     0               },
     440                 :            :                 { "consoleSize",     JSON_VARIANT_OBJECT,  oci_console_size,      0,                                     0               },
     441                 :            :                 { "cwd",             JSON_VARIANT_STRING,  oci_absolute_path,     offsetof(Settings, working_directory), 0               },
     442                 :            :                 { "env",             JSON_VARIANT_ARRAY,   oci_env,               offsetof(Settings, environment),       0               },
     443                 :            :                 { "args",            JSON_VARIANT_ARRAY,   oci_args,              offsetof(Settings, parameters),        0               },
     444                 :            :                 { "rlimits",         JSON_VARIANT_ARRAY,   oci_rlimits,           0,                                     0               },
     445                 :            :                 { "apparmorProfile", JSON_VARIANT_STRING,  oci_unsupported,       0,                                     JSON_PERMISSIVE },
     446                 :            :                 { "capabilities",    JSON_VARIANT_OBJECT,  oci_capabilities,      0,                                     0               },
     447                 :            :                 { "noNewPrivileges", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(Settings, no_new_privileges), 0               },
     448                 :            :                 { "oomScoreAdj",     JSON_VARIANT_INTEGER, oci_oom_score_adj,     0,                                     0               },
     449                 :            :                 { "selinuxLabel",    JSON_VARIANT_STRING,  oci_unsupported,       0,                                     JSON_PERMISSIVE },
     450                 :            :                 { "user",            JSON_VARIANT_OBJECT,  oci_user,              0,                                     0               },
     451                 :            :                 {}
     452                 :            :         };
     453                 :            : 
     454                 :          0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
     455                 :            : }
     456                 :            : 
     457                 :          0 : static int oci_root(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     458                 :            : 
     459                 :            :         static const JsonDispatch table[] = {
     460                 :            :                 { "path",     JSON_VARIANT_STRING,  json_dispatch_string,  offsetof(Settings, root)      },
     461                 :            :                 { "readonly", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(Settings, read_only) },
     462                 :            :                 {}
     463                 :            :         };
     464                 :            : 
     465                 :          0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
     466                 :            : }
     467                 :            : 
     468                 :          0 : static int oci_hostname(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     469                 :          0 :         Settings *s = userdata;
     470                 :            :         const char *n;
     471                 :            : 
     472         [ #  # ]:          0 :         assert(s);
     473                 :            : 
     474         [ #  # ]:          0 :         assert_se(n = json_variant_string(v));
     475                 :            : 
     476         [ #  # ]:          0 :         if (!hostname_is_valid(n, false))
     477         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     478                 :            :                                 "Hostname string is not a valid hostname: %s", n);
     479                 :            : 
     480                 :          0 :         return free_and_strdup_warn(&s->hostname, n);
     481                 :            : }
     482                 :            : 
     483                 :          0 : static bool oci_exclude_mount(const char *path) {
     484                 :            : 
     485                 :            :         /* Returns "true" for all mounts we insist to mount on our own, and hence ignore the OCI data. */
     486                 :            : 
     487   [ #  #  #  #  :          0 :         if (PATH_IN_SET(path,
             #  #  #  # ]
     488                 :            :                         "/dev",
     489                 :            :                         "/dev/mqueue",
     490                 :            :                         "/dev/pts",
     491                 :            :                         "/dev/shm",
     492                 :            :                         "/proc",
     493                 :            :                         "/proc/acpi",
     494                 :            :                         "/proc/apm",
     495                 :            :                         "/proc/asound",
     496                 :            :                         "/proc/bus",
     497                 :            :                         "/proc/fs",
     498                 :            :                         "/proc/irq",
     499                 :            :                         "/proc/kallsyms",
     500                 :            :                         "/proc/kcore",
     501                 :            :                         "/proc/keys",
     502                 :            :                         "/proc/scsi",
     503                 :            :                         "/proc/sys",
     504                 :            :                         "/proc/sys/net",
     505                 :            :                         "/proc/sysrq-trigger",
     506                 :            :                         "/proc/timer_list",
     507                 :            :                         "/run",
     508                 :            :                         "/sys",
     509                 :            :                         "/sys",
     510                 :            :                         "/sys/fs/selinux",
     511                 :            :                         "/tmp"))
     512                 :          0 :                 return true;
     513                 :            : 
     514                 :            :         /* Similar, skip the whole /sys/fs/cgroups subtree */
     515         [ #  # ]:          0 :         if (path_startswith(path, "/sys/fs/cgroup"))
     516                 :          0 :                 return true;
     517                 :            : 
     518                 :          0 :         return false;
     519                 :            : }
     520                 :            : 
     521                 :            : typedef struct oci_mount_data {
     522                 :            :         char *destination;
     523                 :            :         char *source;
     524                 :            :         char *type;
     525                 :            :         char **options;
     526                 :            : } oci_mount_data;
     527                 :            : 
     528                 :          0 : static void cleanup_oci_mount_data(oci_mount_data *data) {
     529                 :          0 :         free(data->destination);
     530                 :          0 :         free(data->source);
     531                 :          0 :         strv_free(data->options);
     532                 :          0 :         free(data->type);
     533                 :          0 : }
     534                 :            : 
     535                 :          0 : static int oci_mounts(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     536                 :          0 :         Settings *s = userdata;
     537                 :            :         JsonVariant *e;
     538                 :            :         int r;
     539                 :            : 
     540         [ #  # ]:          0 :         assert(s);
     541                 :            : 
     542   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
     543                 :            :                 static const JsonDispatch table[] = {
     544                 :            :                         { "destination", JSON_VARIANT_STRING, oci_absolute_path,    offsetof(oci_mount_data, destination), JSON_MANDATORY },
     545                 :            :                         { "source",      JSON_VARIANT_STRING, json_dispatch_string, offsetof(oci_mount_data, source),      0              },
     546                 :            :                         { "options",     JSON_VARIANT_ARRAY,  json_dispatch_strv,   offsetof(oci_mount_data, options),     0,             },
     547                 :            :                         { "type",        JSON_VARIANT_STRING, json_dispatch_string, offsetof(oci_mount_data, type),        0              },
     548                 :            :                         {}
     549                 :            :                 };
     550                 :            : 
     551      [ #  #  # ]:          0 :                 _cleanup_free_ char *joined_options = NULL;
     552                 :            :                 CustomMount *m;
     553      [ #  #  # ]:          0 :                 _cleanup_(cleanup_oci_mount_data) oci_mount_data data = {};
     554                 :            : 
     555                 :          0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &data);
     556         [ #  # ]:          0 :                 if (r < 0)
     557                 :          0 :                         return r;
     558                 :            : 
     559         [ #  # ]:          0 :                 if (!path_is_absolute(data.destination))
     560         [ #  # ]:          0 :                         return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
     561                 :            :                                         "Mount destination not an absolute path: %s", data.destination);
     562                 :            : 
     563         [ #  # ]:          0 :                 if (oci_exclude_mount(data.destination))
     564                 :          0 :                         continue;
     565                 :            : 
     566         [ #  # ]:          0 :                 if (data.options) {
     567                 :          0 :                         joined_options = strv_join(data.options, ",");
     568         [ #  # ]:          0 :                         if (!joined_options)
     569                 :          0 :                                 return log_oom();
     570                 :            :                 }
     571                 :            : 
     572   [ #  #  #  # ]:          0 :                 if (!data.type || streq(data.type, "bind")) {
     573   [ #  #  #  # ]:          0 :                         if (data.source && !path_is_absolute(data.source)) {
     574                 :            :                                 char *joined;
     575                 :            : 
     576                 :          0 :                                 joined = path_join(s->bundle, data.source);
     577         [ #  # ]:          0 :                                 if (!joined)
     578                 :          0 :                                         return log_oom();
     579                 :            : 
     580                 :          0 :                                 free_and_replace(data.source, joined);
     581                 :            :                         }
     582                 :            : 
     583                 :          0 :                         data.type = mfree(data.type);
     584                 :            : 
     585                 :          0 :                         m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_BIND);
     586                 :            :                 } else
     587                 :          0 :                         m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_ARBITRARY);
     588         [ #  # ]:          0 :                 if (!m)
     589                 :          0 :                         return log_oom();
     590                 :            : 
     591                 :          0 :                 m->destination = TAKE_PTR(data.destination);
     592                 :          0 :                 m->source = TAKE_PTR(data.source);
     593                 :          0 :                 m->options = TAKE_PTR(joined_options);
     594                 :          0 :                 m->type_argument = TAKE_PTR(data.type);
     595                 :            :         }
     596                 :            : 
     597                 :          0 :         return 0;
     598                 :            : }
     599                 :            : 
     600                 :          0 : static int oci_namespace_type(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     601                 :          0 :         unsigned long *nsflags = userdata;
     602                 :            :         const char *n;
     603                 :            : 
     604         [ #  # ]:          0 :         assert(nsflags);
     605         [ #  # ]:          0 :         assert_se(n = json_variant_string(v));
     606                 :            : 
     607                 :            :         /* We don't use namespace_flags_from_string() here, as the OCI spec uses slightly different names than the
     608                 :            :          * kernel here. */
     609         [ #  # ]:          0 :         if (streq(n, "pid"))
     610                 :          0 :                 *nsflags = CLONE_NEWPID;
     611         [ #  # ]:          0 :         else if (streq(n, "network"))
     612                 :          0 :                 *nsflags = CLONE_NEWNET;
     613         [ #  # ]:          0 :         else if (streq(n, "mount"))
     614                 :          0 :                 *nsflags = CLONE_NEWNS;
     615         [ #  # ]:          0 :         else if (streq(n, "ipc"))
     616                 :          0 :                 *nsflags = CLONE_NEWIPC;
     617         [ #  # ]:          0 :         else if (streq(n, "uts"))
     618                 :          0 :                 *nsflags = CLONE_NEWUTS;
     619         [ #  # ]:          0 :         else if (streq(n, "user"))
     620                 :          0 :                 *nsflags = CLONE_NEWUSER;
     621         [ #  # ]:          0 :         else if (streq(n, "cgroup"))
     622                 :          0 :                 *nsflags = CLONE_NEWCGROUP;
     623                 :            :         else
     624         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     625                 :            :                                 "Unknown cgroup type, refusing: %s", n);
     626                 :            : 
     627                 :          0 :         return 0;
     628                 :            : }
     629                 :            : 
     630                 :          0 : static int oci_namespaces(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     631                 :          0 :         Settings *s = userdata;
     632                 :          0 :         unsigned long n = 0;
     633                 :            :         JsonVariant *e;
     634                 :            :         int r;
     635                 :            : 
     636         [ #  # ]:          0 :         assert_se(s);
     637                 :            : 
     638   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
     639                 :            : 
     640                 :            :                 struct namespace_data {
     641                 :            :                         unsigned long type;
     642                 :            :                         char *path;
     643                 :          0 :                 } data = {};
     644                 :            : 
     645                 :            :                 static const JsonDispatch table[] = {
     646                 :            :                         { "type", JSON_VARIANT_STRING, oci_namespace_type, offsetof(struct namespace_data, type), JSON_MANDATORY },
     647                 :            :                         { "path", JSON_VARIANT_STRING, oci_absolute_path,  offsetof(struct namespace_data, path), 0              },
     648                 :            :                         {}
     649                 :            :                 };
     650                 :            : 
     651                 :          0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &data);
     652         [ #  # ]:          0 :                 if (r < 0) {
     653                 :          0 :                         free(data.path);
     654                 :          0 :                         return r;
     655                 :            :                 }
     656                 :            : 
     657         [ #  # ]:          0 :                 if (data.path) {
     658         [ #  # ]:          0 :                         if (data.type != CLONE_NEWNET) {
     659                 :          0 :                                 free(data.path);
     660         [ #  # ]:          0 :                                 return json_log(e, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
     661                 :            :                                                 "Specifying namespace path for non-network namespace is not supported.");
     662                 :            :                         }
     663                 :            : 
     664         [ #  # ]:          0 :                         if (s->network_namespace_path) {
     665                 :          0 :                                 free(data.path);
     666         [ #  # ]:          0 :                                 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
     667                 :            :                                                 "Network namespace path specified more than once, refusing.");
     668                 :            :                         }
     669                 :            : 
     670                 :          0 :                         free(s->network_namespace_path);
     671                 :          0 :                         s->network_namespace_path = data.path;
     672                 :            :                 }
     673                 :            : 
     674         [ #  # ]:          0 :                 if (FLAGS_SET(n, data.type)) {
     675         [ #  # ]:          0 :                         return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
     676                 :            :                                         "Duplicate namespace specification, refusing.");
     677                 :            :                 }
     678                 :            : 
     679                 :          0 :                 n |= data.type;
     680                 :            :         }
     681                 :            : 
     682         [ #  # ]:          0 :         if (!FLAGS_SET(n, CLONE_NEWNS))
     683         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
     684                 :            :                                 "Containers without file system namespace aren't supported.");
     685                 :            : 
     686                 :          0 :         s->private_network = FLAGS_SET(n, CLONE_NEWNET);
     687                 :          0 :         s->userns_mode = FLAGS_SET(n, CLONE_NEWUSER) ? USER_NAMESPACE_FIXED : USER_NAMESPACE_NO;
     688                 :          0 :         s->use_cgns = FLAGS_SET(n, CLONE_NEWCGROUP);
     689                 :            : 
     690                 :          0 :         s->clone_ns_flags = n & (CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS);
     691                 :            : 
     692                 :          0 :         return 0;
     693                 :            : }
     694                 :            : 
     695                 :          0 : static int oci_uid_gid_range(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     696                 :          0 :         uid_t *uid = userdata, u;
     697                 :            :         uintmax_t k;
     698                 :            : 
     699         [ #  # ]:          0 :         assert(uid);
     700                 :            :         assert_cc(sizeof(uid_t) == sizeof(gid_t));
     701                 :            : 
     702                 :            :         /* This is very much like oci_uid_gid(), except the checks are a bit different, as this is a UID range rather
     703                 :            :          * than a specific UID, and hence (uid_t) -1 has no special significance. OTOH a range of zero makes no
     704                 :            :          * sense. */
     705                 :            : 
     706                 :          0 :         k = json_variant_unsigned(v);
     707                 :          0 :         u = (uid_t) k;
     708         [ #  # ]:          0 :         if ((uintmax_t) u != k)
     709         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
     710                 :            :                                 "UID/GID out of range: %ji", k);
     711         [ #  # ]:          0 :         if (u == 0)
     712         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
     713                 :            :                                 "UID/GID range can't be zero.");
     714                 :            : 
     715                 :          0 :         *uid = u;
     716                 :          0 :         return 0;
     717                 :            : }
     718                 :            : 
     719                 :          0 : static int oci_uid_gid_mappings(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     720                 :            :         struct mapping_data {
     721                 :            :                 uid_t host_id;
     722                 :            :                 uid_t container_id;
     723                 :            :                 uid_t range;
     724                 :          0 :         } data = {
     725                 :            :                 .host_id = UID_INVALID,
     726                 :            :                 .container_id = UID_INVALID,
     727                 :            :                 .range = 0,
     728                 :            :         };
     729                 :            : 
     730                 :            :         static const JsonDispatch table[] = {
     731                 :            :                 { "containerID", JSON_VARIANT_UNSIGNED, oci_uid_gid,       offsetof(struct mapping_data, container_id), JSON_MANDATORY },
     732                 :            :                 { "hostID",      JSON_VARIANT_UNSIGNED, oci_uid_gid,       offsetof(struct mapping_data, host_id),      JSON_MANDATORY },
     733                 :            :                 { "size",        JSON_VARIANT_UNSIGNED, oci_uid_gid_range, offsetof(struct mapping_data, range),        JSON_MANDATORY },
     734                 :            :                 {}
     735                 :            :         };
     736                 :            : 
     737                 :          0 :         Settings *s = userdata;
     738                 :            :         JsonVariant *e;
     739                 :            :         int r;
     740                 :            : 
     741         [ #  # ]:          0 :         assert(s);
     742                 :            : 
     743         [ #  # ]:          0 :         if (json_variant_elements(v) == 0)
     744                 :          0 :                 return 0;
     745                 :            : 
     746         [ #  # ]:          0 :         if (json_variant_elements(v) > 1)
     747         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
     748                 :            :                                 "UID/GID mappings with more than one entry are not supported.");
     749                 :            : 
     750         [ #  # ]:          0 :         assert_se(e = json_variant_by_index(v, 0));
     751                 :            : 
     752                 :          0 :         r = json_dispatch(e, table, oci_unexpected, flags, &data);
     753         [ #  # ]:          0 :         if (r < 0)
     754                 :          0 :                 return r;
     755                 :            : 
     756         [ #  # ]:          0 :         if (data.host_id + data.range < data.host_id ||
     757         [ #  # ]:          0 :             data.container_id + data.range < data.container_id)
     758         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     759                 :            :                                 "UID/GID range goes beyond UID/GID validity range, refusing.");
     760                 :            : 
     761         [ #  # ]:          0 :         if (data.container_id != 0)
     762         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
     763                 :            :                                 "UID/GID mappings with a non-zero container base are not supported.");
     764                 :            : 
     765         [ #  # ]:          0 :         if (data.range < 0x10000)
     766         [ #  # ]:          0 :                 json_log(v, flags|JSON_WARNING, 0,
     767                 :            :                          "UID/GID mapping with less than 65536 UID/GIDS set up, you are looking for trouble.");
     768                 :            : 
     769         [ #  # ]:          0 :         if (s->uid_range != UID_INVALID &&
     770   [ #  #  #  # ]:          0 :             (s->uid_shift != data.host_id || s->uid_range != data.range))
     771         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
     772                 :            :                                 "Non-matching UID and GID mappings are not supported.");
     773                 :            : 
     774                 :          0 :         s->uid_shift = data.host_id;
     775                 :          0 :         s->uid_range = data.range;
     776                 :            : 
     777                 :          0 :         return 0;
     778                 :            : }
     779                 :            : 
     780                 :          0 : static int oci_device_type(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     781                 :          0 :         mode_t *mode = userdata;
     782                 :            :         const char *t;
     783                 :            : 
     784         [ #  # ]:          0 :         assert(mode);
     785         [ #  # ]:          0 :         assert_se(t = json_variant_string(v));
     786                 :            : 
     787         [ #  # ]:          0 :         if (STR_IN_SET(t, "c", "u"))
     788                 :          0 :                 *mode = (*mode & ~S_IFMT) | S_IFCHR;
     789         [ #  # ]:          0 :         else if (streq(t, "b"))
     790                 :          0 :                 *mode = (*mode & ~S_IFMT) | S_IFBLK;
     791         [ #  # ]:          0 :         else if (streq(t, "p"))
     792                 :          0 :                 *mode = (*mode & ~S_IFMT) | S_IFIFO;
     793                 :            :         else
     794         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     795                 :            :                                 "Unknown device type: %s", t);
     796                 :            : 
     797                 :          0 :         return 0;
     798                 :            : }
     799                 :            : 
     800                 :          0 : static int oci_device_major(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     801                 :          0 :         unsigned *u = userdata;
     802                 :            :         uintmax_t k;
     803                 :            : 
     804         [ #  # ]:          0 :         assert_se(u);
     805                 :            : 
     806                 :          0 :         k = json_variant_unsigned(v);
     807   [ #  #  #  #  :          0 :         if (!DEVICE_MAJOR_VALID(k))
                   #  # ]
     808         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
     809                 :            :                                 "Device major %ji out of range.", k);
     810                 :            : 
     811                 :          0 :         *u = (unsigned) k;
     812                 :          0 :         return 0;
     813                 :            : }
     814                 :            : 
     815                 :          0 : static int oci_device_minor(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     816                 :          0 :         unsigned *u = userdata;
     817                 :            :         uintmax_t k;
     818                 :            : 
     819         [ #  # ]:          0 :         assert_se(u);
     820                 :            : 
     821                 :          0 :         k = json_variant_unsigned(v);
     822   [ #  #  #  #  :          0 :         if (!DEVICE_MINOR_VALID(k))
                   #  # ]
     823         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
     824                 :            :                                 "Device minor %ji out of range.", k);
     825                 :            : 
     826                 :          0 :         *u = (unsigned) k;
     827                 :          0 :         return 0;
     828                 :            : }
     829                 :            : 
     830                 :          0 : static int oci_device_file_mode(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     831                 :          0 :         mode_t *mode = userdata, m;
     832                 :            :         uintmax_t k;
     833                 :            : 
     834         [ #  # ]:          0 :         assert(mode);
     835                 :            : 
     836                 :          0 :         k = json_variant_unsigned(v);
     837                 :          0 :         m = (mode_t) k;
     838                 :            : 
     839   [ #  #  #  # ]:          0 :         if ((m & ~07777) != 0 || (uintmax_t) m != k)
     840         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
     841                 :            :                                 "fileMode out of range, refusing.");
     842                 :            : 
     843                 :          0 :         *mode = m;
     844                 :          0 :         return 0;
     845                 :            : }
     846                 :            : 
     847                 :          0 : static int oci_devices(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     848                 :          0 :         Settings *s = userdata;
     849                 :            :         JsonVariant *e;
     850                 :            :         int r;
     851                 :            : 
     852         [ #  # ]:          0 :         assert(s);
     853                 :            : 
     854   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
     855                 :            : 
     856                 :            :                 static const JsonDispatch table[] = {
     857                 :            :                         { "type",     JSON_VARIANT_STRING,   oci_device_type,      offsetof(DeviceNode, mode),  JSON_MANDATORY },
     858                 :            :                         { "path",     JSON_VARIANT_STRING,   oci_absolute_path,    offsetof(DeviceNode, path),  JSON_MANDATORY },
     859                 :            :                         { "major",    JSON_VARIANT_UNSIGNED, oci_device_major,     offsetof(DeviceNode, major), 0              },
     860                 :            :                         { "minor",    JSON_VARIANT_UNSIGNED, oci_device_minor,     offsetof(DeviceNode, minor), 0              },
     861                 :            :                         { "fileMode", JSON_VARIANT_UNSIGNED, oci_device_file_mode, offsetof(DeviceNode, mode),  0              },
     862                 :            :                         { "uid",      JSON_VARIANT_UNSIGNED, oci_uid_gid,          offsetof(DeviceNode, uid),   0              },
     863                 :            :                         { "gid",      JSON_VARIANT_UNSIGNED, oci_uid_gid,          offsetof(DeviceNode, gid),   0              },
     864                 :            :                         {}
     865                 :            :                 };
     866                 :            : 
     867                 :            :                 DeviceNode *node, *nodes;
     868                 :            : 
     869                 :          0 :                 nodes = reallocarray(s->extra_nodes, s->n_extra_nodes + 1, sizeof(DeviceNode));
     870         [ #  # ]:          0 :                 if (!nodes)
     871                 :          0 :                         return log_oom();
     872                 :            : 
     873                 :          0 :                 s->extra_nodes = nodes;
     874                 :            : 
     875                 :          0 :                 node = nodes + s->n_extra_nodes;
     876                 :          0 :                 *node = (DeviceNode) {
     877                 :            :                         .uid = UID_INVALID,
     878                 :            :                         .gid = GID_INVALID,
     879                 :            :                         .major = (unsigned) -1,
     880                 :            :                         .minor = (unsigned) -1,
     881                 :            :                         .mode = 0644,
     882                 :            :                 };
     883                 :            : 
     884                 :          0 :                 r = json_dispatch(e, table, oci_unexpected, flags, node);
     885         [ #  # ]:          0 :                 if (r < 0)
     886                 :          0 :                         goto fail_element;
     887                 :            : 
     888   [ #  #  #  # ]:          0 :                 if (S_ISCHR(node->mode) || S_ISBLK(node->mode)) {
     889      [ #  #  # ]:          0 :                         _cleanup_free_ char *path = NULL;
     890                 :            : 
     891   [ #  #  #  # ]:          0 :                         if (node->major == (unsigned) -1 || node->minor == (unsigned) -1) {
     892         [ #  # ]:          0 :                                 r = json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
     893                 :            :                                              "Major/minor required when device node is device node");
     894                 :          0 :                                 goto fail_element;
     895                 :            :                         }
     896                 :            : 
     897                 :            :                         /* Suppress a couple of implicit device nodes */
     898                 :          0 :                         r = device_path_make_canonical(node->mode, makedev(node->major, node->minor), &path);
     899         [ #  # ]:          0 :                         if (r < 0)
     900         [ #  # ]:          0 :                                 json_log(e, flags|JSON_DEBUG, 0, "Failed to resolve device node %u:%u, ignoring: %m", node->major, node->minor);
     901                 :            :                         else {
     902   [ #  #  #  #  :          0 :                                 if (PATH_IN_SET(path,
             #  #  #  # ]
     903                 :            :                                                 "/dev/null",
     904                 :            :                                                 "/dev/zero",
     905                 :            :                                                 "/dev/full",
     906                 :            :                                                 "/dev/random",
     907                 :            :                                                 "/dev/urandom",
     908                 :            :                                                 "/dev/tty",
     909                 :            :                                                 "/dev/net/tun",
     910                 :            :                                                 "/dev/ptmx",
     911                 :            :                                                 "/dev/pts/ptmx",
     912                 :            :                                                 "/dev/console")) {
     913                 :            : 
     914         [ #  # ]:          0 :                                         json_log(e, flags|JSON_DEBUG, 0, "Ignoring devices item for device '%s', as it is implicitly created anyway.", path);
     915                 :          0 :                                         free(node->path);
     916                 :          0 :                                         continue;
     917                 :            :                                 }
     918                 :            :                         }
     919                 :            :                 }
     920                 :            : 
     921                 :          0 :                 s->n_extra_nodes++;
     922                 :          0 :                 continue;
     923                 :            : 
     924                 :          0 :         fail_element:
     925                 :          0 :                 free(node->path);
     926                 :          0 :                 return r;
     927                 :            :         }
     928                 :            : 
     929                 :          0 :         return 0;
     930                 :            : }
     931                 :            : 
     932                 :          0 : static int oci_cgroups_path(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     933                 :          0 :         _cleanup_free_ char *slice = NULL, *backwards = NULL;
     934                 :          0 :         Settings *s = userdata;
     935                 :            :         const char *p;
     936                 :            :         int r;
     937                 :            : 
     938         [ #  # ]:          0 :         assert(s);
     939                 :            : 
     940         [ #  # ]:          0 :         assert_se(p = json_variant_string(v));
     941                 :            : 
     942                 :          0 :         r = cg_path_get_slice(p, &slice);
     943         [ #  # ]:          0 :         if (r < 0)
     944         [ #  # ]:          0 :                 return json_log(v, flags, r, "Couldn't derive slice unit name from path '%s': %m", p);
     945                 :            : 
     946                 :          0 :         r = cg_slice_to_path(slice, &backwards);
     947         [ #  # ]:          0 :         if (r < 0)
     948         [ #  # ]:          0 :                 return json_log(v, flags, r, "Couldn't convert slice unit name '%s' back to path: %m", slice);
     949                 :            : 
     950         [ #  # ]:          0 :         if (!path_equal(backwards, p))
     951         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     952                 :            :                                 "Control group path '%s' does not refer to slice unit, refusing.", p);
     953                 :            : 
     954                 :          0 :         free_and_replace(s->slice, slice);
     955                 :          0 :         return 0;
     956                 :            : }
     957                 :            : 
     958                 :          0 : static int oci_cgroup_device_type(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     959                 :          0 :         mode_t *mode = userdata;
     960                 :            :         const char *n;
     961                 :            : 
     962         [ #  # ]:          0 :         assert_se(n = json_variant_string(v));
     963                 :            : 
     964         [ #  # ]:          0 :         if (streq(n, "c"))
     965                 :          0 :                 *mode = S_IFCHR;
     966         [ #  # ]:          0 :         else if (streq(n, "b"))
     967                 :          0 :                 *mode = S_IFBLK;
     968                 :            :         else
     969         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     970                 :            :                                 "Control group device type unknown: %s", n);
     971                 :            : 
     972                 :          0 :         return 0;
     973                 :            : }
     974                 :            : 
     975                 :            : struct device_data {
     976                 :            :         bool allow;
     977                 :            :         bool r;
     978                 :            :         bool w;
     979                 :            :         bool m;
     980                 :            :         mode_t type;
     981                 :            :         unsigned major;
     982                 :            :         unsigned minor;
     983                 :            : };
     984                 :            : 
     985                 :          0 : static int oci_cgroup_device_access(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     986                 :          0 :         struct device_data *d = userdata;
     987                 :          0 :         bool r = false, w = false, m = false;
     988                 :            :         const char *s;
     989                 :            :         size_t i;
     990                 :            : 
     991         [ #  # ]:          0 :         assert_se(s = json_variant_string(v));
     992                 :            : 
     993         [ #  # ]:          0 :         for (i = 0; s[i]; i++)
     994         [ #  # ]:          0 :                 if (s[i] == 'r')
     995                 :          0 :                         r = true;
     996         [ #  # ]:          0 :                 else if (s[i] == 'w')
     997                 :          0 :                         w = true;
     998         [ #  # ]:          0 :                 else if (s[i] == 'm')
     999                 :          0 :                         m = true;
    1000                 :            :                 else
    1001         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1002                 :            :                                         "Unknown device access character '%c'.", s[i]);
    1003                 :            : 
    1004                 :          0 :         d->r = r;
    1005                 :          0 :         d->w = w;
    1006                 :          0 :         d->m = m;
    1007                 :            : 
    1008                 :          0 :         return 0;
    1009                 :            : }
    1010                 :            : 
    1011                 :          0 : static int oci_cgroup_devices(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1012                 :            : 
    1013                 :          0 :         _cleanup_free_ struct device_data *list = NULL;
    1014                 :          0 :         Settings *s = userdata;
    1015                 :          0 :         size_t n_list = 0, i;
    1016                 :          0 :         bool noop = false;
    1017                 :            :         JsonVariant *e;
    1018                 :            :         int r;
    1019                 :            : 
    1020         [ #  # ]:          0 :         assert(s);
    1021                 :            : 
    1022   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
    1023                 :            : 
    1024                 :          0 :                 struct device_data data = {
    1025                 :            :                         .major = (unsigned) -1,
    1026                 :            :                         .minor = (unsigned) -1,
    1027                 :            :                 }, *a;
    1028                 :            : 
    1029                 :            :                 static const JsonDispatch table[] = {
    1030                 :            :                         { "allow",  JSON_VARIANT_BOOLEAN,  json_dispatch_boolean,    offsetof(struct device_data, allow), JSON_MANDATORY },
    1031                 :            :                         { "type",   JSON_VARIANT_STRING,   oci_cgroup_device_type,   offsetof(struct device_data, type),  0              },
    1032                 :            :                         { "major",  JSON_VARIANT_UNSIGNED, oci_device_major,         offsetof(struct device_data, major), 0              },
    1033                 :            :                         { "minor",  JSON_VARIANT_UNSIGNED, oci_device_minor,         offsetof(struct device_data, minor), 0              },
    1034                 :            :                         { "access", JSON_VARIANT_STRING,   oci_cgroup_device_access, 0,                                   0              },
    1035                 :            :                         {}
    1036                 :            :                 };
    1037                 :            : 
    1038                 :          0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &data);
    1039         [ #  # ]:          0 :                 if (r < 0)
    1040                 :          0 :                         return r;
    1041                 :            : 
    1042         [ #  # ]:          0 :                 if (!data.allow) {
    1043                 :            :                         /* The fact that OCI allows 'deny' entries makes really no sense, as 'allow' vs. 'deny' for the
    1044                 :            :                          * devices cgroup controller is really not about whitelisting and blacklisting but about adding
    1045                 :            :                          * and removing entries from the whitelist. Since we always start out with an empty whitelist
    1046                 :            :                          * we hence ignore the whole thing, as removing entries which don't exist make no sense. We'll
    1047                 :            :                          * log about this, since this is really borked in the spec, with one exception: the entry
    1048                 :            :                          * that's supposed to drop the kernel's default we ignore silently */
    1049                 :            : 
    1050   [ #  #  #  #  :          0 :                         if (!data.r || !data.w || !data.m || data.type != 0 || data.major != (unsigned) -1 || data.minor != (unsigned) -1)
          #  #  #  #  #  
                #  #  # ]
    1051         [ #  # ]:          0 :                                 json_log(v, flags|JSON_WARNING, 0, "Devices cgroup whitelist with arbitrary 'allow' entries not supported, ignoring.");
    1052                 :            : 
    1053                 :            :                         /* We ignore the 'deny' entry as for us that's implied */
    1054                 :          0 :                         continue;
    1055                 :            :                 }
    1056                 :            : 
    1057   [ #  #  #  #  :          0 :                 if (!data.r && !data.w && !data.m) {
                   #  # ]
    1058         [ #  # ]:          0 :                         json_log(v, flags|LOG_WARNING, 0, "Device cgroup whitelist entry with no effect found, ignoring.");
    1059                 :          0 :                         continue;
    1060                 :            :                 }
    1061                 :            : 
    1062   [ #  #  #  # ]:          0 :                 if (data.minor != (unsigned) -1 && data.major == (unsigned) -1)
    1063         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
    1064                 :            :                                         "Device cgroup whitelist entries with minors but no majors not supported.");
    1065                 :            : 
    1066   [ #  #  #  # ]:          0 :                 if (data.major != (unsigned) -1 && data.type == 0)
    1067         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
    1068                 :            :                                         "Device cgroup whitelist entries with majors but no device node type not supported.");
    1069                 :            : 
    1070         [ #  # ]:          0 :                 if (data.type == 0) {
    1071   [ #  #  #  #  :          0 :                         if (data.r && data.w && data.m) /* a catchall whitelist entry means we are looking at a noop */
                   #  # ]
    1072                 :          0 :                                 noop = true;
    1073                 :            :                         else
    1074         [ #  # ]:          0 :                                 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
    1075                 :            :                                                 "Device cgroup whitelist entries with no type not supported.");
    1076                 :            :                 }
    1077                 :            : 
    1078                 :          0 :                 a = reallocarray(list, n_list + 1, sizeof(struct device_data));
    1079         [ #  # ]:          0 :                 if (!a)
    1080                 :          0 :                         return log_oom();
    1081                 :            : 
    1082                 :          0 :                 list = a;
    1083                 :          0 :                 list[n_list++] = data;
    1084                 :            :         }
    1085                 :            : 
    1086         [ #  # ]:          0 :         if (noop)
    1087                 :          0 :                 return 0;
    1088                 :            : 
    1089                 :          0 :         r = settings_allocate_properties(s);
    1090         [ #  # ]:          0 :         if (r < 0)
    1091                 :          0 :                 return r;
    1092                 :            : 
    1093                 :          0 :         r = sd_bus_message_open_container(s->properties, 'r', "sv");
    1094         [ #  # ]:          0 :         if (r < 0)
    1095         [ #  # ]:          0 :                 return bus_log_create_error(r);
    1096                 :            : 
    1097                 :          0 :         r = sd_bus_message_append(s->properties, "s", "DeviceAllow");
    1098         [ #  # ]:          0 :         if (r < 0)
    1099         [ #  # ]:          0 :                 return bus_log_create_error(r);
    1100                 :            : 
    1101                 :          0 :         r = sd_bus_message_open_container(s->properties, 'v', "a(ss)");
    1102         [ #  # ]:          0 :         if (r < 0)
    1103         [ #  # ]:          0 :                 return bus_log_create_error(r);
    1104                 :            : 
    1105                 :          0 :         r = sd_bus_message_open_container(s->properties, 'a', "(ss)");
    1106         [ #  # ]:          0 :         if (r < 0)
    1107         [ #  # ]:          0 :                 return bus_log_create_error(r);
    1108                 :            : 
    1109         [ #  # ]:          0 :         for (i = 0; i < n_list; i++) {
    1110         [ #  # ]:          0 :                 _cleanup_free_ char *pattern = NULL;
    1111                 :            :                 char access[4];
    1112                 :          0 :                 size_t n = 0;
    1113                 :            : 
    1114         [ #  # ]:          0 :                 if (list[i].minor == (unsigned) -1) {
    1115                 :            :                         const char *t;
    1116                 :            : 
    1117         [ #  # ]:          0 :                         if (list[i].type == S_IFBLK)
    1118                 :          0 :                                 t = "block";
    1119                 :            :                         else {
    1120         [ #  # ]:          0 :                                 assert(list[i].type == S_IFCHR);
    1121                 :          0 :                                 t = "char";
    1122                 :            :                         }
    1123                 :            : 
    1124         [ #  # ]:          0 :                         if (list[i].major == (unsigned) -1) {
    1125                 :          0 :                                 pattern = strjoin(t, "-*");
    1126         [ #  # ]:          0 :                                 if (!pattern)
    1127                 :          0 :                                         return log_oom();
    1128                 :            :                         } else {
    1129         [ #  # ]:          0 :                                 if (asprintf(&pattern, "%s-%u", t, list[i].major) < 0)
    1130                 :          0 :                                         return log_oom();
    1131                 :            :                         }
    1132                 :            : 
    1133                 :            :                 } else {
    1134         [ #  # ]:          0 :                         assert(list[i].major != (unsigned) -1); /* If a minor is specified, then a major also needs to be specified */
    1135                 :            : 
    1136                 :          0 :                         r = device_path_make_major_minor(list[i].type, makedev(list[i].major, list[i].minor), &pattern);
    1137         [ #  # ]:          0 :                         if (r < 0)
    1138                 :          0 :                                 return log_oom();
    1139                 :            :                 }
    1140                 :            : 
    1141         [ #  # ]:          0 :                 if (list[i].r)
    1142                 :          0 :                         access[n++] = 'r';
    1143         [ #  # ]:          0 :                 if (list[i].w)
    1144                 :          0 :                         access[n++] = 'w';
    1145         [ #  # ]:          0 :                 if (list[i].m)
    1146                 :          0 :                         access[n++] = 'm';
    1147                 :          0 :                 access[n] = 0;
    1148                 :            : 
    1149         [ #  # ]:          0 :                 assert(n > 0);
    1150                 :            : 
    1151                 :          0 :                 r = sd_bus_message_append(s->properties, "(ss)", pattern, access);
    1152         [ #  # ]:          0 :                 if (r < 0)
    1153         [ #  # ]:          0 :                         return bus_log_create_error(r);
    1154                 :            :         }
    1155                 :            : 
    1156                 :          0 :         r = sd_bus_message_close_container(s->properties);
    1157         [ #  # ]:          0 :         if (r < 0)
    1158         [ #  # ]:          0 :                 return bus_log_create_error(r);
    1159                 :            : 
    1160                 :          0 :         r = sd_bus_message_close_container(s->properties);
    1161         [ #  # ]:          0 :         if (r < 0)
    1162         [ #  # ]:          0 :                 return bus_log_create_error(r);
    1163                 :            : 
    1164                 :          0 :         r = sd_bus_message_close_container(s->properties);
    1165         [ #  # ]:          0 :         if (r < 0)
    1166         [ #  # ]:          0 :                 return bus_log_create_error(r);
    1167                 :            : 
    1168                 :          0 :         return 0;
    1169                 :            : }
    1170                 :            : 
    1171                 :          0 : static int oci_cgroup_memory_limit(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1172                 :          0 :         uint64_t *m = userdata;
    1173                 :            :         uintmax_t k;
    1174                 :            : 
    1175         [ #  # ]:          0 :         assert(m);
    1176                 :            : 
    1177         [ #  # ]:          0 :         if (json_variant_is_negative(v)) {
    1178                 :          0 :                 *m = UINT64_MAX;
    1179                 :          0 :                 return 0;
    1180                 :            :         }
    1181                 :            : 
    1182         [ #  # ]:          0 :         if (!json_variant_is_unsigned(v))
    1183         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1184                 :            :                                 "Memory limit is not an unsigned integer");
    1185                 :            : 
    1186                 :          0 :         k = json_variant_unsigned(v);
    1187         [ #  # ]:          0 :         if (k >= UINT64_MAX)
    1188         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    1189                 :            :                                 "Memory limit too large: %ji", k);
    1190                 :            : 
    1191                 :          0 :         *m = (uint64_t) k;
    1192                 :          0 :         return 0;
    1193                 :            : }
    1194                 :            : 
    1195                 :          0 : static int oci_cgroup_memory(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1196                 :            : 
    1197                 :            :         struct memory_data {
    1198                 :            :                 uint64_t limit;
    1199                 :            :                 uint64_t reservation;
    1200                 :            :                 uint64_t swap;
    1201                 :          0 :         } data = {
    1202                 :            :                 .limit = UINT64_MAX,
    1203                 :            :                 .reservation = UINT64_MAX,
    1204                 :            :                 .swap = UINT64_MAX,
    1205                 :            :         };
    1206                 :            : 
    1207                 :            :         static const JsonDispatch table[] = {
    1208                 :            :                 { "limit",            JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, limit),       0               },
    1209                 :            :                 { "reservation",      JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, reservation), 0               },
    1210                 :            :                 { "swap",             JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, swap),        0               },
    1211                 :            :                 { "kernel",           JSON_VARIANT_NUMBER, oci_unsupported,         0,                                         JSON_PERMISSIVE },
    1212                 :            :                 { "kernelTCP",        JSON_VARIANT_NUMBER, oci_unsupported,         0,                                         JSON_PERMISSIVE },
    1213                 :            :                 { "swapiness",        JSON_VARIANT_NUMBER, oci_unsupported,         0,                                         JSON_PERMISSIVE },
    1214                 :            :                 { "disableOOMKiller", JSON_VARIANT_NUMBER, oci_unsupported,         0,                                         JSON_PERMISSIVE },
    1215                 :            :                 {}
    1216                 :            :         };
    1217                 :            : 
    1218                 :          0 :         Settings *s = userdata;
    1219                 :            :         int r;
    1220                 :            : 
    1221                 :          0 :         r = json_dispatch(v, table, oci_unexpected, flags, &data);
    1222         [ #  # ]:          0 :         if (r < 0)
    1223                 :          0 :                 return r;
    1224                 :            : 
    1225         [ #  # ]:          0 :         if (data.swap != UINT64_MAX) {
    1226         [ #  # ]:          0 :                 if (data.limit == UINT64_MAX)
    1227         [ #  # ]:          0 :                         json_log(v, flags|LOG_WARNING, 0, "swap limit without memory limit is not supported, ignoring.");
    1228         [ #  # ]:          0 :                 else if (data.swap < data.limit)
    1229         [ #  # ]:          0 :                         json_log(v, flags|LOG_WARNING, 0, "swap limit is below memory limit, ignoring.");
    1230                 :            :                 else {
    1231                 :          0 :                         r = settings_allocate_properties(s);
    1232         [ #  # ]:          0 :                         if (r < 0)
    1233                 :          0 :                                 return r;
    1234                 :            : 
    1235                 :          0 :                         r = sd_bus_message_append(s->properties, "(sv)", "MemorySwapMax", "t", data.swap - data.limit);
    1236         [ #  # ]:          0 :                         if (r < 0)
    1237         [ #  # ]:          0 :                                 return bus_log_create_error(r);
    1238                 :            :                 }
    1239                 :            :         }
    1240                 :            : 
    1241         [ #  # ]:          0 :         if (data.limit != UINT64_MAX) {
    1242                 :          0 :                 r = settings_allocate_properties(s);
    1243         [ #  # ]:          0 :                 if (r < 0)
    1244                 :          0 :                         return r;
    1245                 :            : 
    1246                 :          0 :                 r = sd_bus_message_append(s->properties, "(sv)", "MemoryMax", "t", data.limit);
    1247         [ #  # ]:          0 :                 if (r < 0)
    1248         [ #  # ]:          0 :                         return bus_log_create_error(r);
    1249                 :            :         }
    1250                 :            : 
    1251         [ #  # ]:          0 :         if (data.reservation != UINT64_MAX) {
    1252                 :          0 :                 r = settings_allocate_properties(s);
    1253         [ #  # ]:          0 :                 if (r < 0)
    1254                 :          0 :                         return r;
    1255                 :            : 
    1256                 :          0 :                 r = sd_bus_message_append(s->properties, "(sv)", "MemoryLow", "t", data.reservation);
    1257         [ #  # ]:          0 :                 if (r < 0)
    1258         [ #  # ]:          0 :                         return bus_log_create_error(r);
    1259                 :            :         }
    1260                 :            : 
    1261                 :          0 :         return 0;
    1262                 :            : }
    1263                 :            : 
    1264                 :            : struct cpu_data {
    1265                 :            :         uint64_t shares;
    1266                 :            :         uint64_t quota;
    1267                 :            :         uint64_t period;
    1268                 :            :         CPUSet cpu_set;
    1269                 :            : };
    1270                 :            : 
    1271                 :          0 : static int oci_cgroup_cpu_shares(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1272                 :          0 :         uint64_t *u = userdata;
    1273                 :            :         uintmax_t k;
    1274                 :            : 
    1275         [ #  # ]:          0 :         assert(u);
    1276                 :            : 
    1277                 :          0 :         k = json_variant_unsigned(v);
    1278   [ #  #  #  # ]:          0 :         if (k < CGROUP_CPU_SHARES_MIN || k > CGROUP_CPU_SHARES_MAX)
    1279         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    1280                 :            :                                 "shares value out of range.");
    1281                 :            : 
    1282                 :          0 :         *u = (uint64_t) k;
    1283                 :          0 :         return 0;
    1284                 :            : }
    1285                 :            : 
    1286                 :          0 : static int oci_cgroup_cpu_quota(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1287                 :          0 :         uint64_t *u = userdata;
    1288                 :            :         uintmax_t k;
    1289                 :            : 
    1290         [ #  # ]:          0 :         assert(u);
    1291                 :            : 
    1292                 :          0 :         k = json_variant_unsigned(v);
    1293   [ #  #  #  # ]:          0 :         if (k <= 0 || k >= UINT64_MAX)
    1294         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    1295                 :            :                                 "period/quota value out of range.");
    1296                 :            : 
    1297                 :          0 :         *u = (uint64_t) k;
    1298                 :          0 :         return 0;
    1299                 :            : }
    1300                 :            : 
    1301                 :          0 : static int oci_cgroup_cpu_cpus(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1302                 :          0 :         struct cpu_data *data = userdata;
    1303                 :            :         CPUSet set;
    1304                 :            :         const char *n;
    1305                 :            :         int r;
    1306                 :            : 
    1307         [ #  # ]:          0 :         assert(data);
    1308                 :            : 
    1309         [ #  # ]:          0 :         assert_se(n = json_variant_string(v));
    1310                 :            : 
    1311                 :          0 :         r = parse_cpu_set(n, &set);
    1312         [ #  # ]:          0 :         if (r < 0)
    1313         [ #  # ]:          0 :                 return json_log(v, flags, r, "Failed to parse CPU set specification: %s", n);
    1314                 :            : 
    1315                 :          0 :         cpu_set_reset(&data->cpu_set);
    1316                 :          0 :         data->cpu_set = set;
    1317                 :            : 
    1318                 :          0 :         return 0;
    1319                 :            : }
    1320                 :            : 
    1321                 :          0 : static int oci_cgroup_cpu(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1322                 :            : 
    1323                 :            :         static const JsonDispatch table[] = {
    1324                 :            :                 { "shares",          JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_shares, offsetof(struct cpu_data, shares), 0 },
    1325                 :            :                 { "quota",           JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_quota,  offsetof(struct cpu_data, quota),  0 },
    1326                 :            :                 { "period",          JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_quota,  offsetof(struct cpu_data, period), 0 },
    1327                 :            :                 { "realtimeRuntime", JSON_VARIANT_UNSIGNED, oci_unsupported,       0,                                 0 },
    1328                 :            :                 { "realtimePeriod",  JSON_VARIANT_UNSIGNED, oci_unsupported,       0,                                 0 },
    1329                 :            :                 { "cpus",            JSON_VARIANT_STRING,   oci_cgroup_cpu_cpus,   0,                                 0 },
    1330                 :            :                 { "mems",            JSON_VARIANT_STRING,   oci_unsupported,       0,                                 0 },
    1331                 :            :                 {}
    1332                 :            :         };
    1333                 :            : 
    1334                 :          0 :         struct cpu_data data = {
    1335                 :            :                 .shares = UINT64_MAX,
    1336                 :            :                 .quota = UINT64_MAX,
    1337                 :            :                 .period = UINT64_MAX,
    1338                 :            :         };
    1339                 :            : 
    1340                 :          0 :         Settings *s = userdata;
    1341                 :            :         int r;
    1342                 :            : 
    1343                 :          0 :         r = json_dispatch(v, table, oci_unexpected, flags, &data);
    1344         [ #  # ]:          0 :         if (r < 0) {
    1345                 :          0 :                 cpu_set_reset(&data.cpu_set);
    1346                 :          0 :                 return r;
    1347                 :            :         }
    1348                 :            : 
    1349                 :          0 :         cpu_set_reset(&s->cpu_set);
    1350                 :          0 :         s->cpu_set = data.cpu_set;
    1351                 :            : 
    1352         [ #  # ]:          0 :         if (data.shares != UINT64_MAX) {
    1353                 :          0 :                 r = settings_allocate_properties(s);
    1354         [ #  # ]:          0 :                 if (r < 0)
    1355                 :          0 :                         return r;
    1356                 :            : 
    1357                 :          0 :                 r = sd_bus_message_append(s->properties, "(sv)", "CPUShares", "t", data.shares);
    1358         [ #  # ]:          0 :                 if (r < 0)
    1359         [ #  # ]:          0 :                         return bus_log_create_error(r);
    1360                 :            :         }
    1361                 :            : 
    1362   [ #  #  #  # ]:          0 :         if (data.quota != UINT64_MAX && data.period != UINT64_MAX) {
    1363                 :          0 :                 r = settings_allocate_properties(s);
    1364         [ #  # ]:          0 :                 if (r < 0)
    1365                 :          0 :                         return r;
    1366                 :            : 
    1367                 :          0 :                 r = sd_bus_message_append(s->properties, "(sv)", "CPUQuotaPerSecUSec", "t", (uint64_t) (data.quota * USEC_PER_SEC / data.period));
    1368         [ #  # ]:          0 :                 if (r < 0)
    1369         [ #  # ]:          0 :                         return bus_log_create_error(r);
    1370                 :            : 
    1371         [ #  # ]:          0 :         } else if ((data.quota != UINT64_MAX) != (data.period != UINT64_MAX))
    1372         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1373                 :            :                                 "CPU quota and period not used together.");
    1374                 :            : 
    1375                 :          0 :         return 0;
    1376                 :            : }
    1377                 :            : 
    1378                 :          0 : static int oci_cgroup_block_io_weight(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1379                 :          0 :         Settings *s = userdata;
    1380                 :            :         uintmax_t k;
    1381                 :            :         int r;
    1382                 :            : 
    1383         [ #  # ]:          0 :         assert(s);
    1384                 :            : 
    1385                 :          0 :         k = json_variant_unsigned(v);
    1386   [ #  #  #  # ]:          0 :         if (k < CGROUP_BLKIO_WEIGHT_MIN || k > CGROUP_BLKIO_WEIGHT_MAX)
    1387         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    1388                 :            :                                 "Block I/O weight out of range.");
    1389                 :            : 
    1390                 :          0 :         r = settings_allocate_properties(s);
    1391         [ #  # ]:          0 :         if (r < 0)
    1392                 :          0 :                 return r;
    1393                 :            : 
    1394                 :          0 :         r = sd_bus_message_append(s->properties, "(sv)", "BlockIOWeight", "t", (uint64_t) k);
    1395         [ #  # ]:          0 :         if (r < 0)
    1396         [ #  # ]:          0 :                 return bus_log_create_error(r);
    1397                 :            : 
    1398                 :          0 :         return 0;
    1399                 :            : }
    1400                 :            : 
    1401                 :          0 : static int oci_cgroup_block_io_weight_device(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1402                 :          0 :         Settings *s = userdata;
    1403                 :            :         JsonVariant *e;
    1404                 :            :         int r;
    1405                 :            : 
    1406         [ #  # ]:          0 :         assert(s);
    1407                 :            : 
    1408   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
    1409                 :            :                 struct device_data {
    1410                 :            :                         unsigned major;
    1411                 :            :                         unsigned minor;
    1412                 :            :                         uintmax_t weight;
    1413                 :          0 :                 } data = {
    1414                 :            :                         .major = (unsigned) -1,
    1415                 :            :                         .minor = (unsigned) -1,
    1416                 :            :                         .weight = UINTMAX_MAX,
    1417                 :            :                 };
    1418                 :            : 
    1419                 :            :                 static const JsonDispatch table[] =  {
    1420                 :            :                         { "major",      JSON_VARIANT_UNSIGNED, oci_device_major,       offsetof(struct device_data, major),  JSON_MANDATORY  },
    1421                 :            :                         { "minor",      JSON_VARIANT_UNSIGNED, oci_device_minor,       offsetof(struct device_data, minor),  JSON_MANDATORY  },
    1422                 :            :                         { "weight",     JSON_VARIANT_UNSIGNED, json_dispatch_unsigned, offsetof(struct device_data, weight), 0               },
    1423                 :            :                         { "leafWeight", JSON_VARIANT_INTEGER,  oci_unsupported,        0,                                    JSON_PERMISSIVE },
    1424                 :            :                         {}
    1425                 :            :                 };
    1426                 :            : 
    1427      [ #  #  # ]:          0 :                 _cleanup_free_ char *path = NULL;
    1428                 :            : 
    1429                 :          0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &data);
    1430         [ #  # ]:          0 :                 if (r < 0)
    1431                 :          0 :                         return r;
    1432                 :            : 
    1433         [ #  # ]:          0 :                 if (data.weight == UINTMAX_MAX)
    1434                 :          0 :                         continue;
    1435                 :            : 
    1436   [ #  #  #  # ]:          0 :                 if (data.weight < CGROUP_BLKIO_WEIGHT_MIN || data.weight > CGROUP_BLKIO_WEIGHT_MAX)
    1437         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    1438                 :            :                                         "Block I/O device weight out of range.");
    1439                 :            : 
    1440                 :          0 :                 r = device_path_make_major_minor(S_IFBLK, makedev(data.major, data.minor), &path);
    1441         [ #  # ]:          0 :                 if (r < 0)
    1442         [ #  # ]:          0 :                         return json_log(v, flags, r, "Failed to build device path: %m");
    1443                 :            : 
    1444                 :          0 :                 r = settings_allocate_properties(s);
    1445         [ #  # ]:          0 :                 if (r < 0)
    1446                 :          0 :                         return r;
    1447                 :            : 
    1448                 :          0 :                 r = sd_bus_message_append(s->properties, "(sv)", "BlockIODeviceWeight", "a(st)", 1, path, (uint64_t) data.weight);
    1449         [ #  # ]:          0 :                 if (r < 0)
    1450         [ #  # ]:          0 :                         return bus_log_create_error(r);
    1451                 :            :         }
    1452                 :            : 
    1453                 :          0 :         return 0;
    1454                 :            : }
    1455                 :            : 
    1456                 :          0 : static int oci_cgroup_block_io_throttle(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1457                 :          0 :         Settings *s = userdata;
    1458                 :            :         const char *pname;
    1459                 :            :         JsonVariant *e;
    1460                 :            :         int r;
    1461                 :            : 
    1462         [ #  # ]:          0 :         assert(s);
    1463                 :            : 
    1464         [ #  # ]:          0 :         pname = streq(name, "throttleReadBpsDevice")  ? "IOReadBandwidthMax" :
    1465         [ #  # ]:          0 :                 streq(name, "throttleWriteBpsDevice") ? "IOWriteBandwidthMax" :
    1466         [ #  # ]:          0 :                 streq(name, "throttleReadIOPSDevice") ? "IOReadIOPSMax" :
    1467                 :            :                                                         "IOWriteIOPSMax";
    1468                 :            : 
    1469   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
    1470                 :            :                 struct device_data {
    1471                 :            :                         unsigned major;
    1472                 :            :                         unsigned minor;
    1473                 :            :                         uintmax_t rate;
    1474                 :          0 :                 } data = {
    1475                 :            :                         .major = (unsigned) -1,
    1476                 :            :                         .minor = (unsigned) -1,
    1477                 :            :                 };
    1478                 :            : 
    1479                 :            :                 static const JsonDispatch table[] = {
    1480                 :            :                         { "major", JSON_VARIANT_UNSIGNED, oci_device_major,       offsetof(struct device_data, major), JSON_MANDATORY },
    1481                 :            :                         { "minor", JSON_VARIANT_UNSIGNED, oci_device_minor,       offsetof(struct device_data, minor), JSON_MANDATORY },
    1482                 :            :                         { "rate",  JSON_VARIANT_UNSIGNED, json_dispatch_unsigned, offsetof(struct device_data, rate),  JSON_MANDATORY },
    1483                 :            :                         {}
    1484                 :            :                 };
    1485                 :            : 
    1486         [ #  # ]:          0 :                 _cleanup_free_ char *path = NULL;
    1487                 :            : 
    1488                 :          0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &data);
    1489         [ #  # ]:          0 :                 if (r < 0)
    1490                 :          0 :                         return r;
    1491                 :            : 
    1492         [ #  # ]:          0 :                 if (data.rate >= UINT64_MAX)
    1493         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    1494                 :            :                                         "Block I/O device rate out of range.");
    1495                 :            : 
    1496                 :          0 :                 r = device_path_make_major_minor(S_IFBLK, makedev(data.major, data.minor), &path);
    1497         [ #  # ]:          0 :                 if (r < 0)
    1498         [ #  # ]:          0 :                         return json_log(v, flags, r, "Failed to build device path: %m");
    1499                 :            : 
    1500                 :          0 :                 r = settings_allocate_properties(s);
    1501         [ #  # ]:          0 :                 if (r < 0)
    1502                 :          0 :                         return r;
    1503                 :            : 
    1504                 :          0 :                 r = sd_bus_message_append(s->properties, "(sv)", pname, "a(st)", 1, path, (uint64_t) data.rate);
    1505         [ #  # ]:          0 :                 if (r < 0)
    1506         [ #  # ]:          0 :                         return bus_log_create_error(r);
    1507                 :            :         }
    1508                 :            : 
    1509                 :          0 :         return 0;
    1510                 :            : }
    1511                 :            : 
    1512                 :          0 : static int oci_cgroup_block_io(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1513                 :            : 
    1514                 :            :         static const JsonDispatch table[] = {
    1515                 :            :                 { "weight",                  JSON_VARIANT_UNSIGNED, oci_cgroup_block_io_weight,        0, 0               },
    1516                 :            :                 { "leafWeight",              JSON_VARIANT_UNSIGNED, oci_unsupported,                   0, JSON_PERMISSIVE },
    1517                 :            :                 { "weightDevice",            JSON_VARIANT_ARRAY,    oci_cgroup_block_io_weight_device, 0, 0               },
    1518                 :            :                 { "throttleReadBpsDevice",   JSON_VARIANT_ARRAY,    oci_cgroup_block_io_throttle,      0, 0               },
    1519                 :            :                 { "throttleWriteBpsDevice",  JSON_VARIANT_ARRAY,    oci_cgroup_block_io_throttle,      0, 0               },
    1520                 :            :                 { "throttleReadIOPSDevice",  JSON_VARIANT_ARRAY,    oci_cgroup_block_io_throttle,      0, 0               },
    1521                 :            :                 { "throttleWriteIOPSDevice", JSON_VARIANT_ARRAY,    oci_cgroup_block_io_throttle,      0, 0               },
    1522                 :            :                 {}
    1523                 :            :         };
    1524                 :            : 
    1525                 :          0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
    1526                 :            : }
    1527                 :            : 
    1528                 :          0 : static int oci_cgroup_pids(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1529                 :            : 
    1530                 :            :         static const JsonDispatch table[] = {
    1531                 :            :                 { "limit", JSON_VARIANT_NUMBER, json_dispatch_variant, 0, JSON_MANDATORY },
    1532                 :            :                 {}
    1533                 :            :         };
    1534                 :            : 
    1535                 :          0 :         _cleanup_(json_variant_unrefp) JsonVariant *k = NULL;
    1536                 :          0 :         Settings *s = userdata;
    1537                 :            :         uint64_t m;
    1538                 :            :         int r;
    1539                 :            : 
    1540         [ #  # ]:          0 :         assert(s);
    1541                 :            : 
    1542                 :          0 :         r = json_dispatch(v, table, oci_unexpected, flags, &k);
    1543         [ #  # ]:          0 :         if (r < 0)
    1544                 :          0 :                 return r;
    1545                 :            : 
    1546         [ #  # ]:          0 :         if (json_variant_is_negative(k))
    1547                 :          0 :                 m = UINT64_MAX;
    1548                 :            :         else {
    1549         [ #  # ]:          0 :                 if (!json_variant_is_unsigned(k))
    1550         [ #  # ]:          0 :                         return json_log(k, flags, SYNTHETIC_ERRNO(EINVAL),
    1551                 :            :                                         "pids limit not unsigned integer, refusing.");
    1552                 :            : 
    1553                 :          0 :                 m = (uint64_t) json_variant_unsigned(k);
    1554                 :            : 
    1555         [ #  # ]:          0 :                 if ((uintmax_t) m != json_variant_unsigned(k))
    1556         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1557                 :            :                                         "pids limit out of range, refusing.");
    1558                 :            :         }
    1559                 :            : 
    1560                 :          0 :         r = settings_allocate_properties(s);
    1561         [ #  # ]:          0 :         if (r < 0)
    1562                 :          0 :                 return r;
    1563                 :            : 
    1564                 :          0 :         r = sd_bus_message_append(s->properties, "(sv)", "TasksMax", "t", m);
    1565         [ #  # ]:          0 :         if (r < 0)
    1566         [ #  # ]:          0 :                 return bus_log_create_error(r);
    1567                 :            : 
    1568                 :          0 :         return 0;
    1569                 :            : }
    1570                 :            : 
    1571                 :          0 : static int oci_resources(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1572                 :            : 
    1573                 :            :         static const JsonDispatch table[] = {
    1574                 :            :                 { "devices",        JSON_VARIANT_ARRAY,  oci_cgroup_devices,  0, 0 },
    1575                 :            :                 { "memory",         JSON_VARIANT_OBJECT, oci_cgroup_memory,   0, 0 },
    1576                 :            :                 { "cpu",            JSON_VARIANT_OBJECT, oci_cgroup_cpu,      0, 0 },
    1577                 :            :                 { "blockIO",        JSON_VARIANT_OBJECT, oci_cgroup_block_io, 0, 0 },
    1578                 :            :                 { "hugepageLimits", JSON_VARIANT_ARRAY,  oci_unsupported,     0, 0 },
    1579                 :            :                 { "network",        JSON_VARIANT_OBJECT, oci_unsupported,     0, 0 },
    1580                 :            :                 { "pids",           JSON_VARIANT_OBJECT, oci_cgroup_pids,     0, 0 },
    1581                 :            :                 { "rdma",           JSON_VARIANT_OBJECT, oci_unsupported,     0, 0 },
    1582                 :            :                 {}
    1583                 :            :         };
    1584                 :            : 
    1585                 :          0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
    1586                 :            : }
    1587                 :            : 
    1588                 :          0 : static bool sysctl_key_valid(const char *s) {
    1589                 :          0 :         bool dot = true;
    1590                 :            : 
    1591                 :            :         /* Note that we are a bit stricter here than in systemd-sysctl, as that inherited semantics from the old sysctl
    1592                 :            :          * tool, which were really weird (as it swaps / and . in both ways) */
    1593                 :            : 
    1594         [ #  # ]:          0 :         if (isempty(s))
    1595                 :          0 :                 return false;
    1596                 :            : 
    1597         [ #  # ]:          0 :         for (; *s; s++) {
    1598                 :            : 
    1599   [ #  #  #  # ]:          0 :                 if (*s <= ' ' || *s >= 127)
    1600                 :          0 :                         return false;
    1601         [ #  # ]:          0 :                 if (*s == '/')
    1602                 :          0 :                         return false;
    1603         [ #  # ]:          0 :                 if (*s == '.') {
    1604                 :            : 
    1605         [ #  # ]:          0 :                         if (dot) /* Don't allow two dots next to each other (or at the beginning) */
    1606                 :          0 :                                 return false;
    1607                 :            : 
    1608                 :          0 :                         dot = true;
    1609                 :            :                 } else
    1610                 :          0 :                         dot = false;
    1611                 :            :         }
    1612                 :            : 
    1613         [ #  # ]:          0 :         if (dot) /* don't allow a dot at the end */
    1614                 :          0 :                 return false;
    1615                 :            : 
    1616                 :          0 :         return true;
    1617                 :            : }
    1618                 :            : 
    1619                 :          0 : static int oci_sysctl(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1620                 :          0 :         Settings *s = userdata;
    1621                 :            :         JsonVariant *w;
    1622                 :            :         const char *k;
    1623                 :            :         int r;
    1624                 :            : 
    1625         [ #  # ]:          0 :         assert(s);
    1626                 :            : 
    1627   [ #  #  #  #  :          0 :         JSON_VARIANT_OBJECT_FOREACH(k, w, v) {
                   #  # ]
    1628                 :            :                 const char *m;
    1629                 :            : 
    1630         [ #  # ]:          0 :                 if (!json_variant_is_string(w))
    1631         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1632                 :            :                                         "sysctl parameter is not a string, refusing.");
    1633                 :            : 
    1634         [ #  # ]:          0 :                 assert_se(m = json_variant_string(w));
    1635                 :            : 
    1636         [ #  # ]:          0 :                 if (sysctl_key_valid(k))
    1637         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1638                 :            :                                         "sysctl key invalid, refusing: %s", k);
    1639                 :            : 
    1640                 :          0 :                 r = strv_extend_strv(&s->sysctl, STRV_MAKE(k, m), false);
    1641         [ #  # ]:          0 :                 if (r < 0)
    1642                 :          0 :                         return log_oom();
    1643                 :            :         }
    1644                 :            : 
    1645                 :          0 :         return 0;
    1646                 :            : }
    1647                 :            : 
    1648                 :            : #if HAVE_SECCOMP
    1649                 :          0 : static int oci_seccomp_action_from_string(const char *name, uint32_t *ret) {
    1650                 :            : 
    1651                 :            :         static const struct {
    1652                 :            :                 const char *name;
    1653                 :            :                 uint32_t action;
    1654                 :            :         } table[] = {
    1655                 :            :                 { "SCMP_ACT_ALLOW",         SCMP_ACT_ALLOW        },
    1656                 :            :                 { "SCMP_ACT_ERRNO",         SCMP_ACT_ERRNO(EPERM) }, /* the OCI spec doesn't document the error, but it appears EPERM is supposed to be used */
    1657                 :            :                 { "SCMP_ACT_KILL",          SCMP_ACT_KILL         },
    1658                 :            : #ifdef SCMP_ACT_KILL_PROCESS
    1659                 :            :                 { "SCMP_ACT_KILL_PROCESS",  SCMP_ACT_KILL_PROCESS },
    1660                 :            : #endif
    1661                 :            : #ifdef SCMP_ACT_KILL_THREAD
    1662                 :            :                 { "SCMP_ACT_KILL_THREAD",   SCMP_ACT_KILL_THREAD  },
    1663                 :            : #endif
    1664                 :            : #ifdef SCMP_ACT_LOG
    1665                 :            :                 { "SCMP_ACT_LOG",           SCMP_ACT_LOG          },
    1666                 :            : #endif
    1667                 :            :                 { "SCMP_ACT_TRAP",          SCMP_ACT_TRAP         },
    1668                 :            : 
    1669                 :            :                 /* We don't support SCMP_ACT_TRACE because that requires a tracer, and that doesn't really make sense
    1670                 :            :                  * here */
    1671                 :            :         };
    1672                 :            : 
    1673                 :            :         size_t i;
    1674                 :            : 
    1675         [ #  # ]:          0 :         for (i = 0; i < ELEMENTSOF(table); i++)
    1676         [ #  # ]:          0 :                 if (streq_ptr(name, table[i].name)) {
    1677                 :          0 :                         *ret = table[i].action;
    1678                 :          0 :                         return 0;
    1679                 :            :                 }
    1680                 :            : 
    1681                 :          0 :         return -EINVAL;
    1682                 :            : }
    1683                 :            : 
    1684                 :          0 : static int oci_seccomp_arch_from_string(const char *name, uint32_t *ret) {
    1685                 :            : 
    1686                 :            :         static const struct {
    1687                 :            :                 const char *name;
    1688                 :            :                 uint32_t arch;
    1689                 :            :         } table[] = {
    1690                 :            :                 { "SCMP_ARCH_AARCH64",     SCMP_ARCH_AARCH64     },
    1691                 :            :                 { "SCMP_ARCH_ARM",         SCMP_ARCH_ARM         },
    1692                 :            :                 { "SCMP_ARCH_MIPS",        SCMP_ARCH_MIPS        },
    1693                 :            :                 { "SCMP_ARCH_MIPS64",      SCMP_ARCH_MIPS64      },
    1694                 :            :                 { "SCMP_ARCH_MIPS64N32",   SCMP_ARCH_MIPS64N32   },
    1695                 :            :                 { "SCMP_ARCH_MIPSEL",      SCMP_ARCH_MIPSEL      },
    1696                 :            :                 { "SCMP_ARCH_MIPSEL64",    SCMP_ARCH_MIPSEL64    },
    1697                 :            :                 { "SCMP_ARCH_MIPSEL64N32", SCMP_ARCH_MIPSEL64N32 },
    1698                 :            :                 { "SCMP_ARCH_NATIVE",      SCMP_ARCH_NATIVE      },
    1699                 :            : #ifdef SCMP_ARCH_PARISC
    1700                 :            :                 { "SCMP_ARCH_PARISC",      SCMP_ARCH_PARISC      },
    1701                 :            : #endif
    1702                 :            : #ifdef SCMP_ARCH_PARISC64
    1703                 :            :                 { "SCMP_ARCH_PARISC64",    SCMP_ARCH_PARISC64    },
    1704                 :            : #endif
    1705                 :            :                 { "SCMP_ARCH_PPC",         SCMP_ARCH_PPC         },
    1706                 :            :                 { "SCMP_ARCH_PPC64",       SCMP_ARCH_PPC64       },
    1707                 :            :                 { "SCMP_ARCH_PPC64LE",     SCMP_ARCH_PPC64LE     },
    1708                 :            :                 { "SCMP_ARCH_S390",        SCMP_ARCH_S390        },
    1709                 :            :                 { "SCMP_ARCH_S390X",       SCMP_ARCH_S390X       },
    1710                 :            :                 { "SCMP_ARCH_X32",         SCMP_ARCH_X32         },
    1711                 :            :                 { "SCMP_ARCH_X86",         SCMP_ARCH_X86         },
    1712                 :            :                 { "SCMP_ARCH_X86_64",      SCMP_ARCH_X86_64      },
    1713                 :            :         };
    1714                 :            : 
    1715                 :            :         size_t i;
    1716                 :            : 
    1717         [ #  # ]:          0 :         for (i = 0; i < ELEMENTSOF(table); i++)
    1718         [ #  # ]:          0 :                 if (streq_ptr(table[i].name, name)) {
    1719                 :          0 :                         *ret = table[i].arch;
    1720                 :          0 :                         return 0;
    1721                 :            :                 }
    1722                 :            : 
    1723                 :          0 :         return -EINVAL;
    1724                 :            : }
    1725                 :            : 
    1726                 :          0 : static int oci_seccomp_compare_from_string(const char *name, enum scmp_compare *ret) {
    1727                 :            : 
    1728                 :            :         static const struct {
    1729                 :            :                 const char *name;
    1730                 :            :                 enum scmp_compare op;
    1731                 :            :         } table[] = {
    1732                 :            :                 { "SCMP_CMP_NE",        SCMP_CMP_NE        },
    1733                 :            :                 { "SCMP_CMP_LT",        SCMP_CMP_LT        },
    1734                 :            :                 { "SCMP_CMP_LE",        SCMP_CMP_LE        },
    1735                 :            :                 { "SCMP_CMP_EQ",        SCMP_CMP_EQ        },
    1736                 :            :                 { "SCMP_CMP_GE",        SCMP_CMP_GE        },
    1737                 :            :                 { "SCMP_CMP_GT",        SCMP_CMP_GT        },
    1738                 :            :                 { "SCMP_CMP_MASKED_EQ", SCMP_CMP_MASKED_EQ },
    1739                 :            :         };
    1740                 :            : 
    1741                 :            :         size_t i;
    1742                 :            : 
    1743         [ #  # ]:          0 :         for (i = 0; i < ELEMENTSOF(table); i++)
    1744         [ #  # ]:          0 :                 if (streq_ptr(table[i].name, name)) {
    1745                 :          0 :                         *ret = table[i].op;
    1746                 :          0 :                         return 0;
    1747                 :            :                 }
    1748                 :            : 
    1749                 :          0 :         return -EINVAL;
    1750                 :            : }
    1751                 :            : 
    1752                 :          0 : static int oci_seccomp_archs(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1753                 :          0 :         scmp_filter_ctx *sc = userdata;
    1754                 :            :         JsonVariant *e;
    1755                 :            :         int r;
    1756                 :            : 
    1757         [ #  # ]:          0 :         assert(sc);
    1758                 :            : 
    1759   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
    1760                 :            :                 uint32_t a;
    1761                 :            : 
    1762         [ #  # ]:          0 :                 if (!json_variant_is_string(e))
    1763         [ #  # ]:          0 :                         return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
    1764                 :            :                                         "Architecture entry is not a string");
    1765                 :            : 
    1766                 :          0 :                 r = oci_seccomp_arch_from_string(json_variant_string(e), &a);
    1767         [ #  # ]:          0 :                 if (r < 0)
    1768         [ #  # ]:          0 :                         return json_log(e, flags, r, "Unknown architecture: %s", json_variant_string(e));
    1769                 :            : 
    1770                 :          0 :                 r = seccomp_arch_add(sc, a);
    1771         [ #  # ]:          0 :                 if (r == -EEXIST)
    1772                 :          0 :                         continue;
    1773         [ #  # ]:          0 :                 if (r < 0)
    1774         [ #  # ]:          0 :                         return json_log(e, flags, r, "Failed to add architecture to seccomp filter: %m");
    1775                 :            :         }
    1776                 :            : 
    1777                 :          0 :         return 0;
    1778                 :            : }
    1779                 :            : 
    1780                 :            : struct syscall_rule {
    1781                 :            :         char **names;
    1782                 :            :         uint32_t action;
    1783                 :            :         struct scmp_arg_cmp *arguments;
    1784                 :            :         size_t n_arguments;
    1785                 :            : };
    1786                 :            : 
    1787                 :          0 : static void syscall_rule_free(struct syscall_rule *rule) {
    1788         [ #  # ]:          0 :         assert(rule);
    1789                 :            : 
    1790                 :          0 :         strv_free(rule->names);
    1791                 :          0 :         free(rule->arguments);
    1792                 :          0 : };
    1793                 :            : 
    1794                 :          0 : static int oci_seccomp_action(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1795                 :          0 :         uint32_t *action = userdata;
    1796                 :            :         int r;
    1797                 :            : 
    1798         [ #  # ]:          0 :         assert(action);
    1799                 :            : 
    1800                 :          0 :         r = oci_seccomp_action_from_string(json_variant_string(v), action);
    1801         [ #  # ]:          0 :         if (r < 0)
    1802         [ #  # ]:          0 :                 return json_log(v, flags, r, "Unknown system call action '%s': %m", json_variant_string(v));
    1803                 :            : 
    1804                 :          0 :         return 0;
    1805                 :            : }
    1806                 :            : 
    1807                 :          0 : static int oci_seccomp_op(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1808                 :          0 :         enum scmp_compare *op = userdata;
    1809                 :            :         int r;
    1810                 :            : 
    1811         [ #  # ]:          0 :         assert(op);
    1812                 :            : 
    1813                 :          0 :         r = oci_seccomp_compare_from_string(json_variant_string(v), op);
    1814         [ #  # ]:          0 :         if (r < 0)
    1815         [ #  # ]:          0 :                 return json_log(v, flags, r, "Unknown seccomp operator '%s': %m", json_variant_string(v));
    1816                 :            : 
    1817                 :          0 :         return 0;
    1818                 :            : }
    1819                 :            : 
    1820                 :          0 : static int oci_seccomp_args(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1821                 :          0 :         struct syscall_rule *rule = userdata;
    1822                 :            :         JsonVariant *e;
    1823                 :            :         int r;
    1824                 :            : 
    1825         [ #  # ]:          0 :         assert(rule);
    1826                 :            : 
    1827   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
    1828                 :            :                 static const struct JsonDispatch table[] = {
    1829                 :            :                         { "index",    JSON_VARIANT_UNSIGNED, json_dispatch_uint32, offsetof(struct scmp_arg_cmp, arg),     JSON_MANDATORY },
    1830                 :            :                         { "value",    JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_a), JSON_MANDATORY },
    1831                 :            :                         { "valueTwo", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_b), 0              },
    1832                 :            :                         { "op",       JSON_VARIANT_STRING,   oci_seccomp_op,       offsetof(struct scmp_arg_cmp, op),      JSON_MANDATORY },
    1833                 :            :                         {},
    1834                 :            :                 };
    1835                 :            : 
    1836                 :            :                 struct scmp_arg_cmp *a, *p;
    1837                 :            :                 int expected;
    1838                 :            : 
    1839                 :          0 :                 a = reallocarray(rule->arguments, rule->n_arguments + 1, sizeof(struct syscall_rule));
    1840         [ #  # ]:          0 :                 if (!a)
    1841                 :          0 :                         return log_oom();
    1842                 :            : 
    1843                 :          0 :                 rule->arguments = a;
    1844                 :          0 :                 p = rule->arguments + rule->n_arguments;
    1845                 :            : 
    1846                 :          0 :                 *p = (struct scmp_arg_cmp) {
    1847                 :            :                         .arg = 0,
    1848                 :            :                         .datum_a = 0,
    1849                 :            :                         .datum_b = 0,
    1850                 :            :                         .op = 0,
    1851                 :            :                 };
    1852                 :            : 
    1853                 :          0 :                 r = json_dispatch(e, table, oci_unexpected, flags, p);
    1854         [ #  # ]:          0 :                 if (r < 0)
    1855                 :          0 :                         return r;
    1856                 :            : 
    1857         [ #  # ]:          0 :                 expected = p->op == SCMP_CMP_MASKED_EQ ? 4 : 3;
    1858         [ #  # ]:          0 :                 if (r != expected)
    1859         [ #  # ]:          0 :                         json_log(e, flags|JSON_WARNING, 0, "Wrong number of system call arguments for JSON data data, ignoring.");
    1860                 :            : 
    1861                 :            :                 /* Note that we are a bit sloppy here and do not insist that SCMP_CMP_MASKED_EQ gets two datum values,
    1862                 :            :                  * and the other only one. That's because buildah for example by default calls things with
    1863                 :            :                  * SCMP_CMP_MASKED_EQ but only one argument. We use 0 when the value is not specified. */
    1864                 :            : 
    1865                 :          0 :                 rule->n_arguments++;
    1866                 :            :         }
    1867                 :            : 
    1868                 :          0 :         return 0;
    1869                 :            : }
    1870                 :            : 
    1871                 :          0 : static int oci_seccomp_syscalls(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1872                 :          0 :         scmp_filter_ctx *sc = userdata;
    1873                 :            :         JsonVariant *e;
    1874                 :            :         int r;
    1875                 :            : 
    1876         [ #  # ]:          0 :         assert(sc);
    1877                 :            : 
    1878   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
    1879                 :            :                 static const JsonDispatch table[] = {
    1880                 :            :                         { "names",  JSON_VARIANT_ARRAY,  json_dispatch_strv, offsetof(struct syscall_rule, names),  JSON_MANDATORY },
    1881                 :            :                         { "action", JSON_VARIANT_STRING, oci_seccomp_action, offsetof(struct syscall_rule, action), JSON_MANDATORY },
    1882                 :            :                         { "args",   JSON_VARIANT_ARRAY,  oci_seccomp_args,   0,                                     0              },
    1883                 :            :                 };
    1884                 :          0 :                 struct syscall_rule rule = {
    1885                 :            :                         .action = (uint32_t) -1,
    1886                 :            :                 };
    1887                 :            :                 char **i;
    1888                 :            : 
    1889                 :          0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &rule);
    1890         [ #  # ]:          0 :                 if (r < 0)
    1891                 :          0 :                         goto fail_rule;
    1892                 :            : 
    1893         [ #  # ]:          0 :                 if (strv_isempty(rule.names)) {
    1894         [ #  # ]:          0 :                         json_log(e, flags, 0, "System call name list is empty.");
    1895                 :          0 :                         r = -EINVAL;
    1896                 :          0 :                         goto fail_rule;
    1897                 :            :                 }
    1898                 :            : 
    1899   [ #  #  #  # ]:          0 :                 STRV_FOREACH(i, rule.names) {
    1900                 :            :                         int nr;
    1901                 :            : 
    1902                 :          0 :                         nr = seccomp_syscall_resolve_name(*i);
    1903         [ #  # ]:          0 :                         if (nr == __NR_SCMP_ERROR) {
    1904         [ #  # ]:          0 :                                 log_debug("Unknown syscall %s, skipping.", *i);
    1905                 :          0 :                                 continue;
    1906                 :            :                         }
    1907                 :            : 
    1908                 :          0 :                         r = seccomp_rule_add_array(sc, rule.action, nr, rule.n_arguments, rule.arguments);
    1909         [ #  # ]:          0 :                         if (r < 0)
    1910                 :          0 :                                 goto fail_rule;
    1911                 :            :                 }
    1912                 :            : 
    1913                 :          0 :                 syscall_rule_free(&rule);
    1914                 :          0 :                 continue;
    1915                 :            : 
    1916                 :          0 :         fail_rule:
    1917                 :          0 :                 syscall_rule_free(&rule);
    1918                 :          0 :                 return r;
    1919                 :            :         }
    1920                 :            : 
    1921                 :          0 :         return 0;
    1922                 :            : }
    1923                 :            : #endif
    1924                 :            : 
    1925                 :          0 : static int oci_seccomp(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1926                 :            : 
    1927                 :            : #if HAVE_SECCOMP
    1928                 :            :         static const JsonDispatch table[] = {
    1929                 :            :                 { "defaultAction", JSON_VARIANT_STRING, NULL,                 0, JSON_MANDATORY },
    1930                 :            :                 { "architectures", JSON_VARIANT_ARRAY,  oci_seccomp_archs,    0, 0              },
    1931                 :            :                 { "syscalls",      JSON_VARIANT_ARRAY,  oci_seccomp_syscalls, 0, 0              },
    1932                 :            :                 {}
    1933                 :            :         };
    1934                 :            : 
    1935                 :          0 :         _cleanup_(seccomp_releasep) scmp_filter_ctx sc = NULL;
    1936                 :          0 :         Settings *s = userdata;
    1937                 :            :         JsonVariant *def;
    1938                 :            :         uint32_t d;
    1939                 :            :         int r;
    1940                 :            : 
    1941         [ #  # ]:          0 :         assert(s);
    1942                 :            : 
    1943                 :          0 :         def = json_variant_by_key(v, "defaultAction");
    1944         [ #  # ]:          0 :         if (!def)
    1945         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), "defaultAction element missing.");
    1946                 :            : 
    1947         [ #  # ]:          0 :         if (!json_variant_is_string(def))
    1948         [ #  # ]:          0 :                 return json_log(def, flags, SYNTHETIC_ERRNO(EINVAL), "defaultAction is not a string.");
    1949                 :            : 
    1950                 :          0 :         r = oci_seccomp_action_from_string(json_variant_string(def), &d);
    1951         [ #  # ]:          0 :         if (r < 0)
    1952         [ #  # ]:          0 :                 return json_log(def, flags, r, "Unknown default action: %s", json_variant_string(def));
    1953                 :            : 
    1954                 :          0 :         sc = seccomp_init(d);
    1955         [ #  # ]:          0 :         if (!sc)
    1956         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ENOMEM), "Couldn't allocate seccomp object.");
    1957                 :            : 
    1958                 :          0 :         r = json_dispatch(v, table, oci_unexpected, flags, sc);
    1959         [ #  # ]:          0 :         if (r < 0)
    1960                 :          0 :                 return r;
    1961                 :            : 
    1962                 :          0 :         seccomp_release(s->seccomp);
    1963                 :          0 :         s->seccomp = TAKE_PTR(sc);
    1964                 :          0 :         return 0;
    1965                 :            : #else
    1966                 :            :         return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), "libseccomp support not enabled, can't parse seccomp object.");
    1967                 :            : #endif
    1968                 :            : }
    1969                 :            : 
    1970                 :          0 : static int oci_rootfs_propagation(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1971                 :            :         const char *s;
    1972                 :            : 
    1973                 :          0 :         s = json_variant_string(v);
    1974                 :            : 
    1975         [ #  # ]:          0 :         if (streq(s, "shared"))
    1976                 :          0 :                 return 0;
    1977                 :            : 
    1978         [ #  # ]:          0 :         json_log(v, flags|JSON_DEBUG, 0, "Ignoring rootfsPropagation setting '%s'.", s);
    1979                 :          0 :         return 0;
    1980                 :            : }
    1981                 :            : 
    1982                 :          0 : static int oci_masked_paths(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1983                 :          0 :         Settings *s = userdata;
    1984                 :            :         JsonVariant *e;
    1985                 :            : 
    1986         [ #  # ]:          0 :         assert(s);
    1987                 :            : 
    1988   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
    1989      [ #  #  # ]:          0 :                 _cleanup_free_ char *destination = NULL;
    1990                 :            :                 CustomMount *m;
    1991                 :            :                 const char *p;
    1992                 :            : 
    1993         [ #  # ]:          0 :                 if (!json_variant_is_string(e))
    1994         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1995                 :            :                                         "Path is not a string, refusing.");
    1996                 :            : 
    1997         [ #  # ]:          0 :                 assert_se(p = json_variant_string(e));
    1998                 :            : 
    1999         [ #  # ]:          0 :                 if (!path_is_absolute(p))
    2000         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    2001                 :            :                                         "Path is not not absolute, refusing: %s", p);
    2002                 :            : 
    2003         [ #  # ]:          0 :                 if (oci_exclude_mount(p))
    2004                 :          0 :                         continue;
    2005                 :            : 
    2006                 :          0 :                 destination = strdup(p);
    2007         [ #  # ]:          0 :                 if (!destination)
    2008                 :          0 :                         return log_oom();
    2009                 :            : 
    2010                 :          0 :                 m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_INACCESSIBLE);
    2011         [ #  # ]:          0 :                 if (!m)
    2012                 :          0 :                         return log_oom();
    2013                 :            : 
    2014                 :          0 :                 m->destination = TAKE_PTR(destination);
    2015                 :            : 
    2016                 :            :                 /* The spec doesn't say this, but apparently pre-existing implementations are lenient towards
    2017                 :            :                  * non-existing paths to mask. Let's hence be too. */
    2018                 :          0 :                 m->graceful = true;
    2019                 :            :         }
    2020                 :            : 
    2021                 :          0 :         return 0;
    2022                 :            : }
    2023                 :            : 
    2024                 :          0 : static int oci_readonly_paths(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    2025                 :          0 :         Settings *s = userdata;
    2026                 :            :         JsonVariant *e;
    2027                 :            : 
    2028         [ #  # ]:          0 :         assert(s);
    2029                 :            : 
    2030   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
    2031   [ #  #  #  #  :          0 :                 _cleanup_free_ char *source = NULL, *destination = NULL;
                   #  # ]
    2032                 :            :                 CustomMount *m;
    2033                 :            :                 const char *p;
    2034                 :            : 
    2035         [ #  # ]:          0 :                 if (!json_variant_is_string(e))
    2036         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    2037                 :            :                                         "Path is not a string, refusing.");
    2038                 :            : 
    2039         [ #  # ]:          0 :                 assert_se(p = json_variant_string(e));
    2040                 :            : 
    2041         [ #  # ]:          0 :                 if (!path_is_absolute(p))
    2042         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    2043                 :            :                                         "Path is not not absolute, refusing: %s", p);
    2044                 :            : 
    2045         [ #  # ]:          0 :                 if (oci_exclude_mount(p))
    2046                 :          0 :                         continue;
    2047                 :            : 
    2048                 :          0 :                 source = strjoin("+", p);
    2049         [ #  # ]:          0 :                 if (!source)
    2050                 :          0 :                         return log_oom();
    2051                 :            : 
    2052                 :          0 :                 destination = strdup(p);
    2053         [ #  # ]:          0 :                 if (!destination)
    2054                 :          0 :                         return log_oom();
    2055                 :            : 
    2056                 :          0 :                 m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_BIND);
    2057         [ #  # ]:          0 :                 if (!m)
    2058                 :          0 :                         return log_oom();
    2059                 :            : 
    2060                 :          0 :                 m->source = TAKE_PTR(source);
    2061                 :          0 :                 m->destination = TAKE_PTR(destination);
    2062                 :          0 :                 m->read_only = true;
    2063                 :            :         }
    2064                 :            : 
    2065                 :          0 :         return 0;
    2066                 :            : }
    2067                 :            : 
    2068                 :          0 : static int oci_linux(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    2069                 :            : 
    2070                 :            :         static const JsonDispatch table[] = {
    2071                 :            :                 { "namespaces",        JSON_VARIANT_ARRAY,  oci_namespaces,         0, 0               },
    2072                 :            :                 { "uidMappings",       JSON_VARIANT_ARRAY,  oci_uid_gid_mappings,   0, 0               },
    2073                 :            :                 { "gidMappings",       JSON_VARIANT_ARRAY,  oci_uid_gid_mappings,   0, 0               },
    2074                 :            :                 { "devices",           JSON_VARIANT_ARRAY,  oci_devices,            0, 0               },
    2075                 :            :                 { "cgroupsPath",       JSON_VARIANT_STRING, oci_cgroups_path,       0, 0               },
    2076                 :            :                 { "resources",         JSON_VARIANT_OBJECT, oci_resources,          0, 0               },
    2077                 :            :                 { "intelRdt",          JSON_VARIANT_OBJECT, oci_unsupported,        0, JSON_PERMISSIVE },
    2078                 :            :                 { "sysctl",            JSON_VARIANT_OBJECT, oci_sysctl,             0, 0               },
    2079                 :            :                 { "seccomp",           JSON_VARIANT_OBJECT, oci_seccomp,            0, 0               },
    2080                 :            :                 { "rootfsPropagation", JSON_VARIANT_STRING, oci_rootfs_propagation, 0, 0               },
    2081                 :            :                 { "maskedPaths",       JSON_VARIANT_ARRAY,  oci_masked_paths,       0, 0               },
    2082                 :            :                 { "readonlyPaths",     JSON_VARIANT_ARRAY,  oci_readonly_paths,     0, 0               },
    2083                 :            :                 { "mountLabel",        JSON_VARIANT_STRING, oci_unsupported,        0, JSON_PERMISSIVE },
    2084                 :            :                 {}
    2085                 :            :         };
    2086                 :            : 
    2087                 :          0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
    2088                 :            : }
    2089                 :            : 
    2090                 :          0 : static int oci_hook_timeout(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    2091                 :          0 :         usec_t *u = userdata;
    2092                 :            :         uintmax_t k;
    2093                 :            : 
    2094                 :          0 :         k = json_variant_unsigned(v);
    2095   [ #  #  #  # ]:          0 :         if (k == 0 || k > (UINT64_MAX-1)/USEC_PER_SEC)
    2096         [ #  # ]:          0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    2097                 :            :                                 "Hook timeout value out of range.");
    2098                 :            : 
    2099                 :          0 :         *u = k * USEC_PER_SEC;
    2100                 :          0 :         return 0;
    2101                 :            : }
    2102                 :            : 
    2103                 :          0 : static int oci_hooks_array(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    2104                 :          0 :         Settings *s = userdata;
    2105                 :            :         JsonVariant *e;
    2106                 :            :         int r;
    2107                 :            : 
    2108         [ #  # ]:          0 :         assert(s);
    2109                 :            : 
    2110   [ #  #  #  #  :          0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
                   #  # ]
    2111                 :            : 
    2112                 :            :                 static const JsonDispatch table[] = {
    2113                 :            :                         { "path",    JSON_VARIANT_STRING,   oci_absolute_path, offsetof(OciHook, path),    JSON_MANDATORY },
    2114                 :            :                         { "args",    JSON_VARIANT_ARRAY,    oci_args,          offsetof(OciHook, args),    0              },
    2115                 :            :                         { "env",     JSON_VARIANT_ARRAY,    oci_env,           offsetof(OciHook, env),     0              },
    2116                 :            :                         { "timeout", JSON_VARIANT_UNSIGNED, oci_hook_timeout,  offsetof(OciHook, timeout), 0              },
    2117                 :            :                         {}
    2118                 :            :                 };
    2119                 :            : 
    2120                 :            :                 OciHook *a, **array, *new_item;
    2121                 :            :                 size_t *n_array;
    2122                 :            : 
    2123         [ #  # ]:          0 :                 if (streq(name, "prestart")) {
    2124                 :          0 :                         array = &s->oci_hooks_prestart;
    2125                 :          0 :                         n_array = &s->n_oci_hooks_prestart;
    2126         [ #  # ]:          0 :                 } else if (streq(name, "poststart")) {
    2127                 :          0 :                         array = &s->oci_hooks_poststart;
    2128                 :          0 :                         n_array = &s->n_oci_hooks_poststart;
    2129                 :            :                 } else {
    2130         [ #  # ]:          0 :                         assert(streq(name, "poststop"));
    2131                 :          0 :                         array = &s->oci_hooks_poststop;
    2132                 :          0 :                         n_array = &s->n_oci_hooks_poststop;
    2133                 :            :                 }
    2134                 :            : 
    2135                 :          0 :                 a = reallocarray(*array, *n_array + 1, sizeof(OciHook));
    2136         [ #  # ]:          0 :                 if (!a)
    2137                 :          0 :                         return log_oom();
    2138                 :            : 
    2139                 :          0 :                 *array = a;
    2140                 :          0 :                 new_item = a + *n_array;
    2141                 :            : 
    2142                 :          0 :                 *new_item = (OciHook) {
    2143                 :            :                         .timeout = USEC_INFINITY,
    2144                 :            :                 };
    2145                 :            : 
    2146                 :          0 :                 r = json_dispatch(e, table, oci_unexpected, flags, userdata);
    2147         [ #  # ]:          0 :                 if (r < 0) {
    2148                 :          0 :                         free(new_item->path);
    2149                 :          0 :                         strv_free(new_item->args);
    2150                 :          0 :                         strv_free(new_item->env);
    2151                 :          0 :                         return r;
    2152                 :            :                 }
    2153                 :            : 
    2154                 :          0 :                 (*n_array) ++;
    2155                 :            :         }
    2156                 :            : 
    2157                 :          0 :         return 0;
    2158                 :            : }
    2159                 :            : 
    2160                 :          0 : static int oci_hooks(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    2161                 :            : 
    2162                 :            :         static const JsonDispatch table[] = {
    2163                 :            :                 { "prestart",  JSON_VARIANT_OBJECT, oci_hooks_array, 0, 0 },
    2164                 :            :                 { "poststart", JSON_VARIANT_OBJECT, oci_hooks_array, 0, 0 },
    2165                 :            :                 { "poststop",  JSON_VARIANT_OBJECT, oci_hooks_array, 0, 0 },
    2166                 :            :                 {}
    2167                 :            :         };
    2168                 :            : 
    2169                 :          0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
    2170                 :            : }
    2171                 :            : 
    2172                 :          0 : static int oci_annotations(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    2173                 :            :         JsonVariant *w;
    2174                 :            :         const char *k;
    2175                 :            : 
    2176   [ #  #  #  #  :          0 :         JSON_VARIANT_OBJECT_FOREACH(k, w, v) {
                   #  # ]
    2177                 :            : 
    2178         [ #  # ]:          0 :                 if (isempty(k))
    2179         [ #  # ]:          0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    2180                 :            :                                         "Annotation with empty key, refusing.");
    2181                 :            : 
    2182         [ #  # ]:          0 :                 if (!json_variant_is_string(w))
    2183         [ #  # ]:          0 :                         return json_log(w, flags, SYNTHETIC_ERRNO(EINVAL),
    2184                 :            :                                         "Annotation has non-string value, refusing.");
    2185                 :            : 
    2186         [ #  # ]:          0 :                 json_log(w, flags|JSON_DEBUG, 0, "Ignoring annotation '%s' with value '%s'.", k, json_variant_string(w));
    2187                 :            :         }
    2188                 :            : 
    2189                 :          0 :         return 0;
    2190                 :            : }
    2191                 :            : 
    2192                 :          0 : int oci_load(FILE *f, const char *bundle, Settings **ret) {
    2193                 :            : 
    2194                 :            :         static const JsonDispatch table[] = {
    2195                 :            :                 { "ociVersion",  JSON_VARIANT_STRING, NULL,            0, JSON_MANDATORY },
    2196                 :            :                 { "process",     JSON_VARIANT_OBJECT, oci_process,     0, 0 },
    2197                 :            :                 { "root",        JSON_VARIANT_OBJECT, oci_root,        0, 0 },
    2198                 :            :                 { "hostname",    JSON_VARIANT_STRING, oci_hostname,    0, 0 },
    2199                 :            :                 { "mounts",      JSON_VARIANT_ARRAY,  oci_mounts,      0, 0 },
    2200                 :            :                 { "linux",       JSON_VARIANT_OBJECT, oci_linux,       0, 0 },
    2201                 :            :                 { "hooks",       JSON_VARIANT_OBJECT, oci_hooks,       0, 0 },
    2202                 :            :                 { "annotations", JSON_VARIANT_OBJECT, oci_annotations, 0, 0 },
    2203                 :            :                 {}
    2204                 :            :         };
    2205                 :            : 
    2206                 :          0 :         _cleanup_(json_variant_unrefp) JsonVariant *oci = NULL;
    2207                 :          0 :         _cleanup_(settings_freep) Settings *s = NULL;
    2208                 :          0 :         unsigned line = 0, column = 0;
    2209                 :            :         JsonVariant *v;
    2210                 :            :         const char *path;
    2211                 :            :         int r;
    2212                 :            : 
    2213         [ #  # ]:          0 :         assert_se(bundle);
    2214                 :            : 
    2215   [ #  #  #  #  :          0 :         path = strjoina(bundle, "/config.json");
          #  #  #  #  #  
                #  #  # ]
    2216                 :            : 
    2217                 :          0 :         r = json_parse_file(f, path, &oci, &line, &column);
    2218         [ #  # ]:          0 :         if (r < 0) {
    2219   [ #  #  #  # ]:          0 :                 if (line != 0 && column != 0)
    2220         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", path, line, column);
    2221                 :            :                 else
    2222         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to parse '%s': %m", path);
    2223                 :            :         }
    2224                 :            : 
    2225                 :          0 :         v = json_variant_by_key(oci, "ociVersion");
    2226         [ #  # ]:          0 :         if (!v) {
    2227         [ #  # ]:          0 :                 log_error("JSON file '%s' is not an OCI bundle configuration file. Refusing.", path);
    2228                 :          0 :                 return -EINVAL;
    2229                 :            :         }
    2230         [ #  # ]:          0 :         if (!streq_ptr(json_variant_string(v), "1.0.0")) {
    2231         [ #  # ]:          0 :                 log_error("OCI bundle version not supported: %s", strna(json_variant_string(v)));
    2232                 :          0 :                 return -EINVAL;
    2233                 :            :         }
    2234                 :            : 
    2235                 :            :         // {
    2236                 :            :         //         _cleanup_free_ char *formatted = NULL;
    2237                 :            :         //         assert_se(json_variant_format(oci, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR, &formatted) >= 0);
    2238                 :            :         //         fputs(formatted, stdout);
    2239                 :            :         // }
    2240                 :            : 
    2241                 :          0 :         s = settings_new();
    2242         [ #  # ]:          0 :         if (!s)
    2243                 :          0 :                 return log_oom();
    2244                 :            : 
    2245                 :          0 :         s->start_mode = START_PID1;
    2246                 :          0 :         s->resolv_conf = RESOLV_CONF_OFF;
    2247                 :          0 :         s->link_journal = LINK_NO;
    2248                 :          0 :         s->timezone = TIMEZONE_OFF;
    2249                 :            : 
    2250                 :          0 :         s->bundle = strdup(bundle);
    2251         [ #  # ]:          0 :         if (!s->bundle)
    2252                 :          0 :                 return log_oom();
    2253                 :            : 
    2254                 :          0 :         r = json_dispatch(oci, table, oci_unexpected, 0, s);
    2255         [ #  # ]:          0 :         if (r < 0)
    2256                 :          0 :                 return r;
    2257                 :            : 
    2258         [ #  # ]:          0 :         if (s->properties) {
    2259                 :          0 :                 r = sd_bus_message_seal(s->properties, 0, 0);
    2260         [ #  # ]:          0 :                 if (r < 0)
    2261         [ #  # ]:          0 :                         return log_error_errno(r, "Cannot seal properties bus message: %m");
    2262                 :            :         }
    2263                 :            : 
    2264                 :          0 :         *ret = TAKE_PTR(s);
    2265                 :          0 :         return 0;
    2266                 :            : }

Generated by: LCOV version 1.14