Go: proposal: os: Buat/Buka/OpenFile() set FILE_SHARE_DELETE di windows

Dibuat pada 16 Mei 2019  ·  194Komentar  ·  Sumber: golang/go

Di Linux & MacOS kita bisa menulis ini; pada Windows gagal dengan "pelanggaran berbagi":

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()

Jika Anda mengembangkan di Windows dan menyebarkan ke Linux dll, dan kode Anda bergantung pada perilaku _undocumented_ GOOS=windows dari os.Rename() & .Remove() ini, kode tersebut rusak dan mungkin rentan. Perhatikan bahwa paket "os" memiliki _fifteen mentions_ dari perilaku khusus Windows lainnya.

Untuk memperbaikinya, syscall.Open() di https://golang.org/src/syscall/syscall_windows.go#L272
membutuhkan sharemode |= FILE_SHARE_DELETE

Microsoft merekomendasikan ini dijadikan default: https://github.com/golang/go/issues/32088#issuecomment -502850674
Rust menjadikannya default: https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html
Mingw-w64 menjadikannya default tujuh tahun lalu:
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858
Erlang menjadikannya default lebih dari enam tahun yang lalu: erlang/ otp@0e02f48
Python tidak dapat mengadopsinya karena keterbatasan runtime MSVC: https://bugs.python.org/issue15244

~Oleh karena itu syscall.Open() harus menggunakan file_share_delete secara default , dan syscall harus menyediakan keduanya:
a) sakelar global untuk menonaktifkannya (untuk aplikasi apa pun yang ada yang mengandalkan ketidakhadirannya), dan
b) tanda untuk digunakan dengan os.OpenFile() untuk menonaktifkannya pada pegangan file tertentu.~

Perbarui setelah https://github.com/golang/go/issues/32088#issuecomment-532784947 oleh @rsc :
a) os.Create/Open/OpenFile() harus selalu mengaktifkan file_share_delete di Windows,
b) syscall.Open() pada Windows harus menerima tanda yang mengaktifkan file_share_delete, dan
c) syscall pada Windows harus mengekspor konstanta untuk flag baru.

Dokumen untuk os.Remove() juga harus mencatat bahwa untuk menggunakan kembali nama file setelah menghapus file yang terbuka di Windows, seseorang harus melakukan: os.Rename(path, unique_path); os.Remove(unique_path) .

Menangkan dokumen API
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

Jika ada alasan untuk tidak melakukan ini, itu harus didokumentasikan di os.Remove() & .Rename().

cc @alexbrainman
@gopherbot tambahkan OS-Windows

FrozenDueToAge OS-Windows Proposal Proposal-FinalCommentPeriod release-blocker

Komentar yang paling membantu

Saya akan mencoba untuk mewakili perspektif tim Windows di sini... kami lebih suka Go selalu mengatur FILE_SHARE_DELETE, tanpa opsi apa pun. Secara umum, untuk membuat porting ke/dari Windows lebih mudah, kami lebih suka perilaku yang konsisten dengan Linux, dan saya tidak melihat mengapa pengguna atau perangkat lunak Windows lebih memilih perilaku default !FILE_SHARE_DELETE cukup untuk ini menjadi pengecualian aturan.

Catatan menarik, bertentangan dengan komentar @mattn : dalam versi Windows terbaru, kami memperbarui DeleteFile (pada NTFS) untuk melakukan penghapusan "POSIX", di mana file tersebut segera dihapus dari namespace alih-alih menunggu semua terbuka menangani file yang akan ditutup. Itu masih menghormati FILE_SHARE_DELETE, tetapi sekarang berperilaku lebih seperti POSIX unlink. Fungsionalitas ini ditambahkan untuk WSL dan dianggap layak digunakan secara default untuk perangkat lunak Windows juga.

Dengan kata lain, jika Anda menjalankan program pengujian mattn dan urutan del, dir, dll. pada versi terbaru Windows, Anda akan melihat file menghilang dari namespace segera setelah file dihapus, bukan setelah program pengujian keluar. Sama seperti Linux.

Jadi, bahkan di Windows sendiri, dengan risiko appcompat yang jauh lebih besar, kami membuat perubahan kecil untuk memudahkan porting perangkat lunak non-Windows ke Windows. Saya sangat mendorong Go untuk melakukan hal yang sama.

Semua 194 komentar

/cc @alexbrainman

syscall.Open() di https://golang.org/src/syscall/syscall_windows.go#L272
harus menggunakan sharemode := ... | FILE_SHARE_DELETE

Mengapa harus?

Alex

FILE_SHARE_DELETE mengaktifkan contoh kode di atas, yang berfungsi di MacOS & Linux tetapi gagal di Windows. Ini juga diperlukan untuk:

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 mengaktifkan contoh kode di atas, yang berfungsi di MacOS & Linux tetapi gagal di Windows.

Mengapa kita tidak menyesuaikan kode MacOS & Linux saja?

Ini juga diperlukan untuk:

Saya tidak mengerti apa yang Anda coba katakan.

Alex

@networkimprov Anda harus memanggil Hapus setelah Tutup () di Windows.

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 ketika melakukan I/O file yang andal (seperti untuk basis data), itu adalah praktik standar untuk membuat file dengan nama sementara, tulis, fsync, dan ganti namanya.

File yang terbuka dapat diganti namanya atau dihapus secara default di Unix. Sepertinya ada kekeliruan bahwa bendera Windows untuk kemampuan ini tidak disetel. Saya ragu kami akan meyakinkan tim Go untuk mengubah cara kerjanya untuk Linux & MacOS :-)

@mattn tolong terapkan perbaikan yang saya jelaskan dan coba kode yang saya posting.

Saya ragu kami akan meyakinkan tim Go untuk mengubah cara kerjanya untuk Linux & MacOS :-)

Saya baik-baik saja seperti sekarang.

Alex

Apakah ada alasan untuk menghilangkan kemampuan umum ini di Windows?

Bisakah Anda memberikan sakelar di syscall_windows.go sehingga kami dapat memilih perilaku Unix saat program dimulai?

Saya setuju kami menambahkan API atau flag baru. Tapi saya keberatan untuk mengubah perilaku saat ini. Karena FILE_SHARE_DELETE tidak sama dengan perilaku 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;
}

Lengkapi kode ini di Windows, dan coba jalankan sebagai test.exe . Sementara aplikasi ini menunggu hit-a-key, buka cmd.exe baru, hapus "test.txt" seperti berikut.

image

File dapat dihapus tetapi tetap ada selama proses berlangsung. Jadi perubahan ini tidak akan bekerja dengan baik seperti yang Anda harapkan.

Saya menyadari entri direktori tidak dihapus tetapi dokumen mengatakan

Panggilan berikutnya ke CreateFile untuk membuka file gagal dengan ERROR_ACCESS_DENIED.

Jadi saya tidak mengerti log layar Anda.

Bagaimanapun, sakelar seperti ini akan baik-baik saja:

syscall.OpenFileShareDelete = true

Saya ingin menyebutkan bahwa saya pernah digigit oleh ini di tempat kerja sebelumnya.

Pada dasarnya kami memiliki:

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

// lots of code

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

Kode berjalan dengan baik di platform CI berbasis unix kami, tetapi meledak di Windows.

Dengan asumsi tidak ada efek samping yang aneh, alangkah baiknya jika semuanya berhasil .

IIRC, kami telah membuat beberapa penyesuaian pada perilaku GOOS=windows & plan9 di masa lalu agar lebih cocok dengan semantik Unix. Saya tidak keberatan menjadikan ini kasus lain jika semantiknya cukup dekat. komentar @mattn , bagaimanapun, menunjukkan perilaku tersebut tidak cukup dekat sehingga mungkin tidak sepadan.

Saya tidak ingin melihat beberapa opsi global. Itu sepertinya mimpi buruk debugging.

@bradfitz
Mungkin tidak, silakan lihat komentar saya di sini
https://groups.google.com/forum/#!topic/golang -dev/R79TJAzsBfM
atau jika Anda lebih suka saya dapat menyalin konten di sini, karena ada juga solusi yang mungkin, meskipun saya tidak akan menerapkannya sebagai perilaku default , karena ini tidak akan konsisten dengan bagaimana program windows berperilaku melainkan solusi untuk membuat persilangan serupa - pengalaman.

@guybrand menulis di utas golang-dev:

program saya sedang menulis file bernama "my.data", dan kemudian memanggil program lain dan tidak menunggu sampai selesai ... program ini katakanlah mengunggah file ini yang membutuhkan waktu 10 detik (sebut saja program lain ini "pengunggah") .
...
Saat program saya memanggil .Remove di Windows (FILE_SHARE_DELETE aktif):
...
pengunggah akan menerima kesalahan yang mengatakan bahwa file tersebut dihapus (mungkin EOF).

Dengan asumsi "pengunggah" membuka file sebelum "program saya" memanggil os.Remove(), saya pikir Anda bertentangan dengan dokumen Win API:

_DeleteFile menandai file untuk dihapus saat ditutup. Oleh karena itu, penghapusan file tidak terjadi hingga pegangan terakhir ke file ditutup. Panggilan berikutnya ke CreateFile untuk membuka file gagal dengan ERROR_ACCESS_DENIED._

Re "memasangkan _open_osfhandle() dari CreateFile ke _fdopen", dapatkah Anda menunjukkan kode contoh?

Jika kita menambahkan FILE_SHARE_DELETE, banyak programmer yang salah menggunakannya untuk mensimulasikan perilaku Unix. Dalam hal ini, Anda dapat membuat fungsi yang dipisahkan oleh batasan build untuk setiap OS. Dan itu harus mengembalikan os.NewFile() , gunakan FILE_SHARE_DELETE di Windows.

buat fungsi yang dipisahkan oleh batasan build untuk setiap OS... kembalikan os.NewFile(), gunakan FILE_SHARE_DELETE di Windows

Untuk mempertahankan fungsionalitas os.OpenFile() rencana ini tampaknya memerlukan penerapan ulang semua:

os.OpenFile()
bukaFileNolog()
membuka file()
bukaDir()
file baru()
memperbaikiLongPath()
panggilan sys.Buka()
makeInheritSa()

@networkimprov

@guybrand menulis di utas golang-dev:

program saya sedang menulis file bernama "my.data", dan kemudian memanggil program lain dan tidak menunggu sampai selesai ... program ini katakanlah mengunggah file ini yang membutuhkan waktu 10 detik (sebut saja program lain ini "pengunggah") .
...
Saat program saya memanggil .Remove di Windows (FILE_SHARE_DELETE aktif):
...
pengunggah akan menerima kesalahan yang mengatakan bahwa file tersebut dihapus (mungkin EOF).

Dengan asumsi "pengunggah" membuka file sebelum "program saya" memanggil os.Remove(), bukankah Anda telah membantah dokumen Win API?

DeleteFile menandai file untuk dihapus saat ditutup. Oleh karena itu, penghapusan file tidak terjadi hingga pegangan terakhir ke file ditutup. Panggilan berikutnya ke CreateFile untuk membuka file gagal dengan ERROR_ACCESS_DENIED.

Re "memasangkan _open_osfhandle() dari CreateFile ke _fdopen", dapatkah Anda menunjukkan kode contoh?

Saya tidak melihat kontradiksi dengan WIndows API, tolong tunjukkan apa yang tampak seperti kontradiksi.

Adapun contoh kode, saya mencari di Google sedikit, dan menemukan ini:
http://blog.httrack.com/blog/2013/10/05/creating-deletable-and-movable-files-on-windows/

TETAPI
Harap dicatat - ini hanya akan memberi Anda perilaku seperti nix secara internal, program eksternal lain apa pun yang bekerja dengannya akan merusaknya (karena 99% menggunakan API windows standar

@networkimprov
Jika maksud Anda "ini terdengar seperti "dir my.data" akan menampilkan file, sejauh yang saya ingat ini adalah perilaku sampai windows .... XP atau 7 (tidak ingat yang mana) dan berubah sejak (mungkin explorer hanya menyembunyikan entah bagaimana) - Saya dapat menguji ulang saat berikutnya saya meluncurkan windows env saya. (terjadi setiap beberapa minggu ...)

Jika maksud Anda "ini terdengar seperti proses lain dengan pegangan ke file masih dapat membaca dan menulis ke file", saya berani bertaruh makan siang begitu Anda membaca tulis ke file seperti itu, Anda mendapatkan kesalahan gaya "EOF" - tapi sekali lagi perlu konfirmasi untuk menjadi 100% positif.

Dalam kedua kasus, poin saya adalah (pada titik ini - saya mengambil "pihak" dalam poin "asli seperti" vs " os-agnostik") - bahkan jika Anda menerapkan solusi gaya _fdopen, Anda akan mendapatkan inkonsistensi antara layanan Anda dan semua executable lain yang berkolaborasi dengan Anda, sehingga penggunaannya hanya dapat berinteraksi dengan executable go lainnya (atau layanan langka yang DO menggunakan fd secara langsung).

Dengan kata lain - aplikasi Anda akan menjadi "anak terpintar di kelas" - yang tidak dapat dipahami oleh anak lain.
Dua contoh dari banyak yang dapat saya pikirkan:
Masukan :
Aplikasi saya mengunduh file, antivirus mengidentifikasi kesalahannya dan menghapus/mengkarantina (== semacam ganti nama), jika saya menggunakan fd - aplikasi saya masih dapat melakukan apa pun yang diinginkan dengannya (yaitu col, tetapi mungkin berakhir terkena virus...)
keluaran :
aplikasi saya menyalurkan file ke layanan lain ("seperti pengunggah"), dan menghapusnya, saya bahkan repot dan menulis penguji untuk melihat bahwa semuanya berfungsi dengan baik - dan tes lulus.
Sekarang alih-alih go test saya, saya menggunakan filezilla, kami mentransfer, dropbx API, apa pun
itu akan gagal/tidak berperilaku dengan cara yang sama seperti tes saya bekerja ...

Apakah Anda masih berpikir mengubah ini ke perilaku default masuk akal?

Apakah ada alasan untuk menghilangkan kemampuan umum ini di Windows?

Saya tidak pernah memikirkan pertanyaan itu. Saya tidak tahu.

Cara kerja file Go di Windows konsisten dengan semua alat pengembang lain yang pernah saya gunakan dalam hidup saya. Akan mengejutkan bagi saya, jika file Go berfungsi seperti yang Anda usulkan. Saya juga curiga, itu akan merusak banyak program yang ada.

Alex

Saya juga curiga, itu akan merusak banyak program yang ada.

@alexbrainman Saya juga menyarankan sakelar untuk mengaktifkannya, alih-alih mengubah default.

@bradfitz ada syscall.SocketDisableIPv6, itu tidak terlalu berbeda dari flag untuk menyesuaikan perilaku syscall.Open().

Sejak syscall_windows*.go menyatakan
func Open(path string, mode int, perm uint32) (fd Handle, err error) {
....
mode berbagi := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE)

dan type_windows*.go has
FILE_SHARE_DELETE = 0x00000004

Semua sudah cukup siap, satu-satunya pertanyaan adalah bagaimana menerapkan bendera.
Saya tidak suka opsi global dan juga tidak eksplisit, dan sementara satu pengembang mungkin ingat dia telah menetapkan os.DeleteUponLastHandleClosed = 1, ini bukan praktik yang baik untuk jangka panjang atau beberapa pengembang.
Opsi lain adalah menyetel nomor khusus yang dicadangkan untuk bendera, seperti di:
fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.DELETE_WHEN_FREED, 0600)
sedangkan DELETE_WHEN_FREED bahkan bisa 0 untuk env. selain jendela,

Pilihan lain adalah menggunakan parameter perm, yang tidak didukung untuk windows, ini mungkin sedikit canggung
fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 777)
777 dicadangkan, jadi kami membutuhkan 1777 atau -777 untuk mendukung kedua sistem
untuk membuat dapat dibaca DELETE_WHEN_FREED | 777

opsi terakhir yang dapat saya pikirkan adalah os.OpenDeletableFile(
Yang akan os.OpenFile di nix dan
berbelok
mode berbagi := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
di jendela

semua solusi di atas mudah diterapkan, sedikit lebih banyak waktu untuk menguji (cross os), hanya perlu pemilih...

Salah satu cara untuk mendukung perilaku ini tanpa mengubah default atau memperluas permukaan API mungkin dengan hanya menerima syscall.FILE_SHARE_DELETE dalam parameter flag dari os.OpenFile pada Windows dan melipatnya ke dalam sharemode . Karena flag syscall.O_* (dan karenanya os.O_* ) pada Windows menggunakan nilai yang dibuat-buat , mereka dapat direkayasa untuk menghindari bertabrakan dengan flag khusus Windows yang ingin disertakan. Untungnya, mereka sudah menghindari tabrakan dengan syscall.FILE_SHARE_DELETE .

Bagaimanapun, saya akan sangat menentang mengubah perilaku default. Menjadikan FILE_SHARE_DELETE sebagai default akan menempatkan Anda pada posisi yang sama dengan sistem POSIX dalam hal tidak dapat memastikan traversal sistem file bebas ras. Menjadikan flag ini opsional adalah mengapa Windows tidak membutuhkan yang setara dengan openat , renameat , readlinkat , dll.

Saya belum benar-benar memikirkan apa yang harus kita lakukan di sini, tetapi saya ingin memperjelas satu hal:

Bisakah Anda memberikan sakelar di syscall_windows.go sehingga kami dapat memilih perilaku Unix saat program dimulai?

Kami tidak akan melakukan ini. Itu akan membuat satu program tidak mungkin menggunakan paket berbeda yang mengharapkan perilaku berbeda.

@havoc-io
Saran Anda akan membuat program rawan kesalahan karena syscall.FILE_SHARE_DELETE berbeda dari perilaku POSIX std.
Harap perhatikan bahwa penulis syscall_windows*.go mengetahui flag FILE_SHARE_DELETE (ada di sana) dan memutuskan menggunakannya bukan perilaku yang disukai.
Mengapa?

mari kita ambil kode Liam, he
tunda fd.Tutup()
menghapus/mengganti nama
dan kemudian membaca/menulis

jadi urutan pengurutan yang sebenarnya adalah
membuka
tandai sebagai dihapus
baca/tulis (dan mungkin lintas proses/lintas pergi rutin baca tulis juga)
menutup

Perilaku windows saat ini memperingatkan pengembang "ini salah", pengembang mungkin akan mendorong penghapusan untuk ditangguhkan juga
Jika Anda Menambahkan FILE_SHARE_DELETE, windows akan memungkinkan Anda untuk "menandai sebagai hapus meskipun file terbuka" TETAPI proses/goroutine lain berjalan secara bersamaan yang akan mencoba mengakses file antara penghapusan dan penutupan (yang mungkin merupakan tugas yang lebih lama dalam kode ini ) akan gagal
Hasil: alih-alih perilaku "Anda bisa melakukan itu" yang konsisten - Anda akan mendapatkan "kadang-kadang - terutama ketika ada banyak pengguna bersamaan, itu gagal dan saya tidak tahu mengapa.
Jadi Anda tidak menyelesaikan masalah, hanya menangani kasus penggunaan tertentu di mana pengembang "hanya menjalankannya sekali"
Suara saya adalah untuk perilaku yang konsisten tentu saja ...

Apa bedanya dengan Posix?
Setelah ditandai sebagai dihapus, file tidak lagi ada di tabel file, hanya ada sebagai fd, memungkinkan rutin/proses lain untuk membuat file dengan nama yang sama.

Saya pikir kita harus berhenti mencari "solusi yang sama" karena ada perbedaan yang tidak akan kita selesaikan dalam go (nama file peka huruf besar-kecil? :) kita akan membiarkan Linux 5.2 melakukannya...)

