W.I.S. Laboratory
menu-bar

NTFS


Windowsのファイル名・パス長の上限文字数についていろいろ

Windowsのファイル名・フォルダ名・パス長には以下のような上限値がある。
単位はすべてUnicodeの「文字」であり、バイトではないので全角・半角の区別はない。

OSのパス長上限:32768文字(NTFS上限と同じ)
可視範囲のOSパス長上限:32767文字(32768 - 終端文字)
エクスプローラー等のパス長上限:260文字(256 + ドライブレター + コロン + バックスラッシュ + 終端文字)
可視範囲のエクスプローラー等パス長上限:259文字(260 - 終端文字)
フォルダ名のみのパス長上限:248文字(260 - ファイル名予約12文字分)
可視範囲のフォルダ名のみパス長上限:247文字(260 - ファイル名予約12文字分 - 終端文字)
単体ファイル名の長さ上限:256文字(NTFS上限と同じ)
可視範囲の単体ファイル名長さ上限:255文字(256 - 終端文字)
単体フォルダ名の長さ上限:245文字(260 - ファイル名予約12文字分 - ドライブレター - コロン - バックスラッシュ)
可視範囲の単体フォルダ名長さ上限:244文字(260 - ファイル名予約12文字分 - ドライブレター - コロン - バックスラッシュ - 終端文字)

Windowsが採用しているファイルシステム、NTFS自体の最大パス長は32768文字(終端文字含む)だ。
にも関わらず、WindowsのエクスプローラーはMAX_PATH(defineされた定数)による制限がかけられており、最大パス長260文字までしか対応していない。
なので、フォルダ名が長い上に階層が深いところにファイルを作成しようとするとファイル名が途中で切られたり、長い名前を付けたファイルが入っているフォルダの名前を後から変更して長くすると、「対象のパスが長すぎます」というエラーが出て開けなくなってしまう。(NTFSの上限を超えていなければ名前変更はできる)
この制限の影響を受けるかどうかは、そのアプリケーションが使用するAPIによって変わる。(例えばエクスプローラーは影響を受けるが、NTFS上限の32768文字まで扱えるアプリケーションもある)
MAX_PATHは、VC++でもMinGWでも stdlib.h 内に記述されている。
超軽量Cコンパイラ、Tiny C Compilerにも記述が見つかった。

VC++のMAX_PATHの値

VisualCのMAX_PATHの値

MinGWのMAX_PATHの値

MinGWのMAX_PATHの値

Tiny C CompilerのMAX_PATHの値

Tiny C CompilerのMAX_PATHの値

正しくは「_MAX_PATH」と前にアンダースコアが付く。
アンダースコアから始まり次が大文字なので予約済み識別子だ。
VC++のコメントに「_makepath()関数と_splitpath()関数で使われるバッファサイズだよ」とあるので、これらの関数を呼び出しているアプリケーションは_MAX_PATHの制限を受けると考えて良いのだろう。
「サイズには終端文字のスペースが含まれてることに注意してね」ともコメントがあるので、可視範囲でいえば259文字となる。
260文字というのはあくまでもパス長の制限であり、1ファイルあたりのファイル名はNTFSの上限で最長256文字までとなっている。(上の「_MAX_FNAME」の値。終端文字をカウントしなければ255文字)

_makepath() 関数は、


のようにして使うと、アドレスfullPathに各引数が結合されたフルパス "C:\dirname\seconddirname\longfilename.txt" が入る。(コロンやピリオドなど不足している区切り文字は自動挿入される)
_MAX_PATH値を超えてまでバッファオーバーランしないようなので、予め


などとしてメモリ確保しておく必要がある。(スタック上でもヒープ上でも良い)

そして _splitpath() 関数は、


のようにして使うと、フルパスを分割して、drive, dir, fname, ext 各々のアドレスに、"C:", "\dirname\seconddirname\", "longfilename", ".txt" が入る。
_makepath()関数と同じくMAX_xxxx各々の値を超えてバッファオーバーランしないようなので、これも予め


などとしておけば良いわけだ。
ちなみにワイド文字列用の _wmakepath() 関数と _wsplitpath() 関数もあるが、引数のポインタがwchar_t型になるだけで使い方は同じだ。
メモリをwchar_tのサイズに合わせて確保すれば良い。


_splitpath()関数が受け入れられる上限パス長は、_MAX_DRIVE + (_MAX_DIR, _MAX_FNAME, _MAX_EXT のうち最大のもの) + 終端文字ということになるので、3 + 256 + 1 = 260文字、つまり_MAX_PATHの値と同じになる。(単純計算で 3 + 256 + 256 + 256 = 771文字 というわけにはいかないので)
この_splitpath()関数が受け入れられる上限パス長が、_MAX_PATH値の根拠になっている感じがする。

