xref: /third_party/lzma/CPP/Windows/FileDir.cpp (revision 370b324c)
1// Windows/FileDir.cpp
2
3#include "StdAfx.h"
4
5
6#ifndef _WIN32
7#include <stdio.h>
8#include <stdlib.h>
9#include <errno.h>
10#include <limits.h>
11#include <unistd.h>
12#include <time.h>
13#include <utime.h>
14#include <fcntl.h>
15#include <sys/stat.h>
16#include <sys/types.h>
17
18#include "../Common/StringConvert.h"
19#include "../Common/C_FileIO.h"
20#endif
21
22#include "FileDir.h"
23#include "FileFind.h"
24#include "FileName.h"
25
26#ifndef _UNICODE
27extern bool g_IsNT;
28#endif
29
30using namespace NWindows;
31using namespace NFile;
32using namespace NName;
33
34#ifndef _WIN32
35
36static bool FiTime_To_timespec(const CFiTime *ft, timespec &ts)
37{
38  if (ft)
39  {
40    ts = *ft;
41    return true;
42  }
43  // else
44  {
45    ts.tv_sec = 0;
46    ts.tv_nsec =
47    #ifdef UTIME_OMIT
48      UTIME_OMIT; // -2 keep old timesptamp
49    #else
50      // UTIME_NOW; -1 // set to the current time
51      0;
52    #endif
53    return false;
54  }
55}
56#endif
57
58namespace NWindows {
59namespace NFile {
60namespace NDir {
61
62#ifdef _WIN32
63
64#ifndef UNDER_CE
65
66bool GetWindowsDir(FString &path)
67{
68  const unsigned kBufSize = MAX_PATH + 16;
69  UINT len;
70  #ifndef _UNICODE
71  if (!g_IsNT)
72  {
73    TCHAR s[kBufSize + 1];
74    s[0] = 0;
75    len = ::GetWindowsDirectory(s, kBufSize);
76    path = fas2fs(s);
77  }
78  else
79  #endif
80  {
81    WCHAR s[kBufSize + 1];
82    s[0] = 0;
83    len = ::GetWindowsDirectoryW(s, kBufSize);
84    path = us2fs(s);
85  }
86  return (len != 0 && len < kBufSize);
87}
88
89
90/*
91new DOCs for GetSystemDirectory:
92  returned path does not end with a backslash unless the
93  system directory is the root directory.
94*/
95
96bool GetSystemDir(FString &path)
97{
98  const unsigned kBufSize = MAX_PATH + 16;
99  UINT len;
100  #ifndef _UNICODE
101  if (!g_IsNT)
102  {
103    TCHAR s[kBufSize + 1];
104    s[0] = 0;
105    len = ::GetSystemDirectory(s, kBufSize);
106    path = fas2fs(s);
107  }
108  else
109  #endif
110  {
111    WCHAR s[kBufSize + 1];
112    s[0] = 0;
113    len = ::GetSystemDirectoryW(s, kBufSize);
114    path = us2fs(s);
115  }
116  return (len != 0 && len < kBufSize);
117}
118#endif // UNDER_CE
119
120
121bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
122{
123  #ifndef _UNICODE
124  if (!g_IsNT)
125  {
126    ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
127    return false;
128  }
129  #endif
130
131  HANDLE hDir = INVALID_HANDLE_VALUE;
132  IF_USE_MAIN_PATH
133    hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
134        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
135  #ifdef Z7_LONG_PATH
136  if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH)
137  {
138    UString superPath;
139    if (GetSuperPath(path, superPath, USE_MAIN_PATH))
140      hDir = ::CreateFileW(superPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
141          NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
142  }
143  #endif
144
145  bool res = false;
146  if (hDir != INVALID_HANDLE_VALUE)
147  {
148    res = BOOLToBool(::SetFileTime(hDir, cTime, aTime, mTime));
149    ::CloseHandle(hDir);
150  }
151  return res;
152}
153
154
155
156bool SetFileAttrib(CFSTR path, DWORD attrib)
157{
158  #ifndef _UNICODE
159  if (!g_IsNT)
160  {
161    if (::SetFileAttributes(fs2fas(path), attrib))
162      return true;
163  }
164  else
165  #endif
166  {
167    IF_USE_MAIN_PATH
168      if (::SetFileAttributesW(fs2us(path), attrib))
169        return true;
170    #ifdef Z7_LONG_PATH
171    if (USE_SUPER_PATH)
172    {
173      UString superPath;
174      if (GetSuperPath(path, superPath, USE_MAIN_PATH))
175        return BOOLToBool(::SetFileAttributesW(superPath, attrib));
176    }
177    #endif
178  }
179  return false;
180}
181
182
183bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib)
184{
185  #ifdef _WIN32
186  if ((attrib & 0xF0000000) != 0)
187    attrib &= 0x3FFF;
188  #endif
189  return SetFileAttrib(path, attrib);
190}
191
192
193bool RemoveDir(CFSTR path)
194{
195  #ifndef _UNICODE
196  if (!g_IsNT)
197  {
198    if (::RemoveDirectory(fs2fas(path)))
199      return true;
200  }
201  else
202  #endif
203  {
204    IF_USE_MAIN_PATH
205      if (::RemoveDirectoryW(fs2us(path)))
206        return true;
207    #ifdef Z7_LONG_PATH
208    if (USE_SUPER_PATH)
209    {
210      UString superPath;
211      if (GetSuperPath(path, superPath, USE_MAIN_PATH))
212        return BOOLToBool(::RemoveDirectoryW(superPath));
213    }
214    #endif
215  }
216  return false;
217}
218
219
220bool MyMoveFile(CFSTR oldFile, CFSTR newFile)
221{
222  #ifndef _UNICODE
223  if (!g_IsNT)
224  {
225    if (::MoveFile(fs2fas(oldFile), fs2fas(newFile)))
226      return true;
227  }
228  else
229  #endif
230  {
231    IF_USE_MAIN_PATH_2(oldFile, newFile)
232    {
233      if (::MoveFileW(fs2us(oldFile), fs2us(newFile)))
234        return true;
235    }
236    #ifdef Z7_LONG_PATH
237    if (USE_SUPER_PATH_2)
238    {
239      UString d1, d2;
240      if (GetSuperPaths(oldFile, newFile, d1, d2, USE_MAIN_PATH_2))
241        return BOOLToBool(::MoveFileW(d1, d2));
242    }
243    #endif
244  }
245  return false;
246}
247
248#ifndef UNDER_CE
249EXTERN_C_BEGIN
250typedef BOOL (WINAPI *Func_CreateHardLinkW)(
251    LPCWSTR lpFileName,
252    LPCWSTR lpExistingFileName,
253    LPSECURITY_ATTRIBUTES lpSecurityAttributes
254    );
255EXTERN_C_END
256#endif // UNDER_CE
257
258bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName)
259{
260  #ifndef _UNICODE
261  if (!g_IsNT)
262  {
263    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
264    return false;
265    /*
266    if (::CreateHardLink(fs2fas(newFileName), fs2fas(existFileName), NULL))
267      return true;
268    */
269  }
270  else
271  #endif
272  {
273    const
274    Func_CreateHardLinkW
275      my_CreateHardLinkW = Z7_GET_PROC_ADDRESS(
276    Func_CreateHardLinkW, ::GetModuleHandleW(L"kernel32.dll"),
277        "CreateHardLinkW");
278    if (!my_CreateHardLinkW)
279      return false;
280    IF_USE_MAIN_PATH_2(newFileName, existFileName)
281    {
282      if (my_CreateHardLinkW(fs2us(newFileName), fs2us(existFileName), NULL))
283        return true;
284    }
285    #ifdef Z7_LONG_PATH
286    if (USE_SUPER_PATH_2)
287    {
288      UString d1, d2;
289      if (GetSuperPaths(newFileName, existFileName, d1, d2, USE_MAIN_PATH_2))
290        return BOOLToBool(my_CreateHardLinkW(d1, d2, NULL));
291    }
292    #endif
293  }
294  return false;
295}
296
297
298/*
299WinXP-64 CreateDir():
300  ""                  - ERROR_PATH_NOT_FOUND
301  \                   - ERROR_ACCESS_DENIED
302  C:\                 - ERROR_ACCESS_DENIED, if there is such drive,
303
304  D:\folder             - ERROR_PATH_NOT_FOUND, if there is no such drive,
305  C:\nonExistent\folder - ERROR_PATH_NOT_FOUND
306
307  C:\existFolder      - ERROR_ALREADY_EXISTS
308  C:\existFolder\     - ERROR_ALREADY_EXISTS
309
310  C:\folder   - OK
311  C:\folder\  - OK
312
313  \\Server\nonExistent    - ERROR_BAD_NETPATH
314  \\Server\Share_Readonly - ERROR_ACCESS_DENIED
315  \\Server\Share          - ERROR_ALREADY_EXISTS
316
317  \\Server\Share_NTFS_drive - ERROR_ACCESS_DENIED
318  \\Server\Share_FAT_drive  - ERROR_ALREADY_EXISTS
319*/
320
321bool CreateDir(CFSTR path)
322{
323  #ifndef _UNICODE
324  if (!g_IsNT)
325  {
326    if (::CreateDirectory(fs2fas(path), NULL))
327      return true;
328  }
329  else
330  #endif
331  {
332    IF_USE_MAIN_PATH
333      if (::CreateDirectoryW(fs2us(path), NULL))
334        return true;
335    #ifdef Z7_LONG_PATH
336    if ((!USE_MAIN_PATH || ::GetLastError() != ERROR_ALREADY_EXISTS) && USE_SUPER_PATH)
337    {
338      UString superPath;
339      if (GetSuperPath(path, superPath, USE_MAIN_PATH))
340        return BOOLToBool(::CreateDirectoryW(superPath, NULL));
341    }
342    #endif
343  }
344  return false;
345}
346
347/*
348  CreateDir2 returns true, if directory can contain files after the call (two cases):
349    1) the directory already exists
350    2) the directory was created
351  path must be WITHOUT trailing path separator.
352
353  We need CreateDir2, since fileInfo.Find() for reserved names like "com8"
354   returns FILE instead of DIRECTORY. And we need to use SuperPath */
355
356static bool CreateDir2(CFSTR path)
357{
358  #ifndef _UNICODE
359  if (!g_IsNT)
360  {
361    if (::CreateDirectory(fs2fas(path), NULL))
362      return true;
363  }
364  else
365  #endif
366  {
367    IF_USE_MAIN_PATH
368      if (::CreateDirectoryW(fs2us(path), NULL))
369        return true;
370    #ifdef Z7_LONG_PATH
371    if ((!USE_MAIN_PATH || ::GetLastError() != ERROR_ALREADY_EXISTS) && USE_SUPER_PATH)
372    {
373      UString superPath;
374      if (GetSuperPath(path, superPath, USE_MAIN_PATH))
375      {
376        if (::CreateDirectoryW(superPath, NULL))
377          return true;
378        if (::GetLastError() != ERROR_ALREADY_EXISTS)
379          return false;
380        NFind::CFileInfo fi;
381        if (!fi.Find(us2fs(superPath)))
382          return false;
383        return fi.IsDir();
384      }
385    }
386    #endif
387  }
388  if (::GetLastError() != ERROR_ALREADY_EXISTS)
389    return false;
390  NFind::CFileInfo fi;
391  if (!fi.Find(path))
392    return false;
393  return fi.IsDir();
394}
395
396#endif // _WIN32
397
398static bool CreateDir2(CFSTR path);
399
400bool CreateComplexDir(CFSTR _path)
401{
402  #ifdef _WIN32
403
404  {
405    const DWORD attrib = NFind::GetFileAttrib(_path);
406    if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0)
407      return true;
408  }
409
410  #ifndef UNDER_CE
411
412  if (IsDriveRootPath_SuperAllowed(_path))
413    return false;
414
415  const unsigned prefixSize = GetRootPrefixSize(_path);
416
417  #endif // UNDER_CE
418
419  #else // _WIN32
420
421  // Posix
422  NFind::CFileInfo fi;
423  if (fi.Find(_path))
424  {
425    if (fi.IsDir())
426      return true;
427  }
428
429  #endif // _WIN32
430
431  FString path (_path);
432
433  int pos = path.ReverseFind_PathSepar();
434  if (pos >= 0 && (unsigned)pos == path.Len() - 1)
435  {
436    if (path.Len() == 1)
437      return true;
438    path.DeleteBack();
439  }
440
441  const FString path2 (path);
442  pos = (int)path.Len();
443
444  for (;;)
445  {
446    if (CreateDir2(path))
447      break;
448    if (::GetLastError() == ERROR_ALREADY_EXISTS)
449      return false;
450    pos = path.ReverseFind_PathSepar();
451    if (pos < 0 || pos == 0)
452      return false;
453
454    #if defined(_WIN32) && !defined(UNDER_CE)
455    if (pos == 1 && IS_PATH_SEPAR(path[0]))
456      return false;
457    if (prefixSize >= (unsigned)pos + 1)
458      return false;
459    #endif
460
461    path.DeleteFrom((unsigned)pos);
462  }
463
464  while (pos < (int)path2.Len())
465  {
466    int pos2 = NName::FindSepar(path2.Ptr((unsigned)pos + 1));
467    if (pos2 < 0)
468      pos = (int)path2.Len();
469    else
470      pos += 1 + pos2;
471    path.SetFrom(path2, (unsigned)pos);
472    if (!CreateDir(path))
473      return false;
474  }
475
476  return true;
477}
478
479
480#ifdef _WIN32
481
482bool DeleteFileAlways(CFSTR path)
483{
484  /* If alt stream, we also need to clear READ-ONLY attribute of main file before delete.
485     SetFileAttrib("name:stream", ) changes attributes of main file. */
486  {
487    DWORD attrib = NFind::GetFileAttrib(path);
488    if (attrib != INVALID_FILE_ATTRIBUTES
489        && (attrib & FILE_ATTRIBUTE_DIRECTORY) == 0
490        && (attrib & FILE_ATTRIBUTE_READONLY) != 0)
491    {
492      if (!SetFileAttrib(path, attrib & ~(DWORD)FILE_ATTRIBUTE_READONLY))
493        return false;
494    }
495  }
496
497  #ifndef _UNICODE
498  if (!g_IsNT)
499  {
500    if (::DeleteFile(fs2fas(path)))
501      return true;
502  }
503  else
504  #endif
505  {
506    /* DeleteFile("name::$DATA") deletes all alt streams (same as delete DeleteFile("name")).
507       Maybe it's better to open "name::$DATA" and clear data for unnamed stream? */
508    IF_USE_MAIN_PATH
509      if (::DeleteFileW(fs2us(path)))
510        return true;
511    #ifdef Z7_LONG_PATH
512    if (USE_SUPER_PATH)
513    {
514      UString superPath;
515      if (GetSuperPath(path, superPath, USE_MAIN_PATH))
516        return BOOLToBool(::DeleteFileW(superPath));
517    }
518    #endif
519  }
520  return false;
521}
522
523
524
525bool RemoveDirWithSubItems(const FString &path)
526{
527  bool needRemoveSubItems = true;
528  {
529    NFind::CFileInfo fi;
530    if (!fi.Find(path))
531      return false;
532    if (!fi.IsDir())
533    {
534      ::SetLastError(ERROR_DIRECTORY);
535      return false;
536    }
537    if (fi.HasReparsePoint())
538      needRemoveSubItems = false;
539  }
540
541  if (needRemoveSubItems)
542  {
543    FString s (path);
544    s.Add_PathSepar();
545    const unsigned prefixSize = s.Len();
546    NFind::CEnumerator enumerator;
547    enumerator.SetDirPrefix(s);
548    NFind::CDirEntry fi;
549    bool isError = false;
550    DWORD lastError = 0;
551    while (enumerator.Next(fi))
552    {
553      s.DeleteFrom(prefixSize);
554      s += fi.Name;
555      if (fi.IsDir())
556      {
557        if (!RemoveDirWithSubItems(s))
558        {
559          lastError = GetLastError();
560          isError = true;
561        }
562      }
563      else if (!DeleteFileAlways(s))
564      {
565        lastError = GetLastError();
566        isError = false;
567      }
568    }
569    if (isError)
570    {
571      SetLastError(lastError);
572      return false;
573    }
574  }
575
576  // we clear read-only attrib to remove read-only dir
577  if (!SetFileAttrib(path, 0))
578    return false;
579  return RemoveDir(path);
580}
581
582#endif // _WIN32
583
584#ifdef UNDER_CE
585
586bool MyGetFullPathName(CFSTR path, FString &resFullPath)
587{
588  resFullPath = path;
589  return true;
590}
591
592#else
593
594bool MyGetFullPathName(CFSTR path, FString &resFullPath)
595{
596  return GetFullPath(path, resFullPath);
597}
598
599#ifdef _WIN32
600
601/* Win10: SetCurrentDirectory() doesn't support long paths and
602    doesn't support super prefix "\\?\", if long path behavior is not
603    enabled in registry (LongPathsEnabled) and in manifest (longPathAware). */
604
605bool SetCurrentDir(CFSTR path)
606{
607  #ifndef _UNICODE
608  if (!g_IsNT)
609  {
610    return BOOLToBool(::SetCurrentDirectory(fs2fas(path)));
611  }
612  else
613  #endif
614  {
615    return BOOLToBool(::SetCurrentDirectoryW(fs2us(path)));
616  }
617}
618
619
620/*
621we use system function GetCurrentDirectory()
622new GetCurrentDirectory() DOCs:
623  - If the function fails, the return value is zero.
624  - If the function succeeds, the return value specifies
625      the number of characters that are written to the buffer,
626      not including the terminating null character.
627  - If the buffer is not large enough, the return value specifies
628      the required size of the buffer, in characters,
629      including the null-terminating character.
630
631GetCurrentDir() calls GetCurrentDirectory().
632GetCurrentDirectory() in win10 in tests:
633  the returned (path) does not end with a backslash, if
634  current directory is not root directory of drive.
635  But that behavior is not guarantied in specification docs.
636*/
637
638bool GetCurrentDir(FString &path)
639{
640  const unsigned kBufSize = MAX_PATH + 16;
641  path.Empty();
642
643  #ifndef _UNICODE
644  if (!g_IsNT)
645  {
646    TCHAR s[kBufSize + 1];
647    s[0] = 0;
648    const DWORD len = ::GetCurrentDirectory(kBufSize, s);
649    if (len == 0 || len >= kBufSize)
650      return false;
651    s[kBufSize] = 0;  // optional guard
652    path = fas2fs(s);
653    return true;
654  }
655  else
656  #endif
657  {
658    DWORD len;
659    {
660      WCHAR s[kBufSize + 1];
661      s[0] = 0;
662      len = ::GetCurrentDirectoryW(kBufSize, s);
663      if (len == 0)
664        return false;
665      if (len < kBufSize)
666      {
667        s[kBufSize] = 0;  // optional guard
668        path = us2fs(s);
669        return true;
670      }
671    }
672    UString temp;
673    const DWORD len2 = ::GetCurrentDirectoryW(len, temp.GetBuf(len));
674    if (len2 == 0)
675      return false;
676    temp.ReleaseBuf_CalcLen(len);
677    if (temp.Len() != len2 || len - 1 != len2)
678    {
679      /* it's unexpected case, if current dir of process
680         was changed between two function calls,
681         or some unexpected function implementation */
682      // SetLastError((DWORD)E_FAIL);  // we can set some error code
683      return false;
684    }
685    path = us2fs(temp);
686    return true;
687  }
688}
689
690#endif // _WIN32
691#endif // UNDER_CE
692
693
694bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName)
695{
696  bool res = MyGetFullPathName(path, resDirPrefix);
697  if (!res)
698    resDirPrefix = path;
699  int pos = resDirPrefix.ReverseFind_PathSepar();
700  pos++;
701  resFileName = resDirPrefix.Ptr((unsigned)pos);
702  resDirPrefix.DeleteFrom((unsigned)pos);
703  return res;
704}
705
706bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix)
707{
708  FString resFileName;
709  return GetFullPathAndSplit(path, resDirPrefix, resFileName);
710}
711
712
713
714bool MyGetTempPath(FString &path)
715{
716  #ifdef _WIN32
717
718  /*
719  new DOCs for GetTempPathW():
720    - The returned string ends with a backslash.
721    - The maximum possible return value is MAX_PATH+1 (261).
722  */
723
724  const unsigned kBufSize = MAX_PATH + 16;
725  DWORD len;
726  #ifndef _UNICODE
727  if (!g_IsNT)
728  {
729    TCHAR s[kBufSize + 1];
730    s[0] = 0;
731    len = ::GetTempPath(kBufSize, s);
732    path = fas2fs(s);
733  }
734  else
735  #endif
736  {
737    WCHAR s[kBufSize + 1];
738    s[0] = 0;
739    len = ::GetTempPathW(kBufSize, s);
740    path = us2fs(s);
741  }
742  /* win10: GetTempPathW() doesn't set backslash at the end of path,
743       if (buffer_size == len_of(path_with_backslash)).
744     So we normalize path here: */
745  NormalizeDirPathPrefix(path);
746  return (len != 0 && len < kBufSize);
747
748  #else  // !_WIN32
749
750  // FIXME: improve that code
751  path = STRING_PATH_SEPARATOR "tmp";
752  const char *s;
753  if (NFind::DoesDirExist_FollowLink(path))
754    s = STRING_PATH_SEPARATOR "tmp" STRING_PATH_SEPARATOR;
755  else
756    s = "." STRING_PATH_SEPARATOR;
757  path = s;
758  return true;
759
760  #endif
761}
762
763
764bool CreateTempFile2(CFSTR prefix, bool addRandom, AString &postfix, NIO::COutFile *outFile)
765{
766  UInt32 d =
767    #ifdef _WIN32
768      (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
769    #else
770      (UInt32)(time(NULL) << 12) ^  ((UInt32)getppid() << 14) ^ (UInt32)(getpid());
771    #endif
772
773  for (unsigned i = 0; i < 100; i++)
774  {
775    postfix.Empty();
776    if (addRandom)
777    {
778      char s[16];
779      UInt32 val = d;
780      unsigned k;
781      for (k = 0; k < 8; k++)
782      {
783        const unsigned t = val & 0xF;
784        val >>= 4;
785        s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
786      }
787      s[k] = '\0';
788      if (outFile)
789        postfix.Add_Dot();
790      postfix += s;
791      UInt32 step = GetTickCount() + 2;
792      if (step == 0)
793        step = 1;
794      d += step;
795    }
796    addRandom = true;
797    if (outFile)
798      postfix += ".tmp";
799    FString path (prefix);
800    path += postfix;
801    if (NFind::DoesFileOrDirExist(path))
802    {
803      SetLastError(ERROR_ALREADY_EXISTS);
804      continue;
805    }
806    if (outFile)
807    {
808      if (outFile->Create(path, false))
809        return true;
810    }
811    else
812    {
813      if (CreateDir(path))
814        return true;
815    }
816    const DWORD error = GetLastError();
817    if (error != ERROR_FILE_EXISTS &&
818        error != ERROR_ALREADY_EXISTS)
819      break;
820  }
821  postfix.Empty();
822  return false;
823}
824
825bool CTempFile::Create(CFSTR prefix, NIO::COutFile *outFile)
826{
827  if (!Remove())
828    return false;
829  _path.Empty();
830  AString postfix;
831  if (!CreateTempFile2(prefix, false, postfix, outFile))
832    return false;
833  _path = prefix;
834  _path += postfix;
835  _mustBeDeleted = true;
836  return true;
837}
838
839bool CTempFile::CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile)
840{
841  if (!Remove())
842    return false;
843  _path.Empty();
844  FString tempPath;
845  if (!MyGetTempPath(tempPath))
846    return false;
847  AString postfix;
848  tempPath += namePrefix;
849  if (!CreateTempFile2(tempPath, true, postfix, outFile))
850    return false;
851  _path = tempPath;
852  _path += postfix;
853  _mustBeDeleted = true;
854  return true;
855}
856
857bool CTempFile::Remove()
858{
859  if (!_mustBeDeleted)
860    return true;
861  _mustBeDeleted = !DeleteFileAlways(_path);
862  return !_mustBeDeleted;
863}
864
865bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore)
866{
867  // DWORD attrib = 0;
868  if (deleteDestBefore)
869  {
870    if (NFind::DoesFileExist_Raw(name))
871    {
872      // attrib = NFind::GetFileAttrib(name);
873      if (!DeleteFileAlways(name))
874        return false;
875    }
876  }
877  DisableDeleting();
878  return MyMoveFile(_path, name);
879
880  /*
881  if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY))
882  {
883    DWORD attrib2 = NFind::GetFileAttrib(name);
884    if (attrib2 != INVALID_FILE_ATTRIBUTES)
885      SetFileAttrib(name, attrib2 | FILE_ATTRIBUTE_READONLY);
886  }
887  */
888}
889
890#ifdef _WIN32
891bool CTempDir::Create(CFSTR prefix)
892{
893  if (!Remove())
894    return false;
895  _path.Empty();
896  FString tempPath;
897  if (!MyGetTempPath(tempPath))
898    return false;
899  tempPath += prefix;
900  AString postfix;
901  if (!CreateTempFile2(tempPath, true, postfix, NULL))
902    return false;
903  _path = tempPath;
904  _path += postfix;
905  _mustBeDeleted = true;
906  return true;
907}
908
909bool CTempDir::Remove()
910{
911  if (!_mustBeDeleted)
912    return true;
913  _mustBeDeleted = !RemoveDirWithSubItems(_path);
914  return !_mustBeDeleted;
915}
916#endif
917
918
919
920#ifndef _WIN32
921
922bool RemoveDir(CFSTR path)
923{
924  return (rmdir(path) == 0);
925}
926
927
928static BOOL My_CopyFile(CFSTR oldFile, CFSTR newFile)
929{
930  NWindows::NFile::NIO::COutFile outFile;
931  if (!outFile.Create(newFile, false))
932    return FALSE;
933
934  NWindows::NFile::NIO::CInFile inFile;
935  if (!inFile.Open(oldFile))
936    return FALSE;
937
938  char buf[1 << 14];
939
940  for (;;)
941  {
942    const ssize_t num = inFile.read_part(buf, sizeof(buf));
943    if (num == 0)
944      return TRUE;
945    if (num < 0)
946      return FALSE;
947    size_t processed;
948    const ssize_t num2 = outFile.write_full(buf, (size_t)num, processed);
949    if (num2 != num || processed != (size_t)num)
950      return FALSE;
951  }
952}
953
954
955bool MyMoveFile(CFSTR oldFile, CFSTR newFile)
956{
957  int res = rename(oldFile, newFile);
958  if (res == 0)
959    return true;
960  if (errno != EXDEV) // (oldFile and newFile are not on the same mounted filesystem)
961    return false;
962
963  if (My_CopyFile(oldFile, newFile) == FALSE)
964    return false;
965
966  struct stat info_file;
967  res = stat(oldFile, &info_file);
968  if (res != 0)
969    return false;
970
971  /*
972  ret = chmod(dst,info_file.st_mode & g_umask.mask);
973  */
974  return (unlink(oldFile) == 0);
975}
976
977
978bool CreateDir(CFSTR path)
979{
980  return (mkdir(path, 0777) == 0); // change it
981}
982
983static bool CreateDir2(CFSTR path)
984{
985  return (mkdir(path, 0777) == 0); // change it
986}
987
988
989bool DeleteFileAlways(CFSTR path)
990{
991  return (remove(path) == 0);
992}
993
994bool SetCurrentDir(CFSTR path)
995{
996  return (chdir(path) == 0);
997}
998
999
1000bool GetCurrentDir(FString &path)
1001{
1002  path.Empty();
1003
1004  #define MY_PATH_MAX  PATH_MAX
1005  // #define MY_PATH_MAX  1024
1006
1007  char s[MY_PATH_MAX + 1];
1008  char *res = getcwd(s, MY_PATH_MAX);
1009  if (res)
1010  {
1011    path = fas2fs(s);
1012    return true;
1013  }
1014  {
1015    // if (errno != ERANGE) return false;
1016    #if defined(__GLIBC__) || defined(__APPLE__)
1017    /* As an extension to the POSIX.1-2001 standard, glibc's getcwd()
1018       allocates the buffer dynamically using malloc(3) if buf is NULL. */
1019    res = getcwd(NULL, 0);
1020    if (res)
1021    {
1022      path = fas2fs(res);
1023      ::free(res);
1024      return true;
1025    }
1026    #endif
1027    return false;
1028  }
1029}
1030
1031
1032
1033// #undef UTIME_OMIT // to debug
1034
1035#ifndef UTIME_OMIT
1036  /* we can define UTIME_OMIT for debian and another systems.
1037     Is it OK to define UTIME_OMIT to -2 here, if UTIME_OMIT is not defined? */
1038  // #define UTIME_OMIT -2
1039#endif
1040
1041
1042
1043
1044
1045bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
1046{
1047  // need testing
1048  /*
1049  struct utimbuf buf;
1050  struct stat st;
1051  UNUSED_VAR(cTime)
1052
1053  printf("\nstat = %s\n", path);
1054  int ret = stat(path, &st);
1055
1056  if (ret == 0)
1057  {
1058    buf.actime  = st.st_atime;
1059    buf.modtime = st.st_mtime;
1060  }
1061  else
1062  {
1063    time_t cur_time = time(0);
1064    buf.actime  = cur_time;
1065    buf.modtime = cur_time;
1066  }
1067
1068  if (aTime)
1069  {
1070    UInt32 ut;
1071    if (NTime::FileTimeToUnixTime(*aTime, ut))
1072      buf.actime = ut;
1073  }
1074
1075  if (mTime)
1076  {
1077    UInt32 ut;
1078    if (NTime::FileTimeToUnixTime(*mTime, ut))
1079      buf.modtime = ut;
1080  }
1081
1082  return utime(path, &buf) == 0;
1083  */
1084
1085  // if (!aTime && !mTime) return true;
1086
1087  struct timespec times[2];
1088  UNUSED_VAR(cTime)
1089
1090  bool needChange;
1091  needChange  = FiTime_To_timespec(aTime, times[0]);
1092  needChange |= FiTime_To_timespec(mTime, times[1]);
1093
1094  /*
1095  if (mTime)
1096  {
1097    printf("\n time = %ld.%9ld\n", mTime->tv_sec, mTime->tv_nsec);
1098  }
1099  */
1100
1101  if (!needChange)
1102    return true;
1103  const int flags = 0; // follow link
1104    // = AT_SYMLINK_NOFOLLOW; // don't follow link
1105  return utimensat(AT_FDCWD, path, times, flags) == 0;
1106}
1107
1108
1109
1110struct C_umask
1111{
1112  mode_t mask;
1113
1114  C_umask()
1115  {
1116    /* by security reasons we restrict attributes according
1117       with process's file mode creation mask (umask) */
1118    const mode_t um = umask(0); // octal :0022 is expected
1119    mask = 0777 & (~um);        // octal: 0755 is expected
1120    umask(um);  // restore the umask
1121    // printf("\n umask = 0%03o mask = 0%03o\n", um, mask);
1122
1123    // mask = 0777; // debug we can disable the restriction:
1124  }
1125};
1126
1127static C_umask g_umask;
1128
1129// #define PRF(x) x;
1130#define PRF(x)
1131
1132#define TRACE_SetFileAttrib(msg) \
1133  PRF(printf("\nSetFileAttrib(%s, %x) : %s\n", (const char *)path, attrib, msg);)
1134
1135#define TRACE_chmod(s, mode) \
1136  PRF(printf("\n chmod(%s, %o)\n", (const char *)path, (unsigned)(mode));)
1137
1138int my_chown(CFSTR path, uid_t owner, gid_t group)
1139{
1140  return chown(path, owner, group);
1141}
1142
1143bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib)
1144{
1145  TRACE_SetFileAttrib("")
1146
1147  struct stat st;
1148
1149  bool use_lstat = true;
1150  if (use_lstat)
1151  {
1152    if (lstat(path, &st) != 0)
1153    {
1154      TRACE_SetFileAttrib("bad lstat()")
1155      return false;
1156    }
1157    // TRACE_chmod("lstat", st.st_mode);
1158  }
1159  else
1160  {
1161    if (stat(path, &st) != 0)
1162    {
1163      TRACE_SetFileAttrib("bad stat()")
1164      return false;
1165    }
1166  }
1167
1168  if (attrib & FILE_ATTRIBUTE_UNIX_EXTENSION)
1169  {
1170    TRACE_SetFileAttrib("attrib & FILE_ATTRIBUTE_UNIX_EXTENSION")
1171    st.st_mode = attrib >> 16;
1172    if (S_ISDIR(st.st_mode))
1173    {
1174      // user/7z must be able to create files in this directory
1175      st.st_mode |= (S_IRUSR | S_IWUSR | S_IXUSR);
1176    }
1177    else if (!S_ISREG(st.st_mode))
1178      return true;
1179  }
1180  else if (S_ISLNK(st.st_mode))
1181  {
1182    /* for most systems: permissions for symlinks are fixed to rwxrwxrwx.
1183       so we don't need chmod() for symlinks. */
1184    return true;
1185    // SetLastError(ENOSYS);
1186    // return false;
1187  }
1188  else
1189  {
1190    TRACE_SetFileAttrib("Only Windows Attributes")
1191    // Only Windows Attributes
1192    if (S_ISDIR(st.st_mode)
1193        || (attrib & FILE_ATTRIBUTE_READONLY) == 0)
1194      return true;
1195    st.st_mode &= ~(mode_t)(S_IWUSR | S_IWGRP | S_IWOTH); // octal: ~0222; // disable write permissions
1196  }
1197
1198  int res;
1199  /*
1200  if (S_ISLNK(st.st_mode))
1201  {
1202    printf("\nfchmodat()\n");
1203    TRACE_chmod(path, (st.st_mode) & g_umask.mask)
1204    // AT_SYMLINK_NOFOLLOW is not implemted still in Linux.
1205    res = fchmodat(AT_FDCWD, path, (st.st_mode) & g_umask.mask,
1206        S_ISLNK(st.st_mode) ? AT_SYMLINK_NOFOLLOW : 0);
1207  }
1208  else
1209  */
1210  {
1211    TRACE_chmod(path, (st.st_mode) & g_umask.mask)
1212    res = chmod(path, (st.st_mode) & g_umask.mask);
1213  }
1214  // TRACE_SetFileAttrib("End")
1215  return (res == 0);
1216}
1217
1218
1219bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName)
1220{
1221  PRF(printf("\nhard link() %s -> %s\n", newFileName, existFileName);)
1222  return (link(existFileName, newFileName) == 0);
1223}
1224
1225#endif // !_WIN32
1226
1227// #endif
1228
1229}}}
1230