From 9084fa27f0f43cfa31dbc3a7ef87e2600c2dc3ca Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Fri, 18 Apr 2014 16:56:34 +0200 Subject: 1.16 --- library/fileHandling.cpp | 342 +++++++++++++++++++++++++++++------------------ 1 file changed, 210 insertions(+), 132 deletions(-) (limited to 'library/fileHandling.cpp') diff --git a/library/fileHandling.cpp b/library/fileHandling.cpp index 688d3640..cda81ef5 100644 --- a/library/fileHandling.cpp +++ b/library/fileHandling.cpp @@ -1,19 +1,13 @@ #include "fileHandling.h" #include #include -#include "resources.h" +#include "../algorithm.h" +#include #ifdef FFS_WIN #include //includes "windows.h" #endif // FFS_WIN -inline -bool endsWithPathSeparator(const Zstring& name) -{ - const size_t len = name.length(); - return len && (name[len - 1] == GlobalResources::FILE_NAME_SEPARATOR); -} - class RecycleBin { @@ -89,19 +83,30 @@ void FreeFileSync::removeFile(const Zstring& filename, const bool useRecycleBin) if (useRecycleBin) { if (!moveToRecycleBin(filename)) - throw FileError(Zstring(_("Error moving to recycle bin:")) + wxT(" \"") + filename + wxT("\"")); + throw FileError(Zstring(_("Error moving to Recycle Bin:")) + wxT("\n\"") + filename + wxT("\"")); return; } #ifdef FFS_WIN + //initialize file attributes if (!SetFileAttributes( - filename.c_str(), //address of filename - FILE_ATTRIBUTE_NORMAL //attributes to set - )) throw FileError(Zstring(_("Error deleting file:")) + wxT(" \"") + filename + wxT("\"")); -#endif //FFS_WIN + filename.c_str(), //address of filename + FILE_ATTRIBUTE_NORMAL)) //attributes to set + { + Zstring errorMessage = Zstring(_("Error deleting file:")) + wxT("\n\"") + filename + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } + //remove file, support for \\?\-prefix + if (DeleteFile(filename.c_str()) == 0) + { + Zstring errorMessage = Zstring(_("Error deleting file:")) + wxT("\n\"") + filename + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } +#else if (!wxRemoveFile(filename.c_str())) - throw FileError(Zstring(_("Error deleting file:")) + wxT(" \"") + filename + wxT("\"")); + throw FileError(Zstring(_("Error deleting file:")) + wxT("\n\"") + filename + wxT("\"")); +#endif } @@ -112,53 +117,65 @@ void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecyc if (useRecycleBin) { if (!moveToRecycleBin(directory)) - throw FileError(Zstring(_("Error moving to recycle bin:")) + wxT(" \"") + directory + wxT("\"")); + throw FileError(Zstring(_("Error moving to Recycle Bin:")) + wxT("\n\"") + directory + wxT("\"")); return; } std::vector fileList; std::vector dirList; - try - { //should be executed in own scope so that directory access does not disturb deletion! - getAllFilesAndDirs(directory, fileList, dirList); - } - catch (const FileError& e) - { - throw FileError(Zstring(_("Error deleting directory:")) + wxT(" \"") + directory + wxT("\"")); - } + getAllFilesAndDirs(directory, fileList, dirList); for (unsigned int j = 0; j < fileList.size(); ++j) removeFile(fileList[j], false); - dirList.insert(dirList.begin(), directory); //this directory will be deleted last + dirList.insert(dirList.begin(), directory); //parent directory will be deleted last for (int j = int(dirList.size()) - 1; j >= 0 ; --j) { #ifdef FFS_WIN + //initialize file attributes if (!SetFileAttributes( dirList[j].c_str(), // address of directory name FILE_ATTRIBUTE_NORMAL)) // attributes to set - throw FileError(Zstring(_("Error deleting directory:")) + wxT(" \"") + dirList[j] + wxT("\"")); -#endif // FFS_WIN + { + Zstring errorMessage = Zstring(_("Error deleting directory:")) + wxT("\n\"") + dirList[j] + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } + //remove directory, support for \\?\-prefix + if (RemoveDirectory(dirList[j].c_str()) == 0) + { + Zstring errorMessage = Zstring(_("Error deleting directory:")) + wxT("\n\"") + dirList[j] + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } +#else if (!wxRmdir(dirList[j].c_str())) - throw FileError(Zstring(_("Error deleting directory:")) + wxT(" \"") + dirList[j] + wxT("\"")); + throw FileError(Zstring(_("Error deleting directory:")) + wxT("\n\"") + dirList[j] + wxT("\"")); +#endif } } void FreeFileSync::createDirectory(const Zstring& directory, const int level) { - if (wxDirExists(directory)) + if (wxDirExists(directory.c_str())) return; if (level == 50) //catch endless recursion return; - //try to create directory - if (wxMkdir(directory.c_str())) + //try to create directory, support for \\?\-prefix +#ifdef FFS_WIN + if (CreateDirectory( + directory.c_str(), // pointer to a directory path string + NULL // pointer to a security descriptor + ) != 0) + return; +#else + if (wxMkdir(directory.c_str())) //wxMkDir has different signature under Linux! return; +#endif //if not successfull try to create parent folders first Zstring parentDir; @@ -176,11 +193,25 @@ void FreeFileSync::createDirectory(const Zstring& directory, const int level) createDirectory(parentDir, level + 1); //now creation should be possible - if (!wxMkdir(directory.c_str())) +#ifdef FFS_WIN + if (CreateDirectory( + directory.c_str(), // pointer to a directory path string + NULL // pointer to a security descriptor + ) == 0) + { + if (level == 0) + { + Zstring errorMessage = Zstring(_("Error creating directory:")) + wxT("\n\"") + directory + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } + } +#else + if (!wxMkdir(directory.c_str())) //wxMkDir has different signature under Linux! { if (level == 0) - throw FileError(Zstring(_("Error creating directory:")) + wxT(" \"") + directory + wxT("\"")); + throw FileError(Zstring(_("Error creating directory:")) + wxT("\n\"") + directory + wxT("\"")); } +#endif } @@ -189,41 +220,44 @@ void FreeFileSync::copyFolderAttributes(const Zstring& source, const Zstring& ta #ifdef FFS_WIN DWORD attr = GetFileAttributes(source.c_str()); // address of the name of a file or directory if (attr == 0xFFFFFFFF) - throw FileError(Zstring(_("Error reading folder attributes:")) + wxT(" \"") + source + wxT("\"")); + { + Zstring errorMessage = Zstring(_("Error reading folder attributes:")) + wxT("\n\"") + source + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } if (!SetFileAttributes( target.c_str(), // address of filename attr)) // address of attributes to set - throw FileError(Zstring(_("Error writing folder attributes:")) + wxT(" \"") + target + wxT("\"")); + { + Zstring errorMessage = Zstring(_("Error writing folder attributes:")) + wxT("\n\"") + target + wxT("\""); + throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } #elif defined FFS_LINUX //Linux doesn't use attributes for files or folders #endif } -class GetAllFilesSimple : public wxDirTraverser +class FilesDirsOnlyTraverser : public FreeFileSync::FullDetailFileTraverser { public: - GetAllFilesSimple(std::vector& files, std::vector& subDirectories) : + FilesDirsOnlyTraverser(std::vector& files, std::vector& dirs) : m_files(files), - m_dirs(subDirectories) {} + m_dirs(dirs) {} - wxDirTraverseResult OnDir(const wxString& dirname) + virtual wxDirTraverseResult OnFile(const Zstring& filename, const FreeFileSync::FileInfo& details) { - m_dirs.push_back(dirname.c_str()); + m_files.push_back(filename); return wxDIR_CONTINUE; } - - wxDirTraverseResult OnFile(const wxString& filename) + virtual wxDirTraverseResult OnDir(const Zstring& dirname) { - m_files.push_back(filename.c_str()); + m_dirs.push_back(dirname); return wxDIR_CONTINUE; } - - wxDirTraverseResult OnOpenError(const wxString& openerrorname) + virtual wxDirTraverseResult OnError(const Zstring& errorText) { - wxMessageBox(openerrorname, _("Error")); - return wxDIR_IGNORE; + throw FileError(errorText); } private: @@ -238,11 +272,8 @@ void FreeFileSync::getAllFilesAndDirs(const Zstring& sourceDir, std::vectorOnError(Zstring(_("Could not retrieve file info for:")) + wxT(" \"") + filename.c_str() + wxT("\"")); + return m_sink->OnError(Zstring(_("Could not retrieve file info for:")) + wxT("\n\"") + filename.c_str() + wxT("\"")); FreeFileSync::FileInfo details; details.lastWriteTimeRaw = fileInfo.st_mtime; //UTC time(ANSI C format); unit: 1 second @@ -310,115 +341,162 @@ private: #endif -bool FreeFileSync::traverseInDetail(const Zstring& directory, FullDetailFileTraverser* sink, const int level) +class TraverseRecursively { -#ifdef FFS_WIN - if (level == 50) //catch endless recursion +public: + TraverseRecursively(const bool traverseSymbolicLinks, FreeFileSync::FullDetailFileTraverser* sink) : + m_traverseSymbolicLinks(traverseSymbolicLinks), + m_sink(sink) {} + + bool traverse(const Zstring& directory, const int level) { - if (sink->OnError(Zstring(_("Error traversing directory:")) + wxT(" ") + directory) == wxDIR_STOP) - return false; - else - return true; - } +#ifdef FFS_WIN + if (level == 50) //catch endless recursion + { + if (m_sink->OnError(Zstring(_("Error traversing directory:")) + wxT("\n\"") + directory + wxT("\"")) == wxDIR_STOP) + return false; + else + return true; + } - Zstring directoryFormatted = directory; - if (!endsWithPathSeparator(directoryFormatted)) - directoryFormatted += GlobalResources::FILE_NAME_SEPARATOR; + Zstring directoryFormatted = directory; + if (!FreeFileSync::endsWithPathSeparator(directoryFormatted)) + directoryFormatted += GlobalResources::FILE_NAME_SEPARATOR; - const Zstring filespec = directoryFormatted + wxT("*.*"); + const Zstring filespec = directoryFormatted + DefaultChar('*'); - WIN32_FIND_DATA fileMetaData; - HANDLE searchHandle = FindFirstFile(filespec.c_str(), //pointer to name of file to search for - &fileMetaData); //pointer to returned information + WIN32_FIND_DATA fileMetaData; + HANDLE searchHandle = FindFirstFile(filespec.c_str(), //pointer to name of file to search for + &fileMetaData); //pointer to returned information - if (searchHandle == INVALID_HANDLE_VALUE) - { - if (GetLastError() == ERROR_FILE_NOT_FOUND) - return true; - //else: we have a problem... - if (sink->OnError(Zstring(_("Error traversing directory:")) + wxT(" ") + directoryFormatted) == wxDIR_STOP) - return false; - else - return true; - } - CloseOnExit dummy(searchHandle); - - do - { //don't return "." and ".." - const wxChar* name = fileMetaData.cFileName; - if ( name[0] == wxChar('.') && - ((name[1] == wxChar('.') && name[2] == wxChar('\0')) || - name[1] == wxChar('\0'))) - continue; - - const Zstring fullName = directoryFormatted + name; - if (fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //a directory... + if (searchHandle == INVALID_HANDLE_VALUE) { - switch (sink->OnDir(fullName)) + const DWORD lastError = GetLastError(); + if (lastError == ERROR_FILE_NOT_FOUND) + return true; + + //else: we have a problem... report it: + Zstring errorMessage = Zstring(_("Error traversing directory:")) + wxT("\n\"") + directory + wxT("\" ") ; + if (m_sink->OnError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted(lastError)) == wxDIR_STOP) + return false; + else + return true; + } + CloseOnExit dummy(searchHandle); + + do + { //don't return "." and ".." + const wxChar* name = fileMetaData.cFileName; + if ( name[0] == wxChar('.') && + ((name[1] == wxChar('.') && name[2] == wxChar('\0')) || + name[1] == wxChar('\0'))) + continue; + + const Zstring fullName = directoryFormatted + name; + + if (fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //a directory... { - case wxDIR_IGNORE: - break; - case wxDIR_CONTINUE: - if (!traverseInDetail(fullName, sink, level + 1)) + switch (m_sink->OnDir(fullName)) + { + case wxDIR_IGNORE: + break; + case wxDIR_CONTINUE: + //traverse into symbolic links, junctions, etc. if requested only: + if (m_traverseSymbolicLinks || (~fileMetaData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + if (!this->traverse(fullName, level + 1)) + return false; + break; + case wxDIR_STOP: return false; - else + default: + assert(false); break; - case wxDIR_STOP: - return false; - default: - assert(false); - break; + } } - } - else //a file... - { - FileInfo details; - getWin32FileInformation(fileMetaData, details); - - switch (sink->OnFile(fullName, details)) + else //a file... { - case wxDIR_IGNORE: - case wxDIR_CONTINUE: - break; - case wxDIR_STOP: - return false; - default: - assert(false); - break; + FreeFileSync::FileInfo details; + getWin32FileInformation(fileMetaData, details); + + switch (m_sink->OnFile(fullName, details)) + { + case wxDIR_IGNORE: + case wxDIR_CONTINUE: + break; + case wxDIR_STOP: + return false; + default: + assert(false); + break; + } } } - } - while (FindNextFile(searchHandle, // handle to search - &fileMetaData)); // pointer to structure for data on found file + while (FindNextFile(searchHandle, // handle to search + &fileMetaData)); // pointer to structure for data on found file - if (GetLastError() == ERROR_NO_MORE_FILES) - return true; //everything okay - else //an error occured - { - if (sink->OnError(Zstring(_("Error traversing directory:")) + wxT(" ") + directoryFormatted) == wxDIR_STOP) + const DWORD lastError = GetLastError(); + if (lastError == ERROR_NO_MORE_FILES) + return true; //everything okay + + //else: we have a problem... report it: + Zstring errorMessage = Zstring(_("Error traversing directory:")) + wxT("\n\"") + directory + wxT("\" ") ; + if (m_sink->OnError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted(lastError)) == wxDIR_STOP) return false; else return true; - } #elif defined FFS_LINUX - //use standard file traverser and enrich output with additional file information - //could be improved with custom traversing algorithm for optimized performance - EnhancedFileTraverser traverser(sink); + //use standard file traverser and enrich output with additional file information + //could be improved with custom traversing algorithm for optimized performance + EnhancedFileTraverser traverser(m_sink); - wxDir dir(directory.c_str()); - if (dir.IsOpened()) - dir.Traverse(traverser); + wxDir dir(directory.c_str()); + if (dir.IsOpened()) + dir.Traverse(traverser); - return true; + return true; #else - adapt this + adapt this #endif + } + +private: + const bool m_traverseSymbolicLinks; + FreeFileSync::FullDetailFileTraverser* m_sink; +}; + + +void FreeFileSync::traverseInDetail(const Zstring& directory, + const bool traverseSymbolicLinks, + FullDetailFileTraverser* sink) +{ + TraverseRecursively filewalker(traverseSymbolicLinks, sink); + filewalker.traverse(directory, 0); } +#ifdef FFS_WIN +inline +Zstring getDriveName(const Zstring& directoryName) //GetVolume() doesn't work under Linux! +{ + const Zstring volumeName = wxFileName(directoryName.c_str()).GetVolume().c_str(); + if (volumeName.empty()) + return Zstring(); + return volumeName + wxFileName::GetVolumeSeparator().c_str() + GlobalResources::FILE_NAME_SEPARATOR; +} +bool FreeFileSync::isFatDrive(const Zstring& directoryName) +{ + const Zstring driveName = getDriveName(directoryName); + if (driveName.empty()) + return false; + wxChar fileSystem[32]; + if (!GetVolumeInformation(driveName.c_str(), NULL, 0, NULL, NULL, NULL, fileSystem, 32)) + return false; + return Zstring(fileSystem).StartsWith(wxT("FAT")); +} +#endif //FFS_WIN -- cgit