Traversing directories using the NtQueryDirectoryFile()
Hi All,
I am trying to traverse directories using the undocumented API NtQueryDirectoryFile() to compare its performance against FindFirstFile/FindNextFile
I have written this program:
Code:
#include<stdio.h>
#include<windows.h>
#include<string.h>
#include<wchar.h>
#include <ntsecapi.h>
#include<time.h>
#include<stdlib.h>
#include<malloc.h>
typedef enum _FILE_INFORMATION_CLASS {
FileDirectoryInformation = 1,
FileFullDirectoryInformation,
FileBothDirectoryInformation,
FileBasicInformation,
FileStandardInformation,
FileInternalInformation,
FileEaInformation,
FileAccessInformation,
FileNameInformation,
FileRenameInformation,
FileLinkInformation,
FileNamesInformation,
FileDispositionInformation,
FilePositionInformation,
FileFullEaInformation,
FileModeInformation,
FileAlignmentInformation,
FileAllInformation,
FileAllocationInformation,
FileEndOfFileInformation,
FileAlternateNameInformation,
FileStreamInformation,
FilePipeInformation,
FilePipeLocalInformation,
FilePipeRemoteInformation,
FileMailslotQueryInformation,
FileMailslotSetInformation,
FileCompressionInformation,
FileCopyOnWriteInformation,
FileCompletionInformation,
FileMoveClusterInformation,
FileOleClassIdInformation,
FileOleStateBitsInformation,
FileNetworkOpenInformation,
FileObjectIdInformation,
FileOleAllInformation,
FileOleDirectoryInformation,
FileContentIndexInformation,
FileInheritContentIndexInformation,
FileOleInformation,
FileMaximumInformation
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
}StatusBlock;
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
typedef struct _FILE_FULL_DIR_INFORMATION {
ULONG NextEntryOffset;
ULONG FileIndex;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
ULONG EaSize;
WCHAR FileName[1];
} FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION;
/*typedef
VOID WINAPI
(*PIO_APC_ROUTINE) (
IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG Reserved
);
*/
DWORD __stdcall NtQueryDirectoryFile(
IN HANDLE FileHandle,
IN HANDLE Event,
//IN PIO_APC_ROUTINE ApcRoutine,
IN void* x,
IN PVOID ApcContext,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN BOOLEAN ReturnSingleEntry,
IN PUNICODE_STRING FileMask,
IN BOOLEAN RestartScan );
typedef DWORD (__stdcall *NQDF)(
IN HANDLE FileHandle,
IN HANDLE Event,
//IN PIO_APC_ROUTINE ApcRoutine,
IN void* x,
IN PVOID ApcContext,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN BOOLEAN ReturnSingleEntry,
IN PUNICODE_STRING FileMask,
IN BOOLEAN RestartScan );
NQDF nqdf = NULL;
unsigned long FileCount = 0, DirCount = 0;
HMODULE hNtdll = NULL;
#define BACKSLASH L"\\"
#define SUCCESS TRUE
#define FAILURE FALSE
#define ALLOCSIZE sizeof(FILE_FULL_DIR_INFORMATION) * 512
void ntTraverse(PWSTR DirName)
{
HANDLE hFile=INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK IoStatusBlock;
PFILE_FULL_DIR_INFORMATION DirInfo=NULL, buffer=NULL;
DWORD err;
BOOLEAN done = TRUE;
hFile = CreateFileW(DirName,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL
);
if (hFile == INVALID_HANDLE_VALUE)
{
wprintf( L"Error 0x%x opening Directory %ws\n", GetLastError(), DirName);
return;
}
buffer = (PFILE_FULL_DIR_INFORMATION)calloc(ALLOCSIZE,1);
if (buffer == NULL)
{
wprintf( L"Memory allocation failed\n");
wprintf( L"error = %ld\n", GetLastError());
exit(1);
}
if ((nqdf = (NQDF) GetProcAddress( hNtdll, "NtQueryDirectoryFile" )) == NULL)
{
wprintf( L"NQDF() missing from NTDLL.DLL\n" );
FreeLibrary( hNtdll );
exit(1);
}
err = nqdf(hFile, NULL, NULL, NULL, &IoStatusBlock, buffer, ALLOCSIZE, FileFullDirectoryInformation, 0, NULL, FALSE);
DirInfo = buffer;
while (err == 0 && DirInfo != NULL)
{
while(done == TRUE)
{
ULONG NameLength = 0;
PWSTR pathname = NULL, FileString = NULL;
size_t pathname_len = 0;
NameLength = DirInfo->FileNameLength;
FileString = DirInfo->FileName;
pathname_len = wcslen(DirName) + wcslen(BACKSLASH) + NameLength + 1;
pathname = (PWSTR)calloc(pathname_len,sizeof(WCHAR));
if(pathname == NULL)
{
wprintf( L"could not allocate pathname\n");
exit(1);
}
wcscpy(pathname, DirName);
wcscat(pathname, BACKSLASH);
wcscat(pathname, FileString);
if(DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
/* Is a directory */
if (wcscmp(DirInfo->FileName, L".") && wcscmp(DirInfo->FileName, L".."))
{
printf("Directory name = %ws\n", pathname);
DirCount++;
ntTraverse(pathname);
}
}
else
{
/* Is a file */
wprintf( L"Filename = %ws\n", pathname);
FileCount++;
}
printf("offset = %lu\n", DirInfo->NextEntryOffset);
if ( DirInfo->NextEntryOffset == 0 )
done=FALSE;
else
{
DirInfo = (PFILE_FULL_DIR_INFORMATION)(((PUCHAR)DirInfo) + (DirInfo->NextEntryOffset));
wprintf( L"name = %ws\n", DirInfo->FileName);
}
free(pathname);
}
err = nqdf(hFile, NULL, NULL, NULL, &IoStatusBlock, buffer, ALLOCSIZE, FileFullDirectoryInformation, 0, NULL, FALSE);
DirInfo = buffer;
}
free(buffer);
CloseHandle(hFile);
}
BOOLEAN LoadDll()
{
BOOLEAN ret = SUCCESS;
hNtdll = LoadLibrary( L"ntdll.dll" );
if ( hNtdll == NULL )
{
wprintf( L"Error loading NTDLL.DLL\nerror = 0x%x\n", GetLastError() );
ret = FAILURE;
}
return ret;
}
int wmain(int argc, wchar_t *argv[])
{
time_t StartTime, EndTime;
BOOLEAN ret = SUCCESS;
if(argc != 2)
{
printf("Usage : traversal.exe <Directory Name>\n");
ret = FAILURE;
}
else
{
if (LoadDll() == SUCCESS)
{
time(&StartTime);
ntTraverse(argv[1]);
time(&EndTime);
wprintf( L"time required = %llu\n", EndTime-StartTime);
wprintf( L"FileCount = %d\nDirCount = %d\n", FileCount, DirCount);
FreeLibrary( hNtdll );
}
else
{
printf("Exiting application\n");
ret = FAILURE;
}
}
return ret;
}
However, when I update the DirInfo pointer as in:
Code:
DirInfo = (PFILE_FULL_DIR_INFORMATION)(((PUCHAR)DirInfo) + (DirInfo->NextEntryOffset));
The pointer gets updated, however the file name gets appended with a `
ex: for a directory xyz : xyz`
What must be the problem ?
Thanks