- DBGKD_WAIT_STATE_CHANGE64 is used in KD protocol 5, not number 6 that we use. Proto...
[reactos.git] / reactos / lib / recyclebin / recyclebin_v5.c
index c5b3e8d..3090055 100644 (file)
-/*\r
- * PROJECT:     Recycle bin management\r
- * LICENSE:     GPL v2 - See COPYING in the top level directory\r
- * FILE:        lib/recyclebin/openclose.c\r
- * PURPOSE:     Deals with recycle bins of Windows 2000/XP/2003\r
- * PROGRAMMERS: Copyright 2006 HervĂ© Poussineau (hpoussin@reactos.org)\r
- */\r
-\r
-#include "recyclebin_v5.h"\r
-\r
-VOID\r
-InitializeCallbacks5(\r
-       IN OUT PRECYCLEBIN_CALLBACKS Callbacks)\r
-{\r
-       Callbacks->CloseHandle     = CloseHandle5;\r
-       Callbacks->DeleteFile      = DeleteFile5;\r
-       Callbacks->EmptyRecycleBin = EmptyRecycleBin5;\r
-       Callbacks->EnumerateFiles  = EnumerateFiles5;\r
-       Callbacks->GetDetails      = GetDetails5;\r
-       Callbacks->RestoreFile     = RestoreFile5;\r
-}\r
-\r
-static BOOL\r
-CloseHandle5(\r
-       IN HANDLE hDeletedFile)\r
-{\r
-       UNREFERENCED_PARAMETER(hDeletedFile);\r
-       /* Nothing to do, as hDeletedFile is simply a DWORD... */\r
-       return TRUE;\r
-}\r
-\r
-static BOOL\r
-DeleteFile5(\r
-       IN PRECYCLE_BIN bin,\r
-       IN LPCWSTR FullPath,\r
-       IN LPCWSTR FileName)\r
-{\r
-       HANDLE hFile = INVALID_HANDLE_VALUE;\r
-       INFO2_HEADER Header;\r
-       DELETED_FILE_RECORD DeletedFile;\r
-       DWORD bytesRead, bytesWritten;\r
-       ULARGE_INTEGER fileSize;\r
-       SYSTEMTIME SystemTime;\r
-       WCHAR RootDir[4];\r
-       WCHAR DeletedFileName[2 * MAX_PATH];\r
-       LPCWSTR Extension;\r
-       DWORD ClusterSize, BytesPerSector, SectorsPerCluster;\r
-       BOOL ret = FALSE;\r
-\r
-       if (wcslen(FullPath) >= MAX_PATH)\r
-       {\r
-               /* Unable to store a too long path in recycle bin */\r
-               SetLastError(ERROR_INVALID_NAME);\r
-               goto cleanup;\r
-       }\r
-\r
-       hFile = CreateFileW(FullPath, 0, 0, NULL, OPEN_EXISTING, 0, NULL);\r
-       if (hFile == INVALID_HANDLE_VALUE)\r
-               goto cleanup;\r
-\r
-       if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)\r
-               goto cleanup;\r
-       if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL))\r
-               goto cleanup;\r
-       if (bytesRead != sizeof(INFO2_HEADER) || Header.dwRecordSize == 0)\r
-       {\r
-               SetLastError(ERROR_GEN_FAILURE);\r
-               goto cleanup;\r
-       }\r
-\r
-       if (Header.dwVersion != 5 || Header.dwRecordSize != sizeof(DELETED_FILE_RECORD))\r
-       {\r
-               SetLastError(ERROR_GEN_FAILURE);\r
-               goto cleanup;\r
-       }\r
-\r
-       /* Get file size */\r
-#if 0\r
-       if (!GetFileSizeEx(hFile, &fileSize))\r
-               goto cleanup;\r
-#else\r
-       fileSize.u.LowPart = GetFileSize(hFile, &fileSize.u.HighPart);\r
-       if (fileSize.u.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)\r
-               goto cleanup;\r
-#endif\r
-       /* Check if file size is > 4Gb */\r
-       if (fileSize.u.HighPart != 0)\r
-       {\r
-               /* FIXME: how to delete files >= 4Gb? */\r
-               SetLastError(ERROR_CALL_NOT_IMPLEMENTED);\r
-               goto cleanup;\r
-       }\r
-       Header.dwTotalLogicalSize += fileSize.u.LowPart;\r
-\r
-       /* Generate new name */\r
-       Header.dwHighestRecordUniqueId++;\r
-       Extension = wcsrchr(FileName, '.');\r
-       wsprintfW(DeletedFileName, L"%s\\D%c%lu%s", bin->Folder, FullPath[0] - 'A' + 'a', Header.dwHighestRecordUniqueId, Extension);\r
-\r
-       /* Get cluster size */\r
-       wsprintfW(RootDir, L"%c:\\", bin->Folder[0]);\r
-       if (!GetDiskFreeSpaceW(RootDir, &SectorsPerCluster, &BytesPerSector, NULL, NULL))\r
-               goto cleanup;\r
-       ClusterSize = BytesPerSector * SectorsPerCluster;\r
-\r
-       /* Get current time */\r
-       GetSystemTime(&SystemTime);\r
-       if (!SystemTimeToFileTime(&SystemTime, &DeletedFile.DeletionTime))\r
-               goto cleanup;\r
-\r
-       /* Update INFO2 */\r
-       memset(&DeletedFile, 0, sizeof(DELETED_FILE_RECORD));\r
-       if (WideCharToMultiByte(CP_ACP, 0, FullPath, -1, DeletedFile.FileNameA, MAX_PATH, NULL, NULL) == 0)\r
-       {\r
-               SetLastError(ERROR_INVALID_NAME);\r
-               goto cleanup;\r
-       }\r
-       DeletedFile.dwRecordUniqueId = Header.dwHighestRecordUniqueId;\r
-       DeletedFile.dwDriveNumber = tolower(bin->Folder[0]) - 'a';\r
-       DeletedFile.dwPhysicalFileSize = ROUND_UP(fileSize.u.LowPart, ClusterSize);\r
-       wcscpy(DeletedFile.FileNameW, FullPath);\r
-\r
-       if (!SetFilePointer(bin->hInfo, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)\r
-               goto cleanup;\r
-       if (!WriteFile(bin->hInfo, &DeletedFile, sizeof(DELETED_FILE_RECORD), &bytesWritten, NULL))\r
-               goto cleanup;\r
-       if (bytesWritten != sizeof(DELETED_FILE_RECORD))\r
-       {\r
-               SetLastError(ERROR_GEN_FAILURE);\r
-               goto cleanup;\r
-       }\r
-       Header.dwNumberOfEntries++;\r
-       if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)\r
-       {\r
-               goto cleanup;\r
-       }\r
-       if (!WriteFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesWritten, NULL))\r
-               goto cleanup;\r
-       if (bytesWritten != sizeof(INFO2_HEADER))\r
-       {\r
-               SetLastError(ERROR_GEN_FAILURE);\r
-               goto cleanup;\r
-       }\r
-\r
-       /* Move file */\r
-       if (!MoveFileW(FullPath, DeletedFileName))\r
-               goto cleanup;\r
-\r
-       ret = TRUE;\r
-\r
-cleanup:\r
-       if (hFile != INVALID_HANDLE_VALUE)\r
-               CloseHandle(hFile);\r
-       return ret;\r
-}\r
-\r
-static BOOL\r
-EmptyRecycleBin5(\r
-       IN PRECYCLE_BIN* bin)\r
-{\r
-       LPWSTR InfoFile = NULL;\r
-       BOOL ret = FALSE;\r
-\r
-       InfoFile = HeapAlloc(GetProcessHeap(), 0, wcslen((*bin)->InfoFile) * sizeof(WCHAR) + sizeof(UNICODE_NULL));\r
-       if (!InfoFile)\r
-       {\r
-               SetLastError(ERROR_NOT_ENOUGH_MEMORY);\r
-               goto cleanup;\r
-       }\r
-       wcscpy(InfoFile, (*bin)->InfoFile);\r
-\r
-       /* Delete all files in the recycle bin */\r
-       if (!EnumerateFiles5(*bin, IntEmptyRecycleBinCallback, *bin))\r
-               goto cleanup;\r
-\r
-       /* Delete INFO2 */\r
-       if (!DereferenceHandle(&(*bin)->refCount))\r
-               goto cleanup;\r
-       if (!DeleteFileW(InfoFile))\r
-               goto cleanup;\r
-       *bin = NULL;\r
-       ret = TRUE;\r
-\r
-cleanup:\r
-       HeapFree(GetProcessHeap(), 0, InfoFile);\r
-       return ret;\r
-}\r
-\r
-static BOOL\r
-EnumerateFiles5(\r
-       IN PRECYCLE_BIN bin,\r
-       IN PINT_ENUMERATE_RECYCLEBIN_CALLBACK pFnCallback,\r
-       IN PVOID Context OPTIONAL)\r
-{\r
-       INFO2_HEADER Header;\r
-       DELETED_FILE_RECORD DeletedFile;\r
-       DWORD bytesRead, dwEntries;\r
-       BOOL ret = FALSE;\r
-\r
-       if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)\r
-               goto cleanup;\r
-       if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL))\r
-               goto cleanup;\r
-       if (bytesRead != sizeof(INFO2_HEADER) || Header.dwRecordSize == 0)\r
-       {\r
-               SetLastError(ERROR_GEN_FAILURE);\r
-               goto cleanup;\r
-       }\r
-\r
-       if (Header.dwVersion != 5 || Header.dwRecordSize != sizeof(DELETED_FILE_RECORD))\r
-       {\r
-               SetLastError(ERROR_GEN_FAILURE);\r
-               goto cleanup;\r
-       }\r
-\r
-       SetLastError(ERROR_SUCCESS);\r
-       for (dwEntries = 0; dwEntries < Header.dwNumberOfEntries; dwEntries++)\r
-       {\r
-               if (!ReadFile(bin->hInfo, &DeletedFile, Header.dwRecordSize, &bytesRead, NULL))\r
-                       goto cleanup;\r
-               if (bytesRead != Header.dwRecordSize)\r
-               {\r
-                       SetLastError(ERROR_GEN_FAILURE);\r
-                       goto cleanup;\r
-               }\r
-               if (!pFnCallback(Context, (HANDLE)(ULONG_PTR)DeletedFile.dwRecordUniqueId))\r
-                       goto cleanup;\r
-               if (SetFilePointer(bin->hInfo, sizeof(INFO2_HEADER) + Header.dwRecordSize * dwEntries, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)\r
-                       goto cleanup;\r
-       }\r
-\r
-       ret = TRUE;\r
-\r
-cleanup:\r
-       return ret;\r
-}\r
-\r
-static BOOL\r
-GetDetails5(\r
-       IN PRECYCLE_BIN bin,\r
-       IN HANDLE hDeletedFile,\r
-       IN DWORD BufferSize,\r
-       IN OUT PDELETED_FILE_DETAILS_W FileDetails OPTIONAL,\r
-       OUT LPDWORD RequiredSize OPTIONAL)\r
-{\r
-       DELETED_FILE_RECORD DeletedFile;\r
-       SIZE_T Needed;\r
-       LPWSTR FullName = NULL;\r
-       HANDLE hFile = INVALID_HANDLE_VALUE;\r
-       BOOL ret = FALSE;\r
-\r
-       /* Check parameters */\r
-       if (BufferSize > 0 && FileDetails == NULL)\r
-       {\r
-               SetLastError(ERROR_INVALID_PARAMETER);\r
-               goto cleanup;\r
-       }\r
-\r
-       if (!IntSearchRecord(bin, hDeletedFile, &DeletedFile, NULL))\r
-               goto cleanup;\r
-       Needed = FIELD_OFFSET(DELETED_FILE_DETAILS_W, FileName) + (wcslen(DeletedFile.FileNameW) + 1) * sizeof(WCHAR);\r
-       if (RequiredSize)\r
-               *RequiredSize = (DWORD)Needed;\r
-       if (Needed > BufferSize)\r
-       {\r
-               SetLastError(ERROR_INSUFFICIENT_BUFFER);\r
-               goto cleanup;\r
-       }\r
-\r
-       if (!IntGetFullName(bin, &DeletedFile, &FullName))\r
-               goto cleanup;\r
-\r
-       /* Open file */\r
-       FileDetails->Attributes = GetFileAttributesW(FullName);\r
-       if (FileDetails->Attributes == INVALID_FILE_ATTRIBUTES)\r
-               goto cleanup;\r
-       if (FileDetails->Attributes & FILE_ATTRIBUTE_DIRECTORY)\r
-               hFile = CreateFileW(FullName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);\r
-       else\r
-               hFile = CreateFileW(FullName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);\r
-       if (hFile == INVALID_HANDLE_VALUE)\r
-               goto cleanup;\r
-\r
-       /* Fill returned structure */\r
-       if (!GetFileTime(hFile, NULL, NULL, &FileDetails->LastModification))\r
-               goto cleanup;\r
-       memcpy(&FileDetails->DeletionTime, &DeletedFile.DeletionTime, sizeof(FILETIME));\r
-       FileDetails->FileSize.u.LowPart = GetFileSize(hFile, &FileDetails->FileSize.u.HighPart);\r
-       if (FileDetails->FileSize.u.LowPart == INVALID_FILE_SIZE)\r
-               goto cleanup;\r
-       FileDetails->PhysicalFileSize.u.HighPart = 0;\r
-       FileDetails->PhysicalFileSize.u.LowPart = DeletedFile.dwPhysicalFileSize;\r
-       wcscpy(FileDetails->FileName, DeletedFile.FileNameW);\r
-\r
-       ret = TRUE;\r
-\r
-cleanup:\r
-       HeapFree(GetProcessHeap(), 0, FullName);\r
-       if (hFile != INVALID_HANDLE_VALUE)\r
-               CloseHandle(hFile);\r
-       return ret;\r
-}\r
-\r
-static BOOL\r
-RestoreFile5(\r
-       IN PRECYCLE_BIN bin,\r
-       IN HANDLE hDeletedFile)\r
-{\r
-       INFO2_HEADER Header;\r
-       DWORD bytesRead, bytesWritten;\r
-       LARGE_INTEGER Position;\r
-       DELETED_FILE_RECORD DeletedFile, LastFile;\r
-       LPWSTR FullName = NULL;\r
-       BOOL ret = FALSE;\r
-\r
-       if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)\r
-               goto cleanup;\r
-       if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL))\r
-               goto cleanup;\r
-       if (bytesRead != sizeof(INFO2_HEADER) || Header.dwRecordSize == 0)\r
-       {\r
-               SetLastError(ERROR_GEN_FAILURE);\r
-               goto cleanup;\r
-       }\r
-\r
-       if (Header.dwVersion != 5 || Header.dwRecordSize != sizeof(DELETED_FILE_RECORD))\r
-       {\r
-               SetLastError(ERROR_GEN_FAILURE);\r
-               goto cleanup;\r
-       }\r
-\r
-       /* Search deleted entry */\r
-       if (!IntSearchRecord(bin, hDeletedFile, &DeletedFile, &Position))\r
-               goto cleanup;\r
-       /* Get destination full name */\r
-       if (!IntGetFullName(bin, &DeletedFile, &FullName))\r
-               goto cleanup;\r
-       /* Restore file */\r
-       if (!MoveFileW(FullName, DeletedFile.FileNameW))\r
-               goto cleanup;\r
-\r
-       /* Update INFO2 */\r
-       /* 1) If not last entry, copy last entry to the current one */\r
-       if (SetFilePointer(bin->hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END) == INVALID_SET_FILE_POINTER)\r
-               goto cleanup;\r
-       if (!ReadFile(bin->hInfo, &LastFile, sizeof(DELETED_FILE_RECORD), &bytesRead, NULL))\r
-               goto cleanup;\r
-       if (bytesRead != sizeof(DELETED_FILE_RECORD))\r
-       {\r
-               SetLastError(ERROR_GEN_FAILURE);\r
-               goto cleanup;\r
-       }\r
-       if (LastFile.dwRecordUniqueId != DeletedFile.dwRecordUniqueId)\r
-       {\r
-               /* Move the last entry to the current one */\r
-               if (SetFilePointer(bin->hInfo, Position.u.LowPart, &Position.u.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER)\r
-                       goto cleanup;\r
-               if (!WriteFile(bin->hInfo, &LastFile, sizeof(DELETED_FILE_RECORD), &bytesWritten, NULL))\r
-                       goto cleanup;\r
-               if (bytesWritten != sizeof(DELETED_FILE_RECORD))\r
-               {\r
-                       SetLastError(ERROR_GEN_FAILURE);\r
-                       goto cleanup;\r
-               }\r
-       }\r
-       /* 2) Update the header */\r
-       Header.dwNumberOfEntries--;\r
-       if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)\r
-               goto cleanup;\r
-       if (!WriteFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesWritten, NULL))\r
-               goto cleanup;\r
-       if (bytesWritten != sizeof(INFO2_HEADER))\r
-       {\r
-               SetLastError(ERROR_GEN_FAILURE);\r
-               goto cleanup;\r
-       }\r
-       /* 3) Truncate file */\r
-       if (SetFilePointer(bin->hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END) == INVALID_SET_FILE_POINTER)\r
-               goto cleanup;\r
-       if (!SetEndOfFile(bin->hInfo))\r
-               goto cleanup;\r
-       ret = TRUE;\r
-\r
-cleanup:\r
-       HeapFree(GetProcessHeap(), 0, FullName);\r
-       return ret;\r
-}\r
-\r
-static BOOL\r
-IntDeleteRecursive(\r
-       IN LPCWSTR FullName)\r
-{\r
-       DWORD RemovableAttributes = FILE_ATTRIBUTE_READONLY;\r
-       DWORD FileAttributes;\r
-       BOOL ret = FALSE;\r
-\r
-       FileAttributes = GetFileAttributesW(FullName);\r
-       if (FileAttributes == INVALID_FILE_ATTRIBUTES)\r
-       {\r
-               if (GetLastError() == ERROR_FILE_NOT_FOUND)\r
-                       ret = TRUE;\r
-               goto cleanup;\r
-       }\r
-       if (FileAttributes & RemovableAttributes)\r
-       {\r
-               if (!SetFileAttributesW(FullName, FileAttributes & ~RemovableAttributes))\r
-                       goto cleanup;\r
-       }\r
-       if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)\r
-       {\r
-               /* Recursive deletion */\r
-               /* FIXME: recursive deletion */\r
-               SetLastError(ERROR_CALL_NOT_IMPLEMENTED);\r
-               goto cleanup;\r
-\r
-               if (!RemoveDirectoryW(FullName))\r
-                       goto cleanup;\r
-       }\r
-       else\r
-       {\r
-               if (!DeleteFileW(FullName))\r
-                       goto cleanup;\r
-       }\r
-       ret = TRUE;\r
-\r
-cleanup:\r
-       return ret;\r
-}\r
-\r
-static BOOL\r
-IntEmptyRecycleBinCallback(\r
-       IN PVOID Context,\r
-       IN HANDLE hDeletedFile)\r
-{\r
-       PRECYCLE_BIN bin = (PRECYCLE_BIN)Context;\r
-       DELETED_FILE_RECORD DeletedFile;\r
-       LPWSTR FullName = NULL;\r
-       BOOL ret = FALSE;\r
-\r
-       if (!IntSearchRecord(bin, hDeletedFile, &DeletedFile, NULL))\r
-               goto cleanup;\r
-\r
-       if (!IntGetFullName(bin, &DeletedFile, &FullName))\r
-               goto cleanup;\r
-\r
-       if (!IntDeleteRecursive(FullName))\r
-               goto cleanup;\r
-       ret = TRUE;\r
-\r
-cleanup:\r
-       HeapFree(GetProcessHeap(), 0, FullName);\r
-       return ret;\r
-}\r
-\r
-static BOOL\r
-IntGetFullName(\r
-       IN PRECYCLE_BIN bin,\r
-       IN PDELETED_FILE_RECORD pDeletedFile,\r
-       OUT LPWSTR* pFullName)\r
-{\r
-       SIZE_T Needed;\r
-       LPCWSTR Extension;\r
-       LPWSTR FullName = NULL;\r
-       BOOL ret = FALSE;\r
-\r
-       *pFullName = NULL;\r
-       Extension = wcsrchr(pDeletedFile->FileNameW, '.');\r
-       if (Extension < wcsrchr(pDeletedFile->FileNameW, '\\'))\r
-               Extension = NULL;\r
-       Needed = wcslen(bin->Folder) + 13;\r
-       if (Extension)\r
-               Needed += wcslen(Extension);\r
-       FullName = HeapAlloc(GetProcessHeap(), 0, Needed * sizeof(WCHAR));\r
-       if (!FullName)\r
-       {\r
-               SetLastError(ERROR_NOT_ENOUGH_MEMORY);\r
-               goto cleanup;\r
-       }\r
-       wsprintfW(FullName, L"%s\\D%c%lu%s", bin->Folder, pDeletedFile->dwDriveNumber + 'a', pDeletedFile->dwRecordUniqueId, Extension);\r
-       *pFullName = FullName;\r
-       ret = TRUE;\r
-\r
-cleanup:\r
-       if (!ret)\r
-               HeapFree(GetProcessHeap(), 0, FullName);\r
-       return ret;\r
-}\r
-\r
-static BOOL\r
-IntSearchRecord(\r
-       IN PRECYCLE_BIN bin,\r
-       IN HANDLE hDeletedFile,\r
-       OUT PDELETED_FILE_RECORD pDeletedFile,\r
-       OUT PLARGE_INTEGER Position OPTIONAL)\r
-{\r
-       INFO2_HEADER Header;\r
-       DELETED_FILE_RECORD DeletedFile;\r
-       DWORD bytesRead, dwEntries;\r
-       BOOL ret = FALSE;\r
-\r
-       if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)\r
-               goto cleanup;\r
-       if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL))\r
-               goto cleanup;\r
-       if (bytesRead != sizeof(INFO2_HEADER) || Header.dwRecordSize == 0)\r
-       {\r
-               SetLastError(ERROR_GEN_FAILURE);\r
-               goto cleanup;\r
-       }\r
-\r
-       if (Header.dwVersion != 5 || Header.dwRecordSize != sizeof(DELETED_FILE_RECORD))\r
-       {\r
-               SetLastError(ERROR_GEN_FAILURE);\r
-               goto cleanup;\r
-       }\r
-\r
-       SetLastError(ERROR_SUCCESS);\r
-       for (dwEntries = 0; dwEntries < Header.dwNumberOfEntries; dwEntries++)\r
-       {\r
-               if (Position)\r
-               {\r
-                       LARGE_INTEGER Zero;\r
-                       Zero.QuadPart = 0;\r
-                       if (!SetFilePointerEx(bin->hInfo, Zero, Position, FILE_CURRENT))\r
-                               goto cleanup;\r
-               }\r
-               if (!ReadFile(bin->hInfo, &DeletedFile, Header.dwRecordSize, &bytesRead, NULL))\r
-                       goto cleanup;\r
-               if (bytesRead != Header.dwRecordSize)\r
-               {\r
-                       SetLastError(ERROR_GEN_FAILURE);\r
-                       goto cleanup;\r
-               }\r
-               if (DeletedFile.dwRecordUniqueId == (DWORD)(ULONG_PTR)hDeletedFile)\r
-               {\r
-                       memcpy(pDeletedFile, &DeletedFile, Header.dwRecordSize);\r
-                       ret = TRUE;\r
-                       goto cleanup;\r
-               }\r
-       }\r
-\r
-       /* Entry not found */\r
-       SetLastError(ERROR_INVALID_HANDLE);\r
-\r
-cleanup:\r
-       return ret;\r
-}\r
+/*
+ * PROJECT:     Recycle bin management
+ * LICENSE:     GPL v2 - See COPYING in the top level directory
+ * FILE:        lib/recyclebin/recyclebin_v5.c
+ * PURPOSE:     Deals with recycle bins of Windows 2000/XP/2003
+ * PROGRAMMERS: Copyright 2006-2007 HervĂ© Poussineau (hpoussin@reactos.org)
+ */
+
+#define COBJMACROS
+#include "recyclebin_v5.h"
+#include <stdio.h>
+
+WINE_DEFAULT_DEBUG_CHANNEL(recyclebin);
+
+static BOOL
+IntDeleteRecursive(
+       IN LPCWSTR FullName)
+{
+       DWORD RemovableAttributes = FILE_ATTRIBUTE_READONLY;
+       WIN32_FIND_DATAW FindData;
+       HANDLE hSearch = INVALID_HANDLE_VALUE;
+       LPWSTR FullPath = NULL, pFilePart;
+       DWORD FileAttributes;
+       SIZE_T dwLength;
+       BOOL ret = FALSE;
+
+       FileAttributes = GetFileAttributesW(FullName);
+       if (FileAttributes == INVALID_FILE_ATTRIBUTES)
+       {
+               if (GetLastError() == ERROR_FILE_NOT_FOUND)
+                       ret = TRUE;
+               goto cleanup;
+       }
+       if (FileAttributes & RemovableAttributes)
+       {
+               if (!SetFileAttributesW(FullName, FileAttributes & ~RemovableAttributes))
+                       goto cleanup;
+       }
+       if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+       {
+               /* Prepare file specification */
+               dwLength = wcslen(FullName);
+               FullPath = HeapAlloc(GetProcessHeap(), 0, (dwLength + 1 + MAX_PATH + 1) * sizeof(WCHAR));
+               if (!FullPath)
+               {
+                       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                       goto cleanup;
+               }
+               wcscpy(FullPath, FullName);
+               if (FullPath[dwLength - 1] != '\\')
+               {
+                       FullPath[dwLength] = '\\';
+                       dwLength++;
+               }
+               pFilePart = &FullPath[dwLength];
+               wcscpy(pFilePart, L"*");
+
+               /* Enumerate contents, and delete it */
+               hSearch = FindFirstFileW(FullPath, &FindData);
+               if (hSearch == INVALID_HANDLE_VALUE)
+                       goto cleanup;
+               do
+               {
+                       if (!(FindData.cFileName[0] == '.' &&
+                               (FindData.cFileName[1] == '\0' || (FindData.cFileName[1] == '.' && FindData.cFileName[2] == '\0'))))
+                       {
+                               wcscpy(pFilePart, FindData.cFileName);
+                               if (!IntDeleteRecursive(FullPath))
+                               {
+                                       FindClose(hSearch);
+                                       goto cleanup;
+                               }
+                       }
+               }
+               while (FindNextFileW(hSearch, &FindData));
+               FindClose(hSearch);
+               if (GetLastError() != ERROR_NO_MORE_FILES)
+                       goto cleanup;
+
+               /* Remove (now empty) directory */
+               if (!RemoveDirectoryW(FullName))
+                       goto cleanup;
+       }
+       else
+       {
+               if (!DeleteFileW(FullName))
+                       goto cleanup;
+       }
+       ret = TRUE;
+
+cleanup:
+       HeapFree(GetProcessHeap(), 0, FullPath);
+       return ret;
+}
+
+struct RecycleBin5
+{
+       ULONG ref;
+       IRecycleBin5 recycleBinImpl;
+       HANDLE hInfo;
+       HANDLE hInfoMapped;
+
+       DWORD EnumeratorCount;
+
+       LPWSTR VolumePath;
+       WCHAR Folder[ANY_SIZE]; /* [drive]:\[RECYCLE_BIN_DIRECTORY]\{SID} */
+};
+
+static HRESULT STDMETHODCALLTYPE
+RecycleBin5_RecycleBin5_QueryInterface(
+       IRecycleBin5 *This,
+       REFIID riid,
+       void **ppvObject)
+{
+       struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
+
+       TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
+
+       if (!ppvObject)
+               return E_POINTER;
+
+       if (IsEqualIID(riid, &IID_IUnknown))
+               *ppvObject = &s->recycleBinImpl;
+       else if (IsEqualIID(riid, &IID_IRecycleBin))
+               *ppvObject = &s->recycleBinImpl;
+       else if (IsEqualIID(riid, &IID_IRecycleBin5))
+               *ppvObject = &s->recycleBinImpl;
+       else
+       {
+               *ppvObject = NULL;
+               return E_NOINTERFACE;
+       }
+
+       IUnknown_AddRef(This);
+       return S_OK;
+}
+
+static ULONG STDMETHODCALLTYPE
+RecycleBin5_RecycleBin5_AddRef(
+       IRecycleBin5 *This)
+{
+       struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
+       ULONG refCount = InterlockedIncrement((PLONG)&s->ref);
+       TRACE("(%p)\n", This);
+       return refCount;
+}
+
+static VOID
+RecycleBin5_Destructor(
+       struct RecycleBin5 *s)
+{
+       TRACE("(%p)\n", s);
+
+       if (s->hInfo && s->hInfo != INVALID_HANDLE_VALUE)
+               CloseHandle(s->hInfo);
+       if (s->hInfoMapped)
+               CloseHandle(s->hInfoMapped);
+       CoTaskMemFree(s);
+}
+
+static ULONG STDMETHODCALLTYPE
+RecycleBin5_RecycleBin5_Release(
+       IRecycleBin5 *This)
+{
+       struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
+       ULONG refCount;
+
+       TRACE("(%p)\n", This);
+
+       refCount = InterlockedDecrement((PLONG)&s->ref);
+
+       if (refCount == 0)
+               RecycleBin5_Destructor(s);
+
+       return refCount;
+}
+
+static HRESULT STDMETHODCALLTYPE
+RecycleBin5_RecycleBin5_DeleteFile(
+       IN IRecycleBin5 *This,
+       IN LPCWSTR szFileName)
+{
+       struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
+       LPWSTR szFullName = NULL;
+       DWORD dwBufferLength = 0;
+       LPWSTR lpFilePart;
+       LPCWSTR Extension;
+       WCHAR DeletedFileName[MAX_PATH];
+       DWORD len;
+       HANDLE hFile = INVALID_HANDLE_VALUE;
+       PINFO2_HEADER pHeader = NULL;
+       PDELETED_FILE_RECORD pDeletedFile;
+       ULARGE_INTEGER FileSize;
+       DWORD dwAttributes, dwEntries;
+       SYSTEMTIME SystemTime;
+       DWORD ClusterSize, BytesPerSector, SectorsPerCluster;
+       HRESULT hr;
+
+       TRACE("(%p, %s)\n", This, debugstr_w(szFileName));
+
+       if (s->EnumeratorCount != 0)
+               return HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION);
+
+       /* Get full file name */
+       while (TRUE)
+       {
+               len = GetFullPathNameW(szFileName, dwBufferLength, szFullName, &lpFilePart);
+               if (len == 0)
+               {
+                       if (szFullName)
+                               CoTaskMemFree(szFullName);
+                       return HRESULT_FROM_WIN32(GetLastError());
+               }
+               else if (len < dwBufferLength)
+                       break;
+               if (szFullName)
+                       CoTaskMemFree(szFullName);
+               dwBufferLength = len;
+               szFullName = CoTaskMemAlloc(dwBufferLength * sizeof(WCHAR));
+               if (!szFullName)
+                       return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
+       }
+
+       /* Check if file exists */
+       dwAttributes = GetFileAttributesW(szFullName);
+       if (dwAttributes == INVALID_FILE_ATTRIBUTES)
+               return HRESULT_FROM_WIN32(GetLastError());
+
+       if (dwBufferLength < 2 || szFullName[1] != ':')
+       {
+               /* Not a local file */
+               CoTaskMemFree(szFullName);
+               return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
+       }
+
+       hFile = CreateFileW(szFullName, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
+       if (hFile == INVALID_HANDLE_VALUE)
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+
+       /* Increase INFO2 file size */
+       CloseHandle(s->hInfoMapped);
+       SetFilePointer(s->hInfo, sizeof(DELETED_FILE_RECORD), NULL, FILE_END);
+       SetEndOfFile(s->hInfo);
+       s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL);
+       if (!s->hInfoMapped)
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+
+       /* Open INFO2 file */
+       pHeader = MapViewOfFile(s->hInfoMapped, FILE_MAP_WRITE, 0, 0, 0);
+       if (!pHeader)
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+
+       /* Get number of entries */
+       FileSize.u.LowPart = GetFileSize(s->hInfo, &FileSize.u.HighPart);
+       if (FileSize.u.LowPart < sizeof(INFO2_HEADER))
+       {
+               UnmapViewOfFile(pHeader);
+               return HRESULT_FROM_WIN32(GetLastError());
+       }
+       dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD)) - 1;
+       pDeletedFile = ((PDELETED_FILE_RECORD)(pHeader + 1)) + dwEntries;
+
+       /* Get file size */
+#if 0
+       if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&FileSize))
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+#else
+       FileSize.u.LowPart = GetFileSize(hFile, &FileSize.u.HighPart);
+       if (FileSize.u.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+#endif
+       /* Check if file size is > 4Gb */
+       if (FileSize.u.HighPart != 0)
+       {
+               /* Yes, this recyclebin can't support this file */
+               hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
+               goto cleanup;
+       }
+       pHeader->dwTotalLogicalSize += FileSize.u.LowPart;
+
+       /* Generate new name */
+       Extension = wcsrchr(szFullName, '.');
+       ZeroMemory(pDeletedFile, sizeof(DELETED_FILE_RECORD));
+       if (dwEntries == 0)
+               pDeletedFile->dwRecordUniqueId = 0;
+       else
+       {
+               PDELETED_FILE_RECORD pLastDeleted = ((PDELETED_FILE_RECORD)(pHeader + 1)) + dwEntries - 1;
+               pDeletedFile->dwRecordUniqueId = pLastDeleted->dwRecordUniqueId + 1;
+       }
+       pDeletedFile->dwDriveNumber = tolower(szFullName[0]) - 'a';
+       _snwprintf(DeletedFileName, MAX_PATH, L"%s\\D%c%lu%s", s->Folder, pDeletedFile->dwDriveNumber + 'a', pDeletedFile->dwRecordUniqueId, Extension);
+
+       /* Get cluster size */
+       if (!GetDiskFreeSpaceW(s->VolumePath, &SectorsPerCluster, &BytesPerSector, NULL, NULL))
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+       ClusterSize = BytesPerSector * SectorsPerCluster;
+
+       /* Get current time */
+       GetSystemTime(&SystemTime);
+       if (!SystemTimeToFileTime(&SystemTime, &pDeletedFile->DeletionTime))
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+       pDeletedFile->dwPhysicalFileSize = ROUND_UP(FileSize.u.LowPart, ClusterSize);
+
+       /* Set name */
+       wcscpy(pDeletedFile->FileNameW, szFullName);
+       if (WideCharToMultiByte(CP_ACP, 0, pDeletedFile->FileNameW, -1, pDeletedFile->FileNameA, MAX_PATH, NULL, NULL) == 0)
+       {
+               hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
+               SetLastError(ERROR_INVALID_NAME);
+               goto cleanup;
+       }
+
+       /* Move file */
+       if (MoveFileW(szFullName, DeletedFileName))
+               hr = S_OK;
+       else
+               hr = HRESULT_FROM_WIN32(GetLastError());
+
+cleanup:
+       if (pHeader)
+               UnmapViewOfFile(pHeader);
+       if (hFile != INVALID_HANDLE_VALUE)
+               CloseHandle(hFile);
+       CoTaskMemFree(szFullName);
+       return hr;
+}
+
+static HRESULT STDMETHODCALLTYPE
+RecycleBin5_RecycleBin5_EmptyRecycleBin(
+       IN IRecycleBin5 *This)
+{
+       IRecycleBinEnumList *prbel;
+       IRecycleBinFile *prbf;
+       HRESULT hr;
+
+       TRACE("(%p)\n", This);
+
+       while (TRUE)
+       {
+               hr = IRecycleBin5_EnumObjects(This, &prbel);
+               if (!SUCCEEDED(hr))
+                       return hr;
+               hr = IRecycleBinEnumList_Next(prbel, 1, &prbf, NULL);
+               IRecycleBinEnumList_Release(prbel);
+               if (hr == S_FALSE)
+                       return S_OK;
+               hr = IRecycleBinFile_Delete(prbf);
+               IRecycleBinFile_Release(prbf);
+               if (!SUCCEEDED(hr))
+                       return hr;
+       }
+}
+
+static HRESULT STDMETHODCALLTYPE
+RecycleBin5_RecycleBin5_EnumObjects(
+       IN IRecycleBin5 *This,
+       OUT IRecycleBinEnumList **ppEnumList)
+{
+       struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
+       IRecycleBinEnumList *prbel;
+       HRESULT hr;
+       IUnknown *pUnk;
+
+       TRACE("(%p, %p)\n", This, ppEnumList);
+
+       hr = RecycleBin5Enum_Constructor(This, s->hInfo, s->hInfoMapped, s->Folder, &pUnk);
+       if (!SUCCEEDED(hr))
+               return hr;
+
+       hr = IUnknown_QueryInterface(pUnk, &IID_IRecycleBinEnumList, (void **)&prbel);
+       if (SUCCEEDED(hr))
+       {
+               s->EnumeratorCount++;
+               *ppEnumList = prbel;
+       }
+       IUnknown_Release(pUnk);
+       return hr;
+}
+
+static HRESULT STDMETHODCALLTYPE
+RecycleBin5_RecycleBin5_Delete(
+       IN IRecycleBin5 *This,
+       IN LPCWSTR pDeletedFileName,
+       IN DELETED_FILE_RECORD *pDeletedFile)
+{
+       struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
+       ULARGE_INTEGER FileSize;
+       PINFO2_HEADER pHeader;
+       DELETED_FILE_RECORD *pRecord, *pLast;
+       DWORD dwEntries, i;
+
+       TRACE("(%p, %s, %p)\n", This, debugstr_w(pDeletedFileName), pDeletedFile);
+
+       if (s->EnumeratorCount != 0)
+               return HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION);
+
+       pHeader = MapViewOfFile(s->hInfoMapped, FILE_MAP_WRITE, 0, 0, 0);
+       if (!pHeader)
+               return HRESULT_FROM_WIN32(GetLastError());
+
+       FileSize.u.LowPart = GetFileSize(s->hInfo, &FileSize.u.HighPart);
+       if (FileSize.u.LowPart == 0)
+       {
+               UnmapViewOfFile(pHeader);
+               return HRESULT_FROM_WIN32(GetLastError());
+       }
+       dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD));
+
+       pRecord = (DELETED_FILE_RECORD *)(pHeader + 1);
+       for (i = 0; i < dwEntries; i++)
+       {
+               if (pRecord->dwRecordUniqueId == pDeletedFile->dwRecordUniqueId)
+               {
+                       /* Delete file */
+                       if (!IntDeleteRecursive(pDeletedFileName))
+                       {
+                               UnmapViewOfFile(pHeader);
+                               return HRESULT_FROM_WIN32(GetLastError());
+                       }
+
+                       /* Clear last entry in the file */
+                       MoveMemory(pRecord, pRecord + 1, (dwEntries - i - 1) * sizeof(DELETED_FILE_RECORD));
+                       pLast = pRecord + (dwEntries - i - 1);
+                       ZeroMemory(pLast, sizeof(DELETED_FILE_RECORD));
+                       UnmapViewOfFile(pHeader);
+
+                       /* Resize file */
+                       CloseHandle(s->hInfoMapped);
+                       SetFilePointer(s->hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END);
+                       SetEndOfFile(s->hInfo);
+                       s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL);
+                       if (!s->hInfoMapped)
+                               return HRESULT_FROM_WIN32(GetLastError());
+                       return S_OK;
+               }
+               pRecord++;
+       }
+       UnmapViewOfFile(pHeader);
+       return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+}
+
+static HRESULT STDMETHODCALLTYPE
+RecycleBin5_RecycleBin5_Restore(
+       IN IRecycleBin5 *This,
+       IN LPCWSTR pDeletedFileName,
+       IN DELETED_FILE_RECORD *pDeletedFile)
+{
+       struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
+       ULARGE_INTEGER FileSize;
+       PINFO2_HEADER pHeader;
+       DELETED_FILE_RECORD *pRecord, *pLast;
+       DWORD dwEntries, i;
+       SHFILEOPSTRUCTW op;
+
+       TRACE("(%p, %s, %p)\n", This, debugstr_w(pDeletedFileName), pDeletedFile);
+
+       if (s->EnumeratorCount != 0)
+               return HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION);
+
+       pHeader = MapViewOfFile(s->hInfoMapped, FILE_MAP_WRITE, 0, 0, 0);
+       if (!pHeader)
+               return HRESULT_FROM_WIN32(GetLastError());
+
+       FileSize.u.LowPart = GetFileSize(s->hInfo, &FileSize.u.HighPart);
+       if (FileSize.u.LowPart == 0)
+       {
+               UnmapViewOfFile(pHeader);
+               return HRESULT_FROM_WIN32(GetLastError());
+       }
+       dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD));
+
+       pRecord = (DELETED_FILE_RECORD *)(pHeader + 1);
+       for (i = 0; i < dwEntries; i++)
+       {
+               if (pRecord->dwRecordUniqueId == pDeletedFile->dwRecordUniqueId)
+               {
+                       /* Restore file */
+                       ZeroMemory(&op, sizeof(op));
+                       op.wFunc = FO_COPY;
+                       op.pFrom = pDeletedFileName;
+                       op.pTo = pDeletedFile->FileNameW;
+
+                       if (!SHFileOperationW(&op))
+                       {
+                               UnmapViewOfFile(pHeader);
+                               return HRESULT_FROM_WIN32(GetLastError());
+                       }
+
+                       /* Clear last entry in the file */
+                       MoveMemory(pRecord, pRecord + 1, (dwEntries - i - 1) * sizeof(DELETED_FILE_RECORD));
+                       pLast = pRecord + (dwEntries - i - 1);
+                       ZeroMemory(pLast, sizeof(DELETED_FILE_RECORD));
+                       UnmapViewOfFile(pHeader);
+
+                       /* Resize file */
+                       CloseHandle(s->hInfoMapped);
+                       SetFilePointer(s->hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END);
+                       SetEndOfFile(s->hInfo);
+                       s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL);
+                       if (!s->hInfoMapped)
+                               return HRESULT_FROM_WIN32(GetLastError());
+                       return S_OK;
+               }
+               pRecord++;
+       }
+
+       UnmapViewOfFile(pHeader);
+       return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+}
+
+static HRESULT STDMETHODCALLTYPE
+RecycleBin5_RecycleBin5_OnClosing(
+       IN IRecycleBin5 *This,
+       IN IRecycleBinEnumList *prbel)
+{
+       struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl);
+       TRACE("(%p, %p)\n", This, prbel);
+       s->EnumeratorCount--;
+       return S_OK;
+}
+
+CONST_VTBL struct IRecycleBin5Vtbl RecycleBin5Vtbl =
+{
+       RecycleBin5_RecycleBin5_QueryInterface,
+       RecycleBin5_RecycleBin5_AddRef,
+       RecycleBin5_RecycleBin5_Release,
+       RecycleBin5_RecycleBin5_DeleteFile,
+       RecycleBin5_RecycleBin5_EmptyRecycleBin,
+       RecycleBin5_RecycleBin5_EnumObjects,
+       RecycleBin5_RecycleBin5_Delete,
+       RecycleBin5_RecycleBin5_Restore,
+       RecycleBin5_RecycleBin5_OnClosing,
+};
+
+static HRESULT
+RecycleBin5_Create(
+       IN LPCWSTR Folder,
+       IN PSID OwnerSid OPTIONAL)
+{
+       LPWSTR BufferName = NULL;
+       LPWSTR Separator; /* Pointer into BufferName buffer */
+       LPWSTR FileName; /* Pointer into BufferName buffer */
+       LPCSTR DesktopIniContents = "[.ShellClassInfo]\r\nCLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n";
+       INFO2_HEADER Info2Contents[] = { { 5, 0, 0, 0x320, 0 } };
+       DWORD BytesToWrite, BytesWritten, Needed;
+       HANDLE hFile = INVALID_HANDLE_VALUE;
+       HRESULT hr;
+
+       Needed = (wcslen(Folder) + 1 + max(wcslen(RECYCLE_BIN_FILE_NAME), wcslen(L"desktop.ini")) + 1) * sizeof(WCHAR);
+       BufferName = HeapAlloc(GetProcessHeap(), 0, Needed);
+       if (!BufferName)
+       {
+               hr = ERROR_NOT_ENOUGH_MEMORY;
+               goto cleanup;
+       }
+
+       wcscpy(BufferName, Folder);
+       Separator = wcsstr(&BufferName[3], L"\\");
+       if (Separator)
+               *Separator = UNICODE_NULL;
+       if (!CreateDirectoryW(BufferName, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+       SetFileAttributesW(BufferName, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+       if (Separator)
+       {
+               *Separator = L'\\';
+               if (!CreateDirectoryW(BufferName, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
+               {
+                       hr = HRESULT_FROM_WIN32(GetLastError());
+                       goto cleanup;
+               }
+       }
+
+       if (OwnerSid)
+       {
+               //DWORD rc;
+
+               /* Add ACL to allow only user/SYSTEM to open it */
+               /* FIXME: rc = SetNamedSecurityInfo(
+                       BufferName,
+                       SE_FILE_OBJECT,
+                       ???,
+                       OwnerSid,
+                       NULL,
+                       ???,
+                       ???);
+               if (rc != ERROR_SUCCESS)
+               {
+                       hr = HRESULT_FROM_WIN32(rc);
+                       goto cleanup;
+               }
+               */
+       }
+
+       wcscat(BufferName, L"\\");
+       FileName = &BufferName[wcslen(BufferName)];
+
+       /* Create desktop.ini */
+       wcscpy(FileName, L"desktop.ini");
+       hFile = CreateFileW(BufferName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, NULL);
+       if (hFile == INVALID_HANDLE_VALUE)
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+       BytesToWrite = strlen(DesktopIniContents);
+       if (!WriteFile(hFile, DesktopIniContents, (DWORD)BytesToWrite, &BytesWritten, NULL))
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+       if (BytesWritten != BytesToWrite)
+       {
+               hr = E_FAIL;
+               goto cleanup;
+       }
+       CloseHandle(hFile);
+       hFile = INVALID_HANDLE_VALUE;
+
+       /* Create empty INFO2 file */
+       wcscpy(FileName, RECYCLE_BIN_FILE_NAME);
+       hFile = CreateFileW(BufferName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
+       if (hFile == INVALID_HANDLE_VALUE)
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+       BytesToWrite = sizeof(Info2Contents);
+       if (!WriteFile(hFile, Info2Contents, (DWORD)BytesToWrite, &BytesWritten, NULL))
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+       if (BytesWritten == BytesToWrite)
+               hr = S_OK;
+       else
+               hr = E_FAIL;
+
+cleanup:
+       HeapFree(GetProcessHeap(), 0, BufferName);
+       if (hFile != INVALID_HANDLE_VALUE)
+               CloseHandle(hFile);
+       return hr;
+}
+
+HRESULT RecycleBin5_Constructor(IN LPCWSTR VolumePath, OUT IUnknown **ppUnknown)
+{
+       struct RecycleBin5 *s = NULL;
+       DWORD FileSystemFlags;
+       LPCWSTR RecycleBinDirectory;
+       HANDLE tokenHandle = INVALID_HANDLE_VALUE;
+       PTOKEN_USER TokenUserInfo = NULL;
+       LPWSTR StringSid = NULL, p;
+       DWORD Needed, DirectoryLength;
+       INT len;
+       HRESULT hr;
+
+       if (!ppUnknown)
+               return E_POINTER;
+
+       /* Get information about file system */
+       if (!GetVolumeInformationW(
+               VolumePath,
+               NULL,
+               0,
+               NULL,
+               NULL,
+               &FileSystemFlags,
+               NULL,
+               0))
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+       if (!(FileSystemFlags & FILE_PERSISTENT_ACLS))
+               RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITHOUT_ACL;
+       else
+       {
+               RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITH_ACL;
+
+               /* Get user SID */
+               if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tokenHandle))
+               {
+                       hr = HRESULT_FROM_WIN32(GetLastError());
+                       goto cleanup;
+               }
+               if (GetTokenInformation(tokenHandle, TokenUser, NULL, 0, &Needed))
+               {
+                       hr = E_FAIL;
+                       goto cleanup;
+               }
+               if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+               {
+                       hr = HRESULT_FROM_WIN32(GetLastError());
+                       goto cleanup;
+               }
+               TokenUserInfo = HeapAlloc(GetProcessHeap(), 0, Needed);
+               if (!TokenUserInfo)
+               {
+                       hr = E_OUTOFMEMORY;
+                       goto cleanup;
+               }
+               if (!GetTokenInformation(tokenHandle, TokenUser, TokenUserInfo, (DWORD)Needed, &Needed))
+               {
+                       hr = HRESULT_FROM_WIN32(GetLastError());
+                       goto cleanup;
+               }
+               if (!ConvertSidToStringSidW(TokenUserInfo->User.Sid, &StringSid))
+               {
+                       hr = HRESULT_FROM_WIN32(GetLastError());
+                       goto cleanup;
+               }
+       }
+
+       DirectoryLength = wcslen(VolumePath) + wcslen(RecycleBinDirectory) + 1;
+       if (StringSid)
+               DirectoryLength += wcslen(StringSid) + 1;
+       DirectoryLength += 1 + wcslen(RECYCLE_BIN_FILE_NAME);
+       DirectoryLength += wcslen(VolumePath) + 1;
+       Needed = (DirectoryLength + 1) * sizeof(WCHAR);
+
+       s = CoTaskMemAlloc(sizeof(struct RecycleBin5) + Needed);
+       if (!s)
+       {
+               hr = E_OUTOFMEMORY;
+               goto cleanup;
+       }
+       ZeroMemory(s, sizeof(struct RecycleBin5));
+       s->recycleBinImpl.lpVtbl = &RecycleBin5Vtbl;
+       s->ref = 1;
+       if (StringSid)
+               len = swprintf(s->Folder, L"%s%s\\%s", VolumePath, RecycleBinDirectory, StringSid);
+       else
+               len = swprintf(s->Folder, L"%s%s", VolumePath, RecycleBinDirectory);
+       p = &s->Folder[len];
+       wcscpy(p, L"\\" RECYCLE_BIN_FILE_NAME);
+       s->hInfo = CreateFileW(s->Folder, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+       if (s->hInfo == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_FILE_NOT_FOUND))
+       {
+               *p = UNICODE_NULL;
+               hr = RecycleBin5_Create(s->Folder, TokenUserInfo ? TokenUserInfo->User.Sid : NULL);
+               *p = L'\\';
+               if (!SUCCEEDED(hr))
+                       goto cleanup;
+               s->hInfo = CreateFileW(s->Folder, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+       }
+       if (s->hInfo == INVALID_HANDLE_VALUE)
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+       s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL);
+       if (!s->hInfoMapped)
+       {
+               hr = HRESULT_FROM_WIN32(GetLastError());
+               goto cleanup;
+       }
+       *p = UNICODE_NULL;
+       s->VolumePath = p + 1;
+       wcscpy(s->VolumePath, VolumePath);
+
+       *ppUnknown = (IUnknown *)&s->recycleBinImpl;
+
+       hr = S_OK;
+
+cleanup:
+       if (tokenHandle != INVALID_HANDLE_VALUE)
+               CloseHandle(tokenHandle);
+       HeapFree(GetProcessHeap(), 0, TokenUserInfo);
+       if (StringSid)
+               LocalFree(StringSid);
+       if (!SUCCEEDED(hr))
+       {
+               if (s)
+                       RecycleBin5_Destructor(s);
+       }
+       return hr;
+}