Apakah pencarian PATH menyertakan symlink?

9

Standar shell POSIX mengatakan di situs ini

http://pubs.opengroup.org/onlinepubs/9699919799/

tentang bagaimana shell digunakan PATHuntuk mencari file yang dapat dieksekusi:

"Daftar harus dicari dari awal hingga akhir, menerapkan nama file untuk setiap awalan, sampai file yang dapat dieksekusi dengan nama yang ditentukan dan izin eksekusi yang sesuai ditemukan."

Nah, ini bukan bagaimana ini tampaknya bekerja dalam implementasi POSIX nyata:

man which mengatakan:

"Mengembalikan nama path file (atau tautan) yang akan dieksekusi di lingkungan saat ini, seandainya argumennya diberikan sebagai perintah dalam shell yang benar-benar sesuai POSIX. Ia melakukan ini dengan mencari PATH untuk file yang dapat dieksekusi yang cocok dengan nama-nama argumen. Itu tidak mengikuti tautan simbolis. "

OK, mari kita lihat situasi ini:

$ pwd /home/mark

$ echo $PATH /home/mark/bin:...

$ ls -l bin/foobar
lrwxrwxrwx 1 mark mark 18 Dec 12 22:51 bin/foobar -> /home/mark/foobar1
$ touch foobar1
$ which foobar
$ chmod a+x foobar1
$ which foobar
/home/mark/bin/foobar

OK, di sini ada tautan simbolik PATHdengan nama yang benar, dan dilaporkan lsdapat dieksekusi.

which tidak melihatnya sama sekali, tetapi hanya tertarik pada apa yang ditunjukkannya.

Terlepas dari kenyataan bahwa keduanya man whichsecara eksplisit mengatakan bahwa itu tidak mengikuti tautan simbolik (dan memang kita melihatnya tidak, karena which foobartidak mencetak foobar1), dan juga dokumentasi shell POSIX yang dikutip di atas, tidak pernah menyebutkan symlink berikut dalam PATHalgoritma.

Jadi, apakah whichkerang yang ada salah, atau apakah saya tidak memahami dokumentasinya?

UNTUK MEMPERJELAS:

Saya tahu dan bisa menjelaskan perilaku yang ada. Pertanyaan saya bukanlah "bagaimana cara kerjanya?". Itu saya tahu.

Pertanyaan saya adalah tentang dokumentasi: di mana kesalahan saya dalam mengikuti dokumentasi yang saya kutip. Atau apakah dokumentasinya salah?

MOTIVASI: Mengapa saya peduli?

Ya, saya pelaksana. Pelaksana yang berbeda memiliki persyaratan yang berbeda pula. Bagi saya, persyaratannya adalah bahwa kata dari standar POSIX saat ini HARUS diikuti secara TEPAT (atau, lebih tepatnya, yang terbaik, karena, standarnya sendiri agak buggy). Seperti itu adalah firman Tuhan.

Sekarang, kata-kata standarnya cukup jelas - symlink berikut tidak disebutkan, di mana di banyak tempat lain, disebutkan di mana itu perlu dilakukan. Jadi dalam hal ini, jangan.

Namun, saya selalu mengecek bagaimana dashdan bashberperilaku, hanya untuk memastikan. Sekarang tentu saja, ada sedikit masalah di sini juga, dashmeskipun itu disebut sebagai POSIX, memiliki banyak bug kecil yang sesuai dengan POSIX. bash, Saya belum menemukan bug dengan POSIX, tapi ... bash sebenarnya bukan POSIX, itu lebih dari itu.

Jadi begitulah. Itu sebabnya saya peduli.

pengguna322908
sumber
Anda tidak mengerti: yang tidak mengikuti symlink pada file . $PATHdapat berisi symlink. Coba which sh.
Ipor Sircer
OK tapi, dalam kasus saya $PATHtidak memiliki symlink.
user322908
Di hampir semua situasi, symlink diikuti secara transparan. Kasus-kasus di mana mereka biasanya tidak disebutkan secara eksplisit (misalnya panggilan sistem seperti lstat(2)), mengikuti mereka biasanya tidak dinyatakan. Misalnya, deskripsi open(2)hanya menyebutkan symlink ketika berbicara tentang perilaku O_CREAT | O_EXCL. Tidak perlu menyatakan bahwa file target akan dibuka.
Barmar

