dokan-dev / dokan-dotnet

Dokan DotNet Wrapper
http://dokan-dev.github.io
MIT License
462 stars 116 forks source link

Mirror sample bug about Info.IsDirectory #274

Open blackeyed7 opened 3 years ago

blackeyed7 commented 3 years ago

in CreateFile method info.IsDirectory not working (not sure if its 100% fail) I see the C mirror code has a check there, so I added check code on mine too

        var filePath = GetPath(fileName);
        FileAttributes attr = File.GetAttributes(filePath);
        if ((attr & FileAttributes.Directory) == FileAttributes.Directory) info.IsDirectory = true;
Liryna commented 3 years ago

Yes, you do need to set this field if the directory exist. This is expected and documented.

Could you explain a little more where you added this code ?

blackeyed7 commented 3 years ago

I've read more code in C# mirror, and I thought its should not be fixed in this way, but I found its more complex than I thought, I think I need more help for this

the key code in C mirror is this (line 273) if (fileAttr != INVALID_FILE_ATTRIBUTES && fileAttr & FILE_ATTRIBUTE_DIRECTORY) { if (CreateOptions & FILE_NON_DIRECTORY_FILE) { DbgPrint(L"\tCannot open a dir as a file\n"); return STATUS_FILE_IS_A_DIRECTORY;

it seems that user software tring to open a directory as a file, so then here it tells that this FILE IS A DIRECTORY, then everything's fine.

But there is no STATUS_FILE_IS_A_DIRECTORY in Dokan.NET I tried to add same long code, and edit the code in CreateFile

            switch (mode)
            {
                case FileMode.Open:

                    if (pathExists)
                    {
                        // check if driver only wants to read attributes, security info, or open directory
                        if (readWriteAttributes || pathIsDirectory)
                        {
                            if (pathIsDirectory && (access & FileAccess.Delete) == FileAccess.Delete
                                && (access & FileAccess.Synchronize) != FileAccess.Synchronize)
                                //It is a DeleteFile request on a directory
                                return Trace(nameof(CreateFile), fileName, info, access, share, mode, options,
                                    attributes, DokanResult.AccessDenied);

                            info.IsDirectory = pathIsDirectory;
                            info.Context = new object();
                            // must set it to something if you return DokanError.Success

                            return Trace(nameof(CreateFile), fileName, info, access, share, mode, options,
                                attributes, DokanResult.FILE_IS_A_DIRECTORY);
                        }
                    }

then I no longer able to open the any directory in my mounted driver, it says "denied"

blackeyed7 commented 3 years ago

Yes, you do need to set this field if the directory exist. This is expected and documented.

Could you explain a little more where you added this code ?

I was adding that code at the very begining of the CreateFile method, it fixed the problem that my user software met when using mirror sample for Dokan.NET

but I am not able to use this fix code in my own code, it is a little complex the fix code FileAttributes attr = File.GetAttributes(filePath); this will throw exception reading "destkop.ini", even I use MountManger, it still happens (not suppose to happen with MountManger?) and if I catch it, still the user software recognize the directory as a file (so my pre problem still there)

blackeyed7 commented 3 years ago

found a workaround, not sure if any side effect so far

            switch (mode)
            {
                case FileMode.Open:

                    if (pathExists)
                    {
                        // check if driver only wants to read attributes, security info, or open directory
                        if (readWriteAttributes || pathIsDirectory)
                        {
                            if (pathIsDirectory && (access & FileAccess.Delete) == FileAccess.Delete
                                && (access & FileAccess.Synchronize) != FileAccess.Synchronize)
                                //It is a DeleteFile request on a directory
                                return Trace(nameof(CreateFile), fileName, info, access, share, mode, options,
                                    attributes, DokanResult.AccessDenied);

                            info.IsDirectory = pathIsDirectory;
                            info.Context = new object();
                            // must set it to something if you return DokanError.Success

                            if (pathIsDirectory)
                            {
                                if (access == FileAccess.GenericRead)
                                    return Trace(nameof(CreateFile), fileName, info, access, share, mode, options, attributes,
                                        (NtStatus)0xC00000BAL);
                            }

                            return Trace(nameof(CreateFile), fileName, info, access, share, mode, options,
                                attributes, DokanResult.Success);
                        }
                    }
Liryna commented 3 years ago

To properly handle this issue we need to support FILE_NON_DIRECTORY_FILE which is not part of the official FileOptions enum. So we would need to extend the current enum and the others present in NtCreateFile.

Also add STATUS_FILE_IS_A_DIRECTORY to the NtStatus: https://github.com/dokan-dev/dokan-dotnet/blob/master/DokanNet/NtStatus.cs


if (pathIsDirectory)
{
  if (access == FileAccess.GenericRead) <=== createOptions & FileOptions.NonDirectoryFile
    return Trace(nameof(CreateFile), fileName, info, access, share, mode, options, attributes,
                                        (NtStatus)0xC00000BAL);
}

GenericRead is too wild and will create many issues.

fiddyschmitt commented 2 years ago

Thanks @blackeyed7,

I was experiencing the same issue when scanning my virtual drive with log4jscanner.

If I ran: log4jscanner.exe N:\

it would instantly return:

2022/01/20 11:37:59 log4jscanner.go:120: Error: scanning N:\: readdir N:\/.: The system cannot find the path specified.

So to scanner seems to enumerate the drive in an unusual way. Also, the scan would run fine on a virtual drive provided through mirror.exe. Those two facts suggested an issue with dokan-dotnet which led me to your post. Thanks!

Thanks @Liryna, I added that block of code to CreateFile() and now log4jscanner processes the drive fine.