Merge "Extend mkyaffs2image to support setting SELinux security contexts in yaffs2 images."
diff --git a/Android.mk b/Android.mk
index e31b6ae..e899a77 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,6 +17,12 @@
 
 LOCAL_MODULE := mkyaffs2image
 
+ifeq ($(HAVE_SELINUX), true)
+LOCAL_C_INCLUDES += external/libselinux/include
+LOCAL_STATIC_LIBRARIES += libselinux
+LOCAL_CFLAGS += -DHAVE_SELINUX
+endif # HAVE_SELINUX
+
 include $(BUILD_HOST_EXECUTABLE)
 
 $(call dist-for-goals, dist_files, $(LOCAL_BUILT_MODULE))
diff --git a/yaffs2/utils/mkyaffs2image.c b/yaffs2/utils/mkyaffs2image.c
index 7b989f6..2b152e6 100644
--- a/yaffs2/utils/mkyaffs2image.c
+++ b/yaffs2/utils/mkyaffs2image.c
@@ -32,6 +32,16 @@
 #include <string.h>
 #include <unistd.h>
 
+#ifdef HAVE_SELINUX
+#define XATTR_NAME_SELINUX "security.selinux"
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+static struct selabel_handle *sehnd;
+static unsigned int seprefixlen;
+static char *mntpoint;
+#endif
+
 #include <private/android_filesystem_config.h>
 
 #include "yaffs_ecc.h"
@@ -247,12 +257,23 @@
 	
 }
 
-static int write_object_header(int objId, yaffs_ObjectType t, struct stat *s, int parent, const char *name, int equivalentObj, const char * alias)
+static int write_object_header(int objId, yaffs_ObjectType t, struct stat *s, int parent, const char *name, int equivalentObj, const char * alias, const char *secontext)
 {
 	__u8 bytes[chunkSize];
 	
 	
 	yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bytes;
+#ifdef HAVE_SELINUX
+	char *xb = (char *)bytes + sizeof(*oh);
+	int xnamelen = strlen(XATTR_NAME_SELINUX) + 1;
+	int xvalsize = 0;
+	int xreclen = 0;
+
+	if (secontext) {
+		xvalsize = strlen(secontext) + 1;
+		xreclen = sizeof(int) + xnamelen + xvalsize;
+	}
+#endif
 	
 	memset(bytes,0xff,sizeof(bytes));
 	
@@ -261,7 +282,16 @@
 	oh->parentObjectId = parent;
 	
 	strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH);
-	
+
+#ifdef HAVE_SELINUX
+	if (xreclen) {
+		memcpy(xb, &xreclen, sizeof(int));
+		xb += sizeof(int);
+		strcpy(xb, XATTR_NAME_SELINUX);
+		xb += xnamelen;
+		memcpy(xb, secontext, xvalsize);
+	}
+#endif
 	
 	if(t != YAFFS_OBJECT_TYPE_HARDLINK)
 	{
@@ -310,6 +340,7 @@
 
 	DIR *dir;
 	struct dirent *entry;
+	char *secontext = NULL;
 
 	nDirectories++;
 	
@@ -325,6 +356,10 @@
 			   strcmp(entry->d_name,".."))
  			{
  				char full_name[500];
+#ifdef HAVE_SELINUX
+				char *suffix, dest_name[500];
+				int ret;
+#endif
 				struct stat stats;
 				int equivalentObj;
 				int newObj;
@@ -332,7 +367,30 @@
 				sprintf(full_name,"%s/%s",path,entry->d_name);
 				
 				lstat(full_name,&stats);
-				
+
+#ifdef HAVE_SELINUX
+				if (sehnd) {
+					suffix = full_name + seprefixlen;
+					ret = snprintf(dest_name,
+						       sizeof dest_name,
+						       "%s%s", mntpoint,
+						       suffix);
+					if (ret < 0 ||
+					    (size_t) ret >= sizeof dest_name) {
+						fprintf(stderr,
+							"snprintf failed on %s%s\n",
+							mntpoint, suffix);
+						exit(1);
+					}
+					if (selabel_lookup(sehnd, &secontext,
+							   dest_name,
+							   stats.st_mode) < 0) {
+						perror("selabel_lookup");
+						exit(1);
+					}
+				}
+#endif
+
 				if(S_ISLNK(stats.st_mode) ||
 				    S_ISREG(stats.st_mode) ||
 				    S_ISDIR(stats.st_mode) ||
@@ -356,7 +414,7 @@
 					{
 					 	/* we need to make a hard link */
 					 	//printf("hard link to object %d\n",equivalentObj);
-						error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_HARDLINK, &stats, parent, entry->d_name, equivalentObj, NULL);
+						error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_HARDLINK, &stats, parent, entry->d_name, equivalentObj, NULL, secontext);
 					}
 					else 
 					{
@@ -373,13 +431,13 @@
 							readlink(full_name,symname,sizeof(symname) -1);
 						
 							//printf("symlink to \"%s\"\n",symname);
-							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SYMLINK, &stats, parent, entry->d_name, -1, symname);
+							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SYMLINK, &stats, parent, entry->d_name, -1, symname, secontext);
 
 						}
 						else if(S_ISREG(stats.st_mode))
 						{
 							//printf("file, ");
-							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_FILE, &stats, parent, entry->d_name, -1, NULL);
+							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_FILE, &stats, parent, entry->d_name, -1, NULL, secontext);
 
 							if(error >= 0)
 							{
@@ -415,27 +473,27 @@
 						else if(S_ISSOCK(stats.st_mode))
 						{
 							//printf("socket\n");
-							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL);
+							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext);
 						}
 						else if(S_ISFIFO(stats.st_mode))
 						{
 							//printf("fifo\n");
-							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL);
+							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext);
 						}
 						else if(S_ISCHR(stats.st_mode))
 						{
 							//printf("character device\n");
-							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL);
+							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext);
 						}
 						else if(S_ISBLK(stats.st_mode))
 						{
 							//printf("block device\n");
-							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL);
+							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext);
 						}
 						else if(S_ISDIR(stats.st_mode))
 						{
 							//printf("directory\n");
-							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, parent, entry->d_name, -1, NULL);
+							error =  write_object_header(newObj, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, parent, entry->d_name, -1, NULL, secontext);
 // NCB modified 10/9/2001				process_directory(1,full_name);
 							process_directory(newObj,full_name,fixstats);
 						}
