blob: 72c8557c4b6dcd8c8fc78aaa6424dcaf88fe56df [file] [log] [blame]
Andreas Huber1aec3972016-08-26 09:26:32 -07001/*
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
Andreas Huber5345ec22016-07-29 13:33:27 -070017#include "Coordinator.h"
18
Iliyan Malcheva72e0d22016-09-09 11:03:08 -070019#include <dirent.h>
Andreas Huberd2943e12016-08-05 11:59:31 -070020#include <sys/stat.h>
Andreas Huber5345ec22016-07-29 13:33:27 -070021
Yifan Hong78b38d12017-02-13 18:14:46 +000022#include <algorithm>
23#include <iterator>
24
25#include <android-base/logging.h>
26#include <hidl-util/StringHelper.h>
27
28#include "AST.h"
29#include "Interface.h"
30
Andreas Huber0d0f9a22016-08-17 10:26:11 -070031extern android::status_t parseFile(android::AST *ast);
Andreas Huber5345ec22016-07-29 13:33:27 -070032
Yifan Hong78b38d12017-02-13 18:14:46 +000033static bool existdir(const char *name) {
34 DIR *dir = opendir(name);
35 if (dir == NULL) {
36 return false;
37 }
38 closedir(dir);
39 return true;
40}
41
Andreas Huber5345ec22016-07-29 13:33:27 -070042namespace android {
43
Andreas Huberdca261f2016-08-04 13:47:51 -070044Coordinator::Coordinator(
45 const std::vector<std::string> &packageRootPaths,
46 const std::vector<std::string> &packageRoots)
47 : mPackageRootPaths(packageRootPaths),
48 mPackageRoots(packageRoots) {
49 // empty
Andreas Huberdc981332016-07-29 15:46:54 -070050}
51
Andreas Huberdca261f2016-08-04 13:47:51 -070052Coordinator::~Coordinator() {
53 // empty
54}
Andreas Huber5345ec22016-07-29 13:33:27 -070055
Yifan Hongf619fc72017-04-07 15:40:06 -070056AST *Coordinator::parse(const FQName &fqName, std::set<AST *> *parsedASTs, bool enforce) {
Andreas Huber68f24592016-07-29 14:53:48 -070057 CHECK(fqName.isFullyQualified());
58
Steven Morelandd537ab02016-09-12 10:32:01 -070059 auto it = mCache.find(fqName);
60 if (it != mCache.end()) {
61 AST *ast = (*it).second;
Andreas Huber5345ec22016-07-29 13:33:27 -070062
Andreas Huber39fa7182016-08-19 14:27:33 -070063 if (ast != nullptr && parsedASTs != nullptr) {
64 parsedASTs->insert(ast);
65 }
66
Andreas Huber5345ec22016-07-29 13:33:27 -070067 return ast;
68 }
69
Andreas Huber68f24592016-07-29 14:53:48 -070070 // Add this to the cache immediately, so we can discover circular imports.
Steven Morelandd537ab02016-09-12 10:32:01 -070071 mCache[fqName] = nullptr;
Andreas Huber39fa7182016-08-19 14:27:33 -070072
73 AST *typesAST = nullptr;
Andreas Huber68f24592016-07-29 14:53:48 -070074
Andreas Huber68f24592016-07-29 14:53:48 -070075 if (fqName.name() != "types") {
76 // Any interface file implicitly imports its package's types.hal.
Yifan Hongfece6ec2017-01-12 17:04:04 -080077 FQName typesName = fqName.getTypesForPackage();
Yifan Hongf619fc72017-04-07 15:40:06 -070078 // Do not enforce on imports.
79 typesAST = parse(typesName, parsedASTs, false /* enforce */);
Andreas Huber68f24592016-07-29 14:53:48 -070080
81 // fall through.
82 }
83
Andreas Huberdca261f2016-08-04 13:47:51 -070084 std::string path = getPackagePath(fqName);
85
Andreas Huber68f24592016-07-29 14:53:48 -070086 path.append(fqName.name());
87 path.append(".hal");
Andreas Huber5345ec22016-07-29 13:33:27 -070088
Andreas Huber0d0f9a22016-08-17 10:26:11 -070089 AST *ast = new AST(this, path);
Andreas Huber39fa7182016-08-19 14:27:33 -070090
91 if (typesAST != NULL) {
92 // If types.hal for this AST's package existed, make it's defined
93 // types available to the (about to be parsed) AST right away.
94 ast->addImportedAST(typesAST);
95 }
96
Andreas Huber0d0f9a22016-08-17 10:26:11 -070097 status_t err = parseFile(ast);
Andreas Huber5345ec22016-07-29 13:33:27 -070098
Andreas Huber68f24592016-07-29 14:53:48 -070099 if (err != OK) {
Andreas Huber6e584b72016-07-29 16:14:07 -0700100 // LOG(ERROR) << "parsing '" << path << "' FAILED.";
101
Andreas Huber68f24592016-07-29 14:53:48 -0700102 delete ast;
Andreas Huber39fa7182016-08-19 14:27:33 -0700103 ast = nullptr;
Andreas Huber68f24592016-07-29 14:53:48 -0700104
Andreas Huber39fa7182016-08-19 14:27:33 -0700105 return nullptr;
Andreas Huber68f24592016-07-29 14:53:48 -0700106 }
107
Andreas Hubera2723d22016-07-29 15:36:07 -0700108 if (ast->package().package() != fqName.package()
109 || ast->package().version() != fqName.version()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700110 fprintf(stderr,
111 "ERROR: File at '%s' does not match expected package and/or "
Andreas Huber0d0f9a22016-08-17 10:26:11 -0700112 "version.\n",
Andreas Huber70a59e12016-08-16 12:57:01 -0700113 path.c_str());
Andreas Hubera2723d22016-07-29 15:36:07 -0700114
115 err = UNKNOWN_ERROR;
116 } else {
117 std::string ifaceName;
118 if (ast->isInterface(&ifaceName)) {
119 if (fqName.name() == "types") {
Andreas Huber70a59e12016-08-16 12:57:01 -0700120 fprintf(stderr,
121 "ERROR: File at '%s' declares an interface '%s' "
Andreas Huber0d0f9a22016-08-17 10:26:11 -0700122 "instead of the expected types common to the package.\n",
Andreas Huber70a59e12016-08-16 12:57:01 -0700123 path.c_str(),
124 ifaceName.c_str());
Andreas Hubera2723d22016-07-29 15:36:07 -0700125
126 err = UNKNOWN_ERROR;
127 } else if (ifaceName != fqName.name()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700128 fprintf(stderr,
129 "ERROR: File at '%s' does not declare interface type "
Andreas Huber0d0f9a22016-08-17 10:26:11 -0700130 "'%s'.\n",
Andreas Huber70a59e12016-08-16 12:57:01 -0700131 path.c_str(),
132 fqName.name().c_str());
Andreas Hubera2723d22016-07-29 15:36:07 -0700133
134 err = UNKNOWN_ERROR;
135 }
136 } else if (fqName.name() != "types") {
Andreas Huber70a59e12016-08-16 12:57:01 -0700137 fprintf(stderr,
138 "ERROR: File at '%s' declares types rather than the "
Andreas Huber0d0f9a22016-08-17 10:26:11 -0700139 "expected interface type '%s'.\n",
Andreas Huber70a59e12016-08-16 12:57:01 -0700140 path.c_str(),
141 fqName.name().c_str());
Andreas Hubera2723d22016-07-29 15:36:07 -0700142
143 err = UNKNOWN_ERROR;
Andreas Huber7c5ddfb2016-09-29 13:45:22 -0700144 } else if (ast->containsInterfaces()) {
145 fprintf(stderr,
146 "ERROR: types.hal file at '%s' declares at least one "
147 "interface type.\n",
148 path.c_str());
149
150 err = UNKNOWN_ERROR;
Andreas Hubera2723d22016-07-29 15:36:07 -0700151 }
152 }
153
154 if (err != OK) {
155 delete ast;
Andreas Huber39fa7182016-08-19 14:27:33 -0700156 ast = nullptr;
Andreas Hubera2723d22016-07-29 15:36:07 -0700157
Andreas Huber39fa7182016-08-19 14:27:33 -0700158 return nullptr;
Andreas Hubera2723d22016-07-29 15:36:07 -0700159 }
160
Andreas Huber39fa7182016-08-19 14:27:33 -0700161 if (parsedASTs != nullptr) { parsedASTs->insert(ast); }
162
Yifan Hongfe07bff2017-02-15 14:55:48 -0800163 // put it into the cache now, so that enforceRestrictionsOnPackage can
164 // parse fqName.
Steven Morelandd537ab02016-09-12 10:32:01 -0700165 mCache[fqName] = ast;
Andreas Huber5345ec22016-07-29 13:33:27 -0700166
Yifan Hongf619fc72017-04-07 15:40:06 -0700167 if (enforce) {
168 // For each .hal file that hidl-gen parses, the whole package will be checked.
169 err = enforceRestrictionsOnPackage(fqName);
170 if (err != OK) {
171 mCache[fqName] = nullptr;
172 delete ast;
173 ast = nullptr;
174 return nullptr;
175 }
Yifan Hong78b38d12017-02-13 18:14:46 +0000176 }
177
Andreas Huber5345ec22016-07-29 13:33:27 -0700178 return ast;
179}
180
Andreas Huberdca261f2016-08-04 13:47:51 -0700181std::vector<std::string>::const_iterator
182Coordinator::findPackageRoot(const FQName &fqName) const {
Andreas Huber5345ec22016-07-29 13:33:27 -0700183 CHECK(!fqName.package().empty());
184 CHECK(!fqName.version().empty());
Andreas Huber5345ec22016-07-29 13:33:27 -0700185
Andreas Huberdca261f2016-08-04 13:47:51 -0700186 // Find the right package prefix and path for this FQName. For
187 // example, if FQName is "android.hardware.nfc@1.0::INfc", and the
188 // prefix:root is set to [ "android.hardware:hardware/interfaces",
189 // "vendor.qcom.hardware:vendor/qcom"], then we will identify the
190 // prefix "android.hardware" and the package root
191 // "hardware/interfaces".
192
Andreas Huberdca261f2016-08-04 13:47:51 -0700193 auto it = mPackageRoots.begin();
Steven Moreland62709d72017-01-18 10:14:51 -0800194 auto ret = mPackageRoots.end();
Andreas Huberdca261f2016-08-04 13:47:51 -0700195 for (; it != mPackageRoots.end(); it++) {
Steven Moreland62709d72017-01-18 10:14:51 -0800196 if (!fqName.inPackage(*it)) {
197 continue;
Andreas Huberdca261f2016-08-04 13:47:51 -0700198 }
Steven Moreland62709d72017-01-18 10:14:51 -0800199
200 CHECK(ret == mPackageRoots.end())
201 << "Multiple package roots found for " << fqName.string()
202 << " (" << *it << " and " << *ret << ")";
203
204 ret = it;
Andreas Huberdca261f2016-08-04 13:47:51 -0700205 }
Steven Moreland62709d72017-01-18 10:14:51 -0800206 CHECK(ret != mPackageRoots.end())
Andreas Huber401cd162016-08-26 10:40:30 -0700207 << "Unable to find package root for " << fqName.string();
Andreas Huberdca261f2016-08-04 13:47:51 -0700208
Steven Moreland62709d72017-01-18 10:14:51 -0800209 return ret;
Andreas Huberdca261f2016-08-04 13:47:51 -0700210}
211
212std::string Coordinator::getPackageRoot(const FQName &fqName) const {
213 auto it = findPackageRoot(fqName);
214 auto prefix = *it;
215 return prefix;
216}
217
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700218std::string Coordinator::getPackageRootPath(const FQName &fqName) const {
219 auto it = findPackageRoot(fqName);
220 auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
221 return root;
222}
223
Yifan Hongc8934042016-11-17 17:10:52 -0800224std::string Coordinator::getPackageRootOption(const FQName &fqName) const {
225 return getPackageRoot(fqName) + ":" + getPackageRootPath(fqName);
226}
227
Andreas Huberdca261f2016-08-04 13:47:51 -0700228std::string Coordinator::getPackagePath(
Yifan Hong97288ac2016-12-12 16:03:51 -0800229 const FQName &fqName, bool relative, bool sanitized) const {
Andreas Huberdca261f2016-08-04 13:47:51 -0700230
231 auto it = findPackageRoot(fqName);
232 auto prefix = *it;
233 auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
234
235 // Make sure the prefix ends on a '.' and the root path on a '/'
236 if ((*--prefix.end()) != '.') {
237 prefix += '.';
238 }
239
240 if ((*--root.end()) != '/') {
241 root += '/';
242 }
243
244 // Given FQName of "android.hardware.nfc@1.0::IFoo" and a prefix
245 // "android.hardware.", the suffix is "nfc@1.0::IFoo".
246 const std::string packageSuffix = fqName.package().substr(prefix.length());
Andreas Huber5345ec22016-07-29 13:33:27 -0700247
Andreas Huber881227d2016-08-02 14:20:21 -0700248 std::string packagePath;
249 if (!relative) {
Andreas Huberdca261f2016-08-04 13:47:51 -0700250 packagePath = root;
Andreas Huber881227d2016-08-02 14:20:21 -0700251 }
Andreas Huber5345ec22016-07-29 13:33:27 -0700252
253 size_t startPos = 0;
254 size_t dotPos;
255 while ((dotPos = packageSuffix.find('.', startPos)) != std::string::npos) {
256 packagePath.append(packageSuffix.substr(startPos, dotPos - startPos));
257 packagePath.append("/");
258
259 startPos = dotPos + 1;
260 }
261 CHECK_LT(startPos + 1, packageSuffix.length());
262 packagePath.append(packageSuffix.substr(startPos));
263 packagePath.append("/");
264
Yifan Hong97288ac2016-12-12 16:03:51 -0800265 packagePath.append(sanitized ? fqName.sanitizedVersion() : fqName.version());
Andreas Huber5345ec22016-07-29 13:33:27 -0700266 packagePath.append("/");
267
268 return packagePath;
269}
270
Andreas Huberd2943e12016-08-05 11:59:31 -0700271status_t Coordinator::getPackageInterfaceFiles(
272 const FQName &package,
273 std::vector<std::string> *fileNames) const {
274 fileNames->clear();
Andreas Huber6cb08cf2016-08-03 15:44:51 -0700275
Andreas Huberd2943e12016-08-05 11:59:31 -0700276 const std::string packagePath = getPackagePath(package);
277
278 DIR *dir = opendir(packagePath.c_str());
279
280 if (dir == NULL) {
Steven Moreland261370a2016-08-29 15:36:59 -0700281 LOG(ERROR) << "Could not open package path: " << packagePath;
Andreas Huberd2943e12016-08-05 11:59:31 -0700282 return -errno;
283 }
284
285 struct dirent *ent;
286 while ((ent = readdir(dir)) != NULL) {
287 if (ent->d_type != DT_REG) {
Andreas Huber6cb08cf2016-08-03 15:44:51 -0700288 continue;
289 }
290
Andreas Huberd2943e12016-08-05 11:59:31 -0700291 const auto suffix = ".hal";
292 const auto suffix_len = std::strlen(suffix);
293 const auto d_namelen = strlen(ent->d_name);
Andreas Hubere61e3f72016-08-03 10:22:03 -0700294
Andreas Huberd2943e12016-08-05 11:59:31 -0700295 if (d_namelen < suffix_len
296 || strcmp(ent->d_name + d_namelen - suffix_len, suffix)) {
297 continue;
Andreas Hubere61e3f72016-08-03 10:22:03 -0700298 }
Andreas Huberd2943e12016-08-05 11:59:31 -0700299
300 fileNames->push_back(std::string(ent->d_name, d_namelen - suffix_len));
301 }
302
303 closedir(dir);
304 dir = NULL;
305
Iliyan Malchev5e8bcfa2016-09-09 16:19:57 -0700306 std::sort(fileNames->begin(), fileNames->end(),
307 [](const std::string& lhs, const std::string& rhs) -> bool {
308 if (lhs == "types") {
309 return true;
310 }
311 if (rhs == "types") {
312 return false;
313 }
314 return lhs < rhs;
315 });
316
Andreas Huberd2943e12016-08-05 11:59:31 -0700317 return OK;
318}
319
Steven Morelandaa186832016-09-26 13:51:43 -0700320status_t Coordinator::appendPackageInterfacesToVector(
Andreas Huberd2943e12016-08-05 11:59:31 -0700321 const FQName &package,
322 std::vector<FQName> *packageInterfaces) const {
323 packageInterfaces->clear();
324
325 std::vector<std::string> fileNames;
326 status_t err = getPackageInterfaceFiles(package, &fileNames);
327
328 if (err != OK) {
329 return err;
330 }
331
332 for (const auto &fileName : fileNames) {
333 FQName subFQName(
Yifan Hong90ea87f2016-11-01 14:25:47 -0700334 package.package() + package.atVersion() + "::" + fileName);
Andreas Huberd2943e12016-08-05 11:59:31 -0700335
336 if (!subFQName.isValid()) {
337 LOG(WARNING)
338 << "Whole-package import encountered invalid filename '"
339 << fileName
340 << "' in package "
341 << package.package()
Yifan Hong90ea87f2016-11-01 14:25:47 -0700342 << package.atVersion();
Andreas Huberd2943e12016-08-05 11:59:31 -0700343
344 continue;
345 }
346
347 packageInterfaces->push_back(subFQName);
Andreas Hubere61e3f72016-08-03 10:22:03 -0700348 }
349
350 return OK;
351}
352
Andreas Huberd2943e12016-08-05 11:59:31 -0700353std::string Coordinator::convertPackageRootToPath(const FQName &fqName) const {
354 std::string packageRoot = getPackageRoot(fqName);
355
356 if (*(packageRoot.end()--) != '.') {
357 packageRoot += '.';
358 }
359
360 std::replace(packageRoot.begin(), packageRoot.end(), '.', '/');
361
362 return packageRoot; // now converted to a path
363}
364
Yifan Hong78b38d12017-02-13 18:14:46 +0000365
366status_t Coordinator::enforceRestrictionsOnPackage(const FQName &fqName) {
367 // need fqName to be something like android.hardware.foo@1.0.
368 // name and valueName is ignored.
369 if (fqName.package().empty() || fqName.version().empty()) {
370 LOG(ERROR) << "Cannot enforce restrictions on package " << fqName.string()
371 << ": package or version is missing.";
372 return BAD_VALUE;
373 }
374 FQName package = fqName.getPackageAndVersion();
375 // look up cache.
376 if (mPackagesEnforced.find(package) != mPackagesEnforced.end()) {
377 return OK;
378 }
379
380 // enforce all rules.
381 status_t err = enforceMinorVersionUprevs(package);
382 if (err != OK) {
383 return err;
384 }
385
386 // cache it so that it won't need to be enforced again.
387 mPackagesEnforced.insert(package);
388 return OK;
389}
390
391status_t Coordinator::enforceMinorVersionUprevs(const FQName &currentPackage) {
392 if(!currentPackage.hasVersion()) {
393 LOG(ERROR) << "Cannot enforce minor version uprevs for " << currentPackage.string()
394 << ": missing version.";
395 return UNKNOWN_ERROR;
396 }
397
398 if (currentPackage.getPackageMinorVersion() == 0) {
399 return OK; // ignore for @x.0
400 }
401
402 bool hasPrevPackage = false;
403 FQName prevPacakge = currentPackage;
404 while (prevPacakge.getPackageMinorVersion() > 0) {
405 prevPacakge = prevPacakge.downRev();
406 if (existdir(getPackagePath(prevPacakge).c_str())) {
407 hasPrevPackage = true;
408 break;
409 }
410 }
411 if (!hasPrevPackage) {
412 // no @x.z, where z < y, exist.
413 return OK;
414 }
415
416 if (prevPacakge != currentPackage.downRev()) {
417 LOG(ERROR) << "Cannot enforce minor version uprevs for " << currentPackage.string()
418 << ": Found package " << prevPacakge.string() << " but missing "
419 << currentPackage.downRev().string() << "; you cannot skip a minor version.";
420 return UNKNOWN_ERROR;
421 }
422
423 status_t err;
424 std::vector<FQName> packageInterfaces;
425 err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
426 if (err != OK) {
427 return err;
428 }
429
Yifan Hongfe07bff2017-02-15 14:55:48 -0800430 bool extendedInterface = false;
Yifan Hong78b38d12017-02-13 18:14:46 +0000431 for (const FQName &currentFQName : packageInterfaces) {
432 if (currentFQName.name() == "types") {
433 continue; // ignore types.hal
434 }
435 // Assume that currentFQName == android.hardware.foo@2.2::IFoo.
436 // Then prevFQName == android.hardware.foo@2.1::IFoo.
437 const Interface *iface = nullptr;
438 AST *currentAST = parse(currentFQName);
439 if (currentAST != nullptr) {
440 iface = currentAST->getInterface();
441 }
442 if (iface == nullptr) {
Yifan Hongfe07bff2017-02-15 14:55:48 -0800443 if (currentAST == nullptr) {
444 LOG(WARNING) << "Warning: Skipping " << currentFQName.string()
445 << " because it could not be found or parsed"
446 << " or " << currentPackage.string()
447 << " doesn't pass all requirements.";
448 } else {
449 LOG(WARNING) << "Warning: Skipping " << currentFQName.string()
450 << " because the file might contain more than one interface.";
451 }
Yifan Hong78b38d12017-02-13 18:14:46 +0000452 continue;
453 }
454
455 // android.hardware.foo@2.2::IFoo exists. Now make sure
456 // @2.2::IFoo extends @2.1::IFoo. If any interface IFoo in @2.2
457 // ensures this, @2.2 passes the enforcement.
458 FQName prevFQName(prevPacakge.package(), prevPacakge.version(),
459 currentFQName.name());
460 if (iface->superType() == nullptr) {
461 // @2.2::IFoo doesn't extend anything. (This is probably IBase.)
462 continue;
463 }
464 if (iface->superType()->fqName() != prevFQName) {
465 // @2.2::IFoo doesn't extend @2.1::IFoo.
Yifan Hongfe07bff2017-02-15 14:55:48 -0800466 if (iface->superType()->fqName().getPackageAndVersion() ==
467 prevPacakge.getPackageAndVersion()) {
Yifan Hong78b38d12017-02-13 18:14:46 +0000468 LOG(ERROR) << "Cannot enforce minor version uprevs for " << currentPackage.string()
469 << ": " << iface->fqName().string() << " extends "
470 << iface->superType()->fqName().string() << ", which is not allowed.";
471 return UNKNOWN_ERROR;
472 }
473 // @2.2::IFoo extends something from a package with a different package name.
474 // Check the next interface.
475 continue;
476 }
477
Yifan Hongfe07bff2017-02-15 14:55:48 -0800478 // @2.2::IFoo passes. Check next interface.
479 extendedInterface = true;
Yifan Hong78b38d12017-02-13 18:14:46 +0000480 LOG(VERBOSE) << "enforceMinorVersionUprevs: " << currentFQName.string() << " passes.";
Yifan Hong78b38d12017-02-13 18:14:46 +0000481 }
482
Yifan Hongfe07bff2017-02-15 14:55:48 -0800483 if (!extendedInterface) {
484 // No interface extends the interface with the same name in @x.(y-1).
485 LOG(ERROR) << currentPackage.string() << " doesn't pass minor version uprev requirement. "
486 << "Requires at least one interface to extend an interface with the same name "
487 << "from " << prevPacakge.string() << ".";
488 return UNKNOWN_ERROR;
489 }
490
491 return OK;
Yifan Hong78b38d12017-02-13 18:14:46 +0000492}
493
Andreas Huberd2943e12016-08-05 11:59:31 -0700494// static
495bool Coordinator::MakeParentHierarchy(const std::string &path) {
496 static const mode_t kMode = 0755;
497
498 size_t start = 1; // Ignore leading '/'
499 size_t slashPos;
500 while ((slashPos = path.find("/", start)) != std::string::npos) {
501 std::string partial = path.substr(0, slashPos);
502
503 struct stat st;
504 if (stat(partial.c_str(), &st) < 0) {
505 if (errno != ENOENT) {
506 return false;
507 }
508
509 int res = mkdir(partial.c_str(), kMode);
510 if (res < 0) {
511 return false;
512 }
513 } else if (!S_ISDIR(st.st_mode)) {
514 return false;
515 }
516
517 start = slashPos + 1;
518 }
519
520 return true;
521}
522
Andreas Huber5345ec22016-07-29 13:33:27 -0700523} // namespace android
524