Fix some bugs which were preventing enumeration of all deleted files
[reactos.git] / reactos / lib / recyclebin / recyclebin_v5.c
1 /*
2 * PROJECT: Recycle bin management
3 * LICENSE: GPL v2 - See COPYING in the top level directory
4 * FILE: lib/recyclebin/recyclebin_v5.c
5 * PURPOSE: Deals with recycle bins of Windows 2000/XP/2003
6 * PROGRAMMERS: Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org)
7 */
8
9 #define COBJMACROS
10 #include "recyclebin_v5.h"
11 #include <stdio.h>
12
13 static BOOL
14 IntDeleteRecursive(
15 IN LPCWSTR FullName)
16 {
17 DWORD RemovableAttributes = FILE_ATTRIBUTE_READONLY;
18 DWORD FileAttributes;
19 BOOL ret = FALSE;
20
21 FileAttributes = GetFileAttributesW(FullName);
22 if (FileAttributes == INVALID_FILE_ATTRIBUTES)
23 {
24 if (GetLastError() == ERROR_FILE_NOT_FOUND)
25 ret = TRUE;
26 goto cleanup;
27 }
28 if (FileAttributes & RemovableAttributes)
29 {
30 if (!SetFileAttributesW(FullName, FileAttributes & ~RemovableAttributes))
31 goto cleanup;
32 }
33 if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
34 {
35 /* Recursive deletion */
36 /* FIXME: recursive deletion */
37 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
38 goto cleanup;
39
40 if (!RemoveDirectoryW(FullName))
41 goto cleanup;
42 }
43 else
44 {
45 if (!DeleteFileW(FullName))
46 goto cleanup;
47 }
48 ret = TRUE;
49
50 cleanup:
51 return ret;
52 }
53
54 struct RecycleBin5
55 {
56 ULONG ref;
57 IRecycleBin5 recycleBinImpl;
58 HANDLE hInfo;
59 HANDLE hInfoMapped;
60
61 DWORD EnumeratorCount;
62
63 LPWSTR VolumePath;
64 WCHAR Folder[ANY_SIZE]; /* [drive]:\[RECYCLE_BIN_DIRECTORY]\{SID} */
65 };
66
67 static HRESULT STDMETHODCALLTYPE
68 RecycleBin5_RecycleBin5_QueryInterface(
69 IRecycleBin5 *This,
70 REFIID riid,
71 void **ppvObject)
72 {
73 struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
74
75 if (!ppvObject)
76 return E_POINTER;
77
78 if (IsEqualIID(riid, &IID_IUnknown))
79 *ppvObject = &s->recycleBinImpl;
80 else if (IsEqualIID(riid, &IID_IRecycleBin))
81 *ppvObject = &s->recycleBinImpl;
82 else if (IsEqualIID(riid, &IID_IRecycleBin5))
83 *ppvObject = &s->recycleBinImpl;
84 else
85 {
86 *ppvObject = NULL;
87 return E_NOINTERFACE;
88 }
89
90 IUnknown_AddRef(This);
91 return S_OK;
92 }
93
94 static ULONG STDMETHODCALLTYPE
95 RecycleBin5_RecycleBin5_AddRef(
96 IRecycleBin5 *This)
97 {
98 struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
99 ULONG refCount = InterlockedIncrement((PLONG)&s->ref);
100 return refCount;
101 }
102
103 static ULONG STDMETHODCALLTYPE
104 RecycleBin5_RecycleBin5_Release(
105 IRecycleBin5 *This)
106 {
107 struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
108 ULONG refCount;
109
110 if (!This)
111 return E_POINTER;
112
113 refCount = InterlockedDecrement((PLONG)&s->ref);
114
115 if (refCount == 0)
116 {
117 CloseHandle(s->hInfo);
118 CloseHandle(s->hInfoMapped);
119 CoTaskMemFree(s);
120 }
121
122 return refCount;
123 }
124
125 static HRESULT STDMETHODCALLTYPE
126 RecycleBin5_RecycleBin5_DeleteFile(
127 IN IRecycleBin5 *This,
128 IN LPCWSTR szFileName)
129 {
130 struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
131 LPWSTR szFullName = NULL;
132 DWORD dwBufferLength = 0;
133 LPWSTR lpFilePart;
134 LPCWSTR Extension;
135 WCHAR DeletedFileName[MAX_PATH];
136 DWORD len;
137 HANDLE hFile = INVALID_HANDLE_VALUE;
138 PINFO2_HEADER pHeader = NULL;
139 PDELETED_FILE_RECORD pDeletedFile;
140 ULARGE_INTEGER fileSize;
141 DWORD dwAttributes;
142 SYSTEMTIME SystemTime;
143 DWORD ClusterSize, BytesPerSector, SectorsPerCluster;
144 HRESULT hr;
145
146 if (s->EnumeratorCount != 0)
147 return E_FAIL;
148
149 /* Get full file name */
150 while (TRUE)
151 {
152 len = GetFullPathNameW(szFileName, dwBufferLength, szFullName, &lpFilePart);
153 if (len == 0)
154 {
155 if (szFullName)
156 CoTaskMemFree(szFullName);
157 return HRESULT_FROM_WIN32(GetLastError());
158 }
159 else if (len < dwBufferLength)
160 break;
161 if (szFullName)
162 CoTaskMemFree(szFullName);
163 dwBufferLength = len;
164 szFullName = CoTaskMemAlloc(dwBufferLength * sizeof(WCHAR));
165 if (!szFullName)
166 return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
167 }
168
169 /* Check if file exists */
170 dwAttributes = GetFileAttributesW(szFullName);
171 if (dwAttributes == INVALID_FILE_ATTRIBUTES)
172 return HRESULT_FROM_WIN32(GetLastError());
173
174 if (dwBufferLength < 2 || szFullName[1] != ':')
175 {
176 /* Not a local file */
177 CoTaskMemFree(szFullName);
178 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
179 }
180
181 hFile = CreateFileW(szFullName, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
182 if (hFile == INVALID_HANDLE_VALUE)
183 {
184 hr = HRESULT_FROM_WIN32(GetLastError());
185 goto cleanup;
186 }
187
188 /* Increase INFO2 file size */
189 CloseHandle(s->hInfoMapped);
190 SetFilePointer(s->hInfo, sizeof(DELETED_FILE_RECORD), NULL, FILE_END);
191 SetEndOfFile(s->hInfo);
192 s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL);
193 if (!s->hInfoMapped)
194 {
195 hr = HRESULT_FROM_WIN32(GetLastError());
196 goto cleanup;
197 }
198
199 /* Open INFO2 file */
200 pHeader = MapViewOfFile(s->hInfoMapped, FILE_MAP_WRITE, 0, 0, 0);
201 if (!pHeader)
202 {
203 hr = HRESULT_FROM_WIN32(GetLastError());
204 goto cleanup;
205 }
206 pDeletedFile = ((PDELETED_FILE_RECORD)(pHeader + 1)) + pHeader->dwNumberOfEntries;
207
208 /* Get file size */
209 #if 0
210 if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&fileSize))
211 {
212 hr = HRESULT_FROM_WIN32(GetLastError());
213 goto cleanup;
214 }
215 #else
216 fileSize.u.LowPart = GetFileSize(hFile, &fileSize.u.HighPart);
217 if (fileSize.u.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
218 {
219 hr = HRESULT_FROM_WIN32(GetLastError());
220 goto cleanup;
221 }
222 #endif
223 /* Check if file size is > 4Gb */
224 if (fileSize.u.HighPart != 0)
225 {
226 /* FIXME: how to delete files >= 4Gb? */
227 hr = E_NOTIMPL;
228 goto cleanup;
229 }
230 pHeader->dwTotalLogicalSize += fileSize.u.LowPart;
231
232 /* Generate new name */
233 pHeader->dwHighestRecordUniqueId++;
234 Extension = wcsrchr(szFullName, '.');
235 ZeroMemory(pDeletedFile, sizeof(DELETED_FILE_RECORD));
236 pDeletedFile->dwRecordUniqueId = pHeader->dwHighestRecordUniqueId;
237 pDeletedFile->dwDriveNumber = tolower(szFullName[0]) - 'a';
238 _snwprintf(DeletedFileName, MAX_PATH, L"%s\\D%c%lu%s", s->Folder, pDeletedFile->dwDriveNumber + 'a', pDeletedFile->dwRecordUniqueId, Extension);
239
240 /* Get cluster size */
241 if (!GetDiskFreeSpaceW(s->VolumePath, &SectorsPerCluster, &BytesPerSector, NULL, NULL))
242 {
243 hr = HRESULT_FROM_WIN32(GetLastError());
244 goto cleanup;
245 }
246 ClusterSize = BytesPerSector * SectorsPerCluster;
247
248 /* Get current time */
249 GetSystemTime(&SystemTime);
250 if (!SystemTimeToFileTime(&SystemTime, &pDeletedFile->DeletionTime))
251 {
252 hr = HRESULT_FROM_WIN32(GetLastError());
253 goto cleanup;
254 }
255 pDeletedFile->dwPhysicalFileSize = ROUND_UP(fileSize.u.LowPart, ClusterSize);
256
257 /* Set name */
258 wcscpy(pDeletedFile->FileNameW, szFullName);
259 if (WideCharToMultiByte(CP_ACP, 0, pDeletedFile->FileNameW, -1, pDeletedFile->FileNameA, MAX_PATH, NULL, NULL) == 0)
260 {
261 hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
262 SetLastError(ERROR_INVALID_NAME);
263 goto cleanup;
264 }
265 pHeader->dwNumberOfEntries++;
266
267 /* Move file */
268 if (MoveFileW(szFullName, DeletedFileName))
269 hr = S_OK;
270 else
271 hr = HRESULT_FROM_WIN32(GetLastError());
272
273 cleanup:
274 if (pHeader)
275 UnmapViewOfFile(pHeader);
276 if (hFile != INVALID_HANDLE_VALUE)
277 CloseHandle(hFile);
278 CoTaskMemFree(szFullName);
279 return hr;
280 }
281
282 static HRESULT STDMETHODCALLTYPE
283 RecycleBin5_RecycleBin5_EmptyRecycleBin(
284 IN IRecycleBin5 *This)
285 {
286 IRecycleBinEnumList *prbel;
287 IRecycleBinFile *prbf;
288 HRESULT hr;
289
290 while (TRUE)
291 {
292 hr = IRecycleBin5_EnumObjects(This, &prbel);
293 if (!SUCCEEDED(hr))
294 return hr;
295 hr = IRecycleBinEnumList_Next(prbel, 1, &prbf, NULL);
296 IRecycleBinEnumList_Release(prbel);
297 if (hr == S_FALSE)
298 return S_OK;
299 hr = IRecycleBinFile_Delete(prbf);
300 IRecycleBinFile_Release(prbf);
301 if (!SUCCEEDED(hr))
302 return hr;
303 }
304 }
305
306 static HRESULT STDMETHODCALLTYPE
307 RecycleBin5_RecycleBin5_EnumObjects(
308 IN IRecycleBin5 *This,
309 OUT IRecycleBinEnumList **ppEnumList)
310 {
311 struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
312 IRecycleBinEnumList *prbel;
313 HRESULT hr;
314 IUnknown *pUnk;
315
316 hr = RecycleBin5_Enumerator_Constructor(This, s->hInfo, s->hInfoMapped, s->Folder, &pUnk);
317 if (!SUCCEEDED(hr))
318 return hr;
319
320 hr = IUnknown_QueryInterface(pUnk, &IID_IRecycleBinEnumList, (void **)&prbel);
321 if (SUCCEEDED(hr))
322 {
323 s->EnumeratorCount++;
324 *ppEnumList = prbel;
325 }
326 IUnknown_Release(pUnk);
327 return hr;
328 }
329
330 static HRESULT STDMETHODCALLTYPE
331 RecycleBin5_RecycleBin5_Delete(
332 IN IRecycleBin5 *This,
333 IN LPCWSTR pDeletedFileName,
334 IN DELETED_FILE_RECORD *pDeletedFile)
335 {
336 struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
337 ULARGE_INTEGER FileSize;
338 PINFO2_HEADER pHeader;
339 DELETED_FILE_RECORD *pRecord, *pLast;
340 DWORD dwEntries, i;
341
342 if (s->EnumeratorCount != 0)
343 return E_FAIL;
344
345 pHeader = MapViewOfFile(s->hInfoMapped, FILE_MAP_WRITE, 0, 0, 0);
346 if (!pHeader)
347 return HRESULT_FROM_WIN32(GetLastError());
348
349 FileSize.u.LowPart = GetFileSize(s->hInfo, &FileSize.u.HighPart);
350 if (FileSize.u.LowPart == 0)
351 {
352 UnmapViewOfFile(pHeader);
353 return HRESULT_FROM_WIN32(GetLastError());
354 }
355 dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD));
356
357 pRecord = (DELETED_FILE_RECORD *)(pHeader + 1);
358 for (i = 0; i < dwEntries; i++)
359 {
360 if (pRecord->dwRecordUniqueId == pDeletedFile->dwRecordUniqueId)
361 {
362 /* Delete file */
363 if (!IntDeleteRecursive(pDeletedFileName))
364 {
365 UnmapViewOfFile(pHeader);
366 return HRESULT_FROM_WIN32(GetLastError());
367 }
368
369 /* Clear last entry in the file */
370 MoveMemory(pRecord, pRecord + 1, (dwEntries - i - 1) * sizeof(DELETED_FILE_RECORD));
371 pLast = pRecord + (dwEntries - i - 1);
372 ZeroMemory(pLast, sizeof(DELETED_FILE_RECORD));
373 UnmapViewOfFile(pHeader);
374
375 /* Resize file */
376 CloseHandle(s->hInfoMapped);
377 SetFilePointer(s->hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END);
378 SetEndOfFile(s->hInfo);
379 s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL);
380 if (!s->hInfoMapped)
381 return HRESULT_FROM_WIN32(GetLastError());
382 return S_OK;
383 }
384 pRecord++;
385 }
386 UnmapViewOfFile(pHeader);
387 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
388 }
389
390 static HRESULT STDMETHODCALLTYPE
391 RecycleBin5_RecycleBin5_Restore(
392 IN IRecycleBin5 *This,
393 IN LPCWSTR pDeletedFileName,
394 IN DELETED_FILE_RECORD *pDeletedFile)
395 {
396 struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
397 ULARGE_INTEGER FileSize;
398 PINFO2_HEADER pHeader;
399 DELETED_FILE_RECORD *pRecord, *pLast;
400 DWORD dwEntries, i;
401
402 if (s->EnumeratorCount != 0)
403 return E_FAIL;
404
405 pHeader = MapViewOfFile(s->hInfoMapped, FILE_MAP_WRITE, 0, 0, 0);
406 if (!pHeader)
407 return HRESULT_FROM_WIN32(GetLastError());
408
409 FileSize.u.LowPart = GetFileSize(s->hInfo, &FileSize.u.HighPart);
410 if (FileSize.u.LowPart == 0)
411 {
412 UnmapViewOfFile(pHeader);
413 return HRESULT_FROM_WIN32(GetLastError());
414 }
415 dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD));
416
417 pRecord = (DELETED_FILE_RECORD *)(pHeader + 1);
418 for (i = 0; i < dwEntries; i++)
419 {
420 if (pRecord->dwRecordUniqueId == pDeletedFile->dwRecordUniqueId)
421 {
422 /* Restore file */
423 if (!MoveFileW(pDeletedFileName, pDeletedFile->FileNameW))
424 {
425 UnmapViewOfFile(pHeader);
426 return HRESULT_FROM_WIN32(GetLastError());
427 }
428
429 /* Clear last entry in the file */
430 MoveMemory(pRecord, pRecord + 1, (dwEntries - i - 1) * sizeof(DELETED_FILE_RECORD));
431 pLast = pRecord + (dwEntries - i - 1);
432 ZeroMemory(pLast, sizeof(DELETED_FILE_RECORD));
433 UnmapViewOfFile(pHeader);
434
435 /* Resize file */
436 CloseHandle(s->hInfoMapped);
437 SetFilePointer(s->hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END);
438 SetEndOfFile(s->hInfo);
439 s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL);
440 if (!s->hInfoMapped)
441 return HRESULT_FROM_WIN32(GetLastError());
442 return S_OK;
443 }
444 pRecord++;
445 }
446
447 UnmapViewOfFile(pHeader);
448 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
449 }
450
451 static HRESULT STDMETHODCALLTYPE
452 RecycleBin5_RecycleBin5_OnClosing(
453 IN IRecycleBin5 *This,
454 IN IRecycleBinEnumList *prb5el)
455 {
456 struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
457 s->EnumeratorCount--;
458 return S_OK;
459 }
460
461 CONST_VTBL struct IRecycleBin5Vtbl RecycleBin5Vtbl =
462 {
463 RecycleBin5_RecycleBin5_QueryInterface,
464 RecycleBin5_RecycleBin5_AddRef,
465 RecycleBin5_RecycleBin5_Release,
466 RecycleBin5_RecycleBin5_DeleteFile,
467 RecycleBin5_RecycleBin5_EmptyRecycleBin,
468 RecycleBin5_RecycleBin5_EnumObjects,
469 RecycleBin5_RecycleBin5_Delete,
470 RecycleBin5_RecycleBin5_Restore,
471 RecycleBin5_RecycleBin5_OnClosing,
472 };
473
474 static HRESULT
475 RecycleBin5_Create(
476 IN LPCWSTR Folder,
477 IN PSID OwnerSid OPTIONAL)
478 {
479 LPWSTR BufferName = NULL;
480 LPWSTR Separator; /* Pointer into BufferName buffer */
481 LPWSTR FileName; /* Pointer into BufferName buffer */
482 LPCSTR DesktopIniContents = "[.ShellClassInfo]\r\nCLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n";
483 INFO2_HEADER Info2Contents[] = { { 5, 0, 0, 0x320, 0 } };
484 SIZE_T BytesToWrite, BytesWritten, Needed;
485 HANDLE hFile = INVALID_HANDLE_VALUE;
486 HRESULT hr;
487
488 Needed = (wcslen(Folder) + 1 + max(wcslen(RECYCLE_BIN_FILE_NAME), wcslen(L"desktop.ini")) + 1) * sizeof(WCHAR);
489 BufferName = HeapAlloc(GetProcessHeap(), 0, Needed);
490 if (!BufferName)
491 {
492 hr = ERROR_NOT_ENOUGH_MEMORY;
493 goto cleanup;
494 }
495
496 wcscpy(BufferName, Folder);
497 Separator = wcsstr(&BufferName[3], L"\\");
498 if (Separator)
499 *Separator = UNICODE_NULL;
500 if (!CreateDirectoryW(BufferName, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
501 {
502 hr = HRESULT_FROM_WIN32(GetLastError());
503 goto cleanup;
504 }
505 SetFileAttributesW(BufferName, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
506 if (Separator)
507 {
508 *Separator = L'\\';
509 if (!CreateDirectoryW(BufferName, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
510 {
511 hr = HRESULT_FROM_WIN32(GetLastError());
512 goto cleanup;
513 }
514 }
515
516 if (OwnerSid)
517 {
518 //DWORD rc;
519
520 /* Add ACL to allow only user/SYSTEM to open it */
521 /* FIXME: rc = SetNamedSecurityInfo(
522 BufferName,
523 SE_FILE_OBJECT,
524 ???,
525 OwnerSid,
526 NULL,
527 ???,
528 ???);
529 if (rc != ERROR_SUCCESS)
530 {
531 hr = HRESULT_FROM_WIN32(rc);
532 goto cleanup;
533 }
534 */
535 }
536
537 wcscat(BufferName, L"\\");
538 FileName = &BufferName[wcslen(BufferName)];
539
540 /* Create desktop.ini */
541 wcscpy(FileName, L"desktop.ini");
542 hFile = CreateFileW(BufferName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, NULL);
543 if (hFile == INVALID_HANDLE_VALUE)
544 {
545 hr = HRESULT_FROM_WIN32(GetLastError());
546 goto cleanup;
547 }
548 BytesToWrite = strlen(DesktopIniContents);
549 if (!WriteFile(hFile, DesktopIniContents, (DWORD)BytesToWrite, &BytesWritten, NULL))
550 {
551 hr = HRESULT_FROM_WIN32(GetLastError());
552 goto cleanup;
553 }
554 if (BytesWritten != BytesToWrite)
555 {
556 hr = E_FAIL;
557 goto cleanup;
558 }
559 CloseHandle(hFile);
560 hFile = INVALID_HANDLE_VALUE;
561
562 /* Create empty INFO2 file */
563 wcscpy(FileName, RECYCLE_BIN_FILE_NAME);
564 hFile = CreateFileW(BufferName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
565 if (hFile == INVALID_HANDLE_VALUE)
566 {
567 hr = HRESULT_FROM_WIN32(GetLastError());
568 goto cleanup;
569 }
570 BytesToWrite = sizeof(Info2Contents);
571 if (!WriteFile(hFile, Info2Contents, (DWORD)BytesToWrite, &BytesWritten, NULL))
572 {
573 hr = HRESULT_FROM_WIN32(GetLastError());
574 goto cleanup;
575 }
576 if (BytesWritten == BytesToWrite)
577 hr = S_OK;
578 else
579 hr = E_FAIL;
580
581 cleanup:
582 HeapFree(GetProcessHeap(), 0, BufferName);
583 if (hFile != INVALID_HANDLE_VALUE)
584 CloseHandle(hFile);
585 return hr;
586 }
587
588 HRESULT RecycleBin5_Constructor(IN LPCWSTR VolumePath, OUT IUnknown **ppUnknown)
589 {
590 struct RecycleBin5 *s = NULL;
591 DWORD FileSystemFlags;
592 LPCWSTR RecycleBinDirectory;
593 HANDLE tokenHandle = INVALID_HANDLE_VALUE;
594 PTOKEN_USER TokenUserInfo = NULL;
595 LPWSTR StringSid = NULL, p;
596 SIZE_T Needed, DirectoryLength;
597 INT len;
598 HRESULT hr;
599
600 if (!ppUnknown)
601 return E_POINTER;
602
603 /* Get information about file system */
604 if (!GetVolumeInformationW(
605 VolumePath,
606 NULL,
607 0,
608 NULL,
609 NULL,
610 &FileSystemFlags,
611 NULL,
612 0))
613 {
614 hr = HRESULT_FROM_WIN32(GetLastError());
615 goto cleanup;
616 }
617 if (!(FileSystemFlags & FILE_PERSISTENT_ACLS))
618 RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITHOUT_ACL;
619 else
620 {
621 RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITH_ACL;
622
623 /* Get user SID */
624 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tokenHandle))
625 {
626 hr = HRESULT_FROM_WIN32(GetLastError());
627 goto cleanup;
628 }
629 if (GetTokenInformation(tokenHandle, TokenUser, NULL, 0, &Needed))
630 {
631 hr = E_FAIL;
632 goto cleanup;
633 }
634 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
635 {
636 hr = HRESULT_FROM_WIN32(GetLastError());
637 goto cleanup;
638 }
639 TokenUserInfo = HeapAlloc(GetProcessHeap(), 0, Needed);
640 if (!TokenUserInfo)
641 {
642 hr = E_OUTOFMEMORY;
643 goto cleanup;
644 }
645 if (!GetTokenInformation(tokenHandle, TokenUser, TokenUserInfo, (DWORD)Needed, &Needed))
646 {
647 hr = HRESULT_FROM_WIN32(GetLastError());
648 goto cleanup;
649 }
650 if (!ConvertSidToStringSidW(TokenUserInfo->User.Sid, &StringSid))
651 {
652 hr = HRESULT_FROM_WIN32(GetLastError());
653 goto cleanup;
654 }
655 }
656
657 DirectoryLength = wcslen(VolumePath) + wcslen(RecycleBinDirectory) + 1;
658 if (StringSid)
659 DirectoryLength += wcslen(StringSid) + 1;
660 DirectoryLength += 1 + wcslen(RECYCLE_BIN_FILE_NAME);
661 DirectoryLength += wcslen(VolumePath) + 1;
662 Needed = (DirectoryLength + 1) * sizeof(WCHAR);
663
664 s = CoTaskMemAlloc(sizeof(struct RecycleBin5) + Needed);
665 if (!s)
666 {
667 hr = E_OUTOFMEMORY;
668 goto cleanup;
669 }
670 ZeroMemory(s, sizeof(struct RecycleBin5));
671 s->recycleBinImpl.lpVtbl = &RecycleBin5Vtbl;
672 s->ref = 1;
673 if (StringSid)
674 len = swprintf(s->Folder, L"%s%s\\%s", VolumePath, RecycleBinDirectory, StringSid);
675 else
676 len = swprintf(s->Folder, L"%s%s", VolumePath, RecycleBinDirectory);
677 p = &s->Folder[len];
678 wcscpy(p, L"\\" RECYCLE_BIN_FILE_NAME);
679 s->hInfo = CreateFileW(s->Folder, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
680 if (s->hInfo == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_FILE_NOT_FOUND))
681 {
682 *p = UNICODE_NULL;
683 hr = RecycleBin5_Create(s->Folder, TokenUserInfo ? TokenUserInfo->User.Sid : NULL);
684 *p = L'\\';
685 if (!SUCCEEDED(hr))
686 goto cleanup;
687 s->hInfo = CreateFileW(s->Folder, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
688 }
689 if (s->hInfo == INVALID_HANDLE_VALUE)
690 {
691 hr = HRESULT_FROM_WIN32(GetLastError());
692 goto cleanup;
693 }
694 s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL);
695 if (!s->hInfoMapped)
696 {
697 hr = HRESULT_FROM_WIN32(GetLastError());
698 goto cleanup;
699 }
700 *p = UNICODE_NULL;
701 s->VolumePath = p + 1;
702 wcscpy(s->VolumePath, VolumePath);
703
704 *ppUnknown = (IUnknown *)&s->recycleBinImpl;
705
706 hr = S_OK;
707
708 cleanup:
709 if (tokenHandle != INVALID_HANDLE_VALUE)
710 CloseHandle(tokenHandle);
711 HeapFree(GetProcessHeap(), 0, TokenUserInfo);
712 if (StringSid)
713 LocalFree(StringSid);
714 if (!SUCCEEDED(hr))
715 {
716 if (s)
717 {
718 if (s->hInfo && s->hInfo != INVALID_HANDLE_VALUE)
719 CloseHandle(s->hInfo);
720 if (s->hInfoMapped)
721 CloseHandle(s->hInfoMapped);
722 CoTaskMemFree(s);
723 }
724 }
725 return hr;
726 }