_splitpath()関数が使用する各バッファ容量を再度見てみる。
Windowsのドライブレターは「1文字 + コロン」なので、MAX_DRIVEの3文字制限は問題ない。
またNTFSのファイル名上限文字数は拡張子込みで256文字なので、拡張子無しや拡張子のみのファイル名が送られてくることを考慮しても、_MAX_FNAMEと_MAX_EXTがそれぞれ256文字分確保されているためこれも問題ない。
_MAX_DIRの256文字、ここが問題だ。
_MAX_DIRは「最長ディレクトリ名」ではなく、「フルパス内のディレクトリ指定部分の最長文字数」のことだ。
つまりここ→ C:\exsamplelongfoldername\exsamplelonglongfoldername\exsamplelongfilename.txt
ここが256文字ではNTFSの最大パス長32768文字に届くはずもないのだが、_splitpath()関数用のバッファは256文字分しか確保されていない。
そのため_makepath()関数が生成できる最長パスを_splitpath()関数の受け入れ可能パス上限長に合わせる必要が出てきて_MAX_PATHを260文字分に制限することになった・・と考えると辻褄は合う。
_MAX_DIR値が256になった理由はマイクロソフトにしか分からないが、最長パス260文字制限の根源はこの辺にありそうだ。

現在のNTFSは文字コードをUTF-16で格納するが、NTFSが設計された当初はまだUTF-16は存在しておらず、全文字16bit固定長のUnicodeしか無い時代だったので、上限パス長260文字=520バイトがWindowsNT設計当初の最長パスサイズということになる。
後に出てきたUTF-16のサロゲートペア文字が1文字とカウントされるのか、2文字とカウントされてしまうのかまでは分からないが、上の _wmakepath() 関数(ワイド文字版)が扱うポインタがwchar_t*型であることを考えると2文字とカウントされなければ辻褄は合わないので、サロゲートペア文字のみを連続したパスを作成すると130文字付近で頭打ちになるのかもしれない。

ちなみにファイル名が含まれない「C:\foldername\secondfoldername\」といったようなフォルダ名のみの最長パスは、_MAX_PATHから12を差し引いた248文字になる。(終端文字をカウントしなければ247文字)
なぜ_MAX_PATHの値よりも短いのかというと、フォルダ名だけで260文字使い切ってしまっては、その中に入れるファイルに名前が付けられなくなってしまうからだ。
「フルパス」というのは、「ドライブレター + : + \ + フォルダ名 + ・・・ + \ + ファイル名 + . + 拡張子」なので、最後のファイル名と拡張子分を予約しておかなければならない。
何故その予約分が12文字分なのかといえば、MS-DOSの「8.3形式」の名残なのだろうと思う。
ファイル名8文字 + ピリオド1文字 + 拡張子3文字で12文字分となる。
では単体のフォルダに付けられる最長の文字数はというと、そこからさらに3ほど少ない245文字だ。(終端文字をカウントしなければ244文字)
フォルダ名のみの最長パスから、ドライブレター + コロン + バックスラッシュの3文字分を差し引いた値となる。

これらパス長の制限は1993年に発売されたWindows NT 3.1からWindows 10に至るまでずっと続いている。
Windows10は1607以降この制限を解除できるようになったが、デフォルトでは相変わらず制限されているようだ。
3万文字を超えるパスが扱えるファイルシステムなのにも関わらず、何故それより遥かに短い260文字で制限がかけられたのか真相は分からないが、MS-DOSの最長パスである260バイト(ドライブ指定3バイトと終端1バイトを差し引くと256バイト)との互換性を考慮したためか、メモリ容量もストレージ容量も今よりずっと少なかった時代にできるだけ無駄を省くため、とりあえずファイル名上限文字数にドライブレターと区切り文字を追加した値にしたためか、そのあたりではないかと思う。
当時マイクロソフトが使っていたCコンパイラの stdlib.h 内にMS-DOS時代の記述が残っていて、マイクロソフトがそれを書き換えるのをうっかり忘れたままコンパイルして出荷され、互換性のために後から変更するわけにいかず現代まで・・考えすぎだ。

260文字は短すぎるなどとよく言われるようだが、Linuxの上限パス長は1023バイト(システムコールに渡す時の上限値)だ。
ASCII文字で考えるとかなりの差があるが、日本語のみで構成されたパスで考えれば倍も違わない。
Linuxの多くはファイルシステムの文字コードにUTF-8を採用しているので、1文字あたり3バイト消費するため、1023バイト=341文字分となる。
「いやいや80文字はでけえよ!」という人もいるだろうが、普段日本語でファイル名やフォルダ名を付けているのであれば、大騒ぎするほど変わらないのではないだろうか。
むしろLinuxはファイル名上限値が255バイトなので、日本語で85文字までしか使えないほうが問題あるような気がする。

Windowsに話を戻す。
ファイル名やフォルダ名に付けられる名前の上限文字数は終端文字込みで256文字であり、これはNTFSの上限値だ。(NTFSはフォルダもファイルの一種として扱うので、ファイル名とフォルダ名の上限文字数に変わりはないのだが、上記したとおりエクスプローラー側でフォルダ名は245文字に制限がかけられている)
NTFSのファイル名の上限が何故256文字なのかを考えてみた。
NTFSの(FATやUNIX系ファイルシステムなどと比べて)ユニークなところは、1KB内に収まるのならファイル名もデータも同じ管理領域(MFT)にまとめて格納される点だ。
もちろんこれら以外のタイムスタンプや、データがディスク上のどこにあるのかという位置情報、アクセス権情報などもこの1KBの中に格納されるので、実際にファイル名やデータが使える領域はそれより小さくなるのだが、この仕様があるので管理領域内に収まったファイルは絶対に断片化しない。

