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