Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ファイルパス長が MAX_PATH を超えるファイルに関して検討する #401

Open
m-tmatma opened this issue Sep 2, 2018 · 19 comments
Labels
enhancement ■機能追加

Comments

@m-tmatma
Copy link
Member

m-tmatma commented Sep 2, 2018

ファイルパス長が MAX_PATH を超えるファイルに関して検討する

#394 (comment)

@berryzplus
Copy link
Contributor

議題は、ファイルパス長が MAX_PATH を超えるファイルを扱えるようにするか?

  1. Windows 10 1607 には MAX_PATH 制限を外すオプションがある
  2. サクラエディタ内の「パスを扱う文字列型」は MAX_PATH を超えるパス長を扱えない
  3. Unicodeアプリは本来、最大 32,763字まで のパス長を扱うことができる

2.の「パスを扱う文字列型」は、共有メモリに突っ込むための固定長配列をstringっぽく使えるように拡張したテンプレート型なので、拡張すると共有メモリに影響が出ます。

どのように実装するかは置いておいて、
まずは MAX_PATH(260文字) を超えるパス長を扱いたいかどうかだと思います。

@KENCHjp
Copy link
Member

KENCHjp commented Sep 3, 2018

MAX_PATH(260文字) を超えるパス長を扱いたいかどうか

扱いたい場面はしばしば生まれますが、この場面が発生したときには、既に他のファイル処理系もグダグダになる事も経験済なので、世の中がみんなロングなのを扱いだしたときに考えるでもいいのかも(しばらく放置)。そのころには何か新しいアーキテクチャうまれてるかもと。

@beru
Copy link
Contributor

beru commented Sep 4, 2018

Windows 10 1607 には MAX_PATH 制限を外すオプションがある

  • ユーザー側でレジストリを変えたりしないといけない
  • Windows 10 1607 以降のみ

という事でこれに頼るのは良くないと思います。 \\?\ プレフィックスを付けるやり方で対処するのが良いと思います。

@beru
Copy link
Contributor

beru commented Sep 4, 2018

扱いたい場面はしばしば生まれますが、この場面が発生したときには、既に他のファイル処理系もグダグダになる事も経験済なので、世の中がみんなロングなのを扱いだしたときに考えるでもいいのかも(しばらく放置)。そのころには何か新しいアーキテクチャうまれてるかもと。

ところで、C/C++で書かれた MAX_PATH 定数を使ってビルドしてるアプリは、仮にOSのAPIのMAX_PATH制限がランタイム側で解除されたとしても、対応出来ないと思います。

という事もあって個々のアプリ単位での対応になると世の中の大多数がLFN対応する頃には人類は恒星間航行を実現していると思います。

@berryzplus
Copy link
Contributor

扱えると嬉しいなぁ・・・でも実装面倒だよなぁ・・・

というニュアンスの書込みは「MAX_PATHを超えるパス長を扱いたい」という意見にカウントしてOKですかね?w

前提として「需要のあるなし」を把握しとかないといけないと思ってます。
「そんな長いパス、何かの間違いだよ」って意見が大勢をしめるかどうかです。
需要があるなら挑戦する意義はあると思いますので 😄

@beru
Copy link
Contributor

beru commented Sep 4, 2018

他のプログラムで長いパスを開けるかどうか試してみました。
ドラッグ&ドロップはシェルが未対応なのかうまく動かないのでファイルダイアログから開きました。

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を入れた際に付いてきた) で試したところ問題無く開いて編集&保存が出来ました。

@k-takata
Copy link
Member

k-takata commented Sep 4, 2018

Git Bash(おそらくGit for Windowsを入れた際に付いてきた) で試したところ問題無く開いて編集&保存が出来ました。

Cygwin/MSYS2 自体がずいぶん前から長いパス名に対応していたと思います。なので、その上で動くVimが長いパス名を扱えても不思議ではありません。
一方で、Win32版のVimは長いパス名には対応していません。

@berryzplus
Copy link
Contributor

ほとんどが未対応なんですねぇ・・・ん?

サクラエディタ

強制終了しました。

強制終了しちゃうのは、扱えるように「する/しない」には関係なくマズくね?という印象です。
なんか前に「メッセージが出る」という話があったので、ダイアログが出て開けない感じになると思ってました。

