141 lines
3.9 KiB
C++
141 lines
3.9 KiB
C++
/*
|
|
* 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 <packagelistparser/packagelistparser.h>
|
|
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/limits.h>
|
|
|
|
#include <memory>
|
|
|
|
#include <log/log.h>
|
|
|
|
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<FILE, decltype(&fclose)> 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<pkg_info, decltype(&packagelist_free)> info(
|
|
static_cast<pkg_info*>(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);
|
|
}
|