LockCheck icon indicating copy to clipboard operation
LockCheck copied to clipboard

Windows NtDll.cs requires `FILE_FLAG_BACKUP_SEMANTICS` for directories

Open domsleee opened this issue 2 years ago • 2 comments

Current behaviour

GetLockingProcessInfos with LockManagerFeatures.UseLowLevelApi does not work if the path is a directory on windows. The simplest example is getting the locks of a directory that is the working directory of a cmd/powershell window.

Environment info

Windows 10 x64 on .NET 7

Possible solution

I've found this line from the documentation: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea

Flag Meaning
FILE_FLAG_BACKUP_SEMANTICS
0x02000000
You must set this flag to obtain a handle to a directory. A directory handle can be passed to some functions instead of a file handle. For more information, see the Remarks section.

And then later in the same docs:

To open a directory using CreateFile, specify the FILE_FLAG_BACKUP_SEMANTICS flag as part of dwFlagsAndAttributes. Appropriate security checks still apply when this flag is used without SE_BACKUP_NAME and SE_RESTORE_NAME privileges.

So I think we should be using this flag if the path is a directory 👍

Other notes

I would be interested to know how this compares to the approach mentioned by @KirillOsenkov in #6. The only case I can think of is that it would also be able to find locks on children of the directory, whereas this approach would not. Finding the lock of a child of a directory seems to be outside the scope of this library though.

Additionally, Restart Manager seems to not support finding locks on directories. Seems like another problem, but I thought I would mention it here.

I am interested in fixing it here because I'm currently using my fork of LockCheck in https://github.com/domsleee/ForceOps. For directories, I am currently just using NtDll (with UseLowLevelApi).

domsleee avatar Jul 05 '23 08:07 domsleee

I added a snippet into my fork, something along these lines:

        public static IEnumerable<ProcessInformation> GetProcessesWithCurrentDirectory(string directory)
        {
            if (string.IsNullOrWhiteSpace(directory))
            {
                return Array.Empty<ProcessInformation>();
            }

            var list = new List<ProcessInformation>();

            var processes = ProcessInformation.GetProcesses();
            foreach (var process in processes)
            {
                if (process.CurrentDirectory is string currentDirectory)
                {
                    if (currentDirectory.StartsWith(directory, StringComparison.OrdinalIgnoreCase))
                    {
                        list.Add(process);
                    }
                }
            }

            return list;
        }

KirillOsenkov avatar Jul 05 '23 18:07 KirillOsenkov

And here's a sample of getting the current directory for any given process: https://github.com/KirillOsenkov/Misc/blob/cf1b6ece2be91459bb301f65eac5b10e2f398562/GetProcessCurrentDirectory.cs#L16

KirillOsenkov avatar Jul 05 '23 19:07 KirillOsenkov