Skip to content

windows.DeleteFile Does not fail on non-empty directories #15315

@xEgoist

Description

@xEgoist

Zig Version

0.11.0-dev.2613+b42562be7

Steps to Reproduce and Observed Behavior

Upon reading this unrelated issue: dokan-dev/dokany#883,
I learned that calling NtCreateFile with FILE_DELETE_ON_CLOSE does not fail on non-empty directories, but simply mark it as DELETE_PENDING with a success NTSTATUS.

Here are simple steps to reproduce this bug:

1- Create any folder, here i am using C:\example
2- Create any empty file inside it C:\example\test.txt

run the following:

const std = @import("std");

pub fn main() !void {
    var fd = try std.fs.openDirAbsolute("C:\\", .{});
    defer fd.close();
    try fd.deleteDir("example");
}

deleteDir will not complain and the folder will not be removed, which is not what the docs says here

Expected Behavior

DIRECTORY_NOT_EMPTY/error.DirNotEmpty should be returned.


Side Note: I am mostly guessing here, and haven't looked deeply in the changes, but I think that the recent CI timeouts #14948 are caused by windows.OpenFile infinite looping through DELETE_PENDING due to this (again, i could definately be wrong here).

zig/lib/std/os/windows.zig

Lines 141 to 156 in 23ac4dd

.USER_MAPPED_FILE => return error.AccessDenied,
.INVALID_HANDLE => unreachable,
.DELETE_PENDING => {
// This error means that there *was* a file in this location on
// the file system, but it was deleted. However, the OS is not
// finished with the deletion operation, and so this CreateFile
// call has failed. There is not really a sane way to handle
// this other than retrying the creation after the OS finishes
// the deletion.
std.time.sleep(std.time.ns_per_ms);
continue;
},
else => return unexpectedStatus(rc),
}
}
}


Solution/Alternative Delete Functions

I explored few solutions to resolve this bug to find out how other delete functions works:

  • kernel32!RemoveDirectory() returns false and sets the last error to 145 - Directory not empty. I haven't traced it, but i assume it calls SetInformationFile.
  • ntdll!NtDeleteFile Marks for deletion and silently fails as well (There is some controversy if it just sets FILE_DELETE_ON_CLOSE ).
  • Open for deletion + ntdll!NtSetInformationFile with FileDispositionInformation: this marks it for deletion and reports if not-empty.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behavior

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions