Create /dev/selinux folder if it was not created by first-stage-init

The /dev/selinux folder is normally created by first-stage-init
https://cs.android.com/android/platform/superproject/+/master:system/core/init/first_stage_init.cpp;l=299-300;drc=07c86bace1e7a7cc4dd975f10b077d21c3743749

However, in some cases the first-stage-init comes from a GKI prebuilt
boot.img and doess not create the required folder (see for example
b/217677967), resulting in bugs like b/244793900.

I modified the selinux.cpp code to create the /dev/selinux folder if it
does not exist already, as a safety measure.

To verify these changes for b/244793900, follow gpaste/4922166775644160

Bug: 243923977
Test: atest SeamendcHostTest, manual verification
Change-Id: I8fe798643b1aeab2b4caac837055348febf70b94
diff --git a/init/selinux.cpp b/init/selinux.cpp
index be8c554..6137988 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -237,9 +237,9 @@
     // If there is an odm partition, precompiled_sepolicy will be in
     // odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.
     static constexpr const char vendor_precompiled_sepolicy[] =
-        "/vendor/etc/selinux/precompiled_sepolicy";
+            "/vendor/etc/selinux/precompiled_sepolicy";
     static constexpr const char odm_precompiled_sepolicy[] =
-        "/odm/etc/selinux/precompiled_sepolicy";
+            "/odm/etc/selinux/precompiled_sepolicy";
     if (access(odm_precompiled_sepolicy, R_OK) == 0) {
         precompiled_sepolicy = odm_precompiled_sepolicy;
     } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {
@@ -525,6 +525,31 @@
                                              "apex_service_contexts", "apex_seapp_contexts",
                                              "apex_test"};
 
+Result<void> CreateTmpfsDirIfNeeded() {
+    mode_t mode = 0744;
+    struct stat stat_data;
+    if (stat(kTmpfsDir.c_str(), &stat_data) != 0) {
+        if (errno != ENOENT) {
+            return ErrnoError() << "Could not stat " << kTmpfsDir;
+        }
+        if (mkdir(kTmpfsDir.c_str(), mode) != 0) {
+            return ErrnoError() << "Could not mkdir " << kTmpfsDir;
+        }
+    } else {
+        if (!S_ISDIR(stat_data.st_mode)) {
+            return Error() << kTmpfsDir << " exists and is not a directory.";
+        }
+    }
+
+    // Need to manually call chmod because mkdir will create a folder with
+    // permissions mode & ~umask.
+    if (chmod(kTmpfsDir.c_str(), mode) != 0) {
+        return ErrnoError() << "Could not chmod " << kTmpfsDir;
+    }
+
+    return {};
+}
+
 Result<void> PutFileInTmpfs(ZipArchiveHandle archive, const std::string& fileName) {
     ZipEntry entry;
     std::string dstPath = kTmpfsDir + fileName;
@@ -538,7 +563,7 @@
     unique_fd fd(TEMP_FAILURE_RETRY(
             open(dstPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)));
     if (fd == -1) {
-        return Error() << "Failed to open " << dstPath;
+        return ErrnoError() << "Failed to open " << dstPath;
     }
 
     ret = ExtractEntryToFile(archive, &entry, fd);
@@ -568,6 +593,11 @@
 
     auto handle_guard = android::base::make_scope_guard([&handle] { CloseArchive(handle); });
 
+    auto create = CreateTmpfsDirIfNeeded();
+    if (!create.ok()) {
+        return create.error();
+    }
+
     for (const auto& file : kApexSepolicy) {
         auto extract = PutFileInTmpfs(handle, file);
         if (!extract.ok()) {