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