It is aimed to be compatible with Windows 2000/XP/2003 (at least) on FAT or NTFS volumes.
svn path=/trunk/; revision=21553
<directory name="3rdparty">
<xi:include href="3rdparty/3rdparty.rbuild" />
</directory>
-<directory name="drivers">
- <xi:include href="drivers/directory.rbuild" />
-</directory>
-
<directory name="crt">
<xi:include href="crt/crt.rbuild" />
</directory>
+<directory name="drivers">
+ <xi:include href="drivers/directory.rbuild" />
+</directory>
<directory name="dxguid">
<xi:include href="dxguid/dxguid.rbuild" />
</directory>
<directory name="pseh">
<xi:include href="pseh/pseh.rbuild" />
</directory>
+<directory name="recyclebin">
+ <xi:include href="recyclebin/recyclebin.rbuild" />
+</directory>
<directory name="rossym">
<xi:include href="rossym/rossym.rbuild" />
</directory>
--- /dev/null
+/*\r
+ * PROJECT: Recycle bin management\r
+ * LICENSE: GPL v2 - See COPYING in the top level directory\r
+ * FILE: lib/recyclebin/openclose.c\r
+ * PURPOSE: Open/close recycle bins\r
+ * PROGRAMMERS: Copyright 2006 Hervé Poussineau (hpoussin@reactos.org)\r
+ */\r
+\r
+#include "recyclebin_private.h"\r
+\r
+BOOL\r
+IntCheckDeletedFileHandle(\r
+ IN HANDLE hDeletedFile)\r
+{\r
+ if (hDeletedFile == NULL || hDeletedFile == INVALID_HANDLE_VALUE)\r
+ return FALSE;\r
+\r
+ if (((PDELETED_FILE_HANDLE)hDeletedFile)->magic != DELETEDFILE_MAGIC)\r
+ return FALSE;\r
+\r
+ return TRUE;\r
+}\r
+\r
+static BOOL\r
+IntCloseRecycleBinHandle(\r
+ IN PREFCOUNT_DATA pData)\r
+{\r
+ PRECYCLE_BIN bin;\r
+\r
+ bin = CONTAINING_RECORD(pData, RECYCLE_BIN, refCount);\r
+ if (!CloseHandle(bin->hInfo))\r
+ return FALSE;\r
+\r
+ RemoveEntryList(&bin->ListEntry);\r
+ HeapFree(GetProcessHeap(), 0, bin);\r
+ return TRUE;\r
+}\r
+\r
+static BOOL\r
+IntCreateEmptyRecycleBin(\r
+ IN PRECYCLE_BIN bin,\r
+ IN PSID OwnerSid OPTIONAL)\r
+{\r
+ LPWSTR BufferName = NULL;\r
+ LPWSTR Separator; /* Pointer into BufferName buffer */\r
+ LPWSTR FileName; /* Pointer into BufferName buffer */\r
+ LPCSTR DesktopIniContents = "[.ShellClassInfo]\r\nCLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n";\r
+ DWORD Info2Contents[] = { 5, 0, 0, 0x320, 0 };\r
+ SIZE_T BytesToWrite, BytesWritten, Needed;\r
+ HANDLE hFile = INVALID_HANDLE_VALUE;\r
+ BOOL ret = FALSE;\r
+\r
+ Needed = (wcslen(bin->Folder) + 1 + max(wcslen(RECYCLE_BIN_FILE_NAME), wcslen(L"desktop.ini")) + 1) * sizeof(WCHAR);\r
+ BufferName = HeapAlloc(GetProcessHeap(), 0, Needed);\r
+ if (!BufferName)\r
+ {\r
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);\r
+ goto cleanup;\r
+ }\r
+\r
+ wcscpy(BufferName, bin->Folder);\r
+ Separator = wcsstr(&BufferName[3], L"\\");\r
+ if (Separator)\r
+ *Separator = UNICODE_NULL;\r
+ ret = CreateDirectoryW(BufferName, NULL);\r
+ if (!ret && GetLastError() != ERROR_ALREADY_EXISTS)\r
+ goto cleanup;\r
+ SetFileAttributesW(BufferName, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);\r
+ if (Separator)\r
+ {\r
+ *Separator = L'\\';\r
+ ret = CreateDirectoryW(BufferName, NULL);\r
+ if (!ret && GetLastError() != ERROR_ALREADY_EXISTS)\r
+ goto cleanup;\r
+ }\r
+\r
+ if (OwnerSid)\r
+ {\r
+ //DWORD rc;\r
+ \r
+ /* Add ACL to allow only user/SYSTEM to open it */\r
+ /* FIXME: rc = SetNamedSecurityInfo(\r
+ BufferName,\r
+ SE_FILE_OBJECT,\r
+ ???,\r
+ OwnerSid,\r
+ NULL,\r
+ ???,\r
+ ???);\r
+ if (rc != ERROR_SUCCESS)\r
+ {\r
+ SetLastError(rc);\r
+ goto cleanup;\r
+ }\r
+ */\r
+ }\r
+\r
+ wcscat(BufferName, L"\\");\r
+ FileName = &BufferName[wcslen(BufferName)];\r
+\r
+ /* Create desktop.ini */\r
+ wcscpy(FileName, L"desktop.ini");\r
+ hFile = CreateFileW(BufferName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, NULL);\r
+ if (hFile == INVALID_HANDLE_VALUE)\r
+ goto cleanup;\r
+ BytesToWrite = strlen(DesktopIniContents);\r
+ ret = WriteFile(hFile, DesktopIniContents, (DWORD)BytesToWrite, &BytesWritten, NULL);\r
+ if (!ret)\r
+ goto cleanup;\r
+ if (BytesWritten != BytesToWrite)\r
+ {\r
+ SetLastError(ERROR_GEN_FAILURE);\r
+ goto cleanup;\r
+ }\r
+ CloseHandle(hFile);\r
+ hFile = INVALID_HANDLE_VALUE;\r
+ \r
+ /* Create empty INFO2 file */\r
+ wcscpy(FileName, RECYCLE_BIN_FILE_NAME);\r
+ hFile = CreateFileW(BufferName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);\r
+ if (hFile == INVALID_HANDLE_VALUE)\r
+ goto cleanup;\r
+ BytesToWrite = sizeof(Info2Contents);\r
+ ret = WriteFile(hFile, Info2Contents, (DWORD)BytesToWrite, &BytesWritten, NULL);\r
+ if (!ret)\r
+ goto cleanup;\r
+ if (BytesWritten != BytesToWrite)\r
+ {\r
+ SetLastError(ERROR_GEN_FAILURE);\r
+ goto cleanup;\r
+ }\r
+\r
+ bin->hInfo = hFile;\r
+ ret = TRUE;\r
+\r
+cleanup:\r
+ HeapFree(GetProcessHeap(), 0, BufferName);\r
+ if (!ret)\r
+ {\r
+ if (hFile != INVALID_HANDLE_VALUE)\r
+ CloseHandle(hFile);\r
+ }\r
+ return ret; \r
+}\r
+\r
+PRECYCLE_BIN\r
+IntReferenceRecycleBin(\r
+ IN WCHAR driveLetter)\r
+{\r
+ PLIST_ENTRY ListEntry;\r
+ PRECYCLE_BIN bin = NULL, ret = NULL;\r
+ WCHAR RootPath[4];\r
+ DWORD FileSystemFlags;\r
+ LPCWSTR RecycleBinDirectory;\r
+ HANDLE tokenHandle = INVALID_HANDLE_VALUE;\r
+ PTOKEN_USER TokenUserInfo = NULL;\r
+ LPWSTR StringSid = NULL;\r
+ SIZE_T Needed, DirectoryLength;\r
+ INFO2_HEADER Header;\r
+ BOOL AlreadyCreated = FALSE;\r
+ DWORD bytesRead;\r
+\r
+ static LIST_ENTRY ListHead;\r
+ static BOOL ListInitialized = FALSE;\r
+\r
+ if (!ListInitialized)\r
+ {\r
+ InitializeListHead(&ListHead);\r
+ ListInitialized = TRUE;\r
+ }\r
+\r
+ /* Search if the recycle bin has already been opened */\r
+ driveLetter = toupper(driveLetter);\r
+ ListEntry = ListHead.Flink;\r
+ while (ListEntry != &ListHead)\r
+ {\r
+ bin = CONTAINING_RECORD(ListEntry, RECYCLE_BIN, ListEntry);\r
+ if (bin->Folder[0] == driveLetter)\r
+ {\r
+ ReferenceHandle(&bin->refCount);\r
+ return bin;\r
+ }\r
+ ListEntry = ListEntry->Flink;\r
+ }\r
+ bin = NULL;\r
+\r
+ /* We need to create a new recycle bin */\r
+\r
+ /* Get information about file system */\r
+ wsprintfW(RootPath, L"%c:\\", driveLetter);\r
+ if (!GetVolumeInformationW(\r
+ RootPath,\r
+ NULL,\r
+ 0,\r
+ NULL,\r
+ NULL,\r
+ &FileSystemFlags,\r
+ NULL,\r
+ 0))\r
+ {\r
+ goto cleanup;\r
+ }\r
+ if (!(FileSystemFlags & FILE_PERSISTENT_ACLS))\r
+ RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITHOUT_ACL;\r
+ else\r
+ {\r
+ RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITH_ACL;\r
+\r
+ /* Get user SID */\r
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tokenHandle))\r
+ goto cleanup;\r
+ if (GetTokenInformation(tokenHandle, TokenUser, NULL, 0, &Needed))\r
+ {\r
+ SetLastError(ERROR_GEN_FAILURE);\r
+ goto cleanup;\r
+ }\r
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)\r
+ goto cleanup;\r
+ TokenUserInfo = HeapAlloc(GetProcessHeap(), 0, Needed);\r
+ if (!TokenUserInfo)\r
+ {\r
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);\r
+ goto cleanup;\r
+ }\r
+ if (!GetTokenInformation(tokenHandle, TokenUser, TokenUserInfo, (DWORD)Needed, &Needed))\r
+ goto cleanup;\r
+ if (!ConvertSidToStringSidW(TokenUserInfo->User.Sid, &StringSid))\r
+ goto cleanup;\r
+ }\r
+\r
+ /* Create RECYCLEBIN structure */\r
+openfile:\r
+ DirectoryLength = 3 + wcslen(RecycleBinDirectory) + 1;\r
+ if (StringSid)\r
+ DirectoryLength += wcslen(StringSid) + 1;\r
+ Needed = FIELD_OFFSET(RECYCLE_BIN, Folder) + (2 * DirectoryLength + 2 + wcslen(RECYCLE_BIN_FILE_NAME))* sizeof(WCHAR);\r
+ bin = HeapAlloc(GetProcessHeap(), 0, Needed);\r
+ if (!bin)\r
+ {\r
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);\r
+ goto cleanup;\r
+ }\r
+ memset(bin, 0, Needed);\r
+ InitializeHandle(&bin->refCount, IntCloseRecycleBinHandle);\r
+ bin->magic = RECYCLEBIN_MAGIC;\r
+ bin->hInfo = INVALID_HANDLE_VALUE;\r
+ bin->InfoFile = &bin->Folder[DirectoryLength];\r
+ bin->Folder[0] = driveLetter;\r
+ bin->Folder[1] = '\0';\r
+ wcscat(bin->Folder, L":\\");\r
+ wcscat(bin->Folder, RecycleBinDirectory);\r
+ if (StringSid)\r
+ {\r
+ wcscat(bin->Folder, L"\\");\r
+ wcscat(bin->Folder, StringSid);\r
+ }\r
+ wcscpy(bin->InfoFile, bin->Folder);\r
+ wcscat(bin->InfoFile, L"\\");\r
+ wcscat(bin->InfoFile, RECYCLE_BIN_FILE_NAME);\r
+ InsertTailList(&ListHead, &bin->ListEntry);\r
+\r
+ /* Open info file */\r
+ bin->hInfo = CreateFileW(bin->InfoFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);\r
+ if (bin->hInfo == INVALID_HANDLE_VALUE)\r
+ {\r
+ if (GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_FILE_NOT_FOUND)\r
+ {\r
+ if (!IntCreateEmptyRecycleBin(bin, TokenUserInfo ? TokenUserInfo->User.Sid : NULL))\r
+ goto cleanup;\r
+ AlreadyCreated = TRUE;\r
+ }\r
+ }\r
+ if (bin->hInfo == 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) && !AlreadyCreated)\r
+ {\r
+ /* Create a new file */\r
+ if (!DereferenceHandle(&bin->refCount))\r
+ goto cleanup;\r
+ if (!DeleteFileW(bin->InfoFile))\r
+ goto cleanup;\r
+ goto openfile;\r
+ }\r
+ switch (Header.dwVersion)\r
+ {\r
+ case 5:\r
+ InitializeCallbacks5(&bin->Callbacks);\r
+ break;\r
+ default:\r
+ /* Unknown recycle bin version */\r
+ SetLastError(ERROR_NOT_SUPPORTED);\r
+ goto cleanup;\r
+ }\r
+\r
+ ret = bin;\r
+\r
+cleanup:\r
+ if (tokenHandle != INVALID_HANDLE_VALUE)\r
+ CloseHandle(tokenHandle);\r
+ HeapFree(GetProcessHeap(), 0, TokenUserInfo);\r
+ if (StringSid)\r
+ LocalFree(StringSid);\r
+ if (!ret)\r
+ {\r
+ if (bin && bin->hInfo != INVALID_HANDLE_VALUE)\r
+ DereferenceHandle(&bin->refCount);\r
+ HeapFree(GetProcessHeap(), 0, bin);\r
+ }\r
+ return ret;\r
+}\r
--- /dev/null
+This library deals with Recycle bin.\r
+It is aimed to be compatible with Windows 2000/XP/2003 (at least) on FAT or NTFS volumes.\r
+\r
+\r
+TODO\r
+- Empty a recycle bin containing directories (v5)\r
+- Set security on recycle bin folder\r
+- Delete files > 4Gb\r
+\r
+3 levels\r
+- 1: recyclebin.c: Public interface\r
+- 2a: openclose.c: Open/close recycle bins\r
+- 2b: refcount.c: Do reference counting on objects\r
+- 3: recyclebin_v5.c: Deals with recycle bins of Windows 2000/XP/2003
\ No newline at end of file
--- /dev/null
+/*\r
+ * PROJECT: Recycle bin management\r
+ * LICENSE: GPL v2 - See COPYING in the top level directory\r
+ * FILE: lib/recyclebin/openclose.c\r
+ * PURPOSE: Public interface\r
+ * PROGRAMMERS: Copyright 2006 Hervé Poussineau (hpoussin@reactos.org)\r
+ */\r
+\r
+#include "recyclebin_private.h"\r
+\r
+typedef struct _ENUMERATE_RECYCLE_BIN_CONTEXT\r
+{\r
+ PRECYCLE_BIN bin;\r
+ PENUMERATE_RECYCLEBIN_CALLBACK pFnCallback;\r
+ PVOID Context;\r
+} ENUMERATE_RECYCLE_BIN_CONTEXT, *PENUMERATE_RECYCLE_BIN_CONTEXT;\r
+\r
+BOOL WINAPI\r
+CloseRecycleBinHandle(\r
+ IN HANDLE hDeletedFile)\r
+{\r
+ BOOL ret = FALSE;\r
+ \r
+ if (!IntCheckDeletedFileHandle(hDeletedFile))\r
+ SetLastError(ERROR_INVALID_HANDLE);\r
+ else\r
+ {\r
+ PDELETED_FILE_HANDLE file = (PDELETED_FILE_HANDLE)hDeletedFile;\r
+ ret = DereferenceHandle(&file->refCount);\r
+ }\r
+ \r
+ return ret;\r
+}\r
+\r
+BOOL WINAPI\r
+DeleteFileToRecycleBinA(\r
+ IN LPCSTR FileName)\r
+{\r
+ int len;\r
+ LPWSTR FileNameW = NULL;\r
+ BOOL ret = FALSE;\r
+ \r
+ len = MultiByteToWideChar(CP_ACP, 0, FileName, -1, NULL, 0);\r
+ if (len == 0)\r
+ goto cleanup;\r
+ FileNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));\r
+ if (!FileNameW)\r
+ {\r
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);\r
+ goto cleanup;\r
+ }\r
+ if (MultiByteToWideChar(CP_ACP, 0, FileName, -1, FileNameW, len) == 0)\r
+ goto cleanup;\r
+\r
+ ret = DeleteFileToRecycleBinW(FileNameW);\r
+\r
+cleanup:\r
+ HeapFree(GetProcessHeap(), 0, FileNameW);\r
+ return ret;\r
+}\r
+\r
+BOOL WINAPI\r
+DeleteFileToRecycleBinW(\r
+ IN LPCWSTR FileName)\r
+{\r
+ LPWSTR FullFileName = NULL;\r
+ DWORD dwBufferLength = 0;\r
+ LPWSTR lpFilePart;\r
+ DWORD len;\r
+ PRECYCLE_BIN bin = NULL;\r
+ BOOL ret = FALSE;\r
+\r
+ /* Get full file name */\r
+ while (TRUE)\r
+ {\r
+ len = GetFullPathNameW(FileName, dwBufferLength, FullFileName, &lpFilePart);\r
+ if (len == 0)\r
+ goto cleanup;\r
+ else if (len < dwBufferLength)\r
+ break;\r
+ HeapFree(GetProcessHeap(), 0, FullFileName);\r
+ dwBufferLength = len;\r
+ FullFileName = HeapAlloc(GetProcessHeap(), 0, dwBufferLength * sizeof(WCHAR));\r
+ if (!FullFileName)\r
+ {\r
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);\r
+ goto cleanup;\r
+ }\r
+ }\r
+\r
+ if (!lpFilePart || dwBufferLength < 2 || FullFileName[1] != ':')\r
+ {\r
+ /* Only a directory name, or not a local file */\r
+ SetLastError(ERROR_INVALID_NAME);\r
+ }\r
+\r
+ /* Open recycle bin */\r
+ bin = IntReferenceRecycleBin(FullFileName[0]);\r
+ if (!bin)\r
+ goto cleanup;\r
+\r
+ if (bin->Callbacks.DeleteFile)\r
+ ret = bin->Callbacks.DeleteFile(bin, FullFileName, lpFilePart);\r
+ else\r
+ SetLastError(ERROR_NOT_SUPPORTED);\r
+\r
+cleanup:\r
+ HeapFree(GetProcessHeap(), 0, FullFileName);\r
+ if (bin)\r
+ DereferenceHandle(&bin->refCount);\r
+ return ret;\r
+}\r
+\r
+BOOL WINAPI\r
+EmptyRecycleBinA(\r
+ IN CHAR driveLetter)\r
+{\r
+ return EmptyRecycleBinW((WCHAR)driveLetter);\r
+}\r
+\r
+BOOL WINAPI\r
+EmptyRecycleBinW(\r
+ IN WCHAR driveLetter)\r
+{\r
+ PRECYCLE_BIN bin = NULL;\r
+ BOOL ret = FALSE;\r
+\r
+ /* Open recycle bin */\r
+ bin = IntReferenceRecycleBin(driveLetter);\r
+ if (!bin)\r
+ goto cleanup;\r
+\r
+ if (bin->Callbacks.EmptyRecycleBin)\r
+ ret = bin->Callbacks.EmptyRecycleBin(&bin);\r
+ else\r
+ SetLastError(ERROR_NOT_SUPPORTED);\r
+\r
+cleanup:\r
+ if (bin)\r
+ DereferenceHandle(&bin->refCount);\r
+ return ret;\r
+}\r
+\r
+BOOL WINAPI\r
+EnumerateRecycleBinA(\r
+ IN CHAR driveLetter,\r
+ IN PENUMERATE_RECYCLEBIN_CALLBACK pFnCallback,\r
+ IN PVOID Context)\r
+{\r
+ return EnumerateRecycleBinW((WCHAR)driveLetter, pFnCallback, Context);\r
+}\r
+\r
+static BOOL\r
+IntCloseDeletedFileHandle(\r
+ IN PREFCOUNT_DATA pData)\r
+{\r
+ PDELETED_FILE_HANDLE file;\r
+\r
+ file = CONTAINING_RECORD(pData, DELETED_FILE_HANDLE, refCount);\r
+ if (!DereferenceHandle(&file->bin->refCount))\r
+ return FALSE;\r
+\r
+ file->magic = 0;\r
+ HeapFree(GetProcessHeap(), 0, file);\r
+ return TRUE;\r
+}\r
+\r
+static BOOL\r
+IntEnumerateRecycleBinCallback(\r
+ IN PVOID Context,\r
+ IN HANDLE hDeletedFile)\r
+{\r
+ PENUMERATE_RECYCLE_BIN_CONTEXT CallbackContext = (PENUMERATE_RECYCLE_BIN_CONTEXT)Context;\r
+ PDELETED_FILE_HANDLE DeletedFileHandle = NULL;\r
+ BOOL ret = FALSE;\r
+\r
+ DeletedFileHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(DELETED_FILE_HANDLE));\r
+ if (!DeletedFileHandle)\r
+ {\r
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);\r
+ goto cleanup;\r
+ }\r
+\r
+ ReferenceHandle(&CallbackContext->bin->refCount);\r
+ InitializeHandle(&DeletedFileHandle->refCount, IntCloseDeletedFileHandle);\r
+ DeletedFileHandle->magic = DELETEDFILE_MAGIC;\r
+ DeletedFileHandle->bin = CallbackContext->bin;\r
+ DeletedFileHandle->hDeletedFile = hDeletedFile;\r
+\r
+ ret = CallbackContext->pFnCallback(CallbackContext->Context, DeletedFileHandle);\r
+\r
+cleanup:\r
+ if (!ret)\r
+ {\r
+ if (DeletedFileHandle)\r
+ DereferenceHandle(&DeletedFileHandle->refCount);\r
+ }\r
+ return ret;\r
+}\r
+\r
+BOOL WINAPI\r
+EnumerateRecycleBinW(\r
+ IN WCHAR driveLetter,\r
+ IN PENUMERATE_RECYCLEBIN_CALLBACK pFnCallback,\r
+ IN PVOID Context)\r
+{\r
+ PRECYCLE_BIN bin = NULL;\r
+ BOOL ret = FALSE;\r
+\r
+ /* Open recycle bin */\r
+ bin = IntReferenceRecycleBin(driveLetter);\r
+ if (!bin)\r
+ goto cleanup;\r
+\r
+ if (bin->Callbacks.EnumerateFiles)\r
+ {\r
+ ENUMERATE_RECYCLE_BIN_CONTEXT CallbackContext;\r
+\r
+ CallbackContext.bin = bin;\r
+ CallbackContext.pFnCallback = pFnCallback;\r
+ CallbackContext.Context = Context;\r
+\r
+ ret = bin->Callbacks.EnumerateFiles(bin, IntEnumerateRecycleBinCallback, &CallbackContext);\r
+ }\r
+ else\r
+ SetLastError(ERROR_NOT_SUPPORTED);\r
+\r
+cleanup:\r
+ if (bin)\r
+ DereferenceHandle(&bin->refCount);\r
+ return ret;\r
+}\r
+\r
+BOOL WINAPI\r
+GetDeletedFileDetailsA(\r
+ IN HANDLE hDeletedFile,\r
+ IN DWORD BufferSize,\r
+ IN OUT PDELETED_FILE_DETAILS_A FileDetails,\r
+ OUT LPDWORD RequiredSize OPTIONAL)\r
+{\r
+ PDELETED_FILE_DETAILS_W FileDetailsW = NULL;\r
+ DWORD BufferSizeW = 0;\r
+ DWORD RequiredSizeW;\r
+ BOOL ret = FALSE;\r
+\r
+ if (BufferSize >= FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName))\r
+ {\r
+ BufferSizeW = FIELD_OFFSET(DELETED_FILE_DETAILS_W, FileName)\r
+ + (BufferSize - FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName)) * sizeof(WCHAR);\r
+ }\r
+ if (FileDetails && BufferSizeW)\r
+ {\r
+ FileDetailsW = HeapAlloc(GetProcessHeap(), 0, BufferSizeW);\r
+ if (!FileDetailsW)\r
+ {\r
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);\r
+ goto cleanup;\r
+ }\r
+ }\r
+\r
+ ret = GetDeletedFileDetailsW(hDeletedFile, BufferSizeW, FileDetailsW, &RequiredSizeW);\r
+ if (!ret)\r
+ goto cleanup;\r
+\r
+ if (FileDetails)\r
+ {\r
+ memcpy(FileDetails, FileDetailsW, FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName));\r
+ if (0 == WideCharToMultiByte(CP_ACP, 0, FileDetailsW->FileName, -1, FileDetails->FileName, BufferSize - FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName), NULL, NULL))\r
+ goto cleanup;\r
+ }\r
+ ret = TRUE;\r
+\r
+cleanup:\r
+ HeapFree(GetProcessHeap(), 0, FileDetailsW);\r
+ return ret;\r
+}\r
+\r
+BOOL WINAPI\r
+GetDeletedFileDetailsW(\r
+ IN HANDLE hDeletedFile,\r
+ IN DWORD BufferSize,\r
+ IN OUT PDELETED_FILE_DETAILS_W FileDetails,\r
+ OUT LPDWORD RequiredSize OPTIONAL)\r
+{\r
+ BOOL ret = FALSE;\r
+\r
+ if (!IntCheckDeletedFileHandle(hDeletedFile))\r
+ SetLastError(ERROR_INVALID_HANDLE);\r
+ else\r
+ {\r
+ PDELETED_FILE_HANDLE DeletedFile = (PDELETED_FILE_HANDLE)hDeletedFile;\r
+ if (DeletedFile->bin->Callbacks.GetDetails)\r
+ ret = DeletedFile->bin->Callbacks.GetDetails(DeletedFile->bin, DeletedFile->hDeletedFile, BufferSize, FileDetails, RequiredSize);\r
+ else\r
+ SetLastError(ERROR_NOT_SUPPORTED);\r
+ }\r
+\r
+ return ret;\r
+}\r
+\r
+BOOL WINAPI\r
+RestoreFile(\r
+ IN HANDLE hDeletedFile)\r
+{\r
+ BOOL ret = FALSE;\r
+\r
+ if (!IntCheckDeletedFileHandle(hDeletedFile))\r
+ SetLastError(ERROR_INVALID_HANDLE);\r
+ else\r
+ {\r
+ PDELETED_FILE_HANDLE DeletedFile = (PDELETED_FILE_HANDLE)hDeletedFile;\r
+ if (DeletedFile->bin->Callbacks.RestoreFile)\r
+ ret = DeletedFile->bin->Callbacks.RestoreFile(DeletedFile->bin, DeletedFile->hDeletedFile);\r
+ else\r
+ SetLastError(ERROR_NOT_SUPPORTED);\r
+ }\r
+\r
+ return ret;\r
+}\r
--- /dev/null
+#ifndef __RECYCLEBIN_H\r
+#define __RECYCLEBIN_H\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+#include <windows.h>\r
+#define ANY_SIZE 1\r
+\r
+typedef struct _DELETED_FILE_DETAILS_A\r
+{\r
+ FILETIME LastModification;\r
+ FILETIME DeletionTime;\r
+ LARGE_INTEGER FileSize;\r
+ LARGE_INTEGER PhysicalFileSize;\r
+ DWORD Attributes;\r
+ CHAR FileName[ANY_SIZE];\r
+} DELETED_FILE_DETAILS_A, *PDELETED_FILE_DETAILS_A;\r
+typedef struct _DELETED_FILE_DETAILS_W\r
+{\r
+ FILETIME LastModification;\r
+ FILETIME DeletionTime;\r
+ LARGE_INTEGER FileSize;\r
+ LARGE_INTEGER PhysicalFileSize;\r
+ DWORD Attributes;\r
+ WCHAR FileName[ANY_SIZE];\r
+} DELETED_FILE_DETAILS_W, *PDELETED_FILE_DETAILS_W;\r
+#ifdef UNICODE\r
+#define DELETED_FILE_DETAILS DELETED_FILE_DETAILS_W\r
+#define PDELETED_FILE_DETAILS PDELETED_FILE_DETAILS_W\r
+#else\r
+#define DELETED_FILE_DETAILS DELETED_FILE_DETAILS_A\r
+#define PDELETED_FILE_DETAILS PDELETED_FILE_DETAILS_A\r
+#endif\r
+\r
+typedef BOOL (WINAPI *PENUMERATE_RECYCLEBIN_CALLBACK)(IN PVOID Context, IN HANDLE hDeletedFile);\r
+\r
+BOOL WINAPI\r
+CloseRecycleBinHandle(\r
+ IN HANDLE hDeletedFile);\r
+\r
+BOOL WINAPI\r
+DeleteFileToRecycleBinA(\r
+ IN LPCSTR FileName);\r
+BOOL WINAPI\r
+DeleteFileToRecycleBinW(\r
+ IN LPCWSTR FileName);\r
+#ifdef UNICODE\r
+#define DeleteFileToRecycleBin DeleteFileToRecycleBinW\r
+#else\r
+#define DeleteFileToRecycleBin DeleteFileToRecycleBinA\r
+#endif\r
+\r
+BOOL WINAPI\r
+EmptyRecycleBinA(\r
+ IN CHAR driveLetter);\r
+BOOL WINAPI\r
+EmptyRecycleBinW(\r
+ IN WCHAR driveLetter);\r
+#ifdef UNICODE\r
+#define EmptyRecycleBin EmptyRecycleBinW\r
+#else\r
+#define EmptyRecycleBin EmptyRecycleBinA\r
+#endif\r
+\r
+BOOL WINAPI\r
+EnumerateRecycleBinA(\r
+ IN CHAR driveLetter,\r
+ IN PENUMERATE_RECYCLEBIN_CALLBACK pFnCallback,\r
+ IN PVOID Context);\r
+BOOL WINAPI\r
+EnumerateRecycleBinW(\r
+ IN WCHAR driveLetter,\r
+ IN PENUMERATE_RECYCLEBIN_CALLBACK pFnCallback,\r
+ IN PVOID Context);\r
+#ifdef UNICODE\r
+#define EnumerateRecycleBin EnumerateRecycleBinW\r
+#else\r
+#define EnumerateRecycleBin EnumerateRecycleBinA\r
+#endif\r
+\r
+BOOL WINAPI\r
+GetDeletedFileDetailsA(\r
+ IN HANDLE hDeletedFile,\r
+ IN DWORD BufferSize,\r
+ IN OUT PDELETED_FILE_DETAILS_A FileDetails,\r
+ OUT LPDWORD RequiredSize OPTIONAL);\r
+BOOL WINAPI\r
+GetDeletedFileDetailsW(\r
+ IN HANDLE hDeletedFile,\r
+ IN DWORD BufferSize,\r
+ IN OUT PDELETED_FILE_DETAILS_W FileDetails,\r
+ OUT LPDWORD RequiredSize OPTIONAL);\r
+#ifdef UNICODE\r
+#define GetDeletedFileDetails GetDeletedFileDetailsW\r
+#else\r
+#define GetDeletedFileDetails GetDeletedFileDetailsA\r
+#endif\r
+\r
+BOOL WINAPI\r
+RestoreFile(\r
+ IN HANDLE hDeletedFile);\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif /* __RECYCLEBIN_H */\r
--- /dev/null
+<module name="recyclebin" type="staticlibrary">\r
+ <define name="__USE_W32API" />\r
+ <file>openclose.c</file>\r
+ <file>recyclebin.c</file>\r
+ <file>recyclebin_v5.c</file>\r
+ <file>refcount.c</file>\r
+</module>\r
--- /dev/null
+#include "recyclebin.h"\r
+#include "sddl.h"\r
+\r
+/* Defines */\r
+#define RECYCLEBIN_MAGIC 0x6e694252\r
+#define DELETEDFILE_MAGIC 0x6e694253\r
+\r
+#define RECYCLE_BIN_DIRECTORY_WITH_ACL L"RECYCLER"\r
+#define RECYCLE_BIN_DIRECTORY_WITHOUT_ACL L"RECYCLED"\r
+#define RECYCLE_BIN_FILE_NAME L"INFO2"\r
+\r
+#define ROUND_UP(N, S) ((( (N) + (S) - 1) / (S) ) * (S) )\r
+\r
+/* List manipulation */\r
+#define InitializeListHead(le) (void)((le)->Flink = (le)->Blink = (le))\r
+#define InsertTailList(le,e) do { PLIST_ENTRY b = (le)->Blink; (e)->Flink = (le); (e)->Blink = b; b->Flink = (e); (le)->Blink = (e); } while (0)\r
+#define RemoveEntryList(Entry) { PLIST_ENTRY _EX_Blink, _EX_Flink; _EX_Flink = (Entry)->Flink; _EX_Blink = (Entry)->Blink; _EX_Blink->Flink = _EX_Flink; _EX_Flink->Blink = _EX_Blink; }\r
+\r
+/* Typedefs */\r
+struct _RECYCLE_BIN;\r
+typedef struct _RECYCLE_BIN *PRECYCLE_BIN;\r
+struct _REFCOUNT_DATA;\r
+typedef struct _REFCOUNT_DATA *PREFCOUNT_DATA;\r
+\r
+typedef BOOL (*PINT_ENUMERATE_RECYCLEBIN_CALLBACK)(IN PVOID Context, IN HANDLE hDeletedFile);\r
+typedef BOOL (*PDESTROY_DATA) (IN PREFCOUNT_DATA pData);\r
+\r
+typedef BOOL (*PCLOSE_HANDLE) (IN HANDLE hHandle);\r
+typedef BOOL (*PDELETE_FILE) (IN PRECYCLE_BIN bin, IN LPCWSTR FullPath, IN LPCWSTR FileName);\r
+typedef BOOL (*PEMPTY_RECYCLEBIN)(IN PRECYCLE_BIN* bin);\r
+typedef BOOL (*PENUMERATE_FILES) (IN PRECYCLE_BIN bin, IN PINT_ENUMERATE_RECYCLEBIN_CALLBACK pFnCallback, IN PVOID Context);\r
+typedef BOOL (*PGET_DETAILS) (IN PRECYCLE_BIN bin, IN HANDLE hDeletedFile, IN DWORD BufferSize, IN OUT PDELETED_FILE_DETAILS_W FileDetails, OUT LPDWORD RequiredSize OPTIONAL);\r
+typedef BOOL (*PRESTORE_FILE) (IN PRECYCLE_BIN bin, IN HANDLE hDeletedFile);\r
+\r
+typedef struct _RECYCLEBIN_CALLBACKS\r
+{\r
+ PCLOSE_HANDLE CloseHandle;\r
+ PDELETE_FILE DeleteFile;\r
+ PEMPTY_RECYCLEBIN EmptyRecycleBin;\r
+ PENUMERATE_FILES EnumerateFiles;\r
+ PGET_DETAILS GetDetails;\r
+ PRESTORE_FILE RestoreFile;\r
+} RECYCLEBIN_CALLBACKS, *PRECYCLEBIN_CALLBACKS;\r
+\r
+typedef struct _REFCOUNT_DATA\r
+{\r
+ DWORD ReferenceCount;\r
+ PDESTROY_DATA Close;\r
+} REFCOUNT_DATA;\r
+\r
+typedef struct _RECYCLE_BIN\r
+{\r
+ DWORD magic; /* RECYCLEBIN_MAGIC */\r
+ LIST_ENTRY ListEntry;\r
+ REFCOUNT_DATA refCount;\r
+ HANDLE hInfo;\r
+ RECYCLEBIN_CALLBACKS Callbacks;\r
+ LPWSTR InfoFile;\r
+ WCHAR Folder[ANY_SIZE]; /* [drive]:\[RECYCLE_BIN_DIRECTORY]\{SID} */\r
+} RECYCLE_BIN;\r
+\r
+typedef struct _DELETED_FILE_HANDLE\r
+{\r
+ DWORD magic; /* DELETEDFILE_MAGIC */\r
+ REFCOUNT_DATA refCount;\r
+ PRECYCLE_BIN bin;\r
+ HANDLE hDeletedFile; /* specific to recycle bin format */\r
+} DELETED_FILE_HANDLE, *PDELETED_FILE_HANDLE;\r
+\r
+/* Structures on disk */\r
+\r
+#include <pshpack1.h>\r
+\r
+typedef struct _INFO2_HEADER\r
+{\r
+ DWORD dwVersion;\r
+ DWORD dwNumberOfEntries;\r
+ DWORD dwHighestRecordUniqueId;\r
+ DWORD dwRecordSize;\r
+ DWORD dwTotalLogicalSize;\r
+} INFO2_HEADER, *PINFO2_HEADER;\r
+\r
+#include <poppack.h>\r
+\r
+/* Prototypes */\r
+\r
+/* openclose.c */\r
+\r
+BOOL\r
+IntCheckDeletedFileHandle(\r
+ IN HANDLE hDeletedFile);\r
+\r
+PRECYCLE_BIN\r
+IntReferenceRecycleBin(\r
+ IN WCHAR driveLetter);\r
+\r
+/* recyclebin_v5.c */\r
+\r
+VOID\r
+InitializeCallbacks5(\r
+ IN OUT PRECYCLEBIN_CALLBACKS Callbacks);\r
+\r
+/* refcount.c */\r
+\r
+BOOL\r
+InitializeHandle(\r
+ IN PREFCOUNT_DATA pData,\r
+ IN PDESTROY_DATA pFnClose OPTIONAL);\r
+\r
+BOOL\r
+ReferenceHandle(\r
+ IN PREFCOUNT_DATA pData);\r
+\r
+BOOL\r
+DereferenceHandle(\r
+ IN PREFCOUNT_DATA pData);\r
--- /dev/null
+/*\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
+ /* 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
+ LARGE_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)\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,\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
+ if (!IntSearchRecord(bin, hDeletedFile, &DeletedFile, NULL))\r
+ goto cleanup;\r
+ Needed = (DWORD)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, -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, -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
--- /dev/null
+/* Recycle bin management\r
+ * This file is under the GPLv2 licence\r
+ * Copyright (C) 2006 Hervé Poussineau <hpoussin@reactos.org>\r
+ */\r
+\r
+#include "recyclebin_private.h"\r
+\r
+#include <pshpack1.h>\r
+\r
+/* MS Windows 2000/XP/2003 */\r
+typedef struct _DELETED_FILE_RECORD\r
+{\r
+ CHAR FileNameA[MAX_PATH];\r
+ DWORD dwRecordUniqueId;\r
+ DWORD dwDriveNumber;\r
+ FILETIME DeletionTime;\r
+ DWORD dwPhysicalFileSize;\r
+ WCHAR FileNameW[MAX_PATH];\r
+} DELETED_FILE_RECORD, *PDELETED_FILE_RECORD;\r
+\r
+#include <poppack.h>\r
+\r
+static BOOL\r
+CloseHandle5(\r
+ IN HANDLE hDeletedFile);\r
+\r
+static BOOL\r
+DeleteFile5(\r
+ IN PRECYCLE_BIN Context,\r
+ IN LPCWSTR FullPath,\r
+ IN LPCWSTR FileName);\r
+\r
+static BOOL\r
+EmptyRecycleBin5(\r
+ IN PRECYCLE_BIN* bin);\r
+\r
+static BOOL\r
+EnumerateFiles5(\r
+ IN PRECYCLE_BIN bin,\r
+ IN PINT_ENUMERATE_RECYCLEBIN_CALLBACK pFnCallback,\r
+ IN PVOID Context);\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,\r
+ OUT LPDWORD RequiredSize OPTIONAL);\r
+\r
+static BOOL\r
+RestoreFile5(\r
+ IN PRECYCLE_BIN Context,\r
+ IN HANDLE hDeletedFile);\r
+\r
+static BOOL\r
+IntDeleteRecursive(\r
+ IN LPCWSTR FullName);\r
+\r
+static BOOL\r
+IntEmptyRecycleBinCallback(\r
+ IN PVOID Context,\r
+ IN HANDLE hDeletedFile);\r
+\r
+static BOOL\r
+IntGetFullName(\r
+ IN PRECYCLE_BIN bin,\r
+ IN PDELETED_FILE_RECORD pDeletedFile,\r
+ OUT LPWSTR* pFullName);\r
+\r
+static BOOL\r
+IntSearchRecord(\r
+ IN PRECYCLE_BIN bin,\r
+ IN HANDLE hDeletedFile,\r
+ OUT PDELETED_FILE_RECORD DeletedFile,\r
+ OUT PLARGE_INTEGER Position OPTIONAL);\r
--- /dev/null
+/*\r
+ * PROJECT: Recycle bin management\r
+ * LICENSE: GPL v2 - See COPYING in the top level directory\r
+ * FILE: lib/recyclebin/openclose.c\r
+ * PURPOSE: Do reference counting on objects\r
+ * PROGRAMMERS: Copyright 2006 Hervé Poussineau (hpoussin@reactos.org)\r
+ */\r
+\r
+#include "recyclebin_private.h"\r
+\r
+BOOL\r
+InitializeHandle(\r
+ IN PREFCOUNT_DATA pData,\r
+ IN PDESTROY_DATA pFnClose OPTIONAL)\r
+{\r
+ pData->ReferenceCount = 1;\r
+ pData->Close = pFnClose;\r
+ return TRUE;\r
+}\r
+\r
+BOOL\r
+ReferenceHandle(\r
+ IN PREFCOUNT_DATA pData)\r
+{\r
+ pData->ReferenceCount++;\r
+ return TRUE;\r
+}\r
+\r
+BOOL\r
+DereferenceHandle(\r
+ IN PREFCOUNT_DATA pData)\r
+{\r
+ pData->ReferenceCount--;\r
+ if (pData->ReferenceCount == 0)\r
+ {\r
+ if (pData->Close)\r
+ return pData->Close(pData);\r
+ else\r
+ return TRUE;\r
+ }\r
+ return TRUE;\r
+}\r