summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2019-03-13 10:36:44 +0000
committerB Stack <bgstack15@gmail.com>2019-03-13 10:36:44 +0000
commit2c01454a3fea1c29df0aeb208862243cb928b74c (patch)
treedc226d83311470a23cdf0c02064feb525fcc5096 /zen
parentMerge branch '10.9' into 'master' (diff)
parent10.10 (diff)
downloadFreeFileSync-2c01454a3fea1c29df0aeb208862243cb928b74c.tar.gz
FreeFileSync-2c01454a3fea1c29df0aeb208862243cb928b74c.tar.bz2
FreeFileSync-2c01454a3fea1c29df0aeb208862243cb928b74c.zip
Merge branch '10.10' into 'master'10.10
10.10 Latest changes: * New option: synchronize selection * Dynamically disable unsuitable context menu options * Support MTP devices without move command * Fall back to copy/delete when implicitly moving to different device (e.g. symlink) * Fixed incorrect statistics after parallel move * Fixed menu button not triggering context menu * Fixed crash on focus change while message popup is dismissed * Fixed crash when trying to shrink empty image * Fixed invisible dialogs when monitor is turned off in multi-monitor setup * Work around GetFileInformationByHandle error code 58 on WD My Cloud EX * Changing deletion handling now correctly triggers updated config * Support root-relative FTP file paths (e.g. FreeNAS) * Move and rename MTP items as a transaction * Exclude AppleDouble files (._) via default filter on macOS * Support home path for FTP folder picker * Use server default permissions when creating SFTP folder * Use native OpenSSL AES-CTR rather than libssh2 fallback * Added context information for cloud connection errors * Updated translation files See merge request opensource-tracking/FreeFileSync!7
Diffstat (limited to 'zen')
-rw-r--r--zen/file_access.cpp77
-rw-r--r--zen/file_access.h17
-rw-r--r--zen/file_error.h3
-rw-r--r--zen/file_io.cpp66
-rw-r--r--zen/file_io.h6
-rw-r--r--zen/http.cpp2
-rw-r--r--zen/shutdown.cpp4
-rw-r--r--zen/zstring.h2
8 files changed, 83 insertions, 94 deletions
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index a60eccb2..4536ec36 100644
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -180,19 +180,23 @@ uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, returns 0
}
-FileDetails zen::getFileDetails(const Zstring& itemPath) //throw FileError
+VolumeId zen::getVolumeId(const Zstring& itemPath) //throw FileError
{
struct ::stat fileInfo = {};
if (::stat(itemPath.c_str(), &fileInfo) != 0)
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"stat");
- return
- {
- makeUnsigned(fileInfo.st_size),
- fileInfo.st_mtime,
- fileInfo.st_dev,
- //FileIndex fileIndex = fileInfo.st_ino;
- };
+ return fileInfo.st_dev;
+}
+
+
+uint64_t zen::getFileSize(const Zstring& filePath) //throw FileError
+{
+ struct ::stat fileInfo = {};
+ if (::stat(filePath.c_str(), &fileInfo) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"stat");
+
+ return fileInfo.st_size;
}
@@ -303,15 +307,15 @@ namespace
Fix8Dot3NameClash()
*/
//wrapper for file system rename function:
-void moveAndRenameFileSub(const Zstring& pathSource, const Zstring& pathTarget, bool replaceExisting) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
+void moveAndRenameFileSub(const Zstring& pathFrom, const Zstring& pathTo, bool replaceExisting) //throw FileError, ErrorMoveUnsupported, ErrorTargetExisting
{
auto throwException = [&](int ec)
{
- const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtPath(pathSource)), L"%y", L"\n" + fmtPath(pathTarget));
+ const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtPath(pathFrom)), L"%y", L"\n" + fmtPath(pathTo));
const std::wstring errorDescr = formatSystemError(L"rename", ec);
if (ec == EXDEV)
- throw ErrorDifferentVolume(errorMsg, errorDescr);
+ throw ErrorMoveUnsupported(errorMsg, errorDescr);
if (ec == EEXIST)
throw ErrorTargetExisting(errorMsg, errorDescr);
throw FileError(errorMsg, errorDescr);
@@ -324,11 +328,11 @@ void moveAndRenameFileSub(const Zstring& pathSource, const Zstring& pathTarget,
if (!replaceExisting)
{
struct ::stat infoSrc = {};
- if (::lstat(pathSource.c_str(), &infoSrc) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(pathSource)), L"stat");
+ if (::lstat(pathFrom.c_str(), &infoSrc) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(pathFrom)), L"stat");
struct ::stat infoTrg = {};
- if (::lstat(pathTarget.c_str(), &infoTrg) == 0)
+ if (::lstat(pathTo.c_str(), &infoTrg) == 0)
{
if (infoSrc.st_dev != infoTrg.st_dev ||
infoSrc.st_ino != infoTrg.st_ino)
@@ -339,7 +343,7 @@ void moveAndRenameFileSub(const Zstring& pathSource, const Zstring& pathTarget,
//else: not existing or access error (hopefully ::rename will also fail!)
}
- if (::rename(pathSource.c_str(), pathTarget.c_str()) != 0)
+ if (::rename(pathFrom.c_str(), pathTo.c_str()) != 0)
throwException(errno);
}
@@ -348,29 +352,29 @@ void moveAndRenameFileSub(const Zstring& pathSource, const Zstring& pathTarget,
//rename file: no copying!!!
-void zen::moveAndRenameItem(const Zstring& pathSource, const Zstring& pathTarget, bool replaceExisting) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
+void zen::moveAndRenameItem(const Zstring& pathFrom, const Zstring& pathTo, bool replaceExisting) //throw FileError, ErrorMoveUnsupported, ErrorTargetExisting
{
try
{
- moveAndRenameFileSub(pathSource, pathTarget, replaceExisting); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
+ moveAndRenameFileSub(pathFrom, pathTo, replaceExisting); //throw FileError, ErrorMoveUnsupported, ErrorTargetExisting
}
catch (ErrorTargetExisting&)
{
#if 0 //"Work around pen drive failing to change file name case" => enable if needed: https://freefilesync.org/forum/viewtopic.php?t=4279
- const Zstring fileNameSrc = afterLast (pathSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
- const Zstring fileNameTrg = afterLast (pathTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
- const Zstring parentPathSrc = beforeLast(pathSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
- const Zstring parentPathTrg = beforeLast(pathTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
+ const Zstring fileNameSrc = afterLast (pathFrom, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
+ const Zstring fileNameTrg = afterLast (pathTo, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
+ const Zstring parentPathSrc = beforeLast(pathFrom, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
+ const Zstring parentPathTrg = beforeLast(pathTo, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
//some (broken) devices may fail to rename case directly:
if (equalNativePath(parentPathSrc, parentPathTrg))
{
if (fileNameSrc == fileNameTrg)
return; //non-sensical request
- const Zstring tempFilePath = getTemporaryPath8Dot3(pathSource); //throw FileError
- moveAndRenameFileSub(pathSource, tempFilePath); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
- ZEN_ON_SCOPE_FAIL(moveAndRenameFileSub(tempFilePath, pathSource)); //"try" our best to be "atomic" :>
- moveAndRenameFileSub(tempFilePath, pathTarget); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
+ const Zstring tempFilePath = getTemporaryPath8Dot3(pathFrom); //throw FileError
+ moveAndRenameFileSub(pathFrom, tempFilePath); //throw FileError, (ErrorMoveUnsupported), ErrorTargetExisting
+ ZEN_ON_SCOPE_FAIL(moveAndRenameFileSub(tempFilePath, pathFrom)); //"try" our best to be "atomic" :>
+ moveAndRenameFileSub(tempFilePath, pathTo); //throw FileError, (ErrorMoveUnsupported), ErrorTargetExisting
return;
}
#endif
@@ -585,27 +589,26 @@ void zen::tryCopyDirectoryAttributes(const Zstring& sourcePath, const Zstring& t
}
-void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions) //throw FileError
+void zen::copySymlink(const Zstring& sourcePath, const Zstring& targetPath, bool copyFilePermissions) //throw FileError
{
- const Zstring linkPath = getSymlinkTargetRaw(sourceLink); //throw FileError; accept broken symlinks
+ const Zstring linkPath = getSymlinkTargetRaw(sourcePath); //throw FileError; accept broken symlinks
- const wchar_t functionName[] = L"symlink";
- if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtPath(sourceLink)), L"%y", L"\n" + fmtPath(targetLink)), functionName);
+ if (::symlink(linkPath.c_str(), targetPath.c_str()) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtPath(sourcePath)), L"%y", L"\n" + fmtPath(targetPath)), L"symlink");
- //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist!
- ZEN_ON_SCOPE_FAIL(try { removeSymlinkPlain(targetLink); /*throw FileError*/ }
+ //allow only consistent objects to be created -> don't place before ::symlink, targetPath may already exist!
+ ZEN_ON_SCOPE_FAIL(try { removeSymlinkPlain(targetPath); /*throw FileError*/ }
catch (FileError&) {});
- //file times: essential for sync'ing a symlink: enforce this! (don't just try!)
+ //file times: essential for syncing a symlink: enforce this! (don't just try!)
struct ::stat sourceInfo = {};
- if (::lstat(sourceLink.c_str(), &sourceInfo) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"lstat");
+ if (::lstat(sourcePath.c_str(), &sourceInfo) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourcePath)), L"lstat");
- setWriteTimeNative(targetLink, sourceInfo.st_mtim, ProcSymlink::DIRECT); //throw FileError
+ setWriteTimeNative(targetPath, sourceInfo.st_mtim, ProcSymlink::DIRECT); //throw FileError
if (copyFilePermissions)
- copyItemPermissions(sourceLink, targetLink, ProcSymlink::DIRECT); //throw FileError
+ copyItemPermissions(sourcePath, targetPath, ProcSymlink::DIRECT); //throw FileError
}
diff --git a/zen/file_access.h b/zen/file_access.h
index e06c64ac..fc4b5833 100644
--- a/zen/file_access.h
+++ b/zen/file_access.h
@@ -52,17 +52,10 @@ enum class ProcSymlink
};
void setFileTime(const Zstring& filePath, time_t modTime, ProcSymlink procSl); //throw FileError
-//symlink handling: always follow
+//symlink handling: always follow:
uint64_t getFreeDiskSpace(const Zstring& path); //throw FileError, returns 0 if not available
-
-struct FileDetails
-{
- uint64_t fileSize = 0;
- time_t modTime = 0; //number of seconds since Jan. 1st 1970 UTC
- VolumeId volumeId = 0;
-};
-//symlink handling: always follow
-FileDetails getFileDetails(const Zstring& itemPath); //throw FileError
+VolumeId getVolumeId(const Zstring& itemPath); //throw FileError
+uint64_t getFileSize(const Zstring& filePath); //throw FileError
//get per-user directory designated for temporary files:
Zstring getTempFolderPath(); //throw FileError
@@ -72,7 +65,7 @@ void removeSymlinkPlain (const Zstring& linkPath); //throw FileError; E
void removeDirectoryPlain(const Zstring& dirPath ); //throw FileError; ERROR if not existing
void removeDirectoryPlainRecursion(const Zstring& dirPath); //throw FileError; ERROR if not existing
-void moveAndRenameItem(const Zstring& itemPathOld, const Zstring& itemPathNew, bool replaceExisting); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
+void moveAndRenameItem(const Zstring& pathFrom, const Zstring& pathTo, bool replaceExisting); //throw FileError, ErrorMoveUnsupported, ErrorTargetExisting
bool supportsPermissions(const Zstring& dirPath); //throw FileError, follows symlinks
//copy permissions for files, directories or symbolic links: requires admin rights
@@ -89,7 +82,7 @@ void createDirectoryIfMissingRecursion(const Zstring& dirPath); //throw FileErro
//reports note-worthy errors only
void tryCopyDirectoryAttributes(const Zstring& sourcePath, const Zstring& targetPath); //throw FileError
-void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions); //throw FileError
+void copySymlink(const Zstring& sourcePath, const Zstring& targetPath, bool copyFilePermissions); //throw FileError
struct FileCopyResult
{
diff --git a/zen/file_error.h b/zen/file_error.h
index 101d6543..e47fb9f4 100644
--- a/zen/file_error.h
+++ b/zen/file_error.h
@@ -31,9 +31,8 @@ private:
#define DEFINE_NEW_FILE_ERROR(X) struct X : public zen::FileError { X(const std::wstring& msg) : FileError(msg) {} X(const std::wstring& msg, const std::wstring& descr) : FileError(msg, descr) {} };
DEFINE_NEW_FILE_ERROR(ErrorTargetExisting);
-//DEFINE_NEW_FILE_ERROR(ErrorTargetPathMissing);
DEFINE_NEW_FILE_ERROR(ErrorFileLocked);
-DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume);
+DEFINE_NEW_FILE_ERROR(ErrorMoveUnsupported);
//CAVEAT: thread-local Win32 error code is easily overwritten => evaluate *before* making any (indirect) system calls:
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index 1c6ab6f2..e788bcfe 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -14,43 +14,12 @@
using namespace zen;
-namespace
-{
-//- "filePath" could be a named pipe which *blocks* forever for open()!
-//- open() with O_NONBLOCK avoids the block, but opens successfully
-//- create sample pipe: "sudo mkfifo named_pipe"
-void checkForUnsupportedType(const Zstring& filePath) //throw FileError
-{
- struct ::stat fileInfo = {};
- if (::stat(filePath.c_str(), &fileInfo) != 0) //follows symlinks
- return; //let the caller handle errors like "not existing"
-
- if (!S_ISREG(fileInfo.st_mode) &&
- !S_ISLNK(fileInfo.st_mode) &&
- !S_ISDIR(fileInfo.st_mode))
- {
- auto getTypeName = [](mode_t m) -> std::wstring
- {
- const wchar_t* name =
- S_ISCHR (m) ? L"character device":
- S_ISBLK (m) ? L"block device" :
- S_ISFIFO(m) ? L"FIFO, named pipe" :
- S_ISSOCK(m) ? L"socket" : nullptr;
- const std::wstring numFmt = printNumber<std::wstring>(L"0%06o", m & S_IFMT);
- return name ? numFmt + L", " + name : numFmt;
- };
- throw FileError(replaceCpy(_("Type of item %x is not supported:"), L"%x", fmtPath(filePath)) + L" " + getTypeName(fileInfo.st_mode));
- }
-}
-}
-
-
- const FileBase::FileHandle FileBase::invalidHandleValue = -1;
+ const FileBase::FileHandle FileBase::invalidHandleValue_ = -1;
FileBase::~FileBase()
{
- if (fileHandle_ != invalidHandleValue)
+ if (fileHandle_ != invalidHandleValue_)
try
{
close(); //throw FileError
@@ -61,9 +30,9 @@ FileBase::~FileBase()
void FileBase::close() //throw FileError
{
- if (fileHandle_ == invalidHandleValue)
+ if (fileHandle_ == invalidHandleValue_)
throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"Contract error: close() called more than once.");
- ZEN_ON_SCOPE_EXIT(fileHandle_ = invalidHandleValue);
+ ZEN_ON_SCOPE_EXIT(fileHandle_ = invalidHandleValue_);
//no need to clean-up on failure here (just like there is no clean on FileOutput::write failure!) => FileOutput is not transactional!
@@ -77,7 +46,32 @@ namespace
{
FileBase::FileHandle openHandleForRead(const Zstring& filePath) //throw FileError, ErrorFileLocked
{
- checkForUnsupportedType(filePath); //throw FileError; opening a named pipe would block forever!
+ //- "filePath" could be a named pipe which *blocks* forever for open()!
+ //- open() with O_NONBLOCK avoids the block, but opens successfully
+ //- create sample pipe: "sudo mkfifo named_pipe"
+ struct ::stat fileInfo = {};
+ if (::stat(filePath.c_str(), &fileInfo) == 0) //follows symlinks
+ {
+ if (!S_ISREG(fileInfo.st_mode) &&
+ !S_ISLNK(fileInfo.st_mode) &&
+ !S_ISDIR(fileInfo.st_mode))
+ {
+ const std::wstring typeName = [m = fileInfo.st_mode]
+ {
+ std::wstring name =
+ S_ISCHR (m) ? L"character device" :
+ S_ISBLK (m) ? L"block device" :
+ S_ISFIFO(m) ? L"FIFO, named pipe" :
+ S_ISSOCK(m) ? L"socket" : L"";
+ if (!name.empty())
+ name += L", ";
+ return name + printNumber<std::wstring>(L"0%06o", m & S_IFMT);
+ }();
+ throw FileError(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(filePath)),
+ _("Unsupported item type.") + L" [" + typeName + L"]");
+ }
+ }
+ //else: let ::open() fail for errors like "not existing"
//don't use O_DIRECT: http://yarchive.net/comp/linux/o_direct.html
const FileBase::FileHandle fileHandle = ::open(filePath.c_str(), O_RDONLY | O_CLOEXEC);
diff --git a/zen/file_io.h b/zen/file_io.h
index bf23d22c..cf5ecc73 100644
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -41,13 +41,13 @@ protected:
~FileBase();
void close(); //throw FileError -> optional, but good place to catch errors when closing stream!
- static const FileHandle invalidHandleValue;
+ static const FileHandle invalidHandleValue_;
private:
FileBase (const FileBase&) = delete;
FileBase& operator=(const FileBase&) = delete;
- FileHandle fileHandle_ = invalidHandleValue;
+ FileHandle fileHandle_ = invalidHandleValue_;
const Zstring filePath_;
};
@@ -108,7 +108,7 @@ template <class BinContainer> inline
BinContainer loadBinContainer(const Zstring& filePath, const IOCallback& notifyUnbufferedIO /*throw X*/) //throw FileError, X
{
FileInput streamIn(filePath, notifyUnbufferedIO); //throw FileError, ErrorFileLocked
- return bufferedLoad<BinContainer>(streamIn); //throw FileError, X;
+ return bufferedLoad<BinContainer>(streamIn); //throw FileError, X
}
diff --git a/zen/http.cpp b/zen/http.cpp
index 1f89bf20..f8538c93 100644
--- a/zen/http.cpp
+++ b/zen/http.cpp
@@ -203,7 +203,7 @@ size_t HttpInputStream::read(void* buffer, size_t bytesToRead) { return pimpl_->
size_t HttpInputStream::getBlockSize() const { return pimpl_->getBlockSize(); }
-std::string HttpInputStream::readAll() { return bufferedLoad<std::string>(*pimpl_); } //throw SysError, X;
+std::string HttpInputStream::readAll() { return bufferedLoad<std::string>(*pimpl_); } //throw SysError, X
namespace
diff --git a/zen/shutdown.cpp b/zen/shutdown.cpp
index f09c4d07..cd37d57c 100644
--- a/zen/shutdown.cpp
+++ b/zen/shutdown.cpp
@@ -44,6 +44,6 @@ void zen::terminateProcess(int exitCode)
//Shut down: systemctl poweroff //alternative requiring admin: sudo shutdown -h 1
//Sleep: systemctl suspend //alternative requiring admin: sudo pm-suspend
//Log off: gnome-session-quit --no-prompt
- // alternative requiring admin: sudo killall Xorg
- // alternative without admin: dbus-send --session --print-reply --dest=org.gnome.SessionManager /org/gnome/SessionManager org.gnome.SessionManager.Logout uint32:1
+ // alternative requiring admin: sudo killall Xorg
+ // alternative without admin: dbus-send --session --print-reply --dest=org.gnome.SessionManager /org/gnome/SessionManager org.gnome.SessionManager.Logout uint32:1
diff --git a/zen/zstring.h b/zen/zstring.h
index 6727253b..42487670 100644
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -33,7 +33,7 @@ Zstring makeUpperCopy(const Zstring& str);
//macOS: decomposed
Zstring getUnicodeNormalForm(const Zstring& str);
// "In fact, Unicode declares that there is an equivalence relationship between decomposed and composed sequences,
-// and conformant software should not treat canonically equivalent sequences, whether composed or decomposed or something inbetween, as different."
+// and conformant software should not treat canonically equivalent sequences, whether composed or decomposed or something in between, as different."
// http://www.win.tue.nl/~aeb/linux/uc/nfc_vs_nfd.html
struct LessUnicodeNormal { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return getUnicodeNormalForm(lhs) < getUnicodeNormalForm(rhs);} };
bgstack15