c - 根据磁盘 ID 删除文件

标签 c winapi ntfs createfile

如所述here , 使用 SetFileInformationByHandleFILE_DISPOSITION_INFO 允许将打开句柄的文件设置为在所有句柄关闭时删除。

但是,我正在尝试根据 FILE_DISPOSITION_INFO 检索到的文件索引(磁盘 ID)删除文件和 OpenFileById为了安全地删除目录中仅大小写不同的文件/目录。 在我的用例中这样做是安全的,因为在 NTFS 系统上,文件索引是 persistent until deletion , 否定当前代码库处理的 ReplaceFile 的使用。

但是,当尝试删除句柄时,出现错误 87 (ERROR_INVALID_PARAMETER)。 如果我使用 CreateFileW 创建的句柄删除,我没有遇到任何问题。 不过,我不能这样做,因为 Windows 无法区分相同大小写的两个文件/文件夹,即使 NTFS 可以。

我也知道用 OpenFileById 打开的硬链接(hard link)文件存在歧义。 , 因为硬链接(hard link)文件共享相同的磁盘 ID。 硬链接(hard link)文件的问题可以被认为与这种情况无关。 我只会按 ID 删除目录,不能硬链接(hard link)。

我的 OpenFileById 中是否缺少参数或设置?称呼? 不知何故,在我的SetFileInformationByHandle打电话?

我尝试过的其他方法:

  • 调用 DuplicateHandleOpenFileById句柄,为 dwDesiredAccess 提供 DELETE,并使用它。 相同的 ERROR_INVALID_PARAMETER 结果。
  • 使用ReOpenFileOpenFileById句柄,为 dwDesiredAccess 提供 DELETE,并使用它。 相同的 ERROR_INVALID_PARAMETER 结果。
  • 使用ReOpenFileOpenFileById句柄,为 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 及更高版本。也欢迎提出代码改进建议。

最佳答案

为了使 FILE_DISPOSITION_INFO 正常工作,您需要在 CreateFile 函数中指定 DELETE 访问权限,如 https://msdn.microsoft.com/en-us/library/windows/desktop/aa365539(v=VS.85).aspx 中所报告的那样:

You must specify appropriate access flags when creating the file handle for use with SetFileInformationByHandle. For example, if the application is using FILE_DISPOSITION_INFO with the DeleteFile member set to TRUE, the file would need DELETE access requested in the call to the CreateFile function. To see an example of this, see the Example Code section. For more information about file permissions, see File Security and Access Rights. I.e.

//...
  HANDLE hFile = CreateFile( TEXT("tempfile"), 
                             GENERIC_READ | GENERIC_WRITE | DELETE,  //Specify DELETE access!
                             0 /* exclusive access */,
                             NULL, 
                             CREATE_ALWAYS,
                             0, 
                             NULL);

但似乎无法使用使用OpenFileById() 创建的句柄,因为该函数无法接受DELETE 标志。
来自 https://msdn.microsoft.com/en-us/library/windows/desktop/aa365432(v=vs.85).aspxOpenFileById() 上可以读取: dw期望

Access [in]
The access to the object. Access can be read, write, or both.

即使设置 DELETEGENERIC_ALL 函数也会失败。
如果将传递给 SetFileInformationByHandle 的句柄替换为使用 CreateFile 函数创建并设置了 DELETE 标志的句柄,如以上,它有效。

关于c - 根据磁盘 ID 删除文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36217150/

相关文章:

active-directory - 在 Active Directory 中,安全审核事件如何传输到域 Controller 事件日志?该机制如何扩展?

c++ - 获取文件的完整路径给定其 "Reference Number"

windows - Git : remove totally nonexistent file from. ..好吧......来 self 不知道在哪里

c - 如何通过函数设置指针?

c++ - 无论如何,是否允许一个应用程序线程在 GDB 中的断点处继续运行?

winapi - 从自定义控件通知父窗口的首选方式是什么?

windows - 在不同进程之间共享 HDC

perl - 我使用什么 Perl 模块从 Windows 上的相对路径获取绝对路径(包括文件名)?

c - 从 C 中的文件读取后,来自标准输入的垃圾字符

c - 指针问题和困惑