Secara keseluruhan sepertinya mencari solusi untuk file sementara, jika ya, windows mendukung GetTempFileName yang dapat dianggap sebagai solusi standar untuk file yang "digunakan dan kemudian dibuang" .

Jika di sisi lain kami ingin mengizinkan pengembang untuk menunda penghapusan di windows, itu mungkin, lihat saran saya di atas, tetapi pengembang harus bertanggung jawab untuk itu, dan memahami kesadaran - oleh karena itu harus menjadi bendera dengan konvensi nama yang baik.

Penggunaan utama untuk fitur ini adalah untuk mengganti nama file yang terbuka setelah membuatnya:

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

Saya tidak tahu mengapa Anda tidak menambahkan flag os.O_WRNDEL (windows rename/delete; no-op pada platform lain), dan dokumentasikan perbedaan mode tersebut dengan Unix; bahwa file yang dihapus tetap berada di direktorinya, tetapi tidak dapat dibuka, hingga os.Close(). Sebutkan juga bahwa memindahkan file ke direktori sementara sebelum menghapusnya memberikan lebih banyak perilaku seperti Unix.

@guybrand Saya pikir mungkin Anda salah paham dengan proposal saya. Saya tidak menganjurkan bahwa FILE_SHARE_DELETE diaktifkan secara default - sebenarnya saya menentangnya karena alasan yang disebutkan dalam paragraf kedua komentar saya. Proposal saya adalah untuk mekanisme yang memungkinkan pengguna untuk ikut serta dalam perilaku FILE_SHARE_DELETE (berdasarkan per-file) sambil tetap menggunakan infrastruktur file paket os . Proposal ini menyediakan mekanisme untuk melakukannya tanpa memperluas permukaan API paket apa pun.

Saya tidak tahu mengapa Anda tidak menambahkan flag os.O_WRNDEL (windows rename/delete; no-op pada platform lain)

@networkimprov Pada dasarnya itulah yang saya usulkan, kecuali bahwa tidak ada gunanya mendefinisikan bendera baru karena yang benar-benar valid sudah ada: syscall.FILE_SHARE_DELETE . Satu-satunya perubahan implementasi adalah dengan memperhatikan flag ini di syscall.Open pada Windows dan menambahkan pemeriksaan untuk memastikan bahwa tidak ada penambahan flag syscall.O_* / os.O_* bertabrakan dengan flag ini. Dokumentasi untuk os.OpenFile kemudian dapat diperbarui untuk mencerminkan penerimaan tanda ini di Windows.

@havoc-io maaf atas kesalahpahamannya, ini adalah takeout saya dari:
" ... untuk hanya menerima syscall.FILE_SHARE_DELETE ... ke dalam mode berbagi yang dihitung..."

Menambahkan os.O_WRNDEL cocok dengan saran kedua saya selain dari tidak cukup eksplisit untuk pengembang dalam hal "apa yang akan menjadi perilaku", mungkin os.WADRNDEL - Windows allow deferred rename/delete) .

@alexbrainman Saya juga menyarankan sakelar untuk mengaktifkannya, alih-alih mengubah default

Saya tidak tertarik. Terima kasih.

Alex

@guybrand mengganti nama file yang terbuka tidak ditangguhkan, saya memeriksa.

Maaf, saya bertanya lagi apa saran Anda? hapus file pembuka? ganti nama file pembuka? keduanya?

Kami bertiga sekarang menyarankan bendera baru, misalnya

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)

Kami bertiga sekarang menyarankan bendera baru

Saya pikir bendera baru tidak pantas, terutama yang tidak mudah diuraikan oleh seseorang yang membaca kode. Menggunakan syscall.FILE_SHARE_DELETE akan jauh lebih eksplisit dan jelas, segera memberi sinyal kepada pembaca bahwa sesuatu yang spesifik platform sedang terjadi.

Ini sudah diperbolehkan di platform POSIX. Ada banyak flag open khusus POSIX (dan bahkan khusus platform) yang tidak dimasukkan ke dalam paket os . Misalnya, tidak akan masuk akal untuk menambahkan sesuatu seperti os._DEVTONLY untuk mendukung Darwin O_EVTONLY bendera, karena Anda hanya dapat melewati bendera dari syscall paket langsung ke os.OpenFile , misalnya:

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

Dalam hal ini, bendera khusus platform akan diturunkan ke panggilan sistem open mendasarinya.

Jika perilaku FILE_SHARE_DELETE benar-benar sesuatu yang dibutuhkan orang, maka saya pikir jawabannya hanyalah membuat os.OpenFile dapat juga melakukan thread flag ke panggilan CreateFileW mendasarinya di Windows. Setidaknya untuk FILE_SHARE_DELETE .

Contoh implementasi dapat ditemukan di sini .

@networkimprov

Kami bertiga sekarang menyarankan bendera baru, misalnya

Apa yang Anda harapkan dari bendera?

@guybrand mengganti nama file yang terbuka tidak ditangguhkan, saya memeriksa.

Benar, tetapi keduanya membutuhkan FILE_SHARE_DELETE agar diizinkan saat file terbuka
Jika Anda mengacu pada terminologi yang saya sarankan, kami dapat
os.WARNDFDEL - Windows allow rename dan deferred delete agak terlalu lama, saya sendiri akan menggunakan singkatan sama sekali WINDOWS_ALLOW_OPENNED_FILE_RENAME_OR_DEFFERED_DELETE
lebih baik IMO.

@guybrand Saya benar-benar tidak melihat nilai menambahkan bendera khusus platform baru dengan nama yang kompleks dan perilaku buram ke paket os ketika bendera yang sangat bagus untuk ini telah ada selama beberapa dekade ( FILE_SHARE_DELETE ). Menggunakan syscall.FILE_SHARE_DELETE sebagai tanda untuk ini jauh lebih jelas dan eksplisit. Seperti yang saya sebutkan di atas , ada banyak flag khusus POSIX dan khusus platform yang tidak ditambahkan ke paket os tetapi masih diterima olehnya.

syscall.FILE_SHARE_DELETE hanya untuk windows; kita membutuhkan sesuatu yang tidak boleh dilakukan di tempat lain. Mungkin seharusnya muncul di pkg os, dengan awalan WIN atau WINDOWS?

@mattn tolong lihat tambalan di atas dari @havoc-io

kita membutuhkan sesuatu yang tidak boleh dilakukan di tempat lain.

@networkimprov Bendera tidak perlu ada di tempat lain. Kode Windows Anda hanya akan terlihat seperti:

import (
    "syscall"
    "os"
)

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

Ya, kode ini perlu di-gated pada platform, tetapi Anda mungkin tetap harus melakukannya karena perilaku dengan flag ini tidak akan cocok dengan sistem POSIX. Atau, jika Anda menginginkan kenyamanan memiliki kode yang sama di semua platform, Anda dapat menentukan konstanta Anda sendiri (dengan nilai syscall.FILE_SHARE_DELETE pada Windows dan 0 pada sistem non-Windows) dan teruskan ke os.OpenFile . Inilah tepatnya yang akan Anda lakukan jika Anda ingin membawa bendera O_* khusus platform yang bukan bagian dari paket os (mis. O_EVTONLY di Darwin atau O_ASYNC di Linux).

Ketika saya menulis kode saya berharap untuk menjadi lintas platform, FILE_SHARE_DELETE tidak ada di syscall_* hanya windows satu (karena itu anf tidak akan dikompilasi pada sistem lain).

Bisakah kita menggunakan const 0x4, sebagai berikut:
FILE_SHARE_DELETE = 0x00000004

Selain jelek,
netbsd_arm
O_NDELAY = 0x4
O_NONBLOCK = 0x4

Jadi menggunakan 0x4 akan membuat kode yang berbeda di netbsd_arm.

Jadi kami menambahkan FILE_SHARE_DELETE ke semua platform, yang windows akan menjadi 0x4 yang lain akan menjadi 0x0 dan dengan demikian "membuangnya", atau membuat bendera khusus untuk masalah ini.

DAN bagaimanapun kita harus mengubah syscall_windows*
buka fungsi (
...
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | isDeleteOn )
di mana isDeleteOn akan memeriksa apakah mode menyertakan flag baru

@guybrand

Ketika saya menulis kode, saya mengharapkannya menjadi lintas platform

Saya akan mengingatkan Anda bahwa hanya karena panggilan os.OpenFile dikompilasi pada banyak platform, itu tidak berarti bahwa kode Anda adalah lintas platform. Ada sejumlah pertimbangan keamanan pada Windows yang perlu Anda perhitungkan saat menggunakan FILE_SHARE_DELETE , dan semantik siklus hidup file sangat berbeda antara POSIX dan sistem Windows sehingga sangat sulit untuk membayangkan skenario di mana Anda kode penanganan file tidak akan terlihat sedikit berbeda di antara platform. Menggunakan FILE_SHARE_DELETE pada dasarnya menempatkan Anda dalam situasi pra-POSIX.1-2008, tanpa cara apa pun untuk memastikan traversal sistem file bebas ras (dan tanpa manfaat dari akses file pasca- POSIX - unlink untuk membuka file).

Bagaimanapun, jika Anda menginginkan flag yang memiliki nilai syscall.FILE_SHARE_DELETE pada Windows dan 0 pada platform lain, Anda masih dapat dengan mudah melakukannya dengan definisi konstan Anda sendiri yang dikendalikan oleh tag build, dan kemudian kode utama Anda yang memanggil os.OpenFile bisa sama di semua platform (menggunakan bendera khusus itu).

Ada banyak flag khusus platform yang ada di paket syscall platform lain, tetapi tidak semuanya dapat diekspos oleh paket os dan tidak boleh dioperasi pada platform di mana mereka berada. t didukung. Jika Anda menginginkan kode dan perilaku khusus platform, Anda perlu menjangkau paket syscall .

Satu-satunya hal yang harus dilakukan oleh pustaka standar Go di sini adalah mendukung syscall.FILE_SHARE_DELETE di syscall.Open (dan akibatnya os.OpenFile ) di Windows.

Seperti yang saya sebutkan di atas , Jika kita menambahkan FILE_SHARE_DELETE, banyak programmer yang salah menggunakannya untuk mensimulasikan perilaku Unix. Tapi itu tidak baik. Misalnya, tolong pikirkan tentang kasus pembuatan aplikasi untuk melihat file yang ada. Disarankan file tidak boleh ditemukan dari aplikasi lain jika suatu aplikasi menghapus file. Tapi FILE_SHARE_DELETE jangan hanya menandai untuk dihapus nanti. Filenya tersisa. Di UNIX, ini berfungsi dengan baik, tetapi Windows tidak. Harap JANGAN gunakan FILE_SHARE_DELETE untuk mensimulasikan perilaku UNIX.

@mattn Saya setuju dengan kekhawatiran Anda - orang dapat mencoba menggunakan flag sebagai kemudahan untuk membuat kode Windows "bekerja sama" dengan sistem POSIX tanpa memahami implikasi halus. Namun, saya pikir jika mereka harus pergi ke paket syscall untuk mengakses flag itu, mereka mungkin telah meluangkan waktu untuk memahami apa fungsinya.

@mattn , untuk perilaku seperti Unix, aplikasi akan membutuhkan 2 baris tambahan kode platform-netral:

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)

Mendokumentasikan ini memerlukan satu kalimat dalam dokumen untuk os.OpenFile().

Tolong jangan membuat saya mendistribusikan patch ke pkg syscall bahwa setiap orang yang bekerja pada kode windows saya harus menerapkannya! Lihat juga https://github.com/golang/go/issues/32088#issuecomment -493876119.

Saya pikir kita semua setuju pada sebagian besar fakta, hanya mengungkapkannya secara berbeda jadi saya akan mencoba meringkas, dan menyiapkan tindakan:

  1. Setuju : Windows dan POSIX berbeda dalam perilaku di atas dan kita tidak boleh mencoba membidik perilaku yang sama.
  2. Setuju : permintaan untuk mengizinkan FILE_SHARE_DELETE sebagai bendera berguna
  3. Saran : ubah tiketnya menjadi "support FILE_SHARE_DELETE" jadi itu akan terdengar seperti permintaan fitur daripada bug
  4. Sebagian besar setuju : Fitur harus menjadi flag pengembang di tingkat program, bukan paket dasar.

Barang Aksi:

  1. ubah syscall_windows.go
    buka fungsi (
    ...
    sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | (mode & FILE_SHARE_DELETE) )
  2. Pengembang yang ingin memanfaatkan perilaku ini juga akan
    file, err := os.OpenFile(..., os.O_RDWR | syscall.FILE_SHARE_DELETE, ...)
    atau jika mereka ingin program mereka menjadi lintas platform akan membuat bendera internal:

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

dan kemudian gunakan:
file, err := os.OpenFile(..., os.O_RDWR | OPENFILEFLAG, ...)

Jika ini disetujui, perbaikannya minimal (satu liner), dengan risiko yang sangat rendah

Beri suara untuk ini, dan kami dapat dengan cepat memperbaikinya

Setuju : permintaan untuk mengizinkan FILE_SHARE_DELETE sebagai bendera berguna

Saya belum setuju dengan ini karena saya tidak mengerti apa tujuan dari masalah ini?

Tujuan : memungkinkan pengembang untuk mengganti nama atau menghapus file yang dibuka.

Apa yang hilang: syscall_windows.go saat ini kode keras :
func Open(path string, mode int, perm uint32) (fd Handle, err error) {
...
sharemode := uint32(**FILE_SHARE_READ | FILE_SHARE_WRITE**)

Perubahan yang diusulkan:
Ketika pengembang melewati parameter mode dengan bitwise 4 (FILE_SHARE_DELETE) dihidupkan, mode berbagi akan berubah sesuai dengan
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | **(mode & FILE_SHARE_DELETE)** )

Saya masih bertanya-tanya mengapa Anda tidak memanggil Close() sebelum Rename() atau Remove(). Anda dapat membaca kode perpustakaan standar Go. File ditutup oleh Close() sebelum Rename() atau Remove(). Mengapa Anda harus memanggil Close() setelah Renamoe/Rename ?

Saya tidak yakin apa kasus bisnis yang sedang dipertimbangkan @networkimprov , takeout saya tentang ini akan menjadi file bersama sementara - aplikasi saya dan aplikasi lain menggunakan file ini, saya ingin memastikan (bahkan jika aplikasi saya mogok) file ini tidak akan ada lagi setelah saya selesai - jadi saya membuatnya, Hapus () itu dan ketika saya menutupnya atau aplikasi saya ditutup - file tersebut dihapus.

tapi sepertinya @networkimprov ingin menggunakan Rename(), jadi mungkin kasus penggunaan yang berbeda.

Seperti yang disebutkan di https://github.com/golang/go/issues/32088#issuecomment -494305074 kami mungkin tidak perlu menutup file yang diubah namanya hingga program keluar.

@mattn ,

Saya ingin memastikan (bahkan jika aplikasi saya mogok) file ini tidak akan ada lagi setelah saya selesai

@guybrand Pernyataan defer tidak dijamin berjalan di setiap jenis kerusakan, jadi jika Anda mencoba memastikan penghapusan file, maka menunda operasi os.Remove bukanlah cara yang dapat diandalkan untuk pergi tentang hal itu. Jika Anda ingin file yang dihapus secara otomatis, file sementara yang dihapus sistem adalah pilihan yang lebih baik. Jika Anda menginginkan lokasi file yang dibagi oleh dua program, itu harus dikoordinasikan oleh kunci (misalnya fcntl kunci pada POSIX dan LockFileEx / UnlockFileEx pada Windows), bukan keberadaan file .

Seperti yang disebutkan di #32088 (komentar) kita mungkin tidak perlu menutup file yang telah diganti namanya sampai program keluar.
@mattn ,

@networkimprov Operasi rename-after-open adalah kasus penggunaan yang valid, tetapi jika Anda adalah program yang menyimpan file HANDLE , tidak bisakah Anda melakukannya? Satu-satunya tujuan FILE_SHARE_DELETE adalah mengizinkan proses lain untuk mengganti nama file saat Anda menahannya terbuka. Bagaimanapun, Anda dapat menggunakan FILE_SHARE_DELETE sekarang, Anda hanya perlu memanggil CreateFileW secara manual dan meneruskan hasilnya ke os.NewFile . Anda dapat menemukan contoh tampilannya di sini . HANDLE dihasilkan hanya dapat diteruskan ke os.NewFile .

@mattn Saya mulai setuju dengan Anda bahwa bendera ini akan lebih merupakan senjata daripada alat yang berguna. Untuk memperjelas, saya sebenarnya tidak menginginkan flag ini, dan satu-satunya argumen yang dapat saya lihat untuk keberadaannya adalah kelengkapan dan paritas kontrol antara POSIX dan Windows. Di sisi lain, seperti yang saya sebutkan di atas, juga relatif mudah untuk memanggil CreateFileW secara manual (dengan bendera ini ditentukan) dan kemudian menyerahkan hasilnya ke os.NewFile , jadi mungkin tidak perlu menambahkan dukungan untuk itu.

@havoc-io tidak ada os.File.Rename(). Itu akan menjadi solusi yang bagus, tetapi ini adalah pengangkatan yang relatif berat.

Kembali CreateFileW + os.NewFile(), lihat https://github.com/golang/go/issues/32088#issuecomment -493876119.

@havoc-io

Saya ingin memastikan (bahkan jika aplikasi saya mogok) file ini tidak akan ada lagi setelah saya selesai
@guybrand Pernyataan penangguhan tidak dijamin berjalan di setiap jenis kerusakan
Inilah yang saya maksud, Ketika saya mengatakan "bahkan jika aplikasi saya mogok".

satu-satunya argumen yang dapat saya lihat untuk keberadaannya adalah kelengkapan dan paritas kontrol antara POSIX dan Windows
Saya tidak setuju ini akan membawa "kelengkapan dan paritas kontrol antara POSIX dan Windows" - IMO itu hanya akan membingungkan pengembang untuk berpikir perilakunya sama padahal tidak, saya memberikan beberapa contoh di atas.

