diff options
Diffstat (limited to 'src/core/namespace.c')
-rw-r--r-- | src/core/namespace.c | 360 |
1 files changed, 228 insertions, 132 deletions
diff --git a/src/core/namespace.c b/src/core/namespace.c index aca47a4d2f..834883267c 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -50,6 +50,8 @@ typedef enum MountMode { /* This is ordered by priority! */ INACCESSIBLE, + BIND_MOUNT, + BIND_MOUNT_RECURSIVE, READONLY, PRIVATE_TMP, PRIVATE_VAR_TMP, @@ -57,13 +59,16 @@ typedef enum MountMode { READWRITE, } MountMode; -typedef struct BindMount { +typedef struct MountEntry { const char *path_const; /* Memory allocated on stack or static */ - MountMode mode:6; + MountMode mode:5; bool ignore:1; /* Ignore if path does not exist? */ bool has_prefix:1; /* Already is prefixed by the root dir? */ + bool read_only:1; /* Shall this mount point be read-only? */ char *path_malloc; /* Use this instead of 'path' if we had to allocate memory */ -} BindMount; + const char *source_const; /* The source path, for bind mounts */ + char *source_malloc; +} MountEntry; /* * The following Protect tables are to protect paths and mark some of them @@ -74,7 +79,7 @@ typedef struct BindMount { */ /* ProtectKernelTunables= option and the related filesystem APIs */ -static const BindMount protect_kernel_tunables_table[] = { +static const MountEntry protect_kernel_tunables_table[] = { { "/proc/sys", READONLY, false }, { "/proc/sysrq-trigger", READONLY, true }, { "/proc/latency_stats", READONLY, true }, @@ -93,7 +98,7 @@ static const BindMount protect_kernel_tunables_table[] = { }; /* ProtectKernelModules= option */ -static const BindMount protect_kernel_modules_table[] = { +static const MountEntry protect_kernel_modules_table[] = { #ifdef HAVE_SPLIT_USR { "/lib/modules", INACCESSIBLE, true }, #endif @@ -104,28 +109,28 @@ static const BindMount protect_kernel_modules_table[] = { * ProtectHome=read-only table, protect $HOME and $XDG_RUNTIME_DIR and rest of * system should be protected by ProtectSystem= */ -static const BindMount protect_home_read_only_table[] = { +static const MountEntry protect_home_read_only_table[] = { { "/home", READONLY, true }, { "/run/user", READONLY, true }, { "/root", READONLY, true }, }; /* ProtectHome=yes table */ -static const BindMount protect_home_yes_table[] = { +static const MountEntry protect_home_yes_table[] = { { "/home", INACCESSIBLE, true }, { "/run/user", INACCESSIBLE, true }, { "/root", INACCESSIBLE, true }, }; /* ProtectSystem=yes table */ -static const BindMount protect_system_yes_table[] = { +static const MountEntry protect_system_yes_table[] = { { "/usr", READONLY, false }, { "/boot", READONLY, true }, { "/efi", READONLY, true }, }; /* ProtectSystem=full includes ProtectSystem=yes */ -static const BindMount protect_system_full_table[] = { +static const MountEntry protect_system_full_table[] = { { "/usr", READONLY, false }, { "/boot", READONLY, true }, { "/efi", READONLY, true }, @@ -140,17 +145,17 @@ static const BindMount protect_system_full_table[] = { * (And of course /home and friends are also left writable, as ProtectHome= * shall manage those, orthogonally). */ -static const BindMount protect_system_strict_table[] = { - { "/", READONLY, false }, - { "/proc", READWRITE, false }, /* ProtectKernelTunables= */ - { "/sys", READWRITE, false }, /* ProtectKernelTunables= */ - { "/dev", READWRITE, false }, /* PrivateDevices= */ - { "/home", READWRITE, true }, /* ProtectHome= */ - { "/run/user", READWRITE, true }, /* ProtectHome= */ - { "/root", READWRITE, true }, /* ProtectHome= */ +static const MountEntry protect_system_strict_table[] = { + { "/", READONLY, false }, + { "/proc", READWRITE, false }, /* ProtectKernelTunables= */ + { "/sys", READWRITE, false }, /* ProtectKernelTunables= */ + { "/dev", READWRITE, false }, /* PrivateDevices= */ + { "/home", READWRITE, true }, /* ProtectHome= */ + { "/run/user", READWRITE, true }, /* ProtectHome= */ + { "/root", READWRITE, true }, /* ProtectHome= */ }; -static const char *bind_mount_path(const BindMount *p) { +static const char *mount_entry_path(const MountEntry *p) { assert(p); /* Returns the path of this bind mount. If the malloc()-allocated ->path_buffer field is set we return that, @@ -159,7 +164,19 @@ static const char *bind_mount_path(const BindMount *p) { return p->path_malloc ?: p->path_const; } -static int append_access_mounts(BindMount **p, char **strv, MountMode mode) { +static bool mount_entry_read_only(const MountEntry *p) { + assert(p); + + return p->read_only || IN_SET(p->mode, READONLY, INACCESSIBLE); +} + +static const char *mount_entry_source(const MountEntry *p) { + assert(p); + + return p->source_malloc ?: p->source_const; +} + +static int append_access_mounts(MountEntry **p, char **strv, MountMode mode) { char **i; assert(p); @@ -183,7 +200,7 @@ static int append_access_mounts(BindMount **p, char **strv, MountMode mode) { if (!path_is_absolute(e)) return -EINVAL; - *((*p)++) = (BindMount) { + *((*p)++) = (MountEntry) { .path_const = e, .mode = mode, .ignore = ignore, @@ -194,7 +211,26 @@ static int append_access_mounts(BindMount **p, char **strv, MountMode mode) { return 0; } -static int append_static_mounts(BindMount **p, const BindMount *mounts, unsigned n, bool ignore_protect) { +static int append_bind_mounts(MountEntry **p, const BindMount *binds, unsigned n) { + unsigned i; + + assert(p); + + for (i = 0; i < n; i++) { + const BindMount *b = binds + i; + + *((*p)++) = (MountEntry) { + .path_const = b->destination, + .mode = b->recursive ? BIND_MOUNT_RECURSIVE : BIND_MOUNT, + .read_only = b->read_only, + .source_const = b->source, + }; + } + + return 0; +} + +static int append_static_mounts(MountEntry **p, const MountEntry *mounts, unsigned n, bool ignore_protect) { unsigned i; assert(p); @@ -203,8 +239,8 @@ static int append_static_mounts(BindMount **p, const BindMount *mounts, unsigned /* Adds a list of static pre-defined entries */ for (i = 0; i < n; i++) - *((*p)++) = (BindMount) { - .path_const = bind_mount_path(mounts+i), + *((*p)++) = (MountEntry) { + .path_const = mount_entry_path(mounts+i), .mode = mounts[i].mode, .ignore = mounts[i].ignore || ignore_protect, }; @@ -212,7 +248,7 @@ static int append_static_mounts(BindMount **p, const BindMount *mounts, unsigned return 0; } -static int append_protect_home(BindMount **p, ProtectHome protect_home, bool ignore_protect) { +static int append_protect_home(MountEntry **p, ProtectHome protect_home, bool ignore_protect) { assert(p); switch (protect_home) { @@ -231,7 +267,7 @@ static int append_protect_home(BindMount **p, ProtectHome protect_home, bool ign } } -static int append_protect_system(BindMount **p, ProtectSystem protect_system, bool ignore_protect) { +static int append_protect_system(MountEntry **p, ProtectSystem protect_system, bool ignore_protect) { assert(p); switch (protect_system) { @@ -254,11 +290,11 @@ static int append_protect_system(BindMount **p, ProtectSystem protect_system, bo } static int mount_path_compare(const void *a, const void *b) { - const BindMount *p = a, *q = b; + const MountEntry *p = a, *q = b; int d; /* If the paths are not equal, then order prefixes first */ - d = path_compare(bind_mount_path(p), bind_mount_path(q)); + d = path_compare(mount_entry_path(p), mount_entry_path(q)); if (d != 0) return d; @@ -272,7 +308,7 @@ static int mount_path_compare(const void *a, const void *b) { return 0; } -static int prefix_where_needed(BindMount *m, unsigned n, const char *root_directory) { +static int prefix_where_needed(MountEntry *m, unsigned n, const char *root_directory) { unsigned i; /* Prefixes all paths in the bind mount table with the root directory if it is specified and the entry needs @@ -287,7 +323,7 @@ static int prefix_where_needed(BindMount *m, unsigned n, const char *root_direct if (m[i].has_prefix) continue; - s = prefix_root(root_directory, bind_mount_path(m+i)); + s = prefix_root(root_directory, mount_entry_path(m+i)); if (!s) return -ENOMEM; @@ -300,8 +336,8 @@ static int prefix_where_needed(BindMount *m, unsigned n, const char *root_direct return 0; } -static void drop_duplicates(BindMount *m, unsigned *n) { - BindMount *f, *t, *previous; +static void drop_duplicates(MountEntry *m, unsigned *n) { + MountEntry *f, *t, *previous; assert(m); assert(n); @@ -312,8 +348,9 @@ static void drop_duplicates(BindMount *m, unsigned *n) { /* The first one wins (which is the one with the more restrictive mode), see mount_path_compare() * above. */ - if (previous && path_equal(bind_mount_path(f), bind_mount_path(previous))) { - log_debug("%s is duplicate.", bind_mount_path(f)); + if (previous && path_equal(mount_entry_path(f), mount_entry_path(previous))) { + log_debug("%s is duplicate.", mount_entry_path(f)); + previous->read_only = previous->read_only || mount_entry_read_only(f); /* Propagate the read-only flag to the remaining entry */ f->path_malloc = mfree(f->path_malloc); continue; } @@ -326,8 +363,8 @@ static void drop_duplicates(BindMount *m, unsigned *n) { *n = t - m; } -static void drop_inaccessible(BindMount *m, unsigned *n) { - BindMount *f, *t; +static void drop_inaccessible(MountEntry *m, unsigned *n) { + MountEntry *f, *t; const char *clear = NULL; assert(m); @@ -340,13 +377,13 @@ static void drop_inaccessible(BindMount *m, unsigned *n) { /* If we found a path set for INACCESSIBLE earlier, and this entry has it as prefix we should drop * it, as inaccessible paths really should drop the entire subtree. */ - if (clear && path_startswith(bind_mount_path(f), clear)) { - log_debug("%s is masked by %s.", bind_mount_path(f), clear); + if (clear && path_startswith(mount_entry_path(f), clear)) { + log_debug("%s is masked by %s.", mount_entry_path(f), clear); f->path_malloc = mfree(f->path_malloc); continue; } - clear = f->mode == INACCESSIBLE ? bind_mount_path(f) : NULL; + clear = f->mode == INACCESSIBLE ? mount_entry_path(f) : NULL; *t = *f; t++; @@ -355,8 +392,8 @@ static void drop_inaccessible(BindMount *m, unsigned *n) { *n = t - m; } -static void drop_nop(BindMount *m, unsigned *n) { - BindMount *f, *t; +static void drop_nop(MountEntry *m, unsigned *n) { + MountEntry *f, *t; assert(m); assert(n); @@ -368,12 +405,12 @@ static void drop_nop(BindMount *m, unsigned *n) { /* Only suppress such subtrees for READONLY and READWRITE entries */ if (IN_SET(f->mode, READONLY, READWRITE)) { - BindMount *p; + MountEntry *p; bool found = false; /* Now let's find the first parent of the entry we are looking at. */ for (p = t-1; p >= m; p--) { - if (path_startswith(bind_mount_path(f), bind_mount_path(p))) { + if (path_startswith(mount_entry_path(f), mount_entry_path(p))) { found = true; break; } @@ -381,7 +418,7 @@ static void drop_nop(BindMount *m, unsigned *n) { /* We found it, let's see if it's the same mode, if so, we can drop this entry */ if (found && p->mode == f->mode) { - log_debug("%s is redundant by %s", bind_mount_path(f), bind_mount_path(p)); + log_debug("%s is redundant by %s", mount_entry_path(f), mount_entry_path(p)); f->path_malloc = mfree(f->path_malloc); continue; } @@ -394,8 +431,8 @@ static void drop_nop(BindMount *m, unsigned *n) { *n = t - m; } -static void drop_outside_root(const char *root_directory, BindMount *m, unsigned *n) { - BindMount *f, *t; +static void drop_outside_root(const char *root_directory, MountEntry *m, unsigned *n) { + MountEntry *f, *t; assert(m); assert(n); @@ -408,8 +445,8 @@ static void drop_outside_root(const char *root_directory, BindMount *m, unsigned for (f = m, t = m; f < m + *n; f++) { - if (!path_startswith(bind_mount_path(f), root_directory)) { - log_debug("%s is outside of root directory.", bind_mount_path(f)); + if (!path_startswith(mount_entry_path(f), root_directory)) { + log_debug("%s is outside of root directory.", mount_entry_path(f)); f->path_malloc = mfree(f->path_malloc); continue; } @@ -421,7 +458,7 @@ static void drop_outside_root(const char *root_directory, BindMount *m, unsigned *n = t - m; } -static int mount_dev(BindMount *m) { +static int mount_dev(MountEntry *m) { static const char devnodes[] = "/dev/null\0" "/dev/zero\0" @@ -526,11 +563,11 @@ static int mount_dev(BindMount *m) { * missing when the service is started with RootDirectory. This is * consistent with mount units creating the mount points when missing. */ - (void) mkdir_p_label(bind_mount_path(m), 0755); + (void) mkdir_p_label(mount_entry_path(m), 0755); /* Unmount everything in old /dev */ - umount_recursive(bind_mount_path(m), 0); - if (mount(dev, bind_mount_path(m), NULL, MS_MOVE, NULL) < 0) { + umount_recursive(mount_entry_path(m), 0); + if (mount(dev, mount_entry_path(m), NULL, MS_MOVE, NULL) < 0) { r = -errno; goto fail; } @@ -560,17 +597,54 @@ fail: return r; } +static int mount_entry_chase( + const char *root_directory, + MountEntry *m, + const char *path, + char **location) { + + char *chased; + int r; + + assert(m); + + /* Since mount() will always follow symlinks and we need to take the different root directory into account we + * chase the symlinks on our own first. This is called for the destination path, as well as the source path (if + * that applies). The result is stored in "location". */ + + r = chase_symlinks(path, root_directory, 0, &chased); + if (r == -ENOENT && m->ignore) { + log_debug_errno(r, "Path %s does not exist, ignoring.", path); + return 0; + } + if (r < 0) + return log_debug_errno(r, "Failed to follow symlinks on %s: %m", path); + + log_debug("Followed symlinks %s → %s.", path, chased); + + free(*location); + *location = chased; + + return 1; +} + static int apply_mount( - BindMount *m, + const char *root_directory, + MountEntry *m, const char *tmp_dir, const char *var_tmp_dir) { const char *what; + bool rbind = true; int r; assert(m); - log_debug("Applying namespace mount on %s", bind_mount_path(m)); + r = mount_entry_chase(root_directory, m, mount_entry_path(m), &m->path_malloc); + if (r <= 0) + return r; + + log_debug("Applying namespace mount on %s", mount_entry_path(m)); switch (m->mode) { @@ -580,10 +654,10 @@ static int apply_mount( /* First, get rid of everything that is below if there * is anything... Then, overmount it with an * inaccessible path. */ - (void) umount_recursive(bind_mount_path(m), 0); + (void) umount_recursive(mount_entry_path(m), 0); - if (lstat(bind_mount_path(m), &target) < 0) - return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", bind_mount_path(m)); + if (lstat(mount_entry_path(m), &target) < 0) + return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", mount_entry_path(m)); what = mode_to_inaccessible_node(target.st_mode); if (!what) { @@ -595,14 +669,26 @@ static int apply_mount( case READONLY: case READWRITE: - - r = path_is_mount_point(bind_mount_path(m), NULL, 0); + r = path_is_mount_point(mount_entry_path(m), root_directory, 0); if (r < 0) - return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", bind_mount_path(m)); + return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", mount_entry_path(m)); if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */ return 0; /* This isn't a mount point yet, let's make it one. */ - what = bind_mount_path(m); + what = mount_entry_path(m); + break; + + case BIND_MOUNT: + rbind = false; + /* fallthrough */ + + case BIND_MOUNT_RECURSIVE: + /* Also chase the source mount */ + r = mount_entry_chase(root_directory, m, mount_entry_source(m), &m->source_malloc); + if (r <= 0) + return r; + + what = mount_entry_source(m); break; case PRIVATE_TMP: @@ -622,22 +708,22 @@ static int apply_mount( assert(what); - if (mount(what, bind_mount_path(m), NULL, MS_BIND|MS_REC, NULL) < 0) - return log_debug_errno(errno, "Failed to mount %s to %s: %m", what, bind_mount_path(m)); + if (mount(what, mount_entry_path(m), NULL, MS_BIND|(rbind ? MS_REC : 0), NULL) < 0) + return log_debug_errno(errno, "Failed to mount %s to %s: %m", what, mount_entry_path(m)); - log_debug("Successfully mounted %s to %s", what, bind_mount_path(m)); + log_debug("Successfully mounted %s to %s", what, mount_entry_path(m)); return 0; } -static int make_read_only(BindMount *m, char **blacklist) { +static int make_read_only(MountEntry *m, char **blacklist) { int r = 0; assert(m); - if (IN_SET(m->mode, INACCESSIBLE, READONLY)) - r = bind_remount_recursive(bind_mount_path(m), true, blacklist); + if (mount_entry_read_only(m)) + r = bind_remount_recursive(mount_entry_path(m), true, blacklist); else if (m->mode == PRIVATE_DEV) { /* Can be readonly but the submounts can't*/ - if (mount(NULL, bind_mount_path(m), NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0) + if (mount(NULL, mount_entry_path(m), NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0) r = -errno; } else return 0; @@ -646,50 +732,9 @@ static int make_read_only(BindMount *m, char **blacklist) { * already stays this way. This improves compatibility with container managers, where we won't attempt to undo * read-only mounts already applied. */ - return r; -} - -/* Chase symlinks and remove failed paths from mounts */ -static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned *n) { - BindMount *f, *t; - int r = 0; - - assert(m); - assert(n); - - /* Since mount() will always follow symlinks and we need to take the different root directory into account we - * chase the symlinks on our own first. This call wil do so for all entries and remove all entries where we - * can't resolve the path, and which have been marked for such removal. */ - - for (f = m, t = m; f < m + *n; f++) { - _cleanup_free_ char *chased = NULL; - int k; - - k = chase_symlinks(bind_mount_path(f), root_directory, 0, &chased); - if (k < 0) { - /* Get only real errors */ - if (r >= 0 && (k != -ENOENT || !f->ignore)) - r = k; - - /* Doesn't exist or failed? Then remove it and continue! */ - log_debug_errno(k, "Failed to chase symlinks for %s: %m", bind_mount_path(f)); - f->path_malloc = mfree(f->path_malloc); - continue; - } - - if (!path_equal(bind_mount_path(f), chased)) { - log_debug("Chased %s → %s", bind_mount_path(f), chased); - - free(f->path_malloc); - f->path_malloc = chased; - chased = NULL; - } - - *t = *f; - t++; - } + if (r == -ENOENT && m->ignore) + r = 0; - *n = t - m; return r; } @@ -698,6 +743,8 @@ static unsigned namespace_calculate_mounts( char** read_write_paths, char** read_only_paths, char** inaccessible_paths, + const BindMount *bind_mounts, + unsigned n_bind_mounts, const char* tmp_dir, const char* var_tmp_dir, ProtectHome protect_home, @@ -722,6 +769,7 @@ static unsigned namespace_calculate_mounts( strv_length(read_write_paths) + strv_length(read_only_paths) + strv_length(inaccessible_paths) + + n_bind_mounts + ns_info->private_dev + (ns_info->protect_kernel_tunables ? ELEMENTSOF(protect_kernel_tunables_table) : 0) + (ns_info->protect_control_groups ? 1 : 0) + @@ -735,13 +783,15 @@ int setup_namespace( char** read_write_paths, char** read_only_paths, char** inaccessible_paths, + const BindMount *bind_mounts, + unsigned n_bind_mounts, const char* tmp_dir, const char* var_tmp_dir, ProtectHome protect_home, ProtectSystem protect_system, unsigned long mount_flags) { - BindMount *m, *mounts = NULL; + MountEntry *m, *mounts = NULL; bool make_slave = false; unsigned n_mounts; int r = 0; @@ -749,19 +799,21 @@ int setup_namespace( if (mount_flags == 0) mount_flags = MS_SHARED; - n_mounts = namespace_calculate_mounts(ns_info, - read_write_paths, - read_only_paths, - inaccessible_paths, - tmp_dir, var_tmp_dir, - protect_home, protect_system); + n_mounts = namespace_calculate_mounts( + ns_info, + read_write_paths, + read_only_paths, + inaccessible_paths, + bind_mounts, n_bind_mounts, + tmp_dir, var_tmp_dir, + protect_home, protect_system); /* Set mount slave mode */ if (root_directory || n_mounts > 0) make_slave = true; if (n_mounts > 0) { - m = mounts = (BindMount *) alloca0(n_mounts * sizeof(BindMount)); + m = mounts = (MountEntry *) alloca0(n_mounts * sizeof(MountEntry)); r = append_access_mounts(&m, read_write_paths, READWRITE); if (r < 0) goto finish; @@ -774,22 +826,26 @@ int setup_namespace( if (r < 0) goto finish; + r = append_bind_mounts(&m, bind_mounts, n_bind_mounts); + if (r < 0) + goto finish; + if (tmp_dir) { - *(m++) = (BindMount) { + *(m++) = (MountEntry) { .path_const = "/tmp", .mode = PRIVATE_TMP, }; } if (var_tmp_dir) { - *(m++) = (BindMount) { + *(m++) = (MountEntry) { .path_const = "/var/tmp", .mode = PRIVATE_VAR_TMP, }; } if (ns_info->private_dev) { - *(m++) = (BindMount) { + *(m++) = (MountEntry) { .path_const = "/dev", .mode = PRIVATE_DEV, }; @@ -808,7 +864,7 @@ int setup_namespace( } if (ns_info->protect_control_groups) { - *(m++) = (BindMount) { + *(m++) = (MountEntry) { .path_const = "/sys/fs/cgroup", .mode = READONLY, }; @@ -829,14 +885,7 @@ int setup_namespace( if (r < 0) goto finish; - /* Resolve symlinks manually first, as mount() will always follow them relative to the host's - * root. Moreover we want to suppress duplicates based on the resolved paths. This of course is a bit - * racy. */ - r = chase_all_symlinks(root_directory, mounts, &n_mounts); - if (r < 0) - goto finish; - - qsort(mounts, n_mounts, sizeof(BindMount), mount_path_compare); + qsort(mounts, n_mounts, sizeof(MountEntry), mount_path_compare); drop_duplicates(mounts, &n_mounts); drop_outside_root(root_directory, mounts, &n_mounts); @@ -877,7 +926,7 @@ int setup_namespace( /* First round, add in all special mounts we need */ for (m = mounts; m < mounts + n_mounts; ++m) { - r = apply_mount(m, tmp_dir, var_tmp_dir); + r = apply_mount(root_directory, m, tmp_dir, var_tmp_dir); if (r < 0) goto finish; } @@ -885,7 +934,7 @@ int setup_namespace( /* Create a blacklist we can pass to bind_mount_recursive() */ blacklist = newa(char*, n_mounts+1); for (j = 0; j < n_mounts; j++) - blacklist[j] = (char*) bind_mount_path(mounts+j); + blacklist[j] = (char*) mount_entry_path(mounts+j); blacklist[j] = NULL; /* Second round, flip the ro bits if necessary. */ @@ -920,6 +969,53 @@ finish: return r; } +void bind_mount_free_many(BindMount *b, unsigned n) { + unsigned i; + + assert(b || n == 0); + + for (i = 0; i < n; i++) { + free(b[i].source); + free(b[i].destination); + } + + free(b); +} + +int bind_mount_add(BindMount **b, unsigned *n, const BindMount *item) { + _cleanup_free_ char *s = NULL, *d = NULL; + BindMount *c; + + assert(b); + assert(n); + assert(item); + + s = strdup(item->source); + if (!s) + return -ENOMEM; + + d = strdup(item->destination); + if (!d) + return -ENOMEM; + + c = realloc_multiply(*b, sizeof(BindMount), *n + 1); + if (!c) + return -ENOMEM; + + *b = c; + + c[(*n) ++] = (BindMount) { + .source = s, + .destination = d, + .read_only = item->read_only, + .recursive = item->recursive, + .ignore_enoent = item->ignore_enoent, + }; + + s = d = NULL; + return 0; +} + static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) { _cleanup_free_ char *x = NULL; char bid[SD_ID128_STRING_MAX]; |