- předchozí článek - následující článek - obsah - úvodní stránka -

Linuxové noviny 05-06/2001

Špatně pojmenované soubory na CD-ROM a co s tím...

Milan Šorm, 14. června 2001

Motivace

Čas od času v překotném spěchu vypálím CD-ROM a nepřemýšlím nad tím, co jsem na něj vlastně naskládal a jak. Později se ukáže, že se jedná o soubory pojmenované tak, že se v jejich názvu vyskytují ne zcela korektní znaky (např. znaky v kódování CP1250) nebo znaky špatné velikosti, s mezerou apod. Některé aplikace - např. ty spouštěné pod Wine - se potom s takovými názvy buď velice špatně nebo vůbec nesnášejí. A je potom škoda vypáleného CD, protože málokdy je člověk ochoten startovat jiný systém jen pro překopírování dat ze špatného CD.

Analogický problém jsem získal v okamžiku, kdy jsem instalační CD pro Oracle (OTN licence) vypálil se špatnou velikostí písmen (všechno malými), takže se s tím instalační program nevypořádal. I tady jsem řešil otázku, zda CD vypálit znovu anebo se s tím nějak vypořádat.

Nakonec jsem se (především proto, že nerad všechno vzdávám) rozhodl, že poupravím ovladač ISO souborového systému v Linuxu, abych byl schopen korektně číst příslušná CD. Moduly pro jádro už jsem několikrát programoval, takže jsem se této operace ani moc nebál.

Jak začít

V době, kdy jsem cítil potřebu něco dělat se špatně vypálenými CD kralovalo na mém počítači jádro 2.2.14, ve kterém se vše potřebné ohledně isofs nachází v adresáři fs/isofs/. Abych nemusel vždy překládat všechny moduly, změnil jsem Makefile v tomto adresáři tak, že byl schopen sestavit modul isofs.o jen po zavolání make. Pro tento účel bylo nutné zjistit, jak vypadá volání gcc (tj. jaké parametry je nutné nastavit) - pro tento účel bohatě postačilo spustit překlad jádra a obtáhnout myší volané parametry (jistě ne zcela hackerský způsob, ale je to poměrně rychlá a účinná metoda). Pro překlad isofs.o tedy potřebujeme nastavit CFLAGS na

-D__KERNEL__ -I.:/usr/src/linux/include -Wall\
-Wstrict-prototypes -O2 -fomit-frame-pointer\
-fno-strict-aliasing -pipe \
-fno-strength-reduce -m486 -malign-loops=2\
-malign-jumps=2 -malign-functions=2 -DCPU=686\
-DMODULE -DPOPDEBUG -c

