<?xml version="1.0" encoding="utf-8"?>

<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
	<!ENTITY % fithesis SYSTEM "http://www.fi.muni.cz/~xpavlov/fithesis.mod"> %fithesis;
]>

<book lang="cs">

<bookinfo>
	<title>Snímek souborového systému</title>
	<subtitle>Diplomová práce</subtitle>
	<author>
		<firstname>Libor</firstname>  
		<surname>Vaněk</surname>
		<fi:woman>false</fi:woman>
		<fi:faculty>fi</fi:faculty>
		<fi:advisor>Jan Kasprzak</fi:advisor>
	</author>
	<pubdate>jaro 2006</pubdate>
	<fi:thanks>
		<para>Na tomto místě bych rád poděkoval několika lidem, kteří mi pomáhali odbornou radou nebo s realizací této práce. Děkuji proto:</para>
		<para>Mgr. Janu Kasprzakovi, za úvod do vývoje v kernel-space</para>
		<para>Mgr. Lukášovi Hejtmánkovi, za konzultace a tipy</para>
		<para>Evgeniy Polyakovovi, autorovi programu connector za pomoc při řešení problémů</para>
	</fi:thanks>
	<abstract>
		<para>První část této práce zkoumáme motivaci pro používání snapshotů a uvedeme přehled a srovnání jednotlivých variant v OS Linux.</para>
		<para>V druhé části práce implementujeme modul pro podporu snapshotů na VFS vrstvě operačního systému Linux.</para>
	</abstract>
	<keywordset>
		<keyword>Linux</keyword>
		<keyword>kernel</keyword>
		<keyword>VFS</keyword>
		<keyword>filesystem</keyword>
		<keyword>snapshot</keyword>
	</keywordset>
</bookinfo>
<toc/>

<chapter id="ch01">
	<title>Úvod</title>
	<sect1 id="sec0101">
	<title>Motivace</title>
	<para>S rostoucím množstvím uchovávaných a zpracovávaných dat se čím dál tím více  potýkáme s problematikou, jak tyto data bezpečně zálohovat a jakým způsobem zajistit jejich konzistentnost. Nejběžnějším postupem pro zálohování dat je vytvoření kopie dat z jednoho disku nebo spíše diskového pole na jiné, nejčastěji však na páskovou mechaniku (streamer).</para>
	<para>Tento způsob zálohování přináší netriviální problémy. Nejčastějším problémem je rychlost zálohování - páskové mechaniky jsou totiž typicky řádově pomalejší než disková pole. I vytváření kopie mezi jednotlivými diskovými poli může trvat i několik desítek hodin v závislosti na objemu dat a typu propojení mezi diskovými poli. Nelze proto očekávat, že po dobu vytváření záloh budou všechny aplikace pozastaveny a nebude docházet ke změně dat.</para>
	<para>Řešením tohoto problému je vytváření tzv. snapshotů<indexterm><primary>snapshot</primary></indexterm> (snímků<indexterm><primary>snímek</primary></indexterm>). Idea toho přístupu spočívá v tom, že ačkoliv je v daný časový okamžik celkový objem dat relativně velký, v průběhu času se mění jen malá část dat. Změnou dat v tomto kontextu chápeme i smazání nebo přidání dat. Tohoto empirického poznatku využijeme tak, že provedeme atomickou operaci "vytvoř snapshot". Tato operace pouze poznačí informaci o vytvoření snapshotu, jaké části dat se snapshot týká a kam se mají kopírovat případné zálohy původních dat v případě požadavku na jejich změnu. Následně mohou aplikace pokračovat v běžném provozu. Při požadavku na změnu dat dojde k pozastavení tohoto požadavku, následně se vyhodnotí, která data mají být změněna, následuje provedení zálohy původních dat. Teprve potom se provede požadavek na změnu dat. Tato technika se nazývá Copy-on-Write<indexterm><primary>Copy-on-Write</primary></indexterm>. Stav dat v okamžiku provedení snapshotu se zrekonstruje pomocí aktuálních dat a odzálohovaných původních dat.</para>
	</sect1>
</chapter>

