Deleting a file with an open Handle - c++

I shouldnt be able to delete a file with an open handle, correct? So i create a file, then i straight away try to delete it, expecting this to fail. Or am i wrong and the handle doesnt have to be closed before deleting the file?
HANDLE hFile = CreateFile (TEXT(file),
GENERIC_WRITE,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
//FAIL
}
if(DeleteFile(file))
{
//Should it ever get here cos i dont close the handle?
}

It depends on how the file has been opened. If the share mode has FILE_SHARE_DELETE specified, then it may be deleted by others.
Even if you memory map the file, and it has been opened with this flag (and read/write sharing), then it can still be deleted by the shell (at least I've tried this and it happens, but perhaps the file has simply been renamed and moved to the recycle bin). In such cases, subsequently accessing the memory will result in an 'InPageError' C-style exception.

Yes, it would fail.
The DeleteFile function fails if an application attempts to delete a
file that is open for normal I/O or as a memory-mapped file.

Have you tried this? MS documentation states that:
The DeleteFile function fails if an application attempts to delete a file that is open for normal I/O or as a memory-mapped file.
So if you're not getting that behaviour I'd suggest it's down to the way you've opened the file. Are you sure that your check on whether the file is open is completely comprehensive?Have you tried writing to the file first? Can you see the file outside of your own code? (i.e. from Explorer) Look here for more details.

Related

Could DropBox interfere with DeleteFile()/rename()

I had the following code which got executed every two
minutes all day long:
int sucessfully_deleted = DeleteFile(dest_filename);
if (!sucessfully_deleted)
{
// this never happens
}
rename(source_filename,dest_filename);
Once every several hours the rename() would fail with errno=13 (EACCES). The files involved were all sitting on a DropBox directory and I had a hunch that DropBox could be the cause. I figured that it might just be possible that the DeleteFile() function may return with a non-zero successfully_deleted but actually DropBox could still be busy doing some stuff in relation to the deletion that prevented rename() from succeeding. What I did next was to change rename() to my_rename() which would attempt a rename() and upon any failure would Sleep() for one second and try a second time. Sure enough that has worked perfectly ever since. What's more, I get a diagnostic message displaying first-attempt-failures every several hours. It has never failed on the second attempt.
So you could say that the problem is entirely solved... but I would like to understand what might be going on so as to better defend myself against any related DropBox issues in the future...
Really I would like to have a new super_delete() function which does not return until the file is properly deleted and finished with in all respects.
under windows request to delete file really never delete file just. it mark it FCB (File Control Block) with special flag (FCB_STATE_DELETE_ON_CLOSE). real deletion will be only when the last file handle will be closed.
The DeleteFile function marks a file for deletion on close. Therefore,
the file deletion does not occur until the last handle to the file is
closed. Subsequent calls to CreateFile to open the file fail with
ERROR_ACCESS_DENIED.
also if exist section ( memory-mapped file ) open on file - file even can not be marked for delete. api call fail with STATUS_CANNOT_DELETE. so in general impossible always delete file.
in case exist another open handles for file (but not section !) begin from windows 10 rs1 exist new functional for delete - FileDispositionInformationEx with FILE_DISPOSITION_POSIX_SEMANTICS. in this case:
Normally a file marked for deletion is not actually deleted until all
open handles for the file have been closed and the link count for the
file is zero. When marking a file for deletion using
FILE_DISPOSITION_POSIX_SEMANTICS, the link gets removed from the visible namespace as soon as the POSIX delete handle has been closed,
but the file’s data streams remain accessible by other existing
handles until the last handle has been closed.
ULONG DeletePosix(PCWSTR lpFileName)
{
HANDLE hFile = CreateFileW(lpFileName, DELETE, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
static FILE_DISPOSITION_INFO_EX fdi = { FILE_DISPOSITION_DELETE| FILE_DISPOSITION_POSIX_SEMANTICS };
ULONG dwError = SetFileInformationByHandle(hFile, FileDispositionInfoEx, &fdi, sizeof(fdi))
? NOERROR : GetLastError();
// win10 rs1: file removed from parent folder here
CloseHandle(hFile);
return dwError;
}
Update
Sorry i didn't get the question correctly the first time. I thought DeleteFile returned error 13.
Now I understand that DeleteFile succeeds but rename fails immediatlely after.
It could be just a sync issue with the filesystem. After calling DeleteFile the file will be deleted when the OS commits the changes to the filesystem. That may not appen immediately.
If you need to perform multiple operations to the same path, you should have a look at transactions https://learn.microsoft.com/it-it/windows/desktop/api/winbase/nf-winbase-deletefiletransacteda.
-- OLD ANSWER --
That is correct. If the another application handles to that file, DeleteFile will fail.
Citing MSDN docs https://learn.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-deletefile :
The DeleteFile function fails if an application attempts to delete a file that has other handles open for normal I/O or as a memory-mapped file (FILE_SHARE_DELETE must have been specified when other handles were opened).
This applies to dropbox, the antivirus, or in general, any other application that may open those files.
Dropbox may open the file to compute its hash (to look for changes) at any moment. Same goes with the antivirus.

How to delete parent of child being monitored by ReadDirectoryChangesW

Monitoring a folder with ReadDirectoryChangesW causes its parent to be locked and can't be deleted.
There is a post about this here:
FindFirstChangeNotification locks parent folder
but the only solution mentioned in it is that we should always listen at the top level.
Has anyone found out a better way to do this instead of watching at the top level?
Sometimes this can go all the way to watching the Drive and that can't take a lot of process time on a machine.
Thanks!
folder can be deleted only in case it empty, otherwise we got error STATUS_DIRECTORY_NOT_EMPTY - Indicates that the directory trying to be deleted is not empty.
from another side - if you have opening handle for file - it can not be deleted until you not close it handle (something here changed begin from win10 rs1)
so if you monitor some child sub-folder with ReadDirectoryChangesW you have opened handle to it, and parent can not (before WIN10_RS1) be deleted until you not close this handle.
in general process look like - when somebody try delete folder - it must enumerate all files(sub-folders) inside it and delete it first. when delete operation will be apply on folder for which ReadDirectoryChangesW called - the io request will be completed with status STATUS_DELETE_PENDING - A non close operation has been requested of a file object with a delete pending. (it converted to win32 error code ERROR_ACCESS_DENIED - Access is denied.). when you got this error from ReadDirectoryChangesW you must close your directory handle used in this call. then is raise - who is first - you close directory handle or another code try delete parent folder...
begin from win10 rs1 possible delete parent, even if somebody hold open handle to it child file(folder) by calling NtSetInformationFile with FileDispositionInformationEx or SetFileInformationByHandle with FileDispositionInfoEx.
the magic here in new flag FILE_DISPOSITION_POSIX_SEMANTICS (Specifies the system should perform a POSIX-style delete)
Normally a file marked for deletion is not actually deleted until all
open handles for the file have been closed and the link count for the
file is zero. When marking a file for deletion using
FILE_DISPOSITION_POSIX_SEMANTICS, the link gets removed from the
visible namespace as soon as the POSIX delete handle has been closed,
but the file’s data streams remain accessible by other existing
handles until the last handle has been closed.
so when we use this - file itself of course will be not deleted, until caller of ReadDirectoryChangesW not close self handle, but file will be removed from the parent folder. as result parent folder can became empty, after which we can delete it.
note that DeleteFileW and RemoveDirectoryW here will be not work here, because they used old information class FileDispositionInformation with FILE_DISPOSITION_INFORMATION
ULONG DeletePosix(PCWSTR lpFileName)
{
HANDLE hFile = CreateFileW(lpFileName, DELETE, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
static FILE_DISPOSITION_INFO_EX fdi = { FILE_DISPOSITION_DELETE| FILE_DISPOSITION_POSIX_SEMANTICS };
ULONG dwError = SetFileInformationByHandle(hFile, FileDispositionInfoEx, &fdi, sizeof(fdi))
? NOERROR : GetLastError();
// win10 rs1: file removed from parent folder here
CloseHandle(hFile);
return dwError;
}
and of course child must be open with FILE_SHARE_DELETE in other calls, otherwise we simply can not open it with DELETE access later
It is important to specify the right attributes to CreateFile() when you obtain the directory handle. Try this:
HANDLE hDir = ::CreateFile(
strDirectoryName,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, // security descriptor
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
It is important to specify FILE_SHARE_DELETE as well for the share mode.

Cygwin: Deleting file when handle is opened

I have a file which I want to delete, it's handle is held by system process so everytime I try to delete it it gives Access denied but for some reason cygwin is able to delete it.
I've downloaded the coreutils and investigated the source code of rm executable and found that it uses unlink function to achieve it. I've created a little test program which uses same function but it gives me Access denied anyway.
Then I found this article and guy describes how cygwin is able to delete a file which is the following:
Cygwin opens files always with all sharing flags
set, so a file opened by a Cygwin process should not result in a sharing
violation in another open call. The exception is the first NtOpenFile
in unlink_nt, which opens the file with FILE_SHARE_DELETE only to find
out if the file has an open handle somewhere else. In that case it gets
a STATUS_SHARING_VIOLATION, the next NtOpenFile will open the file with
all sharing flags set, and unlink_nt will try to delete the file or to
rename it, or to move it to the recycle bin, dependent on its path.
Which makes sense, So I started to implement same thing. Here is my code:
HANDLE file;
PIO_STATUS_BLOCK stat;
UNICODE_STRING myUnicodeStr;
RtlInitUnicodeString(&myUnicodeStr, L"C:\\Program Files (x86)\\TSU\\bin\\TSU.sys");
POBJECT_ATTRIBUTES attr;
InitializeObjectAttributes (attr, &myUnicodeStr, OBJ_OPENIF, NULL, NULL);
NtOpenFile(&file, MAXIMUM_ALLOWED, attr, NULL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_DELETE_ON_CLOSE);
NtClose(file);
As you can see I'm trying to open file with sharing flags set and also used FILE_DELETE_ON_CLOSE since I close the handle and I want it to be deleted afterwards.
The problem I have is Segmentation Fault(I'm using cygwin win10) for some reason. Little debugging showed that problem is in InitializeObjectAttributes function for some reason.
P.S
I know it's not the best solution to delete file when it's handle is held by some other process but the main goal is to mimic the rm.exe's behaviour in this way. Hope you can help. Thanks.
under windows not always possible delete file. for example when some process mapping this file as an image
if try delete running EXE file with rm.exe - it first call ZwOpenFile with DesiredAccess = DELETE, ShareAccess = FILE_SHARE_DELETE and OpenOptions = FILE_OPEN_FOR_BACKUP_INTENT. this is ok. than called ZwSetInformationFile with FileDispositionInformation - the DeleteFile from FILE_DISPOSITION_INFORMATION set to TRUE. this call failed with status STATUS_CANNOT_DELETE.
filesystem return STATUS_CANNOT_DELETE exactly from this place:
// Make sure there is no process mapping this file as an image.
if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers,
MmFlushForDelete )) {
DebugTrace(-1, Dbg, "Cannot delete user mapped image\n", 0);
return STATUS_CANNOT_DELETE;
}
than rm.exe again try open file, already with OpenOptions = FILE_OPEN_FOR_BACKUP_INTENT | FILE_DELETE_ON_CLOSE options. but this call of course fail with the same STATUS_CANNOT_DELETE. now error from this point:
// If the user wants to delete on close, we must check at this
// point though.
//
if (FlagOn(*DesiredAccess, FILE_WRITE_DATA) || DeleteOnClose) {
Fcb->OpenCount += 1;
DecrementFcbOpenCount = TRUE;
if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers,
MmFlushForWrite )) {
Iosb.Status = DeleteOnClose ? STATUS_CANNOT_DELETE :
STATUS_SHARING_VIOLATION;
try_return( Iosb );
}
}
after this rm.exe again call ZwSetInformationFile already with FileRenameInformation - where RootDirectory from FILE_RENAME_INFORMATION point to volume (on which file located) root (so like \Device\HarddiskVolume<N>\ and FileName point to some path in recycle bin. as result file actually moved but not deleted. rm.exe deceives you

Portable temporary files with C++ (C++ standard libraries or Boost)?

Is there a portable way of creating temporary files with C++ that get automatically deleted when the program terminates (regardless of whether it crashes, gets killed, or just reaches return 0; in main().).
On Unix systems, I can open a file, delete it and then keep the still existing handle. This works with FILE *, std::fstream etc.
On Windows, this appears not to work. The only way I found is using CreateFile with the FILE_FLAG_DELETE_ON_CLOSE flag.
Is there something smarter that (1) works both on Linux and Windows, (2) has the "the file is removed on when the program terminates" behaviour as on Linux. I would be fine with #ifdef code as long as the file type that I work with is the same on both systems (e.g. std::fstream or FILE *).
I know about this solution, but this appears only to work on graceful exits and would require me to either set up central handlers and manage all temporarily opened files.
Edit: Rephrased the question to "how can I get files on Windows that are automatically removed as removed-but-still-open files in Linux.
http://www.cplusplus.com/reference/cstdio/tmpfile/
Creates a temporary binary file, open for update ("wb+" mode, see
fopen for details) with a filename guaranteed to be different from any
other existing file.
The temporary file created is automatically deleted when the stream is
closed (fclose) or when the program terminates normally. If the
program terminates abnormally, whether the file is deleted depends on
the specific system and library implementation.
For abnormal termination, handle std::terminate by using: http://en.cppreference.com/w/cpp/error/set_terminate
Clean up the file with std::remove and then re-throw. I haven't tested this abornmal termination stuff yet but it should work. The temporary file creation works for sure. I've used it before.
It can be wrapped in a streambuf for usage with streams.
Also as a partial solution you can write a class, that deletes the file on destruction. On windows you can use MoveFileExW(filepathFull, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) if that fails to have the file deleted on reboot. All of this can be hidden in the class. See for example: tmpfile.h tmpFile.cpp
Alternatively you can use something like:
struct TmpFile{
FILE* file;
TmpFile(std::string path){
#ifdef _WIN32
HANDLE handle = CreateFile(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0,
OPEN_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, 0);
if(handle == INVALID_HANDLE_VALUE) throw "Error";
int fd = _open_osfhandle((intptr_t)h, _O_APPEND | _O_RDONLY);
if(fd == -1){ CloseHandle(handle); throw "Error"; }
file = _fdopen(fd, "a+");
if(f == NULL){ _close(fd); throw "Error"; }
#else
file = fopen(path.c_str(), "w+");
unlink(path.c_str());
#endif
}
~TmpFile(){
fclose(file);
}
};
Check the access modifiers and adjust to your needs (especially the error handling) This has the nice properties of RAII and will delete the file even in exception cases. However some failures where the dtor is not called will leak the handle in which case the file might not be deleted on windows.
The Win32 handling is taken from here: https://stackoverflow.com/a/7369662/1930508 Check the explanations there.

Multiple opening of temporary file only in same process

I have a question about FILE_ATTRIBUTE_TEMPORARY marked files.
First of all, here is what I want to do:
I have a DLL, that takes a Filename, and opens that file internally and reads from it. I do not know how this file is handled inside.
The file I want to give to that DLL will be created by my process. It must be a temporary file and its data must be held only in RAM and must not be accessed by other processes. So I use the Win32 function CreateFile() with the FILE_ATTRIBUTE_TEMPORARY and the FILE_FLAG_DELETE_ON_CLOSE. This so far works, fine.
I have a tes code where I test if the file can be accessed a second time, while still opened. Here it is:
HANDLE WINHandle = CreateFile("TempFileWIN.txt", (GENERIC_WRITE | GENERIC_READ) ,(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), 0, CREATE_ALWAYS, (FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE), 0);
ifstream ifs("TempFileWIN.txt", (ios::in | ios::trunc));
if(ifs.is_open())
{
cout << "Success!" << endl;
}
else if(ifs.fail())
{
cout << "Failed!" << endl;
}
I am using the fstream to test if the file could be opened with a stream. That code up there doesn't work. The output is "Failed!".
I know, that the file could be opened with the CreateFile a second time. I checked that out. But want to know if it is possible to open the file by an external DLL that works with (e.g.) a fstream.
I hope you can help me with this matter.
Best regards.
Edit:
Maybe a better question is how I can lock a file to my process and ensure, that it can never be accessed by an other process (even if my process is killed). The file must be openable with C++ fstream object.
If I were you, I would keep the handle of the open file, and pass it to the DLL code, and not use the filename, since you're likely to run into access restrictions at some point if you try to access a temporary, delete-on-close file using 'normal' file access.
It is possible to use a Windows handle in a fstream object as described in this answer: https://stackoverflow.com/a/476014/393701