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