diff options
Diffstat (limited to '')
| -rw-r--r-- | CPP/Windows/FileIO.cpp | 825 |
1 files changed, 825 insertions, 0 deletions
diff --git a/CPP/Windows/FileIO.cpp b/CPP/Windows/FileIO.cpp new file mode 100644 index 0000000..2974b1b --- /dev/null +++ b/CPP/Windows/FileIO.cpp | |||
| @@ -0,0 +1,825 @@ | |||
| 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 <stdio.h> | ||
| 10 | |||
| 11 | #include "FileIO.h" | ||
| 12 | #include "FileName.h" | ||
| 13 | |||
| 14 | HRESULT GetLastError_noZero_HRESULT() | ||
| 15 | { | ||
| 16 | DWORD res = ::GetLastError(); | ||
| 17 | if (res == 0) | ||
| 18 | return E_FAIL; | ||
| 19 | return HRESULT_FROM_WIN32(res); | ||
| 20 | } | ||
| 21 | |||
| 22 | #ifdef _WIN32 | ||
| 23 | |||
| 24 | #ifndef _UNICODE | ||
| 25 | extern bool g_IsNT; | ||
| 26 | #endif | ||
| 27 | |||
| 28 | using namespace NWindows; | ||
| 29 | using namespace NFile; | ||
| 30 | using namespace NName; | ||
| 31 | |||
| 32 | namespace NWindows { | ||
| 33 | namespace NFile { | ||
| 34 | |||
| 35 | #ifdef SUPPORT_DEVICE_FILE | ||
| 36 | |||
| 37 | namespace NSystem | ||
| 38 | { | ||
| 39 | bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize); | ||
| 40 | } | ||
| 41 | #endif | ||
| 42 | |||
| 43 | namespace NIO { | ||
| 44 | |||
| 45 | /* | ||
| 46 | WinXP-64 CreateFile(): | ||
| 47 | "" - ERROR_PATH_NOT_FOUND | ||
| 48 | :stream - OK | ||
| 49 | .:stream - ERROR_PATH_NOT_FOUND | ||
| 50 | .\:stream - OK | ||
| 51 | |||
| 52 | folder\:stream - ERROR_INVALID_NAME | ||
| 53 | folder:stream - OK | ||
| 54 | |||
| 55 | c:\:stream - OK | ||
| 56 | |||
| 57 | c::stream - ERROR_INVALID_NAME, if current dir is NOT ROOT ( c:\dir1 ) | ||
| 58 | c::stream - OK, if current dir is ROOT ( c:\ ) | ||
| 59 | */ | ||
| 60 | |||
| 61 | bool CFileBase::Create(CFSTR path, DWORD desiredAccess, | ||
| 62 | DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) | ||
| 63 | { | ||
| 64 | if (!Close()) | ||
| 65 | return false; | ||
| 66 | |||
| 67 | #ifdef SUPPORT_DEVICE_FILE | ||
| 68 | IsDeviceFile = false; | ||
| 69 | #endif | ||
| 70 | |||
| 71 | #ifndef _UNICODE | ||
| 72 | if (!g_IsNT) | ||
| 73 | { | ||
| 74 | _handle = ::CreateFile(fs2fas(path), desiredAccess, shareMode, | ||
| 75 | (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL); | ||
| 76 | } | ||
| 77 | else | ||
| 78 | #endif | ||
| 79 | { | ||
| 80 | IF_USE_MAIN_PATH | ||
| 81 | _handle = ::CreateFileW(fs2us(path), desiredAccess, shareMode, | ||
| 82 | (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL); | ||
| 83 | #ifdef WIN_LONG_PATH | ||
| 84 | if (_handle == INVALID_HANDLE_VALUE && USE_SUPER_PATH) | ||
| 85 | { | ||
| 86 | UString superPath; | ||
| 87 | if (GetSuperPath(path, superPath, USE_MAIN_PATH)) | ||
| 88 | _handle = ::CreateFileW(superPath, desiredAccess, shareMode, | ||
| 89 | (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL); | ||
| 90 | } | ||
| 91 | #endif | ||
| 92 | } | ||
| 93 | |||
| 94 | /* | ||
| 95 | #ifndef UNDER_CE | ||
| 96 | #ifndef _SFX | ||
| 97 | if (_handle == INVALID_HANDLE_VALUE) | ||
| 98 | { | ||
| 99 | // it's debug hack to open symbolic links in Windows XP and WSL links in Windows 10 | ||
| 100 | DWORD lastError = GetLastError(); | ||
| 101 | if (lastError == ERROR_CANT_ACCESS_FILE) | ||
| 102 | { | ||
| 103 | CByteBuffer buf; | ||
| 104 | if (NIO::GetReparseData(path, buf, NULL)) | ||
| 105 | { | ||
| 106 | CReparseAttr attr; | ||
| 107 | if (attr.Parse(buf, buf.Size())) | ||
| 108 | { | ||
| 109 | FString dirPrefix, fileName; | ||
| 110 | if (NDir::GetFullPathAndSplit(path, dirPrefix, fileName)) | ||
| 111 | { | ||
| 112 | FString fullPath; | ||
| 113 | if (GetFullPath(dirPrefix, us2fs(attr.GetPath()), fullPath)) | ||
| 114 | { | ||
| 115 | // FIX IT: recursion levels must be restricted | ||
| 116 | return Create(fullPath, desiredAccess, | ||
| 117 | shareMode, creationDisposition, flagsAndAttributes); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
| 121 | } | ||
| 122 | SetLastError(lastError); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | #endif | ||
| 126 | #endif | ||
| 127 | */ | ||
| 128 | |||
| 129 | return (_handle != INVALID_HANDLE_VALUE); | ||
| 130 | } | ||
| 131 | |||
| 132 | bool CFileBase::Close() throw() | ||
| 133 | { | ||
| 134 | if (_handle == INVALID_HANDLE_VALUE) | ||
| 135 | return true; | ||
| 136 | if (!::CloseHandle(_handle)) | ||
| 137 | return false; | ||
| 138 | _handle = INVALID_HANDLE_VALUE; | ||
| 139 | return true; | ||
| 140 | } | ||
| 141 | |||
| 142 | bool CFileBase::GetLength(UInt64 &length) const throw() | ||
| 143 | { | ||
| 144 | #ifdef SUPPORT_DEVICE_FILE | ||
| 145 | if (IsDeviceFile && SizeDefined) | ||
| 146 | { | ||
| 147 | length = Size; | ||
| 148 | return true; | ||
| 149 | } | ||
| 150 | #endif | ||
| 151 | |||
| 152 | DWORD high = 0; | ||
| 153 | const DWORD low = ::GetFileSize(_handle, &high); | ||
| 154 | if (low == INVALID_FILE_SIZE) | ||
| 155 | if (::GetLastError() != NO_ERROR) | ||
| 156 | return false; | ||
| 157 | length = (((UInt64)high) << 32) + low; | ||
| 158 | return true; | ||
| 159 | |||
| 160 | /* | ||
| 161 | LARGE_INTEGER fileSize; | ||
| 162 | // GetFileSizeEx() is unsupported in 98/ME/NT, and supported in Win2000+ | ||
| 163 | if (!GetFileSizeEx(_handle, &fileSize)) | ||
| 164 | return false; | ||
| 165 | length = (UInt64)fileSize.QuadPart; | ||
| 166 | return true; | ||
| 167 | */ | ||
| 168 | } | ||
| 169 | |||
| 170 | |||
| 171 | /* Specification for SetFilePointer(): | ||
| 172 | |||
| 173 | If a new file pointer is a negative value, | ||
| 174 | { | ||
| 175 | the function fails, | ||
| 176 | the file pointer is not moved, | ||
| 177 | the code returned by GetLastError() is ERROR_NEGATIVE_SEEK. | ||
| 178 | } | ||
| 179 | |||
| 180 | If the hFile handle is opened with the FILE_FLAG_NO_BUFFERING flag set | ||
| 181 | { | ||
| 182 | an application can move the file pointer only to sector-aligned positions. | ||
| 183 | A sector-aligned position is a position that is a whole number multiple of | ||
| 184 | the volume sector size. | ||
| 185 | An application can obtain a volume sector size by calling the GetDiskFreeSpace. | ||
| 186 | } | ||
| 187 | |||
| 188 | It is not an error to set a file pointer to a position beyond the end of the file. | ||
| 189 | The size of the file does not increase until you call the SetEndOfFile, WriteFile, or WriteFileEx function. | ||
| 190 | |||
| 191 | If the return value is INVALID_SET_FILE_POINTER and if lpDistanceToMoveHigh is non-NULL, | ||
| 192 | an application must call GetLastError to determine whether or not the function has succeeded or failed. | ||
| 193 | */ | ||
| 194 | |||
| 195 | bool CFileBase::GetPosition(UInt64 &position) const throw() | ||
| 196 | { | ||
| 197 | LONG high = 0; | ||
| 198 | const DWORD low = ::SetFilePointer(_handle, 0, &high, FILE_CURRENT); | ||
| 199 | if (low == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) | ||
| 200 | { | ||
| 201 | // for error case we can set (position) to (-1) or (0) or leave (position) unchanged. | ||
| 202 | // position = (UInt64)(Int64)-1; // for debug | ||
| 203 | position = 0; | ||
| 204 | return false; | ||
| 205 | } | ||
| 206 | position = (((UInt64)(UInt32)high) << 32) + low; | ||
| 207 | return true; | ||
| 208 | // we don't want recursed GetPosition() | ||
| 209 | // return Seek(0, FILE_CURRENT, position); | ||
| 210 | } | ||
| 211 | |||
| 212 | bool CFileBase::Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const throw() | ||
| 213 | { | ||
| 214 | #ifdef SUPPORT_DEVICE_FILE | ||
| 215 | if (IsDeviceFile && SizeDefined && moveMethod == FILE_END) | ||
| 216 | { | ||
| 217 | distanceToMove += Size; | ||
| 218 | moveMethod = FILE_BEGIN; | ||
| 219 | } | ||
| 220 | #endif | ||
| 221 | |||
| 222 | LONG high = (LONG)(distanceToMove >> 32); | ||
| 223 | const DWORD low = ::SetFilePointer(_handle, (LONG)(distanceToMove & 0xFFFFFFFF), &high, moveMethod); | ||
| 224 | if (low == INVALID_SET_FILE_POINTER) | ||
| 225 | { | ||
| 226 | const DWORD lastError = ::GetLastError(); | ||
| 227 | if (lastError != NO_ERROR) | ||
| 228 | { | ||
| 229 | // 21.07: we set (newPosition) to real position even after error. | ||
| 230 | GetPosition(newPosition); | ||
| 231 | SetLastError(lastError); // restore LastError | ||
| 232 | return false; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | newPosition = (((UInt64)(UInt32)high) << 32) + low; | ||
| 236 | return true; | ||
| 237 | } | ||
| 238 | |||
| 239 | bool CFileBase::Seek(UInt64 position, UInt64 &newPosition) const throw() | ||
| 240 | { | ||
| 241 | return Seek((Int64)position, FILE_BEGIN, newPosition); | ||
| 242 | } | ||
| 243 | |||
| 244 | bool CFileBase::SeekToBegin() const throw() | ||
| 245 | { | ||
| 246 | UInt64 newPosition = 0; | ||
| 247 | return Seek(0, newPosition) && (newPosition == 0); | ||
| 248 | } | ||
| 249 | |||
| 250 | bool CFileBase::SeekToEnd(UInt64 &newPosition) const throw() | ||
| 251 | { | ||
| 252 | return Seek(0, FILE_END, newPosition); | ||
| 253 | } | ||
| 254 | |||
| 255 | // ---------- CInFile --------- | ||
| 256 | |||
| 257 | #ifdef SUPPORT_DEVICE_FILE | ||
| 258 | |||
| 259 | void CInFile::CorrectDeviceSize() | ||
| 260 | { | ||
| 261 | // maybe we must decrease kClusterSize to 1 << 12, if we want correct size at tail | ||
| 262 | static const UInt32 kClusterSize = 1 << 14; | ||
| 263 | UInt64 pos = Size & ~(UInt64)(kClusterSize - 1); | ||
| 264 | UInt64 realNewPosition; | ||
| 265 | if (!Seek(pos, realNewPosition)) | ||
| 266 | return; | ||
| 267 | Byte *buf = (Byte *)MidAlloc(kClusterSize); | ||
| 268 | |||
| 269 | bool needbackward = true; | ||
| 270 | |||
| 271 | for (;;) | ||
| 272 | { | ||
| 273 | UInt32 processed = 0; | ||
| 274 | // up test is slow for "PhysicalDrive". | ||
| 275 | // processed size for latest block for "PhysicalDrive0" is 0. | ||
| 276 | if (!Read1(buf, kClusterSize, processed)) | ||
| 277 | break; | ||
| 278 | if (processed == 0) | ||
| 279 | break; | ||
| 280 | needbackward = false; | ||
| 281 | Size = pos + processed; | ||
| 282 | if (processed != kClusterSize) | ||
| 283 | break; | ||
| 284 | pos += kClusterSize; | ||
| 285 | } | ||
| 286 | |||
| 287 | if (needbackward && pos != 0) | ||
| 288 | { | ||
| 289 | pos -= kClusterSize; | ||
| 290 | for (;;) | ||
| 291 | { | ||
| 292 | // break; | ||
| 293 | if (!Seek(pos, realNewPosition)) | ||
| 294 | break; | ||
| 295 | if (!buf) | ||
| 296 | { | ||
| 297 | buf = (Byte *)MidAlloc(kClusterSize); | ||
| 298 | if (!buf) | ||
| 299 | break; | ||
| 300 | } | ||
| 301 | UInt32 processed = 0; | ||
| 302 | // that code doesn't work for "PhysicalDrive0" | ||
| 303 | if (!Read1(buf, kClusterSize, processed)) | ||
| 304 | break; | ||
| 305 | if (processed != 0) | ||
| 306 | { | ||
| 307 | Size = pos + processed; | ||
| 308 | break; | ||
| 309 | } | ||
| 310 | if (pos == 0) | ||
| 311 | break; | ||
| 312 | pos -= kClusterSize; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | MidFree(buf); | ||
| 316 | } | ||
| 317 | |||
| 318 | |||
| 319 | void CInFile::CalcDeviceSize(CFSTR s) | ||
| 320 | { | ||
| 321 | SizeDefined = false; | ||
| 322 | Size = 0; | ||
| 323 | if (_handle == INVALID_HANDLE_VALUE || !IsDeviceFile) | ||
| 324 | return; | ||
| 325 | #ifdef UNDER_CE | ||
| 326 | |||
| 327 | SizeDefined = true; | ||
| 328 | Size = 128 << 20; | ||
| 329 | |||
| 330 | #else | ||
| 331 | |||
| 332 | PARTITION_INFORMATION partInfo; | ||
| 333 | bool needCorrectSize = true; | ||
| 334 | |||
| 335 | /* | ||
| 336 | WinXP 64-bit: | ||
| 337 | |||
| 338 | HDD \\.\PhysicalDrive0 (MBR): | ||
| 339 | GetPartitionInfo == GeometryEx : corrrect size? (includes tail) | ||
| 340 | Geometry : smaller than GeometryEx (no tail, maybe correct too?) | ||
| 341 | MyGetDiskFreeSpace : FAIL | ||
| 342 | Size correction is slow and block size (kClusterSize) must be small? | ||
| 343 | |||
| 344 | HDD partition \\.\N: (NTFS): | ||
| 345 | MyGetDiskFreeSpace : Size of NTFS clusters. Same size can be calculated after correction | ||
| 346 | GetPartitionInfo : size of partition data: NTFS clusters + TAIL; TAIL contains extra empty sectors and copy of first sector of NTFS | ||
| 347 | Geometry / CdRomGeometry / GeometryEx : size of HDD (not that partition) | ||
| 348 | |||
| 349 | CD-ROM drive (ISO): | ||
| 350 | MyGetDiskFreeSpace : correct size. Same size can be calculated after correction | ||
| 351 | Geometry == CdRomGeometry : smaller than corrrect size | ||
| 352 | GetPartitionInfo == GeometryEx : larger than corrrect size | ||
| 353 | |||
| 354 | Floppy \\.\a: (FAT): | ||
| 355 | Geometry : correct size. | ||
| 356 | CdRomGeometry / GeometryEx / GetPartitionInfo / MyGetDiskFreeSpace - FAIL | ||
| 357 | correction works OK for FAT. | ||
| 358 | correction works OK for non-FAT, if kClusterSize = 512. | ||
| 359 | */ | ||
| 360 | |||
| 361 | if (GetPartitionInfo(&partInfo)) | ||
| 362 | { | ||
| 363 | Size = (UInt64)partInfo.PartitionLength.QuadPart; | ||
| 364 | SizeDefined = true; | ||
| 365 | needCorrectSize = false; | ||
| 366 | if ((s)[0] == '\\' && (s)[1] == '\\' && (s)[2] == '.' && (s)[3] == '\\' && (s)[5] == ':' && (s)[6] == 0) | ||
| 367 | { | ||
| 368 | FChar path[4] = { s[4], ':', '\\', 0 }; | ||
| 369 | UInt64 clusterSize, totalSize, freeSize; | ||
| 370 | if (NSystem::MyGetDiskFreeSpace(path, clusterSize, totalSize, freeSize)) | ||
| 371 | Size = totalSize; | ||
| 372 | else | ||
| 373 | needCorrectSize = true; | ||
| 374 | } | ||
| 375 | } | ||
| 376 | |||
| 377 | if (!SizeDefined) | ||
| 378 | { | ||
| 379 | my_DISK_GEOMETRY_EX geomEx; | ||
| 380 | SizeDefined = GetGeometryEx(&geomEx); | ||
| 381 | if (SizeDefined) | ||
| 382 | Size = (UInt64)geomEx.DiskSize.QuadPart; | ||
| 383 | else | ||
| 384 | { | ||
| 385 | DISK_GEOMETRY geom; | ||
| 386 | SizeDefined = GetGeometry(&geom); | ||
| 387 | if (!SizeDefined) | ||
| 388 | SizeDefined = GetCdRomGeometry(&geom); | ||
| 389 | if (SizeDefined) | ||
| 390 | Size = (UInt64)geom.Cylinders.QuadPart * geom.TracksPerCylinder * geom.SectorsPerTrack * geom.BytesPerSector; | ||
| 391 | } | ||
| 392 | } | ||
| 393 | |||
| 394 | if (needCorrectSize && SizeDefined && Size != 0) | ||
| 395 | { | ||
| 396 | CorrectDeviceSize(); | ||
| 397 | SeekToBegin(); | ||
| 398 | } | ||
| 399 | |||
| 400 | // SeekToBegin(); | ||
| 401 | #endif | ||
| 402 | } | ||
| 403 | |||
| 404 | // ((desiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)) == 0 && | ||
| 405 | |||
| 406 | #define MY_DEVICE_EXTRA_CODE \ | ||
| 407 | IsDeviceFile = IsDevicePath(fileName); \ | ||
| 408 | CalcDeviceSize(fileName); | ||
| 409 | #else | ||
| 410 | #define MY_DEVICE_EXTRA_CODE | ||
| 411 | #endif | ||
| 412 | |||
| 413 | bool CInFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) | ||
| 414 | { | ||
| 415 | DWORD desiredAccess = GENERIC_READ; | ||
| 416 | |||
| 417 | #ifdef _WIN32 | ||
| 418 | if (PreserveATime) | ||
| 419 | desiredAccess |= FILE_WRITE_ATTRIBUTES; | ||
| 420 | #endif | ||
| 421 | |||
| 422 | bool res = Create(fileName, desiredAccess, shareMode, creationDisposition, flagsAndAttributes); | ||
| 423 | |||
| 424 | #ifdef _WIN32 | ||
| 425 | if (res && PreserveATime) | ||
| 426 | { | ||
| 427 | FILETIME ft; | ||
| 428 | ft.dwHighDateTime = ft.dwLowDateTime = 0xFFFFFFFF; | ||
| 429 | ::SetFileTime(_handle, NULL, &ft, NULL); | ||
| 430 | } | ||
| 431 | #endif | ||
| 432 | |||
| 433 | MY_DEVICE_EXTRA_CODE | ||
| 434 | return res; | ||
| 435 | } | ||
| 436 | |||
| 437 | bool CInFile::OpenShared(CFSTR fileName, bool shareForWrite) | ||
| 438 | { return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); } | ||
| 439 | |||
| 440 | bool CInFile::Open(CFSTR fileName) | ||
| 441 | { return OpenShared(fileName, false); } | ||
| 442 | |||
| 443 | // ReadFile and WriteFile functions in Windows have BUG: | ||
| 444 | // If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1) | ||
| 445 | // from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES | ||
| 446 | // (Insufficient system resources exist to complete the requested service). | ||
| 447 | |||
| 448 | // Probably in some version of Windows there are problems with other sizes: | ||
| 449 | // for 32 MB (maybe also for 16 MB). | ||
| 450 | // And message can be "Network connection was lost" | ||
| 451 | |||
| 452 | static const UInt32 kChunkSizeMax = (1 << 22); | ||
| 453 | |||
| 454 | bool CInFile::Read1(void *data, UInt32 size, UInt32 &processedSize) throw() | ||
| 455 | { | ||
| 456 | DWORD processedLoc = 0; | ||
| 457 | bool res = BOOLToBool(::ReadFile(_handle, data, size, &processedLoc, NULL)); | ||
| 458 | processedSize = (UInt32)processedLoc; | ||
| 459 | return res; | ||
| 460 | } | ||
| 461 | |||
| 462 | bool CInFile::ReadPart(void *data, UInt32 size, UInt32 &processedSize) throw() | ||
| 463 | { | ||
| 464 | if (size > kChunkSizeMax) | ||
| 465 | size = kChunkSizeMax; | ||
| 466 | return Read1(data, size, processedSize); | ||
| 467 | } | ||
| 468 | |||
| 469 | bool CInFile::Read(void *data, UInt32 size, UInt32 &processedSize) throw() | ||
| 470 | { | ||
| 471 | processedSize = 0; | ||
| 472 | do | ||
| 473 | { | ||
| 474 | UInt32 processedLoc = 0; | ||
| 475 | bool res = ReadPart(data, size, processedLoc); | ||
| 476 | processedSize += processedLoc; | ||
| 477 | if (!res) | ||
| 478 | return false; | ||
| 479 | if (processedLoc == 0) | ||
| 480 | return true; | ||
| 481 | data = (void *)((unsigned char *)data + processedLoc); | ||
| 482 | size -= processedLoc; | ||
| 483 | } | ||
| 484 | while (size > 0); | ||
| 485 | return true; | ||
| 486 | } | ||
| 487 | |||
| 488 | bool CInFile::ReadFull(void *data, size_t size, size_t &processedSize) throw() | ||
| 489 | { | ||
| 490 | processedSize = 0; | ||
| 491 | do | ||
| 492 | { | ||
| 493 | UInt32 processedLoc = 0; | ||
| 494 | const UInt32 sizeLoc = (size > kChunkSizeMax ? (UInt32)kChunkSizeMax : (UInt32)size); | ||
| 495 | const bool res = Read1(data, sizeLoc, processedLoc); | ||
| 496 | processedSize += processedLoc; | ||
| 497 | if (!res) | ||
| 498 | return false; | ||
| 499 | if (processedLoc == 0) | ||
| 500 | return true; | ||
| 501 | data = (void *)((unsigned char *)data + processedLoc); | ||
| 502 | size -= processedLoc; | ||
| 503 | } | ||
| 504 | while (size > 0); | ||
| 505 | return true; | ||
| 506 | } | ||
| 507 | |||
| 508 | // ---------- COutFile --------- | ||
| 509 | |||
| 510 | static inline DWORD GetCreationDisposition(bool createAlways) | ||
| 511 | { return createAlways? CREATE_ALWAYS: CREATE_NEW; } | ||
| 512 | |||
| 513 | bool COutFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) | ||
| 514 | { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); } | ||
| 515 | |||
| 516 | bool COutFile::Open(CFSTR fileName, DWORD creationDisposition) | ||
| 517 | { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); } | ||
| 518 | |||
| 519 | bool COutFile::Create(CFSTR fileName, bool createAlways) | ||
| 520 | { return Open(fileName, GetCreationDisposition(createAlways)); } | ||
| 521 | |||
| 522 | bool COutFile::CreateAlways(CFSTR fileName, DWORD flagsAndAttributes) | ||
| 523 | { return Open(fileName, FILE_SHARE_READ, GetCreationDisposition(true), flagsAndAttributes); } | ||
| 524 | |||
| 525 | bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) throw() | ||
| 526 | { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); } | ||
| 527 | |||
| 528 | bool COutFile::SetMTime(const FILETIME *mTime) throw() { return SetTime(NULL, NULL, mTime); } | ||
| 529 | |||
| 530 | bool COutFile::WritePart(const void *data, UInt32 size, UInt32 &processedSize) throw() | ||
| 531 | { | ||
| 532 | if (size > kChunkSizeMax) | ||
| 533 | size = kChunkSizeMax; | ||
| 534 | DWORD processedLoc = 0; | ||
| 535 | bool res = BOOLToBool(::WriteFile(_handle, data, size, &processedLoc, NULL)); | ||
| 536 | processedSize = (UInt32)processedLoc; | ||
| 537 | return res; | ||
| 538 | } | ||
| 539 | |||
| 540 | bool COutFile::Write(const void *data, UInt32 size, UInt32 &processedSize) throw() | ||
| 541 | { | ||
| 542 | processedSize = 0; | ||
| 543 | do | ||
| 544 | { | ||
| 545 | UInt32 processedLoc = 0; | ||
| 546 | bool res = WritePart(data, size, processedLoc); | ||
| 547 | processedSize += processedLoc; | ||
| 548 | if (!res) | ||
| 549 | return false; | ||
| 550 | if (processedLoc == 0) | ||
| 551 | return true; | ||
| 552 | data = (const void *)((const unsigned char *)data + processedLoc); | ||
| 553 | size -= processedLoc; | ||
| 554 | } | ||
| 555 | while (size != 0); | ||
| 556 | return true; | ||
| 557 | } | ||
| 558 | |||
| 559 | bool COutFile::WriteFull(const void *data, size_t size) throw() | ||
| 560 | { | ||
| 561 | do | ||
| 562 | { | ||
| 563 | UInt32 processedLoc = 0; | ||
| 564 | const UInt32 sizeCur = (size > kChunkSizeMax ? kChunkSizeMax : (UInt32)size); | ||
| 565 | if (!WritePart(data, sizeCur, processedLoc)) | ||
| 566 | return false; | ||
| 567 | if (processedLoc == 0) | ||
| 568 | return (size == 0); | ||
| 569 | data = (const void *)((const unsigned char *)data + processedLoc); | ||
| 570 | size -= processedLoc; | ||
| 571 | } | ||
| 572 | while (size != 0); | ||
| 573 | return true; | ||
| 574 | } | ||
| 575 | |||
| 576 | bool COutFile::SetEndOfFile() throw() { return BOOLToBool(::SetEndOfFile(_handle)); } | ||
| 577 | |||
| 578 | bool COutFile::SetLength(UInt64 length) throw() | ||
| 579 | { | ||
| 580 | UInt64 newPosition; | ||
| 581 | if (!Seek(length, newPosition)) | ||
| 582 | return false; | ||
| 583 | if (newPosition != length) | ||
| 584 | return false; | ||
| 585 | return SetEndOfFile(); | ||
| 586 | } | ||
| 587 | |||
| 588 | bool COutFile::SetLength_KeepPosition(UInt64 length) throw() | ||
| 589 | { | ||
| 590 | UInt64 currentPos = 0; | ||
| 591 | if (!GetPosition(currentPos)) | ||
| 592 | return false; | ||
| 593 | DWORD lastError = 0; | ||
| 594 | const bool result = SetLength(length); | ||
| 595 | if (!result) | ||
| 596 | lastError = GetLastError(); | ||
| 597 | UInt64 currentPos2; | ||
| 598 | const bool result2 = Seek(currentPos, currentPos2); | ||
| 599 | if (lastError != 0) | ||
| 600 | SetLastError(lastError); | ||
| 601 | return (result && result2); | ||
| 602 | } | ||
| 603 | |||
| 604 | }}} | ||
| 605 | |||
| 606 | #else // _WIN32 | ||
| 607 | |||
| 608 | |||
| 609 | // POSIX | ||
| 610 | |||
| 611 | #include <fcntl.h> | ||
| 612 | #include <unistd.h> | ||
| 613 | |||
| 614 | namespace NWindows { | ||
| 615 | namespace NFile { | ||
| 616 | |||
| 617 | namespace NDir { | ||
| 618 | bool SetDirTime(CFSTR path, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime); | ||
| 619 | } | ||
| 620 | |||
| 621 | namespace NIO { | ||
| 622 | |||
| 623 | bool CFileBase::OpenBinary(const char *name, int flags) | ||
| 624 | { | ||
| 625 | #ifdef O_BINARY | ||
| 626 | flags |= O_BINARY; | ||
| 627 | #endif | ||
| 628 | |||
| 629 | Close(); | ||
| 630 | _handle = ::open(name, flags, 0666); | ||
| 631 | return _handle != -1; | ||
| 632 | } | ||
| 633 | |||
| 634 | bool CFileBase::Close() | ||
| 635 | { | ||
| 636 | if (_handle == -1) | ||
| 637 | return true; | ||
| 638 | if (close(_handle) != 0) | ||
| 639 | return false; | ||
| 640 | _handle = -1; | ||
| 641 | return true; | ||
| 642 | } | ||
| 643 | |||
| 644 | bool CFileBase::GetLength(UInt64 &length) const | ||
| 645 | { | ||
| 646 | length = 0; | ||
| 647 | // length = (UInt64)(Int64)-1; // for debug | ||
| 648 | const off_t curPos = seekToCur(); | ||
| 649 | if (curPos == -1) | ||
| 650 | return false; | ||
| 651 | const off_t lengthTemp = seek(0, SEEK_END); | ||
| 652 | seek(curPos, SEEK_SET); | ||
| 653 | length = (UInt64)lengthTemp; | ||
| 654 | return (lengthTemp != -1); | ||
| 655 | } | ||
| 656 | |||
| 657 | off_t CFileBase::seek(off_t distanceToMove, int moveMethod) const | ||
| 658 | { | ||
| 659 | // printf("\nCFileBase::seek() moveMethod = %d, distanceToMove = %lld", moveMethod, (long long)distanceToMove); | ||
| 660 | // off_t res = ::lseek(_handle, distanceToMove, moveMethod); | ||
| 661 | return ::lseek(_handle, distanceToMove, moveMethod); | ||
| 662 | // printf(" res = %lld", (long long)res); | ||
| 663 | // return res; | ||
| 664 | } | ||
| 665 | |||
| 666 | off_t CFileBase::seekToBegin() const throw() | ||
| 667 | { | ||
| 668 | return seek(0, SEEK_SET); | ||
| 669 | } | ||
| 670 | |||
| 671 | off_t CFileBase::seekToCur() const throw() | ||
| 672 | { | ||
| 673 | return seek(0, SEEK_CUR); | ||
| 674 | } | ||
| 675 | |||
| 676 | /* | ||
| 677 | bool CFileBase::SeekToBegin() const throw() | ||
| 678 | { | ||
| 679 | return (::seek(0, SEEK_SET) != -1); | ||
| 680 | } | ||
| 681 | */ | ||
| 682 | |||
| 683 | |||
| 684 | ///////////////////////// | ||
| 685 | // CInFile | ||
| 686 | |||
| 687 | bool CInFile::Open(const char *name) | ||
| 688 | { | ||
| 689 | return CFileBase::OpenBinary(name, O_RDONLY); | ||
| 690 | } | ||
| 691 | |||
| 692 | bool CInFile::OpenShared(const char *name, bool) | ||
| 693 | { | ||
| 694 | return Open(name); | ||
| 695 | } | ||
| 696 | |||
| 697 | /* | ||
| 698 | On Linux (32-bit and 64-bit): | ||
| 699 | read(), write() (and similar system calls) will transfer at most | ||
| 700 | 0x7ffff000 = (2GiB - 4 KiB) bytes, returning the number of bytes actually transferred. | ||
| 701 | */ | ||
| 702 | |||
| 703 | static const size_t kChunkSizeMax = ((size_t)1 << 22); | ||
| 704 | |||
| 705 | ssize_t CInFile::read_part(void *data, size_t size) throw() | ||
| 706 | { | ||
| 707 | if (size > kChunkSizeMax) | ||
| 708 | size = kChunkSizeMax; | ||
| 709 | return ::read(_handle, data, size); | ||
| 710 | } | ||
| 711 | |||
| 712 | bool CInFile::ReadFull(void *data, size_t size, size_t &processed) throw() | ||
| 713 | { | ||
| 714 | processed = 0; | ||
| 715 | do | ||
| 716 | { | ||
| 717 | const ssize_t res = read_part(data, size); | ||
| 718 | if (res < 0) | ||
| 719 | return false; | ||
| 720 | if (res == 0) | ||
| 721 | break; | ||
| 722 | data = (void *)((unsigned char *)data + (size_t)res); | ||
| 723 | size -= (size_t)res; | ||
| 724 | processed += (size_t)res; | ||
| 725 | } | ||
| 726 | while (size > 0); | ||
| 727 | return true; | ||
| 728 | } | ||
| 729 | |||
| 730 | |||
| 731 | ///////////////////////// | ||
| 732 | // COutFile | ||
| 733 | |||
| 734 | bool COutFile::Create(const char *name, bool createAlways) | ||
| 735 | { | ||
| 736 | Path = name; // change it : set it only if open is success. | ||
| 737 | if (createAlways) | ||
| 738 | { | ||
| 739 | Close(); | ||
| 740 | _handle = ::creat(name, 0666); | ||
| 741 | return _handle != -1; | ||
| 742 | } | ||
| 743 | return OpenBinary(name, O_CREAT | O_EXCL | O_WRONLY); | ||
| 744 | } | ||
| 745 | |||
| 746 | bool COutFile::Open(const char *name, DWORD creationDisposition) | ||
| 747 | { | ||
| 748 | UNUSED_VAR(creationDisposition) // FIXME | ||
| 749 | return Create(name, false); | ||
| 750 | } | ||
| 751 | |||
| 752 | ssize_t COutFile::write_part(const void *data, size_t size) throw() | ||
| 753 | { | ||
| 754 | if (size > kChunkSizeMax) | ||
| 755 | size = kChunkSizeMax; | ||
| 756 | return ::write(_handle, data, size); | ||
| 757 | } | ||
| 758 | |||
| 759 | ssize_t COutFile::write_full(const void *data, size_t size, size_t &processed) throw() | ||
| 760 | { | ||
| 761 | processed = 0; | ||
| 762 | do | ||
| 763 | { | ||
| 764 | const ssize_t res = write_part(data, size); | ||
| 765 | if (res < 0) | ||
| 766 | return res; | ||
| 767 | if (res == 0) | ||
| 768 | break; | ||
| 769 | data = (const void *)((const unsigned char *)data + (size_t)res); | ||
| 770 | size -= (size_t)res; | ||
| 771 | processed += (size_t)res; | ||
| 772 | } | ||
| 773 | while (size > 0); | ||
| 774 | return (ssize_t)processed; | ||
| 775 | } | ||
| 776 | |||
| 777 | bool COutFile::SetLength(UInt64 length) throw() | ||
| 778 | { | ||
| 779 | const off_t len2 = (off_t)length; | ||
| 780 | if ((Int64)length != len2) | ||
| 781 | { | ||
| 782 | SetLastError(EFBIG); | ||
| 783 | return false; | ||
| 784 | } | ||
| 785 | // The value of the seek pointer shall not be modified by a call to ftruncate(). | ||
| 786 | int iret = ftruncate(_handle, len2); | ||
| 787 | return (iret == 0); | ||
| 788 | } | ||
| 789 | |||
| 790 | bool COutFile::Close() | ||
| 791 | { | ||
| 792 | bool res = CFileBase::Close(); | ||
| 793 | if (!res) | ||
| 794 | return res; | ||
| 795 | if (CTime_defined || ATime_defined || MTime_defined) | ||
| 796 | { | ||
| 797 | /* bool res2 = */ NWindows::NFile::NDir::SetDirTime(Path, | ||
| 798 | CTime_defined ? &CTime : NULL, | ||
| 799 | ATime_defined ? &ATime : NULL, | ||
| 800 | MTime_defined ? &MTime : NULL); | ||
| 801 | } | ||
| 802 | return res; | ||
| 803 | } | ||
| 804 | |||
| 805 | bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) throw() | ||
| 806 | { | ||
| 807 | // On some OS (cygwin, MacOSX ...), you must close the file before updating times | ||
| 808 | // return true; | ||
| 809 | |||
| 810 | if (cTime) { CTime = *cTime; CTime_defined = true; } else CTime_defined = false; | ||
| 811 | if (aTime) { ATime = *aTime; ATime_defined = true; } else ATime_defined = false; | ||
| 812 | if (mTime) { MTime = *mTime; MTime_defined = true; } else MTime_defined = false; | ||
| 813 | return true; | ||
| 814 | } | ||
| 815 | |||
| 816 | bool COutFile::SetMTime(const FILETIME *mTime) throw() | ||
| 817 | { | ||
| 818 | if (mTime) { MTime = *mTime; MTime_defined = true; } else MTime_defined = false; | ||
| 819 | return true; | ||
| 820 | } | ||
| 821 | |||
| 822 | }}} | ||
| 823 | |||
| 824 | |||
| 825 | #endif | ||