Jawaban:

10

Izin symlink itu sendiri tidak relevan. Anda bahkan tidak dapat mengubahnya jika Anda mencobanya.

Yang penting adalah izin dari file yang mendasarinya.

Anda boleh memiliki direktori di PATH Anda termasuk symlink ke executable. Bahkan, kemungkinan banyak executable di PATH Anda adalah symlink. Sebagai contoh, pada sistem seperti debian / ubuntu:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 23  2017 /bin/sh -> dash

Dokumentasi

Dari man chmod:

chmod tidak pernah mengubah izin tautan simbolik; panggilan sistem chmod tidak dapat mengubah izinnya. Ini bukan masalah karena izin tautan simbolis tidak pernah digunakan. Namun, untuk setiap tautan simbolis yang tercantum pada baris perintah, chmod mengubah izin file yang diarahkan ke. Sebaliknya, chmod mengabaikan tautan simbolik yang ditemukan selama traversal direktori rekursif. [Penekanan ditambahkan.]

Contoh

Shell memiliki tes -x,, untuk menentukan apakah suatu file dapat dieksekusi. Mari kita coba itu:

$ ls -l
total 0
lrwxrwxrwx 1 john1024 john1024 7 Dec 12 23:36 foo -> foobar1
-rw-rw---- 1 john1024 john1024 0 Dec 12 23:36 foobar1
$ [ -x foo ] && echo foo is executable
$ chmod +x foobar1
$ [ -x foo ] && echo foo is executable
foo is executable

Jadi, seperti yang Anda temukan which, shell tidak menganggap softlink dapat dieksekusi kecuali file yang mendasarinya dapat dieksekusi.

Bagaimana cara kerjanya

