rlm@1: // Windows/FileIO.cpp rlm@1: rlm@1: #include "StdAfx.h" rlm@1: rlm@1: #include "FileIO.h" rlm@1: #include "Defs.h" rlm@1: #ifdef WIN_LONG_PATH rlm@1: #include "../Common/MyString.h" rlm@1: #endif rlm@1: #ifndef _UNICODE rlm@1: #include "../Common/StringConvert.h" rlm@1: #endif rlm@1: rlm@1: #ifndef _UNICODE rlm@1: extern bool g_IsNT; rlm@1: #endif rlm@1: rlm@1: namespace NWindows { rlm@1: namespace NFile { rlm@1: rlm@1: #if defined(WIN_LONG_PATH) && defined(_UNICODE) rlm@1: #define WIN_LONG_PATH2 rlm@1: #endif rlm@1: rlm@1: #ifdef WIN_LONG_PATH rlm@1: bool GetLongPathBase(LPCWSTR s, UString &res) rlm@1: { rlm@1: res.Empty(); rlm@1: int len = MyStringLen(s); rlm@1: wchar_t c = s[0]; rlm@1: if (len < 1 || c == L'\\' || c == L'.' && (len == 1 || len == 2 && s[1] == L'.')) rlm@1: return true; rlm@1: UString curDir; rlm@1: bool isAbs = false; rlm@1: if (len > 3) rlm@1: isAbs = (s[1] == L':' && s[2] == L'\\' && (c >= L'a' && c <= L'z' || c >= L'A' && c <= L'Z')); rlm@1: rlm@1: if (!isAbs) rlm@1: { rlm@1: DWORD needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, curDir.GetBuffer(MAX_PATH + 1)); rlm@1: curDir.ReleaseBuffer(); rlm@1: if (needLength == 0 || needLength > MAX_PATH) rlm@1: return false; rlm@1: if (curDir[curDir.Length() - 1] != L'\\') rlm@1: curDir += L'\\'; rlm@1: } rlm@1: res = UString(L"\\\\?\\") + curDir + s; rlm@1: return true; rlm@1: } rlm@1: rlm@1: bool GetLongPath(LPCWSTR path, UString &longPath) rlm@1: { rlm@1: if (GetLongPathBase(path, longPath)) rlm@1: return !longPath.IsEmpty(); rlm@1: return false; rlm@1: } rlm@1: #endif rlm@1: rlm@1: namespace NIO { rlm@1: rlm@1: CFileBase::~CFileBase() { Close(); } rlm@1: rlm@1: bool CFileBase::Create(LPCTSTR fileName, DWORD desiredAccess, rlm@1: DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) rlm@1: { rlm@1: if (!Close()) rlm@1: return false; rlm@1: _handle = ::CreateFile(fileName, desiredAccess, shareMode, rlm@1: (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, rlm@1: flagsAndAttributes, (HANDLE)NULL); rlm@1: #ifdef WIN_LONG_PATH2 rlm@1: if (_handle == INVALID_HANDLE_VALUE) rlm@1: { rlm@1: UString longPath; rlm@1: if (GetLongPath(fileName, longPath)) rlm@1: _handle = ::CreateFileW(longPath, desiredAccess, shareMode, rlm@1: (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, rlm@1: flagsAndAttributes, (HANDLE)NULL); rlm@1: } rlm@1: #endif rlm@1: return (_handle != INVALID_HANDLE_VALUE); rlm@1: } rlm@1: rlm@1: #ifndef _UNICODE rlm@1: bool CFileBase::Create(LPCWSTR fileName, DWORD desiredAccess, rlm@1: DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) rlm@1: { rlm@1: if (!g_IsNT) rlm@1: return Create(UnicodeStringToMultiByte(fileName, ::AreFileApisANSI() ? CP_ACP : CP_OEMCP), rlm@1: desiredAccess, shareMode, creationDisposition, flagsAndAttributes); rlm@1: if (!Close()) rlm@1: return false; rlm@1: _handle = ::CreateFileW(fileName, desiredAccess, shareMode, rlm@1: (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, rlm@1: flagsAndAttributes, (HANDLE)NULL); rlm@1: #ifdef WIN_LONG_PATH rlm@1: if (_handle == INVALID_HANDLE_VALUE) rlm@1: { rlm@1: UString longPath; rlm@1: if (GetLongPath(fileName, longPath)) rlm@1: _handle = ::CreateFileW(longPath, desiredAccess, shareMode, rlm@1: (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, rlm@1: flagsAndAttributes, (HANDLE)NULL); rlm@1: } rlm@1: #endif rlm@1: return (_handle != INVALID_HANDLE_VALUE); rlm@1: } rlm@1: #endif rlm@1: rlm@1: bool CFileBase::Close() rlm@1: { rlm@1: if (_handle == INVALID_HANDLE_VALUE) rlm@1: return true; rlm@1: if (!::CloseHandle(_handle)) rlm@1: return false; rlm@1: _handle = INVALID_HANDLE_VALUE; rlm@1: return true; rlm@1: } rlm@1: rlm@1: bool CFileBase::GetPosition(UInt64 &position) const rlm@1: { rlm@1: return Seek(0, FILE_CURRENT, position); rlm@1: } rlm@1: rlm@1: bool CFileBase::GetLength(UInt64 &length) const rlm@1: { rlm@1: DWORD sizeHigh; rlm@1: DWORD sizeLow = ::GetFileSize(_handle, &sizeHigh); rlm@1: if (sizeLow == 0xFFFFFFFF) rlm@1: if (::GetLastError() != NO_ERROR) rlm@1: return false; rlm@1: length = (((UInt64)sizeHigh) << 32) + sizeLow; rlm@1: return true; rlm@1: } rlm@1: rlm@1: bool CFileBase::Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const rlm@1: { rlm@1: LARGE_INTEGER value; rlm@1: value.QuadPart = distanceToMove; rlm@1: value.LowPart = ::SetFilePointer(_handle, value.LowPart, &value.HighPart, moveMethod); rlm@1: if (value.LowPart == 0xFFFFFFFF) rlm@1: if (::GetLastError() != NO_ERROR) rlm@1: return false; rlm@1: newPosition = value.QuadPart; rlm@1: return true; rlm@1: } rlm@1: rlm@1: bool CFileBase::Seek(UInt64 position, UInt64 &newPosition) rlm@1: { rlm@1: return Seek(position, FILE_BEGIN, newPosition); rlm@1: } rlm@1: rlm@1: bool CFileBase::SeekToBegin() rlm@1: { rlm@1: UInt64 newPosition; rlm@1: return Seek(0, newPosition); rlm@1: } rlm@1: rlm@1: bool CFileBase::SeekToEnd(UInt64 &newPosition) rlm@1: { rlm@1: return Seek(0, FILE_END, newPosition); rlm@1: } rlm@1: rlm@1: bool CFileBase::GetFileInformation(CByHandleFileInfo &fileInfo) const rlm@1: { rlm@1: BY_HANDLE_FILE_INFORMATION winFileInfo; rlm@1: if (!::GetFileInformationByHandle(_handle, &winFileInfo)) rlm@1: return false; rlm@1: fileInfo.Attributes = winFileInfo.dwFileAttributes; rlm@1: fileInfo.CTime = winFileInfo.ftCreationTime; rlm@1: fileInfo.ATime = winFileInfo.ftLastAccessTime; rlm@1: fileInfo.MTime = winFileInfo.ftLastWriteTime; rlm@1: fileInfo.VolumeSerialNumber = winFileInfo.dwFileAttributes; rlm@1: fileInfo.Size = (((UInt64)winFileInfo.nFileSizeHigh) << 32) + winFileInfo.nFileSizeLow; rlm@1: fileInfo.NumberOfLinks = winFileInfo.nNumberOfLinks; rlm@1: fileInfo.FileIndex = (((UInt64)winFileInfo.nFileIndexHigh) << 32) + winFileInfo.nFileIndexLow; rlm@1: return true; rlm@1: } rlm@1: rlm@1: ///////////////////////// rlm@1: // CInFile rlm@1: rlm@1: bool CInFile::Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) rlm@1: { return Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes); } rlm@1: rlm@1: bool CInFile::OpenShared(LPCTSTR fileName, bool shareForWrite) rlm@1: { return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); } rlm@1: rlm@1: bool CInFile::Open(LPCTSTR fileName) rlm@1: { return OpenShared(fileName, false); } rlm@1: rlm@1: #ifndef _UNICODE rlm@1: bool CInFile::Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) rlm@1: { return Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes); } rlm@1: rlm@1: bool CInFile::OpenShared(LPCWSTR fileName, bool shareForWrite) rlm@1: { return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); } rlm@1: rlm@1: bool CInFile::Open(LPCWSTR fileName) rlm@1: { return OpenShared(fileName, false); } rlm@1: #endif rlm@1: rlm@1: // ReadFile and WriteFile functions in Windows have BUG: rlm@1: // If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1) rlm@1: // from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES rlm@1: // (Insufficient system resources exist to complete the requested service). rlm@1: rlm@1: // Probably in some version of Windows there are problems with other sizes: rlm@1: // for 32 MB (maybe also for 16 MB). rlm@1: // And message can be "Network connection was lost" rlm@1: rlm@1: static UInt32 kChunkSizeMax = (1 << 22); rlm@1: rlm@1: bool CInFile::ReadPart(void *data, UInt32 size, UInt32 &processedSize) rlm@1: { rlm@1: if (size > kChunkSizeMax) rlm@1: size = kChunkSizeMax; rlm@1: DWORD processedLoc = 0; rlm@1: bool res = BOOLToBool(::ReadFile(_handle, data, size, &processedLoc, NULL)); rlm@1: processedSize = (UInt32)processedLoc; rlm@1: return res; rlm@1: } rlm@1: rlm@1: bool CInFile::Read(void *data, UInt32 size, UInt32 &processedSize) rlm@1: { rlm@1: processedSize = 0; rlm@1: do rlm@1: { rlm@1: UInt32 processedLoc = 0; rlm@1: bool res = ReadPart(data, size, processedLoc); rlm@1: processedSize += processedLoc; rlm@1: if (!res) rlm@1: return false; rlm@1: if (processedLoc == 0) rlm@1: return true; rlm@1: data = (void *)((unsigned char *)data + processedLoc); rlm@1: size -= processedLoc; rlm@1: } rlm@1: while (size > 0); rlm@1: return true; rlm@1: } rlm@1: rlm@1: ///////////////////////// rlm@1: // COutFile rlm@1: rlm@1: bool COutFile::Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) rlm@1: { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); } rlm@1: rlm@1: static inline DWORD GetCreationDisposition(bool createAlways) rlm@1: { return createAlways? CREATE_ALWAYS: CREATE_NEW; } rlm@1: rlm@1: bool COutFile::Open(LPCTSTR fileName, DWORD creationDisposition) rlm@1: { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); } rlm@1: rlm@1: bool COutFile::Create(LPCTSTR fileName, bool createAlways) rlm@1: { return Open(fileName, GetCreationDisposition(createAlways)); } rlm@1: rlm@1: #ifndef _UNICODE rlm@1: rlm@1: bool COutFile::Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) rlm@1: { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); } rlm@1: rlm@1: bool COutFile::Open(LPCWSTR fileName, DWORD creationDisposition) rlm@1: { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); } rlm@1: rlm@1: bool COutFile::Create(LPCWSTR fileName, bool createAlways) rlm@1: { return Open(fileName, GetCreationDisposition(createAlways)); } rlm@1: rlm@1: #endif rlm@1: rlm@1: bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) rlm@1: { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); } rlm@1: rlm@1: bool COutFile::SetMTime(const FILETIME *mTime) { return SetTime(NULL, NULL, mTime); } rlm@1: rlm@1: bool COutFile::WritePart(const void *data, UInt32 size, UInt32 &processedSize) rlm@1: { rlm@1: if (size > kChunkSizeMax) rlm@1: size = kChunkSizeMax; rlm@1: DWORD processedLoc = 0; rlm@1: bool res = BOOLToBool(::WriteFile(_handle, data, size, &processedLoc, NULL)); rlm@1: processedSize = (UInt32)processedLoc; rlm@1: return res; rlm@1: } rlm@1: rlm@1: bool COutFile::Write(const void *data, UInt32 size, UInt32 &processedSize) rlm@1: { rlm@1: processedSize = 0; rlm@1: do rlm@1: { rlm@1: UInt32 processedLoc = 0; rlm@1: bool res = WritePart(data, size, processedLoc); rlm@1: processedSize += processedLoc; rlm@1: if (!res) rlm@1: return false; rlm@1: if (processedLoc == 0) rlm@1: return true; rlm@1: data = (const void *)((const unsigned char *)data + processedLoc); rlm@1: size -= processedLoc; rlm@1: } rlm@1: while (size > 0); rlm@1: return true; rlm@1: } rlm@1: rlm@1: bool COutFile::SetEndOfFile() { return BOOLToBool(::SetEndOfFile(_handle)); } rlm@1: rlm@1: bool COutFile::SetLength(UInt64 length) rlm@1: { rlm@1: UInt64 newPosition; rlm@1: if (!Seek(length, newPosition)) rlm@1: return false; rlm@1: if (newPosition != length) rlm@1: return false; rlm@1: return SetEndOfFile(); rlm@1: } rlm@1: rlm@1: }}}