/*
* Win32FileScanner.cpp
*
* Copyright (C) 2009-19 by RStudio, PBC
*
* Unless you have received this program directly from RStudio pursuant
* to the terms of a commercial license agreement with RStudio, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
#include
#include
#include
#include
#include
#include
#include
namespace rstudio {
namespace core {
namespace system {
namespace {
FileInfo convertToFileInfo(const FilePath& filePath, bool yield, int *pCount)
{
// yield every 10 files (defend against pegging the cpu for directories
// with a huge number of files)
if (yield)
{
*pCount = *pCount + 1;
if (*pCount % 10 == 0)
boost::this_thread::yield();
}
if (filePath.isDirectory())
{
return FileInfo(filePath.getAbsolutePath(), true, filePath.isSymlink());
}
else if (filePath.exists())
{
return FileInfo(filePath.getAbsolutePath(),
false,
filePath.getSize(),
filePath.getLastWriteTime(),
filePath.isSymlink());
}
else
{
return FileInfo(filePath.getAbsolutePath(), false);
}
}
} // anonymous namespace
// NOTE: we bail with an error if the top level directory can't be
// enumerated however we merely log errors for children. this reflects
// the notion that a top-level failure will report major problems
// (e.g. permission to access a volume/drive) whereas errors which
// occur in children are more likely to refect some idiosyncratic
// problem with a child dir or file, and we don't want that to
// interfere with the caller getting a listing of everything else
// and proceeding with its work
Error scanFiles(const tree::iterator_base& fromNode,
const core::system::FileScannerOptions& options,
tree* pTree)
{
// clear all existing
pTree->erase_children(fromNode);
// create FilePath for root
FilePath rootPath(fromNode->absolutePath());
// yield if requested (only applies to recursive scans)
if (options.recursive && options.yield)
boost::this_thread::yield();
// call onBeforeScanDir hook
if (options.onBeforeScanDir)
{
Error error = options.onBeforeScanDir(*fromNode);
if (error)
return error;
}
// read directory entries
std::vector children;
Error error = rootPath.getChildren(children);
if (error)
return error;
// convert to FileInfo and sort using alphasort equivilant (for
// compatability with scandir, which is what is used in our
// posix-specific implementation
int count = 0;
std::vector childrenFileInfo;
std::transform(children.begin(),
children.end(),
std::back_inserter(childrenFileInfo),
boost::bind(convertToFileInfo, _1, options.yield, &count));
std::sort(childrenFileInfo.begin(),
childrenFileInfo.end(),
fileInfoPathLessThan);
// iterate over entries
for (const FileInfo& childFileInfo : childrenFileInfo)
{
// apply filter if we have one
if (options.filter && !options.filter(childFileInfo))
continue;
// add the correct type of FileEntry
if (childFileInfo.isDirectory())
{
tree::iterator_base child =
pTree->append_child(fromNode, childFileInfo);
if (options.recursive && !childFileInfo.isSymlink())
{
Error error = scanFiles(child, options, pTree);
if (error && (error != boost::system::windows_error::path_not_found))
LOG_ERROR(error);
}
}
else
{
pTree->append_child(fromNode, childFileInfo);
}
}
// return success
return Success();
}
} // namespace system
} // namespace core
} // namespace rstudio