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 15 extern bool g_IsNT; 16 #endif 17 18 namespace NWindows { 19 namespace NFile { 20 21 #ifdef SUPPORT_DEVICE_FILE 22 bool 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 47 bool 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 69 bool 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 94 bool GetLongPath(LPCWSTR path, UString &longPath) 95 { 96 if (GetLongPathBase(path, longPath)) 97 return !longPath.IsEmpty(); 98 return false; 99 } 100 #endif 101 102 namespace NIO { 103 104 CFileBase::~CFileBase() { Close(); } 105 106 bool 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 131 bool 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 159 bool 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 169 bool CFileBase::GetPosition(UInt64 &position) const 170 { 171 return Seek(0, FILE_CURRENT, position); 172 } 173 174 bool 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 193 bool 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 213 bool CFileBase::Seek(UInt64 position, UInt64 &newPosition) 214 { 215 return Seek(position, FILE_BEGIN, newPosition); 216 } 217 218 bool CFileBase::SeekToBegin() 219 { 220 UInt64 newPosition; 221 return Seek(0, newPosition); 222 } 223 224 bool CFileBase::SeekToEnd(UInt64 &newPosition) 225 { 226 return Seek(0, FILE_END, newPosition); 227 } 228 229 bool 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 249 void 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 287 bool 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 294 bool CInFile::OpenShared(LPCTSTR fileName, bool shareForWrite) 295 { return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); } 296 297 bool CInFile::Open(LPCTSTR fileName) 298 { return OpenShared(fileName, false); } 299 300 #ifndef _UNICODE 301 bool 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 308 bool CInFile::OpenShared(LPCWSTR fileName, bool shareForWrite) 309 { return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); } 310 311 bool 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 324 static UInt32 kChunkSizeMax = (1 << 22); 325 326 bool 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 334 bool CInFile::ReadPart(void *data, UInt32 size, UInt32 &processedSize) 335 { 336 if (size > kChunkSizeMax) 337 size = kChunkSizeMax; 338 return Read1(data, size, processedSize); 339 } 340 341 bool 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 363 bool COutFile::Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) 364 { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); } 365 366 static inline DWORD GetCreationDisposition(bool createAlways) 367 { return createAlways? CREATE_ALWAYS: CREATE_NEW; } 368 369 bool COutFile::Open(LPCTSTR fileName, DWORD creationDisposition) 370 { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); } 371 372 bool COutFile::Create(LPCTSTR fileName, bool createAlways) 373 { return Open(fileName, GetCreationDisposition(createAlways)); } 374 375 #ifndef _UNICODE 376 377 bool COutFile::Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) 378 { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); } 379 380 bool COutFile::Open(LPCWSTR fileName, DWORD creationDisposition) 381 { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); } 382 383 bool COutFile::Create(LPCWSTR fileName, bool createAlways) 384 { return Open(fileName, GetCreationDisposition(createAlways)); } 385 386 #endif 387 388 bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) 389 { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); } 390 391 bool COutFile::SetMTime(const FILETIME *mTime) { return SetTime(NULL, NULL, mTime); } 392 393 bool 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 403 bool 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 422 bool COutFile::SetEndOfFile() { return BOOLToBool(::SetEndOfFile(_handle)); } 423 424 bool 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