Windows: tree walk ends error if share discon. detected: no purge will be performed

This commit is contained in:
Jean-Francois Dockes 2019-09-01 08:45:21 +02:00
parent 85a3291fd7
commit c1d593b104

View File

@ -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<DirId> donedirs;
#endif
void logsyserr(const char *call, const string &param) {
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<string> &patterns)
@ -151,9 +150,9 @@ bool FsTreeWalker::setSkippedNames(const vector<string> &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<string> &paths)
{
data->skippedPaths = paths;
for (vector<string>::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<string>::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;
}