Pada sistem Debian, whichadalah skrip shell. Bagian kode yang relevan adalah:

 case $PROGRAM in
  */*)
   if [ -f "$PROGRAM" ] && [ -x "$PROGRAM" ]; then
    puts "$PROGRAM"
    RET=0
   fi
   ;;
  *)
   for ELEMENT in $PATH; do
    if [ -z "$ELEMENT" ]; then
     ELEMENT=.
    fi
    if [ -f "$ELEMENT/$PROGRAM" ] && [ -x "$ELEMENT/$PROGRAM" ]; then
     puts "$ELEMENT/$PROGRAM"
     RET=0
     [ "$ALLMATCHES" -eq 1 ] || break
    fi
   done
   ;;
 esac

Seperti yang Anda lihat, ini menggunakan -xtes untuk menentukan apakah file dapat dieksekusi.

POSIX menentukan -xtes sebagai berikut:

-x pathname
Benar jika pathname memutuskan untuk entri direktori yang sudah ada untuk file yang izinnya untuk mengeksekusi file (atau mencari, jika itu adalah direktori) akan diberikan, seperti yang didefinisikan dalam Baca File, Tulis, dan Pembuatan. Salah jika pathname tidak dapat diselesaikan, atau jika pathname memutuskan ke entri direktori yang ada untuk file yang izinnya untuk mengeksekusi (atau mencari) file tidak akan diberikan. [Penekanan ditambahkan.]

Jadi, POSIX cek apa pathname resolve ke. Dengan kata lain, ia menerima symlink.

Fungsi POSIX exec

Fungsi POSIX exec mengikuti symlinks. Panjang lebar POSIX berlangsung untuk menentukan kondisi kesalahan yang mungkin dilaporkan jika symlink melingkar atau terlalu dalam, seperti:

[ELOOP]
Ada loop dalam tautan simbolik yang ditemukan selama resolusi path atau argumen file.

[ELOOP]
Lebih dari {SYMLOOP_MAX} tautan simbolik ditemui selama resolusi path atau argumen file.
[ENAMETOOLONG]
Sebagai hasil dari pertemuan tautan simbolik dalam resolusi argumen path, panjang string pathname yang diganti melebihi {PATH_MAX}.

John1024
sumber
Saya TAHU semua yang Anda tulis dalam jawaban Anda. Saya tahu bagaimana hal-hal "bekerja". Itu bukan pertanyaan saya. Pertanyaan saya adalah tentang dokumentasi. Arahkan saya di mana saya tidak memahami dokumentasi. Atau katakan padaku bahwa dokumentasinya salah.
user322908
@ user322908 Pada kebanyakan sistem, whichadalah skrip shell. Jadi, sepertinya hanya menggunakan -xtes yang saya tunjukkan. Menurut POSIX, -xmenguji apakah file "diselesaikan" ke file executable. Jika Anda melihat sesuatu yang berbeda, di mana Anda melihat?
John1024
Terima kasih dan saya minta maaf karena telah menyusahkan ... Saya menyadari bahwa pertanyaan saya berbeda dari 99% pertanyaan sehingga sulit untuk memahami apa yang saya cari. Sekali lagi, saya tidak tertarik pada "bagaimana" whichbekerja, atau apakah itu -xatau sesuatu yang lain. Saya tertarik untuk mengetahui di mana saya tidak mengikuti dengan benar dokumentasi yang saya kutip.
user322908
4
@ user322908 The POSIX execfungsi berikut symlink. Itu tampaknya membuat cukup jelas bahwa file symlink dapat dieksekusi di bawah POSIX.
John1024
2
Saya juga memeriksa Ubuntu 17.10 man which yang mengatakan "Itu tidak mengkanoniskan nama jalur." Itu tidak berarti tidak mengikuti tautan. Itu hanya berarti, seperti yang Anda amati, bahwa itu tidak "mengkanoniskan" nama-nama itu.
John1024
3

Dalam hal ini symlink diikuti secara transparan, tanpa mengkanoniskan jalur terakhir. Dengan kata lain, whichtidak peduli apakah /home/mark/binitu symlink atau tidak. Yang penting adalah apakah file itu /home/mark/bin/foobarada atau tidak. Tidak perlu meratakan symlink secara manual di sepanjang jalur - OS dapat melakukannya sendiri.

Dan memang, ketika whichditanya tentang informasi file /home/mark/bin/foobar, OS pemberitahuan /home/mark/binmenjadi symlink, mengikutinya, dan berhasil menemukan foobardi direktori target.

Ini adalah perilaku default kecuali jika program menggunakan open(…, O_NOFOLLOW)atau fstatat(…, AT_SYMLINK_NOFOLLOW)untuk mengakses file.

[komentar bergabung]

Meskipun Anda mengatakan bahwa shell utilitas melakukannya atas dasar kasus per kasus, tidak sama dengan syscalls kernel: semua panggilan terkait berkas lakukan mengikuti symlink secara default, kecuali "nofollow" bendera diberikan. (Bahkan lstat mengikuti symlink di semua komponen jalur kecuali yang terakhir.)

Ketika spesifikasi tidak secara eksplisit menyebutkan apa yang harus dilakukan dengan symlink, itu menyiratkan perilaku default yang akan digunakan. Yaitu, shell yang mengikuti algoritme lintasan yang lebih baru menyelesaikan symlink secara manual juga tidak secara eksplisit memilih keluar dari OS melakukan hal yang sama. (Itu hanya menyatukan setiap komponen $ PATH dengan nama yang dapat dieksekusi.)

Ketika halaman manual mana (1) mengatakan tidak mengikuti symlink, itu bisa berarti beberapa hal, tetapi versi GNU coreutils menyatakannya seperti ini:

Yang akan menganggap dua direktori setara berbeda ketika salah satunya berisi jalur dengan tautan simbolik.

Itu jauh lebih sempit dalam ruang lingkup - itu hanya berarti whichtidak akan mencoba secara manual mengkanonik semua jalur untuk menghilangkan duplikat, tetapi itu tidak menyiratkan bahwa alat akan memilih keluar dari symlink berikut oleh OS pada umumnya. Misalnya, jika /binmerupakan symlink ke /usr/bin, menjalankan which -a shakan mengembalikan keduanya /bin/sh dan /usr/bin/sh.

pengguna1686
sumber
Ya terima kasih, saya tahu semua ini. Pertanyaan saya bukanlah bagaimana hal-hal "bekerja". Saya tahu cara kerjanya. Itu bukan poin saya. Maksud saya adalah tentang dokumentasi. Di mana saya salah mengikuti dokumentasi. Atau apakah dokumentasi salah.
user322908
2
Anda salah memahami dokumentasi - jika tidak menyebutkan symlink berikut, itu artinya tidak secara manual menyelesaikan symlink, tetapi perilaku OS reguler masih berlaku. whichHalaman manual GNU menyatakannya secara berbeda: "Yang akan menganggap dua direktori yang sama berbeda ketika salah satu dari mereka berisi path dengan tautan simbolik."
user1686
Oke, itu lebih baik terima kasih! Saya mencoba untuk memahami ... Tapi ... maafkan saya menjadi sakit di leher: "perilaku OS biasa" BUKAN untuk selalu secara implisit mengikuti symlinks. Ada banyak utilitas yang tidak. Ini berdasarkan kasus per kasus.
user322908
1
Semua panggilan kernel - chdir, buka, chmod, eksekusi ... - akan mengikuti symlink di path dan tail, kecuali Anda menentukan AT_SYMLINK_NOFOLLOW atau yang serupa. (lstat adalah satu-satunya yang tidak melakukan symlink di bagian ekor, tetapi masih melakukannya untuk jalur yang tersisa.) Oleh karena itu perilaku default adalah mengikuti symlink. Misalnya, ketika shell memanggilnya execve("/home/mark/bin/foobar", …), semua symlink akan diikuti.
user1686
OK saya pikir saya membeli execveargumen, sebenarnya, dalam implementasi saya itu execl(), hal yang sama. Silakan jika Anda memasukkan ini dalam jawaban Anda, saya akan menerimanya.
user322908
1

Shell sesuai dengan dokumentasinya karena mengikuti aturan untuk resolusi pathname. whichsesuai dengan dokumentasinya. Keduanya melakukan hal yang sedikit berbeda.

Output dari whichadalah nama file path dan, bukan path ke apa yang ditunjuk symlink. Ini dijabarkan dalam halaman manual.

Ketika sebuah perintah dieksekusi, tautannya "diikuti" sesuai Bagian 4.13 Resolusi Pathname di dalamnya . Klausa yang relevan untuk mengeksekusi file adalah:

Dalam semua kasus lain, sistem harus mengawali nama path yang tersisa, jika ada, dengan isi tautan simbolik, kecuali bahwa jika konten tautan simbolik adalah string kosong, maka resolusi pathname akan gagal dengan fungsi yang melaporkan [ENOENT ] kesalahan dan utilitas menulis pesan diagnostik yang setara, atau pathname dari direktori yang berisi tautan simbolik harus digunakan sebagai pengganti konten dari tautan simbolik. Jika konten dari tautan simbolik hanya terdiri dari karakter, maka semua karakter utama dari sisa nama path harus dihilangkan dari gabungan nama path yang dihasilkan, hanya menyisakan karakter utama dari isi tautan simbolik. Dalam kasus di mana awalan terjadi, jika panjang gabungan melebihi {PATH_MAX}, dan implementasi menganggap ini sebagai kesalahan, resolusi pathname akan gagal dengan fungsi yang melaporkan kesalahan [ENAMETOOLONG] dan utilitas menulis pesan diagnostik yang setara. Jika tidak, pathname yang diselesaikan akan menjadi resolusi pathname yang baru saja dibuat. Jika pathname yang dihasilkan tidak dimulai dengan a, pendahulu dari nama file pertama pathname diambil menjadi direktori yang berisi tautan simbolik.

Dave
sumber