в первую очередь 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);
}
}