summaryrefslogtreecommitdiff
path: root/lib/recycler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/recycler.cpp')
-rw-r--r--lib/recycler.cpp204
1 files changed, 204 insertions, 0 deletions
diff --git a/lib/recycler.cpp b/lib/recycler.cpp
new file mode 100644
index 00000000..30083701
--- /dev/null
+++ b/lib/recycler.cpp
@@ -0,0 +1,204 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) *
+// **************************************************************************
+
+#include "recycler.h"
+#include <stdexcept>
+#include <iterator>
+#include <zen/file_handling.h>
+
+#ifdef FFS_WIN
+#include <algorithm>
+#include <functional>
+#include <vector>
+#include <boost/thread/once.hpp>
+#include <zen/dll.h>
+#include <zen/win.h> //includes "windows.h"
+#include <zen/assert_static.h>
+#include <zen/win_ver.h>
+#include <zen/long_path_prefix.h>
+#include "IFileOperation/file_op.h"
+
+#elif defined FFS_LINUX
+#include <sys/stat.h>
+#include <giomm/file.h>
+#endif
+
+using namespace zen;
+
+
+namespace
+{
+#ifdef FFS_WIN
+/*
+Performance test: delete 1000 files
+------------------------------------
+SHFileOperation - single file 33s
+SHFileOperation - multiple files 2,1s
+IFileOperation - single file 33s
+IFileOperation - multiple files 2,1s
+
+=> SHFileOperation and IFileOperation have nearly IDENTICAL performance characteristics!
+
+Nevertheless, let's use IFileOperation for better error reporting!
+*/
+
+void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw FileError
+{
+ if (filesToDelete.empty())
+ return;
+
+ static bool useIFileOperation = false;
+ static boost::once_flag once = BOOST_ONCE_INIT; //caveat: function scope static initialization is not thread-safe in VS 2010!
+ boost::call_once(once, []() { useIFileOperation = vistaOrLater(); });
+
+ if (useIFileOperation) //new recycle bin usage: available since Vista
+ {
+ std::vector<const wchar_t*> fileNames;
+ std::transform(filesToDelete.begin(), filesToDelete.end(),
+ std::back_inserter(fileNames), std::mem_fun_ref(&Zstring::c_str));
+
+ using namespace fileop;
+ const DllFun<MoveToRecycleBinFct> moveToRecycler(getDllName(), moveToRecycleBinFctName);
+ const DllFun<GetLastErrorFct> getLastError (getDllName(), getLastErrorFctName);
+
+ if (!moveToRecycler || !getLastError)
+ throw FileError(_("Error moving to Recycle Bin:") + "\n\"" + fileNames[0] + "\"" + //report first file only... better than nothing
+ "\n\n" + _("Could not load a required DLL:") + " \"" + getDllName() + "\"");
+
+ //#warning moving long file paths to recycler does not work! clarify!
+ // std::vector<Zstring> temp;
+ // std::transform(filesToDelete.begin(), filesToDelete.end(),
+ // std::back_inserter(temp), std::ptr_fun(zen::removeLongPathPrefix)); //::IFileOperation() can't handle \\?\-prefix!
+
+ if (!moveToRecycler(&fileNames[0], //array must not be empty
+ fileNames.size()))
+ {
+ wchar_t errorMessage[2000];
+ getLastError(errorMessage, 2000);
+ throw FileError(_("Error moving to Recycle Bin:") + "\n\"" + fileNames[0] + "\"" + //report first file only... better than nothing
+ "\n\n" + "(" + errorMessage + ")");
+ }
+ }
+ else //regular recycle bin usage: available since XP
+ {
+ Zstring filenameDoubleNull;
+ for (std::vector<Zstring>::const_iterator i = filesToDelete.begin(); i != filesToDelete.end(); ++i)
+ {
+ //#warning moving long file paths to recycler does not work! clarify!
+ //filenameDoubleNull += removeLongPathPrefix(*i); //::SHFileOperation() can't handle \\?\-prefix!
+ //You should use fully-qualified path names with this function. Using it with relative path names is not thread safe.
+ filenameDoubleNull += *i; //::SHFileOperation() can't handle \\?\-prefix!
+ filenameDoubleNull += L'\0';
+ }
+
+ SHFILEOPSTRUCT fileOp = {};
+ fileOp.hwnd = NULL;
+ fileOp.wFunc = FO_DELETE;
+ fileOp.pFrom = filenameDoubleNull.c_str();
+ fileOp.pTo = NULL;
+ fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
+ fileOp.fAnyOperationsAborted = false;
+ fileOp.hNameMappings = NULL;
+ fileOp.lpszProgressTitle = NULL;
+
+ if (::SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted)
+ {
+ throw FileError(_("Error moving to Recycle Bin:") + "\n\"" + filenameDoubleNull + "\""); //report first file only... better than nothing
+ }
+ }
+}
+#endif
+}
+
+
+bool zen::moveToRecycleBin(const Zstring& filename) //throw FileError
+{
+ if (!somethingExists(filename))
+ return false; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it!
+
+#ifdef FFS_WIN
+ //::SetFileAttributes(applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_NORMAL);
+ //both SHFileOperation and IFileOperation are not able to delete a folder named "System Volume Information" with normal attributes but shamelessly report success
+
+ std::vector<Zstring> fileNames;
+ fileNames.push_back(filename);
+ ::moveToWindowsRecycler(fileNames); //throw FileError
+
+#elif defined FFS_LINUX
+ Glib::RefPtr<Gio::File> fileObj = Gio::File::create_for_path(filename.c_str()); //never fails
+ try
+ {
+ if (!fileObj->trash())
+ throw FileError(_("Error moving to Recycle Bin:") + "\n\"" + filename + "\"" +
+ "\n\n" + "(unknown error)");
+ }
+ catch (const Glib::Error& errorObj)
+ {
+ //implement same behavior as in Windows: if recycler is not existing, delete permanently
+ if (errorObj.code() == G_IO_ERROR_NOT_SUPPORTED)
+ {
+ struct stat fileInfo = {};
+ if (::lstat(filename.c_str(), &fileInfo) != 0)
+ return false;
+
+ if (S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode))
+ removeFile(filename); //throw FileError
+ else if (S_ISDIR(fileInfo.st_mode))
+ removeDirectory(filename); //throw FileError
+ return true;
+ }
+
+ //assemble error message
+ const std::wstring errorMessage = L"Glib Error Code " + toString<std::wstring>(errorObj.code()) + /* ", " +
+ g_quark_to_string(errorObj.domain()) + */ ": " + errorObj.what();
+
+ throw FileError(_("Error moving to Recycle Bin:") + "\n\"" + filename + "\"" +
+ "\n\n" + "(" + errorMessage + ")");
+ }
+#endif
+ return true;
+}
+
+
+#ifdef FFS_WIN
+zen::StatusRecycler zen::recycleBinStatus(const Zstring& pathName)
+{
+ std::vector<wchar_t> buffer(MAX_PATH + 1);
+ if (::GetVolumePathName(applyLongPathPrefix(pathName).c_str(), //__in LPCTSTR lpszFileName,
+ &buffer[0], //__out LPTSTR lpszVolumePathName,
+ static_cast<DWORD>(buffer.size()))) //__in DWORD cchBufferLength
+ {
+ Zstring rootPath = &buffer[0];
+ if (!endsWith(rootPath, FILE_NAME_SEPARATOR)) //a trailing backslash is required
+ rootPath += FILE_NAME_SEPARATOR;
+
+ SHQUERYRBINFO recInfo = {};
+ recInfo.cbSize = sizeof(recInfo);
+ HRESULT rv = ::SHQueryRecycleBin(rootPath.c_str(), //__in_opt LPCTSTR pszRootPath,
+ &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo
+ return rv == S_OK ? STATUS_REC_EXISTS : STATUS_REC_MISSING;
+ }
+ return STATUS_REC_UNKNOWN;
+}
+#elif defined FFS_LINUX
+/*
+We really need access to a similar function to check whether a directory supports trashing and emit a warning if it does not!
+
+The following function looks perfect, alas it is restricted to local files and to the implementation of GIO only:
+
+ gboolean _g_local_file_has_trash_dir(const char* dirname, dev_t dir_dev);
+ See: http://www.netmite.com/android/mydroid/2.0/external/bluetooth/glib/gio/glocalfileinfo.h
+
+ Just checking for "G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH" is not correct, since we find in
+ http://www.netmite.com/android/mydroid/2.0/external/bluetooth/glib/gio/glocalfileinfo.c
+
+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
+ writable && parent_info->has_trash_dir);
+
+ => We're NOT interested in whether the specified folder can be trashed, but whether it supports thrashing its child elements! (Only support, not actual write access!)
+ This renders G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH useless for this purpose.
+*/
+#endif
bgstack15