0

That's a very specific question, but I'll break it down.

This is Bug.js, it conditionally removes a directory and recreates it (effectively, creating it empty):

var fso = new ActiveXObject("Scripting.FileSystemObject");
DIROutputFull = "C:\\Test"
if(fso.FolderExists(DIROutputFull)) fso.DeleteFolder(DIROutputFull);
fso.CreateFolder(DIROutputFull);

Bug.js is run like cscript.exe Bug.js, and it usually works - unless C:\Test pre-exists and I have a Windows Explorer window open in C:\Test. This is kind of expected, because one might think deleting that directory might fail. By contrast, re-creating it is what fails.

So this is the sequence of events:

cscript.exe Bug.js
cscript.exe Bug.js
cscript.exe Bug.js
[...]
cscript.exe Bug.js
start C:\Test
cscript.exe Bug.js
cscript.exe Bug.js

And this is the output:

[...]

C:\>cscript.exe Bug.js
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.


C:\>start C:\Test

C:\>cscript.exe Bug.js
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.

C:\Bug.js(4, 1) Microsoft JScript runtime error: Permission denied


C:\>cscript.exe Bug.js
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.

Note permission denied in line four, not three: C:\Test is in fact deleted, but not re-created.

Why is that? Note also that this very minimal use case is just what I was able to reproduce. I run into this problem much more often than I'd like, and I am using a script that I am willing to modify as it is updated often from an external source.

Some information on my environment:

C:\>ver

Microsoft Windows [Version 10.0.16299.309]

C:\>cscript.exe /?
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.

What else would be relevant?

bers
  • 4,817
  • 2
  • 40
  • 59
  • In Windows, deleting a directory or file sets its "delete disposition", but the file is only unlinked once the last reference is closed. An existing reference with delete access can even unset the delete disposition to prevent unlinking the file or directory. – Eryk Sun Apr 10 '18 at 19:47
  • When a directory is open to read changes, it's usually opened with delete sharing, in which case deleting the directory (i.e. open with delete access and set the delete disposition) will succeed. Once the directory's delete disposition is set, a wait for an `NtNotifyChangeDirectoryFile[Ex]` request will resume immediately with the status `STATUS_DELETE_PENDING`. The directory won't be unlinked until the application (e.g. Explorer) closes the handle. I suggest you retry creating the directory in a loop for up to 100 ms. I know it's an ugly workaround for this race condition. – Eryk Sun Apr 10 '18 at 20:12
  • Thank you both for your comments. You made me find https://blogs.msdn.microsoft.com/oldnewthing/20040607-00/?p=38993 and https://stackoverflow.com/questions/3764072/c-win32-how-to-wait-for-a-pending-delete-to-complete – bers Apr 11 '18 at 09:01
  • The full answer to this question is "NtCreateFile returns STATUS_DELETE_PENDING. [...] But I/O manager translates the error to ERROR_ACCESS_DENIED before return CreateFile." (https://stackoverflow.com/questions/3764072/c-win32-how-to-wait-for-a-pending-delete-to-complete#comment4765231_3776438) – bers Apr 11 '18 at 12:32
  • FYI, `NtCreateFile` and `NtOpenFile` are system functions that are commonly called from user mode. For example, `CreateFile` calls `NtCreateFile` and `DeleteFile` calls `NtOpenFile`. They're implemented in kernel mode, but the result status is returned back to the user-mode caller. Translating `NTSTATUS` codes to WinAPI error codes has nothing to do with the I/O manager in the kernel, which was designed to service multiple subsystems. The Windows subsystem does this translation in user mode, which often loses important information for compatibility with legacy error codes. – Eryk Sun Apr 11 '18 at 21:04
  • And the reason for the problem here has nothing to do with the status/error code. Whatever it is, the problem is the same; some other thread or process has an open File object reference for the directory, so it won't be unlinked until that File is cleaned up (`IRP_MJ_CLEANUP`). In so doing, the filesystem will honor the delete disposition that was set on the underlying file/stream control block (FCB/SCB) to unlink the file from the directory and physically delete it (unless it has other hardlinks, but NTFS doesn't allow hardlinking directories). – Eryk Sun Apr 11 '18 at 21:12

0 Answers0