Home | History | Annotate | Download | only in Common
      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 
     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 
     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 
     65 HRESULT CDirItems::AddError(const FString &path)
     66 {
     67   return AddError(path, ::GetLastError());
     68 }
     69 
     70 static const unsigned kScanProgressStepMask = (1 << 12) - 1;
     71 
     72 HRESULT CDirItems::ScanProgress(const FString &dirPath)
     73 {
     74   if (Callback)
     75     return Callback->ScanProgress(Stat, dirPath, true);
     76   return S_OK;
     77 }
     78 
     79 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 
    103 FString CDirItems::GetPhyPath(unsigned index) const
    104 {
    105   const CDirItem &di = Items[index];
    106   return us2fs(GetPrefixesPath(PhyParents, di.PhyParent, di.Name));
    107 }
    108 
    109 UString CDirItems::GetLogPath(unsigned index) const
    110 {
    111   const CDirItem &di = Items[index];
    112   return GetPrefixesPath(LogParents, di.LogParent, di.Name);
    113 }
    114 
    115 void CDirItems::ReserveDown()
    116 {
    117   Prefixes.ReserveDown();
    118   PhyParents.ReserveDown();
    119   LogParents.ReserveDown();
    120   Items.ReserveDown();
    121 }
    122 
    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 
    130 void CDirItems::DeleteLastPrefix()
    131 {
    132   PhyParents.DeleteBack();
    133   LogParents.DeleteBack();
    134   Prefixes.DeleteBack();
    135 }
    136 
    137 bool InitLocalPrivileges();
    138 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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