Search code examples
javawinapifilesystemsjna

Java JNA OpenFileById fails with error code 87 InvalidParameter


I'm trying to get file HANDLE by NTFS file id.

  1. Having file HANLDE I'm getting FILE_ID_INFO
Memory p = new Memory(128);
            Kernel32.INSTANCE.GetFileInformationByHandleEx(
    hFile, 
    WinBase.FileIdInfo,
    p,
    new DWORD(p.size())
);
FILE_ID_INFO fii = new FILE_ID_INFO(p);

From FILE_ID_INFO I can get VolumeSerialNumber and FileId.Identifier

  1. Now when I have that - I want to open HANDLE using VolumeSerialNumber and FileId.Identifier
  2. First using Kernel32Util.getLogicalDriveStrings() and Kernel32.INSTANCE.GetVolumeInformation I can find VolumeId by VolumeSerialNumber - which in my case would be D:\
  3. Now I open HANDLE for D:\ using Kernel32.INSTANCE.CreateFile
Kernel32.INSTANCE.CreateFile(
    path,
    FILE_READ_ATTRIBUTES,
    1 | 2 | 4,
    null,
    OPEN_EXISTING,
    FILE_FLAG_BACKUP_SEMANTICS,
    null
)
  1. And last step - using OpenFileById I'm trying open file HANDLE by its id. Method definition
HANDLE OpenFileById(
    HANDLE hVolumeHint,
    Structure lpFileId,
    int dwDesiredAccess,
    int dwShareMode,
    SECURITY_ATTRIBUTES lpSecurityAttributes,
    int dwFlagsAndAttributes
);

Required structure

@FieldOrder({"dwSize", "Type", "Id"})
public static class FILE_ID_DESCRIPTOR extends Structure {

    public UINT dwSize;
    public int Type;
    public DUMMYUNIONNAME Id;

    @FieldOrder({"FileId", "ObjectId", "ExtendedFileId"})
    public static class DUMMYUNIONNAME extends Union {

        public long FileId;
        public GUID ObjectId;
        public FILE_ID_128 ExtendedFileId;

        public DUMMYUNIONNAME() {
             super();
        }

        public DUMMYUNIONNAME(long Id) {
            this.FileId = Id;
            write();
        }
    }

    public static UINT sizeOf() {
        return new UINT(Native.getNativeSize(FILE_ID_DESCRIPTOR.class, null));
    }

    public FILE_ID_DESCRIPTOR() {
        super();
    }

    public FILE_ID_DESCRIPTOR(Pointer memory) {
        super(memory);
        read();
    }

    public FILE_ID_DESCRIPTOR(UINT dwSize, int type,
            DUMMYUNIONNAME id) {
        this.dwSize = dwSize;
        this.Type = type;
        this.Id = id;
        write();
    }
}

Usage

FILE_ID_DESCRIPTOR fileIdDesc = new FILE_ID_DESCRIPTOR(FILE_ID_DESCRIPTOR.sizeOf(), 0, new DUMMYUNIONNAME(fii.FileId.Identifier));
HANDLE handle = Kernel32.INSTANCE.OpenFileById(hVolume, fileIdDesc, FILE_READ_ATTRIBUTES, 1 | 2 | 4, null, FILE_FLAG_BACKUP_SEMANTICS);

When I after executing Kernel32.INSTANCE.GetLastError(); It gives me - 87 Invalid Parameter.

I've change FILE_ID_DESCRIPTOR to accept long instead of byte[], and other changes - but everytime - same error.

Similar code works successfully in C#, but I need it to run in Java as well.

Please advise.


Solution

  • You've incorrectly initialized your FILE_ID_DESCRIPTOR values:

    new FILE_ID_DESCRIPTOR(
       FILE_ID_DESCRIPTOR.sizeOf(),
       0,
       new DUMMYUNIONNAME(fii.FileId.Identifier)
    );
    

    You're passing the Type parameter as 0, indicating you want the long FileId from the union.

    But you have hardcoded your dummy union constructor to assign the value to the 8-byte long member when the value of fii.FileId.Identifier is a 16-byte array.

    You've indicated in the comments that:

    this what you see in the post - is the last version I was trying. The DUMMYUNIONNAME was Structure, Pointer, there were NO DUMMYUNIONNAME, but just Id, etc. I was trying from 4 - to 512 bits - error code never changed.

    There is exactly one right answer and it's 16 bytes (128 bits). However, if you pass a Type of 0, it's only reading the first 8 bytes (64 bits) of your fii.FileId.Identifier.

    Most likely you want to set/read the ExtendedFileId field in the union, by setting a Type of 2.

    There are several other issues with your code that are not the cause of the error:

    1. You are over-allocating memory when creating the FILE_ID_INFO by giving 128 bytes to p. You haven't shown your mapping for it, but it's an 8-byte long followed by a structure wrapping a 16-byte array so you only need 24 bytes.
    2. You don't need to implement your own sizeOf(). JNA's Structure has a size() method that does exactly what you're asking. Or you can just use your knowledge that in this case the size is always 24.
    3. The dwSize field of FILE_ID_DESCRIPTOR is a DWORD not a UINT. Since these are both 32-bit values it doesn't make a difference. Since Java does not have a concept of signed integers you may as well just simplify and pass an int here.
    4. If you want to actually leverage JNA's Union class you should fully implement it including overriding the read() in the union to read the appropriate native value. In your case, you're only writing it for use once, so you can just skip the complexity of the union and directly assign the internal field you want. If that's the largest size value (the FILE_ID_128) you can just assign it directly. If it's a smaller size value (the long then you could just add padding to make sure the entire structure size is 24 bytes).