sync with trunk head (34904)
[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 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
476 TRACE("(%p, %s, %p)\n", This, debugstr_w(pDeletedFileName), pDeletedFile);
477
478 if (s->EnumeratorCount != 0)
479 return HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION);
480
481 pHeader = MapViewOfFile(s->hInfoMapped, FILE_MAP_WRITE, 0, 0, 0);
482 if (!pHeader)
483 return HRESULT_FROM_WIN32(GetLastError());
484
485 FileSize.u.LowPart = GetFileSize(s->hInfo, &FileSize.u.HighPart);
486 if (FileSize.u.LowPart == 0)
487 {
488 UnmapViewOfFile(pHeader);
489 return HRESULT_FROM_WIN32(GetLastError());
490 }
491 dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD));
492
493 pRecord = (DELETED_FILE_RECORD *)(pHeader + 1);
494 for (i = 0; i < dwEntries; i++)
495 {
496 if (pRecord->dwRecordUniqueId == pDeletedFile->dwRecordUniqueId)
497 {
498 /* Restore file */
499 if (!MoveFileW(pDeletedFileName, pDeletedFile->FileNameW))
500 {
501 UnmapViewOfFile(pHeader);
502 return HRESULT_FROM_WIN32(GetLastError());
503 }
504
505 /* Clear last entry in the file */
506 MoveMemory(pRecord, pRecord + 1, (dwEntries - i - 1) * sizeof(DELETED_FILE_RECORD));
507 pLast = pRecord + (dwEntries - i - 1);
508 ZeroMemory(pLast, sizeof(DELETED_FILE_RECORD));
509 UnmapViewOfFile(pHeader);
510
511 /* Resize file */
512 CloseHandle(s->hInfoMapped);
513 SetFilePointer(s->hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END);
514 SetEndOfFile(s->hInfo);
515 s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL);
516 if (!s->hInfoMapped)
517 return HRESULT_FROM_WIN32(GetLastError());
518 return S_OK;
519 }
520 pRecord++;
521 }
522
523 UnmapViewOfFile(pHeader);
524 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
525 }
526
527 static HRESULT STDMETHODCALLTYPE
528 RecycleBin5_RecycleBin5_OnClosing(
529 IN IRecycleBin5 *This,
530 IN IRecycleBinEnumList *prbel)
531 {
532 struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
533 TRACE("(%p, %p)\n", This, prbel);
534 s->EnumeratorCount--;
535 return S_OK;
536 }
537
538 CONST_VTBL struct IRecycleBin5Vtbl RecycleBin5Vtbl =
539 {
540 RecycleBin5_RecycleBin5_QueryInterface,
541 RecycleBin5_RecycleBin5_AddRef,
542 RecycleBin5_RecycleBin5_Release,
543 RecycleBin5_RecycleBin5_DeleteFile,
544 RecycleBin5_RecycleBin5_EmptyRecycleBin,
545 RecycleBin5_RecycleBin5_EnumObjects,
546 RecycleBin5_RecycleBin5_Delete,
547 RecycleBin5_RecycleBin5_Restore,
548 RecycleBin5_RecycleBin5_OnClosing,
549 };
550
551 static HRESULT
552 RecycleBin5_Create(
553 IN LPCWSTR Folder,
554 IN PSID OwnerSid OPTIONAL)
555 {
556 LPWSTR BufferName = NULL;
557 LPWSTR Separator; /* Pointer into BufferName buffer */
558 LPWSTR FileName; /* Pointer into BufferName buffer */
559 LPCSTR DesktopIniContents = "[.ShellClassInfo]\r\nCLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n";
560 INFO2_HEADER Info2Contents[] = { { 5, 0, 0, 0x320, 0 } };
561 DWORD BytesToWrite, BytesWritten, Needed;
562 HANDLE hFile = INVALID_HANDLE_VALUE;
563 HRESULT hr;
564
565 Needed = (wcslen(Folder) + 1 + max(wcslen(RECYCLE_BIN_FILE_NAME), wcslen(L"desktop.ini")) + 1) * sizeof(WCHAR);
566 BufferName = HeapAlloc(GetProcessHeap(), 0, Needed);
567 if (!BufferName)
568 {
569 hr = ERROR_NOT_ENOUGH_MEMORY;
570 goto cleanup;
571 }
572
573 wcscpy(BufferName, Folder);
574 Separator = wcsstr(&BufferName[3], L"\\");
575 if (Separator)
576 *Separator = UNICODE_NULL;
577 if (!CreateDirectoryW(BufferName, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
578 {
579 hr = HRESULT_FROM_WIN32(GetLastError());
580 goto cleanup;
581 }
582 SetFileAttributesW(BufferName, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
583 if (Separator)
584 {
585 *Separator = L'\\';
586 if (!CreateDirectoryW(BufferName, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
587 {
588 hr = HRESULT_FROM_WIN32(GetLastError());
589 goto cleanup;
590 }
591 }
592
593 if (OwnerSid)
594 {
595 //DWORD rc;
596
597 /* Add ACL to allow only user/SYSTEM to open it */
598 /* FIXME: rc = SetNamedSecurityInfo(
599 BufferName,
600 SE_FILE_OBJECT,
601 ???,
602 OwnerSid,
603 NULL,
604 ???,
605 ???);
606 if (rc != ERROR_SUCCESS)
607 {
608 hr = HRESULT_FROM_WIN32(rc);
609 goto cleanup;
610 }
611 */
612 }
613
614 wcscat(BufferName, L"\\");
615 FileName = &BufferName[wcslen(BufferName)];
616
617 /* Create desktop.ini */
618 wcscpy(FileName, L"desktop.ini");
619 hFile = CreateFileW(BufferName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, NULL);
620 if (hFile == INVALID_HANDLE_VALUE)
621 {
622 hr = HRESULT_FROM_WIN32(GetLastError());
623 goto cleanup;
624 }
625 BytesToWrite = strlen(DesktopIniContents);
626 if (!WriteFile(hFile, DesktopIniContents, (DWORD)BytesToWrite, &BytesWritten, NULL))
627 {
628 hr = HRESULT_FROM_WIN32(GetLastError());
629 goto cleanup;
630 }
631 if (BytesWritten != BytesToWrite)
632 {
633 hr = E_FAIL;
634 goto cleanup;
635 }
636 CloseHandle(hFile);
637 hFile = INVALID_HANDLE_VALUE;
638
639 /* Create empty INFO2 file */
640 wcscpy(FileName, RECYCLE_BIN_FILE_NAME);
641 hFile = CreateFileW(BufferName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
642 if (hFile == INVALID_HANDLE_VALUE)
643 {
644 hr = HRESULT_FROM_WIN32(GetLastError());
645 goto cleanup;
646 }
647 BytesToWrite = sizeof(Info2Contents);
648 if (!WriteFile(hFile, Info2Contents, (DWORD)BytesToWrite, &BytesWritten, NULL))
649 {
650 hr = HRESULT_FROM_WIN32(GetLastError());
651 goto cleanup;
652 }
653 if (BytesWritten == BytesToWrite)
654 hr = S_OK;
655 else
656 hr = E_FAIL;
657
658 cleanup:
659 HeapFree(GetProcessHeap(), 0, BufferName);
660 if (hFile != INVALID_HANDLE_VALUE)
661 CloseHandle(hFile);
662 return hr;
663 }
664
665 HRESULT RecycleBin5_Constructor(IN LPCWSTR VolumePath, OUT IUnknown **ppUnknown)
666 {
667 struct RecycleBin5 *s = NULL;
668 DWORD FileSystemFlags;
669 LPCWSTR RecycleBinDirectory;
670 HANDLE tokenHandle = INVALID_HANDLE_VALUE;
671 PTOKEN_USER TokenUserInfo = NULL;
672 LPWSTR StringSid = NULL, p;
673 DWORD Needed, DirectoryLength;
674 INT len;
675 HRESULT hr;
676
677 if (!ppUnknown)
678 return E_POINTER;
679
680 /* Get information about file system */
681 if (!GetVolumeInformationW(
682 VolumePath,
683 NULL,
684 0,
685 NULL,
686 NULL,
687 &FileSystemFlags,
688 NULL,
689 0))
690 {
691 hr = HRESULT_FROM_WIN32(GetLastError());
692 goto cleanup;
693 }
694 if (!(FileSystemFlags & FILE_PERSISTENT_ACLS))
695 RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITHOUT_ACL;
696 else
697 {
698 RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITH_ACL;
699
700 /* Get user SID */
701 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tokenHandle))
702 {
703 hr = HRESULT_FROM_WIN32(GetLastError());
704 goto cleanup;
705 }
706 if (GetTokenInformation(tokenHandle, TokenUser, NULL, 0, &Needed))
707 {
708 hr = E_FAIL;
709 goto cleanup;
710 }
711 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
712 {
713 hr = HRESULT_FROM_WIN32(GetLastError());
714 goto cleanup;
715 }
716 TokenUserInfo = HeapAlloc(GetProcessHeap(), 0, Needed);
717 if (!TokenUserInfo)
718 {
719 hr = E_OUTOFMEMORY;
720 goto cleanup;
721 }
722 if (!GetTokenInformation(tokenHandle, TokenUser, TokenUserInfo, (DWORD)Needed, &Needed))
723 {
724 hr = HRESULT_FROM_WIN32(GetLastError());
725 goto cleanup;
726 }
727 if (!ConvertSidToStringSidW(TokenUserInfo->User.Sid, &StringSid))
728 {
729 hr = HRESULT_FROM_WIN32(GetLastError());
730 goto cleanup;
731 }
732 }
733
734 DirectoryLength = wcslen(VolumePath) + wcslen(RecycleBinDirectory) + 1;
735 if (StringSid)
736 DirectoryLength += wcslen(StringSid) + 1;
737 DirectoryLength += 1 + wcslen(RECYCLE_BIN_FILE_NAME);
738 DirectoryLength += wcslen(VolumePath) + 1;
739 Needed = (DirectoryLength + 1) * sizeof(WCHAR);
740
741 s = CoTaskMemAlloc(sizeof(struct RecycleBin5) + Needed);
742 if (!s)
743 {
744 hr = E_OUTOFMEMORY;
745 goto cleanup;
746 }
747 ZeroMemory(s, sizeof(struct RecycleBin5));
748 s->recycleBinImpl.lpVtbl = &RecycleBin5Vtbl;
749 s->ref = 1;
750 if (StringSid)
751 len = swprintf(s->Folder, L"%s%s\\%s", VolumePath, RecycleBinDirectory, StringSid);
752 else
753 len = swprintf(s->Folder, L"%s%s", VolumePath, RecycleBinDirectory);
754 p = &s->Folder[len];
755 wcscpy(p, L"\\" RECYCLE_BIN_FILE_NAME);
756 s->hInfo = CreateFileW(s->Folder, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
757 if (s->hInfo == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_FILE_NOT_FOUND))
758 {
759 *p = UNICODE_NULL;
760 hr = RecycleBin5_Create(s->Folder, TokenUserInfo ? TokenUserInfo->User.Sid : NULL);
761 *p = L'\\';
762 if (!SUCCEEDED(hr))
763 goto cleanup;
764 s->hInfo = CreateFileW(s->Folder, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
765 }
766 if (s->hInfo == INVALID_HANDLE_VALUE)
767 {
768 hr = HRESULT_FROM_WIN32(GetLastError());
769 goto cleanup;
770 }
771 s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL);
772 if (!s->hInfoMapped)
773 {
774 hr = HRESULT_FROM_WIN32(GetLastError());
775 goto cleanup;
776 }
777 *p = UNICODE_NULL;
778 s->VolumePath = p + 1;
779 wcscpy(s->VolumePath, VolumePath);
780
781 *ppUnknown = (IUnknown *)&s->recycleBinImpl;
782
783 hr = S_OK;
784
785 cleanup:
786 if (tokenHandle != INVALID_HANDLE_VALUE)
787 CloseHandle(tokenHandle);
788 HeapFree(GetProcessHeap(), 0, TokenUserInfo);
789 if (StringSid)
790 LocalFree(StringSid);
791 if (!SUCCEEDED(hr))
792 {
793 if (s)
794 RecycleBin5_Destructor(s);
795 }
796 return hr;
797 }