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"
20#if ALIB_FILES_SCANNER_IMPL == ALIB_FILES_SCANNER_POSIX
22# if defined(__linux__)
23# include <asm/unistd.h>
26# if defined(__linux__)
27# include <linux/stat.h>
30# if !defined(__APPLE__)
31# include <sys/sysmacros.h>
33# include <sys/types.h>
46 import ALib.Characters.Functions;
49 import ALib.Strings.Tokenizer;
52 import ALib.Expressions;
56 import ALib.ALox.Impl;
80bool startScan(
FTree& tree,
84 std::vector<ResultsPaths>& resultPaths
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}");
102#if ALIB_FILES_SCANNER_IMPL == ALIB_FILES_SCANNER_POSIX
105# define DBG_CHECKERRNO \
106 ALIB_ASSERT_WARNING(errno == 0, "FILES", "Errno set ({})\"{}\".", \
107 errno, SystemErrors(errno) ) \
109# define DBG_CHECKERRNO_WITH_PATH \
110 ALIB_ASSERT_WARNING(errno == 0, "FILES", "Errno set ({})\"{}\". Current path: {}", \
111 errno, SystemErrors(errno), actPath ) \
114# define DBG_CHECKERRNO
115# define DBG_CHECKERRNO_WITH_PATH
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
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)
132void scanFilePosix( DIR* pxDir,
134 const CPathString& nameOrFullPath,
137 uint64_t currentDevice,
140 std::vector<ResultsPaths>& resultPaths
144 || actPath.
CharAtEnd() != DIRECTORY_SEPARATOR )
145 && actPath.
IndexOf(strings::TLocalString<system::PathCharType,4>(
146 DIRECTORY_SEPARATOR).Append(DIRECTORY_SEPARATOR)) < 0 ,
147 "FILES",
"Given path not absolute or ending with '{}': {}",
148 DIRECTORY_SEPARATOR, actPath )
151 dbgActFile << nameOrFullPath;
153 dbgActFile << actPath;
154 if(dbgActFile.Length()>1)
155 dbgActFile << DIRECTORY_SEPARATOR;
156 dbgActFile << nameOrFullPath;
160 BoxesMA verboseLogables( verboseAllocator);
163 if( verboseLoggers ) {
164 verboseLogables.Add(
"{!AWidth:>} ");
165 if( ¶ms == ¶msPathOnly )
166 verboseLogables.Add(
"PO");
168 auto& depthString= *verboseAllocator().New<
String128>();
170 << ( params.
MaxDepth < std::numeric_limits<unsigned>::max()
173 verboseLogables.Add(depthString);
184auto oldQuality= value.Quality();
189if( value.Quality() ==
FInfo::Qualities::NONE
190 || ( value.Quality() ==
FInfo::Qualities::STATS
191 && params.LinkTreatment !=
ScanParameters::SymbolicLinks::DONT_RESOLVE ) )
195 Path symLinkDestReal;
199 #
if TMP_STATX_AVAILABLE
201 int statResult= statx( pxDir ? dirfd(pxDir) : 0,
203 AT_STATX_DONT_SYNC | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW,
204 STATX_BASIC_STATS | STATX_BTIME,
209 int statResult= pxDir ? fstatat(dirfd(pxDir), nNameOrFullPath, &stats,
211 #
if !defined(__APPLE__)
215 : lstat ( nNameOrFullPath, &stats );
218 ALIB_ASSERT_WARNING( errno != ENOENT,
"FILES",
"File does not exist (anymore) while stating \"{}\"",
220 ALIB_ASSERT_WARNING( errno == ENOENT,
"FILES",
"Unknown error ({}) \"{}\" while stating file \"{}\"",
225 goto APPLY_POST_RECURSION_FILTER;
227 DBG_CHECKERRNO_WITH_PATH
231 uint64_t device= (uint64_t(STAT_DEV_MAJOR) << 32L) + STAT_DEV_MINOR;
232 if( currentDevice == 0) currentDevice= device;
233 else if( currentDevice != device) { currentDevice= device; value.SetCrossingFS(); }
236 if( STAT_DEV_MAJOR == 0
237 && STAT_DEV_MINOR != 35 )
238 value.SetArtificialFS();
241 bool origFileIsSymlink= (STATMEMBER(mode) & S_IFMT) == S_IFLNK;
242 if( origFileIsSymlink
245 value.SetQuality( FInfo::Qualities::RESOLVED );
248 ALIB_STRINGS_TO_NARROW(symLinkDest, nSymLinkDest, 512)
249 ssize_t cntChars= pxDir ? readlinkat( dirfd(pxDir), nNameOrFullPath, nSymLinkDest.VBuffer(), PATH_MAX)
250 : readlink ( nNameOrFullPath, nSymLinkDest.VBuffer(), PATH_MAX);
252 if (cntChars == -1) switch(errno) {
253 case EACCES: value.SetQuality(FInfo::Qualities::NO_ACCESS_SL); ALIB_DBG(errno= 0;)
256 case ENOENT: value.SetQuality(FInfo::Qualities::NO_ACCESS_SL);
257 ALIB_ASSERT_ERROR(STAT_DEV_MAJOR == 0,
"FILES",
258 "Posix raised ({}) \"{}\" on reading a symbolic link which is not located on "
259 "an artificial filesystem (like /proc). File:\"{}\"",
260 errno, SystemErrors(errno), dbgActFile ) ALIB_DBG(errno= 0;)
263 default: value.SetQuality(FInfo::Qualities::UNKNOWN_ERROR);
264 ALIB_ERROR(
"FILES",
"Posix raised ({}) \"{}\" on reading symbolic link \"{}\"",
265 errno, SystemErrors(errno), dbgActFile ) ALIB_DBG(errno= 0;)
268 nSymLinkDest.SetLength(cntChars);
270 symLinkDest.
Reset(nSymLinkDest);
276 actPath << DIRECTORY_SEPARATOR << nameOrFullPath;
280 *nSymLinkDestReal.VBuffer()=
'\0';
281 if(! realpath(nActPath.Terminate(), nSymLinkDestReal.VBuffer() ) ) switch (errno)
283 case ENOENT:
if( *nSymLinkDestReal.VBuffer() !=
'\0')
292 default:
ALIB_ERROR(
"FILES",
"Posix raised ({}) \"{}\" on resolving symbolic link \"{}\"",
296 nSymLinkDestReal.DetectLength();
298 ALIB_DBG(
if( errno == EINVAL) errno= 0;)
299 DBG_CHECKERRNO_WITH_PATH
301 "Real path is not absolute: ", nSymLinkDestReal )
304 DBG_CHECKERRNO_WITH_PATH
305 #if TMP_STATX_AVAILABLE
306 statResult= statx( 0,
307 nSymLinkDestReal.Terminate(),
308 AT_STATX_DONT_SYNC | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW,
312 statResult= stat(nSymLinkDestReal.Terminate(), &stats );
314 DBG_CHECKERRNO_WITH_PATH
315 #if ALIB_CHARACTERS_WIDE
316 symLinkDestReal.Reset(nSymLinkDestReal);
319 if(statResult == -1 ) {
324 goto APPLY_POST_RECURSION_FILTER;
326 "Unhandled error code invoking 'stat()' on resolved symbolic "
327 "link: {} (\"{}\")\n Symbolic link target: \"{}\"",
331 goto APPLY_POST_RECURSION_FILTER;
337 if( STAT_DEV_MAJOR == 0
338 && STAT_DEV_MINOR != 35 )
339 value.SetTargetArtificialFS();
344 DBG_CHECKERRNO_WITH_PATH
352 auto posixType= STATMEMBER(mode) & S_IFMT;
353 if( origFileIsSymlink ) {
357 else switch(STATMEMBER(mode) & S_IFMT ) {
360 case S_IFBLK : type=
FInfo::Types::BLOCK ; break;
361 case S_IFCHR : type=
FInfo::Types::CHARACTER ; break;
362 case S_IFDIR : type=
FInfo::Types::DIRECTORY ; break;
363 case S_IFIFO : type=
FInfo::Types::FIFO ; break;
364 case S_IFREG : type=
FInfo::Types::REGULAR ; break;
365 case S_IFSOCK: type=
FInfo::Types::SOCKET ; break;
367 "Internal error. 'unknown' file type can't happen. File: \"{}\"",
370 value.SetType( type );
374 value.SetPerms(
FInfo::Permissions(STATMEMBER(mode) & int32_t(
FInfo::Permissions::MASK)) );
378 #if defined(__APPLE__)
379 # define st_mtime_name STATMEMBER(mtimespec)
380 # define st_ctime_name STATMEMBER(ctimespec)
381 # define st_atime_name STATMEMBER(atimespec)
383 # if TMP_STATX_AVAILABLE
384 # define st_mtime_name STATMEMBER(mtime)
385 # define st_ctime_name STATMEMBER(ctime)
386 # define st_atime_name STATMEMBER(atime)
387 # define st_btime_name STATMEMBER(btime)
389 # define st_mtime_name STATMEMBER(mtim)
390 # define st_ctime_name STATMEMBER(ctim)
391 # define st_atime_name STATMEMBER(atim)
396 std::chrono::system_clock::time_point {
397 std::chrono::duration_cast<std::chrono::system_clock::duration>(
398 std::chrono::seconds {st_mtime_name.tv_sec }
399 + std::chrono::nanoseconds{st_mtime_name.tv_nsec} ) } );
403 std::chrono::system_clock::time_point {
404 std::chrono::duration_cast<std::chrono::system_clock::duration>(
405 std::chrono::seconds {st_ctime_name.tv_sec }
406 + std::chrono::nanoseconds{st_ctime_name.tv_nsec} ) } );
410 std::chrono::system_clock::time_point {
411 std::chrono::duration_cast<std::chrono::system_clock::duration>(
412 std::chrono::seconds {st_atime_name.tv_sec }
413 + std::chrono::nanoseconds{st_atime_name.tv_nsec} ) } );
416 #if TMP_STATX_AVAILABLE
417 if( STATMEMBER(mask) & STATX_BTIME ) {
419 std::chrono::system_clock::time_point {
420 std::chrono::duration_cast<std::chrono::system_clock::duration>(
421 std::chrono::seconds {st_btime_name.tv_sec }
422 + std::chrono::nanoseconds{st_btime_name.tv_nsec} ) } );
426 auto btime= value.MDate();
427 if( btime > value.CDate() ) btime= value.CDate();
428 if( btime > value.ADate() ) btime= value.ADate();
429 value.SetBDate( btime );
434 auto btime= value.MDate();
435 if( btime > value.CDate() ) btime= value.CDate();
436 if( btime > value.ADate() ) btime= value.ADate();
437 value.SetBDate( btime );
447 value.SetSize(
uinteger(STATMEMBER(size) ) );
450 value.SetOwner( STATMEMBER(uid) );
451 value.SetGroup( STATMEMBER(gid) );
454 value.SetQtyHardlinks( STATMEMBER(nlink) );
458 && (value.IsDirectory() || symLinkDest.
IsNotEmpty()) ) {
466DBG_CHECKERRNO_WITH_PATH
471 goto APPLY_POST_RECURSION_FILTER;
477if( !value.IsDirectory()
479 goto APPLY_POST_RECURSION_FILTER;
483 Log_Prune(
if( verboseLogables.Size() ) verboseLogables.Add(
" NO_AFS"); )
484 value.SetQuality(
FInfo::Qualities::NO_AFS );
485 goto APPLY_POST_RECURSION_FILTER;
489if( value.IsCrossingFS() && !params.CrossFileSystems ) {
490 Log_Prune(
if( verboseLogables.Size() ) verboseLogables.Add(
" NOT_CROSSING_FS"); )
491 value.SetQuality(
FInfo::Qualities::NOT_CROSSING_FS );
492 goto APPLY_POST_RECURSION_FILTER;
496if( depth >= params.MaxDepth ) {
497 Log_Prune(
if( verboseLogables.Size() && (¶ms != ¶msPathOnly) ) verboseLogables.Add(
" MAX_DEPTH_REACHED"); )
498 value.SetQuality(
FInfo::Qualities::MAX_DEPTH_REACHED );
499 ++parentSums.QtyStopsOnMaxDepth;
500 goto APPLY_POST_RECURSION_FILTER;
505 && params.DirectoryFilterPreRecursion
506 && !params.DirectoryFilterPreRecursion->Includes( node, actPath ) )
508 Log_Prune(
if( verboseLogables.Size() ) verboseLogables.Add(
" FILTERED(Pre)"); )
509 goto APPLY_POST_RECURSION_FILTER;
513value.SetQuality(
FInfo::Qualities::RECURSIVE );
516if ( value.Type() ==
FInfo::Types::SYMBOLIC_LINK_DIR ) {
518 || value.IsArtificialFS() )
520 value.SetQuality( FInfo::Qualities::NOT_FOLLOWED );
521 goto APPLY_POST_RECURSION_FILTER;
525 value.SetQuality( FInfo::Qualities::NO_AFS );
526 goto APPLY_POST_RECURSION_FILTER;
531 if( startScan( node.Tree<
FTree>(), value.GetRealLinkTarget(), params, childSums,
534 value.SetSums( childSums );
535 parentSums+= childSums;
537 goto APPLY_POST_RECURSION_FILTER;
542 if( pxDir ==
nullptr ) {
544 actPath.SetLength(nameOrFullPath.Length());
546 if( actPath.Length() > 1 ) actPath << DIRECTORY_SEPARATOR;
547 actPath << nameOrFullPath;
553 fd= openat( dirfd(pxDir), nNameOrFullPath, O_RDONLY | O_DIRECTORY );
556 fd= open( nActPath , O_RDONLY | O_DIRECTORY );
560 DBG_CHECKERRNO_WITH_PATH
562 DIR* childDir = fdopendir(fd);
565 dirent* pxEntry = readdir(childDir);
566 if( pxEntry ==
nullptr ) {
577 "Posix raised ({}) \"{}\" on reading a directory which is not "
578 "located on an artificial filesystem (like /proc). File:\"{}\"",
582 ALIB_ERROR(
"FILES",
"Posix raised ({}) \"{}\" on reading "
583 "directory \"{}\"", errno,
SystemErrors(errno), dbgActFile )
591 if( pxEntry->d_name[0] ==
'.'
592 && ( pxEntry->d_name[1] ==
'\0'
593 || ( pxEntry->d_name[1] ==
'.'
594 && pxEntry->d_name[2] ==
'\0' ) ) )
598 auto childNode= node;
599#if ALIB_CHARACTERS_WIDE
600 Path childName(
const_cast<const char*
>(&pxEntry->d_name[0]));
602 const CString childName(
const_cast<const char*
>(&pxEntry->d_name[0]));
605 childNode.GoToCreateChildIfNotExistent( childName );
607 scanFilePosix( childDir, childNode, childName,
608 depth + 1, params, currentDevice, subSums, actPath,
612 DBG_CHECKERRNO_WITH_PATH
617 parentSums+= value.Sums();
619 value.SetSums(subSums);
620 parentSums+= subSums;
623 goto APPLY_POST_RECURSION_FILTER;
628 "Internal error opening directory. This must never happen")
633 case SystemErrors::eacces:
640 ALIB_ERROR(
"FILES",
"Unknown error {}(\"{}\") while opening directory \"{}\"",
649APPLY_POST_RECURSION_FILTER:
653 if ( value.IsDirectory() ) {
658 && value.Sums().Count() == 0 )
661 Log_Prune(
if( verboseLogables.Size() ) { verboseLogables.Add(
" FILTERED(Post)");
662 Log_Verbose( verboseLogables )
663 verboseLogables.clear(); } )
664 parentSums-= value.Sums();
665 if( params.RemoveEmptyDirectories ) {
676 auto it= node.FirstChild();
677 while ( it.IsValid() ) {
683 it.GoToNextSibling();
687 node.DeleteChildren();
695 Log_Prune(
if( verboseLogables.Size() ) { verboseLogables.Add(
" FILTERED(Post)");
696 Log_Verbose( verboseLogables ) } )
709parentSums.
Add(value);
714DBG_CHECKERRNO_WITH_PATH
718#undef DBG_CHECKERRNO_WITH_PATH
719#undef TMP_STATX_AVAILABLE
727#if ALIB_FILES_FORCE_STD_SCANNER
728# pragma message ("ALIB_FILES_FORCE_STD_SCANNER given. Using std::filesystem for scanning. In file: " __FILE__ )
730# pragma message ("Unknown Platform. Using std::filesystem for scanning. In file: " __FILE__ )
733namespace fs = std::filesystem;
736#if defined(__APPLE__) || defined(_LIBCPP_VERSION) || defined(__ANDROID_NDK__)
739 template <
typename TP>
740 std::time_t to_time_t(TP tp)
742 auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(tp - TP::clock::now()
743 + std::chrono::system_clock::now());
744 return std::chrono::system_clock::to_time_t(sctp);
749# define DBG_CHECKERRNO \
750 ALIB_ASSERT_WARNING(errno == 0, "FILES", "Errno set ({})\"{}\".", \
751 errno, SystemErrors(errno) ) \
753# define DBG_CHECKERRNO_WITH_PATH \
754 ALIB_ASSERT_WARNING(errno == 0, "FILES", "Errno set ({})\"{}\". Current path: {}", \
755 errno, SystemErrors(errno), path.string() ) \
758# define DBG_CHECKERRNO
759# define DBG_CHECKERRNO_WITH_PATH
762namespace alib::files {
namespace {
764void scanFileStdFS(
const fs::path& path,
769 std::vector<ResultsPaths>& resultPaths
772#if defined(__MINGW32__)
773Path pathAsCString(path.c_str());
774pathAsCString.Terminate();
778const PathSubstring parentPath= pathAsCString.
Substring(0, pathAsCString.LastIndexOf(DIRECTORY_SEPARATOR));
782 && ( pathAsCString.Length()==1
783 || pathAsCString.CharAtEnd() != DIRECTORY_SEPARATOR)
784 && pathAsCString.IndexOf(
NString8(DIRECTORY_SEPARATOR).Append(DIRECTORY_SEPARATOR))<0,
785 "FILES",
"Given path not absolute or ending with '{}': {}", DIRECTORY_SEPARATOR, pathAsCString )
788 && pathAsCString.CharAt(2)== DIRECTORY_SEPARATOR
789 && ( pathAsCString.Length()==3
790 || pathAsCString.CharAtEnd() != DIRECTORY_SEPARATOR) )
792 || ( pathAsCString.CharAt(0)== DIRECTORY_SEPARATOR
793 && pathAsCString.CharAt(1)== DIRECTORY_SEPARATOR
794 && ( pathAsCString.Length()==2
795 || pathAsCString.CharAtEnd() != DIRECTORY_SEPARATOR) )
797 && pathAsCString.IndexOf( strings::TLocalString<PathCharType, 8>(
798 DIRECTORY_SEPARATOR).Append(DIRECTORY_SEPARATOR),
800 "FILES",
"Given path not absolute or ending with '{}': {}", DIRECTORY_SEPARATOR, pathAsCString )
804Log_Verbose(
"[{}] {}/{} {}", ¶ms != ¶msPathOnly ?
'>':
'P', depth,
805 params.
MaxDepth < (std::numeric_limits<unsigned>::max)()
810std::error_code errorCode;
811auto& value = node.Value();
812auto oldQuality= value.Quality();
821 Path symLinkDestReal;
824 fs::file_status stats= fs::symlink_status(path);
829 "Unhandled error code invoking 'fs::symlink_status()': {} (\"{}\")\n"
830 " With file: \"{}\"",
831 errorCode.value(), errorCode.message(), pathAsCString )
834 goto APPLY_POST_RECURSION_FILTER;
840 bool origFileIsSymlink= (stats.type() == fs::file_type::symlink);
841 if( origFileIsSymlink
847 fs::path resolved= fs::read_symlink(path, errorCode);
852 {
case SystemErrors::enoent:
857 "Unhandled error code invoking 'fs::read_symlink()': {} (\"{}\")\n"
858 " with file: ", errorCode.value(),
859 errorCode.message(), pathAsCString )
862 goto APPLY_POST_RECURSION_FILTER;
866 DBG_CHECKERRNO_WITH_PATH
867 symLinkDest << resolved.c_str();
871 if( resolved.is_absolute() )
872 realPath= fs::canonical(resolved, errorCode);
875 symLinkDestReal << pathAsCString;
877 symLinkDestReal << symLinkDest;
878 realPath= fs::canonical(fs::path(
879 std::basic_string_view<PathCharType>(symLinkDestReal.
Buffer(),
880 size_t(symLinkDestReal.
Length()))),
882 symLinkDestReal.
Reset();
884 ALIB_DBG(
if(errno==EINVAL && !errorCode) errno= 0;)
885 ALIB_DBG(
if(errno==ENOENT && !errorCode) errno= 0;)
893 default:
ALIB_ERROR(
"FILES",
"Unhandled error code invoking 'fs::canonical()': {} (\"{}\")\n"
894 " with file: ", errorCode.value(), errorCode.message(), pathAsCString )
898 DBG_CHECKERRNO_WITH_PATH
899 symLinkDestReal << realPath.c_str();
903 auto newStatus= fs::status(path, errorCode);
907 if(newStatus.type() != fs::file_type::unknown)
915 "Unhandled error code invoking 'directory_entry::status()': {} (\"{}\")\n"
916 " With file: \"{}\"",
917 errorCode.value(), errorCode.message(), pathAsCString )
927 DBG_CHECKERRNO_WITH_PATH
935 if( origFileIsSymlink )
940 else switch( stats.type() )
950 case fs::file_type::not_found:
952 ALIB_WARNING(
"FILES",
"Internal error. 'not found' file type can't happen. File: ", pathAsCString )
953 ALIB_DBG( errno= 0;)
goto APPLY_POST_RECURSION_FILTER;
954 case fs::file_type::none :
956 ALIB_WARNING(
"FILES",
"Internal error. 'none' file type can't happen. File: ", pathAsCString)
957 ALIB_DBG( errno= 0;)
goto APPLY_POST_RECURSION_FILTER;
958 case fs::file_type::unknown :
960 ALIB_WARNING(
"FILES",
"Internal error. Can't happen. File: ", pathAsCString)
961 ALIB_DBG( errno= 0;)
goto APPLY_POST_RECURSION_FILTER;
964 ALIB_WARNING(
"FILES",
"Unknown fs::file_status::type '{}' with file {}.", stats.type(), pathAsCString)
965 ALIB_DBG( errno= 0;)
goto APPLY_POST_RECURSION_FILTER;
967 value.SetType( type );
977 auto fsTime= std::filesystem::file_time_type(std::filesystem::file_time_type::clock::now());
980 fsTime= fs::last_write_time( path, errorCode );
984 case SystemErrors::enoent:
ALIB_ERROR(
"FILES",
985 "Internal error. This should never happen, checked above. "
986 "Undefined system error handling" )
ALIB_DBG( errno= 0;)
987 value.SetQuality(
FInfo::Qualities::UNKNOWN_ERROR);
991 "Unhandled error code invoking 'fs::last_write_time()': {} (\"{}\")\n"
992 " With file \"{}\".", errorCode.value(), errorCode.message(),
994 fsTime= (decltype(fsTime)::min)();
ALIB_DBG( errno= 0;)
1001 #if defined(__APPLE__) || defined(_LIBCPP_VERSION) || defined(__ANDROID_NDK__)
1005 std::chrono::clock_cast<std::chrono::system_clock>(fsTime) ) ) );
1007 value.SetBDate( value.MDate() );
1008 value.SetCDate( value.MDate() );
1009 value.SetADate( value.MDate() );
1013 value.SetSize( symLinkDest.Length() > 0 ?
uinteger(symLinkDest.Length())
1023 case SystemErrors::eisdir:
1026 case SystemErrors::enoent:
1029 "Internal error. This should never happen. Undefined system error handling" )
1035 "Unhandled error code invoking 'directory_entry::file_size()':{} (\"{}\")\n"
1036 " With file \"{}\".",
1037 errorCode.value(), errorCode.message(), pathAsCString )
ALIB_DBG( errno= 0;)
1044 value.SetOwner(
FInfo::UnknownID );
1045 value.SetGroup(
FInfo::UnknownID );
1048 uint32_t qtyHardLinks= uint32_t( fs::hard_link_count(path, errorCode ) );
1053 "Unhandled error code invoking 'fs::hard_link_count()': {} (\"{}\")\n"
1054 " With file: \"{}\"",
1055 errorCode.value(), errorCode.message(), pathAsCString )
1059 value.SetQtyHardlinks( qtyHardLinks );
1063 && (value.IsDirectory() || symLinkDest.IsNotEmpty()) ) {
1071DBG_CHECKERRNO_WITH_PATH
1076 ++parentSums.QtyErrsBrokenLink;
1077 goto APPLY_POST_RECURSION_FILTER;
1081if( !value.IsDirectory()
1083 goto APPLY_POST_RECURSION_FILTER;
1092 value.SetQuality( FInfo::Qualities::NOT_CROSSING_FS );
1093 goto APPLY_POST_RECURSION_FILTER;
1099 value.SetQuality( FInfo::Qualities::MAX_DEPTH_REACHED );
1100 ++parentSums.QtyStopsOnMaxDepth;
1101 goto APPLY_POST_RECURSION_FILTER;
1108 goto APPLY_POST_RECURSION_FILTER;
1116 if( params.LinkTreatment != ScanParameters::SymbolicLinks::RECURSIVE
1117 || value.IsArtificialFS() )
1119 value.SetQuality( FInfo::Qualities::NOT_FOLLOWED );
1120 goto APPLY_POST_RECURSION_FILTER;
1125 FInfo::DirectorySums childSums;
1126 if( startScan( File(node).GetFTree(), value.GetRealLinkTarget(), params, childSums,
1127 resultPaths IF_ALIB_THREADS(,lock) ) )
1128 value.SetQuality(FInfo::Qualities::DUPLICATE);
1129 value.SetSums( childSums );
1130 parentSums+= childSums;
1131 goto APPLY_POST_RECURSION_FILTER;
1137 fs::directory_iterator dit= fs::directory_iterator(path, errorCode);
1141 for(
const fs::directory_entry& childDir : dit )
1145 Path mingwBuf( childDir.path().c_str());
1150 childName.ConsumeChars(childName.LastIndexOf(DIRECTORY_SEPARATOR) + 1);
1151 auto childNode= node;
1153 childNode.GoToCreateChildIfNotExistent( childName );
1155 scanFileStdFS( childDir.path(), childNode, depth + 1, params, subSums,
1163 parentSums+= value.Sums();
1167 value.SetSums(subSums);
1168 parentSums+= subSums;
1171 goto APPLY_POST_RECURSION_FILTER;
1177 "Internal error opening directory. This must never happen" )
1182 case SystemErrors::einval:
1186 goto APPLY_POST_RECURSION_FILTER;
1189 ALIB_ERROR(
"FILES",
"Unknown error {}(\"{}\") while opening directory \"{}\"",
1190 errorCode.value(),
SystemErrors(errorCode.value()), pathAsCString)
1192 goto APPLY_POST_RECURSION_FILTER;
1198APPLY_POST_RECURSION_FILTER:
1201if( oldQuality ==
FInfo::Qualities::
NONE )
1203 if ( value.IsDirectory() )
1206 && ( ( params.DirectoryFilterPostRecursion
1207 && !params.DirectoryFilterPostRecursion->Includes(node, parentPath ) )
1208 || ( params.RemoveEmptyDirectories
1209 && value.Sums().Count() == 0 )
1212 parentSums-= value.Sums();
1213 if( params.RemoveEmptyDirectories )
1216 node.Tree<FTree>().Notify( FTreeListener::Event::DeleteNode,
1218 IF_ALIB_THREADS( lock, )
1225 auto it= node.FirstChild();
1226 while ( it.IsValid() )
1229 node.Tree<FTree>().Notify( FTreeListener::Event::DeleteNode,
1231 IF_ALIB_THREADS( lock, )
1233 it.GoToNextSibling();
1237 node.DeleteChildren();
1255parentSums.
Add(value);
1264#undef DBG_CHECKERRNO_WITH_PATH
1270namespace alib::files {
1275bool startScan(
FTree& tree,
1276 PathString realPath,
1279 std::vector<ResultsPaths>& resultPaths
1285Path path(DIRECTORY_SEPARATOR);
1295if(realPath.
CharAt(1) ==
':') {
1298 node.GoToCreateChildIfNotExistent(realPath.
Substring(0,2));
1299 pathRemainder= node.GoTo( realPath.
Substring(3) );
1303 integer serverNameEnd= realPath.
IndexOf( DIRECTORY_SEPARATOR, 2);
1304 if( serverNameEnd < 0)
1305 serverNameEnd= realPath.
Length();
1306 path << realPath.
Substring(0, serverNameEnd);
1308 node.GoToCreateChildIfNotExistent(realPath.
Substring(2, serverNameEnd - 2));
1309 pathRemainder= node.GoTo( realPath.
Substring(serverNameEnd + 1) );
1311 path << realPath.
Substring(serverNameEnd, realPath.
Length() - pathRemainder.
Length() -serverNameEnd );
1320if( pathRemainder.
IsEmpty() ) {
1322 if( node->IsDirectory()) {
1324 #if ALIB_FILES_SCANNER_IMPL == ALIB_FILES_SCANNER_POSIX
1328 scanFilePosix(
nullptr, node, fullPathChildName, 0, params, 0, parentSums, path,
1331 scanFileStdFS( fs::path(std::basic_string_view<PathCharType>(path.
Buffer(),
1346strings::util::TTokenizer<PathCharType> tknzr( pathRemainder, DIRECTORY_SEPARATOR );
1347while(tknzr.HasNext()) {
1349 if( path.
Length() != 1 ) {
1352 node= node.CreateChild(name);
1356 bool isLastPathElement= !tknzr.HasNext();
1357 if( isLastPathElement )
1361 auto detectNodeDeletion= node.Depth();
1364 #if ALIB_FILES_SCANNER_IMPL == ALIB_FILES_SCANNER_POSIX
1372 path << node.Name();
1374 fullPathChildName= path;
1377 scanFilePosix(
nullptr, node, fullPathChildName,
1378 0, isLastPathElement ? params : paramsPathOnly,
1380 if( fullPathChildName.
Length() == 1 ) path.
Reset();
1384 scanFileStdFS( fs::path(std::basic_string_view<PathCharType>(path.
Buffer(),
1387 isLastPathElement ? params : paramsPathOnly,
1393 if( isLastPathElement) {
1395 if (detectNodeDeletion == node.Depth() )
1396 resultPaths.insert(resultPaths.begin(),
ResultsPaths(realPath, node,
false));
1417 std::vector<ResultsPaths>& resultPaths
1434 #if ALIB_FILES_SCANNER_IMPL == ALIB_FILES_SCANNER_POSIX
1437 if(!realpath(nPath.Terminate(), nRealPath.VBuffer() ) ) switch (errno) {
1441 default:
ALIB_ERROR(
"FILES",
"Posix raised ({}) \"{}\" on resolving start path \"{}\"",
1445 nRealPath.DetectLength();
1446 #if ALIB_CHARACTERS_WIDE
1447 realPath.Reset(nRealPath);
1451 std::error_code errorCode;
1452 fs::path fsRealPath= fs::canonical(fs::path(std::basic_string_view<PathCharType>(path.
Buffer(),
1455 ALIB_DBG(
if(errno==EINVAL && !errorCode) errno= 0;)
1456 ALIB_DBG(
if(errno==ENOENT && !errorCode) errno= 0;)
1459 if(errorCode)
switch(
SystemErrors(errorCode.value()) )
1464 default:
ALIB_ERROR(
"FILES",
"std::filesystem raised ({}) \"{}\" on resolving start path \"{}\"",
1465 errorCode.value(), errorCode.message(), path )
ALIB_DBG(errno= 0;)
1469 realPath << fsRealPath.c_str();
1475 " F={} DPre={} DPost={} XFS={} AFS={} Depth={}",
1487 Log_Prune(auto firstResultPos= resultPaths.size(); ))
1488 FInfo::DirectorySums dummySums;
1490 startScan( tree, realPath, parameters, dummySums, resultPaths
IF_ALIB_THREADS( , lock) );
1492 Log_Info(
"Scan Results: ", resultPaths.size() - firstResultPos )
1494 for( auto& it : resultPaths ) {
1495 Log_Info(
" Path {}: {} {} (Q={} D={}/F={}}",
1496 cntPaths++, it.Existed ?
' ' :
'+',
1499 it.Node->Quality() >
FInfo::Qualities::STATS && it.Node->IsDirectory() ? it.Node.Value().Sums().CountDirectories() : 0,
1500 it.Node->Quality() >
FInfo::Qualities::STATS && it.Node->IsDirectory() ? it.Node.Value().Sums().CountNonDirectories(): 0 )
1503 return resultPaths.
size() > 0 ? resultPaths.back().Node->Quality()
1507#undef DBG_CHECKERRNO
The entry type which is embedded in each tree node.
@ DIRECTORY
Directory/folder.
@ CHARACTER
A character special file.
@ BLOCK
A block special file.
@ FIFO
A FIFO (also known as pipe) file.
uinteger size
The file size. In case of a directory, this is 0.
Qualities
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.
@ MAX_DEPTH_REACHED
Scanner stopped, because maximum depth was reached.
@ RESOLVED
Read symlink target strings.
@ NO_ACCESS_SL
Scanner failure due to limited access rights.
@ 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.
@ NO_ACCESS
Scanner failure due to limited access rights.
@ NOT_EXISTENT
Set if a given start path does not exist.
Permissions
Permission flags. Compatible with posix* definition.
@ NONE
no permission bits are set
void AllocateExtendedInfo(Cursor &node, const system::PathString &symLinkDest, const system::PathString &symLinkRealPath)
static ALIB_DLL void FixSums(Cursor directory)
constexpr const TChar * Terminate() const
TAString & DeleteEnd(integer regionLength)
TAString & ShortenTo(integer newLength)
integer DetectLength(integer offset=0)
void SetLength(integer newLength)
constexpr integer Length() const
constexpr bool IsEmpty() const
TChar CharAtStart() const
integer IndexOf(TChar needle, integer startIdx=0) const
TChar CharAt(integer idx) const
constexpr bool IsNotEmpty() const
constexpr const TChar * Buffer() const
integer LastIndexOf(TChar needle, integer startIndex=MAX_LEN) const
TString< TChar > Substring(integer regionStart, integer regionLength=MAX_LEN) const
ALIB_DLL void Release(ALIB_DBG_TAKE_CI)
ALIB_DLL void ReleaseShared(ALIB_DBG_TAKE_CI)
ALIB_DLL void AcquireShared(ALIB_DBG_TAKE_CI)
ALIB_DLL void Acquire(ALIB_DBG_TAKE_CI)
static DateTime FromEpochSeconds(time_t epochSeconds)
void Import(TTimePoint timePoint)
#define ALIB_MESSAGE(domain,...)
#define IF_ALIB_THREADS(...)
#define Log_IsActive(result,...)
#define ALIB_STRING_RESETTER(astring)
#define ALIB_WARNINGS_RESTORE
#define ALIB_STRINGS_TO_NARROW( src, dest, bufSize)
#define ALIB_WARNING(domain,...)
#define ALIB_ASSERT_WARNING(cond, domain,...)
#define ALIB_ERROR(domain,...)
#define ALIB_WARNINGS_ALLOW_SPARSE_ENUM_SWITCH
#define Log_SetDomain(...)
#define ALIB_ASSERT_ERROR(cond, domain,...)
#define ALIB_CALLER_PRUNED
#define ALIB_CHARACTERS_WIDE
ALIB_DLL FInfo::Qualities ScanFiles(FTree &tree, ScanParameters ¶meters, 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.
strings::TString< PathCharType > PathString
The string-type used with this ALib Module.
strings::TSubstring< PathCharType > PathSubstring
The string-type used with this ALib Module.
constexpr PathCharType DIRECTORY_SEPARATOR
The standard path separator character. Defaults to '\' on Windows OS, '/' else.
threads::SharedLock SharedLock
Type alias in namespace alib.
files::File File
Type alias in namespace alib.
LocalString< 128 > String128
Type alias name for TLocalString<character,128>.
time::DateTime DateTime
Type alias in namespace alib.
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.
lang::integer integer
Type alias in namespace alib.
system::Path Path
Type alias in namespace alib.
strings::TCString< nchar > NCString
Type alias in namespace alib.
boxing::TBoxes< MonoAllocator > BoxesMA
Type alias in namespace alib.
files::FilesCamp FILES
The singleton instance of ALib Camp class FilesCamp.
strings::TString< character > String
Type alias in namespace alib.
system::SystemErrors SystemErrors
Type alias in namespace alib.
lang::uinteger uinteger
Type alias in namespace alib.
virtual bool Includes(const File &file, const system::PathString &parentPath)=0
Recursively accumulated values for directories.
uint32_t QtyErrsAccess
Number of access errors in the folder and subfolders.
uint32_t QtyErrsBrokenLink
Number of broken symbolic links in the directory and its subfolders.
constexpr DirectorySums & Add(const FInfo &finfo) noexcept
@ DeleteNode
A file or directory entry was deleted.
@ CreateNode
A file or directory entry was created.
Input parameters to function ScanFiles.
unsigned MaxDepth
The maximum recursion depth. Defaults to InfiniteRecursion.
SPFileFilter DirectoryFilterPreRecursion
static constexpr unsigned InfiniteRecursion
Denotes 'infinite' recursion if set to field MaxDepth.
SymbolicLinks LinkTreatment
Denotes how symbolic links are treated.
@ DONT_RESOLVE
Demands not to resolve symbolic links in any way.
Path StartPath
The path to be scanned.
SPFileFilter DirectoryFilterPostRecursion
bool RemoveEmptyDirectories