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 a system-wide recycle bin
5 * COPYRIGHT: Copyright 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
11 class RecycleBinGeneric : public IRecycleBin
12 {
13 public:
14 RecycleBinGeneric();
15 virtual ~RecycleBinGeneric();
16
17 /* IUnknown methods */
18 STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override;
19 STDMETHODIMP_(ULONG) AddRef() override;
20 STDMETHODIMP_(ULONG) Release() override;
21
22 /* IRecycleBin methods */
23 STDMETHODIMP DeleteFile(LPCWSTR szFileName) override;
24 STDMETHODIMP EmptyRecycleBin() override;
25 STDMETHODIMP EnumObjects(IRecycleBinEnumList **ppEnumList) override;
GetDirectory(LPWSTR szPath)26 STDMETHODIMP GetDirectory(LPWSTR szPath) override
27 {
28 return E_UNEXPECTED;
29 }
30
31 protected:
32 LONG m_ref;
33 };
34
QueryInterface(REFIID riid,void ** ppvObject)35 STDMETHODIMP RecycleBinGeneric::QueryInterface(REFIID riid, void **ppvObject)
36 {
37 TRACE("(%p, %s, %p)\n", this, debugstr_guid(&riid), ppvObject);
38
39 if (!ppvObject)
40 return E_POINTER;
41
42 if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IRecycleBin))
43 *ppvObject = static_cast<IRecycleBin *>(this);
44 else
45 {
46 *ppvObject = NULL;
47 return E_NOINTERFACE;
48 }
49
50 AddRef();
51 return S_OK;
52 }
53
STDMETHODIMP_(ULONG)54 STDMETHODIMP_(ULONG) RecycleBinGeneric::AddRef()
55 {
56 ULONG refCount = InterlockedIncrement(&m_ref);
57 TRACE("(%p)\n", this);
58 return refCount;
59 }
60
~RecycleBinGeneric()61 RecycleBinGeneric::~RecycleBinGeneric()
62 {
63 TRACE("(%p)\n", this);
64 }
65
STDMETHODIMP_(ULONG)66 STDMETHODIMP_(ULONG) RecycleBinGeneric::Release()
67 {
68 TRACE("(%p)\n", this);
69
70 ULONG refCount = InterlockedDecrement(&m_ref);
71 if (refCount == 0)
72 delete this;
73 return refCount;
74 }
75
DeleteFile(LPCWSTR szFileName)76 STDMETHODIMP RecycleBinGeneric::DeleteFile(LPCWSTR szFileName)
77 {
78 TRACE("(%p, %s)\n", this, debugstr_w(szFileName));
79
80 /* Get full file name */
81 LPWSTR szFullName = NULL;
82 DWORD dwBufferLength = 0;
83 while (TRUE)
84 {
85 DWORD len = GetFullPathNameW(szFileName, dwBufferLength, szFullName, NULL);
86 if (len == 0)
87 {
88 if (szFullName)
89 CoTaskMemFree(szFullName);
90 return HRESULT_FROM_WIN32(GetLastError());
91 }
92 else if (len < dwBufferLength)
93 break;
94 if (szFullName)
95 CoTaskMemFree(szFullName);
96 dwBufferLength = len;
97 szFullName = (LPWSTR)CoTaskMemAlloc(dwBufferLength * sizeof(WCHAR));
98 if (!szFullName)
99 return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
100 }
101
102 /* Get associated volume path */
103 WCHAR szVolume[MAX_PATH];
104 #ifndef __REACTOS__
105 if (!GetVolumePathNameW(szFullName, szVolume, _countof(szVolume)))
106 {
107 CoTaskMemFree(szFullName);
108 return HRESULT_FROM_WIN32(GetLastError());
109 }
110 #else
111 swprintf(szVolume, L"%c:\\", szFullName[0]);
112 #endif
113
114 /* Skip namespace (if any): "\\.\" or "\\?\" */
115 if (szVolume[0] == '\\' &&
116 szVolume[1] == '\\' &&
117 (szVolume[2] == '.' || szVolume[2] == '?') &&
118 szVolume[3] == '\\')
119 {
120 MoveMemory(szVolume, &szVolume[4], (_countof(szVolume) - 4) * sizeof(WCHAR));
121 }
122
123 IRecycleBin *prb;
124 HRESULT hr = GetDefaultRecycleBin(szVolume, &prb);
125 if (!SUCCEEDED(hr))
126 {
127 CoTaskMemFree(szFullName);
128 return hr;
129 }
130
131 hr = prb->DeleteFile(szFullName);
132 CoTaskMemFree(szFullName);
133 prb->Release();
134 return hr;
135 }
136
EmptyRecycleBin()137 STDMETHODIMP RecycleBinGeneric::EmptyRecycleBin()
138 {
139 TRACE("(%p)\n", this);
140
141 DWORD dwLogicalDrives = GetLogicalDrives();
142 if (dwLogicalDrives == 0)
143 return HRESULT_FROM_WIN32(GetLastError());
144
145 for (DWORD i = 0; i < 'Z' - 'A' + 1; i++)
146 {
147 if (!(dwLogicalDrives & (1 << i)))
148 continue;
149
150 WCHAR szVolumeName[MAX_PATH];
151 swprintf(szVolumeName, L"%c:\\", L'A' + i);
152 if (GetDriveTypeW(szVolumeName) != DRIVE_FIXED)
153 continue;
154
155 IRecycleBin *prb;
156 HRESULT hr = GetDefaultRecycleBin(szVolumeName, &prb);
157 if (!SUCCEEDED(hr))
158 return hr;
159
160 hr = prb->EmptyRecycleBin();
161 prb->Release();
162 }
163
164 return S_OK;
165 }
166
EnumObjects(IRecycleBinEnumList ** ppEnumList)167 STDMETHODIMP RecycleBinGeneric::EnumObjects(IRecycleBinEnumList **ppEnumList)
168 {
169 TRACE("(%p, %p)\n", this, ppEnumList);
170 return RecycleBinGenericEnum_Constructor(ppEnumList);
171 }
172
RecycleBinGeneric()173 RecycleBinGeneric::RecycleBinGeneric()
174 : m_ref(1)
175 {
176 }
177
178 EXTERN_C
RecycleBinGeneric_Constructor(OUT IUnknown ** ppUnknown)179 HRESULT RecycleBinGeneric_Constructor(OUT IUnknown **ppUnknown)
180 {
181 /* This RecycleBin implementation was introduced to be able to manage all
182 * drives at once, and instanciate the 'real' implementations when needed */
183 RecycleBinGeneric *pThis = new RecycleBinGeneric();
184 if (!pThis)
185 return E_OUTOFMEMORY;
186
187 *ppUnknown = static_cast<IRecycleBin *>(pThis);
188 return S_OK;
189 }
190
191 EXTERN_C
RecycleBinGeneric_IsEqualFileIdentity(const RECYCLEBINFILEIDENTITY * p1,const RECYCLEBINFILEIDENTITY * p2)192 BOOL RecycleBinGeneric_IsEqualFileIdentity(const RECYCLEBINFILEIDENTITY *p1, const RECYCLEBINFILEIDENTITY *p2)
193 {
194 return p1->DeletionTime.dwLowDateTime == p2->DeletionTime.dwLowDateTime &&
195 p1->DeletionTime.dwHighDateTime == p2->DeletionTime.dwHighDateTime &&
196 _wcsicmp(p1->RecycledFullPath, p2->RecycledFullPath) == 0;
197 }
198