/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "packagelistparser" #include #include #include #include #include #include #include #include static bool parse_gids(const char* path, size_t line_number, const char* gids, pkg_info* info) { // Nothing to do? if (!gids || !strcmp(gids, "none")) return true; // How much space do we need? info->gids.cnt = 1; for (const char* p = gids; *p; ++p) { if (*p == ',') ++info->gids.cnt; } // Allocate the space. info->gids.gids = new gid_t[info->gids.cnt]; if (!info->gids.gids) return false; // And parse the individual gids. size_t i = 0; while (true) { char* end; unsigned long gid = strtoul(gids, &end, 10); if (gid > GID_MAX) { ALOGE("%s:%zu: gid %lu > GID_MAX", path, line_number, gid); return false; } if (i >= info->gids.cnt) return false; info->gids.gids[i++] = gid; if (*end == '\0') return true; if (*end != ',') return false; gids = end + 1; } return true; } static bool parse_line(const char* path, size_t line_number, const char* line, pkg_info* info) { unsigned long uid; int debuggable; char* gid_list; int profileable_from_shell = 0; int fields = sscanf(line, "%ms %lu %d %ms %ms %ms %d %ld", &info->name, &uid, &debuggable, &info->data_dir, &info->seinfo, &gid_list, &profileable_from_shell, &info->version_code); // Handle the more complicated gids field and free the temporary string. bool gids_okay = parse_gids(path, line_number, gid_list, info); free(gid_list); if (!gids_okay) return false; // Did we see enough fields to be getting on with? // The final fields are optional (and not usually present). if (fields < 6) { ALOGE("%s:%zu: too few fields in line", path, line_number); return false; } // Extra validation. if (uid > UID_MAX) { ALOGE("%s:%zu: uid %lu > UID_MAX", path, line_number, uid); return false; } info->uid = uid; // Integer to bool conversions. info->debuggable = debuggable; info->profileable_from_shell = profileable_from_shell; return true; } bool packagelist_parse_file(const char* path, bool (*callback)(pkg_info*, void*), void* user_data) { std::unique_ptr fp(fopen(path, "re"), &fclose); if (!fp) { ALOGE("couldn't open '%s': %s", path, strerror(errno)); return false; } size_t line_number = 0; char* line = nullptr; size_t allocated_length = 0; while (getline(&line, &allocated_length, fp.get()) > 0) { ++line_number; std::unique_ptr info( static_cast(calloc(1, sizeof(pkg_info))), &packagelist_free); if (!info) { ALOGE("%s:%zu: couldn't allocate pkg_info", path, line_number); return false; } if (!parse_line(path, line_number, line, info.get())) return false; if (!callback(info.release(), user_data)) break; } free(line); return true; } bool packagelist_parse(bool (*callback)(pkg_info*, void*), void* user_data) { return packagelist_parse_file("/data/system/packages.list", callback, user_data); } void packagelist_free(pkg_info* info) { if (!info) return; free(info->name); free(info->data_dir); free(info->seinfo); delete[] info->gids.gids; free(info); }