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