// Build in an x64 Native Tools Command Prompt for Visual Studio: // cl /nologo /std:c++17 /EHsc /W4 repro.cpp /Fe:repro.exe // Run: repro.exe #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma comment(lib, "CldApi.lib") #pragma comment(lib, "WindowsApp.lib") #pragma comment(lib, "Ole32.lib") #pragma comment(lib, "Shell32.lib") #pragma comment(lib, "Advapi32.lib") namespace fs = std::filesystem; using namespace winrt::Windows::Storage; using namespace winrt::Windows::Storage::Provider; bool cleanupOk = true; std::atomic populationError{S_OK}; constexpr std::array directories = {L"tree", L"tree/a", L"tree/a/b", L"tree/a/b/c", L"tree/d", L"tree/d/e", L"tree/d/e/f", L"tree/empty", L"tree/empty/nested"}; constexpr std::array files = {L"tree/a/b/c/file.bin", L"tree/d/e/f/file.bin"}; void check(HRESULT hr, const wchar_t* operation) { if (FAILED(hr)) throw winrt::hresult_error(hr, operation); } void checkWin(BOOL success, const wchar_t* operation) { if (!success) check(HRESULT_FROM_WIN32(GetLastError()), operation); } std::wstring sid() { winrt::handle token; checkWin(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, token.put()), L"OpenProcessToken"); DWORD size = 0; GetTokenInformation(token.get(), TokenUser, nullptr, 0, &size); std::vector buffer(size); checkWin(GetTokenInformation(token.get(), TokenUser, buffer.data(), size, &size), L"GetTokenInformation"); LPWSTR value = nullptr; checkWin(ConvertSidToStringSidW(reinterpret_cast(buffer.data())->User.Sid, &value), L"ConvertSidToStringSid"); std::wstring result(value); LocalFree(value); return result; } // All fixture entries already exist. Enumeration only needs to be completed; // this callback never creates entries, uploads data, or deletes anything. void CALLBACK populateExisting(const CF_CALLBACK_INFO* info, const CF_CALLBACK_PARAMETERS*) { CF_OPERATION_INFO operation{}; operation.StructSize = sizeof(operation); operation.Type = CF_OPERATION_TYPE_TRANSFER_PLACEHOLDERS; operation.ConnectionKey = info->ConnectionKey; operation.TransferKey = info->TransferKey; operation.RequestKey = info->RequestKey; CF_OPERATION_PARAMETERS parameters{}; parameters.ParamSize = static_cast(offsetof(CF_OPERATION_PARAMETERS, TransferPlaceholders) + sizeof(parameters.TransferPlaceholders)); parameters.TransferPlaceholders.Flags = CF_OPERATION_TRANSFER_PLACEHOLDERS_FLAG_DISABLE_ON_DEMAND_POPULATION; HRESULT hr = CfExecute(&operation, ¶meters); if (FAILED(hr)) populationError.store(hr); } struct Root { fs::path path; winrt::hstring shellId; CF_CONNECTION_KEY key{}; bool registered = false, shellRegistered = false, connected = false; explicit Root(fs::path value) : path(std::move(value)) { checkWin(CreateDirectoryW(path.c_str(), nullptr), L"Create new case directory"); } void connect(bool shell, bool alwaysFull) { CF_SYNC_REGISTRATION registration{}; registration.StructSize = sizeof(registration); registration.ProviderName = L"Cloud directory deletion reproduction"; registration.ProviderVersion = L"1.0"; CF_SYNC_POLICIES policies{}; policies.StructSize = sizeof(policies); policies.Hydration.Primary = CF_HYDRATION_POLICY_FULL; policies.Population.Primary = alwaysFull ? CF_POPULATION_POLICY_ALWAYS_FULL : CF_POPULATION_POLICY_FULL; check(CfRegisterSyncRoot(path.c_str(), ®istration, &policies, CF_REGISTER_FLAG_DISABLE_ON_DEMAND_POPULATION_ON_ROOT | CF_REGISTER_FLAG_MARK_IN_SYNC_ON_ROOT), L"CfRegisterSyncRoot"); registered = true; if (shell) { shellId = L"ShellDeleteRepro" + std::to_wstring(GetCurrentProcessId()) + L"!" + sid() + L"!" + path.filename().wstring(); StorageProviderSyncRootInfo info; info.Id(shellId); info.Path(StorageFolder::GetFolderFromPathAsync(path.wstring()).get()); info.DisplayNameResource(L"Cloud directory deletion reproduction"); info.IconResource(L"%SystemRoot%\\system32\\charmap.exe,0"); info.Version(L"1.0"); info.HydrationPolicy(StorageProviderHydrationPolicy::Full); info.PopulationPolicy(alwaysFull ? StorageProviderPopulationPolicy::AlwaysFull : StorageProviderPopulationPolicy::Full); info.AllowPinning(true); StorageProviderSyncRootManager::Register(info); shellRegistered = true; } const CF_CALLBACK_REGISTRATION callbacks[] = { {CF_CALLBACK_TYPE_FETCH_PLACEHOLDERS, populateExisting}, CF_CALLBACK_REGISTRATION_END }; check(CfConnectSyncRoot(path.c_str(), callbacks, nullptr, CF_CONNECT_FLAG_NONE, &key), L"CfConnectSyncRoot"); connected = true; } ~Root() { if (connected && FAILED(CfDisconnectSyncRoot(key))) cleanupOk = false; if (shellRegistered) { try { StorageProviderSyncRootManager::Unregister(shellId); registered = false; // Unregister also removes the native CF registration. } catch (...) { cleanupOk = false; } } if (registered && FAILED(CfUnregisterSyncRoot(path.c_str()))) cleanupOk = false; // Fixed synthetic paths only: never recursively delete arbitrary contents. for (auto name : files) { if (!DeleteFileW((path / name).c_str())) { DWORD error = GetLastError(); if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND) cleanupOk = false; } } for (auto it = directories.rbegin(); it != directories.rend(); ++it) { if (!RemoveDirectoryW((path / *it).c_str())) { DWORD error = GetLastError(); if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND) cleanupOk = false; } } if (!RemoveDirectoryW(path.c_str())) cleanupOk = false; } }; void convert(const fs::path& path, const std::string& identity) { HANDLE file = CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); checkWin(file != INVALID_HANDLE_VALUE, L"Open for conversion"); HRESULT hr = CfConvertToPlaceholder(file, identity.data(), static_cast(identity.size()), CF_CONVERT_FLAG_MARK_IN_SYNC, nullptr, nullptr); CloseHandle(file); check(hr, L"CfConvertToPlaceholder"); } void offlineFile(const fs::path& path, const std::string& identity) { const auto name = path.filename().wstring(); CF_PLACEHOLDER_CREATE_INFO entry{}; entry.RelativeFileName = name.c_str(); entry.FsMetadata.BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; entry.FsMetadata.FileSize.QuadPart = 8; entry.FileIdentity = identity.data(); entry.FileIdentityLength = static_cast(identity.size()); entry.Flags = CF_PLACEHOLDER_CREATE_FLAG_MARK_IN_SYNC; entry.Result = E_PENDING; DWORD processed = 0; check(CfCreatePlaceholders(path.parent_path().c_str(), &entry, 1, CF_CREATE_FLAG_NONE, &processed), L"CfCreatePlaceholders"); check(entry.Result, L"Per-entry creation result"); check(processed == 1 ? S_OK : E_UNEXPECTED, L"EntriesProcessed"); } void inspect(const fs::path& path) { HANDLE file = CreateFileW(path.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); checkWin(file != INVALID_HANDLE_VALUE, L"Open for metadata inspection"); alignas(CF_PLACEHOLDER_BASIC_INFO) BYTE buffer[4096]{}; HRESULT hr = CfGetPlaceholderInfo(file, CF_PLACEHOLDER_INFO_BASIC, buffer, sizeof(buffer), nullptr); CloseHandle(file); check(hr, L"CfGetPlaceholderInfo before deletion"); } void populate(const fs::path& root, bool cloudDirectories, bool offline) { for (auto name : directories) { checkWin(CreateDirectoryW((root / name).c_str(), nullptr), L"Create fixture directory"); if (cloudDirectories) { convert(root / name, fs::path(name).generic_string()); inspect(root / name); } } for (auto name : files) { auto path = root / name; if (offline) offlineFile(path, fs::path(name).generic_string()); else { std::ofstream output(path, std::ios::binary); output.write("contents", 8); output.close(); check(output ? S_OK : E_FAIL, L"Write hydrated fixture"); convert(path, fs::path(name).generic_string()); } inspect(path); } } int shellDelete(const fs::path& path) { winrt::init_apartment(winrt::apartment_type::single_threaded); auto operation = winrt::create_instance(CLSID_FileOperation); // No recycling. No third-party CopyHook, and do not silently continue on error. check(operation->SetOperationFlags(FOF_NO_UI | FOFX_NOCOPYHOOKS | FOFX_EARLYFAILURE), L"SetOperationFlags"); winrt::com_ptr item; check(SHCreateItemFromParsingName(path.c_str(), nullptr, IID_PPV_ARGS(item.put())), L"SHCreateItemFromParsingName"); check(operation->DeleteItem(item.get(), nullptr), L"DeleteItem"); HRESULT hr = operation->PerformOperations(); BOOL aborted = TRUE; check(operation->GetAnyOperationsAborted(&aborted), L"GetAnyOperationsAborted"); std::wcout << L" PerformOperations=0x" << std::hex << std::setw(8) << std::setfill(L'0') << static_cast(hr) << std::dec << L" aborted=" << aborted << L'\n'; check(hr, L"PerformOperations"); return aborted ? 1 : 0; } void deleteInChild(const fs::path& tree) { wchar_t executable[32768]{}; checkWin(GetModuleFileNameW(nullptr, executable, static_cast(std::size(executable))), L"GetModuleFileName"); std::wstring command = L"\"" + std::wstring(executable) + L"\" --delete \"" + tree.wstring() + L"\""; STARTUPINFOW startup{}; startup.cb = sizeof(startup); startup.dwFlags = STARTF_USESTDHANDLES; startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE); startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); startup.hStdError = GetStdHandle(STD_ERROR_HANDLE); PROCESS_INFORMATION child{}; checkWin(CreateProcessW(executable, command.data(), nullptr, nullptr, TRUE, CREATE_NO_WINDOW, nullptr, nullptr, &startup, &child), L"Create delete client"); winrt::handle process(child.hProcess), thread(child.hThread); DWORD wait = WaitForSingleObject(process.get(), 60000); if (wait != WAIT_OBJECT_0) { TerminateProcess(process.get(), 1); WaitForSingleObject(process.get(), 5000); check(HRESULT_FROM_WIN32(ERROR_TIMEOUT), L"Delete client did not exit within 60 seconds"); } DWORD code = 1; checkWin(GetExitCodeProcess(process.get(), &code), L"GetExitCodeProcess"); check(code == 0 ? S_OK : E_FAIL, L"Delete client reported an error or cancellation"); } std::vector remaining(const fs::path& root) { std::vector result; if (!fs::exists(root / L"tree")) return result; result.push_back(L"tree"); for (const auto& item : fs::recursive_directory_iterator(root / L"tree")) { result.push_back(item.path().lexically_relative(root).generic_wstring()); } std::sort(result.begin(), result.end()); return result; } bool runCase(const fs::path& base, const wchar_t* name, bool shell, bool cloudDirectories, bool offline, bool alwaysFull) { Root root(base / (std::wstring(name) + (alwaysFull ? L"-always" : L"-full"))); root.connect(shell, alwaysFull); populate(root.path, cloudDirectories, offline); std::wcout << L"CASE " << name << L" population=" << (alwaysFull ? L"ALWAYS_FULL" : L"FULL") << L" metadata=OK\n" << std::flush; deleteInChild(root.path / L"tree"); check(populationError.load(), L"Population callback"); const auto paths = remaining(root.path); std::wcout << L" remaining=" << paths.size() << L'\n'; for (const auto& path : paths) std::wcout << L" " << path << L'\n'; const std::vector expected = {L"tree", L"tree/a", L"tree/a/b", L"tree/d", L"tree/d/e"}; return shell && cloudDirectories && offline ? paths == expected : paths.empty(); } int wmain(int argc, wchar_t** argv) { fs::path base; bool owned = false; int result = 1; try { if (argc == 3 && std::wstring(argv[1]) == L"--delete") return shellDelete(argv[2]); if (argc != 2) { std::wcerr << L"Usage: repro.exe \n"; return 2; } winrt::init_apartment(); base = fs::weakly_canonical(fs::absolute(argv[1])); wchar_t volume[MAX_PATH]{}, fileSystem[MAX_PATH]{}; checkWin(GetVolumePathNameW(base.parent_path().c_str(), volume, MAX_PATH), L"GetVolumePathName"); checkWin(GetVolumeInformationW(volume, nullptr, 0, nullptr, nullptr, nullptr, fileSystem, MAX_PATH), L"GetVolumeInformation"); check(std::wstring(fileSystem) == L"NTFS" && GetDriveTypeW(volume) == DRIVE_FIXED ? S_OK : E_INVALIDARG, L"Use local NTFS storage"); CF_SYNC_ROOT_BASIC_INFO existing{}; HRESULT parentInfo = CfGetSyncRootInfoByPath(base.parent_path().c_str(), CF_SYNC_ROOT_INFO_BASIC, &existing, sizeof(existing), nullptr); check(SUCCEEDED(parentInfo) ? E_INVALIDARG : S_OK, L"Test directory must be outside existing sync roots"); checkWin(CreateDirectoryW(base.c_str(), nullptr), L"Create new test directory (must not exist)"); owned = true; CF_PLATFORM_INFO platform{}; check(CfGetPlatformInfo(&platform), L"CfGetPlatformInfo"); std::wcout << L"CFAPI " << platform.BuildNumber << L'.' << platform.RevisionNumber << L" integration=" << platform.IntegrationNumber << L" filesystem=" << fileSystem << L'\n'; bool reproduced = true; for (bool always : {false, true}) { reproduced &= runCase(base, L"native-cloud-offline", false, true, true, always); reproduced &= runCase(base, L"shell-ordinary-offline", true, false, true, always); reproduced &= runCase(base, L"shell-cloud-hydrated", true, true, false, always); reproduced &= runCase(base, L"shell-cloud-offline", true, true, true, always); } std::wcout << L"REPRODUCED=" << (reproduced ? L"yes" : L"no") << L'\n'; result = 0; // The experiment completed, whether or not this build reproduces. } catch (const winrt::hresult_error& error) { std::wcerr << L"Experiment stopped: " << error.message().c_str() << L" (0x" << std::hex << static_cast(error.code()) << std::dec << L")\n"; } catch (const std::exception& error) { std::cerr << "Experiment stopped: " << error.what() << '\n'; } if (owned && !RemoveDirectoryW(base.c_str())) cleanupOk = false; std::wcout << L"CLEANUP=" << (cleanupOk ? L"OK" : L"INCOMPLETE") << L'\n'; return cleanupOk ? result : 1; }