From: Corinna Vinschen on
Hi,

I have some code which is supposed to delete a file. If the file is
in use, the file gets moved to some other directory so that it
disappears from directory listings of the parent directory. The
code is basically done like this (some code removed for clarity):

#define NO_SHARE_FLAGS 0
#define ALL_SHARING_FLAGS (FILE_SHARE_READ \
| FILE_SHARE_WRITE \
| FILE_SHARE_DELETE)
bool move = false;

status = ZwOpenFile (..., DELETE, ..., NO_SHARE_FLAGS,
FILE_DELETE_ON_CLOSE);
if (status == STATUS_SHARING_VIOLATION)
{
/* Oops, file is in use, open with full share flags and mark
for moving away. */
status = ZwOpenFile (..., DELETE, ..., ALL_SHARING_FLAGS,
FILE_DELETE_ON_CLOSE);
move = true;
}
if (NT_SUCCESS (status))
{
if (move)
{
prepare_rename_information (filename, dir_to_move_to);
status = ZwSetInformationFile (..., FileRenameInformation);
}
status = ZwClose ();
}

This code works fine... for files. Now I tried to reuse the code for
removing directories. Consider another piece of code (no matter if in
the same process or another one) has opened the directory for a later
call to ZwQueryDirectoryFile:

status = ZwOpenFile (..., SYNCHRONIZE | FILE_LIST_DIRECTORY, ...,
ALL_SHARING_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_BACKUP_INTENT
| FILE_DIRECTORY_FILE);

What happens now in the above code to remove the file/directory is this.
The first call to ZwOpenFile fails, as expected, because the dir has
an open handle. The second call to ZwOpenFile succeeds. So the "move"
flag gets set and ZwSetInformationFile(FileRenameInformation) is called,
and fails! The status code is 0xc0000022, STATUS_ACCESS_DENIED.

Why does the move fail? For the open handle to this directory it
shouldn't matter in which parent directory the directory resides, the
same way as it doesn't matter to a handle to an open file.

And more important: Is there a way around this? I searched for flags
to ZwOpenFile or flags to ZwSetInformationFile which could help in this
matter, but to no avail.

Obviously one way around this is to defer the call to ZwOpenFile in the
code trying to enumerate the directory to the point where the first
directory entry is going to be read. However, this only moves the
problem to a later point in execution, it doesn't solve the problem in
a generic way. So, I'm still looking for a generic solution.


Thanks for any help,
Corinna

--
Corinna Vinschen
Cygwin Project Co-Leader
Red Hat
From: "Jeffrey Tan[MSFT]" on
Hi Corinna,

It seems that you are using undocumented Windows API ZwSetInformationFile
with FileRenameInformation parameter to move the files/directories. It
works well for the file but fail for the directory. Normally, MSDN support
does not provide official support for undocumented API issues. Anyway, I
will try my best to help you.

Have you tried this with more other directories? In this scenario, I
suspect the original code that uses the file may passed 0 as the
"ShareAccess" parameter, which does not share you to use
FileRenameInformation for the locking file.

Normally, you may use Process Monitor from the link below to monitor the
directory access of ZwSetInformationFile calling, there will be a "Access
Deny" record:
http://www.microsoft.com/technet/sysinternals/ProcessesAndThreads/processmon
itor.mspx

Process Monitor will also capture a call stack of this error access, then
you may set up the symbol server correctly and provide the symbolic call
stack for this failure here. It should be informative.

Finally, is it possible for you to create a little sample project with
steps to help me reproduce the problem? Then I will try to give it a
troubleshooting. Thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.

From: Corinna Vinschen on
Hi Jeffrey,

Jeffrey Tan[MSFT] wrote:
> Hi Corinna,
>
> It seems that you are using undocumented Windows API ZwSetInformationFile
> with FileRenameInformation parameter to move the files/directories. It

