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