<chapter id="ch02">
	<title>Přehled variant snapshotů</title>
	<para>S přihlédnutím k obsahu druhé části této práce, která se zabývá implementací snapshotů ve VFS vrstvě OS Linuxu, se v této kapitole zaměříme převážně na možnosti snapshotů v OS Linux.</para>
	<sect1 id="sec0201">
		<title>Definice základních pojmů</title>
		<para><variablelist>
			<varlistentry>
				<term><filename>Copy-on-Write<indexterm><primary>Copy-on-Write</primary></indexterm></filename></term>
				<listitem><para>Technika, kdy se vytvoří virtuální kopie dat a skutečná kopie dat se provádí až v okamžiku požadavku na změnu dat.</para></listitem>
			</varlistentry>
			<varlistentry>
				<term><filename>Kernel-space<indexterm><primary>Kernel-space</primary></indexterm></filename></term>
				<listitem><para>Označení programu běžícího jakou součást jádra operačního systému. Toto má za následek některá omezení (nelze používat žádné externí knihovny, jen funkce jádra) i možnosti (přímý přístup k jednotlivým zařízením a datovým strukturám a funkcím jádra). Z hlediska bezpečnostního i stability je snaha, aby v kernel-space běžela vždy jen ta nejmenší možná část projektu.</para></listitem>
			</varlistentry>
			<varlistentry>
				<term><filename>Read-only snapshot<indexterm><primary>Read-only snapshot</primary></indexterm></filename></term>
				<listitem><para>Snapshot dat, který je k dispozici pouze pro čtení. Typické použití pro zálohování dat.</para></listitem>
			</varlistentry>
			<varlistentry>
				<term><filename>Read-write snapshot<indexterm><primary>Read-write snapshot</primary></indexterm></filename></term>
				<listitem><para>Snapshot dat, kde můžeme data snapshotu dále měnit bez toho, aby byla změněna původní data. Typické použití pro správu více verzí dat. Někdy se nazývá tato vlastnost jako klonování (cloning<indexterm><primary>cloning</primary></indexterm>) dat.</para></listitem>
			</varlistentry>
			<varlistentry>
				<term><filename>Roll-back snapshotu<indexterm><primary>Roll-back snapshotu</primary></indexterm></filename></term>
				<listitem><para>Operace, která uvede aktuální data do stavu v okamžiku snapshotu. Veškeré provedené změny dat od provedení snapshotu budou ztraceny.</para></listitem>
			</varlistentry>
			<varlistentry>
				<term><filename>Snapshot<indexterm><primary>Snapshot</primary></indexterm> (snímek)<indexterm><primary>Snímek</primary></indexterm></filename></term>
				<listitem><para>Stav (obsah) dat v určitý časový okamžik. Často se jedná o virtuální obraz, který je skládán z více datových zdrojů.</para></listitem>
			</varlistentry>
			<varlistentry>
				<term><filename>Syscall<indexterm><primary>Syscall</primary></indexterm> (systémové volání)<indexterm><primary>Systémové volání</primary></indexterm></filename></term>
				<listitem><para>Volání funkce jádra operačního systému user-space programem. Liší se velmi zásadně od volání jiné funkce user-space programu nebo knihovny - dochází ke změně kontextu přerušení a běh funkce je prováděn v privilegovaném režimu jádra. V OS Linux je doporučeno pokud možno vyvarovat se volání systémového volání z jiného systémového jádra nebo obsluhy přerušení.</para></listitem>
			</varlistentry>
			<varlistentry>
				<term><filename>User-space<indexterm><primary>User-space</primary></indexterm></filename></term>
				<listitem><para>Označení "standardního" programu. Program komunikuje s jádrem operačního systému pomocí tzv. systémových volání (syscall).</para></listitem>
			</varlistentry>
			<varlistentry>
				<term><filename>VFS<indexterm><primary>VFS</primary></indexterm> (Virtual Filesystem Layer)</filename></term>
				<listitem><para>Vrstva v OS mající na starosti primárně správu jednotlivých souborových systémů, předávání požadavku na práci se souborovým systémem, soubory a adresáři konkrétním souborovým systémům. Často se také stará o cachování převodu jména souboru nebo adresáře na ukazatel na konkrétní strukturu souborového systému.</para></listitem>
			</varlistentry>
		</variablelist></para>
	</sect1>
	<sect1 id="sec0202">
		<title>Snapshoty na úrovni blokových zařízení</title>
		<para>Blokovým zařízením rozumíme nejnižší možnou úroveň operačního systému přístupu k datům - typicky tedy disky, disková pole nebo různé speciální zařízení (RAM disky, síťové zařízení apod.). V OS Linux jsou snapshoty na úrovni blokových zařízení implementovány ve správě logických svazků (Logical Volume Management)<indexterm><primary>Logical Volume Management</primary></indexterm>.</para>
		<para>Mezi běžně používné nástroje ke správě logických svazků patří LVM2<indexterm><primary>LVM2</primary></indexterm> (vyvíjený primárně firmou RedHat, je standardní součástí jádra Linuxu) a EVMS<indexterm><primary>EVMS</primary></indexterm> (vyvinutý firmou IBM, není součástí standardního jádra Linuxu). Obě tyto implementace poskytují z našeho pohledu stejnou funkčnost - možnost vytvořit read-only snapshot. Zatímco LVM2 umožňuje vytvořit snapshot pouze logických svazků, tak EVMS podporuje snapshoty pro veškeré typy objektů (disky, segmenty, oblasti a tzv. feature-objekty). EVMS také podporuje roll-back snapshotu.</para>
		<sect2 id="sec020201">
			<title>Výhody snapshotů na úrovni blokových zařízení</title>
			<para>Hlavní výhodou snapshotů na úrovni blokových zařízení je jejich vysoká efektivnost. <citation>BLOCK</citation> Protože se pracuje s relativně malými bloky dat (typická velikost - 4 kB) je u typických aplikací dosaženo vysoké efektivity.</para>
			<para>Teoreticky je možné setkat se i se situací, že dojde ke změně několika málo bytů v mnoha blocích. V praxi se s tímto chováním nesetkáme především z toho důvodu, že souborové systémy také pracují s bloky v řádech kB a až na exotické vyjímky nikdy s jednotlivými byty.</para>
			<para>Relativní jednoduchost vede také k vysoké rychlosti a spolehlivosti této varianty snapshotů.</para>
		</sect2>
		<sect2 id="sec020202">
			<title>Nevýhody snapshotů na úrovni blokových zařízení</title>
			<para>Hlavní nevýhodou této varianty je, že je možné provést snapshot pouze celého blokového zařízení (disku, diskového pole, svazku apod.). Toto vyplývá z neznalosti vnitřní struktury dat (typicky souborového systému) a proto nelze provést snapshot pouze určité části dat (např. pouze aktuálního projektu). Na tuto skutečnost je nutné dbát už při počáteční instalaci systému a rozdělování úložného prostoru do svazků a souborových systémů. V podstatě neexistuje žádná jednoduchá možnost, jak následně toto rozdělení změnit. Jedna z možností je odzálohovat všechny data, změnit rozdělení logických svazků a data obnovit. Pokud to souborové systémy umožňují, je možné data postupně přesouvat a podle toho dynamicky měnit velikosti souborových systémů a svazků. Jedná se však o rizikovou činnost.</para>
			<para>Další nevýhodou můžeme spatřovat v tom, že u standardní implementace LVM2<indexterm><primary>LVM2</primary></indexterm> je nutné při vytváření snapshotu specifikovat oblast, kam se budou postupně ukládat zálohovat původní datové bloky. Jakmile však je tato oblast zaplněna, není možné ji zvětšit, změnit ani přidat další oblast. Tento jev se může vyskytnout třeba v případě, že záloha trvá výrazně déle než byl původní odhad a/nebo dochází k častějším změnám dat. V implementaci EVMS<indexterm><primary>EVMS</primary></indexterm> je toto ošetřeno možností přidávat podle potřeby další datové oblasti.</para>
			<para>Z neznalosti vnitřní struktury dat vyplývá nutnost zajistit externími mechanismy konzistentnost dat v okamžiku vytvoření snapshotu. Například aby současně s prováděním snapshotu nebyl prováděn zápis do nějakého kritického souboru. V podstatě všechny moderní souborové systémy podporují funkci dočasného uzamčení souborového systému (tzv. "freeze") právě pro potřeby vytváření snapshotů. Tedy až na speciální aplikace (databáze apod. - více v kapitole <xref linkend="sec0206"/>) lze tuto podmínku splnit.</para>
			<para>Protože jsou prováděny zálohy jednotlivých datových bloků, nelze tyto zálohy jakkoliv využít v případě výpadku aktuálního blokového zařízení. Taktéž nelze tyto odzálohované bloky nijak dále zpracovávat.</para>
		</sect2>
	</sect1>
	<sect1 id="sec0203">
		<title>Podpora snapshotů v souborovém systému</title>
		<para>Se systematickou podporou snapshotů v souborových systémech se setkáváma až v posledních několika letech. Nejprve s touto vlastností přišly souborové systémy WAFL<indexterm><primary>WAFL</primary></indexterm> (firma NetApp) a VxFS<indexterm><primary>VxFS</primary></indexterm> (firma Veritas). V současnosti jsou snapshoty plně podporovány v souborových systémech Fossil<indexterm><primary>Fossil</primary></indexterm> (experimentální OS Plan 9) <citation>PLAN9</citation>, ZFS<indexterm><primary>ZFS</primary></indexterm> (OS Solaris 10), NTFS<indexterm><primary>NTFS</primary></indexterm> (OS Windows) a UFS2<indexterm><primary></primary></indexterm> (OS FreeBSD).</para>
		<para>Ze zajímavých vlastností bych rád upozornil na souborový systém Fossil, který je navržen tak, aby podporoval tvorbu snapshotů přímo jednotlivými uživateli.</para>
		<para>Mezi nejnovější souborové systémy patří ZFS<indexterm><primary>ZFS</primary></indexterm>, který byl uveden v roce 2005 jakou součást nového OS Solaris 10. Nejedná se v pravém slova smyslu o čistě souborový systém, protože v sobě obsahuje i prvky správy logických jednotek (LVM). Díky tomuto heterogennímu modelu podporuje ZFS<indexterm><primary>ZFS</primary></indexterm> jak read-only, tak read-write snapshoty. Za pomoci integrovaného správce logických jednotek provádí snapshoty na úrovni bloků a dosahuje vysoké efektivity read-write snapshotů.</para>
		<para>Souborový systém NTFS<indexterm><primary>NTFS</primary></indexterm> podporuje funkci Shadow Copy<indexterm><primary>Shadow Copy</primary></indexterm>. Jedná se o funkci umožňující jednotlivým uživatelům vytvářet snapshoty jednotlivých adresářů. <citation>NTFS</citation></para>
		<sect2 id="sec020301">
			<title>Výhody snapshotů v souborovém systému</title>
			<para>Znalost vnitřní struktury souborového systému (hlavičky, metadata, transakční logy) umožňuje větší úspornost snapshotů, kdy například nedochází k vytváŕení snapshotu pro transakční log. Taktéž je zajištěna vnitřní konzistenost dat.</para>
		</sect2>
		<sect2 id="sec020302">
			<title>Nevýhody snapshotů v souborovém systému</title>
			<para>Kromě souborového systému NTFS žádný jiný souborový systém nepodporuje snapshoty konkrétního adresáře. Vždy je nutné provést snapshot celého souborového systému, což není vždy žádoucí..</para>
			<para>Časté používání snapshotů souborového systému omezuje doposud také úzká provázanost s konkrétním OS. Tato kombinace konkrétního souborového systému a OS nemusí také vyhovovat pro všechny případy nasazení - např. různá jednoúčelová (embedded) zařízení.</para>
			<para>Každý souborový systém má vlastní filozofii tvorby snapshotů a nástroje na jejich správu.</para>
			<para>Žádná ze současných implementací snapshotů v souborovém systému nepodporuje možnost provádět kopírování snapshotovaných dat na jiný souborový systém. Nelze proto například ukládat data snapshotů na jiné úložiště dat připojené přes počítačovou síť.</para>
			<para>Snapshoty v souborových systémech provádějí (podobně jako snapshoty blokových zařízení) zálohy jednotlivých datových bloků, proto v případě ztráty původního souborového systému nelze z těchto datových bloků extrahovat žádnou konzistetntní informaci.</para>
			<para>Nelze provádět konzistentní snapshoty souborů otevřených pro zápis - více o této problematice v kapitole <xref linkend="sec0206"/>.</para>
		</sect2>
		
	</sect1>
	<sect1 id="sec0204">
		<title>Snapshoty jako speciální souborový systém</title>
		<para>Jde o technicky velmi zajímavý přístup. Je vytvořen virtuální souborový systém, který zobrazuje "pohled" na původní (primární) souborový systém a veškeré operace změn dat jsou přesměrovány do jiného (sekundárního) souborového systému. Tím dochází k zachování původního stavu primárního souborového systému. Nejedná se tedy v čistě formálním smyslu o snapshot. Z hlediska konečného efektu je důsledek stejný - dochází k zachování stavu primárního souborového systému při zachování možnosti provádět na tomto souborovém systému změny.</para>
		<para>Nedílnou součástí tohoto virtuálního souborového systému tvoří funkce nazývaná "filesystem union"<indexterm><primary>filesystem union</primary></indexterm>. Tato funkce umožňuje poskládat - sloučit - stav několik souborových systémů do jednoho virtuálního. Aktuální stav modifikovaného virtuálního souborového systému získáme sloučením primárního (původního) souborového systému a sekundárního systému.</para>
		<para>Jediný stávající souborový systém s touto funkcionalitou, je souborový systém UnionFS<indexterm><primary>UnionFS</primary></indexterm> <citation>UnionFS</citation> pro OS Linux. Tento souborový systém je hojně využíváný při tvorbě tzv. LiveCD/DVD<indexterm><primary>Live CD/DVD</primary></indexterm>. Primární souborový systém uložený na CD nebo DVD je k dispozici pouze pro čtení a tedy veškeré operace změn dat jsou přesměrovány buďto na ramdisk (a tedy po vypnutí stroje ztraceny) nebo třeba na USB memory stick, který umožní jejich zachování i po vypnutí systému.</para>
		<sect2 id="sec020401">
			<title>Výhody speciálního snapshotového souborového systému</title>
			<para>V případě změny jsou soubory a adresáře kopírovany jako celek. Dojde-li k výpadku primárního souborového systému, jsou stále tato data "smysluplná". Dále je tento přístup naprosto nezávislý na použitých souborových systémech.</para>
			<para>Zálohy původních změn souborů mohou být ukládány na libovolný souborový systém - tedy i na sdílený síťový souborový systém.</para>
		</sect2>
		<sect2 id="sec020402">
			<title>Nevýhody speciálního snapshotového souborového systému</title>
			<para>Tato metoda přináší dvě zásadní nevýhody. Jelikož se jedná o práci na úrovni souborů, pak i v případě velmi malé změny dat dochází k provedení zálohy celého souboru. To může v některých případech vést k neúnosné časové náročnosti či dokonce nemožnosti tuto metodu použít - například v případě, kdy souborový systém obsahuje jeden nebo několik velmi velkých souborů. Záloha těchto souborů by v případě žádosti o jejich změnu byla v podstatě srovnatelná se zálohou celého souborového systému.</para>
			<para>Další nevýhodou je skutečnost, že pro vytvoření snapshotu je nutno připojit nový (virtuální) souborový svazek a přesměrovat veškeré aplikace z původního souborového systému na nový. V úvahu připadá také možnost stávající souborový svazek odpojit, připojit jej na jiné místo a následně místo něj připojit nový virtuální svazek. V každém případě je nutno dodržet podmínku, aby pro vytvoŕení snapshotu všechny aplikace uzavřely všechny soubory a to i takové, které jsou otevřeny pouze pro čtení a nemohou snapshot jakkoliv ovlivnit.</para>
		</sect2>
	</sect1>
	<sect1 id="sec0205">
		<title>Snapshoty na úrovni VFS vrstvy</title>
		<para>Jedná se o kompromisní řešení mezi nativní podporou snapshotů přímo v souborovém systému a speciálním souborovým systémem určeným pouze pro snapshoty. Konkrétní implementace tohoto přístupu tvoří náplň druhé části této práce.</para>
		<para>Princip této techniky spočívá v zachytávání požadavků na změnu dat ve VFS<indexterm><primary>VFS</primary></indexterm> vrstvě operačního systému. Tyto pak následně vyhodnocuje na základě jména souboru/adresáře a typu požadované operace. V případě nutnosti provedení zálohy dat a je zpracování požadavku pozastaveno na dobu nutnou k vytvoření záložní kopie dat.</para>
		<para>Pro následné připojení a práci se snapshotem využívá tato konkrétní implementace virtuální souborový systém UnionFS<indexterm><primary>UnionFS</primary></indexterm>, který je pro tyto účely více než postačující. Díky jeho vlastnosti přesměrovávat změny na původních datech do jiného souborového systému lze dokonce vytvářet read-write snapshoty. Read-write snapshot vytvoříme provedením sjednocení původního (primárního) souborového systému a odzálohovaného (sekundárního) souborového systému vytvořeného ve VFS vrstvě. Výsledný pohled na souborové systémy připojíme pomocí UnionFS<indexterm><primary>UnionFS</primary></indexterm> a operace změny dat přesměrujeme do jiného (terciálního) souborového systému.</para>
		<sect2 id="sec020501">
			<title>Výhody snapshotů na úrovni VFS vrstvy</title>
			<para>Protože tato technika pracuje ve VFS<indexterm><primary>VFS</primary></indexterm> vrstvě OS, jedná se o metodu nezávislou na souborovém systému. Lze proto použít libovolný souborový systém. Tato technika, podobně jako speciální snapshotový souborový systém, kopíruje celé soubory a adresáře. Odzálohovaná data jsou tedy konzistetní a použitelná i v případě výpadku původního souborového systému.</para>
			<para>Tímto způsobem je zachována možnost provádět zálohu dat na libovolný souborový systém - tedy i na síťový svazek.</para>
			<para>Hlavním rozdílem a výhodou této metody oproti speciálnímu souborovému systému je, že po instalaci tzv. háčků<indexterm><primary>háčky</primary></indexterm> (hooks<indexterm><primary>hooks</primary></indexterm>) do jádra OS (a typicky následném rebootu) je možné aktivovat snapshoty pro libovolný souborový systém kdykoliv. Možné je následně provádět upgrade na novější verze bez nutnosti rebootu. Háčky v jádře jsou navrženy tak, aby neovlivňovaly funkce a výkon jádra v případě, že nejsou snapshoty aktivní či dokonce není příslušný SW nainstalován.</para>
			<para>Konkrétní implementace demonstrovaná v druhé části práce podporuje snapshoty na úrovni adresářů. Jedná se tedy kromě podpory v souborovém systému NTFS<indexterm><primary>NTFS</primary></indexterm> o jedinou možnost, jak provádět snapshoty konkrétního adresáře bez nutnosti odpojovat a znovu připojovat celý souborový systém. Narozdíl od NTFS<indexterm><primary>NTFS</primary></indexterm> je zde možné provádět zálohu dat na libovolný jiný souborový systém, tedy i datové úložiště.</para>
		</sect2>
		<sect2 id="sec020502">
			<title>Nevýhody snapshotů na úrovni VFS vrstvy</title>
			<para>Jedinou významnou nevýhodu tohoto přístupu (podobně jako u speciálního snapshotového souborového systému) lze nalézt v tom, že v případě změny dat se provádí kopie kompletního souboru se všemi důsledky z toho plynoucími (více viz kapitola <xref linkend="sec020402"/>).</para>
			<para>Konkrétní implementace snapshotů ve VFS<indexterm><primary>VFS</primary></indexterm> provádí kopii souboru pokaždé, když je soubor otevřen pro zápis, bez dalšího zkoumání, zda skutečně dojde ke změně obsahu souboru. Tato vlastnost a možnost nápravy diskutujeme v sekci <xref linkend="chZaver"/>.</para>
		</sect2>
	</sect1>
	<sect1 id="sec0206">
		<title>Snapshoty na úrovni aplikací</title>
		<para>Existuje několik typů aplikací, pro které nelze nebo je neefektivní použít doposud evedené metody snapshotů. Nejčastěji se jedná o databázové aplikace nebo třeba o emailové servery.
			<variablelist>
				<title>Nejčastěji se jedná o tyto omezení:</title>
				<varlistentry>
					<term><filename>Aplikace používá relativně velké soubory</filename></term>
					<listitem><para>Tato vlastnost způsobuje nemožnost použít snapshoty na úrovni VFS vrstvy nebo speciální snapshotový souborový systém. Lze použít snapshoty na úrovni blokového zařízení nebo souborového systému.</para>
					<para>Nejčastěji se jedná o databázový SW nebo emailový server.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>Aplikace vyžaduje neustále otevřené soubory</filename></term>
					<listitem><para>Tato situace může nastat v okamžiku, kdy nelze přerušit běh aplikace pro vytvoření snapshotu. Tato vlastnost způsobuje nemožnost použít snapshoty na úrovni VFS vrstvy nebo speciální snapshotový souborový systém. Lze použít snapshoty na úrovni blokového zařízení nebo souborového systému, pokud souborový systém podporuje snapshoty otevřených souborů. Nutnou podmínkou je i podpora aplikace pro uvedení všech otevřených souborů do stavu, kdy je možné snapshot provést - tedy typicky vyprázdnění všech vnitřních bufferů, dokončení probíhajících transakcí a/nebo podporu žurnálu<indexterm><primary>žurnál</primary></indexterm> (journal<indexterm><primary>journal</primary></indexterm>) v aplikaci.</para>
					<para>Nejčastěji se jedná o databázový SW, emailový server nebo SW, který má na starosti monitoring nebo kolekci logů.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>Vlastní/žádný souborový systém aplikace</filename></term>
					<listitem><para>Aplikace nepoužívá žádný standardní souborový systém a přistupuje přímo k blokovým zařízením. Lze potom využít pouze snapshoty na úrovni blokových zařízení. I zde je nutná podmínka taková, že aplikace musí podporovat uvedení dat na blokovém zařízení do konzistentního stavu, aby bylo moźné snapshot provést.</para>
					<para>Typickou aplikací, která využívá vlastní souborový systém, je databáze Oracle.</para></listitem>
				</varlistentry>
			</variablelist>
		</para>
		<para>Tyto problémy řeší zabudovaná interní podpora snapshotů v aplikaci a propojení s modulem zálohovacího SW. Většinou se jedná o komerční řešení určené pouze pro danou konkrétní aplikaci a zálohovací SW.</para>
		<sect2 id="sec020601">
			<title>Výhody snapshotů na úrovni aplikace</title>
			<para>Díky kompletní znalosti struktury dat lze provádět optimální snapshoty dat a zálohovat pouze skutečně změněná data (nedochází např. k zálohování pomocných tabulek).</para>
		</sect2>
		<sect2 id="sec020602">
			<title>Nevýhody snapshotů na úrovni aplikace</title>
			<para>Základní nevýhodou je nutnost existence příslušného modulu pro danou aplikaci, zálohovací SW a kombinaci OS a HW (platformu). Tyto moduly nemusí být nutně k dispozici pro všechny verze a jsou také často finančně velmi náročné.</para>
			<para>Vzhledem k tomu, že se téměř vždy jedná o komerční řešení bez zdrojových kódů a popisu datových struktur, odzálohovaná data nejsou použitelná pro žádné další zpracování.</para>
		</sect2>
	</sect1>
	<sect1 id="sec0207">
		<title>Limity nasazení snapshotů</title>
		<para>Snapshoty nejsou pochopitelně řešení postihující veškeré možné požadavky na zálohování dat. Jelikož se však jedná o vyzrálé produkty pracující na všmožných vrstvách, ve kterých pracujeme s daty, nesetkáme se v praxi téměř s takovou situací ve které by nešla použít některá ze zmíněných variant snapshotů.</para>
		<para><variablelist>
			<title>Nejčastěji se vyskytující příčiny, proč nelze použít snapshoty:</title>
			<varlistentry>
				<term><filename>Nežádoucí dopad na výkon</filename></term>
				<listitem><para>Aplikace může vyžadovat takovou rychlost provádění operací, že není možné čekat na kopírování dat při požadavku na jejich změnu. Typickým příkladem jsou tzv. real-time aplikace (řízení průmyslových a technologických procesů, zdravotnictví apod.).</para></listitem>
			</varlistentry>
			<varlistentry>
				<term><filename>Příliš mnoho změn</filename></term>
				<listitem><para>Základní idea snapshotu je ta, že většina požadavků na práci s daty je čtení existujících dat. V případě aplikací, kde je tomu naopak a aplikace mění příliš mnoho dat příliš rychle, tak vzrůstá neúměrně prostorová nebo časová složitost na tvorbu záloh dat.</para></listitem>
			</varlistentry>
		</variablelist></para>

	</sect1>
