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
20using namespace NWindows;
21using namespace NFile;
22using namespace NName;
23
24void 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
57HRESULT 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
65HRESULT CDirItems::AddError(const FString &path)
66{
67  return AddError(path, ::GetLastError());
68}
69
70static const unsigned kScanProgressStepMask = (1 << 12) - 1;
71
72HRESULT CDirItems::ScanProgress(const FString &dirPath)
73{
74  if (Callback)
75    return Callback->ScanProgress(Stat, dirPath, true);
76  return S_OK;
77}
78
79UString 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
103FString CDirItems::GetPhyPath(unsigned index) const
104{
105  const CDirItem &di = Items[index];
106  return us2fs(GetPrefixesPath(PhyParents, di.PhyParent, di.Name));
107}
108
109UString CDirItems::GetLogPath(unsigned index) const
110{
111  const CDirItem &di = Items[index];
112  return GetPrefixesPath(LogParents, di.LogParent, di.Name);
113}
114
115void CDirItems::ReserveDown()
116{
117  Prefixes.ReserveDown();
118  PhyParents.ReserveDown();
119  LogParents.ReserveDown();
120  Items.ReserveDown();
121}
122
123unsigned 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
130void CDirItems::DeleteLastPrefix()
131{
132  PhyParents.DeleteBack();
133  LogParents.DeleteBack();
134  Prefixes.DeleteBack();
135}
136
137bool InitLocalPrivileges();
138
139CDirItems::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
154HRESULT 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
212HRESULT 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
252HRESULT 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
310static 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
317static 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
340static 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
376HRESULT 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
396static 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
476static 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
505static 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
515static 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
801HRESULT 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
837void 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