mudah untuk secara manual memanggil CreateFileW
Itu benar-benar tweak, mudah atau tidak, dan dengan kata lain mengatakan: "kami tidak ingin mendukung ini saat berjalan", yang merupakan keputusan yang ok IMO, tetapi pemohon adalah @networkimprov bukan saya.

@havoc-io tidak ada os.File.Rename(). Itu akan menjadi solusi yang bagus, tetapi ini adalah pengangkatan yang relatif berat.
Kembali CreateFileW + os.NewFile(), lihat #32088 (komentar).

@networkimprov Tidak bisakah Anda menggunakan os.Rename(file.Name(), <target>) ? Itulah keindahan Go tidak memiliki FILE_SHARE_DELETE secara default - Anda tahu bahwa file.Name() masih akan menunjuk ke sesuatu yang valid karena Anda memegang file HANDLE (sesuatu yang Anda bisa' t menjamin pada POSIX, bahkan dengan renameat ). Mengenai CreateFileW + os.NewFile , sebagian besar tumpukan yang dijelaskan dalam komentar Anda akan menjadi tidak relevan, dan makeInheritSa hampir pasti sesuatu yang tidak Anda inginkan (itu hanya ada untuk mensimulasikan POSIX perilaku pewarisan deskriptor file default yang buruk - ini bukan default). Satu-satunya hal yang akan Anda lewatkan adalah fungsi jalur internal fixLongPath , yang mungkin tidak diperlukan untuk kasus penggunaan yang telah Anda jelaskan.

@guybrand Hanya untuk memperjelas apa yang saya maksud...

kelengkapan: kelengkapan permukaan API (yaitu kemampuan untuk menggunakan FILE_SHARE_DELETE jika Anda ingin menggunakannya karena alasan tertentu)
parity of control: Kemampuan untuk menentukan flag ke CreateFileW melalui os.OpenFile flag parameter syscall.O_NOFOLLOW melalui os.OpenFile di POSIX, tetapi saya tidak dapat merutekan syscall.FILE_FLAG_OPEN_REPARSE_POINT melalui os.OpenFile di Windows. Meskipun sekali lagi, saya pikir paritas kontrol total di sini adalah mimpi pipa karena paket os dimodelkan di sekitar API POSIX. Saya cukup senang meraih syscall.CreateFileW bila diperlukan.

@mattn terima kasih atas

Utas Python mencatat bahwa Anda harus mengganti nama file yang terbuka sebelum menghapusnya, seperti yang saya tulis di atas.

Erlang telah memiliki file_share_delete sebagai _default_ selama lebih dari ENAM tahun .

Berkaca pada pengalaman Erlang dan utas Python yang ditautkan di atas, jelas bahwa:

  1. File_share_delete sebagai default berfungsi dengan baik di lingkungan lintas platform; satu-satunya peringatan adalah bahwa kode pengguna harus segera mengganti nama file menjadi nama unik sebelum menghapusnya (perubahan sepele) jika nama file asli dapat digunakan kembali.
  2. Sebagian besar program Windows tidak dapat menggunakan file_share_delete hanya karena runtime MSVC hanya mengizinkannya dalam kombinasi dengan O_TEMPORARY.

Oleh karena itu saya menyimpulkan bahwa syscall.Open() harus menggunakan file_share_delete secara default , dan syscall harus menyediakan flag global untuk menonaktifkannya jika diperlukan (misalnya debugging).

Jika masalah muncul dengan pendekatan itu selama siklus 1,14, metode flag eksplisit dapat diterapkan sebagai gantinya.

Oleh karena itu saya menyimpulkan bahwa syscall.Open() harus menggunakan file_share_delete secara default, dan syscall harus menyediakan flag global untuk menonaktifkannya jika diperlukan (misalnya debugging).

@networkimprov Selain memecah sejumlah besar program yang ada dengan cara yang halus dan tidak terlalu halus, faktanya adalah memperkenalkan bendera ini sekarang akan menjadi masalah keamanan untuk program yang mengandalkan tidak adanya FILE_SHARE_DELETE . Secara khusus, itu akan membuka sejumlah kerentanan waktu-pemeriksaan-ke-waktu-penggunaan untuk program-program yang mengandalkan kekekalan file dan direktori yang mereka buka.

Sunting: Untuk bacaan lebih lanjut tentang mengapa mengubah default adalah ide yang buruk dan hampir pasti merusak program yang ada, silakan lihat Hyrum's Law .

Kasus paling umum dari Go di Windows adalah pengembangan, untuk penyebaran ke Linux atau Unix lainnya.

Jika kode Anda bergantung pada perilaku GOOS=windows, kode tersebut rusak dan mungkin rentan di Linux. Jadi kami sudah memiliki kemungkinan masalah keamanan.

os.Rename() & .Remove() tidak mendokumentasikan perbedaan pada Windows, jadi tidak ada yang akan menebaknya. Mendokumentasikannya sekarang tidak akan memperbaiki kode yang ada. Memperbaiki ketidakcocokan dan menerbitkan posting blog di dalamnya akan jauh lebih membantu.

Perhatikan bahwa Erlang telah ada untuk sementara waktu sebelum mengadopsi file_share_delete sebagai default.

Kasus paling umum dari Go di Windows adalah pengembangan, untuk penyebaran ke Linux atau Unix lainnya.

Jika kode Anda bergantung pada perilaku GOOS=windows, kode tersebut rusak dan mungkin rentan di Linux. Jadi kami sudah memiliki kemungkinan masalah keamanan.

@networkimprov

Dengan logika itu, infrastruktur pembukaan file di Windows juga harus dimodifikasi untuk membuat deskriptor file dapat diwariskan secara default di seluruh proses pembuatan, agar sesuai dengan perilaku default POSIX. Program yang ada dapat disalahkan karena mengandalkan perilaku default runtime/library standar sebelumnya, dan template CVE dapat disertakan dalam catatan rilis bagi pengguna untuk mengajukan program mereka.

Tapi tentu saja bukan itu yang dilakukan, dan sebagai gantinya runtime Go dan pustaka standar berusaha sekuat tenaga untuk mengatasi perilaku default dan dirancang dengan buruk POSIX, mencoba untuk mencerminkan apa yang dilakukan Windows.

Situasinya sama dengan akses file bersama. Windows bisa dibilang mendapatkan desain yang benar, dan POSIX.1-2008 harus menambahkan sejumlah fungsi untuk mengatasi keterbatasan desainnya.

Juga, seperti yang telah disebutkan oleh @mattn beberapa kali di atas, termasuk FILE_SHARE_DELETE ketika membuka file TIDAK membuat Windows berperilaku seperti POSIX — masih akan ada perbedaan perilaku yang signifikan yang akan menjadi jauh lebih jelas dengan tanda ini daripada membedakan os.Remove dan os.Rename perilaku.

Saya berpendapat bahwa jika kode Anda secara universal bergantung pada perilaku platform apa pun , kode itu rusak dan mungkin rentan pada platform lain (dan itu termasuk mengandalkan perilaku POSIX di Windows). Jika Anda tidak meluangkan waktu untuk memahami dan mengatasi variasi platform dalam kode Anda, maka Anda tidak melakukan pengembangan lintas platform.

Bagaimanapun, @ianlancetaylor sudah memveto gagasan menggunakan bendera global untuk mengontrol pengaktifan ini, jadi saya sangat ragu Anda akan menonaktifkannya setelah mengubah default.

Menurut pendapat saya, satu-satunya opsi yang tidak melanggar (dan tidak membahayakan keamanan) di sini adalah menambahkan dukungan untuk syscall.FILE_SHARE_DELETE dalam argumen syscall.Open 's flag , atau hanya memaksa pengguna yang ingin fungsionalitas FILE_SHARE_DELETE menggunakan CreateFileW + os.NewFile . Ya, ini membutuhkan sedikit kerja dari pengembang yang menginginkan perilaku ini, tetapi Win32 dan POSIX pada dasarnya adalah binatang yang berbeda dan sungguh menakjubkan bahwa runtime dan pustaka standar melakukan pekerjaan sebaik yang mereka lakukan untuk mengatasi perbedaan mereka. Ada banyak perbedaan yang lebih signifikan antara platform ini yang muncul di perpustakaan standar, misalnya model izin yang sama sekali berbeda, fakta bahwa os.File.Chmod tidak berfungsi di Windows, os.Chown tidak berfungsi pada Windows, fakta bahwa poller internal mendukung berbagai jenis file pada platform yang berbeda, dll. Runtime dan perpustakaan standar Go melakukan yang terbaik yang mereka bisa (dan melakukannya dengan baik), tetapi mereka bukan obat mujarab untuk kesengsaraan pengembangan lintas platform. Pada titik tertentu, pengembang harus menangani perbedaan ini sendiri. Menambahkan perubahan yang melanggar untuk memodifikasi (perhatikan bahwa saya tidak mengatakan yang benar ) kasus tepi perilaku yang tidak jelas tidak masuk akal bagi saya.

Saya ingin membagikan satu kasus penggunaan lagi untuk ini.

Saat ini ketika logika putar log Docker berfungsi dengan cara mesin Docker akan mengganti nama log aktif menjadi nama .1, buat yang baru dengan nama lama.
Setiap klien yang aktif mengikuti log dengan perintah docker logs --follow <container>
akan melihat bahwa dari peristiwa sistem file:
https://github.com/moby/moby/blob/916eabd459fe707b5c4a86377d12e2ad1871b353/daemon/logger/loggerutils/logfile.go#L552 -L593

Ada juga logika ini yang membuat Windows segera memperhatikan peristiwa tersebut:
https://github.com/moby/moby/blob/916eabd459fe707b5c4a86377d12e2ad1871b353/daemon/logger/loggerutils/logfile.go#L670 -L676

Semua ini berfungsi dengan baik di Linux tetapi pada Windows log rotate gagal membuat kesalahan The process cannot access the file because it is being used by another process. jika ada klien yang mengikuti log (masalah: https://github.com/moby/moby/issues/39274 )

Karena dimungkinkan untuk memiliki n jumlah klien yang mengikuti log, akan sangat sulit untuk terlebih dahulu mengumpulkan semua pegangan file yang terbuka dan menutupnya sebelum file diputar, jadi saya benar-benar ingin mendapat dukungan untuk syscall.FILE_SHARE_DELETE bendera.

Hai! Latar belakang cepat: Saya bekerja pada infrastruktur Chrome CI. Kami mem-porting kode pekerja tingkat rendah kami ke Go. Kami menjalankan ratusan ribu permintaan pengujian di Windows per hari.

Saya telah membaca semua komentar dan saya harap berikut ini adalah ringkasan yang jelas dan benar dari pendapat masing-masing individu.

Kasus penggunaan kami sedang membangun Go yang dapat dieksekusi di Windows untuk berjalan di Windows, tidak seperti karakterisasi mengejutkan @networkimprov . Saya akan mengabaikan kasus penggunaan "anggap perilaku POSIX pada Windows" ini dalam komentar ini karena itu hanyalah asumsi yang salah dan menyeret @mattn untuk menyoroti perbedaan, yang IMHO adalah poin yang diperdebatkan, lebih lanjut tentang itu.

Sebagai bagian dari migrasi, kita memerlukan FILE_SHARE_DELETE, sebagian karena rotasi file log, mirip dengan #39274. Itu terjadi juga merupakan masalah untuk masalah #25965.

Saya setuju dengan @ianlancetaylor mengubah seluruh perilaku https://codereview.appspot.com/8203043/ bukan ide yang baik. Satu masalah langsung yang muncul dalam pikiran proposal ini saat menggunakan pegangan file sebagai file kunci. Kami menggunakan perilaku ini. Dan apa yang dikatakan @havoc-io. Jadi mari kita abaikan ini juga.

Untuk menyatakan kembali, saya akan mengabaikan proposal ini di sisa komentar ini:

  • Mencoba berperilaku persis seperti POSIX, itu tidak akan berhasil
  • Ubah perilaku global sebagai opt-out (atau tidak opt-out!), yang merusak pengguna

Ini memberi kami tanda per-panggilan dengan nama yang jelas-jelas khusus Windows. Inilah yang diusulkan oleh

Dengan segala hormat, saya tidak mengerti keberatan @mattn . Apa yang diusulkan @guybrand masuk akal. Ya Windows berperilaku berbeda sehubungan dengan berbagi file. Ini seperti hak penghapusan file, sangat berbeda di Windows . Itu sudah menyimpang banyak cara. Memiliki keberatan untuk bendera khusus Windows pada pendirian bahwa "Pengembang dapat salah memahami perilakunya" bukanlah fakta, ini adalah opini. Anda menganggap pengembang tidak tahu apa yang mereka lakukan atau tidak akan membaca dokumentasi di MSDN. Ini adalah keberatan yang adil untuk kasus umum , tetapi tidak kuat atas dasar memblokir kasus penggunaan yang sah sebagai flag opsional.

Ironisnya, ini berarti bahwa sampai masalah ini teratasi, untuk memperbaiki #25965 saya harus mengubah 'go run' untuk memanggil CreateFileW secara langsung karena kita juga membutuhkan FILE_FLAG_DELETE_ON_CLOSE .

Terima kasih!

Saya hanya ingin tahu bagaimana kasus ada untuk mengetahui apakah kita harus mengimplementasikan fungsi baru (golang.org/x/sys?) untuk membuka/membuat file dengan atribut baru, Atau mengubah perilaku asli. Saat ini, sejauh yang saya tahu dari komentar di atas:

  1. membuat file yang dapat dihapus (bisa diganti nama) untuk masuk.
  2. membuat file untuk file sementara yang akan dihapus saat ditutup.

Untuk 1, dapat disediakan dari fungsi baru untuk membuat/membuka file dengan atribut. Dan kita dapat mengatur file menggunakan logger.SetOutput(file).

Untuk 2, juga dapat membuat/membuka dengan fungsi baru.

BTW, saya kira #25965 tidak terkait dengan masalah ini. Mungkin, itu adalah keterbatasan Windows.

(Bagaimanapun, kami tidak dapat menghapus direktori tempat file dibuka dengan atribut ini ada)

@mattn Bisakah Anda menjelaskan trade off antara:

  • menambahkan fungsi yang sama sekali baru di golang.org/x/sys
  • mendukung flag baru (sudah ditentukan!) ke OpenFile(). [1]

Saya tidak mengerti mengapa kita harus memilih yang pertama daripada yang kedua.

[1] Saya baru menyadari bahwa saya berasumsi di sini mengubah perilaku os.OpenFile() tetapi masalahnya memanggil Open().

Pertama-tama, paket syscall sudah dikunci. Jadi saya mencari cara untuk memperbaikinya tanpa mengubah paket syscall.

BTW, saya kira #25965 tidak terkait dengan masalah ini. Mungkin, itu adalah keterbatasan Windows.

(Bagaimanapun, kami tidak dapat menghapus direktori tempat file dibuka dengan atribut ini ada)

@mattn itu tidak sepenuhnya benar. Bendera FILE_SHARE_DELETE akan memungkinkan Anda memindahkan file-file itu ke folder lain sehingga Anda dapat menghapus yang asli. Berikut adalah contoh PowerShell sederhana untuk itu:

# 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

Pertama-tama, paket syscall sudah dikunci. Jadi saya mencari cara untuk memperbaikinya tanpa mengubah paket syscall.

@mattn Jika ini hanya masalah API yang dibekukan, maka seperti yang saya sebutkan / demo di atas, syscall.Open hanya dapat disesuaikan untuk memahami syscall.FILE_SHARE_DELETE , menyelesaikan masalah tanpa mengubah API.

Jika ini adalah pembekuan implementasi, maka perubahan dapat dilakukan pada fungsi golang.org/x/sys/windows 's Open dan kemudian di-vendorkan ke dalam paket internal/syscall . Kode file_windows.go sudah menggunakan fungsi dari internal/syscall . Satu-satunya ketidakkonsistenan adalah bahwa syscall.Open tidak akan memahami tanda ini, tetapi kebanyakan orang yang ingin mengakses fungsionalitas API Windows tingkat rendah tetap menggunakan golang.org/x/sys/windows , dan mungkin menggunakan CreateFile bukannya Open .

Proposal yang memprakarsai paket syscall "lock-down" menyatakan:
_Repositori inti tidak akan bergantung pada paket go.sys_

Jadi mengubah x/sys tidak akan membantu penelepon os.OpenFile(). Tetapi internal/syscall dapat menambahkan Open(), dan os.OpenFile() akan menyebutnya.

@maruel , proyek Docker, dan mungkin banyak lainnya, mengasumsikan perilaku Unix untuk os.Rename() & .Remove() dari file yang dibuka karena paket "os" tidak menyebutkan perilaku khusus Windows untuk mereka, namun memiliki __fifteen mentions__ perilaku Windows lainnya.

Repositori inti tidak akan bergantung pada paket go.sys
Tetapi internal/syscall dapat menambahkan Open(), dan os.OpenFile() akan menyebutnya.

@networkimprov Oke, saya mendapat kesan yang salah bahwa internal/syscall adalah subset yang di-vendorkan dari golang.org/x/sys , tapi bagaimanapun saya pikir itu adalah strategi yang tepat (menambahkan internal/syscall/windows.Open ). Tentu saja, ini dengan asumsi bahwa perilaku syscall.Open tidak dapat diubah karena pembekuan. Idealnya itu dapat dimodifikasi secara langsung, berpotensi dengan perubahan yang sesuai pada golang.org/x/sys/windows.Open .

Meskipun paket syscall dibekukan, kami dapat mengubahnya untuk memperbaiki bug atau mengatasi kekurangan yang serius. Secara khusus kami dapat mengubahnya untuk mendukung paket os dengan lebih baik.

Tetapi jika orang menelepon syscall.Open secara langsung, maka mereka harus menggunakan paket x/sys/windows, bukan paket syscall.

Tetapi jika orang menelepon syscall.Open secara langsung, maka mereka harus menggunakan paket x/sys/windows, bukan paket syscall.

Saya tidak berpikir ada orang yang ingin menelepon syscall.Open secara langsung - itu hanya target diskusi karena mendasari os.OpenFile (sehingga menambahkan dukungan untuk FILE_SHARE_DELETE di syscall.Open menambahkan dukungan untuk menggunakan flag dengan os.OpenFile ).

Terima kasih. Tidak apa-apa untuk mengubah paket syscall untuk mendukung perubahan pada paket os.

Terima kasih @ianlancetaylor dan @

  • Ubah syscall.Open() dengan maksud eksplisit mengizinkan os.OpenFile() untuk menerima FILE_SHARE_DELETE (dan akhirnya FILE_FLAG_DELETE_ON_CLOSE sebagai kemungkinan tindak lanjut)
  • Perbarui dokumentasi os.OpenFile() untuk menjelaskan bendera khusus Windows yang baru.
  • Perbarui dokumentasi os.OpenFile(), os.Rename() dan os.Remove() untuk memperjelas perbedaan perilaku antara POSIX dan Windows.

Poin ketiga adalah untuk mengatasi kekhawatiran @networkimprov . Saya memahami tantangan di sana, dan meskipun itu bukan tanggung jawab bahasa untuk menggambarkan cara kerja OS, saya mulai setuju dengan @mattn bahwa kasus-kasus ini cukup halus untuk menjamin lebih banyak dokumentasi. Saya pikir dokumen untuk Ganti Nama dan Hapus dapat segera ditingkatkan, tidak perlu menunggu perubahan perilaku apa pun.

Saya bersedia berkontribusi dalam hal ini jika ada persetujuan.

Ini memberi kami tanda per-panggilan dengan nama yang jelas-jelas khusus Windows.

Apakah Anda mengusulkan flag os.Open baru? Kemudian flag harus didukung oleh semua OS - yang merupakan inti dari paket os - menjadi OS independen. Jadi seperti apa semantik bendera itu? Dan Anda akan memerlukan beberapa tes baru yang memverifikasi bahwa flag berfungsi seperti yang didokumentasikan.

Saya tidak mengerti mengapa Anda tidak dapat menulis kode apa pun yang Anda butuhkan menggunakan paket syscall. Saya tidak berpikir kode Anda akan hidup berdampingan dengan baik dengan program Windows lainnya. Dan mungkin tidak apa-apa dalam keadaan khusus. Tetapi saya tidak ingin orang menggunakan fungsi ini dengan enteng - jadi itu tidak boleh menjadi bagian dari perpustakaan standar.

Alex

@alexbrainman
tolong lihat ini

@alexbrainman
tolong lihat ini

Aku memang melihat. Saya masih tidak melihat bagaimana semua perubahan yang Anda usulkan akan bekerja pada OS lain? Tes apa yang akan Anda miliki untuk mengimplementasikan perubahan yang Anda usulkan?

Alex

mengutip ini

sedangkan DELETE_WHEN_FREED bahkan bisa 0 untuk env. selain jendela

Jadi - tidak ada yang perlu diuji pada OS lain, panggilan untuk:
fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.DELETE_WHEN_FREED, 0600)
di windows akan menerjemahkan const ke 4
fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE| 4 , 0600)
Sementara yang lain ( 0 )
fd, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE| 0 , 0600)

