32

Windows (2000 以降を想定) では、ファイル パスの長さは最大で約 32767 文字です。この制限UNICODE_STRINGは、ネイティブ API (カーネル側、ドライバーなど) での内部処理のために存在します。ここまでは順調ですね。私はその部分の背後にある理論を知っています。

制限の理由は、LengthおよびMaximumLengthのメンバーがUNICODE_STRINGのバイト数をカウントするがBuffer、それ自体が 16 ビットの符号なし整数であるためです。

また、制限が設定された制限ではなく概算である理由も知っています。これは主に、ファイル名 (例: \\.\C:\boot.ini) がネイティブ形式 (例: ) に解決され、実際のボリューム デバイス名\??\C:\boot.iniが前に付けられ、その後にそのボリュームへの相対パス (例: .\Device\HarddiskVolume2\boot.ini

さらに、Windows エクスプローラーから (「ANSI」)MAX_PATH制限に達したときの既知の症状は、ファイルまたはフォルダーが Windows の一部のバージョンに存在しないふりをすることです (これはある時点で修正された可能性があります)。

しかし、オブジェクト マネージャー、I/O マネージャー、およびファイル システム ドライバーの各レベルCreateFile()で、次のようなパスで呼び出すと、パス\\.\C:\...\filename.ext全体が制限を超えていないが、制限に達したときに、kernel32.dll呼び出しCreateFile()でそれぞれ何が起こるかを取得します。拡大?...

SDK も WDK も、このトピックについて特におしゃべりしているようには見えません。それとも、間違ったセクションを見ましたか?

4

1 に答える 1

39

私は怠け者なので、テスト プログラムは作成しませんでしたが、長いパス ( よりも長い) や特殊なファイル名 (など) を問題なく処理する優れたFar Managerを使用してテストしました。MAX_PATHconprn

ちょうど 255 文字の文字列 ("12345678901234...012345") を作成し、ネストされたディレクトリの作成を開始しました。幸いなことに、Far の「ディレクトリの作成」機能は、スラッシュで区切られた文字列を使用して「ネストされたディレクトリを作成する」ことを意味するため、内部エディターで文字列をコピー & ペーストして準備することで、ほんの数ステップでそれを行うことができました。

私が作成できた最長のパスは、「C:\」から数えて32739文字でした (つまり、Far によって追加された「\\?\」は含まれません)。ディレクトリまたはファイルを 1 文字だけ追加して作成しようとすると、「ファイル名または拡張子が長すぎます」というエラーが表示されます。そのディレクトリに入ろうとすると、同じエラーが発生します。

編集: デバッガーでしばらく過ごしました。Win32 API レベルで何が起こるかを次に示します。

  1. 制限を 1 文字超えるファイルを作成しようとしています
  2. CreateFileW文字列 "\\?\C:\123[...]012345" を使用したfar 呼び出しは、 32744ワイド文字の長さです (終端のゼロは数えません)。
  3. CreateFileWいくつかの追加チェックを行い、null で終わる文字列をUNICODE_STRING(Length=65488, MaximumLength=65490)に変換し、OBJECT_ATTRIBUTES構造体を準備します。
  4. CreateFileW次に、命令の単なるラッパーであるNtCreateFileinを呼び出します。ntdll.dllsyscall
  5. NtCreateFile0xC0000106 ( ) を返しますSTATUS_NAME_TOO_LONG
  6. 次に、そのステータス値が ( を使用してRtlNtStatusToDosError) Win32 エラー 206 ( ERROR_FILENAME_EXCED_RANGE) に変換されます。

カーネル内で何が起こっているかは調べませんでしたが、それも調べることができたと思います

EDIT2 : WinObj を実行たところ、システムC:に へのシンボリック リンクがあることがわかりました\Device\HarddiskVolume1。この文字列の長さは23 文字です。\C:に渡された文字列の を置き換えるNtCreateFileと、32744 - 3 + 23 = 32764文字になります。終端のゼロと合わせて、これには 65530 バイトが必要です。まだ制限 (0xFFFF=65535) に達していないので、セッション名や名前空間名など、何か余分なものが追加されていると思います。

EDIT3:カーネルを通過した後:

  1. NtCreateFile通話IopCreateFile
  2. IopCreateFile通話ObOpenObjectByName
  3. ObOpenObjectByName通話ObpLookupObjectName
  4. ObpLookupObjectNameObpDosDevicesShortNamePrefix( )のチェック"\??\"-> 成功
  5. "C:"プレフィックスをスキップし、残りの部分をとに分割します"\1234..."
  6. "C:"への呼び出しで解決しますObpLookupDirectoryEntry
  7. 次に、検索したディレクトリ エントリ ( ==と== 3 を使用)と名前の残りの部分をObpParseSymbolicLink渡します。_OBJECT_SYMBOLIC_LINKLinkTarget"\Device\HarddiskVolume1"DosDeviceDriveIndex
  8. 次に、次のようなことを行います(ReactOS によって忠実に再現されています):

    TargetPath = &SymlinkObject->LinkTarget;
    TempLength = TargetPath->Length;
    TotalLength = TempLength + RemainingName->Length;
    if (LengthUsed > 0xFFF0)
        return STATUS_NAME_TOO_LONG;
    

    私たちの場合、46 + 65476 = 65522 (0xfff2) で、制限をわずかに超えています。

    ということで、謎が解けました(願わくば!)。

PS すべて Windows 7 x64 SP1 でテスト済み。

于 2013-03-12T22:49:10.237 に答える