ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
filestatus.cpp
1
2
3namespace alib::system {
4
5//==================================================================================================
6//=== OwnerAndGroupResolver
7//==================================================================================================
9 #if !defined ( _WIN32)
10 auto* result= getpwuid(owner);
11 return NString(result ? result->pw_name : "?");
12 #else
13 return "?";
14 #endif
15}
16
18 #if !defined ( _WIN32)
19 auto* result= getgrgid(group);
20 return NString(result ? result->gr_name : "?");
21 #else
22 return "?";
23 #endif
24}
25
26//==================================================================================================
27//=== Path::Exists
28//==================================================================================================
30 #if ALIB_SYSTEM_FILE_STATUS_IMPL == ALIB_SYSTEM_FILE_POSIX_STATUS
31
32 ALIB_DBG( errno= 0;)
33 struct stat stats;
34 return lstat( Terminate(), &stats ) == 0;
35 #else
36 ALIB_STRINGS_TO_NARROW(*this,nPath,MAX_PATH)
37 fs::path stdPath(nPath.Terminate());
38 return fs::exists(stdPath);
39 #endif
40}
41
42//--------------------------------------------------------------------------------------------------
43//--- Path::Exists UNKNOWN platform, using C++17 filesystem (not all functionality given)
44//--------------------------------------------------------------------------------------------------
45
46//==================================================================================================
47//=== FileStatus::Update Posix version
48//==================================================================================================
49#if ALIB_SYSTEM_FILE_STATUS_IMPL == ALIB_SYSTEM_FILE_POSIX_STATUS
50
51FileStatus::ScanStates FileStatus::Update(std::filesystem::path& path, bool isCanonical) {
52 Path alibPath(path.c_str());
53 return Update(alibPath, isCanonical);
54}
55
57 // check
58 #if ALIB_DEBUG
59 if(isCanonical) {
60 Path pc(path);
61 pc.MakeCanonical();
62 ALIB_ASSERT_ERROR(path.Equals(pc), "SYSTEM",
63 "Non-canonical path given, while parameter 'isCanonical' is true: {}", path)
64 }
65 #endif
66
67 // canonify path
68 if(!isCanonical)
69 path.MakeCanonical();
70
71
72 // prerequisites
73 // Since Kernel 4.11 Linux/glibc has "statx". We use it if available on the current platform.
74# if defined(__NR_statx)
75# define TMP_STATX_AVAILABLE 1
76# define STATMEMBER(Name) stats.stx_ ## Name
77# define STAT_DEV_MAJOR stats.stx_dev_major
78# define STAT_DEV_MINOR stats.stx_dev_minor
79# else
80# define TMP_STATX_AVAILABLE 0
81# define STATMEMBER(Name) stats.st_ ## Name
82# define STAT_DEV_MAJOR major(stats.st_dev)
83# define STAT_DEV_MINOR minor(stats.st_dev)
84# endif
85
86# if ALIB_DEBUG
87# define DBG_CHECKERRNO_WITH_PATH \
88ALIB_ASSERT_WARNING(errno == 0, "SYSTEM", "Errno set ({})\"{}\". Current path: {}", \
89errno, std::errc(errno), path ) \
90errno= 0;
91# else
92# define DBG_CHECKERRNO_WITH_PATH
93# endif
94
96
97 // read base stats
98 ALIB_DBG( errno= 0;)
99 #if TMP_STATX_AVAILABLE
100 struct statx stats;
101 int statResult= statx( AT_FDCWD,
102 path.Terminate(),
103 AT_STATX_DONT_SYNC | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW,
104 STATX_BASIC_STATS | STATX_BTIME,
105 &stats );
106
107 #else
108 struct stat stats;
109 int statResult= lstat ( path.Terminate(), &stats );
110 #endif
111 if( statResult ) {
112 ALIB_ASSERT_WARNING( errno == ENOENT, "SYSTEM", "Unknown error {} while stating file \"{}\"",
113 std::errc(errno), path )
116 ALIB_DBG( errno= 0;)
117 return ScanState();
118 }
119 DBG_CHECKERRNO_WITH_PATH
120
121 // store filesystem type (artificial fs & mount point)
122 SetPosixDevice((uint64_t(STAT_DEV_MAJOR) << 32L) + STAT_DEV_MINOR);
123
124 // Use statfs to properly detect artificial filesystems
125 #if defined(__linux__)
126 struct statfs fsStats;
127 if (statfs(path.Terminate(), &fsStats) == 0) {
128 // Check for known artificial filesystem types
129 switch(fsStats.f_type) {
130 case PROC_SUPER_MAGIC: // /proc
131 case SYSFS_MAGIC: // /sys
132 case CGROUP_SUPER_MAGIC: // cgroups
133 case CGROUP2_SUPER_MAGIC: // cgroups v2
134 case DEBUGFS_MAGIC: // debugfs
135 case DEVPTS_SUPER_MAGIC: // /dev/pts
136 case SELINUX_MAGIC: // selinuxfs
137 case SECURITYFS_MAGIC: // securityfs
138 case TRACEFS_MAGIC: // tracefs
140 break;
141 default:
142 break;
143 } }
144 #elif defined(__APPLE__)
145 struct statfs fsStats;
146 if (statfs(path.Terminate(), &fsStats) == 0) {
147 // Check macOS artificial filesystems
148 if (strcmp(fsStats.f_fstypename, "devfs") == 0 ||
149 strcmp(fsStats.f_fstypename, "autofs") == 0) {
151 } }
152 #else
153 // Fallback to device number check for other systems
154 if( STAT_DEV_MAJOR == 0
155 && STAT_DEV_MINOR != 35 ) // tmpfs
157 #endif
158 // assert that this is not a symbolic link. The stat path with this class is always real.
159 ALIB_ASSERT_ERROR((STATMEMBER(mode) & S_IFMT) != S_IFMT, "SYSTEM",
160 "Internal error: stat'ed file is symbolic link. Path not canonical \"{}\"", path )
161
162 //=========================================== Copy Stats =========================================
163 // 1. type
164 {
166 auto posixType= STATMEMBER(mode) & S_IFMT;
167 switch(posixType ) {
168 case S_IFLNK : type= FileStatus::Types::SYMBOLIC_LINK;
169 ALIB_ERROR( "SYSTEM", "Impossible") break;
170 case S_IFBLK : type= FileStatus::Types::BLOCK ; break;
171 case S_IFCHR : type= FileStatus::Types::CHARACTER ; break;
172 case S_IFDIR : type= FileStatus::Types::DIRECTORY ; break;
173 case S_IFIFO : type= FileStatus::Types::FIFO ; break;
174 case S_IFREG : type= FileStatus::Types::REGULAR ; break;
175 case S_IFSOCK: type= FileStatus::Types::SOCKET ; break;
176 default: ALIB_ERROR("SYSTEM",
177 "Internal error. 'unknown' file type can't happen. File: \"{}\"", path ) break;
178 }
179 SetType( type );
180 }
181
182 // 2. perms
183 SetPerms( FileStatus::Permissions(STATMEMBER(mode) & int32_t(FileStatus::Permissions::MASK)) );
184
185 // 3. timestamps
186 {
187 #if defined(__APPLE__)
188 # define st_mtime_name STATMEMBER(mtimespec)
189 # define st_ctime_name STATMEMBER(ctimespec)
190 # define st_atime_name STATMEMBER(atimespec)
191 #else
192 # if TMP_STATX_AVAILABLE
193 # define st_mtime_name STATMEMBER(mtime)
194 # define st_ctime_name STATMEMBER(ctime)
195 # define st_atime_name STATMEMBER(atime)
196 # define st_btime_name STATMEMBER(btime)
197 # else
198 # define st_mtime_name STATMEMBER(mtim)
199 # define st_ctime_name STATMEMBER(ctim)
200 # define st_atime_name STATMEMBER(atim)
201 # endif
202 #endif
203 DateTime dt;
204 dt.Import(
205 std::chrono::system_clock::time_point {
206 std::chrono::duration_cast<std::chrono::system_clock::duration>(
207 std::chrono::seconds {st_mtime_name.tv_sec }
208 + std::chrono::nanoseconds{st_mtime_name.tv_nsec} ) } );
209 SetMDate(dt);
210
211 dt.Import(
212 std::chrono::system_clock::time_point {
213 std::chrono::duration_cast<std::chrono::system_clock::duration>(
214 std::chrono::seconds {st_ctime_name.tv_sec }
215 + std::chrono::nanoseconds{st_ctime_name.tv_nsec} ) } );
216 SetCDate(dt);
217
218 dt.Import(
219 std::chrono::system_clock::time_point {
220 std::chrono::duration_cast<std::chrono::system_clock::duration>(
221 std::chrono::seconds {st_atime_name.tv_sec }
222 + std::chrono::nanoseconds{st_atime_name.tv_nsec} ) } );
223 SetADate(dt);
224
225 #if TMP_STATX_AVAILABLE
226 if( STATMEMBER(mask) & STATX_BTIME ) { // file systems supports "btime"?
227 dt.Import(
228 std::chrono::system_clock::time_point {
229 std::chrono::duration_cast<std::chrono::system_clock::duration>(
230 std::chrono::seconds {st_btime_name.tv_sec }
231 + std::chrono::nanoseconds{st_btime_name.tv_nsec} ) } );
232 SetBDate(dt);
233 } else {
234 // use smallest of other times for "btime"
235 auto btime= MDate();
236 if( btime > CDate() ) btime= CDate();
237 if( btime > ADate() ) btime= ADate();
238 SetBDate( btime );
239
240 }
241 #else
242 // use smallest of other times for "btime"
243 auto btime= MDate();
244 if( btime > CDate() ) btime= CDate();
245 if( btime > ADate() ) btime= ADate();
246 SetBDate( btime );
247 #endif
248
249
250 #undef st_mtime_name
251 #undef st_ctime_name
252 #undef st_atime_name
253 }
254
255 // 4. size
256 SetSize( uinteger(STATMEMBER(size) ) );
257
258 // 5. uid/gid
259 SetOwner( STATMEMBER(uid) );
260 SetGroup( STATMEMBER(gid) );
261
262 // 6. qty of hard links
263 SetQtyHardlinks( STATMEMBER(nlink) );
264
265 return ScanState();
266
267 #undef DBG_CHECKERRNO_WITH_PATH
268 #undef TMP_STATX_AVAILABLE
269 #undef STATMEMBER
270}
271
272//--------------------------------------------------------------------------------------------------
273//--- UNKNOWN platform, using C++17 filesystem (not all functionality given)
274//--------------------------------------------------------------------------------------------------
275#else
276#if ALIB_SYSTEM_FORCE_STD_FILE_STATUS
277# pragma message ("ALIB_SYSTEM_FORCE_STD_FILE_STATUS given. Using std::filesystem for " \
278 "FileStatus::Update(). (Limited functionality) " )
279#else
280# pragma message ("Non-posix platform. Using std::filesystem for " \
281 "FileStatus::Update(). (Limited functionality) " )
282#endif
283
284FileStatus::ScanStates FileStatus::Update(Path& path, bool isCanonical) {
285 std::filesystem::path stdPath(path.Terminate());
286 return Update(stdPath, isCanonical);
287}
288
289FileStatus::ScanStates FileStatus::Update(std::filesystem::path& path, bool isCanonical) {
290
291 // check
292 #if ALIB_DEBUG
293 if(isCanonical) {
294 auto pc= fs::canonical(path);
295 ALIB_ASSERT_ERROR(path == pc, "SYSTEM",
296 "Non-canonical path given, while parameter 'isCanonical' is true: {}", path.c_str())
297 }
298 #endif
299
300 // canonify path
301 if(!isCanonical)
302 path= fs::canonical(path);
303
304 std::error_code errorCode;
305
307
308 // read base stats (we have to use symlink_status() which does NOT follow the symlink!)
309 fs::file_status stats= fs::symlink_status(path);
311 if(errorCode)
312 {
313 ALIB_ERROR("SYSTEM",
314 "Unhandled error code invoking 'fs::symlink_status()': {} (\"{}\")\n"
315 " With file: \"{}\"",
316 errorCode.value(), errorCode.message(), path.c_str() )
317 ALIB_DBG( errno= 0;)
319 return ScanState();
320 }
322 ALIB_DBG(errno= 0;)
323
324 // assert that this is not a symbolic link. The stat path with this class is always real.
325 ALIB_ASSERT_ERROR(stats.type() != fs::file_type::symlink, "SYSTEM",
326 "Internal error: stat'ed file is symbolic link. Path not canonical \"{}\"", path.c_str() )
327
328 //=========================================== Copy Stats =========================================
329 // 1. type
330 {
332 switch( stats.type() )
333 {
334 case fs::file_type::directory: type= FileStatus::Types::DIRECTORY ; break;
335 case fs::file_type::regular : type= FileStatus::Types::REGULAR ; break;
336 case fs::file_type::symlink : type= FileStatus::Types::SYMBOLIC_LINK; break;
337 case fs::file_type::block : type= FileStatus::Types::BLOCK ; break;
338 case fs::file_type::character: type= FileStatus::Types::CHARACTER ; break;
339 case fs::file_type::fifo : type= FileStatus::Types::FIFO ; break;
340 case fs::file_type::socket : type= FileStatus::Types::SOCKET ; break;
341
342 case fs::file_type::not_found:
343 SetScanState(FileStatus::ScanStates::UNKNOWN_ERROR);
344 ALIB_WARNING("SYSTEM", "Internal error. 'not found' file type can't happen. File: ", path.c_str() )
345 ALIB_DBG( errno= 0;) return ScanState();
346 case fs::file_type::none :
347 SetScanState(FileStatus::ScanStates::UNKNOWN_ERROR);
348 ALIB_WARNING("SYSTEM", "Internal error. 'none' file type can't happen. File: ", path.c_str())
349 ALIB_DBG( errno= 0;) return ScanState();
350 case fs::file_type::unknown :
351 SetScanState(FileStatus::ScanStates::UNKNOWN_ERROR);
352 ALIB_WARNING("SYSTEM", "Internal error. Can't happen. File: ", path.c_str())
353 ALIB_DBG( errno= 0;) return ScanState();
354 default:
355 SetScanState(FileStatus::ScanStates::UNKNOWN_ERROR);
356 ALIB_WARNING("SYSTEM", "Unknown fs::file_status::type '{}' with file {}.", stats.type(), path.c_str())
357 ALIB_DBG( errno= 0;) return ScanState();
358 }
359 SetType( type );
360 }
361
362 // 2. perms
363 SetPerms( FileStatus::Permissions(int32_t(stats.permissions())) );
364
365 // 3. timestamps
366 // attn: This method always follows symbolic link and uses the target's time
367 // This seems to be a confirmed behavior:
368 // https://stackoverflow.com/questions/50778660/boost-filesystem-how-to-get-last-write-time-for-symlink-without-resolving
369 auto fsTime= std::filesystem::file_time_type(std::filesystem::file_time_type::clock::now());
370 if ( ScanState() <= FileStatus::ScanStates::RESOLVED ) // no error
371 {
372 fsTime= fs::last_write_time( path, errorCode );
374 if(errorCode) switch( std::errc(errorCode.value()) )
375 { // This happens if with symbolic links that point to nowhere.
376 case std::errc::no_such_file_or_directory:
377 ALIB_ERROR( "SYSTEM", "Internal error. This should never happen, checked above. "
378 "Undefined system error handling" ) ALIB_DBG( errno= 0;)
380 break;
381
382 default:
383 ALIB_ERROR( "SYSTEM",
384 "Unhandled error code invoking 'fs::last_write_time()': {} (\"{}\")\n"
385 " With file \"{}\".", errorCode.value(), errorCode.message(), path.c_str() )
386 fsTime= (decltype(fsTime)::min)(); ALIB_DBG( errno= 0;)
387 break;
388 }
390 }
391
392
393 #if defined(__APPLE__) || defined(_LIBCPP_VERSION) || defined(__ANDROID_NDK__)
394 SetMDate( DateTime::FromEpochSeconds( to_time_t( fsTime ) ) );
395 #else
396 SetMDate( DateTime::FromEpochSeconds( std::chrono::system_clock::to_time_t(
397 std::chrono::clock_cast<std::chrono::system_clock>(fsTime) ) ) );
398 #endif
399 SetBDate( MDate() );
400 SetCDate( MDate() );
401 SetADate( MDate() );
402
403 // 4. size
404 errorCode.clear();
405 SetSize(uinteger(fs::file_size(path, errorCode)));
406 if( Size() == uinteger(-1))
407 {
408 SetSize(0);
410 switch( std::errc(errorCode.value()) )
411 {
412 // target is a directory (no error)
413 case std::errc::is_a_directory:
414 break;
415
416 case std::errc::no_such_file_or_directory: // this happens if we have a broken symbolic link
419 "Internal error. This should never happen. Undefined system error handling" )
420 break;
421
422 // size not supported. Happens with sockets, files in /proc, etc
423 case std::errc::operation_not_supported: break;
424 default: ALIB_ERROR("SYSTEM",
425 "Unhandled error code invoking 'directory_entry::file_size()':{} (\"{}\")\n"
426 " With file \"{}\".",
427 errorCode.value(), errorCode.message(), path.c_str() ) ALIB_DBG( errno= 0;)
428 break;
429 }
431 }
432
433 // 5. uid/gid
436
437 // 6. qty of hard links
438 qtyHardLinks= uint32_t( fs::hard_link_count(path, errorCode ) );
440 if(errorCode) {
441 // fs::hard_link_coun always returns the hardlink-count of the symlink targets.
442 // This fails on broken links. Therefore, this is not considered an error
443 if( !( std::errc(errorCode.value()) == std::errc::no_such_file_or_directory
445 ALIB_MESSAGE("SYSTEM",
446 "Unhandled error code invoking 'fs::hard_link_count()': {} (\"{}\")\n"
447 " With file: \"{}\"",
448 errorCode.value(), errorCode.message(), path.c_str() )
449 ALIB_DBG( errno= 0;)
450 }
451 }
454
455 // that's it
456 return ScanState();
457}
458#endif
459
460} // namespace [alib::system]
461
#define ALIB_MESSAGE(domain,...)
#define ALIB_ALLOW_SPARSE_ENUM_SWITCH
#define ALIB_ASSERT_WARNING(cond, domain,...)
#define ALIB_ERROR(domain,...)
#define ALIB_POP_ALLOWANCE
#define ALIB_DBG(...)
#define ALIB_ASSERT_ERROR(cond, domain,...)
#define ALIB_BOXING_VTABLE_DEFINE(TMapped, Identifier)
constexpr const PathCharType * Terminate() const
Definition tastring.hpp:614
bool Equals(const TString< TChar > &rhs) const
Definition string.hpp:515
@ CHARACTER
A character special file.
@ BLOCK
A block special file.
@ FIFO
A FIFO (also known as pipe) file.
constexpr DateTime MDate() const noexcept
void SetScanState(ScanStates v) noexcept
void SetType(Types v) noexcept
constexpr uinteger Size() const noexcept
const NString GetOwnerName() const
Definition filestatus.cpp:8
void SetArtificialFS() noexcept
Mark the entry as residing on an artificial filesystem.
void SetADate(DateTime v) noexcept
uinteger size
The file size. In case of a directory, this is 0.
FileStatus()
Default constructor.
uint32_t group
The group id that owns the file.
void SetMDate(DateTime v) noexcept
constexpr Types Type() const noexcept
uint32_t owner
The user id that owns the file.
constexpr DateTime ADate() const noexcept
void SetSize(uinteger v) noexcept
Permissions
Permission flags. Compatible with POSIX definition.
@ MASK
All valid permission bits. Equivalent to all | set_uid | set_gid | sticky_bit.
void SetOwner(uint32_t v) noexcept
void SetPerms(Permissions v) noexcept
static constexpr TOwnerAndGroupID UnknownID
Constant value for owner and group IDs to denote that the field was not determined.
const NString GetGroupName() const
void SetBDate(DateTime v) noexcept
constexpr DateTime CDate() const noexcept
ScanStates Update(Path &path, bool isCanonical=false)
uint32_t qtyHardLinks
The number of hard links to the file.
void SetPosixDevice(uint64_t deviceCode) noexcept
constexpr ScanStates ScanState() const noexcept
void SetCDate(DateTime v) noexcept
ScanStates
Per-entry information about how a node was scanned.
@ STATS
Only stats (size, date, owner, etc.) read.
@ RESOLVED
Read symlink target strings.
@ UNKNOWN_ERROR
Unknown scanner failure.
@ NOT_EXISTENT
Set if a given start path does not exist.
void SetQtyHardlinks(uint32_t v) noexcept
void SetGroup(uint32_t v) noexcept
std::errc MakeCanonical()
Definition path.cpp:445
static DateTime FromEpochSeconds(time_t epochSeconds)
Definition datetime.hpp:62
void Import(TTimePoint timePoint)
strings::TString< nchar > NString
Type alias in namespace #"%alib".
Definition string.hpp:2174
lang::uinteger uinteger
Type alias in namespace #"%alib".
Definition integers.hpp:152
time::DateTime DateTime
Type alias in namespace #"%alib".
Definition datetime.hpp:188
#define ALIB_STRINGS_TO_NARROW( src, dest, bufSize)