diff options
author | B Stack <bgstack15@gmail.com> | 2019-03-13 10:36:44 +0000 |
---|---|---|
committer | B Stack <bgstack15@gmail.com> | 2019-03-13 10:36:44 +0000 |
commit | 2c01454a3fea1c29df0aeb208862243cb928b74c (patch) | |
tree | dc226d83311470a23cdf0c02064feb525fcc5096 /zen | |
parent | Merge branch '10.9' into 'master' (diff) | |
parent | 10.10 (diff) | |
download | FreeFileSync-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.cpp | 77 | ||||
-rw-r--r-- | zen/file_access.h | 17 | ||||
-rw-r--r-- | zen/file_error.h | 3 | ||||
-rw-r--r-- | zen/file_io.cpp | 66 | ||||
-rw-r--r-- | zen/file_io.h | 6 | ||||
-rw-r--r-- | zen/http.cpp | 2 | ||||
-rw-r--r-- | zen/shutdown.cpp | 4 | ||||
-rw-r--r-- | zen/zstring.h | 2 |
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);} }; |