Recently I posted this question and now I am trying to set a reparse point after a file modification with WordPad app. Currently I checked that the reparse point is a Microsoft tag and I can save the reparse point data in a REPARSE_DATA_BUFFER pointer before the file lose the reparse point. But when I try to set the reparse point to the file, after a file modification, I always get the 395 error with the GetLastError function after call to DeviceIoControl function using the control code FSCTL_SET_REPARSE_POINT. The error 395 doesn't appear in this System Error Codes although I found this error here that has sense. I also removed the discretionary access control list (DACL) from the file, to grants full access to the file by everyone, before try to set the reparse point but I got the same error. Here I put two fragments of my code to get and set the reparse point. I will appreciate any help.
Code to get the reparse point
HANDLE hDevice = CreateFile(
filePath.c_str(), // File path in the computer
0,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
NULL);
if (hDevice != INVALID_HANDLE_VALUE)
{
size_t maxReparse = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
rdb = (REPARSE_DATA_BUFFER*)malloc(maxReparse); // rdb was declared as: REPARSE_DATA_BUFFER* rdb;
DWORD outBufferSize = maxReparse;
DWORD bytesReturned = 0;
if (DeviceIoControl(hDevice, FSCTL_GET_REPARSE_POINT, NULL, 0, rdb, outBufferSize, &bytesReturned, NULL))
{
if (rdb != NULL)
{
if (IsReparseTagMicrosoft(rdb->ReparseTag))
{
wprintf(L"Is a Microsoft tag.\n");
}
Code to set the reparse point
LPTSTR pszObjName = const_cast<wchar_t*>(filePath.c_str()); // File path in the computer
PACL newDACL = NULL; // NULL discretionary access control list to grant full access to everyone
DWORD secInfo = SetNamedSecurityInfo(pszObjName, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, newDACL, NULL);
if (secInfo == ERROR_SUCCESS)
{
HANDLE hDevice = CreateFile(filePath.c_str(),
GENERIC_ALL, //GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (hDevice != INVALID_HANDLE_VALUE)
{
size_t maxReparse = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
DWORD inBufferSize = maxReparse;
if (rdb != NULL)
{
DWORD bytesReturned = 0;
if (!DeviceIoControl(hDevice, FSCTL_SET_REPARSE_POINT, rdb, inBufferSize, NULL, 0, &bytesReturned, NULL))
{
DWORD error = GetLastError(); // Error 395
unsigned long errorUL = error;
wprintf(L"Error %lu in DeviceIoControl method.\n", errorUL);
}
Related
I'm trying to change the icon of EXE file (path to EXE file is writing from console) by absolute path to image (.ico). Tried this code but nothing changed. Have anybody some ideas how to change the icon? (It`s now taking icon from one EXE and replace to another. Just change the icon of EXE file which path was written before)
HANDLE file = CreateFile(stringEXE, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE icon = CreateFile(stringICO, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD size = GetFileSize(file, NULL);
DWORD sizeICO = GetFileSize(icon, NULL);
DWORD bytesReadFile, bytesReadIco;
PVOID pointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
PVOID pIco = VirtualAlloc(NULL, sizeICO, MEM_COMMIT, PAGE_READWRITE);
ReadFile(icon, pIco, sizeICO, &bytesReadIco, NULL);
ReadFile(file, pointer, size, &bytesReadFile, NULL);
CloseHandle(file);
CloseHandle(icon);
HANDLE image = BeginUpdateResource(stringEXE, FALSE); // reading current exe
if (image != NULL) { // if success
if (UpdateResource(image, RT_ICON, MAKEINTRESOURCE(1), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPVOID)pIco, bytesReadIco)) { // if updated
std::cout << "Icon has been changed successfully!" << std::endl;
EndUpdateResource(image,FALSE); // then lock it
}
}
Reference is from Microsoft Documentation about updating resources (https://learn.microsoft.com/en-us/windows/win32/menurc/using-resources)
Relevant code is as follows:
std::wstring path = ApplicationData::Current->LocalFolder->Path->Data();
std::wstring testFileName = path + std::wstring(L"\\TestVariablySized");
this->hMappedFile = CreateFile2(
testFileName.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
OPEN_ALWAYS,
NULL);
uint32_t checkF = GetLastError();
DISK_GEOMETRY geo = { 0 };
DWORD bReturned = 0;
bool controlCheck = DeviceIoControl(
(HANDLE)hMappedFile, // handle to device
IOCTL_DISK_GET_DRIVE_GEOMETRY, // dwIoControlCode
NULL, // lpInBuffer
0, // nInBufferSize
(LPVOID)&geo, // output buffer
(DWORD)sizeof(geo), // size of output buffer
(LPDWORD)&bReturned, // number of bytes returned
NULL);
uint32_t check = GetLastError();
After this, controlCheck is false and check is ERROR_INVALID_PARAMETER. checkF is ERROR_ALREADY_EXISTS, which shouldn't be a problem here.
As far as I can tell, I've called DeviceIoControl() in a way consistent with the IOCTL_DISK_GET_DRIVE_GEOMETRY documentation.
, but clearly I'm missing something. Your help is most appreciated.
Edit:
Per responses received, I altered things to be as follows:
HANDLE hDevice = CreateFile2(
L"\\.\PhysicalDrive0",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
OPEN_EXISTING,
NULL);
uint32_t checkF = GetLastError();
DISK_GEOMETRY geo = { 0 };
DWORD bReturned = 0;
bool controlCheck = DeviceIoControl(
hDevice, // handle to device
IOCTL_DISK_GET_DRIVE_GEOMETRY, // dwIoControlCode
NULL, // lpInBuffer
0, // nInBufferSize
(LPVOID)&geo, // output buffer
(DWORD)sizeof(geo), // size of output buffer
(LPDWORD)&bReturned, // number of bytes returned
NULL);
uint32_t check = GetLastError();
CloseHandle(hDevice);
Which should be closer to being correct, even if it's not quite correct yet. checkF is ERROR_FILE_NOT_FOUND, which I found strange. I tried "\\.\PhysicalDrive1" and "\\.\PhysicalDrive2" as well, but receive the same result. controlCheck is still false, but check is now ERROR_INVALID_HANDLE.
As far as I can tell, I've called DeviceIoControl() in a way consistent with the IOCTL_DISK_GET_DRIVE_GEOMETRY documentation
Actually, you are not, because you did not pay attention to this tidbit of the documentation:
hDevice
A handle to the disk device from which the geometry is to be retrieved. To retrieve a device handle, call the CreateFile function.
You are not passing a handle to a disk device, you are passing a handle to a filesystem path instead.
When calling CreateFile2() to get a handle to a disk device, you need to specify a physical device in \\.\PhysicalDriveX format instead, not a filesystem path.
Also, as the CreateFile2() documentation says:
The following requirements must be met for such a call to succeed:
The caller must have administrative privileges. For more information, see Running with Special Privileges.
The dwCreationDisposition parameter must have the OPEN_EXISTING flag.
When opening a volume or floppy disk, the dwShareMode parameter must have the FILE_SHARE_WRITE flag.
You are using OPEN_ALWAYS instead of OPEN_EXISTING.
Please read the "Physical Disks and Volumes" section of the CreateFile2() documentation more carefully.
Try something more like this instead:
std::wstring path = L"\\\\.\\PhysicalDrive0";
DWORD errCode;
hMappedFile = CreateFile2(
path.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
OPEN_EXISTING,
NULL);
if (this->hMappedFile == INVALID_HANDLE_VALUE)
{
errCode = GetLastError();
// handle error as needed...
}
else
{
DISK_GEOMETRY geo = { 0 };
DWORD dwReturned = 0;
bool controlCheck = DeviceIoControl(
hMappedFile, // handle to device
IOCTL_DISK_GET_DRIVE_GEOMETRY, // dwIoControlCode
NULL, // lpInBuffer
0, // nInBufferSize
&geo, // output buffer
sizeof(geo), // size of output buffer
&dwReturned, // number of bytes returned
NULL);
if (!controlCheck)
{
errCode = GetLastError();
// handle error as needed...
}
else
{
// use drive as needed...
}
CloseHandle(hMappedFile);
}
Given one normal file, it could be read and written. Then I change this file attribute to Read-only through
However, this file still could be written through file handler. Here are my codes
#define CREATE_FILE_OPT FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_RANDOM_ACCESS
std::string name("test.txt");
HANDLE hfile = ::CreateFile(name.c_str(),
GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, CREATE_FILE_OPT, NULL);
if (hfile == INVALID_HANDLE_VALUE) {
hfile = ::CreateFile(name.c_str(),
GENERIC_READ | GENERIC_WRITE, NULL, NULL, CREATE_NEW, CREATE_FILE_OPT, NULL);
if (hfile == INVALID_HANDLE_VALUE) {
printf("so sad, invalid file handler....");
return -1;
}
}
int i = 0;
char rbuf[] = "you are";
DWORD bytesWritten;
do {
Sleep(5000);
++i;
bytesWritten = 0;
BOOL bret = ::WriteFile(hfile, rbuf, strlen(rbuf), &bytesWritten, NULL);
if (bret == FALSE) {
printf("Cannot write bytes into file.....");
DWORD err = GetLastError();
printf("The error code is %d\n", err);
}
else
printf("write %d bytes to file\n", bytesWritten);
DWORD ret = GetFileAttributes(name.c_str());
printf("The file attribute is %d\n", ret);
} while (i < 10000);
The file attribute is 32 before I change it to Read-only, but it will be 33 after this file is Read-only.
I want to know why the file could be written after change it to Read-only? Is there any issue in my test codes?
PS: test it in VS2015 under windows 7.
Sorry, I get your meaning finally. I guess you are talking about creating a file first, setting it readonly without closing the program. The READONLY attribute check only happens in CreateFile routine.
In Windows kernel, every object is assigned a temporary access right list once created, unless explicitly refreshed, a thing seldom happens on actual files on fixed disks. So even if you deny all rights after CreateFile using NTFS access control, your program will behave just as when CreateFile is called.
In conclusion, it is natural your program can still write to the file, after your hot-changing it into READONLY, which only writes information onto disk, not changing kernel access table.
I'm trying to shrink my partition to the last used LCN. Has anybody used this control code?
I'm getting System Error code 87 every time in the following code:
HANDLE hDiskHandle = NULL;
DISK_GROW_PARTITION dgp;
DWORD dwBytesReturned = 0;
dgp.PartitionNumber = 2;
dgp.BytesToGrow.QuadPart = -1;
hDiskHandle = CreateFile(_T("\\.\PhysicalDrive0"), GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, NULL, NULL);
if (hDiskHandle == INVALID_HANDLE_VALUE) {
int err = GetLastError();
printf("Unable to get handle on Volume, error : %d", err);
}
if (!DeviceIoControl(
hDiskHandle,
IOCTL_DISK_GROW_PARTITION,
&dgp,
sizeof dgp,
NULL,
0,
&dwBytesReturned,
NULL
)) {
int err = GetLastError();
printf("DeviceIoControl Failed, error : %d", err);;
}
My hard drive has 3 partitions (C, D, E). The E: drive is practically empty.
[OP's solution converted to answer below]
It turns out my program was fine. It started working after changing access from
GENERIC_ALL
to
GENERIC_READ | GENERIC_WRITE
I have CreateFile() to create a hidden file type but the problem that it keeps returning invalid handle.
file = CreateFileW(_T("hey.txt"),
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
0, 0);
error = GetLastError();
WriteFile(file, buff, sizeof(buff),
&dwRet, NULL);
Any idea?
It would probably be best if you showed the exact code that you're using including all the error checking, and how you do it, is important (especially in the case of this question)...
The correct error checking for your code should be something more like...
file = CreateFile(_T("hey.txt"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
if (file == INVALID_HANDLE_VALUE)
{
const DWORD error = GetLastError();
// Do something!
}
else
{
if (!WriteFile(file, buff, sizeof(buff), &dwRet, NULL))
{
const DWORD error = GetLastError();
// Do something!
}
}
You should only be checking for an error if you get a return value of INVALID_FILE_HANDLE as CreateFile() might not reset the last error before it starts and so you might get spurious error values from GetLastError() if the function succeeds...
A last error of 6, ERROR_INVALID_HANDLE, is unusual from CreateFile() unless you're using the template file parameter, which you're not...
Your code using CreateFileW and _T("") is incorrect and wont compile in a non unicode build. Better to use CreateFile and _T("") or CreateFileW and L"".
Your code will not create a hidden file, see molbdnilo's answer.
0 is not a valid parameter for dwFlagsAndAttributes. To create a hidden file, pass FILE_ATTRIBUTE_HIDDEN.
If "C:\test.txt" exists and is hidden, then following code fails (h = INVALID_HANDLE_VALUE) :
h = CreateFile("C:\\test.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
this fails too (argument 6 == FILE_ATTRIBUTES_NORMAL or argument6 == 0 seems so be the same) :
h = CreateFile("C:\\test.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
but this works :
h = CreateFile("C:\\test.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, 0);
So roughly in other words :
if the file already exists and is hidden then CreateFile with "CREATE_ALWAYS" fails if argument 6 != FILE_ATTRIBUTE_HIDDEN.