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>
80 bool 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
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 )
152 dbgActFile << nameOrFullPath;
155 dbgActFile << actPath;
156 if(dbgActFile.Length()>1)
157 dbgActFile << DIRECTORY_SEPARATOR;
158 dbgActFile << nameOrFullPath;
162 BoxesMA verboseLogables( verboseAllocator);
167 verboseLogables.Add(
"{!AWidth:>} ");
168 if( ¶ms == ¶msPathOnly )
169 verboseLogables.Add(
"PO");
172 auto& depthString= *verboseAllocator().New<
String128>();
174 << ( params.
MaxDepth < std::numeric_limits<unsigned int>::max()
177 verboseLogables.Add(depthString);
188 auto oldQuality= value.Quality();
193 if( value.Quality() ==
FInfo::Qualities::NONE
194 || ( value.Quality() ==
FInfo::Qualities::STATS
195 && params.LinkTreatment !=
ScanParameters::SymbolicLinks::DONT_RESOLVE ) )
199 Path symLinkDestReal;
203 #
if TMP_STATX_AVAILABLE
205 int statResult= statx( pxDir ? dirfd(pxDir) : 0,
207 AT_STATX_DONT_SYNC | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW,
208 STATX_BASIC_STATS | STATX_BTIME,
213 int statResult= pxDir ? fstatat(dirfd(pxDir), nNameOrFullPath, &stats,
215 #
if !defined(__APPLE__)
219 : lstat ( nNameOrFullPath, &stats );
223 ALIB_ASSERT_WARNING( errno != ENOENT,
"FILES",
"File does not exist (anymore) while stating \"{}\"",
225 ALIB_ASSERT_WARNING( errno == ENOENT,
"FILES",
"Unknown error ({}) \"{}\" while stating file \"{}\"",
230 goto APPLY_POST_RECURSION_FILTER;
232 DBG_CHECKERRNO_WITH_PATH
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(); }
241 if( STAT_DEV_MAJOR == 0
242 && STAT_DEV_MINOR != 35 )
243 value.SetArtificialFS();
246 bool origFileIsSymlink= (STATMEMBER(mode) & S_IFMT) == S_IFLNK;
247 if( origFileIsSymlink
250 value.SetQuality( FInfo::Qualities::RESOLVED );
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);
257 if (cntChars == -1) switch(errno)
259 case EACCES: value.SetQuality(FInfo::Qualities::NO_ACCESS_SL); ALIB_DBG(errno= 0;)
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;)
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;)
274 nSymLinkDest.SetLength(cntChars);
276 symLinkDest.
Reset(nSymLinkDest);
282 actPath << DIRECTORY_SEPARATOR << nameOrFullPath;
286 *nSymLinkDestReal.VBuffer()=
'\0';
287 if(! realpath(nActPath.Terminate(), nSymLinkDestReal.VBuffer() ) ) switch (errno)
289 case ENOENT:
if( *nSymLinkDestReal.VBuffer() !=
'\0')
298 default:
ALIB_ERROR(
"FILES",
"Posix raised ({}) \"{}\" on resolving symbolic link \"{}\"",
302 nSymLinkDestReal.DetectLength();
304 ALIB_DBG(
if( errno == EINVAL) errno= 0;)
305 DBG_CHECKERRNO_WITH_PATH
307 "Real path is not absolute: ", nSymLinkDestReal )
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,
318 statResult= stat(nSymLinkDestReal.Terminate(), &stats );
320 DBG_CHECKERRNO_WITH_PATH
321 #if ALIB_CHARACTERS_WIDE
322 symLinkDestReal.Reset(nSymLinkDestReal);
325 if(statResult == -1 )
331 goto APPLY_POST_RECURSION_FILTER;
333 "Unhandled error code invoking 'stat()' on resolved symbolic "
334 "link: {} (\"{}\")\n Symbolic link target: \"{}\"",
338 goto APPLY_POST_RECURSION_FILTER;
344 if( STAT_DEV_MAJOR == 0
345 && STAT_DEV_MINOR != 35 )
346 value.SetTargetArtificialFS();
351 DBG_CHECKERRNO_WITH_PATH
359 auto posixType= STATMEMBER(mode) & S_IFMT;
360 if( origFileIsSymlink )
365 else switch(STATMEMBER(mode) & S_IFMT )
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;
376 "Internal error. 'unknown' file type can't happen. File: \"{}\"",
379 value.SetType( type );
383 value.SetPerms(
FInfo::Permissions(STATMEMBER(mode) & int32_t(
FInfo::Permissions::MASK)) );
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)
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)
398 # define st_mtime_name STATMEMBER(mtim)
399 # define st_ctime_name STATMEMBER(ctim)
400 # define st_atime_name STATMEMBER(atim)
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} ) } );
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} ) } );
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} ) } );
425 #if TMP_STATX_AVAILABLE
426 if( STATMEMBER(mask) & STATX_BTIME )
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} ) } );
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 );
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 );
459 value.SetSize(
uinteger(STATMEMBER(size) ) );
462 value.SetOwner( STATMEMBER(uid) );
463 value.SetGroup( STATMEMBER(gid) );
466 value.SetQtyHardlinks( STATMEMBER(nlink) );
470 && (value.IsDirectory() || symLinkDest.
IsNotEmpty()) ) {
478 DBG_CHECKERRNO_WITH_PATH
484 goto APPLY_POST_RECURSION_FILTER;
490 if( !value.IsDirectory()
492 goto APPLY_POST_RECURSION_FILTER;
497 Log_Prune(
if( verboseLogables.Size() ) verboseLogables.Add(
" NO_AFS"); )
498 value.SetQuality(
FInfo::Qualities::NO_AFS );
499 goto APPLY_POST_RECURSION_FILTER;
503 if( value.IsCrossingFS() && !params.CrossFileSystems )
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;
511 if( depth >= params.MaxDepth )
513 Log_Prune(
if( verboseLogables.Size() && (¶ms != ¶msPathOnly) ) verboseLogables.Add(
" MAX_DEPTH_REACHED"); )
514 value.SetQuality(
FInfo::Qualities::MAX_DEPTH_REACHED );
515 ++parentSums.QtyStopsOnMaxDepth;
516 goto APPLY_POST_RECURSION_FILTER;
521 && params.DirectoryFilterPreRecursion
522 && !params.DirectoryFilterPreRecursion->Includes( node, actPath ) )
524 Log_Prune(
if( verboseLogables.Size() ) verboseLogables.Add(
" FILTERED(Pre)"); )
525 goto APPLY_POST_RECURSION_FILTER;
529 value.SetQuality(
FInfo::Qualities::RECURSIVE );
532 if ( value.Type() ==
FInfo::Types::SYMBOLIC_LINK_DIR )
535 || value.IsArtificialFS() )
537 value.SetQuality( FInfo::Qualities::NOT_FOLLOWED );
538 goto APPLY_POST_RECURSION_FILTER;
543 value.SetQuality( FInfo::Qualities::NO_AFS );
544 goto APPLY_POST_RECURSION_FILTER;
549 if( startScan( node.Tree<
FTree>(), value.GetRealLinkTarget(), params, childSums,
552 value.SetSums( childSums );
553 parentSums+= childSums;
555 goto APPLY_POST_RECURSION_FILTER;
560 if( pxDir ==
nullptr )
563 actPath.SetLength(nameOrFullPath.Length());
567 if( actPath.Length() > 1 ) actPath << DIRECTORY_SEPARATOR;
568 actPath << nameOrFullPath;
574 fd= openat( dirfd(pxDir), nNameOrFullPath, O_RDONLY | O_DIRECTORY );
578 fd= open( nActPath , O_RDONLY | O_DIRECTORY );
583 DBG_CHECKERRNO_WITH_PATH
585 DIR* childDir = fdopendir(fd);
589 dirent* pxEntry = readdir(childDir);
590 if( pxEntry ==
nullptr )
603 "Posix raised ({}) \"{}\" on reading a directory which is not "
604 "located on an artificial filesystem (like /proc). File:\"{}\"",
608 ALIB_ERROR(
"FILES",
"Posix raised ({}) \"{}\" on reading "
609 "directory \"{}\"", errno,
SystemErrors(errno), dbgActFile )
617 if( pxEntry->d_name[0] ==
'.'
618 && ( pxEntry->d_name[1] ==
'\0'
619 || ( pxEntry->d_name[1] ==
'.'
620 && pxEntry->d_name[2] ==
'\0' ) ) )
624 auto childNode= node;
625#if ALIB_CHARACTERS_WIDE
626 Path childName(
const_cast<const char*
>(&pxEntry->d_name[0]));
628 const CString childName(
const_cast<const char*
>(&pxEntry->d_name[0]));
631 childNode.GoToCreateChildIfNotExistent( childName );
633 scanFilePosix( childDir, childNode, childName,
634 depth + 1, params, currentDevice, subSums, actPath,
638 DBG_CHECKERRNO_WITH_PATH
644 parentSums+= value.Sums();
648 value.SetSums(subSums);
649 parentSums+= subSums;
652 goto APPLY_POST_RECURSION_FILTER;
657 "Internal error opening directory. This must never happen")
663 case SystemErrors::eacces:
670 ALIB_ERROR(
"FILES",
"Unknown error {}(\"{}\") while opening directory \"{}\"",
680 APPLY_POST_RECURSION_FILTER:
685 if ( value.IsDirectory() )
691 && value.Sums().Count() == 0 )
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 )
710 auto it= node.FirstChild();
711 while ( it.IsValid() )
718 it.GoToNextSibling();
722 node.DeleteChildren();
732 Log_Prune(
if( verboseLogables.Size() ) { verboseLogables.Add(
" FILTERED(Post)");
733 Log_Verbose( verboseLogables ) } )
741 Log_Prune( if( verboseLogables.Size() ) {
748 parentSums.
Add(value);
753 DBG_CHECKERRNO_WITH_PATH
757#undef DBG_CHECKERRNO_WITH_PATH
758#undef TMP_STATX_AVAILABLE
766#if ALIB_FILES_FORCE_STD_SCANNER
767# pragma message ("ALIB_FILES_FORCE_STD_SCANNER given. Using std::filesystem for scanning. In file: " __FILE__ )
769# pragma message ("Unknown Platform. Using std::filesystem for scanning. In file: " __FILE__ )
772namespace fs = std::filesystem;
775#if defined(__APPLE__) || defined(_LIBCPP_VERSION) || defined(__ANDROID_NDK__)
778 template <
typename TP>
779 std::time_t to_time_t(TP tp)
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);
788# define DBG_CHECKERRNO \
789 ALIB_ASSERT_WARNING(errno == 0, "FILES", "Errno set ({})\"{}\".", \
790 errno, SystemErrors(errno) ) \
792# define DBG_CHECKERRNO_WITH_PATH \
793 ALIB_ASSERT_WARNING(errno == 0, "FILES", "Errno set ({})\"{}\". Current path: {}", \
794 errno, SystemErrors(errno), path.string() ) \
797# define DBG_CHECKERRNO
798# define DBG_CHECKERRNO_WITH_PATH
801namespace alib::files {
namespace {
803void scanFileStdFS(
const fs::path& path,
808 std::vector<ResultsPaths>& resultPaths
811#if defined(__MINGW32__)
812 Path pathAsCString(path.c_str());
813 pathAsCString.Terminate();
817 const PathSubstring parentPath= pathAsCString.
Substring(0, pathAsCString.LastIndexOf(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 )
827 && pathAsCString.CharAt(2)== DIRECTORY_SEPARATOR
828 && ( pathAsCString.Length()==3
829 || pathAsCString.CharAtEnd() != DIRECTORY_SEPARATOR) )
831 || ( pathAsCString.CharAt(0)== DIRECTORY_SEPARATOR
832 && pathAsCString.CharAt(1)== DIRECTORY_SEPARATOR
833 && ( pathAsCString.Length()==2
834 || pathAsCString.CharAtEnd() != DIRECTORY_SEPARATOR) )
836 && pathAsCString.IndexOf( strings::TLocalString<PathCharType, 8>(
837 DIRECTORY_SEPARATOR).Append(DIRECTORY_SEPARATOR),
839 "FILES",
"Given path not absolute or ending with '{}': {}", DIRECTORY_SEPARATOR, pathAsCString )
843 Log_Verbose(
"[{}] {}/{} {}", ¶ms != ¶msPathOnly ?
'>':
'P', depth,
844 params.
MaxDepth < (std::numeric_limits<unsigned int>::max)()
849 std::error_code errorCode;
850 auto& value = node.Value();
851 auto oldQuality= value.Quality();
860 Path symLinkDestReal;
863 fs::file_status stats= fs::symlink_status(path);
868 "Unhandled error code invoking 'fs::symlink_status()': {} (\"{}\")\n"
869 " With file: \"{}\"",
870 errorCode.value(), errorCode.message(), pathAsCString )
873 goto APPLY_POST_RECURSION_FILTER;
879 bool origFileIsSymlink= (stats.type() == fs::file_type::symlink);
880 if( origFileIsSymlink
886 fs::path resolved= fs::read_symlink(path, errorCode);
891 {
case SystemErrors::enoent:
896 "Unhandled error code invoking 'fs::read_symlink()': {} (\"{}\")\n"
897 " with file: ", errorCode.value(),
898 errorCode.message(), pathAsCString )
901 goto APPLY_POST_RECURSION_FILTER;
905 DBG_CHECKERRNO_WITH_PATH
906 symLinkDest << resolved.c_str();
910 if( resolved.is_absolute() )
911 realPath= fs::canonical(resolved, errorCode);
914 symLinkDestReal << pathAsCString;
916 symLinkDestReal << symLinkDest;
917 realPath= fs::canonical(fs::path(
918 std::basic_string_view<PathCharType>(symLinkDestReal.
Buffer(),
919 size_t(symLinkDestReal.
Length()))),
921 symLinkDestReal.
Reset();
923 ALIB_DBG(
if(errno==EINVAL && !errorCode) errno= 0;)
924 ALIB_DBG(
if(errno==ENOENT && !errorCode) errno= 0;)
932 default:
ALIB_ERROR(
"FILES",
"Unhandled error code invoking 'fs::canonical()': {} (\"{}\")\n"
933 " with file: ", errorCode.value(), errorCode.message(), pathAsCString )
937 DBG_CHECKERRNO_WITH_PATH
938 symLinkDestReal << realPath.c_str();
942 auto newStatus= fs::status(path, errorCode);
946 if(newStatus.type() != fs::file_type::unknown)
954 "Unhandled error code invoking 'directory_entry::status()': {} (\"{}\")\n"
955 " With file: \"{}\"",
956 errorCode.value(), errorCode.message(), pathAsCString )
966 DBG_CHECKERRNO_WITH_PATH
974 if( origFileIsSymlink )
979 else switch( stats.type() )
989 case fs::file_type::not_found:
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 :
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 :
999 ALIB_WARNING(
"FILES",
"Internal error. Can't happen. File: ", pathAsCString)
1000 ALIB_DBG( errno= 0;)
goto APPLY_POST_RECURSION_FILTER;
1003 ALIB_WARNING(
"FILES",
"Unknown fs::file_status::type '{}' with file {}.", stats.type(), pathAsCString)
1004 ALIB_DBG( errno= 0;)
goto APPLY_POST_RECURSION_FILTER;
1006 value.SetType( type );
1016 auto fsTime= std::filesystem::file_time_type(std::filesystem::file_time_type::clock::now());
1019 fsTime= fs::last_write_time( path, errorCode );
1021 if(errorCode)
switch(
SystemErrors(errorCode.value()) )
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);
1030 "Unhandled error code invoking 'fs::last_write_time()': {} (\"{}\")\n"
1031 " With file \"{}\".", errorCode.value(), errorCode.message(),
1033 fsTime= (decltype(fsTime)::min)();
ALIB_DBG( errno= 0;)
1040 #if defined(__APPLE__) || defined(_LIBCPP_VERSION) || defined(__ANDROID_NDK__)
1044 std::chrono::clock_cast<std::chrono::system_clock>(fsTime) ) ) );
1046 value.SetBDate( value.MDate() );
1047 value.SetCDate( value.MDate() );
1048 value.SetADate( value.MDate() );
1052 value.SetSize( symLinkDest.Length() > 0 ?
uinteger(symLinkDest.Length())
1062 case SystemErrors::eisdir:
1065 case SystemErrors::enoent:
1068 "Internal error. This should never happen. Undefined system error handling" )
1074 "Unhandled error code invoking 'directory_entry::file_size()':{} (\"{}\")\n"
1075 " With file \"{}\".",
1076 errorCode.value(), errorCode.message(), pathAsCString )
ALIB_DBG( errno= 0;)
1083 value.SetOwner(
FInfo::UnknownID );
1084 value.SetGroup(
FInfo::UnknownID );
1087 uint32_t qtyHardLinks= uint32_t( fs::hard_link_count(path, errorCode ) );
1092 "Unhandled error code invoking 'fs::hard_link_count()': {} (\"{}\")\n"
1093 " With file: \"{}\"",
1094 errorCode.value(), errorCode.message(), pathAsCString )
1098 value.SetQtyHardlinks( qtyHardLinks );
1102 && (value.IsDirectory() || symLinkDest.IsNotEmpty()) ) {
1110 DBG_CHECKERRNO_WITH_PATH
1115 ++parentSums.QtyErrsBrokenLink;
1116 goto APPLY_POST_RECURSION_FILTER;
1120 if( !value.IsDirectory()
1122 goto APPLY_POST_RECURSION_FILTER;
1131 value.SetQuality( FInfo::Qualities::NOT_CROSSING_FS );
1132 goto APPLY_POST_RECURSION_FILTER;
1138 value.SetQuality( FInfo::Qualities::MAX_DEPTH_REACHED );
1139 ++parentSums.QtyStopsOnMaxDepth;
1140 goto APPLY_POST_RECURSION_FILTER;
1147 goto APPLY_POST_RECURSION_FILTER;
1155 if( params.LinkTreatment != ScanParameters::SymbolicLinks::RECURSIVE
1156 || value.IsArtificialFS() )
1158 value.SetQuality( FInfo::Qualities::NOT_FOLLOWED );
1159 goto APPLY_POST_RECURSION_FILTER;
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;
1176 fs::directory_iterator dit= fs::directory_iterator(path, errorCode);
1180 for(
const fs::directory_entry& childDir : dit )
1184 Path mingwBuf( childDir.path().c_str());
1189 childName.ConsumeChars(childName.LastIndexOf(DIRECTORY_SEPARATOR) + 1);
1190 auto childNode= node;
1192 childNode.GoToCreateChildIfNotExistent( childName );
1194 scanFileStdFS( childDir.path(), childNode, depth + 1, params, subSums,
1202 parentSums+= value.Sums();
1206 value.SetSums(subSums);
1207 parentSums+= subSums;
1210 goto APPLY_POST_RECURSION_FILTER;
1216 "Internal error opening directory. This must never happen" )
1221 case SystemErrors::einval:
1225 goto APPLY_POST_RECURSION_FILTER;
1228 ALIB_ERROR(
"FILES",
"Unknown error {}(\"{}\") while opening directory \"{}\"",
1229 errorCode.value(),
SystemErrors(errorCode.value()), pathAsCString)
1231 goto APPLY_POST_RECURSION_FILTER;
1237 APPLY_POST_RECURSION_FILTER:
1240 if( oldQuality ==
FInfo::Qualities::
NONE )
1242 if ( value.IsDirectory() )
1245 && ( ( params.DirectoryFilterPostRecursion
1246 && !params.DirectoryFilterPostRecursion->Includes(node, parentPath ) )
1247 || ( params.RemoveEmptyDirectories
1248 && value.Sums().Count() == 0 )
1251 parentSums-= value.Sums();
1252 if( params.RemoveEmptyDirectories )
1255 node.Tree<FTree>().Notify( FTreeListener::Event::DeleteNode,
1257 IF_ALIB_THREADS( lock, )
1264 auto it= node.FirstChild();
1265 while ( it.IsValid() )
1268 node.Tree<FTree>().Notify( FTreeListener::Event::DeleteNode,
1270 IF_ALIB_THREADS( lock, )
1272 it.GoToNextSibling();
1276 node.DeleteChildren();
1294 parentSums.
Add(value);
1303#undef DBG_CHECKERRNO_WITH_PATH
1309namespace alib::files {
1314bool startScan(
FTree& tree,
1315 PathString realPath,
1318 std::vector<ResultsPaths>& resultPaths
1325 Path path(DIRECTORY_SEPARATOR);
1335 if(realPath.
CharAt(1) ==
':')
1339 node.GoToCreateChildIfNotExistent(realPath.
Substring(0,2));
1340 pathRemainder= node.GoTo( realPath.
Substring(3) );
1346 integer serverNameEnd= realPath.
IndexOf( DIRECTORY_SEPARATOR, 2);
1347 if( serverNameEnd < 0)
1348 serverNameEnd= realPath.
Length();
1349 path << realPath.
Substring(0, serverNameEnd);
1351 node.GoToCreateChildIfNotExistent(realPath.
Substring(2, serverNameEnd - 2));
1352 pathRemainder= node.GoTo( realPath.
Substring(serverNameEnd + 1) );
1354 path << realPath.
Substring(serverNameEnd, realPath.
Length() - pathRemainder.
Length() -serverNameEnd );
1366 if( node->IsDirectory())
1369 #if ALIB_FILES_SCANNER_IMPL == ALIB_FILES_SCANNER_POSIX
1373 scanFilePosix(
nullptr, node, fullPathChildName, 0, params, 0, parentSums, path,
1376 scanFileStdFS( fs::path(std::basic_string_view<PathCharType>(path.
Buffer(),
1391 strings::util::TTokenizer<PathCharType> tknzr( pathRemainder, DIRECTORY_SEPARATOR );
1392 while(tknzr.HasNext())
1399 node= node.CreateChild(name);
1403 bool isLastPathElement= !tknzr.HasNext();
1404 if( isLastPathElement )
1408 auto detectNodeDeletion= node.Depth();
1411 #if ALIB_FILES_SCANNER_IMPL == ALIB_FILES_SCANNER_POSIX
1419 path << node.Name();
1421 fullPathChildName= path;
1424 scanFilePosix(
nullptr, node, fullPathChildName,
1425 0, isLastPathElement ? params : paramsPathOnly,
1427 if( fullPathChildName.
Length() == 1 ) path.
Reset();
1431 scanFileStdFS( fs::path(std::basic_string_view<PathCharType>(path.
Buffer(),
1434 isLastPathElement ? params : paramsPathOnly,
1440 if( isLastPathElement)
1443 if (detectNodeDeletion == node.Depth() )
1444 resultPaths.insert(resultPaths.begin(),
ResultsPaths(realPath, node,
false));
1465 std::vector<ResultsPaths>& resultPaths
1484 #if ALIB_FILES_SCANNER_IMPL == ALIB_FILES_SCANNER_POSIX
1487 if(!realpath(nPath.Terminate(), nRealPath.VBuffer() ) ) switch (errno)
1492 default:
ALIB_ERROR(
"FILES",
"Posix raised ({}) \"{}\" on resolving start path \"{}\"",
1496 nRealPath.DetectLength();
1497 #if ALIB_CHARACTERS_WIDE
1498 realPath.Reset(nRealPath);
1502 std::error_code errorCode;
1503 fs::path fsRealPath= fs::canonical(fs::path(std::basic_string_view<PathCharType>(path.
Buffer(),
1506 ALIB_DBG(
if(errno==EINVAL && !errorCode) errno= 0;)
1507 ALIB_DBG(
if(errno==ENOENT && !errorCode) errno= 0;)
1510 if(errorCode)
switch(
SystemErrors(errorCode.value()) )
1515 default:
ALIB_ERROR(
"FILES",
"std::filesystem raised ({}) \"{}\" on resolving start path \"{}\"",
1516 errorCode.value(), errorCode.message(), path )
ALIB_DBG(errno= 0;)
1520 realPath << fsRealPath.c_str();
1526 " F={} DPre={} DPost={} XFS={} AFS={} Depth={}",
1538 auto firstResultPos= resultPaths.size();
1539 FInfo::DirectorySums dummySums;
1541 startScan( tree, realPath, parameters, dummySums, resultPaths
IF_ALIB_THREADS( , lock) );
1543 Log_Info(
"Scan Results: ", resultPaths.size() - firstResultPos )
1545 for( auto& it : resultPaths )
1547 Log_Info(
" Path {}: {} {} (Q={} D={}/F={}}",
1548 cntPaths++, it.Existed ?
' ' :
'+',
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 )
1557 return (*(resultPaths.begin() +
int(firstResultPos))).Node->
Quality();
1560#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.
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.
constexpr Qualities Quality() const noexcept
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)
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.
SPFileFilter DirectoryFilterPreRecursion
static constexpr unsigned int InfiniteRecursion
Denotes 'infinite' recursion if set to field MaxDepth.
SymbolicLinks LinkTreatment
Denotes how symbolic links are treated.
unsigned int MaxDepth
The maximum recursion depth. Defaults to InfiniteRecursion.
@ DONT_RESOLVE
Demands not to resolve symbolic links in any way.
Path StartPath
The path to be scanned.
SPFileFilter DirectoryFilterPostRecursion
bool RemoveEmptyDirectories