Home | History | Annotate | Download | only in hidl
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "Coordinator.h"
     18 
     19 #include <dirent.h>
     20 #include <sys/stat.h>
     21 
     22 #include <algorithm>
     23 #include <iterator>
     24 
     25 #include <android-base/logging.h>
     26 #include <hidl-hash/Hash.h>
     27 #include <hidl-util/StringHelper.h>
     28 
     29 #include "AST.h"
     30 #include "Interface.h"
     31 
     32 extern android::status_t parseFile(android::AST *ast);
     33 
     34 static bool existdir(const char *name) {
     35     DIR *dir = opendir(name);
     36     if (dir == NULL) {
     37         return false;
     38     }
     39     closedir(dir);
     40     return true;
     41 }
     42 
     43 namespace android {
     44 
     45 Coordinator::Coordinator(
     46         const std::vector<std::string> &packageRootPaths,
     47         const std::vector<std::string> &packageRoots,
     48         const std::string &rootPath)
     49     : mPackageRootPaths(packageRootPaths),
     50       mPackageRoots(packageRoots),
     51       mRootPath(rootPath) {
     52     // empty
     53 }
     54 
     55 Coordinator::~Coordinator() {
     56     // empty
     57 }
     58 
     59 void Coordinator::addDefaultPackagePath(const std::string& root, const std::string& path) {
     60     if (std::find(mPackageRoots.begin(), mPackageRoots.end(), root) == mPackageRoots.end()) {
     61         mPackageRoots.push_back(root);
     62         mPackageRootPaths.push_back(path);
     63     }
     64 }
     65 
     66 AST* Coordinator::parse(const FQName& fqName, std::set<AST*>* parsedASTs,
     67                         Enforce enforcement) const {
     68     CHECK(fqName.isFullyQualified());
     69 
     70     auto it = mCache.find(fqName);
     71     if (it != mCache.end()) {
     72         AST *ast = (*it).second;
     73 
     74         if (ast != nullptr && parsedASTs != nullptr) {
     75             parsedASTs->insert(ast);
     76         }
     77 
     78         return ast;
     79     }
     80 
     81     // Add this to the cache immediately, so we can discover circular imports.
     82     mCache[fqName] = nullptr;
     83 
     84     AST *typesAST = nullptr;
     85 
     86     if (fqName.name() != "types") {
     87         // Any interface file implicitly imports its package's types.hal.
     88         FQName typesName = fqName.getTypesForPackage();
     89         // Do not enforce on imports. Do not add imports' imports to this AST.
     90         typesAST = parse(typesName, nullptr, Enforce::NONE);
     91 
     92         // fall through.
     93     }
     94 
     95     std::string path = getAbsolutePackagePath(fqName);
     96 
     97     path.append(fqName.name());
     98     path.append(".hal");
     99 
    100     AST *ast = new AST(this, path);
    101 
    102     if (typesAST != NULL) {
    103         // If types.hal for this AST's package existed, make it's defined
    104         // types available to the (about to be parsed) AST right away.
    105         ast->addImportedAST(typesAST);
    106     }
    107 
    108     status_t err = parseFile(ast);
    109 
    110     if (err != OK) {
    111         delete ast;
    112         ast = nullptr;
    113 
    114         return nullptr;
    115     }
    116 
    117     if (ast->package().package() != fqName.package()
    118             || ast->package().version() != fqName.version()) {
    119         fprintf(stderr,
    120                 "ERROR: File at '%s' does not match expected package and/or "
    121                 "version.\n",
    122                 path.c_str());
    123 
    124         err = UNKNOWN_ERROR;
    125     } else {
    126         if (ast->isInterface()) {
    127             if (fqName.name() == "types") {
    128                 fprintf(stderr,
    129                         "ERROR: File at '%s' declares an interface '%s' "
    130                         "instead of the expected types common to the package.\n",
    131                         path.c_str(),
    132                         ast->getInterface()->localName().c_str());
    133 
    134                 err = UNKNOWN_ERROR;
    135             } else if (ast->getInterface()->localName() != fqName.name()) {
    136                 fprintf(stderr,
    137                         "ERROR: File at '%s' does not declare interface type "
    138                         "'%s'.\n",
    139                         path.c_str(),
    140                         fqName.name().c_str());
    141 
    142                 err = UNKNOWN_ERROR;
    143             }
    144         } else if (fqName.name() != "types") {
    145             fprintf(stderr,
    146                     "ERROR: File at '%s' declares types rather than the "
    147                     "expected interface type '%s'.\n",
    148                     path.c_str(),
    149                     fqName.name().c_str());
    150 
    151             err = UNKNOWN_ERROR;
    152         } else if (ast->containsInterfaces()) {
    153             fprintf(stderr,
    154                     "ERROR: types.hal file at '%s' declares at least one "
    155                     "interface type.\n",
    156                     path.c_str());
    157 
    158             err = UNKNOWN_ERROR;
    159         }
    160     }
    161 
    162     if (err != OK) {
    163         delete ast;
    164         ast = nullptr;
    165 
    166         return nullptr;
    167     }
    168 
    169     if (parsedASTs != nullptr) { parsedASTs->insert(ast); }
    170 
    171     // put it into the cache now, so that enforceRestrictionsOnPackage can
    172     // parse fqName.
    173     mCache[fqName] = ast;
    174 
    175     // For each .hal file that hidl-gen parses, the whole package will be checked.
    176     err = enforceRestrictionsOnPackage(fqName, enforcement);
    177     if (err != OK) {
    178         mCache[fqName] = nullptr;
    179         delete ast;
    180         ast = nullptr;
    181         return nullptr;
    182     }
    183 
    184     return ast;
    185 }
    186 
    187 std::vector<std::string>::const_iterator
    188 Coordinator::findPackageRoot(const FQName &fqName) const {
    189     CHECK(!fqName.package().empty());
    190 
    191     // Find the right package prefix and path for this FQName.  For
    192     // example, if FQName is "android.hardware.nfc (at) 1.0::INfc", and the
    193     // prefix:root is set to [ "android.hardware:hardware/interfaces",
    194     // "vendor.qcom.hardware:vendor/qcom"], then we will identify the
    195     // prefix "android.hardware" and the package root
    196     // "hardware/interfaces".
    197 
    198     auto it = mPackageRoots.begin();
    199     auto ret = mPackageRoots.end();
    200     for (; it != mPackageRoots.end(); it++) {
    201         if (!fqName.inPackage(*it)) {
    202             continue;
    203         }
    204 
    205         CHECK(ret == mPackageRoots.end())
    206             << "Multiple package roots found for " << fqName.string()
    207             << " (" << *it << " and " << *ret << ")";
    208 
    209         ret = it;
    210     }
    211     CHECK(ret != mPackageRoots.end())
    212         << "Unable to find package root for " << fqName.string();
    213 
    214     return ret;
    215 }
    216 
    217 std::string Coordinator::getAbsolutePackagePath(const FQName& fqName) const {
    218     const std::string packagePath = getPackagePath(fqName);
    219 
    220     if (StringHelper::StartsWith(packagePath, "/") || mRootPath.empty()) {
    221         return packagePath;
    222     }
    223 
    224     return StringHelper::RTrim(mRootPath, "/") + "/" + packagePath;
    225 }
    226 
    227 std::string Coordinator::getPackageRoot(const FQName &fqName) const {
    228     auto it = findPackageRoot(fqName);
    229     auto prefix = *it;
    230     return prefix;
    231 }
    232 
    233 std::string Coordinator::getPackageRootPath(const FQName &fqName) const {
    234     auto it = findPackageRoot(fqName);
    235     auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
    236     return root;
    237 }
    238 
    239 std::string Coordinator::getPackageRootOption(const FQName &fqName) const {
    240     return getPackageRoot(fqName) + ":" + getPackageRootPath(fqName);
    241 }
    242 
    243 std::string Coordinator::getPackagePath(
    244         const FQName &fqName, bool relative, bool sanitized) const {
    245 
    246     auto it = findPackageRoot(fqName);
    247     auto prefix = *it;
    248     auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
    249 
    250     // Make sure the prefix ends on a '.' and the root path on a '/'
    251     if ((*--prefix.end()) != '.') {
    252         prefix += '.';
    253     }
    254 
    255     if ((*--root.end()) != '/') {
    256         root += '/';
    257     }
    258 
    259     // Given FQName of "android.hardware.nfc (at) 1.0::IFoo" and a prefix
    260     // "android.hardware.", the suffix is "nfc (at) 1.0::IFoo".
    261     const std::string packageSuffix = fqName.package().substr(prefix.length());
    262 
    263     std::string packagePath;
    264     if (!relative) {
    265         packagePath = root;
    266     }
    267 
    268     size_t startPos = 0;
    269     size_t dotPos;
    270     while ((dotPos = packageSuffix.find('.', startPos)) != std::string::npos) {
    271         packagePath.append(packageSuffix.substr(startPos, dotPos - startPos));
    272         packagePath.append("/");
    273 
    274         startPos = dotPos + 1;
    275     }
    276     CHECK_LT(startPos + 1, packageSuffix.length());
    277     packagePath.append(packageSuffix.substr(startPos));
    278     packagePath.append("/");
    279 
    280     packagePath.append(sanitized ? fqName.sanitizedVersion() : fqName.version());
    281     packagePath.append("/");
    282 
    283     return packagePath;
    284 }
    285 
    286 status_t Coordinator::getPackageInterfaceFiles(
    287         const FQName &package,
    288         std::vector<std::string> *fileNames) const {
    289     fileNames->clear();
    290 
    291     const std::string packagePath = getAbsolutePackagePath(package);
    292 
    293     DIR *dir = opendir(packagePath.c_str());
    294 
    295     if (dir == NULL) {
    296         fprintf(stderr,
    297                 "ERROR: Could not open package path %s for package %s:\n%s\n",
    298                 getPackagePath(package).c_str(),
    299                 package.string().c_str(),
    300                 packagePath.c_str());
    301         return -errno;
    302     }
    303 
    304     struct dirent *ent;
    305     while ((ent = readdir(dir)) != NULL) {
    306         if (ent->d_type != DT_REG) {
    307             continue;
    308         }
    309 
    310         const auto suffix = ".hal";
    311         const auto suffix_len = std::strlen(suffix);
    312         const auto d_namelen = strlen(ent->d_name);
    313 
    314         if (d_namelen < suffix_len
    315                 || strcmp(ent->d_name + d_namelen - suffix_len, suffix)) {
    316             continue;
    317         }
    318 
    319         fileNames->push_back(std::string(ent->d_name, d_namelen - suffix_len));
    320     }
    321 
    322     closedir(dir);
    323     dir = NULL;
    324 
    325     std::sort(fileNames->begin(), fileNames->end(),
    326               [](const std::string& lhs, const std::string& rhs) -> bool {
    327                   if (lhs == "types") {
    328                       return true;
    329                   }
    330                   if (rhs == "types") {
    331                       return false;
    332                   }
    333                   return lhs < rhs;
    334               });
    335 
    336     return OK;
    337 }
    338 
    339 status_t Coordinator::appendPackageInterfacesToVector(
    340         const FQName &package,
    341         std::vector<FQName> *packageInterfaces) const {
    342     packageInterfaces->clear();
    343 
    344     std::vector<std::string> fileNames;
    345     status_t err = getPackageInterfaceFiles(package, &fileNames);
    346 
    347     if (err != OK) {
    348         return err;
    349     }
    350 
    351     for (const auto &fileName : fileNames) {
    352         FQName subFQName(
    353                 package.package() + package.atVersion() + "::" + fileName);
    354 
    355         if (!subFQName.isValid()) {
    356             LOG(WARNING)
    357                 << "Whole-package import encountered invalid filename '"
    358                 << fileName
    359                 << "' in package "
    360                 << package.package()
    361                 << package.atVersion();
    362 
    363             continue;
    364         }
    365 
    366         packageInterfaces->push_back(subFQName);
    367     }
    368 
    369     return OK;
    370 }
    371 
    372 std::string Coordinator::convertPackageRootToPath(const FQName &fqName) const {
    373     std::string packageRoot = getPackageRoot(fqName);
    374 
    375     if (*(packageRoot.end()--) != '.') {
    376         packageRoot += '.';
    377     }
    378 
    379     std::replace(packageRoot.begin(), packageRoot.end(), '.', '/');
    380 
    381     return packageRoot; // now converted to a path
    382 }
    383 
    384 status_t Coordinator::enforceRestrictionsOnPackage(const FQName& fqName,
    385                                                    Enforce enforcement) const {
    386     CHECK(enforcement == Enforce::FULL || enforcement == Enforce::NO_HASH ||
    387           enforcement == Enforce::NONE);
    388 
    389     // need fqName to be something like android.hardware.foo (at) 1.0.
    390     // name and valueName is ignored.
    391     if (fqName.package().empty() || fqName.version().empty()) {
    392         LOG(ERROR) << "Cannot enforce restrictions on package " << fqName.string()
    393                    << ": package or version is missing.";
    394         return BAD_VALUE;
    395     }
    396 
    397     if (enforcement == Enforce::NONE) {
    398         return OK;
    399     }
    400 
    401     FQName package = fqName.getPackageAndVersion();
    402     // look up cache.
    403     if (mPackagesEnforced.find(package) != mPackagesEnforced.end()) {
    404         return OK;
    405     }
    406 
    407     // enforce all rules.
    408     status_t err;
    409 
    410     err = enforceMinorVersionUprevs(package);
    411     if (err != OK) {
    412         return err;
    413     }
    414 
    415     if (enforcement != Enforce::NO_HASH) {
    416         err = enforceHashes(package);
    417         if (err != OK) {
    418             return err;
    419         }
    420     }
    421 
    422     // cache it so that it won't need to be enforced again.
    423     mPackagesEnforced.insert(package);
    424     return OK;
    425 }
    426 
    427 status_t Coordinator::enforceMinorVersionUprevs(const FQName &currentPackage) const {
    428     if(!currentPackage.hasVersion()) {
    429         LOG(ERROR) << "Cannot enforce minor version uprevs for " << currentPackage.string()
    430                    << ": missing version.";
    431         return UNKNOWN_ERROR;
    432     }
    433 
    434     if (currentPackage.getPackageMinorVersion() == 0) {
    435         return OK; // ignore for @x.0
    436     }
    437 
    438     bool hasPrevPackage = false;
    439     FQName prevPackage = currentPackage;
    440     while (prevPackage.getPackageMinorVersion() > 0) {
    441         prevPackage = prevPackage.downRev();
    442         if (existdir(getAbsolutePackagePath(prevPackage).c_str())) {
    443             hasPrevPackage = true;
    444             break;
    445         }
    446     }
    447     if (!hasPrevPackage) {
    448         // no @x.z, where z < y, exist.
    449         return OK;
    450     }
    451 
    452     if (prevPackage != currentPackage.downRev()) {
    453         LOG(ERROR) << "Cannot enforce minor version uprevs for " << currentPackage.string()
    454                    << ": Found package " << prevPackage.string() << " but missing "
    455                    << currentPackage.downRev().string() << "; you cannot skip a minor version.";
    456         return UNKNOWN_ERROR;
    457     }
    458 
    459     status_t err;
    460     std::vector<FQName> packageInterfaces;
    461     err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
    462     if (err != OK) {
    463         return err;
    464     }
    465 
    466     bool extendedInterface = false;
    467     for (const FQName &currentFQName : packageInterfaces) {
    468         if (currentFQName.name() == "types") {
    469             continue; // ignore types.hal
    470         }
    471 
    472         const Interface *iface = nullptr;
    473         AST *currentAST = parse(currentFQName);
    474         if (currentAST != nullptr) {
    475             iface = currentAST->getInterface();
    476         }
    477         if (iface == nullptr) {
    478             if (currentAST == nullptr) {
    479                 LOG(WARNING) << "Warning: Skipping " << currentFQName.string()
    480                              << " because it could not be found or parsed"
    481                              << " or " << currentPackage.string()
    482                              << " doesn't pass all requirements.";
    483             } else {
    484                 LOG(WARNING) << "Warning: Skipping " << currentFQName.string()
    485                              << " because the file might contain more than one interface.";
    486             }
    487             continue;
    488         }
    489 
    490         if (iface->superType() == nullptr) {
    491             CHECK(iface->isIBase());
    492             continue;
    493         }
    494 
    495         // Assume that currentFQName == android.hardware.foo (at) 2.2::IFoo.
    496         FQName lastFQName(prevPackage.package(), prevPackage.version(),
    497                 currentFQName.name());
    498         AST *lastAST = parse(lastFQName);
    499 
    500         for (; lastFQName.getPackageMinorVersion() > 0 &&
    501                (lastAST == nullptr || lastAST->getInterface() == nullptr)
    502              ; lastFQName = lastFQName.downRev(), lastAST = parse(lastFQName)) {
    503             // nothing
    504         }
    505 
    506         // Then lastFQName == android.hardware.foo (at) 2.1::IFoo or
    507         //      lastFQName == android.hardware.foo (at) 2.0::IFoo if 2.1 doesn't exist.
    508 
    509         bool lastFQNameExists = lastAST != nullptr && lastAST->getInterface() != nullptr;
    510 
    511         if (iface->superType()->fqName() != lastFQName && lastFQNameExists) {
    512             LOG(ERROR) << "Cannot enforce minor version uprevs for " << currentPackage.string()
    513                        << ": " << iface->fqName().string() << " extends "
    514                        << iface->superType()->fqName().string()
    515                        << ", which is not allowed. It must extend " << lastFQName.string();
    516             return UNKNOWN_ERROR;
    517         }
    518 
    519         // at least one interface must extend the previous version
    520         if (lastFQName.getPackageAndVersion() == prevPackage.getPackageAndVersion()) {
    521             extendedInterface = true;
    522         }
    523 
    524         LOG(VERBOSE) << "enforceMinorVersionUprevs: " << currentFQName.string() << " passes.";
    525     }
    526 
    527     if (!extendedInterface) {
    528         // No interface extends the interface with the same name in @x.(y-1).
    529         LOG(ERROR) << currentPackage.string() << " doesn't pass minor version uprev requirement. "
    530                    << "Requires at least one interface to extend an interface with the same name "
    531                    << "from " << prevPackage.string() << ".";
    532         return UNKNOWN_ERROR;
    533     }
    534 
    535     return OK;
    536 }
    537 
    538 status_t Coordinator::enforceHashes(const FQName &currentPackage) const {
    539     status_t err = OK;
    540     std::vector<FQName> packageInterfaces;
    541     err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
    542     if (err != OK) {
    543         return err;
    544     }
    545 
    546     for (const FQName &currentFQName : packageInterfaces) {
    547         AST *ast = parse(currentFQName);
    548 
    549         if (ast == nullptr) {
    550             err = UNKNOWN_ERROR;
    551             continue;
    552         }
    553 
    554         std::string hashPath = getPackageRootPath(currentFQName) + "/current.txt";
    555         std::string error;
    556         std::vector<std::string> frozen = Hash::lookupHash(hashPath, currentFQName.string(), &error);
    557 
    558         if (error.size() > 0) {
    559             LOG(ERROR) << error;
    560             err = UNKNOWN_ERROR;
    561             continue;
    562         }
    563 
    564         // hash not define, interface not frozen
    565         if (frozen.size() == 0) {
    566             continue;
    567         }
    568 
    569         std::string currentHash = Hash::getHash(ast->getFilename()).hexString();
    570 
    571         if(std::find(frozen.begin(), frozen.end(), currentHash) == frozen.end()) {
    572             LOG(ERROR) << currentFQName.string() << " has hash " << currentHash
    573                        << " which does not match hash on record. This interface has "
    574                        << "been frozen. Do not change it!";
    575             err = UNKNOWN_ERROR;
    576             continue;
    577         }
    578     }
    579 
    580     return err;
    581 }
    582 
    583 bool Coordinator::MakeParentHierarchy(const std::string &path) {
    584     static const mode_t kMode = 0755;
    585 
    586     size_t start = 1;  // Ignore leading '/'
    587     size_t slashPos;
    588     while ((slashPos = path.find("/", start)) != std::string::npos) {
    589         std::string partial = path.substr(0, slashPos);
    590 
    591         struct stat st;
    592         if (stat(partial.c_str(), &st) < 0) {
    593             if (errno != ENOENT) {
    594                 return false;
    595             }
    596 
    597             int res = mkdir(partial.c_str(), kMode);
    598             if (res < 0) {
    599                 return false;
    600             }
    601         } else if (!S_ISDIR(st.st_mode)) {
    602             return false;
    603         }
    604 
    605         start = slashPos + 1;
    606     }
    607 
    608     return true;
    609 }
    610 
    611 }  // namespace android
    612 
    613