</chapter>

<chapter id="ch03">
	<title>Analýza VFS vrstvy a návrh implementace snapshotů</title>
	<para>VFS<indexterm><primary>VFS</primary></indexterm> vrstva <citation>ZEN</citation> v OS Linux má na starosti tyto úkoly:
		<itemizedlist>
			<listitem><para>Registraci jednotlivých souborových systémů, jejich připojování a odpojování.</para></listitem>
			<listitem><para>Převod jména souboru/adresáře na i-uzel<indexterm><primary>i-uzel</primary></indexterm> (i-node<indexterm><primary>i-node</primary></indexterm>), který jednoznačně soubor/adresář identifikuje v rámci daného souborového systému.</para></listitem>
			<listitem><para>Cachování výsledků převodů jména souboru/adresáře na i-uzel.</para></listitem>
			<listitem><para>Obsluhu systémových volání<indexterm><primary>systémové volání</primary></indexterm> (syscall<indexterm><primary>syscall</primary></indexterm>) souvisejících se souborovými systémy a v případě potřeby převod těchto volání na nativní volání konkrétního souborového systému.</para></listitem>
		</itemizedlist>
			<figure float="1" id="obrSyscall">
				<title>Zjednodušené schéma VFS vrstvy</title>
				<mediaobject>
					<imageobject><imagedata fileref="syscall-vfs.png" format="PNG" scale="50"/></imageobject>
				</mediaobject>
			</figure>
		</para>
	<para>Analýza i následná implementace je prováděna na jádře OS Linux řady 2.6, konkrétně verze 2.6.16.</para>
	<sect1 id="sec0301">
		<title>Anatomie systémového volání</title>
		<para>Motivací pro detailní zkoumání systémového volání je to, že se jedná o jediný způsob, jak volají programy jednotlivé funkce jádra. Proto pokud chceme zjistit požadavek na změnu dat souboru/adresáře před tím, než bude změna provedena, musíme takovéto systémové volání nějakým způsobem odchytit, vyhodnotit a případně pozastavit na dobu provedení kopie původních dat.</para>
		<para>Každé systémové volání je identifikováno jednoznačným číslem volání. Tabulka systémových volání je pro každou architekturu jiná, pro architekturu x86 je uvedena v souboru <filename>arch/i386/kernel/syscall_table.S</filename>. Pokud chce program provést systémové volání, uloží na zásobník aktuální stav registrů, parametry, číslo systémového volání a vyvolá softwarové přerušení. Jádro převezme ze zásobníku parametry systémového volání a podle tabulky systémových volání zavolá příslušnou funkci. Poté převezme návratovou hodnotu, uloží ji na zásobník a vrátí řízení zpět původnímu programu. Ten převezme ze zásobníku návratovou hodnotu systémového volání, obnoví ze zásobníku stav registrů a pokračuje v běhu.</para>
		<para>V předchozí řadě jádra OS Linux (řada 2.4) byla tato tabulka systémových volání dostupná a modifikovatelná v rámci celého jádra. Toto umožňovalo nahradit příslušné systémové volání vlastním - např. pro účely auditu zaznamenat informace o volání a pak použít původní volání. Z bezpečnostních důvodů nelze v aktuální řadě jádra systémová volání přesměrovávat, takže v případě, kdy potřebujeme zachytit nějakou operaci (v našem případě požadavek na změnu souboru/adresáře), je nutno použít jinou metodu.</para>
		<para>Jedinou korektní metodou, jak v současném jádře zachytit systémová volání, je modifikace funkcí, které přímo obsluhující jednotlivá systémová volání. Konkrétní implementace modifikace systémových volání je probírána v kapitole <xref linkend="sec040101"/>.</para>
	</sect1>
	<sect1 id="sec0302">
		<title>Základní struktura VFS vrstvy OS Linux</title>
		<para>Jak již bylo řečeno v úvodu kapitoly, VFS<indexterm><primary>VFS</primary></indexterm> vrstva slouží nejenom k zajištění operací související se souborovými systémy a soubory/adresáři samotnými. Má na starosti i implementaci konkrétních systémových volání a cachování výsledků převodu jména na i-uzel<indexterm><primary>i-uzel</primary></indexterm>. Tato cache je nazývána "dcache"<indexterm><primary>dcache</primary></indexterm> a je implementována v souboru <filename>fs/dcache.c</filename>. Převod jména na konkrétní i-uzel je totiž často časově náročný, proto dcache výrazně urychluje práci se souborovým systémem. Převod jména na položku dcache zajištuje funkce <function>path_lookup</function> (definována v souboru <filename>fs/namei.c</filename>), která používá stukturu <function>nameidata</function> (definována v souboru <filename>include/linux/namei.h</filename>) obsahující ukazatel do dcache a ukazatel na přípojný bod souborového systému s daným souborem.</para>
		<para>Při práci s i-uzly (a tedy i dcache) je nutno mít na paměti, že zatímco převod jména na i-uzel je jednoznačný, nelze k i-uzlu dohledat jednoznačně jméno souboru/adresáře. Toto je způsobeno existencí linků<indexterm><primary>link</primary></indexterm> a také tím, že ačkoliv soubor může už být vymazán. Dokud jej má některá aplikace otevřený, existuje nadále jako i-uzel.</para>
	</sect1>
	<sect1 id="sec0303">
		<title>Navržená implementace, doporučené postupy</title>
		<para>Na základě provedené analýzy bylo doporučeno rozdělit systém do několika funkčních celků (více viz kapitola <xref linkend="sec0401"/>). Základním principem při implementaci by měla být snaha přesunout maximum funkčnosti z funkcí jádra do jaderného modulu nebo do user-space programu. Důvodem je snaha minimalizovat případný dopad potenciálních chyb na stabilitu a bezpečnost zbytku jádra operačního systému.</para>
		<para>Pro komunikaci mezi user-space<indexterm><primary>user-space</primary></indexterm> a kernel-space<indexterm><primary>kernel-space</primary></indexterm> bude použit program "connector<indexterm><primary>connector</primary></indexterm>". Jedná se o součást standardního jádra speciálně určenou právě pro předávání zpráv mezi user-space a kernel-space. Tento program pracuje na principu zasílání datagramů<indexterm><primary>datagram</primary></indexterm>, jedná tedy se o bezestavový protokol bez zaručení doručení zprávy. Více informací o konkrétní implementaci a omezeních je v kapitole <xref linkend="sec0403"/>.</para>
		<para>Při implementaci je také nutno brát v úvahu vznik race-condition, a proto je nutné potencionálně kritická místa ochránit zámky. Je proto využita relativně nová implementace tzv. mutexů<indexterm><primary>mutex</primary></indexterm>. Jedná se o nový typ zámku podporující pouze jednoduché uzamčení, odemčení a test na to, zda je zámek zamknutý. Narozdíl od jiných typů zámků (semafor, read-write zámek) je výrazně rychlejší.</para>
		<para>Pokud nejsou snapshoty ve VFS vrstvě aktivovány, mělo by docházet k žádnému nebo alespoň k co nejmenšímu možnému zpomalení všech operací jádra. V případě, že jsou snapshoty aktivní, měla by kopie dat být provedena co nejpozději. Nejlépe až po vyhodnocení, že daná operace změny dat se skutečně týká souboru/adresáře, který se má snapshotovat a že neselže na nedostačujících uživatelských právech nebo jiných okolnostech.</para>
	</sect1>
