Checking for open files in a given directory tree - c++

Is there a way to determine with C+++ if any file is open in a given directory tree?
Cmd.exe knows instantly if I attempt to rename a folder and a file within that directory tree is currently open. I've used API Monitor to determine that cmd.exe uses NtOpenFile() followed by NtSetInformationFile with FileRenameInformation and that returns STATUS_ACCESS_DENIED when a file is open but I've not been able to determine what's happening at a lower level.
I'm trying to ensure no files are open before beginning a batch process without having to check each file individually as there could be hundreds of thousands of files in the directory tree.
Can anyone expand on this?
Thanks,
Steve Thresher.

You will have to check each file individually, I think. That said, this should do it:
HANDLE hFile = CreateFile
(my_filename, GENERIC_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError ();
if (err == ERROR_SHARING_VIOLATION)
{
// File is already open
}
}
else
CloseHandle (hFile);
Note that dwShareMode is passed as 0, which prevents Windows opening the file if it is already opened elsewhere.

Related

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.

Writing to a memory mapped text file printing NULL till the end of mapped memory

In this code, Im writing some text to a memory mapped text file. Data is written to the file successfully, but when i open it with notepad, after the written data, "NULL" is repeatedly written upto the mapped memory limit which is greater than the text i have written.
What could be the possible reason?
pLogMsg = (char*)calloc(1024,sizeof(char));
printf("[INFO] entering logger writeback thread\n");
log_file = CreateFile (TEXT("one.txt"), // Open one.txt.
GENERIC_READ | GENERIC_WRITE, // Open for reading and writing
FILE_SHARE_WRITE, // file share
NULL, // No security
OPEN_ALWAYS, // Open or create
FILE_ATTRIBUTE_NORMAL, // Normal file
NULL); // No template file
if (log_file == INVALID_HANDLE_VALUE)
{
printf("%d [ERR] cant open file GLE %d\n",GetCurrentThreadId(),GetLastError());
return -1;
}
hMapping = CreateFileMapping( log_file, 0, PAGE_READWRITE, 0,4096 ,0 );
if (hMapping == INVALID_HANDLE_VALUE)
{
printf("%d [ERR] cant create file mapping %d\n",GetCurrentThreadId(),GetLastError());
return -1;
}
pFileData = (CHAR*)MapViewOfFile( hMapping, FILE_MAP_ALL_ACCESS, 0,0, 0 );
if (pFileData == NULL)
{
printf("%d [ERR] cant mapview of file %d\n",GetCurrentThreadId(),GetLastError());
return -1;
}
pLogMsg = LogPrint();//returns a null terminated string
memcpy(pFileData,pLogMsg,strlen(pLogMsg));
pFileData += strlen(pLogMsg);
free(pLogMsg);
first of all CreateFileMapping and MapViewOfFile this is bad solution for log file. you need create/open file with FILE_APPEND_DATA access instead GENERIC_READ | GENERIC_WRITE - so call must look like:
HANDLE log_file = CreateFileW(L"one.txt",
FILE_APPEND_DATA,
FILE_SHARE_WRITE|FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
in this case you open file with FILE_APPEND_DATA and SYNCHRONIZE (because no FILE_FLAG_OVERLAPPED):
If the caller sets only the FILE_APPEND_DATA and SYNCHRONIZE flags, it
can write only to the end of the file, and any offset information
about write operations to the file is ignored. The file will
automatically be extended as necessary for this type of operation.
after this you need log via WriteFile - it will be automatically append to the end of file. this is what exactly need for log file.
however, if want use CreateFileMapping and MapViewOfFile - at first CreateFileMapping return 0 on error, so check
if (hMapping == INVALID_HANDLE_VALUE) is wrong
if you want change file size - you need use for this SetFileInformationByHandle with FileEndOfFileInfo (vista+)or NtSetInformationFile with FileEndOfFileInformation (working everywhere, how not hard understand SetFileInformationByHandle is only very thin shell over NtSetInformationFile or ZwSetInformationFile (in user mode this is the same function)). use SetFilePointer followed by SetEndOfFile also possible but bad and not effective choice - you will be have 2 calls in src code instead on single. in binary - you will even more worse situation: first you set file position, that SetEndOfFile read this file position and finally call NtSetInformationFile with this position. so 3 calls instead one. and assume that nobody change file position (on this handle) between SetFilePointer and SetEndOfFile calls
but need clear understand that call SetFileInformationByHandle with FileEndOfFileInfo you can only after you unmap view and close section. otherwise you got error ERROR_USER_MAPPED_FILE (STATUS_USER_MAPPED_FILE )
If CreateFileMapping is called to create a file mapping object for
hFile, UnmapViewOfFile must be called first to unmap all views and
call CloseHandle to close the file mapping object before you can
call SetEndOfFile.
There is no 'end of file' marker within the file. You need to set the file length, so the OS marks it correctly.
See MSDN documentation for SetEndOfFile
Sets the physical file size for the specified file to the current position of the file pointer.
You cannot set the end-of-file marker through a file mapping. The end-of-file marker does not physically exist in a file. It is a flag raised by the OS when reading past the end of a file.
To set the size of a file you will have to call SetFilePointer followed by SetEndOfFile on the file object used to create the file mapping.

How can I capture File directory from edit control(textbox)

so I am new to the whole c++ windows API. I'm creating a simple dialogbox in which the user types in a directory into a textbox for a time file which has already been created. the program will then read the file and display the time in another edit control. Im having a few problems making the directory entered the parameter for CreateFile(). If I hard code the directory in, the program will work correctly. But I cant figure out how to take the textbox data and plug it into the CreateFile() function. if this doesn't make sense i can try an explain differently. Ive searched an can't seem to find anything.
Thanks
for example:
if the user types c:\test\time.txt into the text box I want "c:\test\time.txt" to be the put into CreateFile();
CHAR temp[20] = "";
HANDLE hFile;
GetDlgItemText(hDlg, IDC_TEXTIN, temp, 20);//IDC_TEXTIN is name of edit control
//open file
hFile = CreateFile(
temp,
GENERIC_READ | GENERIC_WRITE,
0, // no sharing
NULL, // no security
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL // no template
);

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.

Executable reading itself

I need to read data added to the end of an executable from within that executable .
On win32 I have a problem that I cannot open the .exe for reading. I have tried CreateFile and std::ifstream.
Is there a way of specifying non-exclusive read access to a file that wasn't initially opened with sharing.
EDIT- Great thing about stackoverflow, you ask the wrong question and get the right answer.
Why not just use resources which are designed for this functionality. It won't be at the end, but it will be in the executable.
If you are adding to the .exe after it is built -- you don't have to add to the end, you can update resources on a built .exe
http://msdn.microsoft.com/en-us/library/ms648049(VS.85).aspx
We do this in one of our projects. What's the problem with it? If the EXE is running, then it's already held open for reading, and you can continue to open it read-only multiple times. I just checked our code, we just use:
HANDLE file=CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
This works without problem on all versions of 32- and 64-bit Windows to date.
I have no problem opening the executable image of a process using either of these statements:
FILE* f = fopen( fname, "rb");
hFile = CreateFile( fname, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
What's your code?