1// EnumDirItems.cpp
2
3#include "StdAfx.h"
4
5#include <wchar.h>
6// #include <stdio.h>
7
8#ifndef _WIN32
9#include <grp.h>
10#include <pwd.h>
11#include "../../../Common/UTFConvert.h"
12#endif
13
14#include "../../../Common/Wildcard.h"
15
16#include "../../../Windows/FileDir.h"
17#include "../../../Windows/FileIO.h"
18#include "../../../Windows/FileName.h"
19
20#if defined(_WIN32) && !defined(UNDER_CE)
21#define Z7_USE_SECURITY_CODE
22#include "../../../Windows/SecurityUtils.h"
23#endif
24
25#include "EnumDirItems.h"
26#include "SortUtils.h"
27
28using namespace NWindows;
29using namespace NFile;
30using namespace NName;
31
32
33static bool FindFile_KeepDots(NFile::NFind::CFileInfo &fi, const FString &path, bool followLink)
34{
35  const bool res = fi.Find(path, followLink);
36  if (!res)
37    return res;
38  if (path.IsEmpty())
39    return res;
40  // we keep name "." and "..", if it's without tail slash
41  const FChar *p = path.RightPtr(1);
42  if (*p != '.')
43    return res;
44  if (p != path.Ptr())
45  {
46    FChar c = p[-1];
47    if (!IS_PATH_SEPAR(c))
48    {
49      if (c != '.')
50        return res;
51      p--;
52      if (p != path.Ptr())
53      {
54        c = p[-1];
55        if (!IS_PATH_SEPAR(c))
56          return res;
57      }
58    }
59  }
60  fi.Name = p;
61  return res;
62}
63
64
65void CDirItems::AddDirFileInfo(int phyParent, int logParent, int secureIndex,
66    const NFind::CFileInfo &fi)
67{
68  /*
69  CDirItem di(fi);
70  di.PhyParent = phyParent;
71  di.LogParent = logParent;
72  di.SecureIndex = secureIndex;
73  Items.Add(di);
74  */
75  VECTOR_ADD_NEW_OBJECT (Items, CDirItem(fi, phyParent, logParent, secureIndex))
76
77  if (fi.IsDir())
78    Stat.NumDirs++;
79 #ifdef _WIN32
80  else if (fi.IsAltStream)
81  {
82    Stat.NumAltStreams++;
83    Stat.AltStreamsSize += fi.Size;
84  }
85 #endif
86  else
87  {
88    Stat.NumFiles++;
89    Stat.FilesSize += fi.Size;
90  }
91}
92
93// (DWORD)E_FAIL
94#define DI_DEFAULT_ERROR  ERROR_INVALID_FUNCTION
95
96HRESULT CDirItems::AddError(const FString &path, DWORD errorCode)
97{
98  if (errorCode == 0)
99    errorCode = DI_DEFAULT_ERROR;
100  Stat.NumErrors++;
101  if (Callback)
102    return Callback->ScanError(path, errorCode);
103  return S_OK;
104}
105
106HRESULT CDirItems::AddError(const FString &path)
107{
108  return AddError(path, ::GetLastError());
109}
110
111static const unsigned kScanProgressStepMask = (1 << 12) - 1;
112
113HRESULT CDirItems::ScanProgress(const FString &dirPath)
114{
115  if (Callback)
116    return Callback->ScanProgress(Stat, dirPath, true);
117  return S_OK;
118}
119
120UString CDirItems::GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const
121{
122  UString path;
123  unsigned len = name.Len();
124
125  int i;
126  for (i = index; i >= 0; i = parents[(unsigned)i])
127    len += Prefixes[(unsigned)i].Len();
128
129  wchar_t *p = path.GetBuf_SetEnd(len) + len;
130
131  p -= name.Len();
132  wmemcpy(p, (const wchar_t *)name, name.Len());
133
134  for (i = index; i >= 0; i = parents[(unsigned)i])
135  {
136    const UString &s = Prefixes[(unsigned)i];
137    p -= s.Len();
138    wmemcpy(p, (const wchar_t *)s, s.Len());
139  }
140
141  return path;
142}
143
144FString CDirItems::GetPhyPath(unsigned index) const
145{
146  const CDirItem &di = Items[index];
147  return us2fs(GetPrefixesPath(PhyParents, di.PhyParent, di.Name));
148}
149
150UString CDirItems::GetLogPath(unsigned index) const
151{
152  const CDirItem &di = Items[index];
153  return GetPrefixesPath(LogParents, di.LogParent, di.Name);
154}
155
156void CDirItems::ReserveDown()
157{
158  Prefixes.ReserveDown();
159  PhyParents.ReserveDown();
160  LogParents.ReserveDown();
161  Items.ReserveDown();
162}
163
164unsigned CDirItems::AddPrefix(int phyParent, int logParent, const UString &prefix)
165{
166  PhyParents.Add(phyParent);
167  LogParents.Add(logParent);
168  return Prefixes.Add(prefix);
169}
170
171void CDirItems::DeleteLastPrefix()
172{
173  PhyParents.DeleteBack();
174  LogParents.DeleteBack();
175  Prefixes.DeleteBack();
176}
177
178bool InitLocalPrivileges();
179
180CDirItems::CDirItems():
181    SymLinks(false),
182    ScanAltStreams(false)
183    , ExcludeDirItems(false)
184    , ExcludeFileItems(false)
185    , ShareForWrite(false)
186   #ifdef Z7_USE_SECURITY_CODE
187    , ReadSecure(false)
188   #endif
189   #ifndef _WIN32
190    , StoreOwnerName(false)
191   #endif
192    , Callback(NULL)
193{
194  #ifdef Z7_USE_SECURITY_CODE
195  _saclEnabled = InitLocalPrivileges();
196  #endif
197}
198
199
200#ifdef Z7_USE_SECURITY_CODE
201
202HRESULT CDirItems::AddSecurityItem(const FString &path, int &secureIndex)
203{
204  secureIndex = -1;
205
206  SECURITY_INFORMATION securInfo =
207      DACL_SECURITY_INFORMATION |
208      GROUP_SECURITY_INFORMATION |
209      OWNER_SECURITY_INFORMATION;
210  if (_saclEnabled)
211    securInfo |= SACL_SECURITY_INFORMATION;
212
213  DWORD errorCode = 0;
214  DWORD secureSize;
215
216  BOOL res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(void *)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize);
217
218  if (res)
219  {
220    if (secureSize == 0)
221      return S_OK;
222    if (secureSize > TempSecureBuf.Size())
223      errorCode = ERROR_INVALID_FUNCTION;
224  }
225  else
226  {
227    errorCode = GetLastError();
228    if (errorCode == ERROR_INSUFFICIENT_BUFFER)
229    {
230      if (secureSize <= TempSecureBuf.Size())
231        errorCode = ERROR_INVALID_FUNCTION;
232      else
233      {
234        TempSecureBuf.Alloc(secureSize);
235        res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(void *)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize);
236        if (res)
237        {
238          if (secureSize != TempSecureBuf.Size())
239            errorCode = ERROR_INVALID_FUNCTION;
240        }
241        else
242          errorCode = GetLastError();
243      }
244    }
245  }
246
247  if (res)
248  {
249    secureIndex = (int)SecureBlocks.AddUniq(TempSecureBuf, secureSize);
250    return S_OK;
251  }
252
253  return AddError(path, errorCode);
254}
255
256#endif // Z7_USE_SECURITY_CODE
257
258
259HRESULT CDirItems::EnumerateOneDir(const FString &phyPrefix, CObjectVector<NFind::CFileInfo> &files)
260{
261  NFind::CEnumerator enumerator;
262  // printf("\n  enumerator.SetDirPrefix(phyPrefix) \n");
263
264  enumerator.SetDirPrefix(phyPrefix);
265
266  #ifdef _WIN32
267
268  NFind::CFileInfo fi;
269
270  for (unsigned ttt = 0; ; ttt++)
271  {
272    bool found;
273    if (!enumerator.Next(fi, found))
274      return AddError(phyPrefix);
275    if (!found)
276      return S_OK;
277    files.Add(fi);
278    if (Callback && (ttt & kScanProgressStepMask) == kScanProgressStepMask)
279    {
280      RINOK(ScanProgress(phyPrefix))
281    }
282  }
283
284  #else // _WIN32
285
286  // enumerator.SolveLinks = !SymLinks;
287
288  CObjectVector<NFind::CDirEntry> entries;
289
290  for (unsigned ttt = 0; ; ttt++)
291  {
292    bool found;
293    NFind::CDirEntry de;
294    if (!enumerator.Next(de, found))
295    {
296      return AddError(phyPrefix);
297    }
298    if (!found)
299      break;
300    entries.Add(de);
301  }
302
303  FOR_VECTOR(i, entries)
304  {
305    const NFind::CDirEntry &de = entries[i];
306    NFind::CFileInfo fi;
307    if (!enumerator.Fill_FileInfo(de, fi, !SymLinks))
308    // if (!fi.Find_AfterEnumerator(path))
309    {
310      const FString path = phyPrefix + de.Name;
311      {
312        RINOK(AddError(path))
313        continue;
314      }
315    }
316
317    files.Add(fi);
318
319    if (Callback && (i & kScanProgressStepMask) == kScanProgressStepMask)
320    {
321      RINOK(ScanProgress(phyPrefix))
322    }
323  }
324
325  return S_OK;
326
327  #endif // _WIN32
328}
329
330
331
332
333HRESULT CDirItems::EnumerateDir(int phyParent, int logParent, const FString &phyPrefix)
334{
335  RINOK(ScanProgress(phyPrefix))
336
337  CObjectVector<NFind::CFileInfo> files;
338  RINOK(EnumerateOneDir(phyPrefix, files))
339
340  FOR_VECTOR (i, files)
341  {
342    #ifdef _WIN32
343    const NFind::CFileInfo &fi = files[i];
344    #else
345    const NFind::CFileInfo &fi = files[i];
346    /*
347    NFind::CFileInfo fi;
348    {
349      const NFind::CDirEntry &di = files[i];
350      const FString path = phyPrefix + di.Name;
351      if (!fi.Find_AfterEnumerator(path))
352      {
353        RINOK(AddError(path));
354        continue;
355      }
356      fi.Name = di.Name;
357    }
358    */
359    #endif
360
361    if (CanIncludeItem(fi.IsDir()))
362    {
363    int secureIndex = -1;
364    #ifdef Z7_USE_SECURITY_CODE
365    if (ReadSecure)
366    {
367      RINOK(AddSecurityItem(phyPrefix + fi.Name, secureIndex))
368    }
369    #endif
370    AddDirFileInfo(phyParent, logParent, secureIndex, fi);
371    }
372
373    if (Callback && (i & kScanProgressStepMask) == kScanProgressStepMask)
374    {
375      RINOK(ScanProgress(phyPrefix))
376    }
377
378    if (fi.IsDir())
379    {
380      const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR;
381      unsigned parent = AddPrefix(phyParent, logParent, fs2us(name2));
382      RINOK(EnumerateDir((int)parent, (int)parent, phyPrefix + name2))
383    }
384  }
385  return S_OK;
386}
387
388
389/*
390EnumerateItems2()
391  const FStringVector &filePaths - are path without tail slashes.
392  All dir prefixes of filePaths will be not stores in logical paths
393fix it: we can scan AltStream also.
394*/
395
396#ifdef _WIN32
397// #define FOLLOW_LINK_PARAM
398// #define FOLLOW_LINK_PARAM2
399#define FOLLOW_LINK_PARAM , (!SymLinks)
400#define FOLLOW_LINK_PARAM2 , (!dirItems.SymLinks)
401#else
402#define FOLLOW_LINK_PARAM , (!SymLinks)
403#define FOLLOW_LINK_PARAM2 , (!dirItems.SymLinks)
404#endif
405
406HRESULT CDirItems::EnumerateItems2(
407    const FString &phyPrefix,
408    const UString &logPrefix,
409    const FStringVector &filePaths,
410    FStringVector *requestedPaths)
411{
412  const int phyParent = phyPrefix.IsEmpty() ? -1 : (int)AddPrefix(-1, -1, fs2us(phyPrefix));
413  const int logParent = logPrefix.IsEmpty() ? -1 : (int)AddPrefix(-1, -1, logPrefix);
414
415 #ifdef _WIN32
416  const bool phyPrefix_isAltStreamPrefix =
417      NFile::NName::IsAltStreamPrefixWithColon(fs2us(phyPrefix));
418 #endif
419
420  FOR_VECTOR (i, filePaths)
421  {
422    const FString &filePath = filePaths[i];
423    NFind::CFileInfo fi;
424    const FString phyPath = phyPrefix + filePath;
425    if (!FindFile_KeepDots(fi, phyPath  FOLLOW_LINK_PARAM))
426    {
427      RINOK(AddError(phyPath))
428      continue;
429    }
430    if (requestedPaths)
431      requestedPaths->Add(phyPath);
432
433    const int delimiter = filePath.ReverseFind_PathSepar();
434    FString phyPrefixCur;
435    int phyParentCur = phyParent;
436    if (delimiter >= 0)
437    {
438      phyPrefixCur.SetFrom(filePath, (unsigned)(delimiter + 1));
439      phyParentCur = (int)AddPrefix(phyParent, logParent, fs2us(phyPrefixCur));
440    }
441
442    if (CanIncludeItem(fi.IsDir()))
443    {
444    int secureIndex = -1;
445    #ifdef Z7_USE_SECURITY_CODE
446    if (ReadSecure)
447    {
448      RINOK(AddSecurityItem(phyPath, secureIndex))
449    }
450    #endif
451   #ifdef _WIN32
452    if (phyPrefix_isAltStreamPrefix && fi.IsAltStream)
453    {
454      const int pos = fi.Name.Find(FChar(':'));
455      if (pos >= 0)
456        fi.Name.DeleteFrontal((unsigned)pos + 1);
457    }
458   #endif
459    AddDirFileInfo(phyParentCur, logParent, secureIndex, fi);
460    }
461
462    if (fi.IsDir())
463    {
464      const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR;
465      const unsigned parent = AddPrefix(phyParentCur, logParent, fs2us(name2));
466      RINOK(EnumerateDir((int)parent, (int)parent, phyPrefix + phyPrefixCur + name2))
467    }
468  }
469
470  ReserveDown();
471  return S_OK;
472}
473
474
475
476
477static HRESULT EnumerateDirItems(
478    const NWildcard::CCensorNode &curNode,
479    const int phyParent, const int logParent,
480    const FString &phyPrefix,
481    const UStringVector &addParts, // additional parts from curNode
482    CDirItems &dirItems,
483    bool enterToSubFolders);
484
485
486/* EnumerateDirItems_Spec()
487   adds new Dir item prefix, and enumerates dir items,
488   then it can remove that Dir item prefix, if there are no items in that dir.
489*/
490
491
492/*
493  EnumerateDirItems_Spec()
494  it's similar to EnumerateDirItems, but phyPrefix doesn't include (curFolderName)
495*/
496
497static HRESULT EnumerateDirItems_Spec(
498    const NWildcard::CCensorNode &curNode,
499    const int phyParent, const int logParent, const FString &curFolderName,
500    const FString &phyPrefix,      // without (curFolderName)
501    const UStringVector &addParts, // (curNode + addParts) includes (curFolderName)
502    CDirItems &dirItems,
503    bool enterToSubFolders)
504{
505  const FString name2 = curFolderName + FCHAR_PATH_SEPARATOR;
506  const unsigned parent = dirItems.AddPrefix(phyParent, logParent, fs2us(name2));
507  const unsigned numItems = dirItems.Items.Size();
508  HRESULT res = EnumerateDirItems(
509      curNode, (int)parent, (int)parent, phyPrefix + name2,
510      addParts, dirItems, enterToSubFolders);
511  if (numItems == dirItems.Items.Size())
512    dirItems.DeleteLastPrefix();
513  return res;
514}
515
516
517#ifndef UNDER_CE
518
519#ifdef _WIN32
520
521static HRESULT EnumerateAltStreams(
522    const NFind::CFileInfo &fi,
523    const NWildcard::CCensorNode &curNode,
524    const int phyParent, const int logParent,
525    const FString &phyPath,         // with (fi.Name), without tail slash for folders
526    const UStringVector &addParts,  // with (fi.Name), prefix parts from curNode
527    bool addAllSubStreams,
528    CDirItems &dirItems)
529{
530  // we don't use (ExcludeFileItems) rules for AltStreams
531  // if (dirItems.ExcludeFileItems) return S_OK;
532
533  NFind::CStreamEnumerator enumerator(phyPath);
534  for (;;)
535  {
536    NFind::CStreamInfo si;
537    bool found;
538    if (!enumerator.Next(si, found))
539    {
540      return dirItems.AddError(phyPath + FTEXT(":*")); // , (DWORD)E_FAIL
541    }
542    if (!found)
543      return S_OK;
544    if (si.IsMainStream())
545      continue;
546    UStringVector parts = addParts;
547    const UString reducedName = si.GetReducedName();
548    parts.Back() += reducedName;
549    if (curNode.CheckPathToRoot(false, parts, true))
550      continue;
551    if (!addAllSubStreams)
552      if (!curNode.CheckPathToRoot(true, parts, true))
553        continue;
554
555    NFind::CFileInfo fi2 = fi;
556    fi2.Name += us2fs(reducedName);
557    fi2.Size = si.Size;
558    fi2.Attrib &= ~(DWORD)(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT);
559    fi2.IsAltStream = true;
560    dirItems.AddDirFileInfo(phyParent, logParent, -1, fi2);
561  }
562}
563
564#endif // _WIN32
565
566
567/* We get Reparse data and parse it.
568   If there is Reparse error, we free dirItem.Reparse data.
569   Do we need to work with empty reparse data?
570*/
571
572HRESULT CDirItems::SetLinkInfo(CDirItem &dirItem, const NFind::CFileInfo &fi,
573    const FString &phyPrefix)
574{
575  if (!SymLinks)
576    return S_OK;
577
578  #ifdef _WIN32
579    if (!fi.HasReparsePoint() || fi.IsAltStream)
580  #else // _WIN32
581    if (!fi.IsPosixLink())
582  #endif // _WIN32
583      return S_OK;
584
585  const FString path = phyPrefix + fi.Name;
586  CByteBuffer &buf = dirItem.ReparseData;
587  if (NIO::GetReparseData(path, buf))
588  {
589    // if (dirItem.ReparseData.Size() != 0)
590    Stat.FilesSize -= fi.Size;
591    return S_OK;
592  }
593
594  DWORD res = ::GetLastError();
595  buf.Free();
596  return AddError(path, res);
597}
598
599#endif // UNDER_CE
600
601
602
603static HRESULT EnumerateForItem(
604    const NFind::CFileInfo &fi,
605    const NWildcard::CCensorNode &curNode,
606    const int phyParent, const int logParent, const FString &phyPrefix,
607    const UStringVector &addParts, // additional parts from curNode, without (fi.Name)
608    CDirItems &dirItems,
609    bool enterToSubFolders)
610{
611  const UString name = fs2us(fi.Name);
612  UStringVector newParts = addParts;
613  newParts.Add(name);
614
615  // check the path in exclude rules
616  if (curNode.CheckPathToRoot(false, newParts, !fi.IsDir()))
617    return S_OK;
618
619  #if !defined(UNDER_CE)
620  int dirItemIndex = -1;
621  #if defined(_WIN32)
622  bool addAllSubStreams = false;
623  bool needAltStreams = true;
624  #endif // _WIN32
625  #endif // !defined(UNDER_CE)
626
627  // check the path in inlcude rules
628  if (curNode.CheckPathToRoot(true, newParts, !fi.IsDir()))
629  {
630    #if !defined(UNDER_CE)
631    // dirItemIndex = (int)dirItems.Items.Size();
632    #if defined(_WIN32)
633    // we will not check include rules for substreams.
634    addAllSubStreams = true;
635    #endif // _WIN32
636    #endif // !defined(UNDER_CE)
637
638    if (dirItems.CanIncludeItem(fi.IsDir()))
639    {
640      int secureIndex = -1;
641    #ifdef Z7_USE_SECURITY_CODE
642      if (dirItems.ReadSecure)
643      {
644        RINOK(dirItems.AddSecurityItem(phyPrefix + fi.Name, secureIndex))
645      }
646    #endif
647    #if !defined(UNDER_CE)
648      dirItemIndex = (int)dirItems.Items.Size();
649    #endif // !defined(UNDER_CE)
650      dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi);
651    }
652    else
653    {
654      #if defined(_WIN32) && !defined(UNDER_CE)
655        needAltStreams = false;
656      #endif
657    }
658
659    if (fi.IsDir())
660      enterToSubFolders = true;
661  }
662
663  #if !defined(UNDER_CE)
664
665  // we don't scan AltStreams for link files
666
667  if (dirItemIndex >= 0)
668  {
669    CDirItem &dirItem = dirItems.Items[(unsigned)dirItemIndex];
670    RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix))
671    if (dirItem.ReparseData.Size() != 0)
672      return S_OK;
673  }
674
675  #if defined(_WIN32)
676  if (needAltStreams && dirItems.ScanAltStreams)
677  {
678    RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent,
679        phyPrefix + fi.Name,    // with (fi.Name)
680        newParts,               // with (fi.Name)
681        addAllSubStreams,
682        dirItems))
683  }
684  #endif
685
686  #endif // !defined(UNDER_CE)
687
688
689  #ifndef _WIN32
690  if (!fi.IsPosixLink()) // posix link can follow to dir
691  #endif
692  if (!fi.IsDir())
693    return S_OK;
694
695  const NWildcard::CCensorNode *nextNode = NULL;
696
697  if (addParts.IsEmpty())
698  {
699    int index = curNode.FindSubNode(name);
700    if (index >= 0)
701    {
702      nextNode = &curNode.SubNodes[(unsigned)index];
703      newParts.Clear();
704    }
705  }
706
707  if (!nextNode)
708  {
709    if (!enterToSubFolders)
710      return S_OK;
711
712   #ifndef _WIN32
713    if (fi.IsPosixLink())
714    {
715      // here we can try to resolve posix link
716      // if the link to dir, then can we follow it
717      return S_OK; // we don't follow posix link
718    }
719   #else
720    if (dirItems.SymLinks && fi.HasReparsePoint())
721    {
722      /* 20.03: in SymLinks mode: we don't enter to directory that
723         has reparse point and has no CCensorNode
724         NOTE: (curNode and parent nodes) still can have wildcard rules
725         to include some items of target directory (of reparse point),
726         but we ignore these rules here.
727      */
728      return S_OK;
729    }
730   #endif
731    nextNode = &curNode;
732  }
733
734  return EnumerateDirItems_Spec(
735      *nextNode, phyParent, logParent, fi.Name,
736      phyPrefix,   // without (fi.Name)
737      newParts,    // relative to (*nextNode). (*nextNode + newParts) includes (fi.Name)
738      dirItems,
739      enterToSubFolders);
740}
741
742
743static bool CanUseFsDirect(const NWildcard::CCensorNode &curNode)
744{
745  FOR_VECTOR (i, curNode.IncludeItems)
746  {
747    const NWildcard::CItem &item = curNode.IncludeItems[i];
748    if (item.Recursive || item.PathParts.Size() != 1)
749      return false;
750    const UString &name = item.PathParts.Front();
751    /*
752    if (name.IsEmpty())
753      return false;
754    */
755
756    /* Windows doesn't support file name with wildcard
757       But if another system supports file name with wildcard,
758       and wildcard mode is disabled, we can ignore wildcard in name
759    */
760    /*
761    #ifndef _WIN32
762    if (!item.WildcardParsing)
763      continue;
764    #endif
765    */
766    if (DoesNameContainWildcard(name))
767      return false;
768  }
769  return true;
770}
771
772
773#if defined(_WIN32) && !defined(UNDER_CE)
774
775static bool IsVirtualFsFolder(const FString &prefix, const UString &name)
776{
777  UString s = fs2us(prefix);
778  s += name;
779  s.Add_PathSepar();
780  // it returns (true) for non real FS folder path like - "\\SERVER\"
781  return IsPathSepar(s[0]) && GetRootPrefixSize(s) == 0;
782}
783
784#endif
785
786
787
788static HRESULT EnumerateDirItems(
789    const NWildcard::CCensorNode &curNode,
790    const int phyParent, const int logParent, const FString &phyPrefix,
791    const UStringVector &addParts,  // prefix from curNode including
792    CDirItems &dirItems,
793    bool enterToSubFolders)
794{
795  if (!enterToSubFolders)
796  {
797    /* if there are IncludeItems censor rules that affect items in subdirs,
798       then we will enter to all subfolders */
799    if (curNode.NeedCheckSubDirs())
800      enterToSubFolders = true;
801  }
802
803  RINOK(dirItems.ScanProgress(phyPrefix))
804
805  // try direct_names case at first
806  if (addParts.IsEmpty() && !enterToSubFolders)
807  {
808    if (CanUseFsDirect(curNode))
809    {
810      // all names are direct (no wildcards)
811      // so we don't need file_system's dir enumerator
812      CRecordVector<bool> needEnterVector;
813      unsigned i;
814
815      for (i = 0; i < curNode.IncludeItems.Size(); i++)
816      {
817        const NWildcard::CItem &item = curNode.IncludeItems[i];
818        const UString &name = item.PathParts.Front();
819        FString fullPath = phyPrefix + us2fs(name);
820
821        /*
822        // not possible now
823        if (!item.ForDir && !item.ForFile)
824        {
825          RINOK(dirItems.AddError(fullPath, ERROR_INVALID_PARAMETER));
826          continue;
827        }
828        */
829
830        #if defined(_WIN32) && !defined(UNDER_CE)
831        bool needAltStreams = true;
832        #endif
833
834        #ifdef Z7_USE_SECURITY_CODE
835        bool needSecurity = true;
836        #endif
837
838        if (phyPrefix.IsEmpty())
839        {
840          if (!item.ForFile)
841          {
842            /* we don't like some names for alt streams inside archive:
843               ":sname"     for "\"
844               "c:::sname"  for "C:\"
845               So we ignore alt streams for these cases */
846            if (name.IsEmpty())
847            {
848              #if defined(_WIN32) && !defined(UNDER_CE)
849              needAltStreams = false;
850              #endif
851
852              /*
853              // do we need to ignore security info for "\\" folder ?
854              #ifdef Z7_USE_SECURITY_CODE
855              needSecurity = false;
856              #endif
857              */
858
859              fullPath = CHAR_PATH_SEPARATOR;
860            }
861            #if defined(_WIN32) && !defined(UNDER_CE)
862            else if (item.IsDriveItem())
863            {
864              needAltStreams = false;
865              fullPath.Add_PathSepar();
866            }
867            #endif
868          }
869        }
870
871        NFind::CFileInfo fi;
872        #if defined(_WIN32) && !defined(UNDER_CE)
873        if (IsVirtualFsFolder(phyPrefix, name))
874        {
875          fi.SetAsDir();
876          fi.Name = us2fs(name);
877        }
878        else
879        #endif
880        if (!FindFile_KeepDots(fi, fullPath  FOLLOW_LINK_PARAM2))
881        {
882          RINOK(dirItems.AddError(fullPath))
883          continue;
884        }
885
886        /*
887        #ifdef _WIN32
888          #define MY_ERROR_IS_DIR     ERROR_FILE_NOT_FOUND
889          #define MY_ERROR_NOT_DIR    DI_DEFAULT_ERROR
890        #else
891          #define MY_ERROR_IS_DIR     EISDIR
892          #define MY_ERROR_NOT_DIR    ENOTDIR
893        #endif
894        */
895
896        const bool isDir = fi.IsDir();
897        if (isDir ? !item.ForDir : !item.ForFile)
898        {
899          // RINOK(dirItems.AddError(fullPath, isDir ? MY_ERROR_IS_DIR: MY_ERROR_NOT_DIR));
900          RINOK(dirItems.AddError(fullPath, DI_DEFAULT_ERROR))
901          continue;
902        }
903        {
904          UStringVector pathParts;
905          pathParts.Add(fs2us(fi.Name));
906          if (curNode.CheckPathToRoot(false, pathParts, !isDir))
907            continue;
908        }
909
910
911       if (dirItems.CanIncludeItem(fi.IsDir()))
912       {
913        int secureIndex = -1;
914        #ifdef Z7_USE_SECURITY_CODE
915        if (needSecurity && dirItems.ReadSecure)
916        {
917          RINOK(dirItems.AddSecurityItem(fullPath, secureIndex))
918        }
919        #endif
920
921        dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi);
922
923        // we don't scan AltStreams for link files
924
925        #if !defined(UNDER_CE)
926        {
927          CDirItem &dirItem = dirItems.Items.Back();
928          RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix))
929          if (dirItem.ReparseData.Size() != 0)
930            continue;
931        }
932
933        #if defined(_WIN32)
934        if (needAltStreams && dirItems.ScanAltStreams)
935        {
936          UStringVector pathParts;
937          pathParts.Add(fs2us(fi.Name));
938          RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent,
939              fullPath,  // including (name)
940              pathParts, // including (fi.Name)
941              true, /* addAllSubStreams */
942              dirItems))
943        }
944        #endif // defined(_WIN32)
945
946        #endif // !defined(UNDER_CE)
947       }
948
949
950        #ifndef _WIN32
951        if (!fi.IsPosixLink()) // posix link can follow to dir
952        #endif
953        if (!isDir)
954          continue;
955
956        UStringVector newParts;
957        const NWildcard::CCensorNode *nextNode = NULL;
958        int index = curNode.FindSubNode(name);
959        if (index >= 0)
960        {
961          for (int t = (int)needEnterVector.Size(); t <= index; t++)
962            needEnterVector.Add(true);
963          needEnterVector[(unsigned)index] = false;
964          nextNode = &curNode.SubNodes[(unsigned)index];
965        }
966        else
967        {
968         #ifndef _WIN32
969          if (fi.IsPosixLink())
970          {
971            // here we can try to resolve posix link
972            // if the link to dir, then can we follow it
973            continue; // we don't follow posix link
974          }
975         #else
976          if (dirItems.SymLinks)
977          {
978            if (fi.HasReparsePoint())
979            {
980              /* 20.03: in SymLinks mode: we don't enter to directory that
981              has reparse point and has no CCensorNode */
982              continue;
983            }
984          }
985         #endif
986          nextNode = &curNode;
987          newParts.Add(name); // don't change it to fi.Name. It's for shortnames support
988        }
989
990        RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix,
991            newParts, dirItems, true))
992      }
993
994      for (i = 0; i < curNode.SubNodes.Size(); i++)
995      {
996        if (i < needEnterVector.Size())
997          if (!needEnterVector[i])
998            continue;
999        const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i];
1000        FString fullPath = phyPrefix + us2fs(nextNode.Name);
1001        NFind::CFileInfo fi;
1002
1003        if (nextNode.Name.IsEmpty())
1004        {
1005          if (phyPrefix.IsEmpty())
1006            fullPath = CHAR_PATH_SEPARATOR;
1007        }
1008      #ifdef _WIN32
1009        else if(phyPrefix.IsEmpty()
1010            || (phyPrefix.Len() == NName::kSuperPathPrefixSize
1011                && IsSuperPath(phyPrefix)))
1012        {
1013          if (NWildcard::IsDriveColonName(nextNode.Name))
1014            fullPath.Add_PathSepar();
1015        }
1016      #endif
1017
1018        // we don't want to call fi.Find() for root folder or virtual folder
1019        if ((phyPrefix.IsEmpty() && nextNode.Name.IsEmpty())
1020            #if defined(_WIN32) && !defined(UNDER_CE)
1021            || IsVirtualFsFolder(phyPrefix, nextNode.Name)
1022            #endif
1023            )
1024        {
1025          fi.SetAsDir();
1026          fi.Name = us2fs(nextNode.Name);
1027        }
1028        else
1029        {
1030          if (!FindFile_KeepDots(fi, fullPath  FOLLOW_LINK_PARAM2))
1031          {
1032            if (!nextNode.AreThereIncludeItems())
1033              continue;
1034            RINOK(dirItems.AddError(fullPath))
1035            continue;
1036          }
1037
1038          if (!fi.IsDir())
1039          {
1040            RINOK(dirItems.AddError(fullPath, DI_DEFAULT_ERROR))
1041            continue;
1042          }
1043        }
1044
1045        RINOK(EnumerateDirItems_Spec(nextNode, phyParent, logParent, fi.Name, phyPrefix,
1046            UStringVector(), dirItems, false))
1047      }
1048
1049      return S_OK;
1050    }
1051  }
1052
1053  #ifdef _WIN32
1054  #ifndef UNDER_CE
1055
1056  // scan drives, if wildcard is "*:\"
1057
1058  if (phyPrefix.IsEmpty() && curNode.IncludeItems.Size() > 0)
1059  {
1060    unsigned i;
1061    for (i = 0; i < curNode.IncludeItems.Size(); i++)
1062    {
1063      const NWildcard::CItem &item = curNode.IncludeItems[i];
1064      if (item.PathParts.Size() < 1)
1065        break;
1066      const UString &name = item.PathParts.Front();
1067      if (name.Len() != 2 || name[1] != ':')
1068        break;
1069      if (item.PathParts.Size() == 1)
1070        if (item.ForFile || !item.ForDir)
1071          break;
1072      if (NWildcard::IsDriveColonName(name))
1073        continue;
1074      if (name[0] != '*' && name[0] != '?')
1075        break;
1076    }
1077    if (i == curNode.IncludeItems.Size())
1078    {
1079      FStringVector driveStrings;
1080      NFind::MyGetLogicalDriveStrings(driveStrings);
1081      for (i = 0; i < driveStrings.Size(); i++)
1082      {
1083        FString driveName = driveStrings[i];
1084        if (driveName.Len() < 3 || driveName.Back() != '\\')
1085          return E_FAIL;
1086        driveName.DeleteBack();
1087        NFind::CFileInfo fi;
1088        fi.SetAsDir();
1089        fi.Name = driveName;
1090
1091        RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix,
1092            addParts, dirItems, enterToSubFolders))
1093      }
1094      return S_OK;
1095    }
1096  }
1097
1098  #endif
1099  #endif
1100
1101
1102  CObjectVector<NFind::CFileInfo> files;
1103
1104  // for (int y = 0; y < 1; y++)
1105  {
1106    // files.Clear();
1107    RINOK(dirItems.EnumerateOneDir(phyPrefix, files))
1108  /*
1109  FOR_VECTOR (i, files)
1110  {
1111    #ifdef _WIN32
1112    // const NFind::CFileInfo &fi = files[i];
1113    #else
1114    NFind::CFileInfo &fi = files[i];
1115    {
1116      const NFind::CFileInfo &di = files[i];
1117      const FString path = phyPrefix + di.Name;
1118      if (!fi.Find_AfterEnumerator(path))
1119      {
1120        RINOK(dirItems.AddError(path));
1121        continue;
1122      }
1123      fi.Name = di.Name;
1124    }
1125    #endif
1126
1127  }
1128  */
1129  }
1130
1131  FOR_VECTOR (i, files)
1132  {
1133    #ifdef _WIN32
1134    const NFind::CFileInfo &fi = files[i];
1135    #else
1136    const NFind::CFileInfo &fi = files[i];
1137    /*
1138    NFind::CFileInfo fi;
1139    {
1140      const NFind::CDirEntry &di = files[i];
1141      const FString path = phyPrefix + di.Name;
1142      if (!fi.Find_AfterEnumerator(path))
1143      {
1144        RINOK(dirItems.AddError(path));
1145        continue;
1146      }
1147      fi.Name = di.Name;
1148    }
1149    */
1150    #endif
1151
1152    RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix,
1153          addParts, dirItems, enterToSubFolders))
1154    if (dirItems.Callback && (i & kScanProgressStepMask) == kScanProgressStepMask)
1155    {
1156      RINOK(dirItems.ScanProgress(phyPrefix))
1157    }
1158  }
1159
1160  return S_OK;
1161}
1162
1163
1164
1165
1166HRESULT EnumerateItems(
1167    const NWildcard::CCensor &censor,
1168    const NWildcard::ECensorPathMode pathMode,
1169    const UString &addPathPrefix, // prefix that will be added to Logical Path
1170    CDirItems &dirItems)
1171{
1172  FOR_VECTOR (i, censor.Pairs)
1173  {
1174    const NWildcard::CPair &pair = censor.Pairs[i];
1175    const int phyParent = pair.Prefix.IsEmpty() ? -1 : (int)dirItems.AddPrefix(-1, -1, pair.Prefix);
1176    int logParent = -1;
1177
1178    if (pathMode == NWildcard::k_AbsPath)
1179      logParent = phyParent;
1180    else
1181    {
1182      if (!addPathPrefix.IsEmpty())
1183        logParent = (int)dirItems.AddPrefix(-1, -1, addPathPrefix);
1184    }
1185
1186    RINOK(EnumerateDirItems(pair.Head, phyParent, logParent, us2fs(pair.Prefix), UStringVector(),
1187        dirItems,
1188        false // enterToSubFolders
1189        ))
1190  }
1191  dirItems.ReserveDown();
1192
1193 #if defined(_WIN32) && !defined(UNDER_CE)
1194  RINOK(dirItems.FillFixedReparse())
1195 #endif
1196
1197 #ifndef _WIN32
1198  RINOK(dirItems.FillDeviceSizes())
1199 #endif
1200
1201  return S_OK;
1202}
1203
1204
1205#if defined(_WIN32) && !defined(UNDER_CE)
1206
1207HRESULT CDirItems::FillFixedReparse()
1208{
1209  FOR_VECTOR(i, Items)
1210  {
1211    CDirItem &item = Items[i];
1212
1213    if (!SymLinks)
1214    {
1215      // continue; // for debug
1216      if (!item.Has_Attrib_ReparsePoint())
1217        continue;
1218
1219      // if (item.IsDir()) continue;
1220
1221      const FString phyPath = GetPhyPath(i);
1222
1223      NFind::CFileInfo fi;
1224      if (fi.Fill_From_ByHandleFileInfo(phyPath)) // item.IsDir()
1225      {
1226        item.Size = fi.Size;
1227        item.CTime = fi.CTime;
1228        item.ATime = fi.ATime;
1229        item.MTime = fi.MTime;
1230        item.Attrib = fi.Attrib;
1231        continue;
1232      }
1233
1234      /*
1235      // we request properties of target file instead of properies of symbolic link
1236      // here we also can manually parse unsupported links (like WSL links)
1237      NIO::CInFile inFile;
1238      if (inFile.Open(phyPath))
1239      {
1240        BY_HANDLE_FILE_INFORMATION info;
1241        if (inFile.GetFileInformation(&info))
1242        {
1243          // Stat.FilesSize doesn't contain item.Size already
1244          // Stat.FilesSize -= item.Size;
1245          item.Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow;
1246          Stat.FilesSize += item.Size;
1247          item.CTime = info.ftCreationTime;
1248          item.ATime = info.ftLastAccessTime;
1249          item.MTime = info.ftLastWriteTime;
1250          item.Attrib = info.dwFileAttributes;
1251          continue;
1252        }
1253      }
1254      */
1255
1256      RINOK(AddError(phyPath))
1257      continue;
1258    }
1259
1260    // (SymLinks == true) here
1261
1262    if (item.ReparseData.Size() == 0)
1263      continue;
1264
1265    // if (item.Size == 0)
1266    {
1267      // 20.03: we use Reparse Data instead of real data
1268      item.Size = item.ReparseData.Size();
1269    }
1270
1271    CReparseAttr attr;
1272    if (!attr.Parse(item.ReparseData, item.ReparseData.Size()))
1273    {
1274      const FString phyPath = GetPhyPath(i);
1275      AddError(phyPath, attr.ErrorCode);
1276      continue;
1277    }
1278
1279    /* imagex/WIM reduces absolute paths in links (raparse data),
1280       if we archive non root folder. We do same thing here */
1281
1282    bool isWSL = false;
1283    if (attr.IsSymLink_WSL())
1284    {
1285      // isWSL = true;
1286      // we don't change WSL symlinks
1287      continue;
1288    }
1289    else
1290    {
1291      if (attr.IsRelative_Win())
1292        continue;
1293    }
1294
1295    const UString &link = attr.GetPath();
1296    if (!IsDrivePath(link))
1297      continue;
1298    // maybe we need to support networks paths also ?
1299
1300    FString fullPathF;
1301    if (!NDir::MyGetFullPathName(GetPhyPath(i), fullPathF))
1302      continue;
1303    const UString fullPath = fs2us(fullPathF);
1304    const UString logPath = GetLogPath(i);
1305    if (logPath.Len() >= fullPath.Len())
1306      continue;
1307    if (CompareFileNames(logPath, fullPath.RightPtr(logPath.Len())) != 0)
1308      continue;
1309
1310    const UString prefix = fullPath.Left(fullPath.Len() - logPath.Len());
1311    if (!IsPathSepar(prefix.Back()))
1312      continue;
1313
1314    const unsigned rootPrefixSize = GetRootPrefixSize(prefix);
1315    if (rootPrefixSize == 0)
1316      continue;
1317    if (rootPrefixSize == prefix.Len())
1318      continue; // simple case: paths are from root
1319
1320    if (link.Len() <= prefix.Len())
1321      continue;
1322
1323    if (CompareFileNames(link.Left(prefix.Len()), prefix) != 0)
1324      continue;
1325
1326    UString newLink = prefix.Left(rootPrefixSize);
1327    newLink += link.Ptr(prefix.Len());
1328
1329    CByteBuffer data;
1330    bool isSymLink = !attr.IsMountPoint();
1331    if (!FillLinkData(data, newLink, isSymLink, isWSL))
1332      continue;
1333    item.ReparseData2 = data;
1334  }
1335  return S_OK;
1336}
1337
1338#endif
1339
1340
1341#ifndef _WIN32
1342
1343HRESULT CDirItems::FillDeviceSizes()
1344{
1345  {
1346    FOR_VECTOR (i, Items)
1347    {
1348      CDirItem &item = Items[i];
1349
1350      if (S_ISBLK(item.mode) && item.Size == 0)
1351      {
1352        const FString phyPath = GetPhyPath(i);
1353        NIO::CInFile inFile;
1354        inFile.PreserveATime = true;
1355        if (inFile.OpenShared(phyPath, ShareForWrite)) // fixme: OpenShared ??
1356        {
1357          UInt64 size = 0;
1358          if (inFile.GetLength(size))
1359            item.Size = size;
1360        }
1361      }
1362      if (StoreOwnerName)
1363      {
1364        OwnerNameMap.Add_UInt32(item.uid);
1365        OwnerGroupMap.Add_UInt32(item.gid);
1366      }
1367    }
1368  }
1369
1370  if (StoreOwnerName)
1371  {
1372    UString u;
1373    AString a;
1374    {
1375      FOR_VECTOR (i, OwnerNameMap.Numbers)
1376      {
1377        // 200K/sec speed
1378        u.Empty();
1379        const passwd *pw = getpwuid(OwnerNameMap.Numbers[i]);
1380        // printf("\ngetpwuid=%s\n", pw->pw_name);
1381        if (pw)
1382        {
1383          a = pw->pw_name;
1384          ConvertUTF8ToUnicode(a, u);
1385        }
1386        OwnerNameMap.Strings.Add(u);
1387      }
1388    }
1389    {
1390      FOR_VECTOR (i, OwnerGroupMap.Numbers)
1391      {
1392        u.Empty();
1393        const group *gr = getgrgid(OwnerGroupMap.Numbers[i]);
1394        if (gr)
1395        {
1396          // printf("\ngetgrgid %d %s\n", OwnerGroupMap.Numbers[i], gr->gr_name);
1397          a = gr->gr_name;
1398          ConvertUTF8ToUnicode(a, u);
1399        }
1400        OwnerGroupMap.Strings.Add(u);
1401      }
1402    }
1403
1404    FOR_VECTOR (i, Items)
1405    {
1406      CDirItem &item = Items[i];
1407      {
1408        const int index = OwnerNameMap.Find(item.uid);
1409        if (index < 0) throw 1;
1410        item.OwnerNameIndex = index;
1411      }
1412      {
1413        const int index = OwnerGroupMap.Find(item.gid);
1414        if (index < 0) throw 1;
1415        item.OwnerGroupIndex = index;
1416      }
1417    }
1418  }
1419
1420
1421  // if (NeedOwnerNames)
1422  {
1423    /*
1424    {
1425      for (unsigned i = 0 ; i < 10000; i++)
1426      {
1427        const passwd *pw = getpwuid(i);
1428        if (pw)
1429        {
1430          UString u;
1431          ConvertUTF8ToUnicode(AString(pw->pw_name), u);
1432          OwnerNameMap.Add(i, u);
1433          OwnerNameMap.Add(i, u);
1434          OwnerNameMap.Add(i, u);
1435        }
1436        const group *gr = getgrgid(i);
1437        if (gr)
1438        {
1439          // we can use utf-8 here.
1440          UString u;
1441          ConvertUTF8ToUnicode(AString(gr->gr_name), u);
1442          OwnerGroupMap.Add(i, u);
1443        }
1444      }
1445    }
1446    */
1447    /*
1448    {
1449      FOR_VECTOR (i, OwnerNameMap.Strings)
1450      {
1451        AString s;
1452        ConvertUnicodeToUTF8(OwnerNameMap.Strings[i], s);
1453        printf("\n%5d %s", (unsigned)OwnerNameMap.Numbers[i], s.Ptr());
1454      }
1455    }
1456    {
1457      printf("\n\n=========Groups\n");
1458      FOR_VECTOR (i, OwnerGroupMap.Strings)
1459      {
1460        AString s;
1461        ConvertUnicodeToUTF8(OwnerGroupMap.Strings[i], s);
1462        printf("\n%5d %s", (unsigned)OwnerGroupMap.Numbers[i], s.Ptr());
1463      }
1464    }
1465    */
1466  }
1467      /*
1468      for (unsigned i = 0 ; i < 100000000; i++)
1469      {
1470        // const passwd *pw = getpwuid(1000);
1471        // pw = pw;
1472        int pos = OwnerNameMap.Find(1000);
1473        if (pos < 0 - (int)i)
1474          throw 1;
1475      }
1476      */
1477
1478  return S_OK;
1479}
1480
1481#endif
1482
1483
1484
1485static const char * const kCannotFindArchive = "Cannot find archive";
1486
1487HRESULT EnumerateDirItemsAndSort(
1488    NWildcard::CCensor &censor,
1489    NWildcard::ECensorPathMode censorPathMode,
1490    const UString &addPathPrefix,
1491    UStringVector &sortedPaths,
1492    UStringVector &sortedFullPaths,
1493    CDirItemsStat &st,
1494    IDirItemsCallback *callback)
1495{
1496  FStringVector paths;
1497
1498  {
1499    CDirItems dirItems;
1500    dirItems.Callback = callback;
1501    {
1502      HRESULT res = EnumerateItems(censor, censorPathMode, addPathPrefix, dirItems);
1503      st = dirItems.Stat;
1504      RINOK(res)
1505    }
1506
1507    FOR_VECTOR (i, dirItems.Items)
1508    {
1509      const CDirItem &dirItem = dirItems.Items[i];
1510      if (!dirItem.IsDir())
1511        paths.Add(dirItems.GetPhyPath(i));
1512    }
1513  }
1514
1515  if (paths.Size() == 0)
1516  {
1517    // return S_OK;
1518    throw CMessagePathException(kCannotFindArchive);
1519  }
1520
1521  UStringVector fullPaths;
1522
1523  unsigned i;
1524
1525  for (i = 0; i < paths.Size(); i++)
1526  {
1527    FString fullPath;
1528    NFile::NDir::MyGetFullPathName(paths[i], fullPath);
1529    fullPaths.Add(fs2us(fullPath));
1530  }
1531
1532  CUIntVector indices;
1533  SortFileNames(fullPaths, indices);
1534  sortedPaths.ClearAndReserve(indices.Size());
1535  sortedFullPaths.ClearAndReserve(indices.Size());
1536
1537  for (i = 0; i < indices.Size(); i++)
1538  {
1539    unsigned index = indices[i];
1540    sortedPaths.AddInReserved(fs2us(paths[index]));
1541    sortedFullPaths.AddInReserved(fullPaths[index]);
1542    if (i > 0 && CompareFileNames(sortedFullPaths[i], sortedFullPaths[i - 1]) == 0)
1543      throw CMessagePathException("Duplicate archive path:", sortedFullPaths[i]);
1544  }
1545
1546  return S_OK;
1547}
1548
1549
1550
1551
1552#ifdef _WIN32
1553
1554static bool IsDotsName(const wchar_t *s)
1555{
1556  return s[0] == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0));
1557}
1558
1559// This code converts all short file names to long file names.
1560
1561static void ConvertToLongName(const UString &prefix, UString &name)
1562{
1563  if (name.IsEmpty()
1564      || DoesNameContainWildcard(name)
1565      || IsDotsName(name))
1566    return;
1567  NFind::CFileInfo fi;
1568  const FString path (us2fs(prefix + name));
1569  #ifndef UNDER_CE
1570  if (NFile::NName::IsDevicePath(path))
1571    return;
1572  #endif
1573  if (fi.Find(path))
1574    name = fs2us(fi.Name);
1575}
1576
1577static void ConvertToLongNames(const UString &prefix, CObjectVector<NWildcard::CItem> &items)
1578{
1579  FOR_VECTOR (i, items)
1580  {
1581    NWildcard::CItem &item = items[i];
1582    if (item.Recursive || item.PathParts.Size() != 1)
1583      continue;
1584    if (prefix.IsEmpty() && item.IsDriveItem())
1585      continue;
1586    ConvertToLongName(prefix, item.PathParts.Front());
1587  }
1588}
1589
1590static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node)
1591{
1592  ConvertToLongNames(prefix, node.IncludeItems);
1593  ConvertToLongNames(prefix, node.ExcludeItems);
1594  unsigned i;
1595  for (i = 0; i < node.SubNodes.Size(); i++)
1596  {
1597    UString &name = node.SubNodes[i].Name;
1598    if (prefix.IsEmpty() && NWildcard::IsDriveColonName(name))
1599      continue;
1600    ConvertToLongName(prefix, name);
1601  }
1602  // mix folders with same name
1603  for (i = 0; i < node.SubNodes.Size(); i++)
1604  {
1605    NWildcard::CCensorNode &nextNode1 = node.SubNodes[i];
1606    for (unsigned j = i + 1; j < node.SubNodes.Size();)
1607    {
1608      const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j];
1609      if (nextNode1.Name.IsEqualTo_NoCase(nextNode2.Name))
1610      {
1611        nextNode1.IncludeItems += nextNode2.IncludeItems;
1612        nextNode1.ExcludeItems += nextNode2.ExcludeItems;
1613        node.SubNodes.Delete(j);
1614      }
1615      else
1616        j++;
1617    }
1618  }
1619  for (i = 0; i < node.SubNodes.Size(); i++)
1620  {
1621    NWildcard::CCensorNode &nextNode = node.SubNodes[i];
1622    ConvertToLongNames(prefix + nextNode.Name + WCHAR_PATH_SEPARATOR, nextNode);
1623  }
1624}
1625
1626void ConvertToLongNames(NWildcard::CCensor &censor)
1627{
1628  FOR_VECTOR (i, censor.Pairs)
1629  {
1630    NWildcard::CPair &pair = censor.Pairs[i];
1631    ConvertToLongNames(pair.Prefix, pair.Head);
1632  }
1633}
1634
1635#endif
1636
1637
1638CMessagePathException::CMessagePathException(const char *a, const wchar_t *u)
1639{
1640  (*this) += a;
1641  if (u)
1642  {
1643    Add_LF();
1644    (*this) += u;
1645  }
1646}
1647
1648CMessagePathException::CMessagePathException(const wchar_t *a, const wchar_t *u)
1649{
1650  (*this) += a;
1651  if (u)
1652  {
1653    Add_LF();
1654    (*this) += u;
1655  }
1656}
1657