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