-/*\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/openclose.c
+ * PURPOSE: Deals with recycle bins of Windows 2000/XP/2003
+ * PROGRAMMERS: Copyright 2006 Hervé Poussineau (hpoussin@reactos.org)
+ */
+
+#include "recyclebin_v5.h"
+
+VOID
+InitializeCallbacks5(
+ IN OUT PRECYCLEBIN_CALLBACKS Callbacks)
+{
+ Callbacks->CloseHandle = CloseHandle5;
+ Callbacks->DeleteFile = DeleteFile5;
+ Callbacks->EmptyRecycleBin = EmptyRecycleBin5;
+ Callbacks->EnumerateFiles = EnumerateFiles5;
+ Callbacks->GetDetails = GetDetails5;
+ Callbacks->RestoreFile = RestoreFile5;
+}
+
+static BOOL
+CloseHandle5(
+ IN HANDLE hDeletedFile)
+{
+ UNREFERENCED_PARAMETER(hDeletedFile);
+ /* Nothing to do, as hDeletedFile is simply a DWORD... */
+ return TRUE;
+}
+
+static BOOL
+DeleteFile5(
+ IN PRECYCLE_BIN bin,
+ IN LPCWSTR FullPath,
+ IN LPCWSTR FileName)
+{
+ HANDLE hFile = INVALID_HANDLE_VALUE;
+ INFO2_HEADER Header;
+ DELETED_FILE_RECORD DeletedFile;
+ DWORD bytesRead, bytesWritten;
+ ULARGE_INTEGER fileSize;
+ SYSTEMTIME SystemTime;
+ WCHAR RootDir[4];
+ WCHAR DeletedFileName[2 * MAX_PATH];
+ LPCWSTR Extension;
+ DWORD ClusterSize, BytesPerSector, SectorsPerCluster;
+ BOOL ret = FALSE;
+
+ if (wcslen(FullPath) >= MAX_PATH)
+ {
+ /* Unable to store a too long path in recycle bin */
+ SetLastError(ERROR_INVALID_NAME);
+ goto cleanup;
+ }
+
+ hFile = CreateFileW(FullPath, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ goto cleanup;
+
+ if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ goto cleanup;
+ if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL))
+ goto cleanup;
+ if (bytesRead != sizeof(INFO2_HEADER) || Header.dwRecordSize == 0)
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+
+ if (Header.dwVersion != 5 || Header.dwRecordSize != sizeof(DELETED_FILE_RECORD))
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+
+ /* Get file size */
+#if 0
+ if (!GetFileSizeEx(hFile, &fileSize))
+ goto cleanup;
+#else
+ fileSize.u.LowPart = GetFileSize(hFile, &fileSize.u.HighPart);
+ if (fileSize.u.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
+ goto cleanup;
+#endif
+ /* Check if file size is > 4Gb */
+ if (fileSize.u.HighPart != 0)
+ {
+ /* FIXME: how to delete files >= 4Gb? */
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ goto cleanup;
+ }
+ Header.dwTotalLogicalSize += fileSize.u.LowPart;
+
+ /* Generate new name */
+ Header.dwHighestRecordUniqueId++;
+ Extension = wcsrchr(FileName, '.');
+ wsprintfW(DeletedFileName, L"%s\\D%c%lu%s", bin->Folder, FullPath[0] - 'A' + 'a', Header.dwHighestRecordUniqueId, Extension);
+
+ /* Get cluster size */
+ wsprintfW(RootDir, L"%c:\\", bin->Folder[0]);
+ if (!GetDiskFreeSpaceW(RootDir, &SectorsPerCluster, &BytesPerSector, NULL, NULL))
+ goto cleanup;
+ ClusterSize = BytesPerSector * SectorsPerCluster;
+
+ /* Get current time */
+ GetSystemTime(&SystemTime);
+ if (!SystemTimeToFileTime(&SystemTime, &DeletedFile.DeletionTime))
+ goto cleanup;
+
+ /* Update INFO2 */
+ memset(&DeletedFile, 0, sizeof(DELETED_FILE_RECORD));
+ if (WideCharToMultiByte(CP_ACP, 0, FullPath, -1, DeletedFile.FileNameA, MAX_PATH, NULL, NULL) == 0)
+ {
+ SetLastError(ERROR_INVALID_NAME);
+ goto cleanup;
+ }
+ DeletedFile.dwRecordUniqueId = Header.dwHighestRecordUniqueId;
+ DeletedFile.dwDriveNumber = tolower(bin->Folder[0]) - 'a';
+ DeletedFile.dwPhysicalFileSize = ROUND_UP(fileSize.u.LowPart, ClusterSize);
+ wcscpy(DeletedFile.FileNameW, FullPath);
+
+ if (!SetFilePointer(bin->hInfo, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
+ goto cleanup;
+ if (!WriteFile(bin->hInfo, &DeletedFile, sizeof(DELETED_FILE_RECORD), &bytesWritten, NULL))
+ goto cleanup;
+ if (bytesWritten != sizeof(DELETED_FILE_RECORD))
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+ Header.dwNumberOfEntries++;
+ if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ {
+ goto cleanup;
+ }
+ if (!WriteFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesWritten, NULL))
+ goto cleanup;
+ if (bytesWritten != sizeof(INFO2_HEADER))
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+
+ /* Move file */
+ if (!MoveFileW(FullPath, DeletedFileName))
+ goto cleanup;
+
+ ret = TRUE;
+
+cleanup:
+ if (hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hFile);
+ return ret;
+}
+
+static BOOL
+EmptyRecycleBin5(
+ IN PRECYCLE_BIN* bin)
+{
+ LPWSTR InfoFile = NULL;
+ BOOL ret = FALSE;
+
+ InfoFile = HeapAlloc(GetProcessHeap(), 0, wcslen((*bin)->InfoFile) * sizeof(WCHAR) + sizeof(UNICODE_NULL));
+ if (!InfoFile)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto cleanup;
+ }
+ wcscpy(InfoFile, (*bin)->InfoFile);
+
+ /* Delete all files in the recycle bin */
+ if (!EnumerateFiles5(*bin, IntEmptyRecycleBinCallback, *bin))
+ goto cleanup;
+
+ /* Delete INFO2 */
+ if (!DereferenceHandle(&(*bin)->refCount))
+ goto cleanup;
+ if (!DeleteFileW(InfoFile))
+ goto cleanup;
+ *bin = NULL;
+ ret = TRUE;
+
+cleanup:
+ HeapFree(GetProcessHeap(), 0, InfoFile);
+ return ret;
+}
+
+static BOOL
+EnumerateFiles5(
+ IN PRECYCLE_BIN bin,
+ IN PINT_ENUMERATE_RECYCLEBIN_CALLBACK pFnCallback,
+ IN PVOID Context OPTIONAL)
+{
+ INFO2_HEADER Header;
+ DELETED_FILE_RECORD DeletedFile;
+ DWORD bytesRead, dwEntries;
+ BOOL ret = FALSE;
+
+ if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ goto cleanup;
+ if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL))
+ goto cleanup;
+ if (bytesRead != sizeof(INFO2_HEADER) || Header.dwRecordSize == 0)
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+
+ if (Header.dwVersion != 5 || Header.dwRecordSize != sizeof(DELETED_FILE_RECORD))
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+
+ SetLastError(ERROR_SUCCESS);
+ for (dwEntries = 0; dwEntries < Header.dwNumberOfEntries; dwEntries++)
+ {
+ if (!ReadFile(bin->hInfo, &DeletedFile, Header.dwRecordSize, &bytesRead, NULL))
+ goto cleanup;
+ if (bytesRead != Header.dwRecordSize)
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+ if (!pFnCallback(Context, (HANDLE)(ULONG_PTR)DeletedFile.dwRecordUniqueId))
+ goto cleanup;
+ if (SetFilePointer(bin->hInfo, sizeof(INFO2_HEADER) + Header.dwRecordSize * dwEntries, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ goto cleanup;
+ }
+
+ ret = TRUE;
+
+cleanup:
+ return ret;
+}
+
+static BOOL
+GetDetails5(
+ IN PRECYCLE_BIN bin,
+ IN HANDLE hDeletedFile,
+ IN DWORD BufferSize,
+ IN OUT PDELETED_FILE_DETAILS_W FileDetails OPTIONAL,
+ OUT LPDWORD RequiredSize OPTIONAL)
+{
+ DELETED_FILE_RECORD DeletedFile;
+ SIZE_T Needed;
+ LPWSTR FullName = NULL;
+ HANDLE hFile = INVALID_HANDLE_VALUE;
+ BOOL ret = FALSE;
+
+ /* Check parameters */
+ if (BufferSize > 0 && FileDetails == NULL)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ goto cleanup;
+ }
+
+ if (!IntSearchRecord(bin, hDeletedFile, &DeletedFile, NULL))
+ goto cleanup;
+ Needed = FIELD_OFFSET(DELETED_FILE_DETAILS_W, FileName) + (wcslen(DeletedFile.FileNameW) + 1) * sizeof(WCHAR);
+ if (RequiredSize)
+ *RequiredSize = (DWORD)Needed;
+ if (Needed > BufferSize)
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ goto cleanup;
+ }
+
+ if (!IntGetFullName(bin, &DeletedFile, &FullName))
+ goto cleanup;
+
+ /* Open file */
+ FileDetails->Attributes = GetFileAttributesW(FullName);
+ if (FileDetails->Attributes == INVALID_FILE_ATTRIBUTES)
+ goto cleanup;
+ if (FileDetails->Attributes & FILE_ATTRIBUTE_DIRECTORY)
+ hFile = CreateFileW(FullName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ else
+ hFile = CreateFileW(FullName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ goto cleanup;
+
+ /* Fill returned structure */
+ if (!GetFileTime(hFile, NULL, NULL, &FileDetails->LastModification))
+ goto cleanup;
+ memcpy(&FileDetails->DeletionTime, &DeletedFile.DeletionTime, sizeof(FILETIME));
+ FileDetails->FileSize.u.LowPart = GetFileSize(hFile, &FileDetails->FileSize.u.HighPart);
+ if (FileDetails->FileSize.u.LowPart == INVALID_FILE_SIZE)
+ goto cleanup;
+ FileDetails->PhysicalFileSize.u.HighPart = 0;
+ FileDetails->PhysicalFileSize.u.LowPart = DeletedFile.dwPhysicalFileSize;
+ wcscpy(FileDetails->FileName, DeletedFile.FileNameW);
+
+ ret = TRUE;
+
+cleanup:
+ HeapFree(GetProcessHeap(), 0, FullName);
+ if (hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hFile);
+ return ret;
+}
+
+static BOOL
+RestoreFile5(
+ IN PRECYCLE_BIN bin,
+ IN HANDLE hDeletedFile)
+{
+ INFO2_HEADER Header;
+ DWORD bytesRead, bytesWritten;
+ LARGE_INTEGER Position;
+ DELETED_FILE_RECORD DeletedFile, LastFile;
+ LPWSTR FullName = NULL;
+ BOOL ret = FALSE;
+
+ if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ goto cleanup;
+ if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL))
+ goto cleanup;
+ if (bytesRead != sizeof(INFO2_HEADER) || Header.dwRecordSize == 0)
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+
+ if (Header.dwVersion != 5 || Header.dwRecordSize != sizeof(DELETED_FILE_RECORD))
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+
+ /* Search deleted entry */
+ if (!IntSearchRecord(bin, hDeletedFile, &DeletedFile, &Position))
+ goto cleanup;
+ /* Get destination full name */
+ if (!IntGetFullName(bin, &DeletedFile, &FullName))
+ goto cleanup;
+ /* Restore file */
+ if (!MoveFileW(FullName, DeletedFile.FileNameW))
+ goto cleanup;
+
+ /* Update INFO2 */
+ /* 1) If not last entry, copy last entry to the current one */
+ if (SetFilePointer(bin->hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END) == INVALID_SET_FILE_POINTER)
+ goto cleanup;
+ if (!ReadFile(bin->hInfo, &LastFile, sizeof(DELETED_FILE_RECORD), &bytesRead, NULL))
+ goto cleanup;
+ if (bytesRead != sizeof(DELETED_FILE_RECORD))
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+ if (LastFile.dwRecordUniqueId != DeletedFile.dwRecordUniqueId)
+ {
+ /* Move the last entry to the current one */
+ if (SetFilePointer(bin->hInfo, Position.u.LowPart, &Position.u.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ goto cleanup;
+ if (!WriteFile(bin->hInfo, &LastFile, sizeof(DELETED_FILE_RECORD), &bytesWritten, NULL))
+ goto cleanup;
+ if (bytesWritten != sizeof(DELETED_FILE_RECORD))
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+ }
+ /* 2) Update the header */
+ Header.dwNumberOfEntries--;
+ if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ goto cleanup;
+ if (!WriteFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesWritten, NULL))
+ goto cleanup;
+ if (bytesWritten != sizeof(INFO2_HEADER))
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+ /* 3) Truncate file */
+ if (SetFilePointer(bin->hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END) == INVALID_SET_FILE_POINTER)
+ goto cleanup;
+ if (!SetEndOfFile(bin->hInfo))
+ goto cleanup;
+ ret = TRUE;
+
+cleanup:
+ HeapFree(GetProcessHeap(), 0, FullName);
+ return ret;
+}
+
+static BOOL
+IntDeleteRecursive(
+ IN LPCWSTR FullName)
+{
+ DWORD RemovableAttributes = FILE_ATTRIBUTE_READONLY;
+ DWORD FileAttributes;
+ 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)
+ {
+ /* Recursive deletion */
+ /* FIXME: recursive deletion */
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ goto cleanup;
+
+ if (!RemoveDirectoryW(FullName))
+ goto cleanup;
+ }
+ else
+ {
+ if (!DeleteFileW(FullName))
+ goto cleanup;
+ }
+ ret = TRUE;
+
+cleanup:
+ return ret;
+}
+
+static BOOL
+IntEmptyRecycleBinCallback(
+ IN PVOID Context,
+ IN HANDLE hDeletedFile)
+{
+ PRECYCLE_BIN bin = (PRECYCLE_BIN)Context;
+ DELETED_FILE_RECORD DeletedFile;
+ LPWSTR FullName = NULL;
+ BOOL ret = FALSE;
+
+ if (!IntSearchRecord(bin, hDeletedFile, &DeletedFile, NULL))
+ goto cleanup;
+
+ if (!IntGetFullName(bin, &DeletedFile, &FullName))
+ goto cleanup;
+
+ if (!IntDeleteRecursive(FullName))
+ goto cleanup;
+ ret = TRUE;
+
+cleanup:
+ HeapFree(GetProcessHeap(), 0, FullName);
+ return ret;
+}
+
+static BOOL
+IntGetFullName(
+ IN PRECYCLE_BIN bin,
+ IN PDELETED_FILE_RECORD pDeletedFile,
+ OUT LPWSTR* pFullName)
+{
+ SIZE_T Needed;
+ LPCWSTR Extension;
+ LPWSTR FullName = NULL;
+ BOOL ret = FALSE;
+
+ *pFullName = NULL;
+ Extension = wcsrchr(pDeletedFile->FileNameW, '.');
+ if (Extension < wcsrchr(pDeletedFile->FileNameW, '\\'))
+ Extension = NULL;
+ Needed = wcslen(bin->Folder) + 13;
+ if (Extension)
+ Needed += wcslen(Extension);
+ FullName = HeapAlloc(GetProcessHeap(), 0, Needed * sizeof(WCHAR));
+ if (!FullName)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto cleanup;
+ }
+ wsprintfW(FullName, L"%s\\D%c%lu%s", bin->Folder, pDeletedFile->dwDriveNumber + 'a', pDeletedFile->dwRecordUniqueId, Extension);
+ *pFullName = FullName;
+ ret = TRUE;
+
+cleanup:
+ if (!ret)
+ HeapFree(GetProcessHeap(), 0, FullName);
+ return ret;
+}
+
+static BOOL
+IntSearchRecord(
+ IN PRECYCLE_BIN bin,
+ IN HANDLE hDeletedFile,
+ OUT PDELETED_FILE_RECORD pDeletedFile,
+ OUT PLARGE_INTEGER Position OPTIONAL)
+{
+ INFO2_HEADER Header;
+ DELETED_FILE_RECORD DeletedFile;
+ DWORD bytesRead, dwEntries;
+ BOOL ret = FALSE;
+
+ if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
+ goto cleanup;
+ if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL))
+ goto cleanup;
+ if (bytesRead != sizeof(INFO2_HEADER) || Header.dwRecordSize == 0)
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+
+ if (Header.dwVersion != 5 || Header.dwRecordSize != sizeof(DELETED_FILE_RECORD))
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+
+ SetLastError(ERROR_SUCCESS);
+ for (dwEntries = 0; dwEntries < Header.dwNumberOfEntries; dwEntries++)
+ {
+ if (Position)
+ {
+ LARGE_INTEGER Zero;
+ Zero.QuadPart = 0;
+ if (!SetFilePointerEx(bin->hInfo, Zero, Position, FILE_CURRENT))
+ goto cleanup;
+ }
+ if (!ReadFile(bin->hInfo, &DeletedFile, Header.dwRecordSize, &bytesRead, NULL))
+ goto cleanup;
+ if (bytesRead != Header.dwRecordSize)
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+ if (DeletedFile.dwRecordUniqueId == (DWORD)(ULONG_PTR)hDeletedFile)
+ {
+ memcpy(pDeletedFile, &DeletedFile, Header.dwRecordSize);
+ ret = TRUE;
+ goto cleanup;
+ }
+ }
+
+ /* Entry not found */
+ SetLastError(ERROR_INVALID_HANDLE);
+
+cleanup:
+ return ret;
+}