trace: use standard POSIX header for basename()
[project/procd.git] / jail / fs.c
index fb0f504a79e7ad96a0aba27586530de21231ad95..423878b3398f1424ec823a75882de99e874243d6 100644 (file)
--- a/jail/fs.c
+++ b/jail/fs.c
@@ -32,6 +32,7 @@
 #include <libubox/avl-cmp.h>
 #include <libubox/blobmsg.h>
 #include <libubox/list.h>
+#include <libubox/utils.h>
 
 #include "elf.h"
 #include "fs.h"
@@ -46,6 +47,7 @@ struct mount {
        const char *target;
        const char *filesystemtype;
        unsigned long mountflags;
+       unsigned long propflags;
        const char *optstr;
        int error;
        bool inner;
@@ -53,51 +55,28 @@ struct mount {
 
 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 *orig_source, const char *target, const char *filesystemtype,
-                   unsigned long orig_mountflags, const char *optstr, int error, bool inner)
+                   unsigned long orig_mountflags, unsigned long propflags, const char *optstr, int error, bool inner)
 {
        struct stat s;
        char new[PATH_MAX];
        char *source = (char *)orig_source;
-       int fd;
+       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 (!is_mask && orig_source && inner) {
+       if (inner)
                if (asprintf(&source, "%s%s", root, orig_source) < 0)
                        return ENOMEM;
-       }
 
        snprintf(new, sizeof(new), "%s%s", root, target?target:source);
 
@@ -106,14 +85,14 @@ static int do_mount(const char *root, const char *orig_source, const char *targe
                        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;
                }
 
@@ -127,48 +106,58 @@ static int do_mount(const char *root, const char *orig_source, const char *targe
        } 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);
 
-                       if (inner)
-                               free(source);
-
-                       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);
 
-               if (inner)
-                       free(source);
-
-               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");
 
+       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 0;
+       return ret;
 }
 
 static int _add_mount(const char *source, const char *target, const char *filesystemtype,
-             unsigned long mountflags, const char *optstr, int error, bool inner)
+                     unsigned long mountflags, unsigned long propflags, const char *optstr,
+                     int error, bool inner)
 {
        assert(target != NULL);
 
@@ -177,7 +166,9 @@ static int _add_mount(const char *source, const char *target, const char *filesy
 
        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))
@@ -192,6 +183,7 @@ static int _add_mount(const char *source, const char *target, const char *filesy
                m->optstr = strdup(optstr);
 
        m->mountflags = mountflags;
+       m->propflags = propflags;
        m->error = error;
        m->inner = inner;
 
@@ -203,25 +195,30 @@ static int _add_mount(const char *source, const char *target, const char *filesy
 }
 
 int add_mount(const char *source, const char *target, const char *filesystemtype,
-             unsigned long mountflags, const char *optstr, int error)
+             unsigned long mountflags, unsigned long propflags, const char *optstr, int error)
 {
-       return _add_mount(source, target, filesystemtype, mountflags, optstr, error, false);
+       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, const char *optstr, int error)
+             unsigned long mountflags, unsigned long propflags, const char *optstr, int error)
 {
-       return _add_mount(source, target, filesystemtype, mountflags, optstr, error, true);
+       return _add_mount(source, target, filesystemtype, mountflags, propflags, optstr, error, true);
 }
 
-int add_mount_bind(const char *path, int readonly, int error)
+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 {
@@ -248,15 +245,16 @@ struct mount_opt {
 #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);
@@ -318,6 +316,24 @@ static int parseOCImountopts(struct blob_attr *msg, unsigned long *mount_flags,
                        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) ||
@@ -335,6 +351,7 @@ static int parseOCImountopts(struct blob_attr *msg, unsigned long *mount_flags,
        };
 
        *mount_flags = mf;
+       *propagation_flags = pf;
 
        list_for_each_entry(opt, &fsopts, list) {
                if (len)
@@ -343,25 +360,27 @@ static int parseOCImountopts(struct blob_attr *msg, unsigned long *mount_flags,
                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;
 }
@@ -370,6 +389,7 @@ int parseOCImount(struct blob_attr *msg)
 {
        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;
 
@@ -379,7 +399,7 @@ int parseOCImount(struct blob_attr *msg)
                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;
        }
@@ -387,7 +407,7 @@ int parseOCImount(struct blob_attr *msg)
        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);
@@ -399,7 +419,7 @@ static void build_noafile(void) {
        int fd;
 
        fd = creat(UJAIL_NOAFILE, 0000);
-       if (fd == -1)
+       if (fd < 0)
                return;
 
        close(fd);
@@ -416,12 +436,26 @@ int mount_all(const char *jailroot) {
                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, m->inner))
+               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);
 }
@@ -449,9 +483,10 @@ static int add_script_interp(const char *path, const char *map, int size)
        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);
@@ -461,18 +496,18 @@ int add_path_and_deps(const char *path, int readonly, int error, int lib)
        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);