diff options
author | Daniel Wilhelm <shieldwed@outlook.com> | 2017-02-13 21:25:04 -0700 |
---|---|---|
committer | Daniel Wilhelm <shieldwed@outlook.com> | 2017-02-13 21:25:04 -0700 |
commit | 9d071d2a2cec9a7662a02669488569a017f0ea35 (patch) | |
tree | c83a623fbdff098339b66d21ea2e81f3f67344ae /zen/file_traverser.cpp | |
parent | 8.8 (diff) | |
download | FreeFileSync-9d071d2a2cec9a7662a02669488569a017f0ea35.tar.gz FreeFileSync-9d071d2a2cec9a7662a02669488569a017f0ea35.tar.bz2 FreeFileSync-9d071d2a2cec9a7662a02669488569a017f0ea35.zip |
8.9
Diffstat (limited to 'zen/file_traverser.cpp')
-rwxr-xr-x[-rw-r--r--] | zen/file_traverser.cpp | 299 |
1 files changed, 105 insertions, 194 deletions
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index ef6d255c..d5e3912b 100644..100755 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -1,194 +1,105 @@ -// ***************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * -// ***************************************************************************** - -#include "file_traverser.h" -#include "file_error.h" - -#ifdef ZEN_WIN - #include "int64.h" - #include "long_path_prefix.h" - #include "file_access.h" - #include "symlink_target.h" -#elif defined ZEN_MAC - #include "osx_string.h" -#endif - -#if defined ZEN_LINUX || defined ZEN_MAC - #include <cstddef> //offsetof - #include <unistd.h> //::pathconf() - #include <sys/stat.h> - #include <dirent.h> -#endif - -using namespace zen; - - -void zen::traverseFolder(const Zstring& dirPath, - const std::function<void (const FileInfo& fi)>& onFile, - const std::function<void (const FolderInfo& fi)>& onFolder, - const std::function<void (const SymlinkInfo& si)>& onSymlink, - const std::function<void (const std::wstring& errorMsg)>& onError) -{ - try - { -#ifdef ZEN_WIN - WIN32_FIND_DATA findData = {}; - HANDLE hDir = ::FindFirstFile(applyLongPathPrefix(appendSeparator(dirPath) + L'*').c_str(), &findData); - if (hDir == INVALID_HANDLE_VALUE) - { - const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! - if (ec == ERROR_FILE_NOT_FOUND) - try - { - //1. directory may not exist *or* 2. it is completely empty: not all directories contain "., .." entries, e.g. a drive's root directory; NetDrive - // -> FindFirstFile() is a nice example of violating the API design principle of single responsibility - if (getItemType(dirPath) == ItemType::FOLDER) //throw FileError - return; - } - catch (FileError&) {} //previous exception is more relevant - - throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), formatSystemError(L"FindFirstFile", ec)); - } - ZEN_ON_SCOPE_EXIT(::FindClose(hDir)); - - bool firstIteration = true; - for (;;) - { - if (firstIteration) //keep ::FindNextFile at the start of the for-loop to support "continue"! - firstIteration = false; - else if (!::FindNextFile(hDir, &findData)) - { - const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! - if (ec == ERROR_NO_MORE_FILES) //not an error situation - return; - //else we have a problem... report it: - throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), formatSystemError(L"FindNextFile", ec)); - } - - //skip "." and ".." - const wchar_t* const itemNameRaw = findData.cFileName; - - if (itemNameRaw[0] == L'.' && - (itemNameRaw[1] == 0 || (itemNameRaw[1] == L'.' && itemNameRaw[2] == 0))) - continue; - - if (itemNameRaw[0] == 0) - throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item with empty name."); - - const Zstring& itemName = itemNameRaw; - const Zstring& itemPath = appendSeparator(dirPath) + itemName; - - if (zen::isSymlink(findData)) //check first! - { - if (onSymlink) - onSymlink({ itemName, itemPath, filetimeToTimeT(findData.ftLastWriteTime) }); - } - else if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) - { - if (onFolder) - onFolder({ itemName, itemPath }); - } - else //a file - { - if (onFile) - onFile({ itemName, itemPath, get64BitUInt(findData.nFileSizeLow, findData.nFileSizeHigh), filetimeToTimeT(findData.ftLastWriteTime) }); - } - } - -#elif defined ZEN_LINUX || defined ZEN_MAC - /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede - that field within the dirent structure, portable applications that use readdir_r() should allocate - the buffer whose address is passed in entry as follows: - len = offsetof(struct dirent, d_name) + pathconf(dirPath, _PC_NAME_MAX) + 1 - entryp = malloc(len); */ - const size_t nameMax = std::max<long>(::pathconf(dirPath.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1) - std::vector<char> buffer(offsetof(struct ::dirent, d_name) + nameMax + 1); - - DIR* folder = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/" - if (!folder) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir"); - ZEN_ON_SCOPE_EXIT(::closedir(folder)); //never close nullptr handles! -> crash - - for (;;) - { - struct ::dirent* dirEntry = nullptr; - if (::readdir_r(folder, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r"); - //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/ - - if (!dirEntry) //no more items - return; - - //don't return "." and ".." - const char* itemNameRaw = dirEntry->d_name; - - if (itemNameRaw[0] == '.' && - (itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0))) - continue; - -#ifdef ZEN_MAC //normalize all text input (see see native_traverser_impl.h) - Zstring itemName; - try - { - itemName = osx::normalizeUtfForPosix(itemNameRaw); //throw SysError - } - catch (const SysError& e) //failure is not an item-level error since we don't know the normalized name yet!!! - { - throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), - L"Failed to generate normalized file name: " + fmtPath(itemNameRaw) + L"\n" + e.toString()); //too obscure to warrant translation - } -#else - const Zstring& itemName = itemNameRaw; -#endif - if (itemName.empty()) //checks result of osx::normalizeUtfForPosix, too! - throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name."); - - const Zstring& itemPath = appendSeparator(dirPath) + itemName; - - struct ::stat statData = {}; - try - { - if (::lstat(itemPath.c_str(), &statData) != 0) //lstat() does not resolve symlinks - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"lstat"); - } - catch (const FileError& e) - { - if (onError) - onError(e.toString()); - continue; //ignore error: skip file - } - - if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks! - { - if (onSymlink) - onSymlink({ itemName, itemPath, statData.st_mtime}); - } - else if (S_ISDIR(statData.st_mode)) //a directory - { - if (onFolder) - onFolder({ itemName, itemPath }); - } - else //a file or named pipe, ect. - { - if (onFile) - onFile({ itemName, itemPath, makeUnsigned(statData.st_size), statData.st_mtime }); - } - /* - It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios: - - RTS setup watch (essentially wants to read directories only) - - removeDirectory (wants to delete everything; pipes can be deleted just like files via "unlink") - - However an "open" on a pipe will block (https://sourceforge.net/p/freefilesync/bugs/221/), so the copy routines need to be smarter!! - */ - } -#endif - } - catch (const FileError& e) - { - if (onError) - onError(e.toString()); - } -} +// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "file_traverser.h"
+#include "file_error.h"
+
+
+ #include <cstddef> //offsetof
+ #include <unistd.h> //::pathconf()
+ #include <sys/stat.h>
+ #include <dirent.h>
+
+using namespace zen;
+
+
+void zen::traverseFolder(const Zstring& dirPath,
+ const std::function<void (const FileInfo& fi)>& onFile,
+ const std::function<void (const FolderInfo& fi)>& onFolder,
+ const std::function<void (const SymlinkInfo& si)>& onSymlink,
+ const std::function<void (const std::wstring& errorMsg)>& onError)
+{
+ try
+ {
+ /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede
+ that field within the dirent structure, portable applications that use readdir_r() should allocate
+ the buffer whose address is passed in entry as follows:
+ len = offsetof(struct dirent, d_name) + pathconf(dirPath, _PC_NAME_MAX) + 1
+ entryp = malloc(len); */
+ const size_t nameMax = std::max<long>(::pathconf(dirPath.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1)
+ std::vector<char> buffer(offsetof(struct ::dirent, d_name) + nameMax + 1);
+
+ DIR* folder = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/"
+ if (!folder)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir");
+ ZEN_ON_SCOPE_EXIT(::closedir(folder)); //never close nullptr handles! -> crash
+
+ for (;;)
+ {
+ struct ::dirent* dirEntry = nullptr;
+ if (::readdir_r(folder, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r");
+ //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/
+
+ if (!dirEntry) //no more items
+ return;
+
+ //don't return "." and ".."
+ const char* itemNameRaw = dirEntry->d_name;
+
+ if (itemNameRaw[0] == '.' &&
+ (itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0)))
+ continue;
+
+ const Zstring& itemName = itemNameRaw;
+ if (itemName.empty()) //checks result of osx::normalizeUtfForPosix, too!
+ throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name.");
+
+ const Zstring& itemPath = appendSeparator(dirPath) + itemName;
+
+ struct ::stat statData = {};
+ try
+ {
+ if (::lstat(itemPath.c_str(), &statData) != 0) //lstat() does not resolve symlinks
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"lstat");
+ }
+ catch (const FileError& e)
+ {
+ if (onError)
+ onError(e.toString());
+ continue; //ignore error: skip file
+ }
+
+ if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks!
+ {
+ if (onSymlink)
+ onSymlink({ itemName, itemPath, statData.st_mtime});
+ }
+ else if (S_ISDIR(statData.st_mode)) //a directory
+ {
+ if (onFolder)
+ onFolder({ itemName, itemPath });
+ }
+ else //a file or named pipe, ect.
+ {
+ if (onFile)
+ onFile({ itemName, itemPath, makeUnsigned(statData.st_size), statData.st_mtime });
+ }
+ /*
+ It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios:
+ - RTS setup watch (essentially wants to read directories only)
+ - removeDirectory (wants to delete everything; pipes can be deleted just like files via "unlink")
+
+ However an "open" on a pipe will block (https://sourceforge.net/p/freefilesync/bugs/221/), so the copy routines need to be smarter!!
+ */
+ }
+ }
+ catch (const FileError& e)
+ {
+ if (onError)
+ onError(e.toString());
+ }
+}
|