Go: 提案:os:Create / Open / OpenFile()WindowsでFILE_SHARE_DELETEを設定します

作成日 2019年05月16日  ·  194コメント  ·  ソース: golang/go

LinuxとMacOSでは、これを書くことができます。 Windowsでは、「共有違反」で失敗します。

path := "delete-after-open"
fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
if err != nil { ... }
err = os.Remove(path)            // or os.Rename(path, path+"2")
if err != nil { ... }
fd.Close()

Windowsで開発し、Linuxなどにデプロイし、コードがos.Rename()および.Remove()のこの_undocumented_ GOOS = windowsの動作に依存している場合、それは壊れており、おそらく脆弱です。 パッケージ「os」には、他のWindows固有の動作の_15の言及_があることに注意してください。

これを修正するには、 https: //golang.org/src/syscall/syscall_windows.go#L272のsyscall.Open()
sharemode |= FILE_SHARE_DELETEが必要です

マイクロソフトでは、これをデフォルトにすることをお勧めします: https
Rustはそれをデフォルトにしました: https
Mingw-w64は7年前にデフォルトにしました:
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858
Erlangは6年以上前にデフォルトにしました:erlang / otp @ 0e02f48
MSVCランタイムの制限により、 Pythonはそれを採用できませんでした: https

〜したがって、syscall.Open()はデフォルト使用する必要があり、syscallは次の両方を提供する必要があります。
a)それを無効にするグローバルスイッチ(その不在に依存する既存のアプリの場合)、および
b)os.OpenFile()で使用して、特定のファイルハンドルで無効にするためのフラグ。

https://github.com/golang/go/issues/32088#issuecomment-532784947の後に@rscで更新:
a)os.Create / Open / OpenFile()は、Windowsでは常にfile_share_delete
b)Windowsのsyscall.Open()は、file_share_deleteを有効にするフラグを受け入れる必要があります。
c)Windowsのsyscallは、新しいフラグの定数をエクスポートする必要があります。

os.Remove()のドキュメントでは、Windowsで開いているファイルを削除した後にファイル名を再利用するには、 os.Rename(path, unique_path); os.Remove(unique_path)実行する必要があることにも注意してください。

WinAPIドキュメント
https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-deletefilea
https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea

これを行わない理由がある場合は、os.Remove()および.Rename()に文書化する必要があります。

cc @alexbrainman
@gopherbot add OS-Windows

FrozenDueToAge OS-Windows Proposal Proposal-FinalCommentPeriod release-blocker

最も参考になるコメント

ここでは、Windowsチームの視点を表現しようとします...オプションなしで、常にFILE_SHARE_DELETEを設定するだけです。 一般に、Windowsとの移植を容易にするために、Linuxとの一貫した動作を優先します。また、Windowsユーザーまたはソフトウェアがデフォルトの!FILE_SHARE_DELETE動作を優先して、これがルールの例外となる理由がわかりません。

@mattnのコメントとは対照的に、

つまり、最新バージョンのWindowsでmattnのテストプログラムとdel、dirなどのシーケンスを実行すると、テストプログラムが終了した後ではなく、ファイルを削除した直後にファイルが名前空間から消えます。 Linuxと同じように。

そのため、appcompatのリスクが非常に大きいWindows自体でも、Windows以外のソフトウェアのWindowsへの移植を容易にするために小さな変更を加えています。 Goにも同じことをすることを強くお勧めします。

全てのコメント194件

/ cc @alexbrainman

https://golang.org/src/syscall/syscall_windows.go#L272のsyscall.Open()
sharemode := ... | FILE_SHARE_DELETEを使用する必要があります

なぜそれが必要ですか?

アレックス

FILE_SHARE_DELETEは、上記のコード例を有効にします。これはMacOSとLinuxで機能しますが、Windowsでは失敗します。 次の場合にも必要です。

fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
defer fd.Close()
_, err = fd.Write(...)
err = fd.Sync()  // file now safe to share
err = os.Rename(path, path+"2")
_, err = fd.Read(...)

FILE_SHARE_DELETEは、上記のコード例を有効にします。これはMacOSとLinuxで機能しますが、Windowsでは失敗します。

代わりにMacOSとLinuxのコードを調整してみませんか?

次の場合にも必要です。

あなたが何を言おうとしているのかわかりません。

アレックス

@networkimprov WindowsではClose()の後にRemoveを呼び出す必要があります。

path := "delete-after-open"
fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
if err != nil { panic(err) }
fd.Close()
err = os.Remove(path)            // or os.Rename(path, path+"2")
if err != nil { panic(err) }

@alexbrainman信頼性の高いファイルI / Oを実行する場合(データベースの場合)、一時的な名前でファイルを作成し、書き込み、fsyncし、名前を変更するのが標準的な方法です。

Unixでは、開いているファイルの名前をデフォルトで変更または削除できます。 この機能のWindowsフラグが設定されていないのは見落としのようです。 LinuxとMacOSでの動作方法を変更するようにGoチームを説得することはできないでしょう:-)

@mattn plsは、私が説明した修正を適用し、私が投稿したコードを試してください。

LinuxとMacOSでの動作方法を変更するようにGoチームを説得することはできないでしょう:-)

私は今のように元気です。

アレックス

Windowsでこの一般的な機能を省略する理由はありますか?

プログラムの開始時にUnixの動作を選択できるように、syscall_windows.goにスイッチを提供できますか?

新しいAPIまたはフラグを追加しても大丈夫です。 しかし、私は現在の行動を変えることに反対しています。 FILE_SHARE_DELETEはUnixの動作と同じではないため。

#include <windows.h>
#include <stdio.h>

int
main(int argc, char* argv[]) {
  HANDLE h = CreateFile("test.txt",
      GENERIC_READ | GENERIC_WRITE,
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
      NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  getchar();
  char buf[256] = {0};
  DWORD nread;
  printf("%d\n", ReadFile(h, buf, 256, &nread, NULL));
  printf("%s,%d\n", buf, nread);
  return 0;
}

このコードをWindowsで完成させ、 test.exeとして実行してみてください。 このアプリがヒットを待っている間に、新しいcmd.exeを開き、次のように「test.txt」を削除します。

image

ファイルは削除できますが、プロセスが存在する間はそこに残ります。 したがって、この変更は期待どおりには機能しません。

ディレクトリエントリが削除されていないことはわかりましたが、ドキュメントには

その後のCreateFileの呼び出しでファイルを開くと、ERROR_ACCESS_DENIEDで失敗します。

だから私はあなたの画面ログを理解していません。

とにかく、このようなスイッチで問題ありません。

syscall.OpenFileShareDelete = true

私は以前に仕事でこれに噛まれたことがあることを言及したいと思います。

基本的に私たちは持っていました:

f, err := os.Open(...)
if err != nil { ... }
defer f.Close()

// lots of code

if err := os.Rename(...); err != nil { ... }

コードはUNIXベースのCIプラットフォームでは正常に実行されましたが、Windowsでは爆発的に増加しました。

物事はただ働いていた場合任意の奇妙な副作用がないと仮定すると、それはいいだろう。

IIRCでは、過去にGOOS = windowsとplan9の動作にいくつかの調整を加えて、Unixのセマンティクスにより厳密に一致させました。 セマンティクスが十分に近い場合は、これを別のケースにすることを気にしません。 ただし、 @ mattnのコメントは、動作が十分に近くないため、価値がない可能性があることを示唆しています。

ただし、グローバルオプションは見たくありません。 それはデバッグの悪夢のように思えます。

@bradfitz
おそらくそうではないでしょう、ここで私のコメントを参照してください
https://groups.google.com/forum/#!topic/golang -dev / R79TJAzsBfM
または、必要に応じて、ここにコンテンツをコピーすることもできます。これは、Windowsプログラムの動作と一致しないため、デフォルトの動作としては実装しませんが、同様のクロスを作成するための回避策であるため、解決策もあります。 -OSの経験。

@guybrandはgolang-devスレッドに書き込みます:

私のプログラムは「my.data」というファイルを書き込んでいて、別のプログラムを呼び出して終了するのを待ちません...このプログラムでは、このファイルをアップロードするのに10秒かかります(この他のプログラムを「アップローダー」と呼びます)。 。
..。
私のプログラムがWindowsで.Removeを呼び出すとき(FILE_SHARE_DELETEがオン):
..。
アップローダーは、ファイルが削除されたことを通知するエラーを受け取ります(おそらくEOF)。

「myprogram」がos.Remove()を呼び出す前に「uploader」がファイルを開くと仮定すると、WinAPIのドキュメントと矛盾していると思います。

_DeleteFileは、ファイルを閉じるときに削除するようにマークします。 したがって、ファイルの最後のハンドルが閉じられるまで、ファイルの削除は行われません。 その後のCreateFileの呼び出しでファイルを開くと、ERROR_ACCESS_DENIED._で失敗します。

「CreateFileの_open_osfhandle()を_fdopenにペアリングする」再、サンプルコードを指すことができますか?

FILE_SHARE_DELETEを追加すると、多くのプログラマーが誤ってFILE_SHARE_DELETEを使用してUnixの動作をシミュレートします。 この場合、OSごとにビルド制約で区切られた関数を作成できます。 そして、 os.NewFile()を返すはずです。WindowsではFILE_SHARE_DELETEを使用してください。

OSごとにビルド制約で区切られた関数を作成します... os.NewFile()を返し、WindowsではFILE_SHARE_DELETEを使用します

os.OpenFile()の機能を維持するには、この計画では次のすべてを再実装する必要があるようです。

os.OpenFile()
openFileNolog()
openFile()
openDir()
newFile()
fixLongPath()
syscall.Open()
makeInheritSa()

@networkimprov

@guybrandはgolang-devスレッドに書き込みます:

私のプログラムは「my.data」というファイルを書き込んでいて、別のプログラムを呼び出して終了するのを待ちません...このプログラムでは、このファイルをアップロードするのに10秒かかります(この他のプログラムを「アップローダー」と呼びます)。 。
..。
私のプログラムがWindowsで.Removeを呼び出すとき(FILE_SHARE_DELETEがオン):
..。
アップローダーは、ファイルが削除されたことを通知するエラーを受け取ります(おそらくEOF)。

「マイプログラム」がos.Remove()を呼び出す前に「アップローダー」がファイルを開くと仮定すると、Win APIのドキュメントと矛盾していませんか?

DeleteFileは、閉じるときにファイルに削除のマークを付けます。 したがって、ファイルの最後のハンドルが閉じられるまで、ファイルの削除は行われません。 その後のCreateFileの呼び出しでファイルを開くと、ERROR_ACCESS_DENIEDで失敗します。

「CreateFileの_open_osfhandle()を_fdopenにペアリングする」再、サンプルコードを指すことができますか?

WIndows APIとの矛盾は見当たりませんが、矛盾のように見えるものを指摘してください。

コードサンプルに関しては、私は少しグーグルで検索し、これを見つけました:
http://blog.httrack.com/blog/2013/10/05/creating-deletable-and-movable-files-on-windows/

しかし
注意してください-これは内部でnixのような動作を与えるだけであり、それを操作する他の外部プログラムはそれを壊します(標準のWindows APIを99%使用しているため)

@networkimprov
「これは「dirmy.data」のように聞こえますが、これはWindowsまでの動作でした。どういうわけか)-次にWindows環境を起動したときに再テストできます(数週間ごとに発生します...)

「これは、ファイルへのハンドルを持つ他のプロセスでもファイルの読み取りと書き込みができるように聞こえます」という意味の場合、「EOF」スタイルのエラーが発生するようなファイルへの読み取りと書き込みの2番目のランチに賭けます。 -しかし、100%ポジティブであることを再度確認する必要があります。

どちらの場合でも、私のポイントは(この時点で-「ネイティブライク」と「os-agnostic」のポイントで「サイド」を取っています)-_ fdopenスタイルのソリューションを実装したとしても、サービスとコラボレーションする他のすべての実行可能ファイル。そのため、他のgo実行可能ファイル(またはfdを直接使用するまれなサービス)との対話でのみ使用できます。

言い換えれば、あなたのアプリは「クラスで最も賢い子供」であり、他の子供には理解できません。
私が考えることができる多くの例からの2つの例:
入力
私のアプリはファイルをダウンロードし、アンチウイルスはそのharfullを識別し、ファイルを削除/隔離(==名前の変更の一種)します。fdを使用すると、アプリはそれを使ってやりたいことを何でもできます(これはcollですが、終了する可能性があります)ウイルスにぶつかるまで...)
出力
私のアプリはファイルを別の(「アップローダー」のような)サービスにパイプし、それを削除します。私はわざわざテスターを作成して、すべてが正常に機能していることを確認しました。テストは合格です。
今では、goテストの代わりに、filezilla、transfter、dropbxAPIなどを使用しています。
それは失敗する/私のテストが機能するのと同じように動作しないでしょう...

これをデフォルトの動作に変更するのは理にかなっていると思いますか?

Windowsでこの一般的な機能を省略する理由はありますか?

私はその質問を考えたことがありません。 知らない。

GoファイルがWindowsで機能する方法は、私がこれまでに使用した他のすべての開発者ツールと一致しています。 Goファイルがあなたの提案どおりに機能するのであれば、私は驚きます。 私はまた、それが多くの既存のプログラムを壊すだろうと思う。

アレックス

私はまた、それが多くの既存のプログラムを壊すだろうと思う。

@alexbrainmanデフォルトを変更する代わりに、それを有効にするスイッチも提案しました。

@bradfitzには

syscall_windows * .goが述べているので
func Open(path string、mode int、perm uint32)(fd Handle、err error){
...。
sharemode:= uint32(FILE_SHARE_READ | FILE_SHARE_WRITE)

type_windows * .goには
FILE_SHARE_DELETE = 0x00000004

すべての準備が整っています。唯一の問題は、フラグを実装する方法です。
私はグローバルオプションが好きではなく、明示的でもありません。単一の開発者はos.DeleteUponLastHandleClosed = 1を設定したことを覚えているかもしれませんが、長期または複数の開発者にとっては良い習慣ではありません。
他のオプションは、次のように、フラグに特定の予約番号を設定することです。
fd、err:= os.OpenFile(path、os.O_RDWR | os.O_CREATE | os.DELETE_WHEN_FREED、0600)
一方、DELETE_WHEN_FREEDは、envの場合は0にすることもできます。 窓以外は

もう1つのオプションは、Windowsではサポートされていないpermパラメータを使用することです。これは、少し厄介になる可能性があります。
fd、err:= os.OpenFile(path、os.O_RDWR | os.O_CREATE、777)
777は予約されているため、両方のシステムをサポートするには1777または-777roが必要です。
作成するのは読み取り可能ですDELETE_WHEN_FREED | 777

私が考えることができる最後のオプションはos.OpenDeletableFile(
これは、nixのos.OpenFileと
振り向く
sharemode:= uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
窓に

上記のすべてのソリューションは実装が簡単で、テストにもう少し時間がかかり(クロスOS)、投票者が必要です...

デフォルトを変更したりAPIサーフェスを拡張したりせずにこの動作をサポートする1​​つの方法os.OpenFile 、Windowsのflagパラメーターでos.OpenFile syscall.FILE_SHARE_DELETEを受け入れ、計算されたものに折りたたむことです。 sharemode値。 Windowsのsyscall.O_* (したがってos.O_* )フラグはとにかく構成された値を使用するため、含めたいWindows固有のフラグとの衝突を回避するように設計できます。 幸いなことに、彼らはすでにsyscall.FILE_SHARE_DELETEとの衝突を避けています。

いずれにせよ、私はデフォルトの振る舞いを変更することに強く反対します。 FILE_SHARE_DELETEデフォルトにすると、レースフリーのファイルシステムトラバーサルを保証できないという点で、POSIXシステムと同じ立場になります。 このフラグをオプションにすることで、Windowsはopenatrenameatreadlinkatなどに相当するものを必要としません。

ここで何をすべきかについてはあまり考えていませんが、1つ明確にしておきたいことがあります。

プログラムの開始時にUnixの動作を選択できるように、syscall_windows.goにスイッチを提供できますか?

これは行いません。 そのため、1つのプログラムで異なる動作を期待する異なるパッケージを使用することは不可能になります。

@ havoc-io
syscall.FILE_SHARE_DELETEはPOSIXstdの動作とは異なるため、提案するとエラーが発生しやすいプログラムが作成されます。
syscall_windows * .goの作成者は、FILE_SHARE_DELETEフラグ(そこにあります)を認識しており、それを使用することは推奨されない動作であると判断したことに注意してください。
どうして?

リアムのコードを取りましょう、彼
fd.Close()を延期する
削除/名前変更
その後、読み取り/書き込み

したがって、実際の並べ替え順序は
開いた
削除済みとしてマーク
読み取り/書き込み(そしておそらくクロスプロセス/クロスゴールーチンの読み取り/書き込みも)
選ぶ

現在のWindowsの動作は、開発者に「これは間違っています」と警告します。開発者はおそらく削除をプッシュして延期します
FILE_SHARE_DELETEを追加すると、Windowsでは「ファイルが開いていても削除としてマーク」できますが、同時に実行されている別のプロセス/ゴルーチンは、削除とクローズの間にファイルにアクセスしようとします(これはおそらくこのコードでは長いタスクです) )失敗します
結果:一貫した「あなたはそれを行うことができます」という振る舞いの代わりに-あなたは「時々-」を得るでしょう-特に多くの同時ユーザーがいるときそれは失敗し、私は理由がわかりません。
したがって、問題を解決するのではなく、開発者が「一度だけ実行する」特定のユースケースを処理するだけです。
もちろん、私の投票は一貫した行動に賛成です...

Posixとの違いはどうですか?
削除済みとしてマークされると、ファイルはファイルテーブルに存在しなくなり、fdとしてのみ存在し、別のルーチン/プロセスがまったく同じ名前のファイルを作成できるようになります。

go内で解決されない違いがあるため、「同じ解決策」を探すのをやめるべきだと思います(大文字と小文字を区別するファイル名?:) Linux 5.2にそれをさせます...)

