从OpenFileById获取访问冲突

时间:2017-04-30 08:36:08

标签: c# .net windows filesystems

我正在尝试仅使用唯一文件ID获取NTFS文件系统上的文件信息。 我遇到的问题是从ID生成句柄,因为我的代码导致访问冲突,我不知道原因。

要获取唯一文件ID,我使用了HERE中的代码。我很确定问题是描述符。我已经阅读了HERE(第一回答第二条评论)我可以使用长整数而不是guid,但这似乎不起作用。但我不知道如何使用我的文件ID信息创建一个guid。

这是我到目前为止的代码。

public class WinAPI
{
    [DllImport("ntdll.dll", SetLastError = true)]
    public static extern IntPtr NtQueryInformationFile(IntPtr fileHandle, ref IO_STATUS_BLOCK IoStatusBlock, IntPtr pInfoBlock, uint length, FILE_INFORMATION_CLASS fileInformation);

    public struct IO_STATUS_BLOCK
    {
        uint status;
        ulong information;
    }
    public struct _FILE_INTERNAL_INFORMATION
    {
        public ulong IndexNumber;
    }

    // Abbreviated, there are more values than shown
    public enum FILE_INFORMATION_CLASS
    {
        FileDirectoryInformation = 1,     // 1
        FileFullDirectoryInformation,     // 2
        FileBothDirectoryInformation,     // 3
        FileBasicInformation,         // 4
        FileStandardInformation,      // 5
        FileInternalInformation      // 6
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool GetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr OpenFileById(IntPtr hFile, FILE_ID_DESCRIPTOR lpFileID, uint dwDesiredAccess, uint dwShareMode, uint dwFlagas);

    [StructLayout(LayoutKind.Explicit)]
    public struct FILE_ID_DESCRIPTOR
    {
        [FieldOffset(0)] public uint dwSize;
        [FieldOffset(4)] public FILE_ID_TYPE type;
       // [FieldOffset(8)] public Guid guid;
        [FieldOffset(8)] public long FileReferenceNumber;
    }

    public enum FILE_ID_TYPE
    {
        FileIdType = 0,
        ObjectIdType = 1,
        ExtendedFileIdType = 2,
        MaximumFileIdType
    };

    public struct BY_HANDLE_FILE_INFORMATION
    {
        public uint FileAttributes;
        public FILETIME CreationTime;
        public FILETIME LastAccessTime;
        public FILETIME LastWriteTime;
        public uint VolumeSerialNumber;
        public uint FileSizeHigh;
        public uint FileSizeLow;
        public uint NumberOfLinks;
        public uint FileIndexHigh;
        public uint FileIndexLow;
    }
}

public class File_Handle
{

    public ulong Get_Index()
    {
        WinAPI.BY_HANDLE_FILE_INFORMATION objectFileInfo = new WinAPI.BY_HANDLE_FILE_INFORMATION();

        FileInfo fi = new FileInfo(@"D:\Test\Testfile.txt");
        FileStream fs = fi.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

        WinAPI.GetFileInformationByHandle(fs.Handle, out objectFileInfo);

        fs.Close();

        ulong fileIndex = ((ulong)objectFileInfo.FileIndexHigh << 32) + (ulong)objectFileInfo.FileIndexLow;

        return fileIndex;
    }

