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