Update OTA to understand SELinux labels and capabilities

Update the OTA generation script to understand SELinux file
labels and file capabilities.

Make fs_config aware of SELinux labels and file capabilities, and
optionally output those elements whenever we output the
UID / GID / file perms. The information is emitted as a key=value pair
to allow for future extensibility.

Pass the SELinux file label and capabilities to the newly created
set_metadata() and set_metadata_recursive() calls. When the OTA
script fixes up filesystem permissions, it will also fix up the SELinux
labels and file capabilities.

If no SELinux label and capabilities are available for the file, use
the old set_perm and set_perm_recursive calls.

Bug: 8985290
Bug: 10183961
Bug: 10186213
Change-Id: I4fcfb2c234dbfb965cee9e62f060092a4274d22d
diff --git a/tools/fs_config/Android.mk b/tools/fs_config/Android.mk
index 5ef32dd..02deabb 100644
--- a/tools/fs_config/Android.mk
+++ b/tools/fs_config/Android.mk
@@ -17,6 +17,7 @@
 
 LOCAL_SRC_FILES := fs_config.c
 LOCAL_MODULE := fs_config
+LOCAL_STATIC_LIBRARIES := libselinux
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
 include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/fs_config/fs_config.c b/tools/fs_config/fs_config.c
index f6760cc..f594c1e 100644
--- a/tools/fs_config/fs_config.c
+++ b/tools/fs_config/fs_config.c
@@ -15,10 +15,16 @@
  */
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <sys/stat.h>
 #include <errno.h>
 #include <unistd.h>
 #include <string.h>
+#include <inttypes.h>
+
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include <selinux/android.h>
 
 #include "private/android_filesystem_config.h"
 
@@ -27,21 +33,67 @@
 // filename along with its desired uid, gid, and mode (in octal).
 // The leading slash should be stripped from the input.
 //
+// After the first 4 columns, optional key=value pairs are emitted
+// for each file.  Currently, the following keys are supported:
+// * -S: selabel=[selinux_label]
+// * -C: capabilities=[hex capabilities value]
+//
 // Example input:
 //
-//    system/etc/dbus.conf
-//    data/app/
+//      system/etc/dbus.conf
+//      data/app/
 //
 // Output:
 //
-//    system/etc/dbus.conf 1002 1002 440
-//    data/app 1000 1000 771
+//      system/etc/dbus.conf 1002 1002 440
+//      data/app 1000 1000 771
+//
+//   or if, for example, -S is used:
+//
+//      system/etc/dbus.conf 1002 1002 440 selabel=u:object_r:system_file:s0
+//      data/app 1000 1000 771 selabel=u:object_r:apk_data_file:s0
 //
 // Note that the output will omit the trailing slash from
 // directories.
 
+static struct selabel_handle* get_sehnd(const char* context_file) {
+  struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, context_file } };
+  struct selabel_handle* sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+
+  if (!sehnd) {
+    perror("error running selabel_open");
+    exit(EXIT_FAILURE);
+  }
+  return sehnd;
+}
+
+static void usage() {
+  fprintf(stderr, "Usage: fs_config [-S context_file] [-C]\n");
+}
+
 int main(int argc, char** argv) {
   char buffer[1024];
+  const char* context_file = NULL;
+  struct selabel_handle* sehnd = NULL;
+  int print_capabilities = 0;
+  int opt;
+  while((opt = getopt(argc, argv, "CS:")) != -1) {
+    switch(opt) {
+    case 'C':
+      print_capabilities = 1;
+      break;
+    case 'S':
+      context_file = optarg;
+      break;
+    default:
+      usage();
+      exit(EXIT_FAILURE);
+    }
+  }
+
+  if (context_file != NULL) {
+    sehnd = get_sehnd(context_file);
+  }
 
   while (fgets(buffer, 1023, stdin) != NULL) {
     int is_dir = 0;
@@ -64,7 +116,40 @@
     unsigned uid = 0, gid = 0, mode = 0;
     uint64_t capabilities;
     fs_config(buffer, is_dir, &uid, &gid, &mode, &capabilities);
-    printf("%s %d %d %o\n", buffer, uid, gid, mode);
+    printf("%s %d %d %o", buffer, uid, gid, mode);
+
+    if (sehnd != NULL) {
+      size_t buffer_strlen = strnlen(buffer, sizeof(buffer));
+      if (buffer_strlen >= sizeof(buffer)) {
+        fprintf(stderr, "non null terminated buffer, aborting\n");
+        exit(EXIT_FAILURE);
+      }
+      size_t full_name_size = buffer_strlen + 2;
+      char* full_name = (char*) malloc(full_name_size);
+      if (full_name == NULL) {
+        perror("malloc");
+        exit(EXIT_FAILURE);
+      }
+
+      full_name[0] = '/';
+      strncpy(full_name + 1, buffer, full_name_size - 1);
+      full_name[full_name_size - 1] = '\0';
+
+      char* secontext;
+      if (selabel_lookup(sehnd, &secontext, full_name, ( mode | (is_dir ? S_IFDIR : S_IFREG)))) {
+        secontext = strdup("u:object_r:unlabeled:s0");
+      }
+
+      printf(" selabel=%s", secontext);
+      free(full_name);
+      freecon(secontext);
+    }
+
+    if (print_capabilities) {
+      printf(" capabilities=0x%" PRIx64, capabilities);
+    }
+
+    printf("\n");
   }
   return 0;
 }