Finding cluster info of a file - c++

I've been trying to find information on a file by using CreateFile() and DeviceIoControl(). However I keep running into ERROR_HANDLE_EOF which from my understanding means that the starting virtual cluster number is past the end of the file even though I am starting at 0. Here is a few snippets of my code, let me know if you guys have any idea what's going wrong.
HANDLE hFile = INVALID_HANDLE_VALUE; //drive or file to be checked
LPWSTR txtFile = L"Text.txt"; //text file
hFile = CreateFile(txtFile, //target file
GENERIC_READ | GENERIC_WRITE, //read and write
FILE_SHARE_READ|FILE_SHARE_WRITE,//allows sharing of read and writes
NULL, //security prevents child process from inheriting the handle
OPEN_EXISTING, //open file or drive if it exist
FILE_ATTRIBUTE_NORMAL, //default settings for files
NULL); //template file with generiv read access rights
if (hFile == INVALID_HANDLE_VALUE) //error handling
{
cout<<"File does not exist"<<endl;
CloseHandle (hFile);
system("pause");
}
cout<<"you opened the file succesfully: "<<hFile<<endl;
STARTING_VCN_INPUT_BUFFER startVcn;
RETRIEVAL_POINTERS_BUFFER retrievalBuffer;
DWORD error =ERROR_MORE_DATA;
BOOL returns;
startVcn.StartingVcn.QuadPart = 0;
while( error ==ERROR_MORE_DATA){
DWORD bytesReturned;
returns = DeviceIoControl(hFile,
FSCTL_GET_RETRIEVAL_POINTERS,
&startVcn,
sizeof(STARTING_VCN_INPUT_BUFFER),
&retrievalBuffer,
sizeof(RETRIEVAL_POINTERS_BUFFER),
&bytesReturned,
NULL);
error = GetLastError();
switch(error){
case ERROR_HANDLE_EOF:
cout<<"ERROR_HANDLE_EOF"<<endl;
returns = true;
break;
case ERROR_MORE_DATA:
cout<<"ERROR_MORE_DATA"<<endl;
startVcn.StartingVcn = retrievalBuffer.Extents[0].NextVcn;
case NO_ERROR:
cout<<"NO_ERROR, here is some info: "<<endl
<<retrievalBuffer.StartingVcn.QuadPart<<endl
<<retrievalBuffer.Extents[0].Lcn.QuadPart<<endl
<<retrievalBuffer.Extents[0].NextVcn.QuadPart
- retrievalBuffer.StartingVcn.QuadPart<<endl;
returns = true;
break;
default:
cout<<"Error in the code or input error"<<endl;
break;
}
}

I wrote pretty much the same program myself and, like the OP, I found that I was getting ERROR_HANDLE_EOF which I couldn’t explain. When I went looking for an explanation, I never found one but stumbled across this post. The OP’s program looks correct to me so I strongly suspect the explanation for the behaviour is the same as mine. I realize it is way too late to help the OP but for the benefit of anyone else who spends 1 whole day working this out, I think the answer is…
Educated guess: ‘Small files’ don’t get a cluster assigned to them and when you query a ‘small file’ with the above program you will receive ERROR_HANDLE_EOF. This sort of makes sense, but is very unfriendly and useless to a newbie like myself. Experiments on the machine I have led me to believe that a ‘small file’ is 736 bytes or less. 737 bytes or more starts using clusters. I found these limits by performing a binary search on various file sizes using Linux/cygwin ‘dd’ command to generate files of the required size.
At this time, I have not found anyone supporting my claim about 736 vs 737 bytes, but http://www.ntfs.com/ntfs_optimization.htm does say “On NTFS if file is small enough, it can be stored in MFT record itself without using additional clusters.”
My systems are using Windows 10, NTFS simple volumes. I don’t understand NTFS, the MFT or attributes very well. Perhaps someday I will find the appropriate structure that explains why this number comes about (like structure can be up to 1K and already has 288 bytes in use??).

Related

C++ How to get MailSlot Handle via MailSlotName