tidak ada yang diuji pada OS lain

Saya tidak melihat bagaimana kami dapat menambahkan const atau variabel paket os baru yang hanya dapat digunakan di Windows.

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.

Alex

Alex jawaban atas keberatan Anda pertama kali dinyatakan di sini https://github.com/golang/go/issues/32088#issuecomment -494157514

Ujian itu sepele

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

Kami berada pada titik di mana seseorang dapat mengirimkan tambalan; Saya menduga @ianlancetaylor akan menerimanya.

Saya menyelidiki pengaturan FILE_SHARE_DELETE sebagai bagian dari #32188, tetapi menemukan bahwa pengaturan itu tidak secara empiris mengurangi tingkat kesalahan dari panggilan os.Rename dan io.ReadFile ; loop coba lagi masih diperlukan.

Saya akan sangat tertarik untuk melihat tes yang menunjukkan perbedaan signifikan yang dapat diamati dari pengaturan flag ini, karena saya sendiri tidak begitu memahami implikasinya.

Saya telah bereksperimen FILE_SHARE_DELETE beberapa tahun yang lalu, dan itu berperilaku
berbeda.
Saya mungkin dapat meluncurkan kembali env windows saya. dan lihatlah, jika itu akan membantu,
tapi itu akan menjadi ~Go 1.3 atau lebih.

Pada Senin, 10 Jun 2019 pukul 17:44, Bryan C. Mills [email protected]
menulis:

Saya menyelidiki pengaturan FILE_SHARE_DELETE sebagai bagian dari #32188
https://github.com/golang/go/issues/32188 , tetapi menemukan pengaturan itu
tidak secara empiris mengurangi tingkat kesalahan dari os.Rename dan
io.ReadFile panggilan; loop coba lagi masih diperlukan.

Saya akan sangat tertarik untuk melihat tes yang menunjukkan signifikan
perbedaan yang dapat diamati dari pengaturan bendera ini, karena saya tidak benar-benar
memahami implikasinya sendiri.


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4VVIVJ2IEWQPWCWBZTPZZSETA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVX4HJKTDN5
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/ABNEY4STTW7U526HPO6TUHDPZZSETANCNFSM4HNPNYIA
.

@bcmils , membaca #32188 Saya menyimpulkan bahwa Anda melihat kesalahan dari os.Rename() pada file yang _tidak terbuka_ selama penggantian nama? Saya membayangkan seseorang dapat berlari lebih cepat dari jurnal NTFS, yang saya yakini ditulis untuk semua operasi perubahan direktori, dan dengan demikian menyebabkan kesalahan; tapi saya tidak tahu kesalahan apa yang akan terjadi.

Permintaan di sini untuk mengaktifkan FILE_SHARE_DELETE di syscall.Open() adalah untuk mengizinkan os.Rename() bekerja pada file yang dibuka. Saya telah menguji bendera itu dengan 1,12 di Win7, itu berhasil; dan gagal tanpanya.

Saya belum secara khusus menguji os.Rename() pada windows "under load", tetapi belum melihat kesalahan tak terduga dari os.Rename() pada file yang dibuka sejak mengaktifkan fsd. Saya akan melaporkannya jika saya melakukannya.

Saya menyimpulkan bahwa Anda melihat kesalahan dari os.Rename() pada file yang tidak terbuka selama penggantian nama?

Ya.

Saya tidak tahu kesalahan apa yang akan terjadi.

Secara empiris, kesalahan yang saya lihat (dari MoveFileEx , ReplaceFile , dan/atau CreateFile ) adalah ERROR_ACCESS_DENIED , ERROR_SHARING_VIOLATION , dan ERROR_FILE_NOT_FOUND .

Permintaan di sini untuk mengaktifkan FILE_SHARE_DELETE di syscall.Open() adalah untuk mengizinkan os.Rename() bekerja pada file yang dibuka. Saya telah menguji bendera itu dengan 1,12 di Win7, itu berhasil; dan gagal tanpanya.

Benar; masalah terkait yang saya coba selesaikan adalah mengizinkan panggilan serentak ke os.Rename berhasil, dan mengizinkan panggilan ke io.ReadFile bersamaan dengan os.Rename agar berhasil. Mendapatkan satu os.Rename untuk bekerja dengan pegangan yang ada terbuka mungkin merupakan langkah ke arah yang benar, tetapi saya tidak berpikir itu cukup untuk jenis kasus penggunaan yang kami gunakan POSIX rename .

Saya akan sangat tertarik untuk melihat tes yang menunjukkan perbedaan signifikan yang dapat diamati dari pengaturan flag ini, karena saya sendiri tidak begitu memahami implikasinya.

@bcmils PowerShell contoh saat saya menulis itu jauh lebih cepat daripada 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
}

Setelah itu selesai akan ada ribuan file test.txt.??? bawah C:\folder1 tetapi ketika Anda menutup PowerShell file-file itu akan dihapus.

Jadi apa sebenarnya flag FILE_SHARE_DELETE memungkinkan Anda mengganti nama/memindahkan/menghapus file yang terbuka tetapi Anda masih perlu memastikan bahwa nama file tujuan unik karena mereka akan ada di disk selama pegangan terbuka.

"employ the POSIX rename" tidak mungkin di windows, dan mungkin itulah alasan kesalahan yang Anda dapatkan.

POSIX :

buat file "nama file"
baca/tulis dari "nama file"
hapus "nama file"

buat file "nama file"
...
hapus "nama file"

akan berfungsi, karena setiap file yang dibuat akan memiliki fd diff.

Jendela:
buat file "nama file"
baca/tulis dari "nama file"
hapus "nama file"

buat file "nama file" - boom, berbagi pelanggaran

Seharusnya sangat mudah untuk direkonstruksi...

Apa yang dapat dilakukan FILE_SHARE_DELETE?
izinkan penghapusan yang ditangguhkan saat file terbuka.

Itu dia.

Pada Senin, 10 Jun 2019 pukul 19:32, Olli Janatuinen [email protected]
menulis:

Saya akan sangat tertarik untuk melihat tes yang menunjukkan signifikan
perbedaan yang dapat diamati dari pengaturan bendera ini, karena saya tidak benar-benar
memahami implikasinya sendiri.

@bcmills https://github.com/bcmills Contoh PowerShell saat saya menulis itu
jauh lebih cepat dari Go

Item Baru -Direktori Jenis -Path C:folder1for($i=0; $i -le 1000; $i++)
{
$temp = [System.IO.File]::Open("C:folder1test.txt", "Buat", "ReadWrite", "Hapus")
Set-Variable -Nama "file$i" -Nilai $temp -Force
$TempName = "C:folder1test.txt." + (Panduan Baru). Panduan
Ganti Nama-Item -Path C:folder1test.txt -NamaBaru $TempName
Hapus-Item -Path $TempName -Force
}

Setelah itu selesai akan ada ribuan test.txt.??? file di bawah
C:folder1 tetapi ketika Anda menutup PowerShell, file-file itu akan dihapus.

Jadi, apa sebenarnya flag FILE_SHARE_DELETE yang memungkinkan Anda
ganti nama/pindah/hapus file yang terbuka tetapi Anda masih perlu memastikan tujuan itu
nama file unik karena akan ada di disk selama menangani
terbuka.


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4WSTWIUVNVHLYMBFY3PZZ62DA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHjKTDN5WW48
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/ABNEY4VS5XF3EXB6QPBWVQ3PZZ62DANCNFSM4HNPNYIA
.

izinkan panggilan ke io.ReadFile bersamaan dengan os.Rename agar berhasil

@bcmills Saya pikir seseorang harus selalu bekerja dengan set flag fsd, dengan asumsi itu tidak berjalan bersamaan dengan utas lain yang mencoba urutan yang sama pada file yang tidak terkait. Dan biasanya harus gagal tanpa bendera.

Aspek skrip pengujian itu dengan benar menemukan bug di Windows syscall.Open(). Tetapi tidak ada orang lain yang berkomentar di utas ini yang menginginkannya diperbaiki :-( jadi ya, tambal skripnya.

TLDR:
perubahan kecil yang aman ke syscal_windows.go dan berfungsi seperti yang diharapkan.

menjelaskan:
Jadi, inilah yang telah saya lakukan, berfungsi seperti yang diharapkan, dan tanpa risiko (IMO)

  1. tweak 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))

tambahannya adalah : "| (mode & FILE_SHARE_DELETE)"
jika pengembang tidak akan mengirim mode & 4 set (mengapa dia - ini tidak pernah berfungsi sebelumnya ...) - tidak ada yang berubah, jadi kode seperti

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

akan berperilaku PERSIS seperti sebelum perubahan, hanya pengembang yang akan

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

akan "merasakan" perubahannya

  1. menjalankan kode
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())
    }
}

dan itu berhasil

  1. menjalankan kode tanpa |4 dan gagal
  2. menjalankan kode dengan tambahan kecil:
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()
    }
}

dan tentu saja gagal pada F kedua
Tapi itulah perilaku yang diharapkan di windows - tidak perlu "memuat tes" - tidak ada opsi konkurensi pada .Remove yang ditangguhkan, jadi saya pikir kita dapat dengan aman menambahkan setengah baris ini ke syscal_windows, tanpa mengorbankan kode yang ada, dan hanya mengizinkan

yang perlu kita lakukan (dalam huruf tebal):
syscal_windows.go:
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | (mode & FILE_SHARE_DELETE) )

Saya menyelidiki pengaturan FILE_SHARE_DELETE sebagai bagian dari #32188, tetapi menemukan bahwa pengaturan itu tidak secara empiris mengurangi tingkat kesalahan dari panggilan os.Rename dan io.ReadFile ; loop coba lagi masih diperlukan.

Saya akan sangat tertarik untuk melihat tes yang menunjukkan perbedaan signifikan yang dapat diamati dari pengaturan flag ini, karena saya sendiri tidak begitu memahami implikasinya.

@bcmills sekarang Anda dapat menemukan tes unit/regresi untuk ini dari https://github.com/olljanat/go/commit/3828f1a5d0ebb69b4c459d5243799ded36ac1ee8 saat ini gagal kesalahan The process cannot access the file because it is being used by another process. pada Windows dan mulai bekerja setelah FILE_SHARE_DELETE flag disertakan ke dalam kode.

Catatan! bahwa pada Windows Anda masih memerlukan langkah yang memindahkan file log example.log.1 ke nama acak/unik karena alasan lain mengapa mengganti nama example.log menjadi example.log.1 akan gagal menjadi Access is denied. error bahkan ketika flag FILE_SHARE_DELETE diaktifkan. Itu adalah hal khusus platform yang perlu diurus oleh pengembang.

/cc @jhowardmsft @jterry75 @jstarks @ddebroy FYI; Anda mungkin tertarik dengan yang satu ini juga.

@kevpar - FYI karena Anda juga mengatasi ini

Jika ada orang yang bergabung dengan utas dapat menyuarakan alasan (misalnya kode yang ada rusak) untuk menjadikan ini default, vs tersedia melalui bendera khusus Windows di os.OpenFile(), lakukan pipe up!

@networkimprov IMO itu harus ditangani dengan bendera khusus Windows karena pengembang diperlukan untuk menangani juga beberapa hal khusus Windows lainnya seperti pada contoh rotasi log saya.

Namun seperti yang saya lihat, beberapa karyawan Microsoft diundang untuk berdiskusi sehingga menarik untuk melihat apakah mereka melakukan hal yang berbeda.

Adakah yang btw mulai mengembangkan solusi baru untuk yang satu ini? Ada sukarelawan?

@olljanat
Jika ini adalah fix-as-a-flag, maka perbaikannya adalah one liner, harap baca:
https://github.com/golang/go/issues/32088#issuecomment -500562223

Saya akan mencoba untuk mewakili perspektif tim Windows di sini... kami lebih suka Go selalu mengatur FILE_SHARE_DELETE, tanpa opsi apa pun. Secara umum, untuk membuat porting ke/dari Windows lebih mudah, kami lebih suka perilaku yang konsisten dengan Linux, dan saya tidak melihat mengapa pengguna atau perangkat lunak Windows lebih memilih perilaku default !FILE_SHARE_DELETE cukup untuk ini menjadi pengecualian aturan.

Catatan menarik, bertentangan dengan komentar @mattn : dalam versi Windows terbaru, kami memperbarui DeleteFile (pada NTFS) untuk melakukan penghapusan "POSIX", di mana file tersebut segera dihapus dari namespace alih-alih menunggu semua terbuka menangani file yang akan ditutup. Itu masih menghormati FILE_SHARE_DELETE, tetapi sekarang berperilaku lebih seperti POSIX unlink. Fungsionalitas ini ditambahkan untuk WSL dan dianggap layak digunakan secara default untuk perangkat lunak Windows juga.

Dengan kata lain, jika Anda menjalankan program pengujian mattn dan urutan del, dir, dll. pada versi terbaru Windows, Anda akan melihat file menghilang dari namespace segera setelah file dihapus, bukan setelah program pengujian keluar. Sama seperti Linux.

Jadi, bahkan di Windows sendiri, dengan risiko appcompat yang jauh lebih besar, kami membuat perubahan kecil untuk memudahkan porting perangkat lunak non-Windows ke Windows. Saya sangat mendorong Go untuk melakukan hal yang sama.

@jstarks , terima kasih telah membela saya. Saya telah mengambil satu ton panas di utas ini untuk posisi itu.

Saya akan menyarankan opsi global untuk menonaktifkan fsd jika ada penyebaran Windows yang bergantung pada perilaku tidak berdokumen saat ini.

@ianlancetaylor apakah Anda setuju?

Karena perilaku default CreateFile adalah tidak dapat menghapus file pembuka, saya pikir kita harus mempertahankan perilaku default. Seseorang mungkin menginginkan perilaku aslinya. Misalnya, mungkin ada programmer yang memeriksa bahwa aplikasi mereka bekerja dengan cara yang tidak mungkin untuk menghapus file.

Perilaku Python sama dengan perilaku Go saat ini. Dan saya belum pernah mendengar bahwa Python memiliki rencana untuk mengubah perilaku yang tidak dapat menghapus file yang terbuka.

Saya belum meluangkan waktu untuk mengembangkan pendapat serius tentang masalah ini, tetapi saya tidak berpikir bahwa opsi global adalah pilihan yang baik. Kita harus mempertahankan perilaku saat ini (dan mungkin menambahkan tanda untuk memilih perilaku lain) atau kita harus mengubah perilaku (dan mungkin menambahkan tanda untuk memilih perilaku asli, terkini,).

Sehubungan dengan apakah akan mengubah perilaku default atau tidak, saya pikir kita harus bertanya apakah lebih mungkin mengubah perilaku akan memperbaiki program Go yang ditulis untuk berjalan di sistem Unix dan Windows atau apakah lebih mungkin mengubah perilaku akan merusak program Go yang ditulis untuk dijalankan pada sistem Windows. Saya tidak tahu jawaban untuk itu.

Seperti yang disarankan @mattn , ada baiknya juga menanyakan apa yang dilakukan bahasa lain.

Jika ini menjadi default, dan semua penerapan Windows bergantung padanya, saya membayangkan mereka menginginkan flag os.OpenFile(), dan opsi global untuk beralih kembali untuk tujuan transisi.

Ada opsi global untuk menonaktifkan IPv6, terakhir saya periksa.

Salah satu cara untuk menemukan dampak adalah mengubahnya di tip (dan mengumumkan di blog?) dan melihat apa yang dilaporkan.

Python dan Erlang keduanya disebutkan dalam teks masalah:
Erlang menjadikannya default lebih dari enam tahun yang lalu: erlang/ otp@0e02f48
Python ingin tetapi tidak bisa, karena keterbatasan runtime MSVC: https://bugs.python.org/issue15244

Saya akan mencoba untuk mewakili perspektif tim Windows di sini... kami lebih suka Go selalu mengatur FILE_SHARE_DELETE, tanpa opsi apa pun. Secara umum, untuk membuat porting ke/dari Windows lebih mudah, kami lebih suka perilaku yang konsisten dengan Linux, dan saya tidak melihat mengapa pengguna atau perangkat lunak Windows lebih memilih perilaku default !FILE_SHARE_DELETE cukup untuk ini menjadi pengecualian aturan.

di versi Windows terbaru, kami memperbarui DeleteFile (pada NTFS) untuk melakukan penghapusan "POSIX", di mana file akan segera dihapus dari namespace alih-alih menunggu semua pegangan terbuka ke file ditutup. Itu masih menghormati FILE_SHARE_DELETE, tetapi sekarang berperilaku lebih seperti POSIX unlink. Fungsionalitas ini ditambahkan untuk WSL dan dianggap layak digunakan secara default untuk perangkat lunak Windows juga.

@jstarks itu

Seolah-olah Microsoft melakukan itu maka saya lebih suka mengaktifkan FILE_SHARE_DELETE secara default di Go juga.

