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