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
14extern bool g_IsNT;
15#endif
16
17using namespace NWindows;
18using namespace NFile;
19using namespace NName;
20
21namespace NWindows {
22namespace NFile {
23namespace NDir {
24
25#ifndef UNDER_CE
26
27bool 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
49bool 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
72bool 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
105bool 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
131bool 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
157bool 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
185EXTERN_C_BEGIN
186typedef BOOL (WINAPI *Func_CreateHardLinkW)(
187    LPCWSTR lpFileName,
188    LPCWSTR lpExistingFileName,
189    LPSECURITY_ATTRIBUTES lpSecurityAttributes
190    );
191EXTERN_C_END
192
193bool 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
229bool 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
255bool 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
302bool 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
330bool 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
375bool MyGetFullPathName(CFSTR path, FString &resFullPath)
376{
377  resFullPath = path;
378  return true;
379}
380
381#else
382
383bool MyGetFullPathName(CFSTR path, FString &resFullPath)
384{
385  return GetFullPath(path, resFullPath);
386}
387
388bool 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
403bool 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
428bool 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
439bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix)
440{
441  FString resFileName;
442  return GetFullPathAndSplit(path, resDirPrefix, resFileName);
443}
444
445bool 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
468static 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
521bool 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
531bool 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
544bool CTempFile::Remove()
545{
546  if (!_mustBeDeleted)
547    return true;
548  _mustBeDeleted = !DeleteFileAlways(_path);
549  return !_mustBeDeleted;
550}
551
552bool 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
562bool 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
575bool CTempDir::Remove()
576{
577  if (!_mustBeDeleted)
578    return true;
579  _mustBeDeleted = !RemoveDirWithSubItems(_path);
580  return !_mustBeDeleted;
581}
582
583}}}
584