-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
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).
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 callsSetInformationFile.ntdll!NtDeleteFileMarks for deletion and silently fails as well (There is some controversy if it just setsFILE_DELETE_ON_CLOSE).- Open for deletion +
ntdll!NtSetInformationFilewithFileDispositionInformation: this marks it for deletion and reports if not-empty.