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