    public string Retrieve_File(ulong Index)
    {                    
        WinAPI.FILE_ID_DESCRIPTOR Descriptor = new WinAPI.FILE_ID_DESCRIPTOR { dwSize = 24, type = WinAPI.FILE_ID_TYPE.FileIdType, FileReferenceNumber = (long)Index };

        FileInfo fi = new FileInfo(@"D:\Test\TestfileRef.txt");
        FileStream fs = fi.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

        FileStream wf = new FileStream(WinAPI.OpenFileById(fs.Handle, Descriptor, 0, 0, 0x08000000), FileAccess.ReadWrite);

        WinAPI.BY_HANDLE_FILE_INFORMATION objectFileInfo = new WinAPI.BY_HANDLE_FILE_INFORMATION();
        WinAPI.GetFileInformationByHandle(wf.Handle, out objectFileInfo);
        fs.Close();
        wf.Close();

        return "Dummy";

    }
}

2 个答案:

答案 0 :(得分:0)

正如我所说,使用pinvoke中的代码。这工作

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            File_Handle handle = new File_Handle();
            handle.Get_Index();
            handle.Retrieve_File();
        }
    }
    public class WinAPI
    {

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool GetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation);

        [DllImport("kernel32.dll", BestFitMapping = false, ThrowOnUnmappableChar = true)]
        public static extern int OpenFile([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)]string lpFileName, out OFSTRUCT lpReOpenBuff,
           OpenFileStyle uStyle);

        [StructLayout(LayoutKind.Explicit)]
        public struct FILE_ID_DESCRIPTOR
        {
            [FieldOffset(0)]
            public uint dwSize;
            [FieldOffset(4)]
            public FILE_ID_TYPE type;
            // [FieldOffset(8)] public Guid guid;
            [FieldOffset(8)]
            public long FileReferenceNumber;
        }

        public enum FILE_ID_TYPE
        {
            FileIdType = 0,
            ObjectIdType = 1,
            ExtendedFileIdType = 2,
            MaximumFileIdType
        };

        public struct BY_HANDLE_FILE_INFORMATION
        {
            public uint FileAttributes;
            public FILETIME CreationTime;
            public FILETIME LastAccessTime;
            public FILETIME LastWriteTime;
            public uint VolumeSerialNumber;
            public uint FileSizeHigh;
            public uint FileSizeLow;
            public uint NumberOfLinks;
            public uint FileIndexHigh;
            public uint FileIndexLow;
        }

        [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
        public struct OFSTRUCT
        {
            public byte cBytes;
            public byte fFixedDisc;
            public UInt16 nErrCode;
            public UInt16 Reserved1;
            public UInt16 Reserved2;
            [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 128)]
            public string szPathName;
        }
        [Flags]
        public enum OpenFileStyle : uint
        {
            OF_CANCEL = 0x00000800,  // Ignored. For a dialog box with a Cancel button, use OF_PROMPT.
            OF_CREATE = 0x00001000,  // Creates a new file. If file exists, it is truncated to zero (0) length.
            OF_DELETE = 0x00000200,  // Deletes a file.
            OF_EXIST = 0x00004000,  // Opens a file and then closes it. Used to test that a file exists
            OF_PARSE = 0x00000100,  // Fills the OFSTRUCT structure, but does not do anything else.
            OF_PROMPT = 0x00002000,  // Displays a dialog box if a requested file does not exist 
            OF_READ = 0x00000000,  // Opens a file for reading only.
            OF_READWRITE = 0x00000002,  // Opens a file with read/write permissions.
            OF_REOPEN = 0x00008000,  // Opens a file by using information in the reopen buffer.

            // For MS-DOS–based file systems, opens a file with compatibility mode, allows any process on a 
            // specified computer to open the file any number of times.
            // Other efforts to open a file with other sharing modes fail. This flag is mapped to the 
            // FILE_SHARE_READ|FILE_SHARE_WRITE flags of the CreateFile function.
            OF_SHARE_COMPAT = 0x00000000,

            // Opens a file without denying read or write access to other processes.
            // On MS-DOS-based file systems, if the file has been opened in compatibility mode
            // by any other process, the function fails.
            // This flag is mapped to the FILE_SHARE_READ|FILE_SHARE_WRITE flags of the CreateFile function.
            OF_SHARE_DENY_NONE = 0x00000040,

            // Opens a file and denies read access to other processes.
            // On MS-DOS-based file systems, if the file has been opened in compatibility mode,
            // or for read access by any other process, the function fails.
            // This flag is mapped to the FILE_SHARE_WRITE flag of the CreateFile function.
            OF_SHARE_DENY_READ = 0x00000030,

            // Opens a file and denies write access to other processes.
            // On MS-DOS-based file systems, if a file has been opened in compatibility mode,
            // or for write access by any other process, the function fails.
            // This flag is mapped to the FILE_SHARE_READ flag of the CreateFile function.
            OF_SHARE_DENY_WRITE = 0x00000020,

            // Opens a file with exclusive mode, and denies both read/write access to other processes.
            // If a file has been opened in any other mode for read/write access, even by the current process,
            // the function fails.
            OF_SHARE_EXCLUSIVE = 0x00000010,

            // Verifies that the date and time of a file are the same as when it was opened previously.
            // This is useful as an extra check for read-only files.
            OF_VERIFY = 0x00000400,

            // Opens a file for write access only.
            OF_WRITE = 0x00000001
        }
    }

    public class File_Handle
    {

        public ulong Get_Index()
        {
            WinAPI.BY_HANDLE_FILE_INFORMATION objectFileInfo = new WinAPI.BY_HANDLE_FILE_INFORMATION();

            FileInfo fi = new FileInfo(@"c:\Temp\Test.txt");
            FileStream fs = fi.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

            WinAPI.GetFileInformationByHandle(fs.Handle, out objectFileInfo);

            fs.Close();

            ulong fileIndex = ((ulong)objectFileInfo.FileIndexHigh << 32) + (ulong)objectFileInfo.FileIndexLow;

            return fileIndex;
        }

        public string Retrieve_File()
        {

            WinAPI.OFSTRUCT ofStruct = new WinAPI.OFSTRUCT();
            int err = WinAPI.OpenFile(@"c:\Temp\Test.txt", out ofStruct, WinAPI.OpenFileStyle.OF_READ);

            return "Dummy";

        }
    }
}

