//////////////////////////////////////////////////////////////////////////////// // This source file is part of the ZipArchive library source distribution and // is Copyrighted 2000 - 2011 by Artpol Software - Tadeusz Dracz // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // For the licensing details refer to the License.txt file. // // Web Site: http://www.artpol-software.com //////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #ifdef _ZIP_SYSTEM_WIN #define _ZIP_SAFE_WINDOWS_API #include "ZipPlatform.h" #include "ZipFileHeader.h" #include "ZipException.h" #include "ZipAutoBuffer.h" #include #ifndef __BORLANDC__ #include #else #ifndef _UTIMBUF_DEFINED #define _utimbuf utimbuf #define _UTIMBUF_DEFINED #endif #include #endif #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif #ifndef INVALID_SET_FILE_POINTER #define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif #include #include #include #include #include "ZipPathComponent.h" #include "ZipCompatibility.h" const TCHAR CZipPathComponent::m_cSeparator = _T('\\'); ULONGLONG ZipPlatform::GetDeviceFreeSpace(LPCTSTR lpszPath) { ULONGLONG uFreeBytesToCaller = 0, uTotalBytes = 0, uFreeBytes = 0; CZipPathComponent zpc (lpszPath); CZipString szDrive = zpc.GetFileDrive(); if (!GetDiskFreeSpaceEx( szDrive, (PULARGE_INTEGER)&uFreeBytesToCaller, (PULARGE_INTEGER)&uTotalBytes, (PULARGE_INTEGER)&uFreeBytes)) { CZipPathComponent::AppendSeparator(szDrive); // in spite of what is written in MSDN it is sometimes needed (on fixed disks) if (!GetDiskFreeSpaceEx( szDrive, (PULARGE_INTEGER)&uFreeBytesToCaller, (PULARGE_INTEGER)&uTotalBytes, (PULARGE_INTEGER)&uFreeBytes)) return 0; } return uFreeBytes; } CZipString ZipPlatform::GetTmpFileName(LPCTSTR lpszPath, ZIP_SIZE_TYPE uSizeNeeded) { TCHAR empty[] = _T(""); CZipString tempPath; bool bCheckTemp = true; if (lpszPath) { tempPath = lpszPath; bCheckTemp = uSizeNeeded > 0 && GetDeviceFreeSpace(tempPath) < uSizeNeeded; } if (bCheckTemp) { DWORD size = GetTempPath(0, empty); if (size == 0) return (CZipString)empty; GetTempPath(size, tempPath.GetBuffer(size)); tempPath.ReleaseBuffer(); if (GetDeviceFreeSpace(tempPath) < uSizeNeeded) { if (!GetCurrentDirectory(tempPath) || (uSizeNeeded > 0 && GetDeviceFreeSpace(tempPath) < uSizeNeeded)) return (CZipString)empty; } } CZipString tempName; if (!GetTempFileName(tempPath, _T("ZAR"), 0, tempName.GetBuffer(_MAX_PATH))) return (CZipString)empty; tempName.ReleaseBuffer(); return tempName; } bool ZipPlatform::GetCurrentDirectory(CZipString& sz) { DWORD i = ::GetCurrentDirectory(0, NULL); if (!i) return false; TCHAR* pBuf = new TCHAR[i]; bool b = true; if (!::GetCurrentDirectory(i, pBuf)) b = false; else sz = pBuf; delete[] pBuf; return b; } bool ZipPlatform::SetFileAttr(LPCTSTR lpFileName, DWORD uAttr) { return ::SetFileAttributes(CZipPathComponent::AddPrefix(lpFileName), uAttr) != 0; } bool ZipPlatform::GetFileAttr(LPCTSTR lpFileName, DWORD& uAttr) { DWORD temp = ::GetFileAttributes(CZipPathComponent::AddPrefix(lpFileName)); if (temp == INVALID_FILE_ATTRIBUTES) return false; uAttr = temp; return true; } bool ZipPlatform::GetFileModTime(LPCTSTR lpFileName, time_t & ttime) { WIN32_FIND_DATA findData = {0}; HANDLE handle = ::FindFirstFile(CZipPathComponent::AddPrefix(lpFileName), &findData); if (handle == INVALID_HANDLE_VALUE) { ttime = time(NULL); return false; } bool ret = false; if ( findData.ftLastWriteTime.dwLowDateTime || findData.ftLastWriteTime.dwHighDateTime ) { LONGLONG val = (findData.ftLastWriteTime.dwLowDateTime & 0xFFFFFFFF); val |= ((LONGLONG)findData.ftLastWriteTime.dwHighDateTime << 32) & SUFFIX_I64(0xFFFFFFFF00000000); val -= SUFFIX_I64(116444736000000000); ttime = (time_t)(val / 10000000); ret = ttime >= 0; } FindClose(handle); if (ret == false) ttime = time(NULL); return ret; } bool ZipPlatform::SetFileModTime(LPCTSTR lpFileName, time_t ttime) { HANDLE handle = ::CreateFile(CZipPathComponent::AddPrefix(lpFileName), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); if (handle == INVALID_HANDLE_VALUE) { return false; } bool ret = ZipPlatform::SetFileModTime(handle, ttime); CloseHandle(handle); return ret; } bool ZipPlatform::SetFileModTime(HANDLE handle, time_t ttime) { FILETIME ft; LONGLONG val = ((LONGLONG)ttime * 10000000) + SUFFIX_I64(116444736000000000); ft.dwLowDateTime = (DWORD)(val & 0xFFFFFFFF); ft.dwHighDateTime = (DWORD)((val >> 32) & 0xFFFFFFFF); return ::SetFileTime(handle, NULL, NULL, &ft) != 0; } bool ZipPlatform::ChangeDirectory(LPCTSTR lpDirectory) { return _tchdir(lpDirectory) == 0; // returns 0 if ok } int ZipPlatform::FileExists(LPCTSTR lpszName) { DWORD attributes = ::GetFileAttributes(CZipPathComponent::AddPrefix(lpszName)); if (attributes == INVALID_FILE_ATTRIBUTES) return 0; return ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) ? -1 : 1; } ZIPINLINE bool ZipPlatform::IsDriveRemovable(LPCTSTR lpszFilePath) { CZipPathComponent zpc(lpszFilePath); return ::GetDriveType(zpc.GetFileDrive()) == DRIVE_REMOVABLE; } ZIPINLINE bool ZipPlatform::SetVolLabel(LPCTSTR lpszPath, LPCTSTR lpszLabel) { CZipPathComponent zpc(lpszPath); CZipString szDrive = zpc.GetFileDrive(); CZipPathComponent::AppendSeparator(szDrive); return ::SetVolumeLabel(szDrive, lpszLabel) != 0; } ZIPINLINE void ZipPlatform::AnsiOem(CZipAutoBuffer& buffer, bool bAnsiToOem) { #ifdef _ZIP_SAFE_WINDOWS_API UINT cpIn, cpOut; if (bAnsiToOem) { cpIn = CP_ACP; cpOut = CP_OEMCP; } else { cpIn = CP_OEMCP; cpOut = CP_ACP; } CZipAutoBuffer interBuffer; int size = buffer.GetSize(); // iLen doesn't include terminating character int iLen = MultiByteToWideChar(cpIn, MB_PRECOMPOSED, buffer, size, NULL, 0); if (iLen <= 0) return; interBuffer.Allocate(iLen * sizeof(wchar_t)); LPWSTR lpszWide = (LPWSTR)(char*)interBuffer; iLen = MultiByteToWideChar(cpIn, MB_PRECOMPOSED, buffer, size, lpszWide, iLen); ASSERT(iLen != 0); // iLen does not include terminating character size = WideCharToMultiByte(cpOut, 0, lpszWide, iLen, NULL, 0, NULL, NULL); if (size <= 0) return; buffer.Allocate(size); size = WideCharToMultiByte(cpOut, 0, lpszWide, iLen, buffer, size, NULL, NULL); ASSERT(size != 0); #else if (bAnsiToOem) CharToOemBuffA(buffer, buffer, buffer.GetSize()); else OemToCharBuffA(buffer, buffer, buffer.GetSize()); #endif } ZIPINLINE bool ZipPlatform::RemoveFile(LPCTSTR lpszFileName, bool bThrow, int iMode) { if ((iMode & ZipPlatform::dfmRemoveReadOnly) != 0) { DWORD attr; if (ZipPlatform::GetFileAttr(lpszFileName, attr) && (ZipCompatibility::GetAsInternalAttributes(attr, ZipPlatform::GetSystemID()) & ZipCompatibility::attROnly) != 0) { ZipPlatform::SetFileAttr(lpszFileName, ZipPlatform::GetDefaultAttributes()); } } #ifdef SHFileOperation if ((iMode & ZipPlatform::dfmRecycleBin) == 0) { #endif if (::DeleteFile((LPTSTR)(LPCTSTR)CZipPathComponent::AddPrefix(lpszFileName, false))) return true; #ifdef SHFileOperation } else { CZipString file = lpszFileName; #if defined _UNICODE && _MSC_VER >= 1400 if (file.GetLength() >= MAX_PATH) { // cannot prefix for SHFileOperation, use short path CZipString temp = CZipPathComponent::AddPrefix(lpszFileName, false); DWORD size = ::GetShortPathName(temp, NULL, 0); if (size != 0) { size = ::GetShortPathName(temp, file.GetBuffer(size), size); file.ReleaseBuffer(); } if (size == 0) { if (bThrow) CZipException::Throw(CZipException::notRemoved, lpszFileName); return false; } // GetShortPathName keeps the prefix - remove it int prefixLength = CZipPathComponent::IsPrefixed(file); if (prefixLength > 0) { file = file.Mid(prefixLength); } } #endif int length = file.GetLength(); CZipAutoBuffer buffer((length + 2) * sizeof(TCHAR), true); // double NULL is required memcpy(buffer, (LPCTSTR)file, length * sizeof(TCHAR)); SHFILEOPSTRUCT shFileOp = {0}; shFileOp.wFunc = FO_DELETE; shFileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI; shFileOp.pFrom = (LPCTSTR)(char*)buffer; if (SHFileOperation(&shFileOp) == 0 && !shFileOp.fAnyOperationsAborted) return true; } #endif if (bThrow) CZipException::Throw(CZipException::notRemoved, lpszFileName); return false; } ZIPINLINE bool ZipPlatform::RenameFile( LPCTSTR lpszOldName, LPCTSTR lpszNewName, bool bThrow) { if (!::MoveFile((LPTSTR)(LPCTSTR)CZipPathComponent::AddPrefix(lpszOldName, false), (LPTSTR)(LPCTSTR)CZipPathComponent::AddPrefix(lpszNewName, false))) if (bThrow) CZipException::Throw(CZipException::notRenamed, lpszOldName); else return false; return true; } ZIPINLINE bool ZipPlatform::IsDirectory(DWORD uAttr) { return (uAttr & FILE_ATTRIBUTE_DIRECTORY) != 0; } ZIPINLINE bool ZipPlatform::CreateNewDirectory(LPCTSTR lpDirectory) { return ::CreateDirectory(CZipPathComponent::AddPrefix(lpDirectory), NULL) != 0; } ZIPINLINE DWORD ZipPlatform::GetDefaultAttributes() { return FILE_ATTRIBUTE_ARCHIVE; } ZIPINLINE DWORD ZipPlatform::GetDefaultDirAttributes() { return FILE_ATTRIBUTE_DIRECTORY; } ZIPINLINE int ZipPlatform::GetSystemID() { return ZipCompatibility::zcDosFat; } ZIPINLINE bool ZipPlatform::GetSystemCaseSensitivity() { return false; } #ifdef _UNICODE #ifdef _ZIP_UNICODE_NORMALIZE #pragma comment(lib, "Normaliz.lib") #endif int ZipPlatform::WideToMultiByte(LPCWSTR lpszIn, CZipAutoBuffer &szOut, UINT uCodePage) { size_t wideLen = wcslen(lpszIn); if (wideLen == 0) { szOut.Release(); return 0; } char pDefalutChar[1]; memset(pDefalutChar, 0, 1); pDefalutChar[0]=0x20; // iLen does not include terminating character int iLen = WideCharToMultiByte(uCodePage, 0, lpszIn, (int)wideLen, szOut, 0, NULL, NULL); if (iLen > 0) { szOut.Allocate(iLen + 1, true); iLen = WideCharToMultiByte(uCodePage, 0, lpszIn , (int)wideLen + 1, szOut, iLen +1, pDefalutChar, NULL); ASSERT(iLen > 0); return iLen > 0 ? iLen : -1; } else // here it means error { szOut.Release(); return -1; } } int ZipPlatform::MultiByteToWide(const CZipAutoBuffer &szIn, CZipString& szOut, UINT uCodePage) { int singleLen = szIn.GetSize(); if (singleLen == 0) { szOut.Empty(); return 0; } // iLen doesn't include terminating character DWORD dwFlags = uCodePage <= CP_OEMCP ? MB_PRECOMPOSED : 0; int iLen = MultiByteToWideChar(uCodePage, dwFlags, szIn.GetBuffer(), singleLen, NULL, 0); if (iLen > 0) { iLen = MultiByteToWideChar(uCodePage, dwFlags, szIn.GetBuffer(), singleLen, szOut.GetBuffer(iLen) , iLen); szOut.ReleaseBuffer(iLen); ASSERT(iLen > 0); if (iLen == 0) return -1; #ifdef _ZIP_UNICODE_NORMALIZE // if there is a problem with compilation here, you may need uncomment the block defining WINVER = 0x0600 at the bottom of _features.h file if (IsNormalizedString(NormalizationC, szOut, iLen + 1) == TRUE) return iLen; int iNewLen = NormalizeString(NormalizationC, szOut, iLen, NULL, 0); if (iNewLen <= 0) { return iLen; } CZipString szNormalized; iNewLen = NormalizeString(NormalizationC, szOut, iLen, szNormalized.GetBuffer(iNewLen), iNewLen); if (iNewLen <= 0) { szNormalized.ReleaseBuffer(0); return iLen; } szNormalized.ReleaseBuffer(iNewLen); szOut = szNormalized; return iNewLen; #else return iLen; #endif } else { szOut.Empty(); return -1; } } #endif #if defined _ZIP_IMPL_STL || _ZIP_FILE_IMPLEMENTATION == ZIP_ZFI_STL #if _MSC_VER > 1000 #pragma warning( push ) #pragma warning (disable : 4702) // unreachable code #endif bool ZipPlatform::TruncateFile(int iDes, ULONGLONG uSize) { #if (_MSC_VER >= 1400) return _chsize_s(iDes, uSize) == 0; #else if (uSize > _I64_MAX) CZipException::Throw(CZipException::tooBigSize); else { HANDLE handle = (HANDLE)GetFileSystemHandle(iDes); ULARGE_INTEGER li; li.QuadPart = uSize; li.LowPart = SetFilePointer(handle, li.LowPart, (LONG*)&li.HighPart, FILE_BEGIN); if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) return false; return SetEndOfFile(handle) == TRUE; } return false; // for the compiler #endif } #if _MSC_VER > 1000 #pragma warning( pop ) #endif int ZipPlatform::OpenFile(LPCTSTR lpszFileName, UINT iMode, int iShareMode) { switch (iShareMode) { case (CZipFile::shareExclusive): iShareMode = SH_DENYRW; break; case (CZipFile::shareDenyRead): iShareMode = SH_DENYRD; break; case (CZipFile::shareDenyWrite): iShareMode = SH_DENYWR; break; default: iShareMode = SH_DENYNO; } #if _MSC_VER >= 1400 int handle; if (_tsopen_s(&handle, lpszFileName, iMode, iShareMode, S_IREAD | S_IWRITE /*required only when O_CREAT mode*/) != 0) return -1; else return handle; #else return _tsopen(lpszFileName, iMode, iShareMode, S_IREAD | S_IWRITE /*required only when O_CREAT mode*/); #endif } bool ZipPlatform::FlushFile(int iDes) { return _commit(iDes) == 0; } intptr_t ZipPlatform::GetFileSystemHandle(int iDes) { return _get_osfhandle(iDes); } #endif // _ZIP_IMPL_STL #endif // _ZIP_SYSTEM_WIN