Snap for 4620899 from 35b33b92bacefebb0f84fe446d5c996735c68221 to pi-release

Change-Id: Iabfe08ff39c08acb9f6e87516ea8538bf1092fad
diff --git a/minijail0.1 b/minijail0.1
index e713fed..7c535e0 100644
--- a/minijail0.1
+++ b/minijail0.1
@@ -17,7 +17,7 @@
 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 @@
 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 @@
 }
 
 /*
+ * 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 @@
 		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 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 @@
 }
 
 // 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.