/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <sched.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "alloc-util.h"
#include "dev-setup.h"
#include "fd-util.h"
#include "loopback-setup.h"
#include "missing.h"
#include "mkdir.h"
#include "mount-util.h"
#include "namespace.h"
#include "path-util.h"
#include "selinux-util.h"
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
#include "umask-util.h"
#include "user-util.h"
#include "util.h"
typedef enum MountMode {
/* This is ordered by priority! */
} MountMode;
typedef struct BindMount {
const char *path;
bool done;
bool ignore;
} BindMount;
char **i;
assert(p);
(*p)->ignore = false;
(*p)->done = false;
(*p)->ignore = true;
(*i)++;
}
if (!path_is_absolute(*i))
return -EINVAL;
(*p)->path = *i;
(*p)++;
}
return 0;
}
static int mount_path_compare(const void *a, const void *b) {
const BindMount *p = a, *q = b;
int d;
if (d == 0) {
/* If the paths are equal, check the mode */
return -1;
return 1;
return 0;
}
/* If the paths are not equal, then order prefixes first */
return d;
}
assert(m);
assert(n);
/* The first one wins */
continue;
*t = *f;
previous = t;
t++;
}
*n = t - m;
}
static const char devnodes[] =
const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL;
int r;
assert(m);
u = umask(0000);
if (!mkdtemp(temporary_mount))
return -errno;
r = -errno;
goto fail;
}
r = -errno;
goto fail;
}
r = -errno;
goto fail;
}
if (r < 0) {
r = -errno;
goto fail;
}
if (r < 0) {
continue;
r = -errno;
goto fail;
}
r = -EINVAL;
goto fail;
}
continue;
if (!dn) {
r = -ENOMEM;
goto fail;
}
if (r < 0) {
r = -errno;
goto fail;
}
}
/* Create the /dev directory if missing. It is more likely to be
* missing when the service is started with RootDirectory. This is
* consistent with mount units creating the mount points when missing.
*/
r = -errno;
goto fail;
}
return 0;
fail:
if (devpts)
if (devshm)
if (devhugepages)
if (devmqueue)
return r;
}
int r;
assert(m);
u = umask(0000);
if (!mkdtemp(temporary_mount))
r = -errno;
goto fail;
}
* bind-mount the custom endpoint over. */
goto fail;
}
busnode);
goto fail;
}
if (r < 0) {
m->path);
goto fail;
}
if (!basepath) {
r = -ENOMEM;
goto fail;
}
basepath);
goto fail;
}
return 0;
fail:
if (busnode) {
}
return r;
}
static int apply_mount(
BindMount *m,
const char *tmp_dir,
const char *var_tmp_dir) {
const char *what;
int r;
assert(m);
switch (m->mode) {
case INACCESSIBLE:
/* First, get rid of everything that is below if there
* is anything... Then, overmount it with an
* inaccessible directory. */
umount_recursive(m->path, 0);
what = "/run/systemd/inaccessible";
break;
case READONLY:
case READWRITE:
/* Nothing to mount here, we just later toggle the
* MS_RDONLY bit for the mount point */
return 0;
case PRIVATE_TMP:
break;
case PRIVATE_VAR_TMP:
what = var_tmp_dir;
break;
case PRIVATE_DEV:
return mount_dev(m);
case PRIVATE_BUS_ENDPOINT:
return mount_kdbus(m);
default:
assert_not_reached("Unknown mode");
}
if (r >= 0)
return 0;
return r;
}
int r;
assert(m);
r = bind_remount_recursive(m->path, true);
r = bind_remount_recursive(m->path, false);
else
r = 0;
return 0;
return r;
}
int setup_namespace(
const char* root_directory,
char** read_write_dirs,
char** read_only_dirs,
char** inaccessible_dirs,
const char* tmp_dir,
const char* var_tmp_dir,
const char* bus_endpoint_path,
bool private_dev,
unsigned long mount_flags) {
unsigned n;
int r = 0;
if (mount_flags == 0)
if (unshare(CLONE_NEWNS) < 0)
return -errno;
if (n > 0) {
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (tmp_dir) {
m->mode = PRIVATE_TMP;
m++;
}
if (var_tmp_dir) {
m->mode = PRIVATE_VAR_TMP;
m++;
}
if (private_dev) {
m->mode = PRIVATE_DEV;
m++;
}
if (bus_endpoint_path) {
m->mode = PRIVATE_BUS_ENDPOINT;
m++;
}
if (protect_home != PROTECT_HOME_NO) {
if (r < 0)
return r;
}
if (protect_system != PROTECT_SYSTEM_NO) {
if (r < 0)
return r;
}
drop_duplicates(mounts, &n);
}
if (n > 0 || root_directory) {
/* Remount / as SLAVE so that nothing now mounted in the namespace
shows up in the parent */
return -errno;
}
if (root_directory) {
/* Turn directory into bind mount */
return -errno;
}
if (n > 0) {
if (r < 0)
goto fail;
}
r = make_read_only(m);
if (r < 0)
goto fail;
}
}
if (root_directory) {
/* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
/* at this point, we cannot rollback */
if (r < 0)
return r;
}
/* Remount / as the desired mode. Not that this will not
* reestablish propagation from our side to the host, since
* what's disconnected is disconnected. */
/* at this point, we cannot rollback */
return -errno;
return 0;
fail:
if (n > 0) {
if (m->done)
}
return r;
}
_cleanup_free_ char *x = NULL;
int r;
/* We include the boot id in the directory so that after a
* reboot we can easily identify obsolete directories. */
r = sd_id128_get_boot(&boot_id);
if (r < 0)
return r;
x = strjoin(prefix, "/systemd-private-", sd_id128_to_string(boot_id, bid), "-", id, "-XXXXXX", NULL);
if (!x)
return -ENOMEM;
RUN_WITH_UMASK(0077)
if (!mkdtemp(x))
return -errno;
RUN_WITH_UMASK(0000) {
char *y;
y = strjoina(x, "/tmp");
return -errno;
}
*path = x;
x = NULL;
return 0;
}
char *a, *b;
int r;
if (r < 0)
return r;
if (r < 0) {
char *t;
t = strjoina(a, "/tmp");
rmdir(t);
rmdir(a);
free(a);
return r;
}
*tmp_dir = a;
*var_tmp_dir = b;
return 0;
}
int r, q;
assert(netns_storage_socket[0] >= 0);
/* We use the passed socketpair as a storage buffer for our
* namespace reference fd. Whatever process runs this first
* shall create a new namespace, all others should just join
* it. To serialize that we use a file lock on the socket
* pair.
*
* It's a bit crazy, but hey, works great! */
return -errno;
/* Nothing stored yet, so let's create a new namespace */
if (unshare(CLONE_NEWNET) < 0) {
r = -errno;
goto fail;
}
if (netns < 0) {
r = -errno;
goto fail;
}
r = 1;
} else if (netns < 0) {
r = netns;
goto fail;
} else {
/* Yay, found something, so let's join the namespace */
r = -errno;
goto fail;
}
r = 0;
}
if (q < 0) {
r = q;
goto fail;
}
fail:
return r;
}
[PROTECT_HOME_NO] = "no",
[PROTECT_HOME_YES] = "yes",
[PROTECT_HOME_READ_ONLY] = "read-only",
};
[PROTECT_SYSTEM_NO] = "no",
[PROTECT_SYSTEM_YES] = "yes",
[PROTECT_SYSTEM_FULL] = "full",
};