deniz.in

Piyasalar

Hava durumu

Hava durumu yükleniyor

· kaynak dev.to (home feed)

El yapımı tar arşivi, checksum'ın içerikleri değil yalnızca header'ları koruduğunu gösteriyor

Bir dev.to yazarı tar'ı POSIX spesifikasyonundan yeniden uyguladı ve checksum'ın yalnızca header'ları koruyup dosya içeriklerini korumadığını, bunun yanında 8 GiB'lik bir boyut sınırı ile arşiv dizini bulunmadığını ortaya çıkardı.

El yapımı tar arşivi, checksum'ın içerikleri değil yalnızca header'ları koruduğunu gösteriyor

Sıfırdan bir tar uygulaması

dev.to'da yazan bir geliştirici, tar arşiv biçimini POSIX spesifikasyonunu temel alarak yalnızca Python'ın struct modülünü kullanarak elle yeniden inşa etti ve sonuçları GNU tar 1.35 ile Python'ın kendi tarfile kütüphanesine karşı doğruladı. Uygulama yaklaşık kırk satıra sığıyor ve bu çalışma, biçimin yıllar süren günlük kullanımdan sonra gözden kaçması kolay birkaç davranışını ortaya çıkardı.

Yapı son derece sade. Bir arşiv 512 baytlık bloklardan oluşur: her dosya bir header bloğu ve ardından 512 baytluk sınıra kadar doldurulmuş içeriğini katkıda bulunur, arşiv ise iki sıfır bloğuyla sona erer. Magic number yoktur, merkezi dizin yoktur ve o sıfır bloklarının ötesinde hiçbir şey yoktur. Header'daki sayılar — dosya boyutu, değiştirilme zamanı ve checksum'ın kendisi dahil — binary tamsayılar yerine ASCII metinle yazılmış sekizlik (oktal) basamaklar olarak saklanır.

Checksum header'da sona eriyor

Header checksum'ı, checksum alanı geçici olarak boşluklarla doldurulmuşken hesaplanan tüm 512 header baytının düz bir aritmetik toplamıdır ve sekizlik olarak geri yazılır. CRC değildir ve hash değildir. Daha da önemlisi, yalnızca header'ı kapsar.

Yazar bunun sonucunu doğrudan gösterdi. Bir dosyanın içeriğindeki tek bir baytı değiştirmek, GNU tar'ın hiç ses çıkarmadan çıkardığı, sıfır durum koduyla çıkan ve bozuk bir dosyayı diske yazan bir arşiv üretti. Header'daki tek bir baytı, dosya adının ilk karakterini değiştirmek ise tar'ın arşivi tamamen reddetmesine neden oldu.

Yazıya göre bu asimetri kasıtlıdır: checksum, bir bant sürücüsünün bir header bloğunu bir veri bloğundan ayırt edebilmesi ve kötü bir okumadan sonra yeniden eşleşme yapabilmesi için vardır; arşivden çıkanlara güvenebilmeniz için değil.

Sekizlik olarak yazılmış 8 GiB tavanı

Boyutları on bir sekizlik basamak olarak kodlamak, biçimi 8.589.934.591 baytla, yani 8 GiB'nin bir bayt altıyla sınırlar. Yazar, GNU tar'dan seyrek (sparse) bir 9 GiB dosyayı üç biçiminin her birinde arşivlemesini istedi ve üç farklı yanıt aldı. Katı POSIX ustar dosyayı tamamen reddediyor ve hata mesajı tam sınırı belirtiyor. GNU biçimi, kalan baytların sekizlik metin yerine big-endian bir binary tamsayı olduğunu bildirmek için boyut alanının en üst bitini ters çeviriyor. POSIX pax biçimi üçüncü bir yol izliyor: düz key=value kayıtları içeren ekstra bir header yazıyor, gerçek boyutu uzunluk sınırı olmayan ondalık metin olarak oraya yerleştiriyor ve normal boyut alanında sıfır bırakıyor. Yazar, eski bir araç büyük bir arşivde takıldığında bunun nedeninin genellikle bu farklılık olduğuna dikkat çekiyor.

Dizin yok, dolayısıyla aramalar dosyayı baştan tarıyor

İçindekiler tablosu olmadığından, bir dosyayı bulmanın tek yolu header'ları en baştan dolaşmak ve her boyut alanını kullanarak ardından gelen verinin üzerinden atlamaktır. Toplam yaklaşık 100 MB olan 2.000 dosyalık bir benchmark'ta, son üyeyi çıkarmak Python'ın tarfile'ı ile 2.001 seek ve 34 ms boyunca baytların yüzde 3,0'ına dokunmayı gerektirdi; oysa dosyanın sonunda merkezi dizin tutan ZIP için bu oran yüzde 0,2, 7 seek ve 3 ms idi. Sıkıştırma bu seek kısayolunu bile ortadan kaldırır, çünkü bir gzip akışı atlanamaz: bir .tar.gz içindeki son dosyayı okumak önündeki her şeyin tamamen açılmasını zorunlu kıldı, oysa ilkini okumak neredeyse bedavaydı.

Eksik dizinin yazarın öngörmediği bir avantajı var. tar -r ile ekleme hiçbir şeyi yeniden yazmaz; yeni girdi arşivin sondaki doldurma bloklarının üzerine yazılır ve testte arşiv hiç büyümedi bile. Mevcut bir girdiyle aynı adı taşıyan güncellenmiş bir dosya eklemek iki kopya oluşturur ve çıkarma işlemi sonraki kopyayı verir. Bu davranış, GNU tar'ın --occurrence=1 verilmedikçe eşleşme bulduktan sonra bile erken durmak yerine sıkıştırılmış bir arşivi sonuna kadar okumasının nedenini de açıklıyor.

Neden önemli

Çıplak bir .tar, içindeki dosyalar için size hiçbir bütünlük garantisi vermez. İnsanların .tar.gz ile ilişkilendirdiği koruma gzip veya xz'den gelir; bu sıkıştırma biçimleri tüm akış üzerinde kendi kontrollerini taşır ve sıkıştırılmamış arşivler saklayıp aksini varsaydığınızda bilinmesi gereken bir bağımlılıktır. Biçimin yaşı başka şekillerde de kendini gösteriyor: yazar, aynı dosyaları iki saniye arayla arşivlemenin, değiştirilme zamanı alanının içindeki bir baytta farklılık gösteren arşivler ürettiğini buldu. Bunların hiçbiri bozuk bir davranış değil; bu, bant çağından bir biçimin tam olarak tasarlandığı şeyi yapması ve geliştiriciler için pratik çıkarım, garantilerinin tam olarak nerede bittiğini bilmek.

  • #tar
  • #file-formats
  • #data-integrity
  • #python
  • #gnu-tar