@beru
Copy link
Contributor

beru commented Sep 4, 2018

CViewCommander::Command_FILEOPENfiles ローカル変数には、 \\?\ プレフィックス付きでフルパスが入っていますが、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公認と書いているようなのです。

@beru
Copy link
Contributor

beru commented Sep 4, 2018

今の実装は LFN 対応できていないですし対応するのも大変なので、CViewCommander::Command_FILEOPEN 内で

sLoadInfo.cFilePath = files[0].c_str();

する前にファイル名の長さをチェックして _MAX_PATH - 1 より大きかったらエラーメッセージを出して return するのが落ちないようにする対策として良いのではないかと思います。

@KENCHjp
Copy link
Member

KENCHjp commented Sep 4, 2018

落書き
これがよく起きるシチュエーション、
ファイル共有サーバでドライブマッピングしたところにバックアップ取ったWebアプリプロジェクト(Eclipseプロジェクトとかnode.jsとか)が、ドライブマッピングなしだともう手が届かない。。。

node.jsやたら深くなる。。。

@ds14050
Copy link
Contributor

ds14050 commented Sep 5, 2018

http://www.kt.rim.or.jp/~kbk/zakkicho/10/zakkicho1003c.html#D20100321-3

URL を見て嬉しくなっちゃいました。最近ずっと更新がないんですけどブックマークしてるところです。正規表現とか GREP の情報が豊富です。(雑談でした)

雑談だけではなんなので……

前提として「需要のあるなし」を把握しとかないといけないと思ってます。

以前は日本語ユーザーにありがちなこととして、階層が深くなると文字数の倍の早さでサイズ上限に近づいていって、パスが長すぎるとエクスプローラーに拒絶されたものですけど、最近はそういうこともありませんね。

@berryzplus
Copy link
Contributor

ぷろーがー先生の名前が出てくるとわ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 を超えるパス長は、扱いたい、で良さそうですかね?
次は、「じゃあどうやって?」の話題をしてく感じで進めたいと思ってます。

@berryzplus
Copy link
Contributor

recentフォルダにショートカットをぶち込む機能は昔からあったかも、ということに気付く・・・。

@yumetodo
Copy link

cygwinのこの件に関する対応は本当によくできているみたいなわかりやすくまとまっているツイートを前に見かけた気がするけど、どうにも発掘できないでいます。うーん。

node.js使ってるとあっという間にMAX_PATHは突破するんですよね。

@beru
Copy link
Contributor

beru commented May 9, 2020

git for Windows の対応について下記のページに書かれていました。
https://github.com/git-for-windows/git/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path

そこからリンクされている下記のコミットのメッセージに色々書かれていました。
git-for-windows/git@38b94fe

そのコミットではスタックに確保するやり方は変えない為に #define MAX_LONG_PATH 4096
こういう定数を使っています。
git-for-windows/git@38b94fe#diff-6222d26ac3070f3a5a23a7160479fb3eR400-R405

\\?\ プレフィックスを付けるやり方は絶対パスにしか対応していないので、WindowsAPI の GetFullPathNameW で絶対パスに変換して処理してます。
git-for-windows/git@38b94fe#diff-a0c7aa297d8da1149ce761c31dc59328R2106

@k-takata
Copy link
Member

実装が(ちょっとは)簡単そうなのは 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

@beru
Copy link
Contributor

beru commented May 10, 2020

そうですね。その方法だと MAX_PATH で検索して引っかかる箇所をサイズ大きい版の定数に置き換えるだけの対処でいけるかもしれないと思いました。

ただOS側のサポート具合が色々と微妙に感じました。長いパスのファイルのドラッグ&ドロップに対応していないみたいです。あとファイルダイアログでフルパスを打ち込んでも切りつめられてしまいます。将来的にはもっと改善されるのかなぁ…。

例のレジストリは自分の環境だと勝手に追加されてたんですが、多分Pythonのインストーラーが追加したのかもしれないです。そういえばPythonのインストーラーにそんな設定がありましたね。

@yumetodo
Copy link

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 文字より長いパスを使用して、ファイルを開いたり保存したりできるようになりました。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement ■機能追加
Projects
None yet
Development

No branches or pull requests

7 participants