I'm trying to check if a file is open in Win32:
bool CheckFileUnlocked(const TCHAR *file)
{
HANDLE fh = ::CreateFile(file, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if(fh != NULL && fh != INVALID_HANDLE_VALUE) {
return (CloseHandle(fh) == TRUE);
}
return false;
}
I need to be able to distinguish if a file is opened locally, in that case the function must return true against if it is opened from a shared path. The file itself is accessible over network, and is mapped in a shared drive. The function above tries to open file with exclusive access. I tried adding else clause reducing to:
bool CheckFileUnlocked(const TCHAR *file)
{
HANDLE fh = ::CreateFile(file, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if(fh != NULL && fh != INVALID_HANDLE_VALUE) {
return (CloseHandle(fh) == TRUE);
} else {
fh = ::CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if(fh != NULL && fh != INVALID_HANDLE_VALUE) {
return (CloseHandle(fh) == TRUE);
}
}
return false;
}
But I still couldn't figure out if the file was open locally or over network on another system. I also tried LockFileEx() and UnlockFileEx(), but I'm guessing these might be wrong approaches. How do I solve this without actually querying the Application (LibreOffice Writer), assuming it provides API level access to this condition (LO actually provides a popup upon opening said document and allows to open it as ReadOnly, or open a Copy)?
You can try GetFileInformationByHandleEx:
FileRemoteProtocolInfo should return properties of network access, and probably should fail on local files
FileStorageInfo should return properties of the storage, it might fail on network (but need to verify that)
Related
I want to recover the effective rights on a shared folder. To do that I used the methods CreateFile, GetSecurityInfo and GetEffectiveRightsFromAclA. The problem is that when I test the rights it does not work while I set the SID of the administrator. Does this come from a bad implementation of my code? I specify that I have the rights to the file.
//stringName is the file path
HANDLE file = CreateFile(stringName.c_str(), GENERIC_READ,
FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
PSID pSid;
//The SID is that of the administrator
ConvertStringSidToSidA(stringSid.c_str(), &pSid);
PACL ppDacl;
PSECURITY_DESCRIPTOR ppSecurityDescriptor;
GetSecurityInfo(file, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr,
&pSid, &ppDacl, nullptr, &ppSecurityDescriptor);
ACCESS_MASK pAccesMask;
TRUSTEE_A pTrustee;
GetEffectiveRightsFromAclA(ppDacl, &pTrustee, &pAccesMask);
//here conditions are not respected
if((pAccesMask & GENERIC_READ) == GENERIC_READ)
{
std::cout<<"ok"<<std::endl;
}
if((pAccesMask & GENERIC_WRITE) == GENERIC_WRITE)
{
std::cout<<"ok"<<std::endl;
}
if((pAccesMask & GENERIC_EXECUTE) == GENERIC_EXECUTE)
{
std::cout<<"ok"<<std::endl;
}
if((pAccesMask & GENERIC_ALL) == GENERIC_ALL)
{
std::cout<<"ok"<<std::endl;
}
Before use the pAccesMask value make sure the GetEffectiveRightsFromAclA function return ERROR_SUCCESS.
And because a Windows file object maps the GENERIC_READ bit to the READ_CONTROL and SYNCHRONIZE standard access rights. So change the condition check logic for GENERIC_READ like this:
if ((pAccesMask & READ_CONTROL) == READ_CONTROL && (pAccesMask & SYNCHRONIZE) == SYNCHRONIZE) //GENERIC_READ
{
std::cout << "GENERIC_READ" << std::endl;
}
I was under the assumption that if you use CreateFileW requesting exclusive access, that if it was already opened I should receive a sharing violation. However, I've come across an example where this is not the case. If LoadLibraryEx is called to load a DLL with the LOAD_LIBRARY_AS_DATAFILE flag set, then CreateFileW returns successfully. However there are operations that will be rejected (such as setting attributes like end of file). Is there a better way to detect if these files are open? Am I using CreateFileW incorrectly? Loading a DLL without the LOAD_LIBRARY_AS_DATAFILE flag does result in CreateFileW failing indicating it can not access the file because it is in use by another process.
HMODULE hModule = LoadLibraryEx(L"\\Pathto\\module.dll", NULL, NULL);
DWORD access = GENERIC_READ | GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY;
DWORD creation = OPEN_EXISTING;
DWORD flags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT;
HANDLE file = CreateFileW(L"\\Pathto\\module.dll", access, 0, NULL, creation, flags, 0);
The above will result in CreateFileW failing with error code 32 (can't access cause it is in use by another process) which is the expected result. If I add the flag to LoadLibraryEx:
HMODULE hModule = LoadLibraryEx(name, NULL, LOAD_LIBRARY_AS_DATAFILE);
And use the exact same call to CreateFileW then I'm told it's successful, although I will run into problems later when attempting to set end of file (as an example). Additionally, deleting the file will fail with an access denied error (without the application or any other having an open handle).
Other odd behavior involves hard links with loaded libraries. If I generate a new hard link to the loaded module, and try to delete the alternate newly created hard link, it also fails (NO open file handles, just loaded module). Why? Shouldn't it just remove the link and dereference the link count, or schedule deletion on module close since I could have obtained exclusive access previously?
Also of note, the process itself runs in a privileged user account with the following additional privileges enabled:
SE_SECURITY_NAME, SE_BACKUP_NAME, SE_RESTORE_NAME, SE_TAKE_OWNERSHIP_NAME
How would one determine if you truly have exclusive access to a file when libraries are loaded?
Edit: Just to doubly check, I verified no other open handles besides loading the module via the SysInternals tool "handle".
your case can be more clear shown in next test
ULONG TestImageAccess(PCWSTR name, BOOL bImage, BOOL bRequestWriteAccess)
{
SetLastError(0);
HANDLE hFile = CreateFileW(name, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
HANDLE hSection = CreateFileMappingW(hFile, 0,
bImage ? PAGE_READONLY|SEC_IMAGE : PAGE_READONLY|SEC_COMMIT, 0, 0, 0);
CloseHandle(hFile);
if (hSection)
{
hFile = CreateFileW(name,
bRequestWriteAccess ? GENERIC_WRITE : GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
CloseHandle(hSection);
if (hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
}
}
}
return GetLastError();
}
void TestImageAccess(PCWSTR filename)
{
TestImageAccess(filename, TRUE, TRUE); // ERROR_SHARING_VIOLATION
TestImageAccess(filename, FALSE, TRUE); // NOERROR
TestImageAccess(filename, TRUE, FALSE); // NOERROR
TestImageAccess(filename, FALSE, FALSE);// NOERROR
}
we got ERROR_SHARING_VIOLATION when we request write access to file and file mapped as image
this case is described in Executable Images:
If the user wants write access to the file, make sure there is not a
process mapping this file as an image
the call to MmFlushImageSection with MmFlushForWrite fail and you got STATUS_SHARING_VIOLATION - exactly at this line.
LoadLibraryEx with flag LOAD_LIBRARY_AS_DATAFILE create section on file with SEC_COMMIT when with 0 in flag - with SEC_IMAGE - so as image section
the privileges here absolute not related
How would one determine if you truly have exclusive access to a file
when libraries are loaded?
simply open file with access which you need and do what you need. and yes, you can got error ERROR_USER_MAPPED_FILE when you try truncate mapped file. but here nothing can be done. example 2
ULONG TestImage2(PCWSTR name)
{
HANDLE hFile = CreateFileW(name, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
HANDLE hSection = CreateFileMappingW(hFile, 0, PAGE_READONLY|SEC_COMMIT, 0, 0, 0);
CloseHandle(hFile);
if (hSection)
{
PVOID pv = MapViewOfFile(hSection, FILE_MAP_READ, 0, 0, 0);
CloseHandle(hSection);
if (pv)
{
hFile = CreateFileW(name,GENERIC_WRITE|GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
FILE_END_OF_FILE_INFO eof = { };
// ERROR_USER_MAPPED_FILE will be here
SetFileInformationByHandle(hFile, FileEndOfFileInfo, &eof, sizeof(eof));
CloseHandle(hFile);
}
UnmapViewOfFile(pv);
}
}
}
return GetLastError();
}
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.
I wrote a C++ code that writes to file and attempts to flush it to the physical disk. By the end of the code I want to know for sure that the file is written to the physical disk and to commit that I'm in a stable state even if someone unplugs the machine. Nevertheless, when I unplug immediately after the execution of all of the following lines, the file is lost i.e. it hasn't been written to the physical disk although I attempted to flush and used FILE_FLAG_WRITE_THROUGH.
HANDLE hFile = CreateFileA(
filePath.c_str(),
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL| FILE_FLAG_WRITE_THROUGH ,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
throw MyException("CreateFile failed");
}
DWORD bytesWritten = 0;
auto errorFlag = WriteFile(
hFile,
data.data(),
static_cast<DWORD>(data.size()),
&bytesWritten,
NULL);
if (bytesWritten != data.size() || errorFlag != TRUE)
{
CloseHandle(hFile);
throw MyException("WriteFile failed" + std::to_string(GetLastError()));
}
auto ret = FlushFileBuffers(hFile);
if (!ret)
{
CloseHandle(hFile);
throw MyException("FlushFileBuffers failed");
}
CloseHandle(hFile);
// The file isn't written to the disk yet!!!
How will I make sure that the file is already on the disk so I can commit the change?
I have a program writing/reading from a file, and I want to lock the file for other instances of my application. How can I do it (in c++ visual studio 2003)?
I tried using the _locking() but then also I myself cannot reach the file when trying to read/write (in the same instance).
I know there's an option of LockFile() but have no idea how to set it properly.
Please help me.
You can simply use the Win32 API CreateFile and then specify no sharing rights. This will ensure that no other processes can access the file.
The dwShareMode DWORD specifies the type of sharing you would like, for example GENERIC_READ. If you specify 0 then that means no sharing rights should be granted.
Example:
HANDLE hFile = CreateFile(_T("c:\\file.txt"), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
If you want to only lock a certain part of the file you can use LockFile or LockFileEx.
Example:
//Lock the first 1024 bytes
BOOL bLocked = LockFile(hFile, 0, 0, 1024, 0);
For locking on other platforms please see my post here.
You want LockFileEx() (exclusive file locking). Have a look at this discussion from Secure Programming Cookbook for C and C++.
After searching online for a while, I didn't find any good examples.
Here are two calls to CreateFile with the intent of locking the file for the life of a process... I use this along side the CLimitSingleInstance that uses CreateMutex for a global named mutex.
The first call to CreateFile attempts to open it, the second one creates it if necessary.
I have a little bit more thorough implementation. I implemented it in Qt, hence the qCritical() instead of std::cout and the QDir::tempPath() instead of getting that some other way.
class SingleInstance
{
protected:
DWORD m_dwLastError;
HANDLE m_hFile;
public:
SingleInstance(const char *strMutexName) { }
bool attemptToLockTempFile()
{
QString lockFile = QDir::tempPath() + "/My.exe.lock";
m_hFile = CreateFileA(lockFile.toLocal8Bit().data(), GENERIC_READ, 0,
NULL, OPEN_EXISTING, 0, NULL);
DWORD dwLastError = GetLastError();
if(m_hFile != NULL && m_hFile != INVALID_HANDLE_VALUE)
{
return true;
}
else
{
if(dwLastError == ERROR_FILE_NOT_FOUND )
{
m_hFile = CreateFileA(lockFile.toLocal8Bit().data(), GENERIC_READ,
0, NULL, CREATE_NEW, 0, NULL);
dwLastError = GetLastError();
if(m_hFile != NULL && m_hFile != INVALID_HANDLE_VALUE)
{
return true;
}
else if(dwLastError == ERROR_SHARING_VIOLATION)
{
qCritical() << "Sharing Violation on My.exe.lock";
}
else
{
qCritical() << "Error reading" << "My.exe.lock" << "-" << dwLastError;
}
}
else if(dwLastError == ERROR_SHARING_VIOLATION)
{
qCritical() << "Sharing Violation on My.exe.lock";
}
else
{
qCritical() << "Unable to obtain file lock -" << dwLastError;
}
return false;
}
}
~SingleInstance()
{
if ( m_hFile != NULL && m_hFile != INVALID_HANDLE_VALUE)
{
::CloseHandle(m_hFile); //Do as late as possible.
m_hFile = NULL;
}
}
}
Here is what you would have at the top of your main function:
SingleInstance g_SingleInstanceObj(globalId_QA);
// Makes sure that the program doesn't run if there is another
// instance already running
if (g_SingleInstanceObj.IsAnotherInstanceRunning())
{
return 0;
}