forked from openkylin/platform_build
am 46f00b61: am b5d4c0df: Merge "Update OTA to understand SELinux labels and capabilities" into klp-dev
* commit '46f00b6167f4c23dd4874abe9377bac85e965b2f': Update OTA to understand SELinux labels and capabilities
This commit is contained in:
commit
69fecd43c1
|
@ -1173,6 +1173,7 @@ $(BUILT_TARGET_FILES_PACKAGE): \
|
|||
$(INSTALLED_CACHEIMAGE_TARGET) \
|
||||
$(INSTALLED_VENDORIMAGE_TARGET) \
|
||||
$(INSTALLED_ANDROID_INFO_TXT_TARGET) \
|
||||
$(SELINUX_FC) \
|
||||
$(built_ota_tools) \
|
||||
$(APKCERTS_FILE) \
|
||||
$(HOST_OUT_EXECUTABLES)/fs_config \
|
||||
|
@ -1260,13 +1261,14 @@ ifdef PRODUCT_EXTRA_RECOVERY_KEYS
|
|||
$(hide) echo "extra_recovery_keys=$(PRODUCT_EXTRA_RECOVERY_KEYS)" >> $(zip_root)/META/misc_info.txt
|
||||
endif
|
||||
$(hide) echo "mkbootimg_args=$(BOARD_MKBOOTIMG_ARGS)" >> $(zip_root)/META/misc_info.txt
|
||||
$(hide) echo "use_set_metadata=1" >> $(zip_root)/META/misc_info.txt
|
||||
$(call generate-userimage-prop-dictionary, $(zip_root)/META/misc_info.txt)
|
||||
@# Zip everything up, preserving symlinks
|
||||
$(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .)
|
||||
@# Run fs_config on all the system, boot ramdisk, and recovery ramdisk files in the zip, and save the output
|
||||
$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="SYSTEM/" } /^SYSTEM\// {print "system/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config > $(zip_root)/META/filesystem_config.txt
|
||||
$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="BOOT/RAMDISK/" } /^BOOT\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config > $(zip_root)/META/boot_filesystem_config.txt
|
||||
$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="RECOVERY/RAMDISK/" } /^RECOVERY\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config > $(zip_root)/META/recovery_filesystem_config.txt
|
||||
$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="SYSTEM/" } /^SYSTEM\// {print "system/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/filesystem_config.txt
|
||||
$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="BOOT/RAMDISK/" } /^BOOT\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/boot_filesystem_config.txt
|
||||
$(hide) zipinfo -1 $@ | awk 'BEGIN { FS="RECOVERY/RAMDISK/" } /^RECOVERY\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -S $(SELINUX_FC) > $(zip_root)/META/recovery_filesystem_config.txt
|
||||
$(hide) (cd $(zip_root) && zip -q ../$(notdir $@) META/*filesystem_config.txt)
|
||||
|
||||
.PHONY: target-files-package
|
||||
|
|
|
@ -17,6 +17,7 @@ include $(CLEAR_VARS)
|
|||
|
||||
LOCAL_SRC_FILES := fs_config.c
|
||||
LOCAL_MODULE := fs_config
|
||||
LOCAL_STATIC_LIBRARIES := libselinux
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
|
|
@ -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 @@ int main(int argc, char** argv) {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -217,14 +217,33 @@ class EdifyGenerator(object):
|
|||
else:
|
||||
raise ValueError("don't know how to write \"%s\" partitions" % (p.fs_type,))
|
||||
|
||||
def SetPermissions(self, fn, uid, gid, mode):
|
||||
def SetPermissions(self, fn, uid, gid, mode, selabel, capabilities):
|
||||
"""Set file ownership and permissions."""
|
||||
self.script.append('set_perm(%d, %d, 0%o, "%s");' % (uid, gid, mode, fn))
|
||||
if not self.info.get("use_set_metadata", False):
|
||||
self.script.append('set_perm(%d, %d, 0%o, "%s");' % (uid, gid, mode, fn))
|
||||
else:
|
||||
if capabilities is None: capabilities = "0x0"
|
||||
cmd = 'set_metadata("%s", "uid", %d, "gid", %d, "mode", 0%o, ' \
|
||||
'"capabilities", %s' % (fn, uid, gid, mode, capabilities)
|
||||
if selabel is not None:
|
||||
cmd += ', "selabel", "%s"' % ( selabel )
|
||||
cmd += ');'
|
||||
self.script.append(cmd)
|
||||
|
||||
def SetPermissionsRecursive(self, fn, uid, gid, dmode, fmode):
|
||||
def SetPermissionsRecursive(self, fn, uid, gid, dmode, fmode, selabel, capabilities):
|
||||
"""Recursively set path ownership and permissions."""
|
||||
self.script.append('set_perm_recursive(%d, %d, 0%o, 0%o, "%s");'
|
||||
% (uid, gid, dmode, fmode, fn))
|
||||
if not self.info.get("use_set_metadata", False):
|
||||
self.script.append('set_perm_recursive(%d, %d, 0%o, 0%o, "%s");'
|
||||
% (uid, gid, dmode, fmode, fn))
|
||||
else:
|
||||
if capabilities is None: capabilities = "0x0"
|
||||
cmd = 'set_metadata_recursive("%s", "uid", %d, "gid", %d, ' \
|
||||
'"dmode", 0%o, "fmode", 0%o, "capabilities", %s' \
|
||||
% (fn, uid, gid, dmode, fmode, capabilities)
|
||||
if selabel is not None:
|
||||
cmd += ', "selabel", "%s"' % ( selabel )
|
||||
cmd += ');'
|
||||
self.script.append(cmd)
|
||||
|
||||
def MakeSymlinks(self, symlink_list):
|
||||
"""Create symlinks, given a list of (dest, link) pairs."""
|
||||
|
|
|
@ -117,6 +117,8 @@ class Item:
|
|||
self.uid = None
|
||||
self.gid = None
|
||||
self.mode = None
|
||||
self.selabel = None
|
||||
self.capabilities = None
|
||||
self.dir = dir
|
||||
|
||||
if name:
|
||||
|
@ -147,82 +149,88 @@ class Item:
|
|||
@classmethod
|
||||
def GetMetadata(cls, input_zip):
|
||||
|
||||
try:
|
||||
# See if the target_files contains a record of what the uid,
|
||||
# gid, and mode is supposed to be.
|
||||
output = input_zip.read("META/filesystem_config.txt")
|
||||
except KeyError:
|
||||
# Run the external 'fs_config' program to determine the desired
|
||||
# uid, gid, and mode for every Item object. Note this uses the
|
||||
# one in the client now, which might not be the same as the one
|
||||
# used when this target_files was built.
|
||||
p = common.Run(["fs_config"], stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
suffix = { False: "", True: "/" }
|
||||
input = "".join(["%s%s\n" % (i.name, suffix[i.dir])
|
||||
for i in cls.ITEMS.itervalues() if i.name])
|
||||
output, error = p.communicate(input)
|
||||
assert not error
|
||||
# The target_files contains a record of what the uid,
|
||||
# gid, and mode are supposed to be.
|
||||
output = input_zip.read("META/filesystem_config.txt")
|
||||
|
||||
for line in output.split("\n"):
|
||||
if not line: continue
|
||||
name, uid, gid, mode = line.split()
|
||||
columns = line.split()
|
||||
name, uid, gid, mode = columns[:4]
|
||||
selabel = None
|
||||
capabilities = None
|
||||
|
||||
# After the first 4 columns, there are a series of key=value
|
||||
# pairs. Extract out the fields we care about.
|
||||
for element in columns[4:]:
|
||||
key, value = element.split("=")
|
||||
if key == "selabel":
|
||||
selabel = value
|
||||
if key == "capabilities":
|
||||
capabilities = value
|
||||
|
||||
i = cls.ITEMS.get(name, None)
|
||||
if i is not None:
|
||||
i.uid = int(uid)
|
||||
i.gid = int(gid)
|
||||
i.mode = int(mode, 8)
|
||||
i.selabel = selabel
|
||||
i.capabilities = capabilities
|
||||
if i.dir:
|
||||
i.children.sort(key=lambda i: i.name)
|
||||
|
||||
# set metadata for the files generated by this script.
|
||||
i = cls.ITEMS.get("system/recovery-from-boot.p", None)
|
||||
if i: i.uid, i.gid, i.mode = 0, 0, 0644
|
||||
if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
|
||||
i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
|
||||
if i: i.uid, i.gid, i.mode = 0, 0, 0544
|
||||
if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
|
||||
|
||||
def CountChildMetadata(self):
|
||||
"""Count up the (uid, gid, mode) tuples for all children and
|
||||
determine the best strategy for using set_perm_recursive and
|
||||
"""Count up the (uid, gid, mode, selabel, capabilities) tuples for
|
||||
all children and determine the best strategy for using set_perm_recursive and
|
||||
set_perm to correctly chown/chmod all the files to their desired
|
||||
values. Recursively calls itself for all descendants.
|
||||
|
||||
Returns a dict of {(uid, gid, dmode, fmode): count} counting up
|
||||
Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
|
||||
all descendants of this node. (dmode or fmode may be None.) Also
|
||||
sets the best_subtree of each directory Item to the (uid, gid,
|
||||
dmode, fmode) tuple that will match the most descendants of that
|
||||
Item.
|
||||
dmode, fmode, selabel, capabilities) tuple that will match the most
|
||||
descendants of that Item.
|
||||
"""
|
||||
|
||||
assert self.dir
|
||||
d = self.descendants = {(self.uid, self.gid, self.mode, None): 1}
|
||||
d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
|
||||
for i in self.children:
|
||||
if i.dir:
|
||||
for k, v in i.CountChildMetadata().iteritems():
|
||||
d[k] = d.get(k, 0) + v
|
||||
else:
|
||||
k = (i.uid, i.gid, None, i.mode)
|
||||
k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
|
||||
d[k] = d.get(k, 0) + 1
|
||||
|
||||
# Find the (uid, gid, dmode, fmode) tuple that matches the most
|
||||
# descendants.
|
||||
# Find the (uid, gid, dmode, fmode, selabel, capabilities)
|
||||
# tuple that matches the most descendants.
|
||||
|
||||
# First, find the (uid, gid) pair that matches the most
|
||||
# descendants.
|
||||
ug = {}
|
||||
for (uid, gid, _, _), count in d.iteritems():
|
||||
for (uid, gid, _, _, _, _), count in d.iteritems():
|
||||
ug[(uid, gid)] = ug.get((uid, gid), 0) + count
|
||||
ug = MostPopularKey(ug, (0, 0))
|
||||
|
||||
# Now find the dmode and fmode that match the most descendants
|
||||
# with that (uid, gid), and choose those.
|
||||
# Now find the dmode, fmode, selabel, and capabilities that match
|
||||
# the most descendants with that (uid, gid), and choose those.
|
||||
best_dmode = (0, 0755)
|
||||
best_fmode = (0, 0644)
|
||||
best_selabel = (0, None)
|
||||
best_capabilities = (0, None)
|
||||
for k, count in d.iteritems():
|
||||
if k[:2] != ug: continue
|
||||
if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
|
||||
if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
|
||||
self.best_subtree = ug + (best_dmode[1], best_fmode[1])
|
||||
if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
|
||||
if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
|
||||
self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
|
||||
|
||||
return d
|
||||
|
||||
|
@ -234,7 +242,7 @@ class Item:
|
|||
self.CountChildMetadata()
|
||||
|
||||
def recurse(item, current):
|
||||
# current is the (uid, gid, dmode, fmode) tuple that the current
|
||||
# current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
|
||||
# item (and all its children) have already been set to. We only
|
||||
# need to issue set_perm/set_perm_recursive commands if we're
|
||||
# supposed to be something different.
|
||||
|
@ -244,17 +252,21 @@ class Item:
|
|||
current = item.best_subtree
|
||||
|
||||
if item.uid != current[0] or item.gid != current[1] or \
|
||||
item.mode != current[2]:
|
||||
script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
|
||||
item.mode != current[2] or item.selabel != current[4] or \
|
||||
item.capabilities != current[5]:
|
||||
script.SetPermissions("/"+item.name, item.uid, item.gid,
|
||||
item.mode, item.selabel, item.capabilities)
|
||||
|
||||
for i in item.children:
|
||||
recurse(i, current)
|
||||
else:
|
||||
if item.uid != current[0] or item.gid != current[1] or \
|
||||
item.mode != current[3]:
|
||||
script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
|
||||
item.mode != current[3] or item.selabel != current[4] or \
|
||||
item.capabilities != current[5]:
|
||||
script.SetPermissions("/"+item.name, item.uid, item.gid,
|
||||
item.mode, item.selabel, item.capabilities)
|
||||
|
||||
recurse(self, (-1, -1, -1, -1))
|
||||
recurse(self, (-1, -1, -1, -1, None, None))
|
||||
|
||||
|
||||
def CopySystemFiles(input_zip, output_zip=None,
|
||||
|
@ -733,7 +745,7 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
|||
for item in deferred_patch_list:
|
||||
fn, tf, sf, size, _ = item
|
||||
script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
|
||||
script.SetPermissions("/system/build.prop", 0, 0, 0644)
|
||||
script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
|
||||
|
||||
script.AddToZip(target_zip, output_zip)
|
||||
WriteMetadata(metadata, output_zip)
|
||||
|
|
Loading…
Reference in New Issue