Linux copy_file_range Memisahkan Semantik Penyalinan dari Implementasi Transfer

Pemanggilan copy_file_range() yang berhasil melaporkan jumlah byte, bukan janji mengenai jalur fisik yang ditempuh byte tersebut. Linux dapat memenuhi permintaan melalui akselerasi spesifik filesystem, jalur transfer di dalam kernel, atau implementasi lain yang diizinkan interface filesystem aktif. Aplikasi memperoleh operasi penyalinan range dengan semantik offset dan nilai balik yang terdefinisi; aplikasi tidak memperoleh jaminan bahwa blok storage diduplikasi secara fisik.

Batas ini penting pada replikasi file, staging backup, pengisian cache, dan pemindahan object lokal. Kode yang memperlakukan pemanggilan ini hanya sebagai versi lebih cepat dari loop read() dan write() dapat melewatkan properti yang tampak pada batas API: short copy sah, sparse hole dapat berubah menjadi data teralokasi, dukungan lintas filesystem bersifat kondisional, dan implementasi filesystem dapat memanfaatkan sharing copy-on-write.

Kontraknya adalah operasi range, bukan topologi transfer

copy_file_range() menerima descriptor input, pointer offset input opsional, descriptor output, pointer offset output opsional, jumlah byte yang diminta, serta flags. Interface Linux saat ini mengharuskan flags bernilai nol.

Ketika pointer offset bernilai null, offset saat ini pada open file description terkait ikut dipakai dan bertambah sesuai jumlah byte yang disalin. Ketika pointer offset non-null diberikan, nilai offset yang ditunjuk akan bertambah, sementara file offset milik descriptor tidak berubah.

Perbedaan ini membuat operasi dapat dipakai dalam dua model koordinasi. Caller dapat sengaja memakai posisi file bersama, atau membawa state range eksplisit tanpa mengubah posisi tersebut. Kedua model memiliki konsekuensi concurrency berbeda ketika beberapa descriptor merujuk open file description yang sama.

Kernel juga mengizinkan descriptor sumber dan tujuan merujuk file yang sama, selama range sumber dan tujuan tidak overlap. Larangan overlap merupakan bagian kontrak operasi, bukan sekadar saran struktur aplikasi.

Nilai balik positif dapat lebih kecil dari permintaan

Keberhasilan tidak berarti seluruh jumlah yang diminta telah dipindahkan. Nilai balik adalah jumlah byte yang disalin, dan Linux secara eksplisit mengizinkan nilainya lebih kecil dari ukuran permintaan.

Routine penyalinan lengkap karena itu memerlukan state machine di sekitar syscall, bukan satu pemeriksaan sukses:

while (remaining > 0) {
    ssize_t n = copy_file_range(src, &in_off, dst, &out_off,
                                remaining, 0);
    if (n > 0) {
        remaining -= (size_t)n;
        continue;
    }
    if (n == 0)
        break;
    /* inspect errno and apply the caller's error policy */
}

Nilai balik nol berarti tidak ada byte yang disalin. Ketika posisi sumber berada pada atau setelah akhir file, nol merupakan hasil yang diharapkan. Caller yang memiliki panjang sumber dari sumber informasi terpisah dapat membedakan batas akhir yang diharapkan dari operasi yang selesai secara tidak lengkap melalui perhitungan range miliknya sendiri.

Ini merupakan batas umum pada sistem: selesainya syscall dan selesainya operasi tingkat aplikasi adalah dua fakta terpisah. Kernel melaporkan progres satu invocation. Caller menentukan apakah progres tersebut memenuhi transaksi penyalinan yang lebih besar.

Akselerasi penyalinan dapat mengubah storage fisik tanpa mengubah byte logis

Interface ini memberi filesystem peluang untuk mengakselerasi penyalinan. Filesystem dapat mendukung mekanisme seperti sharing copy-on-write bergaya reflink, tempat sumber dan tujuan pada awalnya merujuk storage fisik bersama alih-alih langsung memperoleh dua salinan blok independen.

