How to check whether a directory is readable or writable? - c++

I want a way to know whether a directory/folder is readable or writable. I searched for a direct way to do that by a function or like that but I didn't find it.
I tried to do it indirectly as follow:
Is readable:
WIN32_FIND_DATAA dirData;
HANDLE hDir;
hDir = FindFirstFile("C:\\folder", &dirData);
if (hDir == INVALID_HANDLE_VALUE)
return false;
return true;
Is writable:
DWORD attr = GetFileAttributes(m_dirPath);
if (attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_READONLY)
return false;
return true;
The first code is an indirect way to know whether a directory
readable but it is not efficient because when the directory is empty
it returns 0 which is not readable.
The second code to check whether a directory is writable but it
always returns 1 which is writable although I have changed the
directory permission to read-only.
Is there a direct or indirect way to know whether a directory is readable or writable?

I want a way to know whether a directory/folder is readable or
writable.
Directly to try to open the directory with read/write access permission via CreateFile API:
HANDLE tDir = CreateFile(L"D:\\testNew", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (INVALID_HANDLE_VALUE == tDir)
printf("Open directory failed with error %d \n", GetLastError());
else
printf("Readable. \n");
tDir = CreateFile(L"D:\\testNew", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (INVALID_HANDLE_VALUE == tDir)
printf("Open directory failed with error %d \n", GetLastError());
else
printf("Writable. \n");
If it is a read-only directory you will receive access denied error when you open it with GENERIC_WRITE.
For read-only, the directory maybe set to deny current user to write, however it is not a read-only directory. At this time you will get "This directory is not read-only" result but you still can't write.
Update:
As #RaymondChen pointed out, you can confirm the desired access right to a directory more accurately using file access rights constants. Take FILE_LIST_DIRECTORY as an example:
tDir = CreateFile(L"D:\\testNew", FILE_LIST_DIRECTORY, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (INVALID_HANDLE_VALUE == tDir)
printf("Open directory failed with error %d \n", GetLastError());
else
printf("Has right to list the contents of the directory.\n");

Related

Detect if file is open locally or over share

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)

CreateFileW with exclusive access succeeds when LoadLibraryEx called with LOAD_LIBRARY_AS_DATAFILE

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();
}

Why the file could be written after changing the attribute to readonly under windows?

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.

CreateFile path error in Visual C++

for (int i = 0; i < 1000; i++)
{
sprintf_s(text, "Text from process %d\r\n", GetCurrentProcessId());
HANDLE hFile = CreateFile(_T("\\\\.\\Device\\HarddiskVolume2\\dev\\test\\test.txt"), GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
cout << "Error opening file " << GetLastError() << " at index " << i << endl;
break;
}
SetFilePointer(hFile, GetFileSize(hFile, NULL), NULL, FILE_BEGIN);
DWORD bytes;
WriteFile(hFile, text, strlen(text), &bytes, NULL);
CloseHandle(hFile);
}
i'm trying to open write using the actual device name
\Device\HarddiskVolume2 is mapped to c:\
the folder already exist in c drive
i'm getting error opening file
Error code 3 is ERROR_PATH_NOT_FOUND. The reason you see that is because \\.\Device\HarddiskVolume2 is the path of the raw device. If you use that path, you no longer have access to the file system. What that path gives you is access to the raw disk sectors.
If you must use \\.\Device\HarddiskVolume2 to identify the drive then you need to first convert, by some means or other, to a mapped drive letter, or a DOS device path for the volume. The latter would be, in your case, \\.\HarddiskVolume2
Please refer
ERROR_PATH_NOT_FOUND
3 (0x3)
The system cannot find the path specified.
So please try to check the path is available or not ..\\Device\\HarddiskVolume2\\dev\\test\\test.txt.
this is because program unable to find the path
or try this:
..\\Device\\HarddiskVolume2\\dev\\test\\test.txt
if the path is in same directory as your exe.
Reason
While creating the file the compiler look into the path which is in the directory where the exe file is. If it is not found then Program will check into the Drive where the Program is running.
Also try to get some information from CreateFile.
hope this will help you.

Reading a user-specified file with the Win32 API

I'm trying to prompt the user to enter a filename/path at the console and then attempt to open this file using CreateFile(). At the moment, the call to CreateFile() works if i use a hardcoded filename and the TEXT() macro. However, upon passing it user input the call fails and GetLastError() returns error 123 or "The filename, directory name, or volume label syntax is incorrect". Below is the relevant code, I'm pretty lost as to why this is happening.
LPTSTR dllPath;
LPDWORD dllPathLength;
dllPath = (LPTSTR)calloc(MAX_PATH, sizeof(TCHAR));
dllPathLength = new DWORD;
if(ReadConsole(hStdIn, dllPath, MAX_PATH, dllPathLength, NULL)==0)
{
_tprintf(TEXT("ReadConsole failed with error %d\n"), GetLastError());
return 1;
}
_tprintf(TEXT("File path entered: %s\n"), dllPath);
hDll = CreateFile(dllPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, NULL, NULL);
if (hDll == INVALID_HANDLE_VALUE)
{
_tprintf(TEXT("CreateFile failed with error %d\n"), GetLastError());
return 1;
}
For reference, to make it work with the hardcoded file path I replaced the "dllPath" parameter in the call to CreateFile() with "TEXT("C:\log.log")".
Any help would be much appreciated! Apologies in advance if this is an obvious mistake, i'm still trying to get used to Windows-style C programming, and never was very good with the regular style either.
Try this:
TCHAR dllPath[MAX_PATH+1] = {0};
DWORD dllPathLength = 0;
if(!ReadConsole(hStdIn, dllPath, MAX_PATH, &dllPathLength, NULL))
{
_tprintf(TEXT("ReadConsole failed with error %u\n"), GetLastError());
return 1;
}
_tprintf(TEXT("File path entered: %s\n"), dllPath);
hDll = CreateFile(dllPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, NULL, NULL);
if (hDll == INVALID_HANDLE_VALUE)
{
_tprintf(TEXT("CreateFile failed with error %u\n"), GetLastError());
return 1;
}
If that still does not work, then make sure ReadConsole() is not including a line break or other terminator at the end of the returned path to make it invalid. If it is, you will have to strip it off before calling CreateFile().