1 /* 2 * PROJECT: Recycle bin management 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Enumerates contents of a MS Windows 2000/XP/2003 recyclebin 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 <strsafe.h> 13 14 class RecycleBin5File : public IRecycleBinFile 15 { 16 public: 17 RecycleBin5File(); 18 virtual ~RecycleBin5File(); 19 20 HRESULT Init( 21 _In_ IRecycleBin5 *prb, 22 _In_ LPCWSTR Folder, 23 _In_ PDELETED_FILE_RECORD pDeletedFile); 24 25 /* IUnknown methods */ 26 STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override; 27 STDMETHODIMP_(ULONG) AddRef() override; 28 STDMETHODIMP_(ULONG) Release() override; 29 30 /* IRecycleBinFile methods */ 31 STDMETHODIMP GetLastModificationTime(FILETIME *pLastModificationTime) override; 32 STDMETHODIMP GetDeletionTime(FILETIME *pDeletionTime) override; 33 STDMETHODIMP GetFileSize(ULARGE_INTEGER *pFileSize) override; 34 STDMETHODIMP GetPhysicalFileSize(ULARGE_INTEGER *pPhysicalFileSize) override; 35 STDMETHODIMP GetAttributes(DWORD *pAttributes) override; 36 STDMETHODIMP GetFileName(SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize) override; 37 STDMETHODIMP GetTypeName(SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize) override; 38 STDMETHODIMP Delete() override; 39 STDMETHODIMP Restore() override; 40 41 protected: 42 LONG m_ref; 43 IRecycleBin5 *m_recycleBin; 44 DELETED_FILE_RECORD m_deletedFile; 45 LPWSTR m_FullName; 46 }; 47 48 STDMETHODIMP RecycleBin5File::QueryInterface(REFIID riid, void **ppvObject) 49 { 50 TRACE("(%p, %s, %p)\n", this, debugstr_guid(&riid), ppvObject); 51 52 if (!ppvObject) 53 return E_POINTER; 54 55 if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IRecycleBinFile)) 56 *ppvObject = static_cast<IRecycleBinFile *>(this); 57 else if (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) 58 { 59 DWORD dwAttributes; 60 if (GetAttributes(&dwAttributes) == S_OK) 61 return SHCreateFileExtractIconW(m_FullName, dwAttributes, riid, ppvObject); 62 else 63 return S_FALSE; 64 } 65 else 66 { 67 *ppvObject = NULL; 68 return E_NOINTERFACE; 69 } 70 71 AddRef(); 72 return S_OK; 73 } 74 75 STDMETHODIMP_(ULONG) RecycleBin5File::AddRef() 76 { 77 TRACE("(%p)\n", this); 78 return InterlockedIncrement(&m_ref); 79 } 80 81 RecycleBin5File::~RecycleBin5File() 82 { 83 TRACE("(%p)\n", this); 84 m_recycleBin->Release(); 85 SHFree(m_FullName); 86 } 87 88 STDMETHODIMP_(ULONG) RecycleBin5File::Release() 89 { 90 TRACE("(%p)\n", this); 91 ULONG refCount = InterlockedDecrement(&m_ref); 92 if (refCount == 0) 93 delete this; 94 return refCount; 95 } 96 97 STDMETHODIMP RecycleBin5File::GetLastModificationTime(FILETIME *pLastModificationTime) 98 { 99 TRACE("(%p, %p)\n", this, pLastModificationTime); 100 101 DWORD dwAttributes = ::GetFileAttributesW(m_FullName); 102 if (dwAttributes == INVALID_FILE_ATTRIBUTES) 103 return HRESULT_FROM_WIN32(GetLastError()); 104 105 HANDLE hFile; 106 hFile = CreateFileW(m_FullName, 107 GENERIC_READ, 108 FILE_SHARE_READ | 109 ((dwAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (FILE_SHARE_WRITE | FILE_SHARE_DELETE) : 0), 110 NULL, 111 OPEN_EXISTING, 112 (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILE_FLAG_BACKUP_SEMANTICS : 0, 113 NULL); 114 if (hFile == INVALID_HANDLE_VALUE) 115 return HRESULT_FROM_WIN32(GetLastError()); 116 117 HRESULT hr; 118 if (GetFileTime(hFile, NULL, NULL, pLastModificationTime)) 119 hr = S_OK; 120 else 121 hr = HRESULT_FROM_WIN32(GetLastError()); 122 CloseHandle(hFile); 123 return hr; 124 } 125 126 STDMETHODIMP RecycleBin5File::GetDeletionTime(FILETIME *pDeletionTime) 127 { 128 TRACE("(%p, %p)\n", this, pDeletionTime); 129 *pDeletionTime = m_deletedFile.DeletionTime; 130 return S_OK; 131 } 132 133 STDMETHODIMP RecycleBin5File::GetFileSize(ULARGE_INTEGER *pFileSize) 134 { 135 TRACE("(%p, %p)\n", this, pFileSize); 136 137 DWORD dwAttributes = GetFileAttributesW(m_FullName); 138 if (dwAttributes == INVALID_FILE_ATTRIBUTES) 139 return HRESULT_FROM_WIN32(GetLastError()); 140 if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) 141 { 142 pFileSize->QuadPart = 0; 143 return S_OK; 144 } 145 146 HANDLE hFile = CreateFileW(m_FullName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); 147 if (hFile == INVALID_HANDLE_VALUE) 148 return HRESULT_FROM_WIN32(GetLastError()); 149 pFileSize->u.LowPart = ::GetFileSize(hFile, &pFileSize->u.HighPart); 150 151 HRESULT hr; 152 if (pFileSize->u.LowPart != INVALID_FILE_SIZE) 153 hr = S_OK; 154 else 155 hr = HRESULT_FROM_WIN32(GetLastError()); 156 157 CloseHandle(hFile); 158 return hr; 159 } 160 161 STDMETHODIMP RecycleBin5File::GetPhysicalFileSize(ULARGE_INTEGER *pPhysicalFileSize) 162 { 163 TRACE("(%p, %p)\n", this, pPhysicalFileSize); 164 pPhysicalFileSize->u.HighPart = 0; 165 pPhysicalFileSize->u.LowPart = m_deletedFile.dwPhysicalFileSize; 166 return S_OK; 167 } 168 169 STDMETHODIMP RecycleBin5File::GetAttributes(DWORD *pAttributes) 170 { 171 DWORD dwAttributes; 172 173 TRACE("(%p, %p)\n", this, pAttributes); 174 175 dwAttributes = GetFileAttributesW(m_FullName); 176 if (dwAttributes == INVALID_FILE_ATTRIBUTES) 177 return HRESULT_FROM_WIN32(GetLastError()); 178 179 *pAttributes = dwAttributes; 180 return S_OK; 181 } 182 183 STDMETHODIMP RecycleBin5File::GetFileName(SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize) 184 { 185 DWORD dwRequired; 186 187 TRACE("(%p, %u, %p, %p)\n", this, BufferSize, Buffer, RequiredSize); 188 189 dwRequired = (DWORD)(wcslen(m_deletedFile.FileNameW) + 1) * sizeof(WCHAR); 190 if (RequiredSize) 191 *RequiredSize = dwRequired; 192 193 if (BufferSize == 0 && !Buffer) 194 return S_OK; 195 196 if (BufferSize < dwRequired) 197 return E_OUTOFMEMORY; 198 CopyMemory(Buffer, m_deletedFile.FileNameW, dwRequired); 199 return S_OK; 200 } 201 202 STDMETHODIMP RecycleBin5File::GetTypeName(SIZE_T BufferSize, LPWSTR Buffer, SIZE_T *RequiredSize) 203 { 204 HRESULT hr; 205 DWORD dwRequired; 206 DWORD dwAttributes; 207 SHFILEINFOW shFileInfo; 208 209 TRACE("(%p, %u, %p, %p)\n", this, BufferSize, Buffer, RequiredSize); 210 211 hr = GetAttributes(&dwAttributes); 212 if (!SUCCEEDED(hr)) 213 return hr; 214 215 hr = SHGetFileInfoW(m_FullName, dwAttributes, &shFileInfo, sizeof(shFileInfo), SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES); 216 if (!SUCCEEDED(hr)) 217 return hr; 218 219 dwRequired = (DWORD)(wcslen(shFileInfo.szTypeName) + 1) * sizeof(WCHAR); 220 if (RequiredSize) 221 *RequiredSize = dwRequired; 222 223 if (BufferSize == 0 && !Buffer) 224 return S_OK; 225 226 if (BufferSize < dwRequired) 227 return E_OUTOFMEMORY; 228 CopyMemory(Buffer, shFileInfo.szTypeName, dwRequired); 229 return S_OK; 230 } 231 232 STDMETHODIMP RecycleBin5File::Delete() 233 { 234 TRACE("(%p)\n", this); 235 return m_recycleBin->Delete(m_FullName, &m_deletedFile); 236 } 237 238 STDMETHODIMP RecycleBin5File::Restore() 239 { 240 TRACE("(%p)\n", this); 241 return m_recycleBin->Restore(m_FullName, &m_deletedFile); 242 } 243 244 RecycleBin5File::RecycleBin5File() 245 : m_ref(1) 246 , m_recycleBin(NULL) 247 , m_FullName(NULL) 248 { 249 ZeroMemory(&m_deletedFile, sizeof(m_deletedFile)); 250 } 251 252 HRESULT 253 RecycleBin5File::Init( 254 _In_ IRecycleBin5 *prb, 255 _In_ LPCWSTR Folder, 256 _In_ PDELETED_FILE_RECORD pDeletedFile) 257 { 258 m_recycleBin = prb; 259 m_recycleBin->AddRef(); 260 m_deletedFile = *pDeletedFile; 261 262 WCHAR szUniqueId[32]; 263 StringCchPrintfW(szUniqueId, _countof(szUniqueId), L"%lu", pDeletedFile->dwRecordUniqueId); 264 265 CStringW strFullName(Folder); 266 strFullName += L"\\D"; 267 strFullName += (WCHAR)(L'a' + pDeletedFile->dwDriveNumber); 268 strFullName += szUniqueId; 269 strFullName += PathFindExtensionW(pDeletedFile->FileNameW); 270 if (GetFileAttributesW(strFullName) == INVALID_FILE_ATTRIBUTES) 271 return E_FAIL; 272 273 return SHStrDup(strFullName, &m_FullName); 274 } 275 276 static HRESULT 277 RecycleBin5File_Constructor( 278 _In_ IRecycleBin5 *prb, 279 _In_ LPCWSTR Folder, 280 _In_ PDELETED_FILE_RECORD pDeletedFile, 281 _Out_ IRecycleBinFile **ppFile) 282 { 283 if (!ppFile) 284 return E_POINTER; 285 286 *ppFile = NULL; 287 288 RecycleBin5File *pThis = new RecycleBin5File(); 289 if (!pThis) 290 return E_OUTOFMEMORY; 291 292 HRESULT hr = pThis->Init(prb, Folder, pDeletedFile); 293 if (FAILED(hr)) 294 { 295 delete pThis; 296 return hr; 297 } 298 299 *ppFile = static_cast<IRecycleBinFile *>(pThis); 300 return S_OK; 301 } 302 303 class RecycleBin5Enum : public IRecycleBinEnumList 304 { 305 public: 306 RecycleBin5Enum(); 307 virtual ~RecycleBin5Enum(); 308 309 HRESULT Init( 310 _In_ IRecycleBin5 *prb, 311 _In_ HANDLE hInfo, 312 _In_ HANDLE hInfoMapped, 313 _In_ LPCWSTR pszPrefix); 314 315 /* IUnknown methods */ 316 STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override; 317 STDMETHODIMP_(ULONG) AddRef() override; 318 STDMETHODIMP_(ULONG) Release() override; 319 320 /* IRecycleBinEnumList methods */ 321 STDMETHODIMP Next(DWORD celt, IRecycleBinFile **rgelt, DWORD *pceltFetched) override; 322 STDMETHODIMP Skip(DWORD celt) override; 323 STDMETHODIMP Reset() override; 324 325 protected: 326 LONG m_ref; 327 IRecycleBin5 *m_recycleBin; 328 HANDLE m_hInfo; 329 INFO2_HEADER *m_pInfo; 330 DWORD m_dwCurrent; 331 LPWSTR m_pszPrefix; 332 }; 333 334 STDMETHODIMP RecycleBin5Enum::QueryInterface(REFIID riid, void **ppvObject) 335 { 336 TRACE("(%p, %s, %p)\n", this, debugstr_guid(&riid), ppvObject); 337 338 if (!ppvObject) 339 return E_POINTER; 340 341 if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IRecycleBinEnumList)) 342 *ppvObject = static_cast<IRecycleBinEnumList *>(this); 343 else 344 { 345 *ppvObject = NULL; 346 return E_NOINTERFACE; 347 } 348 349 AddRef(); 350 return S_OK; 351 } 352 353 STDMETHODIMP_(ULONG) RecycleBin5Enum::AddRef() 354 { 355 TRACE("(%p)\n", this); 356 return InterlockedIncrement(&m_ref); 357 } 358 359 RecycleBin5Enum::~RecycleBin5Enum() 360 { 361 TRACE("(%p)\n", this); 362 363 m_recycleBin->OnClosing(this); 364 UnmapViewOfFile(m_pInfo); 365 m_recycleBin->Release(); 366 SHFree(m_pszPrefix); 367 } 368 369 STDMETHODIMP_(ULONG) RecycleBin5Enum::Release() 370 { 371 TRACE("(%p)\n", this); 372 373 ULONG refCount = InterlockedDecrement(&m_ref); 374 if (refCount == 0) 375 delete this; 376 return refCount; 377 } 378 379 STDMETHODIMP RecycleBin5Enum::Next(DWORD celt, IRecycleBinFile **rgelt, DWORD *pceltFetched) 380 { 381 HRESULT hr; 382 383 TRACE("(%p, %u, %p, %p)\n", this, celt, rgelt, pceltFetched); 384 385 if (!rgelt) 386 return E_POINTER; 387 if (!pceltFetched && celt > 1) 388 return E_INVALIDARG; 389 390 ULARGE_INTEGER FileSize; 391 FileSize.u.LowPart = GetFileSize(m_hInfo, &FileSize.u.HighPart); 392 if (FileSize.u.LowPart == 0) 393 return HRESULT_FROM_WIN32(GetLastError()); 394 395 DWORD dwEntries = 396 (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD)); 397 398 DWORD iEntry = m_dwCurrent, fetched = 0; 399 PDELETED_FILE_RECORD pDeletedFile = (PDELETED_FILE_RECORD)(m_pInfo + 1) + iEntry; 400 for (; iEntry < dwEntries && fetched < celt; ++iEntry) 401 { 402 hr = RecycleBin5File_Constructor(m_recycleBin, m_pszPrefix, pDeletedFile, &rgelt[fetched]); 403 if (SUCCEEDED(hr)) 404 fetched++; 405 pDeletedFile++; 406 } 407 408 m_dwCurrent = iEntry; 409 if (pceltFetched) 410 *pceltFetched = fetched; 411 if (fetched == celt) 412 return S_OK; 413 else 414 return S_FALSE; 415 } 416 417 STDMETHODIMP RecycleBin5Enum::Skip(DWORD celt) 418 { 419 TRACE("(%p, %u)\n", this, celt); 420 m_dwCurrent += celt; 421 return S_OK; 422 } 423 424 STDMETHODIMP RecycleBin5Enum::Reset() 425 { 426 TRACE("(%p)\n", this); 427 m_dwCurrent = 0; 428 return S_OK; 429 } 430 431 RecycleBin5Enum::RecycleBin5Enum() 432 : m_ref(1) 433 , m_recycleBin(NULL) 434 , m_hInfo(NULL) 435 , m_pInfo(NULL) 436 , m_dwCurrent(0) 437 , m_pszPrefix(NULL) 438 { 439 } 440 441 HRESULT 442 RecycleBin5Enum::Init( 443 _In_ IRecycleBin5 *prb, 444 _In_ HANDLE hInfo, 445 _In_ HANDLE hInfoMapped, 446 _In_ LPCWSTR pszPrefix) 447 { 448 m_recycleBin = prb; 449 m_recycleBin->AddRef(); 450 451 HRESULT hr = SHStrDup(pszPrefix, &m_pszPrefix); 452 if (FAILED(hr)) 453 return hr; 454 455 m_hInfo = hInfo; 456 m_pInfo = (PINFO2_HEADER)MapViewOfFile(hInfoMapped, FILE_MAP_READ, 0, 0, 0); 457 if (!m_pInfo) 458 return HRESULT_FROM_WIN32(GetLastError()); 459 460 if (m_pInfo->dwVersion != 5 || m_pInfo->dwRecordSize != sizeof(DELETED_FILE_RECORD)) 461 return E_FAIL; 462 463 return S_OK; 464 } 465 466 EXTERN_C 467 HRESULT 468 RecycleBin5Enum_Constructor( 469 _In_ IRecycleBin5 *prb, 470 _In_ HANDLE hInfo, 471 _In_ HANDLE hInfoMapped, 472 _In_ LPCWSTR szPrefix, 473 _Out_ IUnknown **ppUnknown) 474 { 475 if (!ppUnknown) 476 return E_POINTER; 477 478 *ppUnknown = NULL; 479 480 RecycleBin5Enum *pThis = new RecycleBin5Enum(); 481 if (!pThis) 482 return E_OUTOFMEMORY; 483 484 HRESULT hr = pThis->Init(prb, hInfo, hInfoMapped, szPrefix); 485 if (FAILED(hr)) 486 { 487 delete pThis; 488 return hr; 489 } 490 491 *ppUnknown = static_cast<IRecycleBinEnumList *>(pThis); 492 return S_OK; 493 } 494