#!/usr/bin/env python import ConfigParser import re import sys GENERATED = ''' /* * THIS IS AN AUTOGENERATED FILE! DO NOT MODIFY */ ''' INCLUDE = '#include ' 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: 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) # sort entries: # * specified path before prefix match # ** ie foo before f* # * lexicographical less than before other # ** ie boo before foo # Given these paths: # paths=['ac', 'a', 'acd', 'an', 'a*', 'aa', 'ac*'] # The sort order would be: # paths=['a', 'aa', 'ac', 'acd', 'an', 'ac*', 'a*'] # Thus the fs_config tools will match on specified paths before attempting # prefix, and match on the longest matching prefix. files.sort(key= lambda x: file_key(x[1])) # sort on value of (file_name, name, value, strvalue) # This is only cosmetic so AIDS are arranged in ascending order # within the generated file. aids.sort(key=lambda x: x[2]) generate(files, dirs, aids) if __name__ == '__main__': main()