答案 1 :(得分:0)

我知道这很晚了,但是我花了几天时间解决了这个问题:

根据OpenByFileId API,这是方法的定义:

HANDLE OpenFileById(
  HANDLE                hVolumeHint,
  LPFILE_ID_DESCRIPTOR  lpFileId,
  DWORD                 dwDesiredAccess,
  DWORD                 dwShareMode,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  DWORD                 dwFlagsAndAttributes
);

请注意lpFileId是一个LP,也就是指向FILE_ID_DESCRIPTOR结构的指针(在参数说明中也提到了)。因此,如果我们将c#方法修改为采用ref,我们将获得如下签名:

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr OpenFileById(IntPtr hFile, ref FILE_ID_DESCRIPTOR lpFileID, uint dwDesiredAccess, uint dwShareMode, uint dwFlagas);

这应该使您克服看到的访问冲突异常,但是您很可能会从此处返回无效的句柄。这是因为您的FILE_ID_DESCRIPTOR类的大小可能正确,但与FILE_ID_DESCRIPTOR signature不匹配。

我的解决方案

您可以找到一个完整的,兼容的解决方案(在c#和c ++中),它们既可以从path获取FileId,又可以从FileId获取Path:https://github.com/nolanblew/openbyfileid

注意:要使其正常工作,您必须 在要访问的驱动器上的某些文件或目录上具有句柄。此示例(以及github示例)使用程序的本地路径,因此,如果您在C:\上运行程序,但文件位于E:上,则它将失败。

这是我从FileId获取路径的代码:

public static class FileIdHelper
{
    public static string GetFilePath(long fileSystemId)
    {
        using var handle = _CreateSafeFileHandle(".");

        if (handle == null || handle.IsInvalid)
        {
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        }

        var size = Marshal.SizeOf(typeof(FILE_ID_DESCRIPTOR));
        var descriptor = new FILE_ID_DESCRIPTOR { Type = FILE_ID_TYPE.FileIdType, FileId = fileSystemId, dwSize = size };

        try
        {
            using var handle2 = _OpenFileById(handle, ref descriptor);

            if (handle2 == null || handle2.IsInvalid)
            {
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            }

            const int length = 128;
            var builder = new StringBuilder(length);
            GetFinalPathNameByHandleW(handle2, builder, length, 0);
            return builder.ToString();
        }
        catch
        {
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        }

        return null;
    }

    static SafeFileHandle _CreateSafeFileHandle(string path) =>
        CreateFileW(
            path,
            FileAccess.Read,
            FileShare.ReadWrite,
            IntPtr.Zero,
            FileMode.Open,
            (FileAttributes)0x02000000, //FILE_FLAG_BACKUP_SEMANTICS
            IntPtr.Zero);

    static SafeFileHandle _OpenFileById(SafeFileHandle hint, ref FILE_ID_DESCRIPTOR fileId) =>
    OpenFileById(
        hint,
        ref fileId,
        FileAccess.ReadWrite,
        FileShare.ReadWrite,
        IntPtr.Zero,
        (FileAttributes)0x02000000); //FILE_FLAG_BACKUP_SEMANTICS

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CreateFileW")]
    static extern SafeFileHandle CreateFileW(
        [In, MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
        [In, MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
        [In, MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
        [In] IntPtr lpSecurityAttributes,
        [In, MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
        [In, MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
        [In] IntPtr hTemplateFile);

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "OpenFileById")]
    static extern SafeFileHandle OpenFileById(
        [In] SafeFileHandle hVolumeHint,
        [In, Out] ref FILE_ID_DESCRIPTOR lpFileId,
        [In, MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
        [In, MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
        [In] IntPtr lpSecurityAttributes,
        [In, MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes
    );

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "GetFinalPathNameByHandleW")]
    static extern int GetFinalPathNameByHandleW(
        SafeFileHandle hFile,
        StringBuilder lpszFilePath,
        int cchFilePath,
        int dwFlags);
}

[StructLayout(LayoutKind.Explicit)]
public struct FILE_ID_DESCRIPTOR
{
    [FieldOffset(0)]
    public int dwSize;
    [FieldOffset(4)]
    public FILE_ID_TYPE Type;
    [FieldOffset(8)]
    public long FileId;
    [FieldOffset(8)]
    public Guid ObjectId;
    [FieldOffset(8)]
    public Guid ExtendedFileId; //Use for ReFS; need to use v3 structures or later instead of v2 as done in this sample
}

public enum FILE_ID_TYPE
{
    FileIdType = 0,
    ObjectIdType = 1,
    ExtendedFileIdType = 2,
    MaximumFileIdType
};