diff --git a/src/utils/fstreewalk.cpp b/src/utils/fstreewalk.cpp index f1eaf07a..bbd5aaa5 100644 --- a/src/utils/fstreewalk.cpp +++ b/src/utils/fstreewalk.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2004 J.F.Dockes +/* Copyright (C) 2004-2019 J.F.Dockes * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -52,9 +52,8 @@ public: dev_t dev; ino_t ino; DirId(dev_t d, ino_t i) : dev(d), ino(i) {} - bool operator<(const DirId& r) const - { - return dev < r.dev || (dev == r.dev && ino < r.ino); + bool operator<(const DirId& r) const { + return dev < r.dev || (dev == r.dev && ino < r.ino); } }; #endif @@ -62,7 +61,7 @@ public: class FsTreeWalker::Internal { public: Internal(int opts) - : options(opts), depthswitch(4), maxdepth(-1), errors(0) { + : options(opts), depthswitch(4), maxdepth(-1), errors(0) { } int options; int depthswitch; @@ -80,9 +79,9 @@ public: set donedirs; #endif void logsyserr(const char *call, const string ¶m) { - errors++; - reason << call << "(" << param << ") : " << errno << " : " << - strerror(errno) << endl; + errors++; + reason << call << "(" << param << ") : " << errno << " : " << + strerror(errno) << endl; } }; @@ -99,15 +98,15 @@ FsTreeWalker::~FsTreeWalker() void FsTreeWalker::setOpts(int opts) { if (data) { - data->options = opts; + data->options = opts; } } int FsTreeWalker::getOpts() { if (data) { - return data->options; + return data->options; } else { - return 0; + return 0; } } void FsTreeWalker::setDepthSwitch(int ds) @@ -139,8 +138,8 @@ int FsTreeWalker::getErrCnt() bool FsTreeWalker::addSkippedName(const string& pattern) { if (find(data->skippedNames.begin(), - data->skippedNames.end(), pattern) == data->skippedNames.end()) - data->skippedNames.push_back(pattern); + data->skippedNames.end(), pattern) == data->skippedNames.end()) + data->skippedNames.push_back(pattern); return true; } bool FsTreeWalker::setSkippedNames(const vector &patterns) @@ -151,9 +150,9 @@ bool FsTreeWalker::setSkippedNames(const vector &patterns) bool FsTreeWalker::inSkippedNames(const string& name) { for (const auto& pattern : data->skippedNames) { - if (fnmatch(pattern.c_str(), name.c_str(), 0) == 0) { - return true; - } + if (fnmatch(pattern.c_str(), name.c_str(), 0) == 0) { + return true; + } } return false; } @@ -169,9 +168,9 @@ bool FsTreeWalker::inOnlyNames(const string& name) return true; } for (const auto& pattern : data->onlyNames) { - if (fnmatch(pattern.c_str(), name.c_str(), 0) == 0) { - return true; - } + if (fnmatch(pattern.c_str(), name.c_str(), 0) == 0) { + return true; + } } return false; } @@ -180,15 +179,15 @@ bool FsTreeWalker::addSkippedPath(const string& ipath) { string path = (data->options & FtwNoCanon) ? ipath : path_canon(ipath); if (find(data->skippedPaths.begin(), - data->skippedPaths.end(), path) == data->skippedPaths.end()) - data->skippedPaths.push_back(path); + data->skippedPaths.end(), path) == data->skippedPaths.end()) + data->skippedPaths.push_back(path); return true; } bool FsTreeWalker::setSkippedPaths(const vector &paths) { data->skippedPaths = paths; for (vector::iterator it = data->skippedPaths.begin(); - it != data->skippedPaths.end(); it++) + it != data->skippedPaths.end(); it++) if (!(data->options & FtwNoCanon)) *it = path_canon(*it); return true; @@ -202,7 +201,7 @@ bool FsTreeWalker::inSkippedPaths(const string& path, bool ckparents) #endif for (vector::const_iterator it = data->skippedPaths.begin(); - it != data->skippedPaths.end(); it++) { + it != data->skippedPaths.end(); it++) { #ifndef FNM_LEADING_DIR if (ckparents) { string mpath = path; @@ -213,9 +212,9 @@ bool FsTreeWalker::inSkippedPaths(const string& path, bool ckparents) } } else #endif /* FNM_LEADING_DIR */ - if (fnmatch(it->c_str(), path.c_str(), fnmflags) == 0) { - return true; - } + if (fnmatch(it->c_str(), path.c_str(), fnmflags) == 0) { + return true; + } } return false; } @@ -230,7 +229,7 @@ static inline int slashcount(const string& p) } FsTreeWalker::Status FsTreeWalker::walk(const string& _top, - FsTreeWalkerCB& cb) + FsTreeWalkerCB& cb) { string top = (data->options & FtwNoCanon) ? _top : path_canon(_top); @@ -242,10 +241,10 @@ FsTreeWalker::Status FsTreeWalker::walk(const string& _top, struct stat st; // We always follow symlinks at this point. Makes more sense. if (path_fileprops(top, &st) == -1) { - // Note that we do not return an error if the stat call - // fails. A temp file may have gone away. - data->logsyserr("stat", top); - return errno == ENOENT ? FtwOk : FtwError; + // Note that we do not return an error if the stat call + // fails. A temp file may have gone away. + data->logsyserr("stat", top); + return errno == ENOENT ? FtwOk : FtwError; } // Recursive version, using the call stack to store state. iwalk @@ -305,7 +304,7 @@ FsTreeWalker::Status FsTreeWalker::walk(const string& _top, if (!nfather.empty()) { if (path_fileprops(nfather, &st) == -1) { data->logsyserr("stat", nfather); - return errno == ENOENT ? FtwOk : FtwError; + return errno == ENOENT ? FtwOk : FtwError; } if ((status = cb.processone(nfather, &st, FtwDirReturn)) & (FtwStop|FtwError)) { @@ -315,7 +314,7 @@ FsTreeWalker::Status FsTreeWalker::walk(const string& _top, if (path_fileprops(dir, &st) == -1) { data->logsyserr("stat", dir); - return errno == ENOENT ? FtwOk : FtwError; + return errno == ENOENT ? FtwOk : FtwError; } // iwalk will not recurse in this case, just process file entries // and append subdir entries to the queue. @@ -344,29 +343,28 @@ FsTreeWalker::Status FsTreeWalker::walk(const string& _top, // This means that we always go into the top 'walk()' parameter if it is a // directory, even if norecurse is set. Bug or Feature ? FsTreeWalker::Status FsTreeWalker::iwalk(const string &top, - struct stat *stp, - FsTreeWalkerCB& cb) + struct stat *stp, + FsTreeWalkerCB& cb) { Status status = FtwOk; bool nullpush = false; // Tell user to process the top entry itself if (S_ISDIR(stp->st_mode)) { - if ((status = cb.processone(top, stp, FtwDirEnter)) & - (FtwStop|FtwError)) { - return status; - } + if ((status = cb.processone(top, stp, FtwDirEnter)) & + (FtwStop|FtwError)) { + return status; + } } else if (S_ISREG(stp->st_mode)) { - return cb.processone(top, stp, FtwRegular); + return cb.processone(top, stp, FtwRegular); } else { - return status; + return status; } - int curdepth = slashcount(top) - data->basedepth; if (data->maxdepth >= 0 && curdepth >= data->maxdepth) { - LOGDEB1("FsTreeWalker::iwalk: Maxdepth reached: [" << (top) << "]\n" ); - return status; + LOGDEB1("FsTreeWalker::iwalk: Maxdepth reached: [" << (top) << "]\n" ); + return status; } // This is a directory, read it and process entries: @@ -379,36 +377,48 @@ FsTreeWalker::Status FsTreeWalker::iwalk(const string &top, // For now, we'll ignore the "other kind of cycle" part and only monitor // this is FtwFollow is set if (data->options & FtwFollow) { - DirId dirid(stp->st_dev, stp->st_ino); - if (data->donedirs.find(dirid) != data->donedirs.end()) { - LOGINFO("Not processing [" << top << + DirId dirid(stp->st_dev, stp->st_ino); + if (data->donedirs.find(dirid) != data->donedirs.end()) { + LOGINFO("Not processing [" << top << "] (already seen as other path)\n"); - return status; - } - data->donedirs.insert(dirid); + return status; + } + data->donedirs.insert(dirid); } #endif + SYSPATH(top, systop); DIRHDL *d = OPENDIR(systop); - if (d == 0) { - data->logsyserr("opendir", top); - switch (errno) { - case EPERM: - case EACCES: - case ENOENT: + if (nullptr == d) { + data->logsyserr("opendir", top); +#ifdef _WIN32 + int rc = GetLastError(); + LOGERR("opendir failed: LastError " << rc << endl); + if (rc == ERROR_NETNAME_DELETED) { + // 64: share disconnected. + // Not too sure of the errno in this case. + // Make sure it's not one of the permissible ones + errno = ENODEV; + } +#endif + switch (errno) { + case EPERM: + case EACCES: + case ENOENT: #ifdef _WIN32 // We get this quite a lot, don't know why. To be checked. case EINVAL: #endif - goto out; - default: - status = FtwError; - goto out; - } + // No error set: indexing will continue in other directories + goto out; + default: + status = FtwError; + goto out; + } } struct DIRENT *ent; - while ((ent = READDIR(d)) != 0) { + while (errno = 0, ((ent = READDIR(d)) != 0)) { string fn; struct stat st; #ifdef _WIN32 @@ -421,22 +431,30 @@ FsTreeWalker::Status FsTreeWalker::iwalk(const string &top, #else const char *dname = ent->d_name; #endif - // Maybe skip dotfiles - if ((data->options & FtwSkipDotFiles) && dname[0] == '.') - continue; - // Skip . and .. - if (!strcmp(dname, ".") || !strcmp(dname, "..")) - continue; + // Maybe skip dotfiles + if ((data->options & FtwSkipDotFiles) && dname[0] == '.') + continue; + // Skip . and .. + if (!strcmp(dname, ".") || !strcmp(dname, "..")) + continue; - // Skipped file names match ? - if (!data->skippedNames.empty()) { - if (inSkippedNames(dname)) - continue; - } + // Skipped file names match ? + if (!data->skippedNames.empty()) { + if (inSkippedNames(dname)) + continue; + } fn = path_cat(top, dname); int statret = path_fileprops(fn.c_str(), &st, data->options&FtwFollow); if (statret == -1) { data->logsyserr("stat", fn); +#ifdef _WIN32 + int rc = GetLastError(); + LOGERR("stat failed: LastError " << rc << endl); + if (rc == ERROR_NETNAME_DELETED) { + status = FtwError; + goto out; + } +#endif continue; } @@ -465,7 +483,7 @@ FsTreeWalker::Status FsTreeWalker::iwalk(const string &top, // with generating DirReturn callbacks if (!nullpush) { if (!data->dirs.empty() && - !data->dirs.back().empty()) + !data->dirs.back().empty()) data->dirs.push_back(cstr_null); nullpush = true; } @@ -493,10 +511,22 @@ FsTreeWalker::Status FsTreeWalker::iwalk(const string &top, } // We ignore other file types (devices etc...) } // readdir loop + if (errno) { + // Actual readdir error, not eof. + data->logsyserr("readdir", top); +#ifdef _WIN32 + int rc = GetLastError(); + LOGERR("Readdir failed: LastError " << rc << endl); + if (rc == ERROR_NETNAME_DELETED) { + status = FtwError; + goto out; + } +#endif + } - out: +out: if (d) - CLOSEDIR(d); + CLOSEDIR(d); return status; }