Apakah aman untuk memindahkan file yang ditambahkan?

28

Saya memiliki proses node.js yang digunakan fs.appendFileuntuk menambahkan baris file.log. Hanya baris lengkap sekitar 40 karakter per baris ditambahkan, mis. Panggilan mirip fs.appendFile("start-end"), bukan 2 panggilan suka fs.appendFile("start-")dan fs.appendFile("end"). Jika saya memindahkan file ini ke file2.logdapatkah saya memastikan bahwa tidak ada baris yang hilang atau disalin sebagian?

Halus
sumber

Jawaban:

36

Selama Anda tidak memindahkan file melintasi batas sistem file, operasi harus aman. Ini karena mekanisme, bagaimana »bergerak« sebenarnya dilakukan.

Jika Anda mvfile pada sistem file yang sama, file tersebut tidak benar-benar disentuh, tetapi hanya entri sistem file yang diubah.

$ mv foo bar

sebenarnya melakukan sesuatu seperti

$ ln foo bar
$ rm foo

Ini akan membuat tautan keras (entri direktori kedua) untuk file (sebenarnya inode yang ditunjuk oleh entri sistem file) foobernama bardan menghapus fooentri. Karena sekarang ketika menghapus foo, ada entri sistem file kedua yang menunjuk ke fooinode, menghapus entri lama footidak benar-benar menghapus blok milik inode.

Program Anda akan dengan senang hati menambahkan file, karena file-handle yang terbuka menunjuk ke inode file, bukan entri sistem file.

Catatan: Jika program Anda menutup dan membuka kembali file di antara waktu menulis, Anda akhirnya akan membuat file baru dengan entri sistem file lama!

Pemindahan lintas sistem file:

Jika Anda memindahkan file melintasi batas sistem file, segalanya menjadi jelek. Dalam hal ini Anda tidak dapat menjamin agar file Anda tetap konsisten, karena mvsebenarnya

  • buat file baru di sistem file target
  • salin isi dari file lama ke file baru
  • hapus file lama

atau

$ cp /path/to/foo /path/to/bar
$ rm /path/to/foo

resp.

$ touch /path/to/bar
$ cat < /path/to/foo > /path/to/bar
$ rm /path/to/foo

Bergantung pada apakah penyalinan mencapai akhir file selama penulisan aplikasi Anda, bisa jadi Anda hanya memiliki setengah baris dalam file baru.

Selain itu, jika aplikasi Anda tidak menutup dan membuka kembali file lama, itu akan terus menulis ke file lama, bahkan jika itu tampaknya dihapus: kernel tahu file mana yang terbuka dan meskipun itu akan menghapus entri sistem file, itu tidak akan menghapus inode file lama dan blok terkait sampai aplikasi Anda menutup pegangan file yang terbuka.

Andreas Wiese
sumber
3
FYI, Unix versi awal tidak memiliki rename()panggilan sistem. Jadi versi asli dari mvpanggilan sebenarnya link()untuk membuat tautan keras, diikuti oleh unlink()untuk menghapus nama asli. rename()ditambahkan di FreeBSD, untuk mengimplementasikannya secara atomik di kernel.
Barmar
Maaf tapi apa itu file-system borders?
laike9m
1
@ laike9m - Batas sistem file merujuk pada kenyataan bahwa sistem file sederhana harus berada di satu partisi pada satu perangkat memori seperti drive disk. Jika Anda mengganti nama file di dalam sistem file, semua perubahan itu adalah namanya dalam entri direktori. Masih memiliki inode yang sama - jika itu dalam sistem file berdasarkan inode untuk memulai - seperti kebanyakan sistem file Linux. Tetapi, jika Anda memindahkan file ke sistem file lain, data aktual harus dipindahkan dan file tersebut akan mendapatkan inode baru dari sistem file baru. Ini akan mengganggu operasi apa pun pada file yang sedang berlangsung saat ini terjadi.
Joe
9

Karena Anda mengatakan Anda menggunakan node.js, saya menganggap Anda akan menggunakan fs.rename()(atau fs.renameSync()) untuk mengubah nama file. Metode node.js ini didokumentasikan untuk menggunakan rename (2) system call, yang tidak menyentuh file itu sendiri dengan cara apa pun, tetapi hanya mengubah nama yang terdaftar dalam sistem file:

" rename () ganti nama file, pindahkan antar direktori jika diperlukan. Tautan keras lain ke file (seperti yang dibuat menggunakan tautan (2) ) tidak terpengaruh. Buka deskriptor file untuk oldpath juga tidak terpengaruh."

Secara khusus, perhatikan kalimat terakhir yang dikutip di atas, yang mengatakan bahwa setiap deskriptor file terbuka (seperti program Anda akan digunakan untuk menulis ke file) akan terus menunjukkannya bahkan setelah diubah namanya. Dengan demikian, tidak akan ada kehilangan data atau kerusakan bahkan jika file diganti namanya saat sedang ditulis secara bersamaan.


Seperti Andreas Weise catat dalam jawabannya , rename (2) system call (dan dengan demikian fs.rename()dalam node.js) tidak akan bekerja melintasi batas filesystem. Dengan demikian, upaya untuk memindahkan file ke sistem file yang berbeda dengan cara ini akan gagal.

mvPerintah Unix mencoba untuk menyembunyikan batasan ini dengan mendeteksi kesalahan dan, sebaliknya, memindahkan file dengan menyalin kontennya ke file baru dan menghapus yang asli. Sayangnya, memindahkan file seperti ini tidak menghilangkan data risiko jika file dipindahkan saat sedang ditulis. Jadi, jika Anda ingin mengganti nama file yang dapat ditulis secara simultan secara aman, Anda tidak boleh menggunakan mv(atau, setidaknya, Anda harus benar-benar memastikan bahwa jalur baru dan lama berada di sistem file yang sama).

Ilmari Karonen
sumber