Dari sudut pandang byte aplikasi, mekanisme itu tetap dapat memenuhi penyalinan range yang diminta. Write berikutnya tunduk pada perilaku copy-on-write filesystem. Optimisasi ini berada di bawah batas konten logis, tetapi dapat tetap terlihat melalui alokasi storage, efek quota, fragmentasi, dan tool inspeksi spesifik filesystem.

Kode tidak boleh menyimpulkan representasi fisik tertentu dari pemanggilan yang berhasil. Asumsi sebaliknya juga tidak aman: aplikasi tidak dapat mewajibkan perilaku reflink hanya karena interface memungkinkan filesystem memakainya. Deployment yang memerlukan block sharing sebagai syarat semantik perlu memakai interface yang kontraknya secara eksplisit meminta cloning serta menangani filesystem yang tidak mendukungnya.

copy_file_range() dengan demikian merupakan abstraksi atas strategi transfer, bukan API untuk memilih satu strategi transfer.

Input sparse tidak menjamin output sparse

Sparse file dapat memiliki region logis yang terbaca sebagai nol tanpa blok data teralokasi yang bersesuaian. Menyalin range byte tidak selalu mempertahankan topologi alokasi tersebut.

Dokumentasi Linux secara eksplisit mengizinkan copy_file_range() memperluas hole pada range sumber. Tujuan karena itu dapat berisi byte logis yang sama tetapi menempati layout fisik berbeda.

Hal ini memisahkan ekuivalensi konten dari ekuivalensi representasi. Bagi banyak consumer, byte identik adalah properti yang dibutuhkan dan layout sparse hanya insidental. Bagi tool disk image, sistem arsip, atau pipeline yang sensitif terhadap quota, mempertahankan hole dapat menjadi bagian dari hasil yang diwajibkan.

Copier yang sadar sparse dapat menemukan region data dan hole dengan interface filesystem seperti SEEK_DATA dan SEEK_HOLE, jika didukung, lalu menerbitkan operasi penyalinan hanya untuk data extent. Desain itu memiliki kondisi platform dan filesystem tersendiri; mode seek tersebut bukan deskripsi universal untuk setiap backend storage.

Pertanyaan desain yang relevan bukan hanya apakah tujuan dapat dibaca dengan benar. Kontrak aplikasi juga perlu menetapkan apakah struktur alokasi termasuk bagian hasil.

Perilaku lintas filesystem bersifat kondisional

Perilaku historis syscall pada batas filesystem telah berubah. Pada semantik Linux saat ini, penyalinan lintas filesystem bukan kapabilitas tanpa syarat. Dukungan bergantung pada tipe filesystem sumber dan tujuan serta implementasi operasi tersebut.

Sejak semantik Linux 5.19, kombinasi lintas filesystem yang tidak didukung dapat gagal dengan EXDEV, sedangkan filesystem yang tidak mendukung operasi dapat melaporkan EOPNOTSUPP. Aplikasi yang menargetkan beragam kernel atau deployment storage memerlukan kebijakan fallback eksplisit jika penyalinan byte tetap harus dilanjutkan.

Fallback tersebut dapat memakai buffered I/O biasa, tetapi tidak transparan secara semantik. Fallback harus mereproduksi penanganan offset, partial write, kebijakan metadata, kebijakan sparse file, cancellation, dan pelaporan error yang diwajibkan aplikasi. Mengganti satu syscall dengan loop hanya sederhana ketika kontrak penyalinan aplikasi memang sempit.

Perilaku kernel lama juga penting untuk klaim kompatibilitas. Linux 5.3 sampai 5.18 memiliki jalur lintas filesystem generik dengan masalah terdokumentasi pada beberapa virtual filesystem. Program yang mendukung kernel tersebut tidak semestinya memproyeksikan perilaku saat ini ke versi lama tanpa memperhitungkan riwayat itu.

Pemindahan data tidak menyalin metadata file

Operasi ini menyalin range data file. Operasi ini bukan primitive replikasi file lengkap.

Ownership, mode bit, timestamp, extended attribute, access-control list, security label, dan metadata lain memiliki interface serta aturan permission terpisah. File tujuan sudah ada sebagai object yang dibuka untuk write; copy_file_range() memodifikasi data di dalamnya.

