#include <fcntl.h>
#include <linux/limits.h>
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <libubox/avl-cmp.h>
#include <libubox/blobmsg.h>
#include <libubox/list.h>
+#include <libubox/utils.h>
#include "elf.h"
#include "fs.h"
const char *target;
const char *filesystemtype;
unsigned long mountflags;
+ unsigned long propflags;
const char *optstr;
int error;
+ bool inner;
};
struct avl_tree mounts;
-int mkdir_p(char *dir, mode_t mask)
-{
- char *l = strrchr(dir, '/');
- int ret;
-
- if (!l)
- return 0;
-
- *l = '\0';
-
- if (mkdir_p(dir, mask))
- return -1;
-
- *l = '/';
-
- ret = mkdir(dir, mask);
- if (ret && errno == EEXIST)
- return 0;
-
- if (ret)
- ERROR("mkdir(%s, %d) failed: %m\n", dir, mask);
-
- return ret;
-}
-
-static int do_mount(const char *root, const char *source, const char *target, const char *filesystemtype,
- unsigned long orig_mountflags, const char *optstr, int error)
+static int do_mount(const char *root, const char *orig_source, const char *target, const char *filesystemtype,
+ unsigned long orig_mountflags, unsigned long propflags, const char *optstr, int error, bool inner)
{
struct stat s;
char new[PATH_MAX];
- int fd;
+ char *source = (char *)orig_source;
+ int fd, ret = 0;
bool is_bind = (orig_mountflags & MS_BIND);
bool is_mask = (source == (void *)(-1));
unsigned long mountflags = orig_mountflags;
+ assert(!(inner && is_mask));
+ assert(!(inner && !orig_source));
+
if (source && is_bind && stat(source, &s)) {
ERROR("stat(%s) failed: %m\n", source);
return error;
}
+ if (inner)
+ if (asprintf(&source, "%s%s", root, orig_source) < 0)
+ return ENOMEM;
+
snprintf(new, sizeof(new), "%s%s", root, target?target:source);
if (is_mask) {
return 0; /* doesn't exists, nothing to mask */
if (S_ISDIR(s.st_mode)) {/* use empty 0-sized tmpfs for directories */
- if (mount(NULL, new, "tmpfs", MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_NOATIME, "size=0,mode=000"))
+ if (mount("none", new, "tmpfs", MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_NOATIME, "size=0,mode=000"))
return error;
} else {
/* mount-bind 0-sized file having mode 000 */
- if (mount(UJAIL_NOAFILE, new, NULL, MS_BIND, NULL))
+ if (mount(UJAIL_NOAFILE, new, "bind", MS_BIND, NULL))
return error;
- if (mount(UJAIL_NOAFILE, new, NULL, MS_REMOUNT | MS_BIND | MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_NOATIME, NULL))
+ if (mount(UJAIL_NOAFILE, new, "bind", MS_REMOUNT | MS_BIND | MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_NOATIME, NULL))
return error;
}
} else if (is_bind && source) {
mkdir_p(dirname(new), 0755);
snprintf(new, sizeof(new), "%s%s", root, target?target:source);
- fd = creat(new, 0644);
- if (fd == -1) {
- ERROR("creat(%s) failed: %m\n", new);
- return error;
+ fd = open(new, O_CREAT|O_WRONLY|O_TRUNC|O_EXCL, 0644);
+ if (fd >= 0)
+ close(fd);
+
+ if (error && fd < 0 && errno != EEXIST) {
+ ERROR("failed to create mount target %s: %m\n", new);
+
+ ret = errno;
+ goto free_source_out;
}
- close(fd);
}
if (is_bind) {
- if (mount(source?:new, new, filesystemtype, MS_BIND | (mountflags & MS_REC), optstr)) {
+ if (mount(source?:new, new, filesystemtype?:"bind", MS_BIND | (mountflags & MS_REC), optstr)) {
if (error)
ERROR("failed to mount -B %s %s: %m\n", source, new);
- return error;
+ ret = error;
+ goto free_source_out;
}
mountflags |= MS_REMOUNT;
}
- if (mount(source?:(is_bind?new:NULL), new, filesystemtype, mountflags, optstr)) {
+ const char *hack_fstype = ((!filesystemtype || strcmp(filesystemtype, "cgroup"))?filesystemtype:"cgroup2");
+ if (mount(source?:(is_bind?new:NULL), new, hack_fstype?:"none", mountflags, optstr)) {
if (error)
ERROR("failed to mount %s %s: %m\n", source, new);
- return error;
+ ret = error;
+ goto free_source_out;
}
DEBUG("mount %s%s %s (%s)\n", (mountflags & MS_BIND)?"-B ":"", source, new,
(mountflags & MS_RDONLY)?"ro":"rw");
- return 0;
+ if (propflags && mount("none", new, "none", propflags, NULL)) {
+ if (error)
+ ERROR("failed to mount --make-... %s \n", new);
+
+ ret = error;
+ }
+
+free_source_out:
+ if (inner)
+ free(source);
+
+ return ret;
}
-int add_mount(const char *source, const char *target, const char *filesystemtype,
- unsigned long mountflags, const char *optstr, int error)
+static int _add_mount(const char *source, const char *target, const char *filesystemtype,
+ unsigned long mountflags, unsigned long propflags, const char *optstr,
+ int error, bool inner)
{
assert(target != NULL);
struct mount *m;
m = calloc(1, sizeof(struct mount));
- assert(m != NULL);
+ if (!m)
+ return ENOMEM;
+
m->avl.key = m->target = strdup(target);
if (source) {
if (source != (void*)(-1))
m->optstr = strdup(optstr);
m->mountflags = mountflags;
+ m->propflags = propflags;
m->error = error;
+ m->inner = inner;
avl_insert(&mounts, &m->avl);
DEBUG("adding mount %s %s bind(%d) ro(%d) err(%d)\n", (m->source == (void*)(-1))?"mask":m->source, m->target,
return 0;
}
-int add_mount_bind(const char *path, int readonly, int error)
+int add_mount(const char *source, const char *target, const char *filesystemtype,
+ unsigned long mountflags, unsigned long propflags, const char *optstr, int error)
+{
+ return _add_mount(source, target, filesystemtype, mountflags, propflags, optstr, error, false);
+}
+
+int add_mount_inner(const char *source, const char *target, const char *filesystemtype,
+ unsigned long mountflags, unsigned long propflags, const char *optstr, int error)
+{
+ return _add_mount(source, target, filesystemtype, mountflags, propflags, optstr, error, true);
+}
+
+static int _add_mount_bind(const char *path, const char *path2, int readonly, int error)
{
unsigned long mountflags = MS_BIND;
if (readonly)
mountflags |= MS_RDONLY;
- return add_mount(path, path, NULL, mountflags, NULL, error);
+ return add_mount(path, path2, NULL, mountflags, 0, NULL, error);
+}
+
+int add_mount_bind(const char *path, int readonly, int error)
+{
+ return _add_mount_bind(path, path, readonly, error);
}
enum {
#define MS_LAZYTIME (1 << 25)
#endif
-static int parseOCImountopts(struct blob_attr *msg, unsigned long *mount_flags, char **mount_data, int *error)
+static int parseOCImountopts(struct blob_attr *msg, unsigned long *mount_flags, unsigned long *propagation_flags, char **mount_data, int *error)
{
struct blob_attr *cur;
int rem;
unsigned long mf = 0;
+ unsigned long pf = 0;
char *tmp;
struct list_head fsopts = LIST_HEAD_INIT(fsopts);
size_t len = 0;
- struct mount_opt *opt;
+ struct mount_opt *opt, *tmpopt;
blobmsg_for_each_attr(cur, msg, rem) {
tmp = blobmsg_get_string(cur);
mf |= MS_NOSUID;
else if (!strcmp("remount", tmp))
mf |= MS_REMOUNT;
+ /* propagation flags */
+ else if (!strcmp("private", tmp))
+ pf |= MS_PRIVATE;
+ else if (!strcmp("rprivate", tmp))
+ pf |= MS_PRIVATE | MS_REC;
+ else if (!strcmp("slave", tmp))
+ pf |= MS_SLAVE;
+ else if (!strcmp("rslave", tmp))
+ pf |= MS_SLAVE | MS_REC;
+ else if (!strcmp("shared", tmp))
+ pf |= MS_SHARED;
+ else if (!strcmp("rshared", tmp))
+ pf |= MS_SHARED | MS_REC;
+ else if (!strcmp("unbindable", tmp))
+ pf |= MS_UNBINDABLE;
+ else if (!strcmp("runbindable", tmp))
+ pf |= MS_UNBINDABLE | MS_REC;
+ /* special case: 'nofail' */
else if(!strcmp("nofail", tmp))
*error = 0;
else if (!strcmp("auto", tmp) ||
};
*mount_flags = mf;
+ *propagation_flags = pf;
list_for_each_entry(opt, &fsopts, list) {
if (len)
len += strlen(opt->optstr);
};
- if (!len)
- return 0;
-
- *mount_data = calloc(len + 1, sizeof(char));
- if (!mount_data)
- return ENOMEM;
+ if (len) {
+ *mount_data = calloc(len + 1, sizeof(char));
+ if (!(*mount_data))
+ return ENOMEM;
- len = 0;
- list_for_each_entry(opt, &fsopts, list) {
- if (len)
- strcat(*mount_data, ",");
+ len = 0;
+ list_for_each_entry(opt, &fsopts, list) {
+ if (len)
+ strcat(*mount_data, ",");
- strcat(*mount_data, opt->optstr);
- ++len;
- };
+ strcat(*mount_data, opt->optstr);
+ ++len;
+ }
- list_del(&fsopts);
+ list_for_each_entry_safe(opt, tmpopt, &fsopts, list) {
+ list_del(&opt->list);
+ free(opt);
+ }
+ }
- DEBUG("mount flags(%08lx) fsopts(\"%s\")\n", mf, *mount_data?:"");
+ DEBUG("mount flags(%08lx) propagation(%08lx) fsopts(\"%s\")\n", mf, pf, *mount_data?:"");
return 0;
}
{
struct blob_attr *tb[__OCI_MOUNT_MAX];
unsigned long mount_flags = 0;
+ unsigned long propagation_flags = 0;
char *mount_data = NULL;
int ret, err = -1;
return EINVAL;
if (tb[OCI_MOUNT_OPTIONS]) {
- ret = parseOCImountopts(tb[OCI_MOUNT_OPTIONS], &mount_flags, &mount_data, &err);
+ ret = parseOCImountopts(tb[OCI_MOUNT_OPTIONS], &mount_flags, &propagation_flags, &mount_data, &err);
if (ret)
return ret;
}
ret = add_mount(tb[OCI_MOUNT_SOURCE] ? blobmsg_get_string(tb[OCI_MOUNT_SOURCE]) : NULL,
blobmsg_get_string(tb[OCI_MOUNT_DESTINATION]),
tb[OCI_MOUNT_TYPE] ? blobmsg_get_string(tb[OCI_MOUNT_TYPE]) : NULL,
- mount_flags, mount_data, err);
+ mount_flags, propagation_flags, mount_data, err);
if (mount_data)
free(mount_data);
int fd;
fd = creat(UJAIL_NOAFILE, 0000);
- if (fd == -1)
+ if (fd < 0)
return;
close(fd);
add_mount_bind(l->path, 1, -1);
avl_for_each_element(&mounts, m, avl)
- if (do_mount(jailroot, m->source, m->target, m->filesystemtype, m->mountflags, m->optstr, m->error))
+ if (do_mount(jailroot, m->source, m->target, m->filesystemtype, m->mountflags,
+ m->propflags, m->optstr, m->error, m->inner))
return -1;
return 0;
}
+void mount_free(void) {
+ struct mount *m, *tmp;
+
+ avl_remove_all_elements(&mounts, m, avl, tmp) {
+ if (m->source != (void*)(-1))
+ free((void*)m->source);
+ free((void*)m->target);
+ free((void*)m->filesystemtype);
+ free((void*)m->optstr);
+ free(m);
+ }
+}
+
void mount_list_init(void) {
avl_init(&mounts, avl_strcmp, false, NULL);
}
return add_path_and_deps(buf, 1, -1, 0);
}
-int add_path_and_deps(const char *path, int readonly, int error, int lib)
+int add_2paths_and_deps(const char *path, const char *path2, int readonly, int error, int lib)
{
assert(path != NULL);
+ assert(path2 != NULL);
if (lib == 0 && path[0] != '/') {
ERROR("%s is not an absolute path\n", path);
char *map = NULL;
int fd, ret = -1;
if (path[0] == '/') {
- if (avl_find(&mounts, path))
+ if (avl_find(&mounts, path2))
return 0;
fd = open(path, O_RDONLY|O_CLOEXEC);
- if (fd == -1)
+ if (fd < 0)
return error;
- add_mount_bind(path, readonly, error);
+ _add_mount_bind(path, path2, readonly, error);
} else {
if (avl_find(&libraries, path))
return 0;
char *fullpath;
fd = lib_open(&fullpath, path);
- if (fd == -1)
+ if (fd < 0)
return error;
if (fullpath) {
alloc_library(fullpath, path);