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