0

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.

Simon Goodman
  • 1,174
  • 1
  • 8
  • 35
  • Possible duplicate of [How can I tell if a given path is a directory or a file? (C/C++)](https://stackoverflow.com/questions/146924/how-can-i-tell-if-a-given-path-is-a-directory-or-a-file-c-c) – Dalton Cézane Oct 08 '18 at 16:53
  • 3
    No, it it not a duplicate, I mentioned `GetFileAttributesW` in my question. I am asking about elevated privileges. It is not the same – Simon Goodman Oct 08 '18 at 16:56
  • Did you see this link: http://forum.codecall.net/topic/68935-how-to-test-if-file-or-directory/ ? – Dalton Cézane Oct 08 '18 at 16:58
  • @SimonGoodman If you don't have privileges to access the path it is probably not possible. – πάντα ῥεῖ Oct 08 '18 at 16:59
  • what is error(better status) returned by `GetFileAttributesW` ? are you not have access to parent folder ? – RbMm Oct 08 '18 at 17:00
  • I should mention that elevated privileges ("run as admin") are not the same as file access control. – user7860670 Oct 08 '18 at 17:01
  • 1
    how you at all get `path` ? the `GetFileAttributesW` can fail only if you not have read access to parent folder. – RbMm Oct 08 '18 at 17:05
  • @RbMm, I get the file/directory from `ReadDirectoryChangesW`, when I get a modified action I cannot tell if it was on a File or a Folder. – Simon Goodman Oct 08 '18 at 17:10
  • 1
    what is error returned by `GetFileAttributesW` ? sharing violation ? what is error returned by `FindFirstFileW` ? – RbMm Oct 08 '18 at 17:11
  • 1
    `FindFirstFileW` is more power here, because `GetFileAttributesW` can return `ERROR_SHARING_VIOLATION` but `FindFirstFileW` will be ok in this case - all what you need - access to parent folder for read – RbMm Oct 08 '18 at 17:15
  • @SimonGoodman Best you post a [MCVE] of what you're trying to do instead of having loong discussions in comments. That would probably lead to a good answer solving your actual problem. – πάντα ῥεῖ Oct 08 '18 at 17:16
  • @RbMm it returns `ERROR_ACCESS_DENIED`, I should also point out that sometimes, (but not always), `GetFileAttributesW` returns `ERROR_ACCESS_DENIED` where `FindFirstFileW` is able to return valid attributes. – Simon Goodman Oct 08 '18 at 17:17
  • 1
    *GetFileAttributesW returns ERROR_ACCESS_DENIED where FindFirstFileW is able to return valid attributes* - very strong doubt in this. for `GetFileAttributesW` we need only `FILE_READ_ATTRIBUTES` access. but this is special access - ntfs grant it if caller have `FILE_LIST_DIRECTORY` access to parent. but if you not have this access - `FindFirstFileW` will fail – RbMm Oct 08 '18 at 17:22
  • I just tested it and I get a few `ERROR_ACCESS_DENIED` from `GetFileAttributesW` if the file is in the temp folded, but then `FindFirstFileW` successfully returns the file attributes. – Simon Goodman Oct 08 '18 at 17:51
  • this probably because file is being deleted - somebody call `DeleteFile` but still exist open file handles. in this case any access to file return access denied error, but `FindFirstFileW` still able return valid attributes. but you say that in some case `FindFirstFileW` fail too. are also with access denied ? may be with file not found error ? – RbMm Oct 08 '18 at 18:25
  • Yes, when it fails on `FindFirstFileW` it is for the same reason, `ERROR_ACCESS_DENIED`, I ignore the file not found errors, (as I can no longer tell if it was a file/directory anyway). – Simon Goodman Oct 08 '18 at 18:32
  • also call `RtlGetLastNtStatus()` instead `GetLastError()` (this api not enough informative) - i almost sure that you got `STATUS_DELETE_PENDING` after failed `GetFileAttributes` – RbMm Oct 08 '18 at 18:43
  • `FindFirstFileW` fail with access denied (also call `RtlGetLastNtStatus()` for more exactly info -really win32 error 5 not always access denied) -possible you simply have not read access to folder. you can check this by try open parent folder. in call `ReadDirectoryChanges` `bWatchSubtree` is true ? if yes - possible you really have no access to folder – RbMm Oct 08 '18 at 18:46
  • so i strongly recommended call `RtlGetLastNtStatus()` after file api fail. if it return `STATUS_DELETE_PENDING` after failed `GetFileAttributes` this mean that file is being deleting. this not related to any privilege. but `FindFirstFileEx` (always call Ex version!) still able get attributes, if file still exist. – RbMm Oct 08 '18 at 18:49

1 Answers1

1

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 to CreateFile to open the file fail with ERROR_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);
    }
}
RbMm
  • 31,280
  • 3
  • 35
  • 56