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
Create(CFSTR path,DWORD desiredAccess,DWORD shareMode,DWORD creationDisposition,DWORD flagsAndAttributes)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
Close()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
GetPosition(UInt64 & position) const94 bool CFileBase::GetPosition(UInt64 &position) const throw()
95 {
96 return Seek(0, FILE_CURRENT, position);
97 }
98
GetLength(UInt64 & length) const99 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
Seek(Int64 distanceToMove,DWORD moveMethod,UInt64 & newPosition) const118 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
Seek(UInt64 position,UInt64 & newPosition) const137 bool CFileBase::Seek(UInt64 position, UInt64 &newPosition) const throw()
138 {
139 return Seek(position, FILE_BEGIN, newPosition);
140 }
141
SeekToBegin() const142 bool CFileBase::SeekToBegin() const throw()
143 {
144 UInt64 newPosition;
145 return Seek(0, newPosition);
146 }
147
SeekToEnd(UInt64 & newPosition) const148 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
CorrectDeviceSize()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
CalcDeviceSize(CFSTR s)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
Open(CFSTR fileName,DWORD shareMode,DWORD creationDisposition,DWORD flagsAndAttributes)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
OpenShared(CFSTR fileName,bool shareForWrite)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
Open(CFSTR fileName)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
Read1(void * data,UInt32 size,UInt32 & processedSize)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
ReadPart(void * data,UInt32 size,UInt32 & processedSize)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
Read(void * data,UInt32 size,UInt32 & processedSize)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
GetCreationDisposition(bool createAlways)371 static inline DWORD GetCreationDisposition(bool createAlways)
372 { return createAlways? CREATE_ALWAYS: CREATE_NEW; }
373
Open(CFSTR fileName,DWORD shareMode,DWORD creationDisposition,DWORD flagsAndAttributes)374 bool COutFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
375 { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); }
376
Open(CFSTR fileName,DWORD creationDisposition)377 bool COutFile::Open(CFSTR fileName, DWORD creationDisposition)
378 { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); }
379
Create(CFSTR fileName,bool createAlways)380 bool COutFile::Create(CFSTR fileName, bool createAlways)
381 { return Open(fileName, GetCreationDisposition(createAlways)); }
382
CreateAlways(CFSTR fileName,DWORD flagsAndAttributes)383 bool COutFile::CreateAlways(CFSTR fileName, DWORD flagsAndAttributes)
384 { return Open(fileName, FILE_SHARE_READ, GetCreationDisposition(true), flagsAndAttributes); }
385
SetTime(const FILETIME * cTime,const FILETIME * aTime,const FILETIME * mTime)386 bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) throw()
387 { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); }
388
SetMTime(const FILETIME * mTime)389 bool COutFile::SetMTime(const FILETIME *mTime) throw() { return SetTime(NULL, NULL, mTime); }
390
WritePart(const void * data,UInt32 size,UInt32 & processedSize)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
Write(const void * data,UInt32 size,UInt32 & processedSize)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
SetEndOfFile()420 bool COutFile::SetEndOfFile() throw() { return BOOLToBool(::SetEndOfFile(_handle)); }
421
SetLength(UInt64 length)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