| #!/usr/bin/env python |
| |
| import ConfigParser |
| import re |
| import sys |
| |
| |
| GENERATED = ''' |
| /* |
| * THIS IS AN AUTOGENERATED FILE! DO NOT MODIFY |
| */ |
| ''' |
| |
| INCLUDE = '#include <private/android_filesystem_config.h>' |
| |
| DEFINE_NO_DIRS = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS\n' |
| DEFINE_NO_FILES = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES\n' |
| |
| DEFAULT_WARNING = '#warning No device-supplied android_filesystem_config.h, using empty default.' |
| |
| NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY = '{ 00000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_dirs" },' |
| NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES_ENTRY = '{ 00000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_files" },' |
| |
| IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS = '#ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS' |
| ENDIF = '#endif' |
| |
| OPEN_FILE_STRUCT = 'static const struct fs_path_config android_device_files[] = {' |
| OPEN_DIR_STRUCT = 'static const struct fs_path_config android_device_dirs[] = {' |
| CLOSE_FILE_STRUCT = '};' |
| |
| GENERIC_DEFINE = "#define %s\t%s" |
| |
| FILE_COMMENT = '// Defined in file: \"%s\"' |
| |
| # from system/core/include/private/android_filesystem_config.h |
| AID_OEM_RESERVED_RANGES = [ |
| (2900, 2999), |
| (5000, 5999), |
| ] |
| |
| |
| AID_MATCH = re.compile('AID_[a-zA-Z]+') |
| |
| def handle_aid(file_name, section_name, config, aids, seen_aids): |
| value = config.get(section_name, 'value') |
| |
| errmsg = '%s for: \"' + section_name + '" file: \"' + file_name + '\"' |
| |
| if not value: |
| raise Exception(errmsg % 'Found specified but unset "value"') |
| |
| v = convert_int(value) |
| if not v: |
| raise Exception(errmsg % ('Invalid "value", not a number, got: \"%s\"' % value)) |
| |
| # Values must be within OEM range |
| if not any(lower <= v <= upper for (lower, upper) in AID_OEM_RESERVED_RANGES): |
| s = '"value" not in valid range %s, got: %s' |
| s = s % (str(AID_OEM_RESERVED_RANGES), value) |
| raise Exception(errmsg % s) |
| |
| # use the normalized int value in the dict and detect |
| # duplicate definitions of the same vallue |
| v = str(v) |
| if v in seen_aids[1]: |
| # map of value to aid name |
| a = seen_aids[1][v] |
| |
| # aid name to file |
| f = seen_aids[0][a] |
| |
| s = 'Duplicate AID value "%s" found on AID: "%s".' % (value, seen_aids[1][v]) |
| s += ' Previous found in file: "%s."' % f |
| raise Exception(errmsg % s) |
| |
| seen_aids[1][v] = section_name |
| |
| # Append a tuple of (AID_*, base10(value), str(value)) |
| # We keep the str version of value so we can print that out in the |
| # generated header so investigating parties can identify parts. |
| # We store the base10 value for sorting, so everything is ascending |
| # later. |
| aids.append((file_name, section_name, v, value)) |
| |
| def convert_int(num): |
| |
| try: |
| if num.startswith('0x'): |
| return int(num, 16) |
| elif num.startswith('0b'): |
| return int(num, 2) |
| elif num.startswith('0'): |
| return int(num, 8) |
| else: |
| return int(num, 10) |
| except ValueError: |
| pass |
| return None |
| |
| def handle_path(file_name, section_name, config, files, dirs): |
| |
| mode = config.get(section_name, 'mode') |
| user = config.get(section_name, 'user') |
| group = config.get(section_name, 'group') |
| caps = config.get(section_name, 'caps') |
| |
| errmsg = 'Found specified but unset option: \"%s" in file: \"' + file_name + '\"' |
| |
| if not mode: |
| raise Exception(errmsg % 'mode') |
| |
| if not user: |
| raise Exception(errmsg % 'user') |
| |
| if not group: |
| raise Exception(errmsg % 'group') |
| |
| if not caps: |
| raise Exception(errmsg % 'caps') |
| |
| caps = caps.split() |
| |
| tmp = [] |
| for x in caps: |
| if convert_int(x): |
| tmp.append('(' + x + ')') |
| else: |
| tmp.append('(1ULL << CAP_' + x.upper() + ')') |
| |
| caps = tmp |
| |
| path = '"' + section_name + '"' |
| |
| if len(mode) == 3: |
| mode = '0' + mode |
| |
| try: |
| int(mode, 8) |
| except: |
| raise Exception('Mode must be octal characters, got: "' + mode + '"') |
| |
| if len(mode) != 4: |
| raise Exception('Mode must be 3 or 4 characters, got: "' + mode + '"') |
| |
| |
| caps = '|'.join(caps) |
| |
| x = [ mode, user, group, caps, section_name ] |
| if section_name[-1] == '/': |
| dirs.append((file_name, x)) |
| else: |
| files.append((file_name, x)) |
| |
| def handle_dup(name, file_name, section_name, seen): |
| if section_name in seen: |
| dups = '"' + seen[section_name] + '" and ' |
| dups += file_name |
| raise Exception('Duplicate ' + name + ' "' + section_name + '" found in files: ' + dups) |
| |
| def parse(file_name, files, dirs, aids, seen_paths, seen_aids): |
| |
| config = ConfigParser.ConfigParser() |
| config.read(file_name) |
| |
| for s in config.sections(): |
| |
| if AID_MATCH.match(s) and config.has_option(s, 'value'): |
| handle_dup('AID', file_name, s, seen_aids[0]) |
| seen_aids[0][s] = file_name |
| handle_aid(file_name, s, config, aids, seen_aids) |
| else: |
| handle_dup('path', file_name, s, seen_paths) |
| seen_paths[s] = file_name |
| handle_path(file_name, s, config, files, dirs) |
| |
| def generate(files, dirs, aids): |
| print GENERATED |
| print INCLUDE |
| print |
| |
| are_dirs = len(dirs) > 0 |
| are_files = len(files) > 0 |
| are_aids = len(aids) > 0 |
| |
| if are_aids: |
| # sort on value of (file_name, name, value, strvalue) |
| aids.sort(key=lambda x: x[2]) |
| for a in aids: |
| # use the preserved str value |
| print FILE_COMMENT % a[0] |
| print GENERIC_DEFINE % (a[1], a[2]) |
| |
| print |
| |
| if not are_dirs: |
| print DEFINE_NO_DIRS |
| |
| if not are_files: |
| print DEFINE_NO_FILES |
| |
| if not are_files and not are_dirs and not are_aids: |
| print DEFAULT_WARNING |
| return |
| |
| if are_files: |
| print OPEN_FILE_STRUCT |
| for tup in files: |
| f = tup[0] |
| c = tup[1] |
| c[4] = '"' + c[4] + '"' |
| c = '{ ' + ' ,'.join(c) + ' },' |
| print FILE_COMMENT % f |
| print ' ' + c |
| |
| if not are_dirs: |
| print IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS |
| print ' ' + NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY |
| print ENDIF |
| print CLOSE_FILE_STRUCT |
| |
| if are_dirs: |
| print OPEN_DIR_STRUCT |
| for d in dirs: |
| f[4] = '"' + f[4] + '"' |
| d = '{ ' + ' ,'.join(d) + ' },' |
| print ' ' + d |
| |
| print CLOSE_FILE_STRUCT |
| |
| def file_key(x): |
| |
| # Wrapper class for custom prefix matching strings |
| class S(object): |
| def __init__(self, str): |
| |
| self.orig = str |
| self.is_prefix = str[-1] == '*' |
| if self.is_prefix: |
| self.str = str[:-1] |
| else: |
| self.str = str |
| |
| def __lt__(self, other): |
| |
| # if were both suffixed the smallest string |
| # is 'bigger' |
| if self.is_prefix and other.is_prefix: |
| b = len(self.str) > len(other.str) |
| # If I am an the suffix match, im bigger |
| elif self.is_prefix: |
| b = False |
| # If other is the suffix match, he's bigger |
| elif other.is_prefix: |
| b = True |
| # Alphabetical |
| else: |
| b = self.str < other.str |
| return b |
| |
| return S(x[4]) |
| |
| def main(): |
| |
| files = [] |
| dirs = [] |
| aids = [] |
| seen_paths = {} |
| |
| # (name to file, value to aid) |
| seen_aids = ({}, {}) |
| |
| for x in sys.argv[1:]: |
| parse(x, files, dirs, aids, seen_paths, seen_aids) |
| |
| files.sort(key= lambda x: file_key(x[1])) |
| generate(files, dirs, aids) |
| |
| if __name__ == '__main__': |
| main() |