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