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