Sembolik bağlantı özyinelemesi - sıfırlama nedir?


64

Aynı dizine işaret eden sembolik bir bağlantıya devam ettiğimde ne olacağını görmek için küçük bir bash betiği yazdım. Çok uzun bir çalışma dizini oluşturmasını ya da çökmesini bekliyordum. Ama sonuç beni şaşırttı ...

mkdir a
cd a

ln -s ./. a

for i in `seq 1 1000`
do
  cd a
  pwd
done

Çıktının bazıları

${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a
${HOME}/a/a
${HOME}/a/a/a
${HOME}/a/a/a/a
${HOME}/a/a/a/a/a
${HOME}/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a

burada ne oluyor?

Yanıtlar:


88

Patrice, sorunun cevabındaki kaynağını tanımladı , fakat oradan neden aldığına oradan nasıl gidileceğini bilmek istiyorsan, işte uzun hikaye.

Bir işlemin şu andaki çalışma dizini, çok karmaşık olduğunu düşüneceğiniz bir şey değildir. Göreceli yolların (işlem tarafından yapılan sistem çağrılarında) başlayacağı türdeki bir dizine bir dosya tanıtıcısı olan işlemin bir özniteliğidir. Göreceli bir yolu çözerken, çekirdeğin o geçerli dizinin (a) tam yolunu bilmesi gerekmez, sadece göreceli yolun ilk bileşenini bulmak için bu dizin dosyasındaki dizin girişlerini okur (ve ..diğerleri gibi) Bu konuda dosya) ve oradan devam ediyor.

Şimdi, bir kullanıcı olarak, bazen bu dizinin dizin ağacının neresinde olduğunu bilmek istersiniz. Çoğu Unices ile, dizin ağacı döngü içermeyen bir ağaçtır. Yani, ağacın kökünden ( /) herhangi bir dosyaya giden tek bir yol var . Bu yol genellikle kanonik yol olarak adlandırılır.

Geçerli çalışma dizininin yolunu bulmak için, bir işlemin yapması gereken şey sadece yukarı yürümek ( aşağıdan köküne sahip bir ağaç görmek isterseniz aşağı doğru) ağaç köke geri dönerek düğümlerin adlarını bulmaktır. yolda.

Örneğin, geçerli dizinin olduğunu bulmaya çalışan bir işlem /a/b/c, ..dizini açar (göreceli yol, ..geçerli dizindeki girdidir) ve aynı inode numarasına sahip bir tür dizini dosyası arar ., ceşleşir, sonra açılır ../..ve bulana kadar devam eder /. Orada belirsizlik yok.

Yani ne getwd()ya getcwd()C fonksiyonları yapmak ya da en azından yapardı.

Modern Linux gibi bazı sistemlerde, kanonik yolu çekirdek dizinde araştıran geçerli dizine döndürmek için bir sistem çağrısı vardır (ve tüm bileşenlerine okuma erişimi olmasa bile, geçerli dizini bulmanızı sağlar) ve getcwd()orada öyle diyor. Modern Linux'ta, geçerli dizine giden yolu bir readlink () aracılığıyla da bulabilirsiniz /proc/self/cwd.

Bu, çoğu dilin ve ilk kabukların, yolu geçerli dizine döndürürken yaptığı şeydir.

Sizin durumunuzda, Arayabileceğin cd aistediğiniz gibi bir sembolik çünkü, may olarak kez .geçerli dizin böylece tüm değişmez getcwd(), pwd -P, python -c 'import os; print os.getcwd()', perl -MPOSIX -le 'print getcwd'senin dönecekti ${HOME}.

Şimdi, sembolikler bütün bunları karmaşıklaştırmaya başladı.

symlinksdizin ağacında atlamalara izin ver. Gelen /a/b/c, eğer /aya /a/bya /a/b/csonra kurallı yolu bir sembolik olduğu /a/b/ctamamen farklı bir şey olurdu. Özellikle, ..giriş /a/b/cmutlaka gerekli değildir /a/b.

Bourne kabuğunda, eğer yaparsanız:

cd /a/b/c
cd ..

Ya da:

cd /a/b/c/..

Sonunda geleceğin garantisi yok /a/b.

Aynen gibi:

vi /a/b/c/../d

mutlaka aynı değildir:

vi /a/b/d

kshBir şekilde bunun üzerinde çalışmak için bir mantıksal güncel çalışma dizini kavramı ortaya koydu. İnsanlar buna alıştı ve POSIX, bugünlerde çoğu mermi anlamına gelen davranışların bunu yaptığını belirterek sona erdi:

İçin cdve pwd(yerleşik komutların ve sadece onlar için (ayrıca yönelik olsa popd/ pushdonları) sahip kabukları üzerine), kabuk bulunulan dizinin kendi fikrini korur. $PWDÖzel değişkende saklanır .

Ne zaman yaparsın:

cd c/d

olsa bile cya c/dda, sembolik bağlar olurlar $PWDbidonlara /a/b, bu ekler c/dsonu öylesine için $PWDolur /a/b/c/d. Ve ne zaman:

cd ../e

Yapmak yerine chdir("../e")yapar chdir("/a/b/c/e").

Ve pwdkomut sadece $PWDdeğişkenin içeriğini döndürür .

Çünkü interaktif kabuklarda yararlıdır pwdoraya nasıl hakkında bilgi verir ve sürece sadece kullandıkça geçerli dizine bir yolunu çıkarır ..için argüman olarak cdve diğer değil komutlar, bu size sürpriz olasılığı düşüktür, çünkü cd a; cd ..ya cd a/..olur genelde size geri almak olduğun yere.

