根据磁盘ID删除文件

Aly*_*sen 13 c winapi ntfs createfile

如上所述这里,使用SetFileInformationByHandleFILE_DISPOSITION_INFO 允许一个设置文件有打开的句柄在被关闭所有句柄被删除.

不过,我想删除的检索基于其文件索引(磁盘ID)的文件FILE_DISPOSITION_INFO,并 OpenFileById以安全地删除其中的区别仅在情况下的目录文件/目录.这在我的用例中是安全的,因为在NTFS系统上,文件索引在删除之前持久的,否定ReplaceFile当前代码库处理的使用.

但是,在尝试删除句柄时,我收到错误87(ERROR_INVALID_PARAMETER).如果我使用创建的句柄删除CreateFileW,我遇到没有问题.但是,我无法做到这一点,因为Windows无法区分同一案例的两个文件/文件夹,即使NTFS可以.

我也知道打开硬链接文件存在歧义OpenFileById,因为硬链接文件共享相同的磁盘ID.可以认为硬链接文件的问题与此方案无关.我只会按ID删除目录,不能硬链接.

我的OpenFileById通话中是否有参数或设置?不知怎的,在我的SetFileInformationByHandle电话里?

我试过的其他方法:

  • 调用DuplicateHandleOpenFileById处理,提供DELETEdwDesiredAccess和使用.ERROR_INVALID_PARAMETER结果相同.
  • 使用ReOpenFileOpenFileById处理,提供DELETEdwDesiredAccess和使用.ERROR_INVALID_PARAMETER结果相同.
  • 使用ReOpenFileOpenFileById处理,提供DELETEdwDesiredAccess,并提供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;
}
Run Code Online (Sandbox Code Playgroud)

任何解决方案必须适用于Vista及更高版本.也欢迎提出改进代码的建议.

Fra*_*e_C 1

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

创建与 SetFileInformationByHandle 一起使用的文件句柄时,必须指定适当的访问标志。例如,如果应用程序使用 FILE_DISPOSITION_INFO 并将 DeleteFile 成员设置为 TRUE,则文件将需要在调用 CreateFile 函数时请求的 DELETE 访问权限。要查看此示例,请参阅示例代码部分。有关文件权限的详细信息,请参阅文件安全和访问权限。IE

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

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

访问 [in]
对对象的访问。访问可以是读、写或两者兼而有之。

甚至设置DELETEGENERIC_ALL功能失败。
如果将传递给的句柄替换SetFileInformationByHandle为使用设置了标志的CreateFile函数创建的句柄DELETE,如上所述,它就可以工作。