Java, C#, dan semua bahasa utama lainnya yang saya ketahui mengambil perilaku OS default. Menurut pendapat saya, perilaku default harus diserahkan kepada pelaksana OS. Jika Windows benar-benar ingin mengambil pendekatan POSIX tentang membuka file, mereka harus mengambil risiko itu dan melakukannya seperti yang mereka lakukan untuk DeleteFile.

Saya pikir perpustakaan Go harus memungkinkan untuk membuat/membuka berbagi menghapus file. Saya dapat memberikan satu contoh praktis dalam menangani mode file. Saat porting Hadoop ke Windows, kita harus melakukan upaya ekstra untuk membuat metode khusus di JNI untuk mendapatkan penanganan atau streaming file hapus bersama.

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

@jstarks

Saat mengatakan

di versi Windows terbaru, kami memperbarui DeleteFile (pada NTFS) untuk melakukan penghapusan "POSIX", di mana file tersebut segera dihapus dari namespace

maksud Anda file yang terbuka tetap hanya sebagai fd , dan kode di bawah ini:

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()
    }
}

tidak akan gagal pada

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

setelah menerapkan perbaikan yang disarankan ?

Jika demikian, itu benar-benar keren!

Sehubungan dengan apakah akan mengubah perilaku default atau tidak, saya pikir kita harus bertanya apakah lebih mungkin mengubah perilaku akan memperbaiki program Go yang ditulis untuk berjalan di sistem Unix dan Windows atau apakah lebih mungkin mengubah perilaku akan merusak program Go yang ditulis untuk dijalankan pada sistem Windows. Saya tidak tahu jawaban untuk itu.

Tidak ada problem to be fixed menjalankan program Unix di Windows. Windows menangani penghapusan file yang dibuka secara berbeda dari Unix. Ini mirip dengan sensitivitas huruf besar-kecil nama file: Unix peka huruf besar-kecil, dan Windows tidak peka huruf besar-kecil. Tidak ada yang bisa kita lakukan untuk kedua perbedaan itu.

Dan, sementara John meyakinkan bahwa Microsoft sedang mencoba mengubah banyak hal, saya akan menunggu sampai perubahan itu terjadi. Dan banyak pengguna Windows masih menggunakan Windows 7 dan semua versi Windows lainnya. John apakah Anda akan memperbarui Windows 7 juga?

Saya pikir kita harus membiarkan Microsoft menangani perubahan. Jika kami memperbaiki pengguna Go, masih akan ada pengembang non-Go. Jadi Microsoft harus berurusan dengan ini terlepas dari itu.

Juga, FILE_SHARE_DELETE memiliki biola sendiri. Misalnya, membaca https://bugs.python.org/issue15244

Sebenarnya, ini tidak semantik yang sama dengan Unix.

Setelah Anda menghapus file, Anda tidak dapat membuat file dengan nama yang sama atau menghapus direktori yang berisi file tersebut hingga pegangan ditutup.

Namun, seseorang dapat mengatasinya dengan memindahkan file ke tempat lain (seperti direktori root) sebelum menghapusnya.

Ada, mungkin, banyak orang lain seperti itu yang tidak kita ketahui. Kami belum menggunakan FILE_SHARE_DELETE sama sekali. Apakah Anda, mengusulkan, kami mengubah lib Go untuk menggunakan FILE_SHARE_DELETE secara default, dan kemudian menunggu pengguna mengeluh tentang program mereka yang rusak?

Seperti yang disarankan @mattn , ada baiknya juga menanyakan apa yang dilakukan bahasa lain.

Saya hanya memeriksa Mingw C - berperilaku seperti Go. Setelah file dibuka dengan fopen, itu tidak dapat dihapus sampai fclose dipanggil. Saya akan terkejut jika ada alat pengembangan Microsoft (C, C++, C#, VB, dan lainnya) yang berbeda.

Dan saya tidak melihat bagaimana kita bisa mengubah perilaku default seperti itu. Beberapa program mungkin memiliki file yang dibuka secara khusus, sehingga tidak dapat dihapus oleh program lain. Itu adalah pendekatan yang valid pada Windows dalam pandangan saya. Kita harus mendukungnya ke depan.

Alex

Ditambahkan 7 tahun yang lalu, saya percaya:
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858

Sejauh ini, tidak ada yang memposting kasus aplikasi Go apa pun yang bergantung pada perilaku Windows yang tidak terdokumentasi saat ini. Tetapi kami telah mendengar setidaknya 4 kasus aplikasi Go yang gagal di Windows karena itu.

Seperti yang dinyatakan di atas, ada perbaikan sederhana untuk penggunaan kembali nama file setelah os.Remove(), yang harus kita dokumentasikan:
os.Rename(path, unique_path); os.Remove(unique_path)

Sejauh ini, tidak ada yang memposting kasus aplikasi Go apa pun yang bergantung pada perilaku Windows yang tidak terdokumentasi saat ini.

Orang-orang ini bahkan tidak tahu bahwa ada kemungkinan untuk melaporkan bug Go.

Anda seharusnya tidak mengharapkan pengguna ini memantau go issue list untuk perubahan dengan harapan sesuatu akan mempengaruhi mereka. Mereka memiliki kehidupan untuk dijalani. Adalah kewajiban kita untuk tidak merusak alat yang mereka gunakan.

Alex

Alex, jelas kau tidak bisa dibujuk. Kemudian saya melihat dua kemungkinan jalur ke depan sehingga orang dapat menulis kode portabel di Go:

  1. tanda keikutsertaan baru ke Open yang berarti FILE_SHARE_DELETE untuk Windows dan diabaikan untuk OS lain. Kami kemudian akan mendorong siapa pun yang menulis kode yang mengharapkan semantik POSIX untuk meneruskan tanda ini di mana-mana; atau,
  2. paket Go baru posix (di beberapa repo di suatu tempat) dengan satu fungsi, Open , yang membungkus CreateFile dengan FILE_SHARE_DELETE tetapi sebaliknya berperilaku sebagai os.Open . Pada platform non-Windows, itu hanya akan memanggil os.Open secara langsung. Kami kemudian akan mendorong siapa pun yang menulis kode yang mengharapkan semantik POSIX untuk menggunakan posix.Open alih-alih os.Open mana-mana.

Kedua pendekatan ini mengharuskan pengembang Linux untuk mengambil tindakan tambahan untuk mempertahankan perilaku POSIX di Windows, tetapi setidaknya ini lebih baik daripada menulis pembungkus CreateFile satu kali (seperti yang baru saja kita lakukan untuk containerd).

Alex, jelas kau tidak bisa dibujuk.

Saya tidak dibujuk. Tapi Anda tidak perlu membujuk saya, Anda perlu membujuk Go Team. Mereka akan membuat keputusan.

  1. tanda keikutsertaan baru ke Open yang berarti FILE_SHARE_DELETE untuk Windows dan diabaikan untuk OS lain. Kami kemudian akan mendorong siapa pun yang menulis kode yang mengharapkan semantik POSIX untuk meneruskan tanda ini di mana-mana; atau,

Saya juga tidak suka opsi ini.

Pertama-tama, paket os seharusnya menyediakan fasilitas yang tersedia untuk semua OS. Apa pun yang spesifik untuk OS harus masuk ke syscall, atau golang.org/x/sys/windows, atau paket lain apa pun yang kami suka.

Kedua, saya khawatir bahwa saya akan berakhir dengan jenis file FILE_SHARE_DELETE di program saya, bahkan jika saya tidak menginginkannya. Bayangkan, jika saya mengimpor paket eksternal yang memutuskan untuk menggunakan mode file FILE_SHARE_DELETE . Bagaimana saya tahu, bahwa beberapa kode saya menggunakan FILE_SHARE_DELETE ? Saya harus menerima kode sumber paket. Atau dokumentasi paket harus memperingatkan penggunanya. Saya kira, ini juga bisa terjadi, jika beberapa pembuat paket hanya menggunakan syscall.CreateFile dengan FILE_SHARE_DELETE secara langsung. Tapi, saya pikir, jika FILE_SHARE_DELETE diberkati untuk paket os, ini akan lebih umum. Saya menduga, pengguna Linux, yang tidak tahu apa-apa tentang Windows, akan menggunakan flag ini secara default, untuk membuat port program Linux mereka "lebih mudah" ke Windows.

Ketiga, kita harus mendokumentasikan flag FILE_SHARE_DELETE . Kita tidak bisa hanya mengatakan POSIX. Kita harus menjelaskan apa yang dilakukan bendera itu. Dengan kata-kata sederhana. Juga ingat:

Sebenarnya, ini tidak semantik yang sama dengan Unix.

Setelah Anda menghapus file, Anda tidak dapat membuat file dengan nama yang sama atau menghapus direktori yang berisi file tersebut hingga pegangan ditutup.

Namun, seseorang dapat mengatasinya dengan memindahkan file ke tempat lain (seperti direktori root) sebelum menghapusnya.

dari https://github.com/golang/go/issues/32088#issuecomment -504321027. Kami juga harus mendokumentasikannya. Dan semua fungsi lain yang dibawa oleh flag FILE_SHARE_DELETE .

Keempat, kita perlu menambahkan tes baru untuk FILE_SHARE_DELETE . Apa yang akan menjadi tes ini? Apa yang terjadi, jika kita lupa menguji beberapa fungsionalitas, hanya untuk kemudian menemukan bahwa fungsionalitas tersebut tidak dapat dicapai dengan menggunakan flag FILE_SHARE_DELETE ? Kami tidak dapat menghapus flag FILE_SHARE_DELETE , jika kami tidak menyukainya. Setelah masuk, itu tetap. Dan kita harus mendukungnya di masa depan.

2. paket Go baru posix (dalam beberapa repo di suatu tempat) dengan satu fungsi, Open , yang membungkus CreateFile dengan FILE_SHARE_DELETE tetapi sebaliknya berperilaku sebagai os.Open

Saya tidak melihat bagaimana itu mungkin. Tapi, jika memungkinkan, Anda harus bisa membuat paket sendiri. Misalnya, sebagai github.com/jstarks/posix. Tidak? Kami dapat menambahkan paket di bawah golang.org/x/sys/windows/posix atau sesuatu, tetapi saya tidak melihat banyak perbedaan.

Alex

Paket os seharusnya menyediakan fasilitas yang tersedia untuk semua OS...
kita harus mendokumentasikan flag FILE_SHARE_DELETE. Kita tidak bisa hanya mengatakan POSIX...
kita perlu menambahkan tes baru untuk FILE_SHARE_DELETE. Apa yang akan menjadi tes ini?

Semua ini telah dibahas di atas.

jika saya mengimpor paket eksternal yang memutuskan untuk menggunakan mode file FILE_SHARE_DELETE. Bagaimana saya tahu

Itu adalah argumen lain untuk menjadikannya default, dan menyediakan flag global untuk menonaktifkannya.

Saya memposting ke golang-nuts untuk mencari aplikasi Windows yang terpengaruh; Saya akan melakukan ping setiap beberapa hari agar tetap terlihat. Jika itu tidak banyak muncul, menjadikannya default pada tip untuk 1,14 akan memperjelas banyak hal.
https://groups.google.com/d/topic/golang-nuts/8BiP_mPoCd4/discussion

Itu adalah argumen lain untuk menjadikannya default, dan menyediakan flag global untuk menonaktifkannya.

Omong-omong. Apa yang harus dilakukan flag itu di Linux? Afaiu kita sebenarnya juga kehilangan fungsionalitas untuk membuka file di Linux dengan cara yang tidak dapat dihapus oleh proses lain? Berdasarkan https://gavv.github.io/articles/file-locks/#open -file-description-locks-fcntl ini seharusnya dimungkinkan untuk mengimplementasikannya pada versi Linux modern.

Bendera (misalnya var OpenWithout_FILE_SHARE_DELETE = false ) akan didefinisikan di syscall_windows.go, karena hal itu memengaruhi perilaku syscall.Open().

Bendera untuk sesuatu khusus Linux akan memiliki nama yang berbeda, dan didefinisikan di syscall_linux.go. Begitu saja Saya tidak tahu bagaimana mencegah penghapusan file yang terbuka di Linux selain melalui izin pada direktorinya. Tautan yang Anda berikan menjelaskan penguncian "saran".

@jstarks sebelum kami mempertimbangkan untuk memperkenalkan flag FILE_SHARE_DELETE , kami juga perlu memutuskan apa yang harus dilakukan tentang ketidakmampuan Go untuk menghapus file yang dapat dieksekusi yang sedang berjalan. Saya tidak berpikir bendera FILE_SHARE_DELETE dapat membantu kami di sini. Bisa kah? Jika kami tidak dapat mengimplementasikan penghapusan file yang dapat dieksekusi yang masih berjalan, maka kami tidak dapat mengklaim kompatibilitas POSIX apa pun. Dan flag FILE_SHARE_DELETE terlihat seperti setengah solusi.

Terlebih lagi, terkadang kita gagal menghapus file executable dari sebuah proses yang bahkan sudah selesai. Lihat, misalnya, #25965, #19491 dan #32188 untuk detailnya. Pada dasarnya kami memanggil WaitForSingleObject pada pegangan proses hingga memberi sinyal. Tetapi itu tidak menjamin bahwa file yang dapat dieksekusi dapat dihapus. Kami harus menambahkan 5 milidetik menunggu sebelum mencoba untuk menghapus file. Dan bahkan itu tidak selalu berhasil - https://github.com/golang/go/issues/25965#issuecomment -482037476

Juga, tidak terkait dengan masalah ini, tetapi, karena saya mendapat perhatian Anda, mungkin Anda dapat membantu kami menghidupkan kembali port windows-arm. Lihat #32135 untuk detailnya. Pada dasarnya @jordanrh1 porting Buka windows-arm, tetapi kami tidak memiliki windows-arm builder lagi. Jadi kami bahkan tidak tahu apakah port masih berfungsi atau rusak. Jika Anda dapat membantu kami menjalankan builder, mungkin kami dapat mencoba dan terus mendukung port windows-am. Kalau tidak, port mungkin akan dihapus - https://github.com/golang/go/wiki/PortingPolicy Saya tidak tahu, jika seseorang tertarik menjalankan program Go di Windows 10 Iot, tetapi kami sudah memiliki dukungan ini. Akan sedih kehilangannya hanya karena kami tidak memiliki pembangun yang bekerja.

Terima kasih.

Alex

Perhatikan solusi yang disarankan ini untuk menghapus executable sementara: https://github.com/golang/go/issues/25965#issuecomment -495636291

Tidak ada hasil dari pos golang-kacang; memulai utas baru:
https://groups.google.com/d/topic/golang-nuts/aRvSo3iKvJY/discussion

Setidaknya satu kekhawatiran yang tidak terlalu hipotetis yang menurut saya harus diselidiki adalah efek pada penguncian file dan program yang bergantung padanya.

Membuka setiap pegangan file dengan FILE_SHARE_DELETE akan secara efektif melemahkan semantik LockFileEx dengan kunci penasihat POSIX, karena file apa pun dengan FILE_SHARE_DELETE ditentukan dapat dihapus, bahkan jika kuncinya diadakan di wilayah file itu. 1

Tentu saja ini hanya akan berlaku untuk LockFileEx panggilan yang dilakukan dengan deskriptor yang dikembalikan dari os.File.Fd , tapi itulah yang dilakukan di perpustakaan penguncian file Go paling populer (yang saat ini memiliki 33 importir yang dikenal , termasuk Docker , gVisor, dan Kubernetes). Itu juga dilakukan di Terraform , dan LockFileEx digunakan di banyak kode Go lainnya .

Saya masih berpikir perubahan ini tampak seperti usaha yang keliru dalam kasus terbaik (dan pabrik CVE dalam kasus terburuk). Saya benar-benar tidak melihat bagaimana manfaat keruh lebih besar daripada kerusakan kode yang jelas yang akan terjadi.

Saya juga berpikir bahwa sebagian besar pengguna kacang golang tidak akan sepenuhnya memahami implikasi dari perubahan ini. Dengan izin tim Go, postingan di golang-dev mungkin lebih efektif dalam menghasilkan diskusi (terutama karena ini adalah perubahan inti). Sebenarnya, ini secara teknis memengaruhi rantai alat Go, yang juga menggunakan LockFileEx dengan cara yang disebutkan di atas .


1 Ya, saya tahu bahwa beberapa penegakan kunci Windows masih akan lebih kuat daripada di POSIX, tetapi jika Anda dapat menghapus file tersebut, maka menggunakan file kunci untuk reservasi sumber daya akan keluar dari jendela.

Saya memposting ke golang-dev ketika saya membuka masalah.
https://groups.google.com/d/topic/golang-dev/R79TJAzsBfM/discussion

Apakah ada perpustakaan yang Anda daftarkan _bergantung pada_ perilaku GOOS=windows? Anda telah berulang kali mengklaim bahwa perubahan ini akan merusak kode yang ada, tetapi tidak pernah memberikan bukti apa pun.

Mohon berhenti membuat pernyataan sensasional yang tidak didukung. Itu tidak menjelaskan apa-apa, dan menurut saya itu menyinggung.

Saya memposting ke golang-dev ketika saya membuka masalah. https://groups.google.com/d/topic/golang-dev/R79TJAzsBfM/discussion

Sepertinya semua orang di sana menolak gagasan itu karena alasan mereka sendiri (semuanya mirip dengan yang disebutkan sebelumnya di utas ini), tetapi jika gagasan itu sekarang sedang diajukan untuk menerapkan perubahan ini di Go 1.14, tindak lanjut dari itu utas mungkin berguna (dengan tautan pengingat ke diskusi ini).

Apakah ada perpustakaan yang Anda daftarkan bergantung pada perilaku GOOS=windows? Anda telah berulang kali mengklaim bahwa perubahan ini akan merusak kode yang ada, tetapi tidak pernah memberikan bukti apa pun.

Itu akan membutuhkan penilaian keamanan penuh (dari banyak kode) untuk mengatakan dengan pasti. Saya rasa saya telah memberikan cukup bukti bahwa orang-orang mengandalkan perilaku yang akan berubah secara nyata dengan diperkenalkannya bendera ini. Dan itu hanya kode yang tersedia untuk umum di GitHub.

Mohon berhenti membuat pernyataan sensasional yang tidak didukung. Itu tidak menjelaskan apa-apa, dan menurut saya itu menyinggung.

Jika tautan dan kode tidak cukup mendukung, maka saya tidak tahu apa yang akan ada di buku Anda. Anda tidak akan menemukan kode yang berhenti dikompilasi karena perubahan ini, tetapi Anda pasti akan menemukan kode yang berperilaku berbeda dengannya, dan banyak orang akan menganggap kode itu "rusak" karena perubahan perilaku tersebut.

Jangan mengambil keberatan saya secara pribadi, saya pikir semua orang di sini (termasuk Anda) hanya mencoba menambahkan masukan mereka untuk memperbaiki bahasa. Jika Anda ingin memperjuangkan perubahan inti, Anda harus mengharapkan perlawanan yang sehat.

Seperti yang saya jelaskan di atas, menghapus file dengan FILE_SHARE_DELETE tidak memiliki fitur yang sama dengan UNIX. Sebagai contoh, silakan coba langkah-langkah ini.

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

Pertama silahkan jalankan main.exe dan selanjutnya jalankan watch.exe. Di UNIX, batalkan tautan (2) segera hapus file. Jadi proses lain yang melihat file yang ada dapat membuat file baru dengan nama file yang sama. Tetapi pada Windows, notepad.exe menampilkan dialog kesalahan "pelanggaran akses" karena Windows tidak segera menghapus file tersebut. Saya tidak bisa memberi tahu Anda apa yang akan terjadi dengan ini. Masalah keamanan? File penting hilang? Maaf, saya tidak tahu.

@havoc-io Windows tidak akan menghapus file yang terbuka sampai ditutup, dan hanya menahan kunci untuk file yang terbuka. Jadi penggunaan penguncian tidak menyiratkan ketergantungan pada kegagalan os.Remove(). Tolong berikan contoh yang bergantung secara khusus pada kegagalan itu.

Saya akan melakukan ping ke utas golang-dev minggu depan. Dua ppl membalasnya; seseorang belum membaca masalah ini, dan tidak memberikan contoh apa pun.

Klaim yang sensasional dan tidak didukung bukanlah "perlawanan yang sehat".

@havoc-io Windows tidak akan menghapus file yang terbuka sampai ditutup, dan hanya menahan kunci untuk file yang terbuka. Jadi penggunaan penguncian tidak menyiratkan ketergantungan pada kegagalan os.Remove(). Tolong berikan contoh yang bergantung secara khusus pada kegagalan itu.

@networkimprov Anda menggabungkan dua jenis penguncian yang berbeda.

File kunci (seperti yang dikunci dengan LockFileEx ) biasanya digunakan untuk menjaga sumber daya. Salah satu contoh mungkin untuk memastikan bahwa hanya ada satu instance daemon yang berjalan. Jika daemon Windows Go membuka sebuah file dan kemudian mengeksekusi LockFileEx pada file tersebut dan berhasil memperoleh kunci eksklusif, mungkin diasumsikan bahwa file kunci tidak akan dihapus dari bawahnya. Tetapi, jika FILE_SHARE_DELETE diaktifkan secara default, maka semua kode yang ada yang membuat asumsi itu tidak lagi benar. Jika beberapa agen tambahan menghapus file kunci itu (misalnya pengguna secara tidak sengaja menghapus direktori), daemon asli mungkin masih berjalan ketika yang baru memutuskan untuk memulai.

File kunci juga dapat digunakan untuk perintah pipa jika tanda LOCKFILE_FAIL_IMMEDIATELY dihilangkan, menyebabkan LockFileEx menunggu gilirannya untuk akuisisi kunci. Jika file kunci terhapus secara tidak sengaja, ini dapat menyebabkan beberapa perintah dijalankan sekaligus yang seharusnya sudah disalurkan.

Klaim yang sensasional dan tidak didukung bukanlah "perlawanan yang sehat".

Tidak ada yang membuat klaim sensasional atau tidak didukung di utas ini. Setiap orang yang tidak setuju dengan proposal Anda, termasuk saya, telah memberikan argumen yang kuat yang didukung oleh kode. Seperti halnya orang-orang yang telah mendukung proposal Anda.

Tolong tinjau dokumen WinAPI; Windows tidak akan menghapus file yang terbuka hingga file tersebut ditutup, dan hanya menahan kunci LockFileEx() untuk file yang terbuka, sehingga kunci tidak dapat bertahan saat file ditutup, terlepas dari penghapusan. Upaya untuk membuka file yang dikunci (yaitu terbuka) setelah dihapus akan melihat kesalahan, sehingga tidak ada kunci kedua yang dapat diperoleh. Utas menunggu di kunci membuka file, jadi tidak akan terganggu oleh penghapusan.

Kecuali saya salah membaca dokumen, dua skenario terakhir Anda tidak didukung. Dan ini sensasional dan tidak didukung: "perubahan ini tampak seperti ... pabrik CVE dalam kasus terburuk. Saya benar-benar tidak melihat bagaimana manfaat keruh lebih besar daripada kerusakan kode yang jelas yang akan terjadi."

@networkimprov Masalahnya adalah bahwa file kunci yang membatasi sumber daya biasanya didasarkan pada jalur file. Objek file yang mendasarinya mungkin tidak dihapus hingga file ditutup, tetapi jika dihapus melalui DeleteFile , itu tidak akan dapat diakses di jalur sebelumnya pada sistem file 1 , dan itu akan memungkinkan file kunci lainnya dibuat di jalur itu, meniadakan tujuan file kunci.

Saya tidak berpikir itu adalah komentar sensasional - tampaknya cukup jelas bahwa hal seperti ini dapat menyebabkan masalah keamanan jika kode mengandalkan invarian OS tertentu yang tidak lagi benar. Dan manfaatnya di sini tampaknya cukup keruh (seperti yang dikomentari beberapa orang: ini tidak menyelaraskan perilaku Windows dengan POSIX) dan kerusakannya tampak cukup jelas (setidaknya merusak perilaku penguncian tertentu).

1 Setidaknya itulah yang terjadi saat menguji dan berdasarkan komentar ini .

Re "dalam versi terbaru Windows, kami memperbarui DeleteFile (pada NTFS) untuk melakukan penghapusan 'POSIX', di mana file tersebut segera dihapus dari namespace" dari https://github.com/golang/go/issues/ 32088#issuecomment -502850674. AFAIK bahwa versi Windows tidak dirilis. Tes apa yang Anda maksud?

Manfaat kembali, saya pikir Anda harus meninjau seluruh utas.

AFAIK bahwa versi Windows tidak dirilis.

@networkimprov Saya yakin ini sudah terjadi pada Windows 10. Sepertinya saya dapat menghapus file yang dikunci dengan LockFileEx jika FILE_SHARE_DELETE ditentukan ke CreateFileW saat membuka file.

DeleteFileW() tidak akan memberikan kesalahan dalam skenario itu. Bagaimana Anda tahu itu segera dihapus dari sistem file, vs dihapus pada penutupan file/aplikasi? Apakah Anda mencoba membuka/membuat ulang file setelah menghapusnya?

Sebenarnya, ini secara teknis memengaruhi rantai alat Go, yang _juga_ menggunakan LockFileEx dengan cara yang disebutkan di atas.

Di dalam perintah go kami sangat menginginkan semantik POSIX, dan tidak boleh bergantung dengan cara apa pun pada tidak adanya FILE_SHARE_DELETE . Sebenarnya, saya menambahkan paket cmd/go/internal/robustio secara khusus untuk _menyelesaikan_ cara penguncian file Windows menyimpang dari POSIX.

@ianlancetaylor Saya telah memposting berulang kali ke golang-nuts & -dev mencari komentar dari proyek apa pun yang bergantung pada perilaku syscall.Open() Windows saat ini (tidak berdokumen). Tidak ada satu pun kasus penggunaan yang muncul.

Karena beberapa kasus penggunaan yang memerlukan FILE_SHARE_DELETE telah muncul di atas (dan karena Microsoft secara khusus meminta kami untuk menggunakannya), mari atur tanda ini secara default di pra-rilis 1.14, dan evaluasi ulang jika ada kasus penggunaan untuk permukaan perilaku asli selama siklus.

Seperti yang disarankan sebelumnya di utas ini, seseorang yang membutuhkan panggilan open() tanpa flag ini dapat menggulirkannya sendiri, menggunakan CreateFileW() & os.NewFile() .

Bisakah kita setidaknya mengekspos tombol untuk ini?

Ini sangat berguna untuk menerapkan rotasi log di windows.

Ini sangat berguna untuk menerapkan rotasi log di windows.

Saya tidak memastikan tetapi saya kira itu tidak akan berfungsi karena FILE_SHARE_DELETE tidak memungkinkan untuk membuat file baru dengan nama file asli jika pegangan file tidak ditutup.

@Random-Liu apakah Anda meminta cara untuk mengaktifkan fsd di Open(), atau menonaktifkannya?

@mattn Ini bekerja untuk saya. Setelah os.Rename file log lama, saya dapat membuat file log baru dengan nama asli, dan menginformasikan daemon untuk membuka kembali file log.

@networkimprov Saya ingin cara untuk mengaktifkan FILE_SHARE_DELETE . Saat ini saya menggunakan syscall.CreateFile secara langsung.

Versi windows saya:

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

@Random-Liu Tapi saya kira kita tidak bisa menggunakan ini untuk mensimulasikan perilaku UNIX.

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

Aplikasi eksternal tidak dapat mengakses nama file asli.

mattn, tidak perlu terus mengulangi poin itu. Sudah dinyatakan bahwa kita harus mendokumentasikan kebutuhan (pada Windows) untuk mengganti nama file sebelum menghapusnya ketika nama file harus dapat digunakan kembali sebelum pegangan ditutup.

Jika mengganti nama file sebelum ditutup oleh aplikasi itu sendiri, saya merasa tidak ada untungnya bagi pengguna yang ingin melakukan logrotate.

@ianlancetaylor coba lagi: https://github.com/golang/go/issues/32088#issuecomment -526074116

Saya telah menandai masalah ini sebagai pemblokir rilis.

@bcmils bisakah saya menarik minat Anda untuk memposting CL?

Baris ini: https://golang.org/src/syscall/syscall_windows.go#L291
Hanya perlu: sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)

