Saya menggunakan distribusi berbasis Linux 4.x, dan saya baru-baru ini memperhatikan open()
system call kernel mendukung O_PATH
flag terbuka.
Sementara man
halaman untuk itu memang memiliki daftar panggilan sistem yang secara teori dapat digunakan, saya tidak begitu mengerti apa idenya. Apakah saya open(O_PATH)
hanya direktori, bukan file? Dan jika saya melakukannya, mengapa saya ingin menggunakan deskriptor file alih-alih jalur direktori? Juga, sebagian besar panggilan sistem yang terdaftar di sana tampaknya tidak khusus untuk direktori; jadi, apakah saya juga membuka file biasa O_PATH
untuk mendapatkan direktori mereka sebagai deskriptor file? Atau untuk mendapatkan deskriptor file untuk mereka tetapi dengan fungsi terbatas?
Bisakah seseorang memberikan penjelasan yang meyakinkan tentang apa O_PATH
itu dan bagaimana, dan untuk apa, kita seharusnya menggunakannya?
Catatan:
- Tidak perlu menggambarkan sejarah bagaimana ini berevolusi (halaman manual yang relevan menyebutkan perubahan di Linux 2.6.x, 3.5 dan 3.6) kecuali perlu - saya hanya peduli bagaimana keadaannya sekarang.
- Tolong jangan katakan padaku untuk hanya menggunakan libc atau fasilitas tingkat tinggi lainnya, saya tahu itu.
sumber
Jawaban:
Deskripsi di
open(2)
halaman manual memberikan beberapa petunjuk untuk memulai dengan:Terkadang, kami tidak ingin membuka file atau direktori. Sebagai gantinya, kami hanya ingin referensi ke objek sistem file itu untuk melakukan operasi tertentu (misalnya, ke
fchdir()
direktori yang dirujuk oleh deskriptor file yang kami buka menggunakanO_PATH
). Jadi, titik sepele: jika ini adalah tujuan kita, maka pembukaan denganO_PATH
harus sedikit lebih murah, karena file itu sendiri sebenarnya tidak dibuka.Dan poin yang kurang sepele: sebelum keberadaan
O_PATH
, cara mendapatkan referensi seperti itu ke objek sistem file adalah dengan membuka objekO_RDONLY
. Tetapi penggunaanO_RDONLY
mengharuskan kita telah membaca izin pada objek. Namun, ada berbagai kasus penggunaan di mana kita tidak perlu membaca objek: misalnya, menjalankan biner atau mengakses direktori (fchdir()
) atau menjangkau melalui direktori untuk menyentuh objek di dalam direktori.Penggunaan dengan panggilan sistem "* at ()"
Umum, tetapi bukan satu-satunya, penggunaan
O_PATH
adalah untuk membuka direktori, untuk memiliki referensi ke direktori untuk digunakan dengan "* di" panggilan sistem, sepertiopenat()
,fstatat()
,fchownat()
, dan sebagainya. Ini keluarga panggilan sistem, yang kita kira-kira bisa anggap sebagai penerus modern untuk panggilan sistem yang lebih tua dengan nama yang mirip (open()
,fstat()
,fchown()
, dan sebagainya), melayani beberapa tujuan, yang pertama Anda menyentuh ketika Anda meminta " mengapa saya ingin menggunakan deskriptor file alih-alih jalur direktori? " Jika kita melihat lebih jauh ke bawah diopen(2)
halaman manual, kita menemukan teks ini (di bawah judul dengan alasan untuk panggilan sistem "* at"):Untuk membuat ini lebih konkret ... Misalkan kita memiliki program yang ingin melakukan beberapa operasi di direktori selain direktori kerjanya saat ini, artinya kita harus menentukan beberapa awalan direktori sebagai bagian dari nama file yang kita gunakan. Misalkan, misalnya, bahwa nama pathnya
/dir1/dir2/file
dan kami ingin melakukan dua operasi:/dir1/dir2/file
(misalnya, siapa yang memiliki file, atau jam berapa terakhir kali diubah)./dir1/dir2/file.new
.Sekarang, misalkan kita melakukan semuanya menggunakan panggilan sistem berbasis pathname tradisional:
Sekarang, lebih jauh lagi anggaplah bahwa dalam awalan direktori
/dir1/dir2
salah satu komponen (katakanlahdir2
) sebenarnya adalah tautan simbolis (yang merujuk ke direktori), dan bahwa antara panggilan kestat()
dan panggilan keopen()
orang jahat dapat mengubah target dari tautan simbolisdir2
untuk menunjuk ke direktori yang berbeda. Ini adalah kondisi balapan waktu-of-check-of-use klasik. Program kami memeriksa file di satu direktori tetapi kemudian tertipu untuk membuat file di direktori yang berbeda - mungkin direktori yang sensitif terhadap keamanan. Poin kunci di sini adalah bahwa nama path/dir/dir2
tampak sama, tetapi apa yang dirujuknya berubah sepenuhnya.Kita dapat menghindari masalah semacam ini menggunakan panggilan "* at". Pertama-tama, kami memperoleh pegangan yang merujuk ke direktori tempat kami akan melakukan pekerjaan kami:
Titik penting di sini adalah bahwa
dirfd
adalah stabil referensi ke direktori yang disebut oleh jalan/dir1/dir2
pada saatopen()
panggilan. Jika target tautan simbolikdir2
kemudian diubah, ini tidak akan memengaruhi apa yangdirfd
dimaksud. Sekarang, kita dapat melakukan operasi pemeriksaan + menggunakan panggilan "* at" yang setara denganstat()
danopen()
panggilan di atas:Selama langkah-langkah ini setiap manipulasi tautan simbolik dalam pathname tidak
/dir/dir2
akan berdampak: check (fstatat()
) dan operasi (openat()
) dijamin akan terjadi di direktori yang sama.Ada tujuan lain untuk menggunakan panggilan "* at ()", yang berhubungan dengan gagasan "direktori kerja saat ini per-thread" dalam program multithreaded (dan sekali lagi kita bisa membuka direktori menggunakan
O_PATH
), tapi saya pikir penggunaan ini mungkin kurang relevan dengan pertanyaan Anda, dan saya meninggalkan Anda untuk membacaopen(2)
halaman manual jika Anda ingin tahu lebih banyak.Penggunaan dengan deskriptor file untuk file biasa
Salah satu penggunaan
O_PATH
dengan file biasa adalah membuka biner yang izinnya kami jalankan (tetapi tidak harus membaca izin, sehingga kami tidak dapat membuka file itu denganO_RDONLY
). Deskriptor file itu kemudian dapat diteruskan kefexecve(3)
untuk menjalankan program. Semua yangfexecve(fd, argv, envp)
dilakukan denganfd
argumennya pada dasarnya adalah:(Meskipun, dimulai dengan glibc 2.27, implementasi akan menggunakan
execveat(2)
system call, pada kernel yang menyediakan system call itu.)sumber
The problem is that between the existence check and the file creation step, path or to ... could be modified
- tidak dapat menguraikan kalimat ini. Tapi saya mengerti, saya pikir. Jadi ini berfungsi sebagai semacam mekanisme penguncian pada direktori. Tetapi mengapa menggunakanopen()
hasilnya daripada kunci yang sebenarnya?