</chapter>

<chapter id="ch04">
	<title>Implementace snapshotů ve VFS vrstvě</title>
	<para></para>
	
	<sect1 id="sec0401">
		<title>Rozdělení na jednotlivé moduly</title>
		<para>Dle provedené analýzy byla implementace rozdělena do několika oddělených funkčních celků. Celý projekt je nazván <filename>vfs_cow</filename><indexterm><primary>vfs_cow</primary></indexterm> jako akronym pro VFS Copy-On-Write.
		</para>
		<sect2 id="sec040101">
			<title>Háčky přímo v jádře OS</title>
			<para>Jedná se úmyslně o velmi jednoduchý a přehledný kód ve formě tzv. patche<indexterm><primary>patch</primary></indexterm>. Tento patch je vytvořen přímo pro určitou verzi jádra. Pokud chce uživatel použít jinou verzi jádra, může tento patch být použitelný beze změny, vyžadovat malou úpravu nebo zcela novou implementaci.</para>
			<para>VFS vrstva v OS Linux je za posledních několik let velmi stabilní a dochází zde s časem spíše pouze k drobnému vyčištění kódu, malým optimalizacím (konkrétně v poslední době převod řady tzv. semaforů<indexterm><primary>semafor</primary></indexterm> na mnohem rychlejší variantu zamykání - tzv. mutexy<indexterm><primary>mutex</primary></indexterm>), což by mělo umožnit použitelnost patche i pro budoucí verze. Dlouhodobým cílem je, aby byl tento patch začleněn do standardní verze jádra tak, aby zmizela nutnost patchovat jádro úplně.</para>
			<para>Úprava VFS vrstvy se skládá ze 2 částí. První z nich je přidání nových souborů <filename>fs/vfs_cow.c</filename> a <filename>include/linux/vfs_cow.h</filename> deklarující základní funkce a proměnné. Ačkoliv se jedná o velmi malou část z celkového programu (pouze 200 řádků kódu) je tato část pro pochopení celé implementace kritická. Proto jí budu věnovat zvýšenou pozornost v následujícím textu.
			</para>
			<para>
			<variablelist>
				<title>Definované nové proměnné:</title>
				<varlistentry>
					<term><filename>int vfs_cow_loaded<indexterm><primary>vfs_cow_loaded</primary></indexterm></filename></term>
					<listitem><para>Tato proměnná je nenulová v případě, když je načten modul vfs_cow<indexterm><primary>vfs_cow</primary></indexterm> (viz kapitola <xref linkend="sec040102"/>).</para></listitem>
				</varlistentry>
				<varlistentry>
					<term>Ukazatele na funkce modulu</term>
					<listitem><para>Zde jsou definovány ukazatele na funkce <filename>vfs_cow_prepare_functor</filename>, <filename>vfs_cow_submit_functor</filename> a <filename>vfs_cow_commit_functor</filename>. Modul <filename>vfs_cow</filename> při svém inicializace přesměruje tyto ukazatele na vlastní funkce.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>rwlock_t vfs_cow_rwlock<indexterm><primary>vfs_cow_rwlock</primary></indexterm></filename></term>
					<listitem><para>Jedná se o tzv. read-write zámek. Read zámek může držet více procesů najednou, zatímco write zámek může držet pouze jeden proces a po tuto dobu nemůže být aktivní ani žádný read zámek. Tento zámek slouží k prevenci race-condition. Race-condition by mohla nastat v okamžiku, kdy jeden proces vyhodnotí proměnnou <filename>vsf_cow_loaded</filename> jako načtený příslušný modul. V tento okamžik by došlo k přerušení provádění tohoto procesu a jiný proces by odstranil modul <filename>vfs_cow</filename> z paměti. Při obnovení běhu prvního procesu by došlo k volání ukazatele na funkci již neexistujícího modulu. Proto nejprve získáme read zámek, provedeme vyhodnocení, zda je modul načten a  případně voláme příslušnou funkci modulu. Teprve po ukončení tohoto volání zámek uvolníme. V případě, že chceme modul odstranit z jádra, musíme nejprve získat write zámek. Tento postup zaručuje, že s tímto modulem nepracuje v daný okamžik žádný jiný proces. Tento zámek nelze z výkonostních důvodů nahradit mutexem. Použití mutexu by mělo za následek serializaci požadavků na modul jádra, zatímco read-write zámek umožnuje libovolný současný počet read zámků - tedy volání funkcí modulu.</para></listitem>
				</varlistentry>
			</variablelist>
			</para>
			<para>
			<variablelist>
				<title>Definované nové funkce:</title>
				<varlistentry>
					<term><filename>int vfs_cow_prepare (const char *filename, const char *dst, int syscall)<indexterm><primary>vfs_cow_prepare</primary></indexterm></filename></term>
					<listitem><para>Tato funkce je volána až když dojde k vyhodnocení syscallu, že může dojít ke změně dat (např. je požadavek na otevření souboru pro čtení i zápis). Parametry této funkce jsou jméno souboru (parametr filename), typ systémového volání (parametr syscall) a v případě systémového volání rename i cíl (parametr dst). Smyslem této funkce je pouze vytvoří v modulu nový požadavek (typ <filename>cow_request</filename><indexterm><primary>cow_request</primary></indexterm> - viz kapitola <xref linkend="sec040202" />), následná inicializace proměnných a normalizace cest souborů (tzn. v případě relativní cesty se převede na absolutní, dojde k odstranění adresářových položek typu "/../" a "/./". Funkce nevyhodnocuje, zda požadavek se týká snapshotováných dat. Požadavek je poté uložen do oboustranně zřetězeného seznamu a řízení je vráceno obsluze syscallu.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>int vfs_cow_submit (void)<indexterm><primary>vfs_cow_submit</primary></indexterm></filename></term>
					<listitem><para>Tato funkce je volána poté, co dojde k detailnějšímu vyhodnocení syscallu (jsou ověřena přístupová práva, existence/neexistence požadovaného souboru apod.) a to těsně před tím, než je zavolána proprietární funkce konkrétního souborového systému. Pokud proces nemá například dostatečná užitelská práva, nebude tato funkce vůbec volána a bude rovnou volána funkce <filename>vfs_cow_commit</filename> s příslušným číslem chyby. V tento okamžik dojde k vyhodnocení toho, zda je požadovaný soubor/adresář součástí nějakého aktivního snapshotu. Pokud ano, vygeneruje se požadavek na vytvoření záložní kopie a aktuální proces se na dobu nutnou k vytvoření kopie uspí.</para>
					<para>Tato funkce nevyžaduje žádný parametr. Pro identifikaci požadavku využáme skutečnosti, že jeden proces může v jeden okamžik vykonávat pouze jeden syscall a syscally se nemohou vnořovat, takže příslušný požadavek snadno nalezneme v seznamu požadavků podle aktálního PID<indexterm><primary>PID</primary></indexterm> (process ID).</para>
					<para>Teoreticky by bylo možné funkce <filename>vfs_cow_prepare</filename> a <filename>vfs_cow_submit</filename> sloučit do jedné. V praxi však jsou často volány z různých funkcí a bylo by nutno je zásadněji upravit funkce VFS vrstvy. Často už v okamžiku volání funkce <filename>vfs_cow_submit</filename> neznáme původní jméno souboru, ale už přímo konkrétní i-uzel<indexterm><primary>i-uzel</primary></indexterm>.</para>
					<para>Následné volání funkce souborového systému může z mnoha různých důvodů skončit chybou a změny nemusí být provedeny. Záložní kopie souboru tedy byla provedena zbytečně. Tuto situaci však nelze na úrovni VFS jakkoliv předpokládat - museli bychom upravovit příslušná volání veškerých souborových systému v Linuxu a funkci <filename>vfs_cow_submit</filename> volat až přímo z nich.</para>
					</listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>int vfs_cow_commit (int error)<indexterm><primary>vfs_cow_commit</primary></indexterm></filename></term>
					<listitem><para>Tato funkce je volána po dokončení volání souborového systému. Parametr "error" určuje, zda došlo při tomto volání k chybě (nenulová hodnota) nebo vše proběhlo v pořádku. V současné implementaci je reakce na obě situace identická - dojde k odstranění příslušného požadavku ze seznamu požadavků. V budoucích verzích by bylo možné v případě chyby odstranit vytvořenou záložní kopii souboru/adresáře aby nezabírala diskový prostor. Dále by bylo vhodné zaznamenávat informaci o tom, které volání selhalo s jakou chybou na kterém souboru/adresáři pro pozdější vyhodnocení.</para></listitem>
				</varlistentry>
			</variablelist>
			</para>
			<para>
				<figure float="1" id="obrRequest">
					<title>Přehled možných přechodů požadavku na snapshot</title>
					<mediaobject>
						<imageobject><imagedata fileref="request.png" format="PNG" scale="50"/></imageobject>
					</mediaobject>
				</figure>
			</para>
		</sect2>
		<sect2 id="sec040102">
			<title>Jaderný modul "<filename>vfs_cow<indexterm><primary>vfs_cow</primary></indexterm></filename>"</title>
			<para>Tento modul udržuje seznamy požadavků předávaných VFS vrstvou a vyhodnocuje, zda je nutné provést zálohu dat. Následně předává tento požadavek user-space programu <filename>vfs_cowd</filename><indexterm><primary>vfs_cowd</primary></indexterm>. Z tohoto důvodu udržuje seznam požadavků (<filename>cow_requests</filename><indexterm><primary>cow_requests</primary></indexterm>), prefixů (<filename>cow_prefixes</filename><indexterm><primary>cow_prefixes</primary></indexterm>) a požadavků na zálohu dat (<filename>cow_catches</filename><indexterm><primary>cow_catches</primary></indexterm>). Tyto seznamy jsou podrobněji popsány v kapitole <xref linkend="sec0402"/></para>
			<para>Při zavedení modulu nejprve modul nastaví ukazatele na příslušné funkce (viz kapitola <xref linkend="sec040101" />) poté získá write zámek na <filename>vfs_cow_rwlock<indexterm><primary>vfs_cow_rwlock</primary></indexterm></filename>, nastaví proměnnou <filename>vfs_cow_loaded<indexterm><primary>vfs_cow_loaded</primary></indexterm></filename> na 1 a uvolní zámek. Při odstraňování modulu postupuje opačně - nejprve získá write zámek, nastaví proměnnou <filename>vfs_cow_loaded<indexterm><primary>vfs_cow_loaded</primary></indexterm></filename> na 0, uvolní zámek a vynuluje ukazatele na jednotlivé funkce. Protože jakékoliv jiné procesy mohou přistupovat k proměnné proměnnou <filename>vfs_cow_loaded<indexterm><primary>vfs_cow_loaded</primary></indexterm></filename> pouze s read zámkem, je zajištěno, že pokud takovýto proces získá tento zámek, zůstanou ukazatele na funkce platné po celou dobu platnosti read zámku.</para>
			<para>Tento jaderný modul také integruje procfs<indexterm><primary>procfs</primary></indexterm> rozhraní pro monitorování stavu modulu (více v kapitole <xref linkend="sec040105"/>).</para>
			<para>Předávání zpráv do user-space pro zpracování a naopak příjem zpráv o provedení zálohy dat je prováděn pomocí programu <filename>connector<indexterm><primary>connector</primary></indexterm></filename> (více v kapitole <xref linkend="sec0403"/>).</para>
			<para><variablelist>
				<title>Používané termíny v modulu <filename>vfs_cow</filename>:</title>
				<varlistentry>
					<term><filename>prefix<indexterm><primary>prefix</primary></indexterm></filename></term>
					<listitem><para>Záznam o tom, který adresář se má monitorovat na změny a kam se mají provádět případné zálohy dat. Počet těchto záznamů není nijak omezen. Může zde být i více požadavků na monitorování jednoho adresáře nebo dokonce můžou být tyto požadavky vnořené. Administrátor musí pouze dbát na to, aby nedošlo k rekurzi, kdy by se proces vytváření záloh zacyklil.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>request<indexterm><primary>request</primary></indexterm></filename></term>
					<listitem><para>Jedná se o záznam o prováděném syscallu, který obsahuje typ syscallu, typ operace a jméno souboru/adresáře. Je zde také uloženo počet vytvořených požadavků na zálohu tohoto souboru/adresáře.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>catch<indexterm><primary>catch</primary></indexterm></filename></term>
					<listitem><para>Záznam o požadavku na vytvoření záložní kopie. Obsahuje ukazatele na příslušné záznamy <filename>prefix</filename> a <filename>request</filename>.</para></listitem>
				</varlistentry>
			</variablelist></para>
			<para>Pro větší výkon kontroluje modul před odesláním požadavku na vytvoření záložní kopie, zda nebyla tato kopie již někdy v minulosti vytvořena. Kontroluje se také, zda v případě modifikace/smazání souboru/adresáře se nejedná o soubor/adresář, který nebyl vytvořen až po provedení snapshotu. V tomto případě také není provedena záloha tohoto souboru/adresáře.</para>
		</sect2>
		<sect2 id="sec040103">
			<title>Program "<filename>vfs_cowd<indexterm><primary>vfs_cowd</primary></indexterm></filename>"</title>
			<para>Program <filename>vfs_cowd<indexterm><primary>vfs_cowd</primary></indexterm></filename> má na starosti příjem zpráv od jaderného modulu (tzv. "catch"<indexterm><primary>catch</primary></indexterm>), vytvoření záložní kopie původních dat a následné zaslání zprávy jadernému modulu a ten může pokračovat v obsluze pozastaveného syscallu.</para>
			<para>Z důvodu maximalizace výkonu a portability jsou v tomto programu implementovány funkce na kopírování souborů, symbolických linků, speciálních souborů (bloková nebo znaková zařízení), adresářů a synchronizaci rozšířených atributů namísto volání stávajících programů Linux.</para>
			<para>Pro zvýšení výkonu systému se provádí po přijetí požadavku na vytvoření záložní kopie fork, takže v jeden okamžik můźe probíhat libovolný počet zálohování souborů/adresářů.</para>
		</sect2>
		<sect2 id="sec040104">
			<title>Program "<filename>vfs_cow_admin<indexterm><primary>vfs_cow_admin</primary></indexterm></filename>"</title>
			<para>Program <filename>vfs_cow_admin<indexterm><primary>vfs_cow_admin</primary></indexterm></filename> je nástroj pro administrataci nastavení snapshotů. Umožňuje přidávat a odebírat z jaderného modulu tzv. prefixy<indexterm><primary>prefix</primary></indexterm>, tedy které adresáře se mají monitorovat na změny a kam se případné zálohy původních souborů mají provádět. Dále umožňuje pro účely ladění a testování označit požadavky na kopírování dat (tzv. "catch"<indexterm><primary>catch</primary></indexterm>) jako ukončené. Umožňuje také zavolat funkci jaderného modulu, která vyprázdní veškeré vnitřní seznamy požadavků, prefixů nebo požadavků na kopii dat (catch).</para>
		</sect2>
		<sect2 id="sec040105">
			<title>Monitorování stavu modulů</title>
			<para>Pro sledování stavu systému je v jaderném modulu implementováno procfs<indexterm><primary>procfs</primary></indexterm> rozhraní. Při načtení jaderného modulu jsou vytvořeny virtuální soubory <filename>/proc/fs/vfs_cow/catches</filename>, <filename>/proc/fs/vfs_cow/prefixes</filename>,  <filename>/proc/fs/vfs_cow/requests</filename> a <filename>/proc/fs/vfs_cow/version</filename>. Obsah těchto souborů je dynamicky generován při každém požadavku čtení a reflektuje aktuální stav vnitřních seznamů jaderného modulu, resp. verzi modulu (<filename>/proc/fs/vfs_cow/version</filename>).</para>
			<para>Program "<filename>vfs_cowd<indexterm><primary>vfs_cowd</primary></indexterm></filename>" lze spustit s parametrem <filename>-v</filename>, což aktivuje podrobné výpisy o jednotlivých prováděných krocích.</para>
		</sect2>
	</sect1>
	<sect1 id="sec0403">
		<title>Komunikace mezi moduly</title>
		<para>Komunikace mezi háčky v kernelu a jaderným modulem je zprostředkována pomocí ukazatelů na funkce jaderného modulu (viz kapitola <xref linkend="sec040101"/>). Komunikace mezi jaderným modulem a mezi user-space programy <filename>vfs_cow_admin<indexterm><primary>vfs_cow_admin</primary></indexterm></filename> a <filename>vfs_cowd<indexterm><primary>vfs_cowd</primary></indexterm></filename> je realizována pomocí programu connector<indexterm><primary>connector</primary></indexterm>. Jedná se o program určený speciálně pro jednoduché, bezestavové, zasílání datagramů mezi user-space a kernel-space. Každá zpráva je identifikována pomocí dvojice identifikátorů - čísla skupiny (značícího třídu zprávy) a čísla typu zprávy. Každý typ programu, který chce komunikovat tímto způsobem by si měl zvolit jedinečný identifikátor čísla skupiny. Identifikátor typu zprávy je pomocný a není v této implementaci využíván, protože zprávy jsou vždy jen jednoho typu.</para>
		<para>Při volání jednotlivých funkcí jaderného modulu z funkcí obsluhy syscallu je prováděna modulem kontrola, zda lze zasílat zprávy do user-space - tedy, zda je aktivní nějaký příjemce (program <filename>vfs_cowd<indexterm><primary>vfs_cowd</primary></indexterm></filename>). Pokud není aktivní žádný příjemce, nelze jakýkoliv požadavek zpracovat, proto je řízení vráceno syscallu, aby nedošlo k nežádoucímu uváznutí procesu.</para>
		<para>Program connector má v současnosti několik omezení - nelze zasílat z user-space do kernel-space zprávy větší než 1 kB (opačným směrem lze zasílat zprávy libovolné velikosti). Dále není možné potvrdit příjem a/nebo zpracování zprávy příjemcem nebo jednoduše vrátit návratovou hodnotu.</para>
	</sect1>
	<sect1 id="sec0402">
		<title>Důležité datové struktury</title>
		<para>V této části bych se rád podrobněji zmínil o hlavních použitých datových strukturách.</para>
		<sect2 id="sec040201">
			<title>Struktura "cow_msg_struct<indexterm><primary>cow_msg_struct</primary></indexterm>"</title>
			<para>
			<programlisting><![CDATA[
struct cow_msg_struct {
	int id;
	int len;
	int type;
	int pid;
	int action;
	int catch_number;
	int catch_count;
	int match_prefix_id;
	int prefix_len;
	int strings_len;
	char *path;
	char *dst;
};
]]></programlisting>
			</para>
			<para>Tato datová struktura slouží k zasílání zpráv pomocí programu connector<indexterm><primary>connector</primary></indexterm> mezi user-space a kernel-space.
				<variablelist>
				<title>Prvky struktury:</title>
				<varlistentry>
					<term><filename>id</filename></term>
					<listitem><para>ID zprávy nebo prvku v seznamu, jehož se zpráva týká (dle hodnoty proměnné "type").</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>len</filename></term>
					<listitem><para>Celková délka zprávy.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>type</filename></term>
					<listitem><para>Typ zprávy (přidání prefixu, potvrzení provedení zálohy apod.).</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>action</filename></term>
					<listitem><para>Typ změny dat v případě požadavku na zálohu dat - např. vytvoření nového soubou, změna souboru, změna atributů, smazání souboru apod.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>catch_number</filename></term>
					<listitem><para>V případě většího počtu požadavků na zálohování stejného souboru/adresáře do více cílů je zde uloženo pořadové číslo požadavku.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>catch_count</filename></term>
					<listitem><para>Celkový počet požadavků na zálohování tohoto souboru/adresáře.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>match_prefix_id</filename></term>
					<listitem><para>V případě požadavku na zálohování je zde uloženo ID prefixu, který tento požadavek vytvořil.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>prefix_len</filename></term>
					<listitem><para>Délka prefixu v proměnné path.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>strings_len</filename></term>
					<listitem><para>Součet délek řetězců v proměnných path a dst. Jedná se o kontrolu integrity, zda byla zpráva přijata v pořádku celá.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>path</filename></term>
					<listitem><para>V případě požadavku na vytvoření zálohy souboru je zde uloženo jméno souboru/adresáře, jenž chceme zálohovat. V případě požadavku na vytvoření prefixu je zde uloženo, který adresář budeme monitorovat na změny (který adresář chceme snapshotovat).</para></listitem>
				</varlistentry>
				<varlistentry>
					<term><filename>dst</filename></term>
					<listitem><para>V případě požadavku na vytvoření zálohy souboru je zde uložen cílový soubor/adresář kam se má soubor/adresář odzálohovat. V případě požadavku na vytvoření prefixu je zde uložen cílový adresář pro ukládání odzálohovách souborů/adresářů.</para></listitem>
				</varlistentry>
				</variablelist>
			</para>
		</sect2>
		<sect2 id="sec040202">
			<title>Struktura "cow_prefix"</title>
			<para>
<programlisting><![CDATA[
struct cow_prefix {
	struct list_head node_list;
	int id;
	unsigned long timestamp;
	char *path;
	char *dst;
};
]]></programlisting>
			</para>
			<para>Tato datová struktura uchovává informace o prefixu<indexterm><primary>prefix</primary></indexterm> (adersářích, který se mají snapshotovat).
				<variablelist>
					<title>Prvky struktury:</title>
					<varlistentry>
						<term><filename>node_list</filename></term>
						<listitem><para>Struktura zajištující začlenění prvku do oboustranně zřetězeného seznamu.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>id</filename></term>
						<listitem><para>ID prefixu.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>timestamp</filename></term>
						<listitem><para>Čas přidání prefixu.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>path</filename></term>
						<listitem><para>Prefix (adresář, který se má snapshotova).</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>dst</filename></term>
						<listitem><para>Cílový adresář, kam se mají ukládat odzálohované soubory.</para></listitem>
					</varlistentry>
				</variablelist>
			</para>
		</sect2>
		<sect2 id="sec040203">
			<title>Struktura "cow_request"</title>
			<para>
<programlisting><![CDATA[
struct cow_request {
	struct list_head node_list;
	int id;
	int pid;
	int syscall;
	int action;
	int submitted;
	int done; 
	unsigned long timestamp;
	wait_queue_head_t waitq;
	wait_queue_t wait;
	atomic_t catches;
	char *path;
};
]]></programlisting>
			</para>
			<para>Tato datová struktura uchovává informace o požadavcích (request<indexterm><primary>request</primary></indexterm>) vytvářených z obsluhy jednotlivých syscallů.
				<variablelist>
					<title>Prvky struktury:</title>
					<varlistentry>
						<term><filename>node_list</filename></term>
						<listitem><para>Struktura zajištující začlenění prvku do oboustranně zřetězeného seznamu.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>id</filename></term>
						<listitem><para>ID požadavku.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>pid</filename></term>
						<listitem><para>Číslo procesu, který vytvořil požadavek.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>syscall</filename></term>
						<listitem><para>Označení typu syscallu, který vytvořil požadavek.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>action</filename></term>
						<listitem><para>Typ požadované změny (vytvoření nového souboru, změna stávajícího souboru apod.).</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>submitted</filename></term>
						<listitem><para>Označuje, zda byl již požadavek potvrzen a má se provést záloha dat.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>done</filename></term>
						<listitem><para>Označuje, zda je požadavek na zálohu dat již proveden.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>timestamp</filename></term>
						<listitem><para>Čas vytvoření požadavku.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>waitq</filename></term>
						<listitem><para>Čekací fronta (wait queue<indexterm><primary>wait queue</primary></indexterm>). Slouží k uspání procesu po dobu vyřízení požadavku na zálohu dat).</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>wait</filename></term>
						<listitem><para>Prvek čekací fronty.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>catches</filename></term>
						<listitem><para>Počet vytvořených požadavků na zálohu dat. V případě vytvoření požadavku na změnu dat je procesu je zde uložen jejich počet a aktuální proces je uspán dokud zde je kladné nenulové číslo. Jedná se o speciální typ proměnné, která podporuje atomické operace, takže při zvýšení/zmenšení čísla o jedničku není potřeba zajištovat zamykání pro prevenci race-condition (např. pomocí mutexů).</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>path</filename></term>
						<listitem><para>Cesta k souboru/adresáři, který se má zálohovat.</para></listitem>
					</varlistentry>
				</variablelist>
			</para>
		</sect2>
		<sect2 id="sec040204">
			<title>Struktura "cow_catch"</title>
			<para>
