【发布时间】:2016-07-13 01:04:33
【问题描述】:
如here 所述,使用SetFileInformationByHandle 和FILE_DISPOSITION_INFO
允许设置一个打开句柄的文件,在所有句柄关闭时删除。
但是,我正在尝试根据FILE_DISPOSITION_INFO 检索到的文件索引(磁盘 ID)删除文件,并且
OpenFileById 为了安全地删除目录中的文件/目录,这些文件/目录仅在大小写上有所不同。
在我的用例中这样做是安全的,因为在 NTFS 系统上,文件索引是persistent until deletion,
否定使用当前代码库处理的ReplaceFile。
但是,在尝试删除句柄时,我收到错误 87 (ERROR_INVALID_PARAMETER)。
如果我使用CreateFileW 创建的句柄删除,我不会遇到任何问题。
不过,我不能这样做,因为 Windows 将无法区分相同大小写的两个文件/文件夹,即使 NTFS 可以。
我也知道使用OpenFileById 打开的硬链接文件存在歧义,
因为硬链接文件共享相同的磁盘 ID。
硬链接文件的问题可以被认为与这种情况无关。
我只会按 ID 删除目录,不能硬链接。
OpenFileById 调用中是否缺少我的参数或设置?
不知何故,在我的SetFileInformationByHandle 电话中?
我尝试过的其他方法:
- 使用
OpenFileById句柄调用DuplicateHandle,为dwDesiredAccess提供DELETE,并使用它。 相同的ERROR_INVALID_PARAMETER结果。 - 使用
ReOpenFile和OpenFileById句柄,为dwDesiredAccess提供DELETE,并使用它。 相同的ERROR_INVALID_PARAMETER结果。 - 将
ReOpenFile与OpenFileById句柄一起使用,为dwDesiredAccess提供DELETE,并提供FILE_FLAG_DELETE_ON_CLOSE标志。 没有给出错误,但在所有句柄关闭后文件仍然存在。
这是一个重现问题的最小但完整的示例:
#include <stdio.h>
#include <sys/stat.h>
#include <Windows.h>
DWORD getFileID(LPCWSTR path, LARGE_INTEGER *id)
{
HANDLE h = CreateFileW(path, 0, 0, 0, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT |
FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_POSIX_SEMANTICS,
0);
if (h == INVALID_HANDLE_VALUE)
return GetLastError();
BY_HANDLE_FILE_INFORMATION info;
if (!GetFileInformationByHandle(h, &info))
{
DWORD err = GetLastError();
CloseHandle(h);
return err;
}
id->HighPart = info.nFileIndexHigh;
id->LowPart = info.nFileIndexLow;
CloseHandle(h);
return ERROR_SUCCESS;
}
DWORD deleteFileHandle(HANDLE fileHandle)
{
FILE_DISPOSITION_INFO info;
info.DeleteFileW = TRUE;
if (!SetFileInformationByHandle(
fileHandle, FileDispositionInfo, &info, sizeof(info)))
{
return GetLastError();
}
return ERROR_SUCCESS;
}
int wmain(DWORD argc, LPWSTR argv[])
{
if (argc != 3)
{
fwprintf(stderr, L"Arguments: <rootpath> <path>\n");
return 1;
}
DWORD err;
HANDLE rootHandle = CreateFileW(
argv[1], 0, 0, 0, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT |
FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_POSIX_SEMANTICS,
0);
if (rootHandle == INVALID_HANDLE_VALUE)
{
err = GetLastError();
fwprintf(stderr,
L"Could not open root directory '%s', error code %d\n",
argv[1], err);
return err;
}
LARGE_INTEGER fileID;
err = getFileID(argv[2], &fileID);
if (err != ERROR_SUCCESS)
{
fwprintf(stderr,
L"Could not get file ID of file/directory '%s', error code %d\n",
argv[2], err);
CloseHandle(rootHandle);
return err;
}
fwprintf(stdout,
L"The file ID of '%s' is %lld\n",
argv[2], fileID.QuadPart);
FILE_ID_DESCRIPTOR idStruct;
idStruct.Type = FileIdType;
idStruct.FileId = fileID;
HANDLE fileHandle = OpenFileById(
rootHandle, &idStruct, DELETE, FILE_SHARE_DELETE, 0,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
if (fileHandle == INVALID_HANDLE_VALUE)
{
err = GetLastError();
CloseHandle(rootHandle);
fwprintf(stderr,
L"Could not open file by ID %lld, error code %d\n",
fileID.QuadPart, err);
return err;
}
err = deleteFileHandle(fileHandle);
if (err != ERROR_SUCCESS)
{
fwprintf(stderr,
L"Could not delete file by ID '%lld', error code %d\n",
fileID.QuadPart, err);
}
CloseHandle(fileHandle);
struct _stat _tmp;
fwprintf(stdout,
L"File was %ssuccessfully deleted\n",
(_wstat(argv[2], &_tmp) == 0) ? L"not " : L"");
CloseHandle(rootHandle);
return err;
}
任何解决方案都必须适用于 Vista 及更高版本。也欢迎提出代码改进建议。
【问题讨论】:
-
尝试在从 OpenFileById 获得的句柄上使用 DuplicateHandle,将
dwDesiredAccess设置为DELETE。 -
@HarryJohnston 好主意,可惜没用。我已经编辑了这个问题,以包含我迄今为止尝试过的内容。
-
大概系统内核被配置为区分大小写,否则你一开始就不会有这样的文件;我不能使用 NtCreateFile 吗?
-
SWAG:听起来很愚蠢,您可能不需要在 OpenFileById 调用中包含 POSIX_SEMANTICS 来获得“兼容”句柄吗?是的 - 我知道 POSIX_SEMANTICS 名义上只与文件名有关......但世界上有更愚蠢的东西。 @HarryJohnston 提到了 ERROR_INVALID_PARAMETER - 也许在内部,它是“严重”比较标志。
-
作为记录,documentation here: File System Behavior Overview (PDF) 确认(第 4.3.2 节)您不能为由 ID 打开的句柄设置关闭时删除标志。
标签: c winapi ntfs createfile