• 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     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 
58 namespace NWindows {
59 namespace NFile {
60 namespace NDir {
61 
62 #ifdef _WIN32
63 
64 #ifndef UNDER_CE
65 
GetWindowsDir(FString & path)66 bool 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 /*
91 new DOCs for GetSystemDirectory:
92   returned path does not end with a backslash unless the
93   system directory is the root directory.
94 */
95 
GetSystemDir(FString & path)96 bool 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 
SetDirTime(CFSTR path,const CFiTime * cTime,const CFiTime * aTime,const CFiTime * mTime)121 bool 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 
SetFileAttrib(CFSTR path,DWORD attrib)156 bool 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 
SetFileAttrib_PosixHighDetect(CFSTR path,DWORD attrib)183 bool 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 
RemoveDir(CFSTR path)193 bool 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 
MyMoveFile(CFSTR oldFile,CFSTR newFile)220 bool 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
249 EXTERN_C_BEGIN
250 typedef BOOL (WINAPI *Func_CreateHardLinkW)(
251     LPCWSTR lpFileName,
252     LPCWSTR lpExistingFileName,
253     LPSECURITY_ATTRIBUTES lpSecurityAttributes
254     );
255 EXTERN_C_END
256 #endif // UNDER_CE
257 
MyCreateHardLink(CFSTR newFileName,CFSTR existFileName)258 bool 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 /*
299 WinXP-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 
CreateDir(CFSTR path)321 bool 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 
CreateDir2(CFSTR path)356 static 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 
398 static bool CreateDir2(CFSTR path);
399 
CreateComplexDir(CFSTR _path)400 bool 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 
DeleteFileAlways(CFSTR path)482 bool 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 
RemoveDirWithSubItems(const FString & path)525 bool 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 
MyGetFullPathName(CFSTR path,FString & resFullPath)586 bool MyGetFullPathName(CFSTR path, FString &resFullPath)
587 {
588   resFullPath = path;
589   return true;
590 }
591 
592 #else
593 
MyGetFullPathName(CFSTR path,FString & resFullPath)594 bool 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 
SetCurrentDir(CFSTR path)605 bool 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 /*
621 we use system function GetCurrentDirectory()
622 new 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 
631 GetCurrentDir() calls GetCurrentDirectory().
632 GetCurrentDirectory() 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 
GetCurrentDir(FString & path)638 bool 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 
GetFullPathAndSplit(CFSTR path,FString & resDirPrefix,FString & resFileName)694 bool 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 
GetOnlyDirPrefix(CFSTR path,FString & resDirPrefix)706 bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix)
707 {
708   FString resFileName;
709   return GetFullPathAndSplit(path, resDirPrefix, resFileName);
710 }
711 
712 
713 
MyGetTempPath(FString & path)714 bool 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 
CreateTempFile2(CFSTR prefix,bool addRandom,AString & postfix,NIO::COutFile * outFile)764 bool 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 
Create(CFSTR prefix,NIO::COutFile * outFile)825 bool 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 
CreateRandomInTempFolder(CFSTR namePrefix,NIO::COutFile * outFile)839 bool 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 
Remove()857 bool CTempFile::Remove()
858 {
859   if (!_mustBeDeleted)
860     return true;
861   _mustBeDeleted = !DeleteFileAlways(_path);
862   return !_mustBeDeleted;
863 }
864 
MoveTo(CFSTR name,bool deleteDestBefore)865 bool 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
Create(CFSTR prefix)891 bool 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 
Remove()909 bool 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 
RemoveDir(CFSTR path)922 bool RemoveDir(CFSTR path)
923 {
924   return (rmdir(path) == 0);
925 }
926 
927 
My_CopyFile(CFSTR oldFile,CFSTR newFile)928 static 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 
MyMoveFile(CFSTR oldFile,CFSTR newFile)955 bool 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 
CreateDir(CFSTR path)978 bool CreateDir(CFSTR path)
979 {
980   return (mkdir(path, 0777) == 0); // change it
981 }
982 
CreateDir2(CFSTR path)983 static bool CreateDir2(CFSTR path)
984 {
985   return (mkdir(path, 0777) == 0); // change it
986 }
987 
988 
DeleteFileAlways(CFSTR path)989 bool DeleteFileAlways(CFSTR path)
990 {
991   return (remove(path) == 0);
992 }
993 
SetCurrentDir(CFSTR path)994 bool SetCurrentDir(CFSTR path)
995 {
996   return (chdir(path) == 0);
997 }
998 
999 
GetCurrentDir(FString & path)1000 bool 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 
SetDirTime(CFSTR path,const CFiTime * cTime,const CFiTime * aTime,const CFiTime * mTime)1045 bool 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 
1110 struct C_umask
1111 {
1112   mode_t mask;
1113 
C_umaskNWindows::NFile::NDir::C_umask1114   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 
1127 static 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 
my_chown(CFSTR path,uid_t owner,gid_t group)1138 int my_chown(CFSTR path, uid_t owner, gid_t group)
1139 {
1140   return chown(path, owner, group);
1141 }
1142 
SetFileAttrib_PosixHighDetect(CFSTR path,DWORD attrib)1143 bool 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 
MyCreateHardLink(CFSTR newFileName,CFSTR existFileName)1219 bool 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