1 // Windows/FileFind.cpp
2
3 #include "StdAfx.h"
4
5 // #include <stdio.h>
6
7 #ifndef _WIN32
8 #include <fcntl.h> /* Definition of AT_* constants */
9 #include "TimeUtils.h"
10 // for major
11 // #include <sys/sysmacros.h>
12 #endif
13
14 #include "FileFind.h"
15 #include "FileIO.h"
16 #include "FileName.h"
17
18 #ifndef _UNICODE
19 extern bool g_IsNT;
20 #endif
21
22 using namespace NWindows;
23 using namespace NFile;
24 using namespace NName;
25
26 #if defined(_WIN32) && !defined(UNDER_CE)
27
28 #if !defined(Z7_WIN32_WINNT_MIN) || Z7_WIN32_WINNT_MIN < 0x0502 // Win2003
29 #define Z7_USE_DYN_FindFirstStream
30 #endif
31
32 #ifdef Z7_USE_DYN_FindFirstStream
33
34 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
35
36 EXTERN_C_BEGIN
37
38 typedef enum
39 {
40 My_FindStreamInfoStandard,
41 My_FindStreamInfoMaxInfoLevel
42 } MY_STREAM_INFO_LEVELS;
43
44 typedef struct
45 {
46 LARGE_INTEGER StreamSize;
47 WCHAR cStreamName[MAX_PATH + 36];
48 } MY_WIN32_FIND_STREAM_DATA, *MY_PWIN32_FIND_STREAM_DATA;
49
50 typedef HANDLE (WINAPI *Func_FindFirstStreamW)(LPCWSTR fileName, MY_STREAM_INFO_LEVELS infoLevel,
51 LPVOID findStreamData, DWORD flags);
52
53 typedef BOOL (APIENTRY *Func_FindNextStreamW)(HANDLE findStream, LPVOID findStreamData);
54
55 EXTERN_C_END
56
57 #else
58
59 #define MY_WIN32_FIND_STREAM_DATA WIN32_FIND_STREAM_DATA
60 #define My_FindStreamInfoStandard FindStreamInfoStandard
61
62 #endif
63 #endif // defined(_WIN32) && !defined(UNDER_CE)
64
65
66 namespace NWindows {
67 namespace NFile {
68
69
70 #ifdef _WIN32
71 #ifdef Z7_DEVICE_FILE
72 namespace NSystem
73 {
74 bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize);
75 }
76 #endif
77 #endif
78
79 namespace NFind {
80
81 /*
82 #ifdef _WIN32
83 #define MY_CLEAR_FILETIME(ft) ft.dwLowDateTime = ft.dwHighDateTime = 0;
84 #else
85 #define MY_CLEAR_FILETIME(ft) ft.tv_sec = 0; ft.tv_nsec = 0;
86 #endif
87 */
88
ClearBase()89 void CFileInfoBase::ClearBase() throw()
90 {
91 Size = 0;
92 FiTime_Clear(CTime);
93 FiTime_Clear(ATime);
94 FiTime_Clear(MTime);
95
96 #ifdef _WIN32
97 Attrib = 0;
98 // ReparseTag = 0;
99 IsAltStream = false;
100 IsDevice = false;
101 #else
102 dev = 0;
103 ino = 0;
104 mode = 0;
105 nlink = 0;
106 uid = 0;
107 gid = 0;
108 rdev = 0;
109 #endif
110 }
111
112
SetAs_StdInFile()113 bool CFileInfoBase::SetAs_StdInFile()
114 {
115 ClearBase();
116 Size = (UInt64)(Int64)-1;
117 NTime::GetCurUtc_FiTime(MTime);
118 CTime = ATime = MTime;
119
120 #ifdef _WIN32
121
122 /* in GUI mode : GetStdHandle(STD_INPUT_HANDLE) returns NULL,
123 and it doesn't set LastError. */
124 #if 1
125 SetLastError(0);
126 const HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
127 if (!h || h == INVALID_HANDLE_VALUE)
128 {
129 if (GetLastError() == 0)
130 SetLastError(ERROR_INVALID_HANDLE);
131 return false;
132 }
133 BY_HANDLE_FILE_INFORMATION info;
134 if (GetFileInformationByHandle(h, &info)
135 && info.dwVolumeSerialNumber)
136 {
137 Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow;
138 // FileID_Low = (((UInt64)info.nFileIndexHigh) << 32) + info.nFileIndexLow;
139 // NumLinks = SupportHardLinks ? info.nNumberOfLinks : 1;
140 Attrib = info.dwFileAttributes;
141 CTime = info.ftCreationTime;
142 ATime = info.ftLastAccessTime;
143 MTime = info.ftLastWriteTime;
144 }
145 #if 0
146 printf(
147 "\ndwFileAttributes = %8x"
148 "\nftCreationTime = %8x"
149 "\nftLastAccessTime = %8x"
150 "\nftLastWriteTime = %8x"
151 "\ndwVolumeSerialNumber = %8x"
152 "\nnFileSizeHigh = %8x"
153 "\nnFileSizeLow = %8x"
154 "\nnNumberOfLinks = %8x"
155 "\nnFileIndexHigh = %8x"
156 "\nnFileIndexLow = %8x \n",
157 (unsigned)info.dwFileAttributes,
158 (unsigned)info.ftCreationTime.dwHighDateTime,
159 (unsigned)info.ftLastAccessTime.dwHighDateTime,
160 (unsigned)info.ftLastWriteTime.dwHighDateTime,
161 (unsigned)info.dwVolumeSerialNumber,
162 (unsigned)info.nFileSizeHigh,
163 (unsigned)info.nFileSizeLow,
164 (unsigned)info.nNumberOfLinks,
165 (unsigned)info.nFileIndexHigh,
166 (unsigned)info.nFileIndexLow);
167 #endif
168 #endif
169
170 #else // non-Wiondow
171
172 mode = S_IFIFO | 0777; // 0755 : 0775 : 0664 : 0644 :
173 #if 1
174 struct stat st;
175 if (fstat(0, &st) == 0)
176 {
177 SetFrom_stat(st);
178 if (!S_ISREG(st.st_mode)
179 // S_ISFIFO(st->st_mode)
180 || st.st_size == 0)
181 {
182 Size = (UInt64)(Int64)-1;
183 // mode = S_IFIFO | 0777;
184 }
185 }
186 #endif
187 #endif
188
189 return true;
190 }
191
IsDots() const192 bool CFileInfo::IsDots() const throw()
193 {
194 if (!IsDir() || Name.IsEmpty())
195 return false;
196 if (Name[0] != '.')
197 return false;
198 return Name.Len() == 1 || (Name.Len() == 2 && Name[1] == '.');
199 }
200
201
202 #ifdef _WIN32
203
204
205 #define WIN_FD_TO_MY_FI(fi, fd) \
206 fi.Attrib = fd.dwFileAttributes; \
207 fi.CTime = fd.ftCreationTime; \
208 fi.ATime = fd.ftLastAccessTime; \
209 fi.MTime = fd.ftLastWriteTime; \
210 fi.Size = (((UInt64)fd.nFileSizeHigh) << 32) + fd.nFileSizeLow; \
211 /* fi.ReparseTag = fd.dwReserved0; */ \
212 fi.IsAltStream = false; \
213 fi.IsDevice = false;
214
215 /*
216 #ifdef UNDER_CE
217 fi.ObjectID = fd.dwOID;
218 #else
219 fi.ReparseTag = fd.dwReserved0;
220 #endif
221 */
222
Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATAW & fd,CFileInfo & fi)223 static void Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATAW &fd, CFileInfo &fi)
224 {
225 WIN_FD_TO_MY_FI(fi, fd)
226 fi.Name = us2fs(fd.cFileName);
227 #if defined(_WIN32) && !defined(UNDER_CE)
228 // fi.ShortName = us2fs(fd.cAlternateFileName);
229 #endif
230 }
231
232 #ifndef _UNICODE
Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATA & fd,CFileInfo & fi)233 static void Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATA &fd, CFileInfo &fi)
234 {
235 WIN_FD_TO_MY_FI(fi, fd)
236 fi.Name = fas2fs(fd.cFileName);
237 #if defined(_WIN32) && !defined(UNDER_CE)
238 // fi.ShortName = fas2fs(fd.cAlternateFileName);
239 #endif
240 }
241 #endif
242
243 ////////////////////////////////
244 // CFindFile
245
Close()246 bool CFindFileBase::Close() throw()
247 {
248 if (_handle == INVALID_HANDLE_VALUE)
249 return true;
250 if (!::FindClose(_handle))
251 return false;
252 _handle = INVALID_HANDLE_VALUE;
253 return true;
254 }
255
256 /*
257 WinXP-64 FindFirstFile():
258 "" - ERROR_PATH_NOT_FOUND
259 folder\ - ERROR_FILE_NOT_FOUND
260 \ - ERROR_FILE_NOT_FOUND
261 c:\ - ERROR_FILE_NOT_FOUND
262 c: - ERROR_FILE_NOT_FOUND, if current dir is ROOT ( c:\ )
263 c: - OK, if current dir is NOT ROOT ( c:\folder )
264 folder - OK
265
266 \\ - ERROR_INVALID_NAME
267 \\Server - ERROR_INVALID_NAME
268 \\Server\ - ERROR_INVALID_NAME
269
270 \\Server\Share - ERROR_BAD_NETPATH
271 \\Server\Share - ERROR_BAD_NET_NAME (Win7).
272 !!! There is problem : Win7 makes some requests for "\\Server\Shar" (look in Procmon),
273 when we call it for "\\Server\Share"
274
275 \\Server\Share\ - ERROR_FILE_NOT_FOUND
276
277 \\?\UNC\Server\Share - ERROR_INVALID_NAME
278 \\?\UNC\Server\Share - ERROR_BAD_PATHNAME (Win7)
279 \\?\UNC\Server\Share\ - ERROR_FILE_NOT_FOUND
280
281 \\Server\Share_RootDrive - ERROR_INVALID_NAME
282 \\Server\Share_RootDrive\ - ERROR_INVALID_NAME
283
284 e:\* - ERROR_FILE_NOT_FOUND, if there are no items in that root folder
285 w:\* - ERROR_PATH_NOT_FOUND, if there is no such drive w:
286 */
287
FindFirst(CFSTR path,CFileInfo & fi)288 bool CFindFile::FindFirst(CFSTR path, CFileInfo &fi)
289 {
290 if (!Close())
291 return false;
292 #ifndef _UNICODE
293 if (!g_IsNT)
294 {
295 WIN32_FIND_DATAA fd;
296 _handle = ::FindFirstFileA(fs2fas(path), &fd);
297 if (_handle == INVALID_HANDLE_VALUE)
298 return false;
299 Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi);
300 }
301 else
302 #endif
303 {
304 WIN32_FIND_DATAW fd;
305
306 IF_USE_MAIN_PATH
307 _handle = ::FindFirstFileW(fs2us(path), &fd);
308 #ifdef Z7_LONG_PATH
309 if (_handle == INVALID_HANDLE_VALUE && USE_SUPER_PATH)
310 {
311 UString superPath;
312 if (GetSuperPath(path, superPath, USE_MAIN_PATH))
313 _handle = ::FindFirstFileW(superPath, &fd);
314 }
315 #endif
316 if (_handle == INVALID_HANDLE_VALUE)
317 return false;
318 Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi);
319 }
320 return true;
321 }
322
FindNext(CFileInfo & fi)323 bool CFindFile::FindNext(CFileInfo &fi)
324 {
325 #ifndef _UNICODE
326 if (!g_IsNT)
327 {
328 WIN32_FIND_DATAA fd;
329 if (!::FindNextFileA(_handle, &fd))
330 return false;
331 Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi);
332 }
333 else
334 #endif
335 {
336 WIN32_FIND_DATAW fd;
337 if (!::FindNextFileW(_handle, &fd))
338 return false;
339 Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi);
340 }
341 return true;
342 }
343
344 #if defined(_WIN32) && !defined(UNDER_CE)
345
346 ////////////////////////////////
347 // AltStreams
348
349 #ifdef Z7_USE_DYN_FindFirstStream
350 static Func_FindFirstStreamW g_FindFirstStreamW;
351 static Func_FindNextStreamW g_FindNextStreamW;
352 #define MY_FindFirstStreamW g_FindFirstStreamW
353 #define MY_FindNextStreamW g_FindNextStreamW
354 static struct CFindStreamLoader
355 {
CFindStreamLoaderNWindows::NFile::NFind::CFindStreamLoader356 CFindStreamLoader()
357 {
358 const HMODULE hm = ::GetModuleHandleA("kernel32.dll");
359 g_FindFirstStreamW = Z7_GET_PROC_ADDRESS(
360 Func_FindFirstStreamW, hm,
361 "FindFirstStreamW");
362 g_FindNextStreamW = Z7_GET_PROC_ADDRESS(
363 Func_FindNextStreamW, hm,
364 "FindNextStreamW");
365 }
366 } g_FindStreamLoader;
367 #else
368 #define MY_FindFirstStreamW FindFirstStreamW
369 #define MY_FindNextStreamW FindNextStreamW
370 #endif
371
372
IsMainStream() const373 bool CStreamInfo::IsMainStream() const throw()
374 {
375 return StringsAreEqualNoCase_Ascii(Name, "::$DATA");
376 }
377
GetReducedName() const378 UString CStreamInfo::GetReducedName() const
379 {
380 // remove ":$DATA" postfix, but keep postfix, if Name is "::$DATA"
381 UString s (Name);
382 if (s.Len() > 6 + 1 && StringsAreEqualNoCase_Ascii(s.RightPtr(6), ":$DATA"))
383 s.DeleteFrom(s.Len() - 6);
384 return s;
385 }
386
387 /*
388 UString CStreamInfo::GetReducedName2() const
389 {
390 UString s = GetReducedName();
391 if (!s.IsEmpty() && s[0] == ':')
392 s.Delete(0);
393 return s;
394 }
395 */
396
Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(const MY_WIN32_FIND_STREAM_DATA & sd,CStreamInfo & si)397 static void Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(const MY_WIN32_FIND_STREAM_DATA &sd, CStreamInfo &si)
398 {
399 si.Size = (UInt64)sd.StreamSize.QuadPart;
400 si.Name = sd.cStreamName;
401 }
402
403 /*
404 WinXP-64 FindFirstStream():
405 "" - ERROR_PATH_NOT_FOUND
406 folder\ - OK
407 folder - OK
408 \ - OK
409 c:\ - OK
410 c: - OK, if current dir is ROOT ( c:\ )
411 c: - OK, if current dir is NOT ROOT ( c:\folder )
412 \\Server\Share - OK
413 \\Server\Share\ - OK
414
415 \\ - ERROR_INVALID_NAME
416 \\Server - ERROR_INVALID_NAME
417 \\Server\ - ERROR_INVALID_NAME
418 */
419
FindFirst(CFSTR path,CStreamInfo & si)420 bool CFindStream::FindFirst(CFSTR path, CStreamInfo &si)
421 {
422 if (!Close())
423 return false;
424 #ifdef Z7_USE_DYN_FindFirstStream
425 if (!g_FindFirstStreamW)
426 {
427 ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
428 return false;
429 }
430 #endif
431 {
432 MY_WIN32_FIND_STREAM_DATA sd;
433 SetLastError(0);
434 IF_USE_MAIN_PATH
435 _handle = MY_FindFirstStreamW(fs2us(path), My_FindStreamInfoStandard, &sd, 0);
436 if (_handle == INVALID_HANDLE_VALUE)
437 {
438 if (::GetLastError() == ERROR_HANDLE_EOF)
439 return false;
440 // long name can be tricky for path like ".\dirName".
441 #ifdef Z7_LONG_PATH
442 if (USE_SUPER_PATH)
443 {
444 UString superPath;
445 if (GetSuperPath(path, superPath, USE_MAIN_PATH))
446 _handle = MY_FindFirstStreamW(superPath, My_FindStreamInfoStandard, &sd, 0);
447 }
448 #endif
449 }
450 if (_handle == INVALID_HANDLE_VALUE)
451 return false;
452 Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(sd, si);
453 }
454 return true;
455 }
456
FindNext(CStreamInfo & si)457 bool CFindStream::FindNext(CStreamInfo &si)
458 {
459 #ifdef Z7_USE_DYN_FindFirstStream
460 if (!g_FindNextStreamW)
461 {
462 ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
463 return false;
464 }
465 #endif
466 {
467 MY_WIN32_FIND_STREAM_DATA sd;
468 if (!MY_FindNextStreamW(_handle, &sd))
469 return false;
470 Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(sd, si);
471 }
472 return true;
473 }
474
Next(CStreamInfo & si,bool & found)475 bool CStreamEnumerator::Next(CStreamInfo &si, bool &found)
476 {
477 bool res;
478 if (_find.IsHandleAllocated())
479 res = _find.FindNext(si);
480 else
481 res = _find.FindFirst(_filePath, si);
482 if (res)
483 {
484 found = true;
485 return true;
486 }
487 found = false;
488 return (::GetLastError() == ERROR_HANDLE_EOF);
489 }
490
491 #endif
492
493
494 /*
495 WinXP-64 GetFileAttributes():
496 If the function fails, it returns INVALID_FILE_ATTRIBUTES and use GetLastError() to get error code
497
498 \ - OK
499 C:\ - OK, if there is such drive,
500 D:\ - ERROR_PATH_NOT_FOUND, if there is no such drive,
501
502 C:\folder - OK
503 C:\folder\ - OK
504 C:\folderBad - ERROR_FILE_NOT_FOUND
505
506 \\Server\BadShare - ERROR_BAD_NETPATH
507 \\Server\Share - WORKS OK, but MSDN says:
508 GetFileAttributes for a network share, the function fails, and GetLastError
509 returns ERROR_BAD_NETPATH. You must specify a path to a subfolder on that share.
510 */
511
GetFileAttrib(CFSTR path)512 DWORD GetFileAttrib(CFSTR path)
513 {
514 #ifndef _UNICODE
515 if (!g_IsNT)
516 return ::GetFileAttributes(fs2fas(path));
517 else
518 #endif
519 {
520 IF_USE_MAIN_PATH
521 {
522 DWORD dw = ::GetFileAttributesW(fs2us(path));
523 if (dw != INVALID_FILE_ATTRIBUTES)
524 return dw;
525 }
526 #ifdef Z7_LONG_PATH
527 if (USE_SUPER_PATH)
528 {
529 UString superPath;
530 if (GetSuperPath(path, superPath, USE_MAIN_PATH))
531 return ::GetFileAttributesW(superPath);
532 }
533 #endif
534 return INVALID_FILE_ATTRIBUTES;
535 }
536 }
537
538 /* if path is "c:" or "c::" then CFileInfo::Find() returns name of current folder for that disk
539 so instead of absolute path we have relative path in Name. That is not good in some calls */
540
541 /* In CFileInfo::Find() we want to support same names for alt streams as in CreateFile(). */
542
543 /* CFileInfo::Find()
544 We alow the following paths (as FindFirstFile):
545 C:\folder
546 c: - if current dir is NOT ROOT ( c:\folder )
547
548 also we support paths that are not supported by FindFirstFile:
549 \
550 \\.\c:
551 c:\ - Name will be without tail slash ( c: )
552 \\?\c:\ - Name will be without tail slash ( c: )
553 \\Server\Share
554 \\?\UNC\Server\Share
555
556 c:\folder:stream - Name = folder:stream
557 c:\:stream - Name = :stream
558 c::stream - Name = c::stream
559 */
560
Find(CFSTR path,bool followLink)561 bool CFileInfo::Find(CFSTR path, bool followLink)
562 {
563 #ifdef Z7_DEVICE_FILE
564
565 if (IS_PATH_SEPAR(path[0]) &&
566 IS_PATH_SEPAR(path[1]) &&
567 path[2] == '.' &&
568 path[3] == 0)
569 {
570 // 22.00 : it's virtual directory for devices
571 // IsDevice = true;
572 ClearBase();
573 Name = path + 2;
574 Attrib = FILE_ATTRIBUTE_DIRECTORY;
575 return true;
576 }
577
578 if (IsDevicePath(path))
579 {
580 ClearBase();
581 Name = path + 4;
582 IsDevice = true;
583
584 if (NName::IsDrivePath2(path + 4) && path[6] == 0)
585 {
586 FChar drive[4] = { path[4], ':', '\\', 0 };
587 UInt64 clusterSize, totalSize, freeSize;
588 if (NSystem::MyGetDiskFreeSpace(drive, clusterSize, totalSize, freeSize))
589 {
590 Size = totalSize;
591 return true;
592 }
593 }
594
595 NIO::CInFile inFile;
596 // ::OutputDebugStringW(path);
597 if (!inFile.Open(path))
598 return false;
599 // ::OutputDebugStringW(L"---");
600 if (inFile.SizeDefined)
601 Size = inFile.Size;
602 return true;
603 }
604 #endif
605
606 #if defined(_WIN32) && !defined(UNDER_CE)
607
608 const int colonPos = FindAltStreamColon(path);
609 if (colonPos >= 0 && path[(unsigned)colonPos + 1] != 0)
610 {
611 UString streamName = fs2us(path + (unsigned)colonPos);
612 FString filePath (path);
613 filePath.DeleteFrom((unsigned)colonPos);
614 /* we allow both cases:
615 name:stream
616 name:stream:$DATA
617 */
618 const unsigned kPostfixSize = 6;
619 if (streamName.Len() <= kPostfixSize
620 || !StringsAreEqualNoCase_Ascii(streamName.RightPtr(kPostfixSize), ":$DATA"))
621 streamName += ":$DATA";
622
623 bool isOk = true;
624
625 if (IsDrivePath2(filePath) &&
626 (colonPos == 2 || (colonPos == 3 && filePath[2] == '\\')))
627 {
628 // FindFirstFile doesn't work for "c:\" and for "c:" (if current dir is ROOT)
629 ClearBase();
630 Name.Empty();
631 if (colonPos == 2)
632 Name = filePath;
633 }
634 else
635 isOk = Find(filePath, followLink); // check it (followLink)
636
637 if (isOk)
638 {
639 Attrib &= ~(DWORD)(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT);
640 Size = 0;
641 CStreamEnumerator enumerator(filePath);
642 for (;;)
643 {
644 CStreamInfo si;
645 bool found;
646 if (!enumerator.Next(si, found))
647 return false;
648 if (!found)
649 {
650 ::SetLastError(ERROR_FILE_NOT_FOUND);
651 return false;
652 }
653 if (si.Name.IsEqualTo_NoCase(streamName))
654 {
655 // we delete postfix, if alt stream name is not "::$DATA"
656 if (si.Name.Len() > kPostfixSize + 1)
657 si.Name.DeleteFrom(si.Name.Len() - kPostfixSize);
658 Name += us2fs(si.Name);
659 Size = si.Size;
660 IsAltStream = true;
661 return true;
662 }
663 }
664 }
665 }
666
667 #endif
668
669 CFindFile finder;
670
671 #if defined(_WIN32) && !defined(UNDER_CE)
672 {
673 /*
674 DWORD lastError = GetLastError();
675 if (lastError == ERROR_FILE_NOT_FOUND
676 || lastError == ERROR_BAD_NETPATH // XP64: "\\Server\Share"
677 || lastError == ERROR_BAD_NET_NAME // Win7: "\\Server\Share"
678 || lastError == ERROR_INVALID_NAME // XP64: "\\?\UNC\Server\Share"
679 || lastError == ERROR_BAD_PATHNAME // Win7: "\\?\UNC\Server\Share"
680 )
681 */
682
683 unsigned rootSize = 0;
684 if (IsSuperPath(path))
685 rootSize = kSuperPathPrefixSize;
686
687 if (NName::IsDrivePath(path + rootSize) && path[rootSize + 3] == 0)
688 {
689 DWORD attrib = GetFileAttrib(path);
690 if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0)
691 {
692 ClearBase();
693 Attrib = attrib;
694 Name = path + rootSize;
695 Name.DeleteFrom(2);
696 if (!Fill_From_ByHandleFileInfo(path))
697 {
698 }
699 return true;
700 }
701 }
702 else if (IS_PATH_SEPAR(path[0]))
703 {
704 if (path[1] == 0)
705 {
706 DWORD attrib = GetFileAttrib(path);
707 if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0)
708 {
709 ClearBase();
710 Name.Empty();
711 Attrib = attrib;
712 return true;
713 }
714 }
715 else
716 {
717 const unsigned prefixSize = GetNetworkServerPrefixSize(path);
718 if (prefixSize > 0 && path[prefixSize] != 0)
719 {
720 if (NName::FindSepar(path + prefixSize) < 0)
721 {
722 if (Fill_From_ByHandleFileInfo(path))
723 {
724 Name = path + prefixSize;
725 return true;
726 }
727
728 FString s (path);
729 s.Add_PathSepar();
730 s.Add_Char('*'); // CHAR_ANY_MASK
731 bool isOK = false;
732 if (finder.FindFirst(s, *this))
733 {
734 if (Name == FTEXT("."))
735 {
736 Name = path + prefixSize;
737 return true;
738 }
739 isOK = true;
740 /* if "\\server\share" maps to root folder "d:\", there is no "." item.
741 But it's possible that there are another items */
742 }
743 {
744 const DWORD attrib = GetFileAttrib(path);
745 if (isOK || (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0))
746 {
747 ClearBase();
748 if (attrib != INVALID_FILE_ATTRIBUTES)
749 Attrib = attrib;
750 else
751 SetAsDir();
752 Name = path + prefixSize;
753 return true;
754 }
755 }
756 // ::SetLastError(lastError);
757 }
758 }
759 }
760 }
761 }
762 #endif
763
764 bool res = finder.FindFirst(path, *this);
765 if (!followLink
766 || !res
767 || !HasReparsePoint())
768 return res;
769
770 // return FollowReparse(path, IsDir());
771 return Fill_From_ByHandleFileInfo(path);
772 }
773
Fill_From_ByHandleFileInfo(CFSTR path)774 bool CFileInfoBase::Fill_From_ByHandleFileInfo(CFSTR path)
775 {
776 BY_HANDLE_FILE_INFORMATION info;
777 if (!NIO::CFileBase::GetFileInformation(path, &info))
778 return false;
779 {
780 Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow;
781 CTime = info.ftCreationTime;
782 ATime = info.ftLastAccessTime;
783 MTime = info.ftLastWriteTime;
784 Attrib = info.dwFileAttributes;
785 return true;
786 }
787 }
788
789 /*
790 bool CFileInfo::FollowReparse(CFSTR path, bool isDir)
791 {
792 if (isDir)
793 {
794 FString prefix = path;
795 prefix.Add_PathSepar();
796
797 // "folder/." refers to folder itself. So we can't use that path
798 // we must use enumerator and search "." item
799 CEnumerator enumerator;
800 enumerator.SetDirPrefix(prefix);
801 for (;;)
802 {
803 CFileInfo fi;
804 if (!enumerator.NextAny(fi))
805 break;
806 if (fi.Name.IsEqualTo_Ascii_NoCase("."))
807 {
808 // we can copy preperies;
809 CTime = fi.CTime;
810 ATime = fi.ATime;
811 MTime = fi.MTime;
812 Attrib = fi.Attrib;
813 Size = fi.Size;
814 return true;
815 }
816 break;
817 }
818 // LastError(lastError);
819 return false;
820 }
821
822 {
823 NIO::CInFile inFile;
824 if (inFile.Open(path))
825 {
826 BY_HANDLE_FILE_INFORMATION info;
827 if (inFile.GetFileInformation(&info))
828 {
829 ClearBase();
830 Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow;
831 CTime = info.ftCreationTime;
832 ATime = info.ftLastAccessTime;
833 MTime = info.ftLastWriteTime;
834 Attrib = info.dwFileAttributes;
835 return true;
836 }
837 }
838 return false;
839 }
840 }
841 */
842
DoesFileExist_Raw(CFSTR name)843 bool DoesFileExist_Raw(CFSTR name)
844 {
845 CFileInfo fi;
846 return fi.Find(name) && !fi.IsDir();
847 }
848
DoesFileExist_FollowLink(CFSTR name)849 bool DoesFileExist_FollowLink(CFSTR name)
850 {
851 CFileInfo fi;
852 return fi.Find_FollowLink(name) && !fi.IsDir();
853 }
854
DoesDirExist(CFSTR name,bool followLink)855 bool DoesDirExist(CFSTR name, bool followLink)
856 {
857 CFileInfo fi;
858 return fi.Find(name, followLink) && fi.IsDir();
859 }
860
DoesFileOrDirExist(CFSTR name)861 bool DoesFileOrDirExist(CFSTR name)
862 {
863 CFileInfo fi;
864 return fi.Find(name);
865 }
866
867
SetDirPrefix(const FString & dirPrefix)868 void CEnumerator::SetDirPrefix(const FString &dirPrefix)
869 {
870 _wildcard = dirPrefix;
871 _wildcard.Add_Char('*');
872 }
873
NextAny(CFileInfo & fi)874 bool CEnumerator::NextAny(CFileInfo &fi)
875 {
876 if (_findFile.IsHandleAllocated())
877 return _findFile.FindNext(fi);
878 else
879 return _findFile.FindFirst(_wildcard, fi);
880 }
881
Next(CFileInfo & fi)882 bool CEnumerator::Next(CFileInfo &fi)
883 {
884 for (;;)
885 {
886 if (!NextAny(fi))
887 return false;
888 if (!fi.IsDots())
889 return true;
890 }
891 }
892
Next(CFileInfo & fi,bool & found)893 bool CEnumerator::Next(CFileInfo &fi, bool &found)
894 {
895 /*
896 for (;;)
897 {
898 if (!NextAny(fi))
899 break;
900 if (!fi.IsDots())
901 {
902 found = true;
903 return true;
904 }
905 }
906 */
907
908 if (Next(fi))
909 {
910 found = true;
911 return true;
912 }
913
914 found = false;
915 DWORD lastError = ::GetLastError();
916 if (_findFile.IsHandleAllocated())
917 return (lastError == ERROR_NO_MORE_FILES);
918 // we support the case for empty root folder: FindFirstFile("c:\\*") returns ERROR_FILE_NOT_FOUND
919 if (lastError == ERROR_FILE_NOT_FOUND)
920 return true;
921 if (lastError == ERROR_ACCESS_DENIED)
922 {
923 // here we show inaccessible root system folder as empty folder to eliminate redundant user warnings
924 const char *s = "System Volume Information" STRING_PATH_SEPARATOR "*";
925 const int len = (int)strlen(s);
926 const int delta = (int)_wildcard.Len() - len;
927 if (delta == 0 || (delta > 0 && IS_PATH_SEPAR(_wildcard[(unsigned)delta - 1])))
928 if (StringsAreEqual_Ascii(_wildcard.Ptr((unsigned)delta), s))
929 return true;
930 }
931 return false;
932 }
933
934
935 ////////////////////////////////
936 // CFindChangeNotification
937 // FindFirstChangeNotification can return 0. MSDN doesn't tell about it.
938
Close()939 bool CFindChangeNotification::Close() throw()
940 {
941 if (!IsHandleAllocated())
942 return true;
943 if (!::FindCloseChangeNotification(_handle))
944 return false;
945 _handle = INVALID_HANDLE_VALUE;
946 return true;
947 }
948
FindFirst(CFSTR path,bool watchSubtree,DWORD notifyFilter)949 HANDLE CFindChangeNotification::FindFirst(CFSTR path, bool watchSubtree, DWORD notifyFilter)
950 {
951 #ifndef _UNICODE
952 if (!g_IsNT)
953 _handle = ::FindFirstChangeNotification(fs2fas(path), BoolToBOOL(watchSubtree), notifyFilter);
954 else
955 #endif
956 {
957 IF_USE_MAIN_PATH
958 _handle = ::FindFirstChangeNotificationW(fs2us(path), BoolToBOOL(watchSubtree), notifyFilter);
959 #ifdef Z7_LONG_PATH
960 if (!IsHandleAllocated())
961 {
962 UString superPath;
963 if (GetSuperPath(path, superPath, USE_MAIN_PATH))
964 _handle = ::FindFirstChangeNotificationW(superPath, BoolToBOOL(watchSubtree), notifyFilter);
965 }
966 #endif
967 }
968 return _handle;
969 }
970
971 #ifndef UNDER_CE
972
MyGetLogicalDriveStrings(CObjectVector<FString> & driveStrings)973 bool MyGetLogicalDriveStrings(CObjectVector<FString> &driveStrings)
974 {
975 driveStrings.Clear();
976 #ifndef _UNICODE
977 if (!g_IsNT)
978 {
979 driveStrings.Clear();
980 UINT32 size = GetLogicalDriveStrings(0, NULL);
981 if (size == 0)
982 return false;
983 CObjArray<char> buf(size);
984 UINT32 newSize = GetLogicalDriveStrings(size, buf);
985 if (newSize == 0 || newSize > size)
986 return false;
987 AString s;
988 UINT32 prev = 0;
989 for (UINT32 i = 0; i < newSize; i++)
990 {
991 if (buf[i] == 0)
992 {
993 s = buf + prev;
994 prev = i + 1;
995 driveStrings.Add(fas2fs(s));
996 }
997 }
998 return prev == newSize;
999 }
1000 else
1001 #endif
1002 {
1003 UINT32 size = GetLogicalDriveStringsW(0, NULL);
1004 if (size == 0)
1005 return false;
1006 CObjArray<wchar_t> buf(size);
1007 UINT32 newSize = GetLogicalDriveStringsW(size, buf);
1008 if (newSize == 0 || newSize > size)
1009 return false;
1010 UString s;
1011 UINT32 prev = 0;
1012 for (UINT32 i = 0; i < newSize; i++)
1013 {
1014 if (buf[i] == 0)
1015 {
1016 s = buf + prev;
1017 prev = i + 1;
1018 driveStrings.Add(us2fs(s));
1019 }
1020 }
1021 return prev == newSize;
1022 }
1023 }
1024
1025 #endif // UNDER_CE
1026
1027
1028
1029 #else // _WIN32
1030
1031 // ---------- POSIX ----------
1032
MY_lstat(CFSTR path,struct stat * st,bool followLink)1033 static int MY_lstat(CFSTR path, struct stat *st, bool followLink)
1034 {
1035 memset(st, 0, sizeof(*st));
1036 int res;
1037 // #ifdef ENV_HAVE_LSTAT
1038 if (/* global_use_lstat && */ !followLink)
1039 {
1040 // printf("\nlstat\n");
1041 res = lstat(path, st);
1042 }
1043 else
1044 // #endif
1045 {
1046 // printf("\nstat\n");
1047 res = stat(path, st);
1048 }
1049 #if 0
1050 #if defined(__clang__) && __clang_major__ >= 14
1051 #pragma GCC diagnostic ignored "-Wc++98-compat-pedantic"
1052 #endif
1053
1054 printf("\n st_dev = %lld", (long long)(st->st_dev));
1055 printf("\n st_ino = %lld", (long long)(st->st_ino));
1056 printf("\n st_mode = %llx", (long long)(st->st_mode));
1057 printf("\n st_nlink = %lld", (long long)(st->st_nlink));
1058 printf("\n st_uid = %lld", (long long)(st->st_uid));
1059 printf("\n st_gid = %lld", (long long)(st->st_gid));
1060 printf("\n st_size = %lld", (long long)(st->st_size));
1061 printf("\n st_blksize = %lld", (long long)(st->st_blksize));
1062 printf("\n st_blocks = %lld", (long long)(st->st_blocks));
1063 printf("\n st_ctim = %lld", (long long)(ST_CTIME((*st)).tv_sec));
1064 printf("\n st_mtim = %lld", (long long)(ST_MTIME((*st)).tv_sec));
1065 printf("\n st_atim = %lld", (long long)(ST_ATIME((*st)).tv_sec));
1066 printf(S_ISFIFO(st->st_mode) ? "\n FIFO" : "\n NO FIFO");
1067 printf("\n");
1068 #endif
1069
1070 return res;
1071 }
1072
1073
Get_Name_from_Path(CFSTR path)1074 static const char *Get_Name_from_Path(CFSTR path) throw()
1075 {
1076 size_t len = strlen(path);
1077 if (len == 0)
1078 return path;
1079 const char *p = path + len - 1;
1080 {
1081 if (p == path)
1082 return path;
1083 p--;
1084 }
1085 for (;;)
1086 {
1087 char c = *p;
1088 if (IS_PATH_SEPAR(c))
1089 return p + 1;
1090 if (p == path)
1091 return path;
1092 p--;
1093 }
1094 }
1095
1096
Get_WinAttribPosix_From_PosixMode(UInt32 mode)1097 UInt32 Get_WinAttribPosix_From_PosixMode(UInt32 mode)
1098 {
1099 UInt32 attrib = S_ISDIR(mode) ?
1100 FILE_ATTRIBUTE_DIRECTORY :
1101 FILE_ATTRIBUTE_ARCHIVE;
1102 if ((mode & 0222) == 0) // S_IWUSR in p7zip
1103 attrib |= FILE_ATTRIBUTE_READONLY;
1104 return attrib | FILE_ATTRIBUTE_UNIX_EXTENSION | ((mode & 0xFFFF) << 16);
1105 }
1106
1107 /*
1108 UInt32 Get_WinAttrib_From_stat(const struct stat &st)
1109 {
1110 UInt32 attrib = S_ISDIR(st.st_mode) ?
1111 FILE_ATTRIBUTE_DIRECTORY :
1112 FILE_ATTRIBUTE_ARCHIVE;
1113
1114 if ((st.st_mode & 0222) == 0) // check it !!!
1115 attrib |= FILE_ATTRIBUTE_READONLY;
1116
1117 attrib |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((st.st_mode & 0xFFFF) << 16);
1118 return attrib;
1119 }
1120 */
1121
SetFrom_stat(const struct stat & st)1122 void CFileInfoBase::SetFrom_stat(const struct stat &st)
1123 {
1124 // IsDevice = false;
1125
1126 if (S_ISDIR(st.st_mode))
1127 {
1128 Size = 0;
1129 }
1130 else
1131 {
1132 Size = (UInt64)st.st_size; // for a symbolic link, size = size of filename
1133 }
1134
1135 // Attrib = Get_WinAttribPosix_From_PosixMode(st.st_mode);
1136
1137 // NTime::UnixTimeToFileTime(st.st_ctime, CTime);
1138 // NTime::UnixTimeToFileTime(st.st_mtime, MTime);
1139 // NTime::UnixTimeToFileTime(st.st_atime, ATime);
1140 #ifdef __APPLE__
1141 // #ifdef _DARWIN_FEATURE_64_BIT_INODE
1142 /*
1143 here we can use birthtime instead of st_ctimespec.
1144 but we use st_ctimespec for compatibility with previous versions and p7zip.
1145 st_birthtimespec in OSX
1146 st_birthtim : at FreeBSD, NetBSD
1147 */
1148 // timespec_To_FILETIME(st.st_birthtimespec, CTime);
1149 // #else
1150 // timespec_To_FILETIME(st.st_ctimespec, CTime);
1151 // #endif
1152 // timespec_To_FILETIME(st.st_mtimespec, MTime);
1153 // timespec_To_FILETIME(st.st_atimespec, ATime);
1154 CTime = st.st_ctimespec;
1155 MTime = st.st_mtimespec;
1156 ATime = st.st_atimespec;
1157
1158 #else
1159 // timespec_To_FILETIME(st.st_ctim, CTime, &CTime_ns100);
1160 // timespec_To_FILETIME(st.st_mtim, MTime, &MTime_ns100);
1161 // timespec_To_FILETIME(st.st_atim, ATime, &ATime_ns100);
1162 CTime = st.st_ctim;
1163 MTime = st.st_mtim;
1164 ATime = st.st_atim;
1165
1166 #endif
1167
1168 dev = st.st_dev;
1169 ino = st.st_ino;
1170 mode = st.st_mode;
1171 nlink = st.st_nlink;
1172 uid = st.st_uid;
1173 gid = st.st_gid;
1174 rdev = st.st_rdev;
1175
1176 /*
1177 printf("\n sizeof timespec = %d", (int)sizeof(timespec));
1178 printf("\n sizeof st_rdev = %d", (int)sizeof(rdev));
1179 printf("\n sizeof st_ino = %d", (int)sizeof(ino));
1180 printf("\n sizeof mode_t = %d", (int)sizeof(mode_t));
1181 printf("\n sizeof nlink_t = %d", (int)sizeof(nlink_t));
1182 printf("\n sizeof uid_t = %d", (int)sizeof(uid_t));
1183 printf("\n");
1184 */
1185 /*
1186 printf("\n st_rdev = %llx", (long long)rdev);
1187 printf("\n st_dev = %llx", (long long)dev);
1188 printf("\n dev : major = %5x minor = %5x", (unsigned)major(dev), (unsigned)minor(dev));
1189 printf("\n st_ino = %lld", (long long)(ino));
1190 printf("\n rdev : major = %5x minor = %5x", (unsigned)major(rdev), (unsigned)minor(rdev));
1191 printf("\n size = %lld \n", (long long)(Size));
1192 printf("\n");
1193 */
1194 }
1195
1196 /*
1197 int Uid_To_Uname(uid_t uid, AString &name)
1198 {
1199 name.Empty();
1200 struct passwd *passwd;
1201
1202 if (uid != 0 && uid == cached_no_such_uid)
1203 {
1204 *uname = xstrdup ("");
1205 return;
1206 }
1207
1208 if (!cached_uname || uid != cached_uid)
1209 {
1210 passwd = getpwuid (uid);
1211 if (passwd)
1212 {
1213 cached_uid = uid;
1214 assign_string (&cached_uname, passwd->pw_name);
1215 }
1216 else
1217 {
1218 cached_no_such_uid = uid;
1219 *uname = xstrdup ("");
1220 return;
1221 }
1222 }
1223 *uname = xstrdup (cached_uname);
1224 }
1225 */
1226
Find_DontFill_Name(CFSTR path,bool followLink)1227 bool CFileInfo::Find_DontFill_Name(CFSTR path, bool followLink)
1228 {
1229 struct stat st;
1230 if (MY_lstat(path, &st, followLink) != 0)
1231 return false;
1232 // printf("\nFind_DontFill_Name : name=%s\n", path);
1233 SetFrom_stat(st);
1234 return true;
1235 }
1236
1237
Find(CFSTR path,bool followLink)1238 bool CFileInfo::Find(CFSTR path, bool followLink)
1239 {
1240 // printf("\nCEnumerator::Find() name = %s\n", path);
1241 if (!Find_DontFill_Name(path, followLink))
1242 return false;
1243
1244 // printf("\nOK\n");
1245
1246 Name = Get_Name_from_Path(path);
1247 if (!Name.IsEmpty())
1248 {
1249 char c = Name.Back();
1250 if (IS_PATH_SEPAR(c))
1251 Name.DeleteBack();
1252 }
1253 return true;
1254 }
1255
1256
DoesFileExist_Raw(CFSTR name)1257 bool DoesFileExist_Raw(CFSTR name)
1258 {
1259 // FIXME for symbolic links.
1260 struct stat st;
1261 if (MY_lstat(name, &st, false) != 0)
1262 return false;
1263 return !S_ISDIR(st.st_mode);
1264 }
1265
DoesFileExist_FollowLink(CFSTR name)1266 bool DoesFileExist_FollowLink(CFSTR name)
1267 {
1268 // FIXME for symbolic links.
1269 struct stat st;
1270 if (MY_lstat(name, &st, true) != 0)
1271 return false;
1272 return !S_ISDIR(st.st_mode);
1273 }
1274
DoesDirExist(CFSTR name,bool followLink)1275 bool DoesDirExist(CFSTR name, bool followLink)
1276 {
1277 struct stat st;
1278 if (MY_lstat(name, &st, followLink) != 0)
1279 return false;
1280 return S_ISDIR(st.st_mode);
1281 }
1282
DoesFileOrDirExist(CFSTR name)1283 bool DoesFileOrDirExist(CFSTR name)
1284 {
1285 struct stat st;
1286 if (MY_lstat(name, &st, false) != 0)
1287 return false;
1288 return true;
1289 }
1290
1291
~CEnumerator()1292 CEnumerator::~CEnumerator()
1293 {
1294 if (_dir)
1295 closedir(_dir);
1296 }
1297
SetDirPrefix(const FString & dirPrefix)1298 void CEnumerator::SetDirPrefix(const FString &dirPrefix)
1299 {
1300 _wildcard = dirPrefix;
1301 }
1302
IsDots() const1303 bool CDirEntry::IsDots() const throw()
1304 {
1305 /* some systems (like CentOS 7.x on XFS) have (Type == DT_UNKNOWN)
1306 we can call fstatat() for that case, but we use only (Name) check here */
1307
1308 #if !defined(_AIX) && !defined(__sun)
1309 if (Type != DT_DIR && Type != DT_UNKNOWN)
1310 return false;
1311 #endif
1312
1313 return Name.Len() != 0
1314 && Name.Len() <= 2
1315 && Name[0] == '.'
1316 && (Name.Len() == 1 || Name[1] == '.');
1317 }
1318
1319
NextAny(CDirEntry & fi,bool & found)1320 bool CEnumerator::NextAny(CDirEntry &fi, bool &found)
1321 {
1322 found = false;
1323
1324 if (!_dir)
1325 {
1326 const char *w = "./";
1327 if (!_wildcard.IsEmpty())
1328 w = _wildcard.Ptr();
1329 _dir = ::opendir((const char *)w);
1330 if (_dir == NULL)
1331 return false;
1332 }
1333
1334 // To distinguish end of stream from an error, we must set errno to zero before readdir()
1335 errno = 0;
1336
1337 struct dirent *de = readdir(_dir);
1338 if (!de)
1339 {
1340 if (errno == 0)
1341 return true; // it's end of stream, and we report it with (found = false)
1342 // it's real error
1343 return false;
1344 }
1345
1346 fi.iNode = de->d_ino;
1347
1348 #if !defined(_AIX) && !defined(__sun)
1349 fi.Type = de->d_type;
1350 /* some systems (like CentOS 7.x on XFS) have (Type == DT_UNKNOWN)
1351 we can set (Type) from fstatat() in that case.
1352 But (Type) is not too important. So we don't set it here with slow fstatat() */
1353 /*
1354 // fi.Type = DT_UNKNOWN; // for debug
1355 if (fi.Type == DT_UNKNOWN)
1356 {
1357 struct stat st;
1358 if (fstatat(dirfd(_dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) == 0)
1359 if (S_ISDIR(st.st_mode))
1360 fi.Type = DT_DIR;
1361 }
1362 */
1363 #endif
1364
1365 /*
1366 if (de->d_type == DT_DIR)
1367 fi.Attrib = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_UNIX_EXTENSION | ((UInt32)(S_IFDIR) << 16);
1368 else if (de->d_type < 16)
1369 fi.Attrib = FILE_ATTRIBUTE_UNIX_EXTENSION | ((UInt32)(de->d_type) << (16 + 12));
1370 */
1371 fi.Name = de->d_name;
1372
1373 /*
1374 printf("\nCEnumerator::NextAny; len = %d %s \n", (int)fi.Name.Len(), fi.Name.Ptr());
1375 for (unsigned i = 0; i < fi.Name.Len(); i++)
1376 printf (" %02x", (unsigned)(Byte)de->d_name[i]);
1377 printf("\n");
1378 */
1379
1380 found = true;
1381 return true;
1382 }
1383
1384
Next(CDirEntry & fi,bool & found)1385 bool CEnumerator::Next(CDirEntry &fi, bool &found)
1386 {
1387 // printf("\nCEnumerator::Next()\n");
1388 // PrintName("Next", "");
1389 for (;;)
1390 {
1391 if (!NextAny(fi, found))
1392 return false;
1393 if (!found)
1394 return true;
1395 if (!fi.IsDots())
1396 {
1397 /*
1398 if (!NeedFullStat)
1399 return true;
1400 // we silently skip error file here - it can be wrong link item
1401 if (fi.Find_DontFill_Name(path))
1402 return true;
1403 */
1404 return true;
1405 }
1406 }
1407 }
1408
1409 /*
1410 bool CEnumerator::Next(CDirEntry &fileInfo, bool &found)
1411 {
1412 bool found;
1413 if (!Next(fi, found))
1414 return false;
1415 return found;
1416 }
1417 */
1418
Fill_FileInfo(const CDirEntry & de,CFileInfo & fileInfo,bool followLink) const1419 bool CEnumerator::Fill_FileInfo(const CDirEntry &de, CFileInfo &fileInfo, bool followLink) const
1420 {
1421 // printf("\nCEnumerator::Fill_FileInfo()\n");
1422 struct stat st;
1423 // probably it's OK to use fstatat() even if it changes file position dirfd(_dir)
1424 int res = fstatat(dirfd(_dir), de.Name, &st, followLink ? 0 : AT_SYMLINK_NOFOLLOW);
1425 // if fstatat() is not supported, we can use stat() / lstat()
1426
1427 /*
1428 const FString path = _wildcard + s;
1429 int res = MY_lstat(path, &st, followLink);
1430 */
1431
1432 if (res != 0)
1433 return false;
1434 // printf("\nname=%s\n", de.Name.Ptr());
1435 fileInfo.SetFrom_stat(st);
1436 fileInfo.Name = de.Name;
1437 return true;
1438 }
1439
1440 #endif // _WIN32
1441
1442 }}}
1443