Is it possible to keep a handle of a ::ReplaceFile? - c++

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?

Related

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() ERROR_SHARING_VIOLATION with single thread

Why can't a single thread open the same file tqize using CreateFile and an exclusive file lock to the process? The example below will fail on the second attempt to open the file by the same thread with a ERROR_SHARING_VIOLATION exception:
ERROR_SHARING_VIOLATION 32 (0x20) The process cannot access the file
because it is being used by another process.
Emphasis on the word "process" above; it is the same process (and even the same thread) that tries to open the same file twize.
#include <Windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hOutputFile1 = CreateFile(
// File name
L"test.dat",
// Requested access to the file
GENERIC_READ | GENERIC_WRITE,
// Share mode. 0 equals exclusive lock for the process
0,
// Pointer to a security attribute structure
NULL,
// Action to take on file
OPEN_ALWAYS,
// File attributes and flags
FILE_ATTRIBUTE_NORMAL,
// Template file
NULL
);
if (hOutputFile1 == INVALID_HANDLE_VALUE)
{
// Error
DWORD lastError = GetLastError();
return (int)lastError;
}
// opening the same file for the second time will return a ERROR_SHARING_VIOLATION exception
HANDLE hOutputFile2 = CreateFile(
// File name
L"test.dat",
// Requested access to the file
GENERIC_READ | GENERIC_WRITE,
// Share mode. 0 equals exclusive lock by the process
0,
// Pointer to a security attribute structure
NULL,
// Action to take on file
OPEN_ALWAYS,
// File attributes and flags
FILE_ATTRIBUTE_NORMAL,
// Template file
NULL
);
if (hOutputFile2 == INVALID_HANDLE_VALUE)
{
// Error
DWORD lastError = GetLastError();
return (int)lastError;
}
return 0;
}
The error message text is a bit misleading, but the fact that the two calls to CreateFile are made from the same thread in the same process doesn't change anything. Once the first call to CreateFile has been made, then subsequent calls to CreateFile, irrespective of where they originate, must obey the sharing rules.
I guess the error message text tries to capture the most common source of a sharing violation. Namely two processes competing for the same file. But the simple fact is that once you have opened a file with exclusive sharing, then no other attempts to open the file can succeed.
That old message is misleading. It doesn't matter what process or thread is opening a file. File sharing is handle-based.
From MSDN:
dwShareMode [in]:
If this parameter is zero and CreateFile succeeds, the file or device
cannot be shared and cannot be opened again until the handle to the
file or device is closed.
This is just talking about opening the file or device and the error message is something general and confusing.
Instead of trying to re-open the file, you should hold the handle and do your job.

Lock a file using windows c++ LockFIle() then get a stream from it?

I have locked a file using LockFileEx, but I am not able to open a stream from it.
HANDLE indexHandle = CreateFile (indexFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
bool indexLock = false;
OVERLAPPED overlapped;
memset (&overlapped, 0, sizeof (overlapped));
while (noOfTries >0 && !indexLock)
{
if (!LockFileEx (indexHandle, LOCKFILE_EXCLUSIVE_LOCK, 0, 0, UINT_MAX, &overlapped))
{
InfoLog << "Failed to get lock on index file -- Error code is ["
<< GetLastError () <<"]"<<std::endl;
Sleep(sleepTime);
noOfTries--;
}
else
{
indexLock=true;
}
}
After the lock is acquired, I want to do this:
string indexFile = mPath + Util::PATH_SEPARATOR + mIndexFileName;
os.open( indexFile.c_str(), ios_base::app);
if (!os)
{
InfoLog << "BinaryFileSystemObjectStore:: ofstream: Failed to open index for write: " << indexFile.c_str() << endl;
}
I do this because I find it easier to read line by line with streams...
Is there a solution?
From the documentation for LockFileEx:
If the locking process opens the file a second time, it cannot access the specified region through this second handle until it unlocks the region.
So you need to use the handle you already have rather than creating a new one.
The _open_osfhandle function allows you to create a file descriptor from an existing handle, and you can then pass this file descriptor to the ofstream constructor instead of the filename.
You open the file with FILE_SHARE_READ. This means you permit further opening of the file for reading only. Then you try to open it for writing, which will fail.
Use FILE_SHARE_READ | FILE_SHARE_WRITE instead.

DELETE_ON_CLOSE occurring too early

I'm trying to create a process on another machine that deletes itself when its done. I'm using the DELETE_ON_CLOSE flag with CreateFile. This method is a bit popular but I'm having trouble because I can't execute it while it's open(expected but that's what some solutions do). To work around this I tried opening the file with read permissions. The DELETE_ON_CLOSE flag says that it should only delete a file when all pointers to it are gone. I have a pointer to it with the read, I close the write handle and the file deletes leaving my open handle unreadable. Any other way around this would be gladly appreciated.
I also have considered the possibility that since this is a remote file system there's something funky going on with the handles.
I cannot modify the code of the execuatble I'm sending over so a self-deleting executable is the last thing I want to do.
Making my program wait around to clean up the service will cause it to hang for an unacceptably long time because of how long it can take to destroy a service on the remote box.
//Open remote file for reading and set the delete flag
HANDLE remote_fh = CreateFile(&remote_file_location[0],
GENERIC_WRITE,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
NULL);
if(!remote_fh)
{
debug.DebugMessage(Error::GetErrorMessageW(GetLastError()));
RevertToSelf();
return dead_return;
}
//File to read from
HANDLE local_fh = CreateFile(&local_file_location[0],
GENERIC_READ,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(!local_fh)
{
debug.DebugMessage(Error::GetErrorMessageW(GetLastError()));
RevertToSelf();
return dead_return;
}
byte buf[256];
DWORD bytesRead;
DWORD bytesWritten;
//Copy the file
while(ReadFile(local_fh, buf, 256, &bytesRead, NULL) && bytesRead > 0)
{
WriteFile(remote_fh, buf, bytesRead, &bytesWritten, NULL);
}
CloseHandle(local_fh);
//Create a file retainer to hold the pointer so the file doesn't get deleted before the service starts
HANDLE remote_retain_fh = CreateFile(&remote_file_location[0],
GENERIC_READ,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (!remote_retain_fh)
{
debug.DebugMessage(Error::GetErrorMessageW(GetLastError()));
RevertToSelf();
return dead_return;
}
CloseHandle(remote_fh);
//if(!CopyFile(&local_file_location[0], &remote_file_location[0], false))
//{
// debug.DebugMessage(Error::GetErrorMessageW(GetLastError()));
// RevertToSelf();
// return dead_return;
//}
remote_service.Create(Service::GetServiceName().c_str());
//In the words of my daughter: "OH, OH, FILE ALL GONE!"
Pipe pipe(L"\\\\" + *hostname + L"\\pipe\\dbg");
CloseHandle(remote_fh);
FILE_FLAG_DELETE_ON_CLOSE requires FILE_SHARE_DELETE permission. The problem with that is that when a process starts up, the operating system opens a file handle to the executable and requires NOT sharing the FILE_SHARE_DELETE permission so that you can't delete the executable file while the process is open.
Consider: you open a handle to an executable file without exclusive FILE_SHARE_DELETE, but say just FILE_GENERIC_READ. Someone else (you, later, or indeed another thread or process) then launches that executable. No problem because nobody's trying to delete the executable. But if you were then to try to gain FILE_SHARE_DELETE permission, you'd fail because the executable is already running with exclusive file delete permission.

how to get size of a locked file?

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.