<programlisting><![CDATA[
struct cow_catch {
	struct list_head node_list;
	int id;
	unsigned long timestamp;
	struct cow_request *request;
	struct cow_prefix *match_prefix;
};
]]></programlisting>
			</para>
			<para>Tato datová struktura uchovává informace o požadavcích na zálohu dat (catch<indexterm><primary>catch</primary></indexterm>).
				<variablelist>
					<title>Prvky struktury:</title>
					<varlistentry>
						<term><filename>node_list</filename></term>
						<listitem><para>Struktura zajištující začlenění prvku do oboustranně zřetězeného seznamu.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>id</filename></term>
						<listitem><para>ID požadavku na zálohu dat.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>timestamp</filename></term>
						<listitem><para>Čas vytvoření požadavku na zálohu dat.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>request</filename></term>
						<listitem><para>Ukazatel na příslušný požadavek.</para></listitem>
					</varlistentry>
					<varlistentry>
						<term><filename>match_prefix</filename></term>
						<listitem><para>Ukazatel na příslušný prefix snapshotu.</para></listitem>
					</varlistentry>
				</variablelist>
			</para>
		</sect2>
	</sect1>
</chapter>


<chapter id="chZaver">
	<title>Závěr</title>
	<para>Cílem této práce bylo prozkoumat různé varianty snapshotů na různých úrovních zpracování dat. Následně na základě analýzy provést implementaci snapshotů do VFS vrstvy OS Linux. Srovnáním variant jsme dospěli k závěru, že každá varianta snapshotů má své výhody a nevýhody a je velmi odvislé od konkrétního nasazení, kterou zvolit.</para>
	<para>Přínosem této práce byl jak ucelený přehled variant snapshotů usnadňujících uživateli vybrat na základě konkrétních požadavků tu správnou, tak primárně vytvoření implementace snapshotů ve VFS vrstvě OS Linux.</para>
	<sect1 id="chZaverFuture">
		<title>Další možný vývoj</title>
		<para>Dlouhodobým cílem je zveřejnění provedené implementace, získání zkušeností s jejího nasazení v různých prostředích a snaha o její začlenění do standardní distribuce jádra OS Linux.</para>
		<para>Z konkrétních změn by bylo dobré provést případné další optimalizace vytváření záloh dat, aby v případě kopírování souboru na více míst byly požadavky (catch) sloučeny a soubor byl čten jenom jednou. Dále v případě otevření souboru pro zápis by se měla vytvořit záložní kopie až teprve v okamžik, kdy je skutečně proveden zápis do souboru.</para>
		<para>Další vhodnou optimalizací by byla úprava virtuálního souborového systému UnionFS<indexterm><primary>UnionFS</primary></indexterm> tak, aby v případě změny metadat souboru (přístupová práva, vlastník souboru apod.) nebylo nutné provádět zálohu celého původního souboru.</para>
	</sect1>
	
