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