1 // Windows/FileIO.cpp 2 3 #include "StdAfx.h" 4 5 #ifdef SUPPORT_DEVICE_FILE 6 #include "../../C/Alloc.h" 7 #endif 8 9 #include "FileIO.h" 10 #include "FileName.h" 11 12 #ifndef _UNICODE 13 extern bool g_IsNT; 14 #endif 15 16 using namespace NWindows; 17 using namespace NFile; 18 using namespace NName; 19 20 namespace NWindows { 21 namespace NFile { 22 23 #ifdef SUPPORT_DEVICE_FILE 24 25 namespace NSystem 26 { 27 bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize); 28 } 29 #endif 30 31 namespace NIO { 32 33 /* 34 WinXP-64 CreateFile(): 35 "" - ERROR_PATH_NOT_FOUND 36 :stream - OK 37 .:stream - ERROR_PATH_NOT_FOUND 38 .\:stream - OK 39 40 folder\:stream - ERROR_INVALID_NAME 41 folder:stream - OK 42 43 c:\:stream - OK 44 45 c::stream - ERROR_INVALID_NAME, if current dir is NOT ROOT ( c:\dir1 ) 46 c::stream - OK, if current dir is ROOT ( c:\ ) 47 */ 48 49 bool CFileBase::Create(CFSTR path, DWORD desiredAccess, 50 DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) 51 { 52 if (!Close()) 53 return false; 54 55 #ifdef SUPPORT_DEVICE_FILE 56 IsDeviceFile = false; 57 #endif 58 59 #ifndef _UNICODE 60 if (!g_IsNT) 61 { 62 _handle = ::CreateFile(fs2fas(path), desiredAccess, shareMode, 63 (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL); 64 } 65 else 66 #endif 67 { 68 IF_USE_MAIN_PATH 69 _handle = ::CreateFileW(fs2us(path), desiredAccess, shareMode, 70 (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL); 71 #ifdef WIN_LONG_PATH 72 if (_handle == INVALID_HANDLE_VALUE && USE_SUPER_PATH) 73 { 74 UString superPath; 75 if (GetSuperPath(path, superPath, USE_MAIN_PATH)) 76 _handle = ::CreateFileW(superPath, desiredAccess, shareMode, 77 (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL); 78 } 79 #endif 80 } 81 return (_handle != INVALID_HANDLE_VALUE); 82 } 83 84 bool CFileBase::Close() throw() 85 { 86 if (_handle == INVALID_HANDLE_VALUE) 87 return true; 88 if (!::CloseHandle(_handle)) 89 return false; 90 _handle = INVALID_HANDLE_VALUE; 91 return true; 92 } 93 94 bool CFileBase::GetPosition(UInt64 &position) const throw() 95 { 96 return Seek(0, FILE_CURRENT, position); 97 } 98 99 bool CFileBase::GetLength(UInt64 &length) const throw() 100 { 101 #ifdef SUPPORT_DEVICE_FILE 102 if (IsDeviceFile && SizeDefined) 103 { 104 length = Size; 105 return true; 106 } 107 #endif 108 109 DWORD sizeHigh; 110 DWORD sizeLow = ::GetFileSize(_handle, &sizeHigh); 111 if (sizeLow == 0xFFFFFFFF) 112 if (::GetLastError() != NO_ERROR) 113 return false; 114 length = (((UInt64)sizeHigh) << 32) + sizeLow; 115 return true; 116 } 117 118 bool CFileBase::Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const throw() 119 { 120 #ifdef SUPPORT_DEVICE_FILE 121 if (IsDeviceFile && SizeDefined && moveMethod == FILE_END) 122 { 123 distanceToMove += Size; 124 moveMethod = FILE_BEGIN; 125 } 126 #endif 127 128 LONG high = (LONG)(distanceToMove >> 32); 129 DWORD low = ::SetFilePointer(_handle, (LONG)(distanceToMove & 0xFFFFFFFF), &high, moveMethod); 130 if (low == 0xFFFFFFFF) 131 if (::GetLastError() != NO_ERROR) 132 return false; 133 newPosition = (((UInt64)high) << 32) + low; 134 return true; 135 } 136 137 bool CFileBase::Seek(UInt64 position, UInt64 &newPosition) const throw() 138 { 139 return Seek(position, FILE_BEGIN, newPosition); 140 } 141 142 bool CFileBase::SeekToBegin() const throw() 143 { 144 UInt64 newPosition; 145 return Seek(0, newPosition); 146 } 147 148 bool CFileBase::SeekToEnd(UInt64 &newPosition) const throw() 149 { 150 return Seek(0, FILE_END, newPosition); 151 } 152 153 // ---------- CInFile --------- 154 155 #ifdef SUPPORT_DEVICE_FILE 156 157 void CInFile::CorrectDeviceSize() 158 { 159 // maybe we must decrease kClusterSize to 1 << 12, if we want correct size at tail 160 static const UInt32 kClusterSize = 1 << 14; 161 UInt64 pos = Size & ~(UInt64)(kClusterSize - 1); 162 UInt64 realNewPosition; 163 if (!Seek(pos, realNewPosition)) 164 return; 165 Byte *buf = (Byte *)MidAlloc(kClusterSize); 166 167 bool needbackward = true; 168 169 for (;;) 170 { 171 UInt32 processed = 0; 172 // up test is slow for "PhysicalDrive". 173 // processed size for latest block for "PhysicalDrive0" is 0. 174 if (!Read1(buf, kClusterSize, processed)) 175 break; 176 if (processed == 0) 177 break; 178 needbackward = false; 179 Size = pos + processed; 180 if (processed != kClusterSize) 181 break; 182 pos += kClusterSize; 183 } 184 185 if (needbackward && pos != 0) 186 { 187 pos -= kClusterSize; 188 for (;;) 189 { 190 // break; 191 if (!Seek(pos, realNewPosition)) 192 break; 193 if (!buf) 194 { 195 buf = (Byte *)MidAlloc(kClusterSize); 196 if (!buf) 197 break; 198 } 199 UInt32 processed = 0; 200 // that code doesn't work for "PhysicalDrive0" 201 if (!Read1(buf, kClusterSize, processed)) 202 break; 203 if (processed != 0) 204 { 205 Size = pos + processed; 206 break; 207 } 208 if (pos == 0) 209 break; 210 pos -= kClusterSize; 211 } 212 } 213 MidFree(buf); 214 } 215 216 217 void CInFile::CalcDeviceSize(CFSTR s) 218 { 219 SizeDefined = false; 220 Size = 0; 221 if (_handle == INVALID_HANDLE_VALUE || !IsDeviceFile) 222 return; 223 #ifdef UNDER_CE 224 225 SizeDefined = true; 226 Size = 128 << 20; 227 228 #else 229 230 PARTITION_INFORMATION partInfo; 231 bool needCorrectSize = true; 232 233 /* 234 WinXP 64-bit: 235 236 HDD \\.\PhysicalDrive0 (MBR): 237 GetPartitionInfo == GeometryEx : corrrect size? (includes tail) 238 Geometry : smaller than GeometryEx (no tail, maybe correct too?) 239 MyGetDiskFreeSpace : FAIL 240 Size correction is slow and block size (kClusterSize) must be small? 241 242 HDD partition \\.\N: (NTFS): 243 MyGetDiskFreeSpace : Size of NTFS clusters. Same size can be calculated after correction 244 GetPartitionInfo : size of partition data: NTFS clusters + TAIL; TAIL contains extra empty sectors and copy of first sector of NTFS 245 Geometry / CdRomGeometry / GeometryEx : size of HDD (not that partition) 246 247 CD-ROM drive (ISO): 248 MyGetDiskFreeSpace : correct size. Same size can be calculated after correction 249 Geometry == CdRomGeometry : smaller than corrrect size 250 GetPartitionInfo == GeometryEx : larger than corrrect size 251 252 Floppy \\.\a: (FAT): 253 Geometry : correct size. 254 CdRomGeometry / GeometryEx / GetPartitionInfo / MyGetDiskFreeSpace - FAIL 255 correction works OK for FAT. 256 correction works OK for non-FAT, if kClusterSize = 512. 257 */ 258 259 if (GetPartitionInfo(&partInfo)) 260 { 261 Size = partInfo.PartitionLength.QuadPart; 262 SizeDefined = true; 263 needCorrectSize = false; 264 if ((s)[0] == '\\' && (s)[1] == '\\' && (s)[2] == '.' && (s)[3] == '\\' && (s)[5] == ':' && (s)[6] == 0) 265 { 266 FChar path[4] = { s[4], ':', '\\', 0 }; 267 UInt64 clusterSize, totalSize, freeSize; 268 if (NSystem::MyGetDiskFreeSpace(path, clusterSize, totalSize, freeSize)) 269 Size = totalSize; 270 else 271 needCorrectSize = true; 272 } 273 } 274 275 if (!SizeDefined) 276 { 277 my_DISK_GEOMETRY_EX geomEx; 278 SizeDefined = GetGeometryEx(&geomEx); 279 if (SizeDefined) 280 Size = geomEx.DiskSize.QuadPart; 281 else 282 { 283 DISK_GEOMETRY geom; 284 SizeDefined = GetGeometry(&geom); 285 if (!SizeDefined) 286 SizeDefined = GetCdRomGeometry(&geom); 287 if (SizeDefined) 288 Size = geom.Cylinders.QuadPart * geom.TracksPerCylinder * geom.SectorsPerTrack * geom.BytesPerSector; 289 } 290 } 291 292 if (needCorrectSize && SizeDefined && Size != 0) 293 { 294 CorrectDeviceSize(); 295 SeekToBegin(); 296 } 297 298 // SeekToBegin(); 299 #endif 300 } 301 302 // ((desiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)) == 0 && 303 304 #define MY_DEVICE_EXTRA_CODE \ 305 IsDeviceFile = IsDevicePath(fileName); \ 306 CalcDeviceSize(fileName); 307 #else 308 #define MY_DEVICE_EXTRA_CODE 309 #endif 310 311 bool CInFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) 312 { 313 bool res = Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes); 314 MY_DEVICE_EXTRA_CODE 315 return res; 316 } 317 318 bool CInFile::OpenShared(CFSTR fileName, bool shareForWrite) 319 { return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); } 320 321 bool CInFile::Open(CFSTR fileName) 322 { return OpenShared(fileName, false); } 323 324 // ReadFile and WriteFile functions in Windows have BUG: 325 // If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1) 326 // from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES 327 // (Insufficient system resources exist to complete the requested service). 328 329 // Probably in some version of Windows there are problems with other sizes: 330 // for 32 MB (maybe also for 16 MB). 331 // And message can be "Network connection was lost" 332 333 static UInt32 kChunkSizeMax = (1 << 22); 334 335 bool CInFile::Read1(void *data, UInt32 size, UInt32 &processedSize) throw() 336 { 337 DWORD processedLoc = 0; 338 bool res = BOOLToBool(::ReadFile(_handle, data, size, &processedLoc, NULL)); 339 processedSize = (UInt32)processedLoc; 340 return res; 341 } 342 343 bool CInFile::ReadPart(void *data, UInt32 size, UInt32 &processedSize) throw() 344 { 345 if (size > kChunkSizeMax) 346 size = kChunkSizeMax; 347 return Read1(data, size, processedSize); 348 } 349 350 bool CInFile::Read(void *data, UInt32 size, UInt32 &processedSize) throw() 351 { 352 processedSize = 0; 353 do 354 { 355 UInt32 processedLoc = 0; 356 bool res = ReadPart(data, size, processedLoc); 357 processedSize += processedLoc; 358 if (!res) 359 return false; 360 if (processedLoc == 0) 361 return true; 362 data = (void *)((unsigned char *)data + processedLoc); 363 size -= processedLoc; 364 } 365 while (size > 0); 366 return true; 367 } 368 369 // ---------- COutFile --------- 370 371 static inline DWORD GetCreationDisposition(bool createAlways) 372 { return createAlways? CREATE_ALWAYS: CREATE_NEW; } 373 374 bool COutFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) 375 { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); } 376 377 bool COutFile::Open(CFSTR fileName, DWORD creationDisposition) 378 { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); } 379 380 bool COutFile::Create(CFSTR fileName, bool createAlways) 381 { return Open(fileName, GetCreationDisposition(createAlways)); } 382 383 bool COutFile::CreateAlways(CFSTR fileName, DWORD flagsAndAttributes) 384 { return Open(fileName, FILE_SHARE_READ, GetCreationDisposition(true), flagsAndAttributes); } 385 386 bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) throw() 387 { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); } 388 389 bool COutFile::SetMTime(const FILETIME *mTime) throw() { return SetTime(NULL, NULL, mTime); } 390 391 bool COutFile::WritePart(const void *data, UInt32 size, UInt32 &processedSize) throw() 392 { 393 if (size > kChunkSizeMax) 394 size = kChunkSizeMax; 395 DWORD processedLoc = 0; 396 bool res = BOOLToBool(::WriteFile(_handle, data, size, &processedLoc, NULL)); 397 processedSize = (UInt32)processedLoc; 398 return res; 399 } 400 401 bool COutFile::Write(const void *data, UInt32 size, UInt32 &processedSize) throw() 402 { 403 processedSize = 0; 404 do 405 { 406 UInt32 processedLoc = 0; 407 bool res = WritePart(data, size, processedLoc); 408 processedSize += processedLoc; 409 if (!res) 410 return false; 411 if (processedLoc == 0) 412 return true; 413 data = (const void *)((const unsigned char *)data + processedLoc); 414 size -= processedLoc; 415 } 416 while (size > 0); 417 return true; 418 } 419 420 bool COutFile::SetEndOfFile() throw() { return BOOLToBool(::SetEndOfFile(_handle)); } 421 422 bool COutFile::SetLength(UInt64 length) throw() 423 { 424 UInt64 newPosition; 425 if (!Seek(length, newPosition)) 426 return false; 427 if (newPosition != length) 428 return false; 429 return SetEndOfFile(); 430 } 431 432 }}} 433