return FilePtr(new File(fpath, parentdir, type, std::move(statptr)));
}
+std::map<std::string, std::mutex> FsVisitor::m_locks;
+std::mutex FsVisitor::m_lockMapCleanMutex;
+
+void FsVisitor::cleanLocks()
+{
+ std::lock_guard<std::mutex> l(FsVisitor::m_lockMapCleanMutex);
+ FsVisitor::m_locks.clear();
+}
+
+FsVisitor::Dir::Dir(const std::string &dirname) : m_dir(nullptr), m_dirname(dirname)
+{
+ if (isInBlackList(this->m_dirname))
+ throw std::invalid_argument("dirname is in blacklist.");
+
+ DEBUG("try lock() on dir: " << this->m_dirname);
+ // get lock ownership before open directory stream
+ FsVisitor::m_locks[this->m_dirname].lock();
+ DEBUG("get lock for dir: " << this->m_dirname);
+
+ if ((this->m_dir = ::opendir(this->m_dirname.c_str())) == nullptr) {
+ // release lock before throw exception because dtor won't be called.
+ FsVisitor::m_locks[this->m_dirname].unlock();
+ throw std::invalid_argument("cannot open dir");
+ }
+}
+
+FsVisitor::Dir::~Dir()
+{
+ ::closedir(this->m_dir);
+ FsVisitor::m_locks[this->m_dirname].unlock();
+ DEBUG("unlocked with closedir: " << this->m_dirname);
+}
+
+DIR *FsVisitor::Dir::get()
+{
+ return this->m_dir;
+}
+
FsVisitor::DirPtr FsVisitor::openDir(const std::string &dir)
{
- return std::unique_ptr<DIR, int(*)(DIR *)>(
- (isInBlackList(dir) ? nullptr : ::opendir(dir.c_str())), ::closedir);
+ try {
+ return DirPtr(new FsVisitor::Dir(dir));
+ } catch (...) {
+ return DirPtr(nullptr);
+ }
}
FsVisitorPtr FsVisitor::create(TargetHandler &&targetHandler,
FsVisitor::FsVisitor(TargetHandler &&targetHandler, const std::string &dirpath,
bool isBasedOnName, time_t modifiedSince) :
m_targetHandler(std::move(targetHandler)), m_path(dirpath),
- m_since(modifiedSince), m_isDone(true), m_isBasedOnName(isBasedOnName),
- m_entryBuf(static_cast<struct dirent *>(::malloc(offsetof(struct dirent, d_name) + NAME_MAX + 1)))
+ m_since(modifiedSince), m_isDone(true), m_isBasedOnName(isBasedOnName)
{
- if (this->m_entryBuf == nullptr)
- throw std::bad_alloc();
}
FsVisitor::~FsVisitor()
{
- ::free(this->m_entryBuf);
}
void FsVisitor::run(const DirPtr &dirptr, const FilePtr ¤tdir)
{
- struct dirent *result = nullptr;
while (true) {
- auto ret = ::readdir_r(dirptr.get(), this->m_entryBuf, &result);
- if (ret != 0) {
- WARN("readdir_r error on dir: " << currentdir->getPath() <<
- " with errno: " << errno << ". Silently ignore this error & dir stream"
- " to reduce side-effect of traversing all the other file systems.");
- break;
- } else if (result == nullptr) {
- DEBUG("End of stream of dir: " << currentdir->getPath());
+ errno = 0;
+ auto result = ::readdir(dirptr->get());
+ if (result == nullptr) {
+ if (errno != 0)
+ WARN("readdir error on dir: " << currentdir->getPath() <<
+ " with errno: " << errno << ". Silently ignore this error &"
+ " dir stream to reduce side-effect of traversing all the other"
+ " file systems.");
+ else
+ DEBUG("end of stream: " << currentdir->getPath());
+
break;
}
(name_size == 2 && name[0] == '.' && name[1] == '.'))
continue;
- auto ndirptr = openDir(fullpath);
- if (ndirptr == nullptr) {
- WARN("Failed to open dir: " << fullpath << " with errno: " << errno);
- continue;
- }
-
FilePtr ncurrentdir;
try {
ncurrentdir = File::create(fullpath, currentdir);
if (this->m_isBasedOnName && ncurrentdir->isInApp()) {
this->m_targetHandler(ncurrentdir);
} else {
+ auto ndirptr = openDir(fullpath);
+ if (ndirptr == nullptr) {
+ WARN("Failed to open dir: " << fullpath << " with errno: " << errno);
+ continue;
+ }
+
DEBUG("recurse dir : " << fullpath);
this->run(ndirptr, ncurrentdir);
}
void FsVisitor::run()
{
+ auto currentdir = File::create(this->m_path, nullptr);
+
+ INFO("Visiting files start from dir: " << this->m_path);
+
+ if (this->m_isBasedOnName && currentdir->isInApp()) {
+ this->m_targetHandler(currentdir);
+ return;
+ }
+
auto dirptr = openDir(this->m_path);
if (dirptr == nullptr) {
}
}
- auto currentdir = File::create(this->m_path, nullptr);
-
- INFO("Visiting files start from dir: " << this->m_path);
-
- if (this->m_isBasedOnName && currentdir->isInApp())
- this->m_targetHandler(currentdir);
- else
- this->run(dirptr, currentdir);
+ this->run(dirptr, currentdir);
}
} // namespace Csr
#include <memory>
#include <queue>
#include <functional>
+#include <mutex>
+#include <map>
#include <cstddef>
#include <ctime>
const std::string &dirpath, bool isBasedOnName,
time_t modifiedSince = -1);
+ // clean up per directory lock map.
+ // Warning: should be called only no directory is traversed at the time.
+ // this is essential because mutex exists per directory and it can be
+ // stacked a lot because it won't be freed unless process is terminated.
+ static void cleanLocks();
+
private:
- using DirPtr = std::unique_ptr<DIR, int(*)(DIR *)>;
+ class Dir {
+ public:
+ Dir(const std::string &dirname);
+ ~Dir();
+ DIR *get();
+
+ private:
+ DIR *m_dir;
+ std::string m_dirname;
+ };
+
+ using DirPtr = std::unique_ptr<Dir>;
void run(const DirPtr &dirptr, const FilePtr ¤tdir);
FsVisitor(TargetHandler &&targetHandler, const std::string &dirpath,
bool isBasedOnName, time_t modifiedSince = -1);
+ static std::map<std::string, std::mutex> m_locks;
+ static std::mutex m_lockMapCleanMutex;
+
TargetHandler m_targetHandler;
std::string m_path;
time_t m_since;
bool m_isDone;
bool m_isBasedOnName;
- struct dirent *m_entryBuf;
};
} // namespace Csr