diff options
author | Igor Pavlov <87184205+ip7z@users.noreply.github.com> | 2021-12-27 00:00:00 +0000 |
---|---|---|
committer | Igor Pavlov <87184205+ip7z@users.noreply.github.com> | 2022-03-18 15:35:13 +0500 |
commit | f19f813537c7aea1c20749c914e756b54a9c3cf5 (patch) | |
tree | 816ba62ca7c0fa19f2eb46d9e9d6f7dd7c3a744d /CPP/Windows/FileIO.cpp | |
parent | 98e06a519b63b81986abe76d28887f6984a7732b (diff) | |
download | 7zip-21.07.tar.gz 7zip-21.07.tar.bz2 7zip-21.07.zip |
'21.07'21.07
Diffstat (limited to 'CPP/Windows/FileIO.cpp')
-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 | ||