2016-03-03 22:37:04 +08:00
|
|
|
/*
|
|
|
|
|
|
|
|
nsjail - CLONE_NEWNS routines
|
|
|
|
-----------------------------------------
|
|
|
|
|
|
|
|
Copyright 2014 Google Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2018-02-10 01:26:16 +08:00
|
|
|
#include "mnt.h"
|
2016-03-03 22:37:04 +08:00
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2017-10-18 20:46:17 +08:00
|
|
|
#include <inttypes.h>
|
2017-10-18 20:27:34 +08:00
|
|
|
#include <limits.h>
|
2016-07-21 21:48:47 +08:00
|
|
|
#include <linux/sched.h>
|
2016-05-13 04:25:48 +08:00
|
|
|
#include <sched.h>
|
2017-10-18 20:46:17 +08:00
|
|
|
#include <signal.h>
|
2016-03-03 22:37:04 +08:00
|
|
|
#include <stdio.h>
|
2017-09-14 04:03:21 +08:00
|
|
|
#include <stdlib.h>
|
2016-03-03 22:37:04 +08:00
|
|
|
#include <string.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/statvfs.h>
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
#include <sys/types.h>
|
2016-05-13 04:25:48 +08:00
|
|
|
#include <sys/wait.h>
|
2017-10-18 20:46:17 +08:00
|
|
|
#include <syscall.h>
|
2016-03-03 22:37:04 +08:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2018-02-11 04:19:47 +08:00
|
|
|
#include <string>
|
|
|
|
|
2018-02-11 00:49:15 +08:00
|
|
|
#include "logs.h"
|
2018-02-10 12:25:55 +08:00
|
|
|
#include "macros.h"
|
2018-02-10 01:26:16 +08:00
|
|
|
#include "subproc.h"
|
2018-02-10 01:45:50 +08:00
|
|
|
#include "util.h"
|
2018-02-10 01:26:16 +08:00
|
|
|
|
|
|
|
namespace mnt {
|
2016-03-03 22:37:04 +08:00
|
|
|
|
2017-05-21 23:37:18 +08:00
|
|
|
#if !defined(MS_LAZYTIME)
|
2017-09-14 04:03:21 +08:00
|
|
|
#define MS_LAZYTIME (1 << 25)
|
2017-10-09 05:00:45 +08:00
|
|
|
#endif /* if !defined(MS_LAZYTIME) */
|
2017-05-21 23:37:18 +08:00
|
|
|
|
2018-02-11 04:19:47 +08:00
|
|
|
static const std::string flagsToStr(uintptr_t flags) {
|
|
|
|
std::string res;
|
2017-05-21 23:37:18 +08:00
|
|
|
|
2017-10-09 05:03:02 +08:00
|
|
|
static struct {
|
|
|
|
const uintptr_t flag;
|
|
|
|
const char* const name;
|
|
|
|
} const mountFlags[] = {
|
2017-10-26 06:26:02 +08:00
|
|
|
NS_VALSTR_STRUCT(MS_RDONLY),
|
|
|
|
NS_VALSTR_STRUCT(MS_NOSUID),
|
|
|
|
NS_VALSTR_STRUCT(MS_NODEV),
|
|
|
|
NS_VALSTR_STRUCT(MS_NOEXEC),
|
|
|
|
NS_VALSTR_STRUCT(MS_SYNCHRONOUS),
|
|
|
|
NS_VALSTR_STRUCT(MS_REMOUNT),
|
|
|
|
NS_VALSTR_STRUCT(MS_MANDLOCK),
|
|
|
|
NS_VALSTR_STRUCT(MS_DIRSYNC),
|
|
|
|
NS_VALSTR_STRUCT(MS_NOATIME),
|
|
|
|
NS_VALSTR_STRUCT(MS_NODIRATIME),
|
|
|
|
NS_VALSTR_STRUCT(MS_BIND),
|
|
|
|
NS_VALSTR_STRUCT(MS_MOVE),
|
|
|
|
NS_VALSTR_STRUCT(MS_REC),
|
|
|
|
NS_VALSTR_STRUCT(MS_SILENT),
|
|
|
|
NS_VALSTR_STRUCT(MS_POSIXACL),
|
|
|
|
NS_VALSTR_STRUCT(MS_UNBINDABLE),
|
|
|
|
NS_VALSTR_STRUCT(MS_PRIVATE),
|
|
|
|
NS_VALSTR_STRUCT(MS_SLAVE),
|
|
|
|
NS_VALSTR_STRUCT(MS_SHARED),
|
|
|
|
NS_VALSTR_STRUCT(MS_RELATIME),
|
|
|
|
NS_VALSTR_STRUCT(MS_KERNMOUNT),
|
|
|
|
NS_VALSTR_STRUCT(MS_I_VERSION),
|
|
|
|
NS_VALSTR_STRUCT(MS_STRICTATIME),
|
|
|
|
NS_VALSTR_STRUCT(MS_LAZYTIME),
|
2017-10-09 05:03:02 +08:00
|
|
|
};
|
2017-05-21 23:37:18 +08:00
|
|
|
|
2018-02-11 04:19:47 +08:00
|
|
|
uintptr_t knownFlagMask = 0U;
|
2017-05-21 23:37:18 +08:00
|
|
|
for (size_t i = 0; i < ARRAYSIZE(mountFlags); i++) {
|
|
|
|
if (flags & mountFlags[i].flag) {
|
2018-02-11 04:19:47 +08:00
|
|
|
res.append(mountFlags[i].name);
|
|
|
|
res.append("|");
|
2017-05-21 23:37:18 +08:00
|
|
|
}
|
|
|
|
knownFlagMask |= mountFlags[i].flag;
|
|
|
|
}
|
2018-02-11 04:19:47 +08:00
|
|
|
|
|
|
|
char flagstr[32];
|
|
|
|
snprintf(flagstr, sizeof(flagstr), "%#tx", flags & ~(knownFlagMask));
|
|
|
|
res.append(flagstr);
|
|
|
|
|
|
|
|
return res;
|
2017-05-21 23:37:18 +08:00
|
|
|
}
|
|
|
|
|
2018-02-10 01:26:16 +08:00
|
|
|
static bool isDir(const char* path) {
|
2016-06-19 07:35:06 +08:00
|
|
|
/*
|
2016-08-17 03:12:23 +08:00
|
|
|
* If the source dir is NULL, we assume it's a dir (for /proc and tmpfs)
|
2016-06-19 07:35:06 +08:00
|
|
|
*/
|
2016-08-17 03:12:23 +08:00
|
|
|
if (path == NULL) {
|
2016-06-19 07:35:06 +08:00
|
|
|
return true;
|
2016-03-03 22:37:04 +08:00
|
|
|
}
|
|
|
|
struct stat st;
|
|
|
|
if (stat(path, &st) == -1) {
|
2017-05-28 02:19:36 +08:00
|
|
|
PLOG_D("stat('%s')", path);
|
2016-03-03 22:37:04 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (S_ISDIR(st.st_mode)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-02-10 22:50:12 +08:00
|
|
|
static bool mountPt(mount_t* mpt, const char* newroot, const char* tmpdir) {
|
2017-05-29 09:29:14 +08:00
|
|
|
char dst[PATH_MAX];
|
2018-02-10 21:38:01 +08:00
|
|
|
snprintf(dst, sizeof(dst), "%s/%s", newroot, mpt->dst.c_str());
|
2017-05-29 09:29:14 +08:00
|
|
|
|
2018-02-11 07:24:43 +08:00
|
|
|
LOG_D("Mounting '%s'", describeMountPt(*mpt).c_str());
|
2016-03-03 22:37:04 +08:00
|
|
|
|
2017-05-27 21:01:34 +08:00
|
|
|
char srcpath[PATH_MAX];
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!mpt->src.empty()) {
|
|
|
|
snprintf(srcpath, sizeof(srcpath), "%s", mpt->src.c_str());
|
2017-05-27 21:01:34 +08:00
|
|
|
} else {
|
|
|
|
snprintf(srcpath, sizeof(srcpath), "none");
|
2016-08-17 01:54:50 +08:00
|
|
|
}
|
|
|
|
|
2017-10-18 21:41:02 +08:00
|
|
|
if (mpt->isSymlink) {
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!util::createDirRecursively(dst)) {
|
2017-06-29 06:32:20 +08:00
|
|
|
LOG_W("Couldn't create upper directories for '%s'", dst);
|
|
|
|
return false;
|
|
|
|
}
|
2017-10-18 21:41:02 +08:00
|
|
|
} else if (mpt->isDir) {
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!util::createDirRecursively(dst)) {
|
2016-08-19 00:59:06 +08:00
|
|
|
LOG_W("Couldn't create upper directories for '%s'", dst);
|
|
|
|
return false;
|
|
|
|
}
|
2016-03-03 22:37:04 +08:00
|
|
|
if (mkdir(dst, 0711) == -1 && errno != EEXIST) {
|
|
|
|
PLOG_W("mkdir('%s')", dst);
|
|
|
|
}
|
2017-05-24 20:46:44 +08:00
|
|
|
} else {
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!util::createDirRecursively(dst)) {
|
2016-08-19 00:59:06 +08:00
|
|
|
LOG_W("Couldn't create upper directories for '%s'", dst);
|
|
|
|
return false;
|
|
|
|
}
|
2016-09-10 09:20:32 +08:00
|
|
|
int fd = TEMP_FAILURE_RETRY(open(dst, O_CREAT | O_RDONLY | O_CLOEXEC, 0644));
|
2016-03-03 22:37:04 +08:00
|
|
|
if (fd >= 0) {
|
2016-07-29 21:38:22 +08:00
|
|
|
close(fd);
|
2016-03-03 22:37:04 +08:00
|
|
|
} else {
|
2018-01-03 05:43:45 +08:00
|
|
|
PLOG_W("open('%s', O_CREAT|O_RDONLY|O_CLOEXEC, 0644)", dst);
|
2016-03-03 22:37:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-18 21:41:02 +08:00
|
|
|
if (mpt->isSymlink) {
|
2017-06-29 06:32:20 +08:00
|
|
|
LOG_D("symlink('%s', '%s')", srcpath, dst);
|
|
|
|
if (symlink(srcpath, dst) == -1) {
|
2017-07-02 09:39:56 +08:00
|
|
|
if (mpt->mandatory) {
|
|
|
|
PLOG_W("symlink('%s', '%s')", srcpath, dst);
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
PLOG_W("symlink('%s', '%s'), but it's not mandatory, continuing",
|
2017-10-09 05:00:45 +08:00
|
|
|
srcpath, dst);
|
2017-07-02 09:39:56 +08:00
|
|
|
}
|
2017-06-29 06:32:20 +08:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!mpt->src_content.empty()) {
|
2017-09-29 19:07:42 +08:00
|
|
|
static uint64_t df_counter = 0;
|
2017-10-17 21:22:23 +08:00
|
|
|
snprintf(
|
|
|
|
srcpath, sizeof(srcpath), "%s/dynamic_file.%" PRIu64, tmpdir, ++df_counter);
|
|
|
|
int fd = TEMP_FAILURE_RETRY(
|
|
|
|
open(srcpath, O_CREAT | O_EXCL | O_CLOEXEC | O_WRONLY, 0644));
|
2017-05-28 22:56:16 +08:00
|
|
|
if (fd < 0) {
|
2017-09-29 19:07:42 +08:00
|
|
|
PLOG_W("open(srcpath, O_CREAT|O_EXCL|O_CLOEXEC|O_WRONLY, 0644) failed");
|
2017-05-28 22:56:16 +08:00
|
|
|
return false;
|
|
|
|
}
|
2018-02-10 21:38:01 +08:00
|
|
|
if (util::writeToFd(fd, mpt->src_content.data(), mpt->src_content.length()) ==
|
|
|
|
false) {
|
|
|
|
LOG_W("Writting %zu bytes to '%s' failed", mpt->src_content.length(),
|
|
|
|
srcpath);
|
2017-05-28 22:56:16 +08:00
|
|
|
close(fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
close(fd);
|
2017-10-11 08:10:52 +08:00
|
|
|
mpt->flags |= (MS_BIND | MS_REC | MS_PRIVATE);
|
2017-05-28 22:56:16 +08:00
|
|
|
}
|
|
|
|
|
2016-06-19 07:35:06 +08:00
|
|
|
/*
|
|
|
|
* Initially mount it as RW, it will be remounted later on if needed
|
|
|
|
*/
|
|
|
|
unsigned long flags = mpt->flags & ~(MS_RDONLY);
|
2018-02-10 21:38:01 +08:00
|
|
|
if (mount(srcpath, dst, mpt->fs_type.c_str(), flags, mpt->options.c_str()) == -1) {
|
2017-07-02 09:39:56 +08:00
|
|
|
if (errno == EACCES) {
|
2017-10-26 06:26:02 +08:00
|
|
|
PLOG_W(
|
|
|
|
"mount('%s') src:'%s' dst:'%s' failed. "
|
|
|
|
"Try fixing this problem by applying 'chmod o+x' to the '%s' "
|
|
|
|
"directory and its ancestors",
|
2018-02-11 07:24:43 +08:00
|
|
|
describeMountPt(*mpt).c_str(), srcpath, dst, srcpath);
|
2016-03-03 22:37:04 +08:00
|
|
|
} else {
|
2018-02-11 07:24:43 +08:00
|
|
|
PLOG_W("mount('%s') src:'%s' dst:'%s' failed",
|
|
|
|
describeMountPt(*mpt).c_str(), srcpath, dst);
|
2018-02-10 21:38:01 +08:00
|
|
|
if (strcmp(mpt->fs_type.c_str(), "proc") == 0) {
|
2017-10-27 05:00:15 +08:00
|
|
|
PLOG_W(
|
|
|
|
"procfs can only be mounted if the original /proc doesn't have "
|
|
|
|
"any other file-systems mounted on top of it (e.g. /dev/null "
|
|
|
|
"on top of /proc/kcore)");
|
|
|
|
}
|
2017-05-27 21:17:11 +08:00
|
|
|
}
|
2017-10-19 21:46:31 +08:00
|
|
|
return false;
|
2017-10-08 07:28:45 +08:00
|
|
|
} else {
|
|
|
|
mpt->mounted = true;
|
2016-03-03 22:37:04 +08:00
|
|
|
}
|
2017-05-29 10:50:29 +08:00
|
|
|
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!mpt->src_content.empty() && unlink(srcpath) == -1) {
|
2017-05-29 10:50:29 +08:00
|
|
|
PLOG_W("unlink('%s')", srcpath);
|
|
|
|
}
|
2016-03-03 22:37:04 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-02-10 22:50:12 +08:00
|
|
|
static bool remountRO(const mount_t& mpt) {
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!mpt.mounted) {
|
2017-10-08 07:28:45 +08:00
|
|
|
return true;
|
|
|
|
}
|
2018-02-10 21:38:01 +08:00
|
|
|
if (mpt.isSymlink) {
|
2017-06-29 06:32:20 +08:00
|
|
|
return true;
|
|
|
|
}
|
2018-02-10 21:38:01 +08:00
|
|
|
if ((mpt.flags & MS_RDONLY) == 0) {
|
2017-05-22 09:34:54 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-03-03 22:37:04 +08:00
|
|
|
struct statvfs vfs;
|
2018-02-10 21:38:01 +08:00
|
|
|
if (TEMP_FAILURE_RETRY(statvfs(mpt.dst.c_str(), &vfs)) == -1) {
|
|
|
|
PLOG_W("statvfs('%s')", mpt.dst.c_str());
|
2017-07-02 09:39:56 +08:00
|
|
|
return false;
|
2016-03-03 22:37:04 +08:00
|
|
|
}
|
2017-10-26 08:17:52 +08:00
|
|
|
|
2017-10-26 08:29:15 +08:00
|
|
|
static struct {
|
|
|
|
const unsigned long mount_flag;
|
|
|
|
const unsigned long vfs_flag;
|
|
|
|
} const mountPairs[] = {
|
2017-10-26 08:27:18 +08:00
|
|
|
{MS_RDONLY, ST_RDONLY},
|
|
|
|
{MS_NOSUID, ST_NOSUID},
|
|
|
|
{MS_NODEV, ST_NODEV},
|
|
|
|
{MS_NOEXEC, ST_NOEXEC},
|
|
|
|
{MS_SYNCHRONOUS, ST_SYNCHRONOUS},
|
|
|
|
{MS_MANDLOCK, ST_MANDLOCK},
|
|
|
|
{MS_NOATIME, ST_NOATIME},
|
|
|
|
{MS_NODIRATIME, ST_NODIRATIME},
|
|
|
|
{MS_RELATIME, ST_RELATIME},
|
|
|
|
};
|
|
|
|
|
2017-10-26 08:17:52 +08:00
|
|
|
unsigned long new_flags = MS_REMOUNT | MS_RDONLY | MS_BIND;
|
2017-10-26 08:27:18 +08:00
|
|
|
for (size_t i = 0; i < ARRAYSIZE(mountPairs); i++) {
|
|
|
|
if (vfs.f_flag & mountPairs[i].vfs_flag) {
|
|
|
|
new_flags |= mountPairs[i].mount_flag;
|
|
|
|
}
|
2017-10-26 08:17:52 +08:00
|
|
|
}
|
2016-03-03 22:37:04 +08:00
|
|
|
|
2018-02-11 04:19:47 +08:00
|
|
|
LOG_D("Re-mounting R/O '%s' (flags:%s)", mpt.dst.c_str(), flagsToStr(new_flags).c_str());
|
2018-02-10 21:38:01 +08:00
|
|
|
if (mount(mpt.dst.c_str(), mpt.dst.c_str(), NULL, new_flags, 0) == -1) {
|
2018-02-11 04:19:47 +08:00
|
|
|
PLOG_W("mount('%s', flags:%s)", mpt.dst.c_str(), flagsToStr(new_flags).c_str());
|
2017-07-02 09:39:56 +08:00
|
|
|
return false;
|
2016-03-03 22:37:04 +08:00
|
|
|
}
|
2017-05-22 09:34:54 +08:00
|
|
|
|
2016-03-03 22:37:04 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-02-10 01:26:16 +08:00
|
|
|
static bool mkdirAndTest(const char* dir) {
|
2017-06-22 01:18:02 +08:00
|
|
|
if (mkdir(dir, 0755) == -1 && errno != EEXIST) {
|
2017-10-25 07:45:39 +08:00
|
|
|
PLOG_D("Couldn't create '%s' directory", dir);
|
2017-06-22 01:18:02 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (access(dir, R_OK) == -1) {
|
|
|
|
PLOG_W("access('%s', R_OK)", dir);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
LOG_D("Created accessible directory in '%s'", dir);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-02-10 22:50:12 +08:00
|
|
|
static bool getDir(nsjconf_t* nsjconf, char* dir, const char* name) {
|
2017-10-20 04:39:37 +08:00
|
|
|
snprintf(dir, PATH_MAX, "/run/user/%u/nsjail.%s", nsjconf->orig_uid, name);
|
2018-02-10 01:26:16 +08:00
|
|
|
if (mkdirAndTest(dir)) {
|
2017-06-22 07:21:09 +08:00
|
|
|
return true;
|
|
|
|
}
|
2017-06-22 06:38:49 +08:00
|
|
|
snprintf(dir, PATH_MAX, "/tmp/nsjail.%s", name);
|
2018-02-10 01:26:16 +08:00
|
|
|
if (mkdirAndTest(dir)) {
|
2017-06-22 06:38:49 +08:00
|
|
|
return true;
|
|
|
|
}
|
2017-10-09 05:00:45 +08:00
|
|
|
const char* tmp = getenv("TMPDIR");
|
2017-06-22 06:38:49 +08:00
|
|
|
if (tmp) {
|
2017-10-20 04:39:37 +08:00
|
|
|
snprintf(dir, PATH_MAX, "%s/nsjail.%s", tmp, name);
|
2018-02-10 01:26:16 +08:00
|
|
|
if (mkdirAndTest(dir)) {
|
2017-06-22 06:38:49 +08:00
|
|
|
return true;
|
2017-06-22 01:18:02 +08:00
|
|
|
}
|
|
|
|
}
|
2017-10-20 04:39:37 +08:00
|
|
|
snprintf(dir, PATH_MAX, "/dev/shm/nsjail.%s", name);
|
2018-02-10 01:26:16 +08:00
|
|
|
if (mkdirAndTest(dir)) {
|
2017-10-20 04:39:37 +08:00
|
|
|
return true;
|
|
|
|
}
|
2018-02-10 01:45:50 +08:00
|
|
|
snprintf(dir, PATH_MAX, "/tmp/nsjail.%s.%" PRIx64, name, util::rnd64());
|
2018-02-10 01:26:16 +08:00
|
|
|
if (mkdirAndTest(dir)) {
|
2017-06-22 09:06:53 +08:00
|
|
|
return true;
|
|
|
|
}
|
2017-06-22 01:18:02 +08:00
|
|
|
|
2017-06-22 06:38:49 +08:00
|
|
|
LOG_E("Couldn't create tmp directory of type '%s'", name);
|
|
|
|
return false;
|
2017-06-22 01:18:02 +08:00
|
|
|
}
|
|
|
|
|
2018-02-10 22:50:12 +08:00
|
|
|
static bool initNsInternal(nsjconf_t* nsjconf) {
|
2017-10-01 21:54:04 +08:00
|
|
|
/*
|
|
|
|
* If CLONE_NEWNS is not used, we would be changing the global mount namespace, so simply
|
|
|
|
* use --chroot in this case
|
|
|
|
*/
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!nsjconf->clone_newns) {
|
2018-02-10 11:10:18 +08:00
|
|
|
if (nsjconf->chroot.empty()) {
|
2017-10-26 06:26:02 +08:00
|
|
|
PLOG_E(
|
|
|
|
"--chroot was not specified, and it's required when not using "
|
|
|
|
"CLONE_NEWNS");
|
2017-10-01 11:47:10 +08:00
|
|
|
return false;
|
|
|
|
}
|
2018-02-10 11:10:18 +08:00
|
|
|
if (chroot(nsjconf->chroot.c_str()) == -1) {
|
|
|
|
PLOG_E("chroot('%s')", nsjconf->chroot.c_str());
|
2017-10-01 11:47:10 +08:00
|
|
|
return false;
|
2016-03-03 22:37:04 +08:00
|
|
|
}
|
|
|
|
if (chdir("/") == -1) {
|
|
|
|
PLOG_E("chdir('/')");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-08-17 01:54:50 +08:00
|
|
|
if (chdir("/") == -1) {
|
2016-08-17 01:59:51 +08:00
|
|
|
PLOG_E("chdir('/')");
|
2016-08-17 01:54:50 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-06-22 00:29:02 +08:00
|
|
|
char destdir[PATH_MAX];
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!getDir(nsjconf, destdir, "root")) {
|
2017-06-22 06:38:49 +08:00
|
|
|
LOG_E("Couldn't obtain root mount directories");
|
2017-05-29 22:39:08 +08:00
|
|
|
return false;
|
2017-05-29 09:11:32 +08:00
|
|
|
}
|
2017-06-22 01:18:02 +08:00
|
|
|
|
2017-10-01 21:54:04 +08:00
|
|
|
/* Make changes to / (recursively) private, to avoid changing the global mount ns */
|
2017-10-01 11:16:01 +08:00
|
|
|
if (mount("/", "/", NULL, MS_REC | MS_PRIVATE, NULL) == -1) {
|
|
|
|
PLOG_E("mount('/', '/', NULL, MS_REC|MS_PRIVATE, NULL)");
|
2017-10-01 10:51:56 +08:00
|
|
|
return false;
|
|
|
|
}
|
2017-05-29 22:39:08 +08:00
|
|
|
if (mount(NULL, destdir, "tmpfs", 0, "size=16777216") == -1) {
|
2017-05-29 09:11:32 +08:00
|
|
|
PLOG_E("mount('%s', 'tmpfs')", destdir);
|
|
|
|
return false;
|
2016-03-03 22:37:04 +08:00
|
|
|
}
|
2017-06-22 00:29:02 +08:00
|
|
|
|
2017-06-22 06:38:49 +08:00
|
|
|
char tmpdir[PATH_MAX];
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!getDir(nsjconf, tmpdir, "tmp")) {
|
2017-06-22 06:38:49 +08:00
|
|
|
LOG_E("Couldn't obtain temporary mount directories");
|
|
|
|
return false;
|
|
|
|
}
|
2017-05-29 22:39:08 +08:00
|
|
|
if (mount(NULL, tmpdir, "tmpfs", 0, "size=16777216") == -1) {
|
|
|
|
PLOG_E("mount('%s', 'tmpfs')", tmpdir);
|
|
|
|
return false;
|
|
|
|
}
|
2016-03-03 22:37:04 +08:00
|
|
|
|
2018-02-10 21:38:01 +08:00
|
|
|
for (auto& p : nsjconf->mountpts) {
|
|
|
|
if (!mountPt(&p, destdir, tmpdir) && p.mandatory) {
|
2016-03-03 22:37:04 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-29 22:39:08 +08:00
|
|
|
if (umount2(tmpdir, MNT_DETACH) == -1) {
|
|
|
|
PLOG_E("umount2('%s', MNT_DETACH)", tmpdir);
|
|
|
|
return false;
|
|
|
|
}
|
2017-10-01 21:54:04 +08:00
|
|
|
/*
|
2017-10-17 21:22:23 +08:00
|
|
|
* This requires some explanation: It's actually possible to pivot_root('/', '/'). After
|
|
|
|
* this operation has been completed, the old root is mounted over the new root, and it's OK
|
|
|
|
* to simply umount('/') now, and to have new_root as '/'. This allows us not care about
|
|
|
|
* providing any special directory for old_root, which is sometimes not easy, given that
|
|
|
|
* e.g. /tmp might not always be present inside new_root
|
2017-10-01 21:54:04 +08:00
|
|
|
*/
|
2017-05-29 09:11:32 +08:00
|
|
|
if (syscall(__NR_pivot_root, destdir, destdir) == -1) {
|
|
|
|
PLOG_E("pivot_root('%s', '%s')", destdir, destdir);
|
2016-03-03 22:37:04 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-05-29 09:11:32 +08:00
|
|
|
if (umount2("/", MNT_DETACH) == -1) {
|
|
|
|
PLOG_E("umount2('/', MNT_DETACH)");
|
|
|
|
return false;
|
|
|
|
}
|
2018-02-10 11:10:18 +08:00
|
|
|
if (chdir(nsjconf->cwd.c_str()) == -1) {
|
|
|
|
PLOG_E("chdir('%s')", nsjconf->cwd.c_str());
|
2016-03-03 22:37:04 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-02-10 21:38:01 +08:00
|
|
|
for (const auto& p : nsjconf->mountpts) {
|
|
|
|
if (!remountRO(p) && p.mandatory) {
|
2016-03-03 22:37:04 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2016-05-13 04:25:48 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* With mode MODE_STANDALONE_EXECVE it's required to mount /proc inside a new process,
|
2016-10-18 04:53:31 +08:00
|
|
|
* as the current process is still in the original PID namespace (man pid_namespaces)
|
2016-05-13 04:25:48 +08:00
|
|
|
*/
|
2018-02-10 22:50:12 +08:00
|
|
|
bool initNs(nsjconf_t* nsjconf) {
|
2016-05-13 04:25:48 +08:00
|
|
|
if (nsjconf->mode != MODE_STANDALONE_EXECVE) {
|
2018-02-10 01:26:16 +08:00
|
|
|
return initNsInternal(nsjconf);
|
2016-05-13 04:25:48 +08:00
|
|
|
}
|
|
|
|
|
2018-02-10 01:45:50 +08:00
|
|
|
pid_t pid = subproc::cloneProc(CLONE_FS | SIGCHLD);
|
2016-05-13 04:25:48 +08:00
|
|
|
if (pid == -1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pid == 0) {
|
2018-02-10 01:26:16 +08:00
|
|
|
exit(initNsInternal(nsjconf) ? 0 : 0xff);
|
2016-05-13 04:25:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int status;
|
2017-10-09 05:00:45 +08:00
|
|
|
while (wait4(pid, &status, 0, NULL) != pid)
|
|
|
|
;
|
2016-05-13 04:25:48 +08:00
|
|
|
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2017-05-28 06:15:53 +08:00
|
|
|
|
2018-02-10 22:50:12 +08:00
|
|
|
static bool addMountPt(mount_t* mnt, const char* src, const char* dst, const char* fstype,
|
2018-02-10 21:38:01 +08:00
|
|
|
const char* options, uintptr_t flags, isDir_t isDir, bool mandatory, const char* src_env,
|
|
|
|
const char* dst_env, const char* src_content, size_t src_content_len, bool is_symlink) {
|
2017-05-28 06:15:53 +08:00
|
|
|
if (src_env) {
|
2017-10-09 05:00:45 +08:00
|
|
|
const char* e = getenv(src_env);
|
2017-05-28 06:15:53 +08:00
|
|
|
if (e == NULL) {
|
|
|
|
LOG_W("No such envvar:'%s'", src_env);
|
|
|
|
return false;
|
|
|
|
}
|
2018-02-10 21:38:01 +08:00
|
|
|
mnt->src = e;
|
|
|
|
}
|
|
|
|
if (src) {
|
|
|
|
mnt->src.append(src);
|
2017-05-28 06:15:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (dst_env) {
|
2017-10-09 05:00:45 +08:00
|
|
|
const char* e = getenv(dst_env);
|
2017-05-28 06:15:53 +08:00
|
|
|
if (e == NULL) {
|
|
|
|
LOG_W("No such envvar:'%s'", dst_env);
|
|
|
|
return false;
|
|
|
|
}
|
2018-02-10 21:38:01 +08:00
|
|
|
mnt->dst = e;
|
|
|
|
}
|
|
|
|
if (dst) {
|
|
|
|
mnt->dst.append(dst);
|
2017-05-28 06:15:53 +08:00
|
|
|
}
|
|
|
|
|
2018-02-10 21:38:01 +08:00
|
|
|
if (fstype) {
|
|
|
|
mnt->fs_type = fstype;
|
|
|
|
}
|
|
|
|
if (options) {
|
|
|
|
mnt->options = options;
|
|
|
|
}
|
|
|
|
if (src_content) {
|
|
|
|
mnt->src_content.assign(src_content, src_content_len);
|
|
|
|
}
|
|
|
|
mnt->flags = flags;
|
|
|
|
mnt->isDir = true;
|
|
|
|
mnt->isSymlink = is_symlink;
|
|
|
|
mnt->mandatory = mandatory;
|
|
|
|
mnt->mounted = false;
|
2017-05-28 06:15:53 +08:00
|
|
|
|
2017-10-07 06:18:21 +08:00
|
|
|
switch (isDir) {
|
|
|
|
case NS_DIR_YES:
|
2018-02-10 21:38:01 +08:00
|
|
|
mnt->isDir = true;
|
2017-10-07 06:18:21 +08:00
|
|
|
break;
|
|
|
|
case NS_DIR_NO:
|
2018-02-10 21:38:01 +08:00
|
|
|
mnt->isDir = false;
|
2017-10-07 06:18:21 +08:00
|
|
|
break;
|
2017-10-09 05:00:45 +08:00
|
|
|
case NS_DIR_MAYBE: {
|
|
|
|
if (src_content) {
|
2018-02-10 21:38:01 +08:00
|
|
|
mnt->isDir = false;
|
|
|
|
} else if (mnt->src.empty()) {
|
|
|
|
mnt->isDir = true;
|
|
|
|
} else if (mnt->flags & MS_BIND) {
|
|
|
|
mnt->isDir = mnt::isDir(mnt->src.c_str());
|
2017-10-09 05:00:45 +08:00
|
|
|
} else {
|
2018-02-10 21:38:01 +08:00
|
|
|
mnt->isDir = true;
|
2017-05-28 06:15:53 +08:00
|
|
|
}
|
2017-10-09 05:00:45 +08:00
|
|
|
} break;
|
2017-10-07 06:18:21 +08:00
|
|
|
default:
|
|
|
|
LOG_F("Unknown isDir value: %d", isDir);
|
|
|
|
break;
|
2017-05-28 06:15:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2017-05-29 22:52:24 +08:00
|
|
|
|
2018-02-10 22:50:12 +08:00
|
|
|
bool addMountPtHead(nsjconf_t* nsjconf, const char* src, const char* dst, const char* fstype,
|
2018-02-10 01:26:16 +08:00
|
|
|
const char* options, uintptr_t flags, isDir_t isDir, bool mandatory, const char* src_env,
|
|
|
|
const char* dst_env, const char* src_content, size_t src_content_len, bool is_symlink) {
|
2018-02-10 22:50:12 +08:00
|
|
|
mount_t mnt;
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!addMountPt(&mnt, src, dst, fstype, options, flags, isDir, mandatory, src_env, dst_env,
|
|
|
|
src_content, src_content_len, is_symlink)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
nsjconf->mountpts.insert(nsjconf->mountpts.begin(), mnt);
|
|
|
|
return true;
|
2017-10-08 05:32:25 +08:00
|
|
|
}
|
|
|
|
|
2018-02-10 22:50:12 +08:00
|
|
|
bool addMountPtTail(nsjconf_t* nsjconf, const char* src, const char* dst, const char* fstype,
|
2018-02-10 01:26:16 +08:00
|
|
|
const char* options, uintptr_t flags, isDir_t isDir, bool mandatory, const char* src_env,
|
|
|
|
const char* dst_env, const char* src_content, size_t src_content_len, bool is_symlink) {
|
2018-02-10 22:50:12 +08:00
|
|
|
mount_t mnt;
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!addMountPt(&mnt, src, dst, fstype, options, flags, isDir, mandatory, src_env, dst_env,
|
|
|
|
src_content, src_content_len, is_symlink)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
nsjconf->mountpts.push_back(mnt);
|
|
|
|
return true;
|
2017-10-08 05:32:25 +08:00
|
|
|
}
|
|
|
|
|
2018-02-11 07:24:43 +08:00
|
|
|
const std::string describeMountPt(const mount_t& mpt) {
|
|
|
|
char mount_pt_descr[256];
|
2017-05-29 22:52:24 +08:00
|
|
|
|
|
|
|
snprintf(mount_pt_descr, sizeof(mount_pt_descr),
|
2018-02-10 21:38:01 +08:00
|
|
|
"src:'%s' dst:'%s' type:'%s' flags:%s options:'%s' isDir:%s", mpt.src.c_str(),
|
2018-02-11 04:19:47 +08:00
|
|
|
mpt.dst.c_str(), mpt.fs_type.c_str(), flagsToStr(mpt.flags).c_str(),
|
|
|
|
mpt.options.c_str(), mpt.isDir ? "true" : "false");
|
2017-05-29 22:52:24 +08:00
|
|
|
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!mpt.mandatory) {
|
2018-02-10 01:45:50 +08:00
|
|
|
util::sSnPrintf(mount_pt_descr, sizeof(mount_pt_descr), " mandatory:false");
|
2017-05-29 22:52:24 +08:00
|
|
|
}
|
2018-02-10 21:38:01 +08:00
|
|
|
if (!mpt.src_content.empty()) {
|
2018-02-10 01:45:50 +08:00
|
|
|
util::sSnPrintf(mount_pt_descr, sizeof(mount_pt_descr), " src_content_len:%zu",
|
2018-02-10 21:38:01 +08:00
|
|
|
mpt.src_content.length());
|
2017-05-29 22:52:24 +08:00
|
|
|
}
|
2018-02-10 21:38:01 +08:00
|
|
|
if (mpt.isSymlink) {
|
2018-02-10 01:45:50 +08:00
|
|
|
util::sSnPrintf(mount_pt_descr, sizeof(mount_pt_descr), " symlink:true");
|
2017-06-29 06:32:20 +08:00
|
|
|
}
|
2017-05-29 22:52:24 +08:00
|
|
|
|
|
|
|
return mount_pt_descr;
|
|
|
|
}
|
2018-02-10 01:26:16 +08:00
|
|
|
|
|
|
|
} // namespace mnt
|