1 // EnumDirItems.cpp
2
3 #include "StdAfx.h"
4
5 #include <wchar.h>
6 // #include <stdio.h>
7
8 #ifndef _WIN32
9 #include <grp.h>
10 #include <pwd.h>
11 #include "../../../Common/UTFConvert.h"
12 #endif
13
14 #include "../../../Common/Wildcard.h"
15
16 #include "../../../Windows/FileDir.h"
17 #include "../../../Windows/FileIO.h"
18 #include "../../../Windows/FileName.h"
19
20 #if defined(_WIN32) && !defined(UNDER_CE)
21 #define _USE_SECURITY_CODE
22 #include "../../../Windows/SecurityUtils.h"
23 #endif
24
25 #include "EnumDirItems.h"
26 #include "SortUtils.h"
27
28 using namespace NWindows;
29 using namespace NFile;
30 using namespace NName;
31
32
FindFile_KeepDots(NFile::NFind::CFileInfo & fi,const FString & path,bool followLink)33 static bool FindFile_KeepDots(NFile::NFind::CFileInfo &fi, const FString &path, bool followLink)
34 {
35 const bool res = fi.Find(path, followLink);
36 if (!res)
37 return res;
38 if (path.IsEmpty())
39 return res;
40 // we keep name "." and "..", if it's without tail slash
41 const FChar *p = path.RightPtr(1);
42 if (*p != '.')
43 return res;
44 if (p != path.Ptr())
45 {
46 FChar c = p[-1];
47 if (!IS_PATH_SEPAR(c))
48 {
49 if (c != '.')
50 return res;
51 p--;
52 if (p != path.Ptr())
53 {
54 c = p[-1];
55 if (!IS_PATH_SEPAR(c))
56 return res;
57 }
58 }
59 }
60 fi.Name = p;
61 return res;
62 }
63
64
AddDirFileInfo(int phyParent,int logParent,int secureIndex,const NFind::CFileInfo & fi)65 void CDirItems::AddDirFileInfo(int phyParent, int logParent, int secureIndex,
66 const NFind::CFileInfo &fi)
67 {
68 /*
69 CDirItem di(fi);
70 di.PhyParent = phyParent;
71 di.LogParent = logParent;
72 di.SecureIndex = secureIndex;
73 Items.Add(di);
74 */
75 VECTOR_ADD_NEW_OBJECT (Items, CDirItem(fi, phyParent, logParent, secureIndex))
76
77 if (fi.IsDir())
78 Stat.NumDirs++;
79 #ifdef _WIN32
80 else if (fi.IsAltStream)
81 {
82 Stat.NumAltStreams++;
83 Stat.AltStreamsSize += fi.Size;
84 }
85 #endif
86 else
87 {
88 Stat.NumFiles++;
89 Stat.FilesSize += fi.Size;
90 }
91 }
92
93 // (DWORD)E_FAIL
94 #define DI_DEFAULT_ERROR ERROR_INVALID_FUNCTION
95
AddError(const FString & path,DWORD errorCode)96 HRESULT CDirItems::AddError(const FString &path, DWORD errorCode)
97 {
98 if (errorCode == 0)
99 errorCode = DI_DEFAULT_ERROR;
100 Stat.NumErrors++;
101 if (Callback)
102 return Callback->ScanError(path, errorCode);
103 return S_OK;
104 }
105
AddError(const FString & path)106 HRESULT CDirItems::AddError(const FString &path)
107 {
108 return AddError(path, ::GetLastError());
109 }
110
111 static const unsigned kScanProgressStepMask = (1 << 12) - 1;
112
ScanProgress(const FString & dirPath)113 HRESULT CDirItems::ScanProgress(const FString &dirPath)
114 {
115 if (Callback)
116 return Callback->ScanProgress(Stat, dirPath, true);
117 return S_OK;
118 }
119
GetPrefixesPath(const CIntVector & parents,int index,const UString & name) const120 UString CDirItems::GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const
121 {
122 UString path;
123 unsigned len = name.Len();
124
125 int i;
126 for (i = index; i >= 0; i = parents[(unsigned)i])
127 len += Prefixes[(unsigned)i].Len();
128
129 wchar_t *p = path.GetBuf_SetEnd(len) + len;
130
131 p -= name.Len();
132 wmemcpy(p, (const wchar_t *)name, name.Len());
133
134 for (i = index; i >= 0; i = parents[(unsigned)i])
135 {
136 const UString &s = Prefixes[(unsigned)i];
137 p -= s.Len();
138 wmemcpy(p, (const wchar_t *)s, s.Len());
139 }
140
141 return path;
142 }
143
GetPhyPath(unsigned index) const144 FString CDirItems::GetPhyPath(unsigned index) const
145 {
146 const CDirItem &di = Items[index];
147 return us2fs(GetPrefixesPath(PhyParents, di.PhyParent, di.Name));
148 }
149
GetLogPath(unsigned index) const150 UString CDirItems::GetLogPath(unsigned index) const
151 {
152 const CDirItem &di = Items[index];
153 return GetPrefixesPath(LogParents, di.LogParent, di.Name);
154 }
155
ReserveDown()156 void CDirItems::ReserveDown()
157 {
158 Prefixes.ReserveDown();
159 PhyParents.ReserveDown();
160 LogParents.ReserveDown();
161 Items.ReserveDown();
162 }
163
AddPrefix(int phyParent,int logParent,const UString & prefix)164 unsigned CDirItems::AddPrefix(int phyParent, int logParent, const UString &prefix)
165 {
166 PhyParents.Add(phyParent);
167 LogParents.Add(logParent);
168 return Prefixes.Add(prefix);
169 }
170
DeleteLastPrefix()171 void CDirItems::DeleteLastPrefix()
172 {
173 PhyParents.DeleteBack();
174 LogParents.DeleteBack();
175 Prefixes.DeleteBack();
176 }
177
178 bool InitLocalPrivileges();
179
CDirItems()180 CDirItems::CDirItems():
181 SymLinks(false),
182 ScanAltStreams(false)
183 , ExcludeDirItems(false)
184 , ExcludeFileItems(false)
185 , ShareForWrite(false)
186 #ifdef _USE_SECURITY_CODE
187 , ReadSecure(false)
188 #endif
189 #ifndef _WIN32
190 , StoreOwnerName(true)
191 #endif
192 , Callback(NULL)
193 {
194 #ifdef _USE_SECURITY_CODE
195 _saclEnabled = InitLocalPrivileges();
196 #endif
197 }
198
199
200 #ifdef _USE_SECURITY_CODE
201
AddSecurityItem(const FString & path,int & secureIndex)202 HRESULT CDirItems::AddSecurityItem(const FString &path, int &secureIndex)
203 {
204 secureIndex = -1;
205
206 SECURITY_INFORMATION securInfo =
207 DACL_SECURITY_INFORMATION |
208 GROUP_SECURITY_INFORMATION |
209 OWNER_SECURITY_INFORMATION;
210 if (_saclEnabled)
211 securInfo |= SACL_SECURITY_INFORMATION;
212
213 DWORD errorCode = 0;
214 DWORD secureSize;
215
216 BOOL res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(void *)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize);
217
218 if (res)
219 {
220 if (secureSize == 0)
221 return S_OK;
222 if (secureSize > TempSecureBuf.Size())
223 errorCode = ERROR_INVALID_FUNCTION;
224 }
225 else
226 {
227 errorCode = GetLastError();
228 if (errorCode == ERROR_INSUFFICIENT_BUFFER)
229 {
230 if (secureSize <= TempSecureBuf.Size())
231 errorCode = ERROR_INVALID_FUNCTION;
232 else
233 {
234 TempSecureBuf.Alloc(secureSize);
235 res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(void *)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize);
236 if (res)
237 {
238 if (secureSize != TempSecureBuf.Size())
239 errorCode = ERROR_INVALID_FUNCTION;;
240 }
241 else
242 errorCode = GetLastError();
243 }
244 }
245 }
246
247 if (res)
248 {
249 secureIndex = (int)SecureBlocks.AddUniq(TempSecureBuf, secureSize);
250 return S_OK;
251 }
252
253 return AddError(path, errorCode);
254 }
255
256 #endif // _USE_SECURITY_CODE
257
258
EnumerateOneDir(const FString & phyPrefix,CObjectVector<NFind::CFileInfo> & files)259 HRESULT CDirItems::EnumerateOneDir(const FString &phyPrefix, CObjectVector<NFind::CFileInfo> &files)
260 {
261 NFind::CEnumerator enumerator;
262 // printf("\n enumerator.SetDirPrefix(phyPrefix) \n");
263
264 enumerator.SetDirPrefix(phyPrefix);
265
266 #ifdef _WIN32
267
268 NFind::CFileInfo fi;
269
270 for (unsigned ttt = 0; ; ttt++)
271 {
272 bool found;
273 if (!enumerator.Next(fi, found))
274 return AddError(phyPrefix);
275 if (!found)
276 return S_OK;
277 files.Add(fi);
278 if (Callback && (ttt & kScanProgressStepMask) == kScanProgressStepMask)
279 {
280 RINOK(ScanProgress(phyPrefix));
281 }
282 }
283
284 #else // _WIN32
285
286 // enumerator.SolveLinks = !SymLinks;
287
288 CObjectVector<NFind::CDirEntry> entries;
289
290 for (unsigned ttt = 0; ; ttt++)
291 {
292 bool found;
293 NFind::CDirEntry de;
294 if (!enumerator.Next(de, found))
295 {
296 return AddError(phyPrefix);
297 }
298 if (!found)
299 break;
300 entries.Add(de);
301 }
302
303 FOR_VECTOR(i, entries)
304 {
305 const NFind::CDirEntry &de = entries[i];
306 NFind::CFileInfo fi;
307 if (!enumerator.Fill_FileInfo(de, fi, !SymLinks))
308 // if (!fi.Find_AfterEnumerator(path))
309 {
310 const FString path = phyPrefix + de.Name;
311 {
312 RINOK(AddError(path));
313 continue;
314 }
315 }
316
317 files.Add(fi);
318
319 if (Callback && (i & kScanProgressStepMask) == kScanProgressStepMask)
320 {
321 RINOK(ScanProgress(phyPrefix));
322 }
323 }
324
325 return S_OK;
326
327 #endif // _WIN32
328 }
329
330
331
332
EnumerateDir(int phyParent,int logParent,const FString & phyPrefix)333 HRESULT CDirItems::EnumerateDir(int phyParent, int logParent, const FString &phyPrefix)
334 {
335 RINOK(ScanProgress(phyPrefix));
336
337 CObjectVector<NFind::CFileInfo> files;
338 RINOK(EnumerateOneDir(phyPrefix, files));
339
340 FOR_VECTOR (i, files)
341 {
342 #ifdef _WIN32
343 const NFind::CFileInfo &fi = files[i];
344 #else
345 const NFind::CFileInfo &fi = files[i];
346 /*
347 NFind::CFileInfo fi;
348 {
349 const NFind::CDirEntry &di = files[i];
350 const FString path = phyPrefix + di.Name;
351 if (!fi.Find_AfterEnumerator(path))
352 {
353 RINOK(AddError(path));
354 continue;
355 }
356 fi.Name = di.Name;
357 }
358 */
359 #endif
360
361 if (CanIncludeItem(fi.IsDir()))
362 {
363 int secureIndex = -1;
364 #ifdef _USE_SECURITY_CODE
365 if (ReadSecure)
366 {
367 RINOK(AddSecurityItem(phyPrefix + fi.Name, secureIndex));
368 }
369 #endif
370 AddDirFileInfo(phyParent, logParent, secureIndex, fi);
371 }
372
373 if (Callback && (i & kScanProgressStepMask) == kScanProgressStepMask)
374 {
375 RINOK(ScanProgress(phyPrefix));
376 }
377
378 if (fi.IsDir())
379 {
380 const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR;
381 unsigned parent = AddPrefix(phyParent, logParent, fs2us(name2));
382 RINOK(EnumerateDir((int)parent, (int)parent, phyPrefix + name2));
383 }
384 }
385 return S_OK;
386 }
387
388
389 /*
390 EnumerateItems2()
391 const FStringVector &filePaths - are path without tail slashes.
392 All dir prefixes of filePaths will be not stores in logical paths
393 fix it: we can scan AltStream also.
394 */
395
396 #ifdef _WIN32
397 // #define FOLLOW_LINK_PARAM
398 // #define FOLLOW_LINK_PARAM2
399 #define FOLLOW_LINK_PARAM , (!SymLinks)
400 #define FOLLOW_LINK_PARAM2 , (!dirItems.SymLinks)
401 #else
402 #define FOLLOW_LINK_PARAM , (!SymLinks)
403 #define FOLLOW_LINK_PARAM2 , (!dirItems.SymLinks)
404 #endif
405
EnumerateItems2(const FString & phyPrefix,const UString & logPrefix,const FStringVector & filePaths,FStringVector * requestedPaths)406 HRESULT CDirItems::EnumerateItems2(
407 const FString &phyPrefix,
408 const UString &logPrefix,
409 const FStringVector &filePaths,
410 FStringVector *requestedPaths)
411 {
412 const int phyParent = phyPrefix.IsEmpty() ? -1 : (int)AddPrefix(-1, -1, fs2us(phyPrefix));
413 const int logParent = logPrefix.IsEmpty() ? -1 : (int)AddPrefix(-1, -1, logPrefix);
414
415 FOR_VECTOR (i, filePaths)
416 {
417 const FString &filePath = filePaths[i];
418 NFind::CFileInfo fi;
419 const FString phyPath = phyPrefix + filePath;
420 if (!FindFile_KeepDots(fi, phyPath FOLLOW_LINK_PARAM))
421 {
422 RINOK(AddError(phyPath));
423 continue;
424 }
425 if (requestedPaths)
426 requestedPaths->Add(phyPath);
427
428 const int delimiter = filePath.ReverseFind_PathSepar();
429 FString phyPrefixCur;
430 int phyParentCur = phyParent;
431 if (delimiter >= 0)
432 {
433 phyPrefixCur.SetFrom(filePath, (unsigned)(delimiter + 1));
434 phyParentCur = (int)AddPrefix(phyParent, logParent, fs2us(phyPrefixCur));
435 }
436
437 if (CanIncludeItem(fi.IsDir()))
438 {
439 int secureIndex = -1;
440 #ifdef _USE_SECURITY_CODE
441 if (ReadSecure)
442 {
443 RINOK(AddSecurityItem(phyPath, secureIndex));
444 }
445 #endif
446 AddDirFileInfo(phyParentCur, logParent, secureIndex, fi);
447 }
448
449 if (fi.IsDir())
450 {
451 const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR;
452 unsigned parent = AddPrefix(phyParentCur, logParent, fs2us(name2));
453 RINOK(EnumerateDir((int)parent, (int)parent, phyPrefix + phyPrefixCur + name2));
454 }
455 }
456
457 ReserveDown();
458 return S_OK;
459 }
460
461
462
463
464 static HRESULT EnumerateDirItems(
465 const NWildcard::CCensorNode &curNode,
466 const int phyParent, const int logParent,
467 const FString &phyPrefix,
468 const UStringVector &addParts, // additional parts from curNode
469 CDirItems &dirItems,
470 bool enterToSubFolders);
471
472
473 /* EnumerateDirItems_Spec()
474 adds new Dir item prefix, and enumerates dir items,
475 then it can remove that Dir item prefix, if there are no items in that dir.
476 */
477
478
479 /*
480 EnumerateDirItems_Spec()
481 it's similar to EnumerateDirItems, but phyPrefix doesn't include (curFolderName)
482 */
483
EnumerateDirItems_Spec(const NWildcard::CCensorNode & curNode,const int phyParent,const int logParent,const FString & curFolderName,const FString & phyPrefix,const UStringVector & addParts,CDirItems & dirItems,bool enterToSubFolders)484 static HRESULT EnumerateDirItems_Spec(
485 const NWildcard::CCensorNode &curNode,
486 const int phyParent, const int logParent, const FString &curFolderName,
487 const FString &phyPrefix, // without (curFolderName)
488 const UStringVector &addParts, // (curNode + addParts) includes (curFolderName)
489 CDirItems &dirItems,
490 bool enterToSubFolders)
491 {
492 const FString name2 = curFolderName + FCHAR_PATH_SEPARATOR;
493 const unsigned parent = dirItems.AddPrefix(phyParent, logParent, fs2us(name2));
494 const unsigned numItems = dirItems.Items.Size();
495 HRESULT res = EnumerateDirItems(
496 curNode, (int)parent, (int)parent, phyPrefix + name2,
497 addParts, dirItems, enterToSubFolders);
498 if (numItems == dirItems.Items.Size())
499 dirItems.DeleteLastPrefix();
500 return res;
501 }
502
503
504 #ifndef UNDER_CE
505
506 #ifdef _WIN32
507
EnumerateAltStreams(const NFind::CFileInfo & fi,const NWildcard::CCensorNode & curNode,const int phyParent,const int logParent,const FString & phyPath,const UStringVector & addParts,bool addAllSubStreams,CDirItems & dirItems)508 static HRESULT EnumerateAltStreams(
509 const NFind::CFileInfo &fi,
510 const NWildcard::CCensorNode &curNode,
511 const int phyParent, const int logParent,
512 const FString &phyPath, // with (fi.Name), without tail slash for folders
513 const UStringVector &addParts, // with (fi.Name), prefix parts from curNode
514 bool addAllSubStreams,
515 CDirItems &dirItems)
516 {
517 // we don't use (ExcludeFileItems) rules for AltStreams
518 // if (dirItems.ExcludeFileItems) return S_OK;
519
520 NFind::CStreamEnumerator enumerator(phyPath);
521 for (;;)
522 {
523 NFind::CStreamInfo si;
524 bool found;
525 if (!enumerator.Next(si, found))
526 {
527 return dirItems.AddError(phyPath + FTEXT(":*")); // , (DWORD)E_FAIL
528 }
529 if (!found)
530 return S_OK;
531 if (si.IsMainStream())
532 continue;
533 UStringVector parts = addParts;
534 const UString reducedName = si.GetReducedName();
535 parts.Back() += reducedName;
536 if (curNode.CheckPathToRoot(false, parts, true))
537 continue;
538 if (!addAllSubStreams)
539 if (!curNode.CheckPathToRoot(true, parts, true))
540 continue;
541
542 NFind::CFileInfo fi2 = fi;
543 fi2.Name += us2fs(reducedName);
544 fi2.Size = si.Size;
545 fi2.Attrib &= ~(DWORD)(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT);
546 fi2.IsAltStream = true;
547 dirItems.AddDirFileInfo(phyParent, logParent, -1, fi2);
548 }
549 }
550
551 #endif // _WIN32
552
553
554 /* We get Reparse data and parse it.
555 If there is Reparse error, we free dirItem.Reparse data.
556 Do we need to work with empty reparse data?
557 */
558
SetLinkInfo(CDirItem & dirItem,const NFind::CFileInfo & fi,const FString & phyPrefix)559 HRESULT CDirItems::SetLinkInfo(CDirItem &dirItem, const NFind::CFileInfo &fi,
560 const FString &phyPrefix)
561 {
562 if (!SymLinks)
563 return S_OK;
564
565 #ifdef _WIN32
566 if (!fi.HasReparsePoint() || fi.IsAltStream)
567 #else // _WIN32
568 if (!fi.IsPosixLink())
569 #endif // _WIN32
570 return S_OK;
571
572 const FString path = phyPrefix + fi.Name;
573 CByteBuffer &buf = dirItem.ReparseData;
574 if (NIO::GetReparseData(path, buf))
575 {
576 // if (dirItem.ReparseData.Size() != 0)
577 Stat.FilesSize -= fi.Size;
578 return S_OK;
579 }
580
581 DWORD res = ::GetLastError();
582 buf.Free();
583 return AddError(path, res);
584 }
585
586 #endif // UNDER_CE
587
588
589
EnumerateForItem(const NFind::CFileInfo & fi,const NWildcard::CCensorNode & curNode,const int phyParent,const int logParent,const FString & phyPrefix,const UStringVector & addParts,CDirItems & dirItems,bool enterToSubFolders)590 static HRESULT EnumerateForItem(
591 const NFind::CFileInfo &fi,
592 const NWildcard::CCensorNode &curNode,
593 const int phyParent, const int logParent, const FString &phyPrefix,
594 const UStringVector &addParts, // additional parts from curNode, without (fi.Name)
595 CDirItems &dirItems,
596 bool enterToSubFolders)
597 {
598 const UString name = fs2us(fi.Name);
599 UStringVector newParts = addParts;
600 newParts.Add(name);
601
602 // check the path in exclude rules
603 if (curNode.CheckPathToRoot(false, newParts, !fi.IsDir()))
604 return S_OK;
605
606 #if !defined(UNDER_CE)
607 int dirItemIndex = -1;
608 #if defined(_WIN32)
609 bool addAllSubStreams = false;
610 bool needAltStreams = true;
611 #endif // _WIN32
612 #endif // !defined(UNDER_CE)
613
614 // check the path in inlcude rules
615 if (curNode.CheckPathToRoot(true, newParts, !fi.IsDir()))
616 {
617 #if !defined(UNDER_CE)
618 // dirItemIndex = (int)dirItems.Items.Size();
619 #if defined(_WIN32)
620 // we will not check include rules for substreams.
621 addAllSubStreams = true;
622 #endif // _WIN32
623 #endif // !defined(UNDER_CE)
624
625 if (dirItems.CanIncludeItem(fi.IsDir()))
626 {
627 int secureIndex = -1;
628 #ifdef _USE_SECURITY_CODE
629 if (dirItems.ReadSecure)
630 {
631 RINOK(dirItems.AddSecurityItem(phyPrefix + fi.Name, secureIndex));
632 }
633 #endif
634 #if !defined(UNDER_CE)
635 dirItemIndex = (int)dirItems.Items.Size();
636 #endif // !defined(UNDER_CE)
637 dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi);
638 }
639 else
640 {
641 #if defined(_WIN32) && !defined(UNDER_CE)
642 needAltStreams = false;
643 #endif
644 }
645
646 if (fi.IsDir())
647 enterToSubFolders = true;
648 }
649
650 #if !defined(UNDER_CE)
651
652 // we don't scan AltStreams for link files
653
654 if (dirItemIndex >= 0)
655 {
656 CDirItem &dirItem = dirItems.Items[(unsigned)dirItemIndex];
657 RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix));
658 if (dirItem.ReparseData.Size() != 0)
659 return S_OK;
660 }
661
662 #if defined(_WIN32)
663 if (needAltStreams && dirItems.ScanAltStreams)
664 {
665 RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent,
666 phyPrefix + fi.Name, // with (fi.Name)
667 newParts, // with (fi.Name)
668 addAllSubStreams,
669 dirItems));
670 }
671 #endif
672
673 #endif // !defined(UNDER_CE)
674
675
676 #ifndef _WIN32
677 if (!fi.IsPosixLink()) // posix link can follow to dir
678 #endif
679 if (!fi.IsDir())
680 return S_OK;
681
682 const NWildcard::CCensorNode *nextNode = NULL;
683
684 if (addParts.IsEmpty())
685 {
686 int index = curNode.FindSubNode(name);
687 if (index >= 0)
688 {
689 nextNode = &curNode.SubNodes[(unsigned)index];
690 newParts.Clear();
691 }
692 }
693
694 if (!nextNode)
695 {
696 if (!enterToSubFolders)
697 return S_OK;
698
699 #ifndef _WIN32
700 if (fi.IsPosixLink())
701 {
702 // here we can try to resolve posix link
703 // if the link to dir, then can we follow it
704 return S_OK; // we don't follow posix link
705 }
706 #else
707 if (dirItems.SymLinks && fi.HasReparsePoint())
708 {
709 /* 20.03: in SymLinks mode: we don't enter to directory that
710 has reparse point and has no CCensorNode
711 NOTE: (curNode and parent nodes) still can have wildcard rules
712 to include some items of target directory (of reparse point),
713 but we ignore these rules here.
714 */
715 return S_OK;
716 }
717 #endif
718 nextNode = &curNode;
719 }
720
721 return EnumerateDirItems_Spec(
722 *nextNode, phyParent, logParent, fi.Name,
723 phyPrefix, // without (fi.Name)
724 newParts, // relative to (*nextNode). (*nextNode + newParts) includes (fi.Name)
725 dirItems,
726 enterToSubFolders);
727 }
728
729
CanUseFsDirect(const NWildcard::CCensorNode & curNode)730 static bool CanUseFsDirect(const NWildcard::CCensorNode &curNode)
731 {
732 FOR_VECTOR (i, curNode.IncludeItems)
733 {
734 const NWildcard::CItem &item = curNode.IncludeItems[i];
735 if (item.Recursive || item.PathParts.Size() != 1)
736 return false;
737 const UString &name = item.PathParts.Front();
738 /*
739 if (name.IsEmpty())
740 return false;
741 */
742
743 /* Windows doesn't support file name with wildcard
744 But if another system supports file name with wildcard,
745 and wildcard mode is disabled, we can ignore wildcard in name
746 */
747 /*
748 #ifndef _WIN32
749 if (!item.WildcardParsing)
750 continue;
751 #endif
752 */
753 if (DoesNameContainWildcard(name))
754 return false;
755 }
756 return true;
757 }
758
759
760 #if defined(_WIN32) && !defined(UNDER_CE)
761
IsVirtualFsFolder(const FString & prefix,const UString & name)762 static bool IsVirtualFsFolder(const FString &prefix, const UString &name)
763 {
764 UString s = fs2us(prefix);
765 s += name;
766 s.Add_PathSepar();
767 // it returns (true) for non real FS folder path like - "\\SERVER\"
768 return IsPathSepar(s[0]) && GetRootPrefixSize(s) == 0;
769 }
770
771 #endif
772
773
774
EnumerateDirItems(const NWildcard::CCensorNode & curNode,const int phyParent,const int logParent,const FString & phyPrefix,const UStringVector & addParts,CDirItems & dirItems,bool enterToSubFolders)775 static HRESULT EnumerateDirItems(
776 const NWildcard::CCensorNode &curNode,
777 const int phyParent, const int logParent, const FString &phyPrefix,
778 const UStringVector &addParts, // prefix from curNode including
779 CDirItems &dirItems,
780 bool enterToSubFolders)
781 {
782 if (!enterToSubFolders)
783 {
784 /* if there are IncludeItems censor rules that affect items in subdirs,
785 then we will enter to all subfolders */
786 if (curNode.NeedCheckSubDirs())
787 enterToSubFolders = true;
788 }
789
790 RINOK(dirItems.ScanProgress(phyPrefix));
791
792 // try direct_names case at first
793 if (addParts.IsEmpty() && !enterToSubFolders)
794 {
795 if (CanUseFsDirect(curNode))
796 {
797 // all names are direct (no wildcards)
798 // so we don't need file_system's dir enumerator
799 CRecordVector<bool> needEnterVector;
800 unsigned i;
801
802 for (i = 0; i < curNode.IncludeItems.Size(); i++)
803 {
804 const NWildcard::CItem &item = curNode.IncludeItems[i];
805 const UString &name = item.PathParts.Front();
806 FString fullPath = phyPrefix + us2fs(name);
807
808 /*
809 // not possible now
810 if (!item.ForDir && !item.ForFile)
811 {
812 RINOK(dirItems.AddError(fullPath, ERROR_INVALID_PARAMETER));
813 continue;
814 }
815 */
816
817 #if defined(_WIN32) && !defined(UNDER_CE)
818 bool needAltStreams = true;
819 #endif
820
821 #ifdef _USE_SECURITY_CODE
822 bool needSecurity = true;
823 #endif
824
825 if (phyPrefix.IsEmpty())
826 {
827 if (!item.ForFile)
828 {
829 /* we don't like some names for alt streams inside archive:
830 ":sname" for "\"
831 "c:::sname" for "C:\"
832 So we ignore alt streams for these cases */
833 if (name.IsEmpty())
834 {
835 #if defined(_WIN32) && !defined(UNDER_CE)
836 needAltStreams = false;
837 #endif
838
839 /*
840 // do we need to ignore security info for "\\" folder ?
841 #ifdef _USE_SECURITY_CODE
842 needSecurity = false;
843 #endif
844 */
845
846 fullPath = CHAR_PATH_SEPARATOR;
847 }
848 #if defined(_WIN32) && !defined(UNDER_CE)
849 else if (item.IsDriveItem())
850 {
851 needAltStreams = false;
852 fullPath.Add_PathSepar();
853 }
854 #endif
855 }
856 }
857
858 NFind::CFileInfo fi;
859 #if defined(_WIN32) && !defined(UNDER_CE)
860 if (IsVirtualFsFolder(phyPrefix, name))
861 {
862 fi.SetAsDir();
863 fi.Name = us2fs(name);
864 }
865 else
866 #endif
867 if (!FindFile_KeepDots(fi, fullPath FOLLOW_LINK_PARAM2))
868 {
869 RINOK(dirItems.AddError(fullPath));
870 continue;
871 }
872
873 /*
874 #ifdef _WIN32
875 #define MY_ERROR_IS_DIR ERROR_FILE_NOT_FOUND
876 #define MY_ERROR_NOT_DIR DI_DEFAULT_ERROR
877 #else
878 #define MY_ERROR_IS_DIR EISDIR
879 #define MY_ERROR_NOT_DIR ENOTDIR
880 #endif
881 */
882
883 const bool isDir = fi.IsDir();
884 if (isDir ? !item.ForDir : !item.ForFile)
885 {
886 // RINOK(dirItems.AddError(fullPath, isDir ? MY_ERROR_IS_DIR: MY_ERROR_NOT_DIR));
887 RINOK(dirItems.AddError(fullPath, DI_DEFAULT_ERROR));
888 continue;
889 }
890 {
891 UStringVector pathParts;
892 pathParts.Add(fs2us(fi.Name));
893 if (curNode.CheckPathToRoot(false, pathParts, !isDir))
894 continue;
895 }
896
897
898 if (dirItems.CanIncludeItem(fi.IsDir()))
899 {
900 int secureIndex = -1;
901 #ifdef _USE_SECURITY_CODE
902 if (needSecurity && dirItems.ReadSecure)
903 {
904 RINOK(dirItems.AddSecurityItem(fullPath, secureIndex));
905 }
906 #endif
907
908 dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi);
909
910 // we don't scan AltStreams for link files
911
912 #if !defined(UNDER_CE)
913 {
914 CDirItem &dirItem = dirItems.Items.Back();
915 RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix));
916 if (dirItem.ReparseData.Size() != 0)
917 continue;
918 }
919
920 #if defined(_WIN32)
921 if (needAltStreams && dirItems.ScanAltStreams)
922 {
923 UStringVector pathParts;
924 pathParts.Add(fs2us(fi.Name));
925 RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent,
926 fullPath, // including (name)
927 pathParts, // including (fi.Name)
928 true, /* addAllSubStreams */
929 dirItems));
930 }
931 #endif // defined(_WIN32)
932
933 #endif // !defined(UNDER_CE)
934 }
935
936
937 #ifndef _WIN32
938 if (!fi.IsPosixLink()) // posix link can follow to dir
939 #endif
940 if (!isDir)
941 continue;
942
943 UStringVector newParts;
944 const NWildcard::CCensorNode *nextNode = NULL;
945 int index = curNode.FindSubNode(name);
946 if (index >= 0)
947 {
948 for (int t = (int)needEnterVector.Size(); t <= index; t++)
949 needEnterVector.Add(true);
950 needEnterVector[(unsigned)index] = false;
951 nextNode = &curNode.SubNodes[(unsigned)index];
952 }
953 else
954 {
955 #ifndef _WIN32
956 if (fi.IsPosixLink())
957 {
958 // here we can try to resolve posix link
959 // if the link to dir, then can we follow it
960 continue; // we don't follow posix link
961 }
962 #else
963 if (dirItems.SymLinks)
964 {
965 if (fi.HasReparsePoint())
966 {
967 /* 20.03: in SymLinks mode: we don't enter to directory that
968 has reparse point and has no CCensorNode */
969 continue;
970 }
971 }
972 #endif
973 nextNode = &curNode;
974 newParts.Add(name); // don't change it to fi.Name. It's for shortnames support
975 }
976
977 RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix,
978 newParts, dirItems, true));
979 }
980
981 for (i = 0; i < curNode.SubNodes.Size(); i++)
982 {
983 if (i < needEnterVector.Size())
984 if (!needEnterVector[i])
985 continue;
986 const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i];
987 FString fullPath = phyPrefix + us2fs(nextNode.Name);
988 NFind::CFileInfo fi;
989
990 if (phyPrefix.IsEmpty())
991 {
992 {
993 if (nextNode.Name.IsEmpty())
994 fullPath = CHAR_PATH_SEPARATOR;
995 #ifdef _WIN32
996 else if (NWildcard::IsDriveColonName(nextNode.Name))
997 fullPath.Add_PathSepar();
998 #endif
999 }
1000 }
1001
1002 // we don't want to call fi.Find() for root folder or virtual folder
1003 if ((phyPrefix.IsEmpty() && nextNode.Name.IsEmpty())
1004 #if defined(_WIN32) && !defined(UNDER_CE)
1005 || IsVirtualFsFolder(phyPrefix, nextNode.Name)
1006 #endif
1007 )
1008 {
1009 fi.SetAsDir();
1010 fi.Name = us2fs(nextNode.Name);
1011 }
1012 else
1013 {
1014 if (!FindFile_KeepDots(fi, fullPath FOLLOW_LINK_PARAM2))
1015 {
1016 if (!nextNode.AreThereIncludeItems())
1017 continue;
1018 RINOK(dirItems.AddError(fullPath));
1019 continue;
1020 }
1021
1022 if (!fi.IsDir())
1023 {
1024 RINOK(dirItems.AddError(fullPath, DI_DEFAULT_ERROR));
1025 continue;
1026 }
1027 }
1028
1029 RINOK(EnumerateDirItems_Spec(nextNode, phyParent, logParent, fi.Name, phyPrefix,
1030 UStringVector(), dirItems, false));
1031 }
1032
1033 return S_OK;
1034 }
1035 }
1036
1037 #ifdef _WIN32
1038 #ifndef UNDER_CE
1039
1040 // scan drives, if wildcard is "*:\"
1041
1042 if (phyPrefix.IsEmpty() && curNode.IncludeItems.Size() > 0)
1043 {
1044 unsigned i;
1045 for (i = 0; i < curNode.IncludeItems.Size(); i++)
1046 {
1047 const NWildcard::CItem &item = curNode.IncludeItems[i];
1048 if (item.PathParts.Size() < 1)
1049 break;
1050 const UString &name = item.PathParts.Front();
1051 if (name.Len() != 2 || name[1] != ':')
1052 break;
1053 if (item.PathParts.Size() == 1)
1054 if (item.ForFile || !item.ForDir)
1055 break;
1056 if (NWildcard::IsDriveColonName(name))
1057 continue;
1058 if (name[0] != '*' && name[0] != '?')
1059 break;
1060 }
1061 if (i == curNode.IncludeItems.Size())
1062 {
1063 FStringVector driveStrings;
1064 NFind::MyGetLogicalDriveStrings(driveStrings);
1065 for (i = 0; i < driveStrings.Size(); i++)
1066 {
1067 FString driveName = driveStrings[i];
1068 if (driveName.Len() < 3 || driveName.Back() != '\\')
1069 return E_FAIL;
1070 driveName.DeleteBack();
1071 NFind::CFileInfo fi;
1072 fi.SetAsDir();
1073 fi.Name = driveName;
1074
1075 RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix,
1076 addParts, dirItems, enterToSubFolders));
1077 }
1078 return S_OK;
1079 }
1080 }
1081
1082 #endif
1083 #endif
1084
1085
1086 CObjectVector<NFind::CFileInfo> files;
1087
1088 // for (int y = 0; y < 1; y++)
1089 {
1090 // files.Clear();
1091 RINOK(dirItems.EnumerateOneDir(phyPrefix, files));
1092 /*
1093 FOR_VECTOR (i, files)
1094 {
1095 #ifdef _WIN32
1096 // const NFind::CFileInfo &fi = files[i];
1097 #else
1098 NFind::CFileInfo &fi = files[i];
1099 {
1100 const NFind::CFileInfo &di = files[i];
1101 const FString path = phyPrefix + di.Name;
1102 if (!fi.Find_AfterEnumerator(path))
1103 {
1104 RINOK(dirItems.AddError(path));
1105 continue;
1106 }
1107 fi.Name = di.Name;
1108 }
1109 #endif
1110
1111 }
1112 */
1113 }
1114
1115 FOR_VECTOR (i, files)
1116 {
1117 #ifdef _WIN32
1118 const NFind::CFileInfo &fi = files[i];
1119 #else
1120 const NFind::CFileInfo &fi = files[i];
1121 /*
1122 NFind::CFileInfo fi;
1123 {
1124 const NFind::CDirEntry &di = files[i];
1125 const FString path = phyPrefix + di.Name;
1126 if (!fi.Find_AfterEnumerator(path))
1127 {
1128 RINOK(dirItems.AddError(path));
1129 continue;
1130 }
1131 fi.Name = di.Name;
1132 }
1133 */
1134 #endif
1135
1136 RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix,
1137 addParts, dirItems, enterToSubFolders));
1138 if (dirItems.Callback && (i & kScanProgressStepMask) == kScanProgressStepMask)
1139 {
1140 RINOK(dirItems.ScanProgress(phyPrefix));
1141 }
1142 }
1143
1144 return S_OK;
1145 }
1146
1147
1148
1149
EnumerateItems(const NWildcard::CCensor & censor,const NWildcard::ECensorPathMode pathMode,const UString & addPathPrefix,CDirItems & dirItems)1150 HRESULT EnumerateItems(
1151 const NWildcard::CCensor &censor,
1152 const NWildcard::ECensorPathMode pathMode,
1153 const UString &addPathPrefix, // prefix that will be added to Logical Path
1154 CDirItems &dirItems)
1155 {
1156 FOR_VECTOR (i, censor.Pairs)
1157 {
1158 const NWildcard::CPair &pair = censor.Pairs[i];
1159 const int phyParent = pair.Prefix.IsEmpty() ? -1 : (int)dirItems.AddPrefix(-1, -1, pair.Prefix);
1160 int logParent = -1;
1161
1162 if (pathMode == NWildcard::k_AbsPath)
1163 logParent = phyParent;
1164 else
1165 {
1166 if (!addPathPrefix.IsEmpty())
1167 logParent = (int)dirItems.AddPrefix(-1, -1, addPathPrefix);
1168 }
1169
1170 RINOK(EnumerateDirItems(pair.Head, phyParent, logParent, us2fs(pair.Prefix), UStringVector(),
1171 dirItems,
1172 false // enterToSubFolders
1173 ));
1174 }
1175 dirItems.ReserveDown();
1176
1177 #if defined(_WIN32) && !defined(UNDER_CE)
1178 RINOK(dirItems.FillFixedReparse());
1179 #endif
1180
1181 #ifndef _WIN32
1182 RINOK(dirItems.FillDeviceSizes());
1183 #endif
1184
1185 return S_OK;
1186 }
1187
1188
1189 #if defined(_WIN32) && !defined(UNDER_CE)
1190
FillFixedReparse()1191 HRESULT CDirItems::FillFixedReparse()
1192 {
1193 FOR_VECTOR(i, Items)
1194 {
1195 CDirItem &item = Items[i];
1196
1197 if (!SymLinks)
1198 {
1199 // continue; // for debug
1200 if (!item.Has_Attrib_ReparsePoint())
1201 continue;
1202
1203 // if (item.IsDir()) continue;
1204
1205 const FString phyPath = GetPhyPath(i);
1206
1207 NFind::CFileInfo fi;
1208 if (fi.Fill_From_ByHandleFileInfo(phyPath)) // item.IsDir()
1209 {
1210 item.Size = fi.Size;
1211 item.CTime = fi.CTime;
1212 item.ATime = fi.ATime;
1213 item.MTime = fi.MTime;
1214 item.Attrib = fi.Attrib;
1215 continue;
1216 }
1217
1218 /*
1219 // we request properties of target file instead of properies of symbolic link
1220 // here we also can manually parse unsupported links (like WSL links)
1221 NIO::CInFile inFile;
1222 if (inFile.Open(phyPath))
1223 {
1224 BY_HANDLE_FILE_INFORMATION info;
1225 if (inFile.GetFileInformation(&info))
1226 {
1227 // Stat.FilesSize doesn't contain item.Size already
1228 // Stat.FilesSize -= item.Size;
1229 item.Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow;
1230 Stat.FilesSize += item.Size;
1231 item.CTime = info.ftCreationTime;
1232 item.ATime = info.ftLastAccessTime;
1233 item.MTime = info.ftLastWriteTime;
1234 item.Attrib = info.dwFileAttributes;
1235 continue;
1236 }
1237 }
1238 */
1239
1240 RINOK(AddError(phyPath));
1241 continue;
1242 }
1243
1244 // (SymLinks == true) here
1245
1246 if (item.ReparseData.Size() == 0)
1247 continue;
1248
1249 // if (item.Size == 0)
1250 {
1251 // 20.03: we use Reparse Data instead of real data
1252 item.Size = item.ReparseData.Size();
1253 }
1254
1255 CReparseAttr attr;
1256 if (!attr.Parse(item.ReparseData, item.ReparseData.Size()))
1257 {
1258 const FString phyPath = GetPhyPath(i);
1259 AddError(phyPath, attr.ErrorCode);
1260 continue;
1261 }
1262
1263 /* imagex/WIM reduces absolute paths in links (raparse data),
1264 if we archive non root folder. We do same thing here */
1265
1266 bool isWSL = false;
1267 if (attr.IsSymLink_WSL())
1268 {
1269 // isWSL = true;
1270 // we don't change WSL symlinks
1271 continue;
1272 }
1273 else
1274 {
1275 if (attr.IsRelative_Win())
1276 continue;
1277 }
1278
1279 const UString &link = attr.GetPath();
1280 if (!IsDrivePath(link))
1281 continue;
1282 // maybe we need to support networks paths also ?
1283
1284 FString fullPathF;
1285 if (!NDir::MyGetFullPathName(GetPhyPath(i), fullPathF))
1286 continue;
1287 const UString fullPath = fs2us(fullPathF);
1288 const UString logPath = GetLogPath(i);
1289 if (logPath.Len() >= fullPath.Len())
1290 continue;
1291 if (CompareFileNames(logPath, fullPath.RightPtr(logPath.Len())) != 0)
1292 continue;
1293
1294 const UString prefix = fullPath.Left(fullPath.Len() - logPath.Len());
1295 if (!IsPathSepar(prefix.Back()))
1296 continue;
1297
1298 const unsigned rootPrefixSize = GetRootPrefixSize(prefix);
1299 if (rootPrefixSize == 0)
1300 continue;
1301 if (rootPrefixSize == prefix.Len())
1302 continue; // simple case: paths are from root
1303
1304 if (link.Len() <= prefix.Len())
1305 continue;
1306
1307 if (CompareFileNames(link.Left(prefix.Len()), prefix) != 0)
1308 continue;
1309
1310 UString newLink = prefix.Left(rootPrefixSize);
1311 newLink += link.Ptr(prefix.Len());
1312
1313 CByteBuffer data;
1314 bool isSymLink = !attr.IsMountPoint();
1315 if (!FillLinkData(data, newLink, isSymLink, isWSL))
1316 continue;
1317 item.ReparseData2 = data;
1318 }
1319 return S_OK;
1320 }
1321
1322 #endif
1323
1324
1325 #ifndef _WIN32
1326
FillDeviceSizes()1327 HRESULT CDirItems::FillDeviceSizes()
1328 {
1329 {
1330 FOR_VECTOR (i, Items)
1331 {
1332 CDirItem &item = Items[i];
1333
1334 if (S_ISBLK(item.mode) && item.Size == 0)
1335 {
1336 const FString phyPath = GetPhyPath(i);
1337 NIO::CInFile inFile;
1338 inFile.PreserveATime = true;
1339 if (inFile.OpenShared(phyPath, ShareForWrite)) // fixme: OpenShared ??
1340 {
1341 UInt64 size = 0;
1342 if (inFile.GetLength(size))
1343 item.Size = size;
1344 }
1345 }
1346 if (StoreOwnerName)
1347 {
1348 OwnerNameMap.Add_UInt32(item.uid);
1349 OwnerGroupMap.Add_UInt32(item.gid);
1350 }
1351 }
1352 }
1353
1354 if (StoreOwnerName)
1355 {
1356 UString u;
1357 AString a;
1358 {
1359 FOR_VECTOR (i, OwnerNameMap.Numbers)
1360 {
1361 // 200K/sec speed
1362 u.Empty();
1363 const passwd *pw = getpwuid(OwnerNameMap.Numbers[i]);
1364 if (pw)
1365 {
1366 a = pw->pw_name;
1367 ConvertUTF8ToUnicode(a, u);
1368 }
1369 OwnerNameMap.Strings.Add(u);
1370 }
1371 }
1372 {
1373 FOR_VECTOR (i, OwnerGroupMap.Numbers)
1374 {
1375 u.Empty();
1376 const group *gr = getgrgid(OwnerGroupMap.Numbers[i]);
1377 if (gr)
1378 {
1379 // printf("\ngetgrgid %d %s\n", OwnerGroupMap.Numbers[i], gr->gr_name);
1380 a = gr->gr_name;
1381 ConvertUTF8ToUnicode(a, u);
1382 }
1383 OwnerGroupMap.Strings.Add(u);
1384 }
1385 }
1386
1387 FOR_VECTOR (i, Items)
1388 {
1389 CDirItem &item = Items[i];
1390 {
1391 const int index = OwnerNameMap.Find(item.uid);
1392 if (index < 0) throw 1;
1393 item.OwnerNameIndex = index;
1394 }
1395 {
1396 const int index = OwnerGroupMap.Find(item.gid);
1397 if (index < 0) throw 1;
1398 item.OwnerGroupIndex = index;
1399 }
1400 }
1401 }
1402
1403
1404 // if (NeedOwnerNames)
1405 {
1406 /*
1407 {
1408 for (unsigned i = 0 ; i < 10000; i++)
1409 {
1410 const passwd *pw = getpwuid(i);
1411 if (pw)
1412 {
1413 UString u;
1414 ConvertUTF8ToUnicode(AString(pw->pw_name), u);
1415 OwnerNameMap.Add(i, u);
1416 OwnerNameMap.Add(i, u);
1417 OwnerNameMap.Add(i, u);
1418 }
1419 const group *gr = getgrgid(i);
1420 if (gr)
1421 {
1422 // we can use utf-8 here.
1423 UString u;
1424 ConvertUTF8ToUnicode(AString(gr->gr_name), u);
1425 OwnerGroupMap.Add(i, u);
1426 }
1427 }
1428 }
1429 */
1430 /*
1431 {
1432 FOR_VECTOR (i, OwnerNameMap.Strings)
1433 {
1434 AString s;
1435 ConvertUnicodeToUTF8(OwnerNameMap.Strings[i], s);
1436 printf("\n%5d %s", (unsigned)OwnerNameMap.Numbers[i], s.Ptr());
1437 }
1438 }
1439 {
1440 printf("\n\n=========Groups\n");
1441 FOR_VECTOR (i, OwnerGroupMap.Strings)
1442 {
1443 AString s;
1444 ConvertUnicodeToUTF8(OwnerGroupMap.Strings[i], s);
1445 printf("\n%5d %s", (unsigned)OwnerGroupMap.Numbers[i], s.Ptr());
1446 }
1447 }
1448 */
1449 }
1450 /*
1451 for (unsigned i = 0 ; i < 100000000; i++)
1452 {
1453 // const passwd *pw = getpwuid(1000);
1454 // pw = pw;
1455 int pos = OwnerNameMap.Find(1000);
1456 if (pos < 0 - (int)i)
1457 throw 1;
1458 }
1459 */
1460
1461 return S_OK;
1462 }
1463
1464 #endif
1465
1466
1467
1468 static const char * const kCannotFindArchive = "Cannot find archive";
1469
EnumerateDirItemsAndSort(NWildcard::CCensor & censor,NWildcard::ECensorPathMode censorPathMode,const UString & addPathPrefix,UStringVector & sortedPaths,UStringVector & sortedFullPaths,CDirItemsStat & st,IDirItemsCallback * callback)1470 HRESULT EnumerateDirItemsAndSort(
1471 NWildcard::CCensor &censor,
1472 NWildcard::ECensorPathMode censorPathMode,
1473 const UString &addPathPrefix,
1474 UStringVector &sortedPaths,
1475 UStringVector &sortedFullPaths,
1476 CDirItemsStat &st,
1477 IDirItemsCallback *callback)
1478 {
1479 FStringVector paths;
1480
1481 {
1482 CDirItems dirItems;
1483 dirItems.Callback = callback;
1484 {
1485 HRESULT res = EnumerateItems(censor, censorPathMode, addPathPrefix, dirItems);
1486 st = dirItems.Stat;
1487 RINOK(res);
1488 }
1489
1490 FOR_VECTOR (i, dirItems.Items)
1491 {
1492 const CDirItem &dirItem = dirItems.Items[i];
1493 if (!dirItem.IsDir())
1494 paths.Add(dirItems.GetPhyPath(i));
1495 }
1496 }
1497
1498 if (paths.Size() == 0)
1499 {
1500 // return S_OK;
1501 throw CMessagePathException(kCannotFindArchive);
1502 }
1503
1504 UStringVector fullPaths;
1505
1506 unsigned i;
1507
1508 for (i = 0; i < paths.Size(); i++)
1509 {
1510 FString fullPath;
1511 NFile::NDir::MyGetFullPathName(paths[i], fullPath);
1512 fullPaths.Add(fs2us(fullPath));
1513 }
1514
1515 CUIntVector indices;
1516 SortFileNames(fullPaths, indices);
1517 sortedPaths.ClearAndReserve(indices.Size());
1518 sortedFullPaths.ClearAndReserve(indices.Size());
1519
1520 for (i = 0; i < indices.Size(); i++)
1521 {
1522 unsigned index = indices[i];
1523 sortedPaths.AddInReserved(fs2us(paths[index]));
1524 sortedFullPaths.AddInReserved(fullPaths[index]);
1525 if (i > 0 && CompareFileNames(sortedFullPaths[i], sortedFullPaths[i - 1]) == 0)
1526 throw CMessagePathException("Duplicate archive path:", sortedFullPaths[i]);
1527 }
1528
1529 return S_OK;
1530 }
1531
1532
1533
1534
1535 #ifdef _WIN32
1536
IsDotsName(const wchar_t * s)1537 static bool IsDotsName(const wchar_t *s)
1538 {
1539 return s[0] == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0));
1540 }
1541
1542 // This code converts all short file names to long file names.
1543
ConvertToLongName(const UString & prefix,UString & name)1544 static void ConvertToLongName(const UString &prefix, UString &name)
1545 {
1546 if (name.IsEmpty()
1547 || DoesNameContainWildcard(name)
1548 || IsDotsName(name))
1549 return;
1550 NFind::CFileInfo fi;
1551 const FString path (us2fs(prefix + name));
1552 #ifndef UNDER_CE
1553 if (NFile::NName::IsDevicePath(path))
1554 return;
1555 #endif
1556 if (fi.Find(path))
1557 name = fs2us(fi.Name);
1558 }
1559
ConvertToLongNames(const UString & prefix,CObjectVector<NWildcard::CItem> & items)1560 static void ConvertToLongNames(const UString &prefix, CObjectVector<NWildcard::CItem> &items)
1561 {
1562 FOR_VECTOR (i, items)
1563 {
1564 NWildcard::CItem &item = items[i];
1565 if (item.Recursive || item.PathParts.Size() != 1)
1566 continue;
1567 if (prefix.IsEmpty() && item.IsDriveItem())
1568 continue;
1569 ConvertToLongName(prefix, item.PathParts.Front());
1570 }
1571 }
1572
ConvertToLongNames(const UString & prefix,NWildcard::CCensorNode & node)1573 static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node)
1574 {
1575 ConvertToLongNames(prefix, node.IncludeItems);
1576 ConvertToLongNames(prefix, node.ExcludeItems);
1577 unsigned i;
1578 for (i = 0; i < node.SubNodes.Size(); i++)
1579 {
1580 UString &name = node.SubNodes[i].Name;
1581 if (prefix.IsEmpty() && NWildcard::IsDriveColonName(name))
1582 continue;
1583 ConvertToLongName(prefix, name);
1584 }
1585 // mix folders with same name
1586 for (i = 0; i < node.SubNodes.Size(); i++)
1587 {
1588 NWildcard::CCensorNode &nextNode1 = node.SubNodes[i];
1589 for (unsigned j = i + 1; j < node.SubNodes.Size();)
1590 {
1591 const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j];
1592 if (nextNode1.Name.IsEqualTo_NoCase(nextNode2.Name))
1593 {
1594 nextNode1.IncludeItems += nextNode2.IncludeItems;
1595 nextNode1.ExcludeItems += nextNode2.ExcludeItems;
1596 node.SubNodes.Delete(j);
1597 }
1598 else
1599 j++;
1600 }
1601 }
1602 for (i = 0; i < node.SubNodes.Size(); i++)
1603 {
1604 NWildcard::CCensorNode &nextNode = node.SubNodes[i];
1605 ConvertToLongNames(prefix + nextNode.Name + WCHAR_PATH_SEPARATOR, nextNode);
1606 }
1607 }
1608
ConvertToLongNames(NWildcard::CCensor & censor)1609 void ConvertToLongNames(NWildcard::CCensor &censor)
1610 {
1611 FOR_VECTOR (i, censor.Pairs)
1612 {
1613 NWildcard::CPair &pair = censor.Pairs[i];
1614 ConvertToLongNames(pair.Prefix, pair.Head);
1615 }
1616 }
1617
1618 #endif
1619
1620
CMessagePathException(const char * a,const wchar_t * u)1621 CMessagePathException::CMessagePathException(const char *a, const wchar_t *u)
1622 {
1623 (*this) += a;
1624 if (u)
1625 {
1626 Add_LF();
1627 (*this) += u;
1628 }
1629 }
1630
CMessagePathException(const wchar_t * a,const wchar_t * u)1631 CMessagePathException::CMessagePathException(const wchar_t *a, const wchar_t *u)
1632 {
1633 (*this) += a;
1634 if (u)
1635 {
1636 Add_LF();
1637 (*this) += u;
1638 }
1639 }
1640