全体として、一時ファイルの解決策を探しているように見えます。そうであれば、WindowsはGetTempFileNameをサポートします。これは、「使用されてからゴミ箱に移動される」ファイルの標準的な解決策と見なすことができます。

一方、我々は可能だウィンドウで延期削除に現像剤を、許可したい場合は、上記の私の提案を参照してください、しかし、開発者はその責任を取って、意識を理解しなければならない-ので良いの命名規則を持つフラグでなければなりません。

この機能の主な用途は、開いているファイルを作成した後に名前を変更することです。

fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
_, err = fd.Write(...)
err = fd.Sync()  // file now safe to share
err = os.Rename(path, shared_path)
// os.Close() at program exit

フラグos.O_WRNDEL(windowsの名前変更/削除;他のプラットフォームではno-op)を追加しない理由がわかりません。また、そのモードとUnixの違いを文書化します。 削除されたファイルはそのディレクトリに残りますが、os.Close()まで開くことができません。 また、ファイルを削除する前に一時ディレクトリにファイルを移動すると、Unixのような動作が得られることにも注意してください。

@guybrandおそらくあなたは私の提案を誤解していると思います。 私はFILE_SHARE_DELETEがデフォルトで有効になっていることを主張していません。実際、コメントの2番目の段落で述べた理由で反対しています。 私の提案は、ユーザーがosパッケージのファイルインフラストラクチャを使用しながら、(ファイルごとに) FILE_SHARE_DELETE動作にオプトインできるメカニズムを提案しました。 この提案は、パッケージのAPIサーフェスを拡張せずにこれを行うためのメカニズムを提供します。

フラグos.O_WRNDELを追加しない理由がわかりません(Windowsの名前変更/削除;他のプラットフォームではno-op)

@networkimprovこれは基本的に私が提案していることですが、完全に有効なフラグがすでに存在するため、新しいフラグを定義しても意味がありません: syscall.FILE_SHARE_DELETE 。 唯一の実装変更は、Windowsのsyscall.Openでこのフラグを監視し、 syscall.O_* / os.O_*フラグへの今後の追加がこのフラグと衝突しないことを確認するためのチェックを追加することです。 その後、 os.OpenFileのドキュメントを更新して、Windowsでのこのフラグの受け入れを反映することができます。

@ havoc-io誤解してすみません、これは私の持ち帰りでした:
「... syscall.FILE_SHARE_DELETEを受け入れるだけで...計算された共有モードに...」

os.O_WRNDELを追加すると、「動作はどうなるか」という点で開発者にとって十分に明確ではないことを除けば、私の2番目の提案と一致します。おそらくos.WADRNDEL-Windowsでは名前の変更/削除を延期できます)。

@alexbrainmanデフォルトを変更する代わりに、それを有効にするスイッチも提案しました

私は興味がありません。 ありがとう。

アレックス

開いているファイルの@guybrandの名前変更が延期されていないことを確認しました。

申し訳ありませんが、私はあなたの提案は何ですか? 開いているファイルを削除しますか? 開いているファイルの名前を変更しますか? どちらも?

私たち3人は今、新しい旗を提案しています。

fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE| os._WRNDEL, 0600)

fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE| syscall.FILE_SHARE_DELETE, 0600)

私たち3人は今新しい旗を提案しています

新しいフラグは不適切だと思います。特に、コードを読んでいる人が簡単に解析できないフラグはそうです。 syscall.FILE_SHARE_DELETEすると、はるかに明確で明確になり、プラットフォーム固有の何かが発生していることをリーダーにすぐに通知します。

これは、POSIXプラットフォームですでに許可されています。 osパッケージに組み込まれていない多くのPOSIX固有の(さらにはプラットフォーム固有の) openフラグがあります。 たとえば、DarwinのO_EVTONLYフラグをサポートするためにos._DEVTONLYようなものを追加することは意味がありません。これは、フラグをsyscallパッケージからos.OpenFile直接渡すことができるためです。

os.OpenFile(..., os.O_RDONLY | syscall.O_EVTONLY, ...)

この場合、プラットフォーム固有のフラグは、基になるopenシステムコールに渡されます。

FILE_SHARE_DELETE動作が本当に人々が必要としているものである場合、答えは単にos.OpenFileがWindowsで基になるCreateFileW呼び出しにフラグを同様にスレッド化できるようにすることだと思います。 少なくともFILE_SHARE_DELETE

実装例はここにあります

@networkimprov

私たち3人は今、新しい旗を提案しています。

旗は何を期待しますか?

開いているファイルの@guybrandの名前変更が延期されていないことを確認しました。

そうですが、ファイルが開いているときに許可するには、両方ともFILE_SHARE_DELETEが必要です
私の提案する用語を参照している場合は、
os.WARNDFDEL-Windowsは名前の変更を許可し、遅延削除は少し長すぎます。私自身は頭字語を使用しますWINDOWS_ALLOW_OPENNED_FILE_RENAME_OR_DEFFERED_DELETE
より良いIMOです。

@guybrand複雑な名前と不透明な動作を備えた新しいプラットフォーム固有のフラグをosパッケージに追加することの価値は、これに完全に適したフラグが何十年も前から存在している場合には、実際にはわかりません( FILE_SHARE_DELETE )。 syscall.FILE_SHARE_DELETEをフラグとして使用すると、はるかに明確で明確になります。 で述べたosパッケージに追加されていないが、それでも受け入れられるPOSIX固有およびプラットフォーム固有のフラグが多数あります。

syscall.FILE_SHARE_DELETEはWindowsのみです。他の場所では何もできないものが必要です。おそらく、WINまたはWINDOWSプレフィックスを付けてpkg osに表示する必要がありますか?

@mattn plsは、@

他の場所では何もできないものが必要です。

@networkimprovフラグは他の場所に存在する必要はありません。 Windowsコードは次のようになります。

import (
    "syscall"
    "os"
)

file, err := os.OpenFile(..., os.O_RDWR | syscall.FILE_SHARE_DELETE, ...)

はい、このコードはプラットフォームでゲートする必要がありますが、このフラグの動作はPOSIXシステムの動作と一致しないため、とにかくゲートする必要があります。 または、すべてのプラットフォームで同じコードを使用できるようにしたい場合は、独自の定数(Windowsではsyscall.FILE_SHARE_DELETE 、Windows以外のシステムでは0値)を定義して渡すことができます。 os.OpenFileます。 これは、 osパッケージの一部ではないプラットフォーム固有のO_*フラグ(たとえば、DarwinのO_EVTONLYを持ち込みたい場合に行うことです。 LinuxではO_ASYNC )。

クロスプラットフォームであると予想されるコードを作成する場合、FILE_SHARE_DELETEはsyscall_ *には存在せず、Windowsのみに存在します(したがって、anfは他のシステムではコンパイルされません)。

0x4 constをその:として使用できますか?
FILE_SHARE_DELETE = 0x00000004

醜いことは別として、
netbsd_arm
O_NDELAY = 0x4
O_NONBLOCK = 0x4

したがって、0x4を使用すると、netbsd_armで異なるコードが作成されます。

したがって、すべてのプラットフォームにFILE_SHARE_DELETEを追加するか、Windowsのものは0x4になり、他のプラットフォームは0x0になり、それを「破棄」するか、この問題の特定のフラグを作成します。

そしてとにかく、syscall_windows *を変更する必要があります
func open(
..。
sharemode:= uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | isDeleteOn)
ここで、isDeleteOnは、モードに新しいフラグが含まれているかどうかを確認します

@guybrand

私がコードを書くとき、私はそれがクロスプラットフォームであることを期待しています

同じos.OpenFile呼び出しが複数のプラットフォームでコンパイルされるからといって、コードがクロスプラットフォームであることを意味するわけではないことに注意してください。 FILE_SHARE_DELETEを使用するときに考慮する必要のあるWindowsのセキュリティ上の考慮事項がいくつかあり、ファイルライフサイクルのセマンティクスはPOSIXシステムとWindowsシステムで非常に異なるため、次のようなシナリオを想像するのは非常に困難です。ファイル処理コードは、プラットフォーム間で少なくとも少しは異なって見えません。 FILE_SHARE_DELETEを使用すると、基本的にPOSIX.1-2008より前の状況になり、レースのないファイルシステムトラバーサルを保証する方法がなくなります(また、POSIXのunlinkファイルアクセスを開くメリットもありません)。ファイル)。

とにかく、Windowsではsyscall.FILE_SHARE_DELETE値を持ち、他のプラットフォームでは0値を持つフラグが必要な場合でも、ビルドタグによって制御される独自の定数定義を使用して簡単に行うことができます。 os.OpenFileを呼び出すメインコードは、すべてのプラットフォームで同じにすることができます(そのカスタムフラグを使用)。

他のプラットフォームのsyscallパッケージには多くのプラットフォーム固有のフラグが存在しますが、それらすべてをosパッケージで公開したり、そうでないプラットフォームで操作したりすることはできません。 tサポートされています。 プラットフォーム固有のコードと動作が必要な場合は、 syscallパッケージにアクセスする必要があります。

Go標準ライブラリが(潜在的に)ここで行うべき唯一のことは、Windowsでsyscall.Open (したがってos.OpenFile )でsyscall.FILE_SHARE_DELETEをサポートすることです。

で述べた

@mattn私はあなたの懸念に同意します-人々は、微妙な意味を理解せずに、WindowsコードをPOSIXシステムと「同じように機能させる」ための便宜としてフラグを使用しようとするかもしれません。 ただし、そのフラグにアクセスするためにsyscallパッケージにアクセスする必要がある場合は、それが何をするのかを理解するのに時間がかかった可能性があると思います。

@ mattn 、Unixライクな動作の場合、アプリにはプラットフォームに依存しないコードを2行追加する必要があります。

fd, err := os.OpenFile(path, ... | syscall.FILE_SHARE_DELETE, 0600)
...
tmp_path := mkTempName()
err = os.Rename(path, tmp_path)  // works immediately; path is now available
err = os.Remove(tmp_path)

これを文書化するには、os.OpenFile()のドキュメントに1つの文が必要です。

plsは、私のWindowsコードで作業するすべての人が適用しなければならないパッチをpkg syscallに配布させません! https://github.com/golang/go/issues/32088#issuecomment-493876119も参照して

私たちは皆、ほとんどの事実に同意していると思います。言い方を変えて、要約して、行動方針を設定しようと思います。

  1. 同意しました:WindowsとPOSIXは上記の動作が異なるため、同じ動作を目指すべきではありません。
  2. 同意しました:FILE_SHARE_DELETEをフラグとして許可するリクエストは便利です
  3. 提案:チケットを「supportFILE_SHARE_DELETE」に変更して、バグではなく機能リクエストのように聞こえるようにします
  4. ほぼ合意:この機能は、基本パッケージではなく、プログラムレベルの開発者フラグである必要があります。

