2010-08-14 19:59:25 +02:00
|
|
|
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
2010-04-21 22:15:06 +02:00
|
|
|
|
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2010 Lennart Poettering
|
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
2012-04-12 00:20:58 +02:00
|
|
|
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
|
2010-04-21 22:15:06 +02:00
|
|
|
(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
|
2012-04-12 00:20:58 +02:00
|
|
|
Lesser General Public License for more details.
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2012-04-12 00:20:58 +02:00
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
2010-04-21 22:15:06 +02:00
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include <errno.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <sched.h>
|
2010-04-21 22:15:06 +02:00
|
|
|
#include <stdio.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include <sys/mount.h>
|
2010-04-21 22:15:06 +02:00
|
|
|
#include <sys/stat.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <unistd.h>
|
2010-04-24 05:05:01 +02:00
|
|
|
#include <linux/fs.h>
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2014-01-20 19:54:51 +01:00
|
|
|
#include "dev-setup.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "loopback-setup.h"
|
|
|
|
#include "missing.h"
|
|
|
|
#include "mkdir.h"
|
2015-10-26 18:44:13 +01:00
|
|
|
#include "mount-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "namespace.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "path-util.h"
|
2014-12-27 18:46:36 +01:00
|
|
|
#include "selinux-util.h"
|
2015-10-26 01:09:02 +01:00
|
|
|
#include "socket-util.h"
|
2015-10-26 22:31:05 +01:00
|
|
|
#include "string-table.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
|
|
|
#include "strv.h"
|
2015-10-26 23:20:41 +01:00
|
|
|
#include "umask-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "util.h"
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
typedef enum MountMode {
|
2010-04-21 22:15:06 +02:00
|
|
|
/* This is ordered by priority! */
|
|
|
|
INACCESSIBLE,
|
|
|
|
READONLY,
|
2012-08-13 15:27:04 +02:00
|
|
|
PRIVATE_TMP,
|
|
|
|
PRIVATE_VAR_TMP,
|
2014-01-20 19:54:51 +01:00
|
|
|
PRIVATE_DEV,
|
2014-08-22 18:55:21 +02:00
|
|
|
PRIVATE_BUS_ENDPOINT,
|
2010-04-21 22:15:06 +02:00
|
|
|
READWRITE
|
2013-03-14 18:12:27 +01:00
|
|
|
} MountMode;
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
typedef struct BindMount {
|
2010-04-21 22:15:06 +02:00
|
|
|
const char *path;
|
2013-03-14 18:12:27 +01:00
|
|
|
MountMode mode;
|
2012-08-13 15:27:04 +02:00
|
|
|
bool done;
|
2013-08-21 16:43:55 +02:00
|
|
|
bool ignore;
|
2013-03-14 18:12:27 +01:00
|
|
|
} BindMount;
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
static int append_mounts(BindMount **p, char **strv, MountMode mode) {
|
2010-04-21 22:15:06 +02:00
|
|
|
char **i;
|
|
|
|
|
2013-11-27 20:23:18 +01:00
|
|
|
assert(p);
|
|
|
|
|
2010-04-21 22:15:06 +02:00
|
|
|
STRV_FOREACH(i, strv) {
|
|
|
|
|
2013-08-21 16:43:55 +02:00
|
|
|
(*p)->ignore = false;
|
2014-07-03 16:27:53 +02:00
|
|
|
(*p)->done = false;
|
2013-08-21 16:43:55 +02:00
|
|
|
|
2014-03-03 21:40:55 +01:00
|
|
|
if ((mode == INACCESSIBLE || mode == READONLY || mode == READWRITE) && (*i)[0] == '-') {
|
2013-08-21 16:43:55 +02:00
|
|
|
(*p)->ignore = true;
|
|
|
|
(*i)++;
|
|
|
|
}
|
|
|
|
|
2010-04-21 22:15:06 +02:00
|
|
|
if (!path_is_absolute(*i))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
(*p)->path = *i;
|
|
|
|
(*p)->mode = mode;
|
|
|
|
(*p)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
static int mount_path_compare(const void *a, const void *b) {
|
|
|
|
const BindMount *p = a, *q = b;
|
2015-03-16 22:04:21 +01:00
|
|
|
int d;
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2015-03-16 22:04:21 +01:00
|
|
|
d = path_compare(p->path, q->path);
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2015-05-13 14:04:55 +02:00
|
|
|
if (d == 0) {
|
2010-04-21 22:15:06 +02:00
|
|
|
/* If the paths are equal, check the mode */
|
|
|
|
if (p->mode < q->mode)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (p->mode > q->mode)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the paths are not equal, then order prefixes first */
|
2015-03-16 22:04:21 +01:00
|
|
|
return d;
|
2010-04-21 22:15:06 +02:00
|
|
|
}
|
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
static void drop_duplicates(BindMount *m, unsigned *n) {
|
|
|
|
BindMount *f, *t, *previous;
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
assert(m);
|
2010-04-21 22:15:06 +02:00
|
|
|
assert(n);
|
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
for (f = m, t = m, previous = NULL; f < m+*n; f++) {
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2012-08-13 15:27:04 +02:00
|
|
|
/* The first one wins */
|
2010-04-21 22:15:06 +02:00
|
|
|
if (previous && path_equal(f->path, previous->path))
|
|
|
|
continue;
|
|
|
|
|
2014-07-27 16:32:13 +02:00
|
|
|
*t = *f;
|
2010-04-21 22:15:06 +02:00
|
|
|
|
|
|
|
previous = t;
|
|
|
|
|
|
|
|
t++;
|
|
|
|
}
|
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
*n = t - m;
|
2010-04-21 22:15:06 +02:00
|
|
|
}
|
|
|
|
|
2014-01-20 19:54:51 +01:00
|
|
|
static int mount_dev(BindMount *m) {
|
|
|
|
static const char devnodes[] =
|
|
|
|
"/dev/null\0"
|
|
|
|
"/dev/zero\0"
|
|
|
|
"/dev/full\0"
|
|
|
|
"/dev/random\0"
|
|
|
|
"/dev/urandom\0"
|
|
|
|
"/dev/tty\0";
|
|
|
|
|
2014-03-19 16:23:32 +01:00
|
|
|
char temporary_mount[] = "/tmp/namespace-dev-XXXXXX";
|
sd-bus: sync with kdbus upstream (ABI break)
kdbus has seen a larger update than expected lately, most notably with
kdbusfs, a file system to expose the kdbus control files:
* Each time a file system of this type is mounted, a new kdbus
domain is created.
* The layout inside each mount point is the same as before, except
that domains are not hierarchically nested anymore.
* Domains are therefore also unnamed now.
* Unmounting a kdbusfs will automatically also detroy the
associated domain.
* Hence, the action of creating a kdbus domain is now as
privileged as mounting a filesystem.
* This way, we can get around creating dev nodes for everything,
which is last but not least something that is not limited by
20-bit minor numbers.
The kdbus specific bits in nspawn have all been dropped now, as nspawn
can rely on the container OS to set up its own kdbus domain, simply by
mounting a new instance.
A new set of mounts has been added to mount things *after* the kernel
modules have been loaded. For now, only kdbus is in this set, which is
invoked with mount_setup_late().
2014-11-13 20:33:03 +01:00
|
|
|
const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL;
|
2014-01-20 19:54:51 +01:00
|
|
|
_cleanup_umask_ mode_t u;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
u = umask(0000);
|
|
|
|
|
2014-03-19 16:23:32 +01:00
|
|
|
if (!mkdtemp(temporary_mount))
|
|
|
|
return -errno;
|
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
dev = strjoina(temporary_mount, "/dev");
|
2015-03-14 03:20:01 +01:00
|
|
|
(void) mkdir(dev, 0755);
|
2014-03-19 16:23:32 +01:00
|
|
|
if (mount("tmpfs", dev, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=755") < 0) {
|
|
|
|
r = -errno;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
devpts = strjoina(temporary_mount, "/dev/pts");
|
2015-03-14 03:20:01 +01:00
|
|
|
(void) mkdir(devpts, 0755);
|
2014-03-19 16:23:32 +01:00
|
|
|
if (mount("/dev/pts", devpts, NULL, MS_BIND, NULL) < 0) {
|
|
|
|
r = -errno;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
devptmx = strjoina(temporary_mount, "/dev/ptmx");
|
2015-03-14 03:20:47 +01:00
|
|
|
if (symlink("pts/ptmx", devptmx) < 0) {
|
|
|
|
r = -errno;
|
|
|
|
goto fail;
|
|
|
|
}
|
2014-06-04 17:21:18 +02:00
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
devshm = strjoina(temporary_mount, "/dev/shm");
|
2015-03-14 03:20:01 +01:00
|
|
|
(void) mkdir(devshm, 01777);
|
2014-03-19 16:23:32 +01:00
|
|
|
r = mount("/dev/shm", devshm, NULL, MS_BIND, NULL);
|
|
|
|
if (r < 0) {
|
|
|
|
r = -errno;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
devmqueue = strjoina(temporary_mount, "/dev/mqueue");
|
2015-03-14 03:20:01 +01:00
|
|
|
(void) mkdir(devmqueue, 0755);
|
2015-03-14 03:20:47 +01:00
|
|
|
(void) mount("/dev/mqueue", devmqueue, NULL, MS_BIND, NULL);
|
2014-03-19 16:23:32 +01:00
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
devhugepages = strjoina(temporary_mount, "/dev/hugepages");
|
2015-03-14 03:20:01 +01:00
|
|
|
(void) mkdir(devhugepages, 0755);
|
2015-03-14 03:20:47 +01:00
|
|
|
(void) mount("/dev/hugepages", devhugepages, NULL, MS_BIND, NULL);
|
2014-03-19 16:23:32 +01:00
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
devlog = strjoina(temporary_mount, "/dev/log");
|
2015-03-14 03:20:47 +01:00
|
|
|
(void) symlink("/run/systemd/journal/dev-log", devlog);
|
2014-06-04 16:59:13 +02:00
|
|
|
|
2014-01-20 19:54:51 +01:00
|
|
|
NULSTR_FOREACH(d, devnodes) {
|
2014-03-19 16:23:32 +01:00
|
|
|
_cleanup_free_ char *dn = NULL;
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
r = stat(d, &st);
|
2014-01-20 19:54:51 +01:00
|
|
|
if (r < 0) {
|
2014-03-19 16:23:32 +01:00
|
|
|
|
|
|
|
if (errno == ENOENT)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
r = -errno;
|
|
|
|
goto fail;
|
2014-01-20 19:54:51 +01:00
|
|
|
}
|
|
|
|
|
2014-03-19 16:23:32 +01:00
|
|
|
if (!S_ISBLK(st.st_mode) &&
|
|
|
|
!S_ISCHR(st.st_mode)) {
|
|
|
|
r = -EINVAL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (st.st_rdev == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
dn = strappend(temporary_mount, d);
|
|
|
|
if (!dn) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2014-10-23 19:41:27 +02:00
|
|
|
mac_selinux_create_file_prepare(d, st.st_mode);
|
2014-03-19 16:23:32 +01:00
|
|
|
r = mknod(dn, st.st_mode, st.st_rdev);
|
2014-10-23 19:41:27 +02:00
|
|
|
mac_selinux_create_file_clear();
|
2014-06-17 23:51:21 +02:00
|
|
|
|
2014-03-19 16:23:32 +01:00
|
|
|
if (r < 0) {
|
|
|
|
r = -errno;
|
|
|
|
goto fail;
|
|
|
|
}
|
2014-01-20 19:54:51 +01:00
|
|
|
}
|
|
|
|
|
2015-05-21 16:30:58 +02:00
|
|
|
dev_setup(temporary_mount, UID_INVALID, GID_INVALID);
|
2014-01-20 19:54:51 +01:00
|
|
|
|
2015-05-18 12:20:28 +02:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
(void) mkdir_p_label(m->path, 0755);
|
|
|
|
|
|
|
|
if (mount(dev, m->path, NULL, MS_MOVE, NULL) < 0) {
|
2014-03-19 16:23:32 +01:00
|
|
|
r = -errno;
|
|
|
|
goto fail;
|
|
|
|
}
|
2014-01-20 19:54:51 +01:00
|
|
|
|
2014-03-19 16:23:32 +01:00
|
|
|
rmdir(dev);
|
|
|
|
rmdir(temporary_mount);
|
2014-01-20 19:54:51 +01:00
|
|
|
|
2014-03-19 16:23:32 +01:00
|
|
|
return 0;
|
2014-01-20 19:54:51 +01:00
|
|
|
|
2014-03-19 16:23:32 +01:00
|
|
|
fail:
|
|
|
|
if (devpts)
|
|
|
|
umount(devpts);
|
2014-01-20 19:54:51 +01:00
|
|
|
|
2014-03-19 16:23:32 +01:00
|
|
|
if (devshm)
|
|
|
|
umount(devshm);
|
2014-01-20 19:54:51 +01:00
|
|
|
|
2014-03-19 16:23:32 +01:00
|
|
|
if (devhugepages)
|
|
|
|
umount(devhugepages);
|
2014-01-20 19:54:51 +01:00
|
|
|
|
2014-03-19 16:23:32 +01:00
|
|
|
if (devmqueue)
|
|
|
|
umount(devmqueue);
|
2014-01-20 19:54:51 +01:00
|
|
|
|
2014-10-04 01:17:56 +02:00
|
|
|
umount(dev);
|
|
|
|
rmdir(dev);
|
2014-03-19 16:23:32 +01:00
|
|
|
rmdir(temporary_mount);
|
2014-01-20 19:54:51 +01:00
|
|
|
|
2014-03-19 16:23:32 +01:00
|
|
|
return r;
|
2014-01-20 19:54:51 +01:00
|
|
|
}
|
|
|
|
|
2014-08-22 18:55:21 +02:00
|
|
|
static int mount_kdbus(BindMount *m) {
|
|
|
|
|
|
|
|
char temporary_mount[] = "/tmp/kdbus-dev-XXXXXX";
|
|
|
|
_cleanup_free_ char *basepath = NULL;
|
|
|
|
_cleanup_umask_ mode_t u;
|
2014-09-08 22:05:17 +02:00
|
|
|
char *busnode = NULL, *root;
|
2014-08-22 18:55:21 +02:00
|
|
|
struct stat st;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
u = umask(0000);
|
|
|
|
|
2014-11-28 19:57:32 +01:00
|
|
|
if (!mkdtemp(temporary_mount))
|
|
|
|
return log_error_errno(errno, "Failed create temp dir: %m");
|
2014-08-22 18:55:21 +02:00
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
root = strjoina(temporary_mount, "/kdbus");
|
2015-03-14 03:20:01 +01:00
|
|
|
(void) mkdir(root, 0755);
|
2014-08-22 18:55:21 +02:00
|
|
|
if (mount("tmpfs", root, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=777") < 0) {
|
|
|
|
r = -errno;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* create a new /dev/null dev node copy so we have some fodder to
|
|
|
|
* bind-mount the custom endpoint over. */
|
|
|
|
if (stat("/dev/null", &st) < 0) {
|
2015-09-08 19:30:45 +02:00
|
|
|
r = log_error_errno(errno, "Failed to stat /dev/null: %m");
|
2014-08-22 18:55:21 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
busnode = strjoina(root, "/bus");
|
2014-08-22 18:55:21 +02:00
|
|
|
if (mknod(busnode, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0) {
|
2015-09-09 11:08:23 +02:00
|
|
|
r = log_error_errno(errno, "mknod() for %s failed: %m",
|
|
|
|
busnode);
|
2014-08-22 18:55:21 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-03-31 11:50:29 +02:00
|
|
|
r = mount(m->path, busnode, NULL, MS_BIND, NULL);
|
2014-08-22 18:55:21 +02:00
|
|
|
if (r < 0) {
|
2015-09-09 11:08:23 +02:00
|
|
|
r = log_error_errno(errno, "bind mount of %s failed: %m",
|
|
|
|
m->path);
|
2014-08-22 18:55:21 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
basepath = dirname_malloc(m->path);
|
|
|
|
if (!basepath) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mount(root, basepath, NULL, MS_MOVE, NULL) < 0) {
|
2015-09-09 11:08:23 +02:00
|
|
|
r = log_error_errno(errno, "bind mount of %s failed: %m",
|
|
|
|
basepath);
|
2014-08-22 18:55:21 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
rmdir(temporary_mount);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
if (busnode) {
|
|
|
|
umount(busnode);
|
|
|
|
unlink(busnode);
|
|
|
|
}
|
|
|
|
|
2014-10-04 01:16:11 +02:00
|
|
|
umount(root);
|
|
|
|
rmdir(root);
|
2014-08-22 18:55:21 +02:00
|
|
|
rmdir(temporary_mount);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2012-08-13 15:27:04 +02:00
|
|
|
static int apply_mount(
|
2013-03-14 18:12:27 +01:00
|
|
|
BindMount *m,
|
2012-08-13 15:27:04 +02:00
|
|
|
const char *tmp_dir,
|
2013-03-14 18:12:27 +01:00
|
|
|
const char *var_tmp_dir) {
|
2012-08-13 15:27:04 +02:00
|
|
|
|
2010-04-21 22:15:06 +02:00
|
|
|
const char *what;
|
|
|
|
int r;
|
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
assert(m);
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
switch (m->mode) {
|
2010-04-21 22:15:06 +02:00
|
|
|
|
|
|
|
case INACCESSIBLE:
|
2014-06-05 21:35:35 +02:00
|
|
|
|
|
|
|
/* First, get rid of everything that is below if there
|
|
|
|
* is anything... Then, overmount it with an
|
|
|
|
* inaccessible directory. */
|
|
|
|
umount_recursive(m->path, 0);
|
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
what = "/run/systemd/inaccessible";
|
2010-04-21 22:15:06 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case READONLY:
|
|
|
|
case READWRITE:
|
2014-06-06 11:42:25 +02:00
|
|
|
/* Nothing to mount here, we just later toggle the
|
|
|
|
* MS_RDONLY bit for the mount point */
|
|
|
|
return 0;
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2012-08-13 15:27:04 +02:00
|
|
|
case PRIVATE_TMP:
|
|
|
|
what = tmp_dir;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRIVATE_VAR_TMP:
|
|
|
|
what = var_tmp_dir;
|
2010-04-21 22:15:06 +02:00
|
|
|
break;
|
2010-08-11 22:04:22 +02:00
|
|
|
|
2014-06-06 11:42:25 +02:00
|
|
|
case PRIVATE_DEV:
|
|
|
|
return mount_dev(m);
|
|
|
|
|
2014-08-22 18:55:21 +02:00
|
|
|
case PRIVATE_BUS_ENDPOINT:
|
|
|
|
return mount_kdbus(m);
|
|
|
|
|
2010-08-11 22:04:22 +02:00
|
|
|
default:
|
|
|
|
assert_not_reached("Unknown mode");
|
2010-04-21 22:15:06 +02:00
|
|
|
}
|
|
|
|
|
2012-08-13 15:27:04 +02:00
|
|
|
assert(what);
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL);
|
2012-08-13 15:27:04 +02:00
|
|
|
if (r >= 0)
|
2013-03-14 18:12:27 +01:00
|
|
|
log_debug("Successfully mounted %s to %s", what, m->path);
|
2013-08-21 16:43:55 +02:00
|
|
|
else if (m->ignore && errno == ENOENT)
|
2014-06-06 11:42:25 +02:00
|
|
|
return 0;
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2012-08-13 15:27:04 +02:00
|
|
|
return r;
|
|
|
|
}
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
static int make_read_only(BindMount *m) {
|
2012-08-13 15:27:04 +02:00
|
|
|
int r;
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2013-03-14 18:12:27 +01:00
|
|
|
assert(m);
|
2012-08-13 15:27:04 +02:00
|
|
|
|
2014-06-06 11:42:25 +02:00
|
|
|
if (IN_SET(m->mode, INACCESSIBLE, READONLY))
|
|
|
|
r = bind_remount_recursive(m->path, true);
|
2014-07-03 16:27:57 +02:00
|
|
|
else if (IN_SET(m->mode, READWRITE, PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV))
|
2014-06-06 11:42:25 +02:00
|
|
|
r = bind_remount_recursive(m->path, false);
|
|
|
|
else
|
|
|
|
r = 0;
|
2012-08-13 15:27:04 +02:00
|
|
|
|
2014-06-06 11:42:25 +02:00
|
|
|
if (m->ignore && r == -ENOENT)
|
|
|
|
return 0;
|
2012-08-13 15:27:04 +02:00
|
|
|
|
2014-06-06 11:42:25 +02:00
|
|
|
return r;
|
2010-04-21 22:15:06 +02:00
|
|
|
}
|
|
|
|
|
2013-11-27 20:23:18 +01:00
|
|
|
int setup_namespace(
|
2015-05-18 12:20:28 +02:00
|
|
|
const char* root_directory,
|
2013-11-27 20:23:18 +01:00
|
|
|
char** read_write_dirs,
|
|
|
|
char** read_only_dirs,
|
|
|
|
char** inaccessible_dirs,
|
2014-10-17 13:48:55 +02:00
|
|
|
const char* tmp_dir,
|
|
|
|
const char* var_tmp_dir,
|
|
|
|
const char* bus_endpoint_path,
|
2014-01-20 19:54:51 +01:00
|
|
|
bool private_dev,
|
2014-06-04 18:07:55 +02:00
|
|
|
ProtectHome protect_home,
|
|
|
|
ProtectSystem protect_system,
|
2015-01-01 18:40:07 +01:00
|
|
|
unsigned long mount_flags) {
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2013-10-12 01:33:13 +02:00
|
|
|
BindMount *m, *mounts = NULL;
|
2013-11-27 20:23:18 +01:00
|
|
|
unsigned n;
|
2013-03-14 18:12:27 +01:00
|
|
|
int r = 0;
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2013-11-27 20:23:18 +01:00
|
|
|
if (mount_flags == 0)
|
2013-03-14 18:12:27 +01:00
|
|
|
mount_flags = MS_SHARED;
|
2012-08-13 15:27:04 +02:00
|
|
|
|
2013-03-20 23:12:37 +01:00
|
|
|
if (unshare(CLONE_NEWNS) < 0)
|
|
|
|
return -errno;
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2014-08-22 18:55:21 +02:00
|
|
|
n = !!tmp_dir + !!var_tmp_dir + !!bus_endpoint_path +
|
2013-11-27 20:23:18 +01:00
|
|
|
strv_length(read_write_dirs) +
|
|
|
|
strv_length(read_only_dirs) +
|
2014-01-20 19:54:51 +01:00
|
|
|
strv_length(inaccessible_dirs) +
|
2014-06-03 23:41:44 +02:00
|
|
|
private_dev +
|
2014-06-05 21:37:40 +02:00
|
|
|
(protect_home != PROTECT_HOME_NO ? 3 : 0) +
|
2014-06-06 14:48:51 +02:00
|
|
|
(protect_system != PROTECT_SYSTEM_NO ? 2 : 0) +
|
2014-06-04 18:07:55 +02:00
|
|
|
(protect_system == PROTECT_SYSTEM_FULL ? 1 : 0);
|
2013-11-27 20:23:18 +01:00
|
|
|
|
|
|
|
if (n > 0) {
|
2014-07-03 16:27:53 +02:00
|
|
|
m = mounts = (BindMount *) alloca0(n * sizeof(BindMount));
|
2013-11-27 20:23:18 +01:00
|
|
|
r = append_mounts(&m, read_write_dirs, READWRITE);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = append_mounts(&m, read_only_dirs, READONLY);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE);
|
|
|
|
if (r < 0)
|
2013-10-12 01:33:13 +02:00
|
|
|
return r;
|
|
|
|
|
2013-11-27 20:23:18 +01:00
|
|
|
if (tmp_dir) {
|
2015-05-18 12:20:28 +02:00
|
|
|
m->path = prefix_roota(root_directory, "/tmp");
|
2013-10-12 01:33:13 +02:00
|
|
|
m->mode = PRIVATE_TMP;
|
|
|
|
m++;
|
2013-11-27 20:23:18 +01:00
|
|
|
}
|
2013-10-12 01:33:13 +02:00
|
|
|
|
2013-11-27 20:23:18 +01:00
|
|
|
if (var_tmp_dir) {
|
2015-05-18 12:20:28 +02:00
|
|
|
m->path = prefix_roota(root_directory, "/var/tmp");
|
2013-10-12 01:33:13 +02:00
|
|
|
m->mode = PRIVATE_VAR_TMP;
|
|
|
|
m++;
|
|
|
|
}
|
2012-08-13 15:27:04 +02:00
|
|
|
|
2014-01-20 19:54:51 +01:00
|
|
|
if (private_dev) {
|
2015-05-18 12:20:28 +02:00
|
|
|
m->path = prefix_roota(root_directory, "/dev");
|
2014-01-20 19:54:51 +01:00
|
|
|
m->mode = PRIVATE_DEV;
|
|
|
|
m++;
|
|
|
|
}
|
|
|
|
|
2014-08-22 18:55:21 +02:00
|
|
|
if (bus_endpoint_path) {
|
2015-05-18 12:20:28 +02:00
|
|
|
m->path = prefix_roota(root_directory, bus_endpoint_path);
|
2014-08-22 18:55:21 +02:00
|
|
|
m->mode = PRIVATE_BUS_ENDPOINT;
|
|
|
|
m++;
|
|
|
|
}
|
|
|
|
|
2014-06-04 18:07:55 +02:00
|
|
|
if (protect_home != PROTECT_HOME_NO) {
|
2015-05-18 12:20:28 +02:00
|
|
|
const char *home_dir, *run_user_dir, *root_dir;
|
|
|
|
|
|
|
|
home_dir = prefix_roota(root_directory, "/home");
|
|
|
|
home_dir = strjoina("-", home_dir);
|
|
|
|
run_user_dir = prefix_roota(root_directory, "/run/user");
|
|
|
|
run_user_dir = strjoina("-", run_user_dir);
|
|
|
|
root_dir = prefix_roota(root_directory, "/root");
|
|
|
|
root_dir = strjoina("-", root_dir);
|
|
|
|
|
|
|
|
r = append_mounts(&m, STRV_MAKE(home_dir, run_user_dir, root_dir),
|
|
|
|
protect_home == PROTECT_HOME_READ_ONLY ? READONLY : INACCESSIBLE);
|
2014-06-03 23:41:44 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-06-04 18:07:55 +02:00
|
|
|
if (protect_system != PROTECT_SYSTEM_NO) {
|
2015-05-18 12:20:28 +02:00
|
|
|
const char *usr_dir, *boot_dir, *etc_dir;
|
|
|
|
|
2015-05-31 18:51:17 +02:00
|
|
|
usr_dir = prefix_roota(root_directory, "/usr");
|
2015-05-18 12:20:28 +02:00
|
|
|
boot_dir = prefix_roota(root_directory, "/boot");
|
|
|
|
boot_dir = strjoina("-", boot_dir);
|
|
|
|
etc_dir = prefix_roota(root_directory, "/etc");
|
|
|
|
|
|
|
|
r = append_mounts(&m, protect_system == PROTECT_SYSTEM_FULL
|
|
|
|
? STRV_MAKE(usr_dir, boot_dir, etc_dir)
|
|
|
|
: STRV_MAKE(usr_dir, boot_dir), READONLY);
|
2014-06-03 23:41:44 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-10-12 01:33:13 +02:00
|
|
|
assert(mounts + n == m);
|
2012-08-13 15:27:04 +02:00
|
|
|
|
2013-10-12 01:33:13 +02:00
|
|
|
qsort(mounts, n, sizeof(BindMount), mount_path_compare);
|
|
|
|
drop_duplicates(mounts, &n);
|
2010-04-21 22:15:06 +02:00
|
|
|
}
|
|
|
|
|
2015-05-18 12:20:28 +02:00
|
|
|
if (n > 0 || root_directory) {
|
2014-03-20 04:16:39 +01:00
|
|
|
/* Remount / as SLAVE so that nothing now mounted in the namespace
|
|
|
|
shows up in the parent */
|
|
|
|
if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
|
|
|
|
return -errno;
|
2015-05-18 12:20:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (root_directory) {
|
|
|
|
/* Turn directory into bind mount */
|
|
|
|
if (mount(root_directory, root_directory, NULL, MS_BIND|MS_REC, NULL) < 0)
|
|
|
|
return -errno;
|
|
|
|
}
|
2014-03-20 04:16:39 +01:00
|
|
|
|
2015-05-18 12:20:28 +02:00
|
|
|
if (n > 0) {
|
2014-03-20 04:16:39 +01:00
|
|
|
for (m = mounts; m < mounts + n; ++m) {
|
|
|
|
r = apply_mount(m, tmp_dir, var_tmp_dir);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
}
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2014-03-20 04:16:39 +01:00
|
|
|
for (m = mounts; m < mounts + n; ++m) {
|
|
|
|
r = make_read_only(m);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
}
|
2010-04-21 22:15:06 +02:00
|
|
|
}
|
|
|
|
|
2015-05-18 12:20:28 +02:00
|
|
|
if (root_directory) {
|
|
|
|
/* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
|
|
|
|
r = mount_move_root(root_directory);
|
|
|
|
|
|
|
|
/* at this point, we cannot rollback */
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-03-20 04:16:39 +01:00
|
|
|
/* Remount / as the desired mode. Not that this will not
|
|
|
|
* reestablish propagation from our side to the host, since
|
|
|
|
* what's disconnected is disconnected. */
|
2015-09-09 14:23:02 +02:00
|
|
|
if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0)
|
2015-05-18 12:20:28 +02:00
|
|
|
/* at this point, we cannot rollback */
|
|
|
|
return -errno;
|
2010-04-21 22:15:06 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2013-11-27 20:23:18 +01:00
|
|
|
fail:
|
2014-03-20 04:16:39 +01:00
|
|
|
if (n > 0) {
|
|
|
|
for (m = mounts; m < mounts + n; ++m)
|
|
|
|
if (m->done)
|
2015-03-14 03:20:59 +01:00
|
|
|
(void) umount2(m->path, MNT_DETACH);
|
2014-03-20 04:16:39 +01:00
|
|
|
}
|
2013-11-27 20:23:18 +01:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) {
|
|
|
|
_cleanup_free_ char *x = NULL;
|
2013-12-13 02:05:04 +01:00
|
|
|
char bid[SD_ID128_STRING_MAX];
|
|
|
|
sd_id128_t boot_id;
|
|
|
|
int r;
|
2013-11-27 20:23:18 +01:00
|
|
|
|
|
|
|
assert(id);
|
|
|
|
assert(prefix);
|
|
|
|
assert(path);
|
|
|
|
|
2013-12-13 02:05:04 +01:00
|
|
|
/* 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);
|
2013-11-27 20:23:18 +01:00
|
|
|
if (!x)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
RUN_WITH_UMASK(0077)
|
|
|
|
if (!mkdtemp(x))
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
RUN_WITH_UMASK(0000) {
|
|
|
|
char *y;
|
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
y = strjoina(x, "/tmp");
|
2013-11-27 20:23:18 +01:00
|
|
|
|
|
|
|
if (mkdir(y, 0777 | S_ISVTX) < 0)
|
|
|
|
return -errno;
|
2013-03-14 18:12:27 +01:00
|
|
|
}
|
2010-04-21 22:15:06 +02:00
|
|
|
|
2013-11-27 20:23:18 +01:00
|
|
|
*path = x;
|
|
|
|
x = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) {
|
|
|
|
char *a, *b;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(id);
|
|
|
|
assert(tmp_dir);
|
|
|
|
assert(var_tmp_dir);
|
|
|
|
|
|
|
|
r = setup_one_tmp_dir(id, "/tmp", &a);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = setup_one_tmp_dir(id, "/var/tmp", &b);
|
|
|
|
if (r < 0) {
|
|
|
|
char *t;
|
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
t = strjoina(a, "/tmp");
|
2013-11-27 20:23:18 +01:00
|
|
|
rmdir(t);
|
|
|
|
rmdir(a);
|
|
|
|
|
|
|
|
free(a);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
*tmp_dir = a;
|
|
|
|
*var_tmp_dir = b;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int setup_netns(int netns_storage_socket[2]) {
|
|
|
|
_cleanup_close_ int netns = -1;
|
2015-09-23 01:00:04 +02:00
|
|
|
int r, q;
|
2013-11-27 20:23:18 +01:00
|
|
|
|
|
|
|
assert(netns_storage_socket);
|
|
|
|
assert(netns_storage_socket[0] >= 0);
|
|
|
|
assert(netns_storage_socket[1] >= 0);
|
|
|
|
|
|
|
|
/* We use the passed socketpair as a storage buffer for our
|
2013-11-27 20:31:51 +01:00
|
|
|
* 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.
|
2013-11-27 20:23:18 +01:00
|
|
|
*
|
|
|
|
* It's a bit crazy, but hey, works great! */
|
|
|
|
|
|
|
|
if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0)
|
|
|
|
return -errno;
|
|
|
|
|
2015-09-23 01:00:04 +02:00
|
|
|
netns = receive_one_fd(netns_storage_socket[0], MSG_DONTWAIT);
|
|
|
|
if (netns == -EAGAIN) {
|
2013-11-27 20:23:18 +01:00
|
|
|
/* Nothing stored yet, so let's create a new namespace */
|
|
|
|
|
|
|
|
if (unshare(CLONE_NEWNET) < 0) {
|
|
|
|
r = -errno;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
loopback_setup();
|
|
|
|
|
|
|
|
netns = open("/proc/self/ns/net", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
|
|
|
if (netns < 0) {
|
|
|
|
r = -errno;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = 1;
|
|
|
|
|
2015-09-23 01:00:04 +02:00
|
|
|
} else if (netns < 0) {
|
|
|
|
r = netns;
|
|
|
|
goto fail;
|
2013-11-27 20:23:18 +01:00
|
|
|
|
2015-09-23 01:00:04 +02:00
|
|
|
} else {
|
|
|
|
/* Yay, found something, so let's join the namespace */
|
2013-11-27 20:23:18 +01:00
|
|
|
if (setns(netns, CLONE_NEWNET) < 0) {
|
|
|
|
r = -errno;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
2015-09-23 01:00:04 +02:00
|
|
|
q = send_one_fd(netns_storage_socket[1], netns, MSG_DONTWAIT);
|
|
|
|
if (q < 0) {
|
|
|
|
r = q;
|
2013-11-27 20:23:18 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
fail:
|
|
|
|
lockf(netns_storage_socket[0], F_ULOCK, 0);
|
2010-04-21 22:15:06 +02:00
|
|
|
return r;
|
|
|
|
}
|
2014-06-03 23:41:44 +02:00
|
|
|
|
2014-06-04 18:07:55 +02:00
|
|
|
static const char *const protect_home_table[_PROTECT_HOME_MAX] = {
|
|
|
|
[PROTECT_HOME_NO] = "no",
|
|
|
|
[PROTECT_HOME_YES] = "yes",
|
|
|
|
[PROTECT_HOME_READ_ONLY] = "read-only",
|
2014-06-03 23:41:44 +02:00
|
|
|
};
|
|
|
|
|
2014-06-04 18:07:55 +02:00
|
|
|
DEFINE_STRING_TABLE_LOOKUP(protect_home, ProtectHome);
|
|
|
|
|
|
|
|
static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
|
|
|
|
[PROTECT_SYSTEM_NO] = "no",
|
|
|
|
[PROTECT_SYSTEM_YES] = "yes",
|
|
|
|
[PROTECT_SYSTEM_FULL] = "full",
|
|
|
|
};
|
|
|
|
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem);
|