Zwalona obsługa hardlinków w rpm i co z tego wynika

Jakub Bogusz qboosh w prioris.mini.pw.edu.pl
Wto, 25 Gru 2001, 20:41:54 CET


Chciałem poprawić zachowanie rpm przy instalowaniu glibca (hardlinki
oznaczone %lang()) - a okazało się, że w ogóle obsługa hardlinków w rpm
jest zwalona, niezależnie od %lang (obsługa oznaczonych %lang jest
zwalona jeszcze bardziej).

Testowy spec (frament, trzeba dopisać minimalny nagłówek):

#v+
%install
rm -rf $RPM_BUILD_ROOT
install -d $RPM_BUILD_ROOT/test{1,2,3}

echo > $RPM_BUILD_ROOT/test1/a
echo > $RPM_BUILD_ROOT/test1/b
echo > $RPM_BUILD_ROOT/test1/c
ln $RPM_BUILD_ROOT/test1/a $RPM_BUILD_ROOT/test2/a
ln $RPM_BUILD_ROOT/test1/a $RPM_BUILD_ROOT/test3/a
ln $RPM_BUILD_ROOT/test1/b $RPM_BUILD_ROOT/test2/b
ln $RPM_BUILD_ROOT/test2/b $RPM_BUILD_ROOT/test3/b
ln $RPM_BUILD_ROOT/test1/c $RPM_BUILD_ROOT/test3/c
ln $RPM_BUILD_ROOT/test3/c $RPM_BUILD_ROOT/test2/c

%files
%defattr(644,root,root,755)
/test1/*
/test2/*
/test3/*
#v-

Pierwsza rzecz, którą zauważyłem od razu (na początku było touch, a nie
echo) - hardlinki o długości 0 powodują wywalenie rpm z komunikatem:

error: unpacking of archive failed: cpio: Missing hard link

(jest to spowodowane metodą rozpoznawania, czy dana instancja to tylko
hardlink, czy hardlink+zawartość pliku - tylko po rozmiarze)

Przy hardlinkach o długości >0 taki pakiet z dużym prawdopodobieństwem
(to się rozstrzyga na etapie budowania pakietu) też się nie zainstaluje
jeżeli nie ma katalogów /test[123] - przy robieniu link() rpm nie
sprawdza, czy katalog, w którym ma być zrobiony link, w ogóle istnieje!
Efekt taki jak przy glibcu:

error: unpacking of archive failed on file /test1/a: cpio: link failed
- No such file or directory

Czyli żeby w ogóle pakiet miał szanse się instalować:
- hardlinki nie mogą mieć długości 0
- katalog w którym są tworzone musi być na liście plików wcześniej
  (lub być w pakiecie w Prereq: danego)
  (jeżeli w %files jest podany po prostu katalog to jest OK)

Sam plik który ma >1 dowiązanie, jest zapisywany w archiwum cpio przy
jednej ze swoich nazw (chyba niekoniecznie pierwszej czy ostatniej -
najlepiej by było ostatniej, ale nie można tego zakładać...); przy
instalowaniu pakietu lista dowiązań jest zapamiętywana i te, które
wystąpiły po zawartości pliku, są tworzone natychmiast, a te, które
przed - hurtem na końcu.

Problem pojawia się, kiedy tylko niektóre instancje hardlinka mają być
instalowane (np. hardlinki pooznaczane %lang przy
%install_langs!="all", lub jakiś plik "normalny" z hardlinkiem
oznaczonym %doc przy --excludedocs).

Jeżeli instancja, przy której zapisana jest zawartość pliku, nie jest
przeznaczona do instalacji, cała zawartość jest pomijana (eatBytes()),
i nie ma jej już skąd wziąć przy tworzeniu instancji które mają być
instalowane (dostajemy błąd "cpio: Missing hard link").
To wydaje się być poprawione w rpm 4.0.3 (procedura instalacji plików
z pakietu jest tam napisana zupełnie inaczej). Nie wiem jak poprzednio
wymienione błędy.

Jako workaround rpmbuild ma funkcję checkHardLinks(), która ujednolica
znaczniki %lang() dla plików będących hardlinkami.
Ona nie powinna istnieć (bo to instalacja powinna dobrze działać), ale
jest i... powinno zostać, także w rpm 4.0.3 (dziwiłem się dlaczego, ale
już rozumiem) przez jakiś czas (najlepiej do zmiany formatu pakietów
binarnych)!
Po prostu "trefne" pakiety (w rodzaju glibca) zbudowane bez tej funkcji
nie dawałyby się zainstalować starszą wersją rpm :(

Co z tego wynika:
1. Nie będę próbował porządnie naprawiać tworzenia hardlinków w rpm 4.0.2,
i to z czterech powodów:
 - nie chce mi się :P
 - przy procedurze instalowania plików w 4.0.2 byłoby to trudne
 - wydaje się być poprawione w 4.0.3
 - i tak nic to nie da - nadal trzeba robić "grzeczne" pakiety, dające
   się zainstalować nieco starszymi wersjami rpm.

2. W prosty sposób można poprawić tworzenie nie istniejącego katalogu
"pod" hardlinka - chyba wystarczą dwa checkDirectory(), zaraz spróbuję

3. Dobrze by było poprawić checkHardLinks tak, aby uwzględniał katalogi
oznaczone %lang zawierające hardlinki z innymi %lang (lub bez), aby
produkować pakiety zjadliwe dla nie poprawionych wersji rpm.
Wymuszanie upgrade rpm nie jest dobrym pomysłem - znając życie, jego
konieczność pojawiła by się w najmniej sprzyjającym momencie.
Nie mówiąc już o próbie zainstalowania nowego glibca przy upgrade
(glibc-2.1,rpm-3.0.6) -> (glibc-2.2,rpm-4).

BTW, czy dobrze pamiętam, że rpm 3.0.6 z /supported wymaga db3, którego
w formacie rpm3 już nigdzie nie ma?
(na szczęście samo /bin/rpm db3 nie wymaga, więc --nodeps wystarcza,
ale...)


-- 
Jakub Bogusz
http://prioris.mini.pw.edu.pl/~qboosh/



Więcej informacji o liście dyskusyjnej pld-devel-pl