Sebuah pathname yang diberikan ke openat2() dapat ditolak walaupun pathname yang sama berhasil diresolusikan melalui openat(). Perbedaannya berasal dari open_how.resolve: Linux dapat menerapkan batas traversal ketika setiap komponen path diresolusikan untuk satu operasi open.
Batas penanganan path pun berubah. File descriptor direktori dapat berfungsi lebih dari sekadar titik awal; flag resolve dapat membatasi escape, traversal symbolic link, perpindahan mount, dan lookup yang membutuhkan pekerjaan di luar state cache.
Kebijakan resolusi melekat pada satu operasi
openat2() memperluas model openat() dengan struct open_how yang dapat diperluas:
#include <linux/openat2.h>
struct open_how how = {
.flags = O_RDONLY | O_CLOEXEC,
.resolve = RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS,
};Field resolve adalah bit mask yang mengubah resolusi pathname untuk call tersebut. Nilai ini tidak mengubah root proses, current working directory, mount namespace, atau operasi open berikutnya.
Cakupan per operasi ini penting bagi service yang menerima fragmen path dari sumber dengan tingkat kepercayaan lebih rendah. Kebijakan ikut melekat pada lookup yang memerlukannya tanpa bergantung pada perpindahan direktori yang berlaku untuk seluruh proses.
Struktur tersebut perlu diinisialisasi dengan nol. ABI-nya dirancang agar kernel berikutnya dapat menambahkan field, dengan nilai nol pada field baru berarti ekstensi itu tidak digunakan.
RESOLVE_BENEATH menolak escape di atas dirfd
Dengan RESOLVE_BENEATH, resolusi harus tetap berada di bawah direktori yang dirujuk oleh dirfd. Komponen yang membuat resolusi keluar dari subtree tersebut menyebabkan operasi gagal.
Misalnya, sebuah service menyimpan descriptor O_PATH untuk /srv/data dan menerima tenant/report.bin. Lookup relatif dapat dibatasi pada descendant descriptor itu:
int fd = syscall(SYS_openat2, rootfd, "tenant/report.bin",
&how, sizeof(how));Batas ini berlaku pada proses resolusi path, bukan sekadar pemeriksaan tekstual terhadap ... Path absolut dan symbolic link absolut ditolak di bawah RESOLVE_BENEATH karena keduanya akan memilih titik awal resolusi di luar batas direktori yang diberikan.
Perbedaan tersebut mencegah string pathname diperlakukan seolah-olah sudah menjadi objek filesystem yang selesai diresolusikan. Komponen dapat berupa symbolic link, mount point, atau objek yang berubah secara konkuren, sehingga filter leksikal saja tidak menyatakan properti yang sama dengan batas yang ditegakkan kernel.
RESOLVE_IN_ROOT memberi root sementara pada lookup
RESOLVE_IN_ROOT menggunakan direktori yang dirujuk dirfd sebagai root untuk operasi tersebut. Input path absolut diinterpretasikan relatif terhadap direktori itu, dan symbolic link absolut yang ditemui saat resolusi juga diinterpretasikan relatif terhadapnya.
Penanganan .. mengikuti semantik seperti root. Setelah resolusi berada pada root yang diberikan, komponen parent berikutnya tetap berada di sana dan tidak keluar ke atas.
Perilaku ini berbeda dari RESOLVE_BENEATH. Flag tersebut menolak percobaan escape; RESOLVE_IN_ROOT mengubah konteks root yang dipakai lookup. Efeknya menyerupai chroot() sementara untuk resolusi pathname tanpa mengubah root proses secara permanen.
Cakupannya tetap satu call openat2(). Mekanisme ini bukan pengganti isolasi yang lebih luas ketika proses memerlukan batas filesystem yang persisten.
Kontrol symlink memiliki cakupan berbeda
O_NOFOLLOW dan RESOLVE_NO_SYMLINKS tidak setara.
O_NOFOLLOW mengendalikan perlakuan terhadap symbolic link pada komponen path terakhir untuk operasi open. Symbolic link pada komponen antara masih dapat ikut dalam resolusi normal.
RESOLVE_NO_SYMLINKS memblokir resolusi symbolic link pada setiap komponen path. Flag ini juga mengimplikasikan RESOLVE_NO_MAGICLINKS.
Magic link adalah konsep Linux yang terpisah, terutama terlihat pada entri seperti /proc/<pid>/fd/*. RESOLVE_NO_MAGICLINKS memblokir resolusi objek tersebut tanpa menolak symbolic link biasa.
Untuk kedua mode resolve yang melarang link, terdapat kasus khusus pada komponen terakhir: ketika komponen terakhir merupakan jenis link yang dilarang dan open memakai O_PATH | O_NOFOLLOW, call dapat mengembalikan descriptor O_PATH yang merujuk link itu sendiri alih-alih mengikutinya.
Batas mount dapat menjadi bagian dari kebijakan
RESOLVE_NO_XDEV menolak resolusi path yang melintasi mount point. Bind mount termasuk dalam batas ini.
Properti tersebut lebih kuat daripada sekadar mensyaratkan path tetap berada secara tekstual di bawah sebuah direktori. Sebuah subtree dapat berisi filesystem yang di-mount, sehingga komponen path descendant tidak selalu berada pada mount yang sama.
Kekuatan itu juga dapat mengurangi kompatibilitas. Bind mount umum digunakan pada layout filesystem yang sudah diterapkan. Service yang tidak secara khusus membutuhkan properti same-mount dapat menolak path yang sebenarnya valid jika mengaktifkan RESOLVE_NO_XDEV tanpa kebutuhan tersebut.
Karena itu, flag resolve menyatakan batas yang terpisah dan dapat digabungkan sesuai properti yang diperlukan: containment subtree, penanganan link, dan traversal mount merupakan tiga hal berbeda.
RESOLVE_CACHED mengubah kebutuhan lookup menjadi EAGAIN
RESOLVE_CACHED menambahkan batas yang berorientasi pada performa. Operasi hanya berhasil bila resolusi pathname dapat diselesaikan dari informasi lookup yang sudah ada di cache tanpa revalidation atau I/O. Jika syarat itu tidak terpenuhi, openat2() gagal dengan EAGAIN.
Flag ini tidak berarti file yang dinamai akan selalu berada di cache dan tidak membuat jalur pintas terhadap semantik pathname normal. Resolusi berbasis cache saja menjadi syarat untuk percobaan tersebut.
Service event-driven dapat memakai hasil itu sebagai batas dispatch: percobaan cache-only tetap berada pada jalur sensitif terhadap latensi, sedangkan EAGAIN dapat memindahkan operasi ke konteks yang mengizinkan lookup yang berpotensi lebih lambat.
EAGAIN juga dapat muncul bersama RESOLVE_BENEATH atau RESOLVE_IN_ROOT ketika kernel tidak dapat memastikan batas escape yang diminta akibat race. Karena itu, caller perlu menafsirkan error berdasarkan flag yang diberikan.
Kegagalan constraint merupakan bagian dari interface
Pelanggaran kebijakan resolve terlihat sebagai error, bukan fallback diam-diam ke lookup biasa.
Escape yang terdeteksi di bawah RESOLVE_BENEATH atau RESOLVE_IN_ROOT dapat menghasilkan EXDEV. Melintasi mount dengan RESOLVE_NO_XDEV juga menghasilkan EXDEV. Symbolic link yang dilarang oleh RESOLVE_NO_SYMLINKS menghasilkan ELOOP, demikian pula magic link yang dilarang oleh RESOLVE_NO_MAGICLINKS.
Bit resolve yang tidak dikenal ditolak. Penanganan ketat ini penting untuk call yang membawa kebijakan karena constraint yang tidak didukung tidak diabaikan secara diam-diam.
Model akhirnya lebih sempit daripada sandbox umum dan lebih kuat daripada prevalidation pathname. openat2() menempatkan kondisi yang ditegakkan kernel langsung pada satu operasi resolusi, sehingga kode dapat menyatakan properti traversal yang harus terpenuhi sebelum file descriptor dikembalikan.