</chapter>

<bibliography>
	<biblioentry>
	<abbrev>UnionFS</abbrev>
	<authorgroup>
		<author>
			<firstname>C. P.</firstname>
			<surname>Wright</surname>
		</author>  
		<author>
			<firstname>J.</firstname>
			<surname>Dave</surname>
		</author>  
		<author>
			<firstname>P.</firstname>
			<surname>Gupta</surname>
		</author>  
		<author>
			<firstname>H.</firstname>
			<surname>Krishnan</surname>
		</author>  
		<author>
			<firstname>D. P.</firstname>
			<surname>Quigley</surname>
		</author>  
		<author>
			<firstname>E.</firstname>
			<surname>Zadok</surname>
		</author>  
		<author>
			<firstname>M. N.</firstname>
			<surname>Zubair</surname>
		</author>  
	</authorgroup>
	<title>Versatility and Unix Semantics in Namespace Unification</title>
	<publisher>
		<publishername>ACM Transactions on Storage (TOS)</publishername>
	</publisher>
	<pubdate>2006</pubdate>
	</biblioentry>	
	<biblioentry>
		<abbrev>ZEN</abbrev>
		<authorgroup>
			<author>
				<firstname>C. P.</firstname>
				<surname>Wright</surname>
			</author>  
			<author>
				<firstname>R.</firstname>
				<surname>Iyer</surname>
			</author>  
			<author>
				<firstname>N.</firstname>
				<surname>Joukov</surname>
			</author>  
			<author>
				<firstname>N.</firstname>
				<surname>Sivathanu</surname>
			</author>  
			<author>
				<firstname>E.</firstname>
				<surname>Zadok</surname>
			</author>  
		</authorgroup>
		<title>On Incremental File System Development</title>
		<publisher>
			<publishername>ACM Transactions on Storage (TOS)</publishername>
		</publisher>
		<pubdate>2006</pubdate>
	</biblioentry>	
	<biblioentry>
		<abbrev>BLOCK</abbrev>
		<authorgroup>
			<author>
				<firstname>M. D. </firstname>
				<surname>Flouris</surname>
			</author>  
			<author>
				<firstname>A.</firstname>
				<surname>Bilas</surname>
			</author>  
		</authorgroup>
		<title>Transparent Data Versioning at the Block I/O Level</title>
		<publisher>
			<publishername>Proceedings of the 12th NASA Goddard 21st IEEE Conference on Mass Storage Systems and Technologies</publishername>
		</publisher>
		<pubdate>2004</pubdate>
	</biblioentry>	
	<biblioentry>
		<abbrev>PLAN9</abbrev>
		<authorgroup>
			<author>
				<firstname> </firstname>
				<surname>ATT Bell Laboratories</surname>
			</author>  
		</authorgroup>
		<title>Plan 9 - Programmer's Manual</title>
		<pubdate>1995</pubdate>
	</biblioentry>	
	<biblioentry>
		<abbrev>NTFS</abbrev>
		<authorgroup>
			<author>
				<firstname> </firstname>
				<surname>Microsoft Corporation</surname>
			</author>  
		</authorgroup>
		<title>Microsoft MSDN WinFS Documentation</title>
		<pubdate>2004</pubdate>
		<releaseinfo>
			<ulink url="http://msdn.microsoft.com/data/winfs">http://msdn.microsoft.com/data/winfs</ulink>
      </releaseinfo>
	</biblioentry>	
