1 // Windows/FileDir.cpp
2 
3 #include "StdAfx.h"
4 
5 #ifndef _UNICODE
6 #include "../Common/StringConvert.h"
7 #endif
8 
9 #include "FileDir.h"
10 #include "FileFind.h"
11 #include "FileName.h"
12 
13 #ifndef _UNICODE
14 extern bool g_IsNT;
15 #endif
16 
17 using namespace NWindows;
18 using namespace NFile;
19 using namespace NName;
20 
21 namespace NWindows {
22 namespace NFile {
23 namespace NDir {
24 
25 #ifndef UNDER_CE
26 
GetWindowsDir(FString & path)27 bool GetWindowsDir(FString &path)
28 {
29   UINT needLength;
30   #ifndef _UNICODE
31   if (!g_IsNT)
32   {
33     TCHAR s[MAX_PATH + 2];
34     s[0] = 0;
35     needLength = ::GetWindowsDirectory(s, MAX_PATH + 1);
36     path = fas2fs(s);
37   }
38   else
39   #endif
40   {
41     WCHAR s[MAX_PATH + 2];
42     s[0] = 0;
43     needLength = ::GetWindowsDirectoryW(s, MAX_PATH + 1);
44     path = us2fs(s);
45   }
46   return (needLength > 0 && needLength <= MAX_PATH);
47 }
48 
GetSystemDir(FString & path)49 bool GetSystemDir(FString &path)
50 {
51   UINT needLength;
52   #ifndef _UNICODE
53   if (!g_IsNT)
54   {
55     TCHAR s[MAX_PATH + 2];
56     s[0] = 0;
57     needLength = ::GetSystemDirectory(s, MAX_PATH + 1);
58     path = fas2fs(s);
59   }
60   else
61   #endif
62   {
63     WCHAR s[MAX_PATH + 2];
64     s[0] = 0;
65     needLength = ::GetSystemDirectoryW(s, MAX_PATH + 1);
66     path = us2fs(s);
67   }
68   return (needLength > 0 && needLength <= MAX_PATH);
69 }
70 #endif
71 
SetDirTime(CFSTR path,const FILETIME * cTime,const FILETIME * aTime,const FILETIME * mTime)72 bool SetDirTime(CFSTR path, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime)
73 {
74   #ifndef _UNICODE
75   if (!g_IsNT)
76   {
77     ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
78     return false;
79   }
80   #endif
81 
82   HANDLE hDir = INVALID_HANDLE_VALUE;
83   IF_USE_MAIN_PATH
84     hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
85         NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
86   #ifdef WIN_LONG_PATH
87   if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH)
88   {
89     UString longPath;
90     if (GetSuperPath(path, longPath, USE_MAIN_PATH))
91       hDir = ::CreateFileW(longPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
92           NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
93   }
94   #endif
95 
96   bool res = false;
97   if (hDir != INVALID_HANDLE_VALUE)
98   {
99     res = BOOLToBool(::SetFileTime(hDir, cTime, aTime, mTime));
100     ::CloseHandle(hDir);
101   }
102   return res;
103 }
104 
SetFileAttrib(CFSTR path,DWORD attrib)105 bool SetFileAttrib(CFSTR path, DWORD attrib)
106 {
107   #ifndef _UNICODE
108   if (!g_IsNT)
109   {
110     if (::SetFileAttributes(fs2fas(path), attrib))
111       return true;
112   }
113   else
114   #endif
115   {
116     IF_USE_MAIN_PATH
117       if (::SetFileAttributesW(fs2us(path), attrib))
118         return true;
119     #ifdef WIN_LONG_PATH
120     if (USE_SUPER_PATH)
121     {
122       UString longPath;
123       if (GetSuperPath(path, longPath, USE_MAIN_PATH))
124         return BOOLToBool(::SetFileAttributesW(longPath, attrib));
125     }
126     #endif
127   }
128   return false;
129 }
130 
RemoveDir(CFSTR path)131 bool RemoveDir(CFSTR path)
132 {
133   #ifndef _UNICODE
134   if (!g_IsNT)
135   {
136     if (::RemoveDirectory(fs2fas(path)))
137       return true;
138   }
139   else
140   #endif
141   {
142     IF_USE_MAIN_PATH
143       if (::RemoveDirectoryW(fs2us(path)))
144         return true;
145     #ifdef WIN_LONG_PATH
146     if (USE_SUPER_PATH)
147     {
148       UString longPath;
149       if (GetSuperPath(path, longPath, USE_MAIN_PATH))
150         return BOOLToBool(::RemoveDirectoryW(longPath));
151     }
152     #endif
153   }
154   return false;
155 }
156 
MyMoveFile(CFSTR oldFile,CFSTR newFile)157 bool MyMoveFile(CFSTR oldFile, CFSTR newFile)
158 {
159   #ifndef _UNICODE
160   if (!g_IsNT)
161   {
162     if (::MoveFile(fs2fas(oldFile), fs2fas(newFile)))
163       return true;
164   }
165   else
166   #endif
167   {
168     IF_USE_MAIN_PATH_2(oldFile, newFile)
169       if (::MoveFileW(fs2us(oldFile), fs2us(newFile)))
170         return true;
171     #ifdef WIN_LONG_PATH
172     if (USE_SUPER_PATH_2)
173     {
174       UString d1, d2;
175       if (GetSuperPaths(oldFile, newFile, d1, d2, USE_MAIN_PATH_2))
176         return BOOLToBool(::MoveFileW(d1, d2));
177     }
178     #endif
179   }
180   return false;
181 }
182 
183 #ifndef UNDER_CE
184 
185 EXTERN_C_BEGIN
186 typedef BOOL (WINAPI *Func_CreateHardLinkW)(
187     LPCWSTR lpFileName,
188     LPCWSTR lpExistingFileName,
189     LPSECURITY_ATTRIBUTES lpSecurityAttributes
190     );
191 EXTERN_C_END
192 
MyCreateHardLink(CFSTR newFileName,CFSTR existFileName)193 bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName)
194 {
195   #ifndef _UNICODE
196   if (!g_IsNT)
197   {
198     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
199     return false;
200     /*
201     if (::CreateHardLink(fs2fas(newFileName), fs2fas(existFileName), NULL))
202       return true;
203     */
204   }
205   else
206   #endif
207   {
208     Func_CreateHardLinkW my_CreateHardLinkW = (Func_CreateHardLinkW)
209         ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW");
210     if (!my_CreateHardLinkW)
211       return false;
212     IF_USE_MAIN_PATH_2(newFileName, existFileName)
213       if (my_CreateHardLinkW(fs2us(newFileName), fs2us(existFileName), NULL))
214         return true;
215     #ifdef WIN_LONG_PATH
216     if (USE_SUPER_PATH_2)
217     {
218       UString d1, d2;
219       if (GetSuperPaths(newFileName, existFileName, d1, d2, USE_MAIN_PATH_2))
220         return BOOLToBool(my_CreateHardLinkW(d1, d2, NULL));
221     }
222     #endif
223   }
224   return false;
225 }
226 
227 #endif
228 
CreateDir(CFSTR path)229 bool CreateDir(CFSTR path)
230 {
231   #ifndef _UNICODE
232   if (!g_IsNT)
233   {
234     if (::CreateDirectory(fs2fas(path), NULL))
235       return true;
236   }
237   else
238   #endif
239   {
240     IF_USE_MAIN_PATH
241       if (::CreateDirectoryW(fs2us(path), NULL))
242         return true;
243     #ifdef WIN_LONG_PATH
244     if ((!USE_MAIN_PATH || ::GetLastError() != ERROR_ALREADY_EXISTS) && USE_SUPER_PATH)
245     {
246       UString longPath;
247       if (GetSuperPath(path, longPath, USE_MAIN_PATH))
248         return BOOLToBool(::CreateDirectoryW(longPath, NULL));
249     }
250     #endif
251   }
252   return false;
253 }
254 
CreateComplexDir(CFSTR _aPathName)255 bool CreateComplexDir(CFSTR _aPathName)
256 {
257   FString pathName = _aPathName;
258   int pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR);
259   if (pos > 0 && (unsigned)pos == pathName.Len() - 1)
260   {
261     if (pathName.Len() == 3 && pathName[1] == L':')
262       return true; // Disk folder;
263     pathName.Delete(pos);
264   }
265   const FString pathName2 = pathName;
266   pos = pathName.Len();
267 
268   for (;;)
269   {
270     if (CreateDir(pathName))
271       break;
272     if (::GetLastError() == ERROR_ALREADY_EXISTS)
273     {
274       NFind::CFileInfo fileInfo;
275       if (!fileInfo.Find(pathName)) // For network folders
276         return true;
277       if (!fileInfo.IsDir())
278         return false;
279       break;
280     }
281     pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR);
282     if (pos < 0 || pos == 0)
283       return false;
284     if (pathName[pos - 1] == L':')
285       return false;
286     pathName.DeleteFrom(pos);
287   }
288 
289   while (pos < (int)pathName2.Len())
290   {
291     pos = pathName2.Find(FCHAR_PATH_SEPARATOR, pos + 1);
292     if (pos < 0)
293       pos = pathName2.Len();
294     pathName.SetFrom(pathName2, pos);
295     if (!CreateDir(pathName))
296       return false;
297   }
298 
299   return true;
300 }
301 
DeleteFileAlways(CFSTR path)302 bool DeleteFileAlways(CFSTR path)
303 {
304   if (!SetFileAttrib(path, 0))
305     return false;
306   #ifndef _UNICODE
307   if (!g_IsNT)
308   {
309     if (::DeleteFile(fs2fas(path)))
310       return true;
311   }
312   else
313   #endif
314   {
315     IF_USE_MAIN_PATH
316       if (::DeleteFileW(fs2us(path)))
317         return true;
318     #ifdef WIN_LONG_PATH
319     if (USE_SUPER_PATH)
320     {
321       UString longPath;
322       if (GetSuperPath(path, longPath, USE_MAIN_PATH))
323         return BOOLToBool(::DeleteFileW(longPath));
324     }
325     #endif
326   }
327   return false;
328 }
329 
RemoveDirWithSubItems(const FString & path)330 bool RemoveDirWithSubItems(const FString &path)
331 {
332   bool needRemoveSubItems = true;
333   {
334     NFind::CFileInfo fi;
335     if (!fi.Find(path))
336       return false;
337     if (!fi.IsDir())
338     {
339       ::SetLastError(ERROR_DIRECTORY);
340       return false;
341     }
342     if (fi.HasReparsePoint())
343       needRemoveSubItems = false;
344   }
345 
346   if (needRemoveSubItems)
347   {
348     FString s = path;
349     s += FCHAR_PATH_SEPARATOR;
350     unsigned prefixSize = s.Len();
351     s += FCHAR_ANY_MASK;
352     NFind::CEnumerator enumerator(s);
353     NFind::CFileInfo fi;
354     while (enumerator.Next(fi))
355     {
356       s.DeleteFrom(prefixSize);
357       s += fi.Name;
358       if (fi.IsDir())
359       {
360         if (!RemoveDirWithSubItems(s))
361           return false;
362       }
363       else if (!DeleteFileAlways(s))
364         return false;
365     }
366   }
367 
368   if (!SetFileAttrib(path, 0))
369     return false;
370   return RemoveDir(path);
371 }
372 
373 #ifdef UNDER_CE
374 
MyGetFullPathName(CFSTR path,FString & resFullPath)375 bool MyGetFullPathName(CFSTR path, FString &resFullPath)
376 {
377   resFullPath = path;
378   return true;
379 }
380 
381 #else
382 
MyGetFullPathName(CFSTR path,FString & resFullPath)383 bool MyGetFullPathName(CFSTR path, FString &resFullPath)
384 {
385   return GetFullPath(path, resFullPath);
386 }
387 
SetCurrentDir(CFSTR path)388 bool SetCurrentDir(CFSTR path)
389 {
390   // SetCurrentDirectory doesn't support \\?\ prefix
391   #ifndef _UNICODE
392   if (!g_IsNT)
393   {
394     return BOOLToBool(::SetCurrentDirectory(fs2fas(path)));
395   }
396   else
397   #endif
398   {
399     return BOOLToBool(::SetCurrentDirectoryW(fs2us(path)));
400   }
401 }
402 
GetCurrentDir(FString & path)403 bool GetCurrentDir(FString &path)
404 {
405   path.Empty();
406   DWORD needLength;
407   #ifndef _UNICODE
408   if (!g_IsNT)
409   {
410     TCHAR s[MAX_PATH + 2];
411     s[0] = 0;
412     needLength = ::GetCurrentDirectory(MAX_PATH + 1, s);
413     path = fas2fs(s);
414   }
415   else
416   #endif
417   {
418     WCHAR s[MAX_PATH + 2];
419     s[0] = 0;
420     needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, s);
421     path = us2fs(s);
422   }
423   return (needLength > 0 && needLength <= MAX_PATH);
424 }
425 
426 #endif
427 
GetFullPathAndSplit(CFSTR path,FString & resDirPrefix,FString & resFileName)428 bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName)
429 {
430   bool res = MyGetFullPathName(path, resDirPrefix);
431   if (!res)
432     resDirPrefix = path;
433   int pos = resDirPrefix.ReverseFind(FCHAR_PATH_SEPARATOR);
434   resFileName = resDirPrefix.Ptr(pos + 1);
435   resDirPrefix.DeleteFrom(pos + 1);
436   return res;
437 }
438 
GetOnlyDirPrefix(CFSTR path,FString & resDirPrefix)439 bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix)
440 {
441   FString resFileName;
442   return GetFullPathAndSplit(path, resDirPrefix, resFileName);
443 }
444 
MyGetTempPath(FString & path)445 bool MyGetTempPath(FString &path)
446 {
447   path.Empty();
448   DWORD needLength;
449   #ifndef _UNICODE
450   if (!g_IsNT)
451   {
452     TCHAR s[MAX_PATH + 2];
453     s[0] = 0;
454     needLength = ::GetTempPath(MAX_PATH + 1, s);
455     path = fas2fs(s);
456   }
457   else
458   #endif
459   {
460     WCHAR s[MAX_PATH + 2];
461     s[0] = 0;
462     needLength = ::GetTempPathW(MAX_PATH + 1, s);;
463     path = us2fs(s);
464   }
465   return (needLength > 0 && needLength <= MAX_PATH);
466 }
467 
CreateTempFile(CFSTR prefix,bool addRandom,FString & path,NIO::COutFile * outFile)468 static bool CreateTempFile(CFSTR prefix, bool addRandom, FString &path, NIO::COutFile *outFile)
469 {
470   UInt32 d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
471   for (unsigned i = 0; i < 100; i++)
472   {
473     path = prefix;
474     if (addRandom)
475     {
476       FChar s[16];
477       UInt32 value = d;
478       unsigned k;
479       for (k = 0; k < 8; k++)
480       {
481         unsigned t = value & 0xF;
482         value >>= 4;
483         s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
484       }
485       s[k] = '\0';
486       if (outFile)
487         path += FChar('.');
488       path += s;
489       UInt32 step = GetTickCount() + 2;
490       if (step == 0)
491         step = 1;
492       d += step;
493     }
494     addRandom = true;
495     if (outFile)
496       path += FTEXT(".tmp");
497     if (NFind::DoesFileOrDirExist(path))
498     {
499       SetLastError(ERROR_ALREADY_EXISTS);
500       continue;
501     }
502     if (outFile)
503     {
504       if (outFile->Create(path, false))
505         return true;
506     }
507     else
508     {
509       if (CreateDir(path))
510         return true;
511     }
512     DWORD error = GetLastError();
513     if (error != ERROR_FILE_EXISTS &&
514         error != ERROR_ALREADY_EXISTS)
515       break;
516   }
517   path.Empty();
518   return false;
519 }
520 
Create(CFSTR prefix,NIO::COutFile * outFile)521 bool CTempFile::Create(CFSTR prefix, NIO::COutFile *outFile)
522 {
523   if (!Remove())
524     return false;
525   if (!CreateTempFile(prefix, false, _path, outFile))
526     return false;
527   _mustBeDeleted = true;
528   return true;
529 }
530 
CreateRandomInTempFolder(CFSTR namePrefix,NIO::COutFile * outFile)531 bool CTempFile::CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile)
532 {
533   if (!Remove())
534     return false;
535   FString tempPath;
536   if (!MyGetTempPath(tempPath))
537     return false;
538   if (!CreateTempFile(tempPath + namePrefix, true, _path, outFile))
539     return false;
540   _mustBeDeleted = true;
541   return true;
542 }
543 
Remove()544 bool CTempFile::Remove()
545 {
546   if (!_mustBeDeleted)
547     return true;
548   _mustBeDeleted = !DeleteFileAlways(_path);
549   return !_mustBeDeleted;
550 }
551 
MoveTo(CFSTR name,bool deleteDestBefore)552 bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore)
553 {
554   if (deleteDestBefore)
555     if (NFind::DoesFileExist(name))
556       if (!DeleteFileAlways(name))
557         return false;
558   DisableDeleting();
559   return MyMoveFile(_path, name);
560 }
561 
Create(CFSTR prefix)562 bool CTempDir::Create(CFSTR prefix)
563 {
564   if (!Remove())
565     return false;
566   FString tempPath;
567   if (!MyGetTempPath(tempPath))
568     return false;
569   if (!CreateTempFile(tempPath + prefix, true, _path, NULL))
570     return false;
571   _mustBeDeleted = true;
572   return true;
573 }
574 
Remove()575 bool CTempDir::Remove()
576 {
577   if (!_mustBeDeleted)
578     return true;
579   _mustBeDeleted = !RemoveDirWithSubItems(_path);
580   return !_mustBeDeleted;
581 }
582 
583 }}}
584