Cygwin: Deleting file when handle is opened - c++

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

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.

Deleting a file with an open Handle

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.

Receiving a Sharing Violation Opening a File Code 32

I have been trying the following piece of code that does not work. What I am trying to do is to start executing my exe (one that I created a simple dialog based application using VC6.0) then from inside this application modify its own contents stored on the hard drive.
So there is a running copy of the exe and from this running copy it will open the disk copy into a buffer. Once loaded into a buffer then begin a search for a string. Once the string is found it will be replaced with another string which may not be the same size as the original.
Right now I am having an issue of not being able to open the file on disk for reading/writing. GetLastError returns the following error "ERROR_SHARING_VIOLATION The process cannot access the file because it is being used by another process.".
So what I did I renamed the file on disk to another name (essential same name except for the extension). Same error again about sharing violation. I am not sure why I am getting this sharing violation error code of 32. Any suggestions would be appreciated. I'll ask my second part of the question in another thread.
FILE * pFile;
pFile = fopen ("Test.exe","rb");
if (pFile != NULL)
{
// do something like search for a string
}
else
{
// fopen failed.
int value = GetLastError(); // returns 32
exit(1);
}
Read the Windows part of the File Locking wikipedia entry: you can't modify files that are currently executing.
You can rename and copy them, but you can't change them. So what you are trying to do is simply not possible. (Renaming the file doesn't unlock it at all, it's still the same file after the rename, so still not modifiable.)
You could copy your executable, modify that copy, then run that though.

How to remove the file which has opened handles?

PROBLEM HISTORY:
Now I use Windows Media Player SDK 9 to play AVI files in my desktop application. It works well on Windows XP but when I try to run it on Windows 7 I caught an error - I can not remove AVI file immediately after playback. The problem is that there are opened file handles exist. On Windows XP I have 2 opened file handles during the playing file and they are closed after closing of playback window but on Windows 7 I have already 4 opened handles during the playing file and 2 of them remain after the closing of playback window. They are become free only after closing the application.
QUESTION:
How can I solve this problem? How to remove the file which has opened handles? May be exists something like "force deletion"?
The problem is that you're not the only one getting handles to your file. Other processes and services are also able to open the file. So deleting it isn't possible until they release their handles. You can rename the file while those handles are open. You can copy the file while those handles are open. Not sure if you can move the file to another container, however?
Other processes & services esp. including antivirus, indexing, etc.
Here's a function I wrote to accomplish "Immediate Delete" under Windows:
bool DeleteFileNow(const wchar_t * filename)
{
// don't do anything if the file doesn't exist!
if (!PathFileExistsW(filename))
return false;
// determine the path in which to store the temp filename
wchar_t path[MAX_PATH];
wcscpy_s(path, filename);
PathRemoveFileSpecW(path);
// generate a guaranteed to be unique temporary filename to house the pending delete
wchar_t tempname[MAX_PATH];
if (!GetTempFileNameW(path, L".xX", 0, tempname))
return false;
// move the real file to the dummy filename
if (!MoveFileExW(filename, tempname, MOVEFILE_REPLACE_EXISTING))
{
// clean up the temp file
DeleteFileW(tempname);
return false;
}
// queue the deletion (the OS will delete it when all handles (ours or other processes) close)
return DeleteFileW(tempname) != FALSE;
}
Technically you can delete a locked file by using MoveFileEx and passing in MOVEFILE_DELAY_UNTIL_REBOOT. When the lpNewFileName parameter is NULL, the Move turns into a delete and can delete a locked file. However, this is intended for installers and, among other issues, requires administrator privileges.
Have you checked which application is still using the avi file?
you can do this by using handle.exe. You can try deleting/moving the file after closing the process(es) that is/are using that file.
The alternative solution would be to use unlocker appliation (its free).
One of the above two method should fix your problem.
Have you already tried to ask WMP to release the handles instead? (IWMPCore::close seems to do that)