4#include "Stroika/Foundation/StroikaPreComp.h"
14#if qStroika_Foundation_Common_Platform_Windows
19#elif qStroika_Foundation_Common_Platform_POSIX
24#include "Stroika/Foundation/Containers/Set.h"
25#include "Stroika/Foundation/Execution/Exceptions.h"
26#include "Stroika/Foundation/Execution/Throw.h"
27#if qStroika_Foundation_Common_Platform_Windows
28#include "Stroika/Foundation/Execution/Platform/Windows/Exception.h"
29#include "Stroika/Foundation/Execution/Platform/Windows/HRESULTErrorException.h"
31#include "Stroika/Foundation/Containers/Common.h"
44using namespace Stroika::Foundation::IO;
46using namespace Stroika::Foundation::Memory;
48#if qStroika_Foundation_Common_Platform_Windows
60 return Format (
"{} bytes"_f,
static_cast<int> (bytes));
62 else if (bytes < 1000 * 1024) {
63 return Format (
"%{:.1} K"_f,
static_cast<double> (bytes) / 1024.0f);
66 return Format (
"{:.1} MB"_f,
static_cast<double> (bytes) / (1024 * 1024.0f));
81void IO::FileSystem::SetFileAccessWideOpened (
const filesystem::path& filePathName)
83#if qStroika_Foundation_Common_Platform_Windows
84 static PACL pACL =
nullptr;
85 if (pACL ==
nullptr) {
86 PSID pSIDEveryone =
nullptr;
91 SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
92 if (!::AllocateAndInitializeSid (&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSIDEveryone)) {
97 EXPLICIT_ACCESS ea[1]{};
99 ea[0].grfAccessPermissions = GENERIC_ALL;
100 ea[0].grfAccessMode = SET_ACCESS;
101 ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
102 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
103 ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
104 ea[0].Trustee.ptstrName = (LPTSTR)pSIDEveryone;
106 if (ERROR_SUCCESS != ::SetEntriesInAcl (
static_cast<DWORD
> (NEltsOf (ea)), ea,
nullptr, &pACL)) {
107 ::FreeSid (pSIDEveryone);
110 ::FreeSid (pSIDEveryone);
114 [[maybe_unused]] DWORD dwRes = ::SetNamedSecurityInfo (
const_cast<SDKChar*
> (filePathName.c_str ()),
116 DACL_SECURITY_INFORMATION,
121#elif qStroika_Foundation_Common_Platform_POSIX
123 if (filePathName.empty ()) [[unlikely]] {
129 mode_t desiredMode = (S_IRUSR | S_IRGRP | S_IROTH) | (S_IWUSR | S_IWGRP | S_IWOTH);
130 if (S_ISDIR (s.st_mode)) {
131 desiredMode |= (S_IXUSR | S_IXGRP | S_IXOTH);
136 if ((s.st_mode & desiredMode) != desiredMode) {
137 result = ::chmod (filePathName.generic_string ().c_str (), desiredMode);
145#if qStroika_Foundation_Common_Platform_Windows
151bool FileSystem::is_cygwin_symlink (
const filesystem::path& p)
153 if (filesystem::exists (p)) {
154 DWORD attributes = GetFileAttributes (p.native ().c_str ());
155 if (attributes & FILE_ATTRIBUTE_SYSTEM) [[unlikely]] {
157 (void)read_cygwin_symlink (p);
173filesystem::path FileSystem::read_cygwin_symlink (
const filesystem::path& p)
175 if (filesystem::exists (p)) [[likely]] {
176 DWORD attributes = GetFileAttributes (p.native ().c_str ());
177 if (attributes & FILE_ATTRIBUTE_SYSTEM) [[likely]] {
178 ifstream file{p, ios::binary};
179 if (file.is_open ()) [[likely]] {
180 char buffer[MAX_PATH + 11];
181 file.read (buffer,
sizeof (buffer));
182 size_t nBytesRead =
static_cast<size_t> (file.gcount ());
183 constexpr auto kMagicHeader_ =
"!<symlink>"sv;
184 if (nBytesRead > 10 and strncmp (buffer, kMagicHeader_.data (), 10) == 0) [[likely]] {
185 return &buffer[kMagicHeader_.size ()];
190 Execution::Throw (filesystem_error{
"read_cygwin_symlink", p, make_error_code (errc::wrong_protocol_type)});
201#if qStroika_Foundation_Common_Platform_Windows
203 AdjustSysErrorMode errorModeAdjuster (AdjustSysErrorMode::GetErrorMode () | SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
208 BOOL result = ::GetVolumeInformation (driveLetterAbsPath.c_str (), volNameBuf,
static_cast<DWORD
> (NEltsOf (volNameBuf)),
nullptr,
209 &ignored, &ignored, igBuf,
static_cast<DWORD
> (NEltsOf (igBuf)));
211 return String::FromSDKString (volNameBuf);
224void IO::FileSystem::CopyFile (
const filesystem::path& srcFile,
const filesystem::path& destPath)
226#if qStroika_Foundation_Common_Platform_Windows
231 create_directories (destPath.parent_path ());
232 ThrowIfZeroGetLastError (::CopyFile (destPath.c_str (), srcFile.c_str (),
false));
243vector<String> IO::FileSystem::FindFiles (
const filesystem::path& path,
const String& fileNameToMatch)
245 vector<String> result;
249#if qStroika_Foundation_Common_Platform_Windows
251 String matchFullPath = usePath + (fileNameToMatch.empty () ? L
"*" : fileNameToMatch);
252 WIN32_FIND_DATA fd{};
253 HANDLE hFind = ::FindFirstFile (matchFullPath.AsSDKString ().c_str (), &fd);
254 if (hFind != INVALID_HANDLE_VALUE) {
257 String fileName = String::FromSDKString (fd.cFileName);
258 if (not(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
259 result.push_back (usePath + fileName);
261 }
while (::FindNextFile (hFind, &fd));
280vector<String> IO::FileSystem::FindFilesOneDirUnder (
const filesystem::path& path,
const String& fileNameToMatch)
283 return vector<String> ();
287#if qStroika_Foundation_Common_Platform_Windows
290 memset (&fd, 0,
sizeof (fd));
291 HANDLE hFind = ::FindFirstFile ((usePath + L
"*").AsSDKString ().c_str (), &fd);
292 if (hFind != INVALID_HANDLE_VALUE) {
295 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
296 String fileName = String::FromSDKString ((LPCTSTR)&fd.cFileName);
297 static const String kDOT =
"."sv;
298 static const String kDOTDOT =
".."sv;
299 if ((fileName != kDOT) and (fileName != kDOTDOT)) {
300 resultSet +=
Containers::Set<String> (FindFiles (usePath.
As<filesystem::path> () / fileName.
As<filesystem::path> (), fileNameToMatch));
303 }
while (::FindNextFile (hFind, &fd));
312#if qStroika_Foundation_Common_Platform_Windows
318IO::FileSystem::DirectoryChangeWatcher::DirectoryChangeWatcher (
const filesystem::path& directoryName,
bool watchSubTree, DWORD notifyFilter)
319 : fDirectory{directoryName}
320 , fWatchSubTree{watchSubTree}
321 , fDoneEvent{::CreateEvent (nullptr, false, false, nullptr)}
322 , fWatchEvent{::FindFirstChangeNotification (fDirectory.AsSDKString ().c_str (), fWatchSubTree, notifyFilter)}
325 fThread =
Execution::Thread::New ([
this] () { ThreadProc (
this); }, Execution::Thread::eAutoStart, L
"DirectoryChangeWatcher");
328IO::FileSystem::DirectoryChangeWatcher::~DirectoryChangeWatcher ()
331 if (fDoneEvent != INVALID_HANDLE_VALUE) {
332 Verify (::SetEvent (fDoneEvent));
336 IgnoreExceptionsForCall (fThread.AbortAndWaitForDone ());
337 if (fDoneEvent != INVALID_HANDLE_VALUE) {
338 Verify (::CloseHandle (fDoneEvent));
340 if (fWatchEvent != INVALID_HANDLE_VALUE) {
341 Verify (::FindCloseChangeNotification (fWatchEvent));
345void IO::FileSystem::DirectoryChangeWatcher::ValueChanged ()
349void IO::FileSystem::DirectoryChangeWatcher::ThreadProc (
void* lpParameter)
351 DirectoryChangeWatcher* _THS_ =
reinterpret_cast<DirectoryChangeWatcher*
> (lpParameter);
352 while (not _THS_->fQuitting and _THS_->fWatchEvent != INVALID_HANDLE_VALUE) {
354 events[0] = _THS_->fDoneEvent;
355 events[1] = _THS_->fWatchEvent;
356 ::WaitForMultipleObjects (
static_cast<DWORD
> (NEltsOf (events)), events,
false, INFINITE);
357 Verify (::FindNextChangeNotification (_THS_->fWatchEvent));
358 if (not _THS_->fQuitting) {
359 _THS_->ValueChanged ();
365#if qStroika_Foundation_Common_Platform_Windows
371UINT AdjustSysErrorMode::GetErrorMode ()
373 UINT good = ::SetErrorMode (0);
374 ::SetErrorMode (good);
378AdjustSysErrorMode::AdjustSysErrorMode (UINT newErrorMode)
379 : fSavedErrorMode (::SetErrorMode (newErrorMode))
383AdjustSysErrorMode::~AdjustSysErrorMode ()
385 (void)::SetErrorMode (fSavedErrorMode);
#define AssertNotImplemented()
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
static INT_TYPE ThrowPOSIXErrNoIfNegative(INT_TYPE returnCode, const path &p1={}, const path &p2={})
nonvirtual void CheckAccess(const filesystem::path &fileFullPath, AccessMode accessMode=AccessMode::eRead)
nonvirtual Iterator< T > begin() const
Support for ranged for, and STL syntax in general.
static constexpr default_sentinel_t end() noexcept
Support for ranged for, and STL syntax in general.
An Iterator<T> is a copyable object which allows traversing the contents of some container....
conditional_t< qTargetPlatformSDKUseswchar_t, wchar_t, char > SDKChar
Ptr New(const function< void()> &fun2CallOnce, const optional< Characters::String > &name, const optional< Configuration > &configuration)
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
String GetVolumeName(const filesystem::path &driveLetterAbsPath)
String AssureDirectoryPathSlashTerminated(const String &dirPath)