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