ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
fscanner.cpp
1#if !DOXYGEN
3
4
5using namespace alib::system;
6
7namespace alib::filetree { namespace {
8
9// forward declaration
10
11CanonicalResult makeCanonicalRecursion( Path& sourcePath,
12 FTree::Cursor& node,
13 Path& pathToNode,
14 CanonicalPathList* resultPaths,
15 FTree::Cursor callingNode );
16
17// scan parameters used with MakeCanonical to evaluate directory entries
18ScanParameters paramsPathOnly( nullptr, ScanParameters::SymbolicLinks::RESOLVE_BUT_DONT_FOLLOW, 0, true, true );
19
20} // namespace alib::filetree[::anonymous]
21
22#if ALIB_DEBUG
24 A_CHAR(" {:ta h{2,r} on{10,r} gn{10,r} s(IEC){10,r} dm qqq FxFa (rd{3r}' D' rf{3r}' F' re{2r}' EA' rb{2r}'BL) 'nf l}");
25#endif
26
27} // namespace [alib::filetree]
28
29
30//--------------------------------------------------------------------------------------------------
31//--- scanFilePosix
32//--------------------------------------------------------------------------------------------------
33#if ALIB_SYSTEM_FILE_STATUS_IMPL == ALIB_SYSTEM_FILE_POSIX_STATUS
34
35#if ALIB_DEBUG
36# define DBG_CHECKERRNO_WITH_PATH \
37 ALIB_ASSERT_WARNING(errno == 0, "FILETREE", "Errno set ({})\"{}\". Current path: {}", \
38 errno, std::errc(errno), actPath ) \
39 errno= 0;
40#else
41# define DBG_CHECKERRNO_WITH_PATH
42#endif
43
44static_assert(std::is_same_v<alib::PathCharType,char>,
45 "Posix systems use type <char> for file paths, while <alib::PathCharType> is not. "
46 "Cannot compile POSIX file scanner!" );
47
48// Since Kernel 4.11 Linux/glibc has "statx". We use it if available on the current platform.
49#if defined(__NR_statx)
50# define TMP_STATX_AVAILABLE 1
51# define STATMEMBER(Name) stats.stx_ ## Name
52# define STAT_DEV_MAJOR stats.stx_dev_major
53# define STAT_DEV_MINOR stats.stx_dev_minor
54#else
55# define TMP_STATX_AVAILABLE 0
56# define STATMEMBER(Name) stats.st_ ## Name
57# define STAT_DEV_MAJOR major(stats.st_dev)
58# define STAT_DEV_MINOR minor(stats.st_dev)
59#endif
60
61
62namespace alib::filetree { namespace {
63void scanFilePosix( DIR* pxDir,
64 FTree::Cursor& node,
65 const NCString& nameOrFullPath, // if full path, this has the same buffer as actPath!
66 unsigned depth,
67 ScanParameters& params,
68 uint64_t curDev,
69 bool curDevIsArtificial,
70 FTValue::DirectorySums& parentSums ,
71 NAString& actPath,
72 CanonicalPathList* resultPaths ) {
73
74 #if ALIB_DEBUG
76 && ( actPath.Length()==1
77 || actPath.CharAtEnd() != DIRECTORY_SEPARATOR )
78 && actPath.IndexOf(strings::TLocalString<PathCharType,4>(
80 "FILETREE","Given path \"{}\" not absolute or ending with '{}'",
81 actPath, DIRECTORY_SEPARATOR )
82
83 NString512 dbgActFile;
85 if( actPath.Buffer() == nameOrFullPath.Buffer() )
86 dbgActFile << nameOrFullPath;
87 else {
88 dbgActFile << actPath;
89 if(dbgActFile.Length()>1)
90 dbgActFile << DIRECTORY_SEPARATOR;
91 dbgActFile << nameOrFullPath;
92 }
93 #if ALOX_DBG_LOG
94 LocalAllocator<1> verboseAllocator;
95 BoxesMA verboseLogables( verboseAllocator);
96 int verboseLoggers;
97 Log_IsActive(verboseLoggers, Verbosity::Verbose)
98 if( verboseLoggers ) {
99 verboseLogables.Add("{!AWidth:>} ");
100 if( &params == &paramsPathOnly )
101 verboseLogables.Add("PO"); // 'Path Only'
102 else {
103 auto& depthString= *verboseAllocator().New<String128>();
104 depthString << depth << DIRECTORY_SEPARATOR
105 << ( params.MaxDepth < std::numeric_limits<unsigned>::max()
106 ? String128(params.MaxDepth)
107 : String(A_CHAR("M")) );
108 verboseLogables.Add(depthString);
109 }
110
111 verboseLogables.Add(filetree::DBG_FILETREE_SCAN_VERBOSE_LOG_FORMAT, FTFile(node) );
112 }
113 #endif // ALOX_DBG_LOG
114
115 ALIB_ASSERT_WARNING(errno == 0, "FILETREE", "Errno set ({})\"{}\" with current file: {}",
116 errno, std::errc(errno), NString(dbgActFile) )
117 errno= 0;
118 #endif // ALIB_DEBUG
119
120 FTValue& value = *node;
121 auto oldScanState= value.ScanState();
122
123 //------------------------------------------- get stats? -----------------------------------------
127 {
129 strings::TLocalString<PathCharType, PATH_MAX> symLinkDest;
130 strings::TLocalString<PathCharType, PATH_MAX> symLinkDestReal;
131
132 // read base stats
133 ALIB_DBG( errno= 0;)
134 #if TMP_STATX_AVAILABLE
135 struct statx stats;
136 int statResult= statx( pxDir ? dirfd(pxDir) : AT_FDCWD,
137 nameOrFullPath,
138 AT_STATX_DONT_SYNC | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW,
139 STATX_BASIC_STATS | STATX_BTIME,
140 &stats );
141
142 #else
143 struct stat stats;
144 int statResult= pxDir ? fstatat(dirfd(pxDir), nameOrFullPath, &stats,
145 AT_SYMLINK_NOFOLLOW
146 #if !defined(__APPLE__)
147 | AT_NO_AUTOMOUNT
148 #endif
149 )
150 : lstat ( nameOrFullPath, &stats );
151 #endif
152 if( statResult ) {
153 ALIB_ASSERT_WARNING( errno != ENOENT, "FILETREE",
154 "File does not exist (anymore) while stating \"{}\"",
155 NString(dbgActFile) )
156 ALIB_ASSERT_WARNING( errno == ENOENT, "FILETREE", "Unknown error ({}) \"{}\" while stating file \"{}\"",
157 errno, std::errc(errno), NString(dbgActFile) )
158 value.SetScanState(errno == ENOENT ? FTValue::ScanStates::NOT_EXISTENT
160 ALIB_DBG( errno= 0;)
161 goto APPLY_POST_RECURSION_FILTER;
162 }
163 DBG_CHECKERRNO_WITH_PATH
164
165 // check filesystem type (artificial fs & mount point)
166 uint64_t device= (uint64_t(STAT_DEV_MAJOR) << 32L) + STAT_DEV_MINOR;
167 if( curDev == 0 || curDev != device ) {
168 value.SetCrossingFS();
169
170 // Use fstatfs to properly detect artificial filesystems
171#if defined(__linux__)
172 struct statfs fsStats;
173 int fsResult;
174 if (pxDir) {
175 // Need full path for statfs - build it temporarily
176 ALIB_STRING_RESETTER(actPath)
177 if( actPath.Length() > 1 ) actPath << DIRECTORY_SEPARATOR;
178 actPath << nameOrFullPath;
179 fsResult = statfs(actPath, &fsStats);
180 } else {
181 fsResult = statfs(nameOrFullPath, &fsStats);
182 }
183
184 if (fsResult == 0) {
185 // Check for known artificial filesystem types
186 switch(fsStats.f_type) {
187 case PROC_SUPER_MAGIC: // /proc
188 case SYSFS_MAGIC: // /sys
189 case CGROUP_SUPER_MAGIC: // cgroups
190 case CGROUP2_SUPER_MAGIC: // cgroups v2
191 case DEBUGFS_MAGIC: // debugfs
192 case DEVPTS_SUPER_MAGIC: // /dev/pts
193 case SELINUX_MAGIC: // selinuxfs
194 case SECURITYFS_MAGIC: // securityfs
195 case TRACEFS_MAGIC: // tracefs
196 value.SetArtificialFS();
197 break;
198 default:
199 break;
200 } }
201#elif defined(__APPLE__)
202 struct statfs fsStats;
203 int fsResult;
204 if (pxDir) {
205 // Need full path - build it temporarily
206 ALIB_STRING_RESETTER(actPath)
207 if( actPath.Length() > 1 ) actPath << DIRECTORY_SEPARATOR;
208 actPath << nameOrFullPath;
209 fsResult = statfs(actPath.Terminate(), &fsStats);
210 } else {
211 fsResult = statfs(nameOrFullPath, &fsStats);
212 }
213 if (fsResult == 0) {
214 // Check macOS artificial filesystems
215 if (strcmp(fsStats.f_fstypename, "devfs") == 0 ||
216 strcmp(fsStats.f_fstypename, "autofs") == 0) {
217 value.SetTargetArtificialFS();
218 } }
219# else
220 // Fallback to device number check for other systems
221 if( STAT_DEV_MAJOR == 0
222 && STAT_DEV_MINOR != 35 ) // tmpfs
223 value.SetArtificialFS();
224#endif
225 curDev= device;
226 curDevIsArtificial= value.IsArtificialFS();
227 }
228
229
230 //---------------------------------------- is symlink? ---------------------------------------
231 bool origFileIsSymlink= (STATMEMBER(mode) & S_IFMT) == S_IFLNK;
232 if( origFileIsSymlink
234 {
235 value.SetScanState( FTValue::ScanStates::RESOLVED );
236
237 // 1. Read plain symlink target (only to be attached to the entry)
238 symLinkDest.EnsureRemainingCapacity(PATH_MAX - 1);
239 ssize_t cntChars= pxDir ? readlinkat( dirfd(pxDir), nameOrFullPath, symLinkDest.VBuffer(), PATH_MAX)
240 : readlink ( nameOrFullPath, symLinkDest.VBuffer(), PATH_MAX);
241
242 if (cntChars == -1) switch(errno) {
243 case EACCES: value.SetScanState(FTValue::ScanStates::NO_ACCESS_SL); ALIB_DBG(errno= 0;)
244 goto ABORT_SYMLINK;
245
246 case ENOENT: value.SetScanState(FTValue::ScanStates::NO_ACCESS_SL);
247 ALIB_ASSERT_ERROR(STAT_DEV_MAJOR == 0, "FILETREE",
248 "Posix raised ({}) \"{}\" on reading a symbolic link which is not located on "
249 "an artificial filesystem (like /proc). File:\"{}\"",
250 errno, std::errc(errno), NString(dbgActFile) ) ALIB_DBG(errno= 0;)
251 goto ABORT_SYMLINK;
252
253 default: value.SetScanState(FTValue::ScanStates::UNKNOWN_ERROR);
254 ALIB_ERROR("FILETREE", "Posix raised ({}) \"{}\" on reading symbolic link \"{}\"",
255 errno, std::errc(errno), NString(dbgActFile) ) ALIB_DBG(errno= 0;)
256 goto ABORT_SYMLINK;
257 }
258 symLinkDest.SetLength(cntChars);
259
260 // 2. Read symlink's real target path (fully and recursively translated)
261 ALIB_STRING_RESETTER(actPath)
262 if( pxDir )
263 actPath << DIRECTORY_SEPARATOR << nameOrFullPath;
264 errno= 0;
265 symLinkDestReal.EnsureRemainingCapacity(PATH_MAX - 1);
266 *symLinkDestReal.VBuffer()= '\0';
267 if(! realpath(actPath, symLinkDestReal.VBuffer() ) ) switch (errno)
268 { // The named file does not exist.
269 case ENOENT: if( *symLinkDestReal.VBuffer() != '\0')
270 symLinkDestReal.DetectLength();
272 goto ABORT_SYMLINK;
273 case ELOOP: value.SetScanState(FTValue::ScanStates::CIRCULAR_LINK); ALIB_DBG(errno= 0;)
274 goto ABORT_SYMLINK;
275 // this might happen with strange system files
277 goto ABORT_SYMLINK;
278 default: ALIB_ERROR("FILETREE", "Posix raised ({}) \"{}\" on resolving symbolic link \"{}\"",
279 errno, std::errc(errno), NString(dbgActFile) ) ALIB_DBG(errno= 0;)
280 goto ABORT_SYMLINK;
281 }
282 symLinkDestReal.DetectLength();
283
284 ALIB_DBG( if( errno == EINVAL) errno= 0;) // this happens, even though realpath() above returned 'OK'
285 DBG_CHECKERRNO_WITH_PATH
286 ALIB_ASSERT_ERROR( Path::IsAbsolute(symLinkDestReal), "FILETREE",
287 "Real path is not absolute: ", symLinkDestReal )
288
289 // 3. get resolved status
290 DBG_CHECKERRNO_WITH_PATH
291 #if TMP_STATX_AVAILABLE
292 statResult= statx( AT_FDCWD,
293 symLinkDestReal,
294 AT_STATX_DONT_SYNC | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW,
295 STATX_ALL,
296 &stats );
297 #else
298 statResult= stat(symLinkDestReal.Terminate(), &stats );
299 #endif
300 DBG_CHECKERRNO_WITH_PATH
301 #if ALIB_CHARACTERS_WIDE
302 symLinkDestReal.Reset(symLinkDestReal);
303 #endif
304
305 if(statResult == -1 ) {
307 if(errno) switch( std::errc(errno) )
308 { case std::errc::no_such_file_or_directory:
310 ALIB_DBG(errno= 0;)
311 goto APPLY_POST_RECURSION_FILTER;
312
313 default:
314 ALIB_WARNING("FILETREE",
315 "Unhandled error code invoking 'stat()' on resolved symbolic "
316 "link: {} (\"{}\")\n Symbolic link target: \"{}\"",
317 errno, std::errc(errno), NString(dbgActFile) )
318 ALIB_DBG(errno= 0;)
320 goto APPLY_POST_RECURSION_FILTER;
321 }
323 }
324
325 // check for target artificial fs
326 uint64_t linkTargetDevice= (uint64_t(STAT_DEV_MAJOR) << 32L) + STAT_DEV_MINOR;
327 if( linkTargetDevice == curDev ) {
328 if( curDevIsArtificial ) value.SetTargetArtificialFS();
329 }
330 else {
331#if defined(__linux__)
332 struct statfs fsStats;
333 if (statfs(symLinkDestReal, &fsStats) == 0) {
334 // Check for known artificial filesystem types
335 switch(fsStats.f_type) {
336 case PROC_SUPER_MAGIC: // /proc
337 case SYSFS_MAGIC: // /sys
338 case CGROUP_SUPER_MAGIC: // cgroups
339 case CGROUP2_SUPER_MAGIC: // cgroups v2
340 case DEBUGFS_MAGIC: // debugfs
341 case DEVPTS_SUPER_MAGIC: // /dev/pts
342 case SELINUX_MAGIC: // selinuxfs
343 case SECURITYFS_MAGIC: // securityfs
344 case TRACEFS_MAGIC: // tracefs
345 value.SetTargetArtificialFS();
346 break;
347 default:
348 break;
349 } }
350#elif defined(__APPLE__)
351 struct statfs fsStats;
352 if (statfs(symLinkDestReal, &fsStats) == 0) {
353 // Check macOS artificial filesystems
354 if (strcmp(fsStats.f_fstypename, "devfs") == 0 ||
355 strcmp(fsStats.f_fstypename, "autofs") == 0) {
356 value.SetTargetArtificialFS();
357 } }
358#else
359 // Fallback to device number check for other systems
360 if( STAT_DEV_MAJOR == 0
361 && STAT_DEV_MINOR != 35 ) // tmpfs
362 value.SetTargetArtificialFS();
363#endif
364 }
365 } // if is symlink && resolve symlinks
366
367 ABORT_SYMLINK:
368 DBG_CHECKERRNO_WITH_PATH
369
370 //============================================================================================
371 //========================================= Copy Stats =======================================
372 //============================================================================================
373 // 1. type
374 {
376 auto posixType= STATMEMBER(mode) & S_IFMT;
377 if( origFileIsSymlink ) {
378 type= posixType == S_IFDIR ? FileStatus::Types::SYMBOLIC_LINK_DIR
380 }
381 else switch(STATMEMBER(mode) & S_IFMT ) {
382 case S_IFLNK : type= FileStatus::Types::SYMBOLIC_LINK; ALIB_ERROR( "FILETREE", "Impossible")
383 break;
384 case S_IFBLK : type= FileStatus::Types::BLOCK ; break;
385 case S_IFCHR : type= FileStatus::Types::CHARACTER ; break;
386 case S_IFDIR : type= FileStatus::Types::DIRECTORY ; break;
387 case S_IFIFO : type= FileStatus::Types::FIFO ; break;
388 case S_IFREG : type= FileStatus::Types::REGULAR ; break;
389 case S_IFSOCK: type= FileStatus::Types::SOCKET ; break;
390 default: ALIB_ERROR("FILETREE",
391 "Internal error. 'unknown' file type can't happen. File: \"{}\"",
392 NString(dbgActFile) ) break;
393 }
394 value.SetType( type );
395 }
396
397 // 2. perms
398 value.SetPerms( FileStatus::Permissions(STATMEMBER(mode) & int32_t(FileStatus::Permissions::MASK)) );
399
400 // 3. timestamps
401 {
402 #if defined(__APPLE__)
403 # define st_mtime_name STATMEMBER(mtimespec)
404 # define st_ctime_name STATMEMBER(ctimespec)
405 # define st_atime_name STATMEMBER(atimespec)
406 #else
407 # if TMP_STATX_AVAILABLE
408 # define st_mtime_name STATMEMBER(mtime)
409 # define st_ctime_name STATMEMBER(ctime)
410 # define st_atime_name STATMEMBER(atime)
411 # define st_btime_name STATMEMBER(btime)
412 # else
413 # define st_mtime_name STATMEMBER(mtim)
414 # define st_ctime_name STATMEMBER(ctim)
415 # define st_atime_name STATMEMBER(atim)
416 # endif
417 #endif
418 DateTime dt;
419 dt.Import(
420 std::chrono::system_clock::time_point {
421 std::chrono::duration_cast<std::chrono::system_clock::duration>(
422 std::chrono::seconds {st_mtime_name.tv_sec }
423 + std::chrono::nanoseconds{st_mtime_name.tv_nsec} ) } );
424 value.SetMDate(dt);
425
426 dt.Import(
427 std::chrono::system_clock::time_point {
428 std::chrono::duration_cast<std::chrono::system_clock::duration>(
429 std::chrono::seconds {st_ctime_name.tv_sec }
430 + std::chrono::nanoseconds{st_ctime_name.tv_nsec} ) } );
431 value.SetCDate(dt);
432
433 dt.Import(
434 std::chrono::system_clock::time_point {
435 std::chrono::duration_cast<std::chrono::system_clock::duration>(
436 std::chrono::seconds {st_atime_name.tv_sec }
437 + std::chrono::nanoseconds{st_atime_name.tv_nsec} ) } );
438 value.SetADate(dt);
439
440 #if TMP_STATX_AVAILABLE
441 if( STATMEMBER(mask) & STATX_BTIME ) { // file systems supports "btime"?
442 dt.Import(
443 std::chrono::system_clock::time_point {
444 std::chrono::duration_cast<std::chrono::system_clock::duration>(
445 std::chrono::seconds {st_btime_name.tv_sec }
446 + std::chrono::nanoseconds{st_btime_name.tv_nsec} ) } );
447 value.SetBDate(dt);
448 } else {
449 // use smallest of other times for "btime"
450 auto btime= value.MDate();
451 if( btime > value.CDate() ) btime= value.CDate();
452 if( btime > value.ADate() ) btime= value.ADate();
453 value.SetBDate( btime );
454
455 }
456 #else
457 // use smallest of other times for "btime"
458 auto btime= value.MDate();
459 if( btime > value.CDate() ) btime= value.CDate();
460 if( btime > value.ADate() ) btime= value.ADate();
461 value.SetBDate( btime );
462 #endif
463
464
465 #undef st_mtime_name
466 #undef st_ctime_name
467 #undef st_atime_name
468 }
469
470 // 4. size
471 value.SetSize( uinteger(STATMEMBER(size) ) );
472
473 // 5. uid/gid
474 value.SetOwner( STATMEMBER(uid) );
475 value.SetGroup( STATMEMBER(gid) );
476
477 // 6. qty of hard links
478 value.SetQtyHardlinks( STATMEMBER(nlink) );
479
480 // 7. Add extended information
481 if( oldScanState < FTValue::ScanStates::STATS
482 && (value.IsDirectory() || symLinkDest.IsNotEmpty()) ) {
483 FTFile(node).GetFTree().AllocateExtendedInfo( node, symLinkDest, symLinkDestReal );
484 }
485
486 } // if scan stats (state was just path)
487
488 DBG_CHECKERRNO_WITH_PATH
489
490 // Count broken link.
492 ++parentSums.QtyErrsBrokenLink;
493 goto APPLY_POST_RECURSION_FILTER;
494 }
495
496 //================================================================================================
497 //================================== recursion with directories? =================================
498 //================================================================================================
499 if( !value.IsDirectory()
501 goto APPLY_POST_RECURSION_FILTER;
502
503 // stop recursion due to artificial fs?
504 if( value.IsArtificialFS() && !params.IncludeArtificialFS ) {
505 Log_Prune( if( verboseLogables.Size() ) verboseLogables.Add(" NO_AFS"); )
506 value.SetScanState( FTValue::ScanStates::NO_AFS );
507 goto APPLY_POST_RECURSION_FILTER;
508 }
509
510 // stop recursion due to crossing filesystem?
511 if( value.IsCrossingFS() && !params.CrossFileSystems ) {
512 Log_Prune( if( verboseLogables.Size() ) verboseLogables.Add(" NOT_CROSSING_FS"); )
513 value.SetScanState( FTValue::ScanStates::NOT_CROSSING_FS );
514 goto APPLY_POST_RECURSION_FILTER;
515 }
516
517 // stop recursion due to max depth?
518 if( depth >= params.MaxDepth ) {
519 Log_Prune( if( verboseLogables.Size() && (&params != &paramsPathOnly) ) verboseLogables.Add(" MAX_DEPTH_REACHED"); )
520 value.SetScanState( FTValue::ScanStates::MAX_DEPTH_REACHED );
521 ++parentSums.QtyStopsOnMaxDepth;
522 goto APPLY_POST_RECURSION_FILTER;
523 }
524
525 // stop recursion due to filter
526 if( depth > 0
527 && params.DirectoryFilterPreRecursion
528 && !params.DirectoryFilterPreRecursion->Includes( node, actPath ) )
529 {
530 Log_Prune( if( verboseLogables.Size() ) verboseLogables.Add(" FILTERED(Pre)"); )
531 goto APPLY_POST_RECURSION_FILTER;
532 }
533
534 // mark as recursively scanned
535 value.SetScanState( FTValue::ScanStates::RECURSIVE );
536
537 // SYMLINK RECURSION
538 if ( value.Type() == FileStatus::Types::SYMBOLIC_LINK_DIR ) {
539 // stop if not wanted, and: never recurse with symlinks RESIDING on artificial fs!
541 || value.IsArtificialFS() ) {
542 value.SetScanState( FTValue::ScanStates::NOT_FOLLOWED );
543 goto APPLY_POST_RECURSION_FILTER;
544 }
545
546 // stop if target is artificial and such are not wanted
547 if( value.TargetIsArtificialFS() && !params.IncludeArtificialFS ) {
548 value.SetScanState( FTValue::ScanStates::NO_AFS );
549 goto APPLY_POST_RECURSION_FILTER;
550 }
551
552 // recurse into the symlink target
553 Path realTargetPath; realTargetPath.DbgDisableBufferReplacementWarning();
554 realTargetPath._<NC>(actPath);
555 auto targetNode= node.Parent();
556 Path sourcePath= value.GetLinkTarget();
557 CanonicalResult targetSearchResult= MakeCanonical( sourcePath, targetNode,
558 realTargetPath, resultPaths);
559
560 // not found?
561 if(targetNode.IsInvalid()) {
562 node->SetScanState(FTValue::ScanStates::BROKEN_LINK);
563 goto APPLY_POST_RECURSION_FILTER;
564 }
565
566 // apply symbolic parent (only if it was not existing before)
567 if( targetSearchResult.ScanState != FTValue::ScanStates::DUPLICATE )
568 FTFile(targetNode).SetSymbolicParent(node.Export());
569
570 // recurse into target (if it was not existing or if it was not scanned previously)
571 if( targetSearchResult.ScanState != FTValue::ScanStates::DUPLICATE
572 || targetNode->ScanState() == FTValue::ScanStates::NONE ) {
573
574 CPathString cRealTarget= realTargetPath.Terminate();
575 FTValue::DirectorySums childSums;
576 scanFilePosix(nullptr, targetNode, cRealTarget, depth + 1, params,
577 curDev, curDevIsArtificial, parentSums,
578 realTargetPath, resultPaths );
579
580 value.SetSums(childSums);
581 parentSums+= childSums;
582 }
583 goto APPLY_POST_RECURSION_FILTER;
584 }
585
586 // DIRECTORY RECURSION
587 {ALIB_STRING_RESETTER( actPath )
588
589 // open directory
590 if( pxDir == nullptr ) {
591 ALIB_ASSERT_ERROR(actPath.Buffer() == nameOrFullPath.Buffer(),"FILETREE","Internal error")
592 actPath.SetLength(nameOrFullPath.Length());
593 } else {
594 if( actPath.Length() > 1 ) actPath << DIRECTORY_SEPARATOR;
595 actPath << nameOrFullPath;
596 }
597
598 errno= 0;
599 int fd;
600 if( pxDir)
601 fd= openat( dirfd(pxDir), nameOrFullPath, O_RDONLY | O_DIRECTORY );
602 else {
603 fd= open ( actPath , O_RDONLY | O_DIRECTORY );
604 }
605
606 if (fd != -1) { // success?
607 DBG_CHECKERRNO_WITH_PATH
609 DIR* childDir = fdopendir(fd);
610 for(;;) {
611 errno= 0;
612 dirent* pxEntry = readdir(childDir);
613 if( pxEntry == nullptr ) {
614 switch(errno) {
615 // possible errors (according to documentation):
616 // EOVERFLOW One of the values in the structure to be returned cannot be represented correctly.
617 // EBADF The dirp argument does not refer to an open directory stream.
618 // ENOENT The current position of the directory stream is invalid.
619 case 0: break;
621 break;
623 ALIB_ASSERT_ERROR(major(curDev) == 0, "FILETREE",
624 "Posix raised ({}) \"{}\" on reading a directory which is not "
625 "located on an artificial filesystem (like /proc). File:\"{}\"",
626 errno, std::errc(errno), NString(dbgActFile) )
627 break;
629 ALIB_ERROR("FILETREE",
630 "Posix raised ({}) \"{}\" on reading directory \"{}\"",
631 errno, std::errc(errno), NString(dbgActFile) )
632 break;
633 }
634 errno= 0;
635 break;
636 }
637
638 // skip "." and ".."
639 if( pxEntry->d_name[0] == '.'
640 && ( pxEntry->d_name[1] == '\0'
641 || ( pxEntry->d_name[1] == '.'
642 && pxEntry->d_name[2] == '\0' ) ) )
643 continue;
644
645 //----- recursive call -----
646 const NCString childName(const_cast<const char*>(&pxEntry->d_name[0]));
647 auto childNode= node;
648 childNode.GoToCreateChildIfNotExistent( childName );
649 auto childCopy= childNode;
650 scanFilePosix( childDir, childNode, childName,
651 depth + 1, params, curDev, curDevIsArtificial, subSums, actPath,
652 resultPaths );
653 // In case this entry was reached before through a symbolic link, it is now
654 // reached directly. Thus, we clear the symbolic link
655 FTFile(childCopy).ClearSymbolicParent();
656 } // dir entry loop
657 closedir(childDir);
658 DBG_CHECKERRNO_WITH_PATH
659
660 // previously scanned in lower state?
661 if( oldScanState != FTValue::ScanStates::NONE ) {
662 FTree::FixSums( node );
663 parentSums+= value.Sums();
664 } else {
665 value.SetSums(subSums);
666 parentSums+= subSums;
667 }
668 ALIB_DBG( errno= 0;)
669 goto APPLY_POST_RECURSION_FILTER;
670 } // success opening directory
671
672 // error with recursion
673 ALIB_ASSERT_ERROR(errno != ENOTDIR, "FILETREE",
674 "Internal error opening directory. This must never happen")
675
676
678 switch (std::errc(errno)) {
679 case std::errc::permission_denied:
680 ++parentSums.QtyErrsAccess;
682 errno= 0;
683 break;
684
685 default:
686 ALIB_ERROR("FILETREE", "Unknown error {}(\"{}\") while opening directory \"{}\"",
687 errno, std::errc(errno), actPath)
689 break;
690 } }
691
692 //================================================================================================
693 //========================= Apply Post Filter and remove empty directories =======================
694 //================================================================================================
695 APPLY_POST_RECURSION_FILTER:
696 // delete node only if this was a new scan.
697 // It must not be deleted if this node was created as a path.
698 if( oldScanState == FTValue::ScanStates::NONE ) {
699 if ( value.IsDirectory() ) {
700 if( depth > 0
701 && ( ( params.DirectoryFilterPostRecursion
702 && !params.DirectoryFilterPostRecursion->Includes(node, actPath ) )
703 || ( params.RemoveEmptyDirectories
704 && value.Sums().Count() == 0 )
705 ) )
706 {
707 Log_Prune( if( verboseLogables.Size() ) { verboseLogables.Add(" FILTERED(Post)");
708 Log_Verbose( verboseLogables )
709 verboseLogables.clear(); } )
710 parentSums-= value.Sums();
711 value.Sums()= FTValue::DirectorySums();
712
713 // Notify deletion of all children.
714 if( node.HasChildren() && node.Tree<FTree>().HasListeners() ) {
715 integer oldLen= actPath.Length();
716 auto it= node.FirstChild();
717 while ( it.IsValid() ) {
718 actPath << DIRECTORY_SEPARATOR << it.Name();
719 node.Tree<FTree>().Notify( FTreeListener::Event::DeleteNode, node, actPath );
720 it.GoToNextSibling();
721 actPath.ShortenTo(oldLen);
722 } }
723 if( params.RemoveEmptyDirectories ) {
724 ALIB_STRING_RESETTER(actPath)
725 actPath << DIRECTORY_SEPARATOR << node.Name();
726 node.Tree<FTree>().Notify( FTreeListener::Event::DeleteNode, node, actPath );
727 node.Delete();
728 return;
729 }
730
731 // do not return here. Still count the type below
732 node.DeleteChildren();
733 }
734
735 } else {
736 if ( params.FileFilter
737 && !params.FileFilter->Includes(node, actPath ) )
738 {
739 if( node.Tree<FTree>().HasListeners() ) {
740 ALIB_STRING_RESETTER(actPath)
741 actPath << DIRECTORY_SEPARATOR << node.Name();
742 node.Tree<FTree>().Notify( FTreeListener::Event::DeleteNode, node, actPath );
743 }
744 Log_Prune( if( verboseLogables.Size() ) { verboseLogables.Add(" FILTERED(Post)");
745 Log_Verbose( verboseLogables ) } )
746 node.Delete();
747 return;
748 } } }
749
750 Log_Prune( if( verboseLogables.Size() ) Log_Verbose( verboseLogables ) )
751
752 // cnt file type
753 parentSums.Add(value);
754 if(node.Tree<FTree>().HasListeners()) {
755 ALIB_STRING_RESETTER(actPath)
756 actPath << DIRECTORY_SEPARATOR << node.Name();
757 node.Tree<FTree>().Notify( FTreeListener::Event::CreateNode, node, actPath );
758 }
759
761 DBG_CHECKERRNO_WITH_PATH
762} // scanFilePosix()
763
764}} // namespace [alib::filetree::anonymous]
765#undef DBG_CHECKERRNO_WITH_PATH
766#undef TMP_STATX_AVAILABLE
767#undef STATMEMBER
768
769
770//--------------------------------------------------------------------------------------------------
771//--- UNKNOWN platform, using C++17 filesystem (not all functionality given)
772//--------------------------------------------------------------------------------------------------
773#else
774#if ALIB_SYSTEM_FORCE_STD_FILE_STATUS
775# pragma message ("ALIB_SYSTEM_FORCE_STD_FILE_STATUS given. Using std::filesystem for " \
776 "filetree::ScanFiles(). (Limited functionality) " )
777#else
778# pragma message ("Non-posix platform. Using std::filesystem for " \
779 "filetree::ScanFiles(). (Limited functionality) " )
780#endif
781
782
783// Note: MacOS is currently (as of 231210) missing C++20 library features in the area of std::clock
784#if defined(__APPLE__) || defined(_LIBCPP_VERSION) || defined(__ANDROID_NDK__)
785namespace
786{
787 template <typename TP>
788 std::time_t to_time_t(TP tp)
789 {
790 auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(tp - TP::clock::now()
791 + std::chrono::system_clock::now());
792 return std::chrono::system_clock::to_time_t(sctp);
793 }
794}
795#endif
796#if ALIB_DEBUG
797# define DBG_CHECKERRNO_WITH_PATH \
798 ALIB_ASSERT_WARNING(errno == 0, "FILETREE", "Errno set ({})\"{}\". Current path: {}", \
799 errno, std::errc(errno), path.string() ) \
800 errno= 0;
801#else
802# define DBG_CHECKERRNO_WITH_PATH
803#endif
804
805namespace alib::filetree { namespace {
806
807void scanFileStdFS( const fs::path& path,
808 FTree::Cursor& node,
809 unsigned depth,
810 ScanParameters& params,
811 FTValue::DirectorySums& parentSums,
812 CanonicalPathList* resultPaths )
813{
814 #if defined(__MINGW32__)
815 Path pathAsCString(path.c_str());
816 pathAsCString.Terminate();
817 #else
818 CPathString pathAsCString(path.c_str());
819 #endif
820 const PathSubstring parentPath= pathAsCString.Substring(0, pathAsCString.LastIndexOf(DIRECTORY_SEPARATOR));
821
822
823 Log_Verbose( "[{}] {}/{} {}", &params != &paramsPathOnly ? '>':'P', depth,
824 params.MaxDepth < (std::numeric_limits<unsigned>::max)()
825 ? String128(params.MaxDepth)
826 : String(A_CHAR("M")),
827 pathAsCString )
828
829 std::error_code errorCode;
830 auto& value = node.Value();
831 auto oldScanState= value.ScanState();
832
833 //------------------------------------------- get stats? -----------------------------------------
837
839 Path symLinkDest;
840 Path symLinkDestReal;
841
842 // read base stats (we have to use symlink_status() which does NOT follow the symlink!)
843 fs::file_status stats= fs::symlink_status(path);
845 if(errorCode)
846 {
847 ALIB_ERROR("FILETREE",
848 "Unhandled error code invoking 'fs::symlink_status()': {} (\"{}\")\n"
849 " With file: \"{}\"",
850 errorCode.value(), errorCode.message(), pathAsCString )
851 ALIB_DBG( errno= 0;)
853 goto APPLY_POST_RECURSION_FILTER;
854 }
856 ALIB_DBG(errno= 0;)
857
858 //---------------------------------------- is symlink? ---------------------------------------
859 bool origFileIsSymlink= (stats.type() == fs::file_type::symlink);
860 if( origFileIsSymlink
862 {
864
865 // 1. Read plain symlink target (only to be attached to the entry)
866 fs::path resolved= fs::read_symlink(path, errorCode);
867 if(errorCode)
868 {
870 switch( std::errc(errorCode.value()) )
871 { case std::errc::no_such_file_or_directory: // happens with /proc files
872 case std::errc::permission_denied:
874 ALIB_DBG(errno= 0;)
875 goto ABORT_SYMLINK;
876 default:
877 ALIB_ERROR("FILETREE",
878 "Unhandled error code invoking 'fs::read_symlink()': {} (\"{}\")\n"
879 " with file: ", errorCode.value(),
880 errorCode.message(), pathAsCString )
881 ALIB_DBG( errno= 0;)
883 goto APPLY_POST_RECURSION_FILTER;
884 }
886 }
887 DBG_CHECKERRNO_WITH_PATH
888 symLinkDest << resolved.c_str();
889
890 // 2. Read symlink's real target path (fully and recursively translated)
891 fs::path realPath;
892 if( resolved.is_absolute() )
893 realPath= fs::canonical(resolved, errorCode);
894 else
895 {
896 symLinkDestReal << pathAsCString;
897 symLinkDestReal.ShortenTo( symLinkDestReal.LastIndexOf(DIRECTORY_SEPARATOR) + 1);
898 symLinkDestReal << symLinkDest;
899 realPath= fs::canonical(fs::path(
900 std::basic_string_view<PathCharType>(symLinkDestReal.Buffer(),
901 size_t(symLinkDestReal.Length()))),
902 errorCode);
903 symLinkDestReal.Reset();
904 }
905 ALIB_DBG(if(errno==EINVAL && !errorCode) errno= 0;) // this happens!, we do not care, but clean up
906 ALIB_DBG(if(errno==ENOENT && !errorCode) errno= 0;)
907
909 if(errorCode) switch( std::errc(errorCode.value()) )
910 { // we ignore this: std::fs would not create the "real path" if the final directory is not accessible.
911 case std::errc::permission_denied: value.SetScanState(FTValue::ScanStates::NO_ACCESS_SL_TARGET); ALIB_DBG(errno= 0;) goto ABORT_SYMLINK;
912 case std::errc::no_such_file_or_directory: value.SetScanState(FTValue::ScanStates::BROKEN_LINK); ALIB_DBG(errno= 0;) goto ABORT_SYMLINK;
913 case std::errc::too_many_symbolic_link_levels: value.SetScanState(FTValue::ScanStates::CIRCULAR_LINK); ALIB_DBG(errno= 0;) goto ABORT_SYMLINK;
914 default: ALIB_ERROR("FILETREE", "Unhandled error code invoking 'fs::canonical()': {} (\"{}\")\n"
915 " with file: ", errorCode.value(), errorCode.message(), pathAsCString )
916 goto ABORT_SYMLINK;
917 }
919 DBG_CHECKERRNO_WITH_PATH
920 symLinkDestReal << realPath.c_str();
921
922 // 3. get resolved status
924 auto newStatus= fs::status(path, errorCode);
925 if(!errorCode)
926 {
927 // this happens with strange /proc files...
928 if(newStatus.type() != fs::file_type::unknown)
929 stats= newStatus;
930 }
931 else switch( std::errc(errorCode.value()) )
932 { case std::errc::operation_not_permitted: value.SetScanState( FTValue::ScanStates::NO_ACCESS ); ALIB_DBG(errno= 0;) goto ABORT_SYMLINK;
933 case std::errc::no_such_file_or_directory: value.SetScanState( FTValue::ScanStates::BROKEN_LINK ); ALIB_DBG(errno= 0;) goto ABORT_SYMLINK;
934 case std::errc::too_many_symbolic_link_levels: value.SetScanState( FTValue::ScanStates::CIRCULAR_LINK); ALIB_DBG(errno= 0;) goto ABORT_SYMLINK;
935 default: ALIB_WARNING("FILETREE",
936 "Unhandled error code invoking 'directory_entry::status()': {} (\"{}\")\n"
937 " With file: \"{}\"",
938 errorCode.value(), errorCode.message(), pathAsCString )
939 goto ABORT_SYMLINK;
940 }
942
943 // check for target artificial fs
944 // -/- Not available with std::filesystem version
945 }
946
947 ABORT_SYMLINK:
948 DBG_CHECKERRNO_WITH_PATH
949
950 //============================================================================================
951 //========================================= Copy Stats =======================================
952 //============================================================================================
953 // 1. type
954 {
956 if( origFileIsSymlink )
957 {
958 type= is_directory(stats) ? FileStatus::Types::SYMBOLIC_LINK_DIR
960 }
961 else switch( stats.type() )
962 {
963 case fs::file_type::directory: type= FileStatus::Types::DIRECTORY ; break;
964 case fs::file_type::regular : type= FileStatus::Types::REGULAR ; break;
965 case fs::file_type::symlink : type= FileStatus::Types::SYMBOLIC_LINK; break; // for now, this is a file.
966 case fs::file_type::block : type= FileStatus::Types::BLOCK ; break;
967 case fs::file_type::character: type= FileStatus::Types::CHARACTER ; break;
968 case fs::file_type::fifo : type= FileStatus::Types::FIFO ; break;
969 case fs::file_type::socket : type= FileStatus::Types::SOCKET ; break;
970
971 case fs::file_type::not_found:
973 ALIB_WARNING("FILETREE", "Internal error. 'not found' file type can't happen. File: ", pathAsCString )
974 ALIB_DBG( errno= 0;) goto APPLY_POST_RECURSION_FILTER;
975 case fs::file_type::none :
977 ALIB_WARNING("FILETREE", "Internal error. 'none' file type can't happen. File: ", pathAsCString)
978 ALIB_DBG( errno= 0;) goto APPLY_POST_RECURSION_FILTER;
979 case fs::file_type::unknown :
981 ALIB_WARNING("FILETREE", "Internal error. Can't happen. File: ", pathAsCString)
982 ALIB_DBG( errno= 0;) goto APPLY_POST_RECURSION_FILTER;
983 default:
985 ALIB_WARNING("FILETREE", "Unknown fs::file_status::type '{}' with file {}.", stats.type(), pathAsCString)
986 ALIB_DBG( errno= 0;) goto APPLY_POST_RECURSION_FILTER;
987 }
988 value.SetType( type );
989 }
990
991 // 2. perms
992 value.SetPerms( FileStatus::Permissions(int32_t(stats.permissions())) );
993
994 // 3. timestamps
995 // attn: This method always follows symbolic link and uses the target's time
996 // This seems to be a confirmed behavior:
997 // https://stackoverflow.com/questions/50778660/boost-filesystem-how-to-get-last-write-time-for-symlink-without-resolving
998 auto fsTime= std::filesystem::file_time_type(std::filesystem::file_time_type::clock::now());
999 if ( value.ScanState() <= FTValue::ScanStates::RESOLVED ) // no error
1000 {
1001 fsTime= fs::last_write_time( path, errorCode );
1003 if(errorCode) switch( std::errc(errorCode.value()) )
1004 { // This happens if with symbolic links that point to nowhere.
1005 case std::errc::no_such_file_or_directory:
1006 ALIB_ERROR( "FILETREE", "Internal error. This should never happen, checked above. "
1007 "Undefined system error handling" ) ALIB_DBG( errno= 0;)
1008 value.SetScanState(FTValue::ScanStates::UNKNOWN_ERROR);
1009 break;
1010
1011 default:
1012 ALIB_ERROR( "FILETREE",
1013 "Unhandled error code invoking 'fs::last_write_time()': {} (\"{}\")\n"
1014 " With file \"{}\".", errorCode.value(), errorCode.message(), pathAsCString )
1015 fsTime= (decltype(fsTime)::min)(); ALIB_DBG( errno= 0;)
1016 break;
1017 }
1019 }
1020
1021
1022 #if defined(__APPLE__) || defined(_LIBCPP_VERSION) || defined(__ANDROID_NDK__)
1023 value.SetMDate( DateTime::FromEpochSeconds( to_time_t( fsTime ) ) );
1024 #else
1025 value.SetMDate( DateTime::FromEpochSeconds( std::chrono::system_clock::to_time_t(
1026 std::chrono::clock_cast<std::chrono::system_clock>(fsTime) ) ) );
1027 #endif
1028 value.SetBDate( value.MDate() );
1029 value.SetCDate( value.MDate() );
1030 value.SetADate( value.MDate() );
1031
1032 // 4. size
1033 errorCode.clear();
1034 value.SetSize( symLinkDest.Length() > 0 ? uinteger(symLinkDest.Length())
1035 : value.ScanState() <= FTValue::ScanStates::RESOLVED ? uinteger(fs::file_size(path, errorCode))
1036 : 0 );
1037 if( value.Size() == uinteger(-1))
1038 {
1039 value.SetSize(0);
1041 switch( std::errc(errorCode.value()) )
1042 {
1043 // target is a directory (no error)
1044 case std::errc::is_a_directory:
1045 break;
1046
1047 case std::errc::no_such_file_or_directory: // this happens if we have a broken symbolic link
1049 || value.Type() == FileStatus::Types::SYMBOLIC_LINK_DIR , "FILETREE",
1050 "Internal error. This should never happen. Undefined system error handling" )
1051 break;
1052
1053 // size not supported. Happens with sockets, files in /proc, etc
1054 case std::errc::operation_not_supported: break;
1055 default: ALIB_ERROR("FILETREE",
1056 "Unhandled error code invoking 'directory_entry::file_size()':{} (\"{}\")\n"
1057 " With file \"{}\".",
1058 errorCode.value(), errorCode.message(), pathAsCString ) ALIB_DBG( errno= 0;)
1059 break;
1060 }
1062 }
1063
1064 // 5. uid/gid
1065 value.SetOwner( FTValue::UnknownID );
1066 value.SetGroup( FTValue::UnknownID );
1067
1068 // 6. qty of hard links
1069 uint32_t qtyHardLinks= uint32_t( fs::hard_link_count(path, errorCode ) );
1071 if(errorCode)
1072 {
1073 // fs::hard_link_coun always returns the hardlink-count of the symlink targets.
1074 // This fails on broken links. Therefore, this is not considered an error
1075 if( !( std::errc(errorCode.value()) == std::errc::no_such_file_or_directory
1076 && value.Type() == FileStatus::Types::SYMBOLIC_LINK ) ) {
1077 ALIB_MESSAGE("FILETREE",
1078 "Unhandled error code invoking 'fs::hard_link_count()': {} (\"{}\")\n"
1079 " With file: \"{}\"",
1080 errorCode.value(), errorCode.message(), pathAsCString )
1081 ALIB_DBG( errno= 0;)
1082 }
1083 }
1085 value.SetQtyHardlinks( qtyHardLinks );
1086
1087 // 7. Add extended information
1088 if( oldScanState < FTValue::ScanStates::STATS
1089 && (value.IsDirectory() || symLinkDest.IsNotEmpty()) )
1090 FTFile(node).GetFTree().AllocateExtendedInfo( node, symLinkDest, symLinkDestReal );
1091 } // if scan stats (state was just path)
1092
1093 DBG_CHECKERRNO_WITH_PATH
1094
1095 // Count broken link.
1096 if(value.ScanState() == FTValue::ScanStates::BROKEN_LINK)
1097 {
1098 ++parentSums.QtyErrsBrokenLink;
1099 goto APPLY_POST_RECURSION_FILTER;
1100 }
1101
1102 //---------------------------------- recursion with directories? ---------------------------------
1103 if( !value.IsDirectory()
1104 || value.ScanState() >= FTValue::ScanStates::RECURSIVE )
1105 goto APPLY_POST_RECURSION_FILTER;
1106
1107
1108 // stop recursion due to artificial fs?
1109 // Not supported with std::filesystem!
1110
1111 // stop recursion due to crossing filesystem?
1112 if( value.IsCrossingFS() && !params.CrossFileSystems )
1113 {
1114 value.SetScanState( FTValue::ScanStates::NOT_CROSSING_FS );
1115 goto APPLY_POST_RECURSION_FILTER;
1116 }
1117
1118 // stop recursion due to max depth?
1119 if( depth >= params.MaxDepth )
1120 {
1121 value.SetScanState( FTValue::ScanStates::MAX_DEPTH_REACHED );
1122 ++parentSums.QtyStopsOnMaxDepth;
1123 goto APPLY_POST_RECURSION_FILTER;
1124 }
1125
1126 // stop recursion due to filter
1127 if( depth > 0
1129 && !params.DirectoryFilterPreRecursion->Includes( node, parentPath ) )
1130 goto APPLY_POST_RECURSION_FILTER;
1131
1132 // mark as recursively scanned
1133 value.SetScanState( FTValue::ScanStates::RECURSIVE );
1134
1135 // SYMLINK RECURSION
1136 if ( value.Type() == FileStatus::Types::SYMBOLIC_LINK_DIR )
1137 {
1138 // stop if not wanted, and: never recurse with symlinks RESIDING on artificial fs!
1139 if( params.LinkTreatment != ScanParameters::SymbolicLinks::RECURSIVE
1140 || value.IsArtificialFS() ) {
1141 value.SetScanState( FTValue::ScanStates::NOT_FOLLOWED );
1142 goto APPLY_POST_RECURSION_FILTER;
1143 }
1144
1145 // recurse into the symlink target
1146 Path realTargetPath= parentPath;
1147 auto targetNode= node.Parent();
1148 Path sourcePath= value.GetLinkTarget();
1149 CanonicalResult targetSearchResult= MakeCanonical(sourcePath, targetNode,
1150 realTargetPath, resultPaths);
1151
1152 // not found?
1153 if(targetNode.IsInvalid()) {
1154 node->SetScanState(FTValue::ScanStates::BROKEN_LINK);
1155 goto APPLY_POST_RECURSION_FILTER;
1156 }
1157
1158 // apply symbolic parent (only if it was not existing before)
1159 if( targetSearchResult.ScanState != FTValue::ScanStates::DUPLICATE )
1160 FTFile(targetNode).SetSymbolicParent(node.Export());
1161
1162 // recurse into target (if it was not existing or if it was not scanned previously)
1163 if( targetSearchResult.ScanState != FTValue::ScanStates::DUPLICATE
1164 || targetNode->ScanState() == FTValue::ScanStates::NONE ) {
1165
1166 FTValue::DirectorySums childSums;
1167 scanFileStdFS( fs::path(std::basic_string_view<PathCharType>(realTargetPath.Buffer(),
1168 size_t(realTargetPath.Length()))),
1169 targetNode, depth + 1, params, childSums, resultPaths );
1170
1171 value.SetSums(childSums);
1172 parentSums+= childSums;
1173 }
1174 goto APPLY_POST_RECURSION_FILTER;
1175 }
1176
1177 // DIRECTORY RECURSION
1178 {
1179 fs::directory_iterator dit= fs::directory_iterator(path, errorCode);
1180 if(!errorCode) // success?
1181 {
1182 FTValue::DirectorySums subSums;
1183 for( const fs::directory_entry& childDir : dit )
1184 {
1185 // recursive call
1186 #if defined(_WIN32)
1187 Path mingwBuf( childDir.path().c_str());
1188 PathSubstring childName(mingwBuf);
1189 #else
1190 NSubstring childName(NCString(childDir.path().c_str()));
1191 #endif
1192 childName.ConsumeChars(childName.LastIndexOf(DIRECTORY_SEPARATOR) + 1);
1193 auto childNode= node;
1194 childNode.GoToCreateChildIfNotExistent( childName );
1195 auto childCopy= childNode;
1196 scanFileStdFS( childDir.path(), childNode, depth + 1, params, subSums,
1197 resultPaths );
1198
1199 // In case this entry was reached before through a symbolic link, it is now
1200 // reached directly. Thus, we clear the symbolic link
1201 FTFile(childCopy).ClearSymbolicParent();
1202
1203 }
1204
1205 // previously scanned in lower quality?
1206 if( oldScanState != FTValue::ScanStates::NONE )
1207 {
1208 FTree::FixSums( node );
1209 parentSums+= value.Sums();
1210 }
1211 else
1212 {
1213 value.SetSums(subSums);
1214 parentSums+= subSums;
1215 }
1216 ALIB_DBG( errno= 0;)
1217 goto APPLY_POST_RECURSION_FILTER;
1218 }
1219 }
1220
1221 // error with recursion
1222 ALIB_ASSERT_ERROR( errorCode.value() != ENOTDIR, "FILETREE",
1223 "Internal error opening directory. This must never happen" )
1224
1226 if(errorCode) switch (std::errc(errorCode.value()))
1227 {
1228 case std::errc::invalid_argument: // happens with /proc
1229 case std::errc::permission_denied:
1230 ++parentSums.QtyErrsAccess;
1231 value.SetScanState( FTValue::ScanStates::NO_ACCESS_DIR );
1232 ALIB_DBG( errno= 0;)
1233 goto APPLY_POST_RECURSION_FILTER;
1234
1235 default: value.SetScanState(FTValue::ScanStates::UNKNOWN_ERROR);
1236 ALIB_ERROR("FILETREE", "Unknown error {}(\"{}\") while opening directory \"{}\"",
1237 errorCode.value(), errorCode.message(), pathAsCString)
1238 ALIB_DBG( errno= 0;)
1239 goto APPLY_POST_RECURSION_FILTER;
1240 }
1242 ALIB_DBG( errno= 0;)
1243
1244 //------------------------------------------ Apply Filter ----------------------------------------
1245 APPLY_POST_RECURSION_FILTER:
1246 // delete node only if this was a new scan. It must not be deleted if this node was
1247 // created as a path.
1248 if( oldScanState == FTValue::ScanStates::NONE ) {
1249 if ( value.IsDirectory() ) {
1250 if( depth > 0
1251 && ( ( params.DirectoryFilterPostRecursion
1252 && !params.DirectoryFilterPostRecursion->Includes(node, parentPath ) )
1253 || ( params.RemoveEmptyDirectories
1254 && value.Sums().Count() == 0 )
1255 ) )
1256 {
1257 parentSums-= value.Sums();
1258 value.Sums()= FTValue::DirectorySums();
1259
1260 // Notify deletion of all children.
1261 if( node.HasChildren() && node.Tree<FTree>().HasListeners() ) {
1262 Path p= pathAsCString;
1263 auto it= node.FirstChild();
1264 while ( it.IsValid() ) {
1265 p.ShortenTo(pathAsCString.Length());
1266 p << DIRECTORY_SEPARATOR << it.Name();
1267 node.Tree<FTree>().Notify( FTreeListener::Event::DeleteNode, node, p );
1268 it.GoToNextSibling();
1269 }
1270 }
1271
1272 if( params.RemoveEmptyDirectories )
1273 {
1274 node.Tree<FTree>().Notify(FTreeListener::Event::DeleteNode, node, pathAsCString);
1275 node.Delete();
1276 return;
1277 }
1278
1279
1280 // do not return here. Still count the type below
1281 node.DeleteChildren();
1282 }
1283
1284 } else { // not directory
1285 if ( params.FileFilter && !params.FileFilter->Includes(node, parentPath ) ) {
1286 node.Tree<FTree>().Notify(FTreeListener::Event::DeleteNode, node, pathAsCString) ;
1287 node.Delete();
1288 return;
1289 }
1290 }
1291 }
1292
1293 // cnt file type and notify listeners
1294 parentSums.Add(value);
1295 node.Tree<FTree>().Notify( FTreeListener::Event::CreateNode, node, pathAsCString );
1296
1297} // scanFileStdFS
1298
1299
1300}} // namespace [alib::filetree::anonymous]
1301
1302#undef DBG_CHECKERRNO_WITH_PATH
1303#endif // std::fs version
1304
1305//--------------------------------------------------------------------------------------------------
1306//--- ALL Platforms
1307//--------------------------------------------------------------------------------------------------
1308namespace alib::filetree {
1309
1310namespace {
1311
1312
1313struct UnrealPathRootInfo {
1314 PathRootKind kind;
1315 integer prefixLength;
1316 PathSubstring scheme;
1317};
1318
1319inline bool isAsciiAlpha(PathCharType c) {
1320 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
1321}
1322
1323inline bool isSchemeChar(PathCharType c) {
1324 return isAsciiAlpha(c) || (c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.';
1325}
1326
1327inline bool IsAsciiNoCaseEqual(PathCharType c, PathCharType cmp) {
1328 return c == cmp || c == (cmp ^ 0x20);
1329}
1330
1331UnrealPathRootInfo detectAndNormalizeUnrealRoot( Path& sourcePath,
1332 FTree::Cursor& node,
1333 Path& pathToNode ) {
1334
1335 UnrealPathRootInfo info{ PathRootKind::Relative, 0, PathSubstring() };
1336
1337 if( sourcePath.IsEmpty() )
1338 return info;
1339
1340 #if defined(_WIN32)
1341 // Windows device paths: "\\.\"
1342 if( sourcePath.Length() >= 4
1343 && ( (sourcePath.CharAt(0) == '\\' && sourcePath.CharAt(1) == '\\')
1344 || (sourcePath.CharAt(0) == '/' && sourcePath.CharAt(1) == '/') )
1345 && sourcePath.CharAt(2) == '.'
1346 && (sourcePath.CharAt(3) == '\\' || sourcePath.CharAt(3) == '/') ) {
1347 node.GoToRoot();
1348 info.kind= PathRootKind::Device;
1349 sourcePath.DeleteStart(4);
1350 pathToNode.Reset(DIRECTORY_SEPARATOR);
1351
1352 if( sourcePath.Length() >= 4
1353 && IsAsciiNoCaseEqual(sourcePath.CharAt(0), 'U')
1354 && IsAsciiNoCaseEqual(sourcePath.CharAt(1), 'N')
1355 && IsAsciiNoCaseEqual(sourcePath.CharAt(2), 'C')
1356 && (sourcePath.CharAt(3) == '\\' || sourcePath.CharAt(3) == '/') ) {
1357 sourcePath.DeleteStart(4);
1358 Path prefix;
1360 sourcePath.InsertAt(prefix, 0);
1361 } else if( sourcePath.IsEmpty() ) {
1362 return info;
1363 } else {
1364 info.kind= PathRootKind::Device;
1365 info.prefixLength= 4;
1366 return info;
1367 } }
1368 #endif
1369
1370 // URL scheme detection: "<scheme>://"
1371 integer schemeEnd= sourcePath.IndexOf(':');
1372 if( schemeEnd > 0
1373 && schemeEnd + 2 < sourcePath.Length()
1374 && sourcePath.CharAt(schemeEnd + 1) == '/'
1375 && sourcePath.CharAt(schemeEnd + 2) == '/' ) {
1376 bool schemeValid= isAsciiAlpha(sourcePath.CharAt(0));
1377 for( integer i= 1; schemeValid && i < schemeEnd; ++i )
1378 schemeValid= isSchemeChar(sourcePath.CharAt(i));
1379 if( schemeValid ) {
1380 info.kind= PathRootKind::URL;
1381 info.prefixLength= schemeEnd + 3;
1382 info.scheme= sourcePath.Substring(0, schemeEnd);
1383 return info;
1384 }
1385 info.kind= PathRootKind::Errorneous;
1386 return info;
1387 }
1388
1389 // Windows drive letter: "C:\" or "C:/"
1390 #if defined(_WIN32)
1391 if( sourcePath.Length() >= 2
1392 && isAsciiAlpha(sourcePath.CharAt(0))
1393 && sourcePath.CharAt(1) == ':' ) {
1394
1395 // if no backslash is given, this a relative path
1396 if( sourcePath.Length() < 3
1397 || (sourcePath.CharAt(2) != '\\' && sourcePath.CharAt(2) != '/' ) )
1398 return info;
1399
1400 info.kind= PathRootKind::DriveLetter;
1401 info.prefixLength= 2;
1402 node.GoToRoot();
1403 PathSubstring drive= sourcePath.Substring(0, 2);
1404 node.GoToCreateChildIfNotExistent(drive);
1405 pathToNode.Reset();
1406 pathToNode << drive;
1407
1408 if( sourcePath.Length() > 2
1409 && (sourcePath.CharAt(2) == '/' || sourcePath.CharAt(2) == '\\') ) {
1410 ++info.prefixLength;
1411 sourcePath.DeleteStart(3);
1412 pathToNode << DIRECTORY_SEPARATOR;
1413 } else {
1414 sourcePath.DeleteStart(2);
1415 }
1416 return info;
1417 }
1418 #endif
1419
1420 // UNC path (leading double separators).
1421 if( sourcePath.Length() >= 2
1422 && ( (sourcePath.CharAt(0) == '/' && sourcePath.CharAt(1) == '/')
1423 || (sourcePath.CharAt(0) == '\\' && sourcePath.CharAt(1) == '\\') ) ) {
1424 info.kind= PathRootKind::UNC;
1425 info.prefixLength= 2;
1426 node.GoToRoot();
1427 sourcePath.DeleteStart(2);
1428 pathToNode.Reset();
1430 return info;
1431 }
1432
1433 // if non-windows OS, detect common absolute path starting with /.
1434 #if !defined(_WIN32)
1435 int absoluteEnd= Path::IsAbsolute(sourcePath);
1436 if(absoluteEnd==1) {
1437 info.kind= PathRootKind::AbsoluteRoot;
1438 info.prefixLength= 1;
1439 node.GoToRoot();
1440 sourcePath.DeleteStart(1);
1441 pathToNode.Reset(DIRECTORY_SEPARATOR);
1442 }
1443 #endif
1444
1445
1446 return info;
1447}
1448
1449CanonicalResult makeCanonicalRecursion( Path& sourcePath,
1450 FTree::Cursor& node,
1451 Path& pathToNode,
1452 CanonicalPathList* resultPaths,
1453 FTree::Cursor callingNode ) {
1454
1455 auto rootInfo= detectAndNormalizeUnrealRoot(sourcePath, node, pathToNode);
1456 if( rootInfo.kind == PathRootKind::URL ) {
1457 node.GoToRoot();
1458 node.GoToCreateChildIfNotExistent(rootInfo.scheme);
1459 node->SetType(FileStatus::Types::SOCKET);
1460 node->SetScanState(FTValue::ScanStates::NOT_EXISTENT);
1461 pathToNode.Reset();
1462 pathToNode << rootInfo.scheme << A_CHAR("://");
1463 sourcePath.Reset();
1465 }
1466
1467 if( rootInfo.kind == PathRootKind::Device ) {
1468 node.GoToRoot();
1469 Path devName;
1470 devName << A_CHAR("DEV");
1471 if( sourcePath.IsNotEmpty() ) {
1472 devName << A_CHAR(":");
1473 for( integer i= 0; i < sourcePath.Length(); ++i ) {
1474 PathCharType c= sourcePath.CharAt(i);
1475 devName << ( (c == '/' || c == '\\') ? PathCharType(':') : c );
1476 } }
1477 node.GoToCreateChildIfNotExistent(devName);
1478 node->SetType(FileStatus::Types::SOCKET);
1479 node->SetScanState(FTValue::ScanStates::NOT_EXISTENT);
1480 pathToNode.Reset();
1481 pathToNode << devName;
1482 sourcePath.Reset();
1484 }
1485
1486 // existed already?
1487 if( sourcePath.IsEmpty() )
1488 return CanonicalResult{rootInfo.kind, FTValue::ScanStates::DUPLICATE};
1489
1490 // this is our result value
1491 bool isNew= false;
1492
1493 // create folders until we hit a symbolic link
1494 while(sourcePath.IsNotEmpty()) {
1495 do {
1496 PathSubstring name(sourcePath.Substring(0, (std::min)( sourcePath.IndexOfOrLength('/'),
1497 sourcePath.IndexOfOrLength('\\')) ));
1498 if(name.Equals(A_PATH(".")) || name.Equals(A_PATH("/"))) {
1499 sourcePath.DeleteStart(2);
1500 continue;
1501 }
1502 if(name.Equals(A_PATH(".."))) {
1503 sourcePath.DeleteStart(3);
1504 pathToNode.ChangeToParent();
1505 if(!node.IsRoot())
1506 node= node.Parent();
1507 continue;
1508 }
1509 if(name.IsEmpty())
1510 return CanonicalResult{ rootInfo.kind, isNew ? FTValue::ScanStates::NONE
1512
1513 // create or goto existing
1514 isNew= node.GoToCreateChildIfNotExistent(name);
1515 if(pathToNode.Length() > 1)
1516 pathToNode << DIRECTORY_SEPARATOR;
1517 pathToNode << name;
1518 sourcePath.DeleteStart(name.Length() + 1);
1519 } while(!isNew && !node->IsSymbolicLink());
1520
1521 if( isNew ) {
1522 // scan the node non-recursively, with link-resolving
1523 FTValue::DirectorySums dummySums;
1524 #if ALIB_SYSTEM_FILE_STATUS_IMPL == ALIB_SYSTEM_FILE_POSIX_STATUS
1525 pathToNode.Terminate();
1526 CPathString fullPathChildName= pathToNode;
1527 {
1528 // add node name to existing path and use same buffer for fullPathChildName!
1529 fullPathChildName= pathToNode;
1530 }
1531 scanFilePosix( nullptr, node, fullPathChildName,
1532 0, paramsPathOnly,
1533 0, false, dummySums, pathToNode, resultPaths );
1534 #else
1535 scanFileStdFS( fs::path(std::basic_string_view<PathCharType>(pathToNode.Buffer(),
1536 size_t(pathToNode.Length()))),
1537 node, 0,
1538 paramsPathOnly,
1539 dummySums, resultPaths );
1540 #endif
1541
1542 auto quality= node->ScanState();
1543 // if not found, put node name back to sourcePath, set node to invalid and return
1544 // an error.
1545 if( quality == FTValue::ScanStates::NOT_EXISTENT ) {
1546 sourcePath.InsertAt(strings::TLocalString<PathCharType, 8>(DIRECTORY_SEPARATOR), 0);
1547 sourcePath.InsertAt(node.Name(), 0);
1548 node.Delete();
1549 node= FTree::Cursor();
1551 }
1552
1553 // Correct quality from MAX_DEPTH_REACHED to STATS
1554 if(isNew) {
1556 node->SetScanState(FTValue::ScanStates::NONE);
1557 else if( quality != FTValue::ScanStates::RESOLVED )
1558 return CanonicalResult{rootInfo.kind, quality};
1559 } else {
1562 return CanonicalResult{rootInfo.kind, quality};
1563 } }
1564
1565 // Symbolic link found?
1566 if(node->IsSymbolicLink()) {
1567 node->SetScanState(FTValue::ScanStates::RESOLVED);
1568 // recursive call
1569 Path unrealPathToSymlink= node->GetLinkTarget();
1570 auto targetNode= node.Parent();
1571 pathToNode.ChangeToParent();
1572 CanonicalResult result=
1573 makeCanonicalRecursion(unrealPathToSymlink, targetNode, pathToNode, resultPaths, node );
1574 if( targetNode.IsInvalid() || result.ScanState == FTValue::ScanStates::NOT_EXISTENT )
1575 return CanonicalResult{rootInfo.kind, result.ScanState}; // <-- Use THIS level's rootInfo.kind!
1576
1577 if( result.ScanState != FTValue::ScanStates::DUPLICATE
1578 || targetNode->ScanState() == FTValue::ScanStates::NONE ) {
1579 FTFile(targetNode).SetSymbolicParent(node.Export());
1580 if(callingNode.IsValid())
1581 FTFile(node).SetSymbolicParent(callingNode.Export());
1582 }
1583
1584 node= targetNode;
1585 } }
1586 return CanonicalResult{ rootInfo.kind, isNew ? FTValue::ScanStates::NONE
1588
1589} // makeCanonicalRecursion
1590
1591} // namespace alib::filetree[::anonymous]
1592
1593#endif // !DOXYGEN
1594
1596 FTree::Cursor& node,
1597 Path& pathToNode,
1598 CanonicalPathList* resultPaths ) {
1599
1600 FTree::Cursor recursionNode;
1601 CanonicalResult result= makeCanonicalRecursion( sourcePath, node, pathToNode, resultPaths,
1602 recursionNode );
1603
1604 if(node.IsValid() && result.ScanState==FTValue::ScanStates::NONE && resultPaths)
1605 resultPaths->Add(node);
1606 return result;
1607}
1608
1609//--------------------------------------------------------------------------------------------------
1610//--- ScanFiles()
1611//--------------------------------------------------------------------------------------------------
1613 ScanParameters& parameters,
1614 CanonicalPathList* resultPaths,
1615 Path* remainingStart) {
1616
1617 Log_SetDomain( "ALIB/FILETREE", Scope::Path)
1618 Log_SetDomain( "SCAN" , Scope::Filename)
1619
1620
1621 Log_Info( "Scanning: P= {}\n"
1622 " F={} DPre={} DPost={} XFS={} AFS={} Depth={}",
1623 parameters.StartPath,
1624 parameters.FileFilter .get() ? 'Y':'N',
1625 parameters.DirectoryFilterPreRecursion .get() ? 'Y':'N',
1626 parameters.DirectoryFilterPostRecursion.get() ? 'Y':'N',
1627 parameters.CrossFileSystems ? 'Y':'N', parameters.IncludeArtificialFS ? 'Y':'N',
1629 : String128(parameters.MaxDepth)
1630 )
1631
1632 // a relative path was given? Interpret this as relative to the current directory
1633 Path sourcePath(parameters.StartPath);
1634 Log_Prune( bool isAbsolutePath= sourcePath.IsAbsolute(); )
1635 sourcePath.MakeAbsolute();
1636 if( !sourcePath.Exists() ) {
1637 Log_Info( "Scan path (given relative!) not existent. Current folder {!Q} "
1638 "has no child-path {!Q}.", sourcePath, parameters.StartPath )
1639 return FileStatus::ScanStates::NOT_EXISTENT;
1640 }
1641 Log_If( !isAbsolutePath, Verbosity::Info, "Changed relative start path to {}", sourcePath)
1642
1643 //----------------------------------------- start scanning ---------------------------------------
1644 ALIB_DBG( errno=0;
1645 Log_Prune(auto firstResultPos= resultPaths->size(); ))
1646 FTValue::DirectorySums dummySums;
1647
1648 // make path canonical
1649 FTree::Cursor targetNode= tree.Root().AsCursor();
1650 Path realPath; realPath.DbgDisableBufferReplacementWarning();
1651 CanonicalResult result= MakeCanonical( sourcePath, targetNode, realPath, resultPaths );
1652 if(remainingStart) remainingStart->Reset(sourcePath);
1653 if( targetNode.IsInvalid() ) {
1654 Log_Info( "Scan path not resolved. Unresolved portion {!Q}", realPath )
1655 return result.ScanState;
1656 }
1657 auto originalTargetNode= targetNode;
1658
1659 // scan
1660 #if ALIB_SYSTEM_FILE_STATUS_IMPL == ALIB_SYSTEM_FILE_POSIX_STATUS
1661 CPathString fullPathChildName= realPath;
1662 scanFilePosix(nullptr, targetNode, fullPathChildName, 0 ,parameters, 0, false, dummySums,
1663 realPath, resultPaths );
1664 #else
1665 scanFileStdFS( fs::path(std::basic_string_view<PathCharType>(realPath.Buffer(),
1666 size_t(realPath.Length()))),
1667 targetNode, 0, parameters, dummySums, resultPaths );
1668 #endif
1669 if(resultPaths) {
1670 Log_Info( "Scan Results: ", resultPaths->size() - firstResultPos )
1671 Log_Prune( int cntPaths= 0;
1672 for( auto& it : *resultPaths ) {
1673 Path path;
1674
1675 Log_Info( " Path {}: {} (Q={} D={}/F={}}",
1676 cntPaths++, path,
1677 it->ScanState(),
1678 it->ScanState() > FTValue::ScanStates::STATS && it->IsDirectory() ? it->Sums().CountDirectories() : 0,
1679 it->ScanState() > FTValue::ScanStates::STATS && it->IsDirectory() ? it->Sums().CountNonDirectories(): 0 )
1680 } )
1681 }
1682
1683 return resultPaths && resultPaths->size() > 0 ? resultPaths->back()->ScanState()
1684 : targetNode->ScanState();
1685}
1686
1687
1689 for(auto existingIt= begin(); existingIt != end(); ++existingIt) {
1690
1691 // check if the new node supersedes this node
1692 FTFile nodeParents= *existingIt;
1693 while( nodeParents.IsValid() ) {
1694 if( nodeParents == node ){
1695 *existingIt= node;
1696 return;
1697 }
1698 nodeParents= nodeParents.Parent();
1699 }
1700
1701 // check if the new node is superseded by this existing
1702 nodeParents= node.Parent();
1703 while( nodeParents.IsValid() ) {
1704 if( nodeParents == *existingIt )
1705 return;
1706 nodeParents= nodeParents.Parent();
1707 } }
1708 insert(end(), node);
1709}
1710} // namespace [alib::filetree]
1711
1712# include "ALib.Lang.CIMethods.H"
#define ALIB_ALLOW_SPARSE_ENUM_SWITCH
#define A_CHAR(STR)
#define ALIB_WARNING(domain,...)
#define ALIB_ASSERT_WARNING(cond, domain,...)
#define ALIB_ERROR(domain,...)
#define ALIB_POP_ALLOWANCE
#define ALIB_DBG(...)
#define ALIB_ASSERT_ERROR(cond, domain,...)
#define Log_IsActive(result,...)
#define Log_If(...)
#define Log_Prune(...)
#define Log_Verbose(...)
#define Log_SetDomain(...)
#define Log_Info(...)
void SetSymbolicParent(FTree::CursorHandle handle, bool overwrite=false)
Definition ftree.hpp:691
FTFile Parent() const
Definition ftree.hpp:749
FTree & GetFTree() const
Definition ftree.hpp:626
CPathString & GetLinkTarget() const noexcept
Definition ftvalue.hpp:214
constexpr void SetSums(const DirectorySums &sums) const
Definition ftvalue.hpp:191
constexpr DirectorySums & Sums() const
Definition ftvalue.hpp:177
void AllocateExtendedInfo(Cursor &node, const PathString &symLinkDest, const PathString &symLinkRealPath)
Definition ftree.hpp:250
static void FixSums(Cursor directory)
Definition ftree.cpp:214
TAString & InsertAt(const TString< TChar > &src, integer pos)
constexpr const TChar * Terminate() const
Definition tastring.hpp:614
TAString & DeleteStart(integer regionLength)
TAString & ShortenTo(integer newLength)
Definition tastring.hpp:741
integer DetectLength(integer offset=0)
Definition tastring.hpp:714
TChar * VBuffer() const
Definition tastring.hpp:631
void EnsureRemainingCapacity(integer spaceNeeded)
Definition tastring.hpp:555
void DbgDisableBufferReplacementWarning()
Definition tastring.hpp:236
void SetLength(integer newLength)
Definition tastring.hpp:693
constexpr integer Length() const
Definition string.hpp:300
constexpr bool IsEmpty() const
Definition string.hpp:349
TChar CharAtStart() const
Definition string.hpp:417
integer IndexOf(TChar needle, integer startIdx=0) const
Definition string.hpp:799
TChar CharAt(integer idx) const
Definition string.hpp:399
constexpr bool IsNotEmpty() const
Definition string.hpp:353
constexpr const TChar * Buffer() const
Definition string.hpp:295
integer LastIndexOf(TChar needle, integer startIndex=MAX_LEN) const
Definition string.hpp:909
TChar CharAtEnd() const
Definition string.hpp:436
TString< TChar > Substring(integer regionStart, integer regionLength=MAX_LEN) const
Definition string.hpp:368
integer IndexOfOrLength(TChar needle) const
Definition string.hpp:859
@ CHARACTER
A character special file.
@ BLOCK
A block special file.
@ FIFO
A FIFO (also known as pipe) file.
constexpr DateTime MDate() const noexcept
void SetScanState(ScanStates v) noexcept
void SetType(Types v) noexcept
void SetCrossingFS() noexcept
Mark the entry as residing on a different filesystem than its parent.
void SetArtificialFS() noexcept
Mark the entry as residing on an artificial filesystem.
void SetADate(DateTime v) noexcept
void SetMDate(DateTime v) noexcept
constexpr DateTime ADate() const noexcept
void SetSize(uinteger v) noexcept
Permissions
Permission flags. Compatible with POSIX definition.
@ NONE
no permission bits are set
void SetOwner(uint32_t v) noexcept
void SetPerms(Permissions v) noexcept
constexpr bool TargetIsArtificialFS() const noexcept
void SetBDate(DateTime v) noexcept
constexpr DateTime CDate() const noexcept
constexpr bool IsDirectory() const noexcept
constexpr ScanStates ScanState() const noexcept
void SetCDate(DateTime v) noexcept
constexpr bool IsArtificialFS() const noexcept
ScanStates
Per-entry information about how a node was scanned.
@ RECURSIVE
Follow symlink target strings.
@ STATS
Only stats (size, date, owner, etc.) read.
@ NO_ACCESS_DIR
Scanner failure due to limited access rights on a directory.
@ MAX_DEPTH_REACHED
Scanning stopped because maximum depth was reached.
@ RESOLVED
Read symlink target strings.
@ NO_ACCESS_SL
Scanner failure due to limited access rights on a symbolic link.
@ UNKNOWN_ERROR
Unknown scanner failure.
@ BROKEN_LINK
A symbolic link targets a non-existent file or directory.
@ NO_ACCESS_SL_TARGET
Scanner failure due to limited access rights on a symbolic link's target.
@ NO_ACCESS
Scanner failure due to limited access rights.
@ NOT_EXISTENT
Set if a given start path does not exist.
void SetQtyHardlinks(uint32_t v) noexcept
void SetTargetArtificialFS() noexcept
Mark the entry as a symlink who's target is residing on an artificial filesystem.
void SetGroup(uint32_t v) noexcept
bool IsAbsolute() const
Definition path.hpp:360
bool ChangeToParent()
Definition path.cpp:392
static DateTime FromEpochSeconds(time_t epochSeconds)
Definition datetime.hpp:62
void Import(TTimePoint timePoint)
FTValue::ScanStates ScanFiles(FTree &tree, ScanParameters &parameters, CanonicalPathList *resultPaths=nullptr, Path *remainingStart=nullptr)
PathRootKind
Classification of path root formats that cannot be directly scanned.
Definition fscanner.hpp:241
@ DriveLetter
Windows drive letter (C:, D:, etc).
Definition fscanner.hpp:245
@ Errorneous
Errorneous path format.
Definition fscanner.hpp:242
@ AbsoluteRoot
Unix-style absolute path starting with /.
Definition fscanner.hpp:244
@ Relative
Relative path (no special root).
Definition fscanner.hpp:243
@ Device
Windows device path (\\.\...).
Definition fscanner.hpp:248
@ URL
URL scheme (http://, ftp://, file://, etc).
Definition fscanner.hpp:247
@ UNC
Universal Naming Convention (\\server\share).
Definition fscanner.hpp:246
String DBG_FILETREE_SCAN_VERBOSE_LOG_FORMAT
CanonicalResult MakeCanonical(Path &sourcePath, FTree::Cursor &node, Path &pathToNode, CanonicalPathList *resultPaths=nullptr)
platform_specific integer
Definition integers.hpp:32
lox::Verbosity Verbosity
Type alias in namespace #"%alib".
strings::TString< nchar > NString
Type alias in namespace #"%alib".
Definition string.hpp:2174
monomem::TLocalAllocator< TCapacityInKB > LocalAllocator
Type alias in namespace #"%alib".
strings::TCString< nchar > NCString
Type alias in namespace #"%alib".
Definition cstring.hpp:408
strings::TAString< nchar, lang::HeapAllocator > NAString
Type alias in namespace #"%alib".
lang::integer integer
Type alias in namespace #"%alib".
Definition integers.hpp:149
strings::TString< character > String
Type alias in namespace #"%alib".
Definition string.hpp:2165
system::Path Path
Type alias in namespace #"%alib".
Definition path.hpp:417
strings::TCString< PathCharType > CPathString
The string-type used with this ALib Module.
Definition path.hpp:37
strings::TSubstring< nchar > NSubstring
Type alias in namespace #"%alib".
filetree::FTFile FTFile
Type alias in namespace #"%alib".
Definition ftree.hpp:1045
LocalString< 128 > String128
Type alias name for #"TLocalString;TLocalString<character,128>".
std::filesystem::path::value_type PathCharType
Definition path.hpp:12
boxing::TBoxes< MonoAllocator > BoxesMA
Type alias in namespace #"%alib".
Definition boxes.hpp:192
system::FileStatus FileStatus
Type alias in namespace #"%alib".
constexpr PathCharType DIRECTORY_SEPARATOR
The standard path separator character. Defaults to '\' on Windows OS, '/' else.
Definition path.hpp:63
NLocalString< 512 > NString512
Type alias name for #"TLocalString;TLocalString<nchar,512>".
lang::uinteger uinteger
Type alias in namespace #"%alib".
Definition integers.hpp:152
strings::TSubstring< PathCharType > PathSubstring
The string-type used with this ALib Module.
Definition path.hpp:40
time::DateTime DateTime
Type alias in namespace #"%alib".
Definition datetime.hpp:188
#define ALIB_STRING_RESETTER(astring)
Result information from MakeCanonical.
Definition fscanner.hpp:252
virtual bool Includes(const FTFile &file, const PathString &parentPath)=0
Recursively accumulated values for directories.
Definition ftvalue.hpp:25
uint32_t QtyErrsBrokenLink
Number of broken symbolic links in the directory and its subfolders.
Definition ftvalue.hpp:29
uint32_t Count() const noexcept
Definition ftvalue.hpp:86
uint32_t QtyErrsAccess
Number of access errors in the folder and subfolders.
Definition ftvalue.hpp:28
constexpr DirectorySums & Add(const FTValue &finfo) noexcept
Definition ftvalue.hpp:77
Input parameters to function #"ScanFiles(FTree&)".
Definition fscanner.hpp:19
@ DONT_RESOLVE
Demands not to resolve symbolic links in any way.
Definition fscanner.hpp:22
SPFileFilter DirectoryFilterPostRecursion
Definition fscanner.hpp:85
unsigned MaxDepth
The maximum recursion depth. Defaults to #"InfiniteRecursion".
Definition fscanner.hpp:41
static constexpr unsigned InfiniteRecursion
Denotes 'infinite' recursion if set to field #"MaxDepth".
Definition fscanner.hpp:32
SymbolicLinks LinkTreatment
Denotes how symbolic links are treated.
Definition fscanner.hpp:38
Path StartPath
The path to be scanned.
Definition fscanner.hpp:35
SPFileFilter DirectoryFilterPreRecursion
Definition fscanner.hpp:97
#define A_PATH(literal)