Finding the last cluster of a file - c++

I'm trying to find the last cluster of a target file and read the binary data off of it. I started off with CreateFile() and used that result in DeviceIoControl() with control code FSCTL_GET_RETRIEVAL_POINTERS.
hfile = CreateFile(result,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
RETRIEVAL_POINTERS_BUFFER retrievalBuffer;
const DWORD Clusters = 1000;
const DWORD rpBufferSize = sizeof(RETRIEVAL_POINTERS_BUFFER) + (2 * (Clusters - 1) *sizeof(LARGE_INTEGER));
BYTE output[rpBufferSize];
STARTING_VCN_INPUT_BUFFER startVcn;
startVcn.StartingVcn.QuadPart = 0;
returns = DeviceIoControl(hfile,
FSCTL_GET_RETRIEVAL_POINTERS,
&startVcn,
sizeof(startVcn),
&output,
sizeof(output),
&bytesReturned,
NULL);
So I don't really know what to do next. If I display LARGE_INTEGER Lcn from the RETRIEVAL_POINTERS_BUFFER I get a huge number which represents the current extent. I also have a switch error case which comes up as NO_ERROR so I am assuming that all the cluster data was read successfully. What can I do which the Lcn number to help me find the last cluster of the file?

retrievalBuffer should be a pointer:
RETRIEVAL_POINTERS_BUFFER *retrievalBuffer = (RETRIEVAL_POINTERS_BUFFER *) output;
So the last extent starts at
DWORD lastExtentN = retrievalBuffer->ExtentCount - 1;
LARGE_INTEGER extentLcn = retrievalBuffer->Extents[ lastExtentN ].Lcn;
The extent size is
LARGE_INTEGER extentClusters = retrievalBuffer->Extents[ lastExtentN ].NextVcn
- lastExtentN ? retrievalBuffer->Extents[ lastExtentN - 1 ].NextVcn
: retrievalBuffer->StartingVcn;
Thus, last logical cluster number (LCN) of the file is:
LARGE_INTEGER lastLcn = extentLcn + extentClusters - 1;
Now you can open logical volume using CreateFile() and read this cluster using ReadFile()
NOTE: you need to check extentLcn against -1 to support sparse files.

Related

Writing USB Disk Sectors Results in Permission Error 5

The code below is required by my app to write disk sectors of external USB drives. It works on most Win10 PCs, but it's returning error 5 for permission denied on a couple PCs. I have exclusions created for both Windows Defender and Malwarebytes. There's nothing in the event viewer related to the failure. The read function works without error.
I tried adding calls to FSCTL_LOCK_VOLUME and FSCTL_DISMOUNT_VOLUME, but this doesn't help. Probably not needed anyway since I'm accessing the physical disk after it's been cleaned, and not any volumes.
Any idea what could cause this, or how to resolve?
Would be great to learn if there's any alternate methods of reading and writing disk sectors.
BOOL Partitioner::BlockWrite(wchar_t* devIdentifier, unsigned __int64 lngStartbyte, DWORD bytesToRead, BYTE* buf)
{
BOOL ret = FALSE;
HANDLE devHan = CreateFile(devIdentifier, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (devHan != INVALID_HANDLE_VALUE)
{
// Seek to the starting block to write
LARGE_INTEGER startByte;
startByte.QuadPart = lngStartbyte;
SetFilePointer(devHan, startByte.LowPart, (long*)&startByte.HighPart, FILE_BEGIN);
// Write the data (this is where error 5 is returned)
DWORD bytesWritten = 0;
ret = WriteFile(devHan, buf, bytesToRead, &bytesWritten, NULL);
FlushFileBuffers(devHan);
CloseHandle(devHan);
}
else
{
ret = GetLastError();
wchar_t msg[PATH_BUFFER_SIZE] = {0};
swprintf_s(msg, WCSIZE_FULL(msg), L"Error= %d, byte= %llu", ret, lngStartbyte);
mLog->LogError(msg);
}
return ret;
}
I found the answer where I wasn't expecting. I thought this had to be something related to how the file handles were being opened. Instead, turning off Real-time protection in the Virus threat protection settings for Windows 10 caused the error 5s to go away. To resolve without disabling real-time protection, you need to add an allowed app exclusion for each of the installed EXEs.
You can do this in code by scripting PowerShell:
string script = "powershell -Command \"Add-MpPreference -ControlledFolderAccessAllowedApplications '" + GetAppPath() + "\\AppServer.exe" + "'";
Process.Start(new ProcessStartInfo() { FileName = "cmd.exe", Arguments = "/c " + script, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden }).WaitForExit();

Enumerate all partitions and test if they are NTFS

I'm using:
DWORD d = GetLogicalDrives();
for (int i = 0; i < 26; i++)
{
if ((1 << i) & d) // drive letter 'A' + i present on computer
{
wstring s = std::wstring(L"\\\\.\\") + wchar_t('A' + i) + L":";
PARTITION_INFORMATION diskInfo;
DWORD dwResult;
HANDLE dev = CreateFile(LPWSTR(s.c_str()), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &diskInfo, sizeof(diskInfo), &dwResult, NULL);
CloseHandle(dev);
if (diskInfo.PartitionType == PARTITION_IFS)
{
...
}
}
}
to enumerate all NTFS partitions of a computer.
It works on my Windows 7, on a Windows 8.1 I tried it on, and on a Windows 10 computer.
But it fails on another Windows 10 computer: on this one, the volume C: has a diskInfo.PartitionType value equal to 0x00, instead of 0x07 (PARTITION_IFS).
This value is (see the doc here):
PARTITION_ENTRY_UNUSED : 0x00 : An unused entry partition.
This is strange, since, I can confirm, the partition is really NTFS.
Questions:
Is it well-known that IOCTL_DISK_GET_PARTITION_INFO is not 100% reliable to get the partition type?
What would be a more reliable way to enumerate all NTFS volumes?
Note: I also looked at using IOCTL_DISK_GET_PARTITION_INFO_EX instead of IOCTL_DISK_GET_PARTITION_INFO but then the structure PARTITION_INFORMATION_EX does not seem to give informations about PartitionType, whereas the structure PARTITION_INFORMATION does give access to PartitionType.
As #RemyLebeau says, you are not checking the return value for each call.
PARTITION_ENTRY_UNUSED often means the DeviceIoControl() call failed. It depends on the permissions of your user. You should check your user's access rights to see if it has the FILE_READ_DATA permission (included in GENERIC_READ) on volume C:. In my test environment, if you have no access to open volume C: with GENERIC_READ, CreateFile() returns INVALID_HANDLE_VALUE, and then DeviceIoControl() fails as well.
EDIT:
I suggest using GetVolumeInformation(), for example:
wchar_t fs[MAX_PATH + 1] = { 0 };
GetVolumeInformationW(L"C:\\", NULL, 0, NULL, NULL, NULL, fs, MAX_PATH + 1);
And you will see the Type info in the fs buffer.
I did further investigation thanks to #RemyLebeau's comments with:
HANDLE dev = CreateFile(..., GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
if (dev == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError(); // then MessageBox
}
else
{
BOOL ret = DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &diskInfo, sizeof(diskInfo), &dwResult, NULL);
if (ret == FALSE)
{
DWORD err = GetLastError(); // then MessageBox
}
CloseHandle(dev);
}
on the computer where it failed (computer with Windows 10). I found that CreateFile succeeded but then DeviceIoControl failed with GetLastError being 1 i.e. ERROR_INVALID_FUNCTION (see System Error Codes (0-499)).
Conclusion (I quote Remy's comment):
That means IOCTL_DISK_GET_PARTITION_INFO is not supported by the device you passed to DeviceIoControl().
I then tried with IOCTL_DISK_GET_PARTITION_INFO_EX:
PARTITION_INFORMATION_EX diskInfo;
BOOL ret = DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &diskInfo, sizeof(diskInfo), &lpBytesReturned, NULL);
and then it worked. I could see that diskInfo.PartitionStyle was PARTITION_STYLE_GPT (=1), and this was the reason why IOCTL_DISK_GET_PARTITION_INFO failed. I quote Remy's comment again:
IOCTL_DISK_GET_PARTITION_INFO is not supported on GPT partitioned drives.
So here's the conclusion:
use IOCTL_DISK_GET_PARTITION_INFO_EX instead of IOCTL_DISK_GET_PARTITION_INFO
if diskInfo.PartitionStyle is 0 (PARTITION_STYLE_MBR) then diskInfo.Mbr.PartitionType can be tested. If it's 0x07, it's NTFS.
if diskInfo.PartitionStyle is 1 (PARTITION_STYLE_GPT) then diskInfo.Gpt.PartitionType can be tested, see here: https://learn.microsoft.com/en-us/windows/desktop/api/winioctl/ns-winioctl-_partition_information_gpt. Even if the NTFS Wikipedia page mentions the GUID EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 for NTFS in the GPT case, this GUID is in fact unrelated to file system (see comment below about this).
it's probably easier to use GetVolumeInformation() instead and just compare if the result is the "NTFS" string, as in the other answer
in my particular case, I initially wanted to test if a volume is NTFS or not before attempting an indexing with DeviceIoControl(hVol, FSCTL_ENUM_USN_DATA, ...) because I thought such MFT querying would be limited to NTFS volumes. In fact, an easier solution would be to NOT TEST if it's NTFS or not, and just do the FSCTL_ENUM_USN_DATA. The worst that can happen is that FSCTL_ENUM_USN_DATA fails with ERROR_INVALID_FUNCTION error, per the documentation:
"ERROR_INVALID_FUNCTION The file system on the specified volume does not support this control code."

How to initialize disk on Windows server 2008/2012 through a C++ program

We are trying to initialize disk with the properties of some existing disk on Windows server 2008/2012 through a C++ program.
We are using DeviceIoControl() method and IOCTL_DISK_CREATE_DISK, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, IOCTL_DISK_SET_PARTITION_INFO_EX codes from Disk management control codes to make the disk available for use.
Got the following code snippet by searching a bit
//To open the drive
hDevice = CreateFile( TEXT("\\\\.\\PhysicalDrive7"),
GENERIC_READ | GENERIC_WRITE, // no access to the drive
FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
NULL, // default security attributes
OPEN_EXISTING, // disposition
0, // file attributes
NULL); // do not copy file attributes
CREATE_DISK dsk;
dsk.PartitionStyle = PARTITION_STYLE_MBR; //It can also be PARTITION_STYLE_GPT
dsk.Mbr.Signature = 1;
// Initialize disk
bResult = DeviceIoControl( hDevice, // device to be queried
IOCTL_DISK_CREATE_DISK, // operation to perform
&dsk, sizeof(dsk),
NULL, 0, // no output buffer
&junk, // # bytes returned
NULL
);
LARGE_INTEGER lgPartitionSize;
lgPartitionSize.QuadPart = (1024 * 1024 * 1024);
DWORD dwDriverLayoutInfoExLen = sizeof (DRIVE_LAYOUT_INFORMATION_EX) + 3 * sizeof(PARTITION_INFORMATION_EX);
DRIVE_LAYOUT_INFORMATION_EX *pdg = (DRIVE_LAYOUT_INFORMATION_EX *)new BYTE[dwDriverLayoutInfoExLen];
SecureZeroMemory(pdg, dwDriverLayoutInfoExLen);
pdg->PartitionStyle = PARTITION_STYLE_MBR;
pdg->PartitionCount = 1;
pdg->Mbr.Signature = 1;
pdg->PartitionEntry[0].PartitionStyle = PARTITION_STYLE_MBR;
pdg->PartitionEntry[0].StartingOffset.QuadPart = 1048576;
pdg->PartitionEntry[0].PartitionLength.QuadPart = lgPartitionSize.QuadPart * 200;
pdg->PartitionEntry[0].PartitionNumber = 1;
pdg->PartitionEntry[0].RewritePartition = TRUE;
pdg->PartitionEntry[0].Mbr.PartitionType = PARTITION_NTFT; // PARTITION_IFS (NTFS partition or logical drive)
pdg->PartitionEntry[0].Mbr.BootIndicator = TRUE;
pdg->PartitionEntry[0].Mbr.RecognizedPartition = 1;
pdg->PartitionEntry[0].Mbr.HiddenSectors = 32256 / 512;
// Partition a disk
bResult = DeviceIoControl( hDevice, // device to be queried
IOCTL_DISK_SET_DRIVE_LAYOUT_EX, // operation to perform
pdg, sizeof DRIVE_LAYOUT_INFORMATION_EX, //output buffer
NULL, 0, // no output buffer
&junk, // # bytes returned
NULL
);
bResult = DeviceIoControl( hDevice,
IOCTL_DISK_UPDATE_PROPERTIES,
NULL, 0, NULL, 0, &junk, NULL);
PARTITION_INFORMATION_EX dskinfo;
PARTITION_INFORMATION_MBR mbrinfo;
mbrinfo.PartitionType = PARTITION_NTFT;
mbrinfo.HiddenSectors = (32256 / 512);
mbrinfo.BootIndicator = 1;
mbrinfo.RecognizedPartition = 1;
dskinfo.PartitionStyle = PARTITION_STYLE_MBR;
dskinfo.StartingOffset.QuadPart = 1048576;//0;
dskinfo.PartitionLength.QuadPart = lgPartitionSize.QuadPart * 200;
dskinfo.PartitionNumber = 1;
dskinfo.RewritePartition = TRUE;
dskinfo.Mbr = mbrinfo;
bResult = DeviceIoControl( hDevice, // device to be queried
IOCTL_DISK_SET_PARTITION_INFO_EX, // operation to perform
&dskinfo, sizeof(dskinfo), // output buffer
NULL, 0, // no output buffer
&junk, // # bytes returned
NULL
);
All the calls to DeviceIoControl() are getting succeeded except the last one with IOCTL_DISK_SET_PARTITION_INFO_EX code with error 1 (i.e Incorrect function). What could be the reason for this?
If we comment out the last call, the disk is being initialized as raw disk, But this won't meet our requirements.
The above sample is for MBR partition style only. We could not find any sample for GPT,... styles. Please give a link if someone is aware of one.
You're using the wrong structure type with IOCTL_DISK_SET_PARTITION_INFO_EX. It takes a SET_PARTITION_INFORMATION_EX structure, not a PARTITION_INFORMATION_EX structure.
You probably don't need to use IOCTL_DISK_SET_PARTITION_INFO_EX, since it just sets the partition type, which should have already been set with IOCTL_DISK_SET_DRIVE_LAYOUT_EX. Unfortunately you've used it to set the wrong partition type. NTFS partitions have the partition type PARTITION_IFS.
Multiplying lgPartitionSize by 200 is almost certainly wrong. If lgPartitionSize is supposed the size in sectors then you need to multiply this by the sector size of the disk. The sector size of hard drives used to be always 512 bytes (0x200 bytes), but modern drives use a 4096 byte sector size.
Correctly creating partition tables is not easy, and mindlessly copying other people's code like you've done isn't going to work. Even after you fix the problems I've mentioned above you're still likely to encounter other issues. You really need to understand the all the restrictions placed on how partitions should be laid out.
You might want to consider using the diskpart command to programically initialize disks instead of C++ code.

How can I detect only deleted, changed, and created files on a volume?

I need to know if there is an easy way of detecting only the files that were deleted, modified or created on an NTFS volume.
I have written a program for offsite backup in C++. After the first backup, I check the archive bit of each file to see if there was any change made, and back up only the files that were changed. Also, it backs up from the VSS snapshot in order to prevent file locks.
This seems to work fine on most file systems, but for some with lots of files and directories, this process takes too long and often the backup takes more than a day to finish backing up.
I tried using the change journal to easily detect changes made on an NTFS volume, but the change journal would show a lot of records, most of them relating to small temporary files created and destroyed. Also, I could the file name, file reference number, and the parent file reference number, but I could not get the full file path. The parent file reference number is somehow supposed to give you the parent directory path.
EDIT: This needs to run everyday, so at the beginning of every scan, it should record only the changes that took place since the last scan. Or atleast, there should be a way to say changes since so and so time and date.
You can enumerate all the files on a volume using FSCTL_ENUM_USN_DATA. This is a fast process (my tests returned better than 6000 records per second even on a very old machine, and 20000+ is more typical) and only includes files that currently exist.
The data returned includes the file flags as well as the USNs so you could check for changes whichever way you prefer.
You will still need to work out the full path for the files by matching the parent IDs with the file IDs of the directories. One approach would be to use a buffer large enough to hold all the file records simultaneously, and search through the records to find the matching parent for each file you need to back up. For large volumes you would probably need to process the directory records into a more efficient data structure, perhaps a hash table.
Alternately, you can read/reread the records for the parent directories as needed. This would be less efficient, but the performance might still be satisfactory depending on how many files are being backed up. Windows does appear to cache the data returned by FSCTL_ENUM_USN_DATA.
This program searches the C volume for files named test.txt and returns information about any files found, as well as about their parent directories.
#include <Windows.h>
#include <stdio.h>
#define BUFFER_SIZE (1024 * 1024)
HANDLE drive;
USN maxusn;
void show_record (USN_RECORD * record)
{
void * buffer;
MFT_ENUM_DATA mft_enum_data;
DWORD bytecount = 1;
USN_RECORD * parent_record;
WCHAR * filename;
WCHAR * filenameend;
printf("=================================================================\n");
printf("RecordLength: %u\n", record->RecordLength);
printf("MajorVersion: %u\n", (DWORD)record->MajorVersion);
printf("MinorVersion: %u\n", (DWORD)record->MinorVersion);
printf("FileReferenceNumber: %lu\n", record->FileReferenceNumber);
printf("ParentFRN: %lu\n", record->ParentFileReferenceNumber);
printf("USN: %lu\n", record->Usn);
printf("Timestamp: %lu\n", record->TimeStamp);
printf("Reason: %u\n", record->Reason);
printf("SourceInfo: %u\n", record->SourceInfo);
printf("SecurityId: %u\n", record->SecurityId);
printf("FileAttributes: %x\n", record->FileAttributes);
printf("FileNameLength: %u\n", (DWORD)record->FileNameLength);
filename = (WCHAR *)(((BYTE *)record) + record->FileNameOffset);
filenameend= (WCHAR *)(((BYTE *)record) + record->FileNameOffset + record->FileNameLength);
printf("FileName: %.*ls\n", filenameend - filename, filename);
buffer = VirtualAlloc(NULL, BUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (buffer == NULL)
{
printf("VirtualAlloc: %u\n", GetLastError());
return;
}
mft_enum_data.StartFileReferenceNumber = record->ParentFileReferenceNumber;
mft_enum_data.LowUsn = 0;
mft_enum_data.HighUsn = maxusn;
if (!DeviceIoControl(drive, FSCTL_ENUM_USN_DATA, &mft_enum_data, sizeof(mft_enum_data), buffer, BUFFER_SIZE, &bytecount, NULL))
{
printf("FSCTL_ENUM_USN_DATA (show_record): %u\n", GetLastError());
return;
}
parent_record = (USN_RECORD *)((USN *)buffer + 1);
if (parent_record->FileReferenceNumber != record->ParentFileReferenceNumber)
{
printf("=================================================================\n");
printf("Couldn't retrieve FileReferenceNumber %u\n", record->ParentFileReferenceNumber);
return;
}
show_record(parent_record);
}
void check_record(USN_RECORD * record)
{
WCHAR * filename;
WCHAR * filenameend;
filename = (WCHAR *)(((BYTE *)record) + record->FileNameOffset);
filenameend= (WCHAR *)(((BYTE *)record) + record->FileNameOffset + record->FileNameLength);
if (filenameend - filename != 8) return;
if (wcsncmp(filename, L"test.txt", 8) != 0) return;
show_record(record);
}
int main(int argc, char ** argv)
{
MFT_ENUM_DATA mft_enum_data;
DWORD bytecount = 1;
void * buffer;
USN_RECORD * record;
USN_RECORD * recordend;
USN_JOURNAL_DATA * journal;
DWORDLONG nextid;
DWORDLONG filecount = 0;
DWORD starttick, endtick;
starttick = GetTickCount();
printf("Allocating memory.\n");
buffer = VirtualAlloc(NULL, BUFFER_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (buffer == NULL)
{
printf("VirtualAlloc: %u\n", GetLastError());
return 0;
}
printf("Opening volume.\n");
drive = CreateFile(L"\\\\?\\c:", GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_FLAG_NO_BUFFERING, NULL);
if (drive == INVALID_HANDLE_VALUE)
{
printf("CreateFile: %u\n", GetLastError());
return 0;
}
printf("Calling FSCTL_QUERY_USN_JOURNAL\n");
if (!DeviceIoControl(drive, FSCTL_QUERY_USN_JOURNAL, NULL, 0, buffer, BUFFER_SIZE, &bytecount, NULL))
{
printf("FSCTL_QUERY_USN_JOURNAL: %u\n", GetLastError());
return 0;
}
journal = (USN_JOURNAL_DATA *)buffer;
printf("UsnJournalID: %lu\n", journal->UsnJournalID);
printf("FirstUsn: %lu\n", journal->FirstUsn);
printf("NextUsn: %lu\n", journal->NextUsn);
printf("LowestValidUsn: %lu\n", journal->LowestValidUsn);
printf("MaxUsn: %lu\n", journal->MaxUsn);
printf("MaximumSize: %lu\n", journal->MaximumSize);
printf("AllocationDelta: %lu\n", journal->AllocationDelta);
maxusn = journal->MaxUsn;
mft_enum_data.StartFileReferenceNumber = 0;
mft_enum_data.LowUsn = 0;
mft_enum_data.HighUsn = maxusn;
for (;;)
{
// printf("=================================================================\n");
// printf("Calling FSCTL_ENUM_USN_DATA\n");
if (!DeviceIoControl(drive, FSCTL_ENUM_USN_DATA, &mft_enum_data, sizeof(mft_enum_data), buffer, BUFFER_SIZE, &bytecount, NULL))
{
printf("=================================================================\n");
printf("FSCTL_ENUM_USN_DATA: %u\n", GetLastError());
printf("Final ID: %lu\n", nextid);
printf("File count: %lu\n", filecount);
endtick = GetTickCount();
printf("Ticks: %u\n", endtick - starttick);
return 0;
}
// printf("Bytes returned: %u\n", bytecount);
nextid = *((DWORDLONG *)buffer);
// printf("Next ID: %lu\n", nextid);
record = (USN_RECORD *)((USN *)buffer + 1);
recordend = (USN_RECORD *)(((BYTE *)buffer) + bytecount);
while (record < recordend)
{
filecount++;
check_record(record);
record = (USN_RECORD *)(((BYTE *)record) + record->RecordLength);
}
mft_enum_data.StartFileReferenceNumber = nextid;
}
}
Additional notes
As discussed in the comments, you may need to replace MFT_ENUM_DATA with MFT_ENUM_DATA_V0 on versions of Windows later than Windows 7. (This may also depend on what compiler and SDK you are using.)
I'm printing the 64-bit file reference numbers as if they were 32-bit. That was just a mistake on my part. Probably in production code you won't be printing them anyway, but FYI.
The change journal is your best bet. You can use the file reference numbers to match file creation/deletion pairs and thus ignore temporary files, without having to process them any further.
I think you have to scan the Master File Table to make sense of ParentFileReferenceNumber. Of course you only need to keep track of directories when doing this, and use a data structure that will allow you to quickly lookup the information, so you only need to scan the MFT once.
You can use ReadDirectoryChanges and surrounding windows API.
I know how to achieve this in java. It will help you if you implement Java code inside C++.
In Java you can achieve this using Jnotify API.It looks for changes in sub-directory also.

How Do you set MOTW on an Executable

How do you set MOTW (Mark of the Web) on an executable that is downloaded from the internet?
This data is stored in an NTFS alternative file stream alongside an executable. The stream is called Zone.Identifier:
Windows® Internet Explorer® uses the stream name Zone.Identifier for storage of URL security zones.
The fully qualified form is sample.txt:Zone.Identifier:$DATA
The stream is a simple text stream of the form:
[ZoneTransfer]
ZoneId=3
MSDN-SECZONES gives an explanation of security zones.
(N.B. The original has a space between the colon and "Zone" but I think this is erroneous.)
You can find the ZoneIds in UrlMon.h in the SDK; there's an enum which equates to
enum URLZONE {
URLZONE_LOCAL_MACHINE = 0,
URLZONE_INTRANET = 1,
URLZONE_TRUSTED = 2,
URLZONE_INTERNET = 3,
URLZONE_RESTRICTED = 4
};
(The original uses previous value + 1 rather than absolute values.)
As Hans says in the comments, these can be written with the standard Win32 file APIs CreateFile and WriteFile.
Firefox always writes Internet Zone, zone 3 - Firefox code here (MPL/LGPL/GPL tri-license):
bool SetInternetZoneIdentifier(const FilePath& full_path) {
const DWORD kShare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
std::wstring path = full_path.value() + L":Zone.Identifier";
HANDLE file = CreateFile(path.c_str(), GENERIC_WRITE, kShare, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == file)
return false;
const char kIdentifier[] = "[ZoneTransfer]\nZoneId=3";
DWORD written = 0;
BOOL result = WriteFile(file, kIdentifier, arraysize(kIdentifier), &written,
NULL);
CloseHandle(file);
if (!result || written != arraysize(kIdentifier)) {
DCHECK(FALSE);
return false;
}
return true;
}
Alternatively there's an IE COM API CLSID_PersistentZoneIdentifier you can use to abstract this all for you.
It is not explicitly stated in RFC 3514, but today, due to increased security requirements, implementations should really retain the information of the presence or absence of the RFC3514 bit in a network transmission, when they write files out to disk, and vice-versa for reading from disk.