Pemisahan ini berguna karena banyak sistem memang menginginkan pemindahan data tanpa replikasi identitas. File cache tidak selalu perlu mewarisi ownership sumber. File staging dapat sengaja memakai permission lebih ketat. Sebaliknya, software backup atau migrasi yang menjanjikan fidelitas metadata harus membangun fidelitas tersebut di luar operasi penyalinan range.

Durability juga terpisah. Penyalinan byte yang berhasil menetapkan penyelesaian tingkat syscall menurut semantik filesystem terkait; hasil itu tidak dengan sendirinya menggantikan operasi persistence eksplisit yang dibutuhkan protokol crash consistency aplikasi.

State tujuan dapat berubah sebagian saat terjadi kegagalan

Penyalinan multi-call dapat gagal setelah pemanggilan sebelumnya sudah memodifikasi tujuan. Bahkan satu invocation dapat mengembalikan progres positif yang lebih kecil dari permintaan, sehingga tujuan memiliki prefix sah dari range yang dicoba.

Aplikasi yang memerlukan publikasi all-or-nothing membutuhkan batas transaksi pada tingkat lebih tinggi. Desain berorientasi file yang umum menulis ke object staging terpisah, memverifikasi hasil yang diwajibkan, menetapkan batas durability yang diperlukan, lalu mempublikasikannya melalui operasi namespace yang sesuai dengan platform dan deployment.

Pola tersebut tidak membuat penyalinan range menjadi transaksional. Atomic visibility dipindahkan ke mekanisme lain. Perbedaan ini penting saat recovery: object staging yang belum lengkap dan object live yang tertimpa sebagian menghasilkan permukaan kegagalan yang sangat berbeda.

Range tujuan juga menimpa data lama pada bagian tempat penyalinan berhasil. Caller yang memakai kembali file tujuan lebih panjang harus memutuskan secara terpisah apakah byte setelah region salinan tetap dipertahankan, dipotong, atau diganti melalui object file baru.

Offset eksplisit memisahkan perhitungan range dari posisi file bersama

Memberikan pointer offset dapat mengurangi coupling tak sengaja melalui posisi file mutable. Hal ini terutama relevan ketika beberapa operasi bekerja pada open file description yang sama.

Dengan offset eksplisit, kepemilikan range berada pada nilai yang dikelola caller. Syscall memperbarui nilai tersebut sesuai progres salinan tetapi tidak mengubah file offset descriptor saat ini. Range sumber dan tujuan menjadi terlihat dalam state aplikasi dan posisi seek tidak dipakai sebagai channel koordinasi implisit.

Mekanisme ini tidak membuat concurrent write aman dengan sendirinya. Dua worker masih dapat menargetkan range tujuan yang overlap jika pembagian kerja aplikasi keliru. Interaksi tingkat filesystem dengan mutasi sumber secara concurrent juga tetap mengikuti semantik filesystem dan syscall terkait.

Offset eksplisit menghapus satu variabel state bersama; offset tersebut tidak menciptakan transaksi atau snapshot.

Batas abstraksi yang berguna adalah progres logis

copy_file_range() paling tepat diperlakukan sebagai primitive penyalinan range logis dengan implementasi yang sengaja dibiarkan terbuka. Fakta stabilnya adalah descriptor, offset, jumlah yang diminta, progres yang dikembalikan, kondisi error, dan batas filesystem yang didokumentasikan untuk platform aktif.

Block sharing fisik, jalur transfer internal, dan alokasi sparse dapat berubah tanpa melanggar operasi tingkat byte. Semantik file lengkap seperti replikasi metadata, crash durability, publikasi atomik, dan perilaku fallback tetap menjadi tanggung jawab aplikasi kecuali interface lain secara eksplisit menyediakannya.

Pemisahan tersebut memungkinkan kernel dan filesystem mengoptimalkan pemindahan data sambil mempertahankan kewajiban correctness caller tetap terlihat. Syscall dapat menghilangkan transfer buffer userspace dari jalur umum; syscall tidak dapat menghilangkan kebutuhan untuk mendefinisikan arti salinan yang selesai bagi sistem yang memintanya.