例えば、テキストエディタを開き「abc」とだけ入力して改行せずに保存する。
このファイルを右クリックしてプロパティを開くと、「サイズ」は「3バイト」だが「ディスク上のサイズ」が「0バイト」になっていることが分かる。
このくらいのデータなら管理領域内に保存できてしまうサイズなので、データ領域には何も保存されていない、つまり「0バイト」となるわけだ。
この管理領域に収まらない情報は、データであってもファイル名であっても、他の場所(データ領域)に保存されることになる。
このときデータ領域のどこに保存されるかの位置情報は管理領域内に記録されるが、データが断片化し位置情報が肥大化してこれすらも収まらなくなると、その位置情報がデータ領域に保存され、その位置情報への位置が管理領域に記録される。
さらにデータ領域に置かれた位置情報が断片化してくるとそれらに対しての位置情報すらも管理領域に収まらなくなり、位置情報への位置情報がデータ領域へ保存され、それらに対しての位置情報が管理領域に記録され始める。(ややこしい)
これが大量に発生するとHDDではパフォーマンスがとても低下してしまうので、NTFSでは定期的なデフラグが必要となる。

ファイル名の上限文字数はUnicodeの256文字なので、バイト数に換算すると512バイトだ。
最長ギリギリのファイル名を付けると管理領域1KBの半分をファイル名で使用することになるが、それでも数百バイト程度のデータなら残りの領域内に格納できるし、ファイルが断片化して位置情報が大きくなってきてもしばらくは格納できる余裕がある。
これが仮に、ファイル名で1KBすべて消費してしまうと小さなデータが格納できなくなるので断片化が大量発生する上、位置情報を常にデータ領域に置かなければならないのでパフォーマンスが落ちるデメリットがあるし、かといってMS-DOS並しか扱えないのでは新OSとしての意味がない。
そんなところで、256文字という文字数に落ち着いたのではないだろうか、と私は勝手に推測している。
少なくとも、当時のマイクロソフトには「ファイル名(フォルダ名)情報はなるべく管理領域内に収まったほうが良い」という考えがあったのではないかと思う。
もっとも、ファイル作成段階で短い名前を付けておき、そのファイルが断片化しまくった後に長いファイル名に変更すると、ファイル名情報が管理領域内に収まりきらずデータ領域に溢れてしまう可能性はあるが。
当時他のOSのファイル名上限値は、MacOSで31バイト、UNIX系OSで255バイトだったので、ファイル名に255文字が使えるというのは画期的ではあった。

上でも少し触れたが、Windows10(1607以降)では_MAX_PATHの制限を解除でき、これを行うとエクスプローラーなどでNTFS上限値までのパスが扱えるようになる。
ちなみに_MAX_PATHの制限を解除する設定というのは、エクスプローラーなどWindowsプリインストールアプリの260文字制限がなくなるということであって、すべてのアプリケーションの260文字制限がなくなるというわけではない。(とくに古いサードパーティー製のアプリケーション)
_MAX_PATHというのはただのマクロなので、コンパイル前にプリプロセッサがコード内の「_MAX_PATH」を「260」と置き換えるだけのものだ。
そのためアプリケーションのマシンコード内には「260(16進で「0104」)」が埋め込まれているわけなので、これを設定で後から変更することは不可能だ。
再コンパイルすれば別かもしれないが、それでもstdlib.h内の_MAX_PATHの値が260→32768に増えるということではない。
ではどうしてエクスプローラーはそれを解除できるのかといえば、「パスを生成したり分割したりする際に制限を受けるか受けないかをレジストリの内容に従って決める」ようにエクスプローラーが新しく改修されたからだ。
なので正確に言うと、他のアプリケーションも同様に改修されれば_MAX_PATHの制限を受けなくなる、ということになる。
今後新しく出てくるアプリケーションに関しては、あまり心配はいらないのではないかと思う。

話が逸れたが、以下のどちらかの手順で制限の解除ができる。

グループポリシーの設定から行う方法


1. Windowsキーを押しながら「R」を押す
2. 「gpedit.msc」と入力してエンターキーを押す
3. 「コンピュータの構成 →「管理用テンプレート」→「システム」→「ファイルシステム」を開く
4. 「Win32の長いパスを有効にする」をダブルクリック
5. ラジオボタンを「有効」に切り替える
6. 「OK」をクリック

レジストリを直接書き換える方法


1. Windowsキーを押しながら「R」を押す
2. 「regedit」と入力してエンターキーを押す
3. 「HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem」を開く
4. 「LongPathsEnabled」をダブルクリック
5. 「値」に「1」を入力
6. 「OK」をクリック

[ 戻る ]
saluteweb