4 Сканирование текущего каталога и, опционально, подкаталогов на
8 Copyright (c) 1996 Eugene Roshal
9 Copyright (c) 2000 Far Group
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions
15 1. Redistributions of source code must retain the above copyright
16 notice, this list of conditions and the following disclaimer.
17 2. Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
20 3. The name of the authors may not be used to endorse or promote products
21 derived from this software without specific prior written permission.
23 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include "headers.hpp"
37 #include "scantree.hpp"
40 #include "pathmix.hpp"
41 #include "processname.hpp"
43 ScanTree::ScanTree(int RetUpDir
, int Recurse
, int ScanJunction
)
45 Flags
.Change(FSCANTREE_RETUPDIR
, RetUpDir
);
46 Flags
.Change(FSCANTREE_RECUR
, Recurse
);
47 Flags
.Change(FSCANTREE_SCANSYMLINK
, (ScanJunction
== -1 ? Opt
.ScanJunction
: ScanJunction
));
50 void ScanTree::SetFindPath(const wchar_t *Path
, const wchar_t *Mask
, const DWORD NewScanFlags
)
52 Flags
.Flags
= (Flags
.Flags
& 0x0000FFFF) | (NewScanFlags
& 0xFFFF0000);
53 strFindPath
= *Path
? Path
: L
".";
54 strFindMask
= wcscmp(Mask
, L
"*") ? Mask
: L
"";
57 if (strFindPath
!= WGOOD_SLASH
) {
58 DeleteEndSlash(strFindPath
);
61 ScanDirStack
.emplace_back();
62 ConvertNameToReal(strFindPath
.c_str(), ScanDirStack
.back().RealPath
);
67 void ScanTree::CheckForEnterSubdir(const FAR_FIND_DATA_EX
*fdata
)
69 if ((fdata
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) == 0 || !Flags
.Check(FSCANTREE_RECUR
))
72 if ((fdata
->dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) != 0 && !Flags
.Check(FSCANTREE_SCANSYMLINK
))
75 const bool InsideSymlink
=
76 (fdata
->dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) != 0 || IsInsideSymlink();
78 ScanDirStack
.emplace_back();
79 strFindPath
.append(fdata
->strFileName
.CPtr(), fdata
->strFileName
.GetLength());
81 if (fdata
->dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) {
82 ConvertNameToReal(strFindPath
.c_str(), ScanDirStack
.back().RealPath
);
84 // check if converted path points to same location is already scanned or to parent path of already scanned location
85 // NB: in original FAR here was exact-match check all paths (not only symlinks)
86 // that caused excessive scan from FS root cuz in Linux links pointing to / are usual situation unlike Windows
87 // NB2: There're two recursive protection checks, excessive on first look:
88 // Check by path: if real path is equal OR represents parent of some previously scanned path
89 // Check by inode: inode matches some previously scanned inode
90 // while check-by-path 'includes' check-by-node functionality, later is needed cuz in some
91 // complex cases realpath() fails with error like ELOOP fails resulting path be unresolved,
92 // so check-by-inode works as last resort to avoid unlimited recursion scan in such cases.
93 const auto &RealPath
= ScanDirStack
.back().RealPath
;
94 for (auto it
= ScanDirStack
.rbegin();;) {
95 if (ScanDirStack
.rend() == ++it
)
97 const auto &IthPath
= it
->RealPath
;
98 if ((it
->UnixDevice
== fdata
->UnixDevice
&& it
->UnixNode
== fdata
->UnixNode
)
99 || (IthPath
.Begins(RealPath
)
100 && (IthPath
.GetLength() == RealPath
.GetLength() || IthPath
.At(RealPath
.GetLength()) == GOOD_SLASH
101 || RealPath
.GetLength() == 1))) { // recursion! revert state changes made so far and bail out
102 ScanDirStack
.pop_back();
103 strFindPath
.resize(strFindPath
.size() - fdata
->strFileName
.GetLength());
108 ScanDirStack
.back().RealPath
= strFindPath
;
110 ScanDirStack
.back().UnixDevice
= fdata
->UnixDevice
;
111 ScanDirStack
.back().UnixNode
= fdata
->UnixNode
;
112 ScanDirStack
.back().InsideSymlink
= InsideSymlink
;
117 void ScanTree::StartEnumSubdir()
119 if (!strFindPath
.empty() && strFindPath
.back() != LGOOD_SLASH
)
120 strFindPath
+= LGOOD_SLASH
;
122 DWORD WinPortFindFlags
= 0;
123 if (Flags
.Check(FSCANTREE_NOLINKS
))
124 WinPortFindFlags
|= FIND_FILE_FLAG_NO_LINKS
;
125 if (Flags
.Check(FSCANTREE_NOFILES
))
126 WinPortFindFlags
|= FIND_FILE_FLAG_NO_FILES
;
127 if (Flags
.Check(FSCANTREE_NODEVICES
))
128 WinPortFindFlags
|= FIND_FILE_FLAG_NO_DEVICES
;
129 if (Flags
.Check(FSCANTREE_CASE_INSENSITIVE
))
130 WinPortFindFlags
|= FIND_FILE_FLAG_CASE_INSENSITIVE
;
132 strFindPath
+= L
'*'; // append temporary asterisk
134 ScanDirStack
.back().Enumer
.reset(
135 new FindFile(strFindPath
.c_str(), Flags
.Check(FSCANTREE_SCANSYMLINK
), WinPortFindFlags
));
137 strFindPath
.pop_back(); // strip asterisk
140 void ScanTree::LeaveSubdir()
142 if (!ScanDirStack
.empty()) {
143 ScanDirStack
.pop_back();
144 size_t p
= strFindPath
.rfind(GOOD_SLASH
, strFindPath
.size() - 2);
145 if (p
!= std::string::npos
) {
146 strFindPath
.resize(p
+ 1);
150 fprintf(stderr
, "ScanTree::LeaveSubdir() invoked on empty stack!\n");
153 bool ScanTree::ScanDir::GetNext(FAR_FIND_DATA_EX
*fdata
, bool FilesFirst
)
157 if (!Enumer
->Get(*fdata
)) {
161 if (!FilesFirst
|| (fdata
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) == 0)
164 Postponed
.emplace_back(std::move(*fdata
));
167 if (!Postponed
.empty()) {
168 *fdata
= std::move(Postponed
.front());
169 Postponed
.pop_front();
176 bool ScanTree::GetNextName(FAR_FIND_DATA_EX
*fdata
, FARString
&strFullName
)
178 Flags
.Clear(FSCANTREE_SECONDDIRNAME
);
181 if (ScanDirStack
.empty())
184 if (!ScanDirStack
.back().GetNext(fdata
, Flags
.Check(FSCANTREE_FILESFIRST
))) {
185 if (!Flags
.Check(FSCANTREE_RETUPDIR
) || ScanDirStack
.size() == 1) {
190 strFullName
= strFindPath
;
191 DeleteEndSlash(strFullName
);
192 apiGetFindDataEx(strFullName
, *fdata
);
194 Flags
.Set(FSCANTREE_SECONDDIRNAME
);
198 const bool Matched
= strFindMask
.empty() || CmpName(strFindMask
.c_str(), fdata
->strFileName
, false);
200 strFullName
= strFindPath
;
201 strFullName
+= fdata
->strFileName
;
204 CheckForEnterSubdir(fdata
);
211 void ScanTree::SkipDir()