-/*\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;
+}