24ea4996849a4a42d278cc8585c160f196503ecb
[reactos.git] / reactos / lib / recyclebin / openclose.c
1 /*
2 * PROJECT: Recycle bin management
3 * LICENSE: GPL v2 - See COPYING in the top level directory
4 * FILE: lib/recyclebin/openclose.c
5 * PURPOSE: Open/close recycle bins
6 * PROGRAMMERS: Copyright 2006 Hervé Poussineau (hpoussin@reactos.org)
7 */
8
9 #include "recyclebin_private.h"
10
11 BOOL
12 IntCheckDeletedFileHandle(
13 IN HANDLE hDeletedFile)
14 {
15 if (hDeletedFile == NULL || hDeletedFile == INVALID_HANDLE_VALUE)
16 return FALSE;
17
18 if (((PDELETED_FILE_HANDLE)hDeletedFile)->magic != DELETEDFILE_MAGIC)
19 return FALSE;
20
21 return TRUE;
22 }
23
24 static BOOL
25 IntCloseRecycleBinHandle(
26 IN PREFCOUNT_DATA pData)
27 {
28 PRECYCLE_BIN bin;
29
30 bin = CONTAINING_RECORD(pData, RECYCLE_BIN, refCount);
31 if (!CloseHandle(bin->hInfo))
32 return FALSE;
33
34 RemoveEntryList(&bin->ListEntry);
35 HeapFree(GetProcessHeap(), 0, bin);
36 return TRUE;
37 }
38
39 static BOOL
40 IntCreateEmptyRecycleBin(
41 IN PRECYCLE_BIN bin,
42 IN PSID OwnerSid OPTIONAL)
43 {
44 LPWSTR BufferName = NULL;
45 LPWSTR Separator; /* Pointer into BufferName buffer */
46 LPWSTR FileName; /* Pointer into BufferName buffer */
47 LPCSTR DesktopIniContents = "[.ShellClassInfo]\r\nCLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n";
48 DWORD Info2Contents[] = { 5, 0, 0, 0x320, 0 };
49 SIZE_T BytesToWrite, BytesWritten, Needed;
50 HANDLE hFile = INVALID_HANDLE_VALUE;
51 BOOL ret = FALSE;
52
53 Needed = (wcslen(bin->Folder) + 1 + max(wcslen(RECYCLE_BIN_FILE_NAME), wcslen(L"desktop.ini")) + 1) * sizeof(WCHAR);
54 BufferName = HeapAlloc(GetProcessHeap(), 0, Needed);
55 if (!BufferName)
56 {
57 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
58 goto cleanup;
59 }
60
61 wcscpy(BufferName, bin->Folder);
62 Separator = wcsstr(&BufferName[3], L"\\");
63 if (Separator)
64 *Separator = UNICODE_NULL;
65 ret = CreateDirectoryW(BufferName, NULL);
66 if (!ret && GetLastError() != ERROR_ALREADY_EXISTS)
67 goto cleanup;
68 SetFileAttributesW(BufferName, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
69 if (Separator)
70 {
71 *Separator = L'\\';
72 ret = CreateDirectoryW(BufferName, NULL);
73 if (!ret && GetLastError() != ERROR_ALREADY_EXISTS)
74 goto cleanup;
75 }
76
77 if (OwnerSid)
78 {
79 //DWORD rc;
80
81 /* Add ACL to allow only user/SYSTEM to open it */
82 /* FIXME: rc = SetNamedSecurityInfo(
83 BufferName,
84 SE_FILE_OBJECT,
85 ???,
86 OwnerSid,
87 NULL,
88 ???,
89 ???);
90 if (rc != ERROR_SUCCESS)
91 {
92 SetLastError(rc);
93 goto cleanup;
94 }
95 */
96 }
97
98 wcscat(BufferName, L"\\");
99 FileName = &BufferName[wcslen(BufferName)];
100
101 /* Create desktop.ini */
102 wcscpy(FileName, L"desktop.ini");
103 hFile = CreateFileW(BufferName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, NULL);
104 if (hFile == INVALID_HANDLE_VALUE)
105 goto cleanup;
106 BytesToWrite = strlen(DesktopIniContents);
107 ret = WriteFile(hFile, DesktopIniContents, (DWORD)BytesToWrite, &BytesWritten, NULL);
108 if (!ret)
109 goto cleanup;
110 if (BytesWritten != BytesToWrite)
111 {
112 SetLastError(ERROR_GEN_FAILURE);
113 goto cleanup;
114 }
115 CloseHandle(hFile);
116 hFile = INVALID_HANDLE_VALUE;
117
118 /* Create empty INFO2 file */
119 wcscpy(FileName, RECYCLE_BIN_FILE_NAME);
120 hFile = CreateFileW(BufferName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
121 if (hFile == INVALID_HANDLE_VALUE)
122 goto cleanup;
123 BytesToWrite = sizeof(Info2Contents);
124 ret = WriteFile(hFile, Info2Contents, (DWORD)BytesToWrite, &BytesWritten, NULL);
125 if (!ret)
126 goto cleanup;
127 if (BytesWritten != BytesToWrite)
128 {
129 SetLastError(ERROR_GEN_FAILURE);
130 goto cleanup;
131 }
132
133 bin->hInfo = hFile;
134 ret = TRUE;
135
136 cleanup:
137 HeapFree(GetProcessHeap(), 0, BufferName);
138 if (!ret)
139 {
140 if (hFile != INVALID_HANDLE_VALUE)
141 CloseHandle(hFile);
142 }
143 return ret;
144 }
145
146 PRECYCLE_BIN
147 IntReferenceRecycleBin(
148 IN WCHAR driveLetter)
149 {
150 PLIST_ENTRY ListEntry;
151 PRECYCLE_BIN bin = NULL, ret = NULL;
152 WCHAR RootPath[4];
153 DWORD FileSystemFlags;
154 LPCWSTR RecycleBinDirectory;
155 HANDLE tokenHandle = INVALID_HANDLE_VALUE;
156 PTOKEN_USER TokenUserInfo = NULL;
157 LPWSTR StringSid = NULL;
158 SIZE_T Needed, DirectoryLength;
159 INFO2_HEADER Header;
160 BOOL AlreadyCreated = FALSE;
161 DWORD bytesRead;
162
163 static LIST_ENTRY ListHead;
164 static BOOL ListInitialized = FALSE;
165
166 if (!ListInitialized)
167 {
168 InitializeListHead(&ListHead);
169 ListInitialized = TRUE;
170 }
171
172 /* Search if the recycle bin has already been opened */
173 driveLetter = toupper(driveLetter);
174 ListEntry = ListHead.Flink;
175 while (ListEntry != &ListHead)
176 {
177 bin = CONTAINING_RECORD(ListEntry, RECYCLE_BIN, ListEntry);
178 if (bin->Folder[0] == driveLetter)
179 {
180 ReferenceHandle(&bin->refCount);
181 return bin;
182 }
183 ListEntry = ListEntry->Flink;
184 }
185 bin = NULL;
186
187 /* We need to create a new recycle bin */
188
189 /* Get information about file system */
190 wsprintfW(RootPath, L"%c:\\", driveLetter);
191 if (!GetVolumeInformationW(
192 RootPath,
193 NULL,
194 0,
195 NULL,
196 NULL,
197 &FileSystemFlags,
198 NULL,
199 0))
200 {
201 goto cleanup;
202 }
203 if (!(FileSystemFlags & FILE_PERSISTENT_ACLS))
204 RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITHOUT_ACL;
205 else
206 {
207 RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITH_ACL;
208
209 /* Get user SID */
210 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tokenHandle))
211 goto cleanup;
212 if (GetTokenInformation(tokenHandle, TokenUser, NULL, 0, &Needed))
213 {
214 SetLastError(ERROR_GEN_FAILURE);
215 goto cleanup;
216 }
217 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
218 goto cleanup;
219 TokenUserInfo = HeapAlloc(GetProcessHeap(), 0, Needed);
220 if (!TokenUserInfo)
221 {
222 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
223 goto cleanup;
224 }
225 if (!GetTokenInformation(tokenHandle, TokenUser, TokenUserInfo, (DWORD)Needed, &Needed))
226 goto cleanup;
227 if (!ConvertSidToStringSidW(TokenUserInfo->User.Sid, &StringSid))
228 goto cleanup;
229 }
230
231 /* Create RECYCLEBIN structure */
232 openfile:
233 DirectoryLength = 3 + wcslen(RecycleBinDirectory) + 1;
234 if (StringSid)
235 DirectoryLength += wcslen(StringSid) + 1;
236 Needed = FIELD_OFFSET(RECYCLE_BIN, Folder) + (2 * DirectoryLength + 2 + wcslen(RECYCLE_BIN_FILE_NAME))* sizeof(WCHAR);
237 bin = HeapAlloc(GetProcessHeap(), 0, Needed);
238 if (!bin)
239 {
240 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
241 goto cleanup;
242 }
243 memset(bin, 0, Needed);
244 InitializeHandle(&bin->refCount, IntCloseRecycleBinHandle);
245 bin->magic = RECYCLEBIN_MAGIC;
246 bin->hInfo = INVALID_HANDLE_VALUE;
247 bin->InfoFile = &bin->Folder[DirectoryLength];
248 bin->Folder[0] = driveLetter;
249 bin->Folder[1] = '\0';
250 wcscat(bin->Folder, L":\\");
251 wcscat(bin->Folder, RecycleBinDirectory);
252 if (StringSid)
253 {
254 wcscat(bin->Folder, L"\\");
255 wcscat(bin->Folder, StringSid);
256 }
257 wcscpy(bin->InfoFile, bin->Folder);
258 wcscat(bin->InfoFile, L"\\");
259 wcscat(bin->InfoFile, RECYCLE_BIN_FILE_NAME);
260 InsertTailList(&ListHead, &bin->ListEntry);
261
262 /* Open info file */
263 bin->hInfo = CreateFileW(bin->InfoFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
264 if (bin->hInfo == INVALID_HANDLE_VALUE)
265 {
266 if (GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_FILE_NOT_FOUND)
267 {
268 if (!IntCreateEmptyRecycleBin(bin, TokenUserInfo ? TokenUserInfo->User.Sid : NULL))
269 goto cleanup;
270 AlreadyCreated = TRUE;
271 }
272 }
273 if (bin->hInfo == INVALID_HANDLE_VALUE)
274 goto cleanup;
275
276 if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
277 goto cleanup;
278 if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL))
279 goto cleanup;
280 if (bytesRead != sizeof(INFO2_HEADER) && !AlreadyCreated)
281 {
282 /* Create a new file */
283 if (!DereferenceHandle(&bin->refCount))
284 goto cleanup;
285 if (!DeleteFileW(bin->InfoFile))
286 goto cleanup;
287 goto openfile;
288 }
289 switch (Header.dwVersion)
290 {
291 case 5:
292 InitializeCallbacks5(&bin->Callbacks);
293 break;
294 default:
295 /* Unknown recycle bin version */
296 SetLastError(ERROR_NOT_SUPPORTED);
297 goto cleanup;
298 }
299
300 ret = bin;
301
302 cleanup:
303 if (tokenHandle != INVALID_HANDLE_VALUE)
304 CloseHandle(tokenHandle);
305 HeapFree(GetProcessHeap(), 0, TokenUserInfo);
306 if (StringSid)
307 LocalFree(StringSid);
308 if (!ret)
309 {
310 if (bin && bin->hInfo != INVALID_HANDLE_VALUE)
311 DereferenceHandle(&bin->refCount);
312 HeapFree(GetProcessHeap(), 0, bin);
313 }
314 return ret;
315 }