Add library to deal with recycle bin.
authorHervé Poussineau <hpoussin@reactos.org>
Tue, 11 Apr 2006 19:51:55 +0000 (19:51 +0000)
committerHervé Poussineau <hpoussin@reactos.org>
Tue, 11 Apr 2006 19:51:55 +0000 (19:51 +0000)
It is aimed to be compatible with Windows 2000/XP/2003 (at least) on FAT or NTFS volumes.

svn path=/trunk/; revision=21553

reactos/lib/lib.rbuild
reactos/lib/recyclebin/openclose.c [new file with mode: 0644]
reactos/lib/recyclebin/readme.txt [new file with mode: 0644]
reactos/lib/recyclebin/recyclebin.c [new file with mode: 0644]
reactos/lib/recyclebin/recyclebin.h [new file with mode: 0644]
reactos/lib/recyclebin/recyclebin.rbuild [new file with mode: 0644]
reactos/lib/recyclebin/recyclebin_private.h [new file with mode: 0644]
reactos/lib/recyclebin/recyclebin_v5.c [new file with mode: 0644]
reactos/lib/recyclebin/recyclebin_v5.h [new file with mode: 0644]
reactos/lib/recyclebin/refcount.c [new file with mode: 0644]

index 2c093bc..0fd6388 100644 (file)
@@ -4,13 +4,12 @@
 <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>
@@ -38,6 +37,9 @@
 <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>
diff --git a/reactos/lib/recyclebin/openclose.c b/reactos/lib/recyclebin/openclose.c
new file mode 100644 (file)
index 0000000..24ea499
--- /dev/null
@@ -0,0 +1,315 @@
+/*\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
diff --git a/reactos/lib/recyclebin/readme.txt b/reactos/lib/recyclebin/readme.txt
new file mode 100644 (file)
index 0000000..71fc735
--- /dev/null
@@ -0,0 +1,14 @@
+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
diff --git a/reactos/lib/recyclebin/recyclebin.c b/reactos/lib/recyclebin/recyclebin.c
new file mode 100644 (file)
index 0000000..ec75d55
--- /dev/null
@@ -0,0 +1,319 @@
+/*\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
diff --git a/reactos/lib/recyclebin/recyclebin.h b/reactos/lib/recyclebin/recyclebin.h
new file mode 100644 (file)
index 0000000..5f2aa06
--- /dev/null
@@ -0,0 +1,109 @@
+#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
diff --git a/reactos/lib/recyclebin/recyclebin.rbuild b/reactos/lib/recyclebin/recyclebin.rbuild
new file mode 100644 (file)
index 0000000..ef5ff19
--- /dev/null
@@ -0,0 +1,7 @@
+<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
diff --git a/reactos/lib/recyclebin/recyclebin_private.h b/reactos/lib/recyclebin/recyclebin_private.h
new file mode 100644 (file)
index 0000000..0efa411
--- /dev/null
@@ -0,0 +1,116 @@
+#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
diff --git a/reactos/lib/recyclebin/recyclebin_v5.c b/reactos/lib/recyclebin/recyclebin_v5.c
new file mode 100644 (file)
index 0000000..3ad90c5
--- /dev/null
@@ -0,0 +1,539 @@
+/*\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
diff --git a/reactos/lib/recyclebin/recyclebin_v5.h b/reactos/lib/recyclebin/recyclebin_v5.h
new file mode 100644 (file)
index 0000000..47047d6
--- /dev/null
@@ -0,0 +1,76 @@
+/* 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
diff --git a/reactos/lib/recyclebin/refcount.c b/reactos/lib/recyclebin/refcount.c
new file mode 100644 (file)
index 0000000..3ae954b
--- /dev/null
@@ -0,0 +1,42 @@
+/*\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