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 ¤tPackage) 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 ¤tFQName : 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 ¤tPackage) 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 ¤tFQName : 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