We are using window's mailslots in one of our Windows 10 application.
Usually windows is very good at managing them, but we recently came across an issue where the MailSlot does not close anymore, meaning every time our application is started it leads to a crash.
Usually we would diagnose the issue to know exactly where it comes from, but in this case it would require paralysing the production line for several days, which we can't afford.
So we decided to create a script/application to close the mailslot if it exists when we start the program, since the program is currently being launched through a batch file.
I have tried this :
HANDLE OpenProcessByName(LPCTSTR Name, DWORD dwAccess)
{
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 pe;
ZeroMemory(&pe, sizeof(PROCESSENTRY32));
pe.dwSize = sizeof(PROCESSENTRY32);
Process32First(hSnap, &pe);
do
{
if (!lstrcmpi(pe.szExeFile, Name))
{
return OpenProcess(dwAccess, 0, pe.th32ProcessID);
}
} while (Process32Next(hSnap, &pe));
}
return INVALID_HANDLE_VALUE;
}
As you can expect, this return process names not mailslot names
Is there any way to modify the code snippet above to work with mailslot names (for the sake of examples we can assume that the mailslot is located here : \Device\Mailslot\sample_mailslot with sample_mailslot being its name)
Or is there a different way to go about things ?
PS :
I know that the best way would be to store the HANDLE that's being returned when we create the mailslot, the issue is we don't have access to the source code, so we are forced to work outside it.

I'm using QDir().isReadable to check if a drive is readable. In the Qt Creator it runs fine, but when I run the exe it keeps giving me errors

I'm using it like this:
if(QDir("G:/").isReadable()){
qDebug("G Is readable!"); //Do something with G:/
}
As I said, in Qt Creator it runs fine without a problem, it checks if the drive is readable, and if so it prints it to the console, if not, it does nothing.
But when I run the .exe file, it keeps giving me errors each time it does the check (every 2 seconds) if the drive isn't readable.
"There is no disk in the drive. Please insert a disk into drive G:."
I don't want this error to keep appearing, what do I do?
Edit: I think it's the isReadable function that causes the problem, is there any other way to do what I want to do? Or maybe should I write the code myself?
This message is generated by Windows.
There is a workaround for users that have applications that cannot be fixed. The error messages may be suppressed by setting 2 to registry key ErrorMode in:
Computer\HKEY_LOCAL\MACHINE\SYSTEM\CurrentControlSet\Control\Windows
It looks that if QDir::isReadable() is called after removing the media it triggers that error. QDir::exists() always returns true if the drive letter is present in the system, so it cannot be used here.
For now I see that it is possible to check removable media using native Windows API, see the answer to How to detect if media is inserted into a removable drive/card reader
The following code is able to detect that the media is removed without triggering the error:
#include <windows.h>
HANDLE hDevice = CreateFile (L"\\\\.\\G:", // like "\\.\G:"
FILE_READ_ATTRIBUTES, // read access to the attributes
FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
NULL, OPEN_EXISTING, 0, NULL);
if (hDevice == INVALID_HANDLE_VALUE) {
// not valid device
return;
}
WORD cbBytesReturned;
bool bSuccess = DeviceIoControl (hDevice, // device to be queried
IOCTL_STORAGE_CHECK_VERIFY2,
NULL, 0, // no input buffer
NULL, 0, // no output buffer
(LPDWORD)&cbBytesReturned, // # bytes returned
NULL); // synchronous I/O
CloseHandle(hDevice); // close handle
if (bSuccess && QDir("G:/").isReadable()) {
// G is readable
}

Why is CreateFile failing to open a file across a network share?

