/* * Copyright (C) 2017 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 #include #include #include #include #include #include #include #include namespace android { const std::vector Hash::kEmptyHash = std::vector(SHA256_DIGEST_LENGTH, 0); Hash& Hash::getMutableHash(const std::string& path) { static std::map hashes; auto it = hashes.find(path); if (hashes.find(path) == hashes.end()) { it = hashes.insert(it, {path, Hash(path)}); } return it->second; } const Hash& Hash::getHash(const std::string& path) { return getMutableHash(path); } void Hash::clearHash(const std::string& path) { getMutableHash(path).mHash = kEmptyHash; } static std::vector sha256File(const std::string& path) { std::ifstream stream(path); std::stringstream fileStream; fileStream << stream.rdbuf(); std::string fileContent = fileStream.str(); std::vector ret = std::vector(SHA256_DIGEST_LENGTH); SHA256(reinterpret_cast(fileContent.c_str()), fileContent.size(), ret.data()); return ret; } Hash::Hash(const std::string& path) : mPath(path), mHash(sha256File(path)) {} std::string Hash::hexString(const std::vector& hash) { std::ostringstream s; s << std::hex << std::setfill('0'); for (uint8_t i : hash) { s << std::setw(2) << static_cast(i); } return s.str(); } std::string Hash::hexString() const { return hexString(mHash); } const std::vector& Hash::raw() const { return mHash; } const std::string& Hash::getPath() const { return mPath; } #define HASH "([0-9a-f]+)" #define FQNAME "([^\\s]+)" #define SPACES " +" #define MAYBE_SPACES " *" #define OPTIONAL_COMMENT "(?:#.*)?" static const std::regex kHashLine("(?:" MAYBE_SPACES HASH SPACES FQNAME MAYBE_SPACES ")?" OPTIONAL_COMMENT); struct HashFile { static const HashFile* parse(const std::string& path, std::string* err) { static std::map hashfiles; auto it = hashfiles.find(path); if (it == hashfiles.end()) { it = hashfiles.insert(it, {path, readHashFile(path, err)}); } return it->second; } std::vector lookup(const std::string& fqName) const { auto it = hashes.find(fqName); if (it == hashes.end()) { return {}; } return it->second; } private: static HashFile* readHashFile(const std::string& path, std::string* err) { std::ifstream stream(path); if (!stream) { return nullptr; } HashFile* file = new HashFile(); file->path = path; std::string line; while (std::getline(stream, line)) { std::smatch match; bool valid = std::regex_match(line, match, kHashLine); if (!valid) { *err = "Error reading line from " + path + ": " + line; delete file; return nullptr; } CHECK_EQ(match.size(), 3u); std::string hash = match.str(1); std::string fqName = match.str(2); if (hash.size() == 0 && fqName.size() == 0) { continue; } if (hash.size() == 0 || fqName.size() == 0) { *err = "Hash or fqName empty on " + path + ": " + line; delete file; return nullptr; } file->hashes[fqName].push_back(hash); } return file; } std::string path; std::map> hashes; }; std::vector Hash::lookupHash(const std::string& path, const std::string& interfaceName, std::string* err, bool* fileExists) { *err = ""; const HashFile* file = HashFile::parse(path, err); if (file == nullptr || err->size() > 0) { if (fileExists != nullptr) *fileExists = false; return {}; } if (fileExists != nullptr) *fileExists = true; return file->lookup(interfaceName); } } // namespace android