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