init: service file keyword

Solve one more issue where privilege is required to open a file and
we do not want to grant such to the service. This is the service side
of the picture, android_get_control_file() in libcutils is the client.
The file's descriptor is placed into the environment as
"ANDROID_FILE_<path>".  For socket and files where non-alpha and
non-numeric characters in the <name/path> are replaced with _.  There
was an accompanying change in android_get_control_socket() to match
in commit 'libcutils: add android_get_control_socket() test'

Add a gTest unit test for this that tests create_file and
android_get_control_file().

Test: gTest init_tests --gtest_filter=util.create_file
Bug: 32450474
Change-Id: I96eb970c707db6d51a9885873329ba1cb1f23140
diff --git a/init/util.cpp b/init/util.cpp
index e451edd..ab6837d 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -14,32 +14,32 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ftw.h>
+#include <pwd.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <errno.h>
 #include <time.h>
-#include <ftw.h>
-#include <pwd.h>
+#include <unistd.h>
 
-#include <selinux/label.h>
 #include <selinux/android.h>
+#include <selinux/label.h>
 
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/socket.h>
 #include <sys/un.h>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-
 /* for ANDROID_SOCKET_* */
 #include <cutils/sockets.h>
-#include <android-base/stringprintf.h>
 
 #include "init.h"
 #include "log.h"
@@ -164,6 +164,76 @@
     return -1;
 }
 
+/*
+ * create_file - opens and creates a file as dictated in init.rc.
+ * This file is inherited by the daemon. We communicate the file
+ * descriptor's value via the environment variable ANDROID_FILE_<basename>
+ */
+int create_file(const char *path, int flags, mode_t perm, uid_t uid,
+                  gid_t gid, const char *filecon)
+{
+    char *secontext = NULL;
+    int ret;
+
+    if (filecon) {
+        if (setsockcreatecon(filecon) == -1) {
+            PLOG(ERROR) << "setsockcreatecon(\"" << filecon << "\") failed";
+            return -1;
+        }
+    } else if (sehandle) {
+        ret = selabel_lookup(sehandle, &secontext, path, perm);
+        if (ret != -1) {
+            ret = setfscreatecon(secontext);
+            if (ret == -1) {
+                freecon(secontext);
+                PLOG(ERROR) << "setfscreatecon(\"" << secontext << "\") failed";
+                return -1;
+            }
+        }
+    }
+
+    int fd = TEMP_FAILURE_RETRY(open(path, flags | O_NDELAY, perm));
+
+    if (filecon) {
+        setsockcreatecon(NULL);
+        lsetfilecon(path, filecon);
+    } else {
+        setfscreatecon(NULL);
+        freecon(secontext);
+    }
+
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open/create file '" << path << "'";
+        goto out_close;
+    }
+
+    if (!(flags & O_NDELAY)) fcntl(fd, F_SETFD, flags);
+
+    ret = lchown(path, uid, gid);
+    if (ret) {
+        PLOG(ERROR) << "Failed to lchown file '" << path << "'";
+        goto out_close;
+    }
+    if (perm != static_cast<mode_t>(-1)) {
+        ret = fchmodat(AT_FDCWD, path, perm, AT_SYMLINK_NOFOLLOW);
+        if (ret) {
+            PLOG(ERROR) << "Failed to fchmodat file '" << path << "'";
+            goto out_close;
+        }
+    }
+
+    LOG(INFO) << "Created file '" << path << "'"
+              << ", mode " << std::oct << perm << std::dec
+              << ", user " << uid
+              << ", group " << gid;
+
+    return fd;
+
+out_close:
+    if (fd >= 0) close(fd);
+    return -1;
+}
+
 bool read_file(const char* path, std::string* content) {
     content->clear();