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