summaryrefslogtreecommitdiff
path: root/shared/fileHandling.cpp
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:00:50 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:00:50 +0200
commit4ecfd41e36533d858c98d051ef70cab80e69e972 (patch)
treeca07d8745967d2c6a7123a5d32269cfbfaa7bd6c /shared/fileHandling.cpp
parent2.2 (diff)
downloadFreeFileSync-4ecfd41e36533d858c98d051ef70cab80e69e972.tar.gz
FreeFileSync-4ecfd41e36533d858c98d051ef70cab80e69e972.tar.bz2
FreeFileSync-4ecfd41e36533d858c98d051ef70cab80e69e972.zip
2.3
Diffstat (limited to 'shared/fileHandling.cpp')
-rw-r--r--shared/fileHandling.cpp228
1 files changed, 195 insertions, 33 deletions
diff --git a/shared/fileHandling.cpp b/shared/fileHandling.cpp
index a5d3cf57..55fd7ff5 100644
--- a/shared/fileHandling.cpp
+++ b/shared/fileHandling.cpp
@@ -1,8 +1,13 @@
#include "fileHandling.h"
#include <wx/intl.h>
#include "systemFunctions.h"
-#include "globalFunctions.h"
+#include "systemConstants.h"
#include "fileTraverser.h"
+#include <wx/file.h>
+#include <stdexcept>
+#include <boost/bind.hpp>
+#include <algorithm>
+#include <wx/log.h>
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
@@ -55,7 +60,7 @@ public:
return recycleBinAvailable;
}
- bool moveToRecycleBin(const Zstring& filename) const; //throw (RuntimeException)
+ bool moveToRecycleBin(const Zstring& filename) const; //throw (std::logic_error)
private:
RecycleBin() :
@@ -73,10 +78,10 @@ private:
};
-bool RecycleBin::moveToRecycleBin(const Zstring& filename) const //throw (RuntimeException)
+bool RecycleBin::moveToRecycleBin(const Zstring& filename) const //throw (std::logic_error)
{
if (!recycleBinAvailable) //this method should ONLY be called if recycle bin is available
- throw RuntimeException(_("Initialization of Recycle Bin failed!"));
+ throw std::logic_error("Initialization of Recycle Bin failed!");
#ifdef FFS_WIN
Zstring filenameDoubleNull = filename + wxChar(0);
@@ -105,7 +110,7 @@ bool FreeFileSync::recycleBinExists()
inline
-bool moveToRecycleBin(const Zstring& filename) //throw (RuntimeException)
+bool moveToRecycleBin(const Zstring& filename) //throw (std::logic_error)
{
return RecycleBin::getInstance().moveToRecycleBin(filename);
}
@@ -159,7 +164,54 @@ bool FreeFileSync::symlinkExists(const DefaultChar* objname)
}
-void FreeFileSync::removeFile(const Zstring& filename, const bool useRecycleBin) //throw (FileError, ::RuntimeException);
+bool FreeFileSync::isMovable(const Zstring& pathFrom, const Zstring& pathTo)
+{
+ wxLogNull noWxLogs; //prevent wxWidgets logging if dummy file creation failed
+
+ const Zstring dummyFileSource = pathFrom.EndsWith(globalFunctions::FILE_NAME_SEPARATOR) ?
+ pathFrom + wxT("DeleteMe.tmp") :
+ pathFrom + globalFunctions::FILE_NAME_SEPARATOR + wxT("DeleteMe.tmp");
+
+ const Zstring dummyFileTarget = pathTo.EndsWith(globalFunctions::FILE_NAME_SEPARATOR) ?
+ pathTo + wxT("DeleteMe.tmp") :
+ pathTo + globalFunctions::FILE_NAME_SEPARATOR + wxT("DeleteMe.tmp");
+ try
+ {
+ removeFile(dummyFileSource, false);
+ removeFile(dummyFileTarget, false);
+ }
+ catch (...) {}
+
+ //create dummy file
+ {
+ wxFile dummy(dummyFileSource.c_str(), wxFile::write);
+ if (!dummy.IsOpened())
+ return false; //if there's no write access, files can't be moved neither
+ dummy.Write(wxT("FreeFileSync dummy file. May be deleted safely.\n"));
+ }
+
+ const bool result =
+ //try to move the file
+#ifdef FFS_WIN
+ ::MoveFileEx(dummyFileSource.c_str(), //__in LPCTSTR lpExistingFileName,
+ dummyFileTarget.c_str(), //__in_opt LPCTSTR lpNewFileName,
+ 0) != 0; //__in DWORD dwFlags
+#elif defined FFS_LINUX
+ ::rename(dummyFileSource.c_str(), dummyFileTarget.c_str()) == 0;
+#endif
+
+ try
+ {
+ removeFile(dummyFileSource, false);
+ removeFile(dummyFileTarget, false);
+ }
+ catch (...) {}
+
+ return result;
+}
+
+
+void FreeFileSync::removeFile(const Zstring& filename, const bool useRecycleBin) //throw (FileError, std::logic_error);
{
//no error situation if file is not existing! manual deletion relies on it!
#ifdef FFS_WIN
@@ -232,7 +284,7 @@ private:
void FreeFileSync::moveFile(const Zstring& sourceFile, const Zstring& targetFile, MoveFileCallback* callback) //throw (FileError);
{
- if (fileExists(targetFile)) //test file existence: e.g. Linux might silently overwrite existing symlinks
+ if (fileExists(targetFile.c_str())) //test file existence: e.g. Linux might silently overwrite existing symlinks
{
const wxString errorMessage = wxString(_("Error moving file:")) + wxT("\n\"") + sourceFile + wxT("\" ->\n\"") + targetFile + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + _("Target file already existing!"));
@@ -481,18 +533,21 @@ void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecyc
return;
}
-//attention: check if directory is a symlink! Do NOT traverse into it deleting contained files!!!
+
#ifdef FFS_WIN
- if (dirAttr & FILE_ATTRIBUTE_REPARSE_POINT) //remove symlink directly, support for \\?\-prefix
+ //initialize file attributes
+ if (!::SetFileAttributes( // initialize file attributes: actually NEEDED for symbolic links also!
+ directory.c_str(), // address of directory name
+ FILE_ATTRIBUTE_NORMAL)) // attributes to set
{
- if (!::SetFileAttributes( // initialize file attributes: actually NEEDED for symbolic links also!
- directory.c_str(), // address of directory name
- FILE_ATTRIBUTE_NORMAL)) // attributes to set
- {
- wxString errorMessage = wxString(_("Error deleting directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
+ wxString errorMessage = wxString(_("Error deleting directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
+ throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
+ }
+
+//attention: check if directory is a symlink! Do NOT traverse into it deleting contained files!!!
+ if (dirAttr & FILE_ATTRIBUTE_REPARSE_POINT) //remove symlink directly, support for \\?\-prefix
+ {
if (!::RemoveDirectory(directory.c_str()))
{
wxString errorMessage = wxString(_("Error deleting directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
@@ -521,24 +576,13 @@ void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecyc
FreeFileSync::traverseFolder(directory, false, &traverser);
//delete files
- for (std::vector<Zstring>::const_iterator j = fileList.begin(); j != fileList.end(); ++j)
- FreeFileSync::removeFile(*j, false);
+ std::for_each(fileList.begin(), fileList.end(), boost::bind(removeFile, _1, false));
//delete directories recursively
- for (std::vector<Zstring>::const_iterator j = dirList.begin(); j != dirList.end(); ++j)
- FreeFileSync::removeDirectory(*j, false); //call recursively to correctly handle symbolic links
+ std::for_each(dirList.begin(), dirList.end(), boost::bind(removeDirectory, _1, false)); //call recursively to correctly handle symbolic links
//parent directory is deleted last
#ifdef FFS_WIN
- //initialize file attributes
- if (!::SetFileAttributes(
- directory.c_str(), // address of directory name
- FILE_ATTRIBUTE_NORMAL)) // attributes to set
- {
- wxString errorMessage = wxString(_("Error deleting directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
- }
-
//remove directory, support for \\?\-prefix
if (!::RemoveDirectory(directory.c_str()))
{
@@ -563,14 +607,77 @@ public:
~CloseHandleOnExit()
{
- CloseHandle(fileHandle_);
+ ::CloseHandle(fileHandle_);
}
private:
HANDLE fileHandle_;
};
+#endif
+
+
+//optionally: copy directory last change date, DO NOTHING if something fails
+void FreeFileSync::copyDirLastChangeDate(const Zstring& sourceDir, const Zstring& targetDir)
+{
+ if (symlinkExists(sourceDir)) //don't handle symlinks (yet)
+ return;
+
+#ifdef FFS_WIN
+ HANDLE hDirRead = ::CreateFile(sourceDir.c_str(),
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, //needed for directories
+ NULL);
+
+ if (hDirRead == INVALID_HANDLE_VALUE)
+ return;
+ CloseHandleOnExit dummy(hDirRead);
+
+ FILETIME lastWriteTime;
+ if (::GetFileTime(hDirRead,
+ NULL,
+ NULL,
+ &lastWriteTime))
+ {
+ HANDLE hDirWrite = ::CreateFile(targetDir.c_str(),
+ FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, //needed for directories
+ NULL);
+
+ if (hDirWrite != INVALID_HANDLE_VALUE)
+ {
+ CloseHandleOnExit dummy2(hDirWrite);
+
+ //(try to) set new "last write time"
+ ::SetFileTime(hDirWrite,
+ NULL,
+ NULL,
+ &lastWriteTime); //return value not evalutated!
+ }
+ }
+#elif defined FFS_LINUX
+ struct stat dirInfo;
+ if (::stat(sourceDir.c_str(), &dirInfo) == 0) //read file attributes from source directory
+ {
+ //adapt file modification time:
+ struct utimbuf newTimes;
+ ::time(&newTimes.actime); //set file access time to current time
+ newTimes.modtime = dirInfo.st_mtime;
+ //(try to) set new "last write time"
+ ::utime(targetDir.c_str(), &newTimes); //return value not evalutated!
+ }
+#endif
+}
+
+
+#ifdef FFS_WIN
class KernelDllHandler //dynamically load windows API functions
{
typedef DWORD (WINAPI *GetFinalPath)(
@@ -640,6 +747,53 @@ Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target pa
return targetPath;
}
+
+
+//#include <aclapi.h>
+//optionally: copy additional metadata, DO NOTHING if something fails
+//void copyAdditionalMetadata(const Zstring& sourceDir, const Zstring& targetDir)
+//{
+// PSECURITY_DESCRIPTOR pSD;
+//
+// PSID pSidOwner;
+// PSID pSidGroup;
+// PACL pDacl;
+// PACL pSacl;
+// if (::GetNamedSecurityInfo(
+// const_cast<DefaultChar*>(sourceDir.c_str()),
+// SE_FILE_OBJECT, //file or directory
+// OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION,
+// &pSidOwner,
+// &pSidGroup,
+// &pDacl,
+// &pSacl,
+// &pSD
+// ) == ERROR_SUCCESS)
+// {
+// //(try to) set new security information
+// if (::SetNamedSecurityInfo(
+// const_cast<DefaultChar*>(targetDir.c_str()),
+// SE_FILE_OBJECT, //file or directory
+// OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION,
+// pSidOwner,
+// pSidGroup,
+// pDacl,
+// pSacl) != ERROR_SUCCESS) //return value not evalutated!
+// {
+// const wxString errorMessage = wxString(wxT("Error 2:")) + wxT("\n\"") + targetDir.c_str() + wxT("\"");
+// throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
+// }
+//warning BUG!
+//
+// LocalFree(pSD); //pSidOwner, pSidGroup, pDacl, pSacl must not be freed!
+// }
+// else
+// {
+// const wxString errorMessage = wxString(wxT("Error 1:")) + wxT("\n\"") + sourceDir.c_str() + wxT("\"");
+// throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
+// }
+//
+//}
#endif
@@ -694,6 +848,9 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat
const wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
}
+
+ //(try to) copy additional metadata like last modification time: no measurable performance drawback
+ //copyAdditionalMetadata(linkPath, directory);
}
}
else //in all other cases
@@ -706,6 +863,9 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat
const wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
}
+
+ //(try to) copy additional metadata like last modification time: no measurable performance drawback
+ //copyAdditionalMetadata(templateDir, directory);
}
}
#elif defined FFS_LINUX
@@ -874,13 +1034,15 @@ void FreeFileSync::copyFile(const Zstring& sourceFile,
//don't suppress "lastError == ERROR_REQUEST_ABORTED": an user aborted operation IS an error condition!
//if file is locked (try to) use Windows Volume Shadow Copy Service
- if (lastError == ERROR_SHARING_VIOLATION && shadowCopyHandler != NULL)
+ if (shadowCopyHandler != NULL &&
+ (lastError == ERROR_SHARING_VIOLATION ||
+ lastError == ERROR_LOCK_VIOLATION))
{
const Zstring shadowFilename(shadowCopyHandler->makeShadowCopy(sourceFile));
FreeFileSync::copyFile(shadowFilename, //transferred bytes is automatically reset when new file is copied
targetFile,
copyFileSymLinks,
- shadowCopyHandler,
+ NULL,
callback);
return;
}
@@ -958,7 +1120,7 @@ void FreeFileSync::copyFile(const Zstring& sourceFile,
//begin of regular file copy
struct stat fileInfo;
- if (stat(sourceFile.c_str(), &fileInfo) != 0) //read file attributes from source file (resolving symlinks)
+ if (stat(sourceFile.c_str(), &fileInfo) != 0) //read file attributes from source file (resolving symlinks; but cannot be one in this context)
{
const wxString errorMessage = wxString(_("Error reading file attributes:")) + wxT("\n\"") + sourceFile.c_str() + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
bgstack15