Extend recovery and updater to support setting file security contexts.

Extend minzip, recovery, and updater to set the security context on
files based on the file_contexts configuration included in the package.

Change-Id: Ied379f266a16c64f2b4dca15dc39b98fcce16f29
diff --git a/Android.mk b/Android.mk
index 304626e..251c920 100644
--- a/Android.mk
+++ b/Android.mk
@@ -24,12 +24,13 @@
 LOCAL_CFLAGS += -DUSE_EXT4
 LOCAL_C_INCLUDES += system/extras/ext4_utils
 LOCAL_STATIC_LIBRARIES += libext4_utils libz
+endif
+
 ifeq ($(HAVE_SELINUX), true)
 LOCAL_C_INCLUDES += external/libselinux/include
 LOCAL_STATIC_LIBRARIES += libselinux
 LOCAL_CFLAGS += -DHAVE_SELINUX
 endif # HAVE_SELINUX
-endif
 
 # This binary is in the recovery ramdisk, which is otherwise a copy of root.
 # It gets copied there in config/Makefile.  LOCAL_MODULE_TAGS suppresses
diff --git a/minzip/Android.mk b/minzip/Android.mk
index b1ee674..6c1d096 100644
--- a/minzip/Android.mk
+++ b/minzip/Android.mk
@@ -11,7 +11,13 @@
 LOCAL_C_INCLUDES += \
 	external/zlib \
 	external/safe-iop/include
-	
+
+ifeq ($(HAVE_SELINUX),true)
+LOCAL_C_INCLUDES += external/libselinux/include
+LOCAL_STATIC_LIBRARIES += libselinux
+LOCAL_CFLAGS += -DHAVE_SELINUX
+endif
+
 LOCAL_MODULE := libminzip
 
 LOCAL_CFLAGS += -Wall
