3 * dumprecbin - dumps a recycle bin database
5 * Copyright (c) 2005 by Thomas Weidenmueller <w3seek@reactos.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * TODO: - Support for Vista recycle bins (read the DeleteInfo NTFS streams, also NT 5.x)
22 * - Support for INFO databases (win95)
33 #define NT_SUCCESS(status) ((LONG)(status) >= 0)
36 typedef struct _RECYCLE_BIN
38 struct _RECYCLE_BIN
*Next
;
41 WCHAR Path
[MAX_PATH
+ 1];
42 } RECYCLE_BIN
, *PRECYCLE_BIN
;
48 } INFO_VERSION
, *PINFO_VERSION
;
50 typedef struct _INFO2_HEADER
56 } INFO2_HEADER
, *PINFO2_HEADER
;
58 typedef struct _INFO2_RECORD
61 CHAR AnsiFileName
[MAX_PATH
];
64 FILETIME DeletionTime
;
65 DWORD DeletedPhysicalSize
;
66 WCHAR FileName
[MAX_PATH
- 2];
67 } INFO2_RECORD
, *PINFO2_RECORD
;
70 OpenAndMapInfoDatabase(IN LPTSTR szFileName
,
71 OUT PVOID
*MappingBasePtr
,
72 OUT PLARGE_INTEGER FileSize
)
74 HANDLE hFile
, hMapping
= INVALID_HANDLE_VALUE
;
76 hFile
= CreateFile(szFileName
,
81 FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
,
83 if (hFile
!= INVALID_HANDLE_VALUE
)
85 if (GetFileSizeEx(hFile
,
87 FileSize
->QuadPart
>= 0xF)
89 hMapping
= CreateFileMapping(hFile
,
95 if (hMapping
== NULL
||
96 !(*MappingBasePtr
= MapViewOfFile(hMapping
,
102 if (hMapping
!= NULL
)
104 CloseHandle(hMapping
);
106 hMapping
= INVALID_HANDLE_VALUE
;
116 UnmapAndCloseDatabase(IN HANDLE hMapping
,
117 IN PVOID MappingBasePtr
)
119 UnmapViewOfFile(MappingBasePtr
);
120 CloseHandle(hMapping
);
124 DetectDatabaseVersion(PVOID Header
)
126 PINFO2_HEADER Info2
= (PINFO2_HEADER
)Header
;
127 INFO_VERSION Version
= ivUnknown
;
129 if (Info2
->Version
== 5 &&
132 Info2
->RecordSize
== 0x320)
141 IsValidRecycleBin(IN LPTSTR szPath
)
143 TCHAR szFile
[MAX_PATH
+ 1];
145 INFO_VERSION DbVersion
= ivUnknown
;
148 _T("%s\\desktop.ini"),
151 /* check if directory contains a valid desktop.ini for the recycle bin */
152 if (GetPrivateProfileString(TEXT(".ShellClassInfo"),
156 sizeof(szClsId
) / sizeof(szClsId
[0]),
158 !_tcsicmp(_T("{645FF040-5081-101B-9F08-00AA002F954E}"),
162 LARGE_INTEGER FileSize
;
163 PVOID pDbBase
= NULL
;
165 /* open the database and check the signature */
169 hDb
= OpenAndMapInfoDatabase(szFile
,
172 if (hDb
!= INVALID_HANDLE_VALUE
)
174 DbVersion
= DetectDatabaseVersion(pDbBase
);
175 UnmapAndCloseDatabase(hDb
,
180 return DbVersion
!= ivUnknown
;
184 OpenLocalLSAPolicyHandle(IN ACCESS_MASK DesiredAccess
,
185 OUT PLSA_HANDLE PolicyHandle
)
187 LSA_OBJECT_ATTRIBUTES LsaObjectAttributes
= {0};
190 Status
= LsaOpenPolicy(NULL
,
191 &LsaObjectAttributes
,
194 if (!NT_SUCCESS(Status
))
196 SetLastError(LsaNtStatusToWinError(Status
));
204 ConvertSIDToAccountName(IN PSID Sid
,
207 DWORD AccountNameLen
= 0;
208 DWORD DomainNameLen
= 0;
209 SID_NAME_USE NameUse
;
210 DWORD Error
= ERROR_SUCCESS
;
211 LPWSTR AccountName
, DomainName
;
214 if (!LookupAccountSidW(NULL
,
222 Error
= GetLastError();
223 if (Error
== ERROR_NONE_MAPPED
||
224 Error
!= ERROR_INSUFFICIENT_BUFFER
)
226 /* some unexpected error occured! */
231 AccountName
= (LPWSTR
)HeapAlloc(GetProcessHeap(),
233 (AccountNameLen
+ DomainNameLen
) * sizeof(WCHAR
));
234 if (AccountName
!= NULL
)
236 LSA_HANDLE PolicyHandle
;
237 DomainName
= AccountName
+ AccountNameLen
;
239 if (!LookupAccountSidW(NULL
,
247 goto BailFreeAccountName
;
254 if (OpenLocalLSAPolicyHandle(POLICY_LOOKUP_NAMES
| POLICY_VIEW_LOCAL_INFORMATION
,
257 PLSA_REFERENCED_DOMAIN_LIST ReferencedDomain
;
258 PLSA_TRANSLATED_NAME Names
;
259 PLSA_TRUST_INFORMATION Domain
;
260 PLSA_UNICODE_STRING DomainName
;
261 PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo
= NULL
;
264 Status
= LsaLookupSids(PolicyHandle
,
269 if (NT_SUCCESS(Status
))
271 if (ReferencedDomain
!= NULL
&&
272 Names
->DomainIndex
>= 0)
274 Domain
= &ReferencedDomain
->Domains
[Names
->DomainIndex
];
275 DomainName
= &Domain
->Name
;
288 /* query the domain name for BUILTIN accounts */
289 Status
= LsaQueryInformationPolicy(PolicyHandle
,
290 PolicyAccountDomainInformation
,
291 (PVOID
*)&PolicyAccountDomainInfo
);
292 if (NT_SUCCESS(Status
))
294 DomainName
= &PolicyAccountDomainInfo
->DomainName
;
305 /* NOTE: LSA_UNICODE_STRINGs are not always NULL-terminated! */
311 s
= User
+ wcslen(User
);
315 s
+= DomainName
->Length
/ sizeof(WCHAR
);
320 s
+= Names
->Name
.Length
/ sizeof(WCHAR
);
327 case SidTypeWellKnownGroup
:
335 _T("Unhandled SID type: 0x%x\n"),
341 if (PolicyAccountDomainInfo
!= NULL
)
343 LsaFreeMemory(PolicyAccountDomainInfo
);
346 LsaFreeMemory(ReferencedDomain
);
347 LsaFreeMemory(Names
);
350 LsaClose(PolicyHandle
);
352 if (!NT_SUCCESS(Status
))
355 goto BailFreeAccountName
;
361 HeapFree(GetProcessHeap(),
372 Ret
= ConvertSidToStringSidW(Sid
,
378 LocalFree((HLOCAL
)StrSid
);
386 FreeRecycleBinsList(IN OUT PRECYCLE_BIN
*RecycleBinsListHead
)
388 PRECYCLE_BIN CurrentBin
, NextBin
;
390 CurrentBin
= *RecycleBinsListHead
;
391 while (CurrentBin
!= NULL
)
393 NextBin
= CurrentBin
->Next
;
394 LocalFree((HLOCAL
)CurrentBin
->Sid
);
395 HeapFree(GetProcessHeap(),
398 CurrentBin
= NextBin
;
401 *RecycleBinsListHead
= NULL
;
405 LocateRecycleBins(IN LPWSTR szDrive
,
406 OUT PRECYCLE_BIN
*RecycleBinsListHead
)
408 TCHAR szRecBinPath
[MAX_PATH
+ 1];
410 WIN32_FIND_DATA FindData
;
414 FreeRecycleBinsList(RecycleBinsListHead
);
417 * search for recycle bins on volumes that support file security (NTFS)
419 _stprintf(szRecBinPath
,
420 _T("%lS\\RECYCLER\\*"),
422 FindResult
= FindFirstFile(szRecBinPath
,
424 if (FindResult
!= INVALID_HANDLE_VALUE
)
428 if (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
&&
429 _tcscmp(FindData
.cFileName
,
431 _tcscmp(FindData
.cFileName
,
436 if (ConvertStringSidToSid(FindData
.cFileName
,
439 _stprintf(szRecBinPath
,
440 _T("%s\\RECYCLER\\%s"),
443 if (IsValidRecycleBin(szRecBinPath
))
445 NewBin
= (PRECYCLE_BIN
)HeapAlloc(GetProcessHeap(),
447 sizeof(RECYCLE_BIN
));
450 _tcscpy(NewBin
->Path
,
453 /* convert the SID to an account name */
454 ConvertSIDToAccountName(Sid
,
457 /* append the recycle bin */
458 *RecycleBinsListHead
= NewBin
;
459 RecycleBinsListHead
= &NewBin
->Next
;
464 goto ContinueFreeSid
;
469 LocalFree((HLOCAL
)Sid
);
473 } while (FindNextFile(FindResult
,
476 FindClose(FindResult
);
480 * search for recycle bins on volumes that don't support file security (FAT)
482 _stprintf(szRecBinPath
,
485 FindResult
= FindFirstFile(szRecBinPath
,
487 if (FindResult
!= INVALID_HANDLE_VALUE
)
489 if (IsValidRecycleBin(szRecBinPath
))
491 SID_IDENTIFIER_AUTHORITY WorldSia
= {SECURITY_WORLD_SID_AUTHORITY
};
494 /* create an Everyone SID */
495 if (AllocateAndInitializeSid(&WorldSia
,
507 NewBin
= (PRECYCLE_BIN
)HeapAlloc(GetProcessHeap(),
509 sizeof(RECYCLE_BIN
));
512 _tcscpy(NewBin
->Path
,
515 /* convert the SID to an account name */
516 ConvertSIDToAccountName(EveryoneSid
,
519 /* append the recycle bin */
520 *RecycleBinsListHead
= NewBin
;
521 RecycleBinsListHead
= &NewBin
->Next
;
526 FreeSid(EveryoneSid
);
529 FindClose(FindResult
);
536 DiskFileNameFromRecord(OUT LPTSTR szShortFileName
,
537 IN DWORD RecordNumber
,
538 IN WCHAR cDriveLetter
,
539 IN LPWSTR szFileName
)
543 FileExt
= wcsrchr(szFileName
,
547 _stprintf(szShortFileName
,
555 _stprintf(szShortFileName
,
563 DumpRecycleBin(IN PRECYCLE_BIN RecycleBin
)
565 WCHAR szFile
[MAX_PATH
+ 1];
567 LARGE_INTEGER FileSize
;
568 PVOID pDbBase
= NULL
;
569 INFO_VERSION Version
= ivUnknown
;
571 _tprintf(_T("Dumping recycle bin of \"%lS\":\n"),
573 _tprintf(_T("Directory: %lS\n\n"),
579 hDb
= OpenAndMapInfoDatabase(szFile
,
582 if (hDb
!= INVALID_HANDLE_VALUE
)
584 Version
= DetectDatabaseVersion(pDbBase
);
586 /* dump the INFO2 database */
592 PINFO2_HEADER Info2Header
= (PINFO2_HEADER
)pDbBase
;
593 PINFO2_RECORD Info2
= (PINFO2_RECORD
)(Info2Header
+ 1);
596 nRecords
= (FileSize
.QuadPart
- sizeof(INFO2_HEADER
)) / Info2Header
->RecordSize
;
598 while (nRecords
!= 0)
600 /* if the first character of the AnsiFileName is zero, the record
601 is considered deleted */
602 if (Info2
->AnsiFileName
[0] != '\0')
604 _tprintf(_T(" [%d] Record: #%d \"%lS\"\n"),
609 DiskFileNameFromRecord(szFile
,
611 (WCHAR
)Info2
->DriveLetter
+ L
'a',
613 _tprintf(_T(" Name on disk: \"%s\"\n"),
615 _tprintf(_T(" Deleted size on disk: %d KB\n"),
616 Info2
->DeletedPhysicalSize
/ 1024);
629 UnmapAndCloseDatabase(hDb
,
637 SelectRecycleBin(IN LPWSTR szDrive
)
640 PRECYCLE_BIN RecycleBinsList
= NULL
;
642 Ret
= LocateRecycleBins(szDrive
,
646 if (RecycleBinsList
->Next
!= NULL
)
648 PRECYCLE_BIN CurrentBin
= RecycleBinsList
;
651 /* if there are multiple recycle bins ask the user which one to dump */
652 _tprintf(_T("There are several recycle bins on this drive. Select one:\n"));
654 while (CurrentBin
!= NULL
)
656 _tprintf(_T(" [%d] %lS\n"),
659 CurrentBin
= CurrentBin
->Next
;
663 _tprintf(_T("Enter the number: "));
669 _tprintf(_T("Please enter a number between 1 and %d: "),
674 /* walk to the selected recycle bin */
675 CurrentBin
= RecycleBinsList
;
676 while (CurrentBin
!= NULL
&& i
!= 1)
678 CurrentBin
= CurrentBin
->Next
;
683 Ret
= DumpRecycleBin(CurrentBin
);
687 /* dump the first (and only) recycle bin */
688 Ret
= DumpRecycleBin(RecycleBinsList
);
694 _T("No recycle bins on this volume!\n"));
697 FreeRecycleBinsList(&RecycleBinsList
);
706 _T("Usage: dumprecbin C:\n"));
714 strlen(argv
[1]) != 2 || argv
[1][1] != ':' ||
715 toupper(argv
[1][0]) < 'A' || toupper(argv
[1][0]) > 'Z')
727 if (!SelectRecycleBin(szDrive
))