• 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 Z7_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 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 
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 // Z7_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 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 /*
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  #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 
477 static 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 
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)497 static 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 
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)521 static 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 
SetLinkInfo(CDirItem & dirItem,const NFind::CFileInfo & fi,const FString & phyPrefix)572 HRESULT 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 
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)603 static 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 
CanUseFsDirect(const NWildcard::CCensorNode & curNode)743 static 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 
IsVirtualFsFolder(const FString & prefix,const UString & name)775 static 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 
EnumerateDirItems(const NWildcard::CCensorNode & curNode,const int phyParent,const int logParent,const FString & phyPrefix,const UStringVector & addParts,CDirItems & dirItems,bool enterToSubFolders)788 static 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 
EnumerateItems(const NWildcard::CCensor & censor,const NWildcard::ECensorPathMode pathMode,const UString & addPathPrefix,CDirItems & dirItems)1166 HRESULT 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 
FillFixedReparse()1207 HRESULT 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 
FillDeviceSizes()1343 HRESULT 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 
1485 static const char * const kCannotFindArchive = "Cannot find archive";
1486 
EnumerateDirItemsAndSort(NWildcard::CCensor & censor,NWildcard::ECensorPathMode censorPathMode,const UString & addPathPrefix,UStringVector & sortedPaths,UStringVector & sortedFullPaths,CDirItemsStat & st,IDirItemsCallback * callback)1487 HRESULT 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 
IsDotsName(const wchar_t * s)1554 static 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 
ConvertToLongName(const UString & prefix,UString & name)1561 static 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 
ConvertToLongNames(const UString & prefix,CObjectVector<NWildcard::CItem> & items)1577 static 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 
ConvertToLongNames(const UString & prefix,NWildcard::CCensorNode & node)1590 static 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 
ConvertToLongNames(NWildcard::CCensor & censor)1626 void 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 
CMessagePathException(const char * a,const wchar_t * u)1638 CMessagePathException::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 
CMessagePathException(const wchar_t * a,const wchar_t * u)1648 CMessagePathException::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