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}");
33#if ALIB_SYSTEM_FILE_STATUS_IMPL == ALIB_SYSTEM_FILE_POSIX_STATUS
36# define DBG_CHECKERRNO_WITH_PATH \
37 ALIB_ASSERT_WARNING(errno == 0, "FILETREE", "Errno set ({})\"{}\". Current path: {}", \
38 errno, std::errc(errno), actPath ) \
41# define DBG_CHECKERRNO_WITH_PATH
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!" );
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
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)
63void scanFilePosix( DIR* pxDir,
69 bool curDevIsArtificial,
78 && actPath.
IndexOf(strings::TLocalString<PathCharType,4>(
80 "FILETREE",
"Given path \"{}\" not absolute or ending with '{}'",
86 dbgActFile << nameOrFullPath;
88 dbgActFile << actPath;
91 dbgActFile << nameOrFullPath;
95 BoxesMA verboseLogables( verboseAllocator);
98 if( verboseLoggers ) {
99 verboseLogables.Add(
"{!AWidth:>} ");
100 if( ¶ms == ¶msPathOnly )
101 verboseLogables.Add(
"PO");
103 auto& depthString= *verboseAllocator().New<
String128>();
105 << ( params.
MaxDepth < std::numeric_limits<unsigned>::max()
108 verboseLogables.Add(depthString);
116 errno, std::errc(errno),
NString(dbgActFile) )
129 strings::TLocalString<PathCharType, PATH_MAX> symLinkDest;
130 strings::TLocalString<PathCharType, PATH_MAX> symLinkDestReal;
134 #
if TMP_STATX_AVAILABLE
136 int statResult= statx( pxDir ? dirfd(pxDir) : AT_FDCWD,
138 AT_STATX_DONT_SYNC | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW,
139 STATX_BASIC_STATS | STATX_BTIME,
144 int statResult= pxDir ? fstatat(dirfd(pxDir), nameOrFullPath, &stats,
146 #
if !defined(__APPLE__)
150 : lstat ( nameOrFullPath, &stats );
154 "File does not exist (anymore) while stating \"{}\"",
156 ALIB_ASSERT_WARNING( errno == ENOENT,
"FILETREE",
"Unknown error ({}) \"{}\" while stating file \"{}\"",
157 errno, std::errc(errno),
NString(dbgActFile) )
161 goto APPLY_POST_RECURSION_FILTER;
163 DBG_CHECKERRNO_WITH_PATH
166 uint64_t device= (uint64_t(STAT_DEV_MAJOR) << 32L) + STAT_DEV_MINOR;
167 if( curDev == 0 || curDev != device ) {
171#if defined(__linux__)
172 struct statfs fsStats;
178 actPath << nameOrFullPath;
179 fsResult = statfs(actPath, &fsStats);
181 fsResult = statfs(nameOrFullPath, &fsStats);
186 switch(fsStats.f_type) {
187 case PROC_SUPER_MAGIC:
189 case CGROUP_SUPER_MAGIC:
190 case CGROUP2_SUPER_MAGIC:
192 case DEVPTS_SUPER_MAGIC:
194 case SECURITYFS_MAGIC:
201#elif defined(__APPLE__)
202 struct statfs fsStats;
208 actPath << nameOrFullPath;
209 fsResult = statfs(actPath.Terminate(), &fsStats);
211 fsResult = statfs(nameOrFullPath, &fsStats);
215 if (strcmp(fsStats.f_fstypename,
"devfs") == 0 ||
216 strcmp(fsStats.f_fstypename,
"autofs") == 0) {
221 if( STAT_DEV_MAJOR == 0
222 && STAT_DEV_MINOR != 35 )
231 bool origFileIsSymlink= (STATMEMBER(mode) & S_IFMT) == S_IFLNK;
232 if( origFileIsSymlink
235 value.SetScanState( FTValue::ScanStates::RESOLVED );
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);
242 if (cntChars == -1) switch(errno) {
243 case EACCES: value.SetScanState(FTValue::ScanStates::NO_ACCESS_SL); ALIB_DBG(errno= 0;)
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;)
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;)
266 *symLinkDestReal.
VBuffer()=
'\0';
267 if(! realpath(actPath, symLinkDestReal.
VBuffer() ) ) switch (errno)
269 case ENOENT:
if( *symLinkDestReal.
VBuffer() !=
'\0')
278 default:
ALIB_ERROR(
"FILETREE",
"Posix raised ({}) \"{}\" on resolving symbolic link \"{}\"",
284 ALIB_DBG(
if( errno == EINVAL) errno= 0;)
285 DBG_CHECKERRNO_WITH_PATH
287 "Real path is not absolute: ", symLinkDestReal )
290 DBG_CHECKERRNO_WITH_PATH
291 #if TMP_STATX_AVAILABLE
292 statResult= statx( AT_FDCWD,
294 AT_STATX_DONT_SYNC | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW,
298 statResult= stat(symLinkDestReal.
Terminate(), &stats );
300 DBG_CHECKERRNO_WITH_PATH
301 #if ALIB_CHARACTERS_WIDE
302 symLinkDestReal.
Reset(symLinkDestReal);
305 if(statResult == -1 ) {
307 if(errno)
switch( std::errc(errno) )
308 {
case std::errc::no_such_file_or_directory:
311 goto APPLY_POST_RECURSION_FILTER;
315 "Unhandled error code invoking 'stat()' on resolved symbolic "
316 "link: {} (\"{}\")\n Symbolic link target: \"{}\"",
317 errno, std::errc(errno),
NString(dbgActFile) )
320 goto APPLY_POST_RECURSION_FILTER;
326 uint64_t linkTargetDevice= (uint64_t(STAT_DEV_MAJOR) << 32L) + STAT_DEV_MINOR;
327 if( linkTargetDevice == curDev ) {
331#if defined(__linux__)
332 struct statfs fsStats;
333 if (statfs(symLinkDestReal, &fsStats) == 0) {
335 switch(fsStats.f_type) {
336 case PROC_SUPER_MAGIC:
338 case CGROUP_SUPER_MAGIC:
339 case CGROUP2_SUPER_MAGIC:
341 case DEVPTS_SUPER_MAGIC:
343 case SECURITYFS_MAGIC:
350#elif defined(__APPLE__)
351 struct statfs fsStats;
352 if (statfs(symLinkDestReal, &fsStats) == 0) {
354 if (strcmp(fsStats.f_fstypename,
"devfs") == 0 ||
355 strcmp(fsStats.f_fstypename,
"autofs") == 0) {
360 if( STAT_DEV_MAJOR == 0
361 && STAT_DEV_MINOR != 35 )
368 DBG_CHECKERRNO_WITH_PATH
376 auto posixType= STATMEMBER(mode) & S_IFMT;
377 if( origFileIsSymlink ) {
381 else switch(STATMEMBER(mode) & S_IFMT ) {
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;
391 "Internal error. 'unknown' file type can't happen. File: \"{}\"",
394 value.SetType( type );
398 value.SetPerms(
FileStatus::Permissions(STATMEMBER(mode) & int32_t(
FileStatus::Permissions::MASK)) );
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)
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)
413 # define st_mtime_name STATMEMBER(mtim)
414 # define st_ctime_name STATMEMBER(ctim)
415 # define st_atime_name STATMEMBER(atim)
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} ) } );
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} ) } );
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} ) } );
440 #if TMP_STATX_AVAILABLE
441 if( STATMEMBER(mask) & STATX_BTIME ) {
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} ) } );
450 auto btime= value.
MDate();
451 if( btime > value.
CDate() ) btime= value.
CDate();
452 if( btime > value.
ADate() ) btime= value.
ADate();
458 auto btime= value.
MDate();
459 if( btime > value.
CDate() ) btime= value.
CDate();
460 if( btime > value.
ADate() ) btime= value.
ADate();
488 DBG_CHECKERRNO_WITH_PATH
493 goto APPLY_POST_RECURSION_FILTER;
501 goto APPLY_POST_RECURSION_FILTER;
505 Log_Prune(
if( verboseLogables.Size() ) verboseLogables.Add(
" NO_AFS"); )
506 value.SetScanState(
FTValue::ScanStates::NO_AFS );
507 goto APPLY_POST_RECURSION_FILTER;
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;
518 if( depth >= params.MaxDepth ) {
519 Log_Prune(
if( verboseLogables.Size() && (¶ms != ¶msPathOnly) ) verboseLogables.Add(
" MAX_DEPTH_REACHED"); )
520 value.SetScanState(
FTValue::ScanStates::MAX_DEPTH_REACHED );
521 ++parentSums.QtyStopsOnMaxDepth;
522 goto APPLY_POST_RECURSION_FILTER;
527 && params.DirectoryFilterPreRecursion
528 && !params.DirectoryFilterPreRecursion->Includes( node, actPath ) )
530 Log_Prune(
if( verboseLogables.Size() ) verboseLogables.Add(
" FILTERED(Pre)"); )
531 goto APPLY_POST_RECURSION_FILTER;
535 value.SetScanState(
FTValue::ScanStates::RECURSIVE );
538 if ( value.Type() ==
FileStatus::Types::SYMBOLIC_LINK_DIR ) {
542 value.SetScanState( FTValue::ScanStates::NOT_FOLLOWED );
543 goto APPLY_POST_RECURSION_FILTER;
548 value.SetScanState( FTValue::ScanStates::NO_AFS );
549 goto APPLY_POST_RECURSION_FILTER;
554 realTargetPath._<NC>(actPath);
555 auto targetNode= node.Parent();
558 realTargetPath, resultPaths);
561 if(targetNode.IsInvalid()) {
562 node->SetScanState(FTValue::ScanStates::BROKEN_LINK);
563 goto APPLY_POST_RECURSION_FILTER;
568 FTFile(targetNode).SetSymbolicParent(node.Export());
574 CPathString cRealTarget= realTargetPath.Terminate();
575 FTValue::DirectorySums childSums;
576 scanFilePosix(nullptr, targetNode, cRealTarget, depth + 1, params,
577 curDev, curDevIsArtificial, parentSums,
578 realTargetPath, resultPaths );
580 value.SetSums(childSums);
581 parentSums+= childSums;
583 goto APPLY_POST_RECURSION_FILTER;
590 if( pxDir ==
nullptr ) {
591 ALIB_ASSERT_ERROR(actPath.Buffer() == nameOrFullPath.Buffer(),
"FILETREE",
"Internal error")
592 actPath.SetLength(nameOrFullPath.Length());
595 actPath << nameOrFullPath;
601 fd= openat( dirfd(pxDir), nameOrFullPath, O_RDONLY | O_DIRECTORY );
603 fd= open ( actPath , O_RDONLY | O_DIRECTORY );
607 DBG_CHECKERRNO_WITH_PATH
609 DIR* childDir = fdopendir(fd);
612 dirent* pxEntry = readdir(childDir);
613 if( pxEntry ==
nullptr ) {
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) )
630 "Posix raised ({}) \"{}\" on reading directory \"{}\"",
631 errno, std::errc(errno),
NString(dbgActFile) )
639 if( pxEntry->d_name[0] ==
'.'
640 && ( pxEntry->d_name[1] ==
'\0'
641 || ( pxEntry->d_name[1] ==
'.'
642 && pxEntry->d_name[2] ==
'\0' ) ) )
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,
658 DBG_CHECKERRNO_WITH_PATH
663 parentSums+= value.
Sums();
666 parentSums+= subSums;
669 goto APPLY_POST_RECURSION_FILTER;
674 "Internal error opening directory. This must never happen")
678 switch (
std::errc(errno)) {
679 case std::errc::permission_denied:
686 ALIB_ERROR(
"FILETREE",
"Unknown error {}(\"{}\") while opening directory \"{}\"",
687 errno, std::errc(errno), actPath)
695 APPLY_POST_RECURSION_FILTER:
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();
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);
726 node.Tree<
FTree>().Notify( FTreeListener::Event::DeleteNode, node, actPath );
732 node.DeleteChildren();
742 node.Tree<
FTree>().Notify( FTreeListener::Event::DeleteNode, node, actPath );
744 Log_Prune(
if( verboseLogables.Size() ) { verboseLogables.Add(
" FILTERED(Post)");
745 Log_Verbose( verboseLogables ) } )
753 parentSums.Add(value);
754 if(node.Tree<
FTree>().HasListeners()) {
757 node.Tree<
FTree>().Notify( FTreeListener::Event::CreateNode, node, actPath );
761 DBG_CHECKERRNO_WITH_PATH
765#undef DBG_CHECKERRNO_WITH_PATH
766#undef TMP_STATX_AVAILABLE
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) " )
778# pragma message ("Non-posix platform. Using std::filesystem for " \
779 "filetree::ScanFiles(). (Limited functionality) " )
784#if defined(__APPLE__) || defined(_LIBCPP_VERSION) || defined(__ANDROID_NDK__)
787 template <
typename TP>
788 std::time_t to_time_t(TP tp)
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);
797# define DBG_CHECKERRNO_WITH_PATH \
798 ALIB_ASSERT_WARNING(errno == 0, "FILETREE", "Errno set ({})\"{}\". Current path: {}", \
799 errno, std::errc(errno), path.string() ) \
802# define DBG_CHECKERRNO_WITH_PATH
805namespace alib::filetree {
namespace {
807void scanFileStdFS(
const fs::path& path,
814 #if defined(__MINGW32__)
815 Path pathAsCString(path.c_str());
816 pathAsCString.Terminate();
823 Log_Verbose(
"[{}] {}/{} {}", ¶ms != ¶msPathOnly ?
'>':
'P', depth,
824 params.
MaxDepth < (std::numeric_limits<unsigned>::max)()
829 std::error_code errorCode;
830 auto& value = node.Value();
840 Path symLinkDestReal;
843 fs::file_status stats= fs::symlink_status(path);
848 "Unhandled error code invoking 'fs::symlink_status()': {} (\"{}\")\n"
849 " With file: \"{}\"",
850 errorCode.value(), errorCode.message(), pathAsCString )
853 goto APPLY_POST_RECURSION_FILTER;
859 bool origFileIsSymlink= (stats.type() == fs::file_type::symlink);
860 if( origFileIsSymlink
866 fs::path resolved= fs::read_symlink(path, errorCode);
870 switch( std::errc(errorCode.value()) )
871 {
case std::errc::no_such_file_or_directory:
872 case std::errc::permission_denied:
878 "Unhandled error code invoking 'fs::read_symlink()': {} (\"{}\")\n"
879 " with file: ", errorCode.value(),
880 errorCode.message(), pathAsCString )
883 goto APPLY_POST_RECURSION_FILTER;
887 DBG_CHECKERRNO_WITH_PATH
888 symLinkDest << resolved.c_str();
892 if( resolved.is_absolute() )
893 realPath= fs::canonical(resolved, errorCode);
896 symLinkDestReal << pathAsCString;
898 symLinkDestReal << symLinkDest;
899 realPath= fs::canonical(fs::path(
900 std::basic_string_view<PathCharType>(symLinkDestReal.
Buffer(),
901 size_t(symLinkDestReal.
Length()))),
903 symLinkDestReal.
Reset();
905 ALIB_DBG(
if(errno==EINVAL && !errorCode) errno= 0;)
906 ALIB_DBG(
if(errno==ENOENT && !errorCode) errno= 0;)
909 if(errorCode)
switch( std::errc(errorCode.value()) )
914 default:
ALIB_ERROR(
"FILETREE",
"Unhandled error code invoking 'fs::canonical()': {} (\"{}\")\n"
915 " with file: ", errorCode.value(), errorCode.message(), pathAsCString )
919 DBG_CHECKERRNO_WITH_PATH
920 symLinkDestReal << realPath.c_str();
924 auto newStatus= fs::status(path, errorCode);
928 if(newStatus.type() != fs::file_type::unknown)
931 else switch( std::errc(errorCode.value()) )
936 "Unhandled error code invoking 'directory_entry::status()': {} (\"{}\")\n"
937 " With file: \"{}\"",
938 errorCode.value(), errorCode.message(), pathAsCString )
948 DBG_CHECKERRNO_WITH_PATH
956 if( origFileIsSymlink )
961 else switch( stats.type() )
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;
985 ALIB_WARNING(
"FILETREE",
"Unknown fs::file_status::type '{}' with file {}.", stats.type(), pathAsCString)
986 ALIB_DBG( errno= 0;)
goto APPLY_POST_RECURSION_FILTER;
998 auto fsTime= std::filesystem::file_time_type(std::filesystem::file_time_type::clock::now());
1001 fsTime= fs::last_write_time( path, errorCode );
1003 if(errorCode)
switch( std::errc(errorCode.value()) )
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);
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;)
1022 #if defined(__APPLE__) || defined(_LIBCPP_VERSION) || defined(__ANDROID_NDK__)
1026 std::chrono::clock_cast<std::chrono::system_clock>(fsTime) ) ) );
1028 value.SetBDate( value.MDate() );
1029 value.SetCDate( value.MDate() );
1030 value.SetADate( value.MDate() );
1034 value.SetSize( symLinkDest.Length() > 0 ?
uinteger(symLinkDest.Length())
1041 switch( std::errc(errorCode.value()) )
1044 case std::errc::is_a_directory:
1047 case std::errc::no_such_file_or_directory:
1050 "Internal error. This should never happen. Undefined system error handling" )
1054 case
std::errc::operation_not_supported: break;
1056 "Unhandled error code invoking 'directory_entry::file_size()':{} (\"{}\")\n"
1057 " With file \"{}\".",
1058 errorCode.value(), errorCode.message(), pathAsCString )
ALIB_DBG( errno= 0;)
1065 value.SetOwner(
FTValue::UnknownID );
1066 value.SetGroup(
FTValue::UnknownID );
1069 uint32_t qtyHardLinks= uint32_t( fs::hard_link_count(path, errorCode ) );
1075 if( !( std::errc(errorCode.value()) == std::errc::no_such_file_or_directory
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;)
1085 value.SetQtyHardlinks( qtyHardLinks );
1089 && (value.IsDirectory() || symLinkDest.IsNotEmpty()) )
1093 DBG_CHECKERRNO_WITH_PATH
1098 ++parentSums.QtyErrsBrokenLink;
1099 goto APPLY_POST_RECURSION_FILTER;
1103 if( !value.IsDirectory()
1105 goto APPLY_POST_RECURSION_FILTER;
1114 value.SetScanState( FTValue::ScanStates::NOT_CROSSING_FS );
1115 goto APPLY_POST_RECURSION_FILTER;
1121 value.SetScanState( FTValue::ScanStates::MAX_DEPTH_REACHED );
1122 ++parentSums.QtyStopsOnMaxDepth;
1123 goto APPLY_POST_RECURSION_FILTER;
1130 goto APPLY_POST_RECURSION_FILTER;
1139 if( params.LinkTreatment != ScanParameters::SymbolicLinks::RECURSIVE
1140 || value.IsArtificialFS() ) {
1141 value.SetScanState( FTValue::ScanStates::NOT_FOLLOWED );
1142 goto APPLY_POST_RECURSION_FILTER;
1146 Path realTargetPath= parentPath;
1147 auto targetNode= node.Parent();
1148 Path sourcePath= value.GetLinkTarget();
1150 realTargetPath, resultPaths);
1153 if(targetNode.IsInvalid()) {
1154 node->SetScanState(FTValue::ScanStates::BROKEN_LINK);
1155 goto APPLY_POST_RECURSION_FILTER;
1160 FTFile(targetNode).SetSymbolicParent(node.Export());
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 );
1171 value.SetSums(childSums);
1172 parentSums+= childSums;
1174 goto APPLY_POST_RECURSION_FILTER;
1179 fs::directory_iterator dit= fs::directory_iterator(path, errorCode);
1183 for(
const fs::directory_entry& childDir : dit )
1187 Path mingwBuf( childDir.path().c_str());
1193 auto childNode= node;
1194 childNode.GoToCreateChildIfNotExistent( childName );
1195 auto childCopy= childNode;
1196 scanFileStdFS( childDir.path(), childNode, depth + 1, params, subSums,
1209 parentSums+= value.Sums();
1213 value.SetSums(subSums);
1214 parentSums+= subSums;
1217 goto APPLY_POST_RECURSION_FILTER;
1223 "Internal error opening directory. This must never happen" )
1226 if(errorCode) switch (
std::errc(errorCode.value()))
1228 case std::errc::invalid_argument:
1229 case std::errc::permission_denied:
1233 goto APPLY_POST_RECURSION_FILTER;
1236 ALIB_ERROR(
"FILETREE",
"Unknown error {}(\"{}\") while opening directory \"{}\"",
1237 errorCode.value(), errorCode.message(), pathAsCString)
1239 goto APPLY_POST_RECURSION_FILTER;
1245 APPLY_POST_RECURSION_FILTER:
1249 if ( value.IsDirectory() ) {
1251 && ( ( params.DirectoryFilterPostRecursion
1252 && !params.DirectoryFilterPostRecursion->Includes(node, parentPath ) )
1253 || ( params.RemoveEmptyDirectories
1254 && value.Sums().Count() == 0 )
1257 parentSums-= value.Sums();
1258 value.Sums()= FTValue::DirectorySums();
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();
1272 if( params.RemoveEmptyDirectories )
1274 node.Tree<FTree>().Notify(FTreeListener::Event::DeleteNode, node, pathAsCString);
1281 node.DeleteChildren();
1286 node.Tree<
FTree>().Notify(FTreeListener::Event::DeleteNode, node, pathAsCString) ;
1294 parentSums.
Add(value);
1295 node.Tree<
FTree>().Notify( FTreeListener::Event::CreateNode, node, pathAsCString );
1302#undef DBG_CHECKERRNO_WITH_PATH
1308namespace alib::filetree {
1313struct UnrealPathRootInfo {
1320 return (c >=
'A' && c <=
'Z') || (c >=
'a' && c <=
'z');
1324 return isAsciiAlpha(c) || (c >=
'0' && c <=
'9') || c ==
'+' || c ==
'-' || c ==
'.';
1328 return c == cmp || c == (cmp ^ 0x20);
1331UnrealPathRootInfo detectAndNormalizeUnrealRoot(
Path& sourcePath,
1333 Path& pathToNode ) {
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) ==
'/') ) {
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) ==
'/') ) {
1361 }
else if( sourcePath.
IsEmpty() ) {
1365 info.prefixLength= 4;
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));
1381 info.prefixLength= schemeEnd + 3;
1382 info.scheme= sourcePath.
Substring(0, schemeEnd);
1391 if( sourcePath.
Length() >= 2
1392 && isAsciiAlpha(sourcePath.
CharAt(0))
1393 && sourcePath.
CharAt(1) ==
':' ) {
1396 if( sourcePath.
Length() < 3
1397 || (sourcePath.
CharAt(2) !=
'\\' && sourcePath.
CharAt(2) !=
'/' ) )
1401 info.prefixLength= 2;
1404 node.GoToCreateChildIfNotExistent(drive);
1406 pathToNode << drive;
1408 if( sourcePath.
Length() > 2
1409 && (sourcePath.
CharAt(2) ==
'/' || sourcePath.
CharAt(2) ==
'\\') ) {
1410 ++info.prefixLength;
1421 if( sourcePath.
Length() >= 2
1422 && ( (sourcePath.
CharAt(0) ==
'/' && sourcePath.
CharAt(1) ==
'/')
1423 || (sourcePath.
CharAt(0) ==
'\\' && sourcePath.
CharAt(1) ==
'\\') ) ) {
1425 info.prefixLength= 2;
1434 #if !defined(_WIN32)
1436 if(absoluteEnd==1) {
1438 info.prefixLength= 1;
1455 auto rootInfo= detectAndNormalizeUnrealRoot(sourcePath, node, pathToNode);
1458 node.GoToCreateChildIfNotExistent(rootInfo.scheme);
1462 pathToNode << rootInfo.scheme <<
A_CHAR(
"://");
1470 devName <<
A_CHAR(
"DEV");
1475 devName << ( (c ==
'/' || c ==
'\\') ?
PathCharType(
':') : c );
1477 node.GoToCreateChildIfNotExistent(devName);
1481 pathToNode << devName;
1498 if(name.Equals(
A_PATH(
".")) || name.Equals(
A_PATH(
"/"))) {
1502 if(name.Equals(
A_PATH(
".."))) {
1506 node= node.Parent();
1514 isNew= node.GoToCreateChildIfNotExistent(name);
1515 if(pathToNode.
Length() > 1)
1519 }
while(!isNew && !node->IsSymbolicLink());
1524 #if ALIB_SYSTEM_FILE_STATUS_IMPL == ALIB_SYSTEM_FILE_POSIX_STATUS
1529 fullPathChildName= pathToNode;
1531 scanFilePosix(
nullptr, node, fullPathChildName,
1533 0,
false, dummySums, pathToNode, resultPaths );
1535 scanFileStdFS( fs::path(std::basic_string_view<PathCharType>(pathToNode.
Buffer(),
1536 size_t(pathToNode.
Length()))),
1539 dummySums, resultPaths );
1542 auto quality= node->ScanState();
1547 sourcePath.
InsertAt(node.Name(), 0);
1566 if(node->IsSymbolicLink()) {
1569 Path unrealPathToSymlink= node->GetLinkTarget();
1570 auto targetNode= node.Parent();
1573 makeCanonicalRecursion(unrealPathToSymlink, targetNode, pathToNode, resultPaths, node );
1580 if(callingNode.IsValid())
1601 CanonicalResult result= makeCanonicalRecursion( sourcePath, node, pathToNode, resultPaths,
1605 resultPaths->
Add(node);
1615 Path* remainingStart) {
1622 " F={} DPre={} DPost={} XFS={} AFS={} Depth={}",
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 )
1641 Log_If( !isAbsolutePath,
Verbosity::Info,
"Changed relative start path to {}", sourcePath)
1645 Log_Prune(auto firstResultPos= resultPaths->size(); ))
1646 FTValue::DirectorySums dummySums;
1649 FTree::Cursor targetNode= tree.Root().AsCursor();
1650 Path realPath; realPath.DbgDisableBufferReplacementWarning();
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;
1657 auto originalTargetNode= targetNode;
1660 #if ALIB_SYSTEM_FILE_STATUS_IMPL == ALIB_SYSTEM_FILE_POSIX_STATUS
1662 scanFilePosix(
nullptr, targetNode, fullPathChildName, 0 ,parameters, 0,
false, dummySums,
1663 realPath, resultPaths );
1665 scanFileStdFS( fs::path(std::basic_string_view<PathCharType>(realPath.Buffer(),
1666 size_t(realPath.Length()))),
1667 targetNode, 0, parameters, dummySums, resultPaths );
1670 Log_Info(
"Scan Results: ", resultPaths->size() - firstResultPos )
1672 for(
auto& it : *resultPaths ) {
1675 Log_Info(
" Path {}: {} (Q={} D={}/F={}}",
1683 return resultPaths && resultPaths->size() > 0 ? resultPaths->back()->ScanState()
1684 : targetNode->ScanState();
1689 for(
auto existingIt= begin(); existingIt != end(); ++existingIt) {
1692 FTFile nodeParents= *existingIt;
1693 while( nodeParents.IsValid() ) {
1694 if( nodeParents == node ){
1698 nodeParents= nodeParents.
Parent();
1702 nodeParents= node.
Parent();
1703 while( nodeParents.IsValid() ) {
1704 if( nodeParents == *existingIt )
1706 nodeParents= nodeParents.
Parent();
1708 insert(end(), node);
#define ALIB_ALLOW_SPARSE_ENUM_SWITCH
#define ALIB_WARNING(domain,...)
#define ALIB_ASSERT_WARNING(cond, domain,...)
#define ALIB_ERROR(domain,...)
#define ALIB_POP_ALLOWANCE
#define ALIB_ASSERT_ERROR(cond, domain,...)
#define Log_IsActive(result,...)
#define Log_SetDomain(...)
void ClearSymbolicParent()
void SetSymbolicParent(FTree::CursorHandle handle, bool overwrite=false)
CPathString & GetLinkTarget() const noexcept
constexpr void SetSums(const DirectorySums &sums) const
constexpr DirectorySums & Sums() const
void AllocateExtendedInfo(Cursor &node, const PathString &symLinkDest, const PathString &symLinkRealPath)
static void FixSums(Cursor directory)
TAString & InsertAt(const TString< TChar > &src, integer pos)
constexpr const TChar * Terminate() const
TAString & DeleteStart(integer regionLength)
TAString & ShortenTo(integer newLength)
integer DetectLength(integer offset=0)
void EnsureRemainingCapacity(integer spaceNeeded)
void DbgDisableBufferReplacementWarning()
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
integer IndexOfOrLength(TChar needle) const
@ DIRECTORY
Directory/folder.
@ 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
static DateTime FromEpochSeconds(time_t epochSeconds)
void Import(TTimePoint timePoint)
FTValue::ScanStates ScanFiles(FTree &tree, ScanParameters ¶meters, CanonicalPathList *resultPaths=nullptr, Path *remainingStart=nullptr)
PathRootKind
Classification of path root formats that cannot be directly scanned.
@ DriveLetter
Windows drive letter (C:, D:, etc).
@ Errorneous
Errorneous path format.
@ AbsoluteRoot
Unix-style absolute path starting with /.
@ Relative
Relative path (no special root).
@ Device
Windows device path (\\.\...).
@ URL
URL scheme (http://, ftp://, file://, etc).
@ UNC
Universal Naming Convention (\\server\share).
String DBG_FILETREE_SCAN_VERBOSE_LOG_FORMAT
CanonicalResult MakeCanonical(Path &sourcePath, FTree::Cursor &node, Path &pathToNode, CanonicalPathList *resultPaths=nullptr)
platform_specific integer
lox::Verbosity Verbosity
Type alias in namespace #"%alib".
strings::TString< nchar > NString
Type alias in namespace #"%alib".
monomem::TLocalAllocator< TCapacityInKB > LocalAllocator
Type alias in namespace #"%alib".
strings::TCString< nchar > NCString
Type alias in namespace #"%alib".
strings::TAString< nchar, lang::HeapAllocator > NAString
Type alias in namespace #"%alib".
lang::integer integer
Type alias in namespace #"%alib".
strings::TString< character > String
Type alias in namespace #"%alib".
system::Path Path
Type alias in namespace #"%alib".
strings::TCString< PathCharType > CPathString
The string-type used with this ALib Module.
strings::TSubstring< nchar > NSubstring
Type alias in namespace #"%alib".
filetree::FTFile FTFile
Type alias in namespace #"%alib".
LocalString< 128 > String128
Type alias name for #"TLocalString;TLocalString<character,128>".
std::filesystem::path::value_type PathCharType
boxing::TBoxes< MonoAllocator > BoxesMA
Type alias in namespace #"%alib".
system::FileStatus FileStatus
Type alias in namespace #"%alib".
constexpr PathCharType DIRECTORY_SEPARATOR
The standard path separator character. Defaults to '\' on Windows OS, '/' else.
NLocalString< 512 > NString512
Type alias name for #"TLocalString;TLocalString<nchar,512>".
lang::uinteger uinteger
Type alias in namespace #"%alib".
strings::TSubstring< PathCharType > PathSubstring
The string-type used with this ALib Module.
time::DateTime DateTime
Type alias in namespace #"%alib".
#define ALIB_STRING_RESETTER(astring)
Result information from MakeCanonical.
virtual bool Includes(const FTFile &file, const PathString &parentPath)=0
Recursively accumulated values for directories.
uint32_t QtyErrsBrokenLink
Number of broken symbolic links in the directory and its subfolders.
uint32_t Count() const noexcept
uint32_t QtyErrsAccess
Number of access errors in the folder and subfolders.
constexpr DirectorySums & Add(const FTValue &finfo) noexcept
Input parameters to function #"ScanFiles(FTree&)".
bool RemoveEmptyDirectories
@ DONT_RESOLVE
Demands not to resolve symbolic links in any way.
@ RESOLVE_BUT_DONT_FOLLOW
SPFileFilter DirectoryFilterPostRecursion
unsigned MaxDepth
The maximum recursion depth. Defaults to #"InfiniteRecursion".
static constexpr unsigned InfiniteRecursion
Denotes 'infinite' recursion if set to field #"MaxDepth".
SymbolicLinks LinkTreatment
Denotes how symbolic links are treated.
Path StartPath
The path to be scanned.
SPFileFilter DirectoryFilterPreRecursion