diff options
author | Alexander Larsson <alexl@redhat.com> | 2016-02-16 10:03:46 +0100 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2016-02-16 10:03:46 +0100 |
commit | 5d5f8e96145cc399bff0363f7ca529000fc669d0 (patch) | |
tree | 01ddfedd08617634e9e188e43f694fa22eaa66ef | |
parent | 5422cd3952801c9815242bedc7afc20cfac6acc8 (diff) | |
download | bubblewrap-5d5f8e96145cc399bff0363f7ca529000fc669d0.tar.gz |
Break out bind_mount() to separate file
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | bind-mount.c | 282 | ||||
-rw-r--r-- | bind-mount.h | 34 | ||||
-rw-r--r-- | build-root.c | 262 |
4 files changed, 320 insertions, 260 deletions
diff --git a/Makefile.am b/Makefile.am index dbe0c1a..6b8ec98 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,6 +8,8 @@ bin_PROGRAMS = build-root build_root_SOURCES = \ build-root.c \ + bind-mount.h \ + bind-mount.c \ network.h \ network.c \ utils.h \ diff --git a/bind-mount.c b/bind-mount.c new file mode 100644 index 0000000..7a9f6ed --- /dev/null +++ b/bind-mount.c @@ -0,0 +1,282 @@ +/* build-root + * Copyright (C) 2016 Alexander Larsson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "config.h" + +#include <sys/mount.h> + +#include "utils.h" +#include "bind-mount.h" + +static char * +skip_line (char *line) +{ + while (*line != 0 && *line != '\n') + line++; + + if (*line == '\n') + line++; + + return line; +} + +static char * +skip_token (char *line, bool eat_whitespace) +{ + while (*line != ' ' && *line != '\n') + line++; + + if (eat_whitespace && *line == ' ') + line++; + + return line; +} + +static char * +unescape_mountpoint (const char *escaped, ssize_t len) +{ + char *unescaped, *res; + const char *end; + + if (len < 0) + len = strlen (escaped); + end = escaped + len; + + unescaped = res = xmalloc (len + 1); + while (escaped < end) + { + if (*escaped == '\\') + { + *unescaped++ = + ((escaped[1] - '0') << 6) | + ((escaped[2] - '0') << 3) | + ((escaped[3] - '0') << 0); + escaped += 4; + } + else + *unescaped++ = *escaped++; + } + *unescaped = 0; + return res; +} + +static char * +get_mountinfo (int proc_fd, + const char *mountpoint) +{ + char *line_mountpoint, *line_mountpoint_end; + cleanup_free char *mountinfo = NULL; + cleanup_free char *free_me = NULL; + char *line, *line_start; + char *res = NULL; + int i; + + if (mountpoint[0] != '/') + { + cleanup_free char *cwd = getcwd (NULL, 0); + if (cwd == NULL) + die_oom (); + + mountpoint = free_me = strconcat3 (cwd, "/", mountpoint); + } + + mountinfo = load_file_at (proc_fd, "/self/mountinfo"); + if (mountinfo == NULL) + return NULL; + + line = mountinfo; + + while (*line != 0) + { + cleanup_free char *unescaped = NULL; + + line_start = line; + for (i = 0; i < 4; i++) + line = skip_token (line, TRUE); + line_mountpoint = line; + line = skip_token (line, FALSE); + line_mountpoint_end = line; + line = skip_line (line); + + unescaped = unescape_mountpoint (line_mountpoint, line_mountpoint_end - line_mountpoint); + if (strcmp (mountpoint, unescaped) == 0) + { + res = line_start; + line[-1] = 0; + break; + } + } + + if (res) + return xstrdup (res); + return NULL; +} + +static unsigned long +get_mountflags (int proc_fd, + const char *mountpoint) +{ + cleanup_free char *line = NULL; + char *token, *end_token; + int i; + unsigned long flags = 0; + static const struct { int flag; char *name; } flags_data[] = { + { 0, "rw" }, + { MS_RDONLY, "ro" }, + { MS_NOSUID, "nosuid" }, + { MS_NODEV, "nodev" }, + { MS_NOEXEC, "noexec" }, + { MS_NOATIME, "noatime" }, + { MS_NODIRATIME, "nodiratime" }, + { MS_RELATIME, "relatime" }, + { 0, NULL } + }; + + line = get_mountinfo (proc_fd, mountpoint); + if (line == NULL) + return 0; + + token = line; + for (i = 0; i < 5; i++) + token = skip_token (token, TRUE); + + end_token = skip_token (token, FALSE); + *end_token = 0; + + do { + end_token = strchr (token, ','); + if (end_token != NULL) + *end_token = 0; + + for (i = 0; flags_data[i].name != NULL; i++) + { + if (strcmp (token, flags_data[i].name) == 0) + flags |= flags_data[i].flag; + } + + if (end_token) + token = end_token + 1; + else + token = NULL; + } while (token != NULL); + + return flags; +} + + +static char ** +get_submounts (int proc_fd, + const char *parent_mount) +{ + char *mountpoint, *mountpoint_end; + char **submounts; + int i, n_submounts, submounts_size; + cleanup_free char *mountinfo = NULL; + char *line; + + mountinfo = load_file_at (proc_fd, "self/mountinfo"); + if (mountinfo == NULL) + return NULL; + + submounts_size = 8; + n_submounts = 0; + submounts = xmalloc (sizeof (char *) * submounts_size); + + line = mountinfo; + + while (*line != 0) + { + cleanup_free char *unescaped = NULL; + for (i = 0; i < 4; i++) + line = skip_token (line, TRUE); + mountpoint = line; + line = skip_token (line, FALSE); + mountpoint_end = line; + line = skip_line (line); + *mountpoint_end = 0; + + unescaped = unescape_mountpoint (mountpoint, -1); + + if (*unescaped == '/' && + has_prefix (unescaped + 1, parent_mount) && + *(unescaped + 1 + strlen (parent_mount)) == '/') + { + if (n_submounts + 1 >= submounts_size) + { + submounts_size *= 2; + submounts = xrealloc (submounts, sizeof (char *) * submounts_size); + } + submounts[n_submounts++] = xstrdup (unescaped + 1); + } + } + + submounts[n_submounts] = NULL; + + return submounts; +} + +int +bind_mount (int proc_fd, + const char *src, + const char *dest, + bind_option_t options) +{ + bool readonly = (options & BIND_READONLY) != 0; + bool private = (options & BIND_PRIVATE) != 0; + bool devices = (options & BIND_DEVICES) != 0; + bool recursive = (options & BIND_RECURSIVE) != 0; + unsigned long current_flags; + int i; + + if (mount (src, dest, NULL, MS_MGC_VAL|MS_BIND|(recursive?MS_REC:0), NULL) != 0) + return 1; + + if (private) + { + if (mount ("none", dest, + NULL, MS_REC|MS_PRIVATE, NULL) != 0) + return 2; + } + + current_flags = get_mountflags (proc_fd, dest); + + if (mount ("none", dest, + NULL, MS_MGC_VAL|MS_BIND|MS_REMOUNT|current_flags|(devices?0:MS_NODEV)|MS_NOSUID|(readonly?MS_RDONLY:0), NULL) != 0) + return 3; + + /* We need to work around the fact that a bind mount does not apply the flags, so we need to manually + * apply the flags to all submounts in the recursive case. + * Note: This does not apply the flags to mounts which are later propagated into this namespace. + */ + if (recursive) + { + cleanup_strv char **submounts = get_submounts (proc_fd, dest); + if (submounts == NULL) + return 4; + + for (i = 0; submounts[i] != NULL; i++) + { + current_flags = get_mountflags (proc_fd, submounts[i]); + if (mount ("none", submounts[i], + NULL, MS_MGC_VAL|MS_BIND|MS_REMOUNT|current_flags|(devices?0:MS_NODEV)|MS_NOSUID|(readonly?MS_RDONLY:0), NULL) != 0) + return 5; + } + } + + return 0; +} diff --git a/bind-mount.h b/bind-mount.h new file mode 100644 index 0000000..430a520 --- /dev/null +++ b/bind-mount.h @@ -0,0 +1,34 @@ +/* build-root + * Copyright (C) 2016 Alexander Larsson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef __MOUNTS_H__ +#define __MOUNTS_H__ + +typedef enum { + BIND_READONLY = (1<<0), + BIND_PRIVATE = (1<<1), + BIND_DEVICES = (1<<2), + BIND_RECURSIVE = (1<<3), +} bind_option_t; + +int bind_mount (int proc_fd, + const char *src, + const char *dest, + bind_option_t options); + +#endif /* __MOUNTS_H__ */ diff --git a/build-root.c b/build-root.c index a48e66d..d0540a0 100644 --- a/build-root.c +++ b/build-root.c @@ -30,6 +30,7 @@ #include "utils.h" #include "network.h" +#include "bind-mount.h" /* Globals to avoid having to use getuid(), since the uid/gid changes during runtime */ static uid_t uid; @@ -48,265 +49,6 @@ usage () exit (1); } -typedef enum { - BIND_READONLY = (1<<0), - BIND_PRIVATE = (1<<1), - BIND_DEVICES = (1<<2), - BIND_RECURSIVE = (1<<3), -} bind_option_t; - -static char * -skip_line (char *line) -{ - while (*line != 0 && *line != '\n') - line++; - - if (*line == '\n') - line++; - - return line; -} - -static char * -skip_token (char *line, bool eat_whitespace) -{ - while (*line != ' ' && *line != '\n') - line++; - - if (eat_whitespace && *line == ' ') - line++; - - return line; -} - -static char * -unescape_mountpoint (const char *escaped, ssize_t len) -{ - char *unescaped, *res; - const char *end; - - if (len < 0) - len = strlen (escaped); - end = escaped + len; - - unescaped = res = xmalloc (len + 1); - while (escaped < end) - { - if (*escaped == '\\') - { - *unescaped++ = - ((escaped[1] - '0') << 6) | - ((escaped[2] - '0') << 3) | - ((escaped[3] - '0') << 0); - escaped += 4; - } - else - *unescaped++ = *escaped++; - } - *unescaped = 0; - return res; -} - -static char * -get_mountinfo (const char *mountpoint) -{ - char *line_mountpoint, *line_mountpoint_end; - cleanup_free char *mountinfo = NULL; - cleanup_free char *free_me = NULL; - char *line, *line_start; - char *res = NULL; - int i; - - if (mountpoint[0] != '/') - { - cleanup_free char *cwd = getcwd (NULL, 0); - if (cwd == NULL) - die_oom (); - - mountpoint = free_me = strconcat3 (cwd, "/", mountpoint); - } - - mountinfo = load_file_at (proc_fd, "/self/mountinfo"); - if (mountinfo == NULL) - return NULL; - - line = mountinfo; - - while (*line != 0) - { - cleanup_free char *unescaped = NULL; - - line_start = line; - for (i = 0; i < 4; i++) - line = skip_token (line, TRUE); - line_mountpoint = line; - line = skip_token (line, FALSE); - line_mountpoint_end = line; - line = skip_line (line); - - unescaped = unescape_mountpoint (line_mountpoint, line_mountpoint_end - line_mountpoint); - if (strcmp (mountpoint, unescaped) == 0) - { - res = line_start; - line[-1] = 0; - break; - } - } - - if (res) - return xstrdup (res); - return NULL; -} - -static unsigned long -get_mountflags (const char *mountpoint) -{ - cleanup_free char *line = NULL; - char *token, *end_token; - int i; - unsigned long flags = 0; - static const struct { int flag; char *name; } flags_data[] = { - { 0, "rw" }, - { MS_RDONLY, "ro" }, - { MS_NOSUID, "nosuid" }, - { MS_NODEV, "nodev" }, - { MS_NOEXEC, "noexec" }, - { MS_NOATIME, "noatime" }, - { MS_NODIRATIME, "nodiratime" }, - { MS_RELATIME, "relatime" }, - { 0, NULL } - }; - - line = get_mountinfo (mountpoint); - if (line == NULL) - return 0; - - token = line; - for (i = 0; i < 5; i++) - token = skip_token (token, TRUE); - - end_token = skip_token (token, FALSE); - *end_token = 0; - - do { - end_token = strchr (token, ','); - if (end_token != NULL) - *end_token = 0; - - for (i = 0; flags_data[i].name != NULL; i++) - { - if (strcmp (token, flags_data[i].name) == 0) - flags |= flags_data[i].flag; - } - - if (end_token) - token = end_token + 1; - else - token = NULL; - } while (token != NULL); - - return flags; -} - - -static char ** -get_submounts (const char *parent_mount) -{ - char *mountpoint, *mountpoint_end; - char **submounts; - int i, n_submounts, submounts_size; - cleanup_free char *mountinfo = NULL; - char *line; - - mountinfo = load_file_at (proc_fd, "self/mountinfo"); - if (mountinfo == NULL) - return NULL; - - submounts_size = 8; - n_submounts = 0; - submounts = xmalloc (sizeof (char *) * submounts_size); - - line = mountinfo; - - while (*line != 0) - { - cleanup_free char *unescaped = NULL; - for (i = 0; i < 4; i++) - line = skip_token (line, TRUE); - mountpoint = line; - line = skip_token (line, FALSE); - mountpoint_end = line; - line = skip_line (line); - *mountpoint_end = 0; - - unescaped = unescape_mountpoint (mountpoint, -1); - - if (*unescaped == '/' && - has_prefix (unescaped + 1, parent_mount) && - *(unescaped + 1 + strlen (parent_mount)) == '/') - { - if (n_submounts + 1 >= submounts_size) - { - submounts_size *= 2; - submounts = xrealloc (submounts, sizeof (char *) * submounts_size); - } - submounts[n_submounts++] = xstrdup (unescaped + 1); - } - } - - submounts[n_submounts] = NULL; - - return submounts; -} - -static int -bind_mount (const char *src, const char *dest, bind_option_t options) -{ - bool readonly = (options & BIND_READONLY) != 0; - bool private = (options & BIND_PRIVATE) != 0; - bool devices = (options & BIND_DEVICES) != 0; - bool recursive = (options & BIND_RECURSIVE) != 0; - unsigned long current_flags; - int i; - - if (mount (src, dest, NULL, MS_MGC_VAL|MS_BIND|(recursive?MS_REC:0), NULL) != 0) - return 1; - - if (private) - { - if (mount ("none", dest, - NULL, MS_REC|MS_PRIVATE, NULL) != 0) - return 2; - } - - current_flags = get_mountflags (dest); - - if (mount ("none", dest, - NULL, MS_MGC_VAL|MS_BIND|MS_REMOUNT|current_flags|(devices?0:MS_NODEV)|MS_NOSUID|(readonly?MS_RDONLY:0), NULL) != 0) - return 3; - - /* We need to work around the fact that a bind mount does not apply the flags, so we need to manually - * apply the flags to all submounts in the recursive case. - * Note: This does not apply the flags to mounts which are later propagated into this namespace. - */ - if (recursive) - { - cleanup_strv char **submounts = get_submounts (dest); - if (submounts == NULL) - return 4; - - for (i = 0; submounts[i] != NULL; i++) - { - current_flags = get_mountflags (submounts[i]); - if (mount ("none", submounts[i], - NULL, MS_MGC_VAL|MS_BIND|MS_REMOUNT|current_flags|(devices?0:MS_NODEV)|MS_NOSUID|(readonly?MS_RDONLY:0), NULL) != 0) - return 5; - } - } - - return 0; -} - static void block_sigchild (void) { @@ -818,7 +560,7 @@ main (int argc, case SETUP_BIND_MOUNT: if (mkdir_with_parents (dest, 0755, TRUE) != 0) die_with_error ("Can't mkdir parents of %s", op->dest); - if (bind_mount (source, dest, BIND_RECURSIVE) != 0) + if (bind_mount (proc_fd, source, dest, BIND_RECURSIVE) != 0) die_with_error ("Can't bind mount %s on %s", op->source, op->dest); break; default: |