Sunting: Tes untuk memverifikasi itu akan membuat file, lalu coba ganti namanya sebelum menutupnya.

Kami membutuhkan ini segera sehingga ada waktu untuk masalah apa pun muncul sebelum membeku...

Juga os.Remove() docs harus menyebutkan bahwa pada Windows, file yang terbuka harus diganti namanya sebelum dihapus jika nama file perlu tersedia untuk digunakan kembali sebelum file ditutup. Windows tidak menyelesaikan penghapusan pada file yang terbuka sampai file tersebut ditutup.

Sepertinya versi terbaru Windows memenuhi https://github.com/golang/go/issues/32088#issuecomment -502850674. Lihat https://github.com/papertrail/go-tail/pull/10#issuecomment -529460973 untuk detailnya. Kami sekarang dapat menggunakan kembali nama file yang dihapus, bahkan jika masih ada pegangan terbuka untuk file lama, asalkan pegangan itu dibuka dengan mode berbagi FILE_SHARE_DELETE . Tidak perlu mengganti nama sebelum menghapus. Mungkin masuk akal untuk menetapkan FILE_SHARE_DELETE secara default di Go?

Itu maksudnya; kita hanya perlu seseorang untuk mengirimkan CL. (Saya mau, tapi saya tidak bisa menandatangani PKB.)
Dua komentar saya sebelumnya menjelaskan perubahan yang diperlukan...

@jstarks @jordanrh1 @thaJeztah

Pertanyaan:
Bagaimana kita mendapatkan perilaku yang konsisten?
Jika program berjalan pada "Windows terbaru" itu akan memungkinkan pembuatan file dengan nama yang mirip dengan file "pending delete", versi yang lebih lama akan rusak karena kesalahan.

Ini mungkin sebenarnya mengatakan: "lulus tes / qa tetapi tidak bekerja pada sebagian besar target" sekarang, dan
"biasanya berfungsi, dan terkadang tidak" di masa mendatang (pengembang 2 tahun dari sekarang mendapatkan kesalahan "file sudah keluar" tidak akan dapat mereproduksi).

Kita perlu mencari cara untuk mengidentifikasi versi 'lama' dan mengubah perilaku default untuk itu atau memperbaiki kesalahan jika "ada penghapusan yang tertunda" - Saya tidak yakin bagaimana kita bisa melakukannya.
Dan jika kita tidak bisa, bagaimana kita menghadapi inkonsistensi.

@guybrand seperti yang dinyatakan di atas, kami akan mendokumentasikan bahwa di Windows "file yang terbuka harus diganti namanya sebelum dihapus jika nama file perlu tersedia untuk digunakan kembali sebelum file ditutup."

Ini berfungsi untuk versi Windows lama & baru serta POSIX.

Kedengarannya seperti pendekatan yang berbeda: "Selalu ganti nama sebelum digunakan kembali, bahkan pada Windows atau POSIX baru"
Saya menyukainya - ini mendukung praktik umum yang akan berhasil di mana-mana.

Ubah https://golang.org/cl/194957 menyebutkan masalah ini: syscall: allow FILE_SHARE_DELETE on syscall.Open on Windows

Utas golang-nuts ada di sini , dan tidak ada tanggapan. Itu sepertinya menunjukkan bahwa tidak ada orang yang membaca golang-nuts dan tahu tentang perilaku ini yang mengandalkannya.

Utas golang-dev ada di sini dan di sini , dan yang terakhir (utas yang lebih baru) juga tidak mendapat keberatan.

@alexbrainman -2'd CL untuk mengimplementasikannya berdasarkan jelas kurangnya keputusan, jadi saya telah melabel ulang masalah ini sebagai NeedsDecision sehingga kita bisa mendapatkan arah yang jelas.