Samozřejmě místo 686 je třeba uvést procesor, který máme (třeba i univerzální 386) a analogicky je třeba se zamyslet nad -m486 a cestou k hlavičkovým souborům jádra. Dále si v Makefile dodefinujeme pravidla pro jednotlivé .o soubory z našeho adresáře -- tedy volání gcc s CFLAGS a -o jmeno.o jmeno.c (tj. přeložíme všechny zdrojové soubory na objektové.

Závěrečné sestavení isofs.o provedeme pomocí programu ld (opět si zadefinujeme do Makefile isofs.o závislé na všech objektových souborech) např. takto:

isofs.o: dir.o file.o inode.o joliet.o\
         namei.o rock.o symlink.o util.o\
         ld -m elf_i386 -r -o isofs.o\
         dir.o file.o inode.o joliet.o \
         namei.o rock.o symlink.o util.o

Nyní cvičně vyzkoušíme, že nám příkaz make sestaví isofs.o (je třeba mít cíl isofs.o jako první v Makefile nebo si na začátek dopsat all: isofs.o). Pokud vše dopadlo korektně, můžeme začít experimentovat s isofs.

Modul isofs obecně podporuje několik druhů rozšíření pro pojmenování souborů. Pokud víme, že je naše CD konkrétně v Joliet nebo v Rock Ridge formátu, stačí modifikovat přímo získávání jména souboru pro tento formát, v ostatních případech musíme modifikovat již načtené jméno souboru na několika místech. O jaké místa jde zjistíme vyhledáním všech volání get_*_filename v .c zdrojových souborech - získáme dvě místa - dir.c a namei.c. Nejprve se budeme věnovat jen modifikaci pro konkrétní formát a nakonec i modifikaci obecné.

Rock Ridge formát

Funkce pro práci s tímto formátem se nacházejí v souboru rock.c. Funkce get_rock_ridge_filename() má za úkol získat správné jméno souboru pro záznam vyčtený z ISO CD, tj. s využitím struktury iso_directory_record získat jméno souboru a toto jméno umístí do retname (druhý parametr) a délku řetězce vrátí jako svůj výsledek. Pokud se získání jména nezdaří, vrátí 0.

Máme možnost buď udělat zásah přímo do této funkce, nebo si nadefinovat funkci identickou, která bude zavolána, pokud zjistíme, že chceme provést překlad jména pro divné CD. Já si obvykle zadefinuji další parametr pro modul (v inode.c do funkce parse_options() připojím analýzu parametru např. corrupt_cdX, kde X je číslo CD, které se má opravit - mám několik špatně vypálených CD - a toto číslo si poznamenám do struktury popt, kterou si rozšířím o vhodný atribut) a potom uvnitř této funkce (get_rock_ridge_filename) testuji tento parametr v popt - zde se již k němu přistupuje přes strukturu inode (inode->i_sb->u.isofs_sb.atribut) a zavolám příslušnou konverzní funkci. Tímto způsobem můžu mít jeden modul pro čtení CD a při připojování CD nastavením parametru -o corrupt_cdX můžu zapojit přepisovací mechanismus pro příslušné CD nebo přepisování jmen souborů úplně vypojit.

Ještě lepším řešením by bylo vybírat přepisovací mechanismus na základě např. čísla CD, které lze též zjistit, ale tehdy ovšem nebyl čas si s tím příliš vyhrávat. Pro vlastní přepis je třeba zdůraznit, že jméno vyčteného souboru z ISO CD najdete ve struktuře iso_directory_record jako atribut name (de->name) a je složeno z krátkého ISO jména, středníku a pořadového čísla. Tečky v názvu jsou též nahrazeny středníkem. Vlastní přepisovací rutina již poté může začít vytvářet správné jméno - buď velice primitivní metodou srovnání vyčteného jména s nějakým vzorem (strcmp) nebo inteligentnějším způsobem - např. jsem jednou potřeboval ořezat češtinu, což šlo zařídit jedním cyklem a testem na každý znak a jeho případné ořezání atp. Jako vrácenou hodnotu vrátíme strlen() upraveného jména a jméno vrátíme coby ukazatel ve druhém argumentu. Tím je přepisování pro Rock Ridge hotovo.

Joliet rozšíření

Řešení s Joliet rozšířením jsou velmi podobná řešením s Rock Ridge. Zajímá nás zdrojový kód joliet.c a funkce get_joliet_filename. Zde je již připraveno přepisování např. pomocí NLS, takže si můžeme jednoduše doplnit svoje další přepisování např. voláním stejných funkcí jako v případě Rock Ridge. Pouze je třeba myslet na detail, že u Joliet je struktura inode místo ve třetím již ve druhém parametru a vracené jméno naopak místo ve druhém ve třetím parametru - a místo retname se jmenuje outname. Mimo těchto detailů je vše stejné.

Obecná metoda

Obecná metoda spočívá v modifikaci namei.c, kde se provádí získávání jména souboru. Funkce isofs_find_entry() je určena k získání položky adresáře - je nutné zde nalézt místo, kde se provádí volání get_rock_ridge_filename() a get_joliet_filename() (mimo jiné se tu volá i get_acorn_filename() atd.) a doplníme si další else if podle našeho parametru corrupt_cdX. Jméno souboru je nutné vrátit v ukazateli dpnt, jeho délku v dlen. Ukazatel dpnt obvykle ukazuje na page, což je alokovaný prostor pro jméno souboru. V tomto místě lze provést řadu dalších změn (ukrytí souboru, přesunutí v adresářové struktuře apod.).

Bohužel u obecné metody je ještě nutné změnit dir.c, kde se ve funkci do_isofs_readdir() provádí načtení struktury adresáře. Postup je velmi analogický, avšak jméno souboru se umísťuje do ukazatele p (který ukazuje na tmpname prostor) a délka do len.

Obecná metoda umožňuje čtení CD bez ohledu na nastavení parametrů Rock Ridge a Joliet, což jsem využil např. tehdy, když jsem omylem Rock Ridge vypálil jako Joliet (potom bylo nutné při vhodném corrupt_cdX zavolat Rock Ridge mechanismus byť se CD tvářilo jako Joliet, tedy i tato metoda má své využití.

Ladění

Vše se jistě nezaří na první pokus. Při programování jádra nemáme velké možnosti knihovny C (libc), nemáme ani moc ladících prostředků (jen ladící výpisy), ale i tak to může být velmi zábavné.

Pro ladící výpisy s oblibou využijeme printk(), pomocí kterého si můžeme vypisovat hodně různých nápisů, které nám klogd zachytí a (pokud si jej dobře nastavíme), umístí např. do /var/log/kernel.

Pokud máte již podporu CD v jádře napevno nebo si nechcete stávající podporu CD zlikvidovat, je možné udělat koexistenci a nazvat si filesystém např. iso9660b (soubor inode.c, definice struktury file_system_type, první parametr struktury).

Vlastní modul pak zavedete obvyklým způsobem (insmod ./isofs.o) a v /proc/filesystems si můžete existenci nového filesystému ověřit. Při připojování špatného CD pak zadáte příslušný parametr -o corrupt_cdX a pomocí ls si můžete ověřit, zda vše šlape k Vaší plné spokojenosti.

A závěrem...

Prezentované řešení nepatří mezi ideální. Ideální řešení by umožňovalo přes /proc spolupráci s user-space programem pro korekci jmen souborů podle čísla CD - ale jako rychlá berlička pro ty, kteří si přinesou domů z práce nějaké pěkné CD, které si ale špatně vypálili, to pomůže. Já osobně potřebuji podobnou věc skoro každý měsíc (možná by se už vyplatilo dávat si při vypalování pozor). *


- předchozí článek - následující článek - obsah - úvodní stránka -