@@ -447,7 +505,6 @@
 				}
 			}
 		}
-
 		closedir(dir);
 	}
 	
@@ -458,12 +515,14 @@
 static void usage(void)
 {
 	fprintf(stderr,"mkyaffs2image: image building tool for YAFFS2 built "__DATE__"\n");
-	fprintf(stderr,"usage: mkyaffs2image [-f] [-c <size>] [-s <size>] dir image_file [convert]\n");
+	fprintf(stderr,"usage: mkyaffs2image [-f] [-c <size>] [-s <size>] dir image_file [file_contexts mountpoint] [convert]\n");
 	fprintf(stderr,"           -f         fix file stat (mods, user, group) for device\n");
 	fprintf(stderr,"           -c <size>  set the chunk (NAND page) size. default: 2048\n");
 	fprintf(stderr,"           -s <size>  set the spare (NAND OOB) size. default: 64\n");
 	fprintf(stderr,"           dir        the directory tree to be converted\n");
 	fprintf(stderr,"           image_file the output file to hold the image\n");
+	fprintf(stderr,"           file_contexts the file contexts configuration used to assign SELinux file context attributes\n");
+	fprintf(stderr,"           mountpoint the directory where this image be mounted on the device\n");
 	fprintf(stderr,"           'convert'  produce a big-endian image from a little-endian machine\n");
 }
 
@@ -474,6 +533,7 @@
 	int opt;
 	char *image;
 	char *dir;
+	char *secontext = NULL;
 
 	while ((opt = getopt(argc, argv, "fc:s:")) != -1) {
 		switch (opt) {
@@ -497,20 +557,44 @@
 		exit(1);
 	}
 
-	if ((argc - optind < 2) || (argc - optind > 3)) {
+	if ((argc - optind < 2) || (argc - optind > 4)) {
 		usage();
 		exit(1);
 	}
 
 	dir = argv[optind];
+#ifdef HAVE_SELINUX
+	seprefixlen = strlen(dir);
+#endif
 	image = argv[optind + 1];
 
 	if (optind + 2 < argc) {
 		if (!strncmp(argv[optind + 2], "convert", strlen("convert")))
 			convert_endian = 1;
 		else {
+#ifdef HAVE_SELINUX
+			struct selinux_opt seopts[] = {
+				{ SELABEL_OPT_PATH, argv[optind + 2] }
+			};
+			sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+			if (!sehnd) {
+				perror(argv[optind + 2]);
+				usage();
+				exit(1);
+			}
+			if (optind + 3 >= argc) {
+				usage();
+				exit(1);
+			}
+			mntpoint = argv[optind + 3];
+			if (optind + 4 < argc) {
+				if (!strncmp(argv[optind + 4], "convert", strlen("convert")))
+					convert_endian = 1;
+			}
+#else
 			usage();
 			exit(1);
+#endif
 		}
 	}
 
@@ -550,7 +634,16 @@
     }
     
 	//printf("Processing directory %s into image file %s\n",dir,image);
-	error =  write_object_header(1, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, 1,"", -1, NULL);
+#ifdef HAVE_SELINUX
+    if (sehnd) {
+	    if (selabel_lookup(sehnd, &secontext, mntpoint, stats.st_mode) < 0) {
+		    perror("selabel_lookup");
+		    exit(1);
+	    }
+    }
+#endif
+
+    error =  write_object_header(1, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, 1,"", -1, NULL, secontext);
 	if(error)
 	error = process_directory(YAFFS_OBJECTID_ROOT,dir,fixstats);