Sigh. I didn't even realize that this is an undocumented API until
you just told me. I hope you can understand my point here, though. The
idea is to have a generic delete/move operation which allows POSIX
delete semantics while maintaining a speedy operation in bulk remove
operations like the POSIX `rm -rf' case which consists of lots of
unlink/rmdir calls. The big advantage of using the ntdll API here is
the fact that the file has to be opened only once and the handle can
then be used for the whole operation. There's just no comparable win32
API to implement unlink/rmdir this efficient.

> [...]
> Have you tried this with more other directories? In this scenario, I
> suspect the original code that uses the file may passed 0 as the
> "ShareAccess" parameter, which does not share you to use
> FileRenameInformation for the locking file.

If you look into the code snippets in my original posting, you see
that the other handle has been opened with all sharing flags set.
Also keep in mind that the second ZwOpenFile in the unlink code succeeded,
which means, there was an open handle, but that handle didn't disallow
sharing. It's the subsequent FileRenameInformation which fails with
STATUS_ACCESS_DENIED, not the ZwOpenFile.

> Normally, you may use Process Monitor from the link below to monitor the
> directory access of ZwSetInformationFile calling, there will be a "Access
> Deny" record:
> http://www.microsoft.com/technet/sysinternals/ProcessesAndThreads/processmon
> itor.mspx
> [...]

This was a very valuable hint, which actually solved my problem! I
didn't think of testing with other applications but while looking into
the output of Process Monitor I tried to move the file using Explorer
which, to my surprise, succeeded when using FileRenameInformation.

By comparing the different flags used by my unlink code and used by
Explorer, I found the cause of the problem. Apparently, the semantics
of FILE_DELETE_ON_CLOSE are different between files and directories.
When the directory is opened with FILE_DELETE_ON_CLOSE, then
FileRenameInformation fails. When the directory is not opened with
FILE_DELETE_ON_CLOSE, FileRenameInformation succeeds.

So, what my code basically does now is this:

ZwOpenFile (..., NO_SHARING, flags | (dir ? 0 : FILE_DELETE_ON_CLOSE);
if (open_failed)
{
ZwOpenFile (..., ALL_SHARING, flags | (dir ? 0 : FILE_DELETE_ON_CLOSE);
have_to_move = true;
}
if (have_to_move)
ZwSetInformationFile (..., FileRenameInformation);
if (dir)
ZwSetInformationFile (..., FileDispositionInformation);
ZwClose ();

Is there any chance to learn the reason why

open(delete-on-close);
move();
close();

works for files, but emits an access denied error in the move operation
on directories?


Thanks,
Corinna

--
Corinna Vinschen
Cygwin Project Co-Leader
Red Hat
From: Corinna Vinschen on
Corinna Vinschen wrote:
> Jeffrey Tan[MSFT] wrote:
>> It seems that you are using undocumented Windows API ZwSetInformationFile
>> with FileRenameInformation parameter to move the files/directories. It
>
> Sigh. I didn't even realize that this is an undocumented API until
> you just told me. [...]

Btw., it's irritating that ZwSetInformationFile (FileRenameInformation)
is "undocumented". FltSetInformationFile (FileRenameInformation) as
well as the FILE_RENAME_OPERATION structure *are* documented and both
refer to ZwSetInformationFile in terms of the FileRenameInformation
class, see http://msdn2.microsoft.com/en-us/library/aa488684.aspx and
http://msdn2.microsoft.com/en-us/library/ms791548.aspx(*).

I don't quite understand why such a simple and basic operation should be
treated as undocumented. It doesn't do anything hidden, dangerous, or
mysterious.


Corinna


(*) The FILE_RENAME_OPERATION documentation isn't quite correct. It claims

"A file cannot be renamed if it has any open handles, unless it is
only open because of a [...] (oplock) [...]."

A file or directory can be renamed regardless if it has an open handle
or not, as long as the open handle is opened for sharing.

--
Corinna Vinschen
Cygwin Project Co-Leader
Red Hat
From: "Jeffrey Tan[MSFT]" on
Hi Corinna,

Thanks for your feedback!

Glad to see the Process Monitor makes sense for you. Your troubleshooting
information and result are also informative to me and the community.

Yes, I see the 2 links for FltSetInformationFile, ZwSetInformationFile.
However, these 2 MSDN links are both for DDK. They are both exported by
ntoskrnl.exe, not user-mode modules. The reason I say they are undocumented
is that the ZwSetInformationFile you are using is the user-mode
undocumented API exported by ntdll. Microsoft did not document these APIs
since these APIs may not keep backward compatibility, and may be changed
between each OS release.

Regarding the reason of FILE_DELETE_ON_CLOSE fails for directory with
FileRenameInformation, I want to know which account do you execute the
code? Does it have delete child special permission on the directory?

I am not familar with the internal work of ZwSetInformationFile with
FileRenameInformation, so I tried to review the source code of ntoskrnl.exe
ZwSetInformationFile API for FileRenameInformation. I find it did not do
much magic, but pass the work to the file system driver with IoCallDriver
calling. Sorry, I am not a DDK expert, so I will not dig more into to the
File System driver level. Anyway, I will try to consult this issue with
file system team internally for explanation. I will feedback any
information here ASAP.

Thanks for your understanding.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.