</bibliography>

<index/>

<appendix id="apInstall">
	<title>Návod k instalaci a použití programu vfs_cow</title>
	<para></para>
	<sect1 id="appInstallLicence">
		<title>Licenční podmínky</title>
		<para>Kompletní implementace je licencována pod licencí GPL v2.</para>
	</sect1>
	<sect1 id="appInstallHooks">
		<title>Instalace háčků do jádra OS</title>
		<para>Pro instalaci háčků do jádra OS je stačí v spustit v hlavním adresáři se zdrojovými kódy (typicky <filename>/usr/src/linux</filename>) příkaz <filename>cat /usr/src/vfs_cow/kernel_patches/2.6.16-rc1-mm2.patch | patch -p1</filename> (kde <filename>/usr/src/vfs_cow/kernel_patches/2.6.16-rc1-mm2.patch</filename> je cesta k příslušnému patchi jádra)</para>
	</sect1>
	<sect1 id="appInstallModule">
		<title>Kompilace a zavedení jaderného modulu</title>
		<para>Pro kompilaci jaderného modulu by mělo postačovat spustit v adresáři se zdrojovými kódy projektu příkaz <filename>make mods</filename>. Kompilace vyžaduje korektně nainstalované kernel s podporou modulu a s aplikovaným patchem háčků.</para>
		<para>Pro zavedení modulu lze použít příkaz <filename>make load</filename>.</para>
	</sect1>
	<sect1 id="appInstallUser">
		<title>Kompilace a spouštění programů "<filename>vfs_cowd</filename>" a "<filename>vfs_cow_admin</filename>"</title>
		<para>Pro kompilaci programů ho modulu by mělo postačovat spustit v adresáři se zdrojovými kódy projektu příkaz <filename>make progs</filename>.</para>
	</sect1>
	<sect1 id="appInstallUsage">
		<title>Ukázka použití</title>
		<para>Pro monitorování adresáře <filename>/opt/SRC</filename> a ukládání odzálohovaných souborů do adresáře <filename>/opt/DST</filename> stačí spustit v adresáři se zdrojovými kódy projektu příkaz <filename>usr/vfs_cow_admin -a /opt/SRC -p /opt/DST</filename>. Aby se začaly vytvářet záložní kopie, musí být spuštěný program <filename>usr/vfs_cowd</filename></para>
		<para>Pro následné připojení vytvořeného snapshotu se použije připojení pomocí souborového systému UnionFS. Je nutné mít příslušný modul s tímto souborovým systémem zkompilovaný. Pro připojení vytvořeného snapshotu z předchozího odstavce se spustí příkaz: <filename>mount -t unionfs -o ro,dirs=/opt/DST=ro:/opt/SRC=ro,delete=whiteout unionfs /mnt/snapshot</filename></para>
	</sect1>
</appendix>

</book>

