diff options
| -rw-r--r-- | TEST_MAPPING | 3 | ||||
| -rw-r--r-- | adb/sysdeps_win32.cpp | 2 | ||||
| -rw-r--r-- | fs_mgr/liblp/reader.cpp | 1 | ||||
| -rw-r--r-- | fs_mgr/liblp/writer.cpp | 1 | ||||
| -rw-r--r-- | init/action.cpp | 7 | ||||
| -rw-r--r-- | libcutils/Android.bp | 1 | ||||
| -rw-r--r-- | libcutils/include/cutils/jstring.h | 37 | ||||
| l--------- | libcutils/include_vndk/cutils/jstring.h | 1 | ||||
| -rw-r--r-- | libcutils/strdup16to8.cpp | 168 | ||||
| l--------- | libpackagelistparser/.clang-format | 1 | ||||
| -rw-r--r-- | libpackagelistparser/Android.bp | 17 | ||||
| -rw-r--r-- | libpackagelistparser/include/packagelistparser/packagelistparser.h | 117 | ||||
| -rw-r--r-- | libpackagelistparser/packagelistparser.c | 291 | ||||
| -rw-r--r-- | libpackagelistparser/packagelistparser.cpp | 143 | ||||
| -rw-r--r-- | libpackagelistparser/packagelistparser_test.cpp | 134 |
15 files changed, 351 insertions, 573 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING index 66d0c9203b..a3bd44f77e 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -19,6 +19,9 @@ "name": "libbase_test" }, { + "name": "libpackagelistparser_test" + }, + { "name": "libprocinfo_test" }, { diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp index 6372b3dc6a..dc2525c536 100644 --- a/adb/sysdeps_win32.cpp +++ b/adb/sysdeps_win32.cpp @@ -688,7 +688,7 @@ static int _fh_socket_writev(FH f, const adb_iovec* iov, int iovcnt) { android::base::SystemErrorCodeToString(err).c_str()); } _socket_set_errno(err); - result = -1; + return -1; } CHECK_GE(static_cast<DWORD>(std::numeric_limits<int>::max()), bytes_written); return static_cast<int>(bytes_written); diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp index dcee6d2d86..8dbe955fa6 100644 --- a/fs_mgr/liblp/reader.cpp +++ b/fs_mgr/liblp/reader.cpp @@ -18,6 +18,7 @@ #include <stddef.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> #include <functional> diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp index bffcb7e6e1..8a983adf4f 100644 --- a/fs_mgr/liblp/writer.cpp +++ b/fs_mgr/liblp/writer.cpp @@ -17,6 +17,7 @@ #include "writer.h" #include <inttypes.h> +#include <string.h> #include <unistd.h> #include <string> diff --git a/init/action.cpp b/init/action.cpp index 1a66eee4a6..69e40d07fd 100644 --- a/init/action.cpp +++ b/init/action.cpp @@ -195,10 +195,11 @@ bool Action::CheckPropertyTriggers(const std::string& name, found = true; } } else { - std::string prop_val = android::base::GetProperty(trigger_name, ""); - if (prop_val.empty() || (trigger_value != "*" && trigger_value != prop_val)) { - return false; + std::string prop_value = android::base::GetProperty(trigger_name, ""); + if (trigger_value == "*" && !prop_value.empty()) { + continue; } + if (trigger_value != prop_value) return false; } } return found; diff --git a/libcutils/Android.bp b/libcutils/Android.bp index 5144fc6b15..b9420d4e31 100644 --- a/libcutils/Android.bp +++ b/libcutils/Android.bp @@ -67,7 +67,6 @@ cc_library { "native_handle.cpp", "record_stream.cpp", "sockets.cpp", - "strdup16to8.cpp", "strlcpy.c", "threads.cpp", ], diff --git a/libcutils/include/cutils/jstring.h b/libcutils/include/cutils/jstring.h deleted file mode 100644 index 6ede786140..0000000000 --- a/libcutils/include/cutils/jstring.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -#pragma once - -#include <stdint.h> -#include <stddef.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#if __STDC_VERSION__ < 201112L && __cplusplus < 201103L - typedef uint16_t char16_t; -#endif - // otherwise char16_t is a keyword with the right semantics - -extern char * strndup16to8 (const char16_t* s, size_t n); -extern size_t strnlen16to8 (const char16_t* s, size_t n); -extern char * strncpy16to8 (char *dest, const char16_t*s, size_t n); - -#ifdef __cplusplus -} -#endif diff --git a/libcutils/include_vndk/cutils/jstring.h b/libcutils/include_vndk/cutils/jstring.h deleted file mode 120000 index f3fd546ab7..0000000000 --- a/libcutils/include_vndk/cutils/jstring.h +++ /dev/null @@ -1 +0,0 @@ -../../include/cutils/jstring.h
\ No newline at end of file diff --git a/libcutils/strdup16to8.cpp b/libcutils/strdup16to8.cpp deleted file mode 100644 index d89181e14e..0000000000 --- a/libcutils/strdup16to8.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* libs/cutils/strdup16to8.c -** -** Copyright 2006, 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. -*/ - -#include <cutils/jstring.h> - -#include <assert.h> -#include <limits.h> /* for SIZE_MAX */ -#include <stdlib.h> - - -/** - * Given a UTF-16 string, compute the length of the corresponding UTF-8 - * string in bytes. - */ -extern size_t strnlen16to8(const char16_t* utf16Str, size_t len) -{ - size_t utf8Len = 0; - - /* A small note on integer overflow. The result can - * potentially be as big as 3*len, which will overflow - * for len > SIZE_MAX/3. - * - * Moreover, the result of a strnlen16to8 is typically used - * to allocate a destination buffer to strncpy16to8 which - * requires one more byte to terminate the UTF-8 copy, and - * this is generally done by careless users by incrementing - * the result without checking for integer overflows, e.g.: - * - * dst = malloc(strnlen16to8(utf16,len)+1) - * - * Due to this, the following code will try to detect - * overflows, and never return more than (SIZE_MAX-1) - * when it detects one. A careless user will try to malloc - * SIZE_MAX bytes, which will return NULL which can at least - * be detected appropriately. - * - * As far as I know, this function is only used by strndup16(), - * but better be safe than sorry. - */ - - /* Fast path for the usual case where 3*len is < SIZE_MAX-1. - */ - if (len < (SIZE_MAX-1)/3) { - while (len != 0) { - len--; - unsigned int uic = *utf16Str++; - - if (uic > 0x07ff) - utf8Len += 3; - else if (uic > 0x7f || uic == 0) - utf8Len += 2; - else - utf8Len++; - } - return utf8Len; - } - - /* The slower but paranoid version */ - while (len != 0) { - len--; - unsigned int uic = *utf16Str++; - size_t utf8Cur = utf8Len; - - if (uic > 0x07ff) - utf8Len += 3; - else if (uic > 0x7f || uic == 0) - utf8Len += 2; - else - utf8Len++; - - if (utf8Len < utf8Cur) /* overflow detected */ - return SIZE_MAX-1; - } - - /* don't return SIZE_MAX to avoid common user bug */ - if (utf8Len == SIZE_MAX) - utf8Len = SIZE_MAX-1; - - return utf8Len; -} - - -/** - * Convert a Java-Style UTF-16 string + length to a JNI-Style UTF-8 string. - * - * This basically means: embedded \0's in the UTF-16 string are encoded - * as "0xc0 0x80" - * - * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1, - * not just "len". - * - * Please note, a terminated \0 is always added, so your result will always - * be "strlen16to8() + 1" bytes long. - */ -extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len) -{ - char* utf8cur = utf8Str; - - /* Note on overflows: We assume the user did check the result of - * strnlen16to8() properly or at a minimum checked the result of - * its malloc(SIZE_MAX) in case of overflow. - */ - while (len != 0) { - len--; - unsigned int uic = *utf16Str++; - - if (uic > 0x07ff) { - *utf8cur++ = (uic >> 12) | 0xe0; - *utf8cur++ = ((uic >> 6) & 0x3f) | 0x80; - *utf8cur++ = (uic & 0x3f) | 0x80; - } else if (uic > 0x7f || uic == 0) { - *utf8cur++ = (uic >> 6) | 0xc0; - *utf8cur++ = (uic & 0x3f) | 0x80; - } else { - *utf8cur++ = uic; - - if (uic == 0) { - break; - } - } - } - - *utf8cur = '\0'; - - return utf8Str; -} - -/** - * Convert a UTF-16 string to UTF-8. - * - */ -char * strndup16to8 (const char16_t* s, size_t n) -{ - if (s == NULL) { - return NULL; - } - - size_t len = strnlen16to8(s, n); - - /* We are paranoid, and we check for SIZE_MAX-1 - * too since it is an overflow value for our - * strnlen16to8 implementation. - */ - if (len >= SIZE_MAX-1) - return NULL; - - char* ret = static_cast<char*>(malloc(len + 1)); - if (ret == NULL) - return NULL; - - strncpy16to8 (ret, s, n); - - return ret; -} diff --git a/libpackagelistparser/.clang-format b/libpackagelistparser/.clang-format new file mode 120000 index 0000000000..fd0645fdf9 --- /dev/null +++ b/libpackagelistparser/.clang-format @@ -0,0 +1 @@ +../.clang-format-2
\ No newline at end of file diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp index c38594a971..0740e7d650 100644 --- a/libpackagelistparser/Android.bp +++ b/libpackagelistparser/Android.bp @@ -1,12 +1,7 @@ cc_library { - name: "libpackagelistparser", recovery_available: true, - srcs: ["packagelistparser.c"], - cflags: [ - "-Wall", - "-Werror", - ], + srcs: ["packagelistparser.cpp"], shared_libs: ["liblog"], local_include_dirs: ["include"], export_include_dirs: ["include"], @@ -15,3 +10,13 @@ cc_library { misc_undefined: ["integer"], }, } + +cc_test { + name: "libpackagelistparser_test", + srcs: ["packagelistparser_test.cpp"], + shared_libs: [ + "libbase", + "libpackagelistparser", + ], + test_suites: ["device-tests"], +} diff --git a/libpackagelistparser/include/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h index 3cb6b9af0f..e89cb5400d 100644 --- a/libpackagelistparser/include/packagelistparser/packagelistparser.h +++ b/libpackagelistparser/include/packagelistparser/packagelistparser.h @@ -1,94 +1,81 @@ /* - * Copyright 2015, Intel Corporation - * Copyright (C) 2015 The Android Open Source Project + * 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 + * 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. - * - * Written by William Roberts <william.c.roberts@intel.com> - * - * This is a parser library for parsing the packages.list file generated - * by PackageManager service. - * - * This simple parser is sensitive to format changes in - * frameworks/base/services/core/java/com/android/server/pm/Settings.java - * A dependency note has been added to that file to correct - * this parser. */ -#ifndef PACKAGELISTPARSER_H_ -#define PACKAGELISTPARSER_H_ +#pragma once #include <stdbool.h> -#include <sys/cdefs.h> #include <sys/types.h> __BEGIN_DECLS -/** The file containing the list of installed packages on the system */ -#define PACKAGES_LIST_FILE "/data/system/packages.list" - -typedef struct pkg_info pkg_info; -typedef struct gid_list gid_list; - -struct gid_list { - size_t cnt; - gid_t *gids; -}; - -struct pkg_info { - char *name; - uid_t uid; - bool debuggable; - char *data_dir; - char *seinfo; - gid_list gids; - void *private_data; - bool profileable_from_shell; - long version_code; -}; +typedef struct gid_list { + /** Number of gids. */ + size_t cnt; -/** - * Callback function to be used by packagelist_parse() routine. - * @param info - * The parsed package information - * @param userdata - * The supplied userdata pointer to packagelist_parse() - * @return - * true to keep processing, false to stop. - */ -typedef bool (*pfn_on_package)(pkg_info *info, void *userdata); + /** Array of gids. */ + gid_t* gids; +} gid_list; + +typedef struct pkg_info { + /** Package name like "com.android.blah". */ + char* name; + + /** Package uid like 10014. */ + uid_t uid; + + /** Package's AndroidManifest.xml debuggable flag. */ + bool debuggable; + + /** Package data directory like "/data/user/0/com.android.blah" */ + char* data_dir; + + /** Package SELinux info. */ + char* seinfo; + + /** Package's list of gids. */ + gid_list gids; + + /** Spare pointer for the caller to stash extra data off. */ + void* private_data; + + /** Package's AndroidManifest.xml profileable flag. */ + bool profileable_from_shell; + + /** Package's AndroidManifest.xml version code. */ + long version_code; +} pkg_info; /** - * Parses the file specified by PACKAGES_LIST_FILE and invokes the callback on - * each entry found. Once the callback is invoked, ownership of the pkg_info pointer - * is passed to the callback routine, thus they are required to perform any cleanup - * desired. - * @param callback - * The callback function called on each parsed line of the packages list. - * @param userdata - * An optional userdata supplied pointer to pass to the callback function. - * @return - * true on success false on failure. + * Parses the system's default package list. + * Invokes `callback` once for each package. + * The callback owns the `pkg_info*` and should call packagelist_free(). + * The callback should return `false` to exit early or `true` to continue. */ -extern bool packagelist_parse(pfn_on_package callback, void *userdata); +bool packagelist_parse(bool (*callback)(pkg_info* info, void* user_data), void* user_data); /** - * Frees a pkg_info structure. - * @param info - * The struct to free + * Parses the given package list. + * Invokes `callback` once for each package. + * The callback owns the `pkg_info*` and should call packagelist_free(). + * The callback should return `false` to exit early or `true` to continue. */ -extern void packagelist_free(pkg_info *info); +bool packagelist_parse_file(const char* path, bool (*callback)(pkg_info* info, void* user_data), + void* user_data); -__END_DECLS +/** Frees the given `pkg_info`. */ +void packagelist_free(pkg_info* info); -#endif /* PACKAGELISTPARSER_H_ */ +__END_DECLS diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c deleted file mode 100644 index edc533cbff..0000000000 --- a/libpackagelistparser/packagelistparser.c +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright 2015, Intel Corporation - * Copyright (C) 2015 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. - * - * Written by William Roberts <william.c.roberts@intel.com> - * - */ - -#define LOG_TAG "packagelistparser" - -#include <errno.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/limits.h> - -#include <log/log.h> -#include <packagelistparser/packagelistparser.h> - -#define CLOGE(fmt, ...) \ - do {\ - IF_ALOGE() {\ - ALOGE(fmt, ##__VA_ARGS__);\ - }\ - } while(0) - -static size_t get_gid_cnt(const char *gids) -{ - size_t cnt; - - if (*gids == '\0') { - return 0; - } - - if (!strcmp(gids, "none")) { - return 0; - } - - for (cnt = 1; gids[cnt]; gids[cnt] == ',' ? cnt++ : *gids++) - ; - - return cnt; -} - -static bool parse_gids(char *gids, gid_t *gid_list, size_t *cnt) -{ - gid_t gid; - char* token; - char *endptr; - size_t cmp = 0; - - while ((token = strsep(&gids, ",\r\n"))) { - - if (cmp > *cnt) { - return false; - } - - gid = strtoul(token, &endptr, 10); - if (*endptr != '\0') { - return false; - } - - /* - * if unsigned long is greater than size of gid_t, - * prevent a truncation based roll-over - */ - if (gid > GID_MAX) { - CLOGE("A gid in field \"gid list\" greater than GID_MAX"); - return false; - } - - gid_list[cmp++] = gid; - } - return true; -} - -extern bool packagelist_parse(pfn_on_package callback, void *userdata) -{ - - FILE *fp; - char *cur; - char *next; - char *endptr; - unsigned long tmp; - ssize_t bytesread; - - bool rc = false; - char *buf = NULL; - size_t buflen = 0; - unsigned long lineno = 1; - const char *errmsg = NULL; - struct pkg_info *pkg_info = NULL; - - fp = fopen(PACKAGES_LIST_FILE, "re"); - if (!fp) { - CLOGE("Could not open: \"%s\", error: \"%s\"\n", PACKAGES_LIST_FILE, - strerror(errno)); - return false; - } - - while ((bytesread = getline(&buf, &buflen, fp)) > 0) { - - pkg_info = calloc(1, sizeof(*pkg_info)); - if (!pkg_info) { - goto err; - } - - next = buf; - - cur = strsep(&next, " \t\r\n"); - if (!cur) { - errmsg = "Could not get next token for \"package name\""; - goto err; - } - - pkg_info->name = strdup(cur); - if (!pkg_info->name) { - goto err; - } - - cur = strsep(&next, " \t\r\n"); - if (!cur) { - errmsg = "Could not get next token for field \"uid\""; - goto err; - } - - tmp = strtoul(cur, &endptr, 10); - if (*endptr != '\0') { - errmsg = "Could not convert field \"uid\" to integer value"; - goto err; - } - - /* - * if unsigned long is greater than size of uid_t, - * prevent a truncation based roll-over - */ - if (tmp > UID_MAX) { - errmsg = "Field \"uid\" greater than UID_MAX"; - goto err; - } - - pkg_info->uid = (uid_t) tmp; - - cur = strsep(&next, " \t\r\n"); - if (!cur) { - errmsg = "Could not get next token for field \"debuggable\""; - goto err; - } - - tmp = strtoul(cur, &endptr, 10); - if (*endptr != '\0') { - errmsg = "Could not convert field \"debuggable\" to integer value"; - goto err; - } - - /* should be a valid boolean of 1 or 0 */ - if (!(tmp == 0 || tmp == 1)) { - errmsg = "Field \"debuggable\" is not 0 or 1 boolean value"; - goto err; - } - - pkg_info->debuggable = (bool) tmp; - - cur = strsep(&next, " \t\r\n"); - if (!cur) { - errmsg = "Could not get next token for field \"data dir\""; - goto err; - } - - pkg_info->data_dir = strdup(cur); - if (!pkg_info->data_dir) { - goto err; - } - - cur = strsep(&next, " \t\r\n"); - if (!cur) { - errmsg = "Could not get next token for field \"seinfo\""; - goto err; - } - - pkg_info->seinfo = strdup(cur); - if (!pkg_info->seinfo) { - goto err; - } - - cur = strsep(&next, " \t\r\n"); - if (!cur) { - errmsg = "Could not get next token for field \"gid(s)\""; - goto err; - } - - /* - * Parse the gid list, could be in the form of none, single gid or list: - * none - * gid - * gid, gid ... - */ - pkg_info->gids.cnt = get_gid_cnt(cur); - if (pkg_info->gids.cnt > 0) { - - pkg_info->gids.gids = calloc(pkg_info->gids.cnt, sizeof(gid_t)); - if (!pkg_info->gids.gids) { - goto err; - } - - rc = parse_gids(cur, pkg_info->gids.gids, &pkg_info->gids.cnt); - if (!rc) { - errmsg = "Could not parse field \"gid list\""; - goto err; - } - } - - cur = strsep(&next, " \t\r\n"); - if (cur) { - tmp = strtoul(cur, &endptr, 10); - if (*endptr != '\0') { - errmsg = "Could not convert field \"profileable_from_shell\" to integer value"; - goto err; - } - - /* should be a valid boolean of 1 or 0 */ - if (!(tmp == 0 || tmp == 1)) { - errmsg = "Field \"profileable_from_shell\" is not 0 or 1 boolean value"; - goto err; - } - - pkg_info->profileable_from_shell = (bool)tmp; - } - cur = strsep(&next, " \t\r\n"); - if (cur) { - tmp = strtoul(cur, &endptr, 10); - if (*endptr != '\0') { - errmsg = "Could not convert field \"versionCode\" to integer value"; - goto err; - } - pkg_info->version_code = tmp; - } - - rc = callback(pkg_info, userdata); - if (rc == false) { - /* - * We do not log this as this can be intentional from - * callback to abort processing. We go to out to not - * free the pkg_info - */ - rc = true; - goto out; - } - lineno++; - } - - rc = true; - -out: - free(buf); - fclose(fp); - return rc; - -err: - if (errmsg) { - CLOGE("Error Parsing \"%s\" on line: %lu for reason: %s", - PACKAGES_LIST_FILE, lineno, errmsg); - } - rc = false; - packagelist_free(pkg_info); - goto out; -} - -void packagelist_free(pkg_info *info) -{ - if (info) { - free(info->name); - free(info->data_dir); - free(info->seinfo); - free(info->gids.gids); - free(info); - } -} diff --git a/libpackagelistparser/packagelistparser.cpp b/libpackagelistparser/packagelistparser.cpp new file mode 100644 index 0000000000..ddf558b5b4 --- /dev/null +++ b/libpackagelistparser/packagelistparser.cpp @@ -0,0 +1,143 @@ +/* + * 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. + if (debuggable != 0 && debuggable != 1) return false; + info->debuggable = debuggable; + + if (profileable_from_shell != 0 && profileable_from_shell != 1) return false; + 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); +} diff --git a/libpackagelistparser/packagelistparser_test.cpp b/libpackagelistparser/packagelistparser_test.cpp new file mode 100644 index 0000000000..76cb886e21 --- /dev/null +++ b/libpackagelistparser/packagelistparser_test.cpp @@ -0,0 +1,134 @@ +/* + * 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. + */ + +#include <packagelistparser/packagelistparser.h> + +#include <memory> + +#include <android-base/file.h> + +#include <gtest/gtest.h> + +TEST(packagelistparser, smoke) { + TemporaryFile tf; + android::base::WriteStringToFile( + // No gids. + "com.test.a0 10014 0 /data/user/0/com.test.a0 platform:privapp:targetSdkVersion=19 none\n" + // One gid. + "com.test.a1 10007 1 /data/user/0/com.test.a1 platform:privapp:targetSdkVersion=21 1023\n" + // Multiple gids. + "com.test.a2 10011 0 /data/user/0/com.test.a2 media:privapp:targetSdkVersion=30 " + "2001,1065,1023,3003,3007,1024\n" + // The two new fields (profileable flag and version code). + "com.test.a3 10022 0 /data/user/0/com.test.a3 selabel:blah none 1 123\n", + tf.path); + + std::vector<pkg_info*> packages; + packagelist_parse_file( + tf.path, + [](pkg_info* info, void* user_data) -> bool { + reinterpret_cast<std::vector<pkg_info*>*>(user_data)->push_back(info); + return true; + }, + &packages); + + ASSERT_EQ(4U, packages.size()); + + ASSERT_STREQ("com.test.a0", packages[0]->name); + ASSERT_EQ(10014, packages[0]->uid); + ASSERT_FALSE(packages[0]->debuggable); + ASSERT_STREQ("/data/user/0/com.test.a0", packages[0]->data_dir); + ASSERT_STREQ("platform:privapp:targetSdkVersion=19", packages[0]->seinfo); + ASSERT_EQ(0U, packages[0]->gids.cnt); + ASSERT_FALSE(packages[0]->profileable_from_shell); + ASSERT_EQ(0, packages[0]->version_code); + + ASSERT_STREQ("com.test.a1", packages[1]->name); + ASSERT_EQ(10007, packages[1]->uid); + ASSERT_TRUE(packages[1]->debuggable); + ASSERT_STREQ("/data/user/0/com.test.a1", packages[1]->data_dir); + ASSERT_STREQ("platform:privapp:targetSdkVersion=21", packages[1]->seinfo); + ASSERT_EQ(1U, packages[1]->gids.cnt); + ASSERT_EQ(1023U, packages[1]->gids.gids[0]); + ASSERT_FALSE(packages[0]->profileable_from_shell); + ASSERT_EQ(0, packages[0]->version_code); + + ASSERT_STREQ("com.test.a2", packages[2]->name); + ASSERT_EQ(10011, packages[2]->uid); + ASSERT_FALSE(packages[2]->debuggable); + ASSERT_STREQ("/data/user/0/com.test.a2", packages[2]->data_dir); + ASSERT_STREQ("media:privapp:targetSdkVersion=30", packages[2]->seinfo); + ASSERT_EQ(6U, packages[2]->gids.cnt); + ASSERT_EQ(2001U, packages[2]->gids.gids[0]); + ASSERT_EQ(1024U, packages[2]->gids.gids[5]); + ASSERT_FALSE(packages[0]->profileable_from_shell); + ASSERT_EQ(0, packages[0]->version_code); + + ASSERT_STREQ("com.test.a3", packages[3]->name); + ASSERT_EQ(10022, packages[3]->uid); + ASSERT_FALSE(packages[3]->debuggable); + ASSERT_STREQ("/data/user/0/com.test.a3", packages[3]->data_dir); + ASSERT_STREQ("selabel:blah", packages[3]->seinfo); + ASSERT_EQ(0U, packages[3]->gids.cnt); + ASSERT_TRUE(packages[3]->profileable_from_shell); + ASSERT_EQ(123, packages[3]->version_code); + + for (auto& package : packages) packagelist_free(package); +} + +TEST(packagelistparser, early_exit) { + TemporaryFile tf; + android::base::WriteStringToFile( + "com.test.a0 1 0 / a none\n" + "com.test.a1 1 0 / a none\n" + "com.test.a2 1 0 / a none\n", + tf.path); + + std::vector<pkg_info*> packages; + packagelist_parse_file( + tf.path, + [](pkg_info* info, void* user_data) -> bool { + std::vector<pkg_info*>* p = reinterpret_cast<std::vector<pkg_info*>*>(user_data); + p->push_back(info); + return p->size() < 2; + }, + &packages); + + ASSERT_EQ(2U, packages.size()); + + ASSERT_STREQ("com.test.a0", packages[0]->name); + ASSERT_STREQ("com.test.a1", packages[1]->name); + + for (auto& package : packages) packagelist_free(package); +} + +TEST(packagelistparser, system_package_list) { + // Check that we can actually read the packages.list installed on the device. + std::vector<pkg_info*> packages; + packagelist_parse( + [](pkg_info* info, void* user_data) -> bool { + reinterpret_cast<std::vector<pkg_info*>*>(user_data)->push_back(info); + return true; + }, + &packages); + // Not much we can say for sure about what we expect, other than that there + // are likely to be lots of packages... + ASSERT_GT(packages.size(), 10U); +} + +TEST(packagelistparser, packagelist_free_nullptr) { + packagelist_free(nullptr); +} |
