1 /* 2 * PROJECT: Recycle bin management 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Deals with recycle bins of Windows 2000/XP/2003 5 * COPYRIGHT: Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) 6 * Copyright 2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 7 */ 8 9 #include "recyclebin_private.h" 10 #include <atlstr.h> 11 #include <shlwapi.h> 12 #include "sddl.h" 13 14 EXTERN_C HRESULT WINAPI SHUpdateRecycleBinIcon(void); 15 16 class CZZWStr 17 { 18 LPWSTR m_sz; 19 20 public: 21 ~CZZWStr() { SHFree(m_sz); } 22 CZZWStr() : m_sz(NULL) {} 23 CZZWStr(const CZZWStr&) = delete; 24 CZZWStr& operator=(const CZZWStr&) = delete; 25 26 bool Initialize(LPCWSTR Str) 27 { 28 SIZE_T cch = wcslen(Str) + 1; 29 m_sz = (LPWSTR)SHAlloc((cch + 1) * sizeof(*Str)); 30 if (!m_sz) 31 return false; 32 CopyMemory(m_sz, Str, cch * sizeof(*Str)); 33 m_sz[cch] = UNICODE_NULL; // Double-null terminate 34 return true; 35 } 36 inline LPWSTR c_str() { return m_sz; } 37 }; 38 39 static int SHELL_SingleFileOperation(HWND hWnd, UINT Op, LPCWSTR pszFrom, LPCWSTR pszTo, FILEOP_FLAGS Flags) 40 { 41 CZZWStr szzFrom, szzTo; 42 if (!szzFrom.Initialize(pszFrom) || !szzTo.Initialize(pszTo)) 43 return ERROR_OUTOFMEMORY; // Note: Not one of the DE errors but also not in the DE range 44 SHFILEOPSTRUCTW fos = { hWnd, Op, szzFrom.c_str(), szzTo.c_str(), Flags }; 45 return SHFileOperationW(&fos); 46 } 47 48 static BOOL 49 IntDeleteRecursive( 50 IN LPCWSTR FullName) 51 { 52 DWORD RemovableAttributes = FILE_ATTRIBUTE_READONLY; 53 WIN32_FIND_DATAW FindData; 54 HANDLE hSearch = INVALID_HANDLE_VALUE; 55 LPWSTR FullPath = NULL, pFilePart; 56 DWORD FileAttributes; 57 SIZE_T dwLength; 58 BOOL ret = FALSE; 59 60 FileAttributes = GetFileAttributesW(FullName); 61 if (FileAttributes == INVALID_FILE_ATTRIBUTES) 62 { 63 if (GetLastError() == ERROR_FILE_NOT_FOUND) 64 ret = TRUE; 65 goto cleanup; 66 } 67 if (FileAttributes & RemovableAttributes) 68 { 69 if (!SetFileAttributesW(FullName, FileAttributes & ~RemovableAttributes)) 70 goto cleanup; 71 } 72 if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) 73 { 74 /* Prepare file specification */ 75 dwLength = wcslen(FullName); 76 FullPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (dwLength + 1 + MAX_PATH + 1) * sizeof(WCHAR)); 77 if (!FullPath) 78 { 79 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 80 goto cleanup; 81 } 82 wcscpy(FullPath, FullName); 83 if (FullPath[dwLength - 1] != '\\') 84 { 85 FullPath[dwLength] = '\\'; 86 dwLength++; 87 } 88 pFilePart = &FullPath[dwLength]; 89 wcscpy(pFilePart, L"*"); 90 91 /* Enumerate contents, and delete it */ 92 hSearch = FindFirstFileW(FullPath, &FindData); 93 if (hSearch == INVALID_HANDLE_VALUE) 94 goto cleanup; 95 do 96 { 97 if (!(FindData.cFileName[0] == '.' && 98 (FindData.cFileName[1] == '\0' || (FindData.cFileName[1] == '.' && FindData.cFileName[2] == '\0')))) 99 { 100 wcscpy(pFilePart, FindData.cFileName); 101 if (!IntDeleteRecursive(FullPath)) 102 { 103 FindClose(hSearch); 104 goto cleanup; 105 } 106 } 107 } 108 while (FindNextFileW(hSearch, &FindData)); 109 FindClose(hSearch); 110 if (GetLastError() != ERROR_NO_MORE_FILES) 111 goto cleanup; 112 113 /* Remove (now empty) directory */ 114 if (!RemoveDirectoryW(FullName)) 115 goto cleanup; 116 } 117 else 118 { 119 if (!DeleteFileW(FullName)) 120 goto cleanup; 121 } 122 ret = TRUE; 123 124 cleanup: 125 HeapFree(GetProcessHeap(), 0, FullPath); 126 return ret; 127 } 128 129 class RecycleBin5 : public IRecycleBin5 130 { 131 public: 132 RecycleBin5(); 133 virtual ~RecycleBin5(); 134 135 HRESULT Init(_In_ LPCWSTR VolumePath); 136 137 /* IUnknown interface */ 138 STDMETHODIMP QueryInterface(_In_ REFIID riid, _Out_ void **ppvObject) override; 139 STDMETHODIMP_(ULONG) AddRef() override; 140 STDMETHODIMP_(ULONG) Release() override; 141 142 /* IRecycleBin interface */ 143 STDMETHODIMP DeleteFile(_In_ LPCWSTR szFileName) override; 144 STDMETHODIMP EmptyRecycleBin() override; 145 STDMETHODIMP EnumObjects(_Out_ IRecycleBinEnumList **ppEnumList) override; 146 STDMETHODIMP GetDirectory(LPWSTR szPath) override 147 { 148 if (!m_Folder[0]) 149 return E_UNEXPECTED; 150 lstrcpynW(szPath, m_Folder, MAX_PATH); 151 return S_OK; 152 } 153 154 /* IRecycleBin5 interface */ 155 STDMETHODIMP Delete( 156 _In_ LPCWSTR pDeletedFileName, 157 _In_ DELETED_FILE_RECORD *pDeletedFile) override; 158 STDMETHODIMP Restore( 159 _In_ LPCWSTR pDeletedFileName, 160 _In_ DELETED_FILE_RECORD *pDeletedFile) override; 161 STDMETHODIMP OnClosing(_In_ IRecycleBinEnumList *prbel) override; 162 163 protected: 164 LONG m_ref; 165 HANDLE m_hInfo; 166 HANDLE m_hInfoMapped; 167 DWORD m_EnumeratorCount; 168 CStringW m_VolumePath; 169 CStringW m_Folder; /* [drive]:\[RECYCLE_BIN_DIRECTORY]\{SID} */ 170 }; 171 172 STDMETHODIMP RecycleBin5::QueryInterface(_In_ REFIID riid, _Out_ void **ppvObject) 173 { 174 TRACE("(%p, %s, %p)\n", this, debugstr_guid(&riid), ppvObject); 175 176 if (!ppvObject) 177 return E_POINTER; 178 179 if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IRecycleBin)) 180 *ppvObject = static_cast<IRecycleBin5 *>(this); 181 else if (IsEqualIID(riid, IID_IRecycleBin5)) 182 *ppvObject = static_cast<IRecycleBin5 *>(this); 183 else 184 { 185 *ppvObject = NULL; 186 return E_NOINTERFACE; 187 } 188 189 AddRef(); 190 return S_OK; 191 } 192 193 STDMETHODIMP_(ULONG) RecycleBin5::AddRef() 194 { 195 TRACE("(%p)\n", this); 196 return InterlockedIncrement(&m_ref); 197 } 198 199 RecycleBin5::~RecycleBin5() 200 { 201 TRACE("(%p)\n", this); 202 203 if (m_hInfo && m_hInfo != INVALID_HANDLE_VALUE) 204 CloseHandle(m_hInfo); 205 if (m_hInfoMapped) 206 CloseHandle(m_hInfoMapped); 207 } 208 209 STDMETHODIMP_(ULONG) RecycleBin5::Release() 210 { 211 TRACE("(%p)\n", this); 212 213 ULONG refCount = InterlockedDecrement(&m_ref); 214 if (refCount == 0) 215 delete this; 216 return refCount; 217 } 218 219 STDMETHODIMP RecycleBin5::DeleteFile(_In_ LPCWSTR szFileName) 220 { 221 LPWSTR szFullName = NULL; 222 DWORD dwBufferLength = 0; 223 LPWSTR lpFilePart; 224 LPCWSTR Extension; 225 CStringW DeletedFileName; 226 WCHAR szUniqueId[64]; 227 DWORD len; 228 HANDLE hFile = INVALID_HANDLE_VALUE; 229 PINFO2_HEADER pHeader = NULL; 230 PDELETED_FILE_RECORD pDeletedFile; 231 ULARGE_INTEGER FileSize; 232 DWORD dwAttributes, dwEntries; 233 SYSTEMTIME SystemTime; 234 DWORD ClusterSize, BytesPerSector, SectorsPerCluster; 235 HRESULT hr; 236 WIN32_FIND_DATAW wfd = {}; 237 238 TRACE("(%p, %s)\n", this, debugstr_w(szFileName)); 239 240 if (m_EnumeratorCount != 0) 241 return HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION); 242 243 /* Get full file name */ 244 while (TRUE) 245 { 246 len = GetFullPathNameW(szFileName, dwBufferLength, szFullName, &lpFilePart); 247 if (len == 0) 248 { 249 if (szFullName) 250 CoTaskMemFree(szFullName); 251 return HResultFromWin32(GetLastError()); 252 } 253 else if (len < dwBufferLength) 254 break; 255 if (szFullName) 256 CoTaskMemFree(szFullName); 257 dwBufferLength = len; 258 szFullName = (LPWSTR)CoTaskMemAlloc(dwBufferLength * sizeof(WCHAR)); 259 if (!szFullName) 260 return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); 261 } 262 263 /* Check if file exists */ 264 dwAttributes = GetFileAttributesW(szFullName); 265 if (dwAttributes == INVALID_FILE_ATTRIBUTES) 266 { 267 CoTaskMemFree(szFullName); 268 return HResultFromWin32(GetLastError()); 269 } 270 271 if (dwBufferLength < 2 || szFullName[1] != ':') 272 { 273 /* Not a local file */ 274 CoTaskMemFree(szFullName); 275 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME); 276 } 277 278 hFile = CreateFileW(szFullName, 0, 0, NULL, OPEN_EXISTING, (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILE_FLAG_BACKUP_SEMANTICS : 0, NULL); 279 if (hFile == INVALID_HANDLE_VALUE) 280 { 281 hr = HResultFromWin32(GetLastError()); 282 goto cleanup; 283 } 284 285 /* Increase INFO2 file size */ 286 CloseHandle(m_hInfoMapped); 287 SetFilePointer(m_hInfo, sizeof(DELETED_FILE_RECORD), NULL, FILE_END); 288 SetEndOfFile(m_hInfo); 289 m_hInfoMapped = CreateFileMappingW(m_hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL); 290 if (!m_hInfoMapped) 291 { 292 hr = HResultFromWin32(GetLastError()); 293 goto cleanup; 294 } 295 296 /* Open INFO2 file */ 297 pHeader = (PINFO2_HEADER)MapViewOfFile(m_hInfoMapped, FILE_MAP_WRITE, 0, 0, 0); 298 if (!pHeader) 299 { 300 hr = HResultFromWin32(GetLastError()); 301 goto cleanup; 302 } 303 304 /* Get number of entries */ 305 FileSize.u.LowPart = GetFileSize(m_hInfo, &FileSize.u.HighPart); 306 if (FileSize.u.LowPart < sizeof(INFO2_HEADER)) 307 { 308 hr = HResultFromWin32(GetLastError()); 309 goto cleanup; 310 } 311 dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD)) - 1; 312 pDeletedFile = ((PDELETED_FILE_RECORD)(pHeader + 1)) + dwEntries; 313 314 /* Get file size */ 315 #if 0 316 if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&FileSize)) 317 { 318 hr = HResultFromWin32(GetLastError()); 319 goto cleanup; 320 } 321 #else 322 FileSize.u.LowPart = GetFileSize(hFile, &FileSize.u.HighPart); 323 if (FileSize.u.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) 324 { 325 hr = HResultFromWin32(GetLastError()); 326 goto cleanup; 327 } 328 #endif 329 /* Check if file size is > 4Gb */ 330 if (FileSize.u.HighPart != 0) 331 { 332 /* Yes, this recyclebin can't support this file */ 333 hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); 334 goto cleanup; 335 } 336 pHeader->dwTotalLogicalSize += FileSize.u.LowPart; 337 338 /* Generate new name */ 339 Extension = PathFindExtensionW(szFullName); 340 ZeroMemory(pDeletedFile, sizeof(DELETED_FILE_RECORD)); 341 if (dwEntries == 0) 342 pDeletedFile->dwRecordUniqueId = 0; 343 else 344 { 345 PDELETED_FILE_RECORD pLastDeleted = ((PDELETED_FILE_RECORD)(pHeader + 1)) + dwEntries - 1; 346 pDeletedFile->dwRecordUniqueId = pLastDeleted->dwRecordUniqueId + 1; 347 } 348 349 pDeletedFile->dwDriveNumber = tolower(szFullName[0]) - 'a'; 350 _ultow(pDeletedFile->dwRecordUniqueId, szUniqueId, 10); 351 352 DeletedFileName = m_Folder; 353 DeletedFileName += L"\\D"; 354 DeletedFileName += (WCHAR)(L'a' + pDeletedFile->dwDriveNumber); 355 DeletedFileName += szUniqueId; 356 DeletedFileName += Extension; 357 358 /* Get cluster size */ 359 if (!GetDiskFreeSpaceW(m_VolumePath, &SectorsPerCluster, &BytesPerSector, NULL, NULL)) 360 { 361 hr = HResultFromWin32(GetLastError()); 362 goto cleanup; 363 } 364 ClusterSize = BytesPerSector * SectorsPerCluster; 365 366 /* Get current time */ 367 GetSystemTime(&SystemTime); 368 if (!SystemTimeToFileTime(&SystemTime, &pDeletedFile->DeletionTime)) 369 { 370 hr = HResultFromWin32(GetLastError()); 371 goto cleanup; 372 } 373 pDeletedFile->dwPhysicalFileSize = ROUND_UP(FileSize.u.LowPart, ClusterSize); 374 375 /* Set name */ 376 wcscpy(pDeletedFile->FileNameW, szFullName); 377 if (WideCharToMultiByte(CP_ACP, 0, pDeletedFile->FileNameW, -1, pDeletedFile->FileNameA, MAX_PATH, NULL, NULL) == 0) 378 { 379 hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME); 380 SetLastError(ERROR_INVALID_NAME); 381 goto cleanup; 382 } 383 384 wfd.dwFileAttributes = dwAttributes; 385 wfd.nFileSizeLow = FileSize.u.LowPart; 386 GetFileTime(hFile, &wfd.ftCreationTime, &wfd.ftLastAccessTime, &wfd.ftLastWriteTime); 387 388 /* Move file */ 389 if (MoveFileW(szFullName, DeletedFileName)) 390 hr = S_OK; 391 else 392 hr = HResultFromWin32(GetLastError()); 393 394 if (SUCCEEDED(hr)) 395 { 396 RECYCLEBINFILEIDENTITY ident = { pDeletedFile->DeletionTime, DeletedFileName }; 397 CRecycleBin_NotifyRecycled(szFullName, &wfd, &ident); 398 } 399 400 cleanup: 401 if (pHeader) 402 UnmapViewOfFile(pHeader); 403 if (hFile != INVALID_HANDLE_VALUE) 404 CloseHandle(hFile); 405 CoTaskMemFree(szFullName); 406 return hr; 407 } 408 409 STDMETHODIMP RecycleBin5::EmptyRecycleBin() 410 { 411 TRACE("(%p)\n", this); 412 413 while (TRUE) 414 { 415 IRecycleBinEnumList *prbel; 416 HRESULT hr = EnumObjects(&prbel); 417 if (!SUCCEEDED(hr)) 418 return hr; 419 420 IRecycleBinFile *prbf; 421 hr = prbel->Next(1, &prbf, NULL); 422 prbel->Release(); 423 if (hr == S_FALSE) 424 return S_OK; 425 hr = prbf->Delete(); 426 prbf->Release(); 427 if (!SUCCEEDED(hr)) 428 return hr; 429 } 430 } 431 432 STDMETHODIMP RecycleBin5::EnumObjects(_Out_ IRecycleBinEnumList **ppEnumList) 433 { 434 TRACE("(%p, %p)\n", this, ppEnumList); 435 436 IUnknown *pUnk; 437 HRESULT hr = RecycleBin5Enum_Constructor(this, m_hInfo, m_hInfoMapped, m_Folder, &pUnk); 438 if (!SUCCEEDED(hr)) 439 return hr; 440 441 IRecycleBinEnumList *prbel; 442 hr = pUnk->QueryInterface(IID_IRecycleBinEnumList, (void **)&prbel); 443 if (SUCCEEDED(hr)) 444 { 445 m_EnumeratorCount++; 446 *ppEnumList = prbel; 447 } 448 449 pUnk->Release(); 450 return hr; 451 } 452 453 STDMETHODIMP RecycleBin5::Delete( 454 _In_ LPCWSTR pDeletedFileName, 455 _In_ DELETED_FILE_RECORD *pDeletedFile) 456 { 457 ULARGE_INTEGER FileSize; 458 PINFO2_HEADER pHeader; 459 DELETED_FILE_RECORD *pRecord, *pLast; 460 DWORD dwEntries, i; 461 462 TRACE("(%p, %s, %p)\n", this, debugstr_w(pDeletedFileName), pDeletedFile); 463 464 if (m_EnumeratorCount != 0) 465 return HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION); 466 467 pHeader = (PINFO2_HEADER)MapViewOfFile(m_hInfoMapped, FILE_MAP_WRITE, 0, 0, 0); 468 if (!pHeader) 469 return HRESULT_FROM_WIN32(GetLastError()); 470 471 FileSize.u.LowPart = GetFileSize(m_hInfo, &FileSize.u.HighPart); 472 if (FileSize.u.LowPart == 0) 473 { 474 UnmapViewOfFile(pHeader); 475 return HRESULT_FROM_WIN32(GetLastError()); 476 } 477 dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD)); 478 479 pRecord = (DELETED_FILE_RECORD *)(pHeader + 1); 480 for (i = 0; i < dwEntries; i++) 481 { 482 if (pRecord->dwRecordUniqueId == pDeletedFile->dwRecordUniqueId) 483 { 484 /* Delete file */ 485 if (!IntDeleteRecursive(pDeletedFileName)) 486 { 487 UnmapViewOfFile(pHeader); 488 return HRESULT_FROM_WIN32(GetLastError()); 489 } 490 491 /* Clear last entry in the file */ 492 MoveMemory(pRecord, pRecord + 1, (dwEntries - i - 1) * sizeof(DELETED_FILE_RECORD)); 493 pLast = pRecord + (dwEntries - i - 1); 494 ZeroMemory(pLast, sizeof(DELETED_FILE_RECORD)); 495 UnmapViewOfFile(pHeader); 496 497 /* Resize file */ 498 CloseHandle(m_hInfoMapped); 499 SetFilePointer(m_hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END); 500 SetEndOfFile(m_hInfo); 501 m_hInfoMapped = CreateFileMappingW(m_hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL); 502 if (!m_hInfoMapped) 503 return HRESULT_FROM_WIN32(GetLastError()); 504 return S_OK; 505 } 506 pRecord++; 507 } 508 UnmapViewOfFile(pHeader); 509 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 510 } 511 512 STDMETHODIMP RecycleBin5::Restore( 513 _In_ LPCWSTR pDeletedFileName, 514 _In_ DELETED_FILE_RECORD *pDeletedFile) 515 { 516 ULARGE_INTEGER FileSize; 517 PINFO2_HEADER pHeader; 518 DELETED_FILE_RECORD *pRecord, *pLast; 519 DWORD dwEntries, i; 520 int res; 521 522 TRACE("(%p, %s, %p)\n", this, debugstr_w(pDeletedFileName), pDeletedFile); 523 524 if (m_EnumeratorCount != 0) 525 return HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION); 526 527 pHeader = (PINFO2_HEADER)MapViewOfFile(m_hInfoMapped, FILE_MAP_WRITE, 0, 0, 0); 528 if (!pHeader) 529 return HRESULT_FROM_WIN32(GetLastError()); 530 531 FileSize.u.LowPart = GetFileSize(m_hInfo, &FileSize.u.HighPart); 532 if (FileSize.u.LowPart == 0) 533 { 534 UnmapViewOfFile(pHeader); 535 return HRESULT_FROM_WIN32(GetLastError()); 536 } 537 dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD)); 538 539 pRecord = (DELETED_FILE_RECORD *)(pHeader + 1); 540 for (i = 0; i < dwEntries; i++) 541 { 542 if (pRecord->dwRecordUniqueId == pDeletedFile->dwRecordUniqueId) 543 { 544 res = SHELL_SingleFileOperation(NULL, FO_MOVE, pDeletedFileName, pDeletedFile->FileNameW, 0); 545 if (res) 546 { 547 ERR("SHFileOperationW failed with 0x%x\n", res); 548 UnmapViewOfFile(pHeader); 549 return E_FAIL; 550 } 551 552 /* Clear last entry in the file */ 553 MoveMemory(pRecord, pRecord + 1, (dwEntries - i - 1) * sizeof(DELETED_FILE_RECORD)); 554 pLast = pRecord + (dwEntries - i - 1); 555 ZeroMemory(pLast, sizeof(DELETED_FILE_RECORD)); 556 UnmapViewOfFile(pHeader); 557 558 /* Resize file */ 559 CloseHandle(m_hInfoMapped); 560 SetFilePointer(m_hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END); 561 SetEndOfFile(m_hInfo); 562 m_hInfoMapped = CreateFileMappingW(m_hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL); 563 if (!m_hInfoMapped) 564 return HRESULT_FROM_WIN32(GetLastError()); 565 if (dwEntries == 1) 566 SHUpdateRecycleBinIcon(); // Full --> Empty 567 return S_OK; 568 } 569 pRecord++; 570 } 571 572 UnmapViewOfFile(pHeader); 573 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 574 } 575 576 STDMETHODIMP RecycleBin5::OnClosing(_In_ IRecycleBinEnumList *prbel) 577 { 578 TRACE("(%p, %p)\n", this, prbel); 579 m_EnumeratorCount--; 580 return S_OK; 581 } 582 583 static HRESULT 584 RecycleBin5_Create( 585 _In_ LPCWSTR Folder, 586 _In_ PSID OwnerSid OPTIONAL) 587 { 588 LPWSTR BufferName = NULL; 589 LPWSTR Separator; /* Pointer into BufferName buffer */ 590 LPWSTR FileName; /* Pointer into BufferName buffer */ 591 LPCSTR DesktopIniContents = "[.ShellClassInfo]\r\nCLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n"; 592 INFO2_HEADER Info2Contents[] = { { 5, 0, 0, 0x320, 0 } }; 593 DWORD BytesToWrite, BytesWritten, Needed; 594 HANDLE hFile = INVALID_HANDLE_VALUE; 595 HRESULT hr; 596 597 Needed = (wcslen(Folder) + 1 + max(wcslen(RECYCLE_BIN_FILE_NAME), wcslen(L"desktop.ini")) + 1) * sizeof(WCHAR); 598 BufferName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, Needed); 599 if (!BufferName) 600 { 601 hr = ERROR_NOT_ENOUGH_MEMORY; 602 goto cleanup; 603 } 604 605 wcscpy(BufferName, Folder); 606 Separator = wcsstr(&BufferName[3], L"\\"); 607 if (Separator) 608 *Separator = UNICODE_NULL; 609 if (!CreateDirectoryW(BufferName, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) 610 { 611 hr = HRESULT_FROM_WIN32(GetLastError()); 612 goto cleanup; 613 } 614 SetFileAttributesW(BufferName, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); 615 if (Separator) 616 { 617 *Separator = L'\\'; 618 if (!CreateDirectoryW(BufferName, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) 619 { 620 hr = HRESULT_FROM_WIN32(GetLastError()); 621 goto cleanup; 622 } 623 } 624 625 if (OwnerSid) 626 { 627 //DWORD rc; 628 629 /* Add ACL to allow only user/SYSTEM to open it */ 630 /* FIXME: rc = SetNamedSecurityInfo( 631 BufferName, 632 SE_FILE_OBJECT, 633 ???, 634 OwnerSid, 635 NULL, 636 ???, 637 ???); 638 if (rc != ERROR_SUCCESS) 639 { 640 hr = HRESULT_FROM_WIN32(rc); 641 goto cleanup; 642 } 643 */ 644 } 645 646 wcscat(BufferName, L"\\"); 647 FileName = &BufferName[wcslen(BufferName)]; 648 649 /* Create desktop.ini */ 650 wcscpy(FileName, L"desktop.ini"); 651 hFile = CreateFileW(BufferName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, NULL); 652 if (hFile == INVALID_HANDLE_VALUE) 653 { 654 hr = HRESULT_FROM_WIN32(GetLastError()); 655 goto cleanup; 656 } 657 BytesToWrite = strlen(DesktopIniContents); 658 if (!WriteFile(hFile, DesktopIniContents, (DWORD)BytesToWrite, &BytesWritten, NULL)) 659 { 660 hr = HRESULT_FROM_WIN32(GetLastError()); 661 goto cleanup; 662 } 663 if (BytesWritten != BytesToWrite) 664 { 665 hr = E_FAIL; 666 goto cleanup; 667 } 668 CloseHandle(hFile); 669 hFile = INVALID_HANDLE_VALUE; 670 671 /* Create empty INFO2 file */ 672 wcscpy(FileName, RECYCLE_BIN_FILE_NAME); 673 hFile = CreateFileW(BufferName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL); 674 if (hFile == INVALID_HANDLE_VALUE) 675 { 676 hr = HRESULT_FROM_WIN32(GetLastError()); 677 goto cleanup; 678 } 679 BytesToWrite = sizeof(Info2Contents); 680 if (!WriteFile(hFile, Info2Contents, (DWORD)BytesToWrite, &BytesWritten, NULL)) 681 { 682 hr = HRESULT_FROM_WIN32(GetLastError()); 683 goto cleanup; 684 } 685 if (BytesWritten == BytesToWrite) 686 hr = S_OK; 687 else 688 hr = E_FAIL; 689 690 cleanup: 691 HeapFree(GetProcessHeap(), 0, BufferName); 692 if (hFile != INVALID_HANDLE_VALUE) 693 CloseHandle(hFile); 694 return hr; 695 } 696 697 RecycleBin5::RecycleBin5() 698 : m_ref(1) 699 , m_hInfo(NULL) 700 , m_hInfoMapped(NULL) 701 , m_EnumeratorCount(0) 702 { 703 } 704 705 HRESULT RecycleBin5::Init(_In_ LPCWSTR VolumePath) 706 { 707 DWORD FileSystemFlags; 708 LPCWSTR RecycleBinDirectory; 709 HANDLE tokenHandle = INVALID_HANDLE_VALUE; 710 PTOKEN_USER TokenUserInfo = NULL; 711 LPWSTR StringSid = NULL; 712 DWORD Needed; 713 INT len; 714 HRESULT hr; 715 716 m_VolumePath = VolumePath; 717 718 /* Get information about file system */ 719 if (!GetVolumeInformationW(VolumePath, NULL, 0, NULL, NULL, &FileSystemFlags, NULL, 0)) 720 { 721 hr = HRESULT_FROM_WIN32(GetLastError()); 722 goto cleanup; 723 } 724 725 if (!(FileSystemFlags & FILE_PERSISTENT_ACLS)) 726 { 727 RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITHOUT_ACL; 728 } 729 else 730 { 731 RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITH_ACL; 732 733 /* Get user SID */ 734 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tokenHandle)) 735 { 736 hr = HRESULT_FROM_WIN32(GetLastError()); 737 goto cleanup; 738 } 739 if (GetTokenInformation(tokenHandle, TokenUser, NULL, 0, &Needed)) 740 { 741 hr = E_FAIL; 742 goto cleanup; 743 } 744 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 745 { 746 hr = HRESULT_FROM_WIN32(GetLastError()); 747 goto cleanup; 748 } 749 TokenUserInfo = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), 0, Needed); 750 if (!TokenUserInfo) 751 { 752 hr = E_OUTOFMEMORY; 753 goto cleanup; 754 } 755 if (!GetTokenInformation(tokenHandle, TokenUser, TokenUserInfo, (DWORD)Needed, &Needed)) 756 { 757 hr = HRESULT_FROM_WIN32(GetLastError()); 758 goto cleanup; 759 } 760 if (!ConvertSidToStringSidW(TokenUserInfo->User.Sid, &StringSid)) 761 { 762 hr = HRESULT_FROM_WIN32(GetLastError()); 763 goto cleanup; 764 } 765 } 766 767 m_Folder = VolumePath; 768 m_Folder += RecycleBinDirectory; 769 if (StringSid) 770 { 771 m_Folder += L'\\'; 772 m_Folder += StringSid; 773 } 774 len = m_Folder.GetLength(); 775 m_Folder += L"\\" RECYCLE_BIN_FILE_NAME; 776 777 m_hInfo = CreateFileW(m_Folder, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); 778 if (m_hInfo == INVALID_HANDLE_VALUE && 779 (GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_FILE_NOT_FOUND)) 780 { 781 m_Folder = m_Folder.Left(len); 782 hr = RecycleBin5_Create(m_Folder, TokenUserInfo ? TokenUserInfo->User.Sid : NULL); 783 m_Folder += L"\\" RECYCLE_BIN_FILE_NAME; 784 if (!SUCCEEDED(hr)) 785 goto cleanup; 786 m_hInfo = CreateFileW(m_Folder, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); 787 } 788 789 if (m_hInfo == INVALID_HANDLE_VALUE) 790 { 791 hr = HRESULT_FROM_WIN32(GetLastError()); 792 goto cleanup; 793 } 794 795 m_hInfoMapped = CreateFileMappingW(m_hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL); 796 if (!m_hInfoMapped) 797 { 798 hr = HRESULT_FROM_WIN32(GetLastError()); 799 goto cleanup; 800 } 801 802 m_Folder = m_Folder.Left(len); 803 hr = S_OK; 804 805 cleanup: 806 if (tokenHandle != INVALID_HANDLE_VALUE) 807 CloseHandle(tokenHandle); 808 HeapFree(GetProcessHeap(), 0, TokenUserInfo); 809 if (StringSid) 810 LocalFree(StringSid); 811 return hr; 812 } 813 814 EXTERN_C 815 HRESULT RecycleBin5_Constructor(_In_ LPCWSTR VolumePath, _Out_ IUnknown **ppUnknown) 816 { 817 if (!ppUnknown) 818 return E_POINTER; 819 820 *ppUnknown = NULL; 821 822 RecycleBin5 *pThis = new RecycleBin5(); 823 if (!pThis) 824 return E_OUTOFMEMORY; 825 826 HRESULT hr = pThis->Init(VolumePath); 827 if (FAILED(hr)) 828 { 829 delete pThis; 830 return hr; 831 } 832 833 *ppUnknown = static_cast<IRecycleBin5 *>(pThis); 834 return S_OK; 835 } 836