• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // EnumDirItems.cpp
2 
3 #include "StdAfx.h"
4 
5 #include <wchar.h>
6 
7 #include "../../../Common/Wildcard.h"
8 
9 #include "../../../Windows/FileDir.h"
10 #include "../../../Windows/FileIO.h"
11 #include "../../../Windows/FileName.h"
12 
13 #if defined(_WIN32) && !defined(UNDER_CE)
14 #define _USE_SECURITY_CODE
15 #include "../../../Windows/SecurityUtils.h"
16 #endif
17 
18 #include "EnumDirItems.h"
19 
20 using namespace NWindows;
21 using namespace NFile;
22 using namespace NName;
23 
AddDirFileInfo(int phyParent,int logParent,int secureIndex,const NFind::CFileInfo & fi)24 void CDirItems::AddDirFileInfo(int phyParent, int logParent, int secureIndex,
25     const NFind::CFileInfo &fi)
26 {
27   CDirItem di;
28   di.Size = fi.Size;
29   di.CTime = fi.CTime;
30   di.ATime = fi.ATime;
31   di.MTime = fi.MTime;
32   di.Attrib = fi.Attrib;
33   di.IsAltStream = fi.IsAltStream;
34   di.PhyParent = phyParent;
35   di.LogParent = logParent;
36   di.SecureIndex = secureIndex;
37   di.Name = fs2us(fi.Name);
38   #if defined(_WIN32) && !defined(UNDER_CE)
39   // di.ShortName = fs2us(fi.ShortName);
40   #endif
41   Items.Add(di);
42 
43   if (fi.IsDir())
44     Stat.NumDirs++;
45   else if (fi.IsAltStream)
46   {
47     Stat.NumAltStreams++;
48     Stat.AltStreamsSize += fi.Size;
49   }
50   else
51   {
52     Stat.NumFiles++;
53     Stat.FilesSize += fi.Size;
54   }
55 }
56 
AddError(const FString & path,DWORD errorCode)57 HRESULT CDirItems::AddError(const FString &path, DWORD errorCode)
58 {
59   Stat.NumErrors++;
60   if (Callback)
61     return Callback->ScanError(path, errorCode);
62   return S_OK;
63 }
64 
AddError(const FString & path)65 HRESULT CDirItems::AddError(const FString &path)
66 {
67   return AddError(path, ::GetLastError());
68 }
69 
70 static const unsigned kScanProgressStepMask = (1 << 12) - 1;
71 
ScanProgress(const FString & dirPath)72 HRESULT CDirItems::ScanProgress(const FString &dirPath)
73 {
74   if (Callback)
75     return Callback->ScanProgress(Stat, dirPath, true);
76   return S_OK;
77 }
78 
GetPrefixesPath(const CIntVector & parents,int index,const UString & name) const79 UString CDirItems::GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const
80 {
81   UString path;
82   unsigned len = name.Len();
83 
84   int i;
85   for (i = index; i >= 0; i = parents[i])
86     len += Prefixes[i].Len();
87 
88   wchar_t *p = path.GetBuf_SetEnd(len) + len;
89 
90   p -= name.Len();
91   wmemcpy(p, (const wchar_t *)name, name.Len());
92 
93   for (i = index; i >= 0; i = parents[i])
94   {
95     const UString &s = Prefixes[i];
96     p -= s.Len();
97     wmemcpy(p, (const wchar_t *)s, s.Len());
98   }
99 
100   return path;
101 }
102 
GetPhyPath(unsigned index) const103 FString CDirItems::GetPhyPath(unsigned index) const
104 {
105   const CDirItem &di = Items[index];
106   return us2fs(GetPrefixesPath(PhyParents, di.PhyParent, di.Name));
107 }
108 
GetLogPath(unsigned index) const109 UString CDirItems::GetLogPath(unsigned index) const
110 {
111   const CDirItem &di = Items[index];
112   return GetPrefixesPath(LogParents, di.LogParent, di.Name);
113 }
114 
ReserveDown()115 void CDirItems::ReserveDown()
116 {
117   Prefixes.ReserveDown();
118   PhyParents.ReserveDown();
119   LogParents.ReserveDown();
120   Items.ReserveDown();
121 }
122 
AddPrefix(int phyParent,int logParent,const UString & prefix)123 unsigned CDirItems::AddPrefix(int phyParent, int logParent, const UString &prefix)
124 {
125   PhyParents.Add(phyParent);
126   LogParents.Add(logParent);
127   return Prefixes.Add(prefix);
128 }
129 
DeleteLastPrefix()130 void CDirItems::DeleteLastPrefix()
131 {
132   PhyParents.DeleteBack();
133   LogParents.DeleteBack();
134   Prefixes.DeleteBack();
135 }
136 
137 bool InitLocalPrivileges();
138 
CDirItems()139 CDirItems::CDirItems():
140     SymLinks(false),
141     ScanAltStreams(false)
142     #ifdef _USE_SECURITY_CODE
143     , ReadSecure(false)
144     #endif
145     , Callback(NULL)
146 {
147   #ifdef _USE_SECURITY_CODE
148   _saclEnabled = InitLocalPrivileges();
149   #endif
150 }
151 
152 #ifdef _USE_SECURITY_CODE
153 
AddSecurityItem(const FString & path,int & secureIndex)154 HRESULT CDirItems::AddSecurityItem(const FString &path, int &secureIndex)
155 {
156   secureIndex = -1;
157 
158   SECURITY_INFORMATION securInfo =
159       DACL_SECURITY_INFORMATION |
160       GROUP_SECURITY_INFORMATION |
161       OWNER_SECURITY_INFORMATION;
162   if (_saclEnabled)
163     securInfo |= SACL_SECURITY_INFORMATION;
164 
165   DWORD errorCode = 0;
166   DWORD secureSize;
167 
168   BOOL res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize);
169 
170   if (res)
171   {
172     if (secureSize == 0)
173       return S_OK;
174     if (secureSize > TempSecureBuf.Size())
175       errorCode = ERROR_INVALID_FUNCTION;
176   }
177   else
178   {
179     errorCode = GetLastError();
180     if (errorCode == ERROR_INSUFFICIENT_BUFFER)
181     {
182       if (secureSize <= TempSecureBuf.Size())
183         errorCode = ERROR_INVALID_FUNCTION;
184       else
185       {
186         TempSecureBuf.Alloc(secureSize);
187         res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize);
188         if (res)
189         {
190           if (secureSize != TempSecureBuf.Size())
191             errorCode = ERROR_INVALID_FUNCTION;;
192         }
193         else
194           errorCode = GetLastError();
195       }
196     }
197   }
198 
199   if (res)
200   {
201     secureIndex = SecureBlocks.AddUniq(TempSecureBuf, secureSize);
202     return S_OK;
203   }
204 
205   if (errorCode == 0)
206     errorCode = ERROR_INVALID_FUNCTION;
207   return AddError(path, errorCode);
208 }
209 
210 #endif
211 
EnumerateDir(int phyParent,int logParent,const FString & phyPrefix)212 HRESULT CDirItems::EnumerateDir(int phyParent, int logParent, const FString &phyPrefix)
213 {
214   RINOK(ScanProgress(phyPrefix));
215 
216   NFind::CEnumerator enumerator(phyPrefix + FCHAR_ANY_MASK);
217   for (unsigned ttt = 0; ; ttt++)
218   {
219     NFind::CFileInfo fi;
220     bool found;
221     if (!enumerator.Next(fi, found))
222     {
223       return AddError(phyPrefix);
224     }
225     if (!found)
226       return S_OK;
227 
228     int secureIndex = -1;
229     #ifdef _USE_SECURITY_CODE
230     if (ReadSecure)
231     {
232       RINOK(AddSecurityItem(phyPrefix + fi.Name, secureIndex));
233     }
234     #endif
235 
236     AddDirFileInfo(phyParent, logParent, secureIndex, fi);
237 
238     if (Callback && (ttt & kScanProgressStepMask) == kScanProgressStepMask)
239     {
240       RINOK(ScanProgress(phyPrefix));
241     }
242 
243     if (fi.IsDir())
244     {
245       const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR;
246       unsigned parent = AddPrefix(phyParent, logParent, fs2us(name2));
247       RINOK(EnumerateDir(parent, parent, phyPrefix + name2));
248     }
249   }
250 }
251 
EnumerateItems2(const FString & phyPrefix,const UString & logPrefix,const FStringVector & filePaths,FStringVector * requestedPaths)252 HRESULT CDirItems::EnumerateItems2(
253     const FString &phyPrefix,
254     const UString &logPrefix,
255     const FStringVector &filePaths,
256     FStringVector *requestedPaths)
257 {
258   int phyParent = phyPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, fs2us(phyPrefix));
259   int logParent = logPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, logPrefix);
260 
261   FOR_VECTOR (i, filePaths)
262   {
263     const FString &filePath = filePaths[i];
264     NFind::CFileInfo fi;
265     const FString phyPath = phyPrefix + filePath;
266     if (!fi.Find(phyPath))
267     {
268       RINOK(AddError(phyPath));
269       continue;
270     }
271     if (requestedPaths)
272       requestedPaths->Add(phyPath);
273 
274     int delimiter = filePath.ReverseFind_PathSepar();
275     FString phyPrefixCur;
276     int phyParentCur = phyParent;
277     if (delimiter >= 0)
278     {
279       phyPrefixCur.SetFrom(filePath, delimiter + 1);
280       phyParentCur = AddPrefix(phyParent, logParent, fs2us(phyPrefixCur));
281     }
282 
283     int secureIndex = -1;
284     #ifdef _USE_SECURITY_CODE
285     if (ReadSecure)
286     {
287       RINOK(AddSecurityItem(phyPath, secureIndex));
288     }
289     #endif
290 
291     AddDirFileInfo(phyParentCur, logParent, secureIndex, fi);
292 
293     if (fi.IsDir())
294     {
295       const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR;
296       unsigned parent = AddPrefix(phyParentCur, logParent, fs2us(name2));
297       RINOK(EnumerateDir(parent, parent, phyPrefix + phyPrefixCur + name2));
298     }
299   }
300 
301   ReserveDown();
302   return S_OK;
303 }
304 
305 
306 
307 
308 
309 
310 static HRESULT EnumerateDirItems(
311     const NWildcard::CCensorNode &curNode,
312     int phyParent, int logParent, const FString &phyPrefix,
313     const UStringVector &addArchivePrefix,
314     CDirItems &dirItems,
315     bool enterToSubFolders);
316 
EnumerateDirItems_Spec(const NWildcard::CCensorNode & curNode,int phyParent,int logParent,const FString & curFolderName,const FString & phyPrefix,const UStringVector & addArchivePrefix,CDirItems & dirItems,bool enterToSubFolders)317 static HRESULT EnumerateDirItems_Spec(
318     const NWildcard::CCensorNode &curNode,
319     int phyParent, int logParent, const FString &curFolderName,
320     const FString &phyPrefix,
321     const UStringVector &addArchivePrefix,
322     CDirItems &dirItems,
323     bool enterToSubFolders)
324 {
325   const FString name2 = curFolderName + FCHAR_PATH_SEPARATOR;
326   unsigned parent = dirItems.AddPrefix(phyParent, logParent, fs2us(name2));
327   unsigned numItems = dirItems.Items.Size();
328   HRESULT res = EnumerateDirItems(
329       curNode, parent, parent, phyPrefix + name2,
330       addArchivePrefix, dirItems, enterToSubFolders);
331   if (numItems == dirItems.Items.Size())
332     dirItems.DeleteLastPrefix();
333   return res;
334 }
335 
336 #ifndef UNDER_CE
337 
338 #ifdef _WIN32
339 
EnumerateAltStreams(const NFind::CFileInfo & fi,const NWildcard::CCensorNode & curNode,int phyParent,int logParent,const FString & fullPath,const UStringVector & addArchivePrefix,CDirItems & dirItems)340 static HRESULT EnumerateAltStreams(
341     const NFind::CFileInfo &fi,
342     const NWildcard::CCensorNode &curNode,
343     int phyParent, int logParent, const FString &fullPath,
344     const UStringVector &addArchivePrefix,  // prefix from curNode
345     CDirItems &dirItems)
346 {
347   NFind::CStreamEnumerator enumerator(fullPath);
348   for (;;)
349   {
350     NFind::CStreamInfo si;
351     bool found;
352     if (!enumerator.Next(si, found))
353     {
354       return dirItems.AddError(fullPath + FTEXT(":*")); // , (DWORD)E_FAIL
355     }
356     if (!found)
357       return S_OK;
358     if (si.IsMainStream())
359       continue;
360     UStringVector addArchivePrefixNew = addArchivePrefix;
361     UString reducedName = si.GetReducedName();
362     addArchivePrefixNew.Back() += reducedName;
363     if (curNode.CheckPathToRoot(false, addArchivePrefixNew, true))
364       continue;
365     NFind::CFileInfo fi2 = fi;
366     fi2.Name += us2fs(reducedName);
367     fi2.Size = si.Size;
368     fi2.Attrib &= ~FILE_ATTRIBUTE_DIRECTORY;
369     fi2.IsAltStream = true;
370     dirItems.AddDirFileInfo(phyParent, logParent, -1, fi2);
371   }
372 }
373 
374 #endif
375 
SetLinkInfo(CDirItem & dirItem,const NFind::CFileInfo & fi,const FString & phyPrefix)376 HRESULT CDirItems::SetLinkInfo(CDirItem &dirItem, const NFind::CFileInfo &fi,
377     const FString &phyPrefix)
378 {
379   if (!SymLinks || !fi.HasReparsePoint())
380     return S_OK;
381   const FString path = phyPrefix + fi.Name;
382   CByteBuffer &buf = dirItem.ReparseData;
383   if (NIO::GetReparseData(path, buf))
384   {
385     CReparseAttr attr;
386     if (attr.Parse(buf, buf.Size()))
387       return S_OK;
388   }
389   DWORD res = ::GetLastError();
390   buf.Free();
391   return AddError(path , res);
392 }
393 
394 #endif
395 
EnumerateForItem(NFind::CFileInfo & fi,const NWildcard::CCensorNode & curNode,int phyParent,int logParent,const FString & phyPrefix,const UStringVector & addArchivePrefix,CDirItems & dirItems,bool enterToSubFolders)396 static HRESULT EnumerateForItem(
397     NFind::CFileInfo &fi,
398     const NWildcard::CCensorNode &curNode,
399     int phyParent, int logParent, const FString &phyPrefix,
400     const UStringVector &addArchivePrefix,  // prefix from curNode
401     CDirItems &dirItems,
402     bool enterToSubFolders)
403 {
404   const UString name = fs2us(fi.Name);
405   bool enterToSubFolders2 = enterToSubFolders;
406   UStringVector addArchivePrefixNew = addArchivePrefix;
407   addArchivePrefixNew.Add(name);
408   {
409     UStringVector addArchivePrefixNewTemp(addArchivePrefixNew);
410     if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fi.IsDir()))
411       return S_OK;
412   }
413   int dirItemIndex = -1;
414 
415   if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fi.IsDir()))
416   {
417     int secureIndex = -1;
418     #ifdef _USE_SECURITY_CODE
419     if (dirItems.ReadSecure)
420     {
421       RINOK(dirItems.AddSecurityItem(phyPrefix + fi.Name, secureIndex));
422     }
423     #endif
424 
425     dirItemIndex = dirItems.Items.Size();
426     dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi);
427     if (fi.IsDir())
428       enterToSubFolders2 = true;
429   }
430 
431   #ifndef UNDER_CE
432   if (dirItems.ScanAltStreams)
433   {
434     RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent,
435         phyPrefix + fi.Name,
436         addArchivePrefixNew, dirItems));
437   }
438 
439   if (dirItemIndex >= 0)
440   {
441     CDirItem &dirItem = dirItems.Items[dirItemIndex];
442     RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix));
443     if (dirItem.ReparseData.Size() != 0)
444       return S_OK;
445   }
446   #endif
447 
448   if (!fi.IsDir())
449     return S_OK;
450 
451   const NWildcard::CCensorNode *nextNode = 0;
452   if (addArchivePrefix.IsEmpty())
453   {
454     int index = curNode.FindSubNode(name);
455     if (index >= 0)
456       nextNode = &curNode.SubNodes[index];
457   }
458   if (!enterToSubFolders2 && nextNode == 0)
459     return S_OK;
460 
461   addArchivePrefixNew = addArchivePrefix;
462   if (nextNode == 0)
463   {
464     nextNode = &curNode;
465     addArchivePrefixNew.Add(name);
466   }
467 
468   return EnumerateDirItems_Spec(
469       *nextNode, phyParent, logParent, fi.Name, phyPrefix,
470       addArchivePrefixNew,
471       dirItems,
472       enterToSubFolders2);
473 }
474 
475 
CanUseFsDirect(const NWildcard::CCensorNode & curNode)476 static bool CanUseFsDirect(const NWildcard::CCensorNode &curNode)
477 {
478   FOR_VECTOR (i, curNode.IncludeItems)
479   {
480     const NWildcard::CItem &item = curNode.IncludeItems[i];
481     if (item.Recursive || item.PathParts.Size() != 1)
482       return false;
483     const UString &name = item.PathParts.Front();
484     /*
485     if (name.IsEmpty())
486       return false;
487     */
488 
489     /* Windows doesn't support file name with wildcard
490        But if another system supports file name with wildcard,
491        and wildcard mode is disabled, we can ignore wildcard in name */
492     /*
493     if (!item.WildcardParsing)
494       continue;
495     */
496     if (DoesNameContainWildcard(name))
497       return false;
498   }
499   return true;
500 }
501 
502 
503 #if defined(_WIN32) && !defined(UNDER_CE)
504 
IsVirtualFsFolder(const FString & prefix,const UString & name)505 static bool IsVirtualFsFolder(const FString &prefix, const UString &name)
506 {
507   UString s = fs2us(prefix);
508   s += name;
509   s.Add_PathSepar();
510   return IsPathSepar(s[0]) && GetRootPrefixSize(s) == 0;
511 }
512 
513 #endif
514 
EnumerateDirItems(const NWildcard::CCensorNode & curNode,int phyParent,int logParent,const FString & phyPrefix,const UStringVector & addArchivePrefix,CDirItems & dirItems,bool enterToSubFolders)515 static HRESULT EnumerateDirItems(
516     const NWildcard::CCensorNode &curNode,
517     int phyParent, int logParent, const FString &phyPrefix,
518     const UStringVector &addArchivePrefix,  // prefix from curNode
519     CDirItems &dirItems,
520     bool enterToSubFolders)
521 {
522   if (!enterToSubFolders)
523     if (curNode.NeedCheckSubDirs())
524       enterToSubFolders = true;
525 
526   RINOK(dirItems.ScanProgress(phyPrefix));
527 
528   // try direct_names case at first
529   if (addArchivePrefix.IsEmpty() && !enterToSubFolders)
530   {
531     if (CanUseFsDirect(curNode))
532     {
533       // all names are direct (no wildcards)
534       // so we don't need file_system's dir enumerator
535       CRecordVector<bool> needEnterVector;
536       unsigned i;
537 
538       for (i = 0; i < curNode.IncludeItems.Size(); i++)
539       {
540         const NWildcard::CItem &item = curNode.IncludeItems[i];
541         const UString &name = item.PathParts.Front();
542         FString fullPath = phyPrefix + us2fs(name);
543 
544         #if defined(_WIN32) && !defined(UNDER_CE)
545         bool needAltStreams = true;
546         #endif
547 
548         #ifdef _USE_SECURITY_CODE
549         bool needSecurity = true;
550         #endif
551 
552         if (phyPrefix.IsEmpty())
553         {
554           if (!item.ForFile)
555           {
556             /* we don't like some names for alt streams inside archive:
557                ":sname"     for "\"
558                "c:::sname"  for "C:\"
559                So we ignore alt streams for these cases */
560             if (name.IsEmpty())
561             {
562               #if defined(_WIN32) && !defined(UNDER_CE)
563               needAltStreams = false;
564               #endif
565 
566               /*
567               // do we need to ignore security info for "\\" folder ?
568               #ifdef _USE_SECURITY_CODE
569               needSecurity = false;
570               #endif
571               */
572 
573               fullPath = FCHAR_PATH_SEPARATOR;
574             }
575             #if defined(_WIN32) && !defined(UNDER_CE)
576             else if (item.IsDriveItem())
577             {
578               needAltStreams = false;
579               fullPath.Add_PathSepar();
580             }
581             #endif
582           }
583         }
584 
585         NFind::CFileInfo fi;
586         #if defined(_WIN32) && !defined(UNDER_CE)
587         if (IsVirtualFsFolder(phyPrefix, name))
588         {
589           fi.SetAsDir();
590           fi.Name = us2fs(name);
591         }
592         else
593         #endif
594         if (!fi.Find(fullPath))
595         {
596           RINOK(dirItems.AddError(fullPath));
597           continue;
598         }
599 
600         bool isDir = fi.IsDir();
601         if (isDir && !item.ForDir || !isDir && !item.ForFile)
602         {
603           RINOK(dirItems.AddError(fullPath, (DWORD)E_FAIL));
604           continue;
605         }
606         {
607           UStringVector pathParts;
608           pathParts.Add(fs2us(fi.Name));
609           if (curNode.CheckPathToRoot(false, pathParts, !isDir))
610             continue;
611         }
612 
613         int secureIndex = -1;
614         #ifdef _USE_SECURITY_CODE
615         if (needSecurity && dirItems.ReadSecure)
616         {
617           RINOK(dirItems.AddSecurityItem(fullPath, secureIndex));
618         }
619         #endif
620 
621         dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi);
622 
623         #ifndef UNDER_CE
624         {
625           CDirItem &dirItem = dirItems.Items.Back();
626           RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix));
627           if (dirItem.ReparseData.Size() != 0)
628           {
629             if (fi.IsAltStream)
630               dirItems.Stat.AltStreamsSize -= fi.Size;
631             else
632               dirItems.Stat.FilesSize -= fi.Size;
633             continue;
634           }
635         }
636         #endif
637 
638 
639         #ifndef UNDER_CE
640         if (needAltStreams && dirItems.ScanAltStreams)
641         {
642           UStringVector pathParts;
643           pathParts.Add(fs2us(fi.Name));
644           RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent,
645               fullPath, pathParts, dirItems));
646         }
647         #endif
648 
649         if (!isDir)
650           continue;
651 
652         UStringVector addArchivePrefixNew;
653         const NWildcard::CCensorNode *nextNode = 0;
654         int index = curNode.FindSubNode(name);
655         if (index >= 0)
656         {
657           for (int t = needEnterVector.Size(); t <= index; t++)
658             needEnterVector.Add(true);
659           needEnterVector[index] = false;
660           nextNode = &curNode.SubNodes[index];
661         }
662         else
663         {
664           nextNode = &curNode;
665           addArchivePrefixNew.Add(name); // don't change it to fi.Name. It's for shortnames support
666         }
667 
668         RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix,
669             addArchivePrefixNew, dirItems, true));
670       }
671 
672       for (i = 0; i < curNode.SubNodes.Size(); i++)
673       {
674         if (i < needEnterVector.Size())
675           if (!needEnterVector[i])
676             continue;
677         const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i];
678         FString fullPath = phyPrefix + us2fs(nextNode.Name);
679         NFind::CFileInfo fi;
680 
681         if (phyPrefix.IsEmpty())
682         {
683           {
684             if (nextNode.Name.IsEmpty())
685               fullPath = FCHAR_PATH_SEPARATOR;
686             #ifdef _WIN32
687             else if (NWildcard::IsDriveColonName(nextNode.Name))
688               fullPath.Add_PathSepar();
689             #endif
690           }
691         }
692 
693         // we don't want to call fi.Find() for root folder or virtual folder
694         if (phyPrefix.IsEmpty() && nextNode.Name.IsEmpty()
695             #if defined(_WIN32) && !defined(UNDER_CE)
696             || IsVirtualFsFolder(phyPrefix, nextNode.Name)
697             #endif
698             )
699         {
700           fi.SetAsDir();
701           fi.Name = us2fs(nextNode.Name);
702         }
703         else
704         {
705           if (!fi.Find(fullPath))
706           {
707             if (!nextNode.AreThereIncludeItems())
708               continue;
709             RINOK(dirItems.AddError(fullPath));
710             continue;
711           }
712 
713           if (!fi.IsDir())
714           {
715             RINOK(dirItems.AddError(fullPath, (DWORD)E_FAIL));
716             continue;
717           }
718         }
719 
720         RINOK(EnumerateDirItems_Spec(nextNode, phyParent, logParent, fi.Name, phyPrefix,
721             UStringVector(), dirItems, false));
722       }
723 
724       return S_OK;
725     }
726   }
727 
728   #ifdef _WIN32
729   #ifndef UNDER_CE
730 
731   // scan drives, if wildcard is "*:\"
732 
733   if (phyPrefix.IsEmpty() && curNode.IncludeItems.Size() > 0)
734   {
735     unsigned i;
736     for (i = 0; i < curNode.IncludeItems.Size(); i++)
737     {
738       const NWildcard::CItem &item = curNode.IncludeItems[i];
739       if (item.PathParts.Size() < 1)
740         break;
741       const UString &name = item.PathParts.Front();
742       if (name.Len() != 2 || name[1] != ':')
743         break;
744       if (item.PathParts.Size() == 1)
745         if (item.ForFile || !item.ForDir)
746           break;
747       if (NWildcard::IsDriveColonName(name))
748         continue;
749       if (name[0] != '*' && name[0] != '?')
750         break;
751     }
752     if (i == curNode.IncludeItems.Size())
753     {
754       FStringVector driveStrings;
755       NFind::MyGetLogicalDriveStrings(driveStrings);
756       for (i = 0; i < driveStrings.Size(); i++)
757       {
758         FString driveName = driveStrings[i];
759         if (driveName.Len() < 3 || driveName.Back() != '\\')
760           return E_FAIL;
761         driveName.DeleteBack();
762         NFind::CFileInfo fi;
763         fi.SetAsDir();
764         fi.Name = driveName;
765 
766         RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix,
767             addArchivePrefix, dirItems, enterToSubFolders));
768       }
769       return S_OK;
770     }
771   }
772 
773   #endif
774   #endif
775 
776   NFind::CEnumerator enumerator(phyPrefix + FCHAR_ANY_MASK);
777   for (unsigned ttt = 0; ; ttt++)
778   {
779     NFind::CFileInfo fi;
780     bool found;
781     if (!enumerator.Next(fi, found))
782     {
783       RINOK(dirItems.AddError(phyPrefix));
784       break;
785     }
786     if (!found)
787       break;
788 
789     if (dirItems.Callback && (ttt & kScanProgressStepMask) == kScanProgressStepMask)
790     {
791       RINOK(dirItems.ScanProgress(phyPrefix));
792     }
793 
794     RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix,
795           addArchivePrefix, dirItems, enterToSubFolders));
796   }
797 
798   return S_OK;
799 }
800 
EnumerateItems(const NWildcard::CCensor & censor,const NWildcard::ECensorPathMode pathMode,const UString & addPathPrefix,CDirItems & dirItems)801 HRESULT EnumerateItems(
802     const NWildcard::CCensor &censor,
803     const NWildcard::ECensorPathMode pathMode,
804     const UString &addPathPrefix,
805     CDirItems &dirItems)
806 {
807   FOR_VECTOR (i, censor.Pairs)
808   {
809     const NWildcard::CPair &pair = censor.Pairs[i];
810     int phyParent = pair.Prefix.IsEmpty() ? -1 : dirItems.AddPrefix(-1, -1, pair.Prefix);
811     int logParent = -1;
812 
813     if (pathMode == NWildcard::k_AbsPath)
814       logParent = phyParent;
815     else
816     {
817       if (!addPathPrefix.IsEmpty())
818         logParent = dirItems.AddPrefix(-1, -1, addPathPrefix);
819     }
820 
821     RINOK(EnumerateDirItems(pair.Head, phyParent, logParent, us2fs(pair.Prefix), UStringVector(),
822         dirItems,
823         false // enterToSubFolders
824         ));
825   }
826   dirItems.ReserveDown();
827 
828   #if defined(_WIN32) && !defined(UNDER_CE)
829   dirItems.FillFixedReparse();
830   #endif
831 
832   return S_OK;
833 }
834 
835 #if defined(_WIN32) && !defined(UNDER_CE)
836 
FillFixedReparse()837 void CDirItems::FillFixedReparse()
838 {
839   /* imagex/WIM reduces absolute pathes in links (raparse data),
840      if we archive non root folder. We do same thing here */
841 
842   if (!SymLinks)
843     return;
844 
845   FOR_VECTOR(i, Items)
846   {
847     CDirItem &item = Items[i];
848     if (item.ReparseData.Size() == 0)
849       continue;
850 
851     CReparseAttr attr;
852     if (!attr.Parse(item.ReparseData, item.ReparseData.Size()))
853       continue;
854     if (attr.IsRelative())
855       continue;
856 
857     const UString &link = attr.GetPath();
858     if (!IsDrivePath(link))
859       continue;
860     // maybe we need to support networks paths also ?
861 
862     FString fullPathF;
863     if (!NDir::MyGetFullPathName(GetPhyPath(i), fullPathF))
864       continue;
865     UString fullPath = fs2us(fullPathF);
866     const UString logPath = GetLogPath(i);
867     if (logPath.Len() >= fullPath.Len())
868       continue;
869     if (CompareFileNames(logPath, fullPath.RightPtr(logPath.Len())) != 0)
870       continue;
871 
872     const UString prefix = fullPath.Left(fullPath.Len() - logPath.Len());
873     if (!IsPathSepar(prefix.Back()))
874       continue;
875 
876     unsigned rootPrefixSize = GetRootPrefixSize(prefix);
877     if (rootPrefixSize == 0)
878       continue;
879     if (rootPrefixSize == prefix.Len())
880       continue; // simple case: paths are from root
881 
882     if (link.Len() <= prefix.Len())
883       continue;
884 
885     if (CompareFileNames(link.Left(prefix.Len()), prefix) != 0)
886       continue;
887 
888     UString newLink = prefix.Left(rootPrefixSize);
889     newLink += link.Ptr(prefix.Len());
890 
891     CByteBuffer data;
892     if (!FillLinkData(data, newLink, attr.IsSymLink()))
893       continue;
894     item.ReparseData2 = data;
895   }
896 }
897 
898 #endif
899