diff options
Diffstat (limited to 'shared')
-rw-r--r-- | shared/dir_name.cpp | 2 | ||||
-rw-r--r-- | shared/file_handling.cpp | 75 | ||||
-rw-r--r-- | shared/file_handling.h | 1 | ||||
-rw-r--r-- | shared/resolve_path.cpp | 9 |
4 files changed, 56 insertions, 31 deletions
diff --git a/shared/dir_name.cpp b/shared/dir_name.cpp index 6945fb9c..f1b0c3c2 100644 --- a/shared/dir_name.cpp +++ b/shared/dir_name.cpp @@ -37,7 +37,7 @@ void setDirectoryNameImpl(const wxString& dirname, wxDirPickerCtrl* dirPicker, w staticBox->GetStaticBox()->SetLabel(dirNormalized == dirFormatted ? wxString(_("Drag && drop")) : dirFormatted); } - + if (dirPicker) { if (!dirFormatted.empty() && diff --git a/shared/file_handling.cpp b/shared/file_handling.cpp index 6b41f4f5..8fa8568a 100644 --- a/shared/file_handling.cpp +++ b/shared/file_handling.cpp @@ -492,7 +492,7 @@ void zen::moveFile(const Zstring& sourceFile, const Zstring& targetFile, bool ig if (symlinkExists(sourceFile)) copySymlink(sourceFile, targetFile, SYMLINK_TYPE_FILE, false); //throw (FileError) dont copy filesystem permissions else - copyFile(sourceFile, targetFile, false, copyCallback.get()); //throw (FileError); + copyFile(sourceFile, targetFile, false, true, copyCallback.get()); //throw (FileError); //attention: if copy-operation was cancelled an exception is thrown => sourcefile is not deleted, as we wish! } @@ -1441,6 +1441,8 @@ void rawCopyWinApi(const Zstring& sourceFile, dst::isFatDrive(targetFile) && getFilesize(sourceFile) >= 4U * UInt64(1024U * 1024 * 1024)) //throw (FileError) errorMessage += L"\nFAT volume cannot store files larger than 4 gigabyte!"; + + //note: ERROR_INVALID_PARAMETER can also occur when copying to a SharePoint server or MS SkyDrive and the target filename is of a restricted type. } catch(...) {} @@ -1715,7 +1717,7 @@ void rawCopyWinApi(const Zstring& sourceFile, // totalBytesTransferred += bytesRead; // //#ifndef _MSC_VER -//#warning totalBytesTransferred kann größer als filesize sein!! +//#warning totalBytesTransferred kann größer als filesize sein!! //#endif // // //invoke callback method to update progress indicators @@ -1878,41 +1880,60 @@ void copyFileImpl(const Zstring& sourceFile, void zen::copyFile(const Zstring& sourceFile, //throw (FileError: ErrorTargetPathMissing, ErrorFileLocked); const Zstring& targetFile, bool copyFilePermissions, + bool transactionalCopy, CallbackCopyFile* callback) { - Zstring temporary = targetFile + zen::TEMP_FILE_ENDING; //use temporary file until a correct date has been set - Loki::ScopeGuard guardTempFile = Loki::MakeGuard([&]() { removeFile(temporary); }); //transactional behavior: ensure cleanup (e.g. network drop) -> ref to temporary[!] - - //raw file copy - try + if (transactionalCopy) { - copyFileImpl(sourceFile, temporary, callback); //throw FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked - } - catch (ErrorTargetExisting&) - { - //determine non-used temp file name "first": - //using optimistic strategy: assume everything goes well, but recover on error -> minimize file accesses - temporary = createTempName(targetFile); + Zstring temporary = targetFile + zen::TEMP_FILE_ENDING; //use temporary file until a correct date has been set + Loki::ScopeGuard guardTempFile = Loki::MakeGuard([&]() { removeFile(temporary); }); //transactional behavior: ensure cleanup (e.g. network drop) -> ref to temporary[!] - //retry - copyFileImpl(sourceFile, temporary, callback); //throw FileError - } + //raw file copy + try + { + copyFileImpl(sourceFile, temporary, callback); //throw FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked + } + catch (ErrorTargetExisting&) + { + //determine non-used temp file name "first": + //using optimistic strategy: assume everything goes well, but recover on error -> minimize file accesses + temporary = createTempName(targetFile); - //have target file deleted (after read access on source and target has been confirmed) => allow for almost transactional overwrite - if (callback) callback->deleteTargetFile(targetFile); + //retry + copyFileImpl(sourceFile, temporary, callback); //throw FileError + } - //rename temporary file: - //perf: this call is REALLY expensive on unbuffered volumes! ~40% performance decrease on FAT USB stick! - renameFile(temporary, targetFile); //throw (FileError) + //have target file deleted (after read access on source and target has been confirmed) => allow for almost transactional overwrite + if (callback) callback->deleteTargetFile(targetFile); - guardTempFile.Dismiss(); - Loki::ScopeGuard guardTargetFile = Loki::MakeGuard(&removeFile, targetFile); + //rename temporary file: + //perf: this call is REALLY expensive on unbuffered volumes! ~40% performance decrease on FAT USB stick! + renameFile(temporary, targetFile); //throw (FileError) - //perf: interestingly it is much faster to apply file times BEFORE renaming temporary! + guardTempFile.Dismiss(); + + //perf: interestingly it is much faster to apply file times BEFORE renaming temporary! + } + else + { + //have target file deleted + if (callback) callback->deleteTargetFile(targetFile); + + copyFileImpl(sourceFile, targetFile, callback); //throw FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked + } +/* + Note: non-transactional file copy solves at least four problems: + -> skydrive - doesn't allow for .ffs_tmp extension and returns ERROR_INVALID_PARAMETER + -> network renaming issues + -> allow for true delete before copy to handle low disk space problems + -> higher performance on non-buffered drives (e.g. usb sticks) +*/ //set file permissions if (copyFilePermissions) + { + Loki::ScopeGuard guardTargetFile = Loki::MakeGuard(&removeFile, targetFile); copyObjectPermissions(sourceFile, targetFile, true); //throw (FileError) - - guardTargetFile.Dismiss(); //target has been created successfully! + guardTargetFile.Dismiss(); //target has been created successfully! + } } diff --git a/shared/file_handling.h b/shared/file_handling.h index 113168f3..bdd73bea 100644 --- a/shared/file_handling.h +++ b/shared/file_handling.h @@ -66,6 +66,7 @@ void createDirectory(const Zstring& directory); //throw (FileError); -> function void copyFile(const Zstring& sourceFile, //throw (FileError: ErrorTargetPathMissing, ErrorFileLocked (Windows-only)); const Zstring& targetFile, bool copyFilePermissions, + bool transactionalCopy, CallbackCopyFile* callback); //may be NULL //Note: it MAY happen that copyFile() leaves temp files behind, e.g. temporary network drop. // => clean them up at an appropriate time (automatically set sync directions to delete them). They have the following ending: diff --git a/shared/resolve_path.cpp b/shared/resolve_path.cpp index a9d1e0e5..2a9cb7e6 100644 --- a/shared/resolve_path.cpp +++ b/shared/resolve_path.cpp @@ -29,18 +29,21 @@ Zstring resolveBrokenNetworkMap(const Zstring& dirname) //circumvent issue with if (dirname.size() >= 2 && iswalpha(dirname[0]) && dirname[1] == L':') { Zstring driveLetter(dirname.c_str(), 2); //e.g.: "Q:" - if (::GetFileAttributes((driveLetter + L'\\').c_str()) == INVALID_FILE_ATTRIBUTES) + + //if (::GetFileAttributes((driveLetter + L'\\').c_str()) == INVALID_FILE_ATTRIBUTES) <- this will seriously block if network is not reachable!!! { DWORD bufferSize = 10000; std::vector<wchar_t> remoteNameBuffer(bufferSize); DWORD rv = ::WNetGetConnection(driveLetter.c_str(), //__in LPCTSTR lpLocalName in the form "<driveletter>:" &remoteNameBuffer[0], //__out LPTSTR lpRemoteName, &bufferSize); //__inout LPDWORD lpnLength - (void)rv; - //no error check here! remoteNameBuffer will be filled on ERROR_CONNECTION_UNAVAIL and maybe others? + if (rv == NO_ERROR || + rv == ERROR_CONNECTION_UNAVAIL) //remoteNameBuffer will be filled nevertheless! + { Zstring networkShare = &remoteNameBuffer[0]; if (!networkShare.empty()) return networkShare + (dirname.c_str() + 2); //replace "Q:\subdir" by "\\server\share\subdir" + } } } return dirname; |