copy_file_range() meminta Linux menyalin byte di antara regular file tanpa mengharuskan aplikasi memindahkan byte tersebut melalui buffer user space. Pemanggilan ini mendefinisikan operasi rentang byte, tetapi tidak menentukan mekanisme transfer fisiknya. Filesystem dapat melakukan pergerakan data biasa, memakai mekanisme sharing copy-on-write seperti reflink, atau memakai jalur akselerasi lain yang didukung sambil mempertahankan isi file yang terlihat sesuai operasi tersebut.
Pemisahan itu merupakan batas utama API. Aplikasi menentukan rentang sumber dan tujuan lalu mengamati jumlah byte yang dikembalikan. Kernel dan filesystem tetap memiliki keleluasaan untuk memilih mekanisme yang merealisasikan salinan.
Pemanggilan yang sukses dapat menyalin lebih sedikit byte dari permintaan
Argumen size adalah batas atas, bukan janji bahwa satu pemanggilan mentransfer tepat sebanyak itu. copy_file_range() yang sukses mengembalikan jumlah byte yang benar-benar disalin, dan nilainya dapat lebih kecil dari size. Nilai nol berarti tidak ada byte yang disalin; ketika posisi sumber berada pada atau melewati akhir file, nol adalah hasil yang terdokumentasi.
Kode yang membutuhkan seluruh rentang karena itu memerlukan loop progres, bukan asumsi kesetaraan pada satu pemanggilan:
size_t remaining = length;
while (remaining > 0) {
ssize_t n = copy_file_range(src, NULL, dst, NULL, remaining, 0);
if (n > 0) {
remaining -= (size_t)n;
continue;
}
if (n == 0) {
break;
}
if (errno == EINTR) {
continue;
}
/* handle error */
break;
}Loop tersebut tetap memerlukan kebijakan pada level aplikasi untuk hasil nol yang datang lebih awal. Jika kontrak logis menyatakan bahwa length byte harus tersedia di sumber, tercapainya akhir file sebelum remaining menjadi nol merupakan salinan yang tidak lengkap walaupun system call terakhir itu sendiri tidak gagal.
Pembedaan ini serupa dengan interface I/O berorientasi byte lainnya: keberhasilan syscall melaporkan progres sesuai kontrak syscall, sedangkan kelengkapan menjadi milik operasi yang dirangkai oleh pemanggil.
Pointer offset menentukan pemilik state posisi
off_in dan off_out menentukan apakah operasi memakai file offset saat ini dari masing-masing open file description atau posisi eksplisit yang dikelola pemanggil.
Ketika off_in bernilai NULL, penyalinan dimulai dari file offset sumber saat ini dan memajukan offset tersebut sebanyak byte yang disalin. Sisi tujuan berperilaku serupa ketika off_out bernilai NULL.
Ketika pointer non-null diberikan, offset yang ditunjuk memilih posisi byte. Syscall memperbarui nilai yang ditunjuk itu seiring byte disalin, tetapi tidak mengubah file offset yang bersangkutan.
Kedua mode membentuk kontrak concurrency yang berbeda:
NULL offset
fd -> open file description -> posisi saat ini yang dibagi
offset eksplisit
state pemanggil -> nilai off_t -> rentang byte terpilih
posisi fd saat ini tidak berubahDescriptor hasil duplikasi dapat berbagi open file description dengan descriptor lain. Pada bentuk NULL, operasi yang memakai description bersama tersebut berinteraksi melalui offset saat ini. Offset eksplisit tidak memakai posisi mutable itu untuk pemilihan rentang, sehingga kepemilikan dapat menjadi lebih jelas ketika komponen independen bekerja pada rentang berbeda.
Offset eksplisit tidak membuat mutasi file concurrent secara arbitrer menjadi aman. Offset hanya mengubah pencatatan posisi. Jika aktor lain memodifikasi isi sumber atau tujuan selama operasi, semantik aplikasi tetap bergantung pada filesystem dan kontrak sinkronisasi yang mengelilingi mutasi tersebut.
Rentang tujuan ditimpa, bukan ditambahkan
Posisi tujuan menentukan lokasi byte hasil salinan menggantikan byte yang sudah ada. copy_file_range() bukan primitive append, dan Linux menolak open file description tujuan yang memiliki O_APPEND.
Constraint ini mencegah API memiliki dua aturan posisi tujuan yang bersaing. Pemanggil memakai file offset tujuan atau off_out eksplisit; mode append sebaliknya akan menempatkan write berdasarkan semantik akhir file.
Jika rentang tujuan yang dipilih melewati akhir file yang ada dan operasi sukses, file dapat membesar sesuai hasil tersebut. Byte yang ada di luar rentang yang ditulis tidak tersirat berubah hanya karena penyalinan rentang terjadi.
Model rentang juga mengizinkan descriptor sumber dan tujuan merujuk ke file yang sama, tetapi rentang sumber dan tujuan tidak boleh overlap. Linux menolak rentang overlap dalam kasus tersebut dengan EINVAL. Syscall ini karena itu bukan primitive umum untuk memindahkan rentang di dalam file seperti operasi memori yang secara eksplisit mendefinisikan perilaku overlap.
Penyalinan di dalam kernel tidak menyiratkan satu implementasi fisik
Perbedaan yang terlihat pengguna dibanding loop read() lalu write() konvensional adalah data tidak perlu melakukan perjalanan tambahan dari kernel space ke buffer pengguna lalu kembali ke kernel. Hal tersebut tidak berarti setiap pemanggilan yang sukses melewati jalur data internal yang sama.
Linux secara sengaja memberi filesystem kesempatan mengakselerasi operasi. Filesystem dapat memakai sharing block bergaya reflink, saat sumber dan tujuan pada awalnya merujuk ke penyimpanan fisik bersama dan write berikutnya memicu perilaku copy-on-write. Network filesystem dapat memakai penyalinan pada sisi server. Filesystem lain dapat melakukan salinan yang lebih konvensional.
Karena itu, aplikasi tidak semestinya menurunkan jaminan layout penyimpanan dari keberhasilan syscall:
kontrak yang terlihat:
byte sumber -> byte tujuan
jalur implementasi yang mungkin:
salinan fisik
reflink / sharing copy-on-write
akselerasi khusus filesystemIsi logis tujuan adalah interface-nya. Apakah block fisik langsung diduplikasi merupakan properti implementasi kecuali API terpisah menyediakan kontrak layout penyimpanan yang lebih kuat.
Hal ini juga berarti karakteristik performa bukan invariant portabel. Salinan rentang yang dominan metadata pada satu filesystem dapat membutuhkan I/O data dalam jumlah besar pada filesystem lain. API membuka peluang akselerasi; API tidak menjamin teknik akselerasi atau profil biaya tertentu.
Layout sparse pada sumber tidak otomatis dipertahankan
Sparse file memiliki region logis yang dibaca sebagai nol tanpa harus mengalokasikan block data fisik untuk setiap byte. copy_file_range() beroperasi pada rentang byte, bukan pada janji abstrak untuk mereproduksi peta hole milik sumber.
Dokumentasi Linux secara eksplisit mencatat bahwa penyalinan sparse file dapat memperluas hole pada rentang yang disalin. Software yang mensyaratkan alokasi sparse sebagai bagian dari hasil dapat memeriksa region data dan hole dengan lseek() memakai SEEK_DATA dan SEEK_HOLE, jika didukung, lalu menyalin data extent secara selektif.
Hal itu menghasilkan dua gagasan ekuivalensi yang terpisah:
ekuivalensi isi:
read mengembalikan byte yang dimaksud
ekuivalensi alokasi:
hole dan region teralokasi mempertahankan struktur yang dimaksudcopy_file_range() secara langsung melayani gagasan pertama. Gagasan kedua memerlukan penanganan tambahan yang sadar filesystem ketika properti tersebut penting bagi aplikasi.
Verifikasi byte demi byte karena itu dapat sukses meskipun konsumsi ruang disk berbeda dari sumber. Akuntansi penyimpanan dan isi logis adalah properti yang berkaitan, tetapi keduanya tidak identik.
Dukungan lintas filesystem bersifat kondisional
Perilaku lintas filesystem dari syscall ini telah berubah di antara versi Linux. Semantik terdokumentasi saat ini untuk Linux 5.19 dan yang lebih baru mengizinkan penyalinan lintas filesystem ketika filesystem sumber dan tujuan memiliki tipe yang sama dan filesystem tersebut mengimplementasikan operasi itu. Kombinasi yang tidak didukung dapat gagal dengan EXDEV atau EOPNOTSUPP sesuai kondisi yang berlaku.
Dukungan tersebut karena itu merupakan kapabilitas kondisional, bukan properti universal. Program yang membutuhkan kompatibilitas penyalinan luas dapat mencoba copy_file_range() lalu memakai jalur salinan lain ketika kegagalan menunjukkan operasi teroptimasi tidak tersedia.
Logika fallback semacam itu perlu mempertahankan semantik offset dan progres parsial yang sama dengan jalur utama. Jika pemanggilan awal sudah menyalin sejumlah byte sebelum pemanggilan berikutnya gagal, memulai fallback secara buta dari posisi awal dapat menduplikasi atau menimpa data secara keliru. Transisi harus dimulai dari posisi sumber dan tujuan yang tersisa.
Batas libc juga relevan. glibc modern tidak mengemulasikan operasi ini di user space ketika kernel tidak memiliki system call tersebut; pada kernel semacam itu wrapper dapat gagal dengan ENOSYS. Fallback aplikasi tetap menjadi tanggung jawab aplikasi pada lingkungan tersebut.
Selesainya penyalinan terpisah dari persistensi
Return value positif menyatakan bahwa byte telah disalin sesuai semantik I/O system call. Nilai tersebut tidak dengan sendirinya menetapkan crash durability untuk file tujuan atau metadata direktori yang terkait dengan pathname tujuan yang baru dibuat.
Jika aplikasi membutuhkan persistensi melewati kehilangan daya atau kernel crash, aplikasi memerlukan protokol sinkronisasi yang relevan bagi filesystem target dan pola update yang dipakai. Hal itu dapat mencakup sinkronisasi data file dan, ketika perubahan namespace terlibat, metadata direktori. Kontrak durability terpisah dari mekanisme penyalinan rentang.
Batas ini penting untuk workflow publikasi. Proses dapat menyalin data ke file sementara, memvalidasinya, menyinkronkannya sesuai kebutuhan, lalu memublikasikannya melalui operasi namespace. copy_file_range() dapat menjalankan fase transfer data tanpa mengambil alih jaminan durability atau publikasi dari protokol di sekelilingnya.
API penyalinan rentang mengekspos mekanisme tanpa memiliki transaksi yang lebih besar
copy_file_range() sengaja memiliki cakupan sempit. Ia memindahkan hingga jumlah byte yang diminta, melaporkan progres aktual, dan memberi kernel serta filesystem ruang untuk memilih implementasi yang efisien. Argumen offset membuat pemanggil dapat memilih antara state posisi file bersama dan state rentang eksplisit.
Properti di luar batas tersebut tetap terpisah: penegakan kelengkapan rentang, preservasi layout sparse, perilaku fallback, kebijakan mutasi concurrent, crash durability, dan publikasi atomik masing-masing memerlukan kontraknya sendiri.
Pemisahan itu berguna karena sistem penyimpanan memiliki beberapa lapisan state yang dapat diamati. Sebuah file dapat memiliki byte yang benar tetapi alokasi berbeda, salinan yang selesai masih dapat belum durable, dan salinan terakselerasi dapat menampilkan isi logis yang sama dengan salinan fisik. Memperlakukan dimensi tersebut secara independen menjaga jaminan syscall tetap presisi sekaligus mengizinkan optimasi khusus filesystem di bawahnya.