Create a host side checker for property info file correctness

Bug: 36001741
Test: verify a valid property info file and fail due to various failures
Change-Id: Iadd38796aa619f87ec559fe5687bbe2009df8b2d
This commit is contained in:
Tom Cherry 2018-01-03 14:39:28 -08:00
parent 2af1be4c16
commit 919458c350
9 changed files with 164 additions and 51 deletions

View file

@ -58,7 +58,6 @@
#include "init.h"
#include "persistent_properties.h"
#include "space_tokenizer.h"
#include "util.h"
using android::base::ReadFileToString;
@ -69,6 +68,7 @@ using android::base::Timer;
using android::base::Trim;
using android::base::WriteStringToFile;
using android::properties::BuildTrie;
using android::properties::ParsePropertyInfoFile;
using android::properties::PropertyInfoAreaFile;
using android::properties::PropertyInfoEntry;
@ -728,22 +728,6 @@ static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf,
return 0;
}
Result<PropertyInfoEntry> ParsePropertyInfoLine(const std::string& line) {
auto tokenizer = SpaceTokenizer(line);
auto property = tokenizer.GetNext();
if (property.empty()) return Error() << "Did not find a property entry in '" << line << "'";
auto context = tokenizer.GetNext();
if (context.empty()) return Error() << "Did not find a context entry in '" << line << "'";
// It is not an error to not find these, as older files will not contain them.
auto exact_match = tokenizer.GetNext();
auto schema = tokenizer.GetRemaining();
return {property, context, schema, exact_match == "exact"};
}
bool LoadPropertyInfoFromFile(const std::string& filename,
std::vector<PropertyInfoEntry>* property_infos) {
auto file_contents = std::string();
@ -752,20 +736,14 @@ bool LoadPropertyInfoFromFile(const std::string& filename,
return false;
}
for (const auto& line : Split(file_contents, "\n")) {
auto trimmed_line = Trim(line);
if (trimmed_line.empty() || StartsWith(trimmed_line, "#")) {
continue;
}
auto property_info = ParsePropertyInfoLine(line);
if (!property_info) {
LOG(ERROR) << "Could not read line from '" << filename << "': " << property_info.error();
continue;
}
property_infos->emplace_back(*property_info);
auto errors = std::vector<std::string>{};
ParsePropertyInfoFile(file_contents, property_infos, &errors);
// Individual parsing errors are reported but do not cause a failed boot, which is what
// returning false would do here.
for (const auto& error : errors) {
LOG(ERROR) << "Could not read line from '" << filename << "': " << error;
}
return true;
}

View file

@ -1,5 +1,6 @@
cc_library_static {
name: "libpropertyinfoparser",
host_supported: true,
srcs: ["property_info_parser.cpp"],
cpp_std: "experimental",

View file

@ -18,6 +18,7 @@
#define PROPERTY_INFO_PARSER_H
#include <stdint.h>
#include <stdlib.h>
namespace android {
namespace properties {

View file

@ -1,5 +1,6 @@
cc_defaults {
name: "propertyinfoserializer_defaults",
host_supported: true,
cpp_std: "experimental",
sanitize: {
misc_undefined: ["signed-integer-overflow"],
@ -19,6 +20,7 @@ cc_library_static {
name: "libpropertyinfoserializer",
defaults: ["propertyinfoserializer_defaults"],
srcs: [
"property_info_file.cpp",
"property_info_serializer.cpp",
"trie_builder.cpp",
"trie_serializer.cpp",

View file

@ -41,6 +41,10 @@ bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
const std::string& default_context, const std::string& default_schema,
std::string* serialized_trie, std::string* error);
void ParsePropertyInfoFile(const std::string& file_contents,
std::vector<PropertyInfoEntry>* property_infos,
std::vector<std::string>* errors);
} // namespace properties
} // namespace android

View file

@ -0,0 +1,62 @@
#include <property_info_serializer/property_info_serializer.h>
#include <android-base/strings.h>
#include "space_tokenizer.h"
using android::base::Split;
using android::base::StartsWith;
using android::base::Trim;
namespace android {
namespace properties {
bool ParsePropertyInfoLine(const std::string& line, PropertyInfoEntry* out, std::string* error) {
auto tokenizer = SpaceTokenizer(line);
auto property = tokenizer.GetNext();
if (property.empty()) {
*error = "Did not find a property entry in '" + line + "'";
return false;
}
auto context = tokenizer.GetNext();
if (context.empty()) {
*error = "Did not find a context entry in '" + line + "'";
return false;
}
// It is not an error to not find these, as older files will not contain them.
auto exact_match = tokenizer.GetNext();
auto schema = tokenizer.GetRemaining();
*out = {property, context, schema, exact_match == "exact"};
return true;
}
void ParsePropertyInfoFile(const std::string& file_contents,
std::vector<PropertyInfoEntry>* property_infos,
std::vector<std::string>* errors) {
// Do not clear property_infos to allow this function to be called on multiple files, with
// their results concatenated.
errors->clear();
for (const auto& line : Split(file_contents, "\n")) {
auto trimmed_line = Trim(line);
if (trimmed_line.empty() || StartsWith(trimmed_line, "#")) {
continue;
}
auto property_info_entry = PropertyInfoEntry{};
auto parse_error = std::string{};
if (!ParsePropertyInfoLine(trimmed_line, &property_info_entry, &parse_error)) {
errors->emplace_back(parse_error);
continue;
}
property_infos->emplace_back(property_info_entry);
}
}
} // namespace properties
} // namespace android

View file

@ -14,37 +14,37 @@
* limitations under the License.
*/
#ifndef _INIT_SPACE_TOKENIZER_H
#define _INIT_SPACE_TOKENIZER_H
#ifndef PROPERTY_INFO_SERIALIZER_SPACE_TOKENIZER_H
#define PROPERTY_INFO_SERIALIZER_SPACE_TOKENIZER_H
namespace android {
namespace init {
namespace properties {
class SpaceTokenizer {
public:
SpaceTokenizer(const std::string& string)
: string_(string), it_(string_.begin()), end_(string_.end()) {}
public:
SpaceTokenizer(const std::string& string)
: string_(string), it_(string_.begin()), end_(string_.end()) {}
std::string GetNext() {
auto next = std::string();
while (it_ != end_ && !isspace(*it_)) {
next.push_back(*it_++);
}
while (it_ != end_ && isspace(*it_)) {
it_++;
}
return next;
std::string GetNext() {
auto next = std::string();
while (it_ != end_ && !isspace(*it_)) {
next.push_back(*it_++);
}
while (it_ != end_ && isspace(*it_)) {
it_++;
}
return next;
}
std::string GetRemaining() { return std::string(it_, end_); }
std::string GetRemaining() { return std::string(it_, end_); }
private:
std::string string_;
std::string::const_iterator it_;
std::string::const_iterator end_;
private:
std::string string_;
std::string::const_iterator it_;
std::string::const_iterator end_;
};
} // namespace init
} // namespace properties
} // namespace android
#endif

View file

@ -0,0 +1,14 @@
cc_binary {
name: "property_info_checker",
host_supported: true,
cpp_std: "experimental",
sanitize: {
misc_undefined: ["signed-integer-overflow"],
},
static_libs: [
"libpropertyinfoserializer",
"libpropertyinfoparser",
"libbase",
],
srcs: ["property_info_checker.cpp"],
}

View file

@ -0,0 +1,51 @@
#include <iostream>
#include <string>
#include <vector>
#include <android-base/file.h>
#include <property_info_serializer/property_info_serializer.h>
using android::base::ReadFileToString;
using android::properties::BuildTrie;
using android::properties::ParsePropertyInfoFile;
using android::properties::PropertyInfoEntry;
int main(int argc, char** argv) {
if (argc < 2) {
std::cerr << "A list of property info files to be checked is expected on the command line"
<< std::endl;
return -1;
}
auto property_info_entries = std::vector<PropertyInfoEntry>{};
for (int i = 1; i < argc; ++i) {
auto filename = argv[i];
auto file_contents = std::string{};
if (!ReadFileToString(filename, &file_contents)) {
std::cerr << "Could not read properties from '" << filename << "'" << std::endl;
return -1;
}
auto errors = std::vector<std::string>{};
ParsePropertyInfoFile(file_contents, &property_info_entries, &errors);
if (!errors.empty()) {
for (const auto& error : errors) {
std::cerr << "Could not read line from '" << filename << "': " << error << std::endl;
}
return -1;
}
}
auto serialized_contexts = std::string{};
auto build_trie_error = std::string{};
if (!BuildTrie(property_info_entries, "u:object_r:default_prop:s0", "\\s*", &serialized_contexts,
&build_trie_error)) {
std::cerr << "Unable to serialize property contexts: " << build_trie_error << std::endl;
return -1;
}
return 0;
}