diff --git a/reactos/lib/recyclebin/guid.c b/reactos/lib/recyclebin/guid.c new file mode 100644 index 00000000000..3080cf8dfca --- /dev/null +++ b/reactos/lib/recyclebin/guid.c @@ -0,0 +1,14 @@ +/* + * PROJECT: Recycle bin management + * LICENSE: GPL v2 - See COPYING in the top level directory + * FILE: lib/recyclebin/guid.c + * PURPOSE: Define GUID values + * PROGRAMMERS: Copyright 2007 Hervé Poussineau (hpoussin@reactos.org) + */ + +#define INITGUID +#include +DEFINE_GUID(IID_IRecycleBin, 0x392ec73a, 0x45e9, 0x43de, 0xa8, 0x41, 0x4f, 0x32, 0x1a, 0xea, 0xa5, 0x80); +DEFINE_GUID(IID_IRecycleBinEnumList, 0x392ec73a, 0x45e9, 0x43de, 0xa8, 0x41, 0x4f, 0x32, 0x1a, 0xea, 0xa5, 0x81); +DEFINE_GUID(IID_IRecycleBinFile, 0x392ec73a, 0x45e9, 0x43de, 0xa8, 0x41, 0x4f, 0x32, 0x1a, 0xea, 0xa5, 0x82); +DEFINE_GUID(IID_IRecycleBin5, 0x392ec73a, 0x45e9, 0x43de, 0xa8, 0x41, 0x4f, 0x32, 0x1a, 0xea, 0xa5, 0x83); diff --git a/reactos/lib/recyclebin/openclose.c b/reactos/lib/recyclebin/openclose.c deleted file mode 100644 index 760561ed821..00000000000 --- a/reactos/lib/recyclebin/openclose.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * PROJECT: Recycle bin management - * LICENSE: GPL v2 - See COPYING in the top level directory - * FILE: lib/recyclebin/openclose.c - * PURPOSE: Open/close recycle bins - * PROGRAMMERS: Copyright 2006 Hervé Poussineau (hpoussin@reactos.org) - */ - -#include "recyclebin_private.h" - -BOOL -IntCheckDeletedFileHandle( - IN HANDLE hDeletedFile) -{ - if (hDeletedFile == NULL || hDeletedFile == INVALID_HANDLE_VALUE) - return FALSE; - - if (((PDELETED_FILE_HANDLE)hDeletedFile)->magic != DELETEDFILE_MAGIC) - return FALSE; - - return TRUE; -} - -static BOOL -IntCloseRecycleBinHandle( - IN PREFCOUNT_DATA pData) -{ - PRECYCLE_BIN bin; - - bin = CONTAINING_RECORD(pData, RECYCLE_BIN, refCount); - if (!CloseHandle(bin->hInfo)) - return FALSE; - - RemoveEntryList(&bin->ListEntry); - HeapFree(GetProcessHeap(), 0, bin); - return TRUE; -} - -static BOOL -IntCreateEmptyRecycleBin( - IN PRECYCLE_BIN bin, - IN PSID OwnerSid OPTIONAL) -{ - LPWSTR BufferName = NULL; - LPWSTR Separator; /* Pointer into BufferName buffer */ - LPWSTR FileName; /* Pointer into BufferName buffer */ - LPCSTR DesktopIniContents = "[.ShellClassInfo]\r\nCLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n"; - DWORD Info2Contents[] = { 5, 0, 0, 0x320, 0 }; - SIZE_T BytesToWrite, BytesWritten, Needed; - HANDLE hFile = INVALID_HANDLE_VALUE; - BOOL ret = FALSE; - - Needed = (wcslen(bin->Folder) + 1 + max(wcslen(RECYCLE_BIN_FILE_NAME), wcslen(L"desktop.ini")) + 1) * sizeof(WCHAR); - BufferName = HeapAlloc(GetProcessHeap(), 0, Needed); - if (!BufferName) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto cleanup; - } - - wcscpy(BufferName, bin->Folder); - Separator = wcsstr(&BufferName[3], L"\\"); - if (Separator) - *Separator = UNICODE_NULL; - ret = CreateDirectoryW(BufferName, NULL); - if (!ret && GetLastError() != ERROR_ALREADY_EXISTS) - goto cleanup; - SetFileAttributesW(BufferName, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); - if (Separator) - { - *Separator = L'\\'; - ret = CreateDirectoryW(BufferName, NULL); - if (!ret && GetLastError() != ERROR_ALREADY_EXISTS) - goto cleanup; - } - - if (OwnerSid) - { - //DWORD rc; - - /* Add ACL to allow only user/SYSTEM to open it */ - /* FIXME: rc = SetNamedSecurityInfo( - BufferName, - SE_FILE_OBJECT, - ???, - OwnerSid, - NULL, - ???, - ???); - if (rc != ERROR_SUCCESS) - { - SetLastError(rc); - goto cleanup; - } - */ - } - - wcscat(BufferName, L"\\"); - FileName = &BufferName[wcslen(BufferName)]; - - /* Create desktop.ini */ - wcscpy(FileName, L"desktop.ini"); - hFile = CreateFileW(BufferName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, NULL); - if (hFile == INVALID_HANDLE_VALUE) - goto cleanup; - BytesToWrite = strlen(DesktopIniContents); - ret = WriteFile(hFile, DesktopIniContents, (DWORD)BytesToWrite, &BytesWritten, NULL); - if (!ret) - goto cleanup; - if (BytesWritten != BytesToWrite) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - CloseHandle(hFile); - hFile = INVALID_HANDLE_VALUE; - - /* Create empty INFO2 file */ - wcscpy(FileName, RECYCLE_BIN_FILE_NAME); - hFile = CreateFileW(BufferName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL); - if (hFile == INVALID_HANDLE_VALUE) - goto cleanup; - BytesToWrite = sizeof(Info2Contents); - ret = WriteFile(hFile, Info2Contents, (DWORD)BytesToWrite, &BytesWritten, NULL); - if (!ret) - goto cleanup; - if (BytesWritten != BytesToWrite) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - - bin->hInfo = hFile; - ret = TRUE; - -cleanup: - HeapFree(GetProcessHeap(), 0, BufferName); - if (!ret) - { - if (hFile != INVALID_HANDLE_VALUE) - CloseHandle(hFile); - } - return ret; -} - -PRECYCLE_BIN -IntReferenceRecycleBin( - IN WCHAR driveLetter) -{ - PLIST_ENTRY ListEntry; - PRECYCLE_BIN bin = NULL, ret = NULL; - WCHAR RootPath[4]; - DWORD FileSystemFlags; - LPCWSTR RecycleBinDirectory; - HANDLE tokenHandle = INVALID_HANDLE_VALUE; - PTOKEN_USER TokenUserInfo = NULL; - LPWSTR StringSid = NULL; - SIZE_T Needed, DirectoryLength; - INFO2_HEADER Header; - BOOL AlreadyCreated = FALSE; - DWORD bytesRead; - - static LIST_ENTRY ListHead; - static BOOL ListInitialized = FALSE; - - if (!ListInitialized) - { - InitializeListHead(&ListHead); - ListInitialized = TRUE; - } - - /* Search if the recycle bin has already been opened */ - driveLetter = (WCHAR)toupper(driveLetter); - ListEntry = ListHead.Flink; - while (ListEntry != &ListHead) - { - bin = CONTAINING_RECORD(ListEntry, RECYCLE_BIN, ListEntry); - if (bin->Folder[0] == driveLetter) - { - ReferenceHandle(&bin->refCount); - return bin; - } - ListEntry = ListEntry->Flink; - } - bin = NULL; - - /* We need to create a new recycle bin */ - - /* Get information about file system */ - wsprintfW(RootPath, L"%c:\\", driveLetter); - if (!GetVolumeInformationW( - RootPath, - NULL, - 0, - NULL, - NULL, - &FileSystemFlags, - NULL, - 0)) - { - goto cleanup; - } - if (!(FileSystemFlags & FILE_PERSISTENT_ACLS)) - RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITHOUT_ACL; - else - { - RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITH_ACL; - - /* Get user SID */ - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tokenHandle)) - goto cleanup; - if (GetTokenInformation(tokenHandle, TokenUser, NULL, 0, &Needed)) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) - goto cleanup; - TokenUserInfo = HeapAlloc(GetProcessHeap(), 0, Needed); - if (!TokenUserInfo) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto cleanup; - } - if (!GetTokenInformation(tokenHandle, TokenUser, TokenUserInfo, (DWORD)Needed, &Needed)) - goto cleanup; - if (!ConvertSidToStringSidW(TokenUserInfo->User.Sid, &StringSid)) - goto cleanup; - } - - /* Create RECYCLEBIN structure */ -openfile: - DirectoryLength = 3 + wcslen(RecycleBinDirectory) + 1; - if (StringSid) - DirectoryLength += wcslen(StringSid) + 1; - Needed = FIELD_OFFSET(RECYCLE_BIN, Folder) + (2 * DirectoryLength + 2 + wcslen(RECYCLE_BIN_FILE_NAME))* sizeof(WCHAR); - bin = HeapAlloc(GetProcessHeap(), 0, Needed); - if (!bin) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto cleanup; - } - memset(bin, 0, Needed); - InitializeHandle(&bin->refCount, IntCloseRecycleBinHandle); - bin->magic = RECYCLEBIN_MAGIC; - bin->hInfo = INVALID_HANDLE_VALUE; - bin->InfoFile = &bin->Folder[DirectoryLength]; - bin->Folder[0] = driveLetter; - bin->Folder[1] = '\0'; - wcscat(bin->Folder, L":\\"); - wcscat(bin->Folder, RecycleBinDirectory); - if (StringSid) - { - wcscat(bin->Folder, L"\\"); - wcscat(bin->Folder, StringSid); - } - wcscpy(bin->InfoFile, bin->Folder); - wcscat(bin->InfoFile, L"\\"); - wcscat(bin->InfoFile, RECYCLE_BIN_FILE_NAME); - InsertTailList(&ListHead, &bin->ListEntry); - - /* Open info file */ - bin->hInfo = CreateFileW(bin->InfoFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - if (bin->hInfo == INVALID_HANDLE_VALUE) - { - if (GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_FILE_NOT_FOUND) - { - if (!IntCreateEmptyRecycleBin(bin, TokenUserInfo ? TokenUserInfo->User.Sid : NULL)) - goto cleanup; - AlreadyCreated = TRUE; - } - } - if (bin->hInfo == INVALID_HANDLE_VALUE) - goto cleanup; - - if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) - goto cleanup; - if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL)) - goto cleanup; - if (bytesRead != sizeof(INFO2_HEADER) && !AlreadyCreated) - { - /* Create a new file */ - if (!DereferenceHandle(&bin->refCount)) - goto cleanup; - if (!DeleteFileW(bin->InfoFile)) - goto cleanup; - goto openfile; - } - switch (Header.dwVersion) - { - case 5: - InitializeCallbacks5(&bin->Callbacks); - break; - default: - /* Unknown recycle bin version */ - SetLastError(ERROR_NOT_SUPPORTED); - goto cleanup; - } - - ret = bin; - -cleanup: - if (tokenHandle != INVALID_HANDLE_VALUE) - CloseHandle(tokenHandle); - HeapFree(GetProcessHeap(), 0, TokenUserInfo); - if (StringSid) - LocalFree(StringSid); - if (!ret) - { - if (bin && bin->hInfo != INVALID_HANDLE_VALUE) - DereferenceHandle(&bin->refCount); - HeapFree(GetProcessHeap(), 0, bin); - } - return ret; -} diff --git a/reactos/lib/recyclebin/readme.txt b/reactos/lib/recyclebin/readme.txt index 5e7668f9c6f..9cbc6edb310 100644 --- a/reactos/lib/recyclebin/readme.txt +++ b/reactos/lib/recyclebin/readme.txt @@ -1,7 +1,6 @@ This library deals with Recycle bin. It is aimed to be compatible with Windows 2000/XP/2003 (at least) on FAT or NTFS volumes. - TODO - Empty a recycle bin containing directories (v5) - Set security on recycle bin folder @@ -9,7 +8,6 @@ TODO - Make the library thread-safe 3 levels -- 1: recyclebin.c: Public interface -- 2a: openclose.c: Open/close recycle bins -- 2b: refcount.c: Do reference counting on objects -- 3: recyclebin_v5.c: Deals with recycle bins of Windows 2000/XP/2003 \ No newline at end of file +- 1: recyclebin.c : Public C interface +- 2: recyclebin_generic.c : 'System-wide' recycle bin, which knows no implementation detail +- 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 index 51bf5e16685..ebc2717a3a7 100644 --- a/reactos/lib/recyclebin/recyclebin.c +++ b/reactos/lib/recyclebin/recyclebin.c @@ -1,35 +1,30 @@ /* * PROJECT: Recycle bin management * LICENSE: GPL v2 - See COPYING in the top level directory - * FILE: lib/recyclebin/openclose.c + * FILE: lib/recyclebin/recyclebin.c * PURPOSE: Public interface - * PROGRAMMERS: Copyright 2006 Hervé Poussineau (hpoussin@reactos.org) + * PROGRAMMERS: Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) */ +#define COBJMACROS #include "recyclebin_private.h" - -typedef struct _ENUMERATE_RECYCLE_BIN_CONTEXT -{ - PRECYCLE_BIN bin; - PENUMERATE_RECYCLEBIN_CALLBACK pFnCallback; - PVOID Context; -} ENUMERATE_RECYCLE_BIN_CONTEXT, *PENUMERATE_RECYCLE_BIN_CONTEXT; +#include BOOL WINAPI CloseRecycleBinHandle( IN HANDLE hDeletedFile) { - BOOL ret = FALSE; + IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile; + HRESULT hr; - if (!IntCheckDeletedFileHandle(hDeletedFile)) - SetLastError(ERROR_INVALID_HANDLE); + hr = IRecycleBinFile_Release(rbf); + if (SUCCEEDED(hr)) + return TRUE; + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + SetLastError(HRESULT_CODE(hr)); else - { - PDELETED_FILE_HANDLE file = (PDELETED_FILE_HANDLE)hDeletedFile; - ret = DereferenceHandle(&file->refCount); - } - - return ret; + SetLastError(ERROR_GEN_FAILURE); + return FALSE; } BOOL WINAPI @@ -70,89 +65,78 @@ BOOL WINAPI DeleteFileToRecycleBinW( IN LPCWSTR FileName) { - LPWSTR FullFileName = NULL; - DWORD dwBufferLength = 0; - LPWSTR lpFilePart; - DWORD len; - PRECYCLE_BIN bin = NULL; - BOOL ret = FALSE; + IRecycleBin *prb; + HRESULT hr; - /* Check parameters */ - if (FileName == NULL) - { - SetLastError(ERROR_INVALID_PARAMETER); - goto cleanup; - } - - /* Get full file name */ - while (TRUE) - { - len = GetFullPathNameW(FileName, dwBufferLength, FullFileName, &lpFilePart); - if (len == 0) - goto cleanup; - else if (len < dwBufferLength) - break; - HeapFree(GetProcessHeap(), 0, FullFileName); - dwBufferLength = len; - FullFileName = HeapAlloc(GetProcessHeap(), 0, dwBufferLength * sizeof(WCHAR)); - if (!FullFileName) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto cleanup; - } - } - - if (!lpFilePart || dwBufferLength < 2 || FullFileName[1] != ':') - { - /* Only a directory name, or not a local file */ - SetLastError(ERROR_INVALID_NAME); - } - - /* Open recycle bin */ - bin = IntReferenceRecycleBin(FullFileName[0]); - if (!bin) + hr = GetDefaultRecycleBin(NULL, &prb); + if (!SUCCEEDED(hr)) goto cleanup; - if (bin->Callbacks.DeleteFile) - ret = bin->Callbacks.DeleteFile(bin, FullFileName, lpFilePart); - else - SetLastError(ERROR_NOT_SUPPORTED); + hr = IRecycleBin_DeleteFile(prb, FileName); + IRecycleBin_Release(prb); cleanup: - HeapFree(GetProcessHeap(), 0, FullFileName); - if (bin) - DereferenceHandle(&bin->refCount); - return ret; + if (SUCCEEDED(hr)) + return TRUE; + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + SetLastError(HRESULT_CODE(hr)); + else + SetLastError(ERROR_GEN_FAILURE); + return FALSE; } BOOL WINAPI EmptyRecycleBinA( - IN CHAR driveLetter) + IN LPCSTR pszRoot OPTIONAL) { - return EmptyRecycleBinW((WCHAR)driveLetter); + int len; + LPWSTR szRootW = NULL; + BOOL ret = FALSE; + + if (pszRoot) + { + len = MultiByteToWideChar(CP_ACP, 0, pszRoot, -1, NULL, 0); + if (len == 0) + goto cleanup; + szRootW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!szRootW) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto cleanup; + } + if (MultiByteToWideChar(CP_ACP, 0, pszRoot, -1, szRootW, len) == 0) + goto cleanup; + } + + ret = EmptyRecycleBinW(szRootW); + +cleanup: + HeapFree(GetProcessHeap(), 0, szRootW); + return ret; } BOOL WINAPI EmptyRecycleBinW( - IN WCHAR driveLetter) + IN LPCWSTR pszRoot OPTIONAL) { - PRECYCLE_BIN bin = NULL; - BOOL ret = FALSE; + IRecycleBin *prb; + HRESULT hr; - /* Open recycle bin */ - bin = IntReferenceRecycleBin(driveLetter); - if (!bin) + hr = GetDefaultRecycleBin(pszRoot, &prb); + if (!SUCCEEDED(hr)) goto cleanup; - if (bin->Callbacks.EmptyRecycleBin) - ret = bin->Callbacks.EmptyRecycleBin(&bin); - else - SetLastError(ERROR_NOT_SUPPORTED); + hr = IRecycleBin_EmptyRecycleBin(prb); + IRecycleBin_Release(prb); cleanup: - if (bin) - DereferenceHandle(&bin->refCount); - return ret; + if (SUCCEEDED(hr)) + return TRUE; + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + SetLastError(HRESULT_CODE(hr)); + else + SetLastError(ERROR_GEN_FAILURE); + return FALSE; } BOOL WINAPI @@ -164,92 +148,54 @@ EnumerateRecycleBinA( return EnumerateRecycleBinW((WCHAR)driveLetter, pFnCallback, Context); } -static BOOL -IntCloseDeletedFileHandle( - IN PREFCOUNT_DATA pData) -{ - PDELETED_FILE_HANDLE file; - - file = CONTAINING_RECORD(pData, DELETED_FILE_HANDLE, refCount); - if (!DereferenceHandle(&file->bin->refCount)) - return FALSE; - - file->magic = 0; - HeapFree(GetProcessHeap(), 0, file); - return TRUE; -} - -static BOOL -IntEnumerateRecycleBinCallback( - IN PVOID Context, - IN HANDLE hDeletedFile) -{ - PENUMERATE_RECYCLE_BIN_CONTEXT CallbackContext = (PENUMERATE_RECYCLE_BIN_CONTEXT)Context; - PDELETED_FILE_HANDLE DeletedFileHandle = NULL; - BOOL ret = FALSE; - - DeletedFileHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(DELETED_FILE_HANDLE)); - if (!DeletedFileHandle) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto cleanup; - } - - ReferenceHandle(&CallbackContext->bin->refCount); - InitializeHandle(&DeletedFileHandle->refCount, IntCloseDeletedFileHandle); - DeletedFileHandle->magic = DELETEDFILE_MAGIC; - DeletedFileHandle->bin = CallbackContext->bin; - DeletedFileHandle->hDeletedFile = hDeletedFile; - - ret = CallbackContext->pFnCallback(CallbackContext->Context, DeletedFileHandle); - -cleanup: - if (!ret) - { - if (DeletedFileHandle) - DereferenceHandle(&DeletedFileHandle->refCount); - } - return ret; -} - BOOL WINAPI EnumerateRecycleBinW( IN WCHAR driveLetter, IN PENUMERATE_RECYCLEBIN_CALLBACK pFnCallback, IN PVOID Context OPTIONAL) { - PRECYCLE_BIN bin = NULL; - BOOL ret = FALSE; + IRecycleBin *prb = NULL; + IRecycleBinEnumList *prbel = NULL; + IRecycleBinFile *prbf; + HRESULT hr; - /* Check parameters */ - if (pFnCallback == NULL) - { - SetLastError(ERROR_INVALID_PARAMETER); - goto cleanup; - } - - /* Open recycle bin */ - bin = IntReferenceRecycleBin(driveLetter); - if (!bin) + hr = GetDefaultRecycleBin(NULL, &prb); + if (!SUCCEEDED(hr)) goto cleanup; - if (bin->Callbacks.EnumerateFiles) + hr = IRecycleBin_EnumObjects(prb, &prbel); + if (!SUCCEEDED(hr)) + goto cleanup; + while (TRUE) { - ENUMERATE_RECYCLE_BIN_CONTEXT CallbackContext; - - CallbackContext.bin = bin; - CallbackContext.pFnCallback = pFnCallback; - CallbackContext.Context = Context; - - ret = bin->Callbacks.EnumerateFiles(bin, IntEnumerateRecycleBinCallback, &CallbackContext); + hr = IRecycleBinEnumList_Next(prbel, 1, &prbf, NULL); + if (hr == S_FALSE) + { + hr = S_OK; + goto cleanup; + } + else if (!SUCCEEDED(hr)) + goto cleanup; + if (!pFnCallback(Context, (HANDLE)prbf)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + IRecycleBinFile_Release(prbf); } - else - SetLastError(ERROR_NOT_SUPPORTED); cleanup: - if (bin) - DereferenceHandle(&bin->refCount); - return ret; + if (prb) + IRecycleBin_Release(prb); + if (prbel) + IRecycleBinEnumList_Release(prb); + if (SUCCEEDED(hr)) + return TRUE; + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + SetLastError(HRESULT_CODE(hr)); + else + SetLastError(ERROR_GEN_FAILURE); + return FALSE; } BOOL WINAPI @@ -284,7 +230,7 @@ GetDeletedFileDetailsA( if (FileDetails) { - memcpy(FileDetails, FileDetailsW, FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName)); + CopyMemory(FileDetails, FileDetailsW, FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName)); if (0 == WideCharToMultiByte(CP_ACP, 0, FileDetailsW->FileName, -1, FileDetails->FileName, BufferSize - FIELD_OFFSET(DELETED_FILE_DETAILS_A, FileName), NULL, NULL)) goto cleanup; } @@ -302,38 +248,92 @@ GetDeletedFileDetailsW( IN OUT PDELETED_FILE_DETAILS_W FileDetails OPTIONAL, OUT LPDWORD RequiredSize OPTIONAL) { - BOOL ret = FALSE; + IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile; + HRESULT hr; + SIZE_T NameSize, Needed; - if (!IntCheckDeletedFileHandle(hDeletedFile)) - SetLastError(ERROR_INVALID_HANDLE); - else + hr = IRecycleBinFile_GetFileName(rbf, 0, NULL, &NameSize); + if (!SUCCEEDED(hr)) + goto cleanup; + Needed = FIELD_OFFSET(DELETED_FILE_DETAILS_W, FileName) + NameSize; + if (RequiredSize) + *RequiredSize = (DWORD)Needed; + if (Needed > BufferSize) { - PDELETED_FILE_HANDLE DeletedFile = (PDELETED_FILE_HANDLE)hDeletedFile; - if (DeletedFile->bin->Callbacks.GetDetails) - ret = DeletedFile->bin->Callbacks.GetDetails(DeletedFile->bin, DeletedFile->hDeletedFile, BufferSize, FileDetails, RequiredSize); - else - SetLastError(ERROR_NOT_SUPPORTED); + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + goto cleanup; } + hr = IRecycleBinFile_GetFileName(rbf, NameSize, FileDetails->FileName, NULL); + if (!SUCCEEDED(hr)) + goto cleanup; + hr = IRecycleBinFile_GetLastModificationTime(rbf, &FileDetails->LastModification); + if (!SUCCEEDED(hr)) + goto cleanup; + hr = IRecycleBinFile_GetDeletionTime(rbf, &FileDetails->DeletionTime); + if (!SUCCEEDED(hr)) + goto cleanup; + hr = IRecycleBinFile_GetFileSize(rbf, &FileDetails->FileSize); + if (!SUCCEEDED(hr)) + goto cleanup; + hr = IRecycleBinFile_GetPhysicalFileSize(rbf, &FileDetails->PhysicalFileSize); + if (!SUCCEEDED(hr)) + goto cleanup; + hr = IRecycleBinFile_GetAttributes(rbf, &FileDetails->Attributes); + if (!SUCCEEDED(hr)) + goto cleanup; - return ret; +cleanup: + if (SUCCEEDED(hr)) + return TRUE; + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + SetLastError(HRESULT_CODE(hr)); + else + SetLastError(ERROR_GEN_FAILURE); + return FALSE; } BOOL WINAPI RestoreFile( IN HANDLE hDeletedFile) { - BOOL ret = FALSE; + IRecycleBinFile *rbf = (IRecycleBinFile *)hDeletedFile; + HRESULT hr; - if (!IntCheckDeletedFileHandle(hDeletedFile)) - SetLastError(ERROR_INVALID_HANDLE); + hr = IRecycleBinFile_Restore(rbf); + if (SUCCEEDED(hr)) + return TRUE; + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + SetLastError(HRESULT_CODE(hr)); + else + SetLastError(ERROR_GEN_FAILURE); + return FALSE; +} + +HRESULT WINAPI +GetDefaultRecycleBin( + IN LPCWSTR pszVolume OPTIONAL, + OUT IRecycleBin **pprb) +{ + IUnknown *pUnk; + HRESULT hr; + + if (!pprb) + return E_POINTER; + + if (!pszVolume) + hr = RecycleBinGeneric_Constructor(&pUnk); else { - PDELETED_FILE_HANDLE DeletedFile = (PDELETED_FILE_HANDLE)hDeletedFile; - if (DeletedFile->bin->Callbacks.RestoreFile) - ret = DeletedFile->bin->Callbacks.RestoreFile(DeletedFile->bin, DeletedFile->hDeletedFile); - else - SetLastError(ERROR_NOT_SUPPORTED); - } + /* FIXME: do a better validation! */ + if (wcslen(pszVolume) != 3 || pszVolume[1] != ':' || pszVolume[2] != '\\') + return HRESULT_FROM_WIN32(ERROR_INVALID_NAME); - return ret; + /* For now, only support this type of recycle bins... */ + hr = RecycleBin5_Constructor(pszVolume, &pUnk); + } + if (!SUCCEEDED(hr)) + return hr; + hr = IUnknown_QueryInterface(pUnk, &IID_IRecycleBin, (void **)pprb); + IUnknown_Release(pUnk); + return hr; } diff --git a/reactos/lib/recyclebin/recyclebin.h b/reactos/lib/recyclebin/recyclebin.h index a6ba9a9f293..0c55cce78e1 100644 --- a/reactos/lib/recyclebin/recyclebin.h +++ b/reactos/lib/recyclebin/recyclebin.h @@ -54,10 +54,10 @@ DeleteFileToRecycleBinW( BOOL WINAPI EmptyRecycleBinA( - IN CHAR driveLetter); + IN LPCSTR pszRoot); BOOL WINAPI EmptyRecycleBinW( - IN WCHAR driveLetter); + IN LPCWSTR pszRoot); #ifdef UNICODE #define EmptyRecycleBin EmptyRecycleBinW #else @@ -102,6 +102,190 @@ BOOL WINAPI RestoreFile( IN HANDLE hDeletedFile); +/* COM interface */ + +typedef interface IRecycleBinFile IRecycleBinFile; +EXTERN_C const IID IID_IRecycleBinFile; + +typedef struct IRecycleBinFileVtbl +{ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IN IRecycleBinFile *This, + IN REFIID riid, + OUT void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IN IRecycleBinFile *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IN IRecycleBinFile *This); + + HRESULT (STDMETHODCALLTYPE *GetLastModificationTime)( + IN IRecycleBinFile *This, + OUT FILETIME *pLastModificationTime); + + HRESULT (STDMETHODCALLTYPE *GetDeletionTime)( + IN IRecycleBinFile *This, + OUT FILETIME *pDeletionTime); + + HRESULT (STDMETHODCALLTYPE *GetFileSize)( + IN IRecycleBinFile *This, + OUT ULARGE_INTEGER *pFileSize); + + HRESULT (STDMETHODCALLTYPE *GetPhysicalFileSize)( + IN IRecycleBinFile *This, + OUT ULARGE_INTEGER *pPhysicalFileSize); + + HRESULT (STDMETHODCALLTYPE *GetAttributes)( + IN IRecycleBinFile *This, + OUT DWORD *pAttributes); + + HRESULT (STDMETHODCALLTYPE *GetFileName)( + IN IRecycleBinFile *This, + IN SIZE_T BufferSize, + IN OUT LPWSTR Buffer, + OUT SIZE_T *RequiredSize); + + HRESULT (STDMETHODCALLTYPE *Delete)( + IN IRecycleBinFile *This); + + HRESULT (STDMETHODCALLTYPE *Restore)( + IN IRecycleBinFile *This); +} IRecycleBinFileVtbl; + +interface IRecycleBinFile +{ + CONST_VTBL struct IRecycleBinFileVtbl *lpVtbl; +}; + +#ifdef COBJMACROS +#define IRecycleBinFile_QueryInterface(This, riid, ppvObject) \ + (This)->lpVtbl->QueryInterface(This, riid, ppvObject) +#define IRecycleBinFile_AddRef(This) \ + (This)->lpVtbl->AddRef(This) +#define IRecycleBinFile_Release(This) \ + (This)->lpVtbl->Release(This) +#define IRecycleBinFile_GetLastModificationTime(This, pLastModificationTime) \ + (This)->lpVtbl->GetLastModificationTime(This, pLastModificationTime) +#define IRecycleBinFile_GetDeletionTime(This, pDeletionTime) \ + (This)->lpVtbl->GetDeletionTime(This, pDeletionTime) +#define IRecycleBinFile_GetFileSize(This, pFileSize) \ + (This)->lpVtbl->GetFileSize(This, pFileSize) +#define IRecycleBinFile_GetPhysicalFileSize(This, pPhysicalFileSize) \ + (This)->lpVtbl->GetPhysicalFileSize(This, pPhysicalFileSize) +#define IRecycleBinFile_GetAttributes(This, pAttributes) \ + (This)->lpVtbl->GetAttributes(This, pAttributes) +#define IRecycleBinFile_GetFileName(This, BufferSize, Buffer, RequiredSize) \ + (This)->lpVtbl->GetFileName(This, BufferSize, Buffer, RequiredSize) +#define IRecycleBinFile_Delete(This) \ + (This)->lpVtbl->Delete(This) +#define IRecycleBinFile_Restore(This) \ + (This)->lpVtbl->Restore(This) +#endif + +typedef interface IRecycleBinEnumList IRecycleBinEnumList; +EXTERN_C const IID IID_IRecycleBinEnumList; + +typedef struct IRecycleBinEnumListVtbl +{ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IN IRecycleBinEnumList *This, + IN REFIID riid, + OUT void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IN IRecycleBinEnumList *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IN IRecycleBinEnumList *This); + + HRESULT (STDMETHODCALLTYPE *Next)( + IN IRecycleBinEnumList *This, + IN DWORD celt, + IN OUT IRecycleBinFile **rgelt, + OUT DWORD *pceltFetched); + + HRESULT (STDMETHODCALLTYPE *Skip)( + IN IRecycleBinEnumList *This, + IN DWORD celt); + + HRESULT (STDMETHODCALLTYPE *Reset)( + IN IRecycleBinEnumList *This); +} IRecycleBinEnumListVtbl; + +interface IRecycleBinEnumList +{ + CONST_VTBL struct IRecycleBinEnumListVtbl *lpVtbl; +}; + +#ifdef COBJMACROS +#define IRecycleBinEnumList_QueryInterface(This, riid, ppvObject) \ + (This)->lpVtbl->QueryInterface(This, riid, ppvObject) +#define IRecycleBinEnumList_AddRef(This) \ + (This)->lpVtbl->AddRef(This) +#define IRecycleBinEnumList_Release(This) \ + (This)->lpVtbl->Release(This) +#define IRecycleBinEnumList_Next(This, celt, rgelt, pceltFetched) \ + (This)->lpVtbl->Next(This, celt, rgelt, pceltFetched) +#define IRecycleBinEnumList_Skip(This, celt) \ + (This)->lpVtbl->Skip(This, celt) +#define IRecycleBinEnumList_Reset(This) \ + (This)->lpVtbl->Reset(This) +#endif + +typedef interface IRecycleBin IRecycleBin; +EXTERN_C const IID IID_IRecycleBin; + +typedef struct IRecycleBinVtbl +{ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IN IRecycleBin *This, + IN REFIID riid, + OUT void **ppvObject); + + ULONG (STDMETHODCALLTYPE *AddRef)( + IN IRecycleBin *This); + + ULONG (STDMETHODCALLTYPE *Release)( + IN IRecycleBin *This); + + HRESULT (STDMETHODCALLTYPE *DeleteFile)( + IN IRecycleBin *This, + IN LPCWSTR szFileName); + + HRESULT (STDMETHODCALLTYPE *EmptyRecycleBin)( + IN IRecycleBin *This); + + HRESULT (STDMETHODCALLTYPE *EnumObjects)( + IN IRecycleBin *This, + OUT IRecycleBinEnumList **ppEnumList); +} IRecycleBinVtbl; + +interface IRecycleBin +{ + CONST_VTBL struct IRecycleBinVtbl *lpVtbl; +}; + +#ifdef COBJMACROS +#define IRecycleBin_QueryInterface(This, riid, ppvObject) \ + (This)->lpVtbl->QueryInterface(This, riid, ppvObject) +#define IRecycleBin_AddRef(This) \ + (This)->lpVtbl->AddRef(This) +#define IRecycleBin_Release(This) \ + (This)->lpVtbl->Release(This) +#define IRecycleBin_DeleteFile(This, szFileName) \ + (This)->lpVtbl->DeleteFile(This, szFileName) +#define IRecycleBin_EmptyRecycleBin(This) \ + (This)->lpVtbl->EmptyRecycleBin(This) +#define IRecycleBin_EnumObjects(This, ppEnumList) \ + (This)->lpVtbl->EnumObjects(This, ppEnumList) +#endif + +HRESULT WINAPI +GetDefaultRecycleBin( + IN LPCWSTR pszVolume OPTIONAL, + OUT IRecycleBin **pprb); + #ifdef __cplusplus } #endif diff --git a/reactos/lib/recyclebin/recyclebin.rbuild b/reactos/lib/recyclebin/recyclebin.rbuild index 158d6e6abd4..3127c8bea8c 100644 --- a/reactos/lib/recyclebin/recyclebin.rbuild +++ b/reactos/lib/recyclebin/recyclebin.rbuild @@ -1,9 +1,10 @@ - - openclose.c + guid.c recyclebin.c + recyclebin_generic.c + recyclebin_generic_enumerator.c recyclebin_v5.c - refcount.c + recyclebin_v5_enumerator.c diff --git a/reactos/lib/recyclebin/recyclebin_generic.c b/reactos/lib/recyclebin/recyclebin_generic.c new file mode 100644 index 00000000000..8f8bc005694 --- /dev/null +++ b/reactos/lib/recyclebin/recyclebin_generic.c @@ -0,0 +1,200 @@ +/* + * PROJECT: Recycle bin management + * LICENSE: GPL v2 - See COPYING in the top level directory + * FILE: lib/recyclebin/recyclebin_generic.c + * PURPOSE: Deals with a system-wide recycle bin + * PROGRAMMERS: Copyright 2007 Hervé Poussineau (hpoussin@reactos.org) + */ + +#define COBJMACROS +#include "recyclebin_private.h" +#include + +struct RecycleBinGeneric +{ + ULONG ref; + IRecycleBin recycleBinImpl; +}; + +static HRESULT STDMETHODCALLTYPE +RecycleBinGenericVtbl_RecycleBin_QueryInterface( + IRecycleBin *This, + REFIID riid, + void **ppvObject) +{ + struct RecycleBinGeneric *s = CONTAINING_RECORD(This, struct RecycleBinGeneric, recycleBinImpl); + + if (!ppvObject) + return E_POINTER; + + if (IsEqualIID(riid, &IID_IUnknown)) + *ppvObject = &s->recycleBinImpl; + else if (IsEqualIID(riid, &IID_IRecycleBin)) + *ppvObject = &s->recycleBinImpl; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef(This); + return S_OK; +} + +static ULONG STDMETHODCALLTYPE +RecycleBinGenericVtbl_RecycleBin_AddRef( + IRecycleBin *This) +{ + struct RecycleBinGeneric *s = CONTAINING_RECORD(This, struct RecycleBinGeneric, recycleBinImpl); + ULONG refCount = InterlockedIncrement((PLONG)&s->ref); + return refCount; +} + +static ULONG STDMETHODCALLTYPE +RecycleBinGenericVtbl_RecycleBin_Release( + IRecycleBin *This) +{ + struct RecycleBinGeneric *s = CONTAINING_RECORD(This, struct RecycleBinGeneric, recycleBinImpl); + ULONG refCount; + + if (!This) + return E_POINTER; + + refCount = InterlockedDecrement((PLONG)&s->ref); + + if (refCount == 0) + CoTaskMemFree(s); + + return refCount; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBinGenericVtbl_RecycleBin_DeleteFile( + IN IRecycleBin *This, + IN LPCWSTR szFileName) +{ + IRecycleBin *prb; + LPWSTR szFullName = NULL; + DWORD dwBufferLength = 0; + DWORD len; + WCHAR szVolume[MAX_PATH]; + HRESULT hr; + + /* Get full file name */ + while (TRUE) + { + len = GetFullPathNameW(szFileName, dwBufferLength, szFullName, NULL); + if (len == 0) + { + if (szFullName) + CoTaskMemFree(szFullName); + return HRESULT_FROM_WIN32(GetLastError()); + } + else if (len < dwBufferLength) + break; + if (szFullName) + CoTaskMemFree(szFullName); + dwBufferLength = len; + szFullName = CoTaskMemAlloc(dwBufferLength * sizeof(WCHAR)); + if (!szFullName) + return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + } + + /* Get associated volume path */ +#ifndef __REACTOS__ + if (!GetVolumePathNameW(szFullName, szVolume, MAX_PATH)) + { + CoTaskMemFree(szFullName); + return HRESULT_FROM_WIN32(GetLastError()); + } +#else + swprintf(szVolume, L"%c:\\", szFullName[0]); +#endif + + /* Skip namespace (if any) */ + if (szVolume[0] == '\\' + && szVolume[1] == '\\' + && (szVolume[2] == '.' || szVolume[2] == '?') + && szVolume[3] == '\\') + { + MoveMemory(szVolume, &szVolume[4], (MAX_PATH - 4) * sizeof(WCHAR)); + } + + hr = GetDefaultRecycleBin(szVolume, &prb); + if (!SUCCEEDED(hr)) + { + CoTaskMemFree(szFullName); + return hr; + } + + hr = IRecycleBin_DeleteFile(prb, szFullName); + CoTaskMemFree(szFullName); + IRecycleBin_Release(prb); + return hr; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBinGenericVtbl_RecycleBin_EmptyRecycleBin( + IN IRecycleBin *This) +{ + WCHAR szVolumeName[MAX_PATH]; + DWORD dwLogicalDrives, i; + IRecycleBin *prb; + HRESULT hr; + + dwLogicalDrives = GetLogicalDrives(); + if (dwLogicalDrives == 0) + return HRESULT_FROM_WIN32(GetLastError()); + + for (i = 0; i < 26; i++) + { + if (!(dwLogicalDrives & (1 << i))) + continue; + swprintf(szVolumeName, L"%c:\\", 'A' + i); + if (GetDriveTypeW(szVolumeName) != DRIVE_FIXED) + continue; + + hr = GetDefaultRecycleBin(szVolumeName, &prb); + if (!SUCCEEDED(hr)) + return hr; + + hr = IRecycleBin_EmptyRecycleBin(prb); + IRecycleBin_Release(prb); + } + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBinGenericVtbl_RecycleBin_EnumObjects( + IN IRecycleBin *This, + OUT IRecycleBinEnumList **ppEnumList) +{ + return RecycleBinGeneric_Enumerator_Constructor(ppEnumList); +} + +CONST_VTBL struct IRecycleBinVtbl RecycleBinGenericVtbl = +{ + RecycleBinGenericVtbl_RecycleBin_QueryInterface, + RecycleBinGenericVtbl_RecycleBin_AddRef, + RecycleBinGenericVtbl_RecycleBin_Release, + RecycleBinGenericVtbl_RecycleBin_DeleteFile, + RecycleBinGenericVtbl_RecycleBin_EmptyRecycleBin, + RecycleBinGenericVtbl_RecycleBin_EnumObjects, +}; + +HRESULT RecycleBinGeneric_Constructor(OUT IUnknown **ppUnknown) +{ + /* This RecycleBin implementation was introduced to be able to manage all + * drives at once, and instanciate the 'real' implementations when needed */ + struct RecycleBinGeneric *s; + + s = CoTaskMemAlloc(sizeof(struct RecycleBinGeneric)); + if (!s) + return E_OUTOFMEMORY; + s->ref = 1; + s->recycleBinImpl.lpVtbl = &RecycleBinGenericVtbl; + + *ppUnknown = (IUnknown *)&s->recycleBinImpl; + return S_OK; +} diff --git a/reactos/lib/recyclebin/recyclebin_generic_enumerator.c b/reactos/lib/recyclebin/recyclebin_generic_enumerator.c new file mode 100644 index 00000000000..f7b2a40537f --- /dev/null +++ b/reactos/lib/recyclebin/recyclebin_generic_enumerator.c @@ -0,0 +1,221 @@ +/* + * PROJECT: Recycle bin management + * LICENSE: GPL v2 - See COPYING in the top level directory + * FILE: lib/recyclebin/recyclebin_generic_enumerator.c + * PURPOSE: Enumerates contents of all recycle bins + * PROGRAMMERS: Copyright 2007 Hervé Poussineau (hpoussin@reactos.org) + */ + +#define COBJMACROS +#include "recyclebin.h" +#include + +struct RecycleBinGenericEnum +{ + ULONG ref; + IRecycleBinEnumList recycleBinEnumImpl; + IRecycleBinEnumList *current; + DWORD dwLogicalDrives; + SIZE_T skip; +}; + +static HRESULT STDMETHODCALLTYPE +RecycleBinGenericEnum_RecycleBinEnumList_QueryInterface( + IN IRecycleBinEnumList *This, + IN REFIID riid, + OUT void **ppvObject) +{ + struct RecycleBinGenericEnum *s = CONTAINING_RECORD(This, struct RecycleBinGenericEnum, recycleBinEnumImpl); + + if (!ppvObject) + return E_POINTER; + + if (IsEqualIID(riid, &IID_IUnknown)) + *ppvObject = &s->recycleBinEnumImpl; + else if (IsEqualIID(riid, &IID_IRecycleBinEnumList)) + *ppvObject = &s->recycleBinEnumImpl; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef(This); + return S_OK; +} + +static ULONG STDMETHODCALLTYPE +RecycleBinGenericEnum_RecycleBinEnumList_AddRef( + IN IRecycleBinEnumList *This) +{ + struct RecycleBinGenericEnum *s = CONTAINING_RECORD(This, struct RecycleBinGenericEnum, recycleBinEnumImpl); + ULONG refCount = InterlockedIncrement((PLONG)&s->ref); + return refCount; +} + +static ULONG STDMETHODCALLTYPE +RecycleBinGenericEnum_RecycleBinEnumList_Release( + IN IRecycleBinEnumList *This) +{ + struct RecycleBinGenericEnum *s = CONTAINING_RECORD(This, struct RecycleBinGenericEnum, recycleBinEnumImpl); + ULONG refCount; + + if (!This) + return E_POINTER; + + refCount = InterlockedDecrement((PLONG)&s->ref); + + if (refCount == 0) + { + if (s->current) + IRecycleBinEnumList_Release(s->current); + CoTaskMemFree(s); + } + + return refCount; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBinGenericEnum_RecycleBinEnumList_Next( + IN IRecycleBinEnumList *This, + IN DWORD celt, + IN OUT IRecycleBinFile **rgelt, + OUT DWORD *pceltFetched) +{ + struct RecycleBinGenericEnum *s = CONTAINING_RECORD(This, struct RecycleBinGenericEnum, recycleBinEnumImpl); + IRecycleBin *prb; + DWORD i; + DWORD fetched = 0, newFetched; + HRESULT hr; + + if (!rgelt) + return E_POINTER; + if (!pceltFetched && celt > 1) + return E_INVALIDARG; + + while (TRUE) + { + /* Get enumerator implementation */ + if (!s->current && s->dwLogicalDrives) + { + for (i = 0; i < 26; i++) + if (s->dwLogicalDrives & (1 << i)) + { + WCHAR szVolumeName[4]; + szVolumeName[0] = (WCHAR)('A' + i); + szVolumeName[1] = ':'; + szVolumeName[2] = '\\'; + szVolumeName[3] = UNICODE_NULL; + if (GetDriveTypeW(szVolumeName) != DRIVE_FIXED) + { + s->dwLogicalDrives &= ~(1 << i); + continue; + } + hr = GetDefaultRecycleBin(szVolumeName, &prb); + if (!SUCCEEDED(hr)) + return hr; + hr = IRecycleBin_EnumObjects(prb, &s->current); + IRecycleBin_Release(prb); + if (!SUCCEEDED(hr)) + return hr; + s->dwLogicalDrives &= ~(1 << i); + break; + } + } + if (!s->current) + { + /* Nothing more to enumerate */ + if (pceltFetched) + *pceltFetched = fetched; + return S_FALSE; + } + + /* Skip some elements */ + while (s->skip > 0) + { + IRecycleBinFile *rbf; + hr = IRecycleBinEnumList_Next(s->current, 1, &rbf, NULL); + if (hr == S_OK) + hr = IRecycleBinFile_Release(rbf); + else if (hr == S_FALSE) + break; + else if (!SUCCEEDED(hr)) + return hr; + } + if (s->skip > 0) + continue; + + /* Fill area */ + hr = IRecycleBinEnumList_Next(s->current, celt - fetched, &rgelt[fetched], &newFetched); + if (SUCCEEDED(hr)) + fetched += newFetched; + if (hr == S_FALSE || newFetched == 0) + { + hr = IRecycleBinEnumList_Release(s->current); + s->current = NULL; + } + else if (!SUCCEEDED(hr)) + return hr; + if (fetched == celt) + { + if (pceltFetched) + *pceltFetched = fetched; + return S_OK; + } + } + + /* Never go here */ +} + +static HRESULT STDMETHODCALLTYPE +RecycleBinGenericEnum_RecycleBinEnumList_Skip( + IN IRecycleBinEnumList *This, + IN DWORD celt) +{ + struct RecycleBinGenericEnum *s = CONTAINING_RECORD(This, struct RecycleBinGenericEnum, recycleBinEnumImpl); + s->skip += celt; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBinGenericEnum_RecycleBinEnumList_Reset( + IN IRecycleBinEnumList *This) +{ + struct RecycleBinGenericEnum *s = CONTAINING_RECORD(This, struct RecycleBinGenericEnum, recycleBinEnumImpl); + + if (s->current) + { + IRecycleBinEnumList_Release(s->current); + s->current = NULL; + s->skip = 0; + } + s->dwLogicalDrives = GetLogicalDrives(); + return S_OK; +} + +CONST_VTBL struct IRecycleBinEnumListVtbl RecycleBinGenericEnumVtbl = +{ + RecycleBinGenericEnum_RecycleBinEnumList_QueryInterface, + RecycleBinGenericEnum_RecycleBinEnumList_AddRef, + RecycleBinGenericEnum_RecycleBinEnumList_Release, + RecycleBinGenericEnum_RecycleBinEnumList_Next, + RecycleBinGenericEnum_RecycleBinEnumList_Skip, + RecycleBinGenericEnum_RecycleBinEnumList_Reset, +}; + +HRESULT +RecycleBinGeneric_Enumerator_Constructor( + OUT IRecycleBinEnumList **pprbel) +{ + struct RecycleBinGenericEnum *s; + + s = CoTaskMemAlloc(sizeof(struct RecycleBinGenericEnum)); + if (!s) + return E_OUTOFMEMORY; + ZeroMemory(s, sizeof(struct RecycleBinGenericEnum)); + s->ref = 1; + s->recycleBinEnumImpl.lpVtbl = &RecycleBinGenericEnumVtbl; + + *pprbel = &s->recycleBinEnumImpl; + return IRecycleBinEnumList_Reset(*pprbel); +} diff --git a/reactos/lib/recyclebin/recyclebin_private.h b/reactos/lib/recyclebin/recyclebin_private.h index 004733f90f6..fea57a75a60 100644 --- a/reactos/lib/recyclebin/recyclebin_private.h +++ b/reactos/lib/recyclebin/recyclebin_private.h @@ -17,55 +17,6 @@ #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; } /* Typedefs */ -struct _RECYCLE_BIN; -typedef struct _RECYCLE_BIN *PRECYCLE_BIN; -struct _REFCOUNT_DATA; -typedef struct _REFCOUNT_DATA *PREFCOUNT_DATA; - -typedef BOOL (*PINT_ENUMERATE_RECYCLEBIN_CALLBACK)(IN PVOID Context OPTIONAL, IN HANDLE hDeletedFile); -typedef BOOL (*PDESTROY_DATA) (IN PREFCOUNT_DATA pData); - -typedef BOOL (*PCLOSE_HANDLE) (IN HANDLE hHandle); -typedef BOOL (*PDELETE_FILE) (IN PRECYCLE_BIN bin, IN LPCWSTR FullPath, IN LPCWSTR FileName); -typedef BOOL (*PEMPTY_RECYCLEBIN)(IN PRECYCLE_BIN* bin); -typedef BOOL (*PENUMERATE_FILES) (IN PRECYCLE_BIN bin, IN PINT_ENUMERATE_RECYCLEBIN_CALLBACK pFnCallback, IN PVOID Context OPTIONAL); -typedef BOOL (*PGET_DETAILS) (IN PRECYCLE_BIN bin, IN HANDLE hDeletedFile, IN DWORD BufferSize, IN OUT PDELETED_FILE_DETAILS_W FileDetails OPTIONAL, OUT LPDWORD RequiredSize OPTIONAL); -typedef BOOL (*PRESTORE_FILE) (IN PRECYCLE_BIN bin, IN HANDLE hDeletedFile); - -typedef struct _RECYCLEBIN_CALLBACKS -{ - PCLOSE_HANDLE CloseHandle; - PDELETE_FILE DeleteFile; - PEMPTY_RECYCLEBIN EmptyRecycleBin; - PENUMERATE_FILES EnumerateFiles; - PGET_DETAILS GetDetails; - PRESTORE_FILE RestoreFile; -} RECYCLEBIN_CALLBACKS, *PRECYCLEBIN_CALLBACKS; - -typedef struct _REFCOUNT_DATA -{ - DWORD ReferenceCount; - PDESTROY_DATA Close; -} REFCOUNT_DATA; - -typedef struct _RECYCLE_BIN -{ - DWORD magic; /* RECYCLEBIN_MAGIC */ - LIST_ENTRY ListEntry; - REFCOUNT_DATA refCount; - HANDLE hInfo; - RECYCLEBIN_CALLBACKS Callbacks; - LPWSTR InfoFile; - WCHAR Folder[ANY_SIZE]; /* [drive]:\[RECYCLE_BIN_DIRECTORY]\{SID} */ -} RECYCLE_BIN; - -typedef struct _DELETED_FILE_HANDLE -{ - DWORD magic; /* DELETEDFILE_MAGIC */ - REFCOUNT_DATA refCount; - PRECYCLE_BIN bin; - HANDLE hDeletedFile; /* specific to recycle bin format */ -} DELETED_FILE_HANDLE, *PDELETED_FILE_HANDLE; /* Structures on disk */ @@ -84,33 +35,14 @@ typedef struct _INFO2_HEADER /* Prototypes */ -/* openclose.c */ +/* recyclebin_generic.c */ -BOOL -IntCheckDeletedFileHandle( - IN HANDLE hDeletedFile); +HRESULT RecycleBinGeneric_Constructor(OUT IUnknown **ppUnknown); -PRECYCLE_BIN -IntReferenceRecycleBin( - IN WCHAR driveLetter); +/* recyclebin_generic_enumerator.c */ + +HRESULT RecycleBinGeneric_Enumerator_Constructor(OUT IRecycleBinEnumList **pprbel); /* recyclebin_v5.c */ -VOID -InitializeCallbacks5( - IN OUT PRECYCLEBIN_CALLBACKS Callbacks); - -/* refcount.c */ - -BOOL -InitializeHandle( - IN PREFCOUNT_DATA pData, - IN PDESTROY_DATA pFnClose OPTIONAL); - -BOOL -ReferenceHandle( - IN PREFCOUNT_DATA pData); - -BOOL -DereferenceHandle( - IN PREFCOUNT_DATA pData); +HRESULT RecycleBin5_Constructor(IN LPCWSTR VolumePath, OUT IUnknown **ppUnknown); diff --git a/reactos/lib/recyclebin/recyclebin_v5.c b/reactos/lib/recyclebin/recyclebin_v5.c index 87a8c419df6..f2932434ce6 100644 --- a/reactos/lib/recyclebin/recyclebin_v5.c +++ b/reactos/lib/recyclebin/recyclebin_v5.c @@ -1,390 +1,14 @@ /* * PROJECT: Recycle bin management * LICENSE: GPL v2 - See COPYING in the top level directory - * FILE: lib/recyclebin/openclose.c + * FILE: lib/recyclebin/recyclebin_v5.c * PURPOSE: Deals with recycle bins of Windows 2000/XP/2003 - * PROGRAMMERS: Copyright 2006 Hervé Poussineau (hpoussin@reactos.org) + * PROGRAMMERS: Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) */ +#define COBJMACROS #include "recyclebin_v5.h" - -VOID -InitializeCallbacks5( - IN OUT PRECYCLEBIN_CALLBACKS Callbacks) -{ - Callbacks->CloseHandle = CloseHandle5; - Callbacks->DeleteFile = DeleteFile5; - Callbacks->EmptyRecycleBin = EmptyRecycleBin5; - Callbacks->EnumerateFiles = EnumerateFiles5; - Callbacks->GetDetails = GetDetails5; - Callbacks->RestoreFile = RestoreFile5; -} - -static BOOL -CloseHandle5( - IN HANDLE hDeletedFile) -{ - UNREFERENCED_PARAMETER(hDeletedFile); - /* Nothing to do, as hDeletedFile is simply a DWORD... */ - return TRUE; -} - -static BOOL -DeleteFile5( - IN PRECYCLE_BIN bin, - IN LPCWSTR FullPath, - IN LPCWSTR FileName) -{ - HANDLE hFile = INVALID_HANDLE_VALUE; - INFO2_HEADER Header; - DELETED_FILE_RECORD DeletedFile; - DWORD bytesRead, bytesWritten; - ULARGE_INTEGER fileSize; - SYSTEMTIME SystemTime; - WCHAR RootDir[4]; - WCHAR DeletedFileName[2 * MAX_PATH]; - LPCWSTR Extension; - DWORD ClusterSize, BytesPerSector, SectorsPerCluster; - BOOL ret = FALSE; - - if (wcslen(FullPath) >= MAX_PATH) - { - /* Unable to store a too long path in recycle bin */ - SetLastError(ERROR_INVALID_NAME); - goto cleanup; - } - - hFile = CreateFileW(FullPath, 0, 0, NULL, OPEN_EXISTING, 0, NULL); - if (hFile == INVALID_HANDLE_VALUE) - goto cleanup; - - if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) - goto cleanup; - if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL)) - goto cleanup; - if (bytesRead != sizeof(INFO2_HEADER) || Header.dwRecordSize == 0) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - - if (Header.dwVersion != 5 || Header.dwRecordSize != sizeof(DELETED_FILE_RECORD)) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - - /* Get file size */ -#if 0 - if (!GetFileSizeEx(hFile, &fileSize)) - goto cleanup; -#else - fileSize.u.LowPart = GetFileSize(hFile, &fileSize.u.HighPart); - if (fileSize.u.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) - goto cleanup; -#endif - /* Check if file size is > 4Gb */ - if (fileSize.u.HighPart != 0) - { - /* FIXME: how to delete files >= 4Gb? */ - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - goto cleanup; - } - Header.dwTotalLogicalSize += fileSize.u.LowPart; - - /* Generate new name */ - Header.dwHighestRecordUniqueId++; - Extension = wcsrchr(FileName, '.'); - wsprintfW(DeletedFileName, L"%s\\D%c%lu%s", bin->Folder, FullPath[0] - 'A' + 'a', Header.dwHighestRecordUniqueId, Extension); - - /* Get cluster size */ - wsprintfW(RootDir, L"%c:\\", bin->Folder[0]); - if (!GetDiskFreeSpaceW(RootDir, &SectorsPerCluster, &BytesPerSector, NULL, NULL)) - goto cleanup; - ClusterSize = BytesPerSector * SectorsPerCluster; - - /* Get current time */ - GetSystemTime(&SystemTime); - if (!SystemTimeToFileTime(&SystemTime, &DeletedFile.DeletionTime)) - goto cleanup; - - /* Update INFO2 */ - memset(&DeletedFile, 0, sizeof(DELETED_FILE_RECORD)); - if (WideCharToMultiByte(CP_ACP, 0, FullPath, -1, DeletedFile.FileNameA, MAX_PATH, NULL, NULL) == 0) - { - SetLastError(ERROR_INVALID_NAME); - goto cleanup; - } - DeletedFile.dwRecordUniqueId = Header.dwHighestRecordUniqueId; - DeletedFile.dwDriveNumber = tolower(bin->Folder[0]) - 'a'; - DeletedFile.dwPhysicalFileSize = ROUND_UP(fileSize.u.LowPart, ClusterSize); - wcscpy(DeletedFile.FileNameW, FullPath); - - if (!SetFilePointer(bin->hInfo, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) - goto cleanup; - if (!WriteFile(bin->hInfo, &DeletedFile, sizeof(DELETED_FILE_RECORD), &bytesWritten, NULL)) - goto cleanup; - if (bytesWritten != sizeof(DELETED_FILE_RECORD)) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - Header.dwNumberOfEntries++; - if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) - { - goto cleanup; - } - if (!WriteFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesWritten, NULL)) - goto cleanup; - if (bytesWritten != sizeof(INFO2_HEADER)) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - - /* Move file */ - if (!MoveFileW(FullPath, DeletedFileName)) - goto cleanup; - - ret = TRUE; - -cleanup: - if (hFile != INVALID_HANDLE_VALUE) - CloseHandle(hFile); - return ret; -} - -static BOOL -EmptyRecycleBin5( - IN PRECYCLE_BIN* bin) -{ - LPWSTR InfoFile = NULL; - BOOL ret = FALSE; - - InfoFile = HeapAlloc(GetProcessHeap(), 0, wcslen((*bin)->InfoFile) * sizeof(WCHAR) + sizeof(UNICODE_NULL)); - if (!InfoFile) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto cleanup; - } - wcscpy(InfoFile, (*bin)->InfoFile); - - /* Delete all files in the recycle bin */ - if (!EnumerateFiles5(*bin, IntEmptyRecycleBinCallback, *bin)) - goto cleanup; - - /* Delete INFO2 */ - if (!DereferenceHandle(&(*bin)->refCount)) - goto cleanup; - if (!DeleteFileW(InfoFile)) - goto cleanup; - *bin = NULL; - ret = TRUE; - -cleanup: - HeapFree(GetProcessHeap(), 0, InfoFile); - return ret; -} - -static BOOL -EnumerateFiles5( - IN PRECYCLE_BIN bin, - IN PINT_ENUMERATE_RECYCLEBIN_CALLBACK pFnCallback, - IN PVOID Context OPTIONAL) -{ - INFO2_HEADER Header; - DELETED_FILE_RECORD DeletedFile; - DWORD bytesRead, dwEntries; - BOOL ret = FALSE; - - if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) - goto cleanup; - if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL)) - goto cleanup; - if (bytesRead != sizeof(INFO2_HEADER) || Header.dwRecordSize == 0) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - - if (Header.dwVersion != 5 || Header.dwRecordSize != sizeof(DELETED_FILE_RECORD)) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - - SetLastError(ERROR_SUCCESS); - for (dwEntries = 0; dwEntries < Header.dwNumberOfEntries; dwEntries++) - { - if (!ReadFile(bin->hInfo, &DeletedFile, Header.dwRecordSize, &bytesRead, NULL)) - goto cleanup; - if (bytesRead != Header.dwRecordSize) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - if (!pFnCallback(Context, (HANDLE)(ULONG_PTR)DeletedFile.dwRecordUniqueId)) - goto cleanup; - if (SetFilePointer(bin->hInfo, sizeof(INFO2_HEADER) + Header.dwRecordSize * dwEntries, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) - goto cleanup; - } - - ret = TRUE; - -cleanup: - return ret; -} - -static BOOL -GetDetails5( - IN PRECYCLE_BIN bin, - IN HANDLE hDeletedFile, - IN DWORD BufferSize, - IN OUT PDELETED_FILE_DETAILS_W FileDetails OPTIONAL, - OUT LPDWORD RequiredSize OPTIONAL) -{ - DELETED_FILE_RECORD DeletedFile; - SIZE_T Needed; - LPWSTR FullName = NULL; - HANDLE hFile = INVALID_HANDLE_VALUE; - BOOL ret = FALSE; - - /* Check parameters */ - if (BufferSize > 0 && FileDetails == NULL) - { - SetLastError(ERROR_INVALID_PARAMETER); - goto cleanup; - } - - if (!IntSearchRecord(bin, hDeletedFile, &DeletedFile, NULL)) - goto cleanup; - Needed = FIELD_OFFSET(DELETED_FILE_DETAILS_W, FileName) + (wcslen(DeletedFile.FileNameW) + 1) * sizeof(WCHAR); - if (RequiredSize) - *RequiredSize = (DWORD)Needed; - if (Needed > BufferSize) - { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - goto cleanup; - } - - if (!IntGetFullName(bin, &DeletedFile, &FullName)) - goto cleanup; - - /* Open file */ - FileDetails->Attributes = GetFileAttributesW(FullName); - if (FileDetails->Attributes == INVALID_FILE_ATTRIBUTES) - goto cleanup; - if (FileDetails->Attributes & FILE_ATTRIBUTE_DIRECTORY) - hFile = CreateFileW(FullName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - else - hFile = CreateFileW(FullName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if (hFile == INVALID_HANDLE_VALUE) - goto cleanup; - - /* Fill returned structure */ - if (!GetFileTime(hFile, NULL, NULL, &FileDetails->LastModification)) - goto cleanup; - memcpy(&FileDetails->DeletionTime, &DeletedFile.DeletionTime, sizeof(FILETIME)); - FileDetails->FileSize.u.LowPart = GetFileSize(hFile, &FileDetails->FileSize.u.HighPart); - if (FileDetails->FileSize.u.LowPart == INVALID_FILE_SIZE) - goto cleanup; - FileDetails->PhysicalFileSize.u.HighPart = 0; - FileDetails->PhysicalFileSize.u.LowPart = DeletedFile.dwPhysicalFileSize; - wcscpy(FileDetails->FileName, DeletedFile.FileNameW); - - ret = TRUE; - -cleanup: - HeapFree(GetProcessHeap(), 0, FullName); - if (hFile != INVALID_HANDLE_VALUE) - CloseHandle(hFile); - return ret; -} - -static BOOL -RestoreFile5( - IN PRECYCLE_BIN bin, - IN HANDLE hDeletedFile) -{ - INFO2_HEADER Header; - DWORD bytesRead, bytesWritten; - LARGE_INTEGER Position; - DELETED_FILE_RECORD DeletedFile, LastFile; - LPWSTR FullName = NULL; - BOOL ret = FALSE; - - if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) - goto cleanup; - if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL)) - goto cleanup; - if (bytesRead != sizeof(INFO2_HEADER) || Header.dwRecordSize == 0) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - - if (Header.dwVersion != 5 || Header.dwRecordSize != sizeof(DELETED_FILE_RECORD)) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - - /* Search deleted entry */ - if (!IntSearchRecord(bin, hDeletedFile, &DeletedFile, &Position)) - goto cleanup; - /* Get destination full name */ - if (!IntGetFullName(bin, &DeletedFile, &FullName)) - goto cleanup; - /* Restore file */ - if (!MoveFileW(FullName, DeletedFile.FileNameW)) - goto cleanup; - - /* Update INFO2 */ - /* 1) If not last entry, copy last entry to the current one */ - if (SetFilePointer(bin->hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END) == INVALID_SET_FILE_POINTER) - goto cleanup; - if (!ReadFile(bin->hInfo, &LastFile, sizeof(DELETED_FILE_RECORD), &bytesRead, NULL)) - goto cleanup; - if (bytesRead != sizeof(DELETED_FILE_RECORD)) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - if (LastFile.dwRecordUniqueId != DeletedFile.dwRecordUniqueId) - { - /* Move the last entry to the current one */ - if (SetFilePointer(bin->hInfo, Position.u.LowPart, &Position.u.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER) - goto cleanup; - if (!WriteFile(bin->hInfo, &LastFile, sizeof(DELETED_FILE_RECORD), &bytesWritten, NULL)) - goto cleanup; - if (bytesWritten != sizeof(DELETED_FILE_RECORD)) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - } - /* 2) Update the header */ - Header.dwNumberOfEntries--; - if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) - goto cleanup; - if (!WriteFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesWritten, NULL)) - goto cleanup; - if (bytesWritten != sizeof(INFO2_HEADER)) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - /* 3) Truncate file */ - if (SetFilePointer(bin->hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END) == INVALID_SET_FILE_POINTER) - goto cleanup; - if (!SetEndOfFile(bin->hInfo)) - goto cleanup; - ret = TRUE; - -cleanup: - HeapFree(GetProcessHeap(), 0, FullName); - return ret; -} +#include static BOOL IntDeleteRecursive( @@ -427,121 +51,676 @@ cleanup: return ret; } -static BOOL -IntEmptyRecycleBinCallback( - IN PVOID Context, - IN HANDLE hDeletedFile) +struct RecycleBin5 { - PRECYCLE_BIN bin = (PRECYCLE_BIN)Context; - DELETED_FILE_RECORD DeletedFile; - LPWSTR FullName = NULL; - BOOL ret = FALSE; + ULONG ref; + IRecycleBin5 recycleBinImpl; + HANDLE hInfo; + HANDLE hInfoMapped; - if (!IntSearchRecord(bin, hDeletedFile, &DeletedFile, NULL)) - goto cleanup; + DWORD EnumeratorCount; - if (!IntGetFullName(bin, &DeletedFile, &FullName)) - goto cleanup; + LPWSTR VolumePath; + WCHAR Folder[ANY_SIZE]; /* [drive]:\[RECYCLE_BIN_DIRECTORY]\{SID} */ +}; - if (!IntDeleteRecursive(FullName)) - goto cleanup; - ret = TRUE; +static HRESULT STDMETHODCALLTYPE +RecycleBin5_RecycleBin5_QueryInterface( + IRecycleBin5 *This, + REFIID riid, + void **ppvObject) +{ + struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl); -cleanup: - HeapFree(GetProcessHeap(), 0, FullName); - return ret; + if (!ppvObject) + return E_POINTER; + + if (IsEqualIID(riid, &IID_IUnknown)) + *ppvObject = &s->recycleBinImpl; + else if (IsEqualIID(riid, &IID_IRecycleBin)) + *ppvObject = &s->recycleBinImpl; + else if (IsEqualIID(riid, &IID_IRecycleBin5)) + *ppvObject = &s->recycleBinImpl; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef(This); + return S_OK; } -static BOOL -IntGetFullName( - IN PRECYCLE_BIN bin, - IN PDELETED_FILE_RECORD pDeletedFile, - OUT LPWSTR* pFullName) +static ULONG STDMETHODCALLTYPE +RecycleBin5_RecycleBin5_AddRef( + IRecycleBin5 *This) { - SIZE_T Needed; + struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl); + ULONG refCount = InterlockedIncrement((PLONG)&s->ref); + return refCount; +} + +static ULONG STDMETHODCALLTYPE +RecycleBin5_RecycleBin5_Release( + IRecycleBin5 *This) +{ + struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl); + ULONG refCount; + + if (!This) + return E_POINTER; + + refCount = InterlockedDecrement((PLONG)&s->ref); + + if (refCount == 0) + { + CloseHandle(s->hInfo); + CloseHandle(s->hInfoMapped); + CoTaskMemFree(s); + } + + return refCount; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5_RecycleBin5_DeleteFile( + IN IRecycleBin5 *This, + IN LPCWSTR szFileName) +{ + struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl); + LPWSTR szFullName = NULL; + DWORD dwBufferLength = 0; + LPWSTR lpFilePart; LPCWSTR Extension; - LPWSTR FullName = NULL; - BOOL ret = FALSE; + WCHAR DeletedFileName[MAX_PATH]; + DWORD len; + HANDLE hFile = INVALID_HANDLE_VALUE; + PINFO2_HEADER pHeader = NULL; + PDELETED_FILE_RECORD pDeletedFile; + ULARGE_INTEGER fileSize; + DWORD dwAttributes; + SYSTEMTIME SystemTime; + DWORD ClusterSize, BytesPerSector, SectorsPerCluster; + HRESULT hr; - *pFullName = NULL; - Extension = wcsrchr(pDeletedFile->FileNameW, '.'); - if (Extension < wcsrchr(pDeletedFile->FileNameW, '\\')) - Extension = NULL; - Needed = wcslen(bin->Folder) + 13; - if (Extension) - Needed += wcslen(Extension); - FullName = HeapAlloc(GetProcessHeap(), 0, Needed * sizeof(WCHAR)); - if (!FullName) + if (s->EnumeratorCount != 0) + return E_FAIL; + + /* Get full file name */ + while (TRUE) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); + len = GetFullPathNameW(szFileName, dwBufferLength, szFullName, &lpFilePart); + if (len == 0) + { + if (szFullName) + CoTaskMemFree(szFullName); + return HRESULT_FROM_WIN32(GetLastError()); + } + else if (len < dwBufferLength) + break; + if (szFullName) + CoTaskMemFree(szFullName); + dwBufferLength = len; + szFullName = CoTaskMemAlloc(dwBufferLength * sizeof(WCHAR)); + if (!szFullName) + return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + } + + /* Check if file exists */ + dwAttributes = GetFileAttributesW(szFullName); + if (dwAttributes == INVALID_FILE_ATTRIBUTES) + return HRESULT_FROM_WIN32(GetLastError()); + + if (dwBufferLength < 2 || szFullName[1] != ':') + { + /* Not a local file */ + CoTaskMemFree(szFullName); + return HRESULT_FROM_WIN32(ERROR_INVALID_NAME); + } + + hFile = CreateFileW(szFullName, 0, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + hr = HRESULT_FROM_WIN32(GetLastError()); goto cleanup; } - wsprintfW(FullName, L"%s\\D%c%lu%s", bin->Folder, pDeletedFile->dwDriveNumber + 'a', pDeletedFile->dwRecordUniqueId, Extension); - *pFullName = FullName; - ret = TRUE; + + /* Increase INFO2 file size */ + CloseHandle(s->hInfoMapped); + SetFilePointer(s->hInfo, sizeof(DELETED_FILE_RECORD), NULL, FILE_END); + SetEndOfFile(s->hInfo); + s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL); + if (!s->hInfoMapped) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + + /* Open INFO2 file */ + pHeader = MapViewOfFile(s->hInfoMapped, FILE_MAP_WRITE, 0, 0, 0); + if (!pHeader) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + pDeletedFile = ((PDELETED_FILE_RECORD)(pHeader + 1)) + pHeader->dwNumberOfEntries; + + /* Get file size */ +#if 0 + if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&fileSize)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } +#else + fileSize.u.LowPart = GetFileSize(hFile, &fileSize.u.HighPart); + if (fileSize.u.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } +#endif + /* Check if file size is > 4Gb */ + if (fileSize.u.HighPart != 0) + { + /* FIXME: how to delete files >= 4Gb? */ + hr = E_NOTIMPL; + goto cleanup; + } + pHeader->dwTotalLogicalSize += fileSize.u.LowPart; + + /* Generate new name */ + pHeader->dwHighestRecordUniqueId++; + Extension = wcsrchr(szFullName, '.'); + ZeroMemory(pDeletedFile, sizeof(DELETED_FILE_RECORD)); + pDeletedFile->dwRecordUniqueId = pHeader->dwHighestRecordUniqueId; + pDeletedFile->dwDriveNumber = tolower(szFullName[0]) - 'a'; + _snwprintf(DeletedFileName, MAX_PATH, L"%s\\D%c%lu%s", s->Folder, pDeletedFile->dwDriveNumber + 'a', pDeletedFile->dwRecordUniqueId, Extension); + + /* Get cluster size */ + if (!GetDiskFreeSpaceW(s->VolumePath, &SectorsPerCluster, &BytesPerSector, NULL, NULL)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + ClusterSize = BytesPerSector * SectorsPerCluster; + + /* Get current time */ + GetSystemTime(&SystemTime); + if (!SystemTimeToFileTime(&SystemTime, &pDeletedFile->DeletionTime)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + pDeletedFile->dwPhysicalFileSize = ROUND_UP(fileSize.u.LowPart, ClusterSize); + + /* Set name */ + wcscpy(pDeletedFile->FileNameW, szFullName); + if (WideCharToMultiByte(CP_ACP, 0, pDeletedFile->FileNameW, -1, pDeletedFile->FileNameA, MAX_PATH, NULL, NULL) == 0) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME); + SetLastError(ERROR_INVALID_NAME); + goto cleanup; + } + pHeader->dwNumberOfEntries++; + + /* Move file */ + if (MoveFileW(szFullName, DeletedFileName)) + hr = S_OK; + else + hr = HRESULT_FROM_WIN32(GetLastError()); cleanup: - if (!ret) - HeapFree(GetProcessHeap(), 0, FullName); - return ret; + if (pHeader) + UnmapViewOfFile(pHeader); + if (hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + CoTaskMemFree(szFullName); + return hr; } -static BOOL -IntSearchRecord( - IN PRECYCLE_BIN bin, - IN HANDLE hDeletedFile, - OUT PDELETED_FILE_RECORD pDeletedFile, - OUT PLARGE_INTEGER Position OPTIONAL) +static HRESULT STDMETHODCALLTYPE +RecycleBin5_RecycleBin5_EmptyRecycleBin( + IN IRecycleBin5 *This) { - INFO2_HEADER Header; - DELETED_FILE_RECORD DeletedFile; - DWORD bytesRead, dwEntries; - BOOL ret = FALSE; + IRecycleBinEnumList *prbel; + IRecycleBinFile *prbf; + HRESULT hr; - if (SetFilePointer(bin->hInfo, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) - goto cleanup; - if (!ReadFile(bin->hInfo, &Header, sizeof(INFO2_HEADER), &bytesRead, NULL)) - goto cleanup; - if (bytesRead != sizeof(INFO2_HEADER) || Header.dwRecordSize == 0) + while (TRUE) { - SetLastError(ERROR_GEN_FAILURE); + hr = IRecycleBin5_EnumObjects(This, &prbel); + if (!SUCCEEDED(hr)) + return hr; + hr = IRecycleBinEnumList_Next(prbel, 1, &prbf, NULL); + IRecycleBinEnumList_Release(prbel); + if (hr == S_FALSE) + return S_OK; + hr = IRecycleBinFile_Delete(prbf); + IRecycleBinFile_Release(prbf); + if (!SUCCEEDED(hr)) + return hr; + } +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5_RecycleBin5_EnumObjects( + IN IRecycleBin5 *This, + OUT IRecycleBinEnumList **ppEnumList) +{ + struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl); + IRecycleBinEnumList *prbel; + HRESULT hr; + IUnknown *pUnk; + + hr = RecycleBin5_Enumerator_Constructor(This, s->hInfo, s->hInfoMapped, s->Folder, &pUnk); + if (!SUCCEEDED(hr)) + return hr; + + hr = IUnknown_QueryInterface(pUnk, &IID_IRecycleBinEnumList, (void **)&prbel); + if (SUCCEEDED(hr)) + { + s->EnumeratorCount++; + *ppEnumList = prbel; + } + IUnknown_Release(pUnk); + return hr; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5_RecycleBin5_Delete( + IN IRecycleBin5 *This, + IN LPCWSTR pDeletedFileName, + IN DELETED_FILE_RECORD *pDeletedFile) +{ + struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl); + ULARGE_INTEGER FileSize; + PINFO2_HEADER pHeader; + DELETED_FILE_RECORD *pRecord, *pLast; + DWORD dwEntries, i; + + if (s->EnumeratorCount != 0) + return E_FAIL; + + pHeader = MapViewOfFile(s->hInfoMapped, FILE_MAP_WRITE, 0, 0, 0); + if (!pHeader) + return HRESULT_FROM_WIN32(GetLastError()); + + FileSize.u.LowPart = GetFileSize(s->hInfo, &FileSize.u.HighPart); + if (FileSize.u.LowPart == 0) + { + UnmapViewOfFile(pHeader); + return HRESULT_FROM_WIN32(GetLastError()); + } + dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD)); + + pRecord = (DELETED_FILE_RECORD *)(pHeader + 1); + for (i = 0; i < dwEntries; i++) + { + if (pRecord->dwRecordUniqueId == pDeletedFile->dwRecordUniqueId) + { + /* Delete file */ + if (!IntDeleteRecursive(pDeletedFileName)) + { + UnmapViewOfFile(pHeader); + return HRESULT_FROM_WIN32(GetLastError()); + } + + /* Clear last entry in the file */ + MoveMemory(pRecord, pRecord + 1, (dwEntries - i - 1) * sizeof(DELETED_FILE_RECORD)); + pLast = pRecord + (dwEntries - i - 1); + ZeroMemory(pLast, sizeof(DELETED_FILE_RECORD)); + UnmapViewOfFile(pHeader); + + /* Resize file */ + CloseHandle(s->hInfoMapped); + SetFilePointer(s->hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END); + SetEndOfFile(s->hInfo); + s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL); + if (!s->hInfoMapped) + return HRESULT_FROM_WIN32(GetLastError()); + return S_OK; + } + pRecord++; + } + UnmapViewOfFile(pHeader); + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5_RecycleBin5_Restore( + IN IRecycleBin5 *This, + IN LPCWSTR pDeletedFileName, + IN DELETED_FILE_RECORD *pDeletedFile) +{ + struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl); + ULARGE_INTEGER FileSize; + PINFO2_HEADER pHeader; + DELETED_FILE_RECORD *pRecord, *pLast; + DWORD dwEntries, i; + + if (s->EnumeratorCount != 0) + return E_FAIL; + + pHeader = MapViewOfFile(s->hInfoMapped, FILE_MAP_WRITE, 0, 0, 0); + if (!pHeader) + return HRESULT_FROM_WIN32(GetLastError()); + + FileSize.u.LowPart = GetFileSize(s->hInfo, &FileSize.u.HighPart); + if (FileSize.u.LowPart == 0) + { + UnmapViewOfFile(pHeader); + return HRESULT_FROM_WIN32(GetLastError()); + } + dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD)); + + pRecord = (DELETED_FILE_RECORD *)(pHeader + 1); + for (i = 0; i < dwEntries; i++) + { + if (pRecord->dwRecordUniqueId == pDeletedFile->dwRecordUniqueId) + { + /* Restore file */ + if (!MoveFileW(pDeletedFileName, pDeletedFile->FileNameW)) + { + UnmapViewOfFile(pHeader); + return HRESULT_FROM_WIN32(GetLastError()); + } + + /* Clear last entry in the file */ + MoveMemory(pRecord, pRecord + 1, (dwEntries - i - 1) * sizeof(DELETED_FILE_RECORD)); + pLast = pRecord + (dwEntries - i - 1); + ZeroMemory(pLast, sizeof(DELETED_FILE_RECORD)); + UnmapViewOfFile(pHeader); + + /* Resize file */ + CloseHandle(s->hInfoMapped); + SetFilePointer(s->hInfo, -(LONG)sizeof(DELETED_FILE_RECORD), NULL, FILE_END); + SetEndOfFile(s->hInfo); + s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL); + if (!s->hInfoMapped) + return HRESULT_FROM_WIN32(GetLastError()); + return S_OK; + } + pRecord++; + } + + UnmapViewOfFile(pHeader); + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5_RecycleBin5_OnClosing( + IN IRecycleBin5 *This, + IN IRecycleBinEnumList *prb5el) +{ + struct RecycleBin5 *s = CONTAINING_RECORD(This, struct RecycleBin5, recycleBinImpl); + s->EnumeratorCount--; + return S_OK; +} + +CONST_VTBL struct IRecycleBin5Vtbl RecycleBin5Vtbl = +{ + RecycleBin5_RecycleBin5_QueryInterface, + RecycleBin5_RecycleBin5_AddRef, + RecycleBin5_RecycleBin5_Release, + RecycleBin5_RecycleBin5_DeleteFile, + RecycleBin5_RecycleBin5_EmptyRecycleBin, + RecycleBin5_RecycleBin5_EnumObjects, + RecycleBin5_RecycleBin5_Delete, + RecycleBin5_RecycleBin5_Restore, + RecycleBin5_RecycleBin5_OnClosing, +}; + +static HRESULT +RecycleBin5_Create( + IN LPCWSTR Folder, + IN PSID OwnerSid OPTIONAL) +{ + LPWSTR BufferName = NULL; + LPWSTR Separator; /* Pointer into BufferName buffer */ + LPWSTR FileName; /* Pointer into BufferName buffer */ + LPCSTR DesktopIniContents = "[.ShellClassInfo]\r\nCLSID={645FF040-5081-101B-9F08-00AA002F954E}\r\n"; + INFO2_HEADER Info2Contents[] = { { 5, 0, 0, 0x320, 0 } }; + SIZE_T BytesToWrite, BytesWritten, Needed; + HANDLE hFile = INVALID_HANDLE_VALUE; + HRESULT hr; + + Needed = (wcslen(Folder) + 1 + max(wcslen(RECYCLE_BIN_FILE_NAME), wcslen(L"desktop.ini")) + 1) * sizeof(WCHAR); + BufferName = HeapAlloc(GetProcessHeap(), 0, Needed); + if (!BufferName) + { + hr = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } - if (Header.dwVersion != 5 || Header.dwRecordSize != sizeof(DELETED_FILE_RECORD)) + wcscpy(BufferName, Folder); + Separator = wcsstr(&BufferName[3], L"\\"); + if (Separator) + *Separator = UNICODE_NULL; + if (!CreateDirectoryW(BufferName, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { - SetLastError(ERROR_GEN_FAILURE); + hr = HRESULT_FROM_WIN32(GetLastError()); goto cleanup; } - - SetLastError(ERROR_SUCCESS); - for (dwEntries = 0; dwEntries < Header.dwNumberOfEntries; dwEntries++) + SetFileAttributesW(BufferName, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); + if (Separator) { - if (Position) + *Separator = L'\\'; + if (!CreateDirectoryW(BufferName, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { - LARGE_INTEGER Zero; - Zero.QuadPart = 0; - if (!SetFilePointerEx(bin->hInfo, Zero, Position, FILE_CURRENT)) - goto cleanup; - } - if (!ReadFile(bin->hInfo, &DeletedFile, Header.dwRecordSize, &bytesRead, NULL)) - goto cleanup; - if (bytesRead != Header.dwRecordSize) - { - SetLastError(ERROR_GEN_FAILURE); - goto cleanup; - } - if (DeletedFile.dwRecordUniqueId == (DWORD)(ULONG_PTR)hDeletedFile) - { - memcpy(pDeletedFile, &DeletedFile, Header.dwRecordSize); - ret = TRUE; + hr = HRESULT_FROM_WIN32(GetLastError()); goto cleanup; } } - /* Entry not found */ - SetLastError(ERROR_INVALID_HANDLE); + if (OwnerSid) + { + //DWORD rc; + + /* Add ACL to allow only user/SYSTEM to open it */ + /* FIXME: rc = SetNamedSecurityInfo( + BufferName, + SE_FILE_OBJECT, + ???, + OwnerSid, + NULL, + ???, + ???); + if (rc != ERROR_SUCCESS) + { + hr = HRESULT_FROM_WIN32(rc); + goto cleanup; + } + */ + } + + wcscat(BufferName, L"\\"); + FileName = &BufferName[wcslen(BufferName)]; + + /* Create desktop.ini */ + wcscpy(FileName, L"desktop.ini"); + hFile = CreateFileW(BufferName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + BytesToWrite = strlen(DesktopIniContents); + if (!WriteFile(hFile, DesktopIniContents, (DWORD)BytesToWrite, &BytesWritten, NULL)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + if (BytesWritten != BytesToWrite) + { + hr = E_FAIL; + goto cleanup; + } + CloseHandle(hFile); + hFile = INVALID_HANDLE_VALUE; + + /* Create empty INFO2 file */ + wcscpy(FileName, RECYCLE_BIN_FILE_NAME); + hFile = CreateFileW(BufferName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + BytesToWrite = sizeof(Info2Contents); + if (!WriteFile(hFile, Info2Contents, (DWORD)BytesToWrite, &BytesWritten, NULL)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + if (BytesWritten == BytesToWrite) + hr = S_OK; + else + hr = E_FAIL; cleanup: - return ret; + HeapFree(GetProcessHeap(), 0, BufferName); + if (hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + return hr; +} + +HRESULT RecycleBin5_Constructor(IN LPCWSTR VolumePath, OUT IUnknown **ppUnknown) +{ + struct RecycleBin5 *s = NULL; + DWORD FileSystemFlags; + LPCWSTR RecycleBinDirectory; + HANDLE tokenHandle = INVALID_HANDLE_VALUE; + PTOKEN_USER TokenUserInfo = NULL; + LPWSTR StringSid = NULL, p; + SIZE_T Needed, DirectoryLength; + INT len; + HRESULT hr; + + if (!ppUnknown) + return E_POINTER; + + /* Get information about file system */ + if (!GetVolumeInformationW( + VolumePath, + NULL, + 0, + NULL, + NULL, + &FileSystemFlags, + NULL, + 0)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + if (!(FileSystemFlags & FILE_PERSISTENT_ACLS)) + RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITHOUT_ACL; + else + { + RecycleBinDirectory = RECYCLE_BIN_DIRECTORY_WITH_ACL; + + /* Get user SID */ + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tokenHandle)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + if (GetTokenInformation(tokenHandle, TokenUser, NULL, 0, &Needed)) + { + hr = E_FAIL; + goto cleanup; + } + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + TokenUserInfo = HeapAlloc(GetProcessHeap(), 0, Needed); + if (!TokenUserInfo) + { + hr = E_OUTOFMEMORY; + goto cleanup; + } + if (!GetTokenInformation(tokenHandle, TokenUser, TokenUserInfo, (DWORD)Needed, &Needed)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + if (!ConvertSidToStringSidW(TokenUserInfo->User.Sid, &StringSid)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + } + + DirectoryLength = wcslen(VolumePath) + wcslen(RecycleBinDirectory) + 1; + if (StringSid) + DirectoryLength += wcslen(StringSid) + 1; + DirectoryLength += 1 + wcslen(RECYCLE_BIN_FILE_NAME); + DirectoryLength += wcslen(VolumePath) + 1; + Needed = (DirectoryLength + 1) * sizeof(WCHAR); + + s = CoTaskMemAlloc(sizeof(struct RecycleBin5) + Needed); + if (!s) + { + hr = E_OUTOFMEMORY; + goto cleanup; + } + ZeroMemory(s, sizeof(struct RecycleBin5)); + s->recycleBinImpl.lpVtbl = &RecycleBin5Vtbl; + s->ref = 1; + if (StringSid) + len = swprintf(s->Folder, L"%s%s\\%s", VolumePath, RecycleBinDirectory, StringSid); + else + len = swprintf(s->Folder, L"%s%s", VolumePath, RecycleBinDirectory); + p = &s->Folder[len]; + wcscpy(p, L"\\" RECYCLE_BIN_FILE_NAME); + s->hInfo = CreateFileW(s->Folder, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (s->hInfo == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_FILE_NOT_FOUND)) + { + *p = UNICODE_NULL; + hr = RecycleBin5_Create(s->Folder, TokenUserInfo ? TokenUserInfo->User.Sid : NULL); + *p = L'\\'; + if (!SUCCEEDED(hr)) + goto cleanup; + s->hInfo = CreateFileW(s->Folder, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + } + if (s->hInfo == INVALID_HANDLE_VALUE) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + s->hInfoMapped = CreateFileMappingW(s->hInfo, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 0, NULL); + if (!s->hInfoMapped) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto cleanup; + } + *p = UNICODE_NULL; + s->VolumePath = p + 1; + wcscpy(s->VolumePath, VolumePath); + + *ppUnknown = (IUnknown *)&s->recycleBinImpl; + + hr = S_OK; + +cleanup: + if (tokenHandle != INVALID_HANDLE_VALUE) + CloseHandle(tokenHandle); + HeapFree(GetProcessHeap(), 0, TokenUserInfo); + if (StringSid) + LocalFree(StringSid); + if (!SUCCEEDED(hr)) + { + if (s) + { + if (s->hInfo && s->hInfo != INVALID_HANDLE_VALUE) + CloseHandle(s->hInfo); + if (s->hInfoMapped) + CloseHandle(s->hInfoMapped); + CoTaskMemFree(s); + } + } + return hr; } diff --git a/reactos/lib/recyclebin/recyclebin_v5.h b/reactos/lib/recyclebin/recyclebin_v5.h index 8222fd5a2d6..6f6b328d0d4 100644 --- a/reactos/lib/recyclebin/recyclebin_v5.h +++ b/reactos/lib/recyclebin/recyclebin_v5.h @@ -5,6 +5,10 @@ #include "recyclebin_private.h" +#ifdef __cplusplus +extern "C" { +#endif + #include /* MS Windows 2000/XP/2003 */ @@ -20,57 +24,86 @@ typedef struct _DELETED_FILE_RECORD #include -static BOOL -CloseHandle5( - IN HANDLE hDeletedFile); +/* COM interface */ -static BOOL -DeleteFile5( - IN PRECYCLE_BIN bin, - IN LPCWSTR FullPath, - IN LPCWSTR FileName); +typedef interface IRecycleBin5 IRecycleBin5; +EXTERN_C const IID IID_IRecycleBin5; -static BOOL -EmptyRecycleBin5( - IN PRECYCLE_BIN* bin); +typedef struct IRecycleBin5Vtbl +{ + /* IRecycleBin interface */ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + IN IRecycleBin5 *This, + IN REFIID riid, + OUT void **ppvObject); -static BOOL -EnumerateFiles5( - IN PRECYCLE_BIN bin, - IN PINT_ENUMERATE_RECYCLEBIN_CALLBACK pFnCallback, - IN PVOID Context OPTIONAL); + ULONG (STDMETHODCALLTYPE *AddRef)( + IN IRecycleBin5 *This); -static BOOL -GetDetails5( - IN PRECYCLE_BIN bin, - IN HANDLE hDeletedFile, - IN DWORD BufferSize, - IN OUT PDELETED_FILE_DETAILS_W FileDetails OPTIONAL, - OUT LPDWORD RequiredSize OPTIONAL); + ULONG (STDMETHODCALLTYPE *Release)( + IN IRecycleBin5 *This); -static BOOL -RestoreFile5( - IN PRECYCLE_BIN bin, - IN HANDLE hDeletedFile); + HRESULT (STDMETHODCALLTYPE *DeleteFile)( + IN IRecycleBin5 *This, + IN LPCWSTR szFileName); -static BOOL -IntDeleteRecursive( - IN LPCWSTR FullName); + HRESULT (STDMETHODCALLTYPE *EmptyRecycleBin)( + IN IRecycleBin5 *This); -static BOOL -IntEmptyRecycleBinCallback( - IN PVOID Context, - IN HANDLE hDeletedFile); + HRESULT (STDMETHODCALLTYPE *EnumObjects)( + IN IRecycleBin5 *This, + OUT IRecycleBinEnumList **ppEnumList); -static BOOL -IntGetFullName( - IN PRECYCLE_BIN bin, - IN PDELETED_FILE_RECORD pDeletedFile, - OUT LPWSTR* pFullName); + /* IRecycleBin5 interface */ + HRESULT (STDMETHODCALLTYPE *Delete)( + IN IRecycleBin5 *This, + IN LPCWSTR pDeletedFileName, + IN DELETED_FILE_RECORD *pDeletedFile); -static BOOL -IntSearchRecord( - IN PRECYCLE_BIN bin, - IN HANDLE hDeletedFile, - OUT PDELETED_FILE_RECORD DeletedFile, - OUT PLARGE_INTEGER Position OPTIONAL); + HRESULT (STDMETHODCALLTYPE *Restore)( + IN IRecycleBin5 *This, + IN LPCWSTR pDeletedFileName, + IN DELETED_FILE_RECORD *pDeletedFile); + + HRESULT (STDMETHODCALLTYPE *OnClosing)( + IN IRecycleBin5 *This, + IN IRecycleBinEnumList *prb5el); +} IRecycleBin5Vtbl; + +interface IRecycleBin5 +{ + CONST_VTBL struct IRecycleBin5Vtbl *lpVtbl; +}; + +#ifdef COBJMACROS +#define IRecycleBin5_QueryInterface(This, riid, ppvObject) \ + (This)->lpVtbl->QueryInterface(This, riid, ppvObject) +#define IRecycleBin5_AddRef(This) \ + (This)->lpVtbl->AddRef(This) +#define IRecycleBin5_Release(This) \ + (This)->lpVtbl->Release(This) +#define IRecycleBin5_DeleteFile(This, szFileName) \ + (This)->lpVtbl->DeleteFile(This, szFileName) +#define IRecycleBin5_EmptyRecycleBin(This) \ + (This)->lpVtbl->EmptyRecycleBin(This) +#define IRecycleBin5_EnumObjects(This, ppEnumList) \ + (This)->lpVtbl->EnumObjects(This, ppEnumList) +#define IRecycleBin5_Delete(This, pDeletedFileName, pDeletedFile) \ + (This)->lpVtbl->Delete(This, pDeletedFileName, pDeletedFile) +#define IRecycleBin5_Restore(This, pDeletedFileName, pDeletedFile) \ + (This)->lpVtbl->Restore(This, pDeletedFileName, pDeletedFile) +#define IRecycleBin5_OnClosing(This, prb5el) \ + (This)->lpVtbl->OnClosing(This, prb5el) +#endif + +HRESULT +RecycleBin5_Enumerator_Constructor( + IN IRecycleBin5 *prb, + IN HANDLE hInfo, + IN HANDLE hInfoMapped, + IN LPCWSTR szPrefix, + OUT IUnknown **ppUnknown); + +#ifdef __cplusplus +} +#endif diff --git a/reactos/lib/recyclebin/recyclebin_v5_enumerator.c b/reactos/lib/recyclebin/recyclebin_v5_enumerator.c new file mode 100644 index 00000000000..e3f1abec99f --- /dev/null +++ b/reactos/lib/recyclebin/recyclebin_v5_enumerator.c @@ -0,0 +1,450 @@ +/* + * PROJECT: Recycle bin management + * LICENSE: GPL v2 - See COPYING in the top level directory + * FILE: lib/recyclebin/recyclebin_v5_enumerator.c + * PURPOSE: Enumerates contents of a MS Windows 2000/XP/2003 recyclebin + * PROGRAMMERS: Copyright 2006-2007 Hervé Poussineau (hpoussin@reactos.org) + */ + +#define COBJMACROS +#include "recyclebin_v5.h" + +struct RecycleBin5File +{ + ULONG ref; + IRecycleBin5 *recycleBin; + DELETED_FILE_RECORD deletedFile; + IRecycleBinFile recycleBinFileImpl; + WCHAR FullName[ANY_SIZE]; +}; + +static HRESULT STDMETHODCALLTYPE +RecycleBin5File_RecycleBinFile_QueryInterface( + IN IRecycleBinFile *This, + IN REFIID riid, + OUT void **ppvObject) +{ + struct RecycleBin5File *s = CONTAINING_RECORD(This, struct RecycleBin5File, recycleBinFileImpl); + + if (!ppvObject) + return E_POINTER; + + if (IsEqualIID(riid, &IID_IUnknown)) + *ppvObject = &s->recycleBinFileImpl; + else if (IsEqualIID(riid, &IID_IRecycleBinFile)) + *ppvObject = &s->recycleBinFileImpl; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef(This); + return S_OK; +} + +static ULONG STDMETHODCALLTYPE +RecycleBin5File_RecycleBinFile_AddRef( + IN IRecycleBinFile *This) +{ + struct RecycleBin5File *s = CONTAINING_RECORD(This, struct RecycleBin5File, recycleBinFileImpl); + ULONG refCount = InterlockedIncrement((PLONG)&s->ref); + return refCount; +} + +static ULONG STDMETHODCALLTYPE +RecycleBin5File_RecycleBinFile_Release( + IN IRecycleBinFile *This) +{ + struct RecycleBin5File *s = CONTAINING_RECORD(This, struct RecycleBin5File, recycleBinFileImpl); + ULONG refCount; + + if (!This) + return E_POINTER; + + refCount = InterlockedDecrement((PLONG)&s->ref); + + if (refCount == 0) + { + IRecycleBin5_Release(s->recycleBin); + CoTaskMemFree(s); + } + + return refCount; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5File_RecycleBinFile_GetLastModificationTime( + IN IRecycleBinFile *This, + OUT FILETIME *pLastModificationTime) +{ + struct RecycleBin5File *s = CONTAINING_RECORD(This, struct RecycleBin5File, recycleBinFileImpl); + HRESULT hr; + DWORD dwAttributes; + HANDLE hFile; + + dwAttributes = GetFileAttributesW(s->FullName); + if (dwAttributes == INVALID_FILE_ATTRIBUTES) + return HRESULT_FROM_WIN32(GetLastError()); + if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) + hFile = CreateFileW(s->FullName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + else + hFile = CreateFileW(s->FullName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return HRESULT_FROM_WIN32(GetLastError()); + + if (GetFileTime(hFile, NULL, NULL, pLastModificationTime)) + hr = S_OK; + else + hr = HRESULT_FROM_WIN32(GetLastError()); + CloseHandle(hFile); + return hr; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5File_RecycleBinFile_GetDeletionTime( + IN IRecycleBinFile *This, + OUT FILETIME *pDeletionTime) +{ + struct RecycleBin5File *s = CONTAINING_RECORD(This, struct RecycleBin5File, recycleBinFileImpl); + *pDeletionTime = s->deletedFile.DeletionTime; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5File_RecycleBinFile_GetFileSize( + IN IRecycleBinFile *This, + OUT ULARGE_INTEGER *pFileSize) +{ + struct RecycleBin5File *s = CONTAINING_RECORD(This, struct RecycleBin5File, recycleBinFileImpl); + HRESULT hr; + DWORD dwAttributes; + HANDLE hFile; + + dwAttributes = GetFileAttributesW(s->FullName); + if (dwAttributes == INVALID_FILE_ATTRIBUTES) + return HRESULT_FROM_WIN32(GetLastError()); + if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + pFileSize->QuadPart = 0; + return S_OK; + } + + hFile = CreateFileW(s->FullName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return HRESULT_FROM_WIN32(GetLastError()); + pFileSize->u.LowPart = GetFileSize(hFile, &pFileSize->u.HighPart); + if (pFileSize->u.LowPart != INVALID_FILE_SIZE) + hr = S_OK; + else + hr = HRESULT_FROM_WIN32(GetLastError()); + CloseHandle(hFile); + return hr; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5File_RecycleBinFile_GetPhysicalFileSize( + IN IRecycleBinFile *This, + OUT ULARGE_INTEGER *pPhysicalFileSize) +{ + struct RecycleBin5File *s = CONTAINING_RECORD(This, struct RecycleBin5File, recycleBinFileImpl); + pPhysicalFileSize->u.HighPart = 0; + pPhysicalFileSize->u.LowPart = s->deletedFile.dwPhysicalFileSize; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5File_RecycleBinFile_GetAttributes( + IN IRecycleBinFile *This, + OUT DWORD *pAttributes) +{ + struct RecycleBin5File *s = CONTAINING_RECORD(This, struct RecycleBin5File, recycleBinFileImpl); + DWORD dwAttributes; + + dwAttributes = GetFileAttributesW(s->FullName); + if (dwAttributes == INVALID_FILE_ATTRIBUTES) + return HRESULT_FROM_WIN32(GetLastError()); + + *pAttributes = dwAttributes; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5File_RecycleBinFile_GetFileName( + IN IRecycleBinFile *This, + IN DWORD BufferSize, + IN OUT LPWSTR Buffer, + OUT DWORD *RequiredSize) +{ + struct RecycleBin5File *s = CONTAINING_RECORD(This, struct RecycleBin5File, recycleBinFileImpl); + DWORD dwRequired; + + dwRequired = (DWORD)(wcslen(s->deletedFile.FileNameW) + 1) * sizeof(WCHAR); + if (RequiredSize) + *RequiredSize = dwRequired; + + if (BufferSize == 0 && !Buffer) + return S_OK; + + if (BufferSize < dwRequired) + return E_OUTOFMEMORY; + CopyMemory(Buffer, s->deletedFile.FileNameW, dwRequired); + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5File_RecycleBinFile_Delete( + IN IRecycleBinFile *This) +{ + struct RecycleBin5File *s = CONTAINING_RECORD(This, struct RecycleBin5File, recycleBinFileImpl); + return IRecycleBin5_Delete(s->recycleBin, s->FullName, &s->deletedFile); +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5File_RecycleBinFile_Restore( + IN IRecycleBinFile *This) +{ + struct RecycleBin5File *s = CONTAINING_RECORD(This, struct RecycleBin5File, recycleBinFileImpl); + return IRecycleBin5_Restore(s->recycleBin, s->FullName, &s->deletedFile); +} + +CONST_VTBL struct IRecycleBinFileVtbl RecycleBin5FileVtbl = +{ + RecycleBin5File_RecycleBinFile_QueryInterface, + RecycleBin5File_RecycleBinFile_AddRef, + RecycleBin5File_RecycleBinFile_Release, + RecycleBin5File_RecycleBinFile_GetLastModificationTime, + RecycleBin5File_RecycleBinFile_GetDeletionTime, + RecycleBin5File_RecycleBinFile_GetFileSize, + RecycleBin5File_RecycleBinFile_GetPhysicalFileSize, + RecycleBin5File_RecycleBinFile_GetAttributes, + RecycleBin5File_RecycleBinFile_GetFileName, + RecycleBin5File_RecycleBinFile_Delete, + RecycleBin5File_RecycleBinFile_Restore, +}; + +static HRESULT +RecycleBin5_File_Constructor( + IN IRecycleBin5 *prb, + IN LPCWSTR Folder, + IN PDELETED_FILE_RECORD pDeletedFile, + OUT IRecycleBinFile **ppFile) +{ + struct RecycleBin5File *s = NULL; + LPCWSTR Extension; + SIZE_T Needed; + + if (!ppFile) + return E_POINTER; + + Extension = wcsrchr(pDeletedFile->FileNameW, '.'); + if (Extension < wcsrchr(pDeletedFile->FileNameW, '\\')) + Extension = NULL; + Needed = wcslen(Folder) + 13; + if (Extension) + Needed += wcslen(Extension); + Needed *= sizeof(WCHAR); + + s = CoTaskMemAlloc(sizeof(struct RecycleBin5File) + Needed); + if (!s) + return E_OUTOFMEMORY; + ZeroMemory(s, sizeof(struct RecycleBin5File) + Needed); + s->recycleBinFileImpl.lpVtbl = &RecycleBin5FileVtbl; + s->ref = 1; + s->deletedFile = *pDeletedFile; + s->recycleBin = prb; + IRecycleBin5_AddRef(s->recycleBin); + *ppFile = &s->recycleBinFileImpl; + wsprintfW(s->FullName, L"%s\\D%c%lu%s", Folder, pDeletedFile->dwDriveNumber + 'a', pDeletedFile->dwRecordUniqueId, Extension); + + return S_OK; +} + +struct RecycleBin5Enum +{ + ULONG ref; + IRecycleBin5 *recycleBin; + HANDLE hInfo; + INFO2_HEADER *pInfo; + DWORD dwCurrent; + IRecycleBinEnumList recycleBinEnumImpl; + WCHAR szPrefix[ANY_SIZE]; +}; + +static HRESULT STDMETHODCALLTYPE +RecycleBin5Enum_RecycleBinEnumList_QueryInterface( + IN IRecycleBinEnumList *This, + IN REFIID riid, + OUT void **ppvObject) +{ + struct RecycleBin5Enum *s = CONTAINING_RECORD(This, struct RecycleBin5Enum, recycleBinEnumImpl); + + if (!ppvObject) + return E_POINTER; + + if (IsEqualIID(riid, &IID_IUnknown)) + *ppvObject = &s->recycleBinEnumImpl; + else if (IsEqualIID(riid, &IID_IRecycleBinEnumList)) + *ppvObject = &s->recycleBinEnumImpl; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef(This); + return S_OK; +} + +static ULONG STDMETHODCALLTYPE +RecycleBin5Enum_RecycleBinEnumList_AddRef( + IN IRecycleBinEnumList *This) +{ + struct RecycleBin5Enum *s = CONTAINING_RECORD(This, struct RecycleBin5Enum, recycleBinEnumImpl); + ULONG refCount = InterlockedIncrement((PLONG)&s->ref); + return refCount; +} + +static ULONG STDMETHODCALLTYPE +RecycleBin5Enum_RecycleBinEnumList_Release( + IN IRecycleBinEnumList *This) +{ + struct RecycleBin5Enum *s = CONTAINING_RECORD(This, struct RecycleBin5Enum, recycleBinEnumImpl); + ULONG refCount; + + if (!This) + return E_POINTER; + + refCount = InterlockedDecrement((PLONG)&s->ref); + + if (refCount == 0) + { + IRecycleBin5_OnClosing(s->recycleBin, This); + UnmapViewOfFile(s->pInfo); + IRecycleBin5_Release(s->recycleBin); + CoTaskMemFree(s); + } + + return refCount; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5Enum_RecycleBinEnumList_Next( + IRecycleBinEnumList *This, + IN DWORD celt, + IN OUT IRecycleBinFile **rgelt, + OUT DWORD *pceltFetched) +{ + struct RecycleBin5Enum *s = CONTAINING_RECORD(This, struct RecycleBin5Enum, recycleBinEnumImpl); + ULARGE_INTEGER FileSize; + INFO2_HEADER *pHeader = s->pInfo; + DELETED_FILE_RECORD *pDeletedFile; + DWORD fetched = 0, i; + DWORD dwEntries; + HRESULT hr; + + if (!rgelt) + return E_POINTER; + if (!pceltFetched && celt > 1) + return E_INVALIDARG; + + FileSize.u.LowPart = GetFileSize(s->hInfo, &FileSize.u.HighPart); + if (FileSize.u.LowPart == 0) + return HRESULT_FROM_WIN32(GetLastError()); + dwEntries = (DWORD)((FileSize.QuadPart - sizeof(INFO2_HEADER)) / sizeof(DELETED_FILE_RECORD)); + + i = s->dwCurrent; + pDeletedFile = (DELETED_FILE_RECORD *)(pHeader + 1) + i; + for (; i < dwEntries && fetched < celt; i++) + { + hr = RecycleBin5_File_Constructor(s->recycleBin, s->szPrefix, pDeletedFile, &rgelt[fetched]); + if (!SUCCEEDED(hr)) + { + for (i = 0; i < fetched; i++) + IRecycleBinFile_Release(rgelt[i]); + return hr; + } + pDeletedFile++; + fetched++; + } + + s->dwCurrent = i; + if (pceltFetched) + *pceltFetched = fetched; + if (fetched == celt) + return S_OK; + else + return S_FALSE; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5Enum_RecycleBinEnumList_Skip( + IN IRecycleBinEnumList *This, + IN DWORD celt) +{ + struct RecycleBin5Enum *s = CONTAINING_RECORD(This, struct RecycleBin5Enum, recycleBinEnumImpl); + s->dwCurrent += celt; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +RecycleBin5Enum_RecycleBinEnumList_Reset( + IN IRecycleBinEnumList *This) +{ + struct RecycleBin5Enum *s = CONTAINING_RECORD(This, struct RecycleBin5Enum, recycleBinEnumImpl); + s->dwCurrent = 0; + return S_OK; +} + +CONST_VTBL struct IRecycleBinEnumListVtbl RecycleBin5EnumVtbl = +{ + RecycleBin5Enum_RecycleBinEnumList_QueryInterface, + RecycleBin5Enum_RecycleBinEnumList_AddRef, + RecycleBin5Enum_RecycleBinEnumList_Release, + RecycleBin5Enum_RecycleBinEnumList_Next, + RecycleBin5Enum_RecycleBinEnumList_Skip, + RecycleBin5Enum_RecycleBinEnumList_Reset, +}; + +HRESULT +RecycleBin5_Enumerator_Constructor( + IN IRecycleBin5 *prb, + IN HANDLE hInfo, + IN HANDLE hInfoMapped, + IN LPCWSTR szPrefix, + OUT IUnknown **ppUnknown) +{ + struct RecycleBin5Enum *s = NULL; + SIZE_T Needed; + + if (!ppUnknown) + return E_POINTER; + + Needed = (wcslen(szPrefix) + 1) * sizeof(WCHAR); + + s = CoTaskMemAlloc(sizeof(struct RecycleBin5Enum) + Needed); + if (!s) + return E_OUTOFMEMORY; + ZeroMemory(s, sizeof(struct RecycleBin5Enum) + Needed); + s->recycleBinEnumImpl.lpVtbl = &RecycleBin5EnumVtbl; + s->ref = 1; + s->recycleBin = prb; + wcscpy(s->szPrefix, szPrefix); + s->hInfo = hInfo; + s->pInfo = MapViewOfFile(hInfoMapped, FILE_MAP_READ, 0, 0, 0); + if (!s->pInfo) + { + CoTaskMemFree(s); + return HRESULT_FROM_WIN32(GetLastError()); + } + if (s->pInfo->dwVersion != 5 || s->pInfo->dwRecordSize != sizeof(DELETED_FILE_RECORD)) + { + UnmapViewOfFile(s->pInfo); + CoTaskMemFree(s); + return E_FAIL; + } + IRecycleBin5_AddRef(s->recycleBin); + *ppUnknown = (IUnknown *)&s->recycleBinEnumImpl; + + return S_OK; +} diff --git a/reactos/lib/recyclebin/refcount.c b/reactos/lib/recyclebin/refcount.c deleted file mode 100644 index 24e83b8bfae..00000000000 --- a/reactos/lib/recyclebin/refcount.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * PROJECT: Recycle bin management - * LICENSE: GPL v2 - See COPYING in the top level directory - * FILE: lib/recyclebin/openclose.c - * PURPOSE: Do reference counting on objects - * PROGRAMMERS: Copyright 2006 Hervé Poussineau (hpoussin@reactos.org) - */ - -#include "recyclebin_private.h" - -BOOL -InitializeHandle( - IN PREFCOUNT_DATA pData, - IN PDESTROY_DATA pFnClose OPTIONAL) -{ - pData->ReferenceCount = 1; - pData->Close = pFnClose; - return TRUE; -} - -BOOL -ReferenceHandle( - IN PREFCOUNT_DATA pData) -{ - pData->ReferenceCount++; - return TRUE; -} - -BOOL -DereferenceHandle( - IN PREFCOUNT_DATA pData) -{ - pData->ReferenceCount--; - if (pData->ReferenceCount == 0) - { - if (pData->Close) - return pData->Close(pData); - else - return TRUE; - } - return TRUE; -}