Add minijail_copy_jail am: 6dc224fbd5 am: f11bc7ed24 am: be7d912d94

Original change: https://android-review.googlesource.com/c/platform/external/minijail/+/1704166

Change-Id: I129fa16327dde76dbfa69da22709432866f1feed
diff --git a/libminijail.c b/libminijail.c
index b09a05c..0820dbb 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -2518,6 +2518,26 @@
 	return err;
 }
 
+int API minijail_copy_jail(const struct minijail *from, struct minijail *out)
+{
+	size_t sz = minijail_size(from);
+	if (!sz)
+		return -EINVAL;
+
+	char *buf = malloc(sz);
+	if (!buf)
+		return -ENOMEM;
+
+	int err = minijail_marshal(from, buf, sz);
+	if (err)
+		goto error;
+
+	err = minijail_unmarshal(out, buf, sz);
+error:
+	free(buf);
+	return err;
+}
+
 static int setup_preload(const struct minijail *j attribute_unused,
 			 char ***child_env attribute_unused)
 {
diff --git a/libminijail.h b/libminijail.h
index 067fabd..cfd42d2 100644
--- a/libminijail.h
+++ b/libminijail.h
@@ -484,6 +484,16 @@
 void minijail_destroy(struct minijail *j);
 
 /*
+ * Deep copies the minijail in |from| to |out| providing two identical jails
+ * that can be used to contain separate children created with minijail_fork().
+ *
+ * Duplicating a jail is invalid after a jail has been passed to
+ * minijail_fork(). Many minijail_*() calls will yield undefined
+ * results when called on a jail duplicated post-fork.
+ */
+int minijail_copy_jail(const struct minijail *from, struct minijail *out);
+
+/*
  * minijail_log_to_fd: redirects the module-wide logging to an FD instead of
  * syslog.
  * @fd           FD to log to. Caller must ensure this is available after
diff --git a/libminijail_unittest.cc b/libminijail_unittest.cc
index 521982f..78e3cfb 100644
--- a/libminijail_unittest.cc
+++ b/libminijail_unittest.cc
@@ -221,6 +221,10 @@
   EXPECT_EQ(-EINVAL, minijail_unmarshal(j_, buf_, sizeof(buf_)));
 }
 
+TEST_F(MarshalTest, copy_empty) {
+  ASSERT_EQ(0, minijail_copy_jail(m_, j_));
+}
+
 TEST(KillTest, running_process) {
   const ScopedMinijail j(minijail_new());
   char* const argv[] = {"sh", "-c", "sleep 1000", nullptr};
diff --git a/rust/minijail-sys/libminijail.rs b/rust/minijail-sys/libminijail.rs
index aa613cb..594a479 100644
--- a/rust/minijail-sys/libminijail.rs
+++ b/rust/minijail-sys/libminijail.rs
@@ -286,6 +286,9 @@
     ) -> ::std::os::raw::c_int;
 }
 extern "C" {
+    pub fn minijail_copy_jail(from: *const minijail, out: *mut minijail) -> ::std::os::raw::c_int;
+}
+extern "C" {
     pub fn minijail_add_hook(
         j: *mut minijail,
         hook: minijail_hook_t,
diff --git a/rust/minijail/src/lib.rs b/rust/minijail/src/lib.rs
index ba9c8af..ba59075 100644
--- a/rust/minijail/src/lib.rs
+++ b/rust/minijail/src/lib.rs
@@ -266,6 +266,22 @@
         Ok(Minijail { jail: j })
     }
 
+    /// Clones self to a new `Minijail`. Useful because `fork` can only be called once on a
+    /// `Minijail`.
+    pub fn try_clone(&self) -> Result<Minijail> {
+        let jail_out = Minijail::new()?;
+        unsafe {
+            // Safe to clone one minijail to the other as minijail_clone doesn't modify the source
+            // jail(`self`) and leaves a valid minijail in the destination(`jail_out`).
+            let ret = minijail_copy_jail(self.jail, jail_out.jail);
+            if ret < 0 {
+                return Err(Error::ReturnCode(ret as u8));
+            }
+        }
+
+        Ok(jail_out)
+    }
+
     // The following functions are safe because they only set values in the
     // struct already owned by minijail.  The struct's lifetime is tied to
     // `struct Minijail` so it is guaranteed to be valid
@@ -991,6 +1007,15 @@
     }
 
     #[test]
+    fn run_clone() {
+        let j = Minijail::new().unwrap();
+        let b = j.try_clone().unwrap();
+        // Pass the same FDs to both clones and make sure they don't conflict.
+        j.run("/bin/true", &[1, 2], &EMPTY_STRING_SLICE).unwrap();
+        b.run("/bin/true", &[1, 2], &EMPTY_STRING_SLICE).unwrap();
+    }
+
+    #[test]
     fn run_string_vec() {
         let j = Minijail::new().unwrap();
         let args = vec!["ignored".to_string()];