Unable to specify files inode
See original GitHub issueThis code https://github.com/dokan-dev/dokan-dotnet/blob/master/DokanNet/DokanOperationProxy.cs#L507 Causes the inode for two completly different files to be the same if they just have the same filename.
I’m running Python scripts that copy files around on my drive.
And python’s os.path.samefile
checks if the file’s inode and deviceID are different. Which they aren’t if the files happen to have the same name.
In my case I am trying to copy a file to a different directory. So it indeed has the same name and thus the same inode. But they are still seperate files.
My proposed solution would be to provide the ability to set the inode in the FileInformation struct but keep it optional.
What could be done is have JUST GetFileInformation
return a different FileInformation struct than everywhere else which contains the full path instead of just the filename.
In my case I’m storing the FileInformation struct directly in my file node because I don’t need to generate a new one everytime.
Instead of
public NtStatus GetFileInformation(string filename, out FileInformation fileInfo, DokanFileInfo info)
{
var node = GetNode(filename, info);
if (node == null)
{
fileInfo = new FileInformation();
return DokanResult.FileNotFound;
}
fileInfo = node.fileInfo;
return DokanResult.Success;
}
I would need to
public NtStatus GetFileInformation(string filename, out FileInformation fileInfo, DokanFileInfo info)
{
var node = GetNode(filename, info);
if (node == null)
{
fileInfo = new FileInformation();
return DokanResult.FileNotFound;
}
fileInfo = new FileInformation
{
FileName = filename,
Attributes = node.fileInfo.Attributes,
CreationTime = node.fileInfo.CreationTime,
LastAccessTime = node.fileInfo.LastAccessTime,
LastWriteTime = node.fileInfo.LastWriteTime,
Length = node.fileInfo.Length
};
return DokanResult.Success;
}
But that would make GetFileInformation
from now on always return a wrong FileName.
Which is btw what the Mirror sample is already doing.
Issue Analytics
- State:
- Created 5 years ago
- Comments:21 (11 by maintainers)
Top GitHub Comments
For reference (as I already forgot most of the stuff I was looking at back then)
Hash is calculated and used here: https://github.com/dokan-dev/dokan-dotnet/blob/fa556dfc3631eb8feed374322f4285cf17194f88/DokanNet/DokanOperationProxy.cs#L547
This https://github.com/dokan-dev/dokan-dotnet/blob/fa556dfc3631eb8feed374322f4285cf17194f88/DokanNet/DokanOperationProxy.cs#L515 should really just return a struct mirroring the
BY_HANDLE_FILE_INFORMATION
contents in a C# format, we can basically keep most things but just add 64bit(?) number for fileIndex (inode) and dwNumberOfLinksWe need to communicate a way to the users to still let them generate hash from filename, to make sure new users won’t be confused? Might aswell just mention a
//(uint)(fi.FileName?.GetHashCode() ?? 0)
in comments in the new struct.I have my own reasons for wanting to specify the ID, for FILE_OPEN_BY_FILE_ID handling. I honestly think it’d be most appropriate to address this bug by implementing that.
That said… all filenames that come to the functions pointed to in DOKAN_OPERATIONS from dokan1.sys are (currently) passed the full pathname within the filesystem. There’s no “look for current working directory and append the filename to it” that the filesystem implementation needs to do, it’s all handled by the driver. Which is good, because the driver doesn’t pass current working directory information to the implementation. I don’t think the driver can actually even get the current working directory information of the process that called it.
To understand BY_HANDLE_FILE_INFORMATION and its use of nFileIndexLow and nFileIndexHigh, it’s important to realize that those came directly from the definition in the NT header files. Those fields are expressly defined and populated to support FILE_OPEN_BY_FILE_ID. The fact that you want to specify them is awesome, and I wholeheartedly salute it – I just want it done correctly.
If FILE_OPEN_BY_FILE_ID is handled, the file ID is passed to the kernel in raw form (64 raw bits, with no base64 encoding, no padding, no NUL-termination) as FileName. Trying to interpret it as a string will either lead to incorrect behavior (since the first four bytes of a 32 bit value stuffed into a 64-bit space matches the UTF-32 representation of U+0000, thus leading to a string that’s empty) or a bluescreen (since a 64-bit number with no 0x00 octets would have no guarantee that it would be followed by U+0000 in any reasonable space, and the kernel driver would need to interpret it as a string to determine what filename to pass up to user mode) – see https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntcreatefile for more information. But, since the kernel driver would also receive the FILE_OPEN_BY_FILE_ID flag, it would be easy enough for the driver to break the value out into the nFileIndexLow and nFileIndexHigh fields, and pass an empty string in FileName. (This would be the differentiation that would allow a user to know that it’s being requested to open a file by ID instead of by name – if the string is
@""
and not even@"\"
, it’s not referring to a normal name.) [note: the kernel doesn’t allow FILE_OPEN_BY_FILE_ID unless the filesystem is mounted with the FILE_SUPPORTS_OPEN_BY_FILE_ID deviceCharacteristic. This would need to be added to theDokanOptions
enum, and its underlying handlers, to let the implementation tell dokany that it won’t panic if it receives String.Empty in CreateFile.]As an implementation note on your patch,
long? FileIndex
actually won’t work to make it nullable, because long is a value type. Because of the underlying semantics of how memory addresses are interpreted, If ‘null’ were passed there, it would simply be passed the value of 0L, with no way to differentiate whether it’s intended to be null or a literal 0L.I’d also suggest making that parameter
ulong
instead oflong
, since it’s not actually interpreted as an numeric value, and the underlyingnFileIndexLow
andnFileIndexHigh
are both defined asuint
(which would have the potential of making fully half of the nFileIndexHigh values appear to be negative numbers – as well as potentially opening up sign-extension issues).I understand that you probably derived the name
FileIndex
as a name from BY_HANDLE_FILE_INFORMATION.cs, but I’d suggest renaming it toFileID"
, since FILE_ID is what the MSDN documentation calls it. (The namesnFileIndexLow
andnFileIndexHigh
are artifacts of how NTFS implements it – it’s the index of the file into the Master File Table. However, in dokany implementations, they’re not always going to be true “indexes”. I’m working on an implementation that would be able to directly expose underlying object IDs for file streams stored in PostgreSQL, which doesn’t relate to indexing into an MFT. [As a side note, Postgres object IDs are all 32 bits long, which would also lead to me being impacted by a 0x00000000 nFileIndexHigh if it were interpreted as a string, which is why I’m concerned about correctly handling it.])Also, the name that NtCreateFile receives when asked to open by FILE_ID is of either of the forms
[8RawBytesOfFileID]
or\??\C:\[8RawBytesOfFileID]
. I don’t have a means of testing right now, but I would guess that if the Dokan filesystem is mounted on a path, it would be the path to the mountpoint prepended with\??\
. (I don’t have any idea what it would be in a UNC environment, since the IDs provided by\Device\HardDiskVolume1\[16RawBytesOfObjectID]
name syntax are 16-byte GUIDs, and UNC paths are accessed via\Device\MUP\hostname\sharename\
.)