init: use SELinux /dev/null if available

SELinux provides it's own /dev/null character device at
/sys/fs/selinux/null. This character device is exactly the same
as /dev/null, including the same major/minor numbers, and can
be used wherever /dev/null is used.

Use /sys/fs/selinux/null instead of trying to create our own
/dev/__null__ device. This moves us one step closer to eliminating
all uses of mknod() by init.

/sys/fs/selinux/null is only available once the /sys/fs/selinux filesystem
is mounted. It's not available to the first stage init, so we
still have to fall back to mknod then.

Change-Id: Ic733767ea6220a130537de33cc478ae79578ce20
diff --git a/init/util.cpp b/init/util.cpp
index 332aa2a..b7fb867 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -379,23 +379,28 @@
 
 void open_devnull_stdio(void)
 {
-    int fd;
-    static const char *name = "/dev/__null__";
-    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
-        fd = open(name, O_RDWR);
-        unlink(name);
-        if (fd >= 0) {
-            dup2(fd, 0);
-            dup2(fd, 1);
-            dup2(fd, 2);
-            if (fd > 2) {
-                close(fd);
-            }
-            return;
+    // Try to avoid the mknod() call if we can. Since SELinux makes
+    // a /dev/null replacement available for free, let's use it.
+    int fd = open("/sys/fs/selinux/null", O_RDWR);
+    if (fd == -1) {
+        // OOPS, /sys/fs/selinux/null isn't available, likely because
+        // /sys/fs/selinux isn't mounted. Fall back to mknod.
+        static const char *name = "/dev/__null__";
+        if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
+            fd = open(name, O_RDWR);
+            unlink(name);
+        }
+        if (fd == -1) {
+            exit(1);
         }
     }
 
-    exit(1);
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    if (fd > 2) {
+        close(fd);
+    }
 }
 
 void import_kernel_cmdline(int in_qemu,