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