aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@google.com>2018-02-24 00:00:40 +0000
committerandroid-build-merger <android-build-merger@google.com>2018-02-24 00:00:40 +0000
commit13fb44d0febfe2558a77d97f6d526618579f593a (patch)
treedde02ffa9418e0d972c3b32bdc6a246d38717b8f
parent0d40743b674795380c79354fe07f6553d3751689 (diff)
parent5fdba4ed2842acaeb2488d30f7c83bb17556eeaa (diff)
downloadminijail-13fb44d0febfe2558a77d97f6d526618579f593a.tar.gz
create parent paths of target mounts as needed
am: 5fdba4ed28 Change-Id: I7253a2a7d0423d2156dcef9ddc9891feb6e8844d
-rw-r--r--minijail0.15
-rw-r--r--system.c48
-rw-r--r--system.h2
-rw-r--r--system_unittest.cc52
4 files changed, 100 insertions, 7 deletions
diff --git a/minijail0.1 b/minijail0.1
index e713fed..7c535e0 100644
--- a/minijail0.1
+++ b/minijail0.1
@@ -17,7 +17,7 @@ Bind-mount \fIsrc\fR into the chroot directory at \fIdest\fR, optionally writeab
The \fIsrc\fR path must be an absolute path.
If \fIdest\fR is not specified, it will default to \fIsrc\fR.
If the destination does not exist, it will be created as a file or directory
-based on the \fIsrc\fR type.
+based on the \fIsrc\fR type (including missing parent directories).
.TP
\fB-c <caps>\fR
Restrict capabilities to \fIcaps\fR. When used in conjunction with \fB-u\fR and
@@ -81,7 +81,8 @@ fields here are filesystem specific.
If the mount is not a pseudo filesystem (e.g. proc or sysfs), \fIsrc\fR path
must be an absolute path (e.g. \fI/dev/sda1\fR and not \fIsda1\fR).
-If the destination does not exist, it will be created as a directory.
+If the destination does not exist, it will be created as a directory (including
+missing parent directories).
.TP
\fB-K\fR
Don't mark all existing mounts as MS_PRIVATE.
diff --git a/system.c b/system.c
index 74e97c2..bb1abf9 100644
--- a/system.c
+++ b/system.c
@@ -215,6 +215,39 @@ int write_pid_to_path(pid_t pid, const char *path)
}
/*
+ * Create the |path| directory and its parents (if need be) with |mode|.
+ * If not |isdir|, then |path| is actually a file, so the last component
+ * will not be created.
+ */
+int mkdir_p(const char *path, mode_t mode, bool isdir)
+{
+ char *dir = strdup(path);
+ if (!dir)
+ return -errno;
+
+ /* Starting from the root, work our way out to the end. */
+ char *p = strchr(dir + 1, '/');
+ while (p) {
+ *p = '\0';
+ if (mkdir(dir, mode) && errno != EEXIST) {
+ free(dir);
+ return -errno;
+ }
+ *p = '/';
+ p = strchr(p + 1, '/');
+ }
+
+ /*
+ * Create the last directory. We still check EEXIST here in case
+ * of trailing slashes.
+ */
+ free(dir);
+ if (isdir && mkdir(path, mode) && errno != EEXIST)
+ return -errno;
+ return 0;
+}
+
+/*
* setup_mount_destination: Ensures the mount target exists.
* Creates it if needed and possible.
*/
@@ -267,11 +300,16 @@ int setup_mount_destination(const char *source, const char *dest, uid_t uid,
domkdir = true;
}
- /* Now that we know what we want to do, do it! */
- if (domkdir) {
- if (mkdir(dest, 0700))
- return -errno;
- } else {
+ /*
+ * Now that we know what we want to do, do it!
+ * We always create the intermediate dirs and the final path with 0755
+ * perms and root/root ownership. This shouldn't be a problem because
+ * the actual mount will set those perms/ownership on the mount point
+ * which is all people should need to access it.
+ */
+ if (mkdir_p(dest, 0755, domkdir))
+ return -errno;
+ if (!domkdir) {
int fd = open(dest, O_RDWR | O_CREAT | O_CLOEXEC, 0700);
if (fd < 0)
return -errno;
diff --git a/system.h b/system.h
index 7f36ad2..b816f5f 100644
--- a/system.h
+++ b/system.h
@@ -51,6 +51,8 @@ int setup_and_dupe_pipe_end(int fds[2], size_t index, int fd);
int write_pid_to_path(pid_t pid, const char *path);
int write_proc_file(pid_t pid, const char *content, const char *basename);
+int mkdir_p(const char *path, mode_t mode, bool isdir);
+
int setup_mount_destination(const char *source, const char *dest, uid_t uid,
uid_t gid, bool bind);
diff --git a/system_unittest.cc b/system_unittest.cc
index db5fe98..c584808 100644
--- a/system_unittest.cc
+++ b/system_unittest.cc
@@ -8,6 +8,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
#include <unistd.h>
#include <gtest/gtest.h>
@@ -125,6 +126,57 @@ TEST(write_pid_to_path, basic) {
}
// If the destination exists, there's nothing to do.
+// Also check trailing slash handling.
+TEST(mkdir_p, dest_exists) {
+ EXPECT_EQ(0, mkdir_p("/", 0, true));
+ EXPECT_EQ(0, mkdir_p("///", 0, true));
+ EXPECT_EQ(0, mkdir_p("/proc", 0, true));
+ EXPECT_EQ(0, mkdir_p("/proc/", 0, true));
+ EXPECT_EQ(0, mkdir_p("/dev", 0, true));
+ EXPECT_EQ(0, mkdir_p("/dev/", 0, true));
+}
+
+// Create a directory tree that doesn't exist.
+TEST(mkdir_p, create_tree) {
+ char *path = get_temp_path();
+ ASSERT_NE(nullptr, path);
+ unlink(path);
+
+ // Run `mkdir -p <path>/a/b/c`.
+ char *path_a, *path_a_b, *path_a_b_c;
+ ASSERT_NE(-1, asprintf(&path_a, "%s/a", path));
+ ASSERT_NE(-1, asprintf(&path_a_b, "%s/b", path_a));
+ ASSERT_NE(-1, asprintf(&path_a_b_c, "%s/c", path_a_b));
+
+ // First try creating it as a file.
+ EXPECT_EQ(0, mkdir_p(path_a_b_c, 0700, false));
+
+ // Make sure the final path doesn't exist yet.
+ struct stat st;
+ EXPECT_EQ(0, stat(path_a_b, &st));
+ EXPECT_EQ(true, S_ISDIR(st.st_mode));
+ EXPECT_EQ(-1, stat(path_a_b_c, &st));
+
+ // Then create it as a complete dir.
+ EXPECT_EQ(0, mkdir_p(path_a_b_c, 0700, true));
+
+ // Make sure the final dir actually exists.
+ EXPECT_EQ(0, stat(path_a_b_c, &st));
+ EXPECT_EQ(true, S_ISDIR(st.st_mode));
+
+ // Clean up.
+ ASSERT_EQ(0, rmdir(path_a_b_c));
+ ASSERT_EQ(0, rmdir(path_a_b));
+ ASSERT_EQ(0, rmdir(path_a));
+ ASSERT_EQ(0, rmdir(path));
+
+ free(path_a_b_c);
+ free(path_a_b);
+ free(path_a);
+ free(path);
+}
+
+// If the destination exists, there's nothing to do.
TEST(setup_mount_destination, dest_exists) {
// Pick some paths that should always exist. We pass in invalid pointers
// for other args so we crash if the dest check doesn't short circuit.