1 // Windows/FileIO.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "FileIO.h"
6 
7 #ifndef _UNICODE
8 extern bool g_IsNT;
9 #endif
10 
11 namespace NWindows {
12 namespace NFile {
13 
14 #ifdef SUPPORT_DEVICE_FILE
15 
IsDeviceName(CFSTR n)16 bool IsDeviceName(CFSTR n)
17 {
18   #ifdef UNDER_CE
19   int len = (int)MyStringLen(n);
20   if (len < 5 || len > 5 || memcmp(n, FTEXT("DSK"), 3 * sizeof(FCHAR)) != 0)
21     return false;
22   if (n[4] != ':')
23     return false;
24   // for reading use SG_REQ sg; if (DeviceIoControl(dsk, IOCTL_DISK_READ));
25   #else
26   if (n[0] != '\\' || n[1] != '\\' || n[2] != '.' ||  n[3] != '\\')
27     return false;
28   int len = (int)MyStringLen(n);
29   if (len == 6 && n[5] == ':')
30     return true;
31   if (len < 18 || len > 22 || memcmp(n + 4, FTEXT("PhysicalDrive"), 13 * sizeof(FCHAR)) != 0)
32     return false;
33   for (int i = 17; i < len; i++)
34     if (n[i] < '0' || n[i] > '9')
35       return false;
36   #endif
37   return true;
38 }
39 
40 #endif
41 
42 #if defined(WIN_LONG_PATH) && defined(_UNICODE)
43 #define WIN_LONG_PATH2
44 #endif
45 
46 #ifdef WIN_LONG_PATH
GetLongPathBase(CFSTR s,UString & res)47 bool GetLongPathBase(CFSTR s, UString &res)
48 {
49   res.Empty();
50   int len = MyStringLen(s);
51   FChar c = s[0];
52   if (len < 1 || c == '\\' || c == '.' && (len == 1 || len == 2 && s[1] == '.'))
53     return true;
54   UString curDir;
55   bool isAbs = false;
56   if (len > 3)
57     isAbs = (s[1] == ':' && s[2] == '\\' && (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'));
58 
59   if (!isAbs)
60   {
61     WCHAR temp[MAX_PATH + 2];
62     temp[0] = 0;
63     DWORD needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, temp);
64     if (needLength == 0 || needLength > MAX_PATH)
65       return false;
66     curDir = temp;
67     if (curDir.Back() != L'\\')
68       curDir += L'\\';
69   }
70   res = UString(L"\\\\?\\") + curDir + fs2us(s);
71   return true;
72 }
73 
GetLongPath(CFSTR path,UString & longPath)74 bool GetLongPath(CFSTR path, UString &longPath)
75 {
76   if (GetLongPathBase(path, longPath))
77     return !longPath.IsEmpty();
78   return false;
79 }
80 #endif
81 
82 namespace NIO {
83 
~CFileBase()84 CFileBase::~CFileBase() { Close(); }
85 
Create(CFSTR fileName,DWORD desiredAccess,DWORD shareMode,DWORD creationDisposition,DWORD flagsAndAttributes)86 bool CFileBase::Create(CFSTR fileName, DWORD desiredAccess,
87     DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
88 {
89   if (!Close())
90     return false;
91 
92   #ifdef SUPPORT_DEVICE_FILE
93   IsDeviceFile = false;
94   #endif
95 
96   #ifndef _UNICODE
97   if (!g_IsNT)
98   {
99     _handle = ::CreateFile(fs2fas(fileName), desiredAccess, shareMode,
100         (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL);
101   }
102   else
103   #endif
104   {
105     _handle = ::CreateFileW(fs2us(fileName), desiredAccess, shareMode,
106         (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL);
107     #ifdef WIN_LONG_PATH
108     if (_handle == INVALID_HANDLE_VALUE)
109     {
110       UString longPath;
111       if (GetLongPath(fileName, longPath))
112         _handle = ::CreateFileW(longPath, desiredAccess, shareMode,
113             (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL);
114     }
115     #endif
116   }
117   return (_handle != INVALID_HANDLE_VALUE);
118 }
119 
Close()120 bool CFileBase::Close()
121 {
122   if (_handle == INVALID_HANDLE_VALUE)
123     return true;
124   if (!::CloseHandle(_handle))
125     return false;
126   _handle = INVALID_HANDLE_VALUE;
127   return true;
128 }
129 
GetPosition(UInt64 & position) const130 bool CFileBase::GetPosition(UInt64 &position) const
131 {
132   return Seek(0, FILE_CURRENT, position);
133 }
134 
GetLength(UInt64 & length) const135 bool CFileBase::GetLength(UInt64 &length) const
136 {
137   #ifdef SUPPORT_DEVICE_FILE
138   if (IsDeviceFile && LengthDefined)
139   {
140     length = Length;
141     return true;
142   }
143   #endif
144 
145   DWORD sizeHigh;
146   DWORD sizeLow = ::GetFileSize(_handle, &sizeHigh);
147   if (sizeLow == 0xFFFFFFFF)
148     if (::GetLastError() != NO_ERROR)
149       return false;
150   length = (((UInt64)sizeHigh) << 32) + sizeLow;
151   return true;
152 }
153 
Seek(Int64 distanceToMove,DWORD moveMethod,UInt64 & newPosition) const154 bool CFileBase::Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const
155 {
156   #ifdef SUPPORT_DEVICE_FILE
157   if (IsDeviceFile && LengthDefined && moveMethod == FILE_END)
158   {
159     distanceToMove += Length;
160     moveMethod = FILE_BEGIN;
161   }
162   #endif
163 
164   LARGE_INTEGER value;
165   value.QuadPart = distanceToMove;
166   value.LowPart = ::SetFilePointer(_handle, value.LowPart, &value.HighPart, moveMethod);
167   if (value.LowPart == 0xFFFFFFFF)
168     if (::GetLastError() != NO_ERROR)
169       return false;
170   newPosition = value.QuadPart;
171   return true;
172 }
173 
Seek(UInt64 position,UInt64 & newPosition)174 bool CFileBase::Seek(UInt64 position, UInt64 &newPosition)
175 {
176   return Seek(position, FILE_BEGIN, newPosition);
177 }
178 
SeekToBegin()179 bool CFileBase::SeekToBegin()
180 {
181   UInt64 newPosition;
182   return Seek(0, newPosition);
183 }
184 
SeekToEnd(UInt64 & newPosition)185 bool CFileBase::SeekToEnd(UInt64 &newPosition)
186 {
187   return Seek(0, FILE_END, newPosition);
188 }
189 
190 /*
191 bool CFileBase::GetFileInformation(CByHandleFileInfo &fi) const
192 {
193   BY_HANDLE_FILE_INFORMATION wfi;
194   if (!::GetFileInformationByHandle(_handle, &wfi))
195     return false;
196   fi.Attrib = wfi.dwFileAttributes;
197   fi.CTime = wfi.ftCreationTime;
198   fi.ATime = wfi.ftLastAccessTime;
199   fi.MTime = wfi.ftLastWriteTime;
200   fi.Size = (((UInt64)wfi.nFileSizeHigh) << 32) + wfi.nFileSizeLow;
201   fi.VolumeSerialNumber = wfi.dwVolumeSerialNumber;
202   fi.NumLinks = wfi.nNumberOfLinks;
203   fi.FileIndex = (((UInt64)wfi.nFileIndexHigh) << 32) + wfi.nFileIndexLow;
204   return true;
205 }
206 */
207 
208 /////////////////////////
209 // CInFile
210 
211 #ifdef SUPPORT_DEVICE_FILE
GetDeviceLength()212 void CInFile::GetDeviceLength()
213 {
214   if (_handle != INVALID_HANDLE_VALUE && IsDeviceFile)
215   {
216     #ifdef UNDER_CE
217     LengthDefined = true;
218     Length = 128 << 20;
219 
220     #else
221     PARTITION_INFORMATION partInfo;
222     LengthDefined = true;
223     Length = 0;
224 
225     if (GetPartitionInfo(&partInfo))
226       Length = partInfo.PartitionLength.QuadPart;
227     else
228     {
229       DISK_GEOMETRY geom;
230       if (!GetGeometry(&geom))
231         if (!GetCdRomGeometry(&geom))
232           LengthDefined = false;
233       if (LengthDefined)
234         Length = geom.Cylinders.QuadPart * geom.TracksPerCylinder * geom.SectorsPerTrack * geom.BytesPerSector;
235     }
236     // SeekToBegin();
237     #endif
238   }
239 }
240 
241 // ((desiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)) == 0 &&
242 
243 #define MY_DEVICE_EXTRA_CODE \
244   IsDeviceFile = IsDeviceName(fileName); \
245   GetDeviceLength();
246 #else
247 #define MY_DEVICE_EXTRA_CODE
248 #endif
249 
Open(CFSTR fileName,DWORD shareMode,DWORD creationDisposition,DWORD flagsAndAttributes)250 bool CInFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
251 {
252   bool res = Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes);
253   MY_DEVICE_EXTRA_CODE
254   return res;
255 }
256 
OpenShared(CFSTR fileName,bool shareForWrite)257 bool CInFile::OpenShared(CFSTR fileName, bool shareForWrite)
258 { return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); }
259 
Open(CFSTR fileName)260 bool CInFile::Open(CFSTR fileName)
261   { return OpenShared(fileName, false); }
262 
263 // ReadFile and WriteFile functions in Windows have BUG:
264 // If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1)
265 // from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES
266 // (Insufficient system resources exist to complete the requested service).
267 
268 // Probably in some version of Windows there are problems with other sizes:
269 // for 32 MB (maybe also for 16 MB).
270 // And message can be "Network connection was lost"
271 
272 static UInt32 kChunkSizeMax = (1 << 22);
273 
Read1(void * data,UInt32 size,UInt32 & processedSize)274 bool CInFile::Read1(void *data, UInt32 size, UInt32 &processedSize)
275 {
276   DWORD processedLoc = 0;
277   bool res = BOOLToBool(::ReadFile(_handle, data, size, &processedLoc, NULL));
278   processedSize = (UInt32)processedLoc;
279   return res;
280 }
281 
ReadPart(void * data,UInt32 size,UInt32 & processedSize)282 bool CInFile::ReadPart(void *data, UInt32 size, UInt32 &processedSize)
283 {
284   if (size > kChunkSizeMax)
285     size = kChunkSizeMax;
286   return Read1(data, size, processedSize);
287 }
288 
Read(void * data,UInt32 size,UInt32 & processedSize)289 bool CInFile::Read(void *data, UInt32 size, UInt32 &processedSize)
290 {
291   processedSize = 0;
292   do
293   {
294     UInt32 processedLoc = 0;
295     bool res = ReadPart(data, size, processedLoc);
296     processedSize += processedLoc;
297     if (!res)
298       return false;
299     if (processedLoc == 0)
300       return true;
301     data = (void *)((unsigned char *)data + processedLoc);
302     size -= processedLoc;
303   }
304   while (size > 0);
305   return true;
306 }
307 
308 /////////////////////////
309 // COutFile
310 
GetCreationDisposition(bool createAlways)311 static inline DWORD GetCreationDisposition(bool createAlways)
312   { return createAlways? CREATE_ALWAYS: CREATE_NEW; }
313 
Open(CFSTR fileName,DWORD shareMode,DWORD creationDisposition,DWORD flagsAndAttributes)314 bool COutFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
315   { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); }
316 
Open(CFSTR fileName,DWORD creationDisposition)317 bool COutFile::Open(CFSTR fileName, DWORD creationDisposition)
318   { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); }
319 
Create(CFSTR fileName,bool createAlways)320 bool COutFile::Create(CFSTR fileName, bool createAlways)
321   { return Open(fileName, GetCreationDisposition(createAlways)); }
322 
SetTime(const FILETIME * cTime,const FILETIME * aTime,const FILETIME * mTime)323 bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime)
324   { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); }
325 
SetMTime(const FILETIME * mTime)326 bool COutFile::SetMTime(const FILETIME *mTime) {  return SetTime(NULL, NULL, mTime); }
327 
WritePart(const void * data,UInt32 size,UInt32 & processedSize)328 bool COutFile::WritePart(const void *data, UInt32 size, UInt32 &processedSize)
329 {
330   if (size > kChunkSizeMax)
331     size = kChunkSizeMax;
332   DWORD processedLoc = 0;
333   bool res = BOOLToBool(::WriteFile(_handle, data, size, &processedLoc, NULL));
334   processedSize = (UInt32)processedLoc;
335   return res;
336 }
337 
Write(const void * data,UInt32 size,UInt32 & processedSize)338 bool COutFile::Write(const void *data, UInt32 size, UInt32 &processedSize)
339 {
340   processedSize = 0;
341   do
342   {
343     UInt32 processedLoc = 0;
344     bool res = WritePart(data, size, processedLoc);
345     processedSize += processedLoc;
346     if (!res)
347       return false;
348     if (processedLoc == 0)
349       return true;
350     data = (const void *)((const unsigned char *)data + processedLoc);
351     size -= processedLoc;
352   }
353   while (size > 0);
354   return true;
355 }
356 
SetEndOfFile()357 bool COutFile::SetEndOfFile() { return BOOLToBool(::SetEndOfFile(_handle)); }
358 
SetLength(UInt64 length)359 bool COutFile::SetLength(UInt64 length)
360 {
361   UInt64 newPosition;
362   if (!Seek(length, newPosition))
363     return false;
364   if (newPosition != length)
365     return false;
366   return SetEndOfFile();
367 }
368 
369 }}}
370