1 // Copyright 2015 Google Inc. All rights reserved 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build ignore 16 17 #include "find.h" 18 19 #include <dirent.h> 20 #include <fnmatch.h> 21 #include <limits.h> 22 #include <sys/stat.h> 23 #include <sys/types.h> 24 #include <unistd.h> 25 26 #include <memory> 27 #include <vector> 28 29 //#undef NOLOG 30 31 #include "fileutil.h" 32 #include "log.h" 33 #include "string_piece.h" 34 #include "strutil.h" 35 #include "timeutil.h" 36 37 class FindCond { 38 public: 39 virtual ~FindCond() = default; 40 virtual bool IsTrue(const string& path, unsigned char type) const = 0; 41 protected: 42 FindCond() = default; 43 }; 44 45 namespace { 46 47 class NameCond : public FindCond { 48 public: 49 explicit NameCond(const string& n) 50 : name_(n) { 51 } 52 virtual bool IsTrue(const string& path, unsigned char) const override { 53 return fnmatch(name_.c_str(), Basename(path).data(), 0) == 0; 54 } 55 private: 56 string name_; 57 }; 58 59 class TypeCond : public FindCond { 60 public: 61 explicit TypeCond(unsigned char t) 62 : type_(t) { 63 } 64 virtual bool IsTrue(const string&, unsigned char type) const override { 65 return type == type_; 66 } 67 private: 68 unsigned char type_; 69 }; 70 71 class NotCond : public FindCond { 72 public: 73 NotCond(FindCond* c) 74 : c_(c) { 75 } 76 virtual bool IsTrue(const string& path, unsigned char type) const override { 77 return !c_->IsTrue(path, type); 78 } 79 private: 80 unique_ptr<FindCond> c_; 81 }; 82 83 class AndCond : public FindCond { 84 public: 85 AndCond(FindCond* c1, FindCond* c2) 86 : c1_(c1), c2_(c2) { 87 } 88 virtual bool IsTrue(const string& path, unsigned char type) const override { 89 if (c1_->IsTrue(path, type)) 90 return c2_->IsTrue(path, type); 91 return false; 92 } 93 private: 94 unique_ptr<FindCond> c1_, c2_; 95 }; 96 97 class OrCond : public FindCond { 98 public: 99 OrCond(FindCond* c1, FindCond* c2) 100 : c1_(c1), c2_(c2) { 101 } 102 virtual bool IsTrue(const string& path, unsigned char type) const override { 103 if (!c1_->IsTrue(path, type)) 104 return c2_->IsTrue(path, type); 105 return true; 106 } 107 private: 108 unique_ptr<FindCond> c1_, c2_; 109 }; 110 111 class DirentNode { 112 public: 113 virtual ~DirentNode() = default; 114 115 virtual const DirentNode* FindDir(StringPiece) const { 116 return NULL; 117 } 118 virtual bool RunFind(const FindCommand& fc, int d, 119 string* path, 120 unordered_map<const DirentNode*, string>* cur_read_dirs, 121 string* out) const = 0; 122 123 virtual bool IsDirectory() const = 0; 124 125 const string& base() const { return base_; } 126 127 protected: 128 explicit DirentNode(const string& name) { 129 base_ = Basename(name).as_string(); 130 } 131 132 void PrintIfNecessary(const FindCommand& fc, 133 const string& path, 134 unsigned char type, 135 int d, 136 string* out) const { 137 if (fc.print_cond && !fc.print_cond->IsTrue(path, type)) 138 return; 139 if (d < fc.mindepth) 140 return; 141 *out += path; 142 *out += ' '; 143 } 144 145 string base_; 146 }; 147 148 class DirentFileNode : public DirentNode { 149 public: 150 DirentFileNode(const string& name, unsigned char type) 151 : DirentNode(name), type_(type) { 152 } 153 154 virtual bool RunFind(const FindCommand& fc, int d, 155 string* path, 156 unordered_map<const DirentNode*, string>*, 157 string* out) const override { 158 PrintIfNecessary(fc, *path, type_, d, out); 159 return true; 160 } 161 162 virtual bool IsDirectory() const override { return false; } 163 164 private: 165 unsigned char type_; 166 }; 167 168 struct ScopedReadDirTracker { 169 public: 170 ScopedReadDirTracker(const DirentNode* n, 171 const string& path, 172 unordered_map<const DirentNode*, string>* cur_read_dirs) 173 : n_(NULL), cur_read_dirs_(cur_read_dirs) { 174 const auto& p = cur_read_dirs->emplace(n, path); 175 if (p.second) { 176 n_ = n; 177 } else { 178 conflicted_ = p.first->second; 179 } 180 } 181 182 ~ScopedReadDirTracker() { 183 if (n_) 184 cur_read_dirs_->erase(n_); 185 } 186 187 bool ok() const { return conflicted_.empty(); } 188 const string& conflicted() const { return conflicted_; } 189 190 private: 191 string conflicted_; 192 const DirentNode* n_; 193 unordered_map<const DirentNode*, string>* cur_read_dirs_; 194 }; 195 196 class DirentDirNode : public DirentNode { 197 public: 198 explicit DirentDirNode(const string& name) 199 : DirentNode(name) { 200 } 201 202 ~DirentDirNode() { 203 for (auto& p : children_) { 204 delete p.second; 205 } 206 } 207 208 virtual const DirentNode* FindDir(StringPiece d) const override { 209 if (d.empty() || d == ".") 210 return this; 211 size_t index = d.find('/'); 212 const string& p = d.substr(0, index).as_string(); 213 for (auto& child : children_) { 214 if (p == child.first) { 215 if (index == string::npos) 216 return child.second; 217 StringPiece nd = d.substr(index + 1); 218 return child.second->FindDir(nd); 219 } 220 } 221 return NULL; 222 } 223 224 virtual bool RunFind(const FindCommand& fc, int d, 225 string* path, 226 unordered_map<const DirentNode*, string>* cur_read_dirs, 227 string* out) const override { 228 ScopedReadDirTracker srdt(this, *path, cur_read_dirs); 229 if (!srdt.ok()) { 230 fprintf(stderr, "FindEmulator: find: File system loop detected; `%s' is " 231 "part of the same file system loop as `%s'.\n", 232 path->c_str(), srdt.conflicted().c_str()); 233 return true; 234 } 235 236 fc.read_dirs->insert(*path); 237 238 if (fc.prune_cond && fc.prune_cond->IsTrue(*path, DT_DIR)) { 239 if (fc.type != FindCommandType::FINDLEAVES) { 240 *out += *path; 241 *out += ' '; 242 } 243 return true; 244 } 245 246 PrintIfNecessary(fc, *path, DT_DIR, d, out); 247 248 if (d >= fc.depth) 249 return true; 250 251 size_t orig_path_size = path->size(); 252 if (fc.type == FindCommandType::FINDLEAVES) { 253 size_t orig_out_size = out->size(); 254 for (const auto& p : children_) { 255 DirentNode* c = p.second; 256 // We will handle directories later. 257 if (c->IsDirectory()) 258 continue; 259 if ((*path)[path->size()-1] != '/') 260 *path += '/'; 261 *path += c->base(); 262 if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out)) 263 return false; 264 path->resize(orig_path_size); 265 // Found a leaf, stop the search. 266 if (orig_out_size != out->size()) 267 return true; 268 } 269 270 for (const auto& p : children_) { 271 DirentNode* c = p.second; 272 if (!c->IsDirectory()) 273 continue; 274 if ((*path)[path->size()-1] != '/') 275 *path += '/'; 276 *path += c->base(); 277 if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out)) 278 return false; 279 path->resize(orig_path_size); 280 } 281 } else { 282 for (const auto& p : children_) { 283 DirentNode* c = p.second; 284 if ((*path)[path->size()-1] != '/') 285 *path += '/'; 286 *path += c->base(); 287 if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out)) 288 return false; 289 path->resize(orig_path_size); 290 } 291 } 292 return true; 293 } 294 295 virtual bool IsDirectory() const override { return true; } 296 297 void Add(const string& name, DirentNode* c) { 298 children_.emplace(children_.end(), name, c); 299 } 300 301 private: 302 vector<pair<string, DirentNode*>> children_; 303 }; 304 305 class DirentSymlinkNode : public DirentNode { 306 public: 307 explicit DirentSymlinkNode(const string& name) 308 : DirentNode(name), to_(NULL), errno_(0) { 309 } 310 311 virtual const DirentNode* FindDir(StringPiece d) const override { 312 if (errno_ == 0 && to_) 313 return to_->FindDir(d); 314 return NULL; 315 } 316 317 virtual bool RunFind(const FindCommand& fc, int d, 318 string* path, 319 unordered_map<const DirentNode*, string>* cur_read_dirs, 320 string* out) const override { 321 unsigned char type = DT_LNK; 322 if (fc.follows_symlinks && errno_ != ENOENT) { 323 if (errno_) { 324 if (fc.type != FindCommandType::FINDLEAVES) { 325 fprintf(stderr, "FindEmulator: find: `%s': %s\n", 326 path->c_str(), strerror(errno_)); 327 } 328 return true; 329 } 330 331 if (!to_) { 332 LOG("FindEmulator does not support %s", path->c_str()); 333 return false; 334 } 335 336 return to_->RunFind(fc, d, path, cur_read_dirs, out); 337 } 338 PrintIfNecessary(fc, *path, type, d, out); 339 return true; 340 } 341 342 virtual bool IsDirectory() const override { 343 return errno_ == 0 && to_ && to_->IsDirectory(); 344 } 345 346 void set_to(const DirentNode* to) { 347 to_ = to; 348 } 349 350 void set_errno(int e) { 351 errno_ = e; 352 } 353 354 private: 355 const DirentNode* to_; 356 int errno_; 357 }; 358 359 class FindCommandParser { 360 public: 361 FindCommandParser(StringPiece cmd, FindCommand* fc) 362 : cmd_(cmd), fc_(fc), has_if_(false) { 363 } 364 365 bool Parse() { 366 cur_ = cmd_; 367 if (!ParseImpl()) { 368 LOG("FindEmulator: Unsupported find command: %.*s", SPF(cmd_)); 369 return false; 370 } 371 CHECK(TrimLeftSpace(cur_).empty()); 372 return true; 373 } 374 375 private: 376 bool GetNextToken(StringPiece* tok) { 377 if (!unget_tok_.empty()) { 378 *tok = unget_tok_; 379 unget_tok_.clear(); 380 return true; 381 } 382 383 cur_ = TrimLeftSpace(cur_); 384 385 if (cur_[0] == ';') { 386 *tok = cur_.substr(0, 1); 387 cur_ = cur_.substr(1); 388 return true; 389 } 390 if (cur_[0] == '&') { 391 if (cur_.get(1) != '&') { 392 return false; 393 } 394 *tok = cur_.substr(0, 2); 395 cur_ = cur_.substr(2); 396 return true; 397 } 398 399 size_t i = 0; 400 while (i < cur_.size() && !isspace(cur_[i]) && 401 cur_[i] != ';' && cur_[i] != '&') { 402 i++; 403 } 404 405 *tok = cur_.substr(0, i); 406 cur_ = cur_.substr(i); 407 408 const char c = tok->get(0); 409 if (c == '\'' || c == '"') { 410 if (tok->size() < 2 || (*tok)[tok->size()-1] != c) 411 return false; 412 *tok = tok->substr(1, tok->size() - 2); 413 return true; 414 } 415 416 return true; 417 } 418 419 void UngetToken(StringPiece tok) { 420 CHECK(unget_tok_.empty()); 421 if (!tok.empty()) 422 unget_tok_ = tok; 423 } 424 425 bool ParseTest() { 426 if (has_if_ || !fc_->testdir.empty()) 427 return false; 428 StringPiece tok; 429 if (!GetNextToken(&tok) || tok != "-d") 430 return false; 431 if (!GetNextToken(&tok) || tok.empty()) 432 return false; 433 fc_->testdir = tok.as_string(); 434 return true; 435 } 436 437 FindCond* ParseFact(StringPiece tok) { 438 if (tok == "-not" || tok == "\\!") { 439 if (!GetNextToken(&tok) || tok.empty()) 440 return NULL; 441 unique_ptr<FindCond> c(ParseFact(tok)); 442 if (!c.get()) 443 return NULL; 444 return new NotCond(c.release()); 445 } else if (tok == "\\(") { 446 if (!GetNextToken(&tok) || tok.empty()) 447 return NULL; 448 unique_ptr<FindCond> c(ParseExpr(tok)); 449 if (!GetNextToken(&tok) || tok != "\\)") { 450 return NULL; 451 } 452 return c.release(); 453 } else if (tok == "-name") { 454 if (!GetNextToken(&tok) || tok.empty()) 455 return NULL; 456 return new NameCond(tok.as_string()); 457 } else if (tok == "-type") { 458 if (!GetNextToken(&tok) || tok.empty()) 459 return NULL; 460 char type; 461 if (tok == "b") 462 type = DT_BLK; 463 else if (tok == "c") 464 type = DT_CHR; 465 else if (tok == "d") 466 type = DT_DIR; 467 else if (tok == "p") 468 type = DT_FIFO; 469 else if (tok == "l") 470 type = DT_LNK; 471 else if (tok == "f") 472 type = DT_REG; 473 else if (tok == "s") 474 type = DT_SOCK; 475 else 476 return NULL; 477 return new TypeCond(type); 478 } else { 479 UngetToken(tok); 480 return NULL; 481 } 482 } 483 484 FindCond* ParseTerm(StringPiece tok) { 485 unique_ptr<FindCond> c(ParseFact(tok)); 486 if (!c.get()) 487 return NULL; 488 while (true) { 489 if (!GetNextToken(&tok)) 490 return NULL; 491 if (tok != "-and" && tok != "-a") { 492 UngetToken(tok); 493 return c.release(); 494 } 495 if (!GetNextToken(&tok) || tok.empty()) 496 return NULL; 497 unique_ptr<FindCond> r(ParseFact(tok)); 498 if (!r.get()) { 499 return NULL; 500 } 501 c.reset(new AndCond(c.release(), r.release())); 502 } 503 } 504 505 FindCond* ParseExpr(StringPiece tok) { 506 unique_ptr<FindCond> c(ParseTerm(tok)); 507 if (!c.get()) 508 return NULL; 509 while (true) { 510 if (!GetNextToken(&tok)) 511 return NULL; 512 if (tok != "-or" && tok != "-o") { 513 UngetToken(tok); 514 return c.release(); 515 } 516 if (!GetNextToken(&tok) || tok.empty()) 517 return NULL; 518 unique_ptr<FindCond> r(ParseTerm(tok)); 519 if (!r.get()) { 520 return NULL; 521 } 522 c.reset(new OrCond(c.release(), r.release())); 523 } 524 } 525 526 // <expr> ::= <term> {<or> <term>} 527 // <term> ::= <fact> {<and> <fact>} 528 // <fact> ::= <not> <fact> | '\(' <expr> '\)' | <pred> 529 // <not> ::= '-not' | '\!' 530 // <and> ::= '-and' | '-a' 531 // <or> ::= '-or' | '-o' 532 // <pred> ::= <name> | <type> | <maxdepth> 533 // <name> ::= '-name' NAME 534 // <type> ::= '-type' TYPE 535 // <maxdepth> ::= '-maxdepth' MAXDEPTH 536 FindCond* ParseFindCond(StringPiece tok) { 537 return ParseExpr(tok); 538 } 539 540 bool ParseFind() { 541 fc_->type = FindCommandType::FIND; 542 StringPiece tok; 543 while (true) { 544 if (!GetNextToken(&tok)) 545 return false; 546 if (tok.empty() || tok == ";") 547 return true; 548 549 if (tok == "-L") { 550 fc_->follows_symlinks = true; 551 } else if (tok == "-prune") { 552 if (!fc_->print_cond || fc_->prune_cond) 553 return false; 554 if (!GetNextToken(&tok) || tok != "-o") 555 return false; 556 fc_->prune_cond.reset(fc_->print_cond.release()); 557 } else if (tok == "-print") { 558 if (!GetNextToken(&tok) || !tok.empty()) 559 return false; 560 return true; 561 } else if (tok == "-maxdepth") { 562 if (!GetNextToken(&tok) || tok.empty()) 563 return false; 564 const string& depth_str = tok.as_string(); 565 char* endptr; 566 long d = strtol(depth_str.c_str(), &endptr, 10); 567 if (endptr != depth_str.data() + depth_str.size() || 568 d < 0 || d > INT_MAX) { 569 return false; 570 } 571 fc_->depth = d; 572 } else if (tok[0] == '-' || tok == "\\(") { 573 if (fc_->print_cond.get()) 574 return false; 575 FindCond* c = ParseFindCond(tok); 576 if (!c) 577 return false; 578 fc_->print_cond.reset(c); 579 } else if (tok == "2>") { 580 if (!GetNextToken(&tok) || tok != "/dev/null") { 581 return false; 582 } 583 fc_->redirect_to_devnull = true; 584 } else if (tok.find_first_of("|;&><*'\"") != string::npos) { 585 return false; 586 } else { 587 fc_->finddirs.push_back(tok); 588 } 589 } 590 } 591 592 bool ParseFindLeaves() { 593 fc_->type = FindCommandType::FINDLEAVES; 594 fc_->follows_symlinks = true; 595 StringPiece tok; 596 while (true) { 597 if (!GetNextToken(&tok)) 598 return false; 599 if (tok.empty()) { 600 if (fc_->finddirs.size() < 2) 601 return false; 602 fc_->print_cond.reset(new NameCond(fc_->finddirs.back().as_string())); 603 fc_->finddirs.pop_back(); 604 return true; 605 } 606 607 if (HasPrefix(tok, "--prune=")) { 608 FindCond* cond = new NameCond( 609 tok.substr(strlen("--prune=")).as_string()); 610 if (fc_->prune_cond.get()) { 611 cond = new OrCond(fc_->prune_cond.release(), cond); 612 } 613 CHECK(!fc_->prune_cond.get()); 614 fc_->prune_cond.reset(cond); 615 } else if (HasPrefix(tok, "--mindepth=")) { 616 string mindepth_str = tok.substr(strlen("--mindepth=")).as_string(); 617 char* endptr; 618 long d = strtol(mindepth_str.c_str(), &endptr, 10); 619 if (endptr != mindepth_str.data() + mindepth_str.size() || 620 d < INT_MIN || d > INT_MAX) { 621 return false; 622 } 623 fc_->mindepth = d; 624 } else if (HasPrefix(tok, "--")) { 625 WARN("Unknown flag in findleaves.py: %.*s", SPF(tok)); 626 return false; 627 } else { 628 fc_->finddirs.push_back(tok); 629 } 630 } 631 } 632 633 bool ParseImpl() { 634 while (true) { 635 StringPiece tok; 636 if (!GetNextToken(&tok)) 637 return false; 638 639 if (tok.empty()) 640 return true; 641 642 if (tok == "cd") { 643 if (!GetNextToken(&tok) || tok.empty() || !fc_->chdir.empty()) 644 return false; 645 fc_->chdir = tok.as_string(); 646 if (!GetNextToken(&tok) || (tok != ";" && tok != "&&")) 647 return false; 648 } else if (tok == "if") { 649 if (!GetNextToken(&tok) || tok != "[") 650 return false; 651 if (!ParseTest()) 652 return false; 653 if (!GetNextToken(&tok) || tok != "]") 654 return false; 655 if (!GetNextToken(&tok) || tok != ";") 656 return false; 657 if (!GetNextToken(&tok) || tok != "then") 658 return false; 659 has_if_ = true; 660 } else if (tok == "test") { 661 if (!fc_->chdir.empty()) 662 return false; 663 if (!ParseTest()) 664 return false; 665 if (!GetNextToken(&tok) || tok != "&&") 666 return false; 667 } else if (tok == "find") { 668 if (!ParseFind()) 669 return false; 670 if (has_if_) { 671 if (!GetNextToken(&tok) || tok != "fi") 672 return false; 673 } 674 if (!GetNextToken(&tok) || !tok.empty()) 675 return false; 676 return true; 677 } else if (tok == "build/tools/findleaves.py") { 678 if (!ParseFindLeaves()) 679 return false; 680 return true; 681 } else { 682 return false; 683 } 684 } 685 } 686 687 StringPiece cmd_; 688 StringPiece cur_; 689 FindCommand* fc_; 690 bool has_if_; 691 StringPiece unget_tok_; 692 }; 693 694 static FindEmulator* g_instance; 695 696 class FindEmulatorImpl : public FindEmulator { 697 public: 698 FindEmulatorImpl() 699 : node_cnt_(0), is_initialized_(false) { 700 g_instance = this; 701 } 702 703 virtual ~FindEmulatorImpl() = default; 704 705 bool CanHandle(StringPiece s) const { 706 return (!HasPrefix(s, "../") && 707 !HasPrefix(s, "/") && 708 !HasPrefix(s, ".repo") && 709 !HasPrefix(s, ".git") && 710 !HasPrefix(s, "out")); 711 } 712 713 const DirentNode* FindDir(StringPiece d, bool* should_fallback) { 714 const DirentNode* r = root_->FindDir(d); 715 if (!r) { 716 *should_fallback = Exists(d); 717 } 718 return r; 719 } 720 721 virtual bool HandleFind(const string& cmd UNUSED, const FindCommand& fc, 722 string* out) override { 723 if (!CanHandle(fc.chdir)) { 724 LOG("FindEmulator: Cannot handle chdir (%.*s): %s", 725 SPF(fc.chdir), cmd.c_str()); 726 return false; 727 } 728 729 if (!is_initialized_) { 730 ScopedTimeReporter tr("init find emulator time"); 731 root_.reset(ConstructDirectoryTree("")); 732 ResolveSymlinks(); 733 LOG_STAT("%d find nodes", node_cnt_); 734 is_initialized_ = true; 735 } 736 737 if (!fc.testdir.empty()) { 738 if (!CanHandle(fc.testdir)) { 739 LOG("FindEmulator: Cannot handle test dir (%.*s): %s", 740 SPF(fc.testdir), cmd.c_str()); 741 return false; 742 } 743 bool should_fallback = false; 744 if (!FindDir(fc.testdir, &should_fallback)) { 745 LOG("FindEmulator: Test dir (%.*s) not found: %s", 746 SPF(fc.testdir), cmd.c_str()); 747 return !should_fallback; 748 } 749 } 750 751 if (!fc.chdir.empty()) { 752 if (!CanHandle(fc.chdir)) { 753 LOG("FindEmulator: Cannot handle chdir (%.*s): %s", 754 SPF(fc.chdir), cmd.c_str()); 755 return false; 756 } 757 bool should_fallback = false; 758 if (!FindDir(fc.chdir, &should_fallback)) { 759 if (should_fallback) 760 return false; 761 if (!fc.redirect_to_devnull) { 762 fprintf(stderr, 763 "FindEmulator: cd: %.*s: No such file or directory\n", 764 SPF(fc.chdir)); 765 } 766 return true; 767 } 768 } 769 770 const size_t orig_out_size = out->size(); 771 for (StringPiece finddir : fc.finddirs) { 772 const string dir = ConcatDir(fc.chdir, finddir); 773 774 if (!CanHandle(dir)) { 775 LOG("FindEmulator: Cannot handle find dir (%s): %s", 776 dir.c_str(), cmd.c_str()); 777 out->resize(orig_out_size); 778 return false; 779 } 780 781 bool should_fallback = false; 782 const DirentNode* base = FindDir(dir, &should_fallback); 783 if (!base) { 784 if (should_fallback) { 785 out->resize(orig_out_size); 786 return false; 787 } 788 if (!fc.redirect_to_devnull) { 789 fprintf(stderr, 790 "FindEmulator: find: `%s': No such file or directory\n", 791 ConcatDir(fc.chdir, finddir).c_str()); 792 } 793 continue; 794 } 795 796 string path = finddir.as_string(); 797 unordered_map<const DirentNode*, string> cur_read_dirs; 798 if (!base->RunFind(fc, 0, &path, &cur_read_dirs, out)) { 799 LOG("FindEmulator: RunFind failed: %s", cmd.c_str()); 800 out->resize(orig_out_size); 801 return false; 802 } 803 } 804 805 if (!out->empty() && (*out)[out->size()-1] == ' ') 806 out->resize(out->size()-1); 807 808 if (fc.type == FindCommandType::FINDLEAVES) { 809 *out = SortWordsInString(*out); 810 } 811 812 LOG("FindEmulator: OK"); 813 return true; 814 } 815 816 private: 817 static unsigned char GetDtTypeFromStat(const struct stat& st) { 818 if (S_ISREG(st.st_mode)) { 819 return DT_REG; 820 } else if (S_ISDIR(st.st_mode)) { 821 return DT_DIR; 822 } else if (S_ISCHR(st.st_mode)) { 823 return DT_CHR; 824 } else if (S_ISBLK(st.st_mode)) { 825 return DT_BLK; 826 } else if (S_ISFIFO(st.st_mode)) { 827 return DT_FIFO; 828 } else if (S_ISLNK(st.st_mode)) { 829 return DT_LNK; 830 } else if (S_ISSOCK(st.st_mode)) { 831 return DT_SOCK; 832 } else { 833 return DT_UNKNOWN; 834 } 835 } 836 837 static unsigned char GetDtType(const string& path) { 838 struct stat st; 839 if (lstat(path.c_str(), &st)) { 840 PERROR("stat for %s", path.c_str()); 841 } 842 return GetDtTypeFromStat(st); 843 } 844 845 DirentNode* ConstructDirectoryTree(const string& path) { 846 DIR* dir = opendir(path.empty() ? "." : path.c_str()); 847 if (!dir) 848 PERROR("opendir failed: %s", path.c_str()); 849 850 DirentDirNode* n = new DirentDirNode(path); 851 852 struct dirent* ent; 853 while ((ent = readdir(dir)) != NULL) { 854 if (!strcmp(ent->d_name, ".") || 855 !strcmp(ent->d_name, "..") || 856 !strcmp(ent->d_name, ".repo") || 857 !strcmp(ent->d_name, ".git") || 858 !strcmp(ent->d_name, "out")) 859 continue; 860 861 string npath = path; 862 if (!path.empty()) 863 npath += '/'; 864 npath += ent->d_name; 865 866 DirentNode* c = NULL; 867 auto d_type = ent->d_type; 868 if (d_type == DT_UNKNOWN) { 869 d_type = GetDtType(npath); 870 CHECK(d_type != DT_UNKNOWN); 871 } 872 if (d_type == DT_DIR) { 873 c = ConstructDirectoryTree(npath); 874 } else if (d_type == DT_LNK) { 875 auto s = new DirentSymlinkNode(npath); 876 symlinks_.push_back(make_pair(npath, s)); 877 c = s; 878 } else { 879 c = new DirentFileNode(npath, d_type); 880 } 881 node_cnt_++; 882 n->Add(ent->d_name, c); 883 } 884 closedir(dir); 885 886 return n; 887 } 888 889 void ResolveSymlinks() { 890 vector<pair<string, DirentSymlinkNode*>> symlinks; 891 symlinks.swap(symlinks_); 892 for (const auto& p : symlinks) { 893 const string& path = p.first; 894 DirentSymlinkNode* s = p.second; 895 896 char buf[PATH_MAX+1]; 897 buf[PATH_MAX] = 0; 898 ssize_t len = readlink(path.c_str(), buf, PATH_MAX); 899 if (len < 0) { 900 WARN("readlink failed: %s", path.c_str()); 901 continue; 902 } 903 buf[len] = 0; 904 905 struct stat st; 906 unsigned char type = DT_UNKNOWN; 907 if (stat(path.c_str(), &st) == 0) { 908 type = GetDtTypeFromStat(st); 909 } else { 910 s->set_errno(errno); 911 LOG("stat failed: %s: %s", path.c_str(), strerror(errno)); 912 } 913 914 if (*buf != '/') { 915 const string npath = ConcatDir(Dirname(path), buf); 916 bool should_fallback = false; 917 const DirentNode* to = FindDir(npath, &should_fallback); 918 if (to) { 919 s->set_to(to); 920 continue; 921 } 922 } 923 924 if (type == DT_DIR) { 925 if (path.find('/') == string::npos) { 926 s->set_to(ConstructDirectoryTree(path)); 927 } 928 } else if (type != DT_LNK && type != DT_UNKNOWN) { 929 s->set_to(new DirentFileNode(path, type)); 930 } 931 } 932 933 if (!symlinks_.empty()) 934 ResolveSymlinks(); 935 } 936 937 unique_ptr<DirentNode> root_; 938 vector<pair<string, DirentSymlinkNode*>> symlinks_; 939 int node_cnt_; 940 bool is_initialized_; 941 }; 942 943 } // namespace 944 945 FindCommand::FindCommand() 946 : follows_symlinks(false), depth(INT_MAX), mindepth(INT_MIN), 947 redirect_to_devnull(false), 948 read_dirs(new unordered_set<string>()) { 949 } 950 951 FindCommand::~FindCommand() { 952 } 953 954 bool FindCommand::Parse(const string& cmd) { 955 FindCommandParser fcp(cmd, this); 956 if (!HasWord(cmd, "find") && !HasWord(cmd, "build/tools/findleaves.py")) 957 return false; 958 959 if (!fcp.Parse()) 960 return false; 961 962 NormalizePath(&chdir); 963 NormalizePath(&testdir); 964 if (finddirs.empty()) 965 finddirs.push_back("."); 966 return true; 967 } 968 969 FindEmulator* FindEmulator::Get() { 970 return g_instance; 971 } 972 973 void InitFindEmulator() { 974 new FindEmulatorImpl(); 975 } 976