NTFS代替データストリーム(ADS)一覧取得

NTFSに設定されている代替データストリーム(ADS)の一覧を表示したい。
いろいろ調べた結果 NtQueryInformationFile を使うのがよさげらしい。
dsofile.dll とかもあったんだけど、欲しい情報が取れなかったんですべてを取れるわけじゃないっぽい。


ネタ元
http://www.flexhex.com/docs/articles/alternate-streams.phtml
http://www.flexhex.com/docs/articles/download/streamtools.zip
http://www.nslabs.jp/ntfs_alt_stream.rhtml
他多数


んで。
悩んだのが、C#での扱い方。unsafeでやってもいいんだけどしなくてもほとんどの事ができるんだよねってことで。

private const uint STATUS_BUFFER_OVERFLOW = 0x80000005;

public void WriteList( string path ) {
  IntPtr infoPtr = GetInfoPtr( path );

  FileStreamInformation fileStreamInformation;
  do {
    fileStreamInformation = (FileStreamInformation)Marshal.PtrToStructure( infoPtr, typeof( FileStreamInformation ) );

    IntPtr strPointer = new IntPtr( infoPtr.ToInt64() + Marshal.SizeOf( typeof( FileStreamInformation ) ) );
    string streamName = GetStreamName(infoPtr);
    Console.WriteLine( "{0}\t{1}", streamName, fileStreamInformation.StreamSize );

    infoPtr = new IntPtr( (uint)infoPtr.ToInt64() + fileStreamInformation.NextEntryOffset );
  } while ( fileStreamInformation.NextEntryOffset != 0 );
}

private static string GetStreamName(IntPtr infoPtr) {
  IntPtr strPointer = new IntPtr( infoPtr.ToInt64() + Marshal.SizeOf( typeof( FileStreamInformation ) ) );
  return Marshal.PtrToStringUni( strPointer );
}

private static IntPtr GetInfoPtr(string path) {
  NtStatus status;
  IntPtr buffPtr;
  uint buffSize = 16 * 1024;

  do {
    IoStatusBlock ioStatusBlock;
    byte[] buff = new byte[buffSize];

    buffPtr = Marshal.UnsafeAddrOfPinnedArrayElement( buff, 0 );

    status = NtQueryInformationFile(
      File.OpenRead( path ).SafeFileHandle
      , out ioStatusBlock
      , buffPtr
      , buffSize
      , FILE_INFORMATION_CLASS.FileStreamInformation
    );

    buffSize *= 2;
  } while ( status.ErrorCode == STATUS_BUFFER_OVERFLOW );
  return buffPtr;
}


以下アンマネージ定義の移植部分。


FileStreamInformation (FILE_STREAM_INFORMATION)

[StructLayoutAttribute( LayoutKind.Sequential, CharSet = CharSet.Unicode )]
struct FileStreamInformation {
  public uint NextEntryOffset;
  public uint StreamNameLength;
  public ulong StreamSize;
  public ulong StreamAllocationSize;
  //[MarshalAsAttribute( UnmanagedType.ByValTStr, SizeConst = 1 )]
  //public string StreamName;
}

実際の定義としては StreamName がいるんだけど、うまく文字が取れなかった。(文字化けする)
とり方あるのかもしれないんだけどわかんなかったんで、こいつは後でポインタもとめてから取得してる。
それが↑の GetStreamName ってとこ。

なんかstringのポインタがずれてるような気がするんだけどなー。


IoStatusBlock(IO_STATUS_BLOCK)

struct IoStatusBlock {
  uint status;
  ulong information;
}

こいつは実際は使ってないけど。


NtQueryInformationFile

[DllImport( "ntdll.dll", SetLastError = true )]
static extern NtStatus NtQueryInformationFile(
  [In]
  SafeFileHandle fileHandle,
  [Out]
  out IoStatusBlock ioStatusBlock,
  [Out]
  IntPtr fileInformation,
  [In]
  uint length,
  [In]
  FILE_INFORMATION_CLASS fileInformationClass
);

こいつが本体やね。
InとかOutとかはいらないんだけど、自分がどっちだったのか忘れるんでメモ。


FILE_INFORMATION_CLASS

