Windowsのinode的情報BY_HANDLE_FILE_INFORMATIONを取得するPowerShellスクリプト

Windowsでもinode的なところを調べたかったのですが調べてみると、

stackoverflow.com

Open both files with CreateFile, call GetFileInformationByHandle for both, and compare dwVolumeSerialNumber, nFileIndexLow, nFileIndexHigh. If all three are equal they both point to the same file:

とのことで、GetFileInformationByHandle 関数で取得できる、BY_HANDLE_FILE_INFORMATION のStructureを調べれば良いようです。

GetFileInformationByHandle function | Microsoft Docs _BY_HANDLE_FILE_INFORMATION | Microsoft Docs

ここで取得した dwVolumeSerialNumber, nFileIndexLow, nFileIndexHigh を比較するということのようです。

処理の定義

すみません。PowerShellと題名に書いておきながらコードはC#です。

yomon.hatenablog.com

以下をPowerShellに貼り付けてEnterで定義ができます。

Add-Type -TypeDefinition @'
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;

namespace WindowsFileInfo
{
    public static class Kernel32Api
    {
        [StructLayout(LayoutKind.Sequential)]
        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;
        }

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

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

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CloseHandle(SafeHandle hObject);

        public static BY_HANDLE_FILE_INFORMATION GetFileInfo(string filepath)
        {
            SafeFileHandle handle = CreateFile(filepath, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Archive, IntPtr.Zero);
            BY_HANDLE_FILE_INFORMATION fileInfo = new BY_HANDLE_FILE_INFORMATION();
            GetFileInformationByHandle(handle, out fileInfo);
            CloseHandle(handle);
            return fileInfo;
        }
    }
}
'@

確認

VolumeSerialNumberFileIndexHighFileIndexLow を比較して同一のファイルであることを判断します。

PS > echo "a" > a.txt
PS > $filepath = [System.IO.Directory]::GetCurrentDirectory() + "\a.txt"
PS >  [WindowsFileInfo.Kernel32Api]::GetFileInfo($filepath)
FileAttributes     : 32
CreationTime       : System.Runtime.InteropServices.ComTypes.FILETIME
LastAccessTime     : System.Runtime.InteropServices.ComTypes.FILETIME
LastWriteTime      : System.Runtime.InteropServices.ComTypes.FILETIME
VolumeSerialNumber : 2421491731
FileSizeHigh       : 0
FileSizeLow        : 8
NumberOfLinks      : 1
FileIndexHigh      : 327680
FileIndexLow       : 901684
PS > mv a.txt b.txt
PS >  [WindowsFileInfo.Kernel32Api]::GetFileInfo($filepath)
FileAttributes     : 32
CreationTime       : System.Runtime.InteropServices.ComTypes.FILETIME
LastAccessTime     : System.Runtime.InteropServices.ComTypes.FILETIME
LastWriteTime      : System.Runtime.InteropServices.ComTypes.FILETIME
VolumeSerialNumber : 2421491731
FileSizeHigh       : 0
FileSizeLow        : 8
NumberOfLinks      : 1
FileIndexHigh      : 327680
FileIndexLow       : 901684

fsutilでも似た情報を取得可能

同僚に教えてもらいましたが、似たような情報はfsutilでも取得可能です。

PS> fsutil usn readdata .\b.txt

メジャー バージョン   : 0x3
マイナー バージョン     : 0x0
ファイルの参照番号    : 0x0000000000000000008a00000006ff4f
親ファイルの参照番号  : 0x000000000000000000c3000000021499
USN                   : 0x000000026e1def60
タイム スタンプ       : 0x0000000000000000 0:00:00 1601/01/01
理由                  : 0x0
ソース情報            : 0x0
セキュリティ ID       : 0x0
ファイル属性          : 0x20
ファイル名の長さ      : 0xa
ファイル名オフセット  : 0x4c
ファイル名            : b.txt