• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <iostream>
29 
30 #include "AST.h"
31 #include "Interface.h"
32 #include "hidl-gen_l.h"
33 
existdir(const char * name)34 static bool existdir(const char *name) {
35     DIR *dir = opendir(name);
36     if (dir == nullptr) {
37         return false;
38     }
39     closedir(dir);
40     return true;
41 }
42 
43 namespace android {
44 
getRootPath() const45 const std::string &Coordinator::getRootPath() const {
46     return mRootPath;
47 }
48 
setRootPath(const std::string & rootPath)49 void Coordinator::setRootPath(const std::string &rootPath) {
50     mRootPath = rootPath;
51 
52     if (!mRootPath.empty() && !StringHelper::EndsWith(mRootPath, "/")) {
53         mRootPath += "/";
54     }
55 }
56 
setOutputPath(const std::string & outputPath)57 void Coordinator::setOutputPath(const std::string& outputPath) {
58     mOutputPath = outputPath;
59 }
60 
setVerbose(bool verbose)61 void Coordinator::setVerbose(bool verbose) {
62     mVerbose = verbose;
63 }
64 
isVerbose() const65 bool Coordinator::isVerbose() const {
66     return mVerbose;
67 }
68 
setDepFile(const std::string & depFile)69 void Coordinator::setDepFile(const std::string& depFile) {
70     mDepFile = depFile;
71 }
72 
getOwner() const73 const std::string& Coordinator::getOwner() const {
74     return mOwner;
75 }
setOwner(const std::string & owner)76 void Coordinator::setOwner(const std::string& owner) {
77     mOwner = owner;
78 }
79 
addPackagePath(const std::string & root,const std::string & path,std::string * error)80 status_t Coordinator::addPackagePath(const std::string& root, const std::string& path, std::string* error) {
81     FQName package = FQName(root, "0.0", "");
82     for (const PackageRoot &packageRoot : mPackageRoots) {
83         if (packageRoot.root.inPackage(root) || package.inPackage(packageRoot.root.package())) {
84             if (error != nullptr) {
85                 *error = "ERROR: conflicting package roots " +
86                          packageRoot.root.package() +
87                          " and " +
88                          root;
89             }
90 
91             return UNKNOWN_ERROR;
92         }
93     }
94 
95     mPackageRoots.push_back({path, package});
96     return OK;
97 }
addDefaultPackagePath(const std::string & root,const std::string & path)98 void Coordinator::addDefaultPackagePath(const std::string& root, const std::string& path) {
99     addPackagePath(root, path, nullptr /* error */);
100 }
101 
getFormatter(const FQName & fqName,Location location,const std::string & fileName) const102 Formatter Coordinator::getFormatter(const FQName& fqName, Location location,
103                                     const std::string& fileName) const {
104     if (location == Location::STANDARD_OUT) {
105         return Formatter(stdout);
106     }
107 
108     std::string filepath;
109     status_t err = getFilepath(fqName, location, fileName, &filepath);
110     if (err != OK) {
111         return Formatter::invalid();
112     }
113 
114     onFileAccess(filepath, "w");
115 
116     if (!Coordinator::MakeParentHierarchy(filepath)) {
117         fprintf(stderr, "ERROR: could not make directories for %s.\n", filepath.c_str());
118         return Formatter::invalid();
119     }
120 
121     FILE* file = fopen(filepath.c_str(), "w");
122 
123     if (file == nullptr) {
124         fprintf(stderr, "ERROR: could not open file %s: %d\n", filepath.c_str(), errno);
125         return Formatter::invalid();
126     }
127 
128     return Formatter(file);
129 }
130 
getFilepath(const FQName & fqName,Location location,const std::string & fileName,std::string * path) const131 status_t Coordinator::getFilepath(const FQName& fqName, Location location,
132                                   const std::string& fileName, std::string* path) const {
133     status_t err;
134     std::string packagePath;
135     std::string packageRootPath;
136 
137     switch (location) {
138         case Location::DIRECT: { /* nothing */
139             *path = mOutputPath + fileName;
140         } break;
141         case Location::PACKAGE_ROOT: {
142             err = getPackagePath(fqName, false /* relative */, false /* sanitized */, &packagePath);
143             if (err != OK) return err;
144 
145             *path = mOutputPath + packagePath + fileName;
146         } break;
147         case Location::GEN_OUTPUT: {
148             err = convertPackageRootToPath(fqName, &packageRootPath);
149             if (err != OK) return err;
150             err = getPackagePath(fqName, true /* relative */, false /* sanitized */, &packagePath);
151             if (err != OK) return err;
152 
153             *path = mOutputPath + packageRootPath + packagePath + fileName;
154         } break;
155         case Location::GEN_SANITIZED: {
156             err = convertPackageRootToPath(fqName, &packageRootPath);
157             if (err != OK) return err;
158             err = getPackagePath(fqName, true /* relative */, true /* sanitized */, &packagePath);
159             if (err != OK) return err;
160 
161             *path = mOutputPath + packageRootPath + packagePath + fileName;
162         } break;
163         default: { CHECK(false) << "Invalid location: " << static_cast<size_t>(location); }
164     }
165 
166     return OK;
167 }
168 
onFileAccess(const std::string & path,const std::string & mode) const169 void Coordinator::onFileAccess(const std::string& path, const std::string& mode) const {
170     if (mode == "r") {
171         // This is a global list. It's not cleared when a second fqname is processed for
172         // two reasons:
173         // 1). If there is a bug in hidl-gen, the dependencies on the first project from
174         //     the second would be required to recover correctly when the bug is fixed.
175         // 2). This option is never used in Android builds.
176         mReadFiles.insert(StringHelper::LTrim(path, mRootPath));
177     }
178 
179     if (!mVerbose) {
180         return;
181     }
182 
183     fprintf(stderr,
184             "VERBOSE: file access %s %s\n", path.c_str(), mode.c_str());
185 }
186 
writeDepFile(const std::string & forFile) const187 status_t Coordinator::writeDepFile(const std::string& forFile) const {
188     // No dep file requested
189     if (mDepFile.empty()) return OK;
190 
191     onFileAccess(mDepFile, "w");
192 
193     FILE* file = fopen(mDepFile.c_str(), "w");
194     if (file == nullptr) {
195         fprintf(stderr, "ERROR: could not open dep file at %s.\n", mDepFile.c_str());
196         return UNKNOWN_ERROR;
197     }
198 
199     Formatter out(file, 2 /* spacesPerIndent */);
200     out << StringHelper::LTrim(forFile, mOutputPath) << ": \\\n";
201     out.indent([&] {
202         for (const std::string& file : mReadFiles) {
203             out << StringHelper::LTrim(file, mRootPath) << " \\\n";
204         }
205     });
206     return OK;
207 }
208 
parse(const FQName & fqName,std::set<AST * > * parsedASTs,Enforce enforcement) const209 AST* Coordinator::parse(const FQName& fqName, std::set<AST*>* parsedASTs,
210                         Enforce enforcement) const {
211     AST* ret;
212     status_t err = parseOptional(fqName, &ret, parsedASTs, enforcement);
213     if (err != OK) CHECK(ret == nullptr);  // internal consistency
214 
215     // only in a handful of places do we want to distinguish between
216     // a missing file and a bad AST. Everywhere else, we just want to
217     // throw an error if we expect an AST to be present but it is not.
218     return ret;
219 }
220 
parseOptional(const FQName & fqName,AST ** ast,std::set<AST * > * parsedASTs,Enforce enforcement) const221 status_t Coordinator::parseOptional(const FQName& fqName, AST** ast, std::set<AST*>* parsedASTs,
222                                     Enforce enforcement) const {
223     CHECK(fqName.isFullyQualified());
224 
225     auto it = mCache.find(fqName);
226     if (it != mCache.end()) {
227         *ast = (*it).second;
228 
229         if (*ast != nullptr && parsedASTs != nullptr) {
230             parsedASTs->insert(*ast);
231         }
232 
233         if (*ast == nullptr) {
234             // circular import OR that AST has errors in it
235             return UNKNOWN_ERROR;
236         }
237 
238         return OK;
239     }
240 
241     // Add this to the cache immediately, so we can discover circular imports.
242     mCache[fqName] = nullptr;
243 
244     AST *typesAST = nullptr;
245 
246     if (fqName.name() != "types") {
247         // Any interface file implicitly imports its package's types.hal.
248         FQName typesName = fqName.getTypesForPackage();
249         // Do not enforce on imports. Do not add imports' imports to this AST.
250         status_t err = parseOptional(typesName, &typesAST, nullptr, Enforce::NONE);
251         if (err != OK) return err;
252 
253         // fall through.
254     }
255 
256     std::string packagePath;
257     status_t err =
258         getPackagePath(fqName, false /* relative */, false /* sanitized */, &packagePath);
259     if (err != OK) return err;
260 
261     const std::string path = makeAbsolute(packagePath + fqName.name() + ".hal");
262 
263     *ast = new AST(this, &Hash::getHash(path));
264 
265     if (typesAST != nullptr) {
266         // If types.hal for this AST's package existed, make it's defined
267         // types available to the (about to be parsed) AST right away.
268         (*ast)->addImportedAST(typesAST);
269     }
270 
271     std::unique_ptr<FILE, std::function<void(FILE*)>> file(fopen(path.c_str(), "rb"), fclose);
272 
273     if (file == nullptr) {
274         mCache.erase(fqName);  // nullptr in cache is used to find circular imports
275         delete *ast;
276         *ast = nullptr;
277         return OK;  // File does not exist, nullptr AST* == file doesn't exist.
278     }
279 
280     onFileAccess(path, "r");
281 
282     // parse file takes ownership of file
283     if (parseFile(*ast, std::move(file)) != OK || (*ast)->postParse() != OK) {
284         delete *ast;
285         *ast = nullptr;
286         return UNKNOWN_ERROR;
287     }
288 
289     if ((*ast)->package().package() != fqName.package() ||
290         (*ast)->package().version() != fqName.version()) {
291         fprintf(stderr,
292                 "ERROR: File at '%s' does not match expected package and/or "
293                 "version.\n",
294                 path.c_str());
295 
296         err = UNKNOWN_ERROR;
297     } else {
298         if ((*ast)->isInterface()) {
299             if (fqName.name() == "types") {
300                 fprintf(stderr,
301                         "ERROR: File at '%s' declares an interface '%s' "
302                         "instead of the expected types common to the package.\n",
303                         path.c_str(), (*ast)->getInterface()->localName().c_str());
304 
305                 err = UNKNOWN_ERROR;
306             } else if ((*ast)->getInterface()->localName() != fqName.name()) {
307                 fprintf(stderr,
308                         "ERROR: File at '%s' does not declare interface type "
309                         "'%s'.\n",
310                         path.c_str(),
311                         fqName.name().c_str());
312 
313                 err = UNKNOWN_ERROR;
314             }
315         } else if (fqName.name() != "types") {
316             fprintf(stderr,
317                     "ERROR: File at '%s' declares types rather than the "
318                     "expected interface type '%s'.\n",
319                     path.c_str(),
320                     fqName.name().c_str());
321 
322             err = UNKNOWN_ERROR;
323         } else if ((*ast)->definesInterfaces()) {
324             fprintf(stderr,
325                     "ERROR: types.hal file at '%s' declares at least one "
326                     "interface type.\n",
327                     path.c_str());
328 
329             err = UNKNOWN_ERROR;
330         }
331     }
332 
333     if (err != OK) {
334         delete *ast;
335         *ast = nullptr;
336         return err;
337     }
338 
339     if (parsedASTs != nullptr) {
340         parsedASTs->insert(*ast);
341     }
342 
343     // put it into the cache now, so that enforceRestrictionsOnPackage can
344     // parse fqName.
345     mCache[fqName] = *ast;
346 
347     // For each .hal file that hidl-gen parses, the whole package will be checked.
348     err = enforceRestrictionsOnPackage(fqName, enforcement);
349     if (err != OK) {
350         mCache[fqName] = nullptr;
351         delete *ast;
352         *ast = nullptr;
353         return err;
354     }
355 
356     return OK;
357 }
358 
findPackageRoot(const FQName & fqName) const359 const Coordinator::PackageRoot* Coordinator::findPackageRoot(const FQName& fqName) const {
360     CHECK(!fqName.package().empty());
361 
362     // Find the right package prefix and path for this FQName.  For
363     // example, if FQName is "android.hardware.nfc@1.0::INfc", and the
364     // prefix:root is set to [ "android.hardware:hardware/interfaces",
365     // "vendor.qcom.hardware:vendor/qcom"], then we will identify the
366     // prefix "android.hardware" and the package root
367     // "hardware/interfaces".
368 
369     auto ret = mPackageRoots.end();
370     for (auto it = mPackageRoots.begin(); it != mPackageRoots.end(); it++) {
371         if (!fqName.inPackage(it->root.package())) {
372             continue;
373         }
374 
375         if (ret != mPackageRoots.end()) {
376             std::cerr << "ERROR: Multiple package roots found for " << fqName.string() << " ("
377                       << it->root.package() << " and " << ret->root.package() << ")\n";
378             return nullptr;
379         }
380 
381         ret = it;
382     }
383 
384     if (ret == mPackageRoots.end()) {
385         std::cerr << "ERROR: Package root not specified for " << fqName.string() << "\n";
386         return nullptr;
387     }
388 
389     return &(*ret);
390 }
391 
makeAbsolute(const std::string & path) const392 std::string Coordinator::makeAbsolute(const std::string& path) const {
393     if (StringHelper::StartsWith(path, "/") || mRootPath.empty()) {
394         return path;
395     }
396 
397     return mRootPath + path;
398 }
399 
getPackageRoot(const FQName & fqName,std::string * root) const400 status_t Coordinator::getPackageRoot(const FQName& fqName, std::string* root) const {
401     const PackageRoot* packageRoot = findPackageRoot(fqName);
402     if (root == nullptr) {
403         return UNKNOWN_ERROR;
404     }
405     *root = packageRoot->root.package();
406     return OK;
407 }
408 
getPackageRootPath(const FQName & fqName,std::string * path) const409 status_t Coordinator::getPackageRootPath(const FQName& fqName, std::string* path) const {
410     const PackageRoot* packageRoot = findPackageRoot(fqName);
411     if (packageRoot == nullptr) {
412         return UNKNOWN_ERROR;
413     }
414     *path = packageRoot->path;
415     return OK;
416 }
417 
getPackagePath(const FQName & fqName,bool relative,bool sanitized,std::string * path) const418 status_t Coordinator::getPackagePath(const FQName& fqName, bool relative, bool sanitized,
419                                      std::string* path) const {
420     const PackageRoot* packageRoot = findPackageRoot(fqName);
421     if (packageRoot == nullptr) return UNKNOWN_ERROR;
422 
423     // Given FQName of "android.hardware.nfc.test@1.0::IFoo" and a prefix
424     // "android.hardware", the suffix is "nfc.test".
425     std::string suffix = StringHelper::LTrim(fqName.package(), packageRoot->root.package());
426     suffix = StringHelper::LTrim(suffix, ".");
427 
428     std::vector<std::string> suffixComponents;
429     StringHelper::SplitString(suffix, '.', &suffixComponents);
430 
431     std::vector<std::string> components;
432     if (!relative) {
433         components.push_back(StringHelper::RTrimAll(packageRoot->path, "/"));
434     }
435     components.insert(components.end(), suffixComponents.begin(), suffixComponents.end());
436     components.push_back(sanitized ? fqName.sanitizedVersion() : fqName.version());
437 
438     *path = StringHelper::JoinStrings(components, "/") + "/";
439     return OK;
440 }
441 
getPackageInterfaceFiles(const FQName & package,std::vector<std::string> * fileNames) const442 status_t Coordinator::getPackageInterfaceFiles(
443         const FQName &package,
444         std::vector<std::string> *fileNames) const {
445     if (fileNames) fileNames->clear();
446 
447     std::string packagePath;
448     status_t err =
449         getPackagePath(package, false /* relative */, false /* sanitized */, &packagePath);
450     if (err != OK) return err;
451 
452     const std::string path = makeAbsolute(packagePath);
453     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
454 
455     if (dir == nullptr) {
456         fprintf(stderr, "ERROR: Could not open package path %s for package %s:\n%s\n",
457                 packagePath.c_str(), package.string().c_str(), path.c_str());
458         return -errno;
459     }
460 
461     if (fileNames == nullptr) {
462         return OK;
463     }
464 
465     struct dirent *ent;
466     while ((ent = readdir(dir.get())) != nullptr) {
467         // filesystems may not support d_type and return DT_UNKNOWN
468         if (ent->d_type == DT_UNKNOWN) {
469             struct stat sb;
470             const auto filename = packagePath + std::string(ent->d_name);
471             if (stat(filename.c_str(), &sb) == -1) {
472                 fprintf(stderr, "ERROR: Could not stat %s\n", filename.c_str());
473                 return -errno;
474             }
475             if ((sb.st_mode & S_IFMT) != S_IFREG) {
476                 continue;
477             }
478         } else if (ent->d_type != DT_REG) {
479              continue;
480         }
481 
482         const auto suffix = ".hal";
483         const auto suffix_len = std::strlen(suffix);
484         const auto d_namelen = strlen(ent->d_name);
485 
486         if (d_namelen < suffix_len
487                 || strcmp(ent->d_name + d_namelen - suffix_len, suffix)) {
488             continue;
489         }
490 
491         fileNames->push_back(std::string(ent->d_name, d_namelen - suffix_len));
492     }
493 
494     std::sort(fileNames->begin(), fileNames->end(),
495               [](const std::string& lhs, const std::string& rhs) -> bool {
496                   if (lhs == "types") {
497                       return true;
498                   }
499                   if (rhs == "types") {
500                       return false;
501                   }
502                   return lhs < rhs;
503               });
504 
505     return OK;
506 }
507 
appendPackageInterfacesToVector(const FQName & package,std::vector<FQName> * packageInterfaces) const508 status_t Coordinator::appendPackageInterfacesToVector(
509         const FQName &package,
510         std::vector<FQName> *packageInterfaces) const {
511     packageInterfaces->clear();
512 
513     std::vector<std::string> fileNames;
514     status_t err = getPackageInterfaceFiles(package, &fileNames);
515 
516     if (err != OK) {
517         return err;
518     }
519 
520     for (const auto &fileName : fileNames) {
521         FQName subFQName(package.package(), package.version(), fileName);
522         packageInterfaces->push_back(subFQName);
523     }
524 
525     return OK;
526 }
527 
convertPackageRootToPath(const FQName & fqName,std::string * path) const528 status_t Coordinator::convertPackageRootToPath(const FQName& fqName, std::string* path) const {
529     std::string packageRoot;
530     status_t err = getPackageRoot(fqName, &packageRoot);
531     if (err != OK) return err;
532 
533     if (*(packageRoot.end()--) != '.') {
534         packageRoot += '.';
535     }
536 
537     std::replace(packageRoot.begin(), packageRoot.end(), '.', '/');
538 
539     *path = packageRoot;  // now converted to a path
540     return OK;
541 }
542 
isTypesOnlyPackage(const FQName & package,bool * result) const543 status_t Coordinator::isTypesOnlyPackage(const FQName& package, bool* result) const {
544     std::vector<FQName> packageInterfaces;
545 
546     status_t err = appendPackageInterfacesToVector(package, &packageInterfaces);
547 
548     if (err != OK) {
549         *result = false;
550         return err;
551     }
552 
553     *result = packageInterfaces.size() == 1 && packageInterfaces[0].name() == "types";
554     return OK;
555 }
556 
addUnreferencedTypes(const std::vector<FQName> & packageInterfaces,std::set<FQName> * unreferencedDefinitions,std::set<FQName> * unreferencedImports) const557 status_t Coordinator::addUnreferencedTypes(const std::vector<FQName>& packageInterfaces,
558                                            std::set<FQName>* unreferencedDefinitions,
559                                            std::set<FQName>* unreferencedImports) const {
560     CHECK(unreferencedDefinitions != nullptr);
561     CHECK(unreferencedImports != nullptr);
562 
563     std::set<FQName> packageDefinedTypes;
564     std::set<FQName> packageReferencedTypes;
565     std::set<FQName> packageImportedTypes;
566     std::set<FQName> typesDefinedTypes;  // only types.hal types
567 
568     for (const auto& fqName : packageInterfaces) {
569         AST* ast = parse(fqName);
570         if (!ast) {
571             std::cerr << "ERROR: Could not parse " << fqName.string() << ". Aborting." << std::endl;
572 
573             return UNKNOWN_ERROR;
574         }
575 
576         ast->addDefinedTypes(&packageDefinedTypes);
577         ast->addReferencedTypes(&packageReferencedTypes);
578         ast->getAllImportedNamesGranular(&packageImportedTypes);
579 
580         if (fqName.name() == "types") {
581             ast->addDefinedTypes(&typesDefinedTypes);
582         }
583     }
584 
585 #if 0
586     for (const auto &fqName : packageDefinedTypes) {
587         std::cout << "VERBOSE: DEFINED " << fqName.string() << std::endl;
588     }
589 
590     for (const auto &fqName : packageImportedTypes) {
591         std::cout << "VERBOSE: IMPORTED " << fqName.string() << std::endl;
592     }
593 
594     for (const auto &fqName : packageReferencedTypes) {
595         std::cout << "VERBOSE: REFERENCED " << fqName.string() << std::endl;
596     }
597 
598     for (const auto &fqName : typesDefinedTypes) {
599         std::cout << "VERBOSE: DEFINED in types.hal " << fqName.string() << std::endl;
600     }
601 #endif
602 
603     for (const auto& fqName : packageReferencedTypes) {
604         packageDefinedTypes.erase(fqName);
605         packageImportedTypes.erase(fqName);
606     }
607 
608     // A package implicitly imports its own types.hal, only track them in one set.
609     for (const auto& fqName : typesDefinedTypes) {
610         packageImportedTypes.erase(fqName);
611     }
612 
613     // defined but not referenced
614     unreferencedDefinitions->insert(packageDefinedTypes.begin(), packageDefinedTypes.end());
615     // imported but not referenced
616     unreferencedImports->insert(packageImportedTypes.begin(), packageImportedTypes.end());
617     return OK;
618 }
619 
enforceRestrictionsOnPackage(const FQName & fqName,Enforce enforcement) const620 status_t Coordinator::enforceRestrictionsOnPackage(const FQName& fqName,
621                                                    Enforce enforcement) const {
622     CHECK(enforcement == Enforce::FULL || enforcement == Enforce::NO_HASH ||
623           enforcement == Enforce::NONE);
624 
625     // need fqName to be something like android.hardware.foo@1.0.
626     // name and valueName is ignored.
627     if (fqName.package().empty() || fqName.version().empty()) {
628         std::cerr << "ERROR: Cannot enforce restrictions on package " << fqName.string()
629                   << ": package or version is missing." << std::endl;
630         return BAD_VALUE;
631     }
632 
633     if (enforcement == Enforce::NONE) {
634         return OK;
635     }
636 
637     FQName package = fqName.getPackageAndVersion();
638     // look up cache.
639     if (mPackagesEnforced.find(package) != mPackagesEnforced.end()) {
640         return OK;
641     }
642 
643     // enforce all rules.
644     status_t err;
645 
646     err = enforceMinorVersionUprevs(package, enforcement);
647     if (err != OK) {
648         return err;
649     }
650 
651     if (enforcement != Enforce::NO_HASH) {
652         err = enforceHashes(package);
653         if (err != OK) {
654             return err;
655         }
656     }
657 
658     // cache it so that it won't need to be enforced again.
659     mPackagesEnforced.insert(package);
660     return OK;
661 }
662 
enforceMinorVersionUprevs(const FQName & currentPackage,Enforce enforcement) const663 status_t Coordinator::enforceMinorVersionUprevs(const FQName& currentPackage,
664                                                 Enforce enforcement) const {
665     if(!currentPackage.hasVersion()) {
666         std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string()
667                   << ": missing version." << std::endl;
668         return UNKNOWN_ERROR;
669     }
670 
671     if (currentPackage.getPackageMinorVersion() == 0) {
672         return OK; // ignore for @x.0
673     }
674 
675     bool hasPrevPackage = false;
676     FQName prevPackage = currentPackage;
677     while (prevPackage.getPackageMinorVersion() > 0) {
678         prevPackage = prevPackage.downRev();
679 
680         std::string prevPackagePath;
681         status_t err = getPackagePath(prevPackage, false /* relative */, false /* sanitized */,
682                                       &prevPackagePath);
683         if (err != OK) return err;
684 
685         if (existdir(makeAbsolute(prevPackagePath).c_str())) {
686             hasPrevPackage = true;
687             break;
688         }
689     }
690     if (!hasPrevPackage) {
691         // no @x.z, where z < y, exist.
692         return OK;
693     }
694 
695     if (prevPackage != currentPackage.downRev()) {
696         std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string()
697                   << ": Found package " << prevPackage.string() << " but missing "
698                   << currentPackage.downRev().string() << "; you cannot skip a minor version."
699                   << std::endl;
700         return UNKNOWN_ERROR;
701     }
702 
703     bool prevIsTypesOnly;
704     status_t err = isTypesOnlyPackage(prevPackage, &prevIsTypesOnly);
705     if (err != OK) return err;
706 
707     if (prevIsTypesOnly) {
708         // A types only package can be extended in any way.
709         return OK;
710     }
711 
712     std::vector<FQName> packageInterfaces;
713     err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
714     if (err != OK) {
715         return err;
716     }
717 
718     bool extendedInterface = false;
719     for (const FQName &currentFQName : packageInterfaces) {
720         if (currentFQName.name() == "types") {
721             continue; // ignore types.hal
722         }
723 
724         const Interface *iface = nullptr;
725         AST* currentAST = parse(currentFQName, nullptr /* parsedASTs */, enforcement);
726         if (currentAST != nullptr) {
727             iface = currentAST->getInterface();
728         }
729         if (iface == nullptr) {
730             if (currentAST == nullptr) {
731                 std::cerr << "WARNING: Skipping " << currentFQName.string()
732                           << " because it could not be found or parsed"
733                           << " or " << currentPackage.string() << " doesn't pass all requirements."
734                           << std::endl;
735             } else {
736                 std::cerr << "WARNING: Skipping " << currentFQName.string()
737                           << " because the file might contain more than one interface."
738                           << std::endl;
739             }
740             continue;
741         }
742 
743         if (iface->superType() == nullptr) {
744             CHECK(iface->isIBase());
745             continue;
746         }
747 
748         // Assume that currentFQName == android.hardware.foo@2.2::IFoo.
749         FQName lastFQName(prevPackage.package(), prevPackage.version(),
750                 currentFQName.name());
751         AST *lastAST = parse(lastFQName);
752 
753         for (; lastFQName.getPackageMinorVersion() > 0 &&
754                (lastAST == nullptr || lastAST->getInterface() == nullptr)
755              ; lastFQName = lastFQName.downRev(), lastAST = parse(lastFQName)) {
756             // nothing
757         }
758 
759         // Then lastFQName == android.hardware.foo@2.1::IFoo or
760         //      lastFQName == android.hardware.foo@2.0::IFoo if 2.1 doesn't exist.
761 
762         bool lastFQNameExists = lastAST != nullptr && lastAST->getInterface() != nullptr;
763 
764         if (!lastFQNameExists) {
765             continue;
766         }
767 
768         if (iface->superType()->fqName() != lastFQName) {
769             std::cerr << "ERROR: Cannot enforce minor version uprevs for "
770                       << currentPackage.string() << ": " << iface->fqName().string() << " extends "
771                       << iface->superType()->fqName().string()
772                       << ", which is not allowed. It must extend " << lastFQName.string()
773                       << std::endl;
774             return UNKNOWN_ERROR;
775         }
776 
777         // at least one interface must extend the previous version
778         // @2.0::IFoo does not work. It must be @2.1::IFoo for at least one interface.
779         if (lastFQName.getPackageAndVersion() == prevPackage.getPackageAndVersion()) {
780             extendedInterface = true;
781         }
782 
783         if (mVerbose) {
784             std::cout << "VERBOSE: EnforceMinorVersionUprevs: " << currentFQName.string()
785                       << " passes." << std::endl;
786         }
787     }
788 
789     if (!extendedInterface) {
790         // No interface extends the interface with the same name in @x.(y-1).
791         std::cerr << "ERROR: " << currentPackage.string()
792                   << " doesn't pass minor version uprev requirement. "
793                   << "Requires at least one interface to extend an interface with the same name "
794                   << "from " << prevPackage.string() << "." << std::endl;
795         return UNKNOWN_ERROR;
796     }
797 
798     return OK;
799 }
800 
checkHash(const FQName & fqName) const801 Coordinator::HashStatus Coordinator::checkHash(const FQName& fqName) const {
802     AST* ast = parse(fqName);
803     if (ast == nullptr) return HashStatus::ERROR;
804 
805     std::string rootPath;
806     status_t err = getPackageRootPath(fqName, &rootPath);
807     if (err != OK) return HashStatus::ERROR;
808 
809     std::string hashPath = makeAbsolute(rootPath) + "/current.txt";
810     std::string error;
811     bool fileExists;
812     std::vector<std::string> frozen =
813         Hash::lookupHash(hashPath, fqName.string(), &error, &fileExists);
814     if (fileExists) onFileAccess(hashPath, "r");
815 
816     if (error.size() > 0) {
817         std::cerr << "ERROR: " << error << std::endl;
818         return HashStatus::ERROR;
819     }
820 
821     // hash not defined, interface not frozen
822     if (frozen.size() == 0) {
823         // This ensures that it can be detected.
824         Hash::clearHash(ast->getFilename());
825 
826         return HashStatus::UNFROZEN;
827     }
828 
829     std::string currentHash = ast->getFileHash()->hexString();
830 
831     if (std::find(frozen.begin(), frozen.end(), currentHash) == frozen.end()) {
832         std::cerr << "ERROR: " << fqName.string() << " has hash " << currentHash
833                   << " which does not match hash on record. This interface has "
834                   << "been frozen. Do not change it!" << std::endl;
835         return HashStatus::CHANGED;
836     }
837 
838     return HashStatus::FROZEN;
839 }
840 
getUnfrozenDependencies(const FQName & fqName,std::set<FQName> * result) const841 status_t Coordinator::getUnfrozenDependencies(const FQName& fqName,
842                                               std::set<FQName>* result) const {
843     CHECK(result != nullptr);
844 
845     AST* ast = parse(fqName);
846     if (ast == nullptr) return UNKNOWN_ERROR;
847 
848     std::set<FQName> imported;
849     ast->getImportedPackages(&imported);
850 
851     // no circular dependency is already guaranteed by parsing
852     // indirect dependencies will be checked when the imported interface frozen checks are done
853     for (const FQName& importedPackage : imported) {
854         std::vector<FQName> packageInterfaces;
855         status_t err = appendPackageInterfacesToVector(importedPackage, &packageInterfaces);
856         if (err != OK) {
857             return err;
858         }
859 
860         for (const FQName& importedName : packageInterfaces) {
861             HashStatus status = checkHash(importedName);
862             switch (status) {
863                 case HashStatus::CHANGED:
864                 case HashStatus::ERROR:
865                     return UNKNOWN_ERROR;
866                 case HashStatus::FROZEN:
867                     continue;
868                 case HashStatus::UNFROZEN:
869                     result->insert(importedName);
870                     continue;
871                 default:
872                     LOG(FATAL) << static_cast<uint64_t>(status);
873             }
874         }
875     }
876 
877     return OK;
878 }
879 
enforceHashes(const FQName & currentPackage) const880 status_t Coordinator::enforceHashes(const FQName& currentPackage) const {
881     std::vector<FQName> packageInterfaces;
882     status_t err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
883     if (err != OK) {
884         return err;
885     }
886 
887     for (const FQName& currentFQName : packageInterfaces) {
888         HashStatus status = checkHash(currentFQName);
889         switch (status) {
890             case HashStatus::CHANGED:
891             case HashStatus::ERROR:
892                 return UNKNOWN_ERROR;
893             case HashStatus::FROZEN: {
894                 std::set<FQName> unfrozenDependencies;
895                 err = getUnfrozenDependencies(currentFQName, &unfrozenDependencies);
896                 if (err != OK) return err;
897 
898                 if (!unfrozenDependencies.empty()) {
899                     std::cerr << "ERROR: Frozen interface " << currentFQName.string()
900                               << " cannot depend on unfrozen thing(s):" << std::endl;
901                     for (const FQName& name : unfrozenDependencies) {
902                         std::cerr << " (unfrozen) " << name.string() << std::endl;
903                     }
904                     return UNKNOWN_ERROR;
905                 }
906             }
907                 continue;
908             case HashStatus::UNFROZEN:
909                 continue;
910             default:
911                 LOG(FATAL) << static_cast<uint64_t>(status);
912         }
913     }
914 
915     return err;
916 }
917 
MakeParentHierarchy(const std::string & path)918 bool Coordinator::MakeParentHierarchy(const std::string &path) {
919     static const mode_t kMode = 0755;
920 
921     size_t start = 1;  // Ignore leading '/'
922     size_t slashPos;
923     while ((slashPos = path.find('/', start)) != std::string::npos) {
924         std::string partial = path.substr(0, slashPos);
925 
926         struct stat st;
927         if (stat(partial.c_str(), &st) < 0) {
928             if (errno != ENOENT) {
929                 return false;
930             }
931 
932             int res = mkdir(partial.c_str(), kMode);
933             if (res < 0) {
934                 return false;
935             }
936         } else if (!S_ISDIR(st.st_mode)) {
937             return false;
938         }
939 
940         start = slashPos + 1;
941     }
942 
943     return true;
944 }
945 
946 }  // namespace android
947 
948