【问题标题】:Could DropBox interfere with DeleteFile()/rename()DropBox 会干扰 DeleteFile()/rename()
【发布时间】:2018-10-07 09:58:38
【问题描述】:

我有以下代码,每两次执行一次 全天分钟数:

int sucessfully_deleted = DeleteFile(dest_filename);

if (!sucessfully_deleted)
{
    // this never happens
}

rename(source_filename,dest_filename);

每隔几个小时 rename() 就会失败,并显示 errno=13 (EACCES)。涉及的文件都位于 DropBox 目录中,我有预感 DropBox 可能是原因。我认为 DeleteFile() 函数可能会返回非零的successful_deleted,但实际上 DropBox 仍可能忙于做一些与删除相关的事情,从而阻止 rename() 成功。我接下来所做的是将 rename() 更改为 my_rename() ,这将尝试 rename() 并且在任何失败时将 Sleep() 等待一秒钟并尝试第二次。果然,从那以后它一直运行良好。更重要的是,我每隔几个小时就会收到一条显示首次尝试失败的诊断消息。第二次尝试从未失败。

所以你可以说问题已经完全解决了……但我想了解可能发生的情况,以便更好地保护自己免受未来任何相关的 DropBox 问题的影响……

我真的很想有一个新的 super_delete() 函数,直到文件被正确删除并在所有方面完成后才会返回。

【问题讨论】:

  • 你知道 13 是EACCES aka "Permission denied"吗?
  • 在什么操作系统上运行?
  • @MichaelVeksler:来自“DeleteFile()”我断定它是 Windows。
  • 已编辑问题以回答 cmets。
  • 对我来说,这看起来好像DeleteFile() 函数没有“原子地”删除,但不知何故,删除请求会排队。所以你重试的方法是有道理的。另一方面,由于我们不知道“排队”可能需要多长时间......

标签: c++ c visual-studio windows-10 dropbox


【解决方案1】:

windows 下删除文件的请求真的从不删除文件而已。它用特殊标志(FCB_STATE_DELETE_ON_CLOSE)标记它FCB文件控制块)。只有当最后一个文件句柄被关闭时才会真正删除。

DeleteFile 函数将文件标记为在关闭时删除。所以, 在文件的最后一个句柄被删除之前,不会发生文件删除 关闭。随后调用 CreateFile 打开文件失败 ERROR_ACCESS_DENIED.

如果存在部分(内存映射文件)在文件上打开 - 文件甚至无法标记为删除。 api 调用失败,STATUS_CANNOT_DELETE。所以一般不可能总是删除文件。

如果存在另一个打开的文件句柄(但不是部分!)从 Windows 10 rs1 开始,则存在用于删除的新功能 - FileDispositionInformationExFILE_DISPOSITION_POSIX_SEMANTICS。在这种情况下:

通常,标记为删除的文件直到全部删除后才会真正删除 文件的打开句柄已关闭,并且该文件的链接计数 文件为零。使用标记删除文件时 FILE_DISPOSITION_POSIX_SEMANTICS,一旦 POSIX 删除句柄关闭,链接就会从可见命名空间中删除, 但是文件的数据流仍然可以被其他现有的 句柄,直到最后一个句柄被关闭。

ULONG DeletePosix(PCWSTR lpFileName)
{
    HANDLE hFile = CreateFileW(lpFileName, DELETE, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 
        FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT, 0);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        return GetLastError();
    }

    static FILE_DISPOSITION_INFO_EX fdi = { FILE_DISPOSITION_DELETE| FILE_DISPOSITION_POSIX_SEMANTICS };

    ULONG dwError = SetFileInformationByHandle(hFile, FileDispositionInfoEx, &fdi, sizeof(fdi)) 
        ? NOERROR : GetLastError();

    // win10 rs1: file removed from parent folder here
    CloseHandle(hFile);

    return dwError;
}

【讨论】:

  • 这个答案有很多我不明白的地方 - 您是否声称此函数将删除文件并且在文件完全删除之前不会返回(就像我在 OP 中建议的 super_delete() 一样?
  • @Mick - DeletePosix 从可见命名空间中删除。因此,您可以使用此名称创建/重命名新文件。如果此文件没有其他打开的句柄 - 它将被删除,否则只有在最后一个打开的句柄时才会关闭。如果文件中存在部分 - 此调用失败。但在这种情况下都失败了。如果您的 DeleteFile 没有失败 - 这意味着在您的具体案例中没有文件部分
  • @Mick - 所以如果文件上存在其他打开的句柄 - 它不会被完全删除 - 这是不可能的,直到句柄没有关闭。但它的名称将从文件夹中删除(取消链接)。你可以重命名为这个名字
  • @Mick - 无论如何 - 即使从 c++ 视图来看,这也是通用概念 - 对象(文件)在存在对它的引用(句柄)之前不能被删除。 DeleteFile 不能只删除文件。它只能在最后一个对象引用关闭/释放时将其标记为删除。如果存在外部引用 - 它可以无限期地存在 - 如果等待这个 - DeleteFile 永远不会返回。所以按照设计 - 它只是返回,但文件在此之后仍然可以无限期地存在。新(来自 win 10 rs1)功能 - 让删除文件名,不喜欢它。所以文件仍然存在但不可见并且不保留名称 - 重命名不会失败
【解决方案2】:

更新

抱歉,我第一次没有正确回答问题。我以为 DeleteFile 返回错误 13。

现在我知道 DeleteFile 成功但重命名之后立即失败。 可能只是文件系统的同步问题。调用 DeleteFile 后,当操作系统提交对文件系统的更改时,文件将被删除。这可能不会立即发生。 如果您需要对同一路径执行多个操作,您应该查看事务https://docs.microsoft.com/it-it/windows/desktop/api/winbase/nf-winbase-deletefiletransacteda

-- 旧答案--

没错。如果另一个应用程序处理该文件,DeleteFile 将失败。 引用 MSDN 文档https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-deletefile

The DeleteFile function fails if an application attempts to delete a file that has other handles open for normal I/O or as a memory-mapped file (FILE_SHARE_DELETE must have been specified when other handles were opened).

这适用于保管箱、防病毒软件,或者一般来说,适用于可能打开这些文件的任何其他应用程序。 Dropbox 可以随时打开文件以计算其哈希值(以查找更改)。防病毒软件也是如此。

【讨论】:

  • OP 提到 DeleteFile() never 返回失败。
  • 从您链接的文档中,我觉得这更有趣:“DeleteFile 函数将文件标记为在关闭时删除。因此,文件删除直到最后一个句柄才会发生文件已关闭。"
  • 对不起,我的错。我相应地更新了我的答案。是的,DeleteFile 不会立即删除文件,这就是后续调用重命名可能会失败的原因。
  • "DeleteFile 不会立即删除文件" 感觉这个功能完全坏掉了。
  • 我同意这个名称具有误导性。它应该类似于 MarkForDeletion。之前在这里已经讨论过:https://stackoverflow.com/questions/30530730/delete-a-file-instead-of-marking-it-for-deletion
猜你喜欢
  • 2021-02-24
  • 1970-01-01
  • 2016-08-12
  • 1970-01-01
  • 2017-12-23
  • 2020-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多