tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

EnumDirItems.cpp (28643B)


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