Skip to content

std::fs::remove_dir_all() doesn't delete all files on Windows 7 #99934

Closed
@crusader-mike

Description

@crusader-mike

On Windows 7 my incremental builds produce following warnings:

warning: Failed to garbage collect finalized incremental compilation session directory `\\?\D:\Work\<skip>\target\debug\incre
mental\<skip>-h0ku3qc8pde\s-gc3wk9dfjy-13pdd9o-11auxo9w4xmty`: The directory is not empty. (os error 145)

I tracked this issue down to a bug in std::fs::remove_dir_all() (here). This function tries "posix delete" each file and if it fails -- falls back to "windows delete". Problem is that it does not restart enumeration in fallback -- thus first batch of filenames end up being skipped (and subsequent "delete directory" fails because dir is not empty). See this (note false is always passed as restart parameter). Enumeration state is stored within underlying kernel object, not handle -- duplicating handle won't reset it.

I suggest ReOpenFile() before fallback or better yet -- reuse DirBuf returned on first iteration.

$ cargo --version
cargo 1.64.0-nightly (8827baaa7 2022-07-14)

General notes:

  • 1kb DirBuf used by std::fs is way too small (it takes about 5-10 files) -- enumerating directory with 1 mil files will take ages, I suggest 64kb (or better yet -- make it a parameter)
  • If you need an object model that "fits" both Posix and Win32 FS API I suggest smth like this:
    • have a Dir object that represents "open" directory and allows access to other objects using relative path. It should also have:
      fn enumerate(&self, local_path, buf_sz) -> DirEnum;  <-- will call ReOpenFile() (or it's equivalent from NT API)
      fn enumerate_self(self, buf_sz) -> DirEnum;   <-- note this consumes self (no need to ReOpenFile, just move underlying HANDLE)
    
    • have a DirEnum object that represents open directory with active enumeration going on and has same interface plus:
    fn done(&self) -> bool;
    fn next_batch(&self, restart: bool) -> DirEntryBatch;  <-- this should use syscall(SYS_getdents) on Linux (to perform) and returned object can be empty (all entries marked as deleted) without done() being true
    

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions