DELETE_ON_CLOSE occurring too early - c++

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.

Related

C++ Overwriting Contents of a Handle

I am trying to get a number from a HANDLE file, store it in an int, and possibly replace it in the same file. My code right now looks like this
HANDLE numFile = INVALID_HANDLE_VALUE; //Just in case file not found
numFile = CreateFile("numFile.txt",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
ReadFile(numFile, input, sizeof(char), &bytesRead, NULL);
int myNumber = input[0];
I know that there exists a WriteFile method in the API, but it looks like it will append the file as opposed to overwriting the contents. I have briefly considered deleting and recreating the file each time, but this seems unnecessarily complex for this problem. Any ideas out there?
Use the function SetFilePointer prior WriteFile for moving back to the begin
SetFilePointer(numFile, 0, NULL, FILE_BEGIN);

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?

IWICImagingFactory::CreateDecoderFromFilename() keeps the file locked even after the decoder is destroyed

I'm using IWICImagingFactory::CreateDecoderFromFilename() for reading only. Is there a way I can pass something like the FILE_SHARE_READ flag? The file stays open from the function call above until the program terminates.
During another operation within the same program, I attempted to use CFile::Open(), but I got error 32 ("The process cannot access the file because it is being used by another process").
What I ended up doing was using
pImagePoolEntry->hFileHandle = CreateFile(
path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
before the call to CreateDecoderFromFilename().
Since I was already using an Image Pool keeping track of opened, shared images I just added the initial file handle and added code to use CloseHandle() when the image was not longer in use.
I'm not sure what the IWICBitmapDecoder does with the file, but it seems to be keeping it opened past the Release() call on it. It might have a bug where the handle isn't closed, who knows. I couldn't find any information on that behaviour. Actually, apart from this question, nobody seems to have noticed (or nobody cares).
In any case, the solution, as hinted by Tony, is to open the file manually and use CreateDecoderFromFileHandle() instead of CreateDecoderFromFilename(). Note that "the file handle must remain alive during the lifetime of the decoder."
So this:
void f(IWICImagingFactory* factory, const wchar_t* path)
{
IWICBitmapDecoder* decoder = nullptr;
factory->CreateDecoderFromFilename(
path, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &decoder);
// ...
decoder->Release();
}
becomes this:
void f(IWICImagingFactory* factory, const wchar_t* path)
{
auto h = CreateFileW(
path, GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
IWICBitmapDecoder* decoder = nullptr;
factory->CreateDecoderFromFileHandle(
reinterpret_cast<ULONG_PTR>(h), nullptr,
WICDecodeMetadataCacheOnLoad, &decoder);
// ...
decoder->Release();
CloseHandle(h);
}

Why the file could be written after changing the attribute to readonly under windows?

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.

CreateFile / WriteFile is not destroying contents of old file

The old contents are not being wiped out.
Instead the data is being written over, so I still see old contents.
What did I not do?
hFile = CreateFile(fname, // open testfile.txt
GENERIC_WRITE, // open for reading
0, // do not share
NULL, // default security
OPEN_ALWAYS, //
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attribute template
dwBytesToWrite = buff.GetLength();
WriteFile(hFile, buff.GetBuffer(100), dwBytesToWrite, &dwBytesWritten, NULL);
You have specified the wrong value for dwCreationDisposition. You need to specify CREATE_ALWAYS.
Creates a new file, always.
If the specified file exists and is writable, the function overwrites the file, the function succeeds, and last-error code is set to ERROR_ALREADY_EXISTS (183).
If the specified file does not exist and is a valid path, a new file is created, the function succeeds, and the last-error code is set to zero.
In dwCreationDisposition you need to specify CREATE_ALWAYS.
You need dwCreationDisposition = TRUNCATE_EXISTING. This however:
Opens a file and truncates it so that its size is zero bytes, only if
it exists. If the specified file does not exist, the function fails
and the last-error code is set to ERROR_FILE_NOT_FOUND (2). The
calling process must open the file with the GENERIC_WRITE bit set as
part of the dwDesiredAccess parameter.
So I would try and open it first with TRUNCATE_EXISTING. If it fails with ERROR_FILE_NOT_FOUND, then open it with CREATE_NEW.
hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, TRUNCATE_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if ((hFile == NULL) && (GetLastError() == ERROR_FILE_NOT_FOUND))
{
hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL, NULL);
}
EDIT: This is not the best way to do this. CREATE_ALWAYS is the dwCreationDisposition you want to use. See David Heffernan's answer.