Secara pribadi, bagi saya keputusannya tampak jelas: mengingat bahwa orang-orang dari Microsoft di utas ini mendukung perubahan perilaku default untuk menggunakan FILE_SHARE_DELETE (https://github.com/golang/go/issues/32088 #issuecomment-502850674), dan bahwa tidak seorang pun di golang-nuts tampaknya peduli dengan satu atau lain cara, saya pikir kita harus melakukannya.

Keberatan yang saya lihat di utas ini tampaknya terbagi dalam dua kategori:

  1. Kekhawatiran @alexbrainman (https://github.com/golang/go/issues/32088#issuecomment-504321027) dan @mattn (https://github.com/golang/go/issues/32088#issuecomment-494417146) tampaknya karena perilakunya masih tidak cocok dengan POSIX, pengguna yang mengharapkan semantik POSIX mungkin bingung. (Tetapi sudah menjadi kasus bahwa Open pada Windows tidak menyediakan semantik POSIX, jadi bagi saya kekhawatiran itu tampaknya tidak tergantung pada perubahan yang diusulkan.)

  2. Kekhawatiran @ havoc-io (https://github.com/golang/go/issues/32088#issuecomment-510314947) tampaknya FILE_SHARE_DELETE akan mengurangi invarian yang disediakan oleh paket penguncian file saat berjalan di Windows . Tetapi argumen itu tampaknya agak membingungkan bagi saya, karena paket konkret yang dipegang sebagai contoh ( github.com/gofrs/flock ) secara eksplisit mencatat bahwa "perilaku penguncian tidak dijamin sama di setiap platform", dan dari audit cepat tidak ada contoh konkret yang tampaknya bergantung pada interaksi khusus Windows antara penguncian dan penghapusan file.

Komentar di https://github.com/golang/go/issues/32088#issuecomment -498690757 membuat saya penasaran:

Saya setuju dengan @ianlancetaylor mengubah seluruh perilaku https://codereview.appspot.com/8203043/ bukan ide yang baik. Satu masalah langsung yang muncul dalam pikiran proposal ini saat menggunakan pegangan file sebagai file kunci. Kami menggunakan perilaku ini.

@maruel , dapatkah Anda memberikan lebih banyak detail tentang bagaimana program Anda bergantung pada interaksi khusus Windows antara penguncian file dan penghapusan atau penggantian nama file?

Kami di Chrome infra menggunakan keberadaan file sebagai mekanisme penguncian. Saya akan mengatakan untuk tidak terlalu khawatir tentang kasus penggunaan ini. Dalam praktik dari apa yang saya pahami, semantik O_CREATE + FILE_FLAG_DELETE_ON_CLOSE sudah cukup untuk kebutuhan ini dan ini dapat diganti dengan mutex di namespace Global\\ , yang sudah kita lakukan di basis kode python.

Solusi ini tidak disebutkan:

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()

Ini memiliki dua kelemahan:

  • Tutup() tidak tepat setelah Open()
  • Memulihkan kode kesalahan lebih rumit

@iwdgo Solusi ini untuk penggunaan proses tunggal. Sebagian besar diskusi di atas adalah tentang akses bersamaan dari beberapa proses. Itulah yang membuat bendera itu penting.

Untuk melengkapi @bcmils menutup kembali masalah ini, kami melihat tiga alat pengembangan lintas platform lainnya:

Mingw-w64 menjadikannya default tujuh tahun lalu:
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858

Erlang menjadikannya default lebih dari enam tahun yang lalu: erlang/ otp@0e02f48

Python tidak dapat mengadopsinya karena keterbatasan runtime MSVC pada saat mereka mempertimbangkannya: https://bugs.python.org/issue15244

Benang golang-kacang kedua juga tidak melihat keberatan:
https://groups.google.com/d/topic/golang-nuts/aRvSo3iKvJY/discussion

Apakah Anda ingin menjadikan FILE_SHARE_DELETE sebagai default untuk Open() ? Diskusi kita seharusnya tidak seperti itu.

  1. Kekhawatiran @alexbrainman ( #32088 (komentar) ) ... tampaknya karena perilakunya masih tidak cocok dengan POSIX ...

Perhatian utama saya adalah tentang melanggar program orang. Mirip dengan apa yang dikatakan @minux di https://codereview.appspot.com/8203043/#msg10

alasan lain: orang yang menggunakan windows menerima bahwa tidak dapat mengganti nama atau
menghapus file yang terbuka adalah norma dan sudah terbiasa dengan fakta itu.

Kami tidak dapat diam-diam mengubah perilaku program Go 1.0 di sini, mungkin orang
sebenarnya bergantung padanya.

Apa yang terjadi dengan janji kompatibilitas Go 1.0 di sini? Di mana di https://golang.org/doc/go1compat dikatakan, bahwa boleh saja mengubah perilaku file terbuka jika Anda mengumumkan niat Anda di go-nuts?

Mungkin kita bisa mengubah perilaku ini setelah kita memperkenalkan Go 2? Bisakah kita menggunakan modul untuk melindungi kode yang ada agar tidak rusak? Apakah saya salah berasumsi bahwa untuk itulah modul dirancang?

Secara pribadi, bagi saya keputusannya tampak jelas: mengingat bahwa orang-orang dari Microsoft di utas ini mendukung perubahan perilaku default untuk menggunakan FILE_SHARE_DELETE ...

Jika Microsoft adalah otoritas dalam hal ini untuk Anda, maka Anda harus melihat apa yang dilakukan alat pengembangan Microsoft.

Saya menggunakan program ini https://play.golang.org/p/4ZPmV6Df3SD mirip dengan apa yang diposting @mattn di https://github.com/golang/go/issues/32088#issuecomment -493753714 Saya membuatnya dengan kompiler C terbaru

Microsoft (R) C/C++ Mengoptimalkan Versi Kompilator 19.16.27030.1 untuk x64
Hak Cipta (C) Microsoft Corporation. Seluruh hak cipta.

Dan, ketika saya menjalankan program, saya tidak dapat menghapus file C:\Users\Alex\AppData\Local\Temp\a.txt sampai program itu ada.

Sama dengan program C# serupa https://play.golang.org/p/SuM2iWYpZir yang saya gunakan baru-baru ini

Microsoft (R) Visual C# Compiler versi 2.10.0.0 (b9fb1610)
Hak Cipta (C) Microsoft Corporation. Seluruh hak cipta.

Demikian pula program C ini https://play.golang.org/p/6HgxePzEW_W dibangun dengan Mingw terbaru

gcc (x86_64-win32-seh-rev0, Dibangun oleh proyek MinGW-W64) 7.3.0
Hak Cipta (C) 2017 Free Software Foundation, Inc.

memiliki efek yang sama. Jadi tidak satu pun dari alat tersebut yang memiliki flag FILE_SHARE_DELETE yang disetel secara default.

Dan secara pribadi saya tidak melihat manfaat dari perubahan itu. Itu termasuk kontribusi Go saya. Kami masih tidak dapat menghapus file yang dapat dieksekusi saat proses sedang berjalan. Kami masih akan mengalami masalah dalam menghapus direktori jika ada proses yang memiliki direktori saat ini yang disetel di sana.

Alex

Apakah Anda ingin menjadikan FILE_SHARE_DELETE sebagai default untuk Open() ?

Tujuannya adalah untuk membuat os.Open menggunakan FILE_SHARE_DELETE secara default. Ini https://golang.org/cl/194957 rencananya.

Alex

Perhatian utama saya adalah tentang melanggar program orang

Sejauh ini, tidak ada yang bisa menunjukkan satu contoh program yang akan rusak karena bergantung pada kesalahan saat mencoba mengganti nama/menghapus file yang dibuka dengan Go. Tetapi ada kasus yang terdokumentasi di mana perilaku saat ini merusak aplikasi Go di Windows.

Apa yang terjadi dengan janji kompatibilitas Go 1.0 di sini?

Dokumen Go untuk os.Rename() & .Remove() tidak membahas fitur Windows ini, tetapi memiliki 15 penyebutan tentang perilaku khusus Windows lainnya. BTW, Go 2 telah dikirimkan: https://blog.golang.org/go2-here-we-come

gcc (x86_64-win32-seh-rev0, Dibangun oleh proyek MinGW-W64) 7.3.0

C-lib siapa yang Anda gunakan dengan gcc -- mungkin milik Microsoft? Mingw-w64 berada di v6.0 dan menyertakan FILE_SHARE_DELETE di FILE_SHARE_VALID_FLAGS.

Seperti yang didokumentasikan di atas pada Python, di masa lalu runtime MSVC melarang kombinasi flag CreateFile() tertentu karena alasan yang tidak diketahui. Saya tidak tahu apakah itu masih terjadi, tetapi mungkin alasan MS tidak mengubah fopen () C-lib mereka.

Saya tidak melihat manfaat dari perubahan itu.

Manfaatnya telah dijelaskan berulang kali di atas, oleh beberapa orang; terutama mengizinkan os.Rename() pada file yang terbuka.

Ditandai sebagai proposal karena diskusi tidak mudah konvergen.

Untuk mencoba meringkas masalah dan diskusi sejauh ini:

  • Pada sistem Unix, jika satu proses membuka file, proses kedua dapat menghapus file itu. Proses asli dapat tetap menggunakan file (tidak memiliki nama lagi), termasuk membaca dan menulis, yang berhasil. Konten file tidak dihapus dari disk sampai referensi terakhir yang terbuka hilang.

  • Pada sistem Windows, secara default, jika satu proses membuka file, proses kedua _tidak_ dapat menghapus file itu. Sebuah file hanya dapat dihapus ketika tidak ada proses lain yang membukanya. Bendera FILE_SHARE_DELETE mengubah perilaku ini: jika semua referensi terbuka ke file dibuka dengan FILE_SHARE_DELETE, maka proses lain dapat memanggil DeleteFile, dan itu akan berhasil daripada gagal.

Perilaku Windows dengan FILE_SHARE_DELETE tampaknya masih agak berbeda dari Unix. Halaman MSDN mengatakan:

Fungsi DeleteFile gagal jika aplikasi mencoba untuk menghapus file yang memiliki pegangan lain terbuka untuk I/O normal atau sebagai file yang dipetakan memori (FILE_SHARE_DELETE harus telah ditentukan ketika pegangan lain dibuka).

Fungsi DeleteFile menandai file untuk dihapus saat ditutup. Oleh karena itu, penghapusan file tidak terjadi hingga pegangan terakhir ke file ditutup. Panggilan berikutnya ke CreateFile untuk membuka file gagal dengan ERROR_ACCESS_DENIED.

Artinya, file _name_ tidak dihapus dari sistem file sampai referensi terbuka hilang. Ini berbeda dengan Unix, di mana namanya menghilang sebelum referensi terbuka ditutup. Jadi, jika Anda menghapus file sebagai persiapan untuk membuat ulang file dengan nama yang sama, itu berfungsi di Unix tetapi tidak di Windows, bahkan dengan FILE_SHARE_DELETE.

Saran aslinya adalah mengubah syscall.Open untuk selalu menyetel tanda ini. Mengingat syscall seharusnya dekat dengan antarmuka kernel, menambahkan flag itu tampaknya tidak pada tempatnya. Tapi kita bisa mempertimbangkan apakah itu harus disetel selama os.Open / os.OpenFile.

@alexbrainman telah membuat argumen bahwa Unix dan Windows berbeda, mereka akan tetap berbeda bahkan jika kita menyetel flag ini, jadi kita tidak perlu bingung dengan membuat perubahan ini di belakang pengguna.

@jstarks dari Microsoft mengatakan bahwa Microsoft lebih suka kita untuk mengatur FILE_SHARE_DELETE secara otomatis dan bahwa "versi terbaru" Windows (tidak jelas yang mana, atau jika dirilis) mengubah DeleteFile agar semantik Unix dari nama menghilang saat kembali dari DeleteFile. Jadi jika kami melakukan ini, pada Windows terbaru itu, kami benar-benar akan mendapatkan perilaku yang sama di Unix dan Windows.

@networkimprov berpendapat bahwa kita harus membuat perubahan dan tidak ada contoh nyata dari program yang akan rusak.

@alexbrainman sepertinya masih belum yakin.

@mattn juga menyarankan agar kita tidak mengatur bendera secara otomatis, sebagian karena (setidaknya sampai semua orang menggunakan Windows terbaru) itu akan tetap berbeda dari Unix.

Secara keseluruhan, package os mencoba memberikan API portabel, dan sepertinya - terutama dengan perubahan Windows - menyetel flag secara otomatis akan membantu dengan itu. Ada flag lain yang kami atur secara otomatis di os.Open untuk membantu membuat perilaku pengguna tidak terlalu mengejutkan, khususnya O_CLOEXEC (close-on-exec). Program apa pun yang rusak di Windows karena menyetel flag ini di os.Open juga akan rusak di Unix, bukan?

Terutama karena setidaknya satu insinyur di Microsoft mengatakan bahwa kita harus mengaturnya, sepertinya tidak apa-apa untuk mengaturnya dalam paket os. Orang yang tidak ingin flag disetel masih bisa kembali ke paket syscall.

@alexbrainman dan @mattn , misalkan kita menyetel flag di os.Open. Kami tahu bahwa beberapa program akan bekerja lebih baik. Apakah Anda memiliki contoh spesifik dari program nyata (atau kemungkinan) yang akan rusak? Apakah program-program itu tidak akan rusak di Unix? Terima kasih.

@rsc , terima kasih atas masukan Anda. Mengaktifkan ini dalam paket "os" berfungsi untuk saya. Jika itu diadopsi, tanda syscall.Open() untuk mengaktifkan FILE_SHARE_DELETE harus disediakan untuk kelengkapan.

Untuk memperjelas ringkasan Anda, batasan proses bukanlah faktor; efek yang sama dalam satu proses. Dan bendera juga mempengaruhi penggantian nama file; dengan bendera itu selesai segera tidak seperti penghapusan file. (Dan itu memungkinkan solusi untuk ketersediaan nama file segera setelah dihapus.)

Terima kasih @networkimprov. Ya, saya mengerti bahwa batasan proses tidak sepenuhnya relevan; itu hanya membuat situasi lebih mudah untuk dijelaskan.

Saya telah mengubah judul dan merevisi rencana tindakan dalam teks terbitan:

a) os.Create/Open/OpenFile() harus selalu mengaktifkan file_share_delete di Windows,
b) syscall.Open() pada Windows harus menerima tanda yang mengaktifkan file_share_delete, dan
c) syscall pada Windows harus mengekspor konstanta untuk flag baru.

Dokumen untuk os.Remove() juga harus mencatat bahwa untuk menggunakan kembali nama file setelah menghapus file yang terbuka di Windows, seseorang harus melakukan: os.Rename(path, unique_path); os.Remove(unique_path) .

Kemungkinan bahwa ada pengguna yang mengimplementasikan pemrosesan eksklusif menggunakan perilaku os.Open Go saat ini tidak dapat disangkal.

Tetapi ada kasus yang terdokumentasi di mana perilaku saat ini merusak aplikasi Go di Windows.

Apa ini? Apa nomor masalah? Dan apa yang Anda maksud dengan kata break ? Kapan mereka putus?

C-lib siapa yang Anda gunakan dengan gcc -- mungkin milik Microsoft? Mingw-w64 berada di v6.0 dan menyertakan FILE_SHARE_DELETE di FILE_SHARE_VALID_FLAGS.

Saya tidak tahu. Saya baru saja mengunduh file x86_64-7.3.0-release-win32-seh-rt_v5-rev0.7z dari Internet. Anda dapat google untuk itu. Ini adalah versi 7.3.

Saya tidak melihat manfaat dari perubahan itu.

Manfaatnya telah dijelaskan berulang kali di atas, ...

Anda meninggalkan kata personally dari kutipan Anda. Saya berbicara tentang kebutuhan saya sendiri. Dan itu termasuk kontribusi proyek Go saya.

Untuk mencoba meringkas masalah dan diskusi sejauh ini:

Anda tidak mengatakan bahwa alat pengembang Microsoft di Windows tidak menyediakan tanda ini secara default. Lihat komentar saya https://github.com/golang/go/issues/32088#issuecomment -531713562

Anda juga mengabaikan komentar saya tentang kompatibilitas Go 1

Apa yang terjadi dengan janji kompatibilitas Go 1.0 di sini? Di mana di https://golang.org/doc/go1compat dikatakan, bahwa boleh saja mengubah perilaku file terbuka jika Anda mengumumkan niat Anda di go-nuts?

Bagaimana perubahan ini sesuai dengan janji kompatibilitas Go 1.0?

Secara keseluruhan, package os mencoba memberikan API portabel, dan sepertinya - terutama dengan perubahan Windows - menyetel flag secara otomatis akan membantu dengan itu.

Saya setuju perubahan ini bertujuan untuk orang-orang yang mem-porting perangkat lunak Unix ke Windows.

Sayangnya dengan mengorbankan pengguna Windows. Perubahan ini akan mengejutkan pengguna dan pengembang Windows, karena tidak ada program atau alat lain di Windows yang bekerja dengan cara ini. Mungkin di masa depan, tapi belum.

Dan bahkan untuk orang yang mem-porting perangkat lunak Unix ke Windows, perubahan ini melakukan pekerjaan yang sangat buruk IMHO. Lihat bagian 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. dari komentar saya https://github.com/golang/go/issues/32088#issuecomment -531713562

Ada flag lain yang kami atur secara otomatis di os.Open untuk membantu membuat perilaku pengguna tidak terlalu mengejutkan, khususnya O_CLOEXEC (close-on-exec). Program apa pun yang rusak di Windows karena menyetel flag ini di os.Open juga akan rusak di Unix, bukan?

Saya tidak tahu apa yang Anda maksud di sini. O_CLOEXEC di Windows berarti mencegah penanganan file lolos ke proses anak. Dan O_CLOEXEC selalu disetel di os.Open di Windows. Jadi tidak ada file yang dibuka dengan os.Open dapat diakses oleh proses anak di Windows. Terus? Kedengarannya masuk akal.

Apakah program-program itu tidak akan rusak di Unix?

Saya tidak mengerti pertanyaan Anda.

Apakah Anda memiliki contoh spesifik dari program nyata (atau kemungkinan) yang akan rusak?

Saya tidak tahu, apakah saya memiliki program seperti itu. Tidak mungkin untuk mengatakan. Saya tidak ingat semua program yang saya tulis. Saya tidak ingat apa yang mereka lakukan.

Tapi saya bisa membayangkan layanan Windows membuka file yang telah ditentukan, katakanlah c:\mysvc.txt , dan tetap buka. Bayangkan program lain yang memeriksa layanan hidup, dan restart layanan, jika perlu. Program lain ini hanya dapat mencoba dan menghapus c:\mysvc.txt , dan, jika file tersebut hilang, ia dapat dengan aman menganggap bahwa proses layanan sudah mati.

Juga, saya dapat membayangkan, beberapa program mungkin ingin mencegah pengguna menghapus atau memindahkan file mereka - https://stackoverflow.com/questions/11318663/prevent-a-user-from-deleting-moving-or-renaming-a-file

Alex

Berikut adalah titik data penyebaran ulang Go di Windows:
Win8 dikirim 7 tahun yang lalu.
Bug Go yang memengaruhi Win8+ ini dilaporkan hanya 5 bulan yang lalu: #31528

@rsc program apa pun yang mengharapkan kesalahan saat mencoba mengganti nama/menghapus file yang dibuka dengan Go rusak di Unix. Misalnya, skenario c:\mysvc.txt Alex.

Jika seseorang berkembang di Windows dan menyebar ke Linux/etc (atau sebaliknya), ini bisa menjadi sumber bug.

Kasus penggunaan kami adalah kami memiliki logger yang mencatat ke file.
File log mendukung rotasi (misalnya, tulis berikutnya jika file log > maxSize lalu putar).
Kami juga memiliki pembaca yang akan membuka file-file ini dan membiarkannya tetap terbuka saat sedang dibaca.

Bahkan tanpa kemampuan untuk menyetel FILE_SHARE_DELETE kita tidak dapat melakukan rotasi (mengganti nama dan/atau menghapus) saat ada pembaca aktif.

@mattn :

Kemungkinan bahwa ada pengguna yang mengimplementasikan pemrosesan eksklusif menggunakan perilaku os.Open Go saat ini tidak dapat disangkal.

Itu mungkin, tetapi kode seperti itu tetap salah di Unix. Paket os dimaksudkan untuk menjadi antarmuka portabel. Kami juga lebih tertarik pada program _aktual_ daripada kemungkinan.

@alexbrainman :

Anda tidak mengatakan bahwa alat pengembang Microsoft di Windows tidak menyediakan tanda ini secara default. Lihat komentar saya #32088 (komentar)

Ya tapi itu C; kita berbicara tentang Pergi. Kami tidak meniru API C secara umum. Kami mencoba menyediakan abstraksi yang sebagian besar portabel di package os. Poin tentang O_CLOEXEC adalah bahwa pada sistem Unix os.Open menyetel O_CLOEXEC meskipun tidak disetel secara default dalam panggilan C yang sesuai. Kami _do_ mencoba memberikan default yang berguna yang berperilaku kurang lebih sama di semua sistem.

Sejauh kompatibilitas Go 1, mengubah perilaku kasus sudut pada satu OS untuk menyelaraskan lebih baik dengan OS lain tampaknya OK. Kami tidak menjanjikan untuk mempertahankan semua ketidakcocokan lintas-sistem untuk sepanjang waktu.

Pengguna Windows yang bersikeras pada perilaku khusus Window selalu memiliki opsi untuk menggunakan syscall paket, yang akan memperjelas bahwa perilaku yang dicapai benar-benar hanya berlaku untuk Windows.

Sekali lagi, apakah Anda memiliki contoh nyata dari program nyata yang akan rusak?

@cpuguy83 , terima kasih atas kasus penggunaan dunia nyata dari program yang akan membantu.

@rsc Seperti yang Anda katakan, saya belum mengetahui efek putus dari FILE_SHARE_DELETE. Demikian pula, tidak ada konfirmasi yang ditemukan bahwa logrotate dapat diimplementasikan oleh perubahan ini.

Namun, setelah Go menyertakan perubahan ini yang dapat menghapus file, pengguna akan mengharapkan perilaku ini meskipun Go tidak dapat mengimplementasikan logrotate pada Windows dengan perubahan ini. Jadi kita harus berpikir matang-matang tentang perubahan ini. Pada dasarnya, Windows tidak dapat menghapus file yang tidak ditandai dengan FILE_SHARE_DELETE. Pengguna mungkin berpikir bahwa perubahan ini akan memungkinkan Go menghapus file apa pun. Secara khusus, file yang dibuat oleh os.NewFile dengan handle dapat memanipulasi metode struct os.File meskipun flag ini tidak disetel. Programmer harus tahu bahwa file tersebut dapat dihapus pada Windows atau tidak dengan sendirinya, saya pikir. Saya lebih suka mekanisme yang memungkinkan FILE_SHARE_DELETE diteruskan oleh flag untuk os.OpenFile daripada default. Saya pikir perilaku default harus diikuti ke OS.

Ya tapi itu C; kita berbicara tentang Pergi.

Dan C# juga. Saya tidak menyelidiki, tetapi saya cukup yakin alat pengembangan lain di Windows berperilaku seperti ini.

Sejauh kompatibilitas Go 1, mengubah perilaku kasus sudut pada satu OS untuk menyelaraskan lebih baik dengan OS lain tampaknya OK.

Ini adalah kasus sudut untuk pengguna non Windows. Untuk pengguna Windows, perilaku ini standar.

Sekali lagi, apakah Anda memiliki contoh nyata dari program nyata yang akan rusak?

Pertimbangkan contoh layanan saya di atas beton.

@cpuguy83 , terima kasih atas kasus penggunaan dunia nyata dari program yang akan membantu.

@ cpuguy83 dapatkah Anda mengatakan lebih banyak tentang masalah Anda. Saya ingin melihat bagaimana perubahan ini akan menyelesaikan masalah Anda, jadi saya ingin melihat beberapa kode nyata. Jika Anda dapat menyediakan beberapa program kecil, yang sekarang rusak, tetapi akan berfungsi setelah masalah ini diselesaikan, itu akan dihargai. Itu tidak harus membangun, tetapi setidaknya itu harus memberikan cukup bagi semua orang untuk menilai. Terima kasih.

Pada dasarnya, Windows tidak dapat menghapus file yang tidak ditandai dengan FILE_SHARE_DELETE. Pengguna mungkin berpikir bahwa perubahan ini akan memungkinkan Go menghapus file apa pun.

Saya setuju ini adalah poin yang bagus. Hanya file yang dibuka oleh program Go yang dapat dihapus. Tetapi semua file lain yang dibuka oleh program non Go akan tetap tidak dapat dihapus. Itu akan membuat os.Remove tidak dapat diprediksi - terkadang berhasil, dan terkadang tidak.

Alex

Saya memiliki kode yang memanggil syscall.Open() yang ditambal yang mengganti nama file yang terbuka. Begitulah cara Anda menerapkan rotasi log; berhasil.

// 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

Tetapi Anda tidak membutuhkan siapa pun untuk memberi tahu Anda bahwa :-/

Tentu saja os.File dari os.NewFile() bisa memiliki banyak properti yang berbeda dari os.Create/Open/OpenFile(), itu fitur, bukan kejutan.

Anda mengklaim bahwa perilaku Microsoft C fopen() adalah perilaku default Windows; itu tidak benar. Tidak ada mode berbagi default CreateFile(); itu bukan FILE_SHARE_READ | FILE_SHARE_WRITE .

Anda menegaskan bahwa program Windows yang disebarkan biasanya mengasumsikan bahwa __program oleh vendor lain__ selalu membuka file dengan cara tertentu. Jika demikian, Microsoft tidak akan meminta kami untuk mengaktifkan file_share_delete secara default.

@networkimprov

Tetapi Anda tidak membutuhkan siapa pun untuk memberi tahu Anda bahwa :-/

Saya ingin tahu bahwa proses eksternal dapat menghapus & mengganti nama file dengan benar dengan kasus apa pun.

Yang ingin saya ketahui adalah: setelah menyertakan perubahan itu, file log adalah output dari Go, logrotate yang dijalankan oleh proses eksternal berfungsi dengan baik, dan berfungsi sebagai produksi siap tanpa kesalahan.

@alexbrainman

Sayangnya kodenya rumit, tetapi ini adalah kode dunia nyata yang sebenarnya: https://github.com/moby/moby/blob/master/daemon/logger/loggerutils/logfile.go

Mitigasi yang diusulkan saat ini hanya fork os.OpenFile dan syscall.Open: https://github.com/moby/moby/pull/39974/files

Untuk lebih jelasnya, hanya memiliki opsi untuk melewati FILE_SHARE_DELETE dari OpenFile atau bahkan ke syscall.Open sudah cukup untuk menangani kasus kita.

Sayangnya kodenya rumit, tetapi ini adalah kode dunia nyata yang sebenarnya: https://github.com/moby/moby/blob/master/daemon/logger/loggerutils/logfile.go

Mitigasi yang diusulkan saat ini hanya fork os.OpenFile dan syscall.Open: https://github.com/moby/moby/pull/39974/files

@ cpuguy83 terima kasih atas penunjuknya.

Sayangnya pandangan singkat saya tidak memberikan info yang cukup untuk menilai apakah masalah saat ini adalah solusinya. Saya harus mulai dengan repro (mungkin ini https://github.com/moby/moby/issues/39274#issuecomment-497966709 ) dan mengambilnya dari sana. Saya tidak yakin kapan saya akan punya waktu untuk ini. Jika Anda memiliki saran yang lebih baik daripada rencana saya, beri tahu saya.

Alex

Di bawah ini adalah demo kerja rotasi log praktik standar, dengan 100 goroutine masing-masing mencatat 50 baris pada interval 0,1-2 detik setiap kali mereka membuka file log, dan goroutine lain memutar log pada interval 1 menit. Saya menjalankan ini selama beberapa jam kemarin di laptop Win7 tanpa kesalahan.

Itu dibangun dengan tambalan ke syscall.Open() yang memungkinkan FILE_SHARE_DELETE; gagal sebaliknya. Seseorang dapat membuat skema logging yang lebih kompleks tanpa flag ini, tetapi ada banyak kegunaan lain untuk itu; kode saya sendiri mengganti nama file yang terbuka karena berbagai alasan.

@rsc , saya yakin ini melengkapi kasus untuk saran Anda, jika masih ada keraguan. (Dan seperti disebutkan di atas, saya memposting berulang kali di golang-dev dan golang-nuts meminta proyek yang bergantung pada perilaku saat ini, dengan hasil nol.)

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
      }
   }
}

