Home | History | Annotate | Download | only in Common
      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 
     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 
     42 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 
     64 UString CDirItems::GetPhyPath(unsigned index) const
     65 {
     66   const CDirItem &di = Items[index];
     67   return GetPrefixesPath(PhyParents, di.PhyParent, di.Name);
     68 }
     69 
     70 UString CDirItems::GetLogPath(unsigned index) const
     71 {
     72   const CDirItem &di = Items[index];
     73   return GetPrefixesPath(LogParents, di.LogParent, di.Name);
     74 }
     75 
     76 void CDirItems::ReserveDown()
     77 {
     78   Prefixes.ReserveDown();
     79   PhyParents.ReserveDown();
     80   LogParents.ReserveDown();
     81   Items.ReserveDown();
     82 }
     83 
     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 
     91 void CDirItems::DeleteLastPrefix()
     92 {
     93   PhyParents.DeleteBack();
     94   LogParents.DeleteBack();
     95   Prefixes.DeleteBack();
     96 }
     97 
     98 bool InitLocalPrivileges();
     99 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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