sakura
sakura copied to clipboard
ファイルパス長が MAX_PATH を超えるファイルに関して検討する
ファイルパス長が MAX_PATH
を超えるファイルに関して検討する
https://github.com/sakura-editor/sakura/issues/394#issuecomment-417899192
議題は、ファイルパス長が MAX_PATH
を超えるファイルを扱えるようにするか?
- Windows 10 1607 には MAX_PATH 制限を外すオプションがある
- サクラエディタ内の「パスを扱う文字列型」は
MAX_PATH
を超えるパス長を扱えない - Unicodeアプリは本来、最大 32,763字まで のパス長を扱うことができる
2.の「パスを扱う文字列型」は、共有メモリに突っ込むための固定長配列をstringっぽく使えるように拡張したテンプレート型なので、拡張すると共有メモリに影響が出ます。
どのように実装するかは置いておいて、 まずは MAX_PATH(260文字) を超えるパス長を扱いたいかどうかだと思います。
MAX_PATH(260文字) を超えるパス長を扱いたいかどうか
扱いたい場面はしばしば生まれますが、この場面が発生したときには、既に他のファイル処理系もグダグダになる事も経験済なので、世の中がみんなロングなのを扱いだしたときに考えるでもいいのかも(しばらく放置)。そのころには何か新しいアーキテクチャうまれてるかもと。
Windows 10 1607 には MAX_PATH 制限を外すオプションがある
- ユーザー側でレジストリを変えたりしないといけない
- Windows 10 1607 以降のみ
という事でこれに頼るのは良くないと思います。 \\?\
プレフィックスを付けるやり方で対処するのが良いと思います。
扱いたい場面はしばしば生まれますが、この場面が発生したときには、既に他のファイル処理系もグダグダになる事も経験済なので、世の中がみんなロングなのを扱いだしたときに考えるでもいいのかも(しばらく放置)。そのころには何か新しいアーキテクチャうまれてるかもと。
ところで、C/C++で書かれた MAX_PATH 定数を使ってビルドしてるアプリは、仮にOSのAPIのMAX_PATH制限がランタイム側で解除されたとしても、対応出来ないと思います。
という事もあって個々のアプリ単位での対応になると世の中の大多数がLFN対応する頃には人類は恒星間航行を実現していると思います。
扱えると嬉しいなぁ・・・でも実装面倒だよなぁ・・・
というニュアンスの書込みは「MAX_PATHを超えるパス長を扱いたい」という意見にカウントしてOKですかね?w
前提として「需要のあるなし」を把握しとかないといけないと思ってます。 「そんな長いパス、何かの間違いだよ」って意見が大勢をしめるかどうかです。 需要があるなら挑戦する意義はあると思いますので :smile:
他のプログラムで長いパスを開けるかどうか試してみました。 ドラッグ&ドロップはシェルが未対応なのかうまく動かないのでファイルダイアログから開きました。
notepad.exe
開けます。ただし保存時に短いファイル名に自動的に切り詰められてしまいました。
wordpad.exe
---------------------------
ワードパッド
---------------------------
\\?\D:\123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\bbbbbbbbbbb
ファイル名が長すぎます。
---------------------------
OK
---------------------------
こんなエラーメッセージが表示されました。
Visual Studio 2017
Ctrl + o で開くファイルダイアログで選択して開くとアプリが異常終了しました。
フォルダを開く機能で長いパスのフォルダを開こうとすると、
---------------------------
Microsoft Visual Studio
---------------------------
The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.
---------------------------
OK
---------------------------
と表示されました。
Visual Studio Code 1.25.0
Open File で開けませんが落ちはしません。特にエラーメッセージは出ません。
Open Folder でフォルダ開いた後だと開けますし編集して保存も問題なく行えます。grepも問題無いです。
秀丸エディタ Version 8.81 送金まだ
---------------------------
エラー
---------------------------
フォルダ名またはファイル名が長すぎます。
---------------------------
OK
---------------------------
というエラーメッセージが表示されました。課金が足りないのでしょうか…。
サクラエディタ
強制終了しました。
vim (VIM - Vi IMproved 8.0)
Git Bash(おそらくGit for Windowsを入れた際に付いてきた) で試したところ問題無く開いて編集&保存が出来ました。
Git Bash(おそらくGit for Windowsを入れた際に付いてきた) で試したところ問題無く開いて編集&保存が出来ました。
Cygwin/MSYS2 自体がずいぶん前から長いパス名に対応していたと思います。なので、その上で動くVimが長いパス名を扱えても不思議ではありません。 一方で、Win32版のVimは長いパス名には対応していません。
ほとんどが未対応なんですねぇ・・・ん?
サクラエディタ
強制終了しました。
強制終了しちゃうのは、扱えるように「する/しない」には関係なくマズくね?という印象です。 なんか前に「メッセージが出る」という話があったので、ダイアログが出て開けない感じになると思ってました。
CViewCommander::Command_FILEOPEN
の files
ローカル変数には、 \\?\
プレフィックス付きでフルパスが入っていますが、SLoadInfo::cFilePath
に代入されるところでちょん切られてしまっています。
その後、 CLoadAgent::OnCheckLoad
内で CFile
型のローカル変数 cFile
のコンストラクタ内で CFile::SetFilePath
が呼ばれてその中で StaticString::Assign
が呼ばれて wcscpy_s
が呼び出す common_tcscpy_s
の中で _VALIDATE_STRING
マクロが呼ばれて最終的に __fastfail
が呼ばれてタヒんでます。
http://www.kt.rim.or.jp/~kbk/zakkicho/10/zakkicho1003c.html#D20100321-3
を見るとCRT作者の Dinkumware の P.J. Plauger さんが strcpy_s
がプロセスを落とす挙動はMS公認と書いているようなのです。
今の実装は LFN 対応できていないですし対応するのも大変なので、CViewCommander::Command_FILEOPEN
内で
sLoadInfo.cFilePath = files[0].c_str();
する前にファイル名の長さをチェックして _MAX_PATH - 1
より大きかったらエラーメッセージを出して return するのが落ちないようにする対策として良いのではないかと思います。
落書き これがよく起きるシチュエーション、 ファイル共有サーバでドライブマッピングしたところにバックアップ取ったWebアプリプロジェクト(Eclipseプロジェクトとかnode.jsとか)が、ドライブマッピングなしだともう手が届かない。。。
node.jsやたら深くなる。。。
http://www.kt.rim.or.jp/~kbk/zakkicho/10/zakkicho1003c.html#D20100321-3
URL を見て嬉しくなっちゃいました。最近ずっと更新がないんですけどブックマークしてるところです。正規表現とか GREP の情報が豊富です。(雑談でした)
雑談だけではなんなので……
前提として「需要のあるなし」を把握しとかないといけないと思ってます。
以前は日本語ユーザーにありがちなこととして、階層が深くなると文字数の倍の早さでサイズ上限に近づいていって、パスが長すぎるとエクスプローラーに拒絶されたものですけど、最近はそういうこともありませんね。
ぷろーがー先生の名前が出てくるとわw
その後、 CLoadAgent::OnCheckLoad 内で CFile 型のローカル変数 cFile のコンストラクタ内で CFile::SetFilePath が呼ばれてその中で StaticString::Assign が呼ばれて wcscpy_s が呼び出す common_tcscpy_s の中で _VALIDATE_STRING マクロが呼ばれて最終的に __fastfail が呼ばれてタヒんでます。
一応フォローしておくと、Microsoftの主張は、バッファオーバーランの危険性を意識しないと気付いたときに大変なことになってしまうよ、ということで、大変な事態の回避策として危険性を意識せざるをえないバージョンの新しいCRTを提案しているわけです。正確には「Releaseでも落ちる」ではなくて「Releaseでもinvalid_parameterエラーになる」で、global handlerを登録しておけば落ちることもないです。invalid_parameterエラーはMS独自仕様で普通の例外じゃありません。専用のハンドラを書かないとキャッチできない代物ですが回避策はあります・・・もちろん、それはMicrosoftが推奨しない回避策ではあるのですが。
サクラエディタで「パスを扱うための型(≒StaticString)」が用意されている理由は、「最近使ったファイル」などの共有メモリデータを扱うのに「決まったサイズの箱」が必要だったからだと想像しています。現在のwindowsには、アプリごとの「最近使ったファイル」を管理できる機能がありますが、昔のwindowsにはそんなものありませんでした。現状のサクラエディタにはwindows側の「最近使ったファイル」の機能も一部既に取り込まれています。win7以降でタスクバーやスタートメニューのサクラエディタを右クリックすると「最近使ったファイル」が出ますよね?あれはサクラエディタの機能じゃないんです。あれはwindows標準の「ファイルを開く」ダイアログを使うアプリに与えられる恩恵の1つです。まぁ、サクラエディタは標準ダイアログをカスタマイズして使ってますけど。
MAX_PATH
を超えるパス長は、扱いたい、で良さそうですかね?
次は、「じゃあどうやって?」の話題をしてく感じで進めたいと思ってます。
recentフォルダにショートカットをぶち込む機能は昔からあったかも、ということに気付く・・・。
cygwinのこの件に関する対応は本当によくできているみたいなわかりやすくまとまっているツイートを前に見かけた気がするけど、どうにも発掘できないでいます。うーん。
node.js使ってるとあっという間にMAX_PATHは突破するんですよね。
git for Windows の対応について下記のページに書かれていました。 https://github.com/git-for-windows/git/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
そこからリンクされている下記のコミットのメッセージに色々書かれていました。 https://github.com/git-for-windows/git/commit/38b94fe15fb60e3871a166eec8cfd4265fee727f
そのコミットではスタックに確保するやり方は変えない為に #define MAX_LONG_PATH 4096
こういう定数を使っています。
https://github.com/git-for-windows/git/commit/38b94fe15fb60e3871a166eec8cfd4265fee727f#diff-6222d26ac3070f3a5a23a7160479fb3eR400-R405
\\?\
プレフィックスを付けるやり方は絶対パスにしか対応していないので、WindowsAPI の GetFullPathNameW
で絶対パスに変換して処理してます。
https://github.com/git-for-windows/git/commit/38b94fe15fb60e3871a166eec8cfd4265fee727f#diff-a0c7aa297d8da1149ce761c31dc59328R2106
実装が(ちょっとは)簡単そうなのは Win 10 1607 の機能を使う方かなぁ。相対パスもそのまま使えるのだろうか? Python (3.6 以降) はこちらで実装されていますね。インストーラーを動かすと最後に MAX_PATH 制限を外すレジストリ設定を有効にしますかと、聞いてきます。
レジストリとmanifestの具体的な設定はここを参照 https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#enable-long-paths-in-windows-10-version-1607-and-later
そうですね。その方法だと MAX_PATH
で検索して引っかかる箇所をサイズ大きい版の定数に置き換えるだけの対処でいけるかもしれないと思いました。
ただOS側のサポート具合が色々と微妙に感じました。長いパスのファイルのドラッグ&ドロップに対応していないみたいです。あとファイルダイアログでフルパスを打ち込んでも切りつめられてしまいます。将来的にはもっと改善されるのかなぁ…。
例のレジストリは自分の環境だと勝手に追加されてたんですが、多分Pythonのインストーラーが追加したのかもしれないです。そういえばPythonのインストーラーにそんな設定がありましたね。
https://docs.microsoft.com/ja-jp/windows-insider/at-home/Whats-new-wip-at-home-20h1#%E3%83%A1%E3%83%A2%E5%B8%B3%E3%81%AE%E6%94%B9%E5%96%84-%E3%83%93%E3%83%AB%E3%83%89-18963notepad-improvements-build-18963 メモ帳では、MAX_PATH とも呼ばれる 260 文字より長いパスを使用して、ファイルを開いたり保存したりできるようになりました。