diff --git a/minzip/DirUtil.c b/minzip/DirUtil.c
index 20c89cd..0d49b57 100644
--- a/minzip/DirUtil.c
+++ b/minzip/DirUtil.c
@@ -54,7 +54,8 @@
 
 int
 dirCreateHierarchy(const char *path, int mode,
-        const struct utimbuf *timestamp, bool stripFileName)
+        const struct utimbuf *timestamp, bool stripFileName,
+        struct selabel_handle *sehnd)
 {
     DirStatus ds;
 
@@ -144,7 +145,25 @@
         } else if (ds == DMISSING) {
             int err;
 
+#ifdef HAVE_SELINUX
+            char *secontext = NULL;
+
+            if (sehnd) {
+                selabel_lookup(sehnd, &secontext, cpath, mode);
+                setfscreatecon(secontext);
+            }
+#endif
+
             err = mkdir(cpath, mode);
+
+#ifdef HAVE_SELINUX
+
+            if (secontext) {
+                freecon(secontext);
+                setfscreatecon(NULL);
+            }
+#endif
+
             if (err != 0) {
                 free(cpath);
                 return -1;
diff --git a/minzip/DirUtil.h b/minzip/DirUtil.h
index 5d881f5..1f378b7 100644
--- a/minzip/DirUtil.h
+++ b/minzip/DirUtil.h
@@ -20,6 +20,13 @@
 #include <stdbool.h>
 #include <utime.h>
 
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#else
+struct selabel_handle;
+#endif
+
 /* Like "mkdir -p", try to guarantee that all directories
  * specified in path are present, creating as many directories
  * as necessary.  The specified mode is passed to all mkdir
@@ -34,7 +41,8 @@
  * (usually if some element of path is not a directory).
  */
 int dirCreateHierarchy(const char *path, int mode,
-        const struct utimbuf *timestamp, bool stripFileName);
+        const struct utimbuf *timestamp, bool stripFileName,
+        struct selabel_handle* sehnd);
 
 /* rm -rf <path>
  */
diff --git a/minzip/Zip.c b/minzip/Zip.c
index 46d2f82..54d5d55 100644
--- a/minzip/Zip.c
+++ b/minzip/Zip.c
@@ -930,7 +930,8 @@
 bool mzExtractRecursive(const ZipArchive *pArchive,
                         const char *zipDir, const char *targetDir,
                         int flags, const struct utimbuf *timestamp,
-                        void (*callback)(const char *fn, void *), void *cookie)
+                        void (*callback)(const char *fn, void *), void *cookie,
+                        struct selabel_handle *sehnd)
 {
     if (zipDir[0] == '/') {
         LOGE("mzExtractRecursive(): zipDir must be a relative path.\n");
@@ -1045,7 +1046,7 @@
         if (pEntry->fileName[pEntry->fileNameLen-1] == '/') {
             if (!(flags & MZ_EXTRACT_FILES_ONLY)) {
                 int ret = dirCreateHierarchy(
-                        targetFile, UNZIP_DIRMODE, timestamp, false);
+                        targetFile, UNZIP_DIRMODE, timestamp, false, sehnd);
                 if (ret != 0) {
                     LOGE("Can't create containing directory for \"%s\": %s\n",
                             targetFile, strerror(errno));
@@ -1059,7 +1060,7 @@
              * the containing directory exists.
              */
             int ret = dirCreateHierarchy(
-                    targetFile, UNZIP_DIRMODE, timestamp, true);
+                    targetFile, UNZIP_DIRMODE, timestamp, true, sehnd);
             if (ret != 0) {
                 LOGE("Can't create containing directory for \"%s\": %s\n",
                         targetFile, strerror(errno));
@@ -1113,7 +1114,25 @@
                 /* The entry is a regular file.
                  * Open the target for writing.
                  */
+
+#ifdef HAVE_SELINUX
+                char *secontext = NULL;
+
+                if (sehnd) {
+                    selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE);
+                    setfscreatecon(secontext);
+                }
+#endif
+
                 int fd = creat(targetFile, UNZIP_FILEMODE);
+
+#ifdef HAVE_SELINUX
+                if (secontext) {
+                    freecon(secontext);
+                    setfscreatecon(NULL);
+                }
+#endif
+
                 if (fd < 0) {
                     LOGE("Can't create target file \"%s\": %s\n",
                             targetFile, strerror(errno));
diff --git a/minzip/Zip.h b/minzip/Zip.h
index 9f99fba..0b1c9d5 100644
--- a/minzip/Zip.h
+++ b/minzip/Zip.h
@@ -14,6 +14,13 @@
 #include "Hash.h"
 #include "SysUtil.h"
 
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#else
+struct selabel_handle;
+#endif
+
 /*
  * One entry in the Zip archive.  Treat this as opaque -- use accessors below.
  *
@@ -208,6 +215,7 @@
 bool mzExtractRecursive(const ZipArchive *pArchive,
         const char *zipDir, const char *targetDir,
         int flags, const struct utimbuf *timestamp,
-        void (*callback)(const char *fn, void*), void *cookie);
+        void (*callback)(const char *fn, void*), void *cookie,
+        struct selabel_handle *sehnd);
 
 #endif /*_MINZIP_ZIP*/
diff --git a/recovery.c b/recovery.c
index 06d6498..2cb482d 100644
--- a/recovery.c
+++ b/recovery.c
@@ -39,6 +39,8 @@
 #include "roots.h"
 #include "recovery_ui.h"
 
+struct selabel_handle *sehandle;
+
 static const struct option OPTIONS[] = {
   { "send_intent", required_argument, NULL, 's' },
   { "update_package", required_argument, NULL, 'u' },
@@ -132,7 +134,7 @@
 
     // When writing, try to create the containing directory, if necessary.
     // Use generous permissions, the system (init.rc) will reset them.
-    if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1);
+    if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1, sehandle);
 
     FILE *fp = fopen(path, mode);
     return fp;
@@ -763,6 +765,19 @@
         }
     }
 
+#ifdef HAVE_SELINUX
+    struct selinux_opt seopts[] = {
+      { SELABEL_OPT_PATH, "/file_contexts" }
+    };
+
+    sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+
+    if (!sehandle) {
+        fprintf(stderr, "Warning: No file_contexts\n");
+        ui_print("Warning:  No file_contexts\n");
+    }
+#endif
+
     device_recovery_start();
 
     printf("Command:");
diff --git a/roots.c b/roots.c
index cb7e067..f4a256f 100644
--- a/roots.c
+++ b/roots.c
@@ -31,6 +31,8 @@
 static int num_volumes = 0;
 static Volume* device_volumes = NULL;
 
+struct selabel_handle *sehandle;
+
 static int parse_options(char* options, Volume* volume) {
     char* option;
     while (option = strtok(options, ",")) {
@@ -269,7 +271,7 @@
     }
 
     if (strcmp(v->fs_type, "ext4") == 0) {
-        int result = make_ext4fs(v->device, v->length);
+        int result = make_ext4fs(v->device, v->length, volume, sehandle);
         if (result != 0) {
             LOGE("format_volume: make_extf4fs failed on %s\n", v->device);
             return -1;
diff --git a/updater/Android.mk b/updater/Android.mk
index 2c51f99..f8ccb76 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -22,12 +22,13 @@
 LOCAL_CFLAGS += -DUSE_EXT4
 LOCAL_C_INCLUDES += system/extras/ext4_utils
 LOCAL_STATIC_LIBRARIES += libext4_utils libz
+endif
+
 ifeq ($(HAVE_SELINUX), true)
 LOCAL_C_INCLUDES += external/libselinux/include
 LOCAL_STATIC_LIBRARIES += libselinux
 LOCAL_CFLAGS += -DHAVE_SELINUX
 endif # HAVE_SELINUX
-endif
 
 LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
 LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz
diff --git a/updater/install.c b/updater/install.c
index 7b4b99b..a59c4ed 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -79,8 +79,24 @@
         goto done;
     }
 
+#ifdef HAVE_SELINUX
+    char *secontext = NULL;
+
+    if (sehandle) {
+        selabel_lookup(sehandle, &secontext, mount_point, 0755);
+        setfscreatecon(secontext);
+    }
+#endif
+
     mkdir(mount_point, 0755);
 
+#ifdef HAVE_SELINUX
+    if (secontext) {
+        freecon(secontext);
+        setfscreatecon(NULL);
+    }
+#endif
+
     if (strcmp(partition_type, "MTD") == 0) {
         mtd_scan_partitions();
         const MtdPartition* mtd;
@@ -177,25 +193,34 @@
 }
 
 
-// format(fs_type, partition_type, location, fs_size)
+// format(fs_type, partition_type, location, fs_size, mount_point)
 //
-//    fs_type="yaffs2" partition_type="MTD"     location=partition fs_size=<bytes>
-//    fs_type="ext4"   partition_type="EMMC"    location=device    fs_size=<bytes>
+//    fs_type="yaffs2" partition_type="MTD"     location=partition fs_size=<bytes> mount_point=<location>
+//    fs_type="ext4"   partition_type="EMMC"    location=device    fs_size=<bytes> mount_point=<location>
 //    if fs_size == 0, then make_ext4fs uses the entire partition.
 //    if fs_size > 0, that is the size to use
 //    if fs_size < 0, then reserve that many bytes at the end of the partition
+//    mount_point is used with SELinux as the location of the mount point, absent otherwise
 Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
     char* result = NULL;
-    if (argc != 4) {
-        return ErrorAbort(state, "%s() expects 4 args, got %d", name, argc);
+    if (argc != 4 && argc != 5) {
+        return ErrorAbort(state, "%s() expects 4 or 5 args, got %d", name, argc);
     }
     char* fs_type;
     char* partition_type;
     char* location;
     char* fs_size;
+    char* mount_point = NULL;
+
+#ifdef HAVE_SELINUX
+    if (ReadArgs(state, argv, 5, &fs_type, &partition_type, &location, &fs_size, &mount_point) < 0) {
+        return NULL;
+    }
+#else
     if (ReadArgs(state, argv, 4, &fs_type, &partition_type, &location, &fs_size) < 0) {
         return NULL;
     }
+#endif
 
     if (strlen(fs_type) == 0) {
         ErrorAbort(state, "fs_type argument to %s() can't be empty", name);
@@ -211,6 +236,13 @@
         goto done;
     }
 
+#ifdef HAVE_SELINUX
+    if (!mount_point || strlen(mount_point) == 0) {
+        ErrorAbort(state, "mount_point argument to %s() can't be empty", name);
+        goto done;
+    }
+#endif
+
     if (strcmp(partition_type, "MTD") == 0) {
         mtd_scan_partitions();
         const MtdPartition* mtd = mtd_find_partition_by_name(location);
@@ -240,7 +272,7 @@
         result = location;
 #ifdef USE_EXT4
     } else if (strcmp(fs_type, "ext4") == 0) {
-        int status = make_ext4fs(location, atoll(fs_size));
+        int status = make_ext4fs(location, atoll(fs_size), mount_point, sehandle);
         if (status != 0) {
             fprintf(stderr, "%s: make_ext4fs failed (%d) on %s",
                     name, status, location);
@@ -347,7 +379,7 @@
 
     bool success = mzExtractRecursive(za, zip_path, dest_path,
                                       MZ_EXTRACT_FILES_ONLY, &timestamp,
-                                      NULL, NULL);
+                                      NULL, NULL, sehandle);
     free(zip_path);
     free(dest_path);
     return StringValue(strdup(success ? "t" : ""));
diff --git a/updater/updater.c b/updater/updater.c
index aa626d2..5f15808 100644
--- a/updater/updater.c
+++ b/updater/updater.c
@@ -32,6 +32,8 @@
 // (Note it's "updateR-script", not the older "update-script".)
 #define SCRIPT_NAME "META-INF/com/google/android/updater-script"
 
+struct selabel_handle *sehandle;
+
 int main(int argc, char** argv) {
     // Various things log information to stdout or stderr more or less
     // at random.  The log file makes more sense if buffering is
@@ -103,6 +105,19 @@
         return 6;
     }
 
+#ifdef HAVE_SELINUX
+    struct selinux_opt seopts[] = {
+      { SELABEL_OPT_PATH, "/file_contexts" }
+    };
+
+    sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+
+    if (!sehandle) {
+        fprintf(stderr, "Warning:  No file_contexts\n");
+        fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n");
+    }
+#endif
+
     // Evaluate the parsed script.
 
     UpdaterInfo updater_info;
diff --git a/updater/updater.h b/updater/updater.h
index bd60dc1..a00872c 100644
--- a/updater/updater.h
+++ b/updater/updater.h
@@ -20,10 +20,19 @@
 #include <stdio.h>
 #include "minzip/Zip.h"
 
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#else
+struct selabel_handle;
+#endif
+
 typedef struct {
     FILE* cmd_pipe;
     ZipArchive* package_zip;
     int version;
 } UpdaterInfo;
 
+extern struct selabel_handle *sehandle;
+
 #endif