Как определить, является ли путь файлом или каталогом без повышенных прав - PullRequest
0 голосов
/ 08 октября 2018

У меня есть процесс без повышенных привилегий, и из-за этого я не могу получить атрибут File определенных файлов / каталогов

const auto attr = GetFileAttributesW(path);

или

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

В обоих случаях онимеет смысл, что я не могу получить доступ к атрибутам файла (потому что у меня нет повышенных привилегий)

Но все, что мне нужно знать, это то, является ли path файлом или каталогом.

IsЕсть ли способ узнать, является ли допустимый путь файлом или каталогом без повышенных привилегий?

Редактировать Это не дубликат Как я могусказать, является ли данный путь каталогом или файлом?(C / C ++) , (например), я уже знаю, как получить атрибуты файла.

Я спрашиваю, чтобы узнать, является ли путь файлом / каталогом без повышенных привилегий.

1 Ответ

0 голосов
/ 08 октября 2018

в первую очередь ERROR_ACCESS_DENIED, возвращаемое GetLastError, не всегда означает ошибку отказа в доступе.Обычный (но не всегда) источник ошибки - это NTSTATUS код, возвращаемый собственным API, а затем преобразуемый в код ошибки win32 через RtlNtStatusToDosError.но это обращение не инъективно.много разных NTSTATUS кодов, преобразованных в один и тот же ERROR_ACCESS_DENIED, а не только STATUS_ACCESS_DENIED, в результате чего часто возникает путаница относительно реальной причины ошибки.в случае неудачи GetFileAttributes, FindFirstFileExW лучше позвонить

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), указанного для файла. ) - здесь ничего не поделаешь.

но в конкретном случае я думаю, что операционная ошибка действительно не STATUS_ACCESS_DENIED, а STATUS_DELETE_PENDING ошибка.эта ошибка будет, если кто-то вызовет DeleteFile, но все еще существуют открытые дескрипторы файла:

Функция DeleteFile помечает файл для удаления при закрытии.Следовательно, удаление файла не происходит до тех пор, пока последний дескриптор файла не будет закрыт.Последующие вызовы CreateFile для открытия файла завершаются неудачно с ERROR_ACCESS_DENIED.

(в действительности, конечно, код NTSTATUS будет STATUS_DELETE_PENDING в этом случае, но RtlNtStatusToDosError преобразует его в ERROR_ACCESS_DENIED)

Возможная причина, по которой GetFileAttributes может завершиться ошибкой, заключается в том, что файл помечен для удаления при закрытии.но если файл все еще не удален, возможно получить его атрибуты через FindFirstFileExW.примечание - FindFirstFileExW возвращает информацию об атрибутах файла в родительской папке.для вызова этого API нам нужен только FILE_LIST_DIRECTORY доступ к родительской папке.По этой причине файловые системы предоставляют FILE_READ_ATTRIBUTES для файла, если у нас есть FILE_LIST_DIRECTORY для родительской папки.также GetFileAttributes может завершиться с ошибкой STATUS_SHARING_VIOLATION, если вызвать его в файле подкачки (конечно, очень особый случай)если у нас нет FILE_LIST_DIRECTORY доступа к родительской папке (и не включены права на резервное копирование) - FindFirstFileExW тоже не удастся, и здесь ничего не поделаешь.

так что возможное решение, если не файл, помеченный для удаленияпри закрытии - позвоните FindFirstFileExW в этом случае.обратите внимание, что FindFirstFileExW намного более дорогой вызов по сравнению GetFileAttributes, с другой стороны, когда мы запрашиваем файл, помеченный для удаления, очень редко.поэтому всегда лучше сначала позвонить GetFileAttributes и только в случае неудачи с кодом STATUS_DELETE_PENDING попробовать FindFirstFileExW вызов.

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

демонстрационный код для проверки этого:

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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...