BrokeredFileSystem.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. #include "il2cpp-config.h"
  2. #if IL2CPP_SUPPORTS_BROKERED_FILESYSTEM
  3. #include "os/BrokeredFileSystem.h"
  4. #include "os/Atomic.h"
  5. #include "os/Win32/WindowsHelpers.h"
  6. #include "SynchronousOperation.h"
  7. #include "utils/PathUtils.h"
  8. #include "utils/StringUtils.h"
  9. #include <windows.storage.h>
  10. #include <windows.storage.search.h>
  11. using il2cpp::winrt::MakeSynchronousOperation;
  12. using Microsoft::WRL::ComPtr;
  13. using Microsoft::WRL::Wrappers::HStringReference;
  14. namespace winrt_interfaces
  15. {
  16. enum HANDLE_CREATION_OPTIONS
  17. {
  18. HCO_CREATE_NEW = 0x1,
  19. HCO_CREATE_ALWAYS = 0x2,
  20. HCO_OPEN_EXISTING = 0x3,
  21. HCO_OPEN_ALWAYS = 0x4,
  22. HCO_TRUNCATE_EXISTING = 0x5
  23. };
  24. enum HANDLE_ACCESS_OPTIONS
  25. {
  26. HAO_NONE = 0,
  27. HAO_READ_ATTRIBUTES = 0x80,
  28. HAO_READ = 0x120089,
  29. HAO_WRITE = 0x120116,
  30. HAO_DELETE = 0x10000
  31. };
  32. enum HANDLE_SHARING_OPTIONS
  33. {
  34. HSO_SHARE_NONE = 0,
  35. HSO_SHARE_READ = 0x1,
  36. HSO_SHARE_WRITE = 0x2,
  37. HSO_SHARE_DELETE = 0x4
  38. };
  39. enum HANDLE_OPTIONS
  40. {
  41. HO_NONE = 0,
  42. HO_OPEN_REQUIRING_OPLOCK = 0x40000,
  43. HO_DELETE_ON_CLOSE = 0x4000000,
  44. HO_SEQUENTIAL_SCAN = 0x8000000,
  45. HO_RANDOM_ACCESS = 0x10000000,
  46. HO_NO_BUFFERING = 0x20000000,
  47. HO_OVERLAPPED = 0x40000000,
  48. HO_WRITE_THROUGH = 0x80000000,
  49. HO_ALL_POSSIBLE_OPTIONS = HO_OPEN_REQUIRING_OPLOCK | HO_DELETE_ON_CLOSE | HO_SEQUENTIAL_SCAN | HO_RANDOM_ACCESS | HO_NO_BUFFERING | HO_OVERLAPPED | HO_WRITE_THROUGH,
  50. };
  51. MIDL_INTERFACE("DF19938F-5462-48A0-BE65-D2A3271A08D6")
  52. IStorageFolderHandleAccess : public IUnknown
  53. {
  54. public:
  55. virtual HRESULT STDMETHODCALLTYPE Create(
  56. LPCWSTR fileName,
  57. HANDLE_CREATION_OPTIONS creationOptions,
  58. HANDLE_ACCESS_OPTIONS accessOptions,
  59. HANDLE_SHARING_OPTIONS sharingOptions,
  60. HANDLE_OPTIONS options,
  61. struct IOplockBreakingHandler* oplockBreakingHandler,
  62. HANDLE* interopHandle) = 0;
  63. };
  64. MIDL_INTERFACE("5CA296B2-2C25-4D22-B785-B885C8201E6A")
  65. IStorageItemHandleAccess : public IUnknown
  66. {
  67. public:
  68. virtual HRESULT STDMETHODCALLTYPE Create(
  69. HANDLE_ACCESS_OPTIONS accessOptions,
  70. HANDLE_SHARING_OPTIONS sharingOptions,
  71. HANDLE_OPTIONS options,
  72. struct IOplockBreakingHandler* oplockBreakingHandler,
  73. HANDLE* interopHandle) = 0;
  74. };
  75. }
  76. namespace il2cpp
  77. {
  78. namespace os
  79. {
  80. template<typename T, const wchar_t* className>
  81. struct StaticsStorage
  82. {
  83. ~StaticsStorage()
  84. {
  85. Assert(!s_Initialized && "StaticsStorage was not properly disposed before destruction!");
  86. Assert(s_Statics == nullptr && "StaticsStorage was not properly disposed before destruction!");
  87. }
  88. T* Get()
  89. {
  90. if (s_Initialized)
  91. return s_Statics;
  92. T* statics;
  93. auto hr = RoGetActivationFactory(HStringReference(className).Get(), __uuidof(T), reinterpret_cast<void**>(&statics));
  94. if (FAILED(hr))
  95. {
  96. s_Initialized = true;
  97. return nullptr;
  98. }
  99. // The reason this is atomic isn't to prevent multiple RoGetActivationFactory invocations,
  100. // it's there to make sure we don't mess up reference counting
  101. if (Atomic::CompareExchangePointer<T>(&s_Statics, statics, nullptr) != nullptr)
  102. {
  103. statics->Release();
  104. return s_Statics;
  105. }
  106. s_Initialized = true;
  107. return statics;
  108. }
  109. void Release()
  110. {
  111. s_Initialized = false;
  112. if (s_Statics != nullptr)
  113. {
  114. s_Statics->Release();
  115. s_Statics = nullptr;
  116. }
  117. }
  118. private:
  119. // Note: It is not a smart pointer for atomicity
  120. T* s_Statics;
  121. volatile bool s_Initialized;
  122. };
  123. static StaticsStorage<ABI::Windows::Storage::IStorageFileStatics, RuntimeClass_Windows_Storage_StorageFile> s_StorageFileStatics;
  124. static StaticsStorage<ABI::Windows::Storage::IStorageFolderStatics, RuntimeClass_Windows_Storage_StorageFolder> s_StorageFolderStatics;
  125. static int HResultToWin32OrAccessDenied(HRESULT hr)
  126. {
  127. if (SUCCEEDED(hr))
  128. return ERROR_SUCCESS;
  129. if ((hr & 0xFFFF0000) == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, 0))
  130. return HRESULT_CODE(hr);
  131. return ERROR_ACCESS_DENIED;
  132. }
  133. #define StoreErrorAndReturnIfFailed(hr, valueToReturn) do { if (FAILED(hr)) { *error = HResultToWin32OrAccessDenied(hr); return valueToReturn; } } while (false)
  134. #define StoreErrorAndReturnFalseIfFailed(hr) StoreErrorAndReturnIfFailed(hr, false)
  135. static inline bool IsPathRooted(const UTF16String& path)
  136. {
  137. if (path.empty())
  138. return false;
  139. if (path[0] == '\\')
  140. return true;
  141. return path.length() > 1 && path[1] == ':';
  142. }
  143. static inline void FixSlashes(UTF16String& path)
  144. {
  145. for (auto& c : path)
  146. {
  147. if (c == '/')
  148. c = '\\';
  149. }
  150. }
  151. static UTF16String GetFullPath(UTF16String path)
  152. {
  153. FixSlashes(path);
  154. if (IsPathRooted(path))
  155. return path;
  156. UTF16String fullPath;
  157. DWORD fullPathLength = GetFullPathNameW(path.c_str(), 0, nullptr, nullptr);
  158. Assert(fullPathLength != 0 && "GetFullPathNameW failed!");
  159. do
  160. {
  161. fullPath.resize(fullPathLength);
  162. fullPathLength = GetFullPathNameW(path.c_str(), fullPathLength, &fullPath[0], nullptr);
  163. Assert(fullPathLength != 0 && "GetFullPathNameW failed!");
  164. }
  165. while (fullPathLength > fullPath.size());
  166. fullPath.resize(fullPathLength);
  167. return fullPath;
  168. }
  169. static bool SplitPathToFolderAndFileName(UTF16String path, UTF16String& outFolder, UTF16String& outFile)
  170. {
  171. FixSlashes(path);
  172. wchar_t* filePart = nullptr;
  173. DWORD fullPathLength = GetFullPathNameW(path.c_str(), 0, nullptr, nullptr);
  174. Assert(fullPathLength != 0 && "GetFullPathNameW failed!");
  175. do
  176. {
  177. outFolder.resize(fullPathLength);
  178. fullPathLength = GetFullPathNameW(path.c_str(), fullPathLength, &outFolder[0], &filePart);
  179. Assert(fullPathLength != 0 && "GetFullPathNameW failed!");
  180. }
  181. while (fullPathLength > outFolder.size());
  182. if (filePart != nullptr)
  183. {
  184. outFile = filePart;
  185. outFolder.resize(filePart - &outFolder[0] - 1);
  186. return true;
  187. }
  188. else
  189. {
  190. outFolder.resize(fullPathLength);
  191. outFile.clear();
  192. return false;
  193. }
  194. }
  195. static HRESULT GetStorageFolderAsync(const UTF16String& path, ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFolder*>** operation)
  196. {
  197. Assert(IsPathRooted(path) && "GetStorageFolder expects an absolute path.");
  198. auto storageFolderStatics = s_StorageFolderStatics.Get();
  199. Assert(storageFolderStatics != nullptr && "Failed to get StorageFolder statics");
  200. return storageFolderStatics->GetFolderFromPathAsync(HStringReference(path.c_str(), static_cast<uint32_t>(path.length())).Get(), operation);
  201. }
  202. static HRESULT GetStorageFolder(const UTF16String& path, ABI::Windows::Storage::IStorageFolder** storageFolder)
  203. {
  204. ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFolder*> > operation;
  205. auto hr = GetStorageFolderAsync(path, &operation);
  206. if (FAILED(hr))
  207. return hr;
  208. return MakeSynchronousOperation(operation.Get())->GetResults(storageFolder);
  209. }
  210. static HRESULT GetStorageFileAsync(const UTF16String& path, ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFile*>** operation)
  211. {
  212. Assert(IsPathRooted(path) && "GetStorageFile expects an absolute path.");
  213. auto storageFileStatics = s_StorageFileStatics.Get();
  214. Assert(storageFileStatics != nullptr && "Failed to get StorageFile statics");
  215. return storageFileStatics->GetFileFromPathAsync(HStringReference(path.c_str(), static_cast<uint32_t>(path.length())).Get(), operation);
  216. }
  217. static HRESULT GetStorageFile(const UTF16String& path, ABI::Windows::Storage::IStorageFile** storageFile)
  218. {
  219. ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFile*> > operation;
  220. auto hr = GetStorageFileAsync(path, &operation);
  221. if (FAILED(hr))
  222. return hr;
  223. return MakeSynchronousOperation(operation.Get())->GetResults(storageFile);
  224. }
  225. static HRESULT AsStorageItem(IInspectable* itf, ABI::Windows::Storage::IStorageItem** storageItem)
  226. {
  227. return itf->QueryInterface(__uuidof(*storageItem), reinterpret_cast<void**>(storageItem));
  228. }
  229. static HRESULT GetStorageItem(const UTF16String& path, ABI::Windows::Storage::IStorageItem** storageItem)
  230. {
  231. using namespace ABI::Windows::Foundation;
  232. using namespace ABI::Windows::Storage;
  233. auto fullPath = GetFullPath(path);
  234. // We don't know whether it's a folder or a file. Try getting a file first
  235. ComPtr<IStorageFile> storageFile;
  236. auto hr = GetStorageFile(fullPath, &storageFile);
  237. if (SUCCEEDED(hr))
  238. return AsStorageItem(storageFile.Get(), storageItem);
  239. // Perhaps it's not a file but a folder?
  240. ComPtr<IStorageFolder> storageFolder;
  241. hr = GetStorageFolder(fullPath, &storageFolder);
  242. if (SUCCEEDED(hr))
  243. return AsStorageItem(storageFolder.Get(), storageItem);
  244. return hr;
  245. }
  246. int BrokeredFileSystem::CreateDirectoryW(const UTF16String& path)
  247. {
  248. using namespace ABI::Windows::Foundation;
  249. using namespace ABI::Windows::Storage;
  250. ComPtr<IAsyncOperation<StorageFolder*> > creationOperation;
  251. {
  252. UTF16String parentFolderName, name;
  253. if (!SplitPathToFolderAndFileName(path, parentFolderName, name))
  254. return ERROR_ACCESS_DENIED;
  255. ComPtr<IStorageFolder> parentFolder;
  256. auto hr = GetStorageFolder(parentFolderName, &parentFolder);
  257. if (FAILED(hr))
  258. return HResultToWin32OrAccessDenied(hr);
  259. hr = parentFolder->CreateFolderAsync(HStringReference(name.c_str(), static_cast<uint32_t>(name.length())).Get(), CreationCollisionOption_FailIfExists, &creationOperation);
  260. if (FAILED(hr))
  261. return HResultToWin32OrAccessDenied(hr);
  262. }
  263. auto hr = MakeSynchronousOperation(creationOperation.Get())->Wait();
  264. if (FAILED(hr))
  265. return HResultToWin32OrAccessDenied(hr);
  266. return kErrorCodeSuccess;
  267. }
  268. static int DeleteStorageItem(ABI::Windows::Storage::IStorageItem* storageItem)
  269. {
  270. using namespace ABI::Windows::Foundation;
  271. using namespace ABI::Windows::Storage;
  272. ComPtr<IAsyncAction> deletionAction;
  273. auto hr = storageItem->DeleteAsync(StorageDeleteOption_PermanentDelete, &deletionAction);
  274. if (FAILED(hr))
  275. return HResultToWin32OrAccessDenied(hr);
  276. hr = MakeSynchronousOperation(deletionAction.Get())->Wait();
  277. if (FAILED(hr))
  278. return HResultToWin32OrAccessDenied(hr);
  279. return kErrorCodeSuccess;
  280. }
  281. int BrokeredFileSystem::RemoveDirectoryW(const UTF16String& path)
  282. {
  283. using namespace ABI::Windows::Foundation;
  284. using namespace ABI::Windows::Storage;
  285. auto fullPath = GetFullPath(path);
  286. ComPtr<IStorageFolder> storageFolder;
  287. auto hr = GetStorageFolder(fullPath, &storageFolder);
  288. if (FAILED(hr))
  289. return HResultToWin32OrAccessDenied(hr);
  290. ComPtr<IStorageItem> storageItem;
  291. hr = storageFolder.As(&storageItem);
  292. if (FAILED(hr))
  293. return HResultToWin32OrAccessDenied(hr);
  294. return DeleteStorageItem(storageItem.Get());
  295. }
  296. static UnityPalFileAttributes TranslateWinRTAttributesToPALAttributes(ABI::Windows::Storage::FileAttributes winrtAttributes)
  297. {
  298. // Normal file attribute enum value is different.
  299. // The rest are the same.
  300. if (winrtAttributes == ABI::Windows::Storage::FileAttributes_Normal)
  301. return kFileAttributeNormal;
  302. return static_cast<UnityPalFileAttributes>(winrtAttributes);
  303. }
  304. static ABI::Windows::Storage::FileAttributes TranslatePALAttributesToWinRTAttributes(UnityPalFileAttributes attributes)
  305. {
  306. return static_cast<ABI::Windows::Storage::FileAttributes>(attributes & ~kFileAttributeNormal);
  307. }
  308. static HRESULT FindFileSystemEntries(const UTF16String& path, UTF16String pathWithPattern, int* error, ABI::Windows::Foundation::Collections::IVectorView<ABI::Windows::Storage::IStorageItem*>** foundItems)
  309. {
  310. using namespace ABI::Windows::Foundation;
  311. using namespace ABI::Windows::Foundation::Collections;
  312. using namespace ABI::Windows::Storage;
  313. using namespace ABI::Windows::Storage::Search;
  314. ComPtr<IAsyncOperation<StorageFolder*> > getFolderOperation;
  315. auto hr = GetStorageFolderAsync(GetFullPath(path), &getFolderOperation);
  316. StoreErrorAndReturnIfFailed(hr, hr);
  317. ComPtr<IInspectable> queryOptionsInspectable;
  318. hr = RoActivateInstance(HStringReference(RuntimeClass_Windows_Storage_Search_QueryOptions).Get(), &queryOptionsInspectable);
  319. StoreErrorAndReturnIfFailed(hr, hr);
  320. ComPtr<IQueryOptions> queryOptions;
  321. hr = queryOptionsInspectable.As(&queryOptions);
  322. IL2CPP_ASSERT(SUCCEEDED(hr) && "Failed to cast QueryOptions to IQueryOptions");
  323. hr = queryOptions->put_FolderDepth(FolderDepth_Shallow); // We're doing a non-recursive search
  324. StoreErrorAndReturnIfFailed(hr, hr);
  325. auto aqs = L"System.ItemPathDisplay:~\"" + GetFullPath(std::move(pathWithPattern)) + L"\"";
  326. hr = queryOptions->put_ApplicationSearchFilter(HStringReference(aqs.c_str(), static_cast<uint32_t>(aqs.length())).Get());
  327. StoreErrorAndReturnIfFailed(hr, hr);
  328. ComPtr<IStorageFolder> folderToSearch;
  329. hr = MakeSynchronousOperation(getFolderOperation.Get())->GetResults(&folderToSearch);
  330. StoreErrorAndReturnIfFailed(hr, hr);
  331. ComPtr<IStorageFolderQueryOperations> folderQueryOperations;
  332. hr = folderToSearch.As(&folderQueryOperations);
  333. IL2CPP_ASSERT(SUCCEEDED(hr) && "Failed to cast StorageFolder to IStorageFolderQueryOperations!");
  334. ComPtr<IStorageItemQueryResult> queryResult;
  335. hr = folderQueryOperations->CreateItemQueryWithOptions(queryOptions.Get(), &queryResult);
  336. StoreErrorAndReturnIfFailed(hr, hr);
  337. ComPtr<IAsyncOperation<IVectorView<IStorageItem*>*> > itemsOperation;
  338. hr = queryResult->GetItemsAsyncDefaultStartAndCount(&itemsOperation);
  339. StoreErrorAndReturnIfFailed(hr, hr);
  340. hr = MakeSynchronousOperation(itemsOperation.Get())->GetResults(foundItems);
  341. StoreErrorAndReturnIfFailed(hr, hr);
  342. return hr;
  343. }
  344. std::set<std::string> BrokeredFileSystem::GetFileSystemEntries(const UTF16String& path, const UTF16String& pathWithPattern, int32_t attributes, int32_t attributeMask, int* error)
  345. {
  346. using namespace ABI::Windows::Foundation::Collections;
  347. using namespace ABI::Windows::Storage;
  348. std::set<std::string> fileSystemEntries;
  349. ComPtr<IVectorView<IStorageItem*> > foundItems;
  350. auto hr = FindFileSystemEntries(path, pathWithPattern, error, &foundItems);
  351. StoreErrorAndReturnIfFailed(hr, fileSystemEntries);
  352. uint32_t foundCount;
  353. hr = foundItems->get_Size(&foundCount);
  354. StoreErrorAndReturnIfFailed(hr, fileSystemEntries);
  355. for (uint32_t i = 0; i < foundCount; i++)
  356. {
  357. ComPtr<IStorageItem> item;
  358. hr = foundItems->GetAt(i, &item);
  359. if (FAILED(hr)) continue;
  360. FileAttributes winrtAttributes;
  361. hr = item->get_Attributes(&winrtAttributes);
  362. if (FAILED(hr)) continue;
  363. auto palAttributes = TranslateWinRTAttributesToPALAttributes(winrtAttributes);
  364. if ((palAttributes & attributeMask) == attributes)
  365. {
  366. Microsoft::WRL::Wrappers::HString path;
  367. hr = item->get_Path(path.GetAddressOf());
  368. if (FAILED(hr)) continue;
  369. uint32_t pathLength;
  370. auto pathStr = path.GetRawBuffer(&pathLength);
  371. fileSystemEntries.insert(utils::StringUtils::Utf16ToUtf8(pathStr, pathLength));
  372. }
  373. }
  374. return fileSystemEntries;
  375. }
  376. os::ErrorCode BrokeredFileSystem::FindFirstFileW(Directory::FindHandle* findHandle, const utils::StringView<Il2CppNativeChar>& searchPathWithPattern, Il2CppNativeString* resultFileName, int32_t* resultAttributes)
  377. {
  378. using namespace ABI::Windows::Foundation::Collections;
  379. using namespace ABI::Windows::Storage;
  380. int error;
  381. UTF16String searchPath(searchPathWithPattern.Str(), searchPathWithPattern.Length());
  382. FixSlashes(searchPath);
  383. ComPtr<IVectorView<IStorageItem*> > foundItems;
  384. auto hr = FindFileSystemEntries(utils::PathUtils::DirectoryName(searchPath), searchPath, &error, &foundItems);
  385. if (FAILED(hr))
  386. return static_cast<os::ErrorCode>(error);
  387. ComPtr<IIterable<IStorageItem*> > foundItemsIterable;
  388. hr = foundItems.As(&foundItemsIterable);
  389. IL2CPP_ASSERT(SUCCEEDED(hr) && "Failed to cast IVectorView<IStorageItem*> to IIterable<IStorageItem*>");
  390. ComPtr<IIterator<IStorageItem*> > iterator;
  391. hr = foundItemsIterable->First(&iterator);
  392. if (FAILED(hr))
  393. return static_cast<os::ErrorCode>(HResultToWin32OrAccessDenied(hr));
  394. *resultAttributes = kFileAttributeDirectory;
  395. *resultFileName = L".";
  396. findHandle->handleFlags = kUseBrokeredFileSystem;
  397. findHandle->SetOSHandle(iterator.Detach());
  398. return kErrorCodeSuccess;
  399. }
  400. os::ErrorCode BrokeredFileSystem::FindNextFileW(Directory::FindHandle* findHandle, Il2CppNativeString* resultFileName, int32_t* resultAttributes)
  401. {
  402. using namespace ABI::Windows::Foundation::Collections;
  403. using namespace ABI::Windows::Storage;
  404. IL2CPP_ASSERT(findHandle->handleFlags & kUseBrokeredFileSystem);
  405. auto iterator = static_cast<IIterator<IStorageItem*>*>(findHandle->osHandle);
  406. boolean hasCurrent;
  407. auto hr = iterator->get_HasCurrent(&hasCurrent);
  408. if (FAILED(hr))
  409. return static_cast<os::ErrorCode>(HResultToWin32OrAccessDenied(hr));
  410. if (!hasCurrent)
  411. return kErrorCodeNoMoreFiles;
  412. ComPtr<IStorageItem> storageItem;
  413. hr = iterator->get_Current(&storageItem);
  414. if (FAILED(hr))
  415. return static_cast<os::ErrorCode>(HResultToWin32OrAccessDenied(hr));
  416. hr = iterator->MoveNext(&hasCurrent);
  417. if (FAILED(hr))
  418. return static_cast<os::ErrorCode>(HResultToWin32OrAccessDenied(hr));
  419. Microsoft::WRL::Wrappers::HString name;
  420. hr = storageItem->get_Name(name.GetAddressOf());
  421. if (FAILED(hr))
  422. return static_cast<os::ErrorCode>(HResultToWin32OrAccessDenied(hr));
  423. FileAttributes winrtAttributes;
  424. hr = storageItem->get_Attributes(&winrtAttributes);
  425. if (FAILED(hr))
  426. return static_cast<os::ErrorCode>(HResultToWin32OrAccessDenied(hr));
  427. uint32_t nameLength;
  428. auto nameBuffer = name.GetRawBuffer(&nameLength);
  429. resultFileName->assign(nameBuffer, nameBuffer + nameLength);
  430. *resultAttributes = TranslateWinRTAttributesToPALAttributes(winrtAttributes);
  431. return kErrorCodeSuccess;
  432. }
  433. os::ErrorCode BrokeredFileSystem::FindClose(void* osHandle)
  434. {
  435. using namespace ABI::Windows::Foundation::Collections;
  436. using namespace ABI::Windows::Storage;
  437. static_cast<IIterator<IStorageItem*>*>(osHandle)->Release();
  438. return kErrorCodeSuccess;
  439. }
  440. template<typename Operation>
  441. static bool MoveOrCopyFile(const UTF16String& source, const UTF16String& destination, int* error, Operation&& performOperation)
  442. {
  443. using namespace ABI::Windows::Foundation;
  444. using namespace ABI::Windows::Storage;
  445. ComPtr<IAsyncOperation<StorageFile*> > getSourceFileOp;
  446. ComPtr<IAsyncOperation<StorageFolder*> > getDestinationFolderOp;
  447. auto fullSourcePath = GetFullPath(source);
  448. auto hr = GetStorageFileAsync(fullSourcePath, &getSourceFileOp);
  449. StoreErrorAndReturnFalseIfFailed(hr);
  450. UTF16String destinationFolderPath, destinationFileName;
  451. if (!SplitPathToFolderAndFileName(destination, destinationFolderPath, destinationFileName))
  452. {
  453. *error = ERROR_ACCESS_DENIED;
  454. return false;
  455. }
  456. hr = GetStorageFolderAsync(destinationFolderPath, &getDestinationFolderOp);
  457. StoreErrorAndReturnFalseIfFailed(hr);
  458. // We start getting both source file and destination folder before waiting on the first async operation to complete.
  459. ComPtr<IStorageFile> sourceFile;
  460. hr = MakeSynchronousOperation(getSourceFileOp.Get())->GetResults(&sourceFile);
  461. if (FAILED(hr))
  462. {
  463. auto originalHR = hr;
  464. // If source is not a file but a folder, we need to fail with E_ACCESSDENIED
  465. // In this case, GetStorageFile fails with E_INVALIDARG but we cannot tell whether
  466. // that means that the path is malformed or if it points to a folder, so we try to
  467. // get a folder and if we succeed, we change the originalHR to E_ACCESSDENIED
  468. ComPtr<IStorageFolder> sourceFolder;
  469. hr = GetStorageFolder(fullSourcePath, &sourceFolder);
  470. if (SUCCEEDED(hr))
  471. originalHR = E_ACCESSDENIED;
  472. StoreErrorAndReturnFalseIfFailed(originalHR);
  473. }
  474. ComPtr<IStorageFolder> destinationFolder;
  475. hr = MakeSynchronousOperation(getDestinationFolderOp.Get())->GetResults(&destinationFolder);
  476. if (FAILED(hr))
  477. {
  478. // If we cannot retrieve destination folder, we should return ERROR_PATH_NOT_FOUND
  479. if (hr == E_INVALIDARG || hr == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND))
  480. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_PATH_NOT_FOUND);
  481. StoreErrorAndReturnFalseIfFailed(hr);
  482. }
  483. HStringReference destinationFileNameHString(destinationFileName.c_str(), static_cast<uint32_t>(destinationFileName.length()));
  484. hr = performOperation(sourceFile.Get(), destinationFolder.Get(), destinationFileNameHString.Get());
  485. if (FAILED(hr))
  486. {
  487. // We're being consistent with WIN32 API here
  488. if (hr == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_ALREADY_EXISTS))
  489. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_EXISTS);
  490. StoreErrorAndReturnFalseIfFailed(hr);
  491. }
  492. *error = kErrorCodeSuccess;
  493. return true;
  494. }
  495. bool BrokeredFileSystem::CopyFileW(const UTF16String& source, const UTF16String& destination, bool overwrite, int* error)
  496. {
  497. using namespace ABI::Windows::Foundation;
  498. using namespace ABI::Windows::Storage;
  499. return MoveOrCopyFile(source, destination, error, [overwrite](IStorageFile* sourceFile, IStorageFolder* destinationFolder, HSTRING destinationFileName)
  500. {
  501. NameCollisionOption collisionOption = overwrite ? NameCollisionOption_ReplaceExisting : NameCollisionOption_FailIfExists;
  502. ComPtr<IAsyncOperation<StorageFile*> > copyOperation;
  503. auto hr = sourceFile->CopyOverload(destinationFolder, destinationFileName, collisionOption, &copyOperation);
  504. if (FAILED(hr))
  505. return hr;
  506. return MakeSynchronousOperation(copyOperation.Get())->Wait();
  507. });
  508. }
  509. bool BrokeredFileSystem::MoveFileW(const UTF16String& source, const UTF16String& destination, int * error)
  510. {
  511. using namespace ABI::Windows::Foundation;
  512. using namespace ABI::Windows::Storage;
  513. return MoveOrCopyFile(source, destination, error, [](IStorageFile* sourceFile, IStorageFolder* destinationFolder, HSTRING destinationFileName)
  514. {
  515. ComPtr<IAsyncAction> moveOperation;
  516. auto hr = sourceFile->MoveOverloadDefaultOptions(destinationFolder, destinationFileName, &moveOperation);
  517. if (FAILED(hr))
  518. return hr;
  519. return MakeSynchronousOperation(moveOperation.Get())->Wait();
  520. });
  521. }
  522. int BrokeredFileSystem::DeleteFileW(const UTF16String& path)
  523. {
  524. using namespace ABI::Windows::Foundation;
  525. using namespace ABI::Windows::Storage;
  526. auto fullPath = GetFullPath(path);
  527. ComPtr<IStorageFile> storageFile;
  528. auto hr = GetStorageFile(fullPath, &storageFile);
  529. if (FAILED(hr))
  530. return HResultToWin32OrAccessDenied(hr);
  531. ComPtr<IStorageItem> storageItem;
  532. hr = storageFile.As(&storageItem);
  533. if (FAILED(hr))
  534. return HResultToWin32OrAccessDenied(hr);
  535. return DeleteStorageItem(storageItem.Get());
  536. }
  537. UnityPalFileAttributes BrokeredFileSystem::GetFileAttributesW(const UTF16String& path, int* error)
  538. {
  539. ComPtr<ABI::Windows::Storage::IStorageItem> storageItem;
  540. auto hr = GetStorageItem(path, &storageItem);
  541. if (FAILED(hr))
  542. {
  543. *error = HResultToWin32OrAccessDenied(hr);
  544. return static_cast<UnityPalFileAttributes>(INVALID_FILE_ATTRIBUTES);
  545. }
  546. ABI::Windows::Storage::FileAttributes attributes;
  547. hr = storageItem->get_Attributes(&attributes);
  548. if (FAILED(hr))
  549. {
  550. *error = HResultToWin32OrAccessDenied(hr);
  551. return static_cast<UnityPalFileAttributes>(INVALID_FILE_ATTRIBUTES);
  552. }
  553. *error = kErrorCodeSuccess;
  554. return TranslateWinRTAttributesToPALAttributes(attributes);
  555. }
  556. struct StringObjectKeyValuePair : Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::WinRtClassicComMix>, Microsoft::WRL::FtmBase, ABI::Windows::Foundation::Collections::IKeyValuePair<HSTRING, IInspectable*> >
  557. {
  558. public:
  559. StringObjectKeyValuePair(Microsoft::WRL::Wrappers::HString key, ComPtr<IInspectable> value) :
  560. m_Key(std::move(key)),
  561. m_Value(std::move(value))
  562. {
  563. }
  564. HRESULT __stdcall get_Key(HSTRING* key) override
  565. {
  566. return WindowsDuplicateString(m_Key.Get(), key);
  567. }
  568. HRESULT __stdcall get_Value(IInspectable** value) override
  569. {
  570. *value = m_Value.Get();
  571. (*value)->AddRef();
  572. return S_OK;
  573. }
  574. private:
  575. Microsoft::WRL::Wrappers::HString m_Key;
  576. ComPtr<IInspectable> m_Value;
  577. };
  578. // Wraps a single object in IIterable collection
  579. // Used by SetFileAttributesW and GetFileStat... we only ever need one item so there's no reason to implement a full collection
  580. template<typename T>
  581. struct SingleItemIterable : Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::WinRtClassicComMix>, Microsoft::WRL::FtmBase, ABI::Windows::Foundation::Collections::IIterable<T> >
  582. {
  583. SingleItemIterable(T value) :
  584. m_Value(value)
  585. {
  586. il2cpp::winrt::ReferenceCounter<T>::AddRef(m_Value);
  587. }
  588. ~SingleItemIterable()
  589. {
  590. il2cpp::winrt::ReferenceCounter<T>::Release(m_Value);
  591. }
  592. HRESULT __stdcall First(ABI::Windows::Foundation::Collections::IIterator<T>** first) override
  593. {
  594. *first = Microsoft::WRL::Make<Iterator>(m_Value).Detach();
  595. return S_OK;
  596. }
  597. private:
  598. T m_Value;
  599. struct Iterator : Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::WinRtClassicComMix>, Microsoft::WRL::FtmBase, ABI::Windows::Foundation::Collections::IIterator<T> >
  600. {
  601. Iterator(T value) :
  602. m_Value(std::move(value)),
  603. m_HasValue(true)
  604. {
  605. il2cpp::winrt::ReferenceCounter<T>::AddRef(m_Value);
  606. }
  607. ~Iterator()
  608. {
  609. il2cpp::winrt::ReferenceCounter<T>::Release(m_Value);
  610. }
  611. virtual HRESULT __stdcall get_Current(T* current) override
  612. {
  613. if (!m_HasValue)
  614. return E_BOUNDS;
  615. *current = m_Value;
  616. il2cpp::winrt::ReferenceCounter<T>::AddRef(*current);
  617. return S_OK;
  618. }
  619. virtual HRESULT __stdcall get_HasCurrent(boolean* hasCurrent) override
  620. {
  621. *hasCurrent = m_HasValue;
  622. return S_OK;
  623. }
  624. virtual HRESULT __stdcall MoveNext(boolean* hasCurrent) override
  625. {
  626. *hasCurrent = m_HasValue = false;
  627. return S_OK;
  628. }
  629. private:
  630. T m_Value;
  631. bool m_HasValue;
  632. };
  633. };
  634. static HRESULT GetStorageItemBasicProperties(ABI::Windows::Storage::IStorageItem* storageItem, ABI::Windows::Storage::FileProperties::IBasicProperties** result)
  635. {
  636. ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::FileProperties::BasicProperties*> > filePropertiesGetOperation;
  637. auto hr = storageItem->GetBasicPropertiesAsync(&filePropertiesGetOperation);
  638. if (FAILED(hr))
  639. return hr;
  640. return MakeSynchronousOperation(filePropertiesGetOperation.Get())->GetResults(result);
  641. }
  642. bool BrokeredFileSystem::SetFileAttributesW(const UTF16String& path, UnityPalFileAttributes attributes, int* error)
  643. {
  644. using namespace ABI::Windows::Foundation;
  645. using namespace ABI::Windows::Foundation::Collections;
  646. using namespace ABI::Windows::Storage;
  647. using namespace ABI::Windows::Storage::FileProperties;
  648. ComPtr<IAsyncAction> savePropertiesAction;
  649. {
  650. ComPtr<IStorageItem> storageItem;
  651. auto hr = GetStorageItem(path, &storageItem);
  652. StoreErrorAndReturnFalseIfFailed(hr);
  653. ComPtr<IBasicProperties> fileProperties;
  654. hr = GetStorageItemBasicProperties(storageItem.Get(), &fileProperties);
  655. StoreErrorAndReturnFalseIfFailed(hr);
  656. ComPtr<IStorageItemExtraProperties> extraFileProperties;
  657. hr = fileProperties.As(&extraFileProperties);
  658. StoreErrorAndReturnFalseIfFailed(hr);
  659. ComPtr<IPropertyValueStatics> propertyValueStatics;
  660. hr = RoGetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), __uuidof(propertyValueStatics), &propertyValueStatics);
  661. IL2CPP_ASSERT(SUCCEEDED(hr) && "Failed to get PropertyValue statics!"); // This should never fail.
  662. Microsoft::WRL::Wrappers::HString propertyKey;
  663. hr = propertyKey.Set(L"System.FileAttributes");
  664. StoreErrorAndReturnFalseIfFailed(hr);
  665. ComPtr<IInspectable> attributesValue;
  666. hr = propertyValueStatics->CreateUInt32(TranslatePALAttributesToWinRTAttributes(attributes), &attributesValue);
  667. StoreErrorAndReturnFalseIfFailed(hr);
  668. auto pair = Microsoft::WRL::Make<StringObjectKeyValuePair>(std::move(propertyKey), std::move(attributesValue));
  669. auto propertyPair = Microsoft::WRL::Make<SingleItemIterable<IKeyValuePair<HSTRING, IInspectable*>*> >(pair.Get());
  670. hr = extraFileProperties->SavePropertiesAsync(propertyPair.Get(), &savePropertiesAction);
  671. StoreErrorAndReturnFalseIfFailed(hr);
  672. // We release all unneeded smart pointers before waiting on async operation
  673. }
  674. auto hr = MakeSynchronousOperation(savePropertiesAction.Get())->Wait();
  675. StoreErrorAndReturnFalseIfFailed(hr);
  676. *error = il2cpp::os::ErrorCode::kErrorCodeSuccess;
  677. return true;
  678. }
  679. bool BrokeredFileSystem::GetFileStat(const std::string& utf8Path, const UTF16String& path, FileStat* stat, int* error)
  680. {
  681. using namespace ABI::Windows::Foundation;
  682. using namespace ABI::Windows::Foundation::Collections;
  683. using namespace ABI::Windows::Storage;
  684. using namespace ABI::Windows::Storage::FileProperties;
  685. FileAttributes winrtAttributes;
  686. DateTime creationDate, modificationDate, accessDate;
  687. UINT64 fileSize;
  688. ComPtr<IAsyncOperation<IMap<HSTRING, IInspectable*>*> > propertiesRetrievalOperation;
  689. HStringReference dateAccessedKeyString(L"System.DateAccessed");
  690. {
  691. ComPtr<IStorageItem> storageItem;
  692. auto hr = GetStorageItem(path, &storageItem);
  693. StoreErrorAndReturnFalseIfFailed(hr);
  694. hr = storageItem->get_Attributes(&winrtAttributes);
  695. StoreErrorAndReturnFalseIfFailed(hr);
  696. hr = storageItem->get_DateCreated(&creationDate);
  697. StoreErrorAndReturnFalseIfFailed(hr);
  698. ComPtr<IBasicProperties> fileProperties;
  699. hr = GetStorageItemBasicProperties(storageItem.Get(), &fileProperties);
  700. StoreErrorAndReturnFalseIfFailed(hr);
  701. hr = fileProperties->get_DateModified(&modificationDate);
  702. StoreErrorAndReturnFalseIfFailed(hr);
  703. hr = fileProperties->get_Size(&fileSize);
  704. StoreErrorAndReturnFalseIfFailed(hr);
  705. ComPtr<IStorageItemExtraProperties> extraFileProperties;
  706. hr = fileProperties.As(&extraFileProperties);
  707. StoreErrorAndReturnFalseIfFailed(hr);
  708. auto dateAccessedKey = Microsoft::WRL::Make<SingleItemIterable<HSTRING> >(dateAccessedKeyString.Get());
  709. hr = extraFileProperties->RetrievePropertiesAsync(dateAccessedKey.Get(), &propertiesRetrievalOperation);
  710. StoreErrorAndReturnFalseIfFailed(hr);
  711. // We release all unneeded smart pointers before waiting on async operation
  712. }
  713. ComPtr<IMap<HSTRING, IInspectable*> > propertiesMap;
  714. auto hr = MakeSynchronousOperation(propertiesRetrievalOperation.Get())->GetResults(&propertiesMap);
  715. StoreErrorAndReturnFalseIfFailed(hr);
  716. ComPtr<IInspectable> accessDateInspectable; // This will fail for certain file types
  717. if (SUCCEEDED(propertiesMap->Lookup(dateAccessedKeyString.Get(), &accessDateInspectable)))
  718. {
  719. ComPtr<IReference<DateTime> > boxedAccessDate;
  720. hr = accessDateInspectable.As(&boxedAccessDate);
  721. StoreErrorAndReturnFalseIfFailed(hr);
  722. hr = boxedAccessDate->get_Value(&accessDate);
  723. StoreErrorAndReturnFalseIfFailed(hr);
  724. }
  725. else
  726. {
  727. // Fallback to modification date if failed
  728. accessDate = modificationDate;
  729. }
  730. stat->attributes = TranslateWinRTAttributesToPALAttributes(winrtAttributes);
  731. stat->name = il2cpp::utils::PathUtils::Basename(utf8Path);
  732. stat->length = fileSize;
  733. stat->creation_time = creationDate.UniversalTime;
  734. stat->last_write_time = modificationDate.UniversalTime;
  735. stat->last_access_time = accessDate.UniversalTime;
  736. *error = il2cpp::os::ErrorCode::kErrorCodeSuccess;
  737. return true;
  738. }
  739. FileHandle* BrokeredFileSystem::Open(const UTF16String& path, uint32_t desiredAccess, uint32_t shareMode, uint32_t creationDisposition, uint32_t flagsAndAttributes, int* error)
  740. {
  741. using namespace ABI::Windows::Foundation;
  742. using namespace ABI::Windows::Storage;
  743. UTF16String parentFolderName, name;
  744. if (!SplitPathToFolderAndFileName(path, parentFolderName, name))
  745. {
  746. *error = ERROR_ACCESS_DENIED;
  747. return reinterpret_cast<FileHandle*>(INVALID_HANDLE_VALUE);
  748. }
  749. ComPtr<IStorageFolder> parentFolder;
  750. auto hr = GetStorageFolder(parentFolderName, &parentFolder);
  751. if (FAILED(hr))
  752. {
  753. *error = HResultToWin32OrAccessDenied(hr);
  754. return reinterpret_cast<FileHandle*>(INVALID_HANDLE_VALUE);
  755. }
  756. ComPtr<winrt_interfaces::IStorageFolderHandleAccess> folderHandleAccess;
  757. hr = parentFolder.As(&folderHandleAccess);
  758. if (FAILED(hr))
  759. {
  760. *error = ERROR_ACCESS_DENIED;
  761. return reinterpret_cast<FileHandle*>(INVALID_HANDLE_VALUE);
  762. }
  763. int translatedAccess = winrt_interfaces::HAO_NONE;
  764. if (desiredAccess & GENERIC_READ)
  765. translatedAccess |= winrt_interfaces::HAO_READ | winrt_interfaces::HAO_READ_ATTRIBUTES;
  766. if (desiredAccess & GENERIC_WRITE)
  767. translatedAccess |= winrt_interfaces::HAO_WRITE;
  768. HANDLE fileHandle;
  769. hr = folderHandleAccess->Create(name.c_str(),
  770. static_cast<winrt_interfaces::HANDLE_CREATION_OPTIONS>(creationDisposition),
  771. static_cast<winrt_interfaces::HANDLE_ACCESS_OPTIONS>(translatedAccess),
  772. static_cast<winrt_interfaces::HANDLE_SHARING_OPTIONS>(shareMode),
  773. static_cast<winrt_interfaces::HANDLE_OPTIONS>(flagsAndAttributes & winrt_interfaces::HO_ALL_POSSIBLE_OPTIONS),
  774. nullptr,
  775. &fileHandle);
  776. if (FAILED(hr))
  777. {
  778. *error = ERROR_ACCESS_DENIED;
  779. return reinterpret_cast<FileHandle*>(INVALID_HANDLE_VALUE);
  780. }
  781. *error = ERROR_SUCCESS;
  782. return reinterpret_cast<FileHandle*>(fileHandle);
  783. }
  784. void BrokeredFileSystem::CleanupStatics()
  785. {
  786. s_StorageFileStatics.Release();
  787. s_StorageFolderStatics.Release();
  788. }
  789. }
  790. }
  791. #endif