diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:15:16 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:15:16 +0200 |
commit | bd6336c629841c6db3a6ca53a936d629d34db53b (patch) | |
tree | 3721ef997864108df175ce677a8a7d4342a6f1d2 /shared/dst_hack.cpp | |
parent | 4.0 (diff) | |
download | FreeFileSync-bd6336c629841c6db3a6ca53a936d629d34db53b.tar.gz FreeFileSync-bd6336c629841c6db3a6ca53a936d629d34db53b.tar.bz2 FreeFileSync-bd6336c629841c6db3a6ca53a936d629d34db53b.zip |
4.1
Diffstat (limited to 'shared/dst_hack.cpp')
-rw-r--r-- | shared/dst_hack.cpp | 387 |
1 files changed, 0 insertions, 387 deletions
diff --git a/shared/dst_hack.cpp b/shared/dst_hack.cpp deleted file mode 100644 index aa7a0f3e..00000000 --- a/shared/dst_hack.cpp +++ /dev/null @@ -1,387 +0,0 @@ -#include "dst_hack.h" -#include "i18n.h" -#include "long_path_prefix.h" -#include "string_utf8.h" -#include "last_error.h" -#include "assert_static.h" -#include <bitset> -#include "global_func.h" -#include <limits> -#include "int64.h" -#include "file_error.h" -#include "dll_loader.h" - -using namespace zen; - - -namespace -{ -//fast ::GetVolumePathName() clone: let's hope it's not too simple (doesn't honor mount points) -Zstring getVolumeName(const Zstring& filename) -{ - //this call is expensive: ~1.5 ms! - // if (!::GetVolumePathName(applyLongPathPrefix(filename).c_str(), //__in LPCTSTR lpszFileName, - // fsName, //__out LPTSTR lpszVolumePathName, - // BUFFER_SIZE)) //__in DWORD cchBufferLength - // ... - // Zstring volumePath = fsName; - // if (!volumePath.EndsWith(FILE_NAME_SEPARATOR)) //a trailing backslash is required - // volumePath += FILE_NAME_SEPARATOR; - - Zstring nameFmt = removeLongPathPrefix(filename); //throw() - if (!endsWith(nameFmt, FILE_NAME_SEPARATOR)) - nameFmt += FILE_NAME_SEPARATOR; //GetVolumeInformation expects trailing backslash - - if (nameFmt.StartsWith(Zstr("\\\\"))) //UNC path: "\\ComputerName\SharedFolder\" - { - size_t nameSize = nameFmt.size(); - const size_t posFirstSlash = nameFmt.find(Zstr("\\"), 2); - if (posFirstSlash != Zstring::npos) - { - nameSize = posFirstSlash + 1; - const size_t posSecondSlash = nameFmt.find(Zstr("\\"), posFirstSlash + 1); - if (posSecondSlash != Zstring::npos) - nameSize = posSecondSlash + 1; - } - return Zstring(nameFmt.c_str(), nameSize); //include trailing backslash! - } - else //local path: "C:\Folder\" - { - const size_t pos = nameFmt.find(Zstr(":\\")); - if (pos == 1) //expect single letter volume - return Zstring(nameFmt.c_str(), 3); - } - - return Zstring(); -} -} - - -bool dst::isFatDrive(const Zstring& fileName) //throw() -{ - const size_t BUFFER_SIZE = MAX_PATH + 1; - wchar_t fsName[BUFFER_SIZE]; - - const Zstring volumePath = getVolumeName(fileName); - if (volumePath.empty()) - return false; - - //suprisingly fast: ca. 0.03 ms per call! - if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName, - NULL, //__out LPTSTR lpVolumeNameBuffer, - 0, //__in DWORD nVolumeNameSize, - NULL, //__out_opt LPDWORD lpVolumeSerialNumber, - NULL, //__out_opt LPDWORD lpMaximumComponentLength, - NULL, //__out_opt LPDWORD lpFileSystemFlags, - fsName, //__out LPTSTR lpFileSystemNameBuffer, - BUFFER_SIZE)) //__in DWORD nFileSystemNameSize - { - assert(false); //shouldn't happen - return false; - } - //DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised) - return fsName == Zstring(L"FAT") || - fsName == Zstring(L"FAT32"); -} - - -bool dst::vistaOrLater() -{ - OSVERSIONINFO osvi = {}; - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - - //IFileOperation is supported with Vista and later - if (::GetVersionEx(&osvi)) - return osvi.dwMajorVersion > 5; - //XP has majorVersion == 5, minorVersion == 1 - //Vista has majorVersion == 6, minorVersion == 0 - //version overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx - return false; -} - - -bool dst::isFatDrive(HANDLE hFile) //throw() -{ - //dynamically load windows API function - typedef BOOL (WINAPI *GetVolumeInformationByHandleWFunc)(HANDLE hFile, - LPWSTR lpVolumeNameBuffer, - DWORD nVolumeNameSize, - LPDWORD lpVolumeSerialNumber, - LPDWORD lpMaximumComponentLength, - LPDWORD lpFileSystemFlags, - LPWSTR lpFileSystemNameBuffer, - DWORD nFileSystemNameSize); - - const util::DllFun<GetVolumeInformationByHandleWFunc> getVolumeInformationByHandle(L"kernel32.dll", "GetVolumeInformationByHandleW"); - if (!getVolumeInformationByHandle) - { - assert(false); - return false; - } - - const size_t BUFFER_SIZE = MAX_PATH + 1; - wchar_t fsName[BUFFER_SIZE]; - - if (!getVolumeInformationByHandle(hFile, //__in HANDLE hFile, - NULL, //__out LPTSTR lpVolumeNameBuffer, - 0, //__in DWORD nVolumeNameSize, - NULL, //__out_opt LPDWORD lpVolumeSerialNumber, - NULL, //__out_opt LPDWORD lpMaximumComponentLength, - NULL, //__out_opt LPDWORD lpFileSystemFlags, - fsName, //__out LPTSTR lpFileSystemNameBuffer, - BUFFER_SIZE)) //__in DWORD nFileSystemNameSize - { - assert(false); //shouldn't happen - return false; - } - //DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised) - return fsName == Zstring(L"FAT") || - fsName == Zstring(L"FAT32"); -} - - -namespace -{ -//convert UInt64 and Int64 to FILETIME -inline -FILETIME toFiletime(Int64 number) -{ - const UInt64 unsig = to<UInt64>(number); - - FILETIME output = {}; - output.dwLowDateTime = unsig.getLo(); - output.dwHighDateTime = unsig.getHi(); - return output; -} - -FILETIME toFiletime(UInt64 number) -{ - FILETIME output = {}; - output.dwLowDateTime = number.getLo(); - output.dwHighDateTime = number.getHi(); - return output; -} - -inline -UInt64 toUInt64(const FILETIME& fileTime) -{ - return UInt64(fileTime.dwLowDateTime, fileTime.dwHighDateTime); -} - - -inline -Int64 toInt64(const FILETIME& fileTime) -{ - return to<Int64>(UInt64(fileTime.dwLowDateTime, fileTime.dwHighDateTime)); -} - - -FILETIME utcToLocal(const FILETIME& utcTime) //throw (std::runtime_error) -{ - //treat binary local time representation (which is invariant under DST time zone shift) as logical UTC: - FILETIME localTime = {}; - if (!::FileTimeToLocalFileTime( - &utcTime, //__in const FILETIME *lpFileTime, - &localTime)) //__out LPFILETIME lpLocalFileTime - { - const std::wstring errorMessage = _("Conversion error:") + " FILETIME -> local FILETIME: " + "(" + - "High: " + toString<std::wstring>(utcTime.dwHighDateTime) + " " + - "Low: " + toString<std::wstring>(utcTime.dwLowDateTime) + ") " + "\n\n" + getLastErrorFormatted(); - throw std::runtime_error(wideToUtf8<std::string>(errorMessage)); - } - return localTime; -} - - -FILETIME localToUtc(const FILETIME& localTime) //throw (std::runtime_error) -{ - //treat binary local time representation (which is invariant under DST time zone shift) as logical UTC: - FILETIME utcTime = {}; - if (!::LocalFileTimeToFileTime( - &localTime, //__in const FILETIME *lpLocalFileTime, - &utcTime)) //__out LPFILETIME lpFileTime - { - const std::wstring errorMessage = _("Conversion error:") + " local FILETIME -> FILETIME: " + "(" + - "High: " + toString<std::wstring>(localTime.dwHighDateTime) + " " + - "Low: " + toString<std::wstring>(localTime.dwLowDateTime) + ") " + "\n\n" + getLastErrorFormatted(); - throw std::runtime_error(wideToUtf8<std::string>(errorMessage)); - } - return utcTime; -} - - -//struct FILETIME {DWORD dwLowDateTime; DWORD dwHighDateTime;}; -const FILETIME FAT_MIN_TIME = { 13374976, 27846544 }; //1980 \ both are valid max/min FAT dates for 2 second precision -const FILETIME FAT_MAX_TIME = { 14487552, 37251238 }; //2107 / - -//http://en.wikipedia.org/wiki/File_Allocation_Table -const size_t PRECISION_WRITE_TIME = 20000000; //number of 100 ns per step -> 2 s -const size_t PRECISION_CREATE_TIME = 100000; // -> 1/100 s - -/* -Number of bits of information in create time: ln_2((FAT_MAX_TIME - FAT_MIN_TIME) / PRECISION_CREATE_TIME) = 38.55534023 -Number of bits of information in write time: 30.91148404 -*/ -//total size available to store data: -const size_t CREATE_TIME_INFO_BITS = 38; -// I. indicator that offset in II) is present -const size_t INDICATOR_EXISTING_BITS = 1; -// II. local<->UTC time offset -const size_t UTC_LOCAL_OFFSET_BITS = 7; -// III. indicator that offset in II) corresponds to current local write time (this could be a hash of the write time) -const size_t WRITE_TIME_HASH_BITS = CREATE_TIME_INFO_BITS - INDICATOR_EXISTING_BITS - UTC_LOCAL_OFFSET_BITS; - - -template <size_t precision> -FILETIME encodeRawInformation(UInt64 rawInfo) -{ - rawInfo *= precision; - rawInfo += toUInt64(FAT_MIN_TIME); - - assert(rawInfo <= toUInt64(FAT_MAX_TIME)); - return toFiletime(rawInfo); -} - - -template <size_t precision> -UInt64 extractRawInformation(const FILETIME& createTime) -{ - assert(toUInt64(FAT_MIN_TIME) <= toUInt64(createTime)); - assert(toUInt64(createTime) <= toUInt64(FAT_MAX_TIME)); - - //FAT create time ranges from 1980 - 2107 (2^7 years) with 1/100 seconds precision - UInt64 rawInfo = toUInt64(createTime); - - rawInfo -= toUInt64(FAT_MIN_TIME); - rawInfo /= precision; //reduce precision (FILETIME has unit 10^-7 s) - - assert(toUInt64(encodeRawInformation<precision>(rawInfo)) == toUInt64(createTime)); //must be reversible - return rawInfo; -} - - -//convert write time to it's minimal representation (no restriction to FAT range "1980 - 2107") -UInt64 extractRawWriteTime(const FILETIME& writeTime) -{ - UInt64 rawInfo = toUInt64(writeTime); - assert(rawInfo % PRECISION_WRITE_TIME == 0U); - rawInfo /= PRECISION_WRITE_TIME; //reduce precision (FILETIME has unit 10^-7 s) - return rawInfo; -} - - -//files with different resolution than 2 seconds are rounded up when written to FAT -FILETIME roundToFatWriteTime(const FILETIME& writeTime) -{ - UInt64 rawData = toUInt64(writeTime); - - if (rawData % PRECISION_WRITE_TIME != 0U) - rawData += PRECISION_WRITE_TIME; - - rawData /= PRECISION_WRITE_TIME; - rawData *= PRECISION_WRITE_TIME; - return toFiletime(rawData); -} - - -//get 7-bit value representing time shift in number of quarter-hours -std::bitset<UTC_LOCAL_OFFSET_BITS> getUtcLocalShift() -{ - FILETIME utcTime = FAT_MIN_TIME; - utcTime.dwHighDateTime += 5000000; //some arbitrary valid time - - const FILETIME localTime = utcToLocal(utcTime); - - const int timeShiftSec = to<int>((toInt64(localTime) - toInt64(utcTime)) / 10000000); //time shift in seconds - - const int timeShiftQuarter = timeShiftSec / (60 * 15); //time shift in quarter-hours - - const int absValue = common::abs(timeShiftQuarter); //MSVC C++0x bug: std::bitset<>(unsigned long) is ambiguous - - if (std::bitset < UTC_LOCAL_OFFSET_BITS - 1 > (absValue).to_ulong() != static_cast<unsigned long>(absValue) || //time shifts that big shouldn't be possible! - timeShiftSec % (60 * 15) != 0) //all known time shift have at least 15 minute granularity! - { - const std::wstring errorMessage = _("Conversion error:") + " Unexpected UTC <-> local time shift: " + - "(" + toString<std::wstring>(timeShiftSec) + ") " + "\n\n" + getLastErrorFormatted(); - throw std::runtime_error(wideToUtf8<std::string>(errorMessage)); - } - - std::bitset<UTC_LOCAL_OFFSET_BITS> output(absValue); - output[UTC_LOCAL_OFFSET_BITS - 1] = timeShiftQuarter < 0; //sign bit - return output; -} - - -//get time-zone shift in seconds -inline -int convertUtcLocalShift(std::bitset<UTC_LOCAL_OFFSET_BITS> rawShift) -{ - const bool hasSign = rawShift[UTC_LOCAL_OFFSET_BITS - 1]; - rawShift[UTC_LOCAL_OFFSET_BITS - 1] = false; - - assert_static(UTC_LOCAL_OFFSET_BITS <= sizeof(unsigned long) * 8); - return hasSign ? - static_cast<int>(rawShift.to_ulong()) * 15 * 60 * -1 : - static_cast<int>(rawShift.to_ulong()) * 15 * 60; -} -} - - -bool dst::fatHasUtcEncoded(const RawTime& rawTime) //"createTimeRaw" as retrieved by ::FindFirstFile() and ::GetFileAttributesEx(); throw (std::runtime_error) -{ - if (toUInt64(rawTime.createTimeRaw) < toUInt64(FAT_MIN_TIME) || - toUInt64(FAT_MAX_TIME) < toUInt64(rawTime.createTimeRaw)) - { - assert(false); //shouldn't be possible according to FAT specification - return false; - } - - const UInt64 rawInfo = extractRawInformation<PRECISION_CREATE_TIME>(utcToLocal(rawTime.createTimeRaw)); - - assert_static(WRITE_TIME_HASH_BITS == 30); - return (extractRawWriteTime(utcToLocal(rawTime.writeTimeRaw)) & 0x3FFFFFFFU) == (rawInfo & 0x3FFFFFFFU) && //ensure write time wasn't changed externally - rawInfo >> (CREATE_TIME_INFO_BITS - INDICATOR_EXISTING_BITS) == 1U; //extended data available -} - - -dst::RawTime dst::fatEncodeUtcTime(const FILETIME& writeTimeRealUtc) //throw (std::runtime_error) -{ - const FILETIME fatWriteTimeUtc = roundToFatWriteTime(writeTimeRealUtc); //writeTimeRealUtc may have incompatible precision (NTFS) - - //create time lets us store 40 bit of information - - //indicator that utc time is encoded -> hopefully results in a date long way in the future; but even if this bit is accidentally set, we still have the hash! - UInt64 data = 1U; - - const std::bitset<UTC_LOCAL_OFFSET_BITS> utcShift = getUtcLocalShift(); - data <<= UTC_LOCAL_OFFSET_BITS; - data |= utcShift.to_ulong(); - - data <<= WRITE_TIME_HASH_BITS; - data |= extractRawWriteTime(utcToLocal(fatWriteTimeUtc)) & 0x3FFFFFFFU; //trim to last 30 bit of information - assert_static(WRITE_TIME_HASH_BITS == 30); - - const FILETIME encodedData = localToUtc(encodeRawInformation<PRECISION_CREATE_TIME>(data)); //localToUtc: make sure data is physically saved as FAT local time - assert(toUInt64(FAT_MIN_TIME) <= toUInt64(encodedData)); - assert(toUInt64(encodedData) <= toUInt64(FAT_MAX_TIME)); - - return RawTime(encodedData, fatWriteTimeUtc); //keep compatible with other applications, at least until DST shift actually occurs -} - - -FILETIME dst::fatDecodeUtcTime(const RawTime& rawTime) //return real UTC time; throw (std::runtime_error) -{ - if (!fatHasUtcEncoded(rawTime)) - return rawTime.writeTimeRaw; - - const UInt64 rawInfo = extractRawInformation<PRECISION_CREATE_TIME>(utcToLocal(rawTime.createTimeRaw)); - - const std::bitset<UTC_LOCAL_OFFSET_BITS> rawShift(to<int>((rawInfo >> WRITE_TIME_HASH_BITS) & 0x7FU)); //static_cast<int>: a shame MSC... "unsigned long" should be supported instead! - assert_static(UTC_LOCAL_OFFSET_BITS == 7); - - const int timeShiftSec = convertUtcLocalShift(rawShift); - const FILETIME writeTimeLocal = utcToLocal(rawTime.writeTimeRaw); - - const Int64 realUTC = toInt64(writeTimeLocal) - Int64(timeShiftSec) * 10000000; - return toFiletime(realUTC); -} |