Last active
April 16, 2023 04:07
-
-
Save juntalis/79b889793c3da8db3615 to your computer and use it in GitHub Desktop.
Find hard links for a given filepath
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file findlinks.c | |
* @brief Find links | |
*/ | |
#include "pch.h" | |
#include "ntfsdefs.h" | |
typedef struct _FILENAME_PART { | |
LPWSTR Data; | |
UCHAR DataLength; | |
BOOLEAN IsVolumeRoot; | |
struct _FILENAME_PART *Next; | |
struct _FILENAME_PART *Prev; | |
} FILENAME_PART; | |
typedef struct { | |
HANDLE hVolume; | |
FILENAME_PART* Tail; | |
} ReadFileNamePartsData; | |
typedef struct { | |
HANDLE hVolume; | |
USHORT LinkCount; | |
USHORT CurrentLink; | |
LPWSTR* LinkData; | |
} ReadHardLinksData; | |
typedef BOOL(*PROCESSFILERECORD)(IN PFILE_RECORD_HEADER lpFileRecordHeader, IN OPTIONAL LPVOID lpUserData); | |
typedef BOOL(*ENUMATTRIBUTEPROC)(IN PATTRIBUTE lpAttr, IN OPTIONAL LPVOID lpUserData, IN OUT LPBOOL lpbContinue); | |
BOOL ReadFileNamePartsProcessFileRecord(IN PFILE_RECORD_HEADER lpFileRecordHeader, IN OPTIONAL LPVOID lpUserData); | |
static const wchar_t VolumePathTemplate[] = L"\\\\.\\A:"; | |
#define VolumePathTemplateLength WLEN(VolumePathTemplate) | |
FILENAME_PART* AllocateNextPart(FILENAME_PART* lpCurrent) | |
{ | |
FILENAME_PART* lpNext = NULL; | |
if(lpNext = (FILENAME_PART*)malloc(sizeof(FILENAME_PART))) { | |
memset((void*)lpNext, 0, sizeof(FILENAME_PART)); | |
lpNext->Prev = lpCurrent; | |
} | |
return (lpCurrent->Next = lpNext); | |
} | |
BOOL CopyFileNamePart(IN OUT FILENAME_PART* lpPart, IN PFILENAME_ATTRIBUTE lpFileName) | |
{ | |
BOOL bResult = FALSE; | |
if(lpPart->DataLength = lpFileName->NameLength) { | |
SIZE_T szBuffer = sizeof(WCHAR) * (lpFileName->NameLength + 1); | |
if(lpPart->Data = (LPWSTR)malloc(szBuffer)) { | |
memset((void*)lpPart->Data, 0, szBuffer); | |
wcsncpy(lpPart->Data, (const wchar_t*)lpFileName->Name, lpPart->DataLength); | |
bResult = TRUE; | |
} | |
} else { | |
bResult = TRUE; | |
lpPart->Data = NULL; | |
} | |
return bResult; | |
} | |
VOID FreeReadFileNamePartsData(ReadFileNamePartsData* lpData) | |
{ | |
FILENAME_PART* lpCurrent = NULL; | |
if(!lpData) return; | |
lpCurrent = lpData->Tail; | |
while(lpCurrent) { | |
FILENAME_PART* lpLast = lpCurrent; | |
lpCurrent = lpCurrent->Prev; | |
if(lpLast->Data) free((void*)lpLast->Data); | |
free((void*)lpLast); | |
} | |
} | |
VOID FreeReadHardLinksData(ReadHardLinksData* lpData) | |
{ | |
USHORT i = 0; | |
if(!lpData || !lpData->LinkData) return; | |
for(; i < lpData->LinkCount; i++) { | |
if(lpData->LinkData[i]) | |
free((void*)lpData->LinkData[i]); | |
} | |
free((void*)lpData->LinkData); | |
} | |
LPWSTR AssembleHardLinksPath(ReadFileNamePartsData* lpData) | |
{ | |
LPWSTR lpsResult = NULL; | |
if(lpData) { | |
SIZE_T szPathLength = 0; | |
FILENAME_PART* lpLast, *lpCurrent = lpData->Tail; | |
while(lpCurrent) { | |
lpLast = lpCurrent; | |
lpCurrent = lpCurrent->Prev; | |
if(lpLast->Data) { | |
// Add an extra chracter to allow for separarting the folder | |
// hierarchy with \s. If lpLast points at the HEAD entry, we're | |
// dealing with a file, so we don't need the trailing slash. | |
szPathLength += lpCurrent ? lpLast->DataLength + 1 : lpLast->DataLength; | |
} | |
} | |
if(szPathLength) { | |
SIZE_T szBufferSize = sizeof(WCHAR) * (szPathLength + 1); | |
lpsResult = (LPWSTR)malloc(szBufferSize); | |
memset((void*)lpsResult, 0, szBufferSize); | |
lpCurrent = lpData->Tail; | |
while(lpCurrent) { | |
lpLast = lpCurrent; | |
lpCurrent = lpCurrent->Prev; | |
if(lpLast->Data) { | |
wcscat(lpsResult, (const wchar_t*)lpLast->Data); | |
// Add trailing \s for folders, and leave filenames as is. | |
if(lpCurrent) wcscat(lpsResult, L"\\"); | |
free((void*)lpLast->Data); | |
} | |
free((void*)lpLast); | |
} | |
} | |
} | |
return lpsResult; | |
} | |
BOOL EnumRecordAttributes(PFILE_RECORD_HEADER lpFileRecordHeader, IN ENUMATTRIBUTEPROC fnEnumAttrs, IN OPTIONAL LPVOID lpUserData) | |
{ | |
BOOL bSuccess = TRUE, bContinue = TRUE; | |
PATTRIBUTE lpAttr = (PATTRIBUTE)((LPBYTE)lpFileRecordHeader + lpFileRecordHeader->AttributesOffset); | |
// Loop through our attributes (unless our handler says to stop) | |
while(bContinue && lpAttr && lpAttr->AttributeType <= AttributeLoggedUtilityStream) { | |
// Process attribute | |
if(!(bSuccess = fnEnumAttrs(lpAttr, lpUserData, &bContinue))) | |
break; | |
// Figure out the current attribute size, then move on to the next. | |
if(lpAttr->Nonresident) { | |
lpAttr = (PATTRIBUTE)Add2Ptr(lpAttr, sizeof(NONRESIDENT_ATTRIBUTE)); | |
} else if(lpAttr->Length && lpAttr->Length < lpFileRecordHeader->BytesInUse) { | |
lpAttr = (PATTRIBUTE)Add2Ptr(lpAttr, lpAttr->Length); | |
} else { | |
lpAttr = NULL; | |
} | |
} | |
return bSuccess; | |
} | |
BOOL ProcessFileRecord(IN HANDLE hVolume, IN LARGE_INTEGER FileReferenceNumber, IN PROCESSFILERECORD fnProcessFileRecord, IN OPTIONAL LPVOID lpUserData) | |
{ | |
BOOL bSuccess = FALSE; | |
DWORD dwRead, dwBufferSize = FILE_RECORD_OUTPUT_BUFFER_SIZE; | |
C_ASSERT(sizeof(FileReferenceNumber) == sizeof(NTFS_FILE_RECORD_INPUT_BUFFER)); | |
NTFS_FILE_RECORD_OUTPUT_BUFFER* nrb = (NTFS_FILE_RECORD_OUTPUT_BUFFER*)malloc(dwBufferSize); | |
if(DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD, &FileReferenceNumber, sizeof(FileReferenceNumber), nrb, dwBufferSize, &dwRead, NULL)) { | |
// FSCTL_GET_NTFS_FILE_RECORD retrieves one MFT entry | |
// FILE_RECORD_HEADER is the Base struct for the MFT entry | |
// that we will work from | |
PFILE_RECORD_HEADER lpFileRecordHeader = (PFILE_RECORD_HEADER)nrb->FileRecordBuffer; | |
// Verify that the first four bytes are: FILE | |
if(lpFileRecordHeader->Ntfs.Type == 'ELIF') { | |
bSuccess = fnProcessFileRecord(lpFileRecordHeader, lpUserData); | |
} | |
} | |
return bSuccess; | |
} | |
BOOL ReadFileNamePartsEnumAttributes(IN PATTRIBUTE lpAttr, IN OPTIONAL LPVOID lpUserData, IN OUT LPBOOL lpbContinue) | |
{ | |
BOOL bSuccess = TRUE; | |
ReadFileNamePartsData* lpData = (ReadFileNamePartsData*)lpUserData; | |
*lpbContinue = TRUE; | |
if(lpAttr->AttributeType == AttributeFileName) { | |
PRESIDENT_ATTRIBUTE lpResident = (PRESIDENT_ATTRIBUTE)lpAttr; | |
PFILENAME_ATTRIBUTE lpFileName = (PFILENAME_ATTRIBUTE)Add2Ptr(lpAttr, lpResident->ValueOffset); | |
if(lpFileName->NameType & WIN32_NAME || lpFileName->NameType <= 1) { | |
FILENAME_PART* lpNext; | |
bSuccess = FALSE; | |
if((lpNext = AllocateNextPart(lpData->Tail))) { | |
// Test condition to detect FILENAME_ATTRIBUTEs for the volume root. (End of the line) | |
if( | |
lpFileName->DirectoryFileReferenceNumber == 0x5 || | |
((lpFileName->NameLength == 1) && (*lpFileName->Name == L'.')) | |
) { | |
lpData->Tail = lpNext; | |
lpNext->DataLength = VolumePathTemplateLength - 1; | |
bSuccess = (lpNext->Data = _wcsdup(VolumePathTemplate)) != NULL; | |
} else if(CopyFileNamePart(lpNext, lpFileName)) { // Otherwise, we copy the filename field. | |
lpData->Tail = lpNext; | |
bSuccess = ProcessFileRecord(lpData->hVolume, *((LARGE_INTEGER*)&(lpFileName->DirectoryFileReferenceNumber)), ReadFileNamePartsProcessFileRecord, lpUserData); | |
} | |
} | |
*lpbContinue = FALSE; | |
} | |
} | |
return bSuccess; | |
} | |
BOOL ReadFileNamePartsProcessFileRecord(IN PFILE_RECORD_HEADER lpFileRecordHeader, IN OPTIONAL LPVOID lpUserData) | |
{ | |
BOOL bSuccess = FALSE; | |
ReadFileNamePartsData* lpData = (ReadFileNamePartsData*)lpUserData; | |
return EnumRecordAttributes(lpFileRecordHeader, ReadFileNamePartsEnumAttributes, lpUserData); | |
} | |
BOOL ReadHardLinksEnumAttributes(IN PATTRIBUTE lpAttr, IN OPTIONAL LPVOID lpUserData, IN OUT LPBOOL lpbContinue) | |
{ | |
BOOL bSuccess = TRUE; | |
ReadHardLinksData* lpData = (ReadHardLinksData*)lpUserData; | |
*lpbContinue = TRUE; | |
if(lpAttr->AttributeType == AttributeFileName) { | |
PRESIDENT_ATTRIBUTE lpResident = (PRESIDENT_ATTRIBUTE)lpAttr; | |
PFILENAME_ATTRIBUTE lpFileName = (PFILENAME_ATTRIBUTE)Add2Ptr(lpAttr, lpResident->ValueOffset); | |
if(lpFileName->NameType & WIN32_NAME || lpFileName->NameType == 0) { | |
ReadFileNamePartsData oReadData = { lpData->hVolume }; | |
USHORT wCurrent = lpData->CurrentLink++; | |
bSuccess = FALSE; | |
if(oReadData.Tail = (FILENAME_PART*)malloc(sizeof(FILENAME_PART))) { | |
memset((void*)oReadData.Tail, 0, sizeof(FILENAME_PART)); | |
if(CopyFileNamePart(oReadData.Tail, lpFileName)) { | |
if(ProcessFileRecord(lpData->hVolume, *((LARGE_INTEGER*)&lpFileName->DirectoryFileReferenceNumber), ReadFileNamePartsProcessFileRecord, (LPVOID)&oReadData) && | |
(lpData->LinkData[wCurrent] = AssembleHardLinksPath(&oReadData)) | |
) { | |
bSuccess = TRUE; | |
} else { | |
FreeReadFileNamePartsData(&oReadData); | |
} | |
} else { | |
free((void*)oReadData.Tail); | |
} | |
} | |
} | |
} | |
return bSuccess; | |
} | |
BOOL ReadHardLinksProcessFileRecord(IN PFILE_RECORD_HEADER lpFileRecordHeader, IN OPTIONAL LPVOID lpUserData) | |
{ | |
BOOL bSuccess = FALSE; | |
ReadHardLinksData* lpData = (ReadHardLinksData*)lpUserData; | |
size_t szDataSize = sizeof(LPWSTR) * lpFileRecordHeader->LinkCount; | |
lpData->LinkCount = lpFileRecordHeader->LinkCount; | |
if(lpData->LinkData = (LPWSTR*)malloc(szDataSize)) { | |
lpData->CurrentLink = 0; | |
memset((void*)lpData->LinkData, 0, szDataSize); | |
if(!(bSuccess = EnumRecordAttributes(lpFileRecordHeader, ReadHardLinksEnumAttributes, lpUserData))) { | |
//free((void*)lpData->LinkData); | |
//lpData->LinkData = NULL; | |
} | |
} | |
return bSuccess; | |
} | |
VOID ReadHardLinks(IN HANDLE hVolume, IN LARGE_INTEGER FileRef) | |
{ | |
ReadHardLinksData rhlData = { hVolume }; | |
if(ProcessFileRecord(hVolume, FileRef, ReadHardLinksProcessFileRecord, (LPVOID)&rhlData)) { | |
USHORT i = 0; | |
for(; i < rhlData.LinkCount; i++) { | |
wprintf(L"%s\n", rhlData.LinkData[i]); | |
free(rhlData.LinkData[i]); | |
} | |
free(rhlData.LinkData); | |
} else { | |
wprintf(L"ReadHardLinks failed!\n"); | |
FreeReadHardLinksData(&rhlData); | |
} | |
} | |
VOID ReadHardLinkParts(IN HANDLE hVolume, IN LARGE_INTEGER FileRef) | |
{ | |
DWORD dwRead, dwBufferSize = FILE_RECORD_OUTPUT_BUFFER_SIZE; | |
C_ASSERT(sizeof(FileRef) == sizeof(NTFS_FILE_RECORD_INPUT_BUFFER)); | |
NTFS_FILE_RECORD_OUTPUT_BUFFER* nrb = (NTFS_FILE_RECORD_OUTPUT_BUFFER*)malloc(dwBufferSize); | |
if(DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD, &FileRef, sizeof(FileRef), nrb, dwBufferSize, &dwRead, NULL)) { | |
// FSCTL_GET_NTFS_FILE_RECORD retrieves one MFT entry | |
// FILE_RECORD_HEADER is the Base struct for the MFT entry | |
// that we will work from | |
PFILE_RECORD_HEADER lpFileRecordHeader = (PFILE_RECORD_HEADER)nrb->FileRecordBuffer; | |
if(lpFileRecordHeader->Ntfs.Type == 'ELIF') { | |
PATTRIBUTE lpAttr = (PATTRIBUTE)((LPBYTE)lpFileRecordHeader + lpFileRecordHeader->AttributesOffset); | |
while(lpAttr && lpAttr->AttributeType <= AttributeLoggedUtilityStream) { | |
// Only process $FILE_NAME attributes | |
if(lpAttr->AttributeType == AttributeFileName) { | |
PRESIDENT_ATTRIBUTE lpResident = (PRESIDENT_ATTRIBUTE)lpAttr; | |
PFILENAME_ATTRIBUTE lpFileName = (PFILENAME_ATTRIBUTE)Add2Ptr(lpAttr, lpResident->ValueOffset); | |
if(lpFileName->NameType & WIN32_NAME || lpFileName->NameType == 0) { | |
// Reached the root | |
if(lpFileName->DirectoryFileReferenceNumber == 0x5) { | |
wprintf(L"Hit Root\n"); | |
} | |
lpFileName->Name[lpFileName->NameLength] = L'\0'; | |
wprintf(L"FileName :%s\n", lpFileName->Name) ; | |
} | |
} | |
if(lpAttr->Nonresident) { | |
lpAttr = (PATTRIBUTE)Add2Ptr(lpAttr, sizeof(NONRESIDENT_ATTRIBUTE)); | |
} else if(lpAttr->Length && lpAttr->Length < lpFileRecordHeader->BytesInUse) { | |
lpAttr = (PATTRIBUTE)Add2Ptr(lpAttr, lpAttr->Length); | |
} else { | |
lpAttr = NULL; | |
} | |
} | |
} | |
} | |
free(nrb); | |
} | |
// Open the volume | |
HANDLE OpenVolume(WCHAR wcVolume) | |
{ | |
HANDLE hVolume = NULL; | |
WCHAR lpsVolumeRoot[VolumePathTemplateLength]; | |
// Uppercase volume letters. | |
if(iswlower((wint_t)wcVolume)) { | |
wcVolume = towupper((wint_t)wcVolume); | |
} | |
wcscpy(lpsVolumeRoot, VolumePathTemplate); | |
lpsVolumeRoot[4] = wcVolume; | |
hVolume = CreateFileW(lpsVolumeRoot, GENERIC_READ, FILE_SHARE_READWRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |
return VALID_HANDLE(hVolume) ? hVolume : NULL; | |
} | |
HANDLE OpenVolumeOfFilePath(IN LPCWSTR lpsFilePath, OUT WCHAR* lpwcVolume) | |
{ | |
HANDLE hVolume = NULL; | |
WCHAR lpVolumePath[MAX_PATH + 1] = W_EMPTY; | |
*lpwcVolume = L'\0'; | |
if(GetVolumePathName(lpsFilePath, lpVolumePath, WLEN(lpVolumePath) - 1)) { | |
hVolume = OpenVolume(*lpwcVolume = *lpVolumePath); | |
} | |
return hVolume; | |
} | |
int __cdecl _tmain(int argc, TCHAR* argv[]) | |
{ | |
WCHAR wcVolume; | |
LPCWSTR lpFileName; | |
HANDLE hFile, hVolume; | |
LARGE_INTEGER FileRef; | |
BY_HANDLE_FILE_INFORMATION fiItem; | |
DWORD dwFileAttrs, dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; | |
// Check command-line arguments. | |
if(argc == 1) { | |
_tprintf(_T("Usage: %s <filepath>\n"), argv[0]); | |
return 0; | |
} | |
// Determine the flags we'll use for opening our filepath (based on whether or not lpFileName | |
// contains a folder path) | |
lpFileName = (LPCWSTR)argv[1]; | |
dwFileAttrs = GetFileAttributesW(lpFileName); | |
if(HAS_FLAG(dwFileAttrs, INVALID_FILE_ATTRIBUTES)) { | |
FatalError(ERROR_PATH_NOT_FOUND, _T("%s does not exist."), lpFileName); | |
} else if(HAS_FLAG(dwFileAttrs, FILE_ATTRIBUTE_DIRECTORY)) { | |
dwFlagsAndAttributes |= FILE_FLAG_BACKUP_SEMANTICS; | |
} | |
hFile = CreateFile(lpFileName, GENERIC_READ, FILE_SHARE_READWRITE, NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL); | |
CheckStatement(VALID_HANDLE(hFile)); | |
GetFileInformationByHandle(hFile, &fiItem); | |
CloseHandle(hFile); | |
if(!(hVolume = OpenVolumeOfFilePath(lpFileName, &wcVolume))) { | |
FatalApi(CreateFile); | |
} | |
FileRef.LowPart = fiItem.nFileIndexLow; | |
FileRef.HighPart = fiItem.nFileIndexHigh; | |
ReadHardLinks(hVolume, FileRef); | |
CloseHandle(hVolume); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file ntfsdefs.h | |
* | |
* TODO: Description | |
*/ | |
#ifndef _NTFSDEFS_H_ | |
#define _NTFSDEFS_H_ | |
#pragma once | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
// See NTFS_VOLUME_DATA_BUFFER::BytesPerFileRecordSegment | |
#define BytesPerFileRecordSegment 1024 | |
#define FILE_RECORD_OUTPUT_BUFFER_SIZE (FIELD_OFFSET(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer) + BytesPerFileRecordSegment) | |
// These types should be stored in separate | |
// include file, not done here | |
typedef struct { | |
ULONG Type; | |
USHORT UsaOffset; | |
USHORT UsaCount; | |
USN Usn;// LONGLONG | |
} NTFS_RECORD_HEADER, *PNTFS_RECORD_HEADER; | |
typedef struct { | |
NTFS_RECORD_HEADER Ntfs; | |
USHORT SequenceNumber; | |
USHORT LinkCount; | |
USHORT AttributesOffset; | |
USHORT Flags; // 0x0001 = InUse, 0x0002 = Directory | |
ULONG BytesInUse; | |
ULONG BytesAllocated; | |
ULONGLONG BaseFileRecord; | |
USHORT NextAttributeNumber; | |
} FILE_RECORD_HEADER, *PFILE_RECORD_HEADER; | |
typedef enum ATTRIBUTE_TYPE { | |
AttributeInvalid = 0x00, /* Not defined by Windows */ | |
AttributeStandardInformation = 0x10, | |
AttributeAttributeList = 0x20, | |
AttributeFileName = 0x30, | |
AttributeObjectId = 0x40, | |
AttributeSecurityDescriptor = 0x50, | |
AttributeVolumeName = 0x60, | |
AttributeVolumeInformation = 0x70, | |
AttributeData = 0x80, | |
AttributeIndexRoot = 0x90, | |
AttributeIndexAllocation = 0xA0, | |
AttributeBitmap = 0xB0, | |
AttributeReparsePoint = 0xC0, /* Reparse Point = Symbolic link */ | |
AttributeEAInformation = 0xD0, | |
AttributeEA = 0xE0, | |
AttributePropertySet = 0xF0, | |
AttributeLoggedUtilityStream = 0x100 | |
} ATTRIBUTE_TYPE; | |
typedef struct { | |
ATTRIBUTE_TYPE AttributeType; | |
ULONG Length; | |
BOOLEAN Nonresident; | |
UCHAR NameLength; | |
USHORT NameOffset; | |
USHORT Flags; // 0x0001 = Compressed | |
USHORT AttributeNumber; | |
} ATTRIBUTE, *PATTRIBUTE; | |
typedef struct { | |
ATTRIBUTE Attribute; | |
ULONG ValueLength; | |
USHORT ValueOffset; | |
USHORT Flags; // 0x0001 = Indexed | |
} RESIDENT_ATTRIBUTE, *PRESIDENT_ATTRIBUTE; | |
typedef struct { | |
ATTRIBUTE Attribute; | |
ULONGLONG LowVcn; | |
ULONGLONG HighVcn; | |
USHORT RunArrayOffset; | |
UCHAR CompressionUnit; | |
UCHAR AlignmentOrReserved[5]; | |
ULONGLONG AllocatedSize; | |
ULONGLONG DataSize; | |
ULONGLONG InitializedSize; | |
ULONGLONG CompressedSize; // Only when compressed | |
} NONRESIDENT_ATTRIBUTE, *PNONRESIDENT_ATTRIBUTE; | |
typedef struct { | |
ULONGLONG CreationTime; | |
ULONGLONG ChangeTime; | |
ULONGLONG LastWriteTime; | |
ULONGLONG LastAccessTime; | |
ULONG FileAttributes; | |
ULONG AlignmentOrReservedOrUnknown[3]; | |
ULONG QuotaId; // NTFS 3.0 only | |
ULONG SecurityId; // NTFS 3.0 only | |
ULONGLONG QuotaCharge; // NTFS 3.0 only | |
USN Usn; // NTFS 3.0 only | |
} STANDARD_INFORMATION, *PSTANDARD_INFORMATION; | |
typedef struct { | |
ATTRIBUTE_TYPE AttributeType; | |
USHORT Length; | |
UCHAR NameLength; | |
UCHAR NameOffset; | |
ULONGLONG LowVcn; | |
ULONGLONG FileReferenceNumber; | |
USHORT AttributeNumber; | |
USHORT AlignmentOrReserved[3]; | |
} ATTRIBUTE_LIST, *PATTRIBUTE_LIST; | |
typedef struct { | |
ULONGLONG DirectoryFileReferenceNumber; | |
ULONGLONG CreationTime; // Saved when filename last changed | |
ULONGLONG ChangeTime; // ditto | |
ULONGLONG LastWriteTime; // ditto | |
ULONGLONG LastAccessTime; // ditto | |
ULONGLONG AllocatedSize; // ditto | |
ULONGLONG DataSize; // ditto | |
ULONG FileAttributes; // ditto | |
ULONG AlignmentOrReserved; | |
UCHAR NameLength; | |
UCHAR NameType; // 0x01 = Long, 0x02 = Short | |
WCHAR Name[1]; | |
} FILENAME_ATTRIBUTE, *PFILENAME_ATTRIBUTE; | |
typedef struct { | |
GUID ObjectId; | |
union { | |
struct { | |
GUID BirthVolumeId; | |
GUID BirthObjectId; | |
GUID DomainId; | |
} ; | |
UCHAR ExtendedInfo[48]; | |
}; | |
} OBJECTID_ATTRIBUTE, *POBJECTID_ATTRIBUTE; | |
typedef struct { | |
//GUID ObjectId; | |
ULONGLONG DirectoryFileReferenceNumber; | |
BOOLEAN IsDirectory; | |
ULONGLONG Size; | |
ULONGLONG CreationTime; | |
ULONGLONG LastWriteTime; | |
ULONGLONG LastAccessTime; | |
LPWSTR Name; | |
} MFT_FILE_INFO, *PMFT_FILE_INFO; | |
#define WIN32_NAME 1 | |
typedef DWORD (__stdcall *pNtCreateFile)( | |
PHANDLE hFile, | |
DWORD dwAccess, | |
PVOID lpObjectAttrs, | |
PVOID lpIoStatusBlock, | |
PLARGE_INTEGER AllocationSize, | |
DWORD dwAttributes, | |
DWORD dwShareAccess, | |
DWORD dwCreateDisposition, | |
DWORD dwCreateOptions, | |
PVOID lpEaBuffer, | |
DWORD dwEaLength | |
); | |
typedef DWORD (__stdcall *pNtReadFile)( | |
IN HANDLE hFile, | |
IN HANDLE hEvent, | |
IN PVOID lpApcRoutine, | |
IN PVOID lpApcContext, | |
OUT PVOID lpIoStatusBlock, | |
OUT PVOID lpBuffer, | |
IN DWORD dwLength, | |
IN PLARGE_INTEGER lpqByteOffset, | |
IN PULONG lpdKey | |
); | |
typedef struct _UNICODE_STRING { | |
USHORT Length, MaximumLength; | |
PWCH Buffer; | |
} UNICODE_STRING, *PUNICODE_STRING; | |
typedef struct _OBJECT_ATTRIBUTES { | |
ULONG Length; | |
HANDLE RootDirectory; | |
PUNICODE_STRING ObjectName; | |
ULONG Attributes; | |
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR | |
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE | |
} OBJECT_ATTRIBUTES; | |
#define InitializeObjectAttributes( p, n, a, r, s ) { \ | |
(p)->Length = sizeof( OBJECT_ATTRIBUTES ); \ | |
(p)->RootDirectory = r; \ | |
(p)->Attributes = a; \ | |
(p)->ObjectName = n; \ | |
(p)->SecurityDescriptor = s; \ | |
(p)->SecurityQualityOfService = NULL; \ | |
} | |
#define OBJ_CASE_INSENSITIVE 0x00000040L | |
#define FILE_NON_DIRECTORY_FILE 0x00000040 | |
#define FILE_OPEN_BY_FILE_ID 0x00002000 | |
#define FILE_OPEN 0x00000001 | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif /* _NTFSDEFS_H_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file pch.h | |
* | |
* TODO: Description | |
*/ | |
#ifndef _PCH_H_ | |
#define _PCH_H_ | |
#pragma once | |
/** Char Type Config */ | |
#define UNICODE 1 | |
#define _UNICODE UNICODE | |
// Macro used to combine two things. (Including other macros) | |
#define XMERGE2(A,B) A##B | |
#define XMERGE(A,B) XMERGE2(A,B) | |
// Stringifying macros | |
#define XWIDEN(X) XMERGE2(L,X) | |
#define XSTR2(X) #X | |
#define XSTRA(X) XSTR2(X) | |
#define XSTRW(X) XWIDEN(XSTRA(X)) | |
/** Windows Version */ | |
#if !defined(WINVER) | |
# define WINVER 0x0501 | |
#elif (WINVER < 0x0501) | |
# error Windows XP is currently the lowest version of Windows supported by this project. | |
#endif | |
#if !defined(_WIN32_WINNT) | |
# define _WIN32_WINNT 0x0501 | |
#elif (_WIN32_WINNT < 0x0501) | |
# error Windows XP is currently the lowest version of Windows supported by this project. | |
#endif | |
#if !defined(NTDDI_VERSION) | |
# define NTDDI_VERSION 0x05010000 | |
#elif (NTDDI_VERSION < 0x05010000) | |
# error Windows XP is currently the lowest version of Windows supported by this project. | |
#endif | |
/** Compiler Detection & Inline Definition */ | |
#if defined(__clang__) || defined(__MINGW32__) || defined(__GNUC__) | |
# define BUILD_COMPILER_GNU | |
# ifndef noret | |
# define noret void __attribute__((__noreturn)) | |
# endif | |
# ifndef inline | |
# define inline __inline | |
# endif | |
# if defined(__i386__) | |
# define BUILD_ARCH_X86 | |
# define BUILD_ARCH_BITS 32 | |
# elif defined(__x86_64__) | |
# define BUILD_ARCH_X64 | |
# define BUILD_ARCH_BITS 64 | |
# endif | |
#elif defined(RC_INVOKED) | |
# define BUILD_COMPILER_RC | |
# define BUILD_ARCH_X86 | |
# define BUILD_ARCH_BITS 32 | |
# define inline | |
#elif defined(_MSC_VER) | |
# define BUILD_COMPILER_MSVC | |
# ifndef noret | |
# define noret void __declspec(noreturn) | |
# endif | |
# ifndef __cplusplus | |
# ifdef inline | |
# undef inline | |
# endif | |
# define inline __forceinline | |
# endif | |
# if (defined(_M_IA64) || defined(_M_AMD64) || defined(_WIN64)) | |
# define BUILD_ARCH_X64 | |
# define BUILD_ARCH_BITS 64 | |
# elif defined(_M_IX86) | |
# define BUILD_ARCH_X86 | |
# define BUILD_ARCH_BITS 32 | |
# endif | |
#endif | |
#ifndef BUILD_ARCH_BITS | |
# error Could not detect platform architecture. | |
#endif | |
/** Char Type Definitions */ | |
#ifdef _UNICODE | |
# define BUILD_UNICODE | |
# define __TFILE__ XWIDEN(__FILE__) | |
# define XSTR(X) XSTRW(X) | |
# define ISFUNC_CHAR wint_t | |
#else | |
# ifndef _MBCS | |
# define _MBCS 1 | |
# endif | |
# define BUILD_MBCS | |
# define __TFILE__ __FILE__ | |
# define XSTR(X) XSTRA(X) | |
# define ISFUNC_CHAR int | |
#endif | |
/** System Includes */ | |
#include <windows.h> | |
#include <winioctl.h> | |
#include <tchar.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <crtdbg.h> | |
#define W_EMPTY L"" | |
#define WSIZE(COUNT) ((COUNT) * sizeof(WCHAR)) | |
#define WLEN(WBUFFER) (sizeof(WBUFFER) / sizeof(WCHAR)) | |
#define T_EMPTY _T("") | |
#define TSIZE(COUNT) ((COUNT) * sizeof(TCHAR)) | |
#define TLEN(TBUFFER) (sizeof(TBUFFER) / sizeof(TCHAR)) | |
// Compiler-specific Boolean Declarations | |
#ifdef BUILD_COMPILER_GNU | |
# include <stdbool.h> | |
# include <stdint.h> | |
# ifndef _INTPTR_T_DEFINED | |
# ifdef BUILD_ARCH_X64 | |
typedef int64_t intptr_t; | |
typedef uint64_t uintptr_t; | |
# else | |
typedef int32_t intptr_t; | |
typedef uint32_t uintptr_t; | |
# endif | |
# endif | |
typedef int8_t s8; | |
typedef int16_t s16; | |
typedef int32_t s18; | |
typedef int64_t s64; | |
typedef intptr_t sPtr; | |
typedef uint8_t u8; | |
typedef uint16_t u16; | |
typedef uint32_t u18; | |
typedef uint64_t u64; | |
typedef uintptr_t uPtr; | |
#else | |
# ifdef BUILD_COMPILER_MSVC | |
typedef signed __int8 bool; | |
typedef signed __int8 s8; | |
typedef signed __int16 s16; | |
typedef signed __int32 s32; | |
typedef signed __int64 s64; | |
typedef signed __int3264 sPtr; | |
typedef unsigned __int8 u8; | |
typedef unsigned __int16 u16; | |
typedef unsigned __int32 u32; | |
typedef unsigned __int64 u64; | |
typedef unsigned __int3264 uPtr; | |
# else | |
typedef BOOLEAN bool; | |
# endif | |
# define true TRUE | |
# define false FALSE | |
#endif | |
/** Utility Constants */ | |
#ifndef MAX_PATH | |
# define MAX_PATH _MAX_PATH | |
#endif | |
#ifndef FILE_SHARE_READWRITE | |
# define FILE_SHARE_READWRITE (FILE_SHARE_READ | FILE_SHARE_WRITE) | |
#endif | |
// Since sizeof will add 1 for the trailing \0, we'll leave off the leading '\' so the count | |
// isn't off. | |
#define SYSDIR_SUBDIR "system32" | |
/** Utility Macros */ | |
#define Add2Ptr(PTR,OFFSET) (((PUCHAR)(PTR)) + (OFFSET)) | |
#define VALID_HANDLE(hSubject) \ | |
((hSubject) && ((hSubject) != INVALID_HANDLE_VALUE)) | |
#define INVALID_HANDLE(hSubject) \ | |
(!VALID_HANDLE(hSubject)) | |
#define HAS_FLAG(dwSubject,dwFlag) \ | |
(((dwSubject) & (dwFlag)) == (dwFlag)) | |
#define xfree(BUF) \ | |
_xfree_((void*)(BUF)) | |
static inline void _xfree_(void* ptr) | |
{ | |
if(ptr) free(ptr); | |
} | |
/** Utility deallocation function */ | |
static inline void *xmalloc(size_t size) | |
{ | |
void *ptr = malloc(size); | |
if(!ptr) perror("malloc failed."); | |
memset(ptr, 0, size); | |
return ptr; | |
} | |
#define xalloc(type) \ | |
((type)xmalloc(sizeof(type))) | |
#define xaalloc(type,count) \ | |
((type*)xmalloc(((size_t)count) * sizeof(type))) | |
#define xsalloc(type,count) \ | |
xaalloc(type,((count)+1)) | |
#define stralloc(count) xsalloc(char,count) | |
#define wcsalloc(count) xsalloc(wchar_t,count) | |
#define tcsalloc(count) xsalloc(TCHAR,count) | |
static TCHAR* vtcsprintf_alloc(const TCHAR* lpMessage, va_list lpArgs) | |
{ | |
TCHAR *lpResult; | |
size_t szDisplayBuf; | |
// Check the resulting size of the buffer. | |
szDisplayBuf = (size_t)_vsctprintf(lpMessage, lpArgs) + 1; | |
// Allocate our buffer. | |
if(!(lpResult = (TCHAR*)calloc(szDisplayBuf, sizeof(TCHAR)))) { | |
return NULL; | |
} | |
// Finally, fill in the message. | |
_vsntprintf(lpResult, szDisplayBuf, lpMessage, lpArgs); | |
return lpResult; | |
} | |
static TCHAR* tcsprintf_alloc(const TCHAR* lpMessage, ...) | |
{ | |
va_list lpArgs = NULL; | |
TCHAR* lpDisplayBuf = NULL; | |
// Allocate buffer for our resulting format string. | |
va_start(lpArgs, lpMessage); | |
lpDisplayBuf = vtcsprintf_alloc(lpMessage, lpArgs); | |
va_end(lpArgs); | |
} | |
static inline noret FatalStatement(char* prefix, char* statement) | |
{ | |
fprintf(stderr, "%s: FATAL:%s\n", prefix, statement); | |
ExitProcess(1); | |
} | |
#define CheckStatement(statement) \ | |
if(!(statement)) {\ | |
FatalStatement("[" __FILE__ ":" XSTRA(__LINE__) "]", XSTRA(statement)); \ | |
} | |
void ErrorMessage(DWORD dwError, LPCTSTR lpMessage, ...) | |
{ | |
va_list lpArgs = NULL; | |
TCHAR* lpMsgBuf = NULL, *lpDisplayBuf = NULL, *lpFormatted = NULL; | |
size_t szErrLen, szFormatted, szMessage; | |
szErrLen = (szMessage = _tcslen(lpMessage)); | |
if(dwError == ERROR_SUCCESS) dwError = GetLastError(); | |
if(dwError != ERROR_SUCCESS) { | |
const TCHAR *lpErrMsg = NULL; | |
FormatMessage( | |
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS, | |
NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (TCHAR*)&lpErrMsg, 0, NULL | |
); | |
szErrLen += _tcslen(lpErrMsg) + 39; | |
// Allocate our buffer for the error message. | |
CheckStatement(lpMsgBuf = (TCHAR*)calloc(szErrLen + 1, sizeof(TCHAR))); | |
memset(lpMsgBuf, 0, TSIZE(szErrLen + 1)); | |
_sntprintf(lpMsgBuf, szErrLen, _T("ERROR: %s - Windows Error [0x%08X]: %s\n"), lpMessage, dwError, lpErrMsg); | |
LocalFree((HLOCAL)lpErrMsg); | |
} else { | |
szErrLen += 8; | |
CheckStatement(lpMsgBuf = (TCHAR*)calloc(szErrLen + 1, sizeof(TCHAR))); | |
memset(lpMsgBuf, 0, TSIZE(szErrLen)); | |
_sntprintf(lpMsgBuf, szErrLen, _T("ERROR: %s\n"), lpMessage); | |
} | |
va_start(lpArgs, lpMessage); | |
if(szMessage != (szFormatted = _vsctprintf(lpMessage, lpArgs))) { | |
CheckStatement(lpDisplayBuf = vtcsprintf_alloc(lpMsgBuf, lpArgs)); | |
free(lpMsgBuf); | |
} else { | |
lpDisplayBuf = lpMsgBuf; | |
} | |
va_end(lpArgs); | |
_ftprintf(stderr, (LPCTSTR)lpDisplayBuf); | |
xfree(lpDisplayBuf); | |
} | |
#define ShowError(dwError,...) \ | |
ErrorMessage(dwError, __VA_ARGS__) | |
#define ShowWin32Error(fnFailed) \ | |
ErrorMessage(ERROR_SUCCESS, XSTR(fnFailed)) | |
#define FatalError(dwError,...) { \ | |
ErrorMessage(dwError, __VA_ARGS__); \ | |
ExitProcess(dwError); \ | |
} | |
#define FatalApi(fnFailed) FatalError(ERROR_SUCCESS, XSTR(fnFailed)) | |
#ifdef _UNICODE | |
# define AbortMe() FatalError(ERROR_SUCCESS, XWIDEN(__FUNCTION__)) | |
#else | |
# define AbortMe() FatalError(ERROR_SUCCESS, __FUNCTION__) | |
#endif | |
#endif /* _PCH_H_ */ |
Author
juntalis
commented
Jul 18, 2020
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment