How to check if a file is open - winapi

I need to check if any files in a folder is open by other applications or not. Unfortunately, if a file is open, the GetFileAttributesA() return wrong values. Is there a workaround for this or am I doing something wrong ?

GetFileAttributes has nothing to do with file sharing. The only way to know if someone has the file open (thereby preventing you from opening it) is to try an open it yourself.
bool IsFileOpenBySomeoneElse(LPCTSTR pszFilename)
{
HANDLE hfile = CreateFile(pszFilename,
GENERIC_READ /*| GENERIC_WRITE*/,
0, //0 is share-none
NULL,
OPEN_ALWAYS);
if (hfile != INVALID_HANDLE_VALUE)
{
CloseHandle(hfile);
return false;
}
return (GetLastError() == ERROR_SHARING_VIOLATION);
}
But writing this function does you no good, because by the time you get around to opening th e file for processing, some other application may have the file open.
The only safe way to do this is to go ahead an do what you intend to do with the file, and when you try and open it for processing, notice the error value if you fail. Once you have the file open, you must keep it open until you are done or some other process can open it (or delete it!) behind your back.

Related

How do i completly lock a file on windows

I am currently working on a project that includes the complete lock of some files on windows, by complete lock i mean that the file cannot be accessed by another process nor by the system (copy, paste, delete). the Lockfile function was the closest i got to a solution but, i am not sure if the unlockfile function can override and grant access to a different process. so my question is: does Lockfile really lock hermetically the file and can it be overridden?
Third parameter of CreateFile specifies sharing options, by specifying no flags (0) for this param, you get a lock on the file.
HANDLE hFile =
CreateFile(L"filename.txt",
GENERIC_READ|GENERIC_WRITE,
0, // by specifying 0, you get exclusive access
NULL,
OPEN_EXISTING,
FILE_FLAG_NORMAL,
NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
// lock obtained
}
else
{
// file not exist or file is being used by something else
}
Closing the returned file handle releases the lock. (Also, exiting the process will release the lock).
CloseHandle(hFile);
hFile = NULL;

ReplaceFile alternative when application keeps file locked

Editor FooEdit (let's call it) uses ReplaceFile() when saving to ensure that the save operation is effectively atomic, and that if anything goes wrong then the original file on disc is preserved. (The other important benefit of ReplaceFile() is continuity of file identity - creation date and other metadata.)
FooEdit also keeps open a handle to the file with a sharing mode of just FILE_SHARE_READ, so that other processes can open the file but can't write to it while it while FooEdit has it open for writing.
"Obviously", this handle has to be closed briefly while the ReplaceFile operation takes place, and this allows a race in which another process can potentially open the file with write access before FooEdit re-establishes it's FILE_SHARE_READ lock handle.
(If FooEdit doesn't close its FILE_SHARE_READ handle before calling ReplaceFile(), then ReplaceFile() fails with a sharing violation.)
I'd like to know what is the simplest way to resolve this race. The options seem to be either to find another way to lock the file that is compatible with ReplaceFile() (I don't see how this is possible) or to replicate all the behaviour of ReplaceFile(), but using an existing file handle to access the destination file rather than a path. I'm a bit stuck on how all of the operations of ReplaceFile() could be carried out atomically from user code (and reimplementing ReplaceFile() seems a bad idea anyway).
This must be a common problem, so probably there's an obvious solution that I've missed.
(This question seems related but has no answer: Transactionally write a file change on Windows.)
Here's a minimal verifiable example showing what I am trying to achieve (updated 13:18 30/9/2015 UTC). You must supply three file names as command line arguments, all on the same volume. The first must already exist.
I always get a sharing violation from ReplaceFile().
#include <Windows.h>
#include <stdio.h>
#include <assert.h>
int main(int argc, char *argv[])
{
HANDLE lock;
HANDLE temp;
DWORD bytes;
if (argc != 4)
{
puts("First argument is the project file. Second argument is the temporary file.");
puts("The third argument is the backup file.");
}
/* Open and lock the project file to make sure no one else can modify it */
lock = CreateFile(argv[1], GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, 0);
assert(lock != INVALID_HANDLE_VALUE);
/* Save to the temporary file. */
temp = CreateFile(argv[2], GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, 0, 0);
assert(temp != INVALID_HANDLE_VALUE);
WriteFile(temp, "test", 4, &bytes, NULL);
/* Keep temp open so that another process can't modify the file. */
if (!ReplaceFile(argv[1], argv[2], argv[3], 0, NULL, NULL))
{
if (GetLastError() == ERROR_SHARING_VIOLATION)
puts("Sharing violation as I expected");
else
puts("Something went wrong");
}
else
puts("ReplaceFile worked - not what I expected");
/* If it worked the file referenced by temp would now be called argv[1]. */
CloseHandle(lock);
lock = temp;
return EXIT_SUCCESS;
}
Thanks to Hans Passant, who provided some valuable clarifying thoughts in an answer now deleted. Here's what I discovered while following up his suggestions:
It seems ReplaceFile() allows lpReplacedFileName to be open FILE_SHARE_READ | FILE_SHARE_DELETE, but lpReplacementFileName can't be. (And this behaviour doesn't seem to depend on whether lpBackupFileName is supplied.) So it's perfectly possible to replace a file that another process has open even if that other process doesn't allow FILE_SHARE_WRITE, which was Hans' point.
But FooEdit is trying to ensure no other process can open the file with GENERIC_WRITE in the first place. To ensure in FooEdit that there's no race where another process can open the replacement file with GENERIC_WRITE, it seems that FooEdit has to keep hold continuously of a FILE_SHARE_READ | FILE_SHARE_DELETE handle to lpReplacementFileName, which then precludes use of ReplaceFile().
Actually I think there might be a solution that doesn't involve transactions (although transactions are still available as far as I know). I haven't tried it myself, but I think on NTFS it should be possible to create a new file stream (use a long random name to ensure there are no collisions), write your data, and then rename that stream to the stream you actually wanted to write to.
FILE_RENAME_INFORMATION suggests this should be possible, since it talks about renaming data streams.
However, this would only work on NTFS. For other file systems I don't think you have a choice.
I'd like to know what is the simplest way to resolve this race.
There is no simple way to resolve this race. It's an inherent part of the file system which is not transactional. MS introduced a transactional file API with Vista but now strongly advise developers not to use it as it may be removed in a future release.
I have had some experience with ReplaceFile but I think it caused more trouble than it was worth. My recollection was that whilst meta data was preserved, a new file was created. A consequence of this was very annoying behaviour for files saved on the desktop. Because such files have their position preserved, creating a new file resulted in the default position being used. So you'd save a file, you'd drag it to the place on the desktop where you wanted to keep it, and then when you saved the file again, it moved back to the default position.

