diff options
author | Shinichiro Hamaji <shinichiro.hamaji@gmail.com> | 2015-10-13 16:15:34 +0900 |
---|---|---|
committer | Shinichiro Hamaji <shinichiro.hamaji@gmail.com> | 2015-10-15 15:12:17 +0900 |
commit | b7175615d59a91950d547c9c39a7c746c6f22c8b (patch) | |
tree | ce0710b19ab0cfccb333172368f0823d6045be0b /find.cc | |
parent | 14ea0f1f4b5cfd0c4b7d1306417633d331103ca5 (diff) | |
download | android_build_kati-b7175615d59a91950d547c9c39a7c746c6f22c8b.tar.gz android_build_kati-b7175615d59a91950d547c9c39a7c746c6f22c8b.tar.bz2 android_build_kati-b7175615d59a91950d547c9c39a7c746c6f22c8b.zip |
[C++] Make FindEmulator's symlink support better
Now FindEmulator supports symlinks which refer
- a file in FindEmulator or
- a file which isn't a directory nor a symlink
Also findleaves.py follows symbolic links.
Diffstat (limited to 'find.cc')
-rw-r--r-- | find.cc | 205 |
1 files changed, 145 insertions, 60 deletions
@@ -116,7 +116,11 @@ class DirentNode { return NULL; } virtual bool RunFind(const FindCommand& fc, int d, - string* path, string* out) const = 0; + string* path, + unordered_map<const DirentNode*, string>* cur_read_dirs, + string* out) const = 0; + + virtual bool IsDirectory() const = 0; const string& base() const { return base_; } @@ -148,15 +152,47 @@ class DirentFileNode : public DirentNode { } virtual bool RunFind(const FindCommand& fc, int d, - string* path, string* out) const { + string* path, + unordered_map<const DirentNode*, string>*, + string* out) const { PrintIfNecessary(fc, *path, type_, d, out); return true; } + virtual bool IsDirectory() const override { return false; } + private: unsigned char type_; }; +struct ScopedReadDirTracker { + public: + ScopedReadDirTracker(const DirentNode* n, + const string& path, + unordered_map<const DirentNode*, string>* cur_read_dirs) + : n_(NULL), cur_read_dirs_(cur_read_dirs) { + const auto& p = cur_read_dirs->emplace(n, path); + if (p.second) { + n_ = n; + } else { + conflicted_ = p.first->second; + } + } + + ~ScopedReadDirTracker() { + if (n_) + cur_read_dirs_->erase(n_); + } + + bool ok() const { return conflicted_.empty(); } + const string& conflicted() const { return conflicted_; } + + private: + string conflicted_; + const DirentNode* n_; + unordered_map<const DirentNode*, string>* cur_read_dirs_; +}; + class DirentDirNode : public DirentNode { public: explicit DirentDirNode(const string& name) @@ -186,8 +222,18 @@ class DirentDirNode : public DirentNode { } virtual bool RunFind(const FindCommand& fc, int d, - string* path, string* out) const { - fc.read_dirs->push_back(*path); + string* path, + unordered_map<const DirentNode*, string>* cur_read_dirs, + string* out) const { + ScopedReadDirTracker srdt(this, *path, cur_read_dirs); + if (!srdt.ok()) { + fprintf(stderr, "FindEmulator: find: File system loop detected; `%s' is " + "part of the same file system loop as `%s'.\n", + path->c_str(), srdt.conflicted().c_str()); + return true; + } + + fc.read_dirs->insert(*path); if (fc.prune_cond && fc.prune_cond->IsTrue(base_, DT_DIR)) { if (fc.type != FindCommandType::FINDLEAVES) { @@ -208,12 +254,12 @@ class DirentDirNode : public DirentNode { for (const auto& p : children_) { DirentNode* c = p.second; // We will handle directories later. - if (dynamic_cast<DirentDirNode*>(c)) + if (c->IsDirectory()) continue; if ((*path)[path->size()-1] != '/') *path += '/'; *path += c->base(); - if (!c->RunFind(fc, d + 1, path, out)) + if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out)) return false; path->resize(orig_path_size); // Found a leaf, stop the search. @@ -223,12 +269,12 @@ class DirentDirNode : public DirentNode { for (const auto& p : children_) { DirentNode* c = p.second; - if (!dynamic_cast<DirentDirNode*>(c)) + if (!c->IsDirectory()) continue; if ((*path)[path->size()-1] != '/') *path += '/'; *path += c->base(); - if (!c->RunFind(fc, d + 1, path, out)) + if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out)) return false; path->resize(orig_path_size); } @@ -238,7 +284,7 @@ class DirentDirNode : public DirentNode { if ((*path)[path->size()-1] != '/') *path += '/'; *path += c->base(); - if (!c->RunFind(fc, d + 1, path, out)) + if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out)) return false; path->resize(orig_path_size); } @@ -246,6 +292,8 @@ class DirentDirNode : public DirentNode { return true; } + virtual bool IsDirectory() const override { return true; } + void Add(const string& name, DirentNode* c) { children_.emplace(children_.end(), name, c); } @@ -257,58 +305,49 @@ class DirentDirNode : public DirentNode { class DirentSymlinkNode : public DirentNode { public: explicit DirentSymlinkNode(const string& name) - : DirentNode(name) { + : DirentNode(name), to_(NULL), errno_(0) { } virtual bool RunFind(const FindCommand& fc, int d, - string* path, string* out) const { + string* path, + unordered_map<const DirentNode*, string>* cur_read_dirs, + string* out) const { unsigned char type = DT_LNK; - if (fc.follows_symlinks) { - // TODO - LOG("FindEmulator: symlink is hard"); - return false; - - char buf[PATH_MAX+1]; - buf[PATH_MAX] = 0; - LOG("path=%s", path->c_str()); - ssize_t len = readlink(path->c_str(), buf, PATH_MAX); - if (len > 0) { - buf[len] = 0; - string oldpath; - if (buf[0] != '/') { - Dirname(*path).AppendToString(&oldpath); - oldpath += '/'; - } - oldpath += buf; - - LOG("buf=%s old=%s", buf, oldpath.c_str()); - - struct stat st; - if (stat(oldpath.c_str(), &st) == 0) { - LOG("st OK"); - if (S_ISREG(st.st_mode)) { - type = DT_REG; - } else if (S_ISDIR(st.st_mode)) { - type = DT_DIR; - } else if (S_ISCHR(st.st_mode)) { - type = DT_CHR; - } else if (S_ISBLK(st.st_mode)) { - type = DT_BLK; - } else if (S_ISFIFO(st.st_mode)) { - type = DT_FIFO; - } else if (S_ISLNK(st.st_mode)) { - type = DT_LNK; - } else if (S_ISSOCK(st.st_mode)) { - type = DT_SOCK; - } else { - return false; - } + if (fc.follows_symlinks && errno_ != ENOENT) { + if (errno_) { + if (fc.type != FindCommandType::FINDLEAVES) { + fprintf(stderr, "FindEmulator: find: `%s': %s\n", + path->c_str(), strerror(errno_)); } + return true; } + + if (!to_) { + LOG("FindEmulator does not support %s", path->c_str()); + return false; + } + + return to_->RunFind(fc, d, path, cur_read_dirs, out); } PrintIfNecessary(fc, *path, type, d, out); return true; } + + virtual bool IsDirectory() const override { + return errno_ == 0 && to_ && to_->IsDirectory(); + } + + void set_to(const DirentNode* to) { + to_ = to; + } + + void set_errno(int e) { + errno_ = e; + } + + private: + const DirentNode* to_; + int errno_; }; class FindCommandParser { @@ -546,6 +585,7 @@ class FindCommandParser { bool ParseFindLeaves() { fc_->type = FindCommandType::FINDLEAVES; + fc_->follows_symlinks = true; StringPiece tok; while (true) { if (!GetNextToken(&tok)) @@ -683,6 +723,7 @@ class FindEmulatorImpl : public FindEmulator { if (!is_initialized_) { ScopedTimeReporter tr("init find emulator time"); root_.reset(ConstructDirectoryTree("")); + ResolveSymlinks(); LOG_STAT("%d find nodes", node_cnt_); is_initialized_ = true; } @@ -747,7 +788,8 @@ class FindEmulatorImpl : public FindEmulator { } string path = finddir.as_string(); - if (!base->RunFind(fc, 0, &path, out)) { + unordered_map<const DirentNode*, string> cur_read_dirs; + if (!base->RunFind(fc, 0, &path, &cur_read_dirs, out)) { LOG("FindEmulator: RunFind failed: %s", cmd.c_str()); out->resize(orig_out_size); return false; @@ -766,12 +808,7 @@ class FindEmulatorImpl : public FindEmulator { } private: - static unsigned char GetDtType(const string& path) { - struct stat st; - if (lstat(path.c_str(), &st)) { - PERROR("stat for %s", path.c_str()); - } - + static unsigned char GetDtTypeFromStat(const struct stat& st) { if (S_ISREG(st.st_mode)) { return DT_REG; } else if (S_ISDIR(st.st_mode)) { @@ -791,6 +828,14 @@ class FindEmulatorImpl : public FindEmulator { } } + static unsigned char GetDtType(const string& path) { + struct stat st; + if (lstat(path.c_str(), &st)) { + PERROR("stat for %s", path.c_str()); + } + return GetDtTypeFromStat(st); + } + DirentNode* ConstructDirectoryTree(const string& path) { DIR* dir = opendir(path.empty() ? "." : path.c_str()); if (!dir) @@ -821,7 +866,9 @@ class FindEmulatorImpl : public FindEmulator { if (d_type == DT_DIR) { c = ConstructDirectoryTree(npath); } else if (d_type == DT_LNK) { - c = new DirentSymlinkNode(npath); + auto s = new DirentSymlinkNode(npath); + symlinks_.push_back(make_pair(npath, s)); + c = s; } else { c = new DirentFileNode(npath, d_type); } @@ -833,7 +880,45 @@ class FindEmulatorImpl : public FindEmulator { return n; } + void ResolveSymlinks() { + for (const auto& p : symlinks_) { + const string& path = p.first; + DirentSymlinkNode* s = p.second; + + char buf[PATH_MAX+1]; + buf[PATH_MAX] = 0; + ssize_t len = readlink(path.c_str(), buf, PATH_MAX); + if (len < 0) { + WARN("readlink failed: %s", path.c_str()); + continue; + } + buf[len] = 0; + + struct stat st; + if (stat(path.c_str(), &st) < 0) { + s->set_errno(errno); + LOG("stat failed: %s: %s", path.c_str(), strerror(errno)); + } + + if (*buf != '/') { + const string npath = ConcatDir(Dirname(path), buf); + bool should_fallback = false; + const DirentNode* to = FindDir(npath, &should_fallback); + if (to) { + s->set_to(to); + continue; + } + } + + unsigned char type = GetDtTypeFromStat(st); + if (type != DT_DIR && type != DT_LNK) { + s->set_to(new DirentFileNode(path, type)); + } + } + } + unique_ptr<DirentNode> root_; + vector<pair<string, DirentSymlinkNode*>> symlinks_; int node_cnt_; bool is_initialized_; }; @@ -843,7 +928,7 @@ class FindEmulatorImpl : public FindEmulator { FindCommand::FindCommand() : follows_symlinks(false), depth(INT_MAX), mindepth(INT_MIN), redirect_to_devnull(false), - read_dirs(new vector<string>()) { + read_dirs(new unordered_set<string>()) { } FindCommand::~FindCommand() { |