アクションアイテム:

  1. syscall_windows.goを変更します
    func open(
    ..。
    sharemode:= uint32(FILE_SHARE_READ | FILE_SHARE_WRITE |(mode&FILE_SHARE_DELETE)
  2. この動作を利用したい開発者は、
    file, err := os.OpenFile(..., os.O_RDWR | syscall.FILE_SHARE_DELETE, ...)
    または、プログラムをクロスプラットフォームにしたい場合は、次の内部フラグを作成します。

mycode_windows.go
const OPENFILEFLAG = syscall.FILE_SHARE_DELETE
mycode_others.go
const OPENFILEFLAG = 0

次に使用します:
file, err := os.OpenFile(..., os.O_RDWR | OPENFILEFLAG, ...)

これが合意された場合、修正は最小限(1ライナー)で、リスクは非常に低くなります

これに投票してみましょう。すぐに修正できます

同意しました:FILE_SHARE_DELETEをフラグとして許可するリクエストは便利です

この問題の目的がわからないので、私はまだこれに同意しませんか?

目的:開発者が開いているファイルの名前を変更または削除できるようにします。

不足しているもの:syscall_windows.goは現在ハードコーディングされています:
func Open(path string, mode int, perm uint32) (fd Handle, err error) {
..。
sharemode := uint32(**FILE_SHARE_READ | FILE_SHARE_WRITE**)

提案された変更:
開発者がビット単位4(FILE_SHARE_DELETE)をオンにしてモードパラメータを渡すと、それに応じて共有モードが次のように変更されます。
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | **(mode & FILE_SHARE_DELETE)** )

Rename()またはRemove()の前にClose()を呼び出さないのはなぜか疑問に思っています。 Goの標準ライブラリのコードを読むことができます。 ファイルは、Rename()またはRemove()の前にClose()によって閉じられます。 Renamoe / Renameの後にClose()を呼び出さなければならないのはなぜですか?

@networkimprovが検討しているビジネスケースが何であるか

しかし、 @ networkimprovがRename()を使用したいように見えるので、おそらくdiffのユースケースです。

https://github.com/golang/go/issues/32088#issuecomment -494305074で説明されているように、プログラムが終了するまで、名前を変更したファイルを閉じる必要がない場合があります。

@ mattn 、plsは、CおよびC ++開発者が使用できるOS機能を決して使用してはならないと主張していません。

(アプリがクラッシュした場合でも)完了後にこのファイルが存在しないことを確認したい

@guybrand deferステートメントがすべてのタイプのクラッシュで実行されるとは限らないため、ファイルをos.Remove操作を延期することは信頼できる方法ではありません。それについて行きます。 自動的に削除されるファイルが必要な場合は、システムが削除する一時ファイルの方が適しています。 2つのプログラムで共有されるファイルの場所が必要な場合は、ファイルの存在ではなく、ロック(POSIXではfcntlロック、WindowsではLockFileEx / UnlockFileEx )で調整する必要があります。 。

#32088(コメント)で述べたように、プログラムが終了するまで、名前を変更したファイルを閉じる必要がない場合があります。
@ mattn 、plsは、CおよびC ++開発者が使用できるOS機能を決して使用してはならないと主張していません。

@networkimprovオープン後の名前変更操作は有効なユースケースですが、ファイルHANDLE保持しているプログラムの場合、すでにそれを行うことはできませんか? FILE_SHARE_DELETEの唯一の目的は、ファイルを開いたままにして、他のプロセスがファイルの名前を変更できるようにすることです。 いずれの場合も、今すぐFILE_SHARE_DELETE使用できます。手動でCreateFileWを呼び出し、結果をos.NewFile渡す必要があります。 これがどのように見えるかの例をここで見つけることができます。 結果のHANDLEos.NewFile渡すことができます。

@mattnこのフラグは、便利なツールというよりはフットガンになるということに同意し始めています。 明確にするために、私は実際にはこのフラグを望んでいません。その存在について私が見ることができる唯一の議論は、POSIXとWindowsの間の制御の完全性と同等性です。 一方、前述したように、手動でCreateFileW呼び出し(このフラグを指定して)、結果をos.NewFileに渡すことも比較的簡単なので、おそらく必要はありません。それのサポートを追加します。

@ havoc-io os.File.Rename()はありません。 それは良い解決策ですが、それは比較的重いリフトです。

CreateFileW + os.NewFile()を再作成します。https: //github.com/golang/go/issues/32088#issuecomment-493876119を参照して

@ havoc-io

(アプリがクラッシュした場合でも)完了後にこのファイルが存在しないことを確認したい
@guybrand deferステートメントは、すべてのタイプのクラッシュで実行されることが保証されているわけではありません
「アプリがクラッシュしても」と言ったとき、これはまさに私が意味したことでした。

その存在について私が見ることができる唯一の議論は、POSIXとWindowsの間の制御の完全性と同等性です。
これが「POSIXとWindows間の制御の完全性と同等性」をもたらすことに同意しません-IMOは、そうでない場合でも動作が同じであると開発者を混乱させるだけです。上記の例をいくつか挙げました。

手動でCreateFileWを呼び出すのは簡単
これは、簡単かどうかにかかわらず、実際には微調整です。言い換えると、「これをサポートしたくない」ということです。これは、ちなみに、大丈夫な決定IMOですが、要求者は@networkimprovです。

参考までに、FILE_SHARE_DELETEの使用をあきらめた理由は次のとおりです。

CL: https

erlang / otpでコミット: https

Python: https

@ havoc-io os.File.Rename()はありません。 それは良い解決策ですが、それは比較的重いリフトです。
CreateFileW + os.NewFile()を再作成します。#32088(コメント)を参照してください。

@networkimprov os.Rename(file.Name(), <target>)だけを使用することはできませんか? これは、デフォルトでFILE_SHARE_DELETEオンになっていないGoの美しさです。ファイルHANDLE保持しているので、 file.Name()はまだ有効なものを指していることがわかります。 renameatても、POSIXを保証します)。 CreateFileW + os.NewFileに関しては、コメントに記述されているスタックのほとんどは無関係になり、 makeInheritSaはほぼ間違いなくあなた望まfixLongPathパス関数だけです。これは、説明したユースケースではおそらく必要ありません。

@guybrand私が何を意味するのかを明確にするために...

完全性: APIサーフェスの完全性(つまり、何らかの理由で使用したい場合はFILE_SHARE_DELETEを使用する機能)
制御のパリティ: os.OpenFileflagパラメータを介してCreateFileWフラグを指定する機能。 たとえば、POSIXではsyscall.O_NOFOLLOWからos.OpenFileルーティングできますが、Windowsではsyscall.FILE_FLAG_OPEN_REPARSE_POINTからos.OpenFileルーティングできません。 繰り返しになりますが、 osパッケージはPOSIX APIを中心にモデル化されているため、ここでの制御の完全な同等性は夢のようなものだと思います。 必要なときにsyscall.CreateFileWに手を差し伸べることができてとてもうれしいです。

@mattnリンクをありがとう。 Go CLは、syscall.Open()_ default_にパッチを適用することでした。 この議論は、Open()の_flag_を禁止する一般的な理由を示していません。 サードパーティのパッケージがフラグのないos.Fileを返す可能性があることに注意してください。ただし、これは、呼び出し元がそのファイルの名前を変更/削除しようとした場合にのみ問題になります。これはまれなケースです。

Pythonスレッドは、上で書いたように、開いているファイルを削除する前に名前を変更する必要があると述べています。

Erlangは6年以上の間file_share_deleteを_default_として持っています。

上記でリンクされたErlangの経験とPythonスレッドを反映して、次のことが明らかです。

  1. デフォルトとしてのFile_share_deleteは、クロスプラットフォーム環境でうまく機能します。 唯一の注意点は、元のファイル名を再利用できる場合は、ユーザーコードがファイルを削除する直前に一意の名前に名前を変更する必要があることです(些細な変更)。
  2. ほとんどのWindowsプログラムは、MSVCランタイムがO_TEMPORARYとの組み合わせでのみファイルを許可するため、file_share_deleteを使用できません。

したがって、syscall.Open()はデフォルトでfile_share_deleteを使用する必要があり、syscallは必要に応じて無効にするための

1.14サイクル中にそのアプローチで問題が発生した場合は、代わりに明示的なフラグ方式を適用できます。

したがって、syscall.Open()はデフォルトでfile_share_deleteを使用する必要があり、syscallは必要に応じてそれを無効にするグローバルフラグを提供する必要があると結論付けます(デバッグなど)。

@networkimprov膨大な数の既存のプログラムを微妙な方法とそれほど微妙ではない方法で壊すことは別として、問題の事実は、このフラグFILE_SHARE_DELETE不在に依存するプログラムのセキュリティ問題になるということですFILE_SHARE_DELETE 。 特に、開いたままになっているファイルやディレクトリの不変性に依存しているプログラムには、 Time-of-Check-to-Time-of- Useの脆弱性が多数発生します。

編集:デフォルトを変更することが悪い考えであり、既存のプログラムを破ることがほぼ確実である理由の詳細については、 Hyrumの法則を参照してください。

Go on Windowsの最も一般的なケースは、 Linuxまたは他のUnixに

コードがGOOS = windowsの動作に依存している場合、コードは壊れており、Linuxで脆弱である可能性があります。 したがって、セキュリティの問題がすでに発生している可能性があります。

os.Rename()と.Remove()は、Windowsでの違いを文書化していないため、それらが存在するとは誰も推測しません。 今すぐそれらを文書化しても、既存のコードは修正されません。 非互換性を修正し、ブログ投稿を公開すると、はるかに役立ちます。

Erlangがデフォルトとしてfile_share_deleteを採用する前に、しばらくの間存在していたことに注意してください。

Go on Windowsの最も一般的なケースは、 Linuxまたは他のUnixに

コードがGOOS = windowsの動作に依存している場合、コードは壊れており、Linuxで脆弱である可能性があります。 したがって、セキュリティの問題がすでに発生している可能性があります。

@networkimprov

そのロジックにより、POSIXのデフォルトの動作に一致させるために、Windowsのファイルを開くインフラストラクチャも変更して、プロセスの作成全体でファイル記述子をデフォルトで継承できるようにする必要があります。 既存のプログラムは、ランタイム/標準ライブラリの以前のデフォルトの動作に依存していると非難される可能性があり、ユーザーがプログラムを提出するためのリリースノートにテンプレートCVEを含めることができます。

しかしもちろん、それは行われていることではなく、代わりにGoのランタイムと標準ライブラリが逆方向に曲がってPOSIXのデフォルトの不適切に設計された動作を回避し、Windowsの動作を反映しようとします。

状況は共有ファイルアクセスでも同じです。 Windowsは間違いなくデザインを正しくし、POSIX.1-2008はそのデザインの制限を回避するために多くの機能を追加する必要がありました。

また、 @ mattnが上記で何度か言及したように、ファイルを開いてもWindowsがPOSIXのように動作しない場合のFILE_SHARE_DELETEを含めて、 os.Remove違いよりも、このフラグではるかに明らかになる重要な動作の違いがあります。 os.Rename動作。

あなたのコードが普遍的に任意のプラットフォームの振る舞いに依存している場合、それは壊れており、他のプラットフォームではおそらく脆弱であると私は主張します(そしてそれはWindowsでのPOSIXの振る舞いに依存することを含みます)。 コード内のプラットフォームのバリエーションを理解して対処するために時間を割いていない場合は、クロスプラットフォーム開発を行っていません。

いずれにせよ、 @ ianlancetaylorは、これをオンにすることを制御するためにグローバルフラグを使用するという考えをすでに拒否しているので、デフォルトを変更した後にオフにすることになるのではないかと真剣に疑っています。

私の意見では、ここでの唯一の非破壊(および非セキュリティ妥協)オプションは、 syscall.Openflag引数にsyscall.FILE_SHARE_DELETEサポートを追加するか、単に強制することですFILE_SHARE_DELETE機能でCreateFileW + os.NewFileを使用したいユーザー。 はい、この動作を望む開発者の作業が少し必要ですが、Win32とPOSIXは根本的に異なる獣であり、ランタイムと標準ライブラリがそれらの違いを舗装するのと同じくらいうまく機能することは驚くべきことです。 標準ライブラリに現れるこれらのプラットフォームの間には、さらに多くの重要な違いがあります。たとえば、完全に異なるアクセス許可モデル、 os.File.ChmodがWindowsで機能しない、 os.Chownが機能しないという事実Windows、内部ポーラーがさまざまなプラットフォームでさまざまな種類のファイルをサポートしているという事実など。Goのランタイムと標準ライブラリは可能な限り最善を尽くします(そしてそれをうまく実行します)が、クロスプラットフォーム開発の問題の万能薬ではありません。 ある時点で、開発者はこれらの違いを自分で処理する必要があります。 変更するために重大な変更を追加する(私が正しいとは言わなかったことに注意してください)、あいまいな動作のエッジケースは私には意味がありません。

このためのもう1つのユースケースを共有したいと思いました。

現在、Dockerログローテーションロジックが動作しているときに、Dockerエンジンがアクティブログの名前を.1に変更し、古い名前で新しいログを作成します。
コマンドdocker logs --follow <container>ログをアクティブにフォローしているクライアント
ファイルシステムイベントから次のことに気付くでしょう。
https://github.com/moby/moby/blob/916eabd459fe707b5c4a86377d12e2ad1871b353/daemon/logger/loggerutils/logfile.go#L552 -L593

Windowsがこれらのイベントにすぐに気付くようにするこのロジックもあります。
https://github.com/moby/moby/blob/916eabd459fe707b5c4a86377d12e2ad1871b353/daemon/logger/loggerutils/logfile.go#L670 -L676

これはすべてLinuxでうまく機能しますが、Windowsでは、ログに続くクライアントがある場合、ログローテーションはエラーThe process cannot access the file because it is being used by another process.失敗します(問題:https://github.com/moby/moby/issues/39274)

ログをフォローしているクライアントがn人いる可能性があるため、開いているファイルハンドルをすべて収集し、ファイルをローテーションする前に閉じるのは非常に難しいので、 syscall.FILE_SHARE_DELETEをサポートしてもらいたいと思います。

こんにちは! 簡単な背景:私はChromeCIインフラストラクチャに取り組んでいます。 低レベルのワーカーコードをGoに移植しています。 1日にWindowsで数十万のテスト呼び出しを実行します。

私はすべてのコメントを読みました。以下が各個人の意見の明確で正しい要約であることを願っています。

私たちのユースケースは、 @ networkimprovの驚くべき特性とは異なり、Windows上で実行できるようにWindows上でGo実行可能ファイルを構築することです。 このコメントでは、この「WindowsでのPOSIXの動作を想定する」ユースケースを無視します。これは、単なる誤った想定であり、 @ mattnをドラッグしてIMHOが重要なポイントです。

移行の一環として、#39274と同様に、ログファイルのローテーションが原因でFILE_SHARE_DELETEが必要になります。 問題#25965の問題でもあることが起こります。

https://codereview.appspot.com/8203043/で提案されているように、 @ ianlancetaylorがツールチェーン全体の動作を変更することに同意します。 ファイルハンドルをロックファイルとして使用するときにこの提案を思い浮かぶ1つの差し迫った問題。 この動作を使用します。 そして@ havoc-ioが言ったこと。 それで、これも無視しましょう。

言い換えると、このコメントの残りの部分では、これらの提案を無視します。

  • POSIXとまったく同じように動作しようとすると、機能しません
  • オプトアウト(またはオプトアウトなし!)としてグローバルな動作を変更し、ユーザーを破壊します

これにより、明らかにWindows固有の名前の呼び出しごとのフラグが残ります。 これはまさに@guybrandが提案していることです。

敬意を表して、 @ mattnの異議を理解していません。 @guybrandが提案することは合理的です。 はいWindowsはファイル共有に関して異なる動作をします。 これはファイル削除権のようなもので、Windowsでは大きく異なります。 それはすでに多くの方法で分岐しています。 「開発者はその振る舞いを誤解する可能性がある」というスタンスでWindows固有のフラグに異議を唱えることは事実ではなく、意見です。 開発者は自分が何をしているのかわからないか、MSDNのドキュメントを読まないと想定しています。 これは一般的なケースに対する公正な異議です😎が、オプションのフラグとして正当なユースケースをブロックすることに基づく強い異議ではありません。

皮肉なことに、これは、この問題が解決するまで、#25965を修正するには、 FILE_FLAG_DELETE_ON_CLOSEも必要なので、 CreateFileW直接呼び出すように「gorun」を変更する必要があることを意味します🙃。

ありがとう!

新しい属性でファイルを開く/作成するための新しい関数(golang.org/x/sys?)を実装する必要があるかどうか、または元の動作を変更する必要があるかどうかを知るために、どのようにケースが存在するかを知りたいだけです。 現在、上記のコメントから私が知る限り:

  1. ロギング用の削除可能(名前変更可能)ファイルを作成します。
  2. クローズ時に削除される一時ファイルのファイルを作成します。

1の場合、属性を持つファイルを作成/開くための新しい関数から提供できます。 そして、logger.SetOutput(file)を使用してファイルを設定できます。

2の場合、新しい関数で作成/開くこともできます。

ところで、#25965はこの問題とは関係がないと思います。 おそらく、Windowsの制限です。

(いずれの場合も、この属性で開いたファイルが存在するディレクトリを削除することはできません)

@mattn以下の間のトレードオフを説明できますか?

  • golang.org/x/sysに完全に新しい関数を追加する
  • OpenFile()への新しいフラグ(すでに定義されています!)をサポートします。 [1]

なぜ2番目ではなく1番目を優先する必要があるのか​​わかりません。

[1]ここでos.OpenFile()の動作を変更すると仮定したのに気づきましたが、この問題はOpen()を呼び出します。

まず第一に、syscallパッケージはすでにロックダウンされています。 だから私はsyscallパッケージを変更せずにこれを修正する方法を探しています。

ところで、#25965はこの問題とは関係がないと思います。 おそらく、Windowsの制限です。

(いずれの場合も、この属性で開いたファイルが存在するディレクトリを削除することはできません)

@mattnそれは完全には真実ではありません。 FILE_SHARE_DELETEフラグを使用すると、これらのファイルを別のフォルダーに移動して、元のファイルを削除できます。 その簡単なPowerShellの例を次に示します。

# Create folder and open new file with FILE_SHARE_DELETE flag
New-Item -Type Directory -Path C:\folder1
$file = [System.IO.File]::Open("C:\folder1\test.txt", "Create", "ReadWrite", "Delete")

# Create temp folder and move all open files to there
New-Item -Type Directory -Path C:\folder1-removing
Get-ChildItem -Path C:\folder1 -Recurse | Move-Item -Destination C:\folder1-removing\

# Remove original folder
Remove-Item -Path C:\folder1

# Trigger removal for files on temp folder (NOTE! Those will exist on disk until they are closed).
Get-ChildItem -Path C:\folder1-removing -File -Recurse | Remove-Item -Force

# Close file and remove temp folder
$file.Close()
Remove-Item -Path C:\folder1-removing

まず第一に、syscallパッケージはすでにロックダウンされています。 だから私はsyscallパッケージを変更せずにこれを修正する方法を探しています。

@mattn APIがフリーズするだけの問題である場合は、前述のように/デモで説明したように、 syscall.Open調整してsyscall.FILE_SHARE_DELETEを理解し、APIを変更せずに問題を解決できます。

実装のフリーズの場合は、 golang.org/x/sys/windowsOpen関数に変更を加えてから、 internal/syscallパッケージにベンダーすることができます。 file_windows.goコードは、すでにinternal/syscall関数を使用しています。 唯一の矛盾は、 syscall.Openがこのフラグを理解しないことですが、低レベルのWindows API機能にアクセスしようとしているほとんどの人golang.org/x/sys/windowsとにかくCreateFile Open CreateFile代わりにOpen

syscallパッケージの「ロックダウン」を開始した提案は次のように述べています。
_コアリポジトリはgo.sysパッケージに依存しません_

したがって、x / sysを変更しても、os.OpenFile()の呼び出し元には役立ちません。 ただし、internal / syscallはOpen()を追加でき、os.OpenFile()はそれを呼び出します。

@ maruel 、Dockerプロジェクト、およびおそらく他の多くのプロジェクトは、開いたファイルのos.Rename()および.Remove()に対してUnixの動作を想定しました。これは、パッケージ「os」がWindows固有の動作について言及していないためですが、その他のWindowsの動作。

コアリポジトリはgo.sysパッケージに依存しません
ただし、internal / syscallはOpen()を追加でき、os.OpenFile()はそれを呼び出します。

@networkimprovさて、私はinternal/syscallgolang.org/x/sysベンダーのサブセットであるという誤った印象を受けましたが、どちらにしてもそれは正しい戦略だと思います( internal/syscall/windows.Open追加)。 もちろん、これはsyscall.Openの動作がフリーズのために不変であるという仮定の下にあります。 理想的には、 golang.org/x/sys/windows.Open対応する変更を加えて、直接変更することができます。

syscallパッケージは凍結されていますが、バグを修正したり、重大な欠陥に対処したりするために変更することができます。 特に、osパッケージをより適切にサポートするように変更できます。

ただし、 syscall.Open直接呼び出す場合は、syscallパッケージではなく、x / sys / windowsパッケージを使用する必要があります。

ただし、 syscall.Open直接呼び出す場合は、syscallパッケージではなく、x / sys / windowsパッケージを使用する必要があります。

syscall.Open直接呼び出すことを考えている人はいないと思います- os.OpenFile基礎となるため、これは議論の対象にすぎません(したがって、 syscall.Open FILE_SHARE_DELETEサポートを追加しますsyscall.Openは、 os.OpenFileフラグを使用するためのサポートを追加します)。

ありがとう。 osパッケージへの変更をサポートするためにsyscallパッケージを変更することは問題ありません。

@ianlancetaylorと@

  • os.OpenFile()がFILE_SHARE_DELETE(および最終的には潜在的なフォローアップとしてFILE_FLAG_DELETE_ON_CLOSE)を受け入れることを許可するという
  • os.OpenFile()ドキュメントを更新して、新しいWindows専用フラグについて説明します。
  • os.OpenFile()、os.Rename()、およびos.Remove()のドキュメントを更新して、POSIXとWindowsの動作の違いを明確にします。

3番目のポイントは、 @ networkimprovの懸念に対処@mattnに同意し

承認があれば、これに貢献したいと思います。

これにより、明らかにWindows固有の名前の呼び出しごとのフラグが残ります。

新しいos.Openフラグを提案しますか? 次に、フラグはすべてのOSでサポートされる必要があります(つまり、osパッケージの要点です)。OSに依存しません。 では、そのフラグのセマンティクスはどうなるでしょうか? また、フラグが文書化されているとおりに機能することを確認するいくつかの新しいテストが必要になります。

syscallパッケージを使用して必要なコードを記述できない理由がわかりません。 あなたのコードは他のWindowsプログラムとうまく共存できないと思います。 そして多分それは特別な状況で大丈夫です。 しかし、私は人々がこの機能を軽く使用することを望まないので、標準ライブラリの一部であってはなりません。

アレックス

@alexbrainman
こちらをご覧ください

私は見ました。 提案されたすべての変更が他のOSでどのように機能するかまだわかりませんか? 提案された変更を実装するには、どのようなテストが必要ですか?

アレックス

これを引用

一方、DELETE_WHEN_FREEDは、envの場合は0にすることもできます。 窓以外

したがって、他のOSでテストするものは何もありません。
fd、err:= os.OpenFile(path、os.O_RDWR | os.O_CREATE | os.DELETE_WHEN_FREED、0600)
ウィンドウでは、constを4に変換します
FD、ERR = os.OpenFile(パス、os.O_RDWR | os.O_CREATE | 4、0600)
他の人( 0
FD、ERR:= os.OpenFile(パス、os.O_RDWR | os.O_CREATE | 0、0600)

他のOSでテストするものはありません

Windowsでのみ使用できる新しいOSパッケージの定数または変数を追加する方法がわかりません。

c:\>go doc os
package os // import "os"

Package os provides a platform-independent interface to operating system
functionality. 
...
The os interface is intended to be uniform across all operating systems.
Features not generally available appear in the system-specific package
syscall.

アレックス

アレックスあなたの異議への答えは最初にここに述べられましたhttps://github.com/golang/go/issues/32088#issuecomment-494157514

テストは簡単です

_, err := os.OpenFile(path, os.O_CREATE | syscall.FILE_SHARE_DELETE, 0);
err = os.Rename(path, path+"2")

誰かがパッチを提出できるようになりました。 @ianlancetaylorがそれを受け入れるだろうと私は思う。

#32188の一部としてFILE_SHARE_DELETEを設定することを調査しましたが、設定してもos.Renameおよびio.ReadFile呼び出しからのエラー率が経験的に減少しないことがわかりました。 とにかく再試行ループがまだ必要でした。

私はこのフラグを設定することとの有意な観察可能な違いを示すテストを見ることに非常に興味があります。なぜなら、私はその意味を自分自身で本当に理解していないからです。

数年前にFILE_SHARE_DELETEを試しましたが、動作しました
別の方法で。
おそらくWindows環境を再起動できます。 それが助けになるなら、それを調べてください、
しかし、それは〜Go1.3かそこらになります。

2019年6月10日月曜日17:44、ブライアンC.ミルズ[email protected]
書きました:

#32188の一部としてFILE_SHARE_DELETEを設定することを調査しました
https://github.com/golang/go/issues/32188 、しかしそれを設定することがわかりました
os.Renameおよび
io.ReadFile呼び出し; とにかく再試行ループがまだ必要でした。

私は重要なことを実証するテストを見たいと思います
私は実際にはそうではないので、このフラグを設定することとの観察可能な違い
その意味を自分で理解してください。


あなたが言及されたので、あなたはこれを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4VVIVJ2IEWQPWCWBZTPZZSETA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOOR
またはスレッドをミュートします
https://github.com/notifications/unsubscribe-auth/ABNEY4STTW7U526HPO6TUHDPZZSETANCNFSM4HNPNYIA

@ bcmills 、#32188を読んでいます。名前の変更中に_開いていなかった_ファイルでos.Rename()からエラーが表示されていたことがわかりましたか? すべてのディレクトリ変更操作用に作成されていると思われるNTFSジャーナルを追い越して、エラーを引き起こす可能性があると思います。 しかし、それがどのようなエラーになるかはわかりません。

syscall.Open()でFILE_SHARE_DELETEを有効にするためのここでの要求は、os.Rename()が開いているファイルで機能できるようにすることです。 Win7で1.12を使用してそのフラグをテストしましたが、機能します。 それなしでは失敗します。

「負荷がかかっている」Windowsでos.Rename()を具体的にテストしていませんが、fsdを有効にしてから、開いているファイルでos.Rename()から予期しないエラーが発生することはありません。

名前の変更中に開かれなかったファイルでos.Rename()からのエラーが表示されていたと思いますか?

はい。

どんなエラーになるのかわかりません。

経験的に、私が見ているエラー( MoveFileExReplaceFile 、および/またはCreateFile )はERROR_ACCESS_DENIEDERROR_SHARING_VIOLATION 、およびERROR_FILE_NOT_FOUND

syscall.Open()でFILE_SHARE_DELETEを有効にするためのここでの要求は、os.Rename()が開いているファイルで機能できるようにすることです。 Win7で1.12を使用してそのフラグをテストしましたが、機能します。 それなしでは失敗します。

右; 私が解決しようとしている関連する問題は、 os.Renameへの同時呼び出しが成功することを許可し、 os.Renameと同時にio.ReadFileへの呼び出しが成功することを許可することです。 単一のos.Renameを開いて既存のハンドルを操作することは正しい方向への一歩かもしれませんが、POSIX renameを採用しているようなユースケースには十分ではないと思います。

私はこのフラグを設定することとの有意な観察可能な違いを示すテストを見ることに非常に興味があります。なぜなら、私はその意味を自分自身で本当に理解していないからです。

@bcmills PowerShellの例は、Goよりもはるかに高速に記述しています

New-Item -Type Directory -Path C:\folder1
for($i=0; $i -le 1000; $i++)
{
    $temp = [System.IO.File]::Open("C:\folder1\test.txt", "Create", "ReadWrite", "Delete")
    Set-Variable -Name "file$i" -Value $temp -Force
    $TempName = "C:\folder1\test.txt." + (New-Guid).Guid
    Rename-Item -Path C:\folder1\test.txt -NewName $TempName
    Remove-Item -Path $TempName -Force
}

その後、 C:\folder1下に1,000個のtest.txt.???ファイルがありますが、PowerShellを閉じると、これらのファイルは削除されます。

したがって、実際にFILE_SHARE_DELETEフラグを使用すると、開いているファイルの名前を変更/移動/削除できますが、ハンドルが開いている限りディスク上に存在するため、宛先ファイル名が一意であることを確認する必要があります。

「POSIXの名前変更を採用する」ことはWindowsでは不可能であり、おそらくそれがエラーの原因です。

POSIX:

ファイル「filename」を作成します
「ファイル名」からの読み取り/書き込み
「ファイル名」を削除

ファイル「filename」を作成します
..。
「ファイル名」を削除

各作成ファイルにはdifffdがあるため、機能します。

ウィンドウズ:
ファイル「filename」を作成します
「ファイル名」からの読み取り/書き込み
「ファイル名」を削除

ファイル「filename」の作成-ブーム、共有違反

再構築は非常に簡単なはずです...

次に、FILE_SHARE_DELETEで何ができるでしょうか。
ファイルが開いている間、遅延削除を許可します。

それでおしまい。

オッリJanatuinen月、夜07時32分に2019年6月10日、オン[email protected]
書きました:

私は重要なことを実証するテストを見たいと思います
私は実際にはそうではないので、このフラグを設定することとの観察可能な違い
その意味を自分で理解してください。

@bcmillshttps ://github.com/bcmills私が書いているPowerShellの例
Goよりもはるかに高速

New-Item -Type Directory -Path C:folder1for($ i = 0; $ i -le 1000; $ i ++)
{{
$ temp = [System.IO.File] :: Open( "C:folder1test.txt"、 "Create"、 "ReadWrite"、 "Delete")
Set-Variable -Name "file $ i" -Value $ temp -Force
$ TempName = "C:folder1test.txt。" +(New-Guid).Guid
名前の変更-アイテム-パスC:folder1test.txt -NewName $ TempName
Remove-Item -Path $ TempName -Force
}

それが終わった後、千のtest.txtがあります。 下のファイル
C:folder1ですが、PowerShellを閉じると、これらのファイルは削除されます。

つまり、FILE_SHARE_DELETEフラグが実際に実行できるのは、
開いているファイルの名前を変更/移動/削除しますが、それでも宛先を確認する必要があります
ファイル名は、ハンドルが存在する限りディスク上に存在するため、一意です。
開いています。


あなたが言及されたので、あなたはこれを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4WSTWIUVNVHLYMBFY3PZZ62DA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2Z
またはスレッドをミュートします
https://github.com/notifications/unsubscribe-auth/ABNEY4VS5XF3EXB6QPBWVQ3PZZ62DANCNFSM4HNPNYIA

os.Renameと同時にio.ReadFileへの呼び出しが成功することを許可する

@bcmills関係のないファイルで同じシーケンスを試行している他のスレッドと同時に実行されていない場合は、常にfsdフラグを設定して作業する必要があると思います。 そして、それは通常、フラグなしで失敗するはずです。

テストスクリプトのその側面により、Windows syscall.Open()のバグが正しく検出されました。 しかし、このスレッドでコメントしている他の誰もそれを修正することを望んでいません:-(そうそう、スクリプトにパッチを当ててください。

TLDR:
syscal_windows.goへのマイナーな安全な変更であり、期待どおりに機能します。

説明:
だから、これが私がやったことであり、期待通りに機能し、リスクはありません(IMO)

  1. syscal_windows.goを微調整します
func Open(path string, mode int, perm uint32) (fd Handle, err error) {

sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | (mode & FILE_SHARE_DELETE))

追加は: "|(mode&FILE_SHARE_DELETE)"
開発者がモードと4セットを送信しない場合(なぜ彼は-これは以前は機能していなかった...)-何も変更されていないので、次のようなコード

os.OpenFile(filename,os.O_RDWR|os.O_CREATE, 0600)

変更前とまったく同じように動作します。

os.OpenFile(filename,os.O_RDWR|os.O_CREATE  | 4 , 0600)

変化を「感じる」

  1. コードを実行しました
package main

import (
    "fmt"
    "os"
)

func main() {
    filename := "./myfile"
    if f ,err := os.OpenFile(filename,os.O_RDWR|os.O_CREATE|4, 0600);err!=nil{
        fmt.Printf("Open file %s error : %s" , filename, err.Error())
    } else if _,err:= f.WriteString("bla bla") ;err!=nil{
        fmt.Printf("writing to %s returned error : %s" , filename, err.Error())
    } else if err := os.Remove(filename);err!=nil{
        fmt.Printf("removing %s returned error : %s" , filename, err.Error())
    }
}

そしてそれはうまくいった

  1. | 4なしでコードを実行しましたが、失敗しました
  2. 少し追加してコードを実行しました:
package main

import (
    "fmt"
    "os"
)

func main() {
    filename := "./myfile"
    if f ,err := os.OpenFile(filename,os.O_RDWR|os.O_CREATE|4, 0600);err!=nil{
        fmt.Printf("Open file %s error : %s" , filename, err.Error())
    } else if _,err:= f.WriteString("bla bla") ;err!=nil{
        fmt.Printf("writing to %s returned error : %s" , filename, err.Error())
    } else if err := os.Remove(filename);err!=nil{
        fmt.Printf("removing %s returned error : %s" , filename, err.Error())
    } else if secondF ,err := os.OpenFile(filename,os.O_RDWR|os.O_CREATE|4, 0600);err!=nil{
        fmt.Printf("reOpen file %s error : %s" , filename, err.Error())
    } else {
        secondF.Close()
    }
}

そしてもちろんそれはsecondFで失敗しました
しかし、それはWindowsで予想される動作であり、「テストをロードする」必要はありません。遅延.Removeには同時実行オプションがないため、既存のコードを損なうことなくこの、開発者のみに許可を与えることができると思います。たい

私たちがする必要があるすべて(太字):
syscal_windows.go:
sharemode:= uint32(FILE_SHARE_READ | FILE_SHARE_WRITE |(mode&FILE_SHARE_DELETE)

#32188の一部としてFILE_SHARE_DELETEを設定することを調査しましたが、設定してもos.Renameおよびio.ReadFile呼び出しからのエラー率が経験的に減少しないことがわかりました。 とにかく再試行ループがまだ必要でした。

私はこのフラグを設定することとの有意な観察可能な違いを示すテストを見ることに非常に興味があります。なぜなら、私はその意味を自分自身で本当に理解していないからです。

@bcmillsは、 https://github.com/olljanat/go/commit/3828f1a5d0ebb69b4c459d5243799ded36ac1ee8からこのユニット/回帰テストを見つけることができますThe process cannot access the file because it is being used by another process.に失敗し、 FILE_SHARE_DELETE後に動作を開始します。

ノート! Windowsでは、ログexample.log.1ファイルをランダム/一意の名前に移動するステップが必要です。これは、 example.log名前をexample.log.1に変更するとAccess is denied.エラーが発生しないためです。 FILE_SHARE_DELETEフラグが有効になっている場合でも。 これは、開発者が処理する必要のあるプラットフォーム固有のものです。

/ cc @jhowardmsft @ jterry75 @jstarks @ddebroy FYI; これにも興味があるかもしれません。

@ kevpar-これも回避しているので

スレッドに参加している人が理由を声に出して(たとえば、既存のコードが壊れているなど)これをデフォルトにすることができる場合は、os.OpenFile()のWindowsのみのフラグを介して利用できるので、パイプアップしてください!

@networkimprov IMOログローテーションの例のように、開発者は他のWindows固有のものも処理する必要があるため、Windowsのみのフラグで処理する必要があります。

ただし、マイクロソフトの従業員数名がディスカッションに招待されたことがわかるので、彼らがこれとは異なることをしているのかどうかを確認するのは興味深いことです。

誰かがこれに対する新しいソリューションを開発し始めましたか? ボランティアはいますか?

@olljanat
フラグとしての修正の場合、修正はワンライナーです。以下をお読みください。
https://github.com/golang/go/issues/32088#issuecomment -500562223

ここでは、Windowsチームの視点を表現しようとします...オプションなしで、常にFILE_SHARE_DELETEを設定するだけです。 一般に、Windowsとの移植を容易にするために、Linuxとの一貫した動作を優先します。また、Windowsユーザーまたはソフトウェアがデフォルトの!FILE_SHARE_DELETE動作を優先して、これがルールの例外となる理由がわかりません。

@mattnのコメントとは対照的に、

つまり、最新バージョンのWindowsでmattnのテストプログラムとdel、dirなどのシーケンスを実行すると、テストプログラムが終了した後ではなく、ファイルを削除した直後にファイルが名前空間から消えます。 Linuxと同じように。

そのため、appcompatのリスクが非常に大きいWindows自体でも、Windows以外のソフトウェアのWindowsへの移植を容易にするために小さな変更を加えています。 Goにも同じことをすることを強くお勧めします。

@jstarks 、私を立証してくれてありがとう。 私はその位置のためにこのスレッドで大量の熱を取りました。

Windowsの展開が現在の文書化されていない動作に依存している場合に備えて、fsdを無効にするグローバルオプションをお勧めします。

@ianlancetaylor同意しますか?

CreateFileのデフォルトの動作では、開いているファイルを削除できないため、デフォルトの動作を維持する必要があると思います。 誰かが元の動作を望んでいる可能性があります。 たとえば、ファイルを削除できない方法を使用してアプリケーションが機能していることを確認しているプログラマーがいる場合があります。

Pythonの動作は、Goの現在の動作と同じです。 そして、Pythonが開いているファイルを削除できない動作を変更する計画があるとは聞いたことがありません。

私はこの問題について真剣な意見を述べるのに時間をかけていませんが、グローバルな選択肢は良い選択ではないと思います。 現在の動作を維持する(そしておそらく他の動作を選択するためのフラグを追加する)か、動作を変更する(そしておそらく元の現在の動作を選択するためのフラグを追加する)必要があります。

デフォルトの動作を変更するかどうかについては、動作を変更すると、UnixおよびWindowsシステムで実行するように記述されたGoプログラムが修正される可能性が高いのか、それとも動作を変更する可能性が高いのかを尋ねる必要があると思います。 Windowsシステムで実行するように作成されたGoプログラムを中断します。 その答えはわかりません。

@mattnが示唆しているように、他の言語が何をしているのかを尋ねる価値もあります。

これがデフォルトになり、Windowsの展開がそれに依存している場合は、os.OpenFile()フラグと、移行目的で元に戻すグローバルオプションの両方が必要になると思います。

最後に確認したように、IPv6を無効にするグローバルオプションがあります。

影響を発見する1つの方法は、それをチップで変更し(そしてブログで発表しますか?)、報告された内容を確認することです。

PythonとErlangは両方とも問題のテキストで言及されています:
Erlangは6年以上前にデフォルトにしました:erlang / otp @ 0e02f48
Pythonは、MSVCランタイムの制限により、望んでいましたができませんでした: https

ここでは、Windowsチームの視点を表現しようとします...オプションなしで、常にFILE_SHARE_DELETEを設定するだけです。 一般に、Windowsとの移植を容易にするために、Linuxとの一貫した動作を優先します。また、Windowsユーザーまたはソフトウェアがデフォルトの!FILE_SHARE_DELETE動作を優先して、これがルールの例外となる理由がわかりません。

最新バージョンのWindowsでは、DeleteFile(NTFS上)を更新して「POSIX」削除を実行しました。この場合、ファイルの開いているすべてのハンドルが閉じられるのを待つのではなく、ファイルが名前空間からすぐに削除されます。 それでもFILE_SHARE_DELETEを尊重しますが、それ以外の点ではPOSIXunlinkのように動作します。 この機能はWSLに追加され、Windowsソフトウェアにもデフォルトで使用する価値があると考えられていました。

非常に興味深い詳細である@jstarks 。 これは、Microsoftが古いOSバージョンにバックポートすることも期待できるものですか? 特に、最新のWindows Server 2019については、リリースされたばかりのLTSCバージョンで、今後も長くサポートされると思います。

Microsoftがそうしているように、GoでもデフォルトでFILE_SHARE_DELETEを有効にしたいと思います。

Java、C#、および私が知っている他のすべての主要言語は、デフォルトのOS動作を採用しています。 私の意見では、デフォルトの動作はOSの実装者に任せるべきです。 Windowsが本当にファイルを開くことに関してPOSIXアプローチを採用したい場合は、そのリスクを冒して、DeleteFileの場合と同じようにする必要があります。

Goライブラリを使用すると、共有削除ファイルを作成/開くことができるはずです。 ファイルモードを扱う際の実用的な例を1つ挙げることができます。 HadoopをWindowsに移植する場合、共有削除ファイルのハンドルまたはストリームを取得するために、JNIで特別なメソッドを作成するために特別な努力を払う必要があります。

https://github.com/apache/hadoop/search?q=getShareDeleteFileInputStream&unscoped_q=getShareDeleteFileInputStream

@jstarks

言うとき

最新バージョンのWindowsでは、DeleteFile(NTFS上)を更新して「POSIX」削除を実行しました。この場合、ファイルは名前空間からすぐに削除されます。

開いているファイルがfdとしてのみ残るということですか、以下のコードは次のとおりです。

package main

import (
    "fmt"
    "os"
)

func main() {
    filename := "./myfile"
    if f ,err := os.OpenFile(filename,os.O_RDWR|os.O_CREATE|4, 0600);err!=nil{
        fmt.Printf("Open file %s error : %s" , filename, err.Error())
    } else if _,err:= f.WriteString("bla bla") ;err!=nil{
        fmt.Printf("writing to %s returned error : %s" , filename, err.Error())
    } else if err := os.Remove(filename);err!=nil{
        fmt.Printf("removing %s returned error : %s" , filename, err.Error())
    } else if secondF ,err := os.OpenFile(filename,os.O_RDWR|os.O_CREATE|4, 0600);err!=nil{
        fmt.Printf("reOpen file %s error : %s" , filename, err.Error())
    } else {
        secondF.Close()
    }
}

で失敗しません

} else if secondF ,err := os.OpenFile(filename,os.O_RDWR|os.O_CREATE|4, 0600);err!=nil{

提案された修正を適用した後?

もしそうなら、それは実際にクールです!

デフォルトの動作を変更するかどうかについては、動作を変更すると、UnixおよびWindowsシステムで実行するように記述されたGoプログラムが修正される可能性が高いのか、それとも動作を変更する可能性が高いのかを尋ねる必要があると思います。 Windowsシステムで実行するように作成されたGoプログラムを中断します。 その答えはわかりません。

WindowsでUnixプログラムを実行しているproblem to be fixedはありません。 Windowsは、開いているファイルの削除をUnixとは異なる方法で処理します。 これは、ファイル名の大文字と小文字の区別に似ています。Unixでは大文字と小文字が区別され、Windowsでは大文字と小文字が区別されません。 両方の違いについて私たちにできることは何もありません。

そして、ジョンはマイクロソフトが物事を変えようとしていることを保証しているが、私は変化がここに来るまで待つだろう。 そして、多くのWindowsユーザーはまだWindows7と他のすべてのWindowsバージョンを使用しています。 ジョンもWindows7を更新しますか?

マイクロソフトに変更を処理させるべきだと思います。 Goユーザーを修正しても、Go以外の開発者はまだ存在します。 したがって、Microsoftはこれに関係なく対処する必要があります。

また、FILE_SHARE_DELETEには独自のフィドルがあります。 たとえば、 https://bugs.python.org/issue15244を読んで

実際、これはUnixとまったく同じセマンティクスではありません。

ファイルを削除した後は、ハンドルを閉じるまで、同じ名前のファイルを作成したり、そのファイルを含むディレクトリを削除したりすることはできません。

ただし、ファイルを削除する前に、ファイルを別の場所(ルートディレクトリなど)に移動することで、この問題を回避できます。

おそらく、私たちが知らないそのようなものは他にもたくさんあります。 FILE_SHARE_DELETEはまったく使用していません。 Go libsをデフォルトでFILE_SHARE_DELETEを使用するように変更し、ユーザーが壊れたプログラムについて文句を言うのを待ちますか?

@mattnが示唆しているように、他の言語が何をしているのかを尋ねる価値もあります。

MingwCのみをチェックしました-Goと同じように動作します。 ファイルをfopenで開くと、fcloseが呼び出されるまで削除できません。 マイクロソフトの開発ツール(C、C ++、C#、VBなど)のいずれかが異なっているとしたら、私は驚きます。

そして、そのようなデフォルトの動作をどのように変更できるかわかりません。 一部のプログラムではファイルが特別に開かれている可能性があるため、他のプログラムで削除することはできません。 私の見解では、これはWindowsで有効なアプローチです。 今後もサポートしていきます。

アレックス

7年前に追加された、私は信じています:
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858

これまでのところ、現在の文書化されていないWindowsの動作に依存するGoアプリのケースを投稿した人は誰もいません。 しかし、それが原因でWindowsで失敗したGoアプリのケースが少なくとも4つあると聞いています。

上で述べたように、os.Remove()の後にファイル名を再利用するための簡単な修正があります。これを文書化する必要があります。
os.Rename(path, unique_path); os.Remove(unique_path)

これまでのところ、現在の文書化されていないWindowsの動作に依存するGoアプリのケースを投稿した人は誰もいません。

これらの人々は、Goのバグを報告できることすら知りません。

何かが彼らに影響を与えることを期待して、これらのユーザーが変更についてgoissueリストを監視することを期待するべきではありません。 彼らには生きる人生があります。 彼らが使用するツールを壊さないようにするのは私たちの責任です。

アレックス

アレックス、あなたが説得されていないのは明らかです。 次に、Goでポータブルコードを記述できるように、2つの可能なパスがあります。

  1. Openへの新しいオプトインフラグ。これは、WindowsではFILE_SHARE_DELETEを意味し、他のOSでは無視されます。 次に、POSIXセマンティクスを期待するコードを作成する人は、このフラグをどこにでも渡すことをお勧めします。 また、
  2. 新しいGoパッケージposix (どこかのリポジトリ内)に1つの関数Open 、CreateFileをFILE_SHARE_DELETEでラップしますが、それ以外の場合はos.Openとして動作します。 Windows以外のプラットフォームでは、 os.Open直接呼び出すだけです。 次に、POSIXセマンティクスを期待するコードを作成する人は、どこでもos.Openではなくposix.Openを使用することをお勧めします。

これらのアプローチはどちらも、Linux開発者がWindowsでPOSIXの動作を維持するために追加のアクションを実行する必要がありますが、少なくともこれらは、1回限りのCreateFileラッパーを作成するよりも優れています(containerdに対して行ったように)。

アレックス、あなたが説得されていないのは明らかです。

私は説得されていません。 しかし、あなたは私を説得する必要はありません、あなたは囲碁チームを説得する必要があります。 彼らが決定を下します。

  1. Openへの新しいオプトインフラグ。これは、WindowsではFILE_SHARE_DELETEを意味し、他のOSでは無視されます。 次に、POSIXセマンティクスを期待するコードを作成する人は、このフラグをどこにでも渡すことをお勧めします。 また、

私もこのオプションが好きではありません。

まず第一に、osパッケージはすべてのOSで利用可能な機能を提供することになっています。 OS固有のものはすべて、syscall、golang.org / x / sys / windows、またはその他の好きなパッケージに入れる必要があります。

第二に、私が望まない場合でも、プログラムにFILE_SHARE_DELETEファイルタイプが含まれることになるのではないかと心配しています。 FILE_SHARE_DELETEファイルモードを使用することを決定した外部パッケージをインポートするとします。 私のコードの一部がFILE_SHARE_DELETE使用していることをどうやって知ることができますか? パッケージのソースコードをgrepする必要があります。 または、パッケージのドキュメントでユーザーに警告する必要があります。 一部のパッケージ作成者がFILE_SHARE_DELETE syscall.CreateFileを直接使用した場合にも、これが発生する可能性があると思います。 しかし、 FILE_SHARE_DELETEがosパッケージに恵まれている場合、これはより一般的だと思います。 Windowsについて何も知らないLinuxユーザーは、デフォルトでこのフラグを使用して、LinuxプログラムをWindowsに「簡単に」移植できると思います。

第三に、 FILE_SHARE_DELETEフラグを文書化する必要があります。 POSIXとだけ言うことはできません。 フラグが何をするかを説明する必要があります。 簡単に言えば。 また覚えておいてください:

実際、これはUnixとまったく同じセマンティクスではありません。

ファイルを削除した後は、ハンドルを閉じるまで、同じ名前のファイルを作成したり、そのファイルを含むディレクトリを削除したりすることはできません。

ただし、ファイルを削除する前に、ファイルを別の場所(ルートディレクトリなど)に移動することで、この問題を回避できます。

https://github.com/golang/go/issues/32088#issuecomment-504321027から。 それも文書化する必要があります。 そして、 FILE_SHARE_DELETEフラグがもたらす他のすべての機能。

第4に、 FILE_SHARE_DELETE新しいテストを追加する必要があります。 これらのテストは何でしょうか? 一部の機能をテストするのを忘れた場合、後でFILE_SHARE_DELETEフラグを使用してその機能を実現できないことがわかった場合はどうなりますか? FILE_SHARE_DELETEフラグが気に入らない場合は、削除できません。 それが入ると、それはとどまります。 そして、私たちは将来それをサポートしなければなりません。

2. CreateFileをFILE_SHARE_DELETEでラップする1つの関数Open含む新しいGoパッケージposix (どこかのリポジトリ内)。それ以外の場合はos.Openとして動作します。

それがどのように可能かわかりません。 ただし、可能であれば、自分でパッケージを作成できるはずです。 たとえば、github.com / jstarks / posixのように。 番号? golang.org/x/sys/windows/posixなどでパッケージを追加できますが、あまり違いはありません。

アレックス

osパッケージは、あらゆるOSで利用可能な機能を提供することになっています。
FILE_SHARE_DELETEフラグを文書化する必要があります。 POSIXとだけ言うことはできません...
FILE_SHARE_DELETEの新しいテストを追加する必要があります。 これらのテストは何でしょうか?

これらはすべて上記で対処されました。

FILE_SHARE_DELETEファイルモードを使用することを決定した外部パッケージをインポートした場合。 どうすればわかりますか

これは、デフォルトにし、無効にするためのグローバルフラグを提供するためのもう1つの引数です。

影響を受けるWindowsアプリを探すためにgolang-nutsに投稿しました。 表示を維持するために、数日ごとにpingを実行します。 それがあまり表面化しない場合は、1.14のチップのデフォルトにすることで問題が明確になるはずです。
https://groups.google.com/d/topic/golang-nuts/8BiP_mPoCd4/discussion

これは、デフォルトにし、無効にするためのグローバルフラグを提供するためのもう1つの引数です。

ところで。 そのフラグはLinuxで何をすべきですか? Afaiu実際には、他のプロセスで削除できない方法でLinuxでファイルを開く機能もありませんか? このhttps://gavv.github.io/articles/file-locks/#open-file-description-locks-fcntlに基づいて、最新バージョンのLinuxに実装できるはずです。

フラグ(例: var OpenWithout_FILE_SHARE_DELETE = false )は、syscall.Open()の動作に影響を与えるため、syscall_windows.goで定義されます。

Linux固有のフラグは別の名前になり、syscall_linux.goで定義されます。 オフハンドLinuxで開いているファイルの削除を防ぐ方法は、そのディレクトリのアクセス許可を介する以外にわかりません。 あなたが提供したリンクは、「助言的な」ロックについて説明しています。

@jstarksは、 FILE_SHARE_DELETEフラグの導入を検討する前に、実行中の実行可能ファイルを削除できないようにするための対処方法も決定する必要があります。 FILE_SHARE_DELETEフラグがここで役立つとは思いません。 それをできる? まだ実行中の実行可能ファイルの削除を実装できない場合、POSIX互換性を主張することはできません。 そして、 FILE_SHARE_DELETEフラグは半分の解決策のように見えます。

さらに、終了したプロセスの実行可能ファイルを削除できない場合があります。 詳細については、たとえば、#25965、#19491、および#32188を参照してください。 基本的に、シグナルが出るまでプロセスハンドルでWaitForSingleObjectを呼び出します。 ただし、実行可能ファイルを削除できることを保証するものではありません。 ファイルを削除する前に、5ミリ秒の待機時間を追加する必要がありました。 そしてそれでも常に機能するとは限りません-https //github.com/golang/go/issues/25965#issuecomment-482037476

また、この問題とは関係ありませんが、私があなたの注意を払っていることを考えると、多分あなたは私たちがwindows-armポートを復活させるのを手伝ってくれるでしょう。 詳細については、#32135を参照してください。 基本的に@ jordanrh1はWindows-armに移植されましたが、windows-armビルダーはもうありません。 そのため、ポートがまだ機能しているか壊れているかさえわかりません。 Builderを実行するのを手伝っていただければ、windows-amポートのサポートを継続して試みることができます。 そうしないと、ポートが削除される可能性があります-https://github.com/golang/go/wiki/PortingPolicy誰かがWindows 10 IotでGoプログラムを実行することに興味があるかどうかはわかりませんが、すでにこのサポートがあります。 ビルダーが機能していないという理由だけでそれを失うのは悲しいことです。

ありがとう。

アレックス

一時実行可能ファイルを削除するためのこの推奨ソリューションに注意してください: https

golang-nutsの投稿からの結果はありません。 新しいスレッドを開始しました:
https://groups.google.com/d/topic/golang-nuts/aRvSo3iKvJY/discussion

調査する必要があると私が考える少なくとも1つのそれほど仮想的ではない懸念は、ファイルロックとそれに依存するプログラムへの影響です。

すべてのファイルハンドルを開くFILE_SHARE_DELETE効果的の意味弱めるLockFileEx持つ任意のファイルので、POSIX勧告的ロックのそれにFILE_SHARE_DELETE指定されたロックであっても、削除することができますそのファイルの領域で保持されます。 1

もちろん、これはos.File.Fdから返された記述子を使用して行われたLockFileEx呼び出しにのみ適用されますが、これは最も人気のあるGoファイルロックライブラリ(現在、Dockerを含む33の既知のインポーターがあります)で行われいることです。 、gVisor、およびKubernetes)。 これはTerraformでも行われ、 LockFileEx他のGoコードのロードで使用され

私はまだ、この変更は、最良の場合(そして最悪の場合はCVEファクトリー)では不適切な試みのように思われると思います。 あいまいなメリットが、発生する可能性のある明確なコードの破損をどのように上回るかは、実際にはわかりません。

また、ほとんどのgolang-nutsユーザーは、この変更の影響を完全に理解するつもりはないと思います。 Goチームの許可があれば、golang-devへの投稿がディスカッションの生成に効果的である可能性があります(特にこれはコアの変更であるため)。 実際には、これはGoツールチェーンに技術的に影響します。Goツールチェーン前述の方法でLockFileExを使用します


1はい、Windowsのロックの強制の一部はPOSIXよりも強力であることはわかっていますが、ファイルを削除できる場合は、リソースの予約にロックファイルを使用するとウィンドウが表示されなくなります。

問題を開いたときにgolang-devに投稿しました。
https://groups.google.com/d/topic/golang-dev/R79TJAzsBfM/discussion

リストしたライブラリのいずれかがGOOS = windowsの動作に依存していますか? あなたは、この変更が既存のコードを壊すだろうと繰り返し主張しましたが、証拠を提供することはありませんでした。

センセーショナルな支持できない発言をするのはやめてください。 それは何も明らかにしていません、そして私はそれを不快に感じます。

問題を開いたときにgolang-devに投稿しました。 https://groups.google.com/d/topic/golang-dev/R79TJAzsBfM/discussion

そこにいる全員が自分の理由でアイデアを打ち切ったようです(すべてこのスレッドで前述したものと似ています)が、Go 1.14でこの変更を適用するためにアイデアが浮かんでいる場合は、そのフォローアップスレッドが役立つ場合があります(このディスカッションへのリマインダーリンク付き)。

リストしたライブラリのいずれかがGOOS = windowsの動作に依存していますか? あなたは、この変更が既存のコードを壊すだろうと繰り返し主張しましたが、証拠を提供することはありませんでした。

それには、確かに言うには、(多くのコードの)完全なセキュリティ評価が必要になります。 私は確かに、人々がこの旗の導入によって目に見えて変化する行動に依存しているという十分な証拠を提供したと思います。 そして、それはGitHubで公開されているコードです。

センセーショナルな支持できない発言をするのはやめてください。 それは何も明らかにしていません、そして私はそれを不快に感じます。

リンクとコードが十分にサポートされていない場合、あなたの本に何が含まれるかわかりません。 この変更によってコンパイルが停止するコードは見つかりませんが、動作が異なるコードは確かに見つかります。多くの人は、これらの動作の変更により、そのコードが「壊れている」と見なします。

私の異議を個人的に受け取らないでください。ここにいる全員(あなたを含む)は、言語を改善するために自分の意見を追加しようとしているだけだと思います。 コアチェンジを支持するつもりなら、健全な抵抗を期待するべきです。

上で説明したように、FILE_SHARE_DELETEを使用してファイルを削除しても、UNIXの機能と同じ機能はありません。 たとえば、この手順を試してください。

main.c

#include <windows.h>
#include <stdio.h>

int
main() {
  // ensure delete the file
  DeleteFile("test.txt");

  // create test.txt with FILE_SHARE_DELETE
  HANDLE h = CreateFile("test.txt",
      GENERIC_READ | GENERIC_WRITE,
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
      NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  puts("waiting 10sec, please run watch.exe on another cmd.exe");
  Sleep(10000);
  if (DeleteFile("test.txt")) {
    puts("deleted");
  }
  getchar();
  return 0;
}

watch.c

#include <stdio.h>
#include <shlwapi.h>

int
main() {
  // waiting test.txt will be removed.
  while (PathFileExists("test.txt")) {
    puts("watching...");
    Sleep(1000);
  }
  // now test.txt should not exists. So this is possible to create new file
  system("notepad test.txt");
  return 0;
}

Makefile

all : main.exe watch.exe

watch.exe : watch.c
    gcc -o watch.exe watch.c -lshlwapi

main.exe : main.c
    gcc -o main.exe main.c

最初にmain.exeを実行し、次にwatch.exeを実行してください。 UNIXの場合、unlink(2)はファイルをすぐに削除します。 したがって、ファイルが存在することを監視している別のプロセスは、同じファイル名で新しいファイルを作成することができます。 ただし、Windowsでは、Windowsはファイルをすぐに削除しないため、notepad.exeはエラーダイアログ「アクセス違反」を表示します。 これで何が起こるかはわかりません。 安全保障問題? 重要なファイルを紛失しましたか? すみません、わかりません。

@ havoc-io Windowsは、開いているファイルを閉じるまで削除せず、開いているファイルのロックのみを保持します。 したがって、ロックの使用は、os.Remove()の失敗への依存を意味するものではありません。 plsは、特にその障害に依存する例を提供します。

来週、golang-devスレッドにpingを実行します。 2人のpplがそれに答えました。 この問題を読んでいない人もいれば、例も提供していません。

センセーショナルで支持できない主張は「健康的な抵抗」ではありません。

@ havoc-io Windowsは、開いているファイルを閉じるまで削除せず、開いているファイルのロックのみを保持します。 したがって、ロックの使用は、os.Remove()の失敗への依存を意味するものではありません。 plsは、特にその障害に依存する例を提供します。

@ networkimprov2つの異なるタイプのロックを

ロックファイル( LockFileExロックされている)は通常、リソースを保護するために使用されます。 1つの例は、実行中のデーモンのインスタンスが1つだけであることを確認することです。 Windows Goデーモンがファイルを開いて、そのファイルに対してLockFileExを実行し、排他ロックを正常に取得した場合、そのファイルの下からロックファイルが削除されないと正しく想定される可能性があります。 ただし、 FILE_SHARE_DELETEがデフォルトで有効になっている場合、その仮定を行う既存のコードはすべて正しくありません。 追加のエージェントがそのロックファイルを削除した場合(たとえば、ユーザーが誤ってディレクトリを削除した場合)、新しいデーモンが起動することを決定したときに、元のデーモンがまだ実行されている可能性があります。

LOCKFILE_FAIL_IMMEDIATELYフラグが省略されている場合、ロックファイルを使用してコマンドをパイプライン処理することもできます。これにより、 LockFileExはロックの取得を順番に待機します。 ロックファイルが誤って削除された場合、パイプライン化されているはずの複数のコマンドが一度に実行される可能性があります。

センセーショナルで支持できない主張は「健康的な抵抗」ではありません。

このスレッドでは、センセーショナルな主張や支持できない主張をしている人は誰もいません。 私も含めて、あなたの提案に反対したすべての人は、コードによってサポートされた健全な議論を提供しました。 あなたの提案を支持して

plsはWinAPIドキュメントを確認します。 Windowsは、開いているファイルを閉じるまで削除せず、開いているファイルのLockFileEx()ロックのみを保持するため、ファイルを閉じたときに、削除に関係なくロックを保持できません。 削除後にロックされた(つまり開いた)ファイルを開こうとするとエラーが発生するため、2番目のロックを取得できませんでした。 ロックを待機しているスレッドはファイルを開いているので、削除によって中断されることはありません。

ドキュメントを読み間違えない限り、最後の2つのシナリオはサポートされていません。 そして、これはセンセーショナルでサポートされていませんでした。「この変更は、最悪の場合、CVEファクトリのようです。あいまいなメリットが、発生する明確なコードの破損をどのように上回るかはわかりません。」

@networkimprov問題は、リソースを制限するロックファイルが通常ファイルパスに基づいていることです。 基になるファイルオブジェクトは、ファイルが閉じられるまで削除されない場合がありますが、 DeleteFileを介して削除された場合、ファイルシステム1の前のパスからアクセスできなくなり、他のロックファイルを作成できるようになります。そのパスで、ロックファイルの目的を否定します。

センセーショナルなコメントではなかったと思います。コードが、もはや真実ではない特定のOS不変条件に依存している場合、このようなことがセキュリティの問題を引き起こす可能性があることは明らかです。 そして、ここでの利点はかなり曖昧に見え(何人かの人々がコメントしているように:これはWindowsの動作をPOSIXに合わせません)、破損はかなり明確に見えます(少なくとも特定のロック動作を壊します)。

1少なくとも、このコメントに基づいてテストする場合はそうです。

「最新バージョンのWindowsでは、(NTFS上の)DeleteFileを更新して、「POSIX」削除を実行しました。ファイルは名前空間からすぐに削除されます」 https://github.com/golang/go/issues/ 32088#issuecomment- 502850674。 そのバージョンのWindowsはリリースされていません。 どのようなテストを参照していますか?

メリットについては、スレッド全体を確認する必要があると思います。

そのバージョンのWindowsはリリースされていません。

@networkimprov Windows 10の場合はすでにそうだと思います。ファイルを開くときに、 FILE_SHARE_DELETECreateFileWに指定すると、 LockFileExロックされたファイルを削除できるようです。

DeleteFileW()は、そのシナリオではエラーを発生させません。 ファイルシステムからすぐに削除されたのに対し、ファイル/アプリを閉じるときに削除されたことをどのように知っていますか? ファイルを削除した後、ファイルを開いたり再作成したりしましたか?

実際には、これはGoツールチェーンに技術的に影響します。Goツールチェーンは、前述の方法でLockFileExを使用します。

goコマンド内では、POSIXセマンティクスが非常に必要であり、 FILE_SHARE_DELETEがないことに依存するべきではありません。 実際、私はcmd/go/internal/robustioパッケージを追加して、WindowsのファイルロックがPOSIXから逸脱する方法を_回避_しました。

@ianlancetaylor現在の(文書化されていない)Windows syscall.Open()の動作に依存するプロジェクトからのコメントを求めてgolang-nuts&-devに繰り返し投稿しました。 単一のユースケースは出現していません。

FILE_SHARE_DELETEを必要とするいくつかのユースケースが上に表示されているので(そしてMicrosoftが特にそれを使用するように要求したので)、プレリリース1.14でデフォルトでこのフラグを設定し、サイクル中に元の動作のユースケースがあるかどうかを再評価しましょう。

このスレッドの前半で提案したように、このフラグなしでopen()呼び出しが必要な人は、 CreateFileW()os.NewFile()を使用して自分自身をロールすることができます。

少なくともこのためのノブを公開できますか?

これは、ウィンドウにログローテーションを実装する場合に非常に便利です。

これは、ウィンドウにログローテーションを実装する場合に非常に便利です。

よくわかりませんが、FILE_SHARE_DELETEでは、ファイルのハンドルが閉じられていない場合、元のファイル名で新しいファイルを作成できないため、機能しないと思います。

@ Random-Liuは、Open()でfsdを有効にする方法、または無効にする方法を求めていますか?

@mattnそれは私のために働きます。 古いログファイルをos.Renameた後、元の名前で新しいログファイルを作成し、デーモンにログファイルを再度開くように通知できます。

@networkimprov FILE_SHARE_DELETEを有効にする方法が必要です。 現在、 syscall.CreateFile直接使用しています。

私のWindowsバージョン:

Major  Minor  Build  Revision
-----  -----  -----  --------
10     0      17763  0

@ Random-Liuしかし、UNIXの動作をシミュレートするためにこれを使用することはできないと思います。

https://github.com/golang/go/issues/32088#issuecomment -510345908

外部アプリケーションは元のファイル名にアクセスできません。

mattn、その点を繰り返し続ける必要はありません。 ハンドルを閉じる前にファイル名を再利用可能にする必要がある場合は、ファイルを削除する前にファイルの名前を変更する必要があることを(Windowsで)文書化する必要があることはすでに述べました。

アプリケーション自体で閉じる前にファイルの名前を変更している場合、ログローテーションを実行したいユーザーにはプロがいないように感じます。

@ianlancetaylor再試行: https

この問題をリリースブロッカーとしてマークしました。

@bcmills CLの投稿に興味がありますか?

この行: https
必要なもの: sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)

編集:ファイルが作成されることを確認するためのテスト。次に、ファイルを閉じる前に名前を変更しようとします。

すぐにこれが必要になるので、フリーズする前に問題が表面化する時間があります...

また、os.Remove()のドキュメントには、Windowsでは、ファイルを閉じる前にファイル名を再利用できるようにする必要がある場合は、開いているファイルの名前を削除してから削除する必要があることが記載されています。 Windowsは、開いているファイルを閉じるまで、ファイルの削除を完了しません。

最新バージョンのWindowsはhttps://github.com/golang/go/issues/32088#issuecomment-502850674を満たしているようです。 詳細については、 https: //github.com/papertrail/go-tail/pull/10#issuecomment-529460973を参照してください。 FILE_SHARE_DELETE共有モードでハンドルが開かれていれば、古いファイルへのハンドルが開いている場合でも、削除されたファイルの名前を再利用できるようになりました。 削除する前に名前を変更する必要はありません。 おそらく、GoでデフォルトでFILE_SHARE_DELETEを設定することは最終的に意味がありますか?

それが意図です。 CLを提出する人が必要です。 (私はそうしますが、CLAに署名することはできません。)
私の前の2つのコメントは、必要な変更について説明しています...

@jstarks @ jordanrh1 @thaJeztah

質問:
一貫した動作を得るにはどうすればよいですか?
プログラムが「最新のWindows」で実行されている場合、「保留中の削除」ファイルと同様の名前のファイルを作成できますが、古いバージョンではエラーが発生します。

これは実際には次のように言うかもしれません:「テスト/ qaに合格しましたが、ほとんどのターゲットでは機能しません」
将来的には「通常は機能しますが、機能しない場合もあります」(2年後の開発者は、「ファイルがすでに終了しています」というエラーが発生しても再現できなくなります)。

「古い」バージョンを特定する方法を理解し、それらのデフォルトの動作を変更するか、「保留中の削除がある」場合のエラーを改善する必要があります。どうすればよいかわかりません。
そして、私たちができない場合、どのように矛盾に対処しますか。

@guybrandは、前述のように、Windowsでは、「ファイルを閉じる前にファイル名を再利用できるようにする必要がある場合は、開いているファイルの名前を削除してから削除する必要がある」ことを文書化します。

これは、POSIXだけでなく、新旧のWindowsバージョンでも機能します。

これは別のアプローチのように聞こえます。「新しいWindowsやPOSIXでも、再利用する前に必ず名前を変更してください」
私はそれが好きです-それはどこでも成功するであろう一般的な慣行を支持します。

変更https://golang.org/cl/194957はこの問題に言及しています: syscall: allow FILE_SHARE_DELETE on syscall.Open on Windows

golang-nutsスレッドはここにあり、応答はありませんでした。 これは、 golang-nutsを読んで、この動作を知っている人が誰もそれに依存していないことを示しているようです。

golang-devスレッドはここここにあり、後者(より最近のスレッド)も異議を唱えませんでした。

@alexbrainman -2は、明らかに決定の欠如に基づいてCLを実装するようにしたので、明確な方向性を得ることができるように、この問題のラベルをNeedsDecision変更しました。

個人的には、決定は明確に思えます。このスレッドのMicrosoftの人々は、デフォルトの動作をFILE_SHARE_DELETE (https://github.com/golang/go/issues/32088)を使用するように変更することをサポートしています。 #issuecomment-502850674)、そしてgolang-nuts誰もどちらの方法でも気にしないように思われるので、私たちはそれをすべきだと思います。

このスレッドで私が見た反対意見は、2つのカテゴリに分類されるようです。

  1. @alexbrainman (https://github.com/golang/go/issues/32088#issuecomment-504321027)と@mattn (https://github.com/golang/go/issues/32088#issuecomment-494417146)の懸念動作はまだPOSIXと一致しないため、POSIXセマンティクスを期待しているユーザーは混乱する可能性があるようです。 (しかし、WindowsのOpenがPOSIXセマンティクスを提供しないことはすでに事実であるため、私には、その懸念は提案された変更とは無関係のように思われます。)

  2. @ havoc-ioの懸念(https://github.com/golang/go/issues/32088#issuecomment-510314947)は、 FILE_SHARE_DELETEが、Windowsで実行しているときにファイルロックパッケージによって提供される不変条件を減らすことであるようです。 。 しかし、例として保持されている具体的なパッケージ( github.com/gofrs/flock )は、「各プラットフォームでロック動作が同じであることが保証されていない」ことを明示的に示しているため、その議論は少し混乱しているようです。具体的な例はどれも、ロックとファイル削除の間のWindows固有の相互作用に依存しているようには見えません。

https://github.com/golang/go/issues/32088#issuecomment -498690757のコメントは、私を興味をそそります。

https://codereview.appspot.com/8203043/で提案されているように、 @ ianlancetaylorがツールチェーン全体の動作を変更することに同意します。 ファイルハンドルをロックファイルとして使用するときにこの提案を思い浮かぶ1つの差し迫った問題。 この動作を使用します。

@maruel 、プログラムがファイルロックとファイルの削除または名前変更の間のWindows固有の相互作用にどのように依存しているかについてもう少し詳しく教えてください。

Chromeインフラでは、ロックメカニズムとしてファイルプレゼンスを使用しています。 このユースケースについてはあまり心配しないでください。 実際には、私が理解していることから、O_CREATE + FILE_FLAG_DELETE_ON_CLOSEセマンティクスはこのニーズに十分であり、これはGlobal\\名前空間のミューテックスに置き換えることができます。これはPythonコードベースですでに行っています。

この解決策は言及されていません:

path := "delete-after-open"
fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
if err != nil { ... }
// defer statements are executed in LIFO
defer os.Remove(path)            // or defer os.Rename(path, path+"2")
if err != nil { ... }
defer fd.Close()

これには2つの欠点があります。

  • Close()はOpen()の直後ではありません
  • エラーコードの回復はより複雑です

@iwdgoこのソリューションは、単一プロセスで使用するためのものです。 上記の説明のほとんどは、複数のプロセスからの同時アクセスに関するものです。 それが旗を重要なものにしているのです。

この問題の@bcmillsの要約を補足するために、他の3つのクロスプラットフォーム開発ツールを調べました。

Mingw-w64は7年前にデフォルトにしました:
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858

Erlangは6年以上前にデフォルトにしました:erlang / otp @ 0e02f48

検討時のMSVCランタイムの制限により、 Pythonはそれを採用できませんでした: https

2番目のgolang-nutsスレッドにも異論はありませんでした。
https://groups.google.com/d/topic/golang-nuts/aRvSo3iKvJY/discussion

Open()のデフォルトでFILE_SHARE_DELETEを作成しますか? 私たちの議論はそれであってはなりません。

  1. @alexbrainman#32088(コメント) )の懸念は...動作がまだPOSIXと一致しないため...

私の主な関心事は、人々のプログラムを破ることです。 @minuxhttps://codereview.appspot.com/8203043/#msg10で言ったことと同様です

別の理由:ウィンドウを使用している人々は、名前を変更できないこと、または
開いているファイルを削除するのが一般的であり、すでにその事実に慣れています。

ここでは、Go1.0プログラムの動作を黙って変更することはできません。
実際にそれに依存しています。

ここでGo1.0の互換性の約束はどうなりましたか? https://golang.org/doc/go1compatのどこに、go-nutsで意図を発表した場合、開いているファイルの動作を変更してもよいと書かれていますか?

Go 2を導入したら、この動作を変更できるかもしれません。 モジュールを使用して、既存のコードが破損しないように保護できますか? それがモジュールが設計されたものであると仮定するのは間違っていますか?

個人的には、決定は明確に思えます。このスレッドのMicrosoftの人々が、デフォルトの動作をFILE_SHARE_DELETEを使用するように変更することをサポートしていることを考えると...

マイクロソフトがこのテーマの権威である場合は、マイクロソフト開発ツールの機能を確認する必要があります。

このプログラムは、 @ mattnhttps://github.com/golang/go/issues/32088#issuecommentに投稿したものと同様のhttps://play.golang.org/p/4ZPmV6Df3SDを使用しました -493753714最近のCコンパイラでビルドしました。

Microsoft(R)C / C ++最適化コンパイラバージョン19.16.27030.1for x64
Copyright(C)MicrosoftCorporation。 全著作権所有。

また、プログラムを実行すると、プログラムが存在するまでC:\Users\Alex\AppData\Local\Temp\a.txtファイルを削除できません

最近使用した同様のC#プログラムhttps://play.golang.org/p/SuM2iWYpZirと同じ

Microsoft(R)Visual C#コンパイラバージョン2.10.0.0(b9fb1610)
Copyright(C)MicrosoftCorporation。 全著作権所有。

同様に、このCプログラムhttps://play.golang.org/p/6HgxePzEW_Wは最近のMingwで構築されました

gcc(x86_64-win32-seh-rev0、MinGW-W64プロジェクトによってビルド)7.3.0
Copyright(C)2017 Free Software Foundation、Inc。

同じ効果があります。 したがって、これらのツールのどちらにも、デフォルトでFILE_SHARE_DELETEフラグが設定されていません。

そして個人的には、その変更によるメリットは見当たりません。 これには、私のGoの貢献も含まれます。 プロセスの実行中は、実行可能ファイルを削除することはできません。 現在のディレクトリが設定されているプロセスがある場合でも、ディレクトリの削除で問題が発生します。

アレックス

Open()のデフォルトでFILE_SHARE_DELETEを作成しますか?

目的は、os.OpenがデフォルトでFILE_SHARE_DELETEを使用するようにすることです。 このhttps://golang.org/cl/194957が計画です。

アレックス

私の主な関心事は人々のプログラムを破ることです

これまでのところ、Goで開いたファイルの名前を変更/削除しようとするとエラーが発生することに依存しているため、壊れてしまうプログラムの1つの例を指摘することはできません。 ただし、現在の動作がWindows上のGoアプリを壊すという文書化されたケースがあります。

ここでGo1.0の互換性の約束はどうなりましたか?

os.Rename()および.Remove()のGoドキュメントには、このWindows機能について、他のWindows固有の動作https

gcc(x86_64-win32-seh-rev0、MinGW-W64プロジェクトによってビルド)7.3.0

gccで使用しているC-libは誰ですか?おそらくMicrosoftのものですか? Mingw-w64はv6.0であり、FILE_SHARE_VALID_FLAGSにFILE_SHARE_DELETEが含まれています。

Pythonに関して上記で説明したように、過去には、MSVCランタイムは、不明な理由で特定のCreateFile()フラグの組み合わせを許可していませんでした。 それがまだ当てはまるかどうかはわかりませんが、MSがC-lib fopen()を変更していない理由かもしれません。

その変更によるメリットは見当たりません。

利点は、何人かの人々によって、上で繰り返し説明されてきました。 特に、開いているファイルでos.Rename()を許可します。

議論がなかなか収まらないため、提案としてマークする。

これまでの問題と議論を要約しようとすると:

  • Unixシステムでは、1つのプロセスがファイルを開くと、2番目のプロセスがそのファイルを削除できます。 元のプロセスは、読み取りと書き込みを含め、ファイルを使用し続けることができ(名前はもうありません)、成功します。 最後に開いていた参照がなくなるまで、ファイルの内容はディスクから削除されません。

  • Windowsシステムでは、デフォルトでは、1つのプロセスがファイルを開くと、2番目のプロセスはそのファイルを削除できません。 ファイルを削除できるのは、他のプロセスでファイルが開かれていない場合のみです。 FILE_SHARE_DELETEフラグは、この動作を変更します。ファイルへの開いているすべての参照がFILE_SHARE_DELETEで開かれた場合、別のプロセスがDeleteFileを呼び出すことができ、失敗する代わりに成功します。

FILE_SHARE_DELETEを使用したWindowsの動作は、Unixとはまだ微妙に異なるようです。 MSDNページには次のように書かれています。

通常のI / O用に他のハンドルが開いているファイルまたはメモリマップファイルとしてアプリケーションがファイルを削除しようとすると、DeleteFile関数は失敗します(他のハンドルが開かれたときにFILE_SHARE_DELETEが指定されている必要があります)。

DeleteFile関数は、閉じるときにファイルに削除のマークを付けます。 したがって、ファイルの最後のハンドルが閉じられるまで、ファイルの削除は行われません。 その後のCreateFileの呼び出しでファイルを開くと、ERROR_ACCESS_DENIEDで失敗します。

つまり、開いている参照がなくなるまで、ファイル_name_はファイルシステムから削除されません。 これは、開いている参照が閉じられる前に名前が消えるUnixとは異なります。 したがって、同じ名前のファイルを再作成する準備としてファイルを削除する場合、それはUnixで機能しますが、Windowsでは機能しません。FILE_SHARE_DELETEでも機能します。

元々の提案は、syscall.Openを変更して、このフラグを常に設定することでした。 syscallがカーネルインターフェースに近いと想定されていることを考えると、そのフラグを追加することは場違いのようです。 ただし、代わりに、os.Open /os.OpenFile中に設定する必要があるかどうかを検討できます。

@alexbrainmanは、UnixとWindowsは異なり、このフラグを設定しても異なるままであると主張しているため、ユーザーの背後でこの変更を行って問題を混乱させないでください。

Microsoftの@jstarksによると、MicrosoftはFILE_SHARE_DELETEを自動的に設定することを望んでおり、Windowsの「最新バージョン」(どちらがリリースされているか、またはリリースされているかは不明)によってDeleteFileが変更され、DeleteFileから戻ったときに名前のUnixセマンティクスが消えます。 したがって、これを実行した場合、その最新のWindowsでは、UnixとWindowsで実際に同じ動作が得られます。

@networkimprovは、変更を加える必要があり、破損するプログラムの実際の例はないと主張しています。

@alexbrainmanはまだ納得していないようです。

@mattnはまた、フラグを自動的に設定しないように提案しています。これは、(少なくともすべての人が最新のWindowsを使用するまで)フラグがUnixとは異なるためです。

全体として、パッケージosはポータブルAPIを提供しようとしていますが、特にWindowsの変更では、フラグを自動的に設定すると役立つようです。 os.Openに自動的に設定される他のフラグ、特にO_CLOEXEC(close-on-exec)は、ユーザーの動作をそれほど驚くべきものにしないようにするためのものです。 os.Openでこのフラグを設定したためにWindowsで壊れたプログラムは、Unixでも必ず壊れますよね?

特に、Microsoftの少なくとも1人のエンジニアが設定する必要があると言っているので、パッケージosで設定しても問題ないようです。 フラグを設定したくない人は、パッケージsyscallにフォールバックすることができます。

@alexbrainman@ mattnos.Openにフラグを設定したとします。 一部のプログラムの方がうまく機能することはわかっています。 壊れてしまう実際の(または可能性のある)プログラムの具体例はありますか? それらのプログラムはUnixですでに壊れていませんか? ありがとう。

@rsc 、ご入力いただきありがとう

要約を明確にするために、プロセスの境界は要因ではありません。 効果は単一のプロセス内で同じです。 また、フラグはファイルの名前変更にも影響します。 フラグを使用すると、ファイルの削除とは異なり、すぐに完了します。 (これにより、削除直後のファイル名の可用性の回避策が可能になります。)

@networkimprovに感謝します。 はい、プロセスの境界は厳密には関係がないことを理解しています。 状況を説明しやすくするだけです。

タイトルを変更し、問題のテキストのアクションプランを改訂しました。

a)os.Create / Open / OpenFile()は、Windowsでは常にfile_share_deleteを有効にする必要があります。
b)Windowsのsyscall.Open()は、file_share_deleteを有効にするフラグを受け入れる必要があります。
c)Windowsのsyscallは、新しいフラグの定数をエクスポートする必要があります。

os.Remove()のドキュメントでは、Windowsで開いているファイルを削除した後にファイル名を再利用するには、 os.Rename(path, unique_path); os.Remove(unique_path)実行する必要があることにも注意してください。

現在のGoのos.Open動作を使用して排他的処理を実装しているユーザーがいる可能性は否定できません。

ただし、現在の動作がWindows上のGoアプリを壊すという文書化されたケースがあります。

これは何? 発行番号は何ですか? そして、 breakという言葉はどういう意味ですか? 彼らはいつ壊れましたか?

gccで使用しているC-libは誰ですか?おそらくMicrosoftのものですか? Mingw-w64はv6.0であり、FILE_SHARE_VALID_FLAGSにFILE_SHARE_DELETEが含まれています。

知らない。 インターネットからx86_64-7.3.0-release-win32-seh-rt_v5-rev0.7zファイルをダウンロードしました。 あなたはそれをグーグルすることができます。 バージョン7.3です。

その変更によるメリットは見当たりません。

利点は上で繰り返し説明されています...

personallyという単語を見積もりから除外しました。 私は自分のニーズについて話します。 そして、それは私のGoプロジェクトの貢献を含みます。

これまでの問題と議論を要約しようとすると:

Windows上のMicrosoft開発者ツールがデフォルトでこのフラグを提供しないとは言わなかった。 私のコメントを参照してくださいhttps://github.com/golang/go/issues/32088#issuecomment-531713652

Go1の互換性に関する私のコメントも無視しました

ここでGo1.0の互換性の約束はどうなりましたか? https://golang.org/doc/go1compatのどこに、go-nutsで意図を発表した場合、開いているファイルの動作を変更してもよいと書かれていますか?

この変更は、Go 1.0の互換性の約束にどのように適合しますか?

全体として、パッケージosはポータブルAPIを提供しようとしていますが、特にWindowsの変更では、フラグを自動的に設定すると役立つようです。

この変更は、UnixソフトウェアをWindowsに移植することを目的としていることに同意します。

残念ながら、Windowsユーザーを犠牲にして。 この変更は、Windowsの他のプログラムやツールがこのように機能しないため、Windowsユーザーと開発者にとっては驚くべきことです。 たぶん将来ですが、まだです。

そして、UnixソフトウェアをWindowsに移植している人々にとってさえ、この変更は私見ではかなり悪い仕事をしています。 コメントのWe still won't be able to delete executable files while process is running. We will still have problems deleting directories if any process have their current directories set there.部分をくださいhttps://github.com/golang/go/issues/32088#issuecomment -531713562

os.Openに自動的に設定される他のフラグ、特にO_CLOEXEC(close-on-exec)は、ユーザーの動作をそれほど驚くべきものにしないようにするためのものです。 os.Openでこのフラグを設定したためにWindowsで壊れたプログラムは、Unixでも必ず壊れますよね?

ここであなたが何を意味するのかわかりません。 WindowsのO_CLOEXECは、ファイルハンドルが子プロセスにエスケープされるのを防ぐことを意味します。 また、O_CLOEXECは常にWindowsのos.Openで設定されます。 したがって、os.Openで開かれたファイルは、Windowsの子プロセスからアクセスできません。 だから何? それは合理的に聞こえます。

それらのプログラムはUnixですでに壊れていませんか?

私はあなたの質問を理解していません。

壊れてしまう実際の(または可能性のある)プログラムの具体例はありますか?

私がそのようなプログラムを持っているかどうかはわかりません。 言うことは不可能です。 私が書いたすべてのプログラムを覚えているわけではありません。 彼らが何をしているのか覚えていません。

しかし、Windowsサービスが事前定義されたファイルを開き、たとえばc:\mysvc.txtとすると、それを開いたままにしておくことを想像できます。 サービスが動作していることを確認し、必要に応じてサービスを再起動する別のプログラムを想像してみてください。 この別のプログラムは、 c:\mysvc.txtを削除しようとするだけで、ファイルがなくなった場合、サービスプロセスが停止していると安全に想定できます。

また、一部のプログラムでは、ユーザーがファイルを削除または移動できないようにする必要があるかもしれません-https://stackoverflow.com/questions/11318663/prevent-a-user-from-deleting-moving-or-renaming-a-file

アレックス

Go onWindowsのデータポイントの再展開は次のとおりです。
Win8は7年前に出荷されました。
Win8 +に影響を与えるこのGoのバグは、わずか5か月前に報告されました:#31528

@rsc Goで開いたファイルの名前を変更/削除しようとしたときにエラーが発生することを予期しているプログラムは、Unixでは壊れています。 たとえば、Alexのc:\mysvc.txtシナリオ。

Windowsで開発し、Linux / etcにデプロイする場合(またはその逆の場合)、これがバグの原因となる可能性があります。

私たちのユースケースは、ファイルにログを記録するロガーがあることです。
ログファイルはローテーションをサポートします(たとえば、ログファイルが> maxSizeの場合は次の書き込み、次にローテーションします)。
また、これらのファイルを開いて、読み取っている間も開いたままにするリーダーもあります。

FILE_SHARE_DELETEを設定する機能さえなければ、アクティブなリーダーがある間はローテーション(名前の変更や削除)を実行できません。

@mattn

現在のGoのos.Open動作を使用して排他的処理を実装しているユーザーがいる可能性は否定できません。

それはそうかもしれませんが、とにかくそのようなコードはUnixでは正しくありません。 パッケージOSは、ポータブルインターフェイスを目的としています。 また、可能性よりも_実際の_プログラムに関心があります。

@alexbrainman

Windows上のMicrosoft開発者ツールがデフォルトでこのフラグを提供しないとは言わなかった。 私のコメントを見る#32088(コメント)

はい、でもそれはCです。 私たちは囲碁について話している。 一般的にCAPIを模倣していません。 私たちは、パッケージosでほとんど移植可能な抽象化を提供しようとしています。 O_CLOEXECのポイントは、Unixシステムではos.Openが対応するC呼び出しでデフォルトで設定されていない場合でも、O_CLOEXECを設定することです。 すべてのシステムでほぼ同じように動作する便利なデフォルトを提供しようとしています。

Go 1の互換性に関しては、あるOSのコーナーケースの動作を変更して、他のOSとの整合性を高めることは問題ないようです。 システム間の非互換性を常に維持することを約束しているわけではありません。

ウィンドウ固有の動作を主張するWindowsユーザーは、常にパッケージsyscallを使用するオプションがあります。これにより、到達する動作が実際にWindowsにのみ適用されることが明確になります。

繰り返しますが、壊れてしまう実際のプログラムの具体例はありますか?

@ cpuguy83 、助けになるプログラムの実際のユースケースに感謝します。

@rscあなたが言うように、私はまだFILE_SHARE_DELETEの破壊的な影響を理解していません。 同様に、この変更によってlogrotateを実装できるという確認は見つかりませんでした。

ただし、ファイルを削除できるこの変更がGoに含まれると、Goがこの変更によってWindowsにログローテーションを実装できない場合でも、ユーザーはこの動作を期待します。 したがって、この変更について慎重に検討する必要があります。 基本的に、WindowsはFILE_SHARE_DELETEのフラグが付いていないファイルを削除できません。 ユーザーは、この変更によりGoがすべてのファイルを削除できるようになると考えるかもしれません。 特に、ハンドル付きのos.NewFileによって作成されたファイルは、このフラグが設定されていなくても、os.File構造体のメソッドを操作できます。 プログラマーは、ファイルがWindowsで削除できるか、自分で削除できないかを知っておく必要があると思います。 私は、FILE_SHARE_DELETEがデフォルトの代わりにos.OpenFileのフラグによって渡されることを可能にするメカニズムを好みます。 デフォルトの動作はOSの動作に従うべきだと思います。

はい、でもそれはCです。 私たちは囲碁について話している。

そしてC#も。 私は調査しませんでしたが、Windows上の他の開発ツールはこのように動作すると確信しています。

Go 1の互換性に関しては、あるOSのコーナーケースの動作を変更して、他のOSとの整合性を高めることは問題ないようです。

これは、Windows以外のユーザーにとってはコーナーケースです。 Windowsユーザーの場合、この動作は標準です。

繰り返しますが、壊れてしまう実際のプログラムの具体例はありますか?

上記の具体的なサービスの例を考えてみましょう。

@ cpuguy83 、助けになるプログラムの実際のユースケースに感謝します。

@ cpuguy83あなたはあなたの問題についてもっと言うことができます。 この変更によって問題がどのように解決されるかを確認したいので、実際のコードをいくつか確認したいと思います。 あなたがいくつかの小さなプログラムを提供することができれば、それは今は壊れていますが、この問題が解決されれば機能するでしょう、それはありがたいです。 構築する必要はありませんが、少なくとも、誰もが判断できる十分なものを提供する必要があります。 ありがとう。

基本的に、WindowsはFILE_SHARE_DELETEのフラグが付いていないファイルを削除できません。 ユーザーは、この変更によりGoがすべてのファイルを削除できるようになると考えるかもしれません。

これが良い点だと思います。 Goプログラムで開いたファイルのみを削除できます。 ただし、Go以外のプログラムによって開かれた他のすべてのファイルは、引き続き削除できません。 これにより、os.Removeが予測不能になります。機能する場合と機能しない場合があります。

アレックス

開いているファイルの名前を変更するパッチを適用したsyscall.Open()を呼び出すコードがあります。 これがログローテーションを実装する方法です。 できます。

// several processes or goroutines do this
   fd, err := os.OpenFile("log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, ...)
   _, err = fd.Write(log_entry) // one or more times
   err = fd.Close()

// log rotator process does this
   err := os.Rename("log", "log_old") // active writers not disturbed
   fmt.Println(err) // "sharing violation" without FILE_SHARE_DELETE

しかし、あなたはそれをあなたに言うために誰かを必要としませんでした:-/

当然、os.NewFile()のos.Fileは、os.Create / Open / OpenFile()のプロパティとは異なる多くのプロパティを持つ可能性があります。これは機能であり、驚くことではありません。

Microsoft Cのfopen()の動作がWindowsのデフォルトの動作であると主張します。 それは真実ではない。 CreateFile()のデフォルトの共有モードはありません。 FILE_SHARE_READ | FILE_SHARE_WRITEはありません。

デプロイされたWindowsプログラムは、通常、__他のベンダーのプログラム__が常に特定の方法でファイルを開くことを前提としていると主張します。 その場合、Microsoftはデフォルトでfile_share_deleteを有効にするように要求していなかったでしょう。

@networkimprov

しかし、あなたはそれをあなたに言うために誰かを必要としませんでした:-/

外部プロセスがどのような場合でもファイルを正しく削除して名前を変更できることを知りたいです。

私が知りたいのは、その変更を含めた後、ログファイルがGoから出力され、外部プロセスによって実行されたlogrotateが正常に機能し、エラーなしで本番環境として機能することです。

@alexbrainman

残念ながら、コードは複雑ですが、これは実際のコードです: https

提案されている緩和策は、現在os.OpenFileとsyscall.Openをフォークすることです: https

明確にするために、OpenFileからFILE_SHARE_DELETEを通過するオプション、またはsyscall.Openを通過するオプションがあるだけで、このケースを処理するのに十分です。

残念ながら、コードは複雑ですが、これは実際のコードです: https

提案されている緩和策は、現在os.OpenFileとsyscall.Openをフォークすることです: https

@ cpuguy83ポインタをありがとう。

残念ながら、それを簡単に見てみると、この現在の問題が解決策であるかどうかを判断するのに十分な情報が得られません。 repro(おそらくこのhttps://github.com/moby/moby/issues/39274#issuecomment-497966709)から始めて、そこから取得する必要があります。 いつこれに費やす時間ができるかわかりません。 私の計画よりも良い提案があれば、私に知らせてください。

アレックス

以下は、標準的なログローテーションの実際のデモです。100個のゴルーチンがログファイルを開くたびに0.1〜2秒間隔で50行をログに記録し、別のゴルーチンが1分間隔でログをローテーションします。 私は昨日これをWin7ラップトップでエラーなしで数時間実行しました。

FILE_SHARE_DELETEを有効にするsyscall.Open()へのパッチで構築されています。 それ以外の場合は失敗します。 このフラグなしでより複雑なロギングスキームを作成することもできますが、他にも多くの用途があります。 私自身のコードは、さまざまな理由で開いているファイルの名前を変更します。

@rsc 、まだ疑問があれば、これであなたの提案のケースは完成すると思います。 (そして、上記のように、私はgolang-devとgolang-nutsに繰り返し投稿し、現在の動作に依存するプロジェクトを求めましたが、結果はゼロでした。)

package main

import (
   "fmt"
   "os"
   "math/rand"
   "syscall"
   "time"
)

const kLogFile = "winrotate"

func main() {
   syscall.Open_FileShareDelete = true
   rand.Seed(time.Now().UnixNano())

   for a := 0; a < 100; a++ {
      go runLog()
   }

   fmt.Println("rotating to "+ kLogFile +"N.txt")
   for a := 0; true; a++ {
      if a >= 5 {
         a = 0
      }
      time.Sleep(60 * time.Second)
      err := os.Rename(kLogFile +".txt", kLogFile + string('0'+a) +".txt")
      if err != nil {
         fmt.Println(err)
         os.Exit(1)
      }
   }
}

const kLineVal = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func runLog() {
   aLine := make([]byte, 102)
   for {
      aFd, err := os.OpenFile(kLogFile +".txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
      if err != nil {
         fmt.Println(err)
         return
      }
      for _, aV := range kLineVal {
         for a := range aLine {
            aLine[a] = byte(aV)
         }
         aEnd := aV - ('z' - 100) // limit line len to 100
         copy(aLine[aEnd:], []byte{'\r','\n'})
         _, err = aFd.Write(aLine[:aEnd + 2])
         if err != nil {
            fmt.Println(err)
            return
         }
         time.Sleep(time.Duration(100 + rand.Intn(1900)) * time.Millisecond)
      }
      err = aFd.Close()
      if err != nil {
         fmt.Println(err)
         return
      }
   }
}

syscall_windows.goへのパッチ:

diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go
index de05840..e1455d5 100644
--- a/src/syscall/syscall_windows.go
+++ b/src/syscall/syscall_windows.go
@@ -245,6 +245,8 @@ func makeInheritSa() *SecurityAttributes {
    return &sa
 }

+var Open_FileShareDelete = false
+
 func Open(path string, mode int, perm uint32) (fd Handle, err error) {
    if len(path) == 0 {
        return InvalidHandle, ERROR_FILE_NOT_FOUND
@@ -270,6 +272,9 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) {
        access |= FILE_APPEND_DATA
    }
    sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE)
+   if Open_FileShareDelete {
+       sharemode |= FILE_SHARE_DELETE
+   }
    var sa *SecurityAttributes
    if mode&O_CLOEXEC == 0 {
        sa = makeInheritSa()

@networkimprovファイルは外部プロセスによって名前が変更されていません。 ファイルの名前変更が同じプロセスで行われている場合、それを実装する他の方法がいくつかあります。

https://github.com/mattn/go-sizedwriter/blob/master/_example/example.go#L14

以下は、標準的なプラクティスのログローテーションの実用的なデモです

例をありがとう、 @ networkimprov 。 あなたがやろうとしていることがわかります。

コードの唯一の問題は、外部プロセスがFILE_SHARE_DELETEなしで書き込み中のファイルを開く場合、Goで行った変更に関係なく、現在と同じ問題が発生することです。

@ cpuguy83 @networkimprovの例で彼が試していることの写真が得られるので、私はあなたの問題を見ません(ここで約束したようにhttps://github.com/golang/go/issues/32088#issuecomment-536233195)。やること。

アレックス

アレックス、外部アクセスが許可されている場合は名前変更の試行をループでき、許可されていない場合はアクティブなログファイルを制限できます。

Mattn、rotateの名前変更は通常別のプロセスで行われます。

ここには明確なコンセンサスがないようです。 Unixのバックグラウンドを持つ開発者はこれを支持しているようですが、最もアクティブな2人のWindows開発者( @alexbranmanと@mattn)はそうではありません。 この変更は、FILE_SHARE_DELETEに対して新しいUnixライクな動作をする最新バージョンのWindowsのみを真に統合することにもなります。

新しいフラグの動作がどれほど広く利用可能であり、他の言語が一般的な動作に収束しているかどうかを確認するために、数年後にこのトピックを再検討する価値があるかもしれません。 誰かがこれを再考するために、たとえば2年かそこらで新しい問題を提出したいのであれば、それは問題ありません。

しかし、今のところ、コンセンサスが不足していることを考えると、これはおそらく減少のようです。

最終的なコメントのために1週間開いたままにします。

オプションを利用可能にするだけで競合はありますか?

チャイムを鳴らす声をもっと探しているなら、私は間違いなく_何か_をすることに賛成です。現在のところ、唯一の解決策は、Goの標準ライブラリから文字通り大量のコードをコピーし、1行を変更して、そのフォークを永久に維持することです。ログモニターまたはライブ「テール」のすべての実装は、最終的にこれを行うようです。

例については、 https: //github.com/elastic/beats/blob/master/libbeat/common/file/file_windows.go#L85-L103を参照して

私が正しく従えば、この提案は実際には2つの部分に分かれています。

  1. ファイルを開くときにフラグFILE_SHARE_DELETEの使用を許可します-
    実装は簡単で安全でなければなりません、開発者は明示的にしなければなりません
    新しいファイルを開くときにこのモードを要求する
  2. このフラグをデフォルトでオンにします-リスクがあり、十分にサポートされている場合のみ
    最後のWindows10ビルドを使用する場合。

すべてが1に同意する場合、おそらく開発者がフラグを許可することができます
今のところ明示的にリクエストし、数年後に2を再訪してください。

2019年10月2日水曜日、19:32 Mark Dascher、 notifications @ github.comは次のように書いています。

チャイムを鳴らす声をもっと探しているなら、私は間違いなく賛成です
何かをする
Goの標準ライブラリから大量のコードを取り出し、1行変更してから、
そのフォークを永遠に維持します。 ログモニターまたはライブのすべての実装
「しっぽ」は最終的にこれを行うようです。

例については、を参照してください。
https://github.com/elastic/beats/blob/master/libbeat/common/file/file_windows.go#L85 -L103


あなたが言及されたので、あなたはこれを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4VFQLSYVI66ENT6G4LQMTLJ7A5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2Z
またはスレッドをミュートします
https://github.com/notifications/unsubscribe-auth/ABNEY4U5L3WFZYNIUOSEY2TQMTLJ7ANCNFSM4HNPNYIA

間違いなく_少なくとも_に賛成1.持っている(ファイルを開くときにフラグFILE_SHARE_DELETEの使用を許可する)

私の以前の提案実装例のフォローアップを再検討するだけです:

syscall.FILE_SHARE_DELETEフラグは、 os.OpenFileflagパラメーターにうまく適合し、この動作をオンデマンドで有効にする簡単なメカニズムを提供します。 これは、Windows上の他のos.O_*フラグと衝突せず(これは強制/設計できます)、システム固有のファイルを開く動作を指定する慣用的な方法を提供します( os 'として非公式) ■POSIX指向のインターフェースは、Windowsでは慣用的なものと見なすことができます。 これは、POSIXプラットフォームでシステム固有のファイルを開く動作を渡すためにすでに使用されているルートと同じです(たとえば、Linuxではsyscall.O_DIRECTORY )。

デフォルトでオンにしないという決定が下されたとしても(これは正しい呼び出しだと思います)、フラグにはそのユースケース(@networkimprovによって指摘されたものを含む)があり、 CreateFileW呼び出しに頼ったり、ボイラープレートコードをコピーしたりすることなく、オンにすることができます。

開発者が必要に応じてFILE_SHARE_DELETEを設定し、Go標準ライブラリコードの分岐したチャンクを防ぐことができる上記の代替案[1]を検討してください。

__Rust__もデフォルトでこのフラグを設定し、FILE_SHARE_ *オプションのいずれかを_スイッチオフ_できるようにします。
https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html

@rsc提案に対してなされた技術的議論のどれが反駁されなかったかを尋ねることができますか?

提案とディスカッション全体を読んだ後、 @ networkimprovは、これを閉じて、ossyscallパッケージに新しいウィンドウ固有のフラグを追加することについて明示的に新しい問題を再度開く必要があると思います。 これにより、新しい動作がオプトアウトではなくオプトインになります。 現在の提案は、人々を心配する既存のコードを変更することを意味します。

@rsc提案に対してなされた技術的議論のどれが反駁されなかったかを尋ねることができますか?

申し訳ありませんが、それは提案プロセスの仕組みではありません。 他の人の議論を「反駁」するだけでは十分ではありません。 「提案プロセスの目標は、結果についてタイムリーに一般的なコンセンサスに達することです。」 ここでの前進について明確なコンセンサスはありません。 技術的な議論がなされており、Windowsポートに多大な貢献をした主要なGo開発者(つまり、 @ alexbrainmanと@mattn)を説得していませんでした。 明確なコンセンサスがないことに加えて、今日何かをする緊急性の明確な兆候はありません。この問題が提起される前のほぼ10年間、GoはWindowsで正常に機能していました。 私が理解しているように、すべてをそのままにしておくことは、Goがいつものように機能し続けることを意味します。

私は#34681を提出して、これが拒否された(まだ可能性が高い)イベントでFILE_SHARE_DELETEを使用してファイルを開く簡単な方法を提供しました。 非常に長くなっているこのアイデアを継続するよりも、そのアイデアに限定された新しいスレッドを開始する方が役立つように思われました。

@rsc 、これを

この議論の早い段階で、彼はos.Create / Open / OpenFile()の提案に同意しなかったのと同じ理由で、file_share_deleteを設定する新しいos.OpenFile()フラグに反対しました。 彼は、MSVC fopen()がファイルを開くことができないため、他のプログラムがその方法でファイルを開くことはないと想定していることを懸念しています。 したがって、これらの(まだ指定されていない)プログラムは、フラグを設定したGoプログラムを中断します。

アレックス、外部アクセスが許可されている場合は、名前変更の試行をループできます...

名前の変更をループする準備ができている場合は、Goリポジトリコードで何も変更する必要はありません。 あなたのプログラムは今と同じようにうまくいくでしょう。

現在のところ、唯一の解決策は、Goの標準ライブラリから文字通り大量のコードをコピーし、1行を変更して、そのフォークを永久に維持することです。

コードを別のパッケージにコピーするのは問題ないと思います。 https://github.com/moby/moby/pull/39974を調査しているときに、同じ考えが浮かびました。 維持するコードはあまりないと思います。 実際、私はそれを実装しました

https://github.com/alexbrainman/goissue34681

そのままコピーまたは使用してください。 たぶん、この機能に非常に関心があるので、他の人が共有して使用できる適切なパッケージを実際に作成します。 このようにして、最新の状態に保ち、バグを修正することができます。

@rsc 、これを

してみてください

https://github.com/alexbrainman/goissue34681

初め。 何らかの理由で満足できない場合は、Goリポジトリに新しい機能を追加することについて話し合うことができます。

ありがとう。

アレックス

私はこの反応に本当に失望しています。

この大量のコードをコピーしますが、これはほとんど理解されていません(
なぜこれがそれがしていることをしているのですか)単一のオプションを追加できるようにするためです
システム自体がサポートしていること、そのGoだけ、ほとんどの場合、私はそうではないと思います
意図的にさえ、オプションをに渡す手段を提供しません
syscall.Openはとんでもない状況のようです。

このオプションを渡すためにsyscall.Openを書き直す必要があるのはなぜですか?

土の上に、2019年10月5日は18:43アレックスBrainmanので[email protected]書きました:

アレックス、外部アクセスが許可されている場合は、名前変更の試行をループできます...

名前の変更をループする準備ができている場合は、何も変更する必要はありません
Goリポジトリコードで。 あなたのプログラムは今と同じようにうまくいくでしょう。

今のところ、唯一の解決策は文字通りたくさんのコードをコピーすることです
Goの標準ライブラリを使用して、1行を変更し、そのフォークを永久に維持します。

コードを別のパッケージにコピーするのは問題ないと思います。 調査中
moby / moby#39974https ://github.com/moby/moby/pull/39974同じものを入手しました
考え。 維持するコードはあまりないと思います。 実際、私は
まさにそれを実装しました

https://github.com/alexbrainman/goissue34681

そのままコピーまたは使用してください。 たぶん、与えられて、非常に多くの関心があります
この機能では、実際に共有できる適切なパッケージを作成します
他の人が使用します。 このようにして、最新の状態に保ち、バグを修正することができます。

@rsc https://github.com/rsc 、これを拒否する前に、何を聞いてみましょう
アレックスはあなたのO_ALLOW_DELETE提案について考えます。

してみてください

https://github.com/alexbrainman/goissue34681

初め。 何らかの理由で満足できない場合は、新しい追加について話し合うことができます
Goリポジトリへの機能。

ありがとう。

アレックス


あなたが言及されたので、あなたはこれを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=AAGDCZXHULQEMHAPTO6ZUJTQNFGE7A5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW
またはスレッドをミュートします
https://github.com/notifications/unsubscribe-auth/AAGDCZWYUNMCTAGO567AV73QNFGE7ANCNFSM4HNPNYIA

>>

  • ブライアンゴフ

私はこの反応に本当に失望しています。

そのように感じてすみません。 しかし、実際に私のパッケージを使おうとしましたか?

アレックス

@ rsc 、Alexもos.OpenFile()フラグに反対しているので、何もする必要はありませんか?

この機能をビルドフラグの後ろに置くのはどうですか?

「GoがWindowsで10年近く正常に機能した」かどうかについては、開いているファイルの名前を変更する必要がある場合は、間違いなく機能しませんでした。 (過去7年間、Windows 8/10ラップトップでも壊れていました。)

私はos.OpenFileとsyscall.Openの独自のフォークをすでにmoby / mobyに持っています。

なぜ私たちはここでそんなに否定的であるのですか?

火の上、2019年10月8日は1時47分アレックスBrainmanので[email protected]書きました:

私はこの反応に本当に失望しています。

そのように感じてすみません。 しかし、実際に私のパッケージを使おうとしましたか?

アレックス


あなたが言及されたので、あなたはこれを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=AAGDCZRV72GY4IJQJVJWMYTQNRCIHA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW
またはスレッドをミュートします
https://github.com/notifications/unsubscribe-auth/AAGDCZRNYSMIE6BX77XJVWDQNRCIHANCNFSM4HNPNYIA

>>

  • ブライアンゴフ

この特定の提案は拒否することをお勧めします。 os.OpenFileの既存の動作に依存していたWindowsユーザー向けにTOCTOUのセキュリティバグを多数紹介するつもりはありません。

ここでのより大きな質問は、「WindowsのCreateFileおよびNtCreateFile関数のさまざまな興味深いフラグをGoユーザーにどのように公開できるか」です。 さまざまな提案でそれらに対処できると思いますが、ここで示唆されているように、デフォルトですべてをオンにすることはできません。

@ zx2c4スレッド全体を読みましたか? 何度も試みたにもかかわらず、既存の文書化されていない動作に依存しているGoユーザーの1つのケースを特定することはできませんでした。

https://github.com/golang/go/issues/32088#issuecomment -537590826から1週間が経過しましたが、明確なコンセンサスはまだほとんどありません。つまり、これを拒否する必要があります。

辞退。

https://github.com/golang/go/issues/34681#issuecomment-565853605に賛否両論のオプションの概要を投稿しました。

このページは役に立ちましたか?
0 / 5 - 0 評価