1

In this post it is suggested to use GetFileAttributes() to check if a directory exists. But apparently, GetFileAttributes() can succeed even if it is passed an invalid path.

For example, let's assume the current directory is D:/test but doing the following still doesn't return INVALID_FILE_ATTRIBUTES although the path passed to GetFileAttributes() clearly doesn't exist because there are way too many levels of ..:

DWORD attrs = GetFileAttributes("../../../../../../..");

So how can I detect if a path actually exists?

Andreas
  • 9,245
  • 9
  • 49
  • 97
  • 1
    question wrong asked. the `GetFileAttributes` work correct and the best solution here for check. but how convert relative name to nt-path ? here problem not in `GetFileAttributes` but in path conversation. try `RtlDosPathNameToNtPathName_U_WithStatus` and look to what converted your `"../../../../../../.."`. to the \??\D:\ anyway – RbMm Nov 03 '19 at 13:30

1 Answers1

2

But apparently, GetFileAttributes() can succeed even if it is passed an invalid path.

this is wrong. if path really invalid - GetFileAttributes return appropriate error (ERROR_INVALID_NAME or ERROR_FILE_NOT_FOUND usually). not also that need check for ERROR_SHARING_VIOLATION and ERROR_ACCESS_DENIED in case GetFileAttributes return INVALID_FILE_ATTRIBUTES. because win32 convert to ERROR_ACCESS_DENIED not only STATUS_ACCESS_DENIED but many other unrelated status - more correct use NtQueryAttributesFile. also exist undocumented

extern "C" NTSYSAPI BOOLEAN NTAPI RtlDoesFileExists_U( _In_ PWSTR FileName );

which do this job (internal it call NtQueryAttributesFile and check also for STATUS_SHARING_VIOLATION AND STATUS_ACCESS_DENIED)

also after api return - file already can be deleted (or created) - as result returned value can be already wrong. so usual if we need some file/folder - need not try check but create or open it. or get error in this operation


but really your problem not in GetFileAttributes but in win32 to nt path conversion.

you assume that path D:/test../../../../../../.. is wrong. but system think that not. the RtlDosPathNameToNtPathName_U_WithStatus (or related) used by system for convert win32 path to nt. if test this api with your path, this give:

UNICODE_STRING us;
if (0 <= RtlDosPathNameToNtPathName_U_WithStatus(L"D:/test../../../../../../..", &us, 0, 0))
{
    DbgPrint("%wZ\n", &us);
    RtlFreeUnicodeString(&us);
}
if (0 <= RtlDosPathNameToNtPathName_U_WithStatus(L"../../../../../../..", &us, 0, 0))
{
    DbgPrint("%wZ\n", &us);
    RtlFreeUnicodeString(&us);
}

\??\D:\ and \??\<X>:\ so system convert this to root folder on drive. and this folder exist.

RbMm
  • 31,280
  • 3
  • 35
  • 56
  • So I have to manually check if the level of `..` is valid against the current directory because Windows apparently chooses to ignore all `..` beyond the root volume and treat it as a valid path? – Andreas Nov 03 '19 at 18:48
  • @Andreas - i not think that. in this case your conversion will be conflict with system provided. what is your final target ? – RbMm Nov 03 '19 at 19:11
  • Right, there seems to be some sort of convention among operating systems that superfluous occurrences of `..` are just ignored and that such constructions aren't invalid paths because Linux and macOS show the same behaviour as Windows so this seems to be some sort of convention that I wasn't aware of. I was of the opinion that an out-of-range number of `..` would construct an illegal path but apparently that's not the case so I can just leave everything like it is. – Andreas Nov 04 '19 at 16:20