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 |
William Roberts | 580f2c4 | 2016-04-08 22:03:42 -0700 | [diff] [blame] | 36 | AID_OEM_RESERVED_RANGES = [ |
| 37 | (2900, 2999), |
| 38 | (5000, 5999), |
| 39 | ] |
William Roberts | c950a35 | 2016-03-04 18:12:29 -0800 | [diff] [blame] | 40 | |
| 41 | |
| 42 | AID_MATCH = re.compile('AID_[a-zA-Z]+') |
| 43 | |
| 44 | def handle_aid(file_name, section_name, config, aids, seen_aids): |
| 45 | value = config.get(section_name, 'value') |
| 46 | |
| 47 | errmsg = '%s for: \"' + section_name + '" file: \"' + file_name + '\"' |
| 48 | |
| 49 | if not value: |
| 50 | raise Exception(errmsg % 'Found specified but unset "value"') |
| 51 | |
| 52 | v = convert_int(value) |
| 53 | if not v: |
| 54 | raise Exception(errmsg % ('Invalid "value", not a number, got: \"%s\"' % value)) |
| 55 | |
| 56 | # Values must be within OEM range |
William Roberts | 580f2c4 | 2016-04-08 22:03:42 -0700 | [diff] [blame] | 57 | if not any(lower <= v <= upper for (lower, upper) in AID_OEM_RESERVED_RANGES): |
| 58 | s = '"value" not in valid range %s, got: %s' |
| 59 | s = s % (str(AID_OEM_RESERVED_RANGES), value) |
William Roberts | c950a35 | 2016-03-04 18:12:29 -0800 | [diff] [blame] | 60 | raise Exception(errmsg % s) |
| 61 | |
| 62 | # use the normalized int value in the dict and detect |
| 63 | # duplicate definitions of the same vallue |
| 64 | v = str(v) |
| 65 | if v in seen_aids[1]: |
| 66 | # map of value to aid name |
| 67 | a = seen_aids[1][v] |
| 68 | |
| 69 | # aid name to file |
| 70 | f = seen_aids[0][a] |
| 71 | |
| 72 | s = 'Duplicate AID value "%s" found on AID: "%s".' % (value, seen_aids[1][v]) |
| 73 | s += ' Previous found in file: "%s."' % f |
| 74 | raise Exception(errmsg % s) |
| 75 | |
| 76 | seen_aids[1][v] = section_name |
| 77 | |
| 78 | # Append a tuple of (AID_*, base10(value), str(value)) |
| 79 | # We keep the str version of value so we can print that out in the |
| 80 | # generated header so investigating parties can identify parts. |
| 81 | # We store the base10 value for sorting, so everything is ascending |
| 82 | # later. |
| 83 | aids.append((file_name, section_name, v, value)) |
| 84 | |
| 85 | def convert_int(num): |
| 86 | |
| 87 | try: |
| 88 | if num.startswith('0x'): |
| 89 | return int(num, 16) |
| 90 | elif num.startswith('0b'): |
| 91 | return int(num, 2) |
| 92 | elif num.startswith('0'): |
| 93 | return int(num, 8) |
| 94 | else: |
| 95 | return int(num, 10) |
| 96 | except ValueError: |
| 97 | pass |
| 98 | return None |
| 99 | |
| 100 | def handle_path(file_name, section_name, config, files, dirs): |
| 101 | |
| 102 | mode = config.get(section_name, 'mode') |
| 103 | user = config.get(section_name, 'user') |
| 104 | group = config.get(section_name, 'group') |
| 105 | caps = config.get(section_name, 'caps') |
| 106 | |
| 107 | errmsg = 'Found specified but unset option: \"%s" in file: \"' + file_name + '\"' |
| 108 | |
| 109 | if not mode: |
| 110 | raise Exception(errmsg % 'mode') |
| 111 | |
| 112 | if not user: |
| 113 | raise Exception(errmsg % 'user') |
| 114 | |
| 115 | if not group: |
| 116 | raise Exception(errmsg % 'group') |
| 117 | |
| 118 | if not caps: |
| 119 | raise Exception(errmsg % 'caps') |
| 120 | |
| 121 | caps = caps.split() |
| 122 | |
| 123 | tmp = [] |
| 124 | for x in caps: |
| 125 | if convert_int(x): |
| 126 | tmp.append('(' + x + ')') |
| 127 | else: |
| 128 | tmp.append('(1ULL << CAP_' + x.upper() + ')') |
| 129 | |
| 130 | caps = tmp |
| 131 | |
| 132 | path = '"' + section_name + '"' |
| 133 | |
| 134 | if len(mode) == 3: |
| 135 | mode = '0' + mode |
| 136 | |
| 137 | try: |
| 138 | int(mode, 8) |
| 139 | except: |
| 140 | raise Exception('Mode must be octal characters, got: "' + mode + '"') |
| 141 | |
| 142 | if len(mode) != 4: |
| 143 | raise Exception('Mode must be 3 or 4 characters, got: "' + mode + '"') |
| 144 | |
| 145 | |
| 146 | caps = '|'.join(caps) |
| 147 | |
| 148 | x = [ mode, user, group, caps, section_name ] |
| 149 | if section_name[-1] == '/': |
| 150 | dirs.append((file_name, x)) |
| 151 | else: |
| 152 | files.append((file_name, x)) |
| 153 | |
| 154 | def handle_dup(name, file_name, section_name, seen): |
| 155 | if section_name in seen: |
| 156 | dups = '"' + seen[section_name] + '" and ' |
| 157 | dups += file_name |
| 158 | raise Exception('Duplicate ' + name + ' "' + section_name + '" found in files: ' + dups) |
| 159 | |
| 160 | def parse(file_name, files, dirs, aids, seen_paths, seen_aids): |
| 161 | |
| 162 | config = ConfigParser.ConfigParser() |
| 163 | config.read(file_name) |
| 164 | |
| 165 | for s in config.sections(): |
| 166 | |
| 167 | if AID_MATCH.match(s) and config.has_option(s, 'value'): |
| 168 | handle_dup('AID', file_name, s, seen_aids[0]) |
| 169 | seen_aids[0][s] = file_name |
| 170 | handle_aid(file_name, s, config, aids, seen_aids) |
| 171 | else: |
| 172 | handle_dup('path', file_name, s, seen_paths) |
| 173 | seen_paths[s] = file_name |
| 174 | handle_path(file_name, s, config, files, dirs) |
| 175 | |
| 176 | def generate(files, dirs, aids): |
| 177 | print GENERATED |
| 178 | print INCLUDE |
| 179 | print |
| 180 | |
| 181 | are_dirs = len(dirs) > 0 |
| 182 | are_files = len(files) > 0 |
| 183 | are_aids = len(aids) > 0 |
| 184 | |
| 185 | if are_aids: |
William Roberts | c950a35 | 2016-03-04 18:12:29 -0800 | [diff] [blame] | 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 | |
William Roberts | 8cb6a18 | 2016-04-08 22:06:19 -0700 | [diff] [blame^] | 273 | # sort entries: |
| 274 | # * specified path before prefix match |
| 275 | # ** ie foo before f* |
| 276 | # * lexicographical less than before other |
| 277 | # ** ie boo before foo |
| 278 | # Given these paths: |
| 279 | # paths=['ac', 'a', 'acd', 'an', 'a*', 'aa', 'ac*'] |
| 280 | # The sort order would be: |
| 281 | # paths=['a', 'aa', 'ac', 'acd', 'an', 'ac*', 'a*'] |
| 282 | # Thus the fs_config tools will match on specified paths before attempting |
| 283 | # prefix, and match on the longest matching prefix. |
William Roberts | c950a35 | 2016-03-04 18:12:29 -0800 | [diff] [blame] | 284 | files.sort(key= lambda x: file_key(x[1])) |
William Roberts | 8cb6a18 | 2016-04-08 22:06:19 -0700 | [diff] [blame^] | 285 | |
| 286 | # sort on value of (file_name, name, value, strvalue) |
| 287 | # This is only cosmetic so AIDS are arranged in ascending order |
| 288 | # within the generated file. |
| 289 | aids.sort(key=lambda x: x[2]) |
| 290 | |
William Roberts | c950a35 | 2016-03-04 18:12:29 -0800 | [diff] [blame] | 291 | generate(files, dirs, aids) |
| 292 | |
| 293 | if __name__ == '__main__': |
| 294 | main() |