1// Copyright (C) 2018 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package selinux
16
17// This file contains "se_cil_compat_map" module type used to build and install
18// sepolicy backwards compatibility mapping files.
19
20import (
21	"android/soong/android"
22	"fmt"
23	"io"
24
25	"github.com/google/blueprint"
26	"github.com/google/blueprint/proptools"
27)
28
29var (
30	combine_maps    = pctx.HostBinToolVariable("combine_maps", "combine_maps")
31	combineMapsCmd  = "${combine_maps} -t ${topHalf} -b ${bottomHalf} -o $out"
32	combineMapsRule = pctx.StaticRule(
33		"combineMapsRule",
34		blueprint.RuleParams{
35			Command:     combineMapsCmd,
36			CommandDeps: []string{"${combine_maps}"},
37		},
38		"topHalf",
39		"bottomHalf",
40	)
41
42	String        = proptools.String
43	TopHalfDepTag = dependencyTag{name: "top"}
44)
45
46func init() {
47	android.RegisterModuleType("se_cil_compat_map", cilCompatMapFactory)
48	pctx.Import("android/soong/android")
49}
50
51func cilCompatMapFactory() android.Module {
52	c := &cilCompatMap{}
53	c.AddProperties(&c.properties)
54	android.InitAndroidArchModule(c, android.DeviceSupported, android.MultilibCommon)
55	return c
56}
57
58type cilCompatMapProperties struct {
59	// se_cil_compat_map module representing a compatibility mapping file for
60	// platform versions (x->y). Bottom half represents a mapping (y->z).
61	// Together the halves are used to generate a (x->z) mapping.
62	Top_half *string
63	// list of source (.cil) files used to build an the bottom half of sepolicy
64	// compatibility mapping file. bottom_half may reference the outputs of
65	// other modules that produce source files like genrule or filegroup using
66	// the syntax ":module". srcs has to be non-empty.
67	Bottom_half []string
68	// name of the output
69	Stem *string
70}
71
72type cilCompatMap struct {
73	android.ModuleBase
74	properties cilCompatMapProperties
75	// (.intermediate) module output path as installation source.
76	installSource android.Path
77	installPath   android.InstallPath
78}
79
80type CilCompatMapGenerator interface {
81	GeneratedMapFile() android.Path
82}
83
84func expandTopHalf(ctx android.ModuleContext) android.OptionalPath {
85	var topHalf android.OptionalPath
86	ctx.VisitDirectDeps(func(dep android.Module) {
87		depTag := ctx.OtherModuleDependencyTag(dep)
88		switch depTag {
89		case TopHalfDepTag:
90			topHalf = android.OptionalPathForPath(dep.(CilCompatMapGenerator).GeneratedMapFile())
91		}
92	})
93	return topHalf
94}
95
96func expandSeSources(ctx android.ModuleContext, srcFiles []string) android.Paths {
97	expandedSrcFiles := make(android.Paths, 0, len(srcFiles))
98	for _, s := range srcFiles {
99		if m := android.SrcIsModule(s); m != "" {
100			module := ctx.GetDirectDepWithTag(m, android.SourceDepTag)
101			if module == nil {
102				// Error will have been handled by ExtractSourcesDeps
103				continue
104			}
105			if fg, ok := module.(*fileGroup); ok {
106				if ctx.ProductSpecific() {
107					expandedSrcFiles = append(expandedSrcFiles, fg.ProductPrivateSrcs()...)
108				} else if ctx.SystemExtSpecific() {
109					expandedSrcFiles = append(expandedSrcFiles, fg.SystemExtPrivateSrcs()...)
110				} else {
111					expandedSrcFiles = append(expandedSrcFiles, fg.SystemPrivateSrcs()...)
112				}
113			} else {
114				ctx.ModuleErrorf("srcs dependency %q is not an selinux filegroup", m)
115			}
116		} else {
117			p := android.PathForModuleSrc(ctx, s)
118			expandedSrcFiles = append(expandedSrcFiles, p)
119		}
120	}
121	return expandedSrcFiles
122}
123
124func (c *cilCompatMap) GenerateAndroidBuildActions(ctx android.ModuleContext) {
125	c.installPath = android.PathForModuleInstall(ctx, "etc", "selinux", "mapping")
126
127	srcFiles := expandSeSources(ctx, c.properties.Bottom_half)
128
129	for _, src := range srcFiles {
130		if src.Ext() != ".cil" {
131			ctx.PropertyErrorf("bottom_half", "%s has to be a .cil file.", src.String())
132		}
133	}
134
135	bottomHalf := android.PathForModuleGen(ctx, "bottom_half")
136	ctx.Build(pctx, android.BuildParams{
137		Rule:   android.Cat,
138		Output: bottomHalf,
139		Inputs: srcFiles,
140	})
141
142	topHalf := expandTopHalf(ctx)
143	if topHalf.Valid() {
144		out := android.PathForModuleGen(ctx, c.Name())
145		ctx.ModuleBuild(pctx, android.ModuleBuildParams{
146			Rule:   combineMapsRule,
147			Output: out,
148			Implicits: []android.Path{
149				topHalf.Path(),
150				bottomHalf,
151			},
152			Args: map[string]string{
153				"topHalf":    topHalf.String(),
154				"bottomHalf": bottomHalf.String(),
155			},
156		})
157		c.installSource = out
158	} else {
159		c.installSource = bottomHalf
160	}
161}
162
163func (c *cilCompatMap) DepsMutator(ctx android.BottomUpMutatorContext) {
164	android.ExtractSourcesDeps(ctx, c.properties.Bottom_half)
165	if c.properties.Top_half != nil {
166		ctx.AddDependency(c, TopHalfDepTag, String(c.properties.Top_half))
167	}
168}
169
170func (c *cilCompatMap) AndroidMk() android.AndroidMkData {
171	ret := android.AndroidMkData{
172		OutputFile: android.OptionalPathForPath(c.installSource),
173		Class:      "ETC",
174	}
175	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
176		fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", c.installPath.ToMakePath().String())
177		if c.properties.Stem != nil {
178			fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", String(c.properties.Stem))
179		}
180	})
181	return ret
182}
183
184var _ CilCompatMapGenerator = (*cilCompatMap)(nil)
185
186func (c *cilCompatMap) GeneratedMapFile() android.Path {
187	return c.installSource
188}
189