Şimdi, $PWDbir yapmadıkça değiştirilmez cd. Bir dahaki sefere cdya da pwdbir çok şey olabilir, bileşenlerinin herhangi biri $PWDyeniden adlandırılabilir. Geçerli dizin asla değişmez (silinmesine rağmen her zaman aynı inode'dur), ancak dizin ağacındaki yolu tamamen değişebilir. getcwd()geçerli dizini her seferinde dizin ağacından aşağıya doğru yürüterek hesaplar, böylece bilgileri her zaman doğrudur, ancak POSIX kabukları tarafından uygulanan mantıksal dizin için, içerdiği bilgiler $PWDeski olabilir. Yani koşarken cdya da pwdbazı mermiler buna karşı korunmak isteyebilir.

Bu özel durumda, farklı mermilerle farklı davranışlar görüyorsunuz.

Bazıları ksh93sorunu tamamen görmezden gelir, bu yüzden aradıktan sonra bile yanlış bilgi döndürür cd(ve bashorada gördüğünüz davranışı görmezsiniz ).

Gibi bazı bashveya zshkontrol yapmak $PWDhala üzerine geçerli dizine bir yol olduğunu cdancak üzerine pwd.

pdksh her ikisini de kontrol eder pwdve cd(ancak pwdgüncelleme yapmaz $PWD)

ash(en azından Debian üzerinde bulunan bir) kontrol etmez, ve bunu yaparken cd a, bu gerçekten oluyor cd "$PWD/a"geçerli dizin değişti ve eğer öyleyse, $PWDgeçerli dizine artık puan, aslında hiç değişmeyecek ageçerli dizinde dizinde , ancak bir tane $PWD(ve yoksa bir hata döndür).

Onunla oynamak istiyorsanız, şunları yapabilirsiniz:

cd
mkdir -p a/b
cd a
pwd
mv ~/a ~/b 
pwd
echo "$PWD"
cd b
pwd; echo "$PWD"; pwd -P # (and notice the bug in ksh93)

çeşitli kabuklarda.

Sizin durumunuzda, kullandığınızdan beri , a'dan bashsonra hala geçerli dizine işaret eden kontroller . Bunu yapmak için, inode numarasını kontrol etmek ve onunkiyle karşılaştırmak için değerini çağırır .cd abash$PWDstat()$PWD.

Kadar bakarken Ama $PWDyolu bu, çok fazla sembolik çözme içerir stat()bir hata ile döner, böylece kabuk olmadığı kontrol edilemiyor $PWDhala geçerli dizine karşılık, bu yüzden birlikte tekrar bir hesaplar getcwd()ve güncellemeler $PWDbuna göre.

Şimdi, Patrice'in cevabını açıklığa kavuşturmak için, bir yol ararken karşılaşılan sembolik bağlantıların kontrolünün sembolik bağlantı halkalarına karşı korunma sağlanmasıdır. En basit döngü ile yapılabilir

rm -f a b
ln -s a b
ln -s b a

Bu güvenlik görevlisi olmasaydı, bir cd a/xsistem üzerine , abağlantıların nerede olduğunu bulması , bulması bve bulduğu bağlantıyı bulması ve asüresiz olarak devam etmesi gerekirdi. Buna karşı korunmanın en basit yolu, rastgele sayıda sembolik işaretten fazlasını çözdükten sonra pes etmektir.

Şimdi mantıksal çalışma dizinine geri dönelim ve neden bu kadar iyi bir özellik değil. Bunun sadece cdkabuğun içinde olduğunu ve diğer komutların olmadığını fark etmek önemlidir .

Örneğin:

cd -- "$dir" &&  vi -- "$file"

her zaman aynı değildir:

vi -- "$dir/$file"

Bu nedenle, bazen insanların cd -Pkafa karışıklığını önlemek için komut dosyalarında her zaman kullanılmasını önerdiğini anlarsınız ( ../xyalnızca başka bir dilin yerine kabuğa yazılmış olduğundan yazılımınızın diğer komutlardan farklı bir argümanı ele almasını istemezsiniz ).

-PSeçenek devre dışı bırakmaktır mantıksal dizin böylece taşıma cd -P -- "$var"sesleniyor aslında chdir()içeriğine $var(dışında $varolduğunu -bu başka bir hikaye ama). Ve bir sonraki cd -P, $PWDbir kanonik yolunu içerecektir.


7
Tatlı İsa! Böyle kapsamlı bir cevap için teşekkürler, gerçekten çok ilginç :)
Lucas

Müthiş cevap, çok teşekkürler! Ben gibi hissediyorum tür tüm bunları biliyordum ama anlaşılan veya hepsi bir araya gelerek nasıl düşünmemiştim hiç. Harika bir açıklama.
dimo414

42

Bu, Linux çekirdek kaynağında kodlanmış bir sınırın sonucudur; reddi hizmet önlemek için, iç içe geçmiş sembolik sayısı sınırı (bulunan 40 follow_link()fonksiyonu içinde fs/namei.cdenilen, nested_symlink()çekirdek kaynak).

Büyük olasılıkla sembolik bağları destekleyen diğer çekirdekleri ile benzer bir davranış (ve muhtemelen 40'tan büyük bir limit) alırsınız.


1
Sadece durmak yerine “sıfırlamak” için bir sebep var mı? yani x%40yerine max(x,40). Sanırım dizini değiştirdiğini hala görebiliyorsun.
Lucas

4
Kaynağa bir bağlantı, merak eden herkes için: lxr.linux.no/linux+v3.9.6/fs/namei.c#L818
Ben
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.