ALib C++ Library
Library Version: 2510 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
fscanner.cpp
1// #################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2025 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software lbicense, see LICENSE.txt)
6// #################################################################################################
7#include "alib_precompile.hpp"
8#if !defined(ALIB_C20_MODULES) || ((ALIB_C20_MODULES != 0) && (ALIB_C20_MODULES != 1))
9# error "Symbol ALIB_C20_MODULES has to be given to the compiler as either 0 or 1"
10#endif
11#if ALIB_C20_MODULES
12 module;
13#endif
14// ====================================== Global Fragment ======================================
15
19#include <vector>
20#if ALIB_FILES_SCANNER_IMPL == ALIB_FILES_SCANNER_POSIX
21# include <unistd.h>
22# if defined(__linux__)
23# include <asm/unistd.h>
24# endif
25# include <dirent.h>
26# if defined(__linux__)
27# include <linux/stat.h>
28# endif
29# include <sys/stat.h>
30# if !defined(__APPLE__)
31# include <sys/sysmacros.h>
32# else
33# include <sys/types.h>
34# endif
35# include <pwd.h>
36# include <fcntl.h>
37# include <pwd.h>
38# include <grp.h>
39#else
40# include <filesystem>
41#endif
42// =========================================== Module ==========================================
43#if ALIB_C20_MODULES
44 module ALib.Files;
45 import ALib.Lang;
47 import ALib.Monomem;
48 import ALib.Strings;
50 import ALib.System;
51# if ALIB_EXPRESSIONS
52 import ALib.Expressions;
53# endif
54# if ALIB_ALOX
55 import ALib.ALox;
56 import ALib.ALox.Impl;
57# endif
58#else
59# include "ALib.Lang.H"
61# include "ALib.Monomem.H"
62# include "ALib.Strings.H"
64# include "ALib.System.H"
65# include "ALib.Expressions.H"
66# include "ALib.ALox.H"
67# include "ALib.ALox.Impl.H"
68# include "ALib.Files.H"
69#endif
70// ====================================== Implementation =======================================
71#if !DOXYGEN
73
74
75using namespace alib::system;
76
77namespace alib::files { namespace {
78
79 // forward declaration of startScan()
80 bool startScan( FTree& tree,
81 PathString realPath,
82 ScanParameters& params,
83 FInfo::DirectorySums& parentSums,
84 std::vector<ResultsPaths>& resultPaths
85 IF_ALIB_THREADS(, SharedLock* lock ) );
86
87 // scan parameters used with startScan to evaluate directory entries
88 ScanParameters paramsPathOnly( nullptr, ScanParameters::SymbolicLinks::DONT_RESOLVE, 0, true, true );
89} // namespace alib::files[::anonymous]
90
91#if ALIB_DEBUG
93 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}");
94#endif
95
96} // namespace [alib::files]
97
98
99//--------------------------------------------------------------------------------------------------
100//--- scanFilePosix
101//--------------------------------------------------------------------------------------------------
102#if ALIB_FILES_SCANNER_IMPL == ALIB_FILES_SCANNER_POSIX
103
104#if ALIB_DEBUG
105# define DBG_CHECKERRNO \
106 ALIB_ASSERT_WARNING(errno == 0, "FILES", "Errno set ({})\"{}\".", \
107 errno, SystemErrors(errno) ) \
108 errno= 0;
109# define DBG_CHECKERRNO_WITH_PATH \
110 ALIB_ASSERT_WARNING(errno == 0, "FILES", "Errno set ({})\"{}\". Current path: {}", \
111 errno, SystemErrors(errno), actPath ) \
112 errno= 0;
113#else
114# define DBG_CHECKERRNO
115# define DBG_CHECKERRNO_WITH_PATH
116#endif
117
118// Since Kernel 4.11 Linux/glibc has "statx". We use it if available on the current platform.
119#if defined(__NR_statx)
120# define TMP_STATX_AVAILABLE 1
121# define STATMEMBER(Name) stats.stx_ ## Name
122# define STAT_DEV_MAJOR stats.stx_dev_major
123# define STAT_DEV_MINOR stats.stx_dev_minor
124#else
125# define TMP_STATX_AVAILABLE 0
126# define STATMEMBER(Name) stats.st_ ## Name
127# define STAT_DEV_MAJOR major(stats.st_dev)
128# define STAT_DEV_MINOR minor(stats.st_dev)
129#endif
130
131namespace alib::files { namespace {
132void scanFilePosix( DIR* pxDir,
133 FTree::Cursor& node,
134 const CPathString& nameOrFullPath, // if full path, this has the same buffer as actPath!
135 unsigned int depth,
136 ScanParameters& params,
137 uint64_t currentDevice,
138 FInfo::DirectorySums& parentSums ,
139 Path& actPath,
140 std::vector<ResultsPaths>& resultPaths
141 IF_ALIB_THREADS(, SharedLock* lock) )
142{
143 ALIB_ASSERT_ERROR( actPath.CharAtStart()== DIRECTORY_SEPARATOR
144 && ( actPath.Length()==1
145 || actPath.CharAtEnd() != DIRECTORY_SEPARATOR )
146 && actPath.IndexOf(strings::TLocalString<system::PathCharType,4>(
147 DIRECTORY_SEPARATOR).Append(DIRECTORY_SEPARATOR)) < 0 ,
148 "FILES","Given path not absolute or ending with '{}': {}",
149 DIRECTORY_SEPARATOR, actPath )
150 ALIB_DBG( Path dbgActFile;
151 if( actPath.Buffer() == nameOrFullPath.Buffer() )
152 dbgActFile << nameOrFullPath;
153 else
154 {
155 dbgActFile << actPath;
156 if(dbgActFile.Length()>1)
157 dbgActFile << DIRECTORY_SEPARATOR;
158 dbgActFile << nameOrFullPath;
159 } )
160 Log_Prune(
161 LocalAllocator<1> verboseAllocator;
162 BoxesMA verboseLogables( verboseAllocator);
163 int verboseLoggers;
164 Log_IsActive(verboseLoggers, Verbosity::Verbose)
165 if( verboseLoggers )
166 {
167 verboseLogables.Add("{!AWidth:>} ");
168 if( &params == &paramsPathOnly )
169 verboseLogables.Add("PO"); // 'Path Only'
170 else
171 {
172 auto& depthString= *verboseAllocator().New<String128>();
173 depthString << depth << DIRECTORY_SEPARATOR
174 << ( params.MaxDepth < std::numeric_limits<unsigned int>::max()
175 ? String128(params.MaxDepth)
176 : String(A_CHAR("M")) );
177 verboseLogables.Add(depthString);
178 }
179
180 verboseLogables.Add(files::DBG_FILES_SCAN_VERBOSE_LOG_FORMAT, File(node) );
181 }
182 )
183 ALIB_ASSERT_WARNING(errno == 0, "FILES", "Errno set ({})\"{}\" with current file: {}",
184 errno, SystemErrors(errno), dbgActFile )
185 ALIB_DBG( errno= 0;)
186
187 auto& value = *node;
188 auto oldQuality= value.Quality();
189
190 ALIB_STRINGS_TO_NARROW(nameOrFullPath, nNameOrFullPath, 512)
191
192 // ------------------------------ get stats? ------------------------------
193 if( value.Quality() == FInfo::Qualities::NONE
194 || ( value.Quality() == FInfo::Qualities::STATS
195 && params.LinkTreatment != ScanParameters::SymbolicLinks::DONT_RESOLVE ) )
196 {
197 value.SetQuality(FInfo::Qualities::STATS);
198 Path symLinkDest;
199 Path symLinkDestReal;
200
201 // read base stats
202 ALIB_DBG( errno= 0;)
203 #if TMP_STATX_AVAILABLE
204 struct statx stats;
205 int statResult= statx( pxDir ? dirfd(pxDir) : 0,
206 nNameOrFullPath,
207 AT_STATX_DONT_SYNC | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW,
208 STATX_BASIC_STATS | STATX_BTIME,
209 &stats );
210
211 #else
212 struct stat stats;
213 int statResult= pxDir ? fstatat(dirfd(pxDir), nNameOrFullPath, &stats,
214 AT_SYMLINK_NOFOLLOW
215 #if !defined(__APPLE__)
216 | AT_NO_AUTOMOUNT
217 #endif
218 )
219 : lstat ( nNameOrFullPath, &stats );
220 #endif
221 if( statResult )
222 {
223 ALIB_ASSERT_WARNING( errno != ENOENT, "FILES", "File does not exist (anymore) while stating \"{}\"",
224 dbgActFile )
225 ALIB_ASSERT_WARNING( errno == ENOENT, "FILES", "Unknown error ({}) \"{}\" while stating file \"{}\"",
226 errno, SystemErrors(errno), dbgActFile )
227 value.SetQuality(errno == ENOENT ? FInfo::Qualities::NOT_EXISTENT
229 ALIB_DBG( errno= 0;)
230 goto APPLY_POST_RECURSION_FILTER;
231 }
232 DBG_CHECKERRNO_WITH_PATH
233
234 // check filesystem type (artificial fs & mount point)
235 {
236 uint64_t device= (uint64_t(STAT_DEV_MAJOR) << 32L) + STAT_DEV_MINOR;
237 if( currentDevice == 0) currentDevice= device;
238 else if( currentDevice != device) { currentDevice= device; value.SetCrossingFS(); }
239 }
240
241 if( STAT_DEV_MAJOR == 0 // artificial?
242 && STAT_DEV_MINOR != 35 ) // tmpfs included, not considered artificial!
243 value.SetArtificialFS();
244
245 //------------ is symlink? ------------
246 bool origFileIsSymlink= (STATMEMBER(mode) & S_IFMT) == S_IFLNK;
247 if( origFileIsSymlink
249 {
250 value.SetQuality( FInfo::Qualities::RESOLVED );
251
252 // 1. Read plain symlink target (only to be attached to the entry)
253 ALIB_STRINGS_TO_NARROW(symLinkDest, nSymLinkDest, 512)
254 ssize_t cntChars= pxDir ? readlinkat( dirfd(pxDir), nNameOrFullPath, nSymLinkDest.VBuffer(), PATH_MAX)
255 : readlink ( nNameOrFullPath, nSymLinkDest.VBuffer(), PATH_MAX);
256
257 if (cntChars == -1) switch(errno)
258 {
259 case EACCES: value.SetQuality(FInfo::Qualities::NO_ACCESS_SL); ALIB_DBG(errno= 0;)
260 goto ABORT_SYMLINK;
261
262 case ENOENT: value.SetQuality(FInfo::Qualities::NO_ACCESS_SL);
263 ALIB_ASSERT_ERROR(STAT_DEV_MAJOR == 0, "FILES",
264 "Posix raised ({}) \"{}\" on reading a symbolic link which is not located on "
265 "an artificial filesystem (like /proc). File:\"{}\"",
266 errno, SystemErrors(errno), dbgActFile ) ALIB_DBG(errno= 0;)
267 goto ABORT_SYMLINK;
268
269 default: value.SetQuality(FInfo::Qualities::UNKNOWN_ERROR);
270 ALIB_ERROR("FILES", "Posix raised ({}) \"{}\" on reading symbolic link \"{}\"",
271 errno, SystemErrors(errno), dbgActFile ) ALIB_DBG(errno= 0;)
272 goto ABORT_SYMLINK;
273 }
274 nSymLinkDest.SetLength(cntChars);
276 symLinkDest.Reset(nSymLinkDest);
277 #endif
278
279 // 2. Read symlink's real target path (fully and recursively translated)
280 ALIB_STRING_RESETTER(actPath);
281 if( pxDir )
282 actPath << DIRECTORY_SEPARATOR << nameOrFullPath;
283 errno= 0;
284 ALIB_STRINGS_TO_NARROW(actPath , nActPath , 512)
285 ALIB_STRINGS_TO_NARROW(symLinkDestReal, nSymLinkDestReal, 512)
286 *nSymLinkDestReal.VBuffer()= '\0';
287 if(! realpath(nActPath.Terminate(), nSymLinkDestReal.VBuffer() ) ) switch (errno)
288 { // The named file does not exist.
289 case ENOENT: if( *nSymLinkDestReal.VBuffer() != '\0')
290 nSymLinkDestReal.DetectLength();
291 value.SetQuality(FInfo::Qualities::BROKEN_LINK); ALIB_DBG(errno= 0;)
292 goto ABORT_SYMLINK;
293 case ELOOP: value.SetQuality(FInfo::Qualities::CIRCULAR_LINK); ALIB_DBG(errno= 0;)
294 goto ABORT_SYMLINK;
295 // this might happen with strange system files
296 case EACCES: value.SetQuality(FInfo::Qualities::NO_ACCESS_SL_TARGET); ALIB_DBG(errno= 0;)
297 goto ABORT_SYMLINK;
298 default: ALIB_ERROR("FILES", "Posix raised ({}) \"{}\" on resolving symbolic link \"{}\"",
299 errno, SystemErrors(errno), dbgActFile ) ALIB_DBG(errno= 0;)
300 goto ABORT_SYMLINK;
301 }
302 nSymLinkDestReal.DetectLength();
303
304 ALIB_DBG( if( errno == EINVAL) errno= 0;) // this happens, even though realpath() above returned 'OK'
305 DBG_CHECKERRNO_WITH_PATH
306 ALIB_ASSERT_ERROR( Path::IsAbsolute(symLinkDestReal), "FILES",
307 "Real path is not absolute: ", nSymLinkDestReal )
308
309 // 3. get resolved status
310 DBG_CHECKERRNO_WITH_PATH
311 #if TMP_STATX_AVAILABLE
312 statResult= statx( 0,
313 nSymLinkDestReal.Terminate(),
314 AT_STATX_DONT_SYNC | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW,
315 STATX_ALL,
316 &stats );
317 #else
318 statResult= stat(nSymLinkDestReal.Terminate(), &stats );
319 #endif
320 DBG_CHECKERRNO_WITH_PATH
321 #if ALIB_CHARACTERS_WIDE
322 symLinkDestReal.Reset(nSymLinkDestReal);
323 #endif
324
325 if(statResult == -1 )
326 {
328 if(errno) switch( SystemErrors(errno) )
329 { case SystemErrors::enoent: value.SetQuality(FInfo::Qualities::BROKEN_LINK);
330 ALIB_DBG(errno= 0;)
331 goto APPLY_POST_RECURSION_FILTER;
332 default: ALIB_WARNING("FILES",
333 "Unhandled error code invoking 'stat()' on resolved symbolic "
334 "link: {} (\"{}\")\n Symbolic link target: \"{}\"",
335 errno, SystemErrors(errno), dbgActFile )
336 ALIB_DBG(errno= 0;)
337 value.SetQuality(FInfo::Qualities::UNKNOWN_ERROR);
338 goto APPLY_POST_RECURSION_FILTER;
339 }
341 }
342
343 // check for target artificial fs
344 if( STAT_DEV_MAJOR == 0 // artificial?
345 && STAT_DEV_MINOR != 35 ) // tempfs included!
346 value.SetTargetArtificialFS();
347
348 } // if is symlink && resolve symlinks
349
350 ABORT_SYMLINK:
351 DBG_CHECKERRNO_WITH_PATH
352
353 // =========================================================================================
354 // ================================== Copy Stats ==================================
355 // =========================================================================================
356 // 1. type
357 {
359 auto posixType= STATMEMBER(mode) & S_IFMT;
360 if( origFileIsSymlink )
361 {
362 type= posixType == S_IFDIR ? FInfo::Types::SYMBOLIC_LINK_DIR
364 }
365 else switch(STATMEMBER(mode) & S_IFMT )
366 {
367 case S_IFLNK : type= FInfo::Types::SYMBOLIC_LINK; ALIB_ERROR( "FILES", "Impossible")
368 break;
369 case S_IFBLK : type= FInfo::Types::BLOCK ; break;
370 case S_IFCHR : type= FInfo::Types::CHARACTER ; break;
371 case S_IFDIR : type= FInfo::Types::DIRECTORY ; break;
372 case S_IFIFO : type= FInfo::Types::FIFO ; break;
373 case S_IFREG : type= FInfo::Types::REGULAR ; break;
374 case S_IFSOCK: type= FInfo::Types::SOCKET ; break;
375 default: ALIB_ERROR("FILES",
376 "Internal error. 'unknown' file type can't happen. File: \"{}\"",
377 dbgActFile ) break;
378 }
379 value.SetType( type );
380 }
381
382 // 2. perms
383 value.SetPerms( FInfo::Permissions(STATMEMBER(mode) & int32_t(FInfo::Permissions::MASK)) );
384
385 // 3. timestamps
386 {
387 #if defined(__APPLE__)
388 # define st_mtime_name STATMEMBER(mtimespec)
389 # define st_ctime_name STATMEMBER(ctimespec)
390 # define st_atime_name STATMEMBER(atimespec)
391 #else
392 # if TMP_STATX_AVAILABLE
393 # define st_mtime_name STATMEMBER(mtime)
394 # define st_ctime_name STATMEMBER(ctime)
395 # define st_atime_name STATMEMBER(atime)
396 # define st_btime_name STATMEMBER(btime)
397 # else
398 # define st_mtime_name STATMEMBER(mtim)
399 # define st_ctime_name STATMEMBER(ctim)
400 # define st_atime_name STATMEMBER(atim)
401 # endif
402 #endif
403 DateTime dt;
404 dt.Import(
405 std::chrono::system_clock::time_point {
406 std::chrono::duration_cast<std::chrono::system_clock::duration>(
407 std::chrono::seconds {st_mtime_name.tv_sec }
408 + std::chrono::nanoseconds{st_mtime_name.tv_nsec} ) } );
409 value.SetMDate(dt);
410
411 dt.Import(
412 std::chrono::system_clock::time_point {
413 std::chrono::duration_cast<std::chrono::system_clock::duration>(
414 std::chrono::seconds {st_ctime_name.tv_sec }
415 + std::chrono::nanoseconds{st_ctime_name.tv_nsec} ) } );
416 value.SetCDate(dt);
417
418 dt.Import(
419 std::chrono::system_clock::time_point {
420 std::chrono::duration_cast<std::chrono::system_clock::duration>(
421 std::chrono::seconds {st_atime_name.tv_sec }
422 + std::chrono::nanoseconds{st_atime_name.tv_nsec} ) } );
423 value.SetADate(dt);
424
425 #if TMP_STATX_AVAILABLE
426 if( STATMEMBER(mask) & STATX_BTIME ) // file systems supports "btime"?
427 {
428 dt.Import(
429 std::chrono::system_clock::time_point {
430 std::chrono::duration_cast<std::chrono::system_clock::duration>(
431 std::chrono::seconds {st_btime_name.tv_sec }
432 + std::chrono::nanoseconds{st_btime_name.tv_nsec} ) } );
433 value.SetBDate(dt);
434 }
435 else
436 {
437 // use smallest of other times for "btime"
438 auto btime= value.MDate();
439 if( btime > value.CDate() ) btime= value.CDate();
440 if( btime > value.ADate() ) btime= value.ADate();
441 value.SetBDate( btime );
442
443 }
444 #else
445 // use smallest of other times for "btime"
446 auto btime= value.MDate();
447 if( btime > value.CDate() ) btime= value.CDate();
448 if( btime > value.ADate() ) btime= value.ADate();
449 value.SetBDate( btime );
450 #endif
451
452
453 #undef st_mtime_name
454 #undef st_ctime_name
455 #undef st_atime_name
456 }
457
458 // 4. size
459 value.SetSize( uinteger(STATMEMBER(size) ) );
460
461 // 5. uid/gid
462 value.SetOwner( STATMEMBER(uid) );
463 value.SetGroup( STATMEMBER(gid) );
464
465 // 6. qty of symlinks
466 value.SetQtyHardlinks( STATMEMBER(nlink) );
467
468 // 7. Add extended information
469 if( oldQuality < FInfo::Qualities::STATS
470 && (value.IsDirectory() || symLinkDest.IsNotEmpty()) ) {
471 IF_ALIB_THREADS( if (lock) lock->Acquire(ALIB_CALLER_PRUNED); )
472 File(node).GetFTree().AllocateExtendedInfo( node, symLinkDest, symLinkDestReal );
473 IF_ALIB_THREADS( if (lock) lock->Release(ALIB_CALLER_PRUNED); )
474 }
475
476 } // if scan stats (quality was just path)
477
478 DBG_CHECKERRNO_WITH_PATH
479
480 // Count broken link.
481 if(value.Quality() == FInfo::Qualities::BROKEN_LINK)
482 {
483 ++parentSums.QtyErrsBrokenLink;
484 goto APPLY_POST_RECURSION_FILTER;
485 }
486
487 // =============================================================================================
488 // ============================= recursion with directories? ============================
489 // =============================================================================================
490 if( !value.IsDirectory()
491 || value.Quality() >= FInfo::Qualities::RECURSIVE )
492 goto APPLY_POST_RECURSION_FILTER;
493
494 // stop recursion due to artificial fs?
495 if( value.IsArtificialFS() && !params.IncludeArtificialFS )
496 {
497 Log_Prune( if( verboseLogables.Size() ) verboseLogables.Add(" NO_AFS"); )
498 value.SetQuality( FInfo::Qualities::NO_AFS );
499 goto APPLY_POST_RECURSION_FILTER;
500 }
501
502 // stop recursion due to crossing filesystem?
503 if( value.IsCrossingFS() && !params.CrossFileSystems )
504 {
505 Log_Prune( if( verboseLogables.Size() ) verboseLogables.Add(" NOT_CROSSING_FS"); )
506 value.SetQuality( FInfo::Qualities::NOT_CROSSING_FS );
507 goto APPLY_POST_RECURSION_FILTER;
508 }
509
510 // stop recursion due to max depth?
511 if( depth >= params.MaxDepth )
512 {
513 Log_Prune( if( verboseLogables.Size() && (&params != &paramsPathOnly) ) verboseLogables.Add(" MAX_DEPTH_REACHED"); )
514 value.SetQuality( FInfo::Qualities::MAX_DEPTH_REACHED );
515 ++parentSums.QtyStopsOnMaxDepth;
516 goto APPLY_POST_RECURSION_FILTER;
517 }
518
519 // stop recursion due to filter
520 if( depth > 0
521 && params.DirectoryFilterPreRecursion
522 && !params.DirectoryFilterPreRecursion->Includes( node, actPath ) )
523 {
524 Log_Prune( if( verboseLogables.Size() ) verboseLogables.Add(" FILTERED(Pre)"); )
525 goto APPLY_POST_RECURSION_FILTER;
526 }
527
528 // mark as recursively scanned
529 value.SetQuality( FInfo::Qualities::RECURSIVE );
530
531 // SYMLINK RECURSION
532 if ( value.Type() == FInfo::Types::SYMBOLIC_LINK_DIR )
533 {
535 || value.IsArtificialFS() ) // never recurse with symlinks RESIDING on artificial fs!
536 {
537 value.SetQuality( FInfo::Qualities::NOT_FOLLOWED );
538 goto APPLY_POST_RECURSION_FILTER;
539 }
540
541 if( value.TargetIsArtificialFS() && !params.IncludeArtificialFS )
542 {
543 value.SetQuality( FInfo::Qualities::NO_AFS );
544 goto APPLY_POST_RECURSION_FILTER;
545 }
546
547 // recurse into symlink target
548 FInfo::DirectorySums childSums;
549 if( startScan( node.Tree<FTree>(), value.GetRealLinkTarget(), params, childSums,
550 resultPaths IF_ALIB_THREADS(,lock) ) )
551 value.SetQuality(FInfo::Qualities::DUPLICATE);
552 value.SetSums( childSums );
553 parentSums+= childSums;
554
555 goto APPLY_POST_RECURSION_FILTER;
556 }
557
558 // DIRECTORY RECURSION
559 {ALIB_STRING_RESETTER( actPath );
560 if( pxDir == nullptr )
561 {
562 ALIB_ASSERT_ERROR(actPath.Buffer() == nameOrFullPath.Buffer(),"FILES","Internal error")
563 actPath.SetLength(nameOrFullPath.Length());
564 }
565 else
566 {
567 if( actPath.Length() > 1 ) actPath << DIRECTORY_SEPARATOR;
568 actPath << nameOrFullPath;
569 }
570
571 errno= 0;
572 int fd;
573 if( pxDir)
574 fd= openat( dirfd(pxDir), nNameOrFullPath, O_RDONLY | O_DIRECTORY );
575 else
576 {
577 ALIB_STRINGS_TO_NARROW(actPath, nActPath, 512)
578 fd= open( nActPath , O_RDONLY | O_DIRECTORY );
579 }
580
581 if (fd != -1) // success?
582 {
583 DBG_CHECKERRNO_WITH_PATH
584 FInfo::DirectorySums subSums;
585 DIR* childDir = fdopendir(fd);
586 for(;;)
587 {
588 errno= 0;
589 dirent* pxEntry = readdir(childDir);
590 if( pxEntry == nullptr )
591 {
592 switch(errno)
593 {
594 // possible errors (according to documentation):
595 // EOVERFLOW One of the values in the structure to be returned cannot be represented correctly.
596 // EBADF The dirp argument does not refer to an open directory stream.
597 // ENOENT The current position of the directory stream is invalid.
598 case 0: break;
599 case EACCES: value.SetQuality(FInfo::Qualities::NO_ACCESS_DIR);
600 break;
601 case EINVAL: value.SetQuality( FInfo::Qualities::NO_ACCESS_DIR);
602 ALIB_ASSERT_ERROR(major(currentDevice) == 0, "FILES",
603 "Posix raised ({}) \"{}\" on reading a directory which is not "
604 "located on an artificial filesystem (like /proc). File:\"{}\"",
605 errno, SystemErrors(errno), dbgActFile )
606 break;
607 default: value.SetQuality(FInfo::Qualities::UNKNOWN_ERROR);
608 ALIB_ERROR("FILES", "Posix raised ({}) \"{}\" on reading "
609 "directory \"{}\"", errno, SystemErrors(errno), dbgActFile )
610 break;
611 }
612 errno= 0;
613 break;
614 }
615
616 // skip "." and ".."
617 if( pxEntry->d_name[0] == '.'
618 && ( pxEntry->d_name[1] == '\0'
619 || ( pxEntry->d_name[1] == '.'
620 && pxEntry->d_name[2] == '\0' ) ) )
621 continue;
622
623 //----- recursive call -----
624 auto childNode= node;
625#if ALIB_CHARACTERS_WIDE
626 Path childName(const_cast<const char*>(&pxEntry->d_name[0]));
627#else
628 const CString childName(const_cast<const char*>(&pxEntry->d_name[0]));
629#endif
630 IF_ALIB_THREADS( if (lock) lock->Acquire(ALIB_CALLER_PRUNED); )
631 childNode.GoToCreateChildIfNotExistent( childName );
632 IF_ALIB_THREADS( if (lock) lock->Release(ALIB_CALLER_PRUNED); )
633 scanFilePosix( childDir, childNode, childName,
634 depth + 1, params, currentDevice, subSums, actPath,
635 resultPaths IF_ALIB_THREADS(,lock) );
636 } // dir entry loop
637 closedir(childDir);
638 DBG_CHECKERRNO_WITH_PATH
639
640 // previously scanned in lower quality?
641 if( oldQuality != FInfo::Qualities::NONE )
642 {
643 FTree::FixSums( node );
644 parentSums+= value.Sums();
645 }
646 else
647 {
648 value.SetSums(subSums);
649 parentSums+= subSums;
650 }
651 ALIB_DBG( errno= 0;)
652 goto APPLY_POST_RECURSION_FILTER;
653 } // success opening director
654
655 // error with recursion
656 ALIB_ASSERT_ERROR(errno != ENOTDIR, "FILES",
657 "Internal error opening directory. This must never happen")
658
659
661 switch (SystemErrors(errno))
662 {
663 case SystemErrors::eacces:
664 ++parentSums.QtyErrsAccess;
665 value.SetQuality( FInfo::Qualities::NO_ACCESS_DIR );
666 errno= 0;
667 break;
668
669 default:
670 ALIB_ERROR("FILES", "Unknown error {}(\"{}\") while opening directory \"{}\"",
671 errno, SystemErrors(errno), actPath)
672 value.SetQuality( FInfo::Qualities::UNKNOWN_ERROR );
673 break;
674 }
675 }
676
677 // =============================================================================================
678 // ==================== Apply Post Filter and remove empty directories ===================
679 // =============================================================================================
680 APPLY_POST_RECURSION_FILTER:
681 // delete node only if this was a new scan.
682 // It must not be deleted if this node was created as a path.
683 if( oldQuality == FInfo::Qualities::NONE )
684 {
685 if ( value.IsDirectory() )
686 {
687 if( depth > 0
688 && ( ( params.DirectoryFilterPostRecursion
689 && !params.DirectoryFilterPostRecursion->Includes(node, actPath ) )
690 || ( params.RemoveEmptyDirectories
691 && value.Sums().Count() == 0 )
692 ) )
693 {
694 Log_Prune( if( verboseLogables.Size() ) { verboseLogables.Add(" FILTERED(Post)");
695 Log_Verbose( verboseLogables )
696 verboseLogables.clear(); } )
697 parentSums-= value.Sums();
698 if( params.RemoveEmptyDirectories )
699 {
700 File file(node);
701 node.Tree<FTree>().Notify( FTreeListener::Event::DeleteNode,
702 file,
703 IF_ALIB_THREADS(lock,)
704 actPath );
705 node.Delete();
706 return;
707 }
708
709 // Notify deletion of all children.
710 auto it= node.FirstChild();
711 while ( it.IsValid() )
712 {
713 File file(node);
714 node.Tree<FTree>().Notify( FTreeListener::Event::DeleteNode,
715 file,
716 IF_ALIB_THREADS(lock,)
717 actPath );
718 it.GoToNextSibling();
719 }
720
721 // do not return here. Still count the type below
722 node.DeleteChildren();
723 }
724
725 }
726 else
727 {
728 if ( params.FileFilter
729 && !params.FileFilter->Includes(node, actPath ) )
730 {
731 IF_ALIB_THREADS( if (lock) lock->Acquire(ALIB_CALLER_PRUNED); )
732 Log_Prune( if( verboseLogables.Size() ) { verboseLogables.Add(" FILTERED(Post)");
733 Log_Verbose( verboseLogables ) } )
734 node.Delete();
735 IF_ALIB_THREADS( if (lock) lock->Release(ALIB_CALLER_PRUNED); )
736 return;
737 }
738 }
739 }
740
741 Log_Prune( if( verboseLogables.Size() ) {
743 Log_Verbose( verboseLogables )
745 })
746
747 // cnt file type
748 parentSums.Add(value);
749 File file(node);
750 node.Tree<FTree>().Notify( FTreeListener::Event::CreateNode, file, IF_ALIB_THREADS(lock,) actPath );
751
753 DBG_CHECKERRNO_WITH_PATH
754} // scanFilePosix()
755
756}} // namespace [alib::files::anonymous]
757#undef DBG_CHECKERRNO_WITH_PATH
758#undef TMP_STATX_AVAILABLE
759#undef STATMEMBER
760
761
762//--------------------------------------------------------------------------------------------------
763//--- UNKNOWN platform, using C++17 filesystem (not all functionality given)
764//--------------------------------------------------------------------------------------------------
765#else
766#if ALIB_FILES_FORCE_STD_SCANNER
767# pragma message ("ALIB_FILES_FORCE_STD_SCANNER given. Using std::filesystem for scanning. In file: " __FILE__ )
768#else
769# pragma message ("Unknown Platform. Using std::filesystem for scanning. In file: " __FILE__ )
770#endif
771
772namespace fs = std::filesystem;
773
774// Note: MacOS is currently (as of 231210) missing C++20 library features in the area of std::clock
775#if defined(__APPLE__) || defined(_LIBCPP_VERSION) || defined(__ANDROID_NDK__)
776 namespace
777 {
778 template <typename TP>
779 std::time_t to_time_t(TP tp)
780 {
781 auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(tp - TP::clock::now()
782 + std::chrono::system_clock::now());
783 return std::chrono::system_clock::to_time_t(sctp);
784 }
785 }
786#endif
787#if ALIB_DEBUG
788# define DBG_CHECKERRNO \
789 ALIB_ASSERT_WARNING(errno == 0, "FILES", "Errno set ({})\"{}\".", \
790 errno, SystemErrors(errno) ) \
791 errno= 0;
792# define DBG_CHECKERRNO_WITH_PATH \
793 ALIB_ASSERT_WARNING(errno == 0, "FILES", "Errno set ({})\"{}\". Current path: {}", \
794 errno, SystemErrors(errno), path.string() ) \
795 errno= 0;
796#else
797# define DBG_CHECKERRNO
798# define DBG_CHECKERRNO_WITH_PATH
799#endif
800
801namespace alib::files { namespace {
802
803void scanFileStdFS( const fs::path& path,
804 FTree::Cursor& node,
805 unsigned int depth,
806 ScanParameters& params,
807 FInfo::DirectorySums& parentSums,
808 std::vector<ResultsPaths>& resultPaths
809 IF_ALIB_THREADS(, SharedLock* lock) )
810{
811#if defined(__MINGW32__)
812 Path pathAsCString(path.c_str());
813 pathAsCString.Terminate();
814#else
815 CPathString pathAsCString(path.c_str());
816#endif
817 const PathSubstring parentPath= pathAsCString.Substring(0, pathAsCString.LastIndexOf(DIRECTORY_SEPARATOR));
818
819 #if !defined(_WIN32)
820 ALIB_ASSERT_ERROR( pathAsCString.CharAtStart()== DIRECTORY_SEPARATOR
821 && ( pathAsCString.Length()==1
822 || pathAsCString.CharAtEnd() != DIRECTORY_SEPARATOR)
823 && pathAsCString.IndexOf(NString8(DIRECTORY_SEPARATOR).Append(DIRECTORY_SEPARATOR))<0,
824 "FILES","Given path not absolute or ending with '{}': {}", DIRECTORY_SEPARATOR, pathAsCString )
825 #else
826 ALIB_ASSERT_ERROR( ( ( pathAsCString.CharAt(1)== ':'
827 && pathAsCString.CharAt(2)== DIRECTORY_SEPARATOR
828 && ( pathAsCString.Length()==3
829 || pathAsCString.CharAtEnd() != DIRECTORY_SEPARATOR) )
830
831 || ( pathAsCString.CharAt(0)== DIRECTORY_SEPARATOR
832 && pathAsCString.CharAt(1)== DIRECTORY_SEPARATOR
833 && ( pathAsCString.Length()==2
834 || pathAsCString.CharAtEnd() != DIRECTORY_SEPARATOR) )
835 )
836 && pathAsCString.IndexOf( strings::TLocalString<PathCharType, 8>(
837 DIRECTORY_SEPARATOR).Append(DIRECTORY_SEPARATOR),
838 2 ) < 0,
839 "FILES","Given path not absolute or ending with '{}': {}", DIRECTORY_SEPARATOR, pathAsCString )
840 #endif
841
842
843 Log_Verbose( "[{}] {}/{} {}", &params != &paramsPathOnly ? '>':'P', depth,
844 params.MaxDepth < (std::numeric_limits<unsigned int>::max)()
845 ? String128(params.MaxDepth)
846 : String(A_CHAR("M")),
847 pathAsCString )
848
849 std::error_code errorCode;
850 auto& value = node.Value();
851 auto oldQuality= value.Quality();
852
853 // ------------------------------ get stats? ------------------------------
854 if( value.Quality() == FInfo::Qualities::NONE
855 || ( value.Quality() == FInfo::Qualities::STATS
857 {
858 value.SetQuality( FInfo::Qualities::STATS );
859 Path symLinkDest;
860 Path symLinkDestReal;
861
862 // read base stats (we have to use symlink_status() which does NOT follow the symlink!)
863 fs::file_status stats= fs::symlink_status(path);
865 if(errorCode)
866 {
867 ALIB_ERROR("FILES",
868 "Unhandled error code invoking 'fs::symlink_status()': {} (\"{}\")\n"
869 " With file: \"{}\"",
870 errorCode.value(), errorCode.message(), pathAsCString )
871 ALIB_DBG( errno= 0;)
872 value.SetQuality(FInfo::Qualities::UNKNOWN_ERROR);
873 goto APPLY_POST_RECURSION_FILTER;
874 }
876 ALIB_DBG(errno= 0;)
877
878 //------------ is symlink? ------------
879 bool origFileIsSymlink= (stats.type() == fs::file_type::symlink);
880 if( origFileIsSymlink
882 {
883 value.SetQuality( FInfo::Qualities::RESOLVED );
884
885 // 1. Read plain symlink target (only to be attached to the entry)
886 fs::path resolved= fs::read_symlink(path, errorCode);
887 if(errorCode)
888 {
890 switch( SystemErrors(errorCode.value()) )
891 { case SystemErrors::enoent: // happens with /proc files
892 case SystemErrors::eacces: value.SetQuality(FInfo::Qualities::NO_ACCESS_SL);
893 ALIB_DBG(errno= 0;)
894 goto ABORT_SYMLINK;
895 default: ALIB_ERROR("FILES",
896 "Unhandled error code invoking 'fs::read_symlink()': {} (\"{}\")\n"
897 " with file: ", errorCode.value(),
898 errorCode.message(), pathAsCString )
899 ALIB_DBG( errno= 0;)
900 value.SetQuality(FInfo::Qualities::UNKNOWN_ERROR);
901 goto APPLY_POST_RECURSION_FILTER;
902 }
904 }
905 DBG_CHECKERRNO_WITH_PATH
906 symLinkDest << resolved.c_str();
907
908 // 2. Read symlink's real target path (fully and recursively translated)
909 fs::path realPath;
910 if( resolved.is_absolute() )
911 realPath= fs::canonical(resolved, errorCode);
912 else
913 {
914 symLinkDestReal << pathAsCString;
915 symLinkDestReal.ShortenTo( symLinkDestReal.LastIndexOf(DIRECTORY_SEPARATOR) + 1);
916 symLinkDestReal << symLinkDest;
917 realPath= fs::canonical(fs::path(
918 std::basic_string_view<PathCharType>(symLinkDestReal.Buffer(),
919 size_t(symLinkDestReal.Length()))),
920 errorCode);
921 symLinkDestReal.Reset();
922 }
923 ALIB_DBG(if(errno==EINVAL && !errorCode) errno= 0;) // this happens!, we do not care, but clean up
924 ALIB_DBG(if(errno==ENOENT && !errorCode) errno= 0;)
925
927 if(errorCode) switch( SystemErrors(errorCode.value()) )
928 { // we ignore this: std::fs would not create the "real path" if the final directory is not accessible.
929 case SystemErrors::eacces: value.SetQuality(FInfo::Qualities::NO_ACCESS_SL_TARGET); ALIB_DBG(errno= 0;) goto ABORT_SYMLINK;
930 case SystemErrors::enoent: value.SetQuality(FInfo::Qualities::BROKEN_LINK); ALIB_DBG(errno= 0;) goto ABORT_SYMLINK;
931 case SystemErrors::eloop: value.SetQuality(FInfo::Qualities::CIRCULAR_LINK); ALIB_DBG(errno= 0;) goto ABORT_SYMLINK;
932 default: ALIB_ERROR("FILES", "Unhandled error code invoking 'fs::canonical()': {} (\"{}\")\n"
933 " with file: ", errorCode.value(), errorCode.message(), pathAsCString )
934 goto ABORT_SYMLINK;
935 }
937 DBG_CHECKERRNO_WITH_PATH
938 symLinkDestReal << realPath.c_str();
939
940 // 3. get resolved status
942 auto newStatus= fs::status(path, errorCode);
943 if(!errorCode)
944 {
945 // this happens with strange /proc files...
946 if(newStatus.type() != fs::file_type::unknown)
947 stats= newStatus;
948 }
949 else switch( SystemErrors(errorCode.value()) )
950 { case SystemErrors::eperm: value.SetQuality( FInfo::Qualities::NO_ACCESS ); ALIB_DBG(errno= 0;) goto ABORT_SYMLINK;
951 case SystemErrors::enoent: value.SetQuality( FInfo::Qualities::BROKEN_LINK ); ALIB_DBG(errno= 0;) goto ABORT_SYMLINK;
952 case SystemErrors::eloop: value.SetQuality( FInfo::Qualities::CIRCULAR_LINK); ALIB_DBG(errno= 0;) goto ABORT_SYMLINK;
953 default: ALIB_WARNING("FILES",
954 "Unhandled error code invoking 'directory_entry::status()': {} (\"{}\")\n"
955 " With file: \"{}\"",
956 errorCode.value(), errorCode.message(), pathAsCString )
957 goto ABORT_SYMLINK;
958 }
960
961 // check for target artificial fs
962 // -/- Not available with std::filesystem version
963 }
964
965 ABORT_SYMLINK:
966 DBG_CHECKERRNO_WITH_PATH
967
968 // =========================================================================================
969 // ================================== Copy Stats ==================================
970 // =========================================================================================
971 // 1. type
972 {
974 if( origFileIsSymlink )
975 {
976 type= is_directory(stats) ? FInfo::Types::SYMBOLIC_LINK_DIR
978 }
979 else switch( stats.type() )
980 {
981 case fs::file_type::directory: type= FInfo::Types::DIRECTORY ; break;
982 case fs::file_type::regular : type= FInfo::Types::REGULAR ; break;
983 case fs::file_type::symlink : type= FInfo::Types::SYMBOLIC_LINK; break; // for now, this is a file.
984 case fs::file_type::block : type= FInfo::Types::BLOCK ; break;
985 case fs::file_type::character: type= FInfo::Types::CHARACTER ; break;
986 case fs::file_type::fifo : type= FInfo::Types::FIFO ; break;
987 case fs::file_type::socket : type= FInfo::Types::SOCKET ; break;
988
989 case fs::file_type::not_found:
990 value.SetQuality(FInfo::Qualities::UNKNOWN_ERROR);
991 ALIB_WARNING("FILES", "Internal error. 'not found' file type can't happen. File: ", pathAsCString )
992 ALIB_DBG( errno= 0;) goto APPLY_POST_RECURSION_FILTER;
993 case fs::file_type::none :
994 value.SetQuality(FInfo::Qualities::UNKNOWN_ERROR);
995 ALIB_WARNING("FILES", "Internal error. 'none' file type can't happen. File: ", pathAsCString)
996 ALIB_DBG( errno= 0;) goto APPLY_POST_RECURSION_FILTER;
997 case fs::file_type::unknown :
998 value.SetQuality(FInfo::Qualities::UNKNOWN_ERROR);
999 ALIB_WARNING("FILES", "Internal error. Can't happen. File: ", pathAsCString)
1000 ALIB_DBG( errno= 0;) goto APPLY_POST_RECURSION_FILTER;
1001 default:
1002 value.SetQuality(FInfo::Qualities::UNKNOWN_ERROR);
1003 ALIB_WARNING("FILES", "Unknown fs::file_status::type '{}' with file {}.", stats.type(), pathAsCString)
1004 ALIB_DBG( errno= 0;) goto APPLY_POST_RECURSION_FILTER;
1005 }
1006 value.SetType( type );
1007 }
1008
1009 // 2. perms
1010 value.SetPerms( FInfo::Permissions(int32_t(stats.permissions())) );
1011
1012 // 3. timestamps
1013 // attn: This method always follows symbolic link and uses the target's time
1014 // This seems to be a confirmed behavior:
1015 // https://stackoverflow.com/questions/50778660/boost-filesystem-how-to-get-last-write-time-for-symlink-without-resolving
1016 auto fsTime= std::filesystem::file_time_type(std::filesystem::file_time_type::clock::now());
1017 if ( value.Quality() <= FInfo::Qualities::RESOLVED ) // no error
1018 {
1019 fsTime= fs::last_write_time( path, errorCode );
1021 if(errorCode) switch( SystemErrors(errorCode.value()) )
1022 { // This happens if with symbolic links that point to nowhere.
1023 case SystemErrors::enoent: ALIB_ERROR( "FILES",
1024 "Internal error. This should never happen, checked above. "
1025 "Undefined system error handling" ) ALIB_DBG( errno= 0;)
1026 value.SetQuality(FInfo::Qualities::UNKNOWN_ERROR);
1027 break;
1028
1029 default: ALIB_ERROR( "FILES",
1030 "Unhandled error code invoking 'fs::last_write_time()': {} (\"{}\")\n"
1031 " With file \"{}\".", errorCode.value(), errorCode.message(),
1032 pathAsCString )
1033 fsTime= (decltype(fsTime)::min)(); ALIB_DBG( errno= 0;)
1034 break;
1035 }
1037 }
1038
1039
1040 #if defined(__APPLE__) || defined(_LIBCPP_VERSION) || defined(__ANDROID_NDK__)
1041 value.SetMDate( DateTime::FromEpochSeconds( to_time_t( fsTime ) ) );
1042 #else
1043 value.SetMDate( DateTime::FromEpochSeconds( std::chrono::system_clock::to_time_t(
1044 std::chrono::clock_cast<std::chrono::system_clock>(fsTime) ) ) );
1045 #endif
1046 value.SetBDate( value.MDate() );
1047 value.SetCDate( value.MDate() );
1048 value.SetADate( value.MDate() );
1049
1050 // 4. size
1051 errorCode.clear();
1052 value.SetSize( symLinkDest.Length() > 0 ? uinteger(symLinkDest.Length())
1053 : value.Quality() <= FInfo::Qualities::RESOLVED ? uinteger(fs::file_size(path, errorCode))
1054 : 0 );
1055 if( value.Size() == uinteger(-1))
1056 {
1057 value.SetSize(0);
1059 switch( SystemErrors(errorCode.value()) )
1060 {
1061 // target is a directory (no error)
1062 case SystemErrors::eisdir:
1063 break;
1064
1065 case SystemErrors::enoent: // this happens if we have a broken symbolic link
1067 || value.Type() == FInfo::Types::SYMBOLIC_LINK_DIR , "FILES",
1068 "Internal error. This should never happen. Undefined system error handling" )
1069 break;
1070
1071 // size not supported. Happens with sockets, files in /proc, etc
1072 case SystemErrors::eopnotsupp: break;
1073 default: ALIB_ERROR("FILES",
1074 "Unhandled error code invoking 'directory_entry::file_size()':{} (\"{}\")\n"
1075 " With file \"{}\".",
1076 errorCode.value(), errorCode.message(), pathAsCString ) ALIB_DBG( errno= 0;)
1077 break;
1078 }
1080 }
1081
1082 // 5. uid/gid
1083 value.SetOwner( FInfo::UnknownID );
1084 value.SetGroup( FInfo::UnknownID );
1085
1086 // 6. qty of symlinks
1087 uint32_t qtyHardLinks= uint32_t( fs::hard_link_count(path, errorCode ) );
1089 if(errorCode)
1090 {
1091 ALIB_MESSAGE("FILES",
1092 "Unhandled error code invoking 'fs::hard_link_count()': {} (\"{}\")\n"
1093 " With file: \"{}\"",
1094 errorCode.value(), errorCode.message(), pathAsCString )
1095 ALIB_DBG( errno= 0;)
1096 }
1098 value.SetQtyHardlinks( qtyHardLinks );
1099
1100 // 7. Add extended information
1101 if( oldQuality < FInfo::Qualities::STATS
1102 && (value.IsDirectory() || symLinkDest.IsNotEmpty()) ) {
1103 IF_ALIB_THREADS( if (lock) lock->Acquire(ALIB_CALLER_PRUNED); )
1104 File(node).GetFTree().AllocateExtendedInfo( node, symLinkDest, symLinkDestReal );
1105 IF_ALIB_THREADS( if (lock) lock->Release(ALIB_CALLER_PRUNED); )
1106 }
1107
1108 } // if scan stats (quality was just path)
1109
1110 DBG_CHECKERRNO_WITH_PATH
1111
1112 // Count broken link.
1113 if(value.Quality() == FInfo::Qualities::BROKEN_LINK)
1114 {
1115 ++parentSums.QtyErrsBrokenLink;
1116 goto APPLY_POST_RECURSION_FILTER;
1117 }
1118
1119 // ------------------------------ recursion with directories? ------------------------------
1120 if( !value.IsDirectory()
1121 || value.Quality() >= FInfo::Qualities::RECURSIVE )
1122 goto APPLY_POST_RECURSION_FILTER;
1123
1124
1125 // stop recursion due to artificial fs?
1126 // Not supported with std::filesystem!
1127
1128 // stop recursion due to crossing filesystem?
1129 if( value.IsCrossingFS() && !params.CrossFileSystems )
1130 {
1131 value.SetQuality( FInfo::Qualities::NOT_CROSSING_FS );
1132 goto APPLY_POST_RECURSION_FILTER;
1133 }
1134
1135 // stop recursion due to max depth?
1136 if( depth >= params.MaxDepth )
1137 {
1138 value.SetQuality( FInfo::Qualities::MAX_DEPTH_REACHED );
1139 ++parentSums.QtyStopsOnMaxDepth;
1140 goto APPLY_POST_RECURSION_FILTER;
1141 }
1142
1143 // stop recursion due to filter
1144 if( depth > 0
1146 && !params.DirectoryFilterPreRecursion->Includes( node, parentPath ) )
1147 goto APPLY_POST_RECURSION_FILTER;
1148
1149 // mark as recursively scanned
1150 value.SetQuality( FInfo::Qualities::RECURSIVE );
1151
1152 // SYMLINK RECURSION
1153 if ( value.Type() == FInfo::Types::SYMBOLIC_LINK_DIR )
1154 {
1155 if( params.LinkTreatment != ScanParameters::SymbolicLinks::RECURSIVE
1156 || value.IsArtificialFS() ) // never recurse with symlinks RESIDING on artificial fs!
1157 {
1158 value.SetQuality( FInfo::Qualities::NOT_FOLLOWED );
1159 goto APPLY_POST_RECURSION_FILTER;
1160 }
1161 else
1162 {
1163 // recurse into symlink target
1164 FInfo::DirectorySums childSums;
1165 if( startScan( File(node).GetFTree(), value.GetRealLinkTarget(), params, childSums,
1166 resultPaths IF_ALIB_THREADS(,lock) ) )
1167 value.SetQuality(FInfo::Qualities::DUPLICATE);
1168 value.SetSums( childSums );
1169 parentSums+= childSums;
1170 goto APPLY_POST_RECURSION_FILTER;
1171 }
1172 }
1173
1174 // DIRECTORY RECURSION
1175 {
1176 fs::directory_iterator dit= fs::directory_iterator(path, errorCode);
1177 if(!errorCode) // success?
1178 {
1179 FInfo::DirectorySums subSums;
1180 for( const fs::directory_entry& childDir : dit )
1181 {
1182 // recursive call
1183 #if defined(_WIN32)
1184 Path mingwBuf( childDir.path().c_str());
1185 PathSubstring childName(mingwBuf);
1186 #else
1187 NSubstring childName(NCString(childDir.path().c_str()));
1188 #endif
1189 childName.ConsumeChars(childName.LastIndexOf(DIRECTORY_SEPARATOR) + 1);
1190 auto childNode= node;
1191 IF_ALIB_THREADS( if (lock) lock->Acquire(ALIB_CALLER_PRUNED); )
1192 childNode.GoToCreateChildIfNotExistent( childName );
1193 IF_ALIB_THREADS( if (lock) lock->Release(ALIB_CALLER_PRUNED); )
1194 scanFileStdFS( childDir.path(), childNode, depth + 1, params, subSums,
1195 resultPaths IF_ALIB_THREADS(,lock) );
1196 }
1197
1198 // previously scanned in lower quality?
1199 if( oldQuality != FInfo::Qualities::NONE )
1200 {
1201 FTree::FixSums( node );
1202 parentSums+= value.Sums();
1203 }
1204 else
1205 {
1206 value.SetSums(subSums);
1207 parentSums+= subSums;
1208 }
1209 ALIB_DBG( errno= 0;)
1210 goto APPLY_POST_RECURSION_FILTER;
1211 }
1212 }
1213
1214 // error with recursion
1215 ALIB_ASSERT_ERROR( errorCode.value() != ENOTDIR, "FILES",
1216 "Internal error opening directory. This must never happen" )
1217
1219 if(errorCode) switch (SystemErrors(errorCode.value()))
1220 {
1221 case SystemErrors::einval: // happens with /proc
1222 case SystemErrors::eacces: ++parentSums.QtyErrsAccess;
1223 value.SetQuality( FInfo::Qualities::NO_ACCESS_DIR );
1224 ALIB_DBG( errno= 0;)
1225 goto APPLY_POST_RECURSION_FILTER;
1226
1227 default: value.SetQuality(FInfo::Qualities::UNKNOWN_ERROR);
1228 ALIB_ERROR("FILES", "Unknown error {}(\"{}\") while opening directory \"{}\"",
1229 errorCode.value(), SystemErrors(errorCode.value()), pathAsCString)
1230 ALIB_DBG( errno= 0;)
1231 goto APPLY_POST_RECURSION_FILTER;
1232 }
1234 ALIB_DBG( errno= 0;)
1235
1236 // ------------------------------------ Apply Filter ------------------------------------------
1237 APPLY_POST_RECURSION_FILTER:
1238 // delete node only if this was a new scan. It must not be deleted if this node was
1239 // created as a path.
1240 if( oldQuality == FInfo::Qualities::NONE )
1241 {
1242 if ( value.IsDirectory() )
1243 {
1244 if( depth > 0
1245 && ( ( params.DirectoryFilterPostRecursion
1246 && !params.DirectoryFilterPostRecursion->Includes(node, parentPath ) )
1247 || ( params.RemoveEmptyDirectories
1248 && value.Sums().Count() == 0 )
1249 ) )
1250 {
1251 parentSums-= value.Sums();
1252 if( params.RemoveEmptyDirectories )
1253 {
1254 File file(node);
1255 node.Tree<FTree>().Notify( FTreeListener::Event::DeleteNode,
1256 file,
1257 IF_ALIB_THREADS( lock, )
1258 parentPath );
1259 node.Delete();
1260 return;
1261 }
1262
1263 // Notify deletion of all children.
1264 auto it= node.FirstChild();
1265 while ( it.IsValid() )
1266 {
1267 File file(node);
1268 node.Tree<FTree>().Notify( FTreeListener::Event::DeleteNode,
1269 file,
1270 IF_ALIB_THREADS( lock, )
1271 parentPath );
1272 it.GoToNextSibling();
1273 }
1274
1275 // do not return here. Still count the type below
1276 node.DeleteChildren();
1277 }
1278
1279 }
1280 else
1281 {
1282 if ( params.FileFilter
1283 && !params.FileFilter->Includes(node, parentPath ) )
1284 {
1285 IF_ALIB_THREADS( if (lock) lock->Acquire(ALIB_CALLER_PRUNED); )
1286 node.Delete();
1287 IF_ALIB_THREADS( if (lock) lock->Release(ALIB_CALLER_PRUNED); )
1288 return;
1289 }
1290 }
1291 }
1292
1293 // cnt file type
1294 parentSums.Add(value);
1295 File file(node);
1296 node.Tree<FTree>().Notify( FTreeListener::Event::CreateNode, file, IF_ALIB_THREADS(lock,) parentPath );
1297
1298} // scanFileStdFS
1299
1300
1301}} // namespace [alib::files::anonymous]
1302
1303#undef DBG_CHECKERRNO_WITH_PATH
1304#endif // std::fs version
1305
1306//--------------------------------------------------------------------------------------------------
1307//--- ALL Platforms
1308//--------------------------------------------------------------------------------------------------
1309namespace alib::files {
1310
1311namespace {
1312
1313// Creates start path nodes and invokes scanFileXXX
1314bool startScan( FTree& tree,
1315 PathString realPath,
1316 ScanParameters& params,
1317 FInfo::DirectorySums& parentSums,
1318 std::vector<ResultsPaths>& resultPaths
1319 IF_ALIB_THREADS(, SharedLock* lock) )
1320{
1321 ALIB_ASSERT_ERROR( Path::IsAbsolute(realPath), "FILES","Real path is not absolute: ", realPath )
1322
1323 FTree::Cursor node= tree.Root().AsCursor();
1324#if !defined(_WIN32)
1325 Path path(DIRECTORY_SEPARATOR);
1326
1327 // travel any existing portion of the path
1329 PathSubstring pathRemainder= node.GoTo( realPath );
1331 path << realPath.Substring(1, realPath.Length() - pathRemainder.Length() - 1);
1332#else
1333 Path path;
1334 PathSubstring pathRemainder;
1335 if(realPath.CharAt(1) == ':')
1336 {
1337 path << realPath.Substring(0,3);
1338 IF_ALIB_THREADS( if (lock) lock->Acquire(ALIB_CALLER_PRUNED); )
1339 node.GoToCreateChildIfNotExistent(realPath.Substring(0,2));
1340 pathRemainder= node.GoTo( realPath.Substring(3) );
1341 IF_ALIB_THREADS( if (lock) lock->Release(ALIB_CALLER_PRUNED); )
1342 path << realPath.Substring(3, realPath.Length() - pathRemainder.Length() -3 );
1343 }
1344 else
1345 {
1346 integer serverNameEnd= realPath.IndexOf( DIRECTORY_SEPARATOR, 2);
1347 if( serverNameEnd < 0)
1348 serverNameEnd= realPath.Length();
1349 path << realPath.Substring(0, serverNameEnd);
1350 IF_ALIB_THREADS( if (lock) lock->Acquire(ALIB_CALLER_PRUNED); )
1351 node.GoToCreateChildIfNotExistent(realPath.Substring(2, serverNameEnd - 2));
1352 pathRemainder= node.GoTo( realPath.Substring(serverNameEnd + 1) );
1353 IF_ALIB_THREADS( if (lock) lock->Release(ALIB_CALLER_PRUNED); )
1354 path << realPath.Substring(serverNameEnd, realPath.Length() - pathRemainder.Length() -serverNameEnd );
1355 }
1356
1357#endif
1358
1359 DBG_CHECKERRNO
1360
1361
1362 // existed already?
1363 if( pathRemainder.IsEmpty() )
1364 {
1365 // For directories, call scan just for the case of having 'higher' scan parameters
1366 if( node->IsDirectory())
1367 {
1368
1369 #if ALIB_FILES_SCANNER_IMPL == ALIB_FILES_SCANNER_POSIX
1370 path.Terminate();
1371 CPathString fullPathChildName(path);
1372 path.SetLength(path.LastIndexOf(DIRECTORY_SEPARATOR) );
1373 scanFilePosix( nullptr, node, fullPathChildName, 0, params, 0, parentSums, path,
1374 resultPaths IF_ALIB_THREADS(,lock));
1375 #else
1376 scanFileStdFS( fs::path(std::basic_string_view<PathCharType>(path.Buffer(),
1377 size_t(path.Length()))),
1378 node, 0, params, parentSums, resultPaths IF_ALIB_THREADS(,lock) );
1379 #endif
1380
1381 //resultPaths.emplace_back(ResultsPaths(realPath, node, true));
1382 }
1383
1384 return true;
1385 }
1386
1387 // did not exist already
1388 if( path.Length() > 1 )
1389 path.DeleteEnd<NC>(1);
1390
1391 strings::util::TTokenizer<PathCharType> tknzr( pathRemainder, DIRECTORY_SEPARATOR );
1392 while(tknzr.HasNext())
1393 {
1394 PathString name;
1395 if( path.Length() != 1 )
1396 {
1397 name= tknzr.Next();
1398IF_ALIB_THREADS( if (lock) lock->Acquire(ALIB_CALLER_PRUNED); )
1399 node= node.CreateChild(name);
1400IF_ALIB_THREADS( if (lock) lock->Release(ALIB_CALLER_PRUNED); )
1401 }
1402
1403 bool isLastPathElement= !tknzr.HasNext();
1404 if( isLastPathElement )
1405 parentSums= FInfo::DirectorySums(); // clear the sums, because only the results of the last element are used.
1406
1408 auto detectNodeDeletion= node.Depth();
1410
1411 #if ALIB_FILES_SCANNER_IMPL == ALIB_FILES_SCANNER_POSIX
1412
1413 if( path.IsEmpty() ) path << DIRECTORY_SEPARATOR;
1414 CPathString fullPathChildName;
1415 {
1416 // add node name to existing path and use same buffer for fullPathChildName!
1417 ALIB_STRING_RESETTER( path );
1418 if( path.Length() > 1 ) path << DIRECTORY_SEPARATOR;
1419 path << node.Name();
1420 path.Terminate();
1421 fullPathChildName= path;
1422 }
1423
1424 scanFilePosix( nullptr, node, fullPathChildName,
1425 0, isLastPathElement ? params : paramsPathOnly,
1426 0, parentSums, path, resultPaths IF_ALIB_THREADS(,lock) );
1427 if( fullPathChildName.Length() == 1 ) path.Reset();
1428 else { if(path.Length() > 1) path << DIRECTORY_SEPARATOR; path << name; }
1429 #else
1430 if( path.Length() != 1 ) path << DIRECTORY_SEPARATOR << name;
1431 scanFileStdFS( fs::path(std::basic_string_view<PathCharType>(path.Buffer(),
1432 size_t(path.Length()))),
1433 node, 0,
1434 isLastPathElement ? params : paramsPathOnly,
1435 parentSums, resultPaths IF_ALIB_THREADS(,lock) );
1436 if( path.Length() == 1 ) path.Reset();
1437 #endif
1438
1439 // if the just created node was not deleted during scan, add it to the result list
1440 if( isLastPathElement)
1441 {
1443 if (detectNodeDeletion == node.Depth() )
1444 resultPaths.insert(resultPaths.begin(), ResultsPaths(realPath, node, false));
1446 }
1447
1448 // Correct quality from max depth to stats
1449 if( !isLastPathElement && node->Quality() == FInfo::Qualities::MAX_DEPTH_REACHED)
1450 node->SetQuality(FInfo::Qualities::STATS);
1451 }
1452
1453 return false;
1454}
1455
1456} // namespace alib::files[::anonymous]
1457
1458#endif // !DOXYGEN
1459
1460// --------------------------------------------------------------------------------------------------
1461//--- ScanFiles()
1462//--------------------------------------------------------------------------------------------------
1463enum FInfo::Qualities ScanFiles( FTree& tree,
1464 ScanParameters& parameters,
1465 std::vector<ResultsPaths>& resultPaths
1466 IF_ALIB_THREADS( , SharedLock* lock) )
1467
1468{
1469 Log_SetDomain( "ALIB/FILES", Scope::Path)
1470 Log_SetDomain( "SCAN" , Scope::Filename)
1471
1472 ALIB_DBG( if( alib::FILES.IsBootstrapped())
1473 {
1474 Log_SetDomain( "ALIB/FILES", Scope::Path)
1475 Log_SetDomain( "SCAN" , Scope::Filename)
1476 } )
1477
1478
1479 //-------------------------------------- get real path ---------------------------------------
1480 Path path(parameters.StartPath);
1481 Path realPath;
1482 realPath.Terminate();
1483
1484 #if ALIB_FILES_SCANNER_IMPL == ALIB_FILES_SCANNER_POSIX
1485 ALIB_STRINGS_TO_NARROW(path , nPath , 512)
1486 ALIB_STRINGS_TO_NARROW(realPath, nRealPath, 512)
1487 if(!realpath(nPath.Terminate(), nRealPath.VBuffer() ) ) switch (errno)
1488 {
1489 case EACCES: ALIB_DBG(errno= 0;) return FInfo::Qualities::NO_ACCESS;
1490 case ENOENT: ALIB_DBG(errno= 0;) return FInfo::Qualities::NOT_EXISTENT;
1491 case ELOOP: ALIB_DBG(errno= 0;) return FInfo::Qualities::CIRCULAR_LINK;
1492 default: ALIB_ERROR("FILES", "Posix raised ({}) \"{}\" on resolving start path \"{}\"",
1493 errno, SystemErrors(errno), path ) ALIB_DBG(errno= 0;)
1495 }
1496 nRealPath.DetectLength();
1497 #if ALIB_CHARACTERS_WIDE
1498 realPath.Reset(nRealPath);
1499 #endif
1500 #else
1501 {
1502 std::error_code errorCode;
1503 fs::path fsRealPath= fs::canonical(fs::path(std::basic_string_view<PathCharType>(path.Buffer(),
1504 size_t(path.Length()))),
1505 errorCode);
1506 ALIB_DBG(if(errno==EINVAL && !errorCode) errno= 0;) // this happens!, we do not care, but clean up
1507 ALIB_DBG(if(errno==ENOENT && !errorCode) errno= 0;)
1508
1510 if(errorCode) switch( SystemErrors(errorCode.value()) )
1511 { // we ignore this: std::fs would not create the "real path" if the final directory is not accessible.
1512 case SystemErrors::eacces: return FInfo::Qualities::NO_ACCESS;
1513 case SystemErrors::enoent: return FInfo::Qualities::NOT_EXISTENT;
1514 case SystemErrors::eloop: return FInfo::Qualities::CIRCULAR_LINK;
1515 default: ALIB_ERROR("FILES", "std::filesystem raised ({}) \"{}\" on resolving start path \"{}\"",
1516 errorCode.value(), errorCode.message(), path ) ALIB_DBG(errno= 0;)
1518 }
1520 realPath << fsRealPath.c_str();
1521 }
1522 #endif
1523
1524 Log_Info( "Scanning: P= {}\n"
1525 " RP= {}\n"
1526 " F={} DPre={} DPost={} XFS={} AFS={} Depth={}",
1527 parameters.StartPath, realPath,
1528 parameters.FileFilter .get() ? 'Y':'N',
1529 parameters.DirectoryFilterPreRecursion .get() ? 'Y':'N',
1530 parameters.DirectoryFilterPostRecursion.get() ? 'Y':'N',
1531 parameters.CrossFileSystems ? 'Y':'N', parameters.IncludeArtificialFS ? 'Y':'N',
1533 : String128(parameters.MaxDepth)
1534 )
1535
1536 //-------------------------------------- start scanning --------------------------------------
1537 ALIB_DBG( errno=0; )
1538 auto firstResultPos= resultPaths.size();
1539 FInfo::DirectorySums dummySums;
1540
1541 startScan( tree, realPath, parameters, dummySums, resultPaths IF_ALIB_THREADS( , lock) );
1542
1543 Log_Info( "Scan Results: ", resultPaths.size() - firstResultPos )
1544 Log_Prune( int cntPaths= 0;
1545 for( auto& it : resultPaths )
1546 {
1547 Log_Info( " Path {}: {} {} (Q={} D={}/F={}}",
1548 cntPaths++, it.Existed ? ' ' : '+',
1549 it.RealPath,
1550 it.Node->Quality(),
1551 it.Node->Quality() > FInfo::Qualities::STATS && it.Node->IsDirectory() ? it.Node.Value().Sums().CountDirectories() : 0,
1552 it.Node->Quality() > FInfo::Qualities::STATS && it.Node->IsDirectory() ? it.Node.Value().Sums().CountNonDirectories(): 0 )
1553 } )
1554
1555
1556
1557 return (*(resultPaths.begin() + int(firstResultPos))).Node->Quality();
1558}
1559
1560#undef DBG_CHECKERRNO
1561
1562} // namespace [alib::files]
1563
1564# include "ALib.Lang.CIMethods.H"
The entry type which is embedded in each tree node.
Definition finfo.inl:15
@ DIRECTORY
Directory/folder.
Definition finfo.inl:26
@ CHARACTER
A character special file.
Definition finfo.inl:33
@ BLOCK
A block special file.
Definition finfo.inl:32
@ SOCKET
A socket file.
Definition finfo.inl:35
@ REGULAR
Regular file.
Definition finfo.inl:29
@ FIFO
A FIFO (also known as pipe) file.
Definition finfo.inl:34
Qualities
Per-entry information about how a node was scanned.
Definition finfo.inl:123
@ RECURSIVE
Follow symlink target strings.
Definition finfo.inl:134
@ STATS
Only stats (size, date, owner, etc.) read.
Definition finfo.inl:125
@ NO_ACCESS_DIR
Scanner failure due to limited access rights.
Definition finfo.inl:139
@ MAX_DEPTH_REACHED
Scanner stopped, because maximum depth was reached.
Definition finfo.inl:129
@ RESOLVED
Read symlink target strings.
Definition finfo.inl:127
@ NO_ACCESS_SL
Scanner failure due to limited access rights.
Definition finfo.inl:137
@ UNKNOWN_ERROR
Unknown scanner failure.
Definition finfo.inl:148
@ BROKEN_LINK
A symbolic link targets a non-existent file or directory.
Definition finfo.inl:140
@ NO_ACCESS_SL_TARGET
Scanner failure due to limited access rights.
Definition finfo.inl:138
@ NO_ACCESS
Scanner failure due to limited access rights.
Definition finfo.inl:136
@ NOT_EXISTENT
Set if a given start path does not exist.
Definition finfo.inl:147
constexpr Qualities Quality() const noexcept
Definition finfo.inl:390
Permissions
Permission flags. Compatible with posix* definition.
Definition finfo.inl:89
@ NONE
no permission bits are set
Definition finfo.inl:90
void AllocateExtendedInfo(Cursor &node, const system::PathString &symLinkDest, const system::PathString &symLinkRealPath)
Definition ftree.inl:263
static ALIB_DLL void FixSums(Cursor directory)
Definition ftree.cpp:272
FTree & GetFTree() const
Definition ftree.inl:693
Cursor & AsCursor()
Definition ftree.inl:737
constexpr const TChar * Terminate() const
Definition tastring.inl:682
TAString & DeleteEnd(integer regionLength)
TAString & ShortenTo(integer newLength)
Definition tastring.inl:816
void SetLength(integer newLength)
Definition tastring.inl:771
constexpr integer Length() const
Definition string.inl:318
constexpr bool IsEmpty() const
Definition string.inl:367
TChar CharAtStart() const
Definition string.inl:440
integer IndexOf(TChar needle, integer startIdx=0) const
Definition string.inl:844
TChar CharAt(integer idx) const
Definition string.inl:421
constexpr bool IsNotEmpty() const
Definition string.inl:371
constexpr const TChar * Buffer() const
Definition string.inl:313
integer LastIndexOf(TChar needle, integer startIndex=MAX_LEN) const
Definition string.inl:967
TChar CharAtEnd() const
Definition string.inl:460
TString< TChar > Substring(integer regionStart, integer regionLength=MAX_LEN) const
Definition string.inl:386
int IsAbsolute() const
Definition path.inl:310
ALIB_DLL void Release(ALIB_DBG_TAKE_CI)
Definition locks.cpp:468
ALIB_DLL void ReleaseShared(ALIB_DBG_TAKE_CI)
Definition locks.cpp:535
ALIB_DLL void AcquireShared(ALIB_DBG_TAKE_CI)
Definition locks.cpp:478
ALIB_DLL void Acquire(ALIB_DBG_TAKE_CI)
Definition locks.cpp:420
static DateTime FromEpochSeconds(time_t epochSeconds)
Definition datetime.inl:70
void Import(TTimePoint timePoint)
#define ALIB_MESSAGE(domain,...)
Definition alib.inl:1047
#define IF_ALIB_THREADS(...)
Definition alib.inl:401
#define A_CHAR(STR)
#define Log_IsActive(result,...)
#define ALIB_STRING_RESETTER(astring)
#define ALIB_WARNINGS_RESTORE
Definition alib.inl:705
#define ALIB_STRINGS_TO_NARROW( src, dest, bufSize)
#define ALIB_WARNING(domain,...)
Definition alib.inl:1046
#define Log_Prune(...)
#define ALIB_ASSERT_WARNING(cond, domain,...)
Definition alib.inl:1050
#define ALIB_ERROR(domain,...)
Definition alib.inl:1045
#define Log_Verbose(...)
#define ALIB_WARNINGS_ALLOW_SPARSE_ENUM_SWITCH
Definition alib.inl:637
#define Log_SetDomain(...)
#define ALIB_DBG(...)
Definition alib.inl:836
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1049
#define ALIB_CALLER_PRUNED
Definition alib.inl:1007
#define Log_Info(...)
#define ALIB_CHARACTERS_WIDE
Definition alib.inl:871
ALIB_DLL FInfo::Qualities ScanFiles(FTree &tree, ScanParameters &parameters, std::vector< ResultsPaths > &resultPaths, SharedLock *lock)
String DBG_FILES_SCAN_VERBOSE_LOG_FORMAT
strings::TCString< PathCharType > CPathString
The string-type used with this ALib Module.
Definition path.inl:36
strings::TString< PathCharType > PathString
The string-type used with this ALib Module.
Definition path.inl:33
strings::TSubstring< PathCharType > PathSubstring
The string-type used with this ALib Module.
Definition path.inl:39
constexpr PathCharType DIRECTORY_SEPARATOR
The standard path separator character. Defaults to '\' on Windows OS, '/' else.
Definition path.inl:63
threads::SharedLock SharedLock
Type alias in namespace alib.
files::File File
Type alias in namespace alib.
Definition ftree.inl:1036
LocalString< 128 > String128
Type alias name for TLocalString<character,128>.
time::DateTime DateTime
Type alias in namespace alib.
Definition datetime.inl:224
NLocalString< 8 > NString8
Type alias name for TLocalString<nchar,8>.
monomem::TLocalAllocator< TCapacityInKB > LocalAllocator
Type alias in namespace alib.
strings::TSubstring< nchar > NSubstring
Type alias in namespace alib.
strings::TCString< character > CString
Type alias in namespace alib.
Definition cstring.inl:503
lang::integer integer
Type alias in namespace alib.
Definition integers.inl:149
system::Path Path
Type alias in namespace alib.
Definition path.inl:392
strings::TCString< nchar > NCString
Type alias in namespace alib.
Definition cstring.inl:512
boxing::TBoxes< MonoAllocator > BoxesMA
Type alias in namespace alib.
Definition boxes.inl:245
files::FilesCamp FILES
The singleton instance of ALib Camp class FilesCamp.
Definition filescamp.cpp:47
strings::TString< character > String
Type alias in namespace alib.
Definition string.inl:2381
system::SystemErrors SystemErrors
Type alias in namespace alib.
lang::uinteger uinteger
Type alias in namespace alib.
Definition integers.inl:152
virtual bool Includes(const File &file, const system::PathString &parentPath)=0
Recursively accumulated values for directories.
Definition finfo.inl:185
uint32_t QtyErrsAccess
Number of access errors in the folder and subfolders.
Definition finfo.inl:188
uint32_t QtyErrsBrokenLink
Number of broken symbolic links in the directory and its subfolders.
Definition finfo.inl:189
constexpr DirectorySums & Add(const FInfo &finfo) noexcept
Definition finfo.inl:242
@ DeleteNode
A file or directory entry was deleted.
Definition ftree.inl:99
@ CreateNode
A file or directory entry was created.
Definition ftree.inl:98
Input parameters to function ScanFiles.
Definition fscanner.inl:20
SPFileFilter DirectoryFilterPreRecursion
Definition fscanner.inl:99
static constexpr unsigned int InfiniteRecursion
Denotes 'infinite' recursion if set to field MaxDepth.
Definition fscanner.inl:34
SymbolicLinks LinkTreatment
Denotes how symbolic links are treated.
Definition fscanner.inl:40
unsigned int MaxDepth
The maximum recursion depth. Defaults to InfiniteRecursion.
Definition fscanner.inl:43
@ DONT_RESOLVE
Demands not to resolve symbolic links in any way.
Definition fscanner.inl:24
Path StartPath
The path to be scanned.
Definition fscanner.inl:37
SPFileFilter DirectoryFilterPostRecursion
Definition fscanner.inl:87