William Roberts | c950a35 | 2016-03-04 18:12:29 -0800 | [diff] [blame^] | 1 | #!/usr/bin/env python |
| 2 | |
| 3 | import ConfigParser |
| 4 | import re |
| 5 | import sys |
| 6 | |
| 7 | |
| 8 | GENERATED = ''' |
| 9 | /* |
| 10 | * THIS IS AN AUTOGENERATED FILE! DO NOT MODIFY |
| 11 | */ |
| 12 | ''' |
| 13 | |
| 14 | INCLUDE = '#include <private/android_filesystem_config.h>' |
| 15 | |
| 16 | DEFINE_NO_DIRS = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS\n' |
| 17 | DEFINE_NO_FILES = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES\n' |
| 18 | |
| 19 | DEFAULT_WARNING = '#warning No device-supplied android_filesystem_config.h, using empty default.' |
| 20 | |
| 21 | NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY = '{ 00000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_dirs" },' |
| 22 | NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES_ENTRY = '{ 00000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_files" },' |
| 23 | |
| 24 | IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS = '#ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS' |
| 25 | ENDIF = '#endif' |
| 26 | |
| 27 | OPEN_FILE_STRUCT = 'static const struct fs_path_config android_device_files[] = {' |
| 28 | OPEN_DIR_STRUCT = 'static const struct fs_path_config android_device_dirs[] = {' |
| 29 | CLOSE_FILE_STRUCT = '};' |
| 30 | |
| 31 | GENERIC_DEFINE = "#define %s\t%s" |
| 32 | |
| 33 | FILE_COMMENT = '// Defined in file: \"%s\"' |
| 34 | |
| 35 | # from system/core/include/private/android_filesystem_config.h |
| 36 | AID_OEM_RESERVED_START = 2900 |
| 37 | AID_OEM_RESERVED_END = 2999 |
| 38 | |
| 39 | |
| 40 | AID_MATCH = re.compile('AID_[a-zA-Z]+') |
| 41 | |
| 42 | def handle_aid(file_name, section_name, config, aids, seen_aids): |
| 43 | value = config.get(section_name, 'value') |
| 44 | |
| 45 | errmsg = '%s for: \"' + section_name + '" file: \"' + file_name + '\"' |
| 46 | |
| 47 | if not value: |
| 48 | raise Exception(errmsg % 'Found specified but unset "value"') |
| 49 | |
| 50 | v = convert_int(value) |
| 51 | if not v: |
| 52 | raise Exception(errmsg % ('Invalid "value", not a number, got: \"%s\"' % value)) |
| 53 | |
| 54 | # Values must be within OEM range |
| 55 | if (v < AID_OEM_RESERVED_START) or (v > AID_OEM_RESERVED_END): |
| 56 | s = '"value" not in valid range %d - %d, got: %s' |
| 57 | s = s % (AID_OEM_RESERVED_START, AID_OEM_RESERVED_END, value) |
| 58 | raise Exception(errmsg % s) |
| 59 | |
| 60 | # use the normalized int value in the dict and detect |
| 61 | # duplicate definitions of the same vallue |
| 62 | v = str(v) |
| 63 | if v in seen_aids[1]: |
| 64 | # map of value to aid name |
| 65 | a = seen_aids[1][v] |
| 66 | |
| 67 | # aid name to file |
| 68 | f = seen_aids[0][a] |
| 69 | |
| 70 | s = 'Duplicate AID value "%s" found on AID: "%s".' % (value, seen_aids[1][v]) |
| 71 | s += ' Previous found in file: "%s."' % f |
| 72 | raise Exception(errmsg % s) |
| 73 | |
| 74 | seen_aids[1][v] = section_name |
| 75 | |
| 76 | # Append a tuple of (AID_*, base10(value), str(value)) |
| 77 | # We keep the str version of value so we can print that out in the |
| 78 | # generated header so investigating parties can identify parts. |
| 79 | # We store the base10 value for sorting, so everything is ascending |
| 80 | # later. |
| 81 | aids.append((file_name, section_name, v, value)) |
| 82 | |
| 83 | def convert_int(num): |
| 84 | |
| 85 | try: |
| 86 | if num.startswith('0x'): |
| 87 | return int(num, 16) |
| 88 | elif num.startswith('0b'): |
| 89 | return int(num, 2) |
| 90 | elif num.startswith('0'): |
| 91 | return int(num, 8) |
| 92 | else: |
| 93 | return int(num, 10) |
| 94 | except ValueError: |
| 95 | pass |
| 96 | return None |
| 97 | |
| 98 | def handle_path(file_name, section_name, config, files, dirs): |
| 99 | |
| 100 | mode = config.get(section_name, 'mode') |
| 101 | user = config.get(section_name, 'user') |
| 102 | group = config.get(section_name, 'group') |
| 103 | caps = config.get(section_name, 'caps') |
| 104 | |
| 105 | errmsg = 'Found specified but unset option: \"%s" in file: \"' + file_name + '\"' |
| 106 | |
| 107 | if not mode: |
| 108 | raise Exception(errmsg % 'mode') |
| 109 | |
| 110 | if not user: |
| 111 | raise Exception(errmsg % 'user') |
| 112 | |
| 113 | if not group: |
| 114 | raise Exception(errmsg % 'group') |
| 115 | |
| 116 | if not caps: |
| 117 | raise Exception(errmsg % 'caps') |
| 118 | |
| 119 | caps = caps.split() |
| 120 | |
| 121 | tmp = [] |
| 122 | for x in caps: |
| 123 | if convert_int(x): |
| 124 | tmp.append('(' + x + ')') |
| 125 | else: |
| 126 | tmp.append('(1ULL << CAP_' + x.upper() + ')') |
| 127 | |
| 128 | caps = tmp |
| 129 | |
| 130 | path = '"' + section_name + '"' |
| 131 | |
| 132 | if len(mode) == 3: |
| 133 | mode = '0' + mode |
| 134 | |
| 135 | try: |
| 136 | int(mode, 8) |
| 137 | except: |
| 138 | raise Exception('Mode must be octal characters, got: "' + mode + '"') |
| 139 | |
| 140 | if len(mode) != 4: |
| 141 | raise Exception('Mode must be 3 or 4 characters, got: "' + mode + '"') |
| 142 | |
| 143 | |
| 144 | caps = '|'.join(caps) |
| 145 | |
| 146 | x = [ mode, user, group, caps, section_name ] |
| 147 | if section_name[-1] == '/': |
| 148 | dirs.append((file_name, x)) |
| 149 | else: |
| 150 | files.append((file_name, x)) |
| 151 | |
| 152 | def handle_dup(name, file_name, section_name, seen): |
| 153 | if section_name in seen: |
| 154 | dups = '"' + seen[section_name] + '" and ' |
| 155 | dups += file_name |
| 156 | raise Exception('Duplicate ' + name + ' "' + section_name + '" found in files: ' + dups) |
| 157 | |
| 158 | def parse(file_name, files, dirs, aids, seen_paths, seen_aids): |
| 159 | |
| 160 | config = ConfigParser.ConfigParser() |
| 161 | config.read(file_name) |
| 162 | |
| 163 | for s in config.sections(): |
| 164 | |
| 165 | if AID_MATCH.match(s) and config.has_option(s, 'value'): |
| 166 | handle_dup('AID', file_name, s, seen_aids[0]) |
| 167 | seen_aids[0][s] = file_name |
| 168 | handle_aid(file_name, s, config, aids, seen_aids) |
| 169 | else: |
| 170 | handle_dup('path', file_name, s, seen_paths) |
| 171 | seen_paths[s] = file_name |
| 172 | handle_path(file_name, s, config, files, dirs) |
| 173 | |
| 174 | def generate(files, dirs, aids): |
| 175 | print GENERATED |
| 176 | print INCLUDE |
| 177 | print |
| 178 | |
| 179 | are_dirs = len(dirs) > 0 |
| 180 | are_files = len(files) > 0 |
| 181 | are_aids = len(aids) > 0 |
| 182 | |
| 183 | if are_aids: |
| 184 | # sort on value of (file_name, name, value, strvalue) |
| 185 | aids.sort(key=lambda x: x[2]) |
| 186 | for a in aids: |
| 187 | # use the preserved str value |
| 188 | print FILE_COMMENT % a[0] |
| 189 | print GENERIC_DEFINE % (a[1], a[2]) |
| 190 | |
| 191 | print |
| 192 | |
| 193 | if not are_dirs: |
| 194 | print DEFINE_NO_DIRS |
| 195 | |
| 196 | if not are_files: |
| 197 | print DEFINE_NO_FILES |
| 198 | |
| 199 | if not are_files and not are_dirs and not are_aids: |
| 200 | print DEFAULT_WARNING |
| 201 | return |
| 202 | |
| 203 | if are_files: |
| 204 | print OPEN_FILE_STRUCT |
| 205 | for tup in files: |
| 206 | f = tup[0] |
| 207 | c = tup[1] |
| 208 | c[4] = '"' + c[4] + '"' |
| 209 | c = '{ ' + ' ,'.join(c) + ' },' |
| 210 | print FILE_COMMENT % f |
| 211 | print ' ' + c |
| 212 | |
| 213 | if not are_dirs: |
| 214 | print IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS |
| 215 | print ' ' + NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY |
| 216 | print ENDIF |
| 217 | print CLOSE_FILE_STRUCT |
| 218 | |
| 219 | if are_dirs: |
| 220 | print OPEN_DIR_STRUCT |
| 221 | for d in dirs: |
| 222 | f[4] = '"' + f[4] + '"' |
| 223 | d = '{ ' + ' ,'.join(d) + ' },' |
| 224 | print ' ' + d |
| 225 | |
| 226 | print CLOSE_FILE_STRUCT |
| 227 | |
| 228 | def file_key(x): |
| 229 | |
| 230 | # Wrapper class for custom prefix matching strings |
| 231 | class S(object): |
| 232 | def __init__(self, str): |
| 233 | |
| 234 | self.orig = str |
| 235 | self.is_prefix = str[-1] == '*' |
| 236 | if self.is_prefix: |
| 237 | self.str = str[:-1] |
| 238 | else: |
| 239 | self.str = str |
| 240 | |
| 241 | def __lt__(self, other): |
| 242 | |
| 243 | # if were both suffixed the smallest string |
| 244 | # is 'bigger' |
| 245 | if self.is_prefix and other.is_prefix: |
| 246 | b = len(self.str) > len(other.str) |
| 247 | # If I am an the suffix match, im bigger |
| 248 | elif self.is_prefix: |
| 249 | b = False |
| 250 | # If other is the suffix match, he's bigger |
| 251 | elif other.is_prefix: |
| 252 | b = True |
| 253 | # Alphabetical |
| 254 | else: |
| 255 | b = self.str < other.str |
| 256 | return b |
| 257 | |
| 258 | return S(x[4]) |
| 259 | |
| 260 | def main(): |
| 261 | |
| 262 | files = [] |
| 263 | dirs = [] |
| 264 | aids = [] |
| 265 | seen_paths = {} |
| 266 | |
| 267 | # (name to file, value to aid) |
| 268 | seen_aids = ({}, {}) |
| 269 | |
| 270 | for x in sys.argv[1:]: |
| 271 | parse(x, files, dirs, aids, seen_paths, seen_aids) |
| 272 | |
| 273 | files.sort(key= lambda x: file_key(x[1])) |
| 274 | generate(files, dirs, aids) |
| 275 | |
| 276 | if __name__ == '__main__': |
| 277 | main() |