Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
IPlatformFileManagedStorageWrapper.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Algo/Find.h"
6#include "Algo/IndexOf.h"
7#include "Async/Async.h"
8#include "Async/TaskGraphInterfaces.h"
9#include "Containers/Array.h"
10#include "Containers/ArrayView.h"
11#include "Containers/Map.h"
12#include "Containers/SparseArray.h"
13#include "Containers/UnrealString.h"
14#include "CoreGlobals.h"
15#include "CoreTypes.h"
16#include "GenericPlatform/GenericPlatformFile.h"
17#include "HAL/CriticalSection.h"
18#include "HAL/PlatformCrt.h"
19#include "HAL/PlatformString.h"
20#include "Logging/LogCategory.h"
21#include "Logging/LogMacros.h"
22#include "Math/NumericLimits.h"
23#include "Math/UnrealMathUtility.h"
24#include "Misc/AssertionMacros.h"
25#include "Misc/CommandLine.h"
26#include "Misc/ConfigCacheIni.h"
27#include "Misc/EnumClassFlags.h"
28#include "Misc/Optional.h"
29#include "Misc/Parse.h"
30#include "Misc/Paths.h"
31#include "Misc/ScopeLock.h"
32#include "Misc/ScopeRWLock.h"
33#include "Templates/UniquePtr.h"
34#include "Templates/UnrealTemplate.h"
35#include "Trace/Detail/Channel.h"
36
37#include <atomic>
38
40
42{
43 // Same as FPaths::IsUnderDirectory, but assume the paths are already full
44 // Also is *always* case insensitive since we are concerned with filtering and
45 // not whether a directory actually exists.
46 bool IsUnderDirectory(const FString& InPath, const FString& InDirectory);
47}
48
50{
53
54 bool IsValid() const { return Category >= 0; }
55 explicit operator bool() const { return Category >= 0; }
56
57 void Clear()
58 {
60 }
61};
62
64{
65private:
67
68 FCriticalSection* GetLock(const FString& InFilename)
69 {
70 uint32 KeyHash = GetTypeHash(InFilename);
71
73
74 FLockData& LockData = FileLockMap.FindOrAddByHash(KeyHash, InFilename);
75 ++LockData.RefCount;
76
77 return LockData.CS.Get();
78 }
79
80 void ReleaseLock(const FString& InFilename)
81 {
82 uint32 KeyHash = GetTypeHash(InFilename);
83
85
86 FLockData* LockData = FileLockMap.FindByHash(KeyHash, InFilename);
87 check(LockData);
88
89 --LockData->RefCount;
90
91 if (LockData->RefCount == 0)
92 {
93 FileLockMap.RemoveByHash(KeyHash, InFilename);
94 }
95 }
96
97private:
98
99 struct FLockData
100 {
102 int32 RefCount = 0;
103 };
104
107};
108
110{
111public:
114 {
115 Lock();
116 }
117
119 {
120 Unlock();
121 }
122
123 void Unlock();
124
125private:
126 void Lock();
127
128private:
131};
132
134{
135 None = 0,
136 OnlyUpdateIfLess = (1 << 0),
137 RespectQuota = (1 << 1)
138};
140
142{
143public:
144 FPersistentStorageCategory(FString InCategoryName, TArray<FString> InDirectories, const int64 InQuota, const int64 InOptionalQuota)
147 , StorageQuota(InQuota)
148 , OptionalStorageQuota(InOptionalQuota)
149 {
150 check(OptionalStorageQuota < StorageQuota || StorageQuota <= 0); // optional storage quota should be a subset of the total storage quota
151 }
152
154 {
155 return CategoryName;
156 }
157
158 int64 GetCategoryQuota( bool bIncludeOptional = true ) const
159 {
160 if (bIncludeOptional || StorageQuota < 0)
161 {
162 return StorageQuota;
163 }
164 else
165 {
167 }
168 }
169
171 {
173 }
174
175 int64 GetUsedSize() const
176 {
177 return UsedQuota;
178 }
179
180 int64 GetAvailableSize(bool bIncludeOptionalStorage = true) const
181 {
182 int64 ActualStorageQuota = StorageQuota;
183 if (ActualStorageQuota >= 0)
184 {
185 if (!bIncludeOptionalStorage)
186 {
187 ActualStorageQuota -= OptionalStorageQuota;
188 }
189 }
190 else
191 {
192 ActualStorageQuota = MAX_int64;
193 }
194 return ActualStorageQuota - UsedQuota;
195 }
196
197 bool IsInCategory(const FString& Path) const
198 {
199 return ShouldManageFile(Path);
200 }
201
202 bool IsCategoryFull() const
203 {
204 return GetAvailableSize() <= 0;
205 }
206
208 {
209 uint32 KeyHash = GetTypeHash(Filename);
210
211 int64 OldFileSize = 0;
212 {
214 int64* pOldFileSize = FileSizes.FindByHash(KeyHash, Filename);
215 if (pOldFileSize)
216 {
217 OldFileSize = *pOldFileSize;
218 }
219 }
220
221 EPersistentStorageManagerFileSizeFlags Result = TryUpdateQuota(OldFileSize, FileSize, Flags);
223 {
225 FileSizes.AddByHash(KeyHash, Filename, FileSize);
226
227 UE_LOG(LogPlatformFileManagedStorage, Verbose, TEXT("File %s is added to category %s"), *Filename, *CategoryName);
228 }
229
230 return Result;
231 }
232
233 bool RemoveFile(const FString& Filename)
234 {
235 FileSizesLock.WriteLock(); // FWriteScopeLock doesn't have an early unlock :(
236
237 int64 OldSize;
238 if (FileSizes.RemoveAndCopyValue(Filename, OldSize))
239 {
241
242 UsedQuota -= OldSize;
243
244 UE_LOG(LogPlatformFileManagedStorage, Verbose, TEXT("File %s is removed from category %s"), *Filename, *CategoryName);
245
246 return true;
247 }
248
250 return false;
251 }
252
253 const TArray<FString>& GetDirectories() const { return Directories; }
254
256 {
258 {
259 if (TotalSize < 0)
260 {
261 return FString::Printf(TEXT("Category %s: %.3f MiB (%" INT64_FMT ") / unlimited used"), *CategoryName, (float)UsedSize / 1024.f / 1024.f, UsedSize);
262 }
263 else
264 {
265 return FString::Printf(TEXT("Category %s: %.3f MiB (%" INT64_FMT ") / %.3f MiB used"), *CategoryName, (float)UsedSize / 1024.f / 1024.f, UsedSize, (float)TotalSize / 1024.f / 1024.f);
266 }
267 }
268
270 int64 UsedSize = 0;
271 int64 TotalSize = -1;
274 };
275
276 // Note this will not be accurate if the category is being modified while this is called but it is low level thread safe
278 {
280 return CategoryStat{ CategoryName, UsedQuota, StorageQuota, FileSizes, Directories };
281 }
282
283 FManagedStorageFileLockRegistry& GetLockRegistry() { return FileLockRegistry; }
284
285private:
287 {
288 check(OldFileSize >= 0);
289 check(NewFileSize >= 0);
290
291 if (NewFileSize <= OldFileSize)
292 {
293 UsedQuota -= (OldFileSize - NewFileSize);
295 }
296
298 {
300 }
301
302 if (EnumHasAnyFlags(Flags, EPersistentStorageManagerFileSizeFlags::RespectQuota) && StorageQuota >= 0)
303 {
304 int64 OldUsedQuota = UsedQuota;
305 int64 NewUsedQuota;
306 do
307 {
308 NewUsedQuota = OldUsedQuota + (NewFileSize - OldFileSize);
309 if (NewUsedQuota > StorageQuota)
310 {
311 return EPersistentStorageManagerFileSizeFlags::RespectQuota;
312 }
313 } while (!UsedQuota.compare_exchange_weak(OldUsedQuota, NewUsedQuota));
314
316 }
317
318 UsedQuota += (NewFileSize - OldFileSize);
320 }
321
322 bool ShouldManageFile(const FString& Filename) const
323 {
324 for (const FString& Directory : Directories)
325 {
326 if (ManagedStorageInternal::IsUnderDirectory(Filename, Directory))
327 {
328 return true;
329 }
330 }
331
332 return false;
333 }
334
335private:
338
339 const int64 StorageQuota = -1;
340 const int64 OptionalStorageQuota = 0;
341
342 std::atomic<int64> UsedQuota{ 0 };
343
346
348};
349
350// NOTE: is not used on the whole class because then FCategoryInfo is exported which appears to force the generation
351// of copy constructors for FPersistentStorageCategory which causes a compile error because std::atomic can't be copied.
353{
354public:
355 static bool IsReady()
356 {
357 // FPersistentStorageManager depends on FPaths which depends on the command line being initialized.
358 // FPersistentStorageManager depends on GConfig.
359 // FPersistentStorageManager can't be constructed until its dependencies are ready
360 // FPersistentStorageManager will try and allocate memory during a crash but this could hang during log file flushing
362 }
363
364 /** Singleton access **/
366
368
370 {
371 if (bInitialized)
372 {
373 return;
374 }
375
376 // Check to add files
377 AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, []()
378 {
379 class FInitStorageVisitor : public IPlatformFile::FDirectoryVisitor
380 {
381 public:
382 FInitStorageVisitor() : IPlatformFile::FDirectoryVisitor(EDirectoryVisitorFlags::ThreadSafe) // Go wide with parallel file scan
383 {}
384
385 virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory)
386 {
387 if (FilenameOrDirectory && !bIsDirectory)
388 {
389 FPersistentStorageManager& Man = FPersistentStorageManager::Get();
390 if (FPersistentManagedFile File = Man.TryManageFile(FilenameOrDirectory))
391 {
392 FManagedStorageScopeFileLock ScopeFileLock(File);
393
394 // This must be done under the lock because another thread may be modifying or deleting the file while we scan
395 FFileStatData StatData = IPlatformFile::GetPlatformPhysical().GetStatData(FilenameOrDirectory);
396 if (ensureAlways(StatData.bIsValid))
397 {
398 if (!ensureAlways(StatData.FileSize >= 0))
399 {
400 UE_LOG(LogPlatformFileManagedStorage, Error, TEXT("Invalid File Size for %s!"), FilenameOrDirectory);
401 }
402
403 Man.AddOrUpdateFile(File, StatData.FileSize);
404 }
405 else
406 {
407 UE_LOG(LogPlatformFileManagedStorage, Error, TEXT("Invalid Stat Data for %s!"), FilenameOrDirectory);
408 }
409 }
410 }
411
412 return true;
413 }
414 };
415
416 FInitStorageVisitor Visitor;
417
418 for (const FString& RootDir : FPersistentStorageManager::Get().GetRootDirectories())
419 {
420 UE_LOG(LogPlatformFileManagedStorage, Display, TEXT("Scan directory %s"), *RootDir);
421
422 IPlatformFile::GetPlatformPhysical().IterateDirectoryRecursively(*RootDir, Visitor);
423 }
424
425 UE_LOG(LogPlatformFileManagedStorage, Display, TEXT("Done scanning root directories"));
426 });
427
428 bInitialized = true;
429 }
430
432 {
434 OutFile.FullFilename = FPaths::ConvertRelativePathToFull(Filename);
435
437
438 return OutFile;
439 }
440
442 {
444 OutFile.FullFilename = FPaths::ConvertRelativePathToFull(MoveTemp(Filename));
445
447
448 return OutFile;
449 }
450
451private:
453 {
454 int32 CategoryIndex = 0;
455 for (const FPersistentStorageCategory& Category : Categories.GetCategories())
456 {
457 if (Category.IsInCategory(OutFile.FullFilename))
458 {
459 OutFile.Category = CategoryIndex;
460 return;
461 }
462 ++CategoryIndex;
463 }
464
465 bool bIsUnderRootDir = !!Algo::FindByPredicate(RootDirectories.GetRootDirectories(), [&OutFile](const FString& RootDir) { return ManagedStorageInternal::IsUnderDirectory(OutFile.FullFilename, RootDir); });
466 if (bIsUnderRootDir)
467 {
468 OutFile.Category = Categories.GetDefaultCategoryIndex();
469 }
470 }
471
472public:
475 {
476 if (!File)
477 {
479 }
480
481 return Categories.GetCategories()[File.Category].AddOrUpdateFile(File.FullFilename, FileSize, Flags);
482 }
483
485 {
486 if (!File)
487 {
488 return false;
489 }
490
491 return Categories.GetCategories()[File.Category].RemoveFile(File.FullFilename);
492 File.Clear();
493 }
494
495 int64 GetTotalUsedSize() const
496 {
497 int64 TotalUsedSize = 0LL;
498 for(const FPersistentStorageCategory& Category : Categories.GetCategories())
499 {
500 TotalUsedSize += Category.GetUsedSize();
501 }
502
503 return TotalUsedSize;
504 }
505
506 /// <summary>
507 ///
508 /// </summary>
509 /// <param name="Path">path to check for free space</param>
510 /// <param name="UsedSpace">amount of used space</param>
511 /// <param name="RemainingSpace">amount of remaining free space</param>
512 /// <param name="Quota">total storage allocated to the related category</param>
513 /// <param name="OptionalQuota">subset of the storage which is optional i.e. will always be smaller then total Quota</param>
514 /// <returns>returns if function succeeds</returns>
515 bool GetPersistentStorageUsage(FString Path, int64& UsedSpace, int64 &RemainingSpace, int64& Quota, int64* OptionalQuota = nullptr)
516 {
517 FString FullPath = FPaths::ConvertRelativePathToFull(MoveTemp(Path));
518
519 for (const FPersistentStorageCategory& Category : Categories.GetCategories())
520 {
521 if (Category.IsInCategory(FullPath))
522 {
523 UsedSpace = Category.GetUsedSize();
524 RemainingSpace = Category.GetAvailableSize();
525 Quota = Category.GetCategoryQuota();
526 if (OptionalQuota != nullptr)
527 {
528 *OptionalQuota = Category.GetCategoryOptionalQuota();
529 }
530 return true;
531 }
532 }
533 return false;
534 }
535
536 bool GetPersistentStorageUsageByCategory(const FString& InCategory, int64& UsedSpace, int64& RemainingSpace, int64& Quota, int64* OptionalQuota = nullptr)
537 {
538 FPersistentStorageCategory* Category = Algo::FindBy(Categories.GetCategories(), InCategory, [](const FPersistentStorageCategory& Cat) { return Cat.GetCategoryName(); });
539 if (Category)
540 {
541 UsedSpace = Category->GetUsedSize();
542 RemainingSpace = Category->GetAvailableSize();
543 Quota = Category->GetCategoryQuota();
544 if (OptionalQuota != nullptr)
545 {
546 *OptionalQuota = Category->GetCategoryOptionalQuota();
547 }
548 return true;
549 }
550 return false;
551 }
552
553 /// <summary>
554 /// Returns any additional required free space and optional free space
555 /// </summary>
556 /// <param name="RequiredSpace">Required persistent storage space to run</param>
557 /// <param name="OptionalSpace">persistent storage categories marked as optional</param>
558 /// <returns></returns>
559 bool GetPersistentStorageSize(int64& UsedSpace, int64& RequiredSpace, int64& OptionalSpace) const
560 {
561 for (const FPersistentStorageCategory& Category : Categories.GetCategories())
562 {
563 UsedSpace += Category.GetUsedSize();
564 RequiredSpace += Category.GetCategoryQuota();
565 OptionalSpace += Category.GetCategoryOptionalQuota();
566 }
567 return true;
568 }
569
570 bool IsInitialized() const
571 {
572 return bInitialized;
573 }
574
576 {
577 if (!File)
578 {
579 return false;
580 }
581
582 return Categories.GetCategories()[File.Category].IsCategoryFull();
583 }
584
586 {
587 TMap<FString, FPersistentStorageCategory::CategoryStat> CategoryStats;
588 CategoryStats.Reserve(Categories.GetCategories().Num());
589
590 for (const FPersistentStorageCategory& Category : Categories.GetCategories())
591 {
592 CategoryStats.Add(Category.GetCategoryName(), Category.GetStat());
593 }
594
595 return CategoryStats;
596 }
597
599 {
601
602 for (const FPersistentStorageCategory& Category : Categories.GetCategories())
603 {
604 if (Category.GetCategoryName() == InCategory)
605 {
606 Result.Emplace(Category.GetStat());
607 break;
608 }
609 }
610
611 return Result;
612 }
613
614 TArrayView<const FString> GetRootDirectories() const { return RootDirectories.GetRootDirectories(); }
615
616private:
617 friend class FManagedStorageScopeFileLock; // For access to Categories
618
620
621 // Top level of all managed storage
622 // Wrapper to prevent changing or resizing after init
624 {
625 private:
627
628 public:
629 void Init(TArrayView<const FPersistentStorageCategory> Categories);
630 TArrayView<const FString> GetRootDirectories() const { return MakeArrayView(RootDirectories); }
631 };
633
634 // Wrapper for Category Array to prevent resizing after init
636 {
637 private:
639 int32 DefaultCategory = -1;
640
641 public:
642 FCategoryInfo(TArray<FPersistentStorageCategory>&& InCategories, int32 InDefaultCategory);
643
644 TArrayView<FPersistentStorageCategory> GetCategories() { return MakeArrayView(Categories); }
645 TArrayView<const FPersistentStorageCategory> GetCategories() const { return MakeArrayView(Categories); }
646
647 FPersistentStorageCategory* GetDefaultCategory() { return DefaultCategory >= 0 ? &Categories[DefaultCategory] : nullptr; }
648 const FPersistentStorageCategory* GetDefaultCategory() const { return DefaultCategory >= 0 ? &Categories[DefaultCategory] : nullptr; }
649
650 int32 GetDefaultCategoryIndex() const { return DefaultCategory; }
651 };
653
655};
656
657// Only write handle
659{
660private:
661 static bool IsReady()
662 {
663 // FManagedStoragePlatformFile will just pass through to LowerLevel until FPersistentStorageManager is ready
665 }
666
668 {
669 if (!File && IsReady())
670 {
671 File = FPersistentStorageManager::Get().TryManageFile(File.FullFilename);
672 }
673
674 return File.IsValid();
675 }
676
677public:
681 {
682 }
683
685 {
686 if (!TryManageFile())
687 {
688 return;
689 }
690
691 FManagedStorageScopeFileLock ScopeFileLock(File);
692
693 FPersistentStorageManager::Get().AddOrUpdateFile(File, FileHandle->Size());
694 }
695
696 virtual int64 Tell() override
697 {
698 return FileHandle->Tell();
699 }
700
701 virtual bool Seek(int64 NewPosition) override
702 {
703 return FileHandle->Seek(NewPosition);
704 }
705
706 virtual bool SeekFromEnd(int64 NewPositionRelativeToEnd = 0) override
707 {
708 return FileHandle->SeekFromEnd(NewPositionRelativeToEnd);
709 }
710
711 virtual bool Read(uint8* Destination, int64 BytesToRead) override
712 {
713 return FileHandle->Read(Destination, BytesToRead);
714 }
715
716 virtual bool Write(const uint8* Source, int64 BytesToWrite) override
717 {
718 if (!TryManageFile())
719 {
720 return FileHandle->Write(Source, BytesToWrite);
721 }
722
724
725 FManagedStorageScopeFileLock ScopeFileLock(File);
726
727 int64 NewSize = FMath::Max(FileHandle->Tell() + BytesToWrite, FileHandle->Size());
728
729 EPersistentStorageManagerFileSizeFlags Result = Manager.AddOrUpdateFile(File, NewSize, EPersistentStorageManagerFileSizeFlags::RespectQuota);
730 bool bIsFileCategoryFull = Result != EPersistentStorageManagerFileSizeFlags::None;
731 if (bIsFileCategoryFull)
732 {
733 UE_LOG(LogPlatformFileManagedStorage, Error, TEXT("Failed to write to file %s. The category of the file has reach quota limit in peristent storage."), *File.FullFilename);
734 return false;
735 }
736
737 bool bSuccess = FileHandle->Write(Source, BytesToWrite);
738 if (!bSuccess)
739 {
740 int64 FileSize = FileHandle->Size();
741 if (ensureAlways(FileSize >= 0))
742 {
743 verify(Manager.AddOrUpdateFile(File, FileSize) == EPersistentStorageManagerFileSizeFlags::None);
744 }
745 }
746
747 return bSuccess;
748 }
749
750 virtual int64 Size() override
751 {
752 return FileHandle->Size();
753 }
754
755 virtual bool Flush(const bool bFullFlush = false) override
756 {
757 const bool bOldIsValid = File.IsValid();
758 if (!TryManageFile())
759 {
760 return FileHandle->Flush(bFullFlush);
761 }
762
763 FManagedStorageScopeFileLock ScopeFileLock(File);
764
765 const bool bSuccess = FileHandle->Flush(bFullFlush);
766 const bool bForceSizeUpdate = !bOldIsValid;
767 if (!bSuccess || bForceSizeUpdate)
768 {
769 int64 FileSize = FileHandle->Size();
770 if (ensureAlways(FileSize >= 0))
771 {
772 verify(FPersistentStorageManager::Get().AddOrUpdateFile(File, FileSize) == EPersistentStorageManagerFileSizeFlags::None);
773 }
774 }
775
776 return bSuccess;
777 }
778
779 virtual bool Truncate(int64 NewSize) override
780 {
781 if (!TryManageFile())
782 {
783 return FileHandle->Truncate(NewSize);
784 }
785
787
788 FManagedStorageScopeFileLock ScopeFileLock(File);
789
790 int64 FileSize = FileHandle->Size();
791
792 if (NewSize <= FileSize)
793 {
794 bool bSuccess = FileHandle->Truncate(NewSize);
795 FileSize = FileHandle->Size();
796 verify(Manager.AddOrUpdateFile(File, FileSize) == EPersistentStorageManagerFileSizeFlags::None);
797 return bSuccess;
798 }
799
800 if (Manager.AddOrUpdateFile(File, NewSize, EPersistentStorageManagerFileSizeFlags::RespectQuota) != EPersistentStorageManagerFileSizeFlags::None)
801 {
802 UE_LOG(LogPlatformFileManagedStorage, Error, TEXT("Failed to truncate file %s. The category of the file has reach quota limit in peristent storage."), *File.FullFilename);
803 return false;
804 }
805
806 if (!FileHandle->Truncate(NewSize))
807 {
808 FileSize = FileHandle->Size();
809 verify(Manager.AddOrUpdateFile(File, FileSize) == EPersistentStorageManagerFileSizeFlags::None);
810 return false;
811 }
812
813 return true;
814 }
815
816 virtual void ShrinkBuffers() override
817 {
818 FileHandle->ShrinkBuffers();
819 }
820
821private:
824};
825
826// NOTE: This is templated rather than a polymorphic wrapper because a lot code expects the physical layer not to be a wrapper.
827// It also has the benefit of not needing updating every time a new function is added to IPlatformFile.
828template<class BaseClass>
829class TManagedStoragePlatformFile : public BaseClass
830{
831private:
832 static bool IsReady()
833 {
834 // FManagedStoragePlatformFile will just pass through to LowerLevel until FPersistentStorageManager is ready
836 }
837
838public:
839
841 {
842 // NOTE: using LowLevelFatalError here because UE_LOG is not yet available at static init time
843
844 // Book keeping is handled by the base implementation calling DeleteFile()
848 {
849 LowLevelFatalError(TEXT("TManagedStoragePlatformFile cannot track all deletes!"));
850 }
851
852 // Book keeping is handled by DeleteFile() and CopyFile() which will be called by the base implementation
856 {
857 LowLevelFatalError(TEXT("TManagedStoragePlatformFile cannot track all deletes and copies!"));
858 }
859 }
860
861 virtual bool DeleteFile(const TCHAR* Filename) override
862 {
863 if (!IsReady())
864 {
865 return BaseClass::DeleteFile(Filename);
866 }
867
869
871
873
874 bool bSuccess = BaseClass::DeleteFile(Filename);
875 if (bSuccess)
876 {
878 }
879
880 return bSuccess;
881 }
882
883 virtual bool MoveFile(const TCHAR* To, const TCHAR* From) override
884 {
885 if (!IsReady())
886 {
887 return BaseClass::MoveFile(To, From);
888 }
889
891
894
897
898 const int64 SizeFrom = this->FileSize(From);
899 if (SizeFrom < 0)
900 {
901 return false;
902 }
903
906 {
907 UE_LOG(LogPlatformFileManagedStorage, Error, TEXT("Failed to move file to %s. The target category of the destination has reach quota limit in peristent storage."), To);
908 return false;
909 }
910
911 bool bSuccess = BaseClass::MoveFile(To, From);
912 if (bSuccess)
913 {
916 }
917 else
918 {
919 // On some implementations MoveFile can operate across volumes, so don't make assumptions about the state of
920 // of the file system in the case of failure.
921
922 if (ManagedFrom && !this->FileExists(From))
923 {
925 }
926
927 if (ManagedTo)
928 {
929 if (!this->FileExists(To))
930 {
932 }
933 else
934 {
935 const int64 SizeTo = this->FileSize(To);
936 if (ensureAlways(SizeTo >= 0))
937 {
939 }
940 else
941 {
943 }
944 }
945 }
946 }
947
948 return bSuccess;
949 }
950
951 virtual IFileHandle* OpenWrite(const TCHAR* Filename, bool bAppend = false, bool bAllowRead = false) override
952 {
953 if (!IsReady())
954 {
955 // Always wrap handle if not ready. FManagedStorageFileWriteHandle will start managing the file
956 // internally when we become ready.
958 if (!InnerHandle)
959 {
960 return nullptr;
961 }
962
966 }
967
969
971
973 {
974 UE_LOG(LogPlatformFileManagedStorage, Error, TEXT("Failed to open file %s for write. The category of the file has reach quota limit in peristent storage."), Filename);
975 return nullptr;
976 }
977
979
981 if (!InnerHandle)
982 {
983 return nullptr;
984 }
985
987 if (ManagedFile)
988 {
990 }
991 else
992 {
993 return InnerHandle;
994 }
995 }
996
997 virtual bool CopyFile(const TCHAR* To, const TCHAR* From, EPlatformFileRead ReadFlags = EPlatformFileRead::None, EPlatformFileWrite WriteFlags = EPlatformFileWrite::None) override
998 {
999 if (!IsReady())
1000 {
1001 return BaseClass::CopyFile(To, From, ReadFlags, WriteFlags);
1002 }
1003
1005
1008
1011
1012 const int64 SizeFrom = this->FileSize(From);
1013 if (SizeFrom < 0)
1014 {
1015 return false;
1016 }
1017
1020 {
1021 UE_LOG(LogPlatformFileManagedStorage, Error, TEXT("Failed to copy file to %s. The category of the destination has reach quota limit in peristent storage."), To);
1022 return false;
1023 }
1024
1025 bool bSuccess = BaseClass::CopyFile(To, From, ReadFlags, WriteFlags);
1026 if (bSuccess)
1027 {
1029 }
1030 else if(ManagedTo)
1031 {
1032 if (!this->FileExists(To))
1033 {
1035 }
1036 else
1037 {
1038 const int64 SizeTo = this->FileSize(To);
1039 if (ensureAlways(SizeTo >= 0))
1040 {
1042 }
1043 else
1044 {
1046 }
1047 }
1048 }
1049
1050 return bSuccess;
1051 }
1052};
#define ensureAlways( InExpression)
#define check(expr)
#define LowLevelFatalError(Format,...)
FConfigCacheIni * GConfig
bool GIsCriticalError
@ INDEX_NONE
#define ENUM_CLASS_FLAGS(Enum)
EPersistentStorageManagerFileSizeFlags
Definition Enums.h:7503
DECLARE_LOG_CATEGORY_EXTERN(LogChunkInstaller, Log, All)
EPlatformFileRead
EPlatformFileWrite
#define MAX_int64
#define TEXT(x)
Definition Platform.h:1108
FWindowsCriticalSection FCriticalSection
FWindowsRWLock FRWLock
#define INT64_FMT
FCriticalSection * GetLock(const FString &InFilename)
virtual bool SeekFromEnd(int64 NewPositionRelativeToEnd=0) override
virtual bool Write(const uint8 *Source, int64 BytesToWrite) override
FManagedStorageFileWriteHandle(IFileHandle *InFileHandle, FPersistentManagedFile InFile)
virtual bool Truncate(int64 NewSize) override
virtual bool Flush(const bool bFullFlush=false) override
virtual bool Seek(int64 NewPosition) override
virtual bool Read(uint8 *Destination, int64 BytesToRead) override
FManagedStorageScopeFileLock(FPersistentManagedFile InManagedFile)
Definition Paths.h:36
static FString ConvertRelativePathToFull(const FString &InPath)
static FCategoryInfo InitCategories()
void TryManageFileInternal(FPersistentManagedFile &OutFile)
FPersistentManagedFile TryManageFile(FString &&Filename)
EPersistentStorageManagerFileSizeFlags AddOrUpdateFile(const FPersistentManagedFile &File, const int64 FileSize, EPersistentStorageManagerFileSizeFlags Flags=EPersistentStorageManagerFileSizeFlags::None)
TMap< FString, FPersistentStorageCategory::CategoryStat > GenerateCategoryStats() const
static FPersistentStorageManager & Get()
bool GetPersistentStorageUsageByCategory(const FString &InCategory, int64 &UsedSpace, int64 &RemainingSpace, int64 &Quota, int64 *OptionalQuota=nullptr)
TArrayView< const FString > GetRootDirectories() const
bool IsCategoryForFileFull(const FPersistentManagedFile &File) const
FPersistentManagedFile TryManageFile(const FString &Filename)
TOptional< FPersistentStorageCategory::CategoryStat > GetCategoryStat(const FString &InCategory) const
bool GetPersistentStorageSize(int64 &UsedSpace, int64 &RequiredSpace, int64 &OptionalSpace) const
Returns any additional required free space and optional free space.
bool GetPersistentStorageUsage(FString Path, int64 &UsedSpace, int64 &RemainingSpace, int64 &Quota, int64 *OptionalQuota=nullptr)
bool RemoveFileFromManager(FPersistentManagedFile &File)
UE_NODISCARD_CTOR FReadScopeLock(FRWLock &InLock)
Definition ScopeRWLock.h:14
UE_NODISCARD_CTOR FScopeLock(FCriticalSection *InSynchObject)
Definition ScopeLock.h:35
friend FORCEINLINE uint32 GetTypeHash(const FString &S)
FORCEINLINE void WriteLock()
FORCEINLINE void WriteUnlock()
UE_NODISCARD_CTOR FWriteScopeLock(FRWLock &InLock)
Definition ScopeRWLock.h:35
virtual IFileHandle * OpenWrite(const TCHAR *Filename, bool bAppend=false, bool bAllowRead=false) override
bool IsUnderDirectory(const FString &InPath, const FString &InDirectory)
Definition json.hpp:4518
static bool IsInitialized()
bool IsInCategory(const FString &Path) const
int64 GetCategoryQuota(bool bIncludeOptional=true) const
FPersistentStorageCategory(FString InCategoryName, TArray< FString > InDirectories, const int64 InQuota, const int64 InOptionalQuota)
FManagedStorageFileLockRegistry & GetLockRegistry()
int64 GetAvailableSize(bool bIncludeOptionalStorage=true) const
EPersistentStorageManagerFileSizeFlags AddOrUpdateFile(const FString &Filename, const int64 FileSize, EPersistentStorageManagerFileSizeFlags Flags)
EPersistentStorageManagerFileSizeFlags TryUpdateQuota(const int64 OldFileSize, const int64 NewFileSize, EPersistentStorageManagerFileSizeFlags Flags)
const TArray< FString > & GetDirectories() const
FManagedStorageFileLockRegistry FileLockRegistry
bool ShouldManageFile(const FString &Filename) const
const FPersistentStorageCategory * GetDefaultCategory() const
FCategoryInfo(TArray< FPersistentStorageCategory > &&InCategories, int32 InDefaultCategory)
TArrayView< FPersistentStorageCategory > GetCategories()
TArrayView< const FPersistentStorageCategory > GetCategories() const
void Init(TArrayView< const FPersistentStorageCategory > Categories)