关于c ++:如何判断路径是没有提升特权的文件还是目录

How to tell if a path is a file or a directory without elevated privileges

我有一个没有提升特权的进程,因此,我无法获得某些文件/目录的File属性。

1
const auto attr = GetFileAttributesW(path);

要么

1
2
auto *pwfd = new WIN32_FIND_DATAW;
const auto handle = FindFirstFileW(path, pwfd);

在这两种情况下,我都无法访问文件属性是有道理的(因为我没有提升的特权)

但是我只需要知道path是文件还是目录。

有没有办法知道有效路径是没有提升特权的文件还是目录?

编辑这不是"如何确定给定路径是目录还是文件"的副本。 (例如,C / C ++),我已经知道如何获取文件属性。

我想知道是否该路径是没有提升特权的文件/目录。


首先,GetLastError返回的ERROR_ACCESS_DENIED并不总是意味着访问被拒绝错误。通常(但并非总是)错误源-是NTSTATUS代码,由本机api返回,然后通过RtlNtStatusToDosError转换为win32错误代码。但是这种转换不是内射的。许多不同的NTSTATUS代码不仅转换为STATUS_ACCESS_DENIED,而且转换为相同的ERROR_ACCESS_DENIED,结果经常导致关于真正错误原因的混淆。如果GetFileAttributesFindFirstFileExW无法更好地调用

1
extern"C" NTSYSAPI ULONG NTAPI RtlGetLastNtStatus();

api,而不是GetLastError()。这没有记录,但是在某些情况下很有帮助。

现在关于具体的GetFileAttributesW-我们需要对文件具有FILE_READ_ATTRIBUTES访问权限,以免访问失败而被拒绝。但是文件系统将FILE_READ_ATTRIBUTES授予调用者,或者如果文件安全描述符授予了它,则父文件夹是否授予了调用者FILE_LIST_DIRECTORY。当然,如果我们同时没有为父文件夹提供FILE_LIST_DIRECTORY且为文件和调用者提供的FILE_READ_ATTRIBUTES都没有SeBackupPrivilege(此特权将导致系统将所有文件的读取访问控制权授予任何文件,而不管访问控制列表(ACL )为文件指定的内容。)-此处无法执行任何操作。

但在具体情况下,我想op确实不是STATUS_ACCESS_DENIED而是STATUS_DELETE_PENDING错误。如果有人调用DeleteFile但文件上仍然存在打开的句柄,则会出现此错误:

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.

(在这种情况下,NTSTATUS代码当然会是STATUS_DELETE_PENDING,但RtlNtStatusToDosError会将其转换为ERROR_ACCESS_DENIED)

因此GetFileAttributes可能失败的可能原因是该文件在关闭时被标记为删除。但如果文件仍未删除,则可以通过FindFirstFileExW获取它的属性。注意-FindFirstFileExW返回有关父文件夹中文件属性的信息。对于调用此api,我们仅需要FILE_LIST_DIRECTORY访问父文件夹。这就是为什么如果父文件夹有FILE_LIST_DIRECTORY的情况,文件系统会为文件授予FILE_READ_ATTRIBUTES的原因。如果在页面文件上调用GetFileAttributes,它也会因错误STATUS_SHARING_VIOLATION而失败(当然是非常特殊的情况)。如果我们没有对父文件夹的FILE_LIST_DIRECTORY访问权限(并且未启用备份权限)-FindFirstFileExW也会失败,因此无法执行任何操作。

因此,在关闭时标记为要删除的文件时,可能的解决方案(不会失败)-在这种情况下调用FindFirstFileExW。请注意,FindFirstFileExW调用比GetFileAttributes昂贵得多,从另一方面来看,当我们查询标记为删除的文件时非常罕见。因此始终最好先调用GetFileAttributes,并且仅当失败并使用代码STATUS_DELETE_PENDING失败时才尝试FindFirstFileExW调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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;
}

演示代码进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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);
    }
}