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