Skip to content

System.IO.Directory.Delete should check for the name surrogate bit on reparse tags before deciding if it should enumerate directories with reparse points #23646

Description

@Petermarcu

Copied from internal bug 402782

System.IO.Directory.Delete recursive delete will not recurse through directory placeholders as it should (directory placeholders are a feature of GvFlt and the
container filter).

Christian Allred [callred] found that the Delete code "will avoid trying to enumerate directories that are any kind of reparse point, rather than just name surrogates ... It should be fixed to check for the name surrogate bit on the reparse tag, same as rmdir /s does"

See: http://ddindex/?rightProject=mscorlib&file=system%5cio%5cdirectory.cs&line=1324)

rmdir code

//
//  We now enumerate and delete children iff:
//      directory and not reparse/namesurrogate
//
if (IsDirectory( find_data.dwFileAttributes )
    && !(IsReparse( find_data.dwFileAttributes ) && IsReparseTagNameSurrogate( find_data.dwReserved0 ))) {
    ...
    //  (Enumerate, deleting files and recursing into directories)
    ...
}

return RemoveDirectoryForce(pszDirectory);

...

Rmdir /s checks the name surrogate bit using IsReparseTagNameSurrogate, which is in ntifs.h. It is defined as:

#define IsReparseTagNameSurrogate(_tag) (
((_tag) & 0x20000000)
)

If you're doing a FindFirst/FindNext, the _tag value is the dwReserved0 field of the WIN32_FIND_DATA structure. That is how rmdir /s does its check:

//
//  We now enumerate and delete children iff:
//      directory and not reparse/namesurrogate
//
if (IsDirectory( find_data.dwFileAttributes )
    && !(IsReparse( find_data.dwFileAttributes ) && IsReparseTagNameSurrogate( find_data.dwReserved0 ))) {
    ...
    //  (Enumerate, deleting files and recursing into directories)
    ...
}
return RemoveDirectoryForce(pszDirectory);

...

Forgot to mention, the name surrogate bit is set on directory junctions, symlinks, etc. So having this check correctly identifies other types of reparse points that a recursive delete would not want to recurse in to.

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions