Add file_contexts property to filesystem

Filesystems like ext4 can store file contexts itself. This supports
passing file_contexts file to build_image.

Bug: 178993690
Test: boot and see selinux denials are gone
Change-Id: I97d4a981e4b9c89434ea2f1303173ae91cce94e3
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 5092ad0..177296c 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -51,6 +51,9 @@
 	// Type of the filesystem. Currently, ext4 and compressed_cpio are supported. Default is
 	// ext4.
 	Type *string
+
+	// file_contexts file to make image. Currently, only ext4 is supported.
+	File_contexts *string `android:"path"`
 }
 
 // android_filesystem packages a set of modules and their transitive dependencies into a filesystem
@@ -142,6 +145,16 @@
 	return output
 }
 
+func (f *filesystem) buildFileContexts(ctx android.ModuleContext) android.OutputPath {
+	builder := android.NewRuleBuilder(pctx, ctx)
+	fcBin := android.PathForModuleOut(ctx, "file_contexts.bin")
+	builder.Command().BuiltTool("sefcontext_compile").
+		FlagWithOutput("-o ", fcBin).
+		Input(android.PathForModuleSrc(ctx, proptools.String(f.properties.File_contexts)))
+	builder.Build("build_filesystem_file_contexts", fmt.Sprintf("Creating filesystem file contexts for %s", f.BaseModuleName()))
+	return fcBin.OutputPath
+}
+
 func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) {
 	type prop struct {
 		name  string
@@ -188,6 +201,10 @@
 		addStr("partition_name", f.Name())
 	}
 
+	if proptools.String(f.properties.File_contexts) != "" {
+		addPath("selinux_fc", f.buildFileContexts(ctx))
+	}
+
 	propFile = android.PathForModuleOut(ctx, "prop").OutputPath
 	builder := android.NewRuleBuilder(pctx, ctx)
 	builder.Command().Text("rm").Flag("-rf").Output(propFile)
@@ -207,6 +224,10 @@
 			"Consider adding this to bootimg module and signing the entire boot image.")
 	}
 
+	if proptools.String(f.properties.File_contexts) != "" {
+		ctx.PropertyErrorf("file_contexts", "file_contexts is not supported for compressed cpio image.")
+	}
+
 	zipFile := android.PathForModuleOut(ctx, "temp.zip").OutputPath
 	f.CopyDepsToZip(ctx, zipFile)