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