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