LockFileEx returns success, but seems to have no effect

I'm trying to lock a file, because it is sitting on a network drive, and multiple instances of a program from multiple computers need to edit it. To prevent damage, I intend to set it up so that only one of the instances has rights to it at a time.
I implemented a lock, which would theoretically lock the first 100 bytes of the file from any access. I'm using Qt with its own file handling, but it has a method of returning a generic file handle.
QFile file(path);
HANDLE handle = (HANDLE)_get_osfhandle(file.handle());
OVERLAPPED ov1;
memset(&ov1, 0, sizeof(ov1));
ov1.Offset = 0;
ov1.OffsetHigh = 0;
if (handle == INVALID_HANDLE_VALUE)
{
// error
return;
}
LockFileEx(handle, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK, 0, 100, 0, &ov1);
qDebug() << file.readLine();
LockFileEx() returns 1, so it seems to have been successful. However, if I run the program in multiple instances, all of them can read and print the first line of the file. More than this, I can freely edit the file with any text editor.
Being a network file is not an issue, as it behaves similarly with a local file.
The problem was that, while the program does not terminate, the QFile variable was local, so after finishing the function, the destructor of the QFile was called, so it released the file. The OS then seemed to have released the lock.
If my QFile survives the scope, everything works just fine. A minor issue is, that while I expected the file to be locked against reading, external programs do have a read-only access to it. It's not a problem, as my program can check whether it can create a lock, and detect failure to do so. This means that the intended mutex functionality works.

How to un-CreateFile

Let us say that i am writing data to a file handle:
hFile = CreateFile(filename, GENERICREAD | GENERICWRITE, 0, null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
//[snip error check]
try
{
if (!WriteFile(hFile, buffer, count, ref bytesWritten, null))
throw new Win32Exception(GetLastError());
}
finally
{
CloseHandle();
}
If my write of data failed, i want the file to be deleted when i close the handle. I.e.: i want the file to be "un-CreatFile'd".
i've tried the obvious, delete the file if there was a problem:
hFile = CreateFile(filename, GENERICREAD | GENERICWRITE, 0, null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
//[snip error check]
try
{
try
{
if (!WriteFile(hFile, buffer, count, ref bytesWritten, null))
throw new Win32Exception(GetLastError());
}
finally
{
CloseHandle();
}
}
catch
{
DeleteFile(filename);
throw;
}
There are two problems with that approach:
There's a race condition, where someone could open my file after i close it, but before i delete it
i might have permission to Create a file, but not the Delete it.
What i would like is a way to retroactively specify:
FILE_FLAG_DELETE_ON_CLOSE: The file is to be deleted immediately after all of its handles are closed, which includes the specified handle and any other open or duplicated handles.
to the file that i have open.
i created the file! Surely i can uncreate it! All because i forgot to specify a flag before-hand?
In all versions of Windows, you can
Specify FILE_SHARE_DELETE when opening the file originally.
Open it a second time, specifying FILE_FLAG_DELETE_ON_CLOSE
Close the second handle.
The file will be deleted when all handles are closed.
This is explained in the FILE_FLAG_DELETE_ON_CLOSE description in the documentation for CreateFile:
The file is to be deleted immediately after all of its handles are closed, which includes the specified handle and any other open or duplicated handles.
If there are existing open handles to a file, the call fails unless they were all opened with the FILE_SHARE_DELETE share mode.
If the automatic deletion isn't good enough, you could look into Transactional NTFS, putting file creation and all writes into a transaction, and then rolling it back if necessary.
In Windows Vista and newer, this could be done using SetFileInformationByHandle and FILE_DISPOSITION_INFO.
If you're using Windows 2003 server or Vista or higher, ReOpenFile() is what you're looking for. It allows you to specify a new 'flags' parameter for the reopened handle. Shame it's apparently not available on XP.

Opening a handle to flash drive gives me an "Access Denied" error code

I would like to know why when I try to create a handle to a USB flash drive, I receive a path not found error.
HANDLE aFile = CreateFile(_T("\\\\.\\F:\\"), GENERIC_READ, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (aFile == INVALID_HANDLE_VALUE)
{
printf("\n");
printf("Bad handle value. Error %d \n", GetLastError());
}
From there I want to read a stream of 512 bytes (the boot sector) to a .bin file, but I can't seem to get past the handle creation first. Does Windows prevent applications from opening a handle to removable drives?
That code has two problems. First, the path. You are actually specifying the root folder of the drive; what you really need is the volume. Remove the trailing backslash from the path; i.e. _T("\\\\.\\F:"). Secondly, you need to specify FILE_SHARE_READ | FILE_SHARE_WRITE; you are trying to open it in exclusive mode, and this will fail. See MSDN documentation for CreateFile for more information.

Resources