1// Windows/FileIO.cpp 2 3#include "StdAfx.h" 4 5#include "FileIO.h" 6 7#if defined(WIN_LONG_PATH) || defined(SUPPORT_DEVICE_FILE) 8#include "../Common/MyString.h" 9#endif 10#ifndef _UNICODE 11#include "../Common/StringConvert.h" 12#endif 13 14#ifndef _UNICODE 15extern bool g_IsNT; 16#endif 17 18namespace NWindows { 19namespace NFile { 20 21#ifdef SUPPORT_DEVICE_FILE 22bool IsDeviceName(LPCTSTR n) 23{ 24 #ifdef UNDER_CE 25 int len = (int)MyStringLen(n); 26 if (len < 5 || len > 5 || memcmp(n, TEXT("DSK"), 3 * sizeof(TCHAR)) != 0) 27 return false; 28 if (n[4] != ':') 29 return false; 30 // for reading use SG_REQ sg; if (DeviceIoControl(dsk, IOCTL_DISK_READ)); 31 #else 32 if (n[0] != '\\' || n[1] != '\\' || n[2] != '.' || n[3] != '\\') 33 return false; 34 int len = (int)MyStringLen(n); 35 if (len == 6 && n[5] == ':') 36 return true; 37 if (len < 18 || len > 22 || memcmp(n + 4, TEXT("PhysicalDrive"), 13 * sizeof(TCHAR)) != 0) 38 return false; 39 for (int i = 17; i < len; i++) 40 if (n[i] < '0' || n[i] > '9') 41 return false; 42 #endif 43 return true; 44} 45 46#ifndef _UNICODE 47bool IsDeviceName(LPCWSTR n) 48{ 49 if (n[0] != '\\' || n[1] != '\\' || n[2] != '.' || n[3] != '\\') 50 return false; 51 int len = (int)wcslen(n); 52 if (len == 6 && n[5] == ':') 53 return true; 54 if (len < 18 || len > 22 || wcsncmp(n + 4, L"PhysicalDrive", 13) != 0) 55 return false; 56 for (int i = 17; i < len; i++) 57 if (n[i] < '0' || n[i] > '9') 58 return false; 59 return true; 60} 61#endif 62#endif 63 64#if defined(WIN_LONG_PATH) && defined(_UNICODE) 65#define WIN_LONG_PATH2 66#endif 67 68#ifdef WIN_LONG_PATH 69bool GetLongPathBase(LPCWSTR s, UString &res) 70{ 71 res.Empty(); 72 int len = MyStringLen(s); 73 wchar_t c = s[0]; 74 if (len < 1 || c == L'\\' || c == L'.' && (len == 1 || len == 2 && s[1] == L'.')) 75 return true; 76 UString curDir; 77 bool isAbs = false; 78 if (len > 3) 79 isAbs = (s[1] == L':' && s[2] == L'\\' && (c >= L'a' && c <= L'z' || c >= L'A' && c <= L'Z')); 80 81 if (!isAbs) 82 { 83 DWORD needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, curDir.GetBuffer(MAX_PATH + 1)); 84 curDir.ReleaseBuffer(); 85 if (needLength == 0 || needLength > MAX_PATH) 86 return false; 87 if (curDir[curDir.Length() - 1] != L'\\') 88 curDir += L'\\'; 89 } 90 res = UString(L"\\\\?\\") + curDir + s; 91 return true; 92} 93 94bool GetLongPath(LPCWSTR path, UString &longPath) 95{ 96 if (GetLongPathBase(path, longPath)) 97 return !longPath.IsEmpty(); 98 return false; 99} 100#endif 101 102namespace NIO { 103 104CFileBase::~CFileBase() { Close(); } 105 106bool CFileBase::Create(LPCTSTR fileName, DWORD desiredAccess, 107 DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) 108{ 109 if (!Close()) 110 return false; 111 _handle = ::CreateFile(fileName, desiredAccess, shareMode, 112 (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, 113 flagsAndAttributes, (HANDLE)NULL); 114 #ifdef WIN_LONG_PATH2 115 if (_handle == INVALID_HANDLE_VALUE) 116 { 117 UString longPath; 118 if (GetLongPath(fileName, longPath)) 119 _handle = ::CreateFileW(longPath, desiredAccess, shareMode, 120 (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, 121 flagsAndAttributes, (HANDLE)NULL); 122 } 123 #endif 124 #ifdef SUPPORT_DEVICE_FILE 125 IsDeviceFile = false; 126 #endif 127 return (_handle != INVALID_HANDLE_VALUE); 128} 129 130#ifndef _UNICODE 131bool CFileBase::Create(LPCWSTR fileName, DWORD desiredAccess, 132 DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) 133{ 134 if (!g_IsNT) 135 return Create(UnicodeStringToMultiByte(fileName, ::AreFileApisANSI() ? CP_ACP : CP_OEMCP), 136 desiredAccess, shareMode, creationDisposition, flagsAndAttributes); 137 if (!Close()) 138 return false; 139 _handle = ::CreateFileW(fileName, desiredAccess, shareMode, 140 (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, 141 flagsAndAttributes, (HANDLE)NULL); 142 #ifdef WIN_LONG_PATH 143 if (_handle == INVALID_HANDLE_VALUE) 144 { 145 UString longPath; 146 if (GetLongPath(fileName, longPath)) 147 _handle = ::CreateFileW(longPath, desiredAccess, shareMode, 148 (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, 149 flagsAndAttributes, (HANDLE)NULL); 150 } 151 #endif 152 #ifdef SUPPORT_DEVICE_FILE 153 IsDeviceFile = false; 154 #endif 155 return (_handle != INVALID_HANDLE_VALUE); 156} 157#endif 158 159bool CFileBase::Close() 160{ 161 if (_handle == INVALID_HANDLE_VALUE) 162 return true; 163 if (!::CloseHandle(_handle)) 164 return false; 165 _handle = INVALID_HANDLE_VALUE; 166 return true; 167} 168 169bool CFileBase::GetPosition(UInt64 &position) const 170{ 171 return Seek(0, FILE_CURRENT, position); 172} 173 174bool CFileBase::GetLength(UInt64 &length) const 175{ 176 #ifdef SUPPORT_DEVICE_FILE 177 if (IsDeviceFile && LengthDefined) 178 { 179 length = Length; 180 return true; 181 } 182 #endif 183 184 DWORD sizeHigh; 185 DWORD sizeLow = ::GetFileSize(_handle, &sizeHigh); 186 if (sizeLow == 0xFFFFFFFF) 187 if (::GetLastError() != NO_ERROR) 188 return false; 189 length = (((UInt64)sizeHigh) << 32) + sizeLow; 190 return true; 191} 192 193bool CFileBase::Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const 194{ 195 #ifdef SUPPORT_DEVICE_FILE 196 if (IsDeviceFile && LengthDefined && moveMethod == FILE_END) 197 { 198 distanceToMove += Length; 199 moveMethod = FILE_BEGIN; 200 } 201 #endif 202 203 LARGE_INTEGER value; 204 value.QuadPart = distanceToMove; 205 value.LowPart = ::SetFilePointer(_handle, value.LowPart, &value.HighPart, moveMethod); 206 if (value.LowPart == 0xFFFFFFFF) 207 if (::GetLastError() != NO_ERROR) 208 return false; 209 newPosition = value.QuadPart; 210 return true; 211} 212 213bool CFileBase::Seek(UInt64 position, UInt64 &newPosition) 214{ 215 return Seek(position, FILE_BEGIN, newPosition); 216} 217 218bool CFileBase::SeekToBegin() 219{ 220 UInt64 newPosition; 221 return Seek(0, newPosition); 222} 223 224bool CFileBase::SeekToEnd(UInt64 &newPosition) 225{ 226 return Seek(0, FILE_END, newPosition); 227} 228 229bool CFileBase::GetFileInformation(CByHandleFileInfo &fileInfo) const 230{ 231 BY_HANDLE_FILE_INFORMATION winFileInfo; 232 if (!::GetFileInformationByHandle(_handle, &winFileInfo)) 233 return false; 234 fileInfo.Attrib = winFileInfo.dwFileAttributes; 235 fileInfo.CTime = winFileInfo.ftCreationTime; 236 fileInfo.ATime = winFileInfo.ftLastAccessTime; 237 fileInfo.MTime = winFileInfo.ftLastWriteTime; 238 fileInfo.VolumeSerialNumber = winFileInfo.dwFileAttributes; 239 fileInfo.Size = (((UInt64)winFileInfo.nFileSizeHigh) << 32) + winFileInfo.nFileSizeLow; 240 fileInfo.NumberOfLinks = winFileInfo.nNumberOfLinks; 241 fileInfo.FileIndex = (((UInt64)winFileInfo.nFileIndexHigh) << 32) + winFileInfo.nFileIndexLow; 242 return true; 243} 244 245///////////////////////// 246// CInFile 247 248#ifdef SUPPORT_DEVICE_FILE 249void CInFile::GetDeviceLength() 250{ 251 if (_handle != INVALID_HANDLE_VALUE && IsDeviceFile) 252 { 253 #ifdef UNDER_CE 254 LengthDefined = true; 255 Length = 128 << 20; 256 257 #else 258 PARTITION_INFORMATION partInfo; 259 LengthDefined = true; 260 Length = 0; 261 262 if (GetPartitionInfo(&partInfo)) 263 Length = partInfo.PartitionLength.QuadPart; 264 else 265 { 266 DISK_GEOMETRY geom; 267 if (!GetGeometry(&geom)) 268 if (!GetCdRomGeometry(&geom)) 269 LengthDefined = false; 270 if (LengthDefined) 271 Length = geom.Cylinders.QuadPart * geom.TracksPerCylinder * geom.SectorsPerTrack * geom.BytesPerSector; 272 } 273 // SeekToBegin(); 274 #endif 275 } 276} 277 278// ((desiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)) == 0 && 279 280#define MY_DEVICE_EXTRA_CODE \ 281 IsDeviceFile = IsDeviceName(fileName); \ 282 GetDeviceLength(); 283#else 284#define MY_DEVICE_EXTRA_CODE 285#endif 286 287bool CInFile::Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) 288{ 289 bool res = Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes); 290 MY_DEVICE_EXTRA_CODE 291 return res; 292} 293 294bool CInFile::OpenShared(LPCTSTR fileName, bool shareForWrite) 295{ return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); } 296 297bool CInFile::Open(LPCTSTR fileName) 298 { return OpenShared(fileName, false); } 299 300#ifndef _UNICODE 301bool CInFile::Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) 302{ 303 bool res = Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes); 304 MY_DEVICE_EXTRA_CODE 305 return res; 306} 307 308bool CInFile::OpenShared(LPCWSTR fileName, bool shareForWrite) 309{ return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); } 310 311bool CInFile::Open(LPCWSTR fileName) 312 { return OpenShared(fileName, false); } 313#endif 314 315// ReadFile and WriteFile functions in Windows have BUG: 316// If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1) 317// from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES 318// (Insufficient system resources exist to complete the requested service). 319 320// Probably in some version of Windows there are problems with other sizes: 321// for 32 MB (maybe also for 16 MB). 322// And message can be "Network connection was lost" 323 324static UInt32 kChunkSizeMax = (1 << 22); 325 326bool CInFile::Read1(void *data, UInt32 size, UInt32 &processedSize) 327{ 328 DWORD processedLoc = 0; 329 bool res = BOOLToBool(::ReadFile(_handle, data, size, &processedLoc, NULL)); 330 processedSize = (UInt32)processedLoc; 331 return res; 332} 333 334bool CInFile::ReadPart(void *data, UInt32 size, UInt32 &processedSize) 335{ 336 if (size > kChunkSizeMax) 337 size = kChunkSizeMax; 338 return Read1(data, size, processedSize); 339} 340 341bool CInFile::Read(void *data, UInt32 size, UInt32 &processedSize) 342{ 343 processedSize = 0; 344 do 345 { 346 UInt32 processedLoc = 0; 347 bool res = ReadPart(data, size, processedLoc); 348 processedSize += processedLoc; 349 if (!res) 350 return false; 351 if (processedLoc == 0) 352 return true; 353 data = (void *)((unsigned char *)data + processedLoc); 354 size -= processedLoc; 355 } 356 while (size > 0); 357 return true; 358} 359 360///////////////////////// 361// COutFile 362 363bool COutFile::Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) 364 { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); } 365 366static inline DWORD GetCreationDisposition(bool createAlways) 367 { return createAlways? CREATE_ALWAYS: CREATE_NEW; } 368 369bool COutFile::Open(LPCTSTR fileName, DWORD creationDisposition) 370 { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); } 371 372bool COutFile::Create(LPCTSTR fileName, bool createAlways) 373 { return Open(fileName, GetCreationDisposition(createAlways)); } 374 375#ifndef _UNICODE 376 377bool COutFile::Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) 378 { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); } 379 380bool COutFile::Open(LPCWSTR fileName, DWORD creationDisposition) 381 { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); } 382 383bool COutFile::Create(LPCWSTR fileName, bool createAlways) 384 { return Open(fileName, GetCreationDisposition(createAlways)); } 385 386#endif 387 388bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) 389 { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); } 390 391bool COutFile::SetMTime(const FILETIME *mTime) { return SetTime(NULL, NULL, mTime); } 392 393bool COutFile::WritePart(const void *data, UInt32 size, UInt32 &processedSize) 394{ 395 if (size > kChunkSizeMax) 396 size = kChunkSizeMax; 397 DWORD processedLoc = 0; 398 bool res = BOOLToBool(::WriteFile(_handle, data, size, &processedLoc, NULL)); 399 processedSize = (UInt32)processedLoc; 400 return res; 401} 402 403bool COutFile::Write(const void *data, UInt32 size, UInt32 &processedSize) 404{ 405 processedSize = 0; 406 do 407 { 408 UInt32 processedLoc = 0; 409 bool res = WritePart(data, size, processedLoc); 410 processedSize += processedLoc; 411 if (!res) 412 return false; 413 if (processedLoc == 0) 414 return true; 415 data = (const void *)((const unsigned char *)data + processedLoc); 416 size -= processedLoc; 417 } 418 while (size > 0); 419 return true; 420} 421 422bool COutFile::SetEndOfFile() { return BOOLToBool(::SetEndOfFile(_handle)); } 423 424bool COutFile::SetLength(UInt64 length) 425{ 426 UInt64 newPosition; 427 if (!Seek(length, newPosition)) 428 return false; 429 if (newPosition != length) 430 return false; 431 return SetEndOfFile(); 432} 433 434}}} 435