Tambal ke 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 File tidak diganti namanya oleh proses eksternal. Jika mengganti nama file dilakukan dalam proses yang sama, ada beberapa cara lain untuk mengimplementasikannya.

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

Di bawah ini adalah demo kerja rotasi log praktik standar

Terima kasih atas contoh Anda, @networkimprov . Saya bisa melihat sekarang apa yang Anda coba lakukan.

Satu-satunya masalah dengan kode Anda adalah, jika beberapa proses eksternal akan membuka file yang Anda tulis tanpa FILE_SHARE_DELETE, maka Anda akan memiliki masalah yang sama seperti yang Anda miliki sekarang terlepas dari perubahan yang kami buat di Go.

@cpuguy83 Saya tidak akan melihat masalah Anda (seperti yang saya janjikan di sini https://github.com/golang/go/issues/32088#issuecomment-536233195 ), karena @networkimprov contoh memberi saya gambaran tentang apa yang dia coba melakukan.

Alex

Alex, seseorang dapat mengulang upaya penggantian nama jika akses luar diizinkan, atau membatasi file log aktif jika tidak.

Mattn, pergantian nama rotate biasanya dilakukan dengan proses terpisah.

Sepertinya tidak ada konsensus yang jelas di sini. Pengembang dengan latar belakang Unix tampaknya mendukung hal ini, tetapi dua pengembang Windows kami yang paling aktif ( @alexbranman dan @mattn) tidak. Perubahan juga hanya akan benar-benar menyatukan versi terbaru Windows yang memiliki perilaku mirip Unix baru untuk FILE_SHARE_DELETE.

Mungkin ada baiknya meninjau kembali topik ini dalam beberapa tahun, untuk melihat seberapa luas tersedia perilaku bendera baru dan apakah bahasa lain telah menyatu dengan perilaku umum. Jika seseorang ingin mengajukan masalah baru dalam, katakanlah, dua tahun atau lebih untuk memikirkan kembali ini, itu tidak masalah.

Tetapi untuk saat ini, mengingat kurangnya konsensus, ini sepertinya kemungkinan akan menurun .

Meninggalkan terbuka selama seminggu untuk komentar akhir.

Apakah ada perselisihan tentang hanya menyediakan opsi?

Jika kita mencari lebih banyak suara untuk didengungkan, saya pasti akan melakukan _sesuatu_. Saat ini, satu-satunya solusi adalah menyalin banyak kode dari pustaka standar Go, mengubah satu baris, dan kemudian mempertahankan fork itu selamanya. Setiap implementasi monitor log atau "ekor" langsung tampaknya melakukan ini.

Sebagai contoh, lihat https://github.com/elastic/beats/blob/master/libbeat/common/file/file_windows.go#L85 -L103

Jika saya mengikuti dengan benar, proposal ini sebenarnya memiliki dua bagian:

  1. Izinkan penggunaan flag FILE_SHARE_DELETE saat membuka file -
    implementasi harus mudah dan aman, pengembang harus secara eksplisit
    minta mode ini saat membuka file baru
  2. Aktifkan tanda ini secara default - dapat berisiko, dan hanya didukung dengan baik
    saat menggunakan build windows 10 terakhir.

Jika semua setuju pada 1, mungkin kita bisa mengizinkan bendera saat pengembang
secara eksplisit memintanya untuk saat ini, dan mengunjungi kembali 2 dalam beberapa tahun.

Pada Rabu, 2 Okt 2019, 19:32 Mark Dascher, [email protected] menulis:

Jika kita mencari lebih banyak suara untuk didengungkan, saya pasti akan mendukungnya
dari melakukan sesuatu . Saat ini, satu-satunya solusi adalah menyalin secara harfiah
sekumpulan kode dari perpustakaan standar Go, ubah satu baris, dan kemudian
menjaga garpu itu selamanya. Setiap implementasi monitor log atau live
"ekor" akhirnya tampaknya melakukan ini.

Sebagai contoh, lihat
https://github.com/elastic/beats/blob/master/libbeat/common/file/file_windows.go#L85 -L103


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=ABNEY4VFQLSYVI66ENT6G4LQMTLJ7A5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVQW114Zcommentor5
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/ABNEY4U5L3WFZYNIUOSEY2TQMTLJ7ANCNFSM4HNPNYIA
.

Pasti mendukung _setidaknya_ memiliki 1. (Izinkan penggunaan flag FILE_SHARE_DELETE saat membuka file)

Hanya untuk meninjau kembali proposal saya sebelumnya dan menindaklanjuti dengan contoh implementasi :

Bendera syscall.FILE_SHARE_DELETE akan cocok dengan baik dalam parameter os.OpenFile 's flag dan akan menyediakan mekanisme sepele untuk mengaktifkan perilaku ini sesuai permintaan. Itu tidak bertabrakan dengan flag os.O_* di Windows (dan ini dapat ditegakkan/direkayasa) dan ini menyediakan cara idiomatis untuk menentukan perilaku pembukaan file khusus sistem (sejauh os ' s antarmuka berorientasi POSIX dapat dianggap idiomatis pada Windows). Ini adalah rute yang sama yang telah digunakan untuk meneruskan perilaku pembukaan file khusus sistem pada platform POSIX (mis. syscall.O_DIRECTORY di Linux).

Bahkan jika keputusan dibuat untuk tidak menyalakannya secara default (yang menurut saya adalah panggilan yang tepat), saya setuju bahwa flag memiliki kasus penggunaannya (termasuk yang ditunjukkan oleh @networkimprov) dan akan berguna untuk menjadi dapat menyalakannya tanpa harus menggunakan panggilan CreateFileW dan menyalin kode boilerplate.

Harap pertimbangkan alternatif [1] di atas yang memungkinkan pengembang opsi untuk menyetel FILE_SHARE_DELETE bila perlu dan mencegah potongan bercabang dari kode pustaka standar Go.

__Rust__ juga menyetel tanda ini secara default, dan memungkinkan Anda untuk _mematikan_ salah satu opsi FILE_SHARE_*.
https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html

@rsc bisakah saya bertanya argumen teknis mana yang dibuat terhadap proposal yang tidak disangkal?

@networkimprov setelah membaca seluruh proposal dan diskusi, saya pikir Anda perlu menutup ini dan membuka kembali masalah baru secara eksplisit tentang menambahkan flag khusus windows baru ke paket ossyscall. Ini akan memastikan bahwa perilaku baru tersebut adalah memilih untuk ikut dan bukan untuk tidak ikut serta. Proposal saat ini menyiratkan perubahan kode yang ada, yang mengkhawatirkan orang.

@rsc bisakah saya bertanya argumen teknis mana yang dibuat terhadap proposal yang tidak disangkal?

Maaf, tapi bukan itu cara kerja proses proposal . Tidaklah cukup untuk "membantah" argumen orang lain. "Tujuan dari proses proposal adalah untuk mencapai konsensus umum tentang hasil pada waktu yang tepat." Tidak ada konsensus yang jelas tentang jalan ke depan di sini. Argumen teknis telah dibuat, dan mereka tidak meyakinkan pengembang kunci Go yang telah memberikan kontribusi signifikan ke port Windows (yaitu, @alexbrainman dan @mattn). Selain kurangnya konsensus yang jelas, tidak ada tanda yang jelas tentang urgensi untuk melakukan sesuatu hari ini: Go bekerja dengan baik di Windows selama hampir 10 tahun sebelum masalah ini diajukan. Seperti yang saya pahami, meninggalkan semuanya sendiri berarti Go akan terus bekerja seperti biasanya.

Saya mengajukan #34681 untuk menyediakan cara yang lebih mudah untuk membuka file dengan FILE_SHARE_DELETE jika (masih mungkin) jika yang ini ditolak. Tampaknya lebih membantu untuk memulai utas baru yang terbatas pada gagasan itu daripada melanjutkan yang ini, yang sudah sangat lama.

@rsc , sebelum Anda menolaknya, mari dengarkan pendapat Alex tentang proposal O_ALLOW_DELETE Anda.

Di awal diskusi ini, dia menentang flag os.OpenFile() baru yang menetapkan file_share_delete, untuk alasan yang sama dia tidak setuju dengan saran os.Create/Open/OpenFile() Anda. Dia khawatir bahwa program lain menganggap bahwa tidak ada yang pernah membuka file seperti itu, karena MSVC fopen() tidak dapat melakukannya. Oleh karena itu, program-program tersebut (masih belum ditentukan) akan merusak program-program Go yang menyetel flag.

Alex, seseorang dapat mengulangi upaya penggantian nama jika akses luar diizinkan, ...

Jika Anda siap untuk mengulang penggantian nama, Anda tidak perlu mengubah apa pun dalam kode repo Go. Program Anda akan bekerja sebaik itu bekerja sekarang.

Saat ini, satu-satunya solusi adalah menyalin banyak kode dari pustaka standar Go, mengubah satu baris, dan kemudian mempertahankan fork itu selamanya.

Saya pikir menyalin kode ke dalam paket terpisah tidak masalah. Saat menyelidiki https://github.com/moby/moby/pull/39974 saya mendapat ide yang sama. Saya tidak berpikir ada banyak kode di sana untuk dipelihara. Sebenarnya saya menerapkan hal itu

https://github.com/alexbrainman/goissue34681

Jangan ragu untuk menyalin atau menggunakan apa adanya. Mungkin, mengingat begitu banyak minat pada fungsi ini, Anda benar-benar membuat paket yang tepat yang dapat dibagikan dan digunakan oleh orang lain. Dengan cara ini Anda dapat tetap memperbaruinya dan memperbaiki bug.

@rsc , sebelum Anda menolaknya, mari dengarkan pendapat Alex tentang proposal O_ALLOW_DELETE Anda.

Silakan, coba

https://github.com/alexbrainman/goissue34681

pertama. Jika Anda tidak puas karena suatu alasan, kami dapat mendiskusikan penambahan fungsionalitas baru ke repo Go.

Terima kasih.

Alex

Saya sangat kecewa dengan tanggapan ini.

Menyalin petak besar kode ini, yang sebagian besar bahkan tidak dipahami (sebagai
mengapa ini melakukan apa yang dilakukannya) supaya kami dapat menambahkan satu opsi
bahwa sistem itu sendiri mendukung, hanya saja Go, kemungkinan besar saya berasumsi tidak
bahkan dengan sengaja, tidak menyediakan sarana untuk memberikan opsi bahkan ke
syscall.Open sepertinya situasi yang konyol.

Mengapa saya harus menulis ulang syscall.Open untuk meneruskan opsi ini?

Pada Sabtu, 5 Oktober 2019 pukul 18:43 Alex Brainman [email protected] menulis:

Alex, seseorang dapat mengulangi upaya penggantian nama jika akses luar diizinkan, ...

Jika Anda siap untuk mengulang penggantian nama, Anda tidak perlu mengubah apa pun
dalam kode repo Go. Program Anda akan bekerja sebaik itu bekerja sekarang.

Saat ini, satu-satunya solusi adalah benar-benar menyalin banyak kode dari
Pustaka standar Go, ubah satu baris, lalu pertahankan garpu itu selamanya.

Saya pikir menyalin kode ke dalam paket terpisah tidak masalah. Saat menyelidiki
moby/moby#39974 https://github.com/moby/moby/pull/39974 Saya mendapatkan hal yang sama
ide. Saya tidak berpikir ada banyak kode di sana untuk dipelihara. sebenarnya aku
dilaksanakan hanya itu

https://github.com/alexbrainman/goissue34681

Jangan ragu untuk menyalin atau menggunakan apa adanya. Mungkin, mengingat, ada begitu banyak minat pada
fungsi ini, Anda benar-benar membuat paket yang tepat yang dapat dibagikan
dan digunakan oleh orang lain. Dengan cara ini Anda dapat tetap memperbaruinya dan memperbaiki bug.

@rsc https://github.com/rsc , sebelum Anda menolak ini, mari kita dengar apa
Alex memikirkan proposal O_ALLOW_DELETE Anda.

Silakan, coba

https://github.com/alexbrainman/goissue34681

pertama. Jika Anda tidak puas karena suatu alasan, kita bisa mendiskusikan menambahkan yang baru
fungsionalitas ke Go repo.

Terima kasih.

Alex


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=AAGDCZXHULQEMHAPTO6ZUJTQNFGE7A5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVQX2ZLONDOR7WSQ2ZLONDOR5WXHJKTDN5
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AAGDCZWYUNMCTAGO567AV73QNFGE7ANCNFSM4HNPNYIA
.

>

  • Brian Goff

Saya sangat kecewa dengan tanggapan ini.

Saya minta maaf Anda merasa seperti itu. Tapi apakah Anda benar-benar mencoba menggunakan paket saya?

Alex

@rsc , karena Alex juga menentang flag os.OpenFile() , haruskah kita tidak melakukan apa-apa?

Bagaimana dengan menempatkan fitur ini di belakang flag build?

Mengenai apakah "Go berfungsi dengan baik di Windows selama hampir 10 tahun," jelas tidak, jika Anda perlu mengganti nama file yang terbuka. (Itu juga rusak pada laptop Windows 8/10 selama 7 tahun terakhir.)

Saya memiliki fork os.OpenFile dan syscall.Open saya sendiri di moby/moby.

Mengapa kita begitu meremehkan di sini?

Pada Tue, 8 Oktober 2019 di 01:47 Alex brainman [email protected] menulis:

Saya sangat kecewa dengan tanggapan ini.

Saya minta maaf Anda merasa seperti itu. Tapi apakah Anda benar-benar mencoba menggunakan paket saya?

Alex


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/golang/go/issues/32088?email_source=notifications&email_token=AAGDCZRV72GY4IJQJVJWMYTQNRCIHA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMV46WZ2ZLONATAW46WZ2ZLONATAW46XHJKTDN541W
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AAGDCZRNYSMIE6BX77XJVWDQNRCIHANCNFSM4HNPNYIA
.

>

  • Brian Goff

Saya sarankan proposal khusus ini ditolak. Kami tidak akan memperkenalkan sekumpulan bug keamanan TOCTOU untuk pengguna Windows yang mengandalkan perilaku os.OpenFile yang ada.

Pertanyaan yang lebih besar di sini adalah, "bagaimana kami dapat memaparkan kepada pengguna Go berbagai macam flag menarik untuk fungsi Windows CreateFile dan NtCreateFile ?" Saya berharap kita akan dapat mengatasinya dalam proposal yang berbeda, tetapi tentu saja tidak dengan mengaktifkan semuanya secara default seperti yang disarankan di sini.

@zx2c4 apakah Anda membaca seluruh

Sudah seminggu sejak https://github.com/golang/go/issues/32088#issuecomment -537590826, dan masih belum ada konsensus yang jelas, yang berarti kita harus menolaknya.

Ditolak.

Saya telah memposting ringkasan opsi dengan pro & kontra di https://github.com/golang/go/issues/34681#issuecomment -565853605.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat