summaryrefslogtreecommitdiff
path: root/zen/file_access.cpp
diff options
context:
space:
mode:
authorDaniel Wilhelm <shieldwed@outlook.com>2016-10-29 11:41:53 +0200
committerDaniel Wilhelm <shieldwed@outlook.com>2016-10-29 11:41:53 +0200
commit7302bb4484d517a72cdffbd13ec7a9f2324cde01 (patch)
tree17d2964c6768d49510206836a496fb1802a63e08 /zen/file_access.cpp
parent8.5 (diff)
downloadFreeFileSync-7302bb4484d517a72cdffbd13ec7a9f2324cde01.tar.gz
FreeFileSync-7302bb4484d517a72cdffbd13ec7a9f2324cde01.tar.bz2
FreeFileSync-7302bb4484d517a72cdffbd13ec7a9f2324cde01.zip
8.6
Diffstat (limited to 'zen/file_access.cpp')
-rw-r--r--zen/file_access.cpp326
1 files changed, 178 insertions, 148 deletions
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index 2e9d93f8..bad1b60d 100644
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -730,7 +730,7 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError
if (creationTime)
basicInfo.CreationTime = toLargeInteger(*creationTime);
- //set file time + attributes
+ //set file time + attributes
if (!::SetFileInformationByHandle(hFile, //__in HANDLE hFile,
FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
&basicInfo, //__in LPVOID lpFileInformation,
@@ -906,21 +906,21 @@ void setWriteTimeNative(const Zstring& itemPath,
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
#if 0 //waiting for user feedback...
-#ifdef ZEN_WIN_VISTA_AND_LATER
- //bugs, bugs, bugs.... on "SharePoint" SetFileAttributes() seems to affect file modification time: http://www.freefilesync.org/forum/viewtopic.php?t=3699
- //on Vista we can avoid reopening the file (and the SharePoint bug)
-ZEN_ON_SCOPE_EXIT(
- if (attribsToRestore != INVALID_FILE_ATTRIBUTES)
- {
- FILE_BASIC_INFO basicInfo = {};
- basicInfo.FileAttributes = attribsToRestore;
- ::SetFileInformationByHandle(hFile, //__in HANDLE hFile,
- FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
- &basicInfo, //__in LPVOID lpFileInformation,
- sizeof(basicInfo)); //__in DWORD dwBufferSize
- attribsToRestore = INVALID_FILE_ATTRIBUTES;
- }
- );
+#ifdef ZEN_WIN_VISTA_AND_LATER
+ //bugs, bugs, bugs.... on "SharePoint" SetFileAttributes() seems to affect file modification time: http://www.freefilesync.org/forum/viewtopic.php?t=3699
+ //on Vista we can avoid reopening the file (and the SharePoint bug)
+ ZEN_ON_SCOPE_EXIT(
+ if (attribsToRestore != INVALID_FILE_ATTRIBUTES)
+ {
+ FILE_BASIC_INFO basicInfo = {};
+ basicInfo.FileAttributes = attribsToRestore;
+ ::SetFileInformationByHandle(hFile, //__in HANDLE hFile,
+ FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
+ &basicInfo, //__in LPVOID lpFileInformation,
+ sizeof(basicInfo)); //__in DWORD dwBufferSize
+ attribsToRestore = INVALID_FILE_ATTRIBUTES;
+ }
+ );
#endif
#endif
@@ -1407,26 +1407,26 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P
}
-void makeDirectoryRecursivelyImpl(const Zstring& directory) //FileError
+void makeDirectoryRecursivelyImpl(const Zstring& dirPath) //FileError
{
- assert(!endsWith(directory, FILE_NAME_SEPARATOR)); //even "C:\" should be "C:" as input!
+ assert(!endsWith(dirPath, FILE_NAME_SEPARATOR)); //even "C:\" should be "C:" as input!
try
{
- copyNewDirectory(Zstring(), directory, false /*copyFilePermissions*/); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
+ copyNewDirectory(Zstring(), dirPath, false /*copyFilePermissions*/); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
}
catch (const ErrorTargetExisting&) {} //*something* existing: folder or FILE!
catch (const ErrorTargetPathMissing&)
{
//we need to create parent directories first
- const Zstring dirParent = beforeLast(directory, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
- if (!dirParent.empty())
+ const Zstring parentPath = beforeLast(dirPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
+ if (!parentPath.empty())
{
//recurse...
- makeDirectoryRecursivelyImpl(dirParent); //throw FileError
+ makeDirectoryRecursivelyImpl(parentPath); //throw FileError
//now try again...
- copyNewDirectory(Zstring(), directory, false /*copyFilePermissions*/); //throw FileError, (ErrorTargetExisting), (ErrorTargetPathMissing)
+ copyNewDirectory(Zstring(), dirPath, false /*copyFilePermissions*/); //throw FileError, (ErrorTargetExisting), (ErrorTargetPathMissing)
return;
}
throw;
@@ -1586,7 +1586,7 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath,
nullptr); //_Inout_opt_ LPOVERLAPPED lpOverlapped
}
- //(try to) set creation and modification time
+ //(try to) set creation (and modification) time
/*bool rv = */::SetFileTime(hDirTrg, //_In_ HANDLE hFile,
&dirInfo.ftCreationTime, //_Out_opt_ LPFILETIME lpCreationTime,
nullptr, //_Out_opt_ LPFILETIME lpLastAccessTime,
@@ -1598,6 +1598,16 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath,
#elif defined ZEN_MAC
if (hasNativeSupportForExtendedAtrributes(targetPath)) //throw FileError
::copyfile(sourcePath.c_str(), targetPath.c_str(), nullptr, COPYFILE_XATTR); //ignore errors, see related comments in copyFileOsSpecific()
+
+ //(try to) set creation (and modification) time
+ if (dirInfo.st_birthtimespec.tv_sec != 0)
+ try
+ {
+ setWriteTimeNative(targetPath, dirInfo.st_mtimespec, &dirInfo.st_birthtimespec, ProcSymlink::FOLLOW); //throw FileError
+ //dirInfo.st_birthtime; -> only seconds-precision
+ //dirInfo.st_mtime; ->
+ }
+ catch (FileError&) {}
#endif
ZEN_ON_SCOPE_FAIL(try { removeDirectorySimple(targetPath); }
@@ -1691,7 +1701,7 @@ namespace
{
#ifdef ZEN_WIN
/*
- CopyFileEx() BackupRead() FileRead()
+ CopyFileEx() BackupRead() ReadFile()
--------------------------------------------
Attributes YES NO NO
create time NO NO NO
@@ -1702,7 +1712,7 @@ Sparse NO YES NO
Nonstandard FS YES UNKNOWN -> error writing ADS to Samba, issues reading from NAS, error copying files having "blocked" state... ect.
PERF - 6% faster
-Mark stream as compressed: FSCTL_SET_COMPRESSION - compatible with both BackupRead() and FileRead()
+Mark stream as compressed: FSCTL_SET_COMPRESSION - compatible with both BackupRead() and ReadFile()
Current support for combinations of NTFS extended attributes:
@@ -1710,9 +1720,9 @@ Current support for combinations of NTFS extended attributes:
source attr | tf normal | tf compressed | tf encrypted | handled by
============|==================================================================
--- | --- -C- E-- copyFileWindowsDefault
- --S | --S -CS E-S copyFileWindowsBackupStream
+ --S | --S -CS E-S copyFileWindowsStream
-C- | -C- -C- E-- copyFileWindowsDefault
- -CS | -CS -CS E-S copyFileWindowsBackupStream
+ -CS | -CS -CS E-S copyFileWindowsStream
E-- | E-- E-- E-- copyFileWindowsDefault
E-S | E-- (NOK) E-- (NOK) E-- (NOK) copyFileWindowsDefault -> may fail with ERROR_DISK_FULL for large sparse files!!
@@ -1810,18 +1820,26 @@ bool canCopyAsSparse(const Zstring& sourceFile, const Zstring& targetFile) //thr
return false;
ZEN_ON_SCOPE_EXIT(::CloseHandle(hSource));
- BY_HANDLE_FILE_INFORMATION fileInfoSource = {};
- if (!::GetFileInformationByHandle(hSource, &fileInfoSource))
+ BY_HANDLE_FILE_INFORMATION sourceInfo = {};
+ if (!::GetFileInformationByHandle(hSource, &sourceInfo))
return false;
- return canCopyAsSparse(fileInfoSource.dwFileAttributes, targetFile); //throw ()
+ return canCopyAsSparse(sourceInfo.dwFileAttributes, targetFile); //throw ()
}
//=============================================================================================
-InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting, ErrorFileLocked
- const Zstring& targetFile,
- const std::function<void(std::int64_t bytesDelta)>& notifyProgress)
+enum class StreamCopyType
+{
+ READ_FILE,
+ BACKUP_READ,
+};
+
+
+InSyncAttributes copyFileWindowsStream(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting, ErrorFileLocked
+ const Zstring& targetFile,
+ StreamCopyType scType,
+ const std::function<void(std::int64_t bytesDelta)>& notifyProgress)
{
//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
@@ -1830,7 +1848,6 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
try { activatePrivilege(PrivilegeName::RESTORE); }
catch (const FileError&) {}
- //open sourceFile for reading
HANDLE hFileSource = ::CreateFile(applyLongPathPrefix(sourceFile).c_str(), //_In_ LPCTSTR lpFileName,
GENERIC_READ, //_In_ DWORD dwDesiredAccess,
FILE_SHARE_READ | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
@@ -1862,16 +1879,18 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
throw FileError(errorMsg, errorDescr);
}
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileSource));
+ FileInput fileIn(hFileSource, sourceFile); //pass ownership
+
+ if (notifyProgress) notifyProgress(0); //throw X!
//----------------------------------------------------------------------
- BY_HANDLE_FILE_INFORMATION fileInfoSource = {};
- if (!::GetFileInformationByHandle(hFileSource, &fileInfoSource))
+ BY_HANDLE_FILE_INFORMATION sourceInfo = {};
+ if (!::GetFileInformationByHandle(fileIn.getHandle(), &sourceInfo))
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceFile)), L"GetFileInformationByHandle");
//encrypted files cannot be read with BackupRead which would fail silently!
- const bool sourceIsEncrypted = (fileInfoSource.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0;
- if (sourceIsEncrypted)
+ const bool sourceIsEncrypted = (sourceInfo.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0;
+ if (sourceIsEncrypted && scType == StreamCopyType::BACKUP_READ)
throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: Source file is encrypted.");
//----------------------------------------------------------------------
@@ -1883,7 +1902,6 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; //
//FILE_ATTRIBUTE_ENCRYPTED -> no!
- //create targetFile and open it for writing
HANDLE hFileTarget = ::CreateFile(applyLongPathPrefix(targetFile).c_str(), //_In_ LPCTSTR lpFileName,
GENERIC_READ | GENERIC_WRITE | DELETE, //_In_ DWORD dwDesiredAccess,
//GENERIC_READ required for FSCTL_SET_COMPRESSION, DELETE for ::SetFileInformationByHandle(),FileDispositionInfo
@@ -1892,7 +1910,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
CREATE_NEW, //_In_ DWORD dwCreationDisposition,
//FILE_FLAG_OVERLAPPED must not be used! FILE_FLAG_NO_BUFFERING should not be used!
- (fileInfoSource.dwFileAttributes & validAttribs) |
+ (sourceInfo.dwFileAttributes & validAttribs) |
FILE_FLAG_SEQUENTIAL_SCAN | //_In_ DWORD dwFlagsAndAttributes,
FILE_FLAG_BACKUP_SEMANTICS, //-> also required by FSCTL_SET_SPARSE
nullptr); //_In_opt_ HANDLE hTemplateFile
@@ -1911,14 +1929,14 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
throw FileError(errorMsg, errorDescr);
}
#ifdef ZEN_WIN_VISTA_AND_LATER
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileTarget));
+ FileOutput fileOut(hFileTarget, targetFile); //pass ownership
//no need for ::DeleteFile(), we already have an open handle! Maybe this also prevents needless buffer-flushing in ::CloseHandle()??? Anyway, same behavior like ::CopyFileEx()
ZEN_ON_SCOPE_FAIL
(
FILE_DISPOSITION_INFO di = {};
di.DeleteFile = true;
- if (!::SetFileInformationByHandle(hFileTarget, //_In_ HANDLE hFile,
+ if (!::SetFileInformationByHandle(fileOut.getHandle(), //_In_ HANDLE hFile,
FileDispositionInfo, //_In_ FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
&di, //_In_ LPVOID lpFileInformation,
sizeof(di))) //_In_ DWORD dwBufferSize
@@ -1928,29 +1946,30 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
ZEN_ON_SCOPE_FAIL(try { removeFile(targetFile); }
catch (FileError&) {} ); //transactional behavior: guard just after opening target and before managing hFileTarget
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileTarget));
+ FileOutput fileOut(hFileTarget, targetFile); //pass ownership
#endif
+ if (notifyProgress) notifyProgress(0); //throw X!
//----------------------------------------------------------------------
- BY_HANDLE_FILE_INFORMATION fileInfoTarget = {};
- if (!::GetFileInformationByHandle(hFileTarget, &fileInfoTarget))
+ BY_HANDLE_FILE_INFORMATION targetInfo = {};
+ if (!::GetFileInformationByHandle(fileOut.getHandle(), &targetInfo))
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"GetFileInformationByHandle");
//return up-to-date file attributes
InSyncAttributes newAttrib;
- newAttrib.fileSize = get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh);
- newAttrib.modificationTime = filetimeToTimeT(fileInfoSource.ftLastWriteTime);
- newAttrib.sourceFileId = extractFileId(fileInfoSource);
- newAttrib.targetFileId = extractFileId(fileInfoTarget);
+ newAttrib.fileSize = get64BitUInt(sourceInfo.nFileSizeLow, sourceInfo.nFileSizeHigh);
+ newAttrib.modificationTime = filetimeToTimeT(sourceInfo.ftLastWriteTime);
+ newAttrib.sourceFileId = extractFileId(sourceInfo);
+ newAttrib.targetFileId = extractFileId(targetInfo);
//#################### copy NTFS compressed attribute #########################
- const bool sourceIsCompressed = (fileInfoSource.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0;
- const bool targetIsCompressed = (fileInfoTarget.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; //already set by CreateFile if target parent folder is compressed!
+ const bool sourceIsCompressed = (sourceInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0;
+ const bool targetIsCompressed = (targetInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; //already set by CreateFile if target parent folder is compressed!
if (sourceIsCompressed && !targetIsCompressed)
{
USHORT cmpState = COMPRESSION_FORMAT_DEFAULT;
DWORD bytesReturned = 0;
- if (!::DeviceIoControl(hFileTarget, //_In_ HANDLE hDevice,
+ if (!::DeviceIoControl(fileOut.getHandle(), //_In_ HANDLE hDevice,
FSCTL_SET_COMPRESSION, //_In_ DWORD dwIoControlCode,
&cmpState, //_In_opt_ LPVOID lpInBuffer,
sizeof(cmpState), //_In_ DWORD nInBufferSize,
@@ -1969,91 +1988,97 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw
//The application should retrieve the attributes by using GetFileAttributes prior to creating a backup with BackupRead.
//If a file originally had the sparse attribute (FILE_ATTRIBUTE_SPARSE_FILE), the backup utility must explicitly set the
//attribute on the restored file.
-
+ if (scType == StreamCopyType::BACKUP_READ)
#ifdef ZEN_WIN_VISTA_AND_LATER
- if (canCopyAsSparse(fileInfoSource.dwFileAttributes, hFileTarget)) //throw ()
+ if (canCopyAsSparse(sourceInfo.dwFileAttributes, fileOut.getHandle())) //throw ()
#else
- if (canCopyAsSparse(fileInfoSource.dwFileAttributes, targetFile)) //throw ()
+ if (canCopyAsSparse(sourceInfo.dwFileAttributes, targetFile)) //throw ()
#endif
- {
- DWORD bytesReturned = 0;
- if (!::DeviceIoControl(hFileTarget, //_In_ HANDLE hDevice,
- FSCTL_SET_SPARSE, //_In_ DWORD dwIoControlCode,
- nullptr, //_In_opt_ LPVOID lpInBuffer,
- 0, //_In_ DWORD nInBufferSize,
- nullptr, //_Out_opt_ LPVOID lpOutBuffer,
- 0, //_In_ DWORD nOutBufferSize,
- &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned,
- nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(targetFile)), L"DeviceIoControl, FSCTL_SET_SPARSE");
- }
+ {
+ DWORD bytesReturned = 0;
+ if (!::DeviceIoControl(fileOut.getHandle(), //_In_ HANDLE hDevice,
+ FSCTL_SET_SPARSE, //_In_ DWORD dwIoControlCode,
+ nullptr, //_In_opt_ LPVOID lpInBuffer,
+ 0, //_In_ DWORD nInBufferSize,
+ nullptr, //_Out_opt_ LPVOID lpOutBuffer,
+ 0, //_In_ DWORD nOutBufferSize,
+ &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned,
+ nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(targetFile)), L"DeviceIoControl, FSCTL_SET_SPARSE");
+ }
//----------------------------------------------------------------------
- const DWORD BUFFER_SIZE = std::max(128 * 1024, static_cast<int>(sizeof(WIN32_STREAM_ID))); //must be greater than sizeof(WIN32_STREAM_ID)!
- std::vector<BYTE> buffer(BUFFER_SIZE);
+ if (scType == StreamCopyType::READ_FILE)
+ unbufferedStreamCopy(fileIn, fileOut, notifyProgress); //throw FileError, X
+ else
+ {
+ const DWORD BUFFER_SIZE = std::max(128 * 1024, static_cast<int>(sizeof(WIN32_STREAM_ID))); //must be greater than sizeof(WIN32_STREAM_ID)!
+ std::vector<BYTE> buffer(BUFFER_SIZE);
- LPVOID contextRead = nullptr; //manage context for BackupRead()/BackupWrite()
- LPVOID contextWrite = nullptr; //
+ LPVOID contextRead = nullptr; //manage context for BackupRead()/BackupWrite()
+ LPVOID contextWrite = nullptr; //
- ZEN_ON_SCOPE_EXIT(
- if (contextRead ) ::BackupRead (0, nullptr, 0, nullptr, true, false, &contextRead); //MSDN: "lpContext must be passed [...] all other parameters are ignored."
- if (contextWrite) ::BackupWrite(0, nullptr, 0, nullptr, true, false, &contextWrite); ); //
+ ZEN_ON_SCOPE_EXIT(
+ if (contextRead ) ::BackupRead (0, nullptr, 0, nullptr, true, false, &contextRead); //MSDN: "lpContext must be passed [...] all other parameters are ignored."
+ if (contextWrite) ::BackupWrite(0, nullptr, 0, nullptr, true, false, &contextWrite); ); //
- //stream-copy sourceFile to targetFile
- bool eof = false;
- bool someBytesRead = false; //try to detect failure reading encrypted files
- do
- {
- DWORD bytesRead = 0;
- if (!::BackupRead(hFileSource, //__in HANDLE hFile,
- &buffer[0], //__out LPBYTE lpBuffer,
- BUFFER_SIZE, //__in DWORD nNumberOfBytesToRead,
- &bytesRead, //__out LPDWORD lpNumberOfBytesRead,
- false, //__in BOOL bAbort,
- false, //__in BOOL bProcessSecurity,
- &contextRead)) //__out LPVOID *lpContext
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead"); //better use fine-granular error messages "reading/writing"!
-
- if (bytesRead > BUFFER_SIZE)
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: buffer overflow."); //user should never see this
-
- if (bytesRead < BUFFER_SIZE)
- eof = true;
-
- DWORD bytesWritten = 0;
- if (!::BackupWrite(hFileTarget, //__in HANDLE hFile,
- &buffer[0], //__in LPBYTE lpBuffer,
- bytesRead, //__in DWORD nNumberOfBytesToWrite,
- &bytesWritten, //__out LPDWORD lpNumberOfBytesWritten,
- false, //__in BOOL bAbort,
- false, //__in BOOL bProcessSecurity,
- &contextWrite)) //__out LPVOID *lpContext
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)), L"BackupWrite");
-
- if (bytesWritten != bytesRead)
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)), L"BackupWrite: incomplete write."); //user should never see this
-
- //total bytes transferred may be larger than file size! context information + ADS or smaller (sparse, compressed)!
- if (notifyProgress) notifyProgress(bytesRead); //throw X!
-
- if (bytesRead > 0)
- someBytesRead = true;
- }
- while (!eof);
+ //stream-copy sourceFile to targetFile
+ bool eof = false;
+ bool someBytesRead = false; //try to detect failure reading encrypted files
+ do
+ {
+ DWORD bytesRead = 0;
+ if (!::BackupRead(fileIn.getHandle(), //__in HANDLE hFile,
+ &buffer[0], //__out LPBYTE lpBuffer,
+ BUFFER_SIZE, //__in DWORD nNumberOfBytesToRead,
+ &bytesRead, //__out LPDWORD lpNumberOfBytesRead,
+ false, //__in BOOL bAbort,
+ false, //__in BOOL bProcessSecurity,
+ &contextRead)) //__out LPVOID *lpContext
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead"); //better use fine-granular error messages "reading/writing"!
+
+ if (bytesRead > BUFFER_SIZE)
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: buffer overflow."); //user should never see this
+
+ if (bytesRead < BUFFER_SIZE)
+ eof = true;
+
+ DWORD bytesWritten = 0;
+ if (!::BackupWrite(fileOut.getHandle(), //__in HANDLE hFile,
+ &buffer[0], //__in LPBYTE lpBuffer,
+ bytesRead, //__in DWORD nNumberOfBytesToWrite,
+ &bytesWritten, //__out LPDWORD lpNumberOfBytesWritten,
+ false, //__in BOOL bAbort,
+ false, //__in BOOL bProcessSecurity,
+ &contextWrite)) //__out LPVOID *lpContext
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)), L"BackupWrite");
+
+ if (bytesWritten != bytesRead)
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)), L"BackupWrite: incomplete write."); //user should never see this
+
+ //total bytes transferred may be larger than file size! context information + ADS or smaller (sparse, compressed)!
+ if (notifyProgress) notifyProgress(bytesRead); //throw X!
+
+ if (bytesRead > 0)
+ someBytesRead = true;
+ }
+ while (!eof);
- //::BackupRead() silently fails reading encrypted files -> double check!
- if (!someBytesRead && get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh) != 0U)
- //note: there is no guaranteed ordering relation beween bytes transferred and file size! Consider ADS (>) and compressed/sparse files (<)!
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: unknown error"); //user should never see this -> this method is called only if "canCopyAsSparse()"
+ //::BackupRead() silently fails reading encrypted files -> double check!
+ if (!someBytesRead && get64BitUInt(sourceInfo.nFileSizeLow, sourceInfo.nFileSizeHigh) != 0U)
+ //note: there is no guaranteed ordering relation beween bytes transferred and file size! Consider ADS (>) and compressed/sparse files (<)!
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: unknown error"); //user should never see this -> this method is called only if "canCopyAsSparse()"
+ }
- //time needs to be set at the end: BackupWrite() changes modification time
- setFileTimeByHandle(hFileTarget, &fileInfoSource.ftCreationTime,fileInfoSource.ftLastWriteTime, targetFile); //throw FileError
+ //time needs to be set at the end: WriteFile/BackupWrite() change modification time
+ setFileTimeByHandle(fileOut.getHandle(), &sourceInfo.ftCreationTime,sourceInfo.ftLastWriteTime, targetFile); //throw FileError
return newAttrib;
}
-DEFINE_NEW_FILE_ERROR(ErrorFallbackToCopyAsBackupStream);
+DEFINE_NEW_FILE_ERROR(ErrorFallbackToCopyViaBackupRead);
+DEFINE_NEW_FILE_ERROR(ErrorFallbackToCopyViaReadFile);
struct CallbackData
@@ -2127,7 +2152,7 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
#else
if (canCopyAsSparse(cbd.fileInfoSrc.dwFileAttributes, cbd.targetFile_)) //throw ()
#endif
- throw ErrorFallbackToCopyAsBackupStream(L"sparse, callback"); //use a different copy routine!
+ throw ErrorFallbackToCopyViaBackupRead(L"sparse, callback"); //use a different copy routine!
//#################### copy file creation time ################################
::SetFileTime(hDestinationFile, &cbd.fileInfoSrc.ftCreationTime, nullptr, nullptr); //no error handling!
@@ -2170,8 +2195,8 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
}
-InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorFallbackToCopyAsBackupStream
- const Zstring& targetFile,
+InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting, ErrorFileLocked,
+ const Zstring& targetFile, // ErrorFallbackToCopyViaReadFile, ErrorFallbackToCopyViaBackupRead
const std::function<void(std::int64_t bytesDelta)>& notifyProgress)
{
//try to get backup read and write privileges: may help solve some "access denied" errors
@@ -2205,27 +2230,28 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE
nullptr, //__in_opt LPBOOL pbCancel,
copyFlags) != FALSE; //__in DWORD dwCopyFlags
if (cbd.exception)
- std::rethrow_exception(cbd.exception); //throw ?, process errors in callback first!
+ std::rethrow_exception(cbd.exception); //throw X, process errors in callback first!
if (!success)
{
const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls!
- //don't suppress "lastError == ERROR_REQUEST_ABORTED": a user aborted operation IS an error condition!
+ //don't suppress "lastError == ERROR_REQUEST_ABORTED": a user-aborted operation IS an error condition!
//trying to copy huge sparse files may directly fail with ERROR_DISK_FULL before entering the callback function
if (canCopyAsSparse(sourceFile, targetFile)) //noexcept
- throw ErrorFallbackToCopyAsBackupStream(L"sparse, copy failure");
+ throw ErrorFallbackToCopyViaBackupRead(L"sparse, copy failure");
if (ec == ERROR_ACCESS_DENIED && backupPrivilegesActive)
- //chances are good this will work with copyFileWindowsBackupStream: http://www.freefilesync.org/forum/viewtopic.php?t=1714
- throw ErrorFallbackToCopyAsBackupStream(L"access denied");
+ //chances are good this will work with copyFileWindowsStream: http://www.freefilesync.org/forum/viewtopic.php?t=1714
+ throw ErrorFallbackToCopyViaReadFile(L"access denied");
- //copying ADS may incorrectly fail with ERROR_FILE_NOT_FOUND: http://www.freefilesync.org/forum/viewtopic.php?t=446
+ //- copying ADS may incorrectly fail with ERROR_FILE_NOT_FOUND: http://www.freefilesync.org/forum/viewtopic.php?t=446
+ //- even BackupWrite may fail => use ReadFile: http://www.freefilesync.org/forum/viewtopic.php?t=2321
if (ec == ERROR_FILE_NOT_FOUND &&
cbd.fileInfoSrc.nNumberOfLinks > 0 &&
cbd.fileInfoTrg.nNumberOfLinks > 0)
- throw ErrorFallbackToCopyAsBackupStream(L"bogus file not found");
+ throw ErrorFallbackToCopyViaReadFile(L"bogus file not found");
//assemble error message...
const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot copy file %x to %y."), L"%x", L"\n" + fmtPath(sourceFile)), L"%y", L"\n" + fmtPath(targetFile));
@@ -2288,11 +2314,15 @@ InSyncAttributes copyFileWindowsSelectRoutine(const Zstring& sourceFile, const Z
{
try
{
- return copyFileWindowsDefault(sourceFile, targetFile, notifyProgress); //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorFallbackToCopyAsBackupStream
+ return copyFileWindowsDefault(sourceFile, targetFile, notifyProgress); //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorFallbackToCopyViaReadFile, ErrorFallbackToCopyViaBackupRead
+ }
+ catch (ErrorFallbackToCopyViaReadFile&)
+ {
+ return copyFileWindowsStream(sourceFile, targetFile, StreamCopyType::READ_FILE, notifyProgress); //throw FileError, ErrorTargetExisting, ErrorFileLocked
}
- catch (ErrorFallbackToCopyAsBackupStream&)
+ catch (ErrorFallbackToCopyViaBackupRead&)
{
- return copyFileWindowsBackupStream(sourceFile, targetFile, notifyProgress); //throw FileError, ErrorTargetExisting, ErrorFileLocked
+ return copyFileWindowsStream(sourceFile, targetFile, StreamCopyType::BACKUP_READ, notifyProgress); //throw FileError, ErrorTargetExisting, ErrorFileLocked
}
}
@@ -2325,7 +2355,7 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError
const Zstring& targetFile,
const std::function<void(std::int64_t bytesDelta)>& notifyProgress)
{
- FileInput fileIn(sourceFile); //throw FileError
+ FileInput fileIn(sourceFile); //throw FileError, (ErrorFileLocked -> Windows-only)
if (notifyProgress) notifyProgress(0); //throw X!
struct ::stat sourceInfo = {};
@@ -2409,16 +2439,16 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError
#endif
/*
- ------------------
- |File Copy Layers|
- ------------------
- copyNewFile
- |
- copyFileOsSpecific (solve 8.3 issue on Windows)
- |
- copyFileWindowsSelectRoutine
- / \
-copyFileWindowsDefault(::CopyFileEx) copyFileWindowsBackupStream(::BackupRead/::BackupWrite)
+ ------------------
+ |File Copy Layers|
+ ------------------
+ copyNewFile
+ |
+ copyFileOsSpecific (solve 8.3 issue on Windows)
+ |
+ copyFileWindowsSelectRoutine
+ / \
+copyFileWindowsDefault(::CopyFileEx) copyFileWindowsStream(::BackupRead/::BackupWrite)
*/
}
bgstack15