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