Reputation: 1204
I have an process without elevated privileges, and because of that, I cannot get the File attribute of certains files/directories
const auto attr = GetFileAttributesW(path);
or
auto *pwfd = new WIN32_FIND_DATAW;
const auto handle = FindFirstFileW(path, pwfd);
In both cases it makes sense that I cannot access the file attributes, (because I do not have elevated privileges)
But all I need to know is if the path
is a file or directory.
Is there a way of knowing if a valid path is a file or a directory without elevated privileges?
Edit It is not a duplicate of How can I tell if a given path is a directory or a file? (C/C++), (for example), I already know how to get file attributes.
I am asking about knowing if the path is a file/directory without elevated privileges.
Upvotes: 0
Views: 785
Reputation: 33804
first of all ERROR_ACCESS_DENIED
returned by GetLastError
not always mean access denied error. usual (but not always) source of error - is NTSTATUS
code, returned by native api and then is converted to win32 error code via RtlNtStatusToDosError
. but this conversion not injective. many different NTSTATUS
codes converted to the same ERROR_ACCESS_DENIED
, not only STATUS_ACCESS_DENIED
, as result frequently exist confusion about real reason of error. in case GetFileAttributes
, FindFirstFileExW
fail better call
extern "C" NTSYSAPI ULONG NTAPI RtlGetLastNtStatus();
api, instead GetLastError()
. this is undocumented, but very helpful in some cases.
now about concrete GetFileAttributesW
- we need have FILE_READ_ATTRIBUTES
access to the file, for not fail with access denied. but file systems grant FILE_READ_ATTRIBUTES
to caller or if file security descriptor grant it, of if parent folder grant FILE_LIST_DIRECTORY
for caller. of course if we have not both FILE_LIST_DIRECTORY
for parent folder and FILE_READ_ATTRIBUTES
for file and caller have not SeBackupPrivilege
(This privilege causes the system to grant all read access control to any file, regardless of the access control list (ACL) specified for the file.) - here nothing can be done.
but in concrete case i guess that op really got not STATUS_ACCESS_DENIED
but STATUS_DELETE_PENDING
error. this error will be if somebody call DeleteFile
but still exist open handles on file:
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 toCreateFile
to open the file fail withERROR_ACCESS_DENIED
.
(really of course NTSTATUS code will be STATUS_DELETE_PENDING
in this case but RtlNtStatusToDosError
convert it to ERROR_ACCESS_DENIED
)
so possible reason, why GetFileAttributes
can fail, is that file marked for deletion on close. but if file still not deleted, possible get it attributes via FindFirstFileExW
. note - FindFirstFileExW
return information about file attributes in parent folder. for call this api we need only FILE_LIST_DIRECTORY
access to the parent folder. this is reason why file systems grant FILE_READ_ATTRIBUTES
for file if we have FILE_LIST_DIRECTORY
for parent folder. also GetFileAttributes
can fail with error STATUS_SHARING_VIOLATION
if call it on page file (of course very special case). if we have not FILE_LIST_DIRECTORY
access to parent folder (and no backup privilege enabled) - FindFirstFileExW
fail too and here nothing can be done.
so possible solution, for not fail, when file marked for deletion on close - call FindFirstFileExW
in this case. note that FindFirstFileExW
much more expensive call compare GetFileAttributes
, from another side situation when we query file marked for deletion very rare. so always better first call GetFileAttributes
and only if it fail with code STATUS_DELETE_PENDING
try FindFirstFileExW
call.
NTSTATUS GetFileAttributesEx(PCWSTR FileName, DWORD* pdwFileAttributes)
{
DWORD dwFileAttributes = GetFileAttributes(FileName);
if (INVALID_FILE_ATTRIBUTES != dwFileAttributes)
{
*pdwFileAttributes = dwFileAttributes;
return STATUS_SUCCESS;
}
NTSTATUS status = RtlGetLastNtStatus();
switch (status)
{
case STATUS_SHARING_VIOLATION: // this is only for pagefile i think can be
case STATUS_DELETE_PENDING:
WIN32_FIND_DATAW fd;
HANDLE hFile = FindFirstFileExW(FileName, FindExInfoBasic, &fd, FindExSearchNameMatch, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
*pdwFileAttributes = fd.dwFileAttributes;
FindClose(hFile);
status = STATUS_SUCCESS;
}
else
{
status = RtlGetLastNtStatus();
}
break;
}
return status;
}
demo code for test this:
ULONG cb = 0, rcb = 0x80;
static volatile UCHAR guz = 0;
PVOID stack = alloca(guz);
PWSTR FileName = 0;
do
{
if (cb < rcb)
{
cb = (ULONG)((PWSTR)stack - (FileName = (PWSTR)alloca((rcb - cb)* sizeof(WCHAR))));
}
rcb = ExpandEnvironmentStringsW(L"%tmp%/test.tmp", FileName, cb);
} while (cb < rcb);
if (rcb)
{
HANDLE hFile = CreateFile(FileName, DELETE, 0, 0, CREATE_NEW,
FILE_ATTRIBUTE_TEMPORARY|FILE_ATTRIBUTE_HIDDEN|FILE_FLAG_DELETE_ON_CLOSE, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
ULONG dwFileAttributes;
GetFileAttributesEx(FileName, &dwFileAttributes);
static FILE_DISPOSITION_INFO fdi = { TRUE };
SetFileInformationByHandle(hFile, FileDispositionInfo, &fdi, sizeof(fdi));
GetFileAttributesEx(FileName, &dwFileAttributes);
CloseHandle(hFile);
GetFileAttributesEx(FileName, &dwFileAttributes);
}
}
Upvotes: 1