enum FILE_INFORMATION_CLASS {
  FileDirectoryInformation = 1,     // 1
  FileFullDirectoryInformation,     // 2
  FileBothDirectoryInformation,     // 3
  FileBasicInformation,         // 4
  FileStandardInformation,      // 5
  FileInternalInformation,      // 6
  FileEaInformation,            // 7
  FileAccessInformation,        // 8
  FileNameInformation,          // 9
  FileRenameInformation,        // 10
  FileLinkInformation,          // 11
  FileNamesInformation,         // 12
  FileDispositionInformation,   // 13
  FilePositionInformation,      // 14
  FileFullEaInformation,        // 15
  FileModeInformation,          // 16
  FileAlignmentInformation,     // 17
  FileAllInformation,           // 18
  FileAllocationInformation,    // 19
  FileEndOfFileInformation,     // 20
  FileAlternateNameInformation, // 21
  FileStreamInformation,        // 22
  FilePipeInformation,          // 23
  FilePipeLocalInformation,     // 24
  FilePipeRemoteInformation,    // 25
  FileMailslotQueryInformation, // 26
  FileMailslotSetInformation,   // 27
  FileCompressionInformation,   // 28
  FileObjectIdInformation,      // 29
  FileCompletionInformation,    // 30
  FileMoveClusterInformation,   // 31
  FileQuotaInformation,         // 32
  FileReparsePointInformation,  // 33
  FileNetworkOpenInformation,   // 34
  FileAttributeTagInformation,  // 35
  FileTrackingInformation,      // 36
  FileIdBothDirectoryInformation,   // 37
  FileIdFullDirectoryInformation,   // 38
  FileValidDataLengthInformation,   // 39
  FileShortNameInformation,       // 40
  FileHardLinkInformation = 46    // 46    
}

22しかつかわん(笑)


NtStatus 構造体

/// <summary>
/// ステータスを文字列表示するためのラッパー。
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct NtStatus {
  [DllImport( "Kernel32.dll", SetLastError = true )]
  static extern uint FormatMessage( FormatMessageValue dwFlags, IntPtr lpSource,
     uint dwMessageId, int dwLanguageId, ref IntPtr lpBuffer,
     uint nSize, IntPtr pArguments);

  [Flags]
  enum FormatMessageValue {
    AllocateBuffer =0x100,
    IgnoreInserts =0x200,
    FromString =0x400,
    FromHmodule =0x800,
    FromSystem =0x1000,
    ArgumentArray =0x2000,
    MaxWidthMask =0xFF,
  }

  [DllImport( "kernel32", SetLastError = true )]
  static extern IntPtr LoadLibrary( string lpFileName );

  [FieldOffset(0)]
  private readonly uint _errorCode;
  public uint ErrorCode {
    get { return _errorCode; }
  }

  public NtStatus( uint errorCode ) {
    _errorCode = errorCode;
  }

  public override string ToString() {
    IntPtr lpMsgBuf = IntPtr.Zero;

    FormatMessage(
      FormatMessageValue.AllocateBuffer | FormatMessageValue.FromSystem | FormatMessageValue.FromHmodule
      , LoadLibrary( "NTDLL.DLL" )
      , _errorCode
      , Thread.CurrentThread.CurrentCulture.LCID
      , ref lpMsgBuf
      , 0
      , IntPtr.Zero
    );

    return string.Format( "ErrorCode:0x{0:X}\n{1}", _errorCode, Marshal.PtrToStringAnsi( lpMsgBuf ) );
  }
}

コメントのとおり。
何がしたいってコードじゃわからんから文字表示したかっただけ。
でもなんか定義自体がなかったりするのも多い。"\0 みたいな3文字って定義もあったり・・・。
ヌルを無理やり変換させたのかと思ったら普通の文字列だった。


で。結局オレは何がしたかったのかといえば
Zone.Identifier
ってのを取得したかっただけなんだが。


それにしてもなんでこんな(デフォルトで)アクセス(表示)しにくい領域つくるんだろうな。
ファイルサイズにしてもそうだし。ウイルスの温床だし。
あるのはかまわないが情報開示はきっちりして欲しいな。
プロパティで全部表示できるとか。