I wrote a small program which frequently opens small, user text files and until now haven't encountered any problems with read/write access or any sorts of conflict. The files are selected in another piece of software which I have no control over, and are passed to me as a string.
When attempting to open a file from a mapped network drive I am getting a "The system cannot find the path specified" error (GetLastError() = 3).
The call is shown below, *iNCfileName = "z:\\Validation\\Sample Files\\1_1-4 120MM.CC", where Z: is a mapped folder on our domain.
iNCfile = CreateFile( iNCfileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if ( iNCfile == INVALID_HANDLE_VALUE )
{
string msg; // lots of better ways to get this printed ... but ...
dw = GetLastError();
msg = iNCfileName;
msg += ": ";
msg += _com_error(dw).ErrorMessage();
print_error(dw , (char*)msg.c_str() );
return 102;
}
The file opens from my program if I copy it to the local hard drive. It also opens in notepad from the mapped drive.
Could this be a problem between the "Z:\whatever.txt" mapped representation and the true file name (\mydomain\Validation\S....??)?
If so, how can I convert from one to the other in a programmatic way (assume I won't know the domain/share names ahead of time)?
If it makes any difference I use VS2010 and the application executes on a Win XP machine.
Related: my follow up question
I've encountered this before. When using a path like \\DOMAIN\PATH\FILE.TXT I had to first call WNetAddConnection2().
Here is my code (of course you can exclude the NULL members):
NETRESOURCE nr = {0}; //new structure for network resource
nr.dwType = RESOURCETYPE_ANY; //generic resource (any type allowed)
nr.lpLocalName = NULL; //does not use a device
nr.lpRemoteName = "\\\\DOMAIN\\PATH\\FOLDER"; //For me, this pointed to an account's documents folder, and from there I could use a subfolder
nr.lpProvider = NULL; //no provider
DWORD ret = WNetAddConnection2 (&nr, NULL, NULL, CONNECT_TEMPORARY); //add connection
Don't forget the header and library.
I just had the same issue; trying to create a file using API CreateFileW under a mapped drive ( Z:\folder ) did not worked; howerver, after researching this subject i tried to create the file using the real path ( \\Shared_computer_name\folder\ ) immediately worked successfully.
Now I have to work a function to retrieve the real name of a mapped drive, to use it when necessary... just found WNetGetUniversalName, have to make it to work.

Unzip by using the "CompressedFolder" COM object

I unpack a zip archive using Win API. This API is based on COM interfaces; the COM model is accessible through the CompressFolder COM object.
I encountered the following problem. When I unpack a small file (3.5 MB) it takes a long time. I figured out that IStream::Read() causes this problem. It works slowly. I use a small buffer (1KB) to read this file in many iterations; if I use a buffer that nearly equals the file size, then it works much faster.
How can I get it to unpack fast even if the buffer size is much smaller than file size? Is it possible? I think it is important because the files may be big, say 1 GB.
Here is a fragment of the code that reads a file:
...
CComPtr<IEnumSTATSTG> pEnum = NULL;
pStorage->EnumElements(0, NULL, 0, &pEnum);
STATSTG stasStg;
while (S_OK == pFolderEnum->Next(1, &stasStg, NULL)) {
if (stasStg.type == STGTY_STREAM) {
CComPtr<IStream> pStream = NULL;
pStorage->OpenStream(stasStg.pwcsName, NULL, STGM_READ, NULL, &pStream);
...
while (hr == S_OK) {
// reading
pStream->Read(btBuffer, 1024, &ulByresRead); // it works slowly
}
}
}
A side question I have:
Is there method to detect a packed file size through IStream without reading the file?
It is not possible to achieve fast read with small buffers. Indeed, the more I/O operation you do, the more time it takes.
Try to limit the number of I/O operation by taking a relatively big buffer size. Then of course you must limit it in accordance with the memory you want to allocate to your program.
Aside, you may get delay because program loads libraries. This doesn't happen for Winzip if associated dll already is loaded.

How to use file bandwidth reservation (scheduled file IO) in Windows

I need to implement an application that streams data from disk. It is important that the data throughput is fairly constant and is not interupted by any other activity on the disk.
From Windows Vista onwards, the GetFileBandwidthReservation() and SetFileBandwidthReservation() functions have been introduced specifically for this purpose. However, I cannot get this to work. I've searched the internet but I cannot find much information on this (and no working code samples seem to exist online).
Code to reproduce:
HANDLE h = ::CreateFile(L"D:\\testfile", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
DWORD periodMilliseconds, bytesPerPeriod, transferSize, numOutstandingRequests;
BOOL discardable;
BOOL result = ::GetFileBandwidthReservation(h, &periodMilliseconds, &bytesPerPeriod,
&discardable, &transferSize, &numOutstandingRequests);
if (result == FALSE) // result is always false!
{
DWORD reason = ::GetLastError(); // reason is always 1!
std::cout << "Error: " << reason << std::endl;
}
result = ::CloseHandle(h);
The call to GetFileBandwidthReservation always returns FALSE which indicates a failure. GetLastError returns 1 which isn't very helpfull. If a try to invoke *Set*FileBandwithReservation I get the same result.
I am testing this on a PC with Windows Server 2008 SP2 (32-bit).
Does anybody have any idea of what I am doing wrong? Any help will be greatly appreciated.
This requires support from the disk device driver. The kind of driver that you'd find in an upscale server, not a consumer level machine. Ask more questions about this at serverfault.com