how to get size of a locked file? - c++

I've tried to use this approach:
#include <windows.h>
#include <iostream>
int main() {
LARGE_INTEGER size;
HANDLE hFile = CreateFile("c:\\pagefile.sys", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return(1);
GetFileSizeEx(hFile, &size);
CloseHandle(hFile);
std::cout << size.QuadPart << std::endl;
}
But as you see I point to "pagefile.sys" which is locked, and program encounters INVALID_HANDLE_VALUE. But non system apps can see sizes of locked files. For example total commander gives me about 1GB and it must get this value from somewhere (not to mention simple right-clicking on that file, but that is system process so file is not locked to it). So, are there any winapi calls for that case ?
I've updated code to included suggested corrections, but it still doesn't work:
#include <windows.h>
#include <iostream>
int main() {
LARGE_INTEGER size;
HANDLE hFile = CreateFile("c:\\pagefile.sys", 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
std::cout << "GetLastError: " << GetLastError() << std::endl;
//says: 5 (0x5) ERROR_ACCESS_DENIED
if (hFile == INVALID_HANDLE_VALUE) return(1);
GetFileSizeEx(hFile, &size);
CloseHandle(hFile);
std::cout << size.QuadPart << std::endl;
}

You can get info out of the directory entry for a file, there is no mechanism to lock that. Which requires FindFirstFile/FindNextFile to iterate the directory. The returned WIN32_FIND_DATA.nFileSizeHigh/Low gives you the info you want.
The actual number you get is not reliable, it is merely a snapshot and it is likely to be stale. Especially so for the paging file, Windows can rapidly change its size. Getting a reliable size requires locking the file so nobody can change it, like you did. Which will not work for the paging file, the operating system keeps a hard lock on it so nobody can mess with the file content or read security sensitive data from the file.

According to MSDN, you should set the dwDesiredAccess parameter to 0 (zero) if you only want information without opening the file.

Related

Is it possible to keep a handle of a ::ReplaceFile?

I would like to call ::ReplaceFile while still having a lock of the replaced file for my process so that no other process is reading the file while the replacement takes place. Having a lock requires that the file is opened and stays open as long as the lock should exist.
If a process [...] closes a file that has outstanding locks, the locks are unlocked by the operating system.
See here.
Unfortunately, ::ReplaceFile fails as long as I have an open handle:
#include <iostream>
#include "Windows.h"
int main() {
HANDLE fileHandle = ::CreateFile(
L"Hello.txt",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_ALWAYS,
0,
0);
if (!::ReplaceFile(L"World.txt", L"Hello.txt", NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0))
{
const DWORD err(::GetLastError());
std::cout << "Uh oh! " << err << "\n";
}
::CloseHandle(fileHandle);
}
Uh oh! 32
32 is ERROR_SHARING_VIOLATION. If I removed the ::CreateFile call, the replacement succeeds.
Is there a way to keep an open handle on a replacement file? If not, is there an alternative to ::ReplaceFile that still has the atomic property described here?

Realocating file clusters with FSCTL_MOVE_FILE too slow

I'm writing a small program to relocate virtual clusters of large files (from 1GB up to 4GB) inside a USB pendrive using DeviceIoControl with the FSCTL_MOVE_FILE control code. The pendrive is formatted as FAT32 (this is a requirement) with a 64K allocation unit size. So far I'm able to move files without problem but the process is very slow.
I did some testing with an unfragmented 100MB file (I made sure no other processes were using the pendrive while moving the file) and it takes aprox. 2 minutes to realocate. Copying files inside the pendrive doesn't take nealry as long so it should be possible to achieve better speeds than that.
Here's the relevant part of my code:
HANDLE volumeHandle = CreateFile( // Opening volume handle
volumeDrive.c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (volumeHandle == INVALID_HANDLE_VALUE)
{
ReportError(L"Invalid volume handle");
return 1;
}
MOVE_FILE_DATA moveData = {0};
moveData.FileHandle = CreateFile( // Opening file handle
argv[1],
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (moveData.FileHandle == INVALID_HANDLE_VALUE)
{
ReportError(L"Invalid file handle");
return 1;
}
// Fill rest of input buffer
moveData.StartingVcn.QuadPart = 0;
moveData.StartingLcn.QuadPart = destination.startingLCN;
moveData.ClusterCount = (DWORD)totalFileLengthInClusters;
DWORD unused;
// Move file
BOOL result = DeviceIoControl(
volumeHandle, // handle to volume
FSCTL_MOVE_FILE, // dwIoControlCode
&moveData, // MOVE_FILE_DATA structure
sizeof(moveData), // size of input buffer
NULL, // lpOutBuffer
0, // nOutBufferSize
&unused, // number of bytes returned
NULL // OVERLAPPED structure
);
My question is: Am I using the right flags when opening the volume and file handles for optimal speed? Is there anything else I can do to speed up the relocation process?

How to append in file in Windows in UnBuffered mode using CreateFile

Every time my function is getting called it is overwriting to the file. Kindly note I am opening file in unbuffered mode using below flags.
FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH
If I am using simple buffered mode it is working fine.
FILE_ATTRIBUTE_NORMAL
I am getting following error in unbuffered mode.
** ERROR ** CreateFile failed: The parameter is incorrect.
Kindly find the code snippets below. This piece of code getting called many times.
HANDLE hFile;
LPCWSTR file_path = convertCharArrayToLPCWSTR(UNBUFFERED_FILE);
hFile = CreateFile(file_path,
FILE_APPEND_DATA,
FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,
NULL
);
if (hFile == INVALID_HANDLE_VALUE)
{
std::cout << "Unable to open/create file for writing" << std::endl;
PrintError(TEXT("CreateFile failed"));
}
Data *data = new Data();
DWORD dwBytesToWrite = sizeof(Data);
DWORD dwBytesWritten = 0;
BOOL bErrorFlag = FALSE;
bErrorFlag = WriteFile(
hFile, // open file handle
data, // start of data to write
dwBytesToWrite, // number of bytes to write
&dwBytesWritten, // number of bytes that were written
NULL);
if (bErrorFlag == FALSE)
{
std::cout << "Unable to write to file" << std::endl;
PrintError(TEXT("Unable to write to file"));
}
if (dwBytesToWrite != dwBytesWritten)
{
std::cout << "Error in writing: Whole data not written" << std::endl;
PrintError(TEXT("Error in writing: Whole data not written"));
}
CloseHandle(hFile);
.
Kindly suggest if any alternative idea is available.
from NtCreateFile
FILE_NO_INTERMEDIATE_BUFFERING
The file cannot be cached or buffered in a driver's internal
buffers. This flag is incompatible with the DesiredAccess
parameter's FILE_APPEND_DATA flag.
so when you call
CreateFile(file_path,
FILE_APPEND_DATA, // !!
FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_FLAG_NO_BUFFERING /*!!*/| FILE_FLAG_WRITE_THROUGH,
NULL
);
you use FILE_FLAG_NO_BUFFERING (mapped to FILE_NO_INTERMEDIATE_BUFFERING) with FILE_APPEND_DATA - you and must got ERROR_INVALID_PARAMETER. you need remove one flag. i suggest remove FILE_FLAG_NO_BUFFERING flag, because with it you can write only integral of the sector size.

CreateFile path error in Visual C++

for (int i = 0; i < 1000; i++)
{
sprintf_s(text, "Text from process %d\r\n", GetCurrentProcessId());
HANDLE hFile = CreateFile(_T("\\\\.\\Device\\HarddiskVolume2\\dev\\test\\test.txt"), GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
cout << "Error opening file " << GetLastError() << " at index " << i << endl;
break;
}
SetFilePointer(hFile, GetFileSize(hFile, NULL), NULL, FILE_BEGIN);
DWORD bytes;
WriteFile(hFile, text, strlen(text), &bytes, NULL);
CloseHandle(hFile);
}
i'm trying to open write using the actual device name
\Device\HarddiskVolume2 is mapped to c:\
the folder already exist in c drive
i'm getting error opening file
Error code 3 is ERROR_PATH_NOT_FOUND. The reason you see that is because \\.\Device\HarddiskVolume2 is the path of the raw device. If you use that path, you no longer have access to the file system. What that path gives you is access to the raw disk sectors.
If you must use \\.\Device\HarddiskVolume2 to identify the drive then you need to first convert, by some means or other, to a mapped drive letter, or a DOS device path for the volume. The latter would be, in your case, \\.\HarddiskVolume2
Please refer
ERROR_PATH_NOT_FOUND
3 (0x3)
The system cannot find the path specified.
So please try to check the path is available or not ..\\Device\\HarddiskVolume2\\dev\\test\\test.txt.
this is because program unable to find the path
or try this:
..\\Device\\HarddiskVolume2\\dev\\test\\test.txt
if the path is in same directory as your exe.
Reason
While creating the file the compiler look into the path which is in the directory where the exe file is. If it is not found then Program will check into the Drive where the Program is running.
Also try to get some information from CreateFile.
hope this will help you.

Writing raw data to a disk in windows fails with error code 83 (invalid parameter)

I have a little snippet of code that I am trying to use to write a string that is roughly 100 bytes long to a disk at the address 0x600.
UCHAR szStr[0x4C] = {0};
memcpy(szStr, "Example string", 8);
DWORD bytes, cb;
HANDLE hDisk = CreateFile("\\\\.\\I:", GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hDisk == INVALID_HANDLE_VALUE)
{
cout << "CreateFile error"<< endl << GetLastError();
}
if (!DeviceIoControl(hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &cb, NULL))
{
cout << "IO Error"<< endl << GetLastError();
}
if (SetFilePointer(hDisk, 0x600, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
cout << "Pointer error"<< endl << GetLastError();
}
if (!WriteFile(hDisk, szStr, 0x4C, &bytes, 0))
{
cout << "Write error" << endl << GetLastError(); //Only one that triggers (err code 87)
}
CloseHandle(hDisk);
For some reason everything is OK until I call WriteFile() which fails. GetLastError() returns a code of 83 which means INVALID_PARAMETER. I have tried debugging it by specifing a normal file on the desktop instead of the drive I:\ and the operation fires off successfully so my best guess would be the problem lies with the disk handle to the drive I:\.
I read an article by MSDN located here that explains that raw disk I/O gets blocked if you don't lock/dismount the drive first. I am confused as to why that would be a problem as in my code I call DeviceIoControl with FSCTL_LOCK_VOLUME to lock the device before I perform anything else. The disk has an active file system (FAT32/FAT16) and the 0x600 region likely lies within the filesystem, but according to the article if I lock the drive I should be able to write to it. Any idea what is going on here? Thanks!
Are you checking DeviceIoControl and SetFilePointer for an error indication?
Do you have the rights to do the operations you're attempting? Administrator account is required for raw disk writes IIRC.
Is the disk you're trying to open a system disk? (Does it contain the OS files?) If so the Lock IOCTL will fail.
If there's a page file on the disk you're trying to lock the Lock IOCTL will fail.