composd: prepare staging directory w/ libartpalette-system

Output files are staged until they are fully generated, then move to the
final location. The staging directory has a different SELinux context to
prevent misuse, and should change when it's moved to the final
directory.

This change makes composd to follow the same setup, using libartpalette.
As a result, the output are no longer stored in CompOS's own apexdata
(which was not intentional).

This change does not use bindgen, which seems have some difficulty to
bridge `const char**` correctly. Neither cxx, since it doesn't seem to
simplify the (already simple) call.

Bug: 205750213
Test: See odrefresh produces output in the staging directory

Change-Id: Ifc97b31a98052a31209556449d1642089a8c0e2e
diff --git a/compos/composd/native/lib.rs b/compos/composd/native/lib.rs
index ace9600..cbec7fd 100644
--- a/compos/composd/native/lib.rs
+++ b/compos/composd/native/lib.rs
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Bindings native helpers for composd.
+//! Native helpers for composd.
 
-pub use ffi::*;
+pub use art::*;
+pub use crypto::*;
 
 #[cxx::bridge]
-mod ffi {
+mod crypto {
     /// Contains either a key or a reason why the key could not be extracted.
     struct KeyResult {
         /// The extracted key. If empty, the attempt to extract the key failed.
@@ -36,3 +37,38 @@
         fn extract_rsa_public_key(der_certificate: &[u8]) -> KeyResult;
     }
 }
+
+mod art {
+    use anyhow::{anyhow, Result};
+    use libc::c_char;
+    use std::ffi::{CStr, OsStr};
+    use std::io::Error;
+    use std::os::unix::ffi::OsStrExt;
+    use std::path::Path;
+    use std::ptr::null;
+
+    // From libartpalette(-system)
+    extern "C" {
+        fn PaletteCreateOdrefreshStagingDirectory(out_staging_dir: *mut *const c_char) -> i32;
+    }
+    const PALETTE_STATUS_OK: i32 = 0;
+    const PALETTE_STATUS_CHECK_ERRNO: i32 = 1;
+
+    /// Creates and returns the staging directory for odrefresh.
+    pub fn palette_create_odrefresh_staging_directory() -> Result<&'static Path> {
+        let mut staging_dir: *const c_char = null();
+        // SAFETY: The C function always returns a non-null C string (after created the directory).
+        let status = unsafe { PaletteCreateOdrefreshStagingDirectory(&mut staging_dir) };
+        match status {
+            PALETTE_STATUS_OK => {
+                // SAFETY: The previously returned `*const c_char` should point to a legitimate C
+                // string.
+                let cstr = unsafe { CStr::from_ptr(staging_dir) };
+                let path = OsStr::from_bytes(cstr.to_bytes()).as_ref();
+                Ok(path)
+            }
+            PALETTE_STATUS_CHECK_ERRNO => Err(anyhow!(Error::last_os_error().to_string())),
+            _ => Err(anyhow!("Failed with palette status {}", status)),
+        }
+    }
+}