From 8d66e8a2b8cfe4eef4b946a1ab64354dfd7da00b Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Sat, 29 Oct 2016 11:34:19 +0200 Subject: 8.4 --- zen/file_access.cpp | 133 ++++++++++++++++++++++------------------------------ 1 file changed, 56 insertions(+), 77 deletions(-) (limited to 'zen/file_access.cpp') diff --git a/zen/file_access.cpp b/zen/file_access.cpp index c2072a26..d8a1f3b7 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -1,8 +1,8 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** #include "file_access.h" #include @@ -810,7 +810,7 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError } -void setWriteTimeNative(const Zstring& filePath, +void setWriteTimeNative(const Zstring& itemPath, const FILETIME& lastWriteTime, const FILETIME* creationTime, //optional ProcSymlink procSl) //throw FileError @@ -839,21 +839,21 @@ void setWriteTimeNative(const Zstring& filePath, DWORD attribs = INVALID_FILE_ATTRIBUTES; ZEN_ON_SCOPE_EXIT( if (attribs != INVALID_FILE_ATTRIBUTES) - ::SetFileAttributes(applyLongPathPrefix(filePath).c_str(), attribs); + ::SetFileAttributes(applyLongPathPrefix(itemPath).c_str(), attribs); ); auto removeReadonly = [&]() -> bool //throw FileError; may need to remove the readonly-attribute (e.g. on FAT usb drives) { if (attribs == INVALID_FILE_ATTRIBUTES) { - const DWORD tmpAttr = ::GetFileAttributes(applyLongPathPrefix(filePath).c_str()); + const DWORD tmpAttr = ::GetFileAttributes(applyLongPathPrefix(itemPath).c_str()); if (tmpAttr == INVALID_FILE_ATTRIBUTES) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileAttributes"); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"GetFileAttributes"); if (tmpAttr & FILE_ATTRIBUTE_READONLY) { - if (!::SetFileAttributes(applyLongPathPrefix(filePath).c_str(), FILE_ATTRIBUTE_NORMAL)) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(filePath)), L"SetFileAttributes"); + if (!::SetFileAttributes(applyLongPathPrefix(itemPath).c_str(), FILE_ATTRIBUTE_NORMAL)) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(itemPath)), L"SetFileAttributes"); attribs = tmpAttr; //reapplied on scope exit return true; @@ -864,7 +864,7 @@ void setWriteTimeNative(const Zstring& filePath, auto openFile = [&](bool conservativeApproach) { - return ::CreateFile(applyLongPathPrefix(filePath).c_str(), //_In_ LPCTSTR lpFileName, + return ::CreateFile(applyLongPathPrefix(itemPath).c_str(), //_In_ LPCTSTR lpFileName, (conservativeApproach ? //some NAS seem to have issues with FILE_WRITE_ATTRIBUTES, even worse, they may fail silently! //http://sourceforge.net/tracker/?func=detail&atid=1093081&aid=3536680&group_id=234430 @@ -906,20 +906,20 @@ void setWriteTimeNative(const Zstring& filePath, continue; //3. after these herculean stunts we give up... - throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), formatSystemError(L"CreateFile", ec)); + throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), formatSystemError(L"CreateFile", ec)); } } break; } ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); - setFileTimeByHandle(hFile, creationTime, lastWriteTime, filePath); //throw FileError + setFileTimeByHandle(hFile, creationTime, lastWriteTime, itemPath); //throw FileError } #ifndef NDEBUG //verify written data FILETIME creationTimeDbg = {}; FILETIME lastWriteTimeDbg = {}; - HANDLE hFile = ::CreateFile(applyLongPathPrefix(filePath).c_str(), //_In_ LPCTSTR lpFileName, + HANDLE hFile = ::CreateFile(applyLongPathPrefix(itemPath).c_str(), //_In_ LPCTSTR lpFileName, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, //_In_ DWORD dwDesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, @@ -942,26 +942,7 @@ void setWriteTimeNative(const Zstring& filePath, #elif defined ZEN_LINUX -void setWriteTimeFallback(const Zstring& filePath, std::int64_t modTime, ProcSymlink procSl) //throw FileError -{ - struct ::timeval writeTime[2] = {}; - writeTime[0].tv_sec = ::time(nullptr); //access time (seconds) - writeTime[1].tv_sec = modTime; //modification time (seconds) - - if (procSl == ProcSymlink::FOLLOW) - { - if (::utimes(filePath.c_str(), writeTime) != 0) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"utimes"); - } - else - { - if (::lutimes(filePath.c_str(), writeTime) != 0) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"lutimes"); - } -} - - -void setWriteTimeNative(const Zstring& filePath, std::int64_t modTime, ProcSymlink procSl) //throw FileError +void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTime, ProcSymlink procSl) //throw FileError { /* [2013-05-01] sigh, we can't use utimensat() on NTFS volumes on Ubuntu: silent failure!!! what morons are programming this shit??? @@ -970,36 +951,33 @@ void setWriteTimeNative(const Zstring& filePath, std::int64_t modTime, ProcSymli [2015-03-09] - cannot reproduce issues with NTFS and utimensat() on Ubuntu - utimensat() is supposed to obsolete utime/utimes and is also used by "cp" and "touch" - - solves utimes() EINVAL bug for certain CIFS/NTFS drives: http://www.freefilesync.org/forum/viewtopic.php?t=387 - => don't use utimensat() directly, but open file descriptor manually, else EINVAL, again! - - => let's give utimensat another chance: + => let's give utimensat another chance: + using open()/futimens() for regular files and utimensat(AT_SYMLINK_NOFOLLOW) for symlinks is consistent with "cp" and "touch"! */ struct ::timespec newTimes[2] = {}; - newTimes[0].tv_sec = ::time(nullptr); //access time; using UTIME_OMIT for tv_nsec would trigger even more bugs!! - //http://www.freefilesync.org/forum/viewtopic.php?t=1701 - newTimes[1].tv_sec = modTime; //modification time + newTimes[0].tv_sec = ::time(nullptr); //access time; using UTIME_OMIT for tv_nsec would trigger even more bugs: http://www.freefilesync.org/forum/viewtopic.php?t=1701 + newTimes[1] = modTime; //modification time - //=> using open()/futimens() for regular files and utimensat(AT_SYMLINK_NOFOLLOW) for symlinks is consistent with "cp" and "touch"! if (procSl == ProcSymlink::FOLLOW) { - const int fdFile = ::open(filePath.c_str(), O_WRONLY); - if (fdFile == -1) - { - if (errno == EACCES) //bullshit, access denied even with 0777 permissions! => utimes should work! - return setWriteTimeFallback(filePath, modTime, procSl); //throw FileError + //hell knows why files on gvfs-mounted Samba shares fail to open(O_WRONLY) returning EOPNOTSUPP: + //http://www.freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works + if (::utimensat(AT_FDCWD, itemPath.c_str(), newTimes, 0) == 0) + return; - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"open"); - } + //in other cases utimensat() returns EINVAL for CIFS/NTFS drives, but open+futimens works: http://www.freefilesync.org/forum/viewtopic.php?t=387 + const int fdFile = ::open(itemPath.c_str(), O_WRONLY); + if (fdFile == -1) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"open"); ZEN_ON_SCOPE_EXIT(::close(fdFile)); if (::futimens(fdFile, newTimes) != 0) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"futimens"); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"futimens"); } else { - if (::utimensat(AT_FDCWD, filePath.c_str(), newTimes, AT_SYMLINK_NOFOLLOW) != 0) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"utimensat"); + if (::utimensat(AT_FDCWD, itemPath.c_str(), newTimes, AT_SYMLINK_NOFOLLOW) != 0) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"utimensat"); } } @@ -1013,7 +991,7 @@ struct AttrBufFileTimes } __attribute__((aligned(4), packed)); -void setWriteTimeNative(const Zstring& filePath, +void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& writeTime, const struct ::timespec* createTime, //optional ProcSymlink procSl) //throw FileError @@ -1035,18 +1013,18 @@ void setWriteTimeNative(const Zstring& filePath, attrBuf.writeTime.tv_sec = writeTime.tv_sec; attrBuf.writeTime.tv_nsec = writeTime.tv_nsec; - const int rv = ::setattrlist(filePath.c_str(), //const char* path, + const int rv = ::setattrlist(itemPath.c_str(), //const char* path, &attribs, //struct ::attrlist* attrList, createTime ? &attrBuf.createTime : &attrBuf.writeTime, //void* attrBuf, (createTime ? sizeof(attrBuf.createTime) : 0) + sizeof(attrBuf.writeTime), //size_t attrBufSize, procSl == ProcSymlink::DIRECT ? FSOPT_NOFOLLOW : 0); //unsigned long options if (rv != 0) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"setattrlist"); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"setattrlist"); } /* void getFileTimeRaw(int fd, //throw FileError - const Zstring& filePath, //for error reporting only + const Zstring& itemPath, //for error reporting only struct ::timespec& createTime, //out struct ::timespec& writeTime) // { @@ -1063,7 +1041,7 @@ void getFileTimeRaw(int fd, //throw FileError sizeof(attrBuf), //size_t attrBufSize, 0); //unsigned long options if (rv != 0) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"fgetattrlist"); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"fgetattrlist"); createTime.tv_sec = attrBuf.createTime.tv_sec; createTime.tv_nsec = attrBuf.createTime.tv_nsec; @@ -1111,7 +1089,9 @@ void zen::setFileTime(const Zstring& filePath, std::int64_t modTime, ProcSymlink setWriteTimeNative(filePath, timetToFileTime(modTime), nullptr, procSl); //throw FileError #elif defined ZEN_LINUX - setWriteTimeNative(filePath, modTime, procSl); //throw FileError + struct ::timespec writeTime = {}; + writeTime.tv_sec = modTime; + setWriteTimeNative(filePath, writeTime, procSl); //throw FileError #elif defined ZEN_MAC struct ::timespec writeTime = {}; @@ -1217,15 +1197,15 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P try { //enable privilege: required to read/write SACL information (only) - activatePrivilege(SE_SECURITY_NAME); //throw FileError + activatePrivilege(PrivilegeName::SECURITY); //throw FileError //Note: trying to copy SACL (SACL_SECURITY_INFORMATION) may return ERROR_PRIVILEGE_NOT_HELD (1314) on Samba shares. This is not due to missing privileges! //However, this is okay, since copying NTFS permissions doesn't make sense in this case anyway //the following privilege may be required according to https://msdn.microsoft.com/en-us/library/aa364399 (although not needed nor active in my tests) - activatePrivilege(SE_BACKUP_NAME); //throw FileError + activatePrivilege(PrivilegeName::BACKUP); //throw FileError //enable privilege: required to copy owner information - activatePrivilege(SE_RESTORE_NAME); //throw FileError + activatePrivilege(PrivilegeName::RESTORE); //throw FileError } catch (const FileError& e)//add some more context description (e.g. user is not an admin) { @@ -1674,22 +1654,21 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool setWriteTimeNative(targetLink, sourceAttr.ftLastWriteTime, &sourceAttr.ftCreationTime, ProcSymlink::DIRECT); //throw FileError -#elif defined ZEN_LINUX +#else 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"); - setWriteTimeNative(targetLink, sourceInfo.st_mtime, ProcSymlink::DIRECT); //throw FileError - +#ifdef ZEN_LINUX + setWriteTimeNative(targetLink, sourceInfo.st_mtim, ProcSymlink::DIRECT); //throw FileError #elif defined ZEN_MAC - 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 (hasNativeSupportForExtendedAtrributes(targetLink)) //throw FileError ::copyfile(sourceLink.c_str(), targetLink.c_str(), nullptr, COPYFILE_XATTR | COPYFILE_NOFOLLOW); //ignore errors, see related comments in copyFileOsSpecific() setWriteTimeNative(targetLink, sourceInfo.st_mtimespec, &sourceInfo.st_birthtimespec, ProcSymlink::DIRECT); //throw FileError +#else +#error WTF +#endif #endif if (copyFilePermissions) @@ -1835,9 +1814,9 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw { //try to get backup read and write privileges: help solve most "access denied" errors with FILE_FLAG_BACKUP_SEMANTICS: //http://www.freefilesync.org/forum/viewtopic.php?t=1714 - try { activatePrivilege(SE_BACKUP_NAME); } + try { activatePrivilege(PrivilegeName::BACKUP); } catch (const FileError&) {} - try { activatePrivilege(SE_RESTORE_NAME); } + try { activatePrivilege(PrivilegeName::RESTORE); } catch (const FileError&) {} //open sourceFile for reading @@ -1903,8 +1882,8 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw CREATE_NEW, //_In_ DWORD dwCreationDisposition, //FILE_FLAG_OVERLAPPED must not be used! FILE_FLAG_NO_BUFFERING should not be used! (fileInfoSource.dwFileAttributes & validAttribs) | - FILE_FLAG_SEQUENTIAL_SCAN | - FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, + FILE_FLAG_SEQUENTIAL_SCAN | //_In_ DWORD dwFlagsAndAttributes, + FILE_FLAG_BACKUP_SEMANTICS, //-> also required by FSCTL_SET_SPARSE nullptr); //_In_opt_ HANDLE hTemplateFile if (hFileTarget == INVALID_HANDLE_VALUE) { @@ -2186,9 +2165,9 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE { //try to get backup read and write privileges: may help solve some "access denied" errors bool backupPrivilegesActive = true; - try { activatePrivilege(SE_BACKUP_NAME); } + try { activatePrivilege(PrivilegeName::BACKUP); } catch (const FileError&) { backupPrivilegesActive = false; } - try { activatePrivilege(SE_RESTORE_NAME); } + try { activatePrivilege(PrivilegeName::RESTORE); } catch (const FileError&) { backupPrivilegesActive = false; } auto guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); @@ -2402,7 +2381,7 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError //sourceInfo.st_birthtime; -> only seconds-precision //sourceInfo.st_mtime; -> #else - setWriteTimeNative(targetFile, sourceInfo.st_mtime, ProcSymlink::FOLLOW); //throw FileError + setWriteTimeNative(targetFile, sourceInfo.st_mtim, ProcSymlink::FOLLOW); //throw FileError #endif InSyncAttributes newAttrib; @@ -2410,7 +2389,7 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError #ifdef ZEN_MAC newAttrib.modificationTime = sourceInfo.st_mtimespec.tv_sec; //use same time variable like setWriteTimeNative() for consistency #else - newAttrib.modificationTime = sourceInfo.st_mtime; + newAttrib.modificationTime = sourceInfo.st_mtim.tv_sec; // #endif newAttrib.sourceFileId = extractFileId(sourceInfo); newAttrib.targetFileId = extractFileId(targetInfo); -- cgit