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