Sistema de ficheiros unificados UnionFS
Objectivo
O objectivo desta segunda fase do do desenvolvimento do m2installer é o estudo de uma forma de conseguir que quando são instalados ou removidos pacotes, os ficheiros por eles criados, modificados e apagados, apenas se repercutem no sistema se a instalação/remoção dos pacotes for efectuada com sucesso.
Sobre o UnionFS
O UnionFS (stackable unification file system) é um sistema de ficheiros que permite que directórios ("branches", em inglês) sejam sobrepostos de uma forma transparente criando, assim, um directório virtual resultante destes. É importante referir que os directórios podem ser de diferentes tipos de sistemas de ficheiros e mesmo assim o unionfs proporciona ao utilizador a mesma transparências no que diz respeito ao directório virtual que corresponde ao "merge" (sobreposição) dos directórios, ou seja, para o utilizador não haverá qualquer diferença durante a utilização do directório virtual em relação a directórios do mesmo sistema de ficheiros. Para além disto, note-se que o conteúdo físico dos diferentes directórios é sempre mantido separadamente, ou seja, a sobreposição de directórios é apenas virtual.
Pré-Requisitos
Temos como pre-requesitos que o unionfs esteja instalado e que o m2installer esteja também instalado e configurado correctamente.
A explicação do modo como se deve proceder à instalação e configuração do m2installer encontra-se na seguinte página: http://softwarelivre.sapo.pt/minstaller/wiki/Configurator. Note que não é necessário ter o sistema instalado dentro de um lv do LVM para poder utilizar qualquer tipo de funcionalidades relacionadas com o UnionFS. É, por isso, possível utilizar o m2intaller num sistema de ficheiros comum, como por exemplo o ext3. O problema é que desse modo não será possível utilizar as funcionalidades relacionadas com os Snapshots do LVM.
Descrição geral do modo de funcionamento do unionFS
Para simplificar a compreensão do texto a partir de agora, vamos chamar branches aos directórios que irão ser sobrepostos e vamos, por outro lado, chamar merge ao directório que irá conter a sobreposição dos branches.
Quando é montado um sistema de ficheiros unionfs, podemos ter qualquer número de branches e apenas um merge. O merge contém, de forma transparente, todos os ficheiros e pastas dos branches. Dado que no merge podemos criar, modificar ou apagar ficheiros/pastas, vamos descrever o funcionamento de cada uma destas operações.
- criar: quando criamos ficheiros ou pastas no merge, estes serão criadas no branch com mais prioridade. Isto porque como estes não existiam nos branches não têm definido em que branch se encontram e como tal poderiam ser colocados em qualquer branch. A prioridade dos branches é, por defeito, definida pela ordem pela qual eles aparecem no comando que faz a montagem unionfs, assim têm mais prioridade as branches que aparecem primeiro.
- modificar/apagar: quando modificamos/apagamos ficheiros ou pastas no merge, estes serão modificados/apagados no respectivo branch onde se encontram.
No entanto, isto só acontece quando os branches, em que irão ser efectuadas alterações, permitem a leitura e a escrita, ou seja, quando são rw ("read-write"). Pois quando existe um branch que apenas permite a leitura, ou seja, quando são ro ("read-only"), as respectivas operações de criar, modificar e apagar não podem ser efectuadas nestes. Neste caso a diferença em relação ao caso em que ambos os branches são rw, é que todas as modificações que deveriam ser efectuadas no branch ro, sao efectudas no branch rw. (No caso de existirem vários branches rw, as modificações referidas seriam efectuadas no branch com mais prioridade.) Assim, se modificarmos ou criarmos um ficheiro/pasta, este será criado no branch rw.
Nestas situações existe uma particularidade, que é quando apagamos um ficheiro cuja origem é o branch ro. Pois se ele fosse criado ou modificado, automaticamente seria criado esse mesmo ficheiro no branch ro, mas no caso em que ele é apagado isto não é possível. Dado isto o unionfs definiu uma forma de indicar que apesar do ficheiro continuar a existir no branch ro (evidentemente, pelo simples facto de não poder apagado), este não deverá existir no merge. Essa forma resume-se à criação de um ficheiro "whiteout" que, no fundo, é um ficheiro com 0 bytes de tamanho e tem o sou nome igual ao respectivo ficheiro que não deverá existir, mas com um ".wh." antes. Por exemplo, se o nome de um ficheiro for "package.rpm", o whiteout que diz respeito a este mesmo ficheiro terá como nome ".wh.package.rpm".
Para que seja mais fácil a compreensão do texto anterior, vamos demonstrar o funcionamento do unionfs através de alguns exemplos. Imaginemos, então, que temos os directórios "frutos" e "vegetais". Dentro do directório "frutos" temos os ficheiros "pera.txt" e "tomate.txt", sendo que o conteúdo do ficheiro "pera.txt" é "A pera é um fruto!" e o conteúdo do ficheiro "tomate.txt" é "O tomate é um fruto!". Dentro do directório "vegetais" temos os ficheiros "cenoura.txt" e "tomate.txt", sendo que o conteúdo do ficheiro "cenoura.txt" é "A cenoura é um vegetal!" e o conteúdo do ficheiro "tomate.txt" é "O tomate é um vegetal!". Gráficamente teremos:
Para começar, vamos à situação em que todos os branches são rw. O comando para efectuar a montagem unionfs é o seguinte:
mount -t unionfs -o dirs=/frutos/=rw:/vegetais/=rw unionfs /comida_saudavel/
Neste comando os directórios "frutos" e "vegetais" são os branches e o directório "comida_saudavel" é o merge.
O que este comando irá fazer é montar no merge "comida_saudavel" os branches "frutos" e "vegetais". O "-t unionfs" indica que o sistema de ficheiros a utilizar na montagem é o unionfs e o "-o dirs=/frutos/=rw:/vegetais/=rw" indica quais são os branches e que estes são "wr" ("read-write"), isto é, é permitido ao unionfs ler e escrever nestes branches. Por outro lado o "unionfs /comida_saudavel/" indica que o directório onde vão ser montados os branches é o "comida_saudavel", ou seja, o merge é "comida_saudavel".
O que é importante perceber aqui, é que o merge vai conter todas as pastas e todos os ficheiros de ambos os branches e com a mesma estrutura presente em cada em deles. Assim, depois de executado este comando, teremos dentro do directório "comida_saudavel" os ficheiros "cenoura.txt", "pera.txt" e "tomate.txt". Com isto, podemos verificar que a questão das prioridades, já anteriormente explicada, se verifica aqui, porque o conteúdo do ficheiro "tomate.txt" é "O tomate é um fruto!" (o directório frutos tem mais prioridade porque aparece primeiro no comando do que o vegetais).
Graficamente teremos:
No que diz respeito à modificação/remoção de ficheiros, se modificarmos os ficheiro "pera.txt" e "cenoura.txt", e se criarmos um outro com o nome "alimento.txt", verificamos que as alterações serão reflectidas nos respectivos ficheiros de cada branch e o ficheiro que é criado surge no branch com mais prioridade. Já no caso de, por exemplo, removermos o ficheiro "pera.txt", o que acontece é que este será também removido no respectivo branch.
Se no comando anterior trocarmos a ordem dos directórios, o conteúdo do ficheiro "tomate.txt" será "O tomate é um fruto!". Deste modo o comando seria:
mount -t unionfs -o dirs=/vegetais/=rw:/frutos/=rw unionfs /comida_saudavel/
Graficamente veríamos o seguinte:
Vamos agora à situação em que um dos branches é rw e o outro é ro. O comando é o seguinte:
mount -t unionfs -o dirs=/frutos/=rw:/vegetais/=ro unionfs /comida_saudavel/
A diferença em relação aos comandos anteriores é apenas que o directório frutos é, agora, só de leitura. Portanto a montagem será a mesma no que toca aos ficheiros existentes no merge, mas verificamos diferenças na criação/modificação de ficheiros. Como tal, se modificarmos os ficheiro "pera.txt" e "cenoura.txt", criarmos um outro com o nome "alimento.txt". verificamos que as alterações efectuadas ao ficheiro "pera.txt" serão reflectidas no ficheiro original que está no branch "frutos", dado que este é rw. Verificamos, também, que o ficheiro que foi criado, é colocado no branch vegetais sem qualquer problema, porque o branch frutos apesar de aparecer em primeiro lugar no comando como e além do mais é rw. Já no caso de, por exemplo, removermos o ficheiro "cenoura.txt", o que acontece é que, como o ficheiro "cenoura.txt" pertence ao branch vegetais que é ro, é criado o ficheiro ".wh.cenoura.txt", que indica que o ficheiro apesar de existir num dos branches não deverá existir no merge.
Nota: apesar de em todos os exemplo apenas serem utilizados dois branches, é possível utilizar mais do que dois para montar um sistema de ficheiros unionfs. Para mais de dois branches aplicam-se na mesma as regras de prioridade e de quando são ro. A diferença em relação a aquando apenas se utilizam dois, é que quando se apaga um ficheiro, no merge, que existe em vários branches com o mesmo nome, este será apagado em todos eles.
Utilização do m2installer (funcionalidades relacionadas a instalação e remoção pacotes sobre o unionfs)
A versão actual do m2installer encontra-se neste link: m2installer
Relembra-se, que o m2installer é o script principal de todo o projecto, pois é ele que deverá ser executado para realizar todas as operações e funcionalidades estão descritas na página principal do projecto m2installer ( http://softwarelivre.sapo.pt/minstaller/wiki ). Para além disso, para que seja possível a utilização destas funcionalidades relacionadas com o sistema de ficheiros UnionFs, é necessário que tenha sido utilizado correctamente o script configurator.sh. Dado que este é utilizado como forma de instalar as configurações principais do m2installer e permite a instalação do sistema dentro de um lv do LVM. Assim parte-se do pressuposto que nesta fase o m2intaller já se encontra correctamente instalado e configurado. Para mais informação acerca do processo de instalação consulte o seguinte link: http://softwarelivre.sapo.pt/minstaller/wiki/Configurator.
Por outro lado, é importante referir que este projecto é fundamentalmente um "proof-of-concept". Por esse motivo o controlo de excepções referentes à introdução de dados pelo utilizador é bastante simples. E além disso este script também não tem em conta questões relativas ao suporte em todo o tipo de sistemas, como por exemplo a instalação de pacotes utilizando outros tipos instaladores e/ou distribuições que não sejam as utilizadas/recomendadas neste projecto.
A seguir serão descritas apenas algumas das funcionalidades do m2installer no que se refere à instalação e remoção pacotes sobre o unionfs. Isto porque apesar de estarem descritas as principais funcionalidades que tinham sido mencionadas na pagina principal do projecto, o m2installer tem mais algumas opções que servem como complemento a operações envolvendo o LVM e os snapshots. De qualquer das formas, o m2installer já se encontra bastante documentado para que seja fácil a interpretação do código deste script.
Exemplo de um ficheiro de configuração: m2installer.conf.example
Instalar pacotes, tendo por base o instalador apt-get (argumento --apti / opção "Install packages using the apt-get installer" no menu)
Esta opção permite instalar pacotes no o sistema sobre o Unionfs e apenas manter a instalação caso o utilizador pretenda. Para que isto seja possível o que temos de fazer [teoricamente] é montar sobre cada directório do sistema, esse mesmo directório mas em modo "ro" e tendo um outro directório vazio que vai servir de "branch" "rw". Assim, a partir desse momento todas as modificações sejam introduzidas apenas nesse mesmo directório. Posteriormente se a instalação correr bem, o que se faz é copiar os ficheiros dos "braches" "rw" para os respectivos directórios do sistema. Mas isto não é completamente possível porque um directório que irá contém a montagem unionfs não pode ser simultaneamente "ro". Deste modo para resolver este problemas, o que se faz é montar todo o sistema (que está dentro de um lv ou dentro de um disco físico) num outro ponto reutilizar esses directórios como "branches" "ro". Por outro lado temos de ter especial atenção que certos directórios do sistema não podem e/ou necessitam de ser montados porque sabemos que qualquer instalação não irá efectuar modificações nestes. Assim são excluídos da montagem unionfs os seguintes directórios: "boot" "dev" "proc" "sys". Também não podemos montar o directório que contém a montagem de todo o sistema para que não entre em "loop". Para isso é criado o directório "/m2installer" que irá conter os "branches" "rw" e a montagem do lv do sistema, sendo, que este directório também será excluído das montagens. Resumindo, para a instalação de pacotes temos os seguintes passos:
- montar o lv que contém o sistema instalado, ou o próprio disco físico que contém todo o sistema
- montar directório do sistema (excluindo os já indicados) com o tipo unionfs
- executar o comando apt-get para instalar os pacotes
- copiar as modificações para o sistema de ficheiros real ou descartar essas modificações (consoante o que o utilizador pretender)
- [opcional - consoante o que o utilizador pretender] guardar os ficheiros novos, que foram modificados ou criados, num tarbal
- [opcional - consoante o que o utilizador pretender] guardar os ficheiros originais, antes de serem modificados ou apagados, num tarbal
Note-se que é utilizado o rsync para efectuar a cópia dos ficheiros para o sistema de ficheiros real e serão apagados aqueles ficheiros que tenham os correspondentes "whiteouts" criados pelo unionfs. Existe aqui uma particularidade, que consiste em as bases de dados não suportam ser copiadas pelo rsync, logo optou-se por fazer uma cópia das bases de dados do rpm utilizando o comando cp. Estas encontram-se no directório /var/lib/rpm.
Nota*
Video exemplificativo 1: http://www.vimeo.com/2020111
Video exemplificativo 2: http://www.vimeo.com/2019978
Remover pacotes, tendo por base o instalador apt-get (argumento --aptr / opção "Remove packages using the apt-get installer" no menu)
Semelhante à instalação de pacotes tendo por base o instalador apt-get, com a diferença de que o comando utilizado, em vez de ser para instalar pacotes, é para remover pacotes.
Nota*
Video exemplificativo 1: http://www.vimeo.com/2020126
Video exemplificativo 2: http://www.vimeo.com/2020116
Instalar pacotes, tendo por base o instalador urpm (argumento --urpmi / opção "Install packages using the urpm installer" no menu)
Semelhante à instalação de pacotes que tem por base o apt-get, com a diferença de que o comando utilizado para instalar pacotes, em vez de ser o apt-get, é o urpmi.
Nota*
Video exemplificativo 1: http://www.vimeo.com/2020465
Video exemplificativo 2: http://www.vimeo.com/2020458
Remover pacotes, tendo por base o instalador urpm (argumento --urpme / opção "Remove packages using the urpm installer" no menu)
Semelhante à remoção de pacotes que tem por base o apt-get, com a diferença de que o comando utilizado para remover pacotes, em vez de ser o apt-get, é o urpme.
Nota*
Video exemplificativo 1: http://www.vimeo.com/2020421
Video exemplificativo 2: http://www.vimeo.com/2020407
Reversão(rollback) da instalação/remoção de pacotes (argumento --rollbackpkg / opção "Rollback instalation/removal of packages" no menu)
Esta opção irá apresentar ao utilizador as diferentes "Transaction IDs" existentes e o utilizador poderá então optar por fazer o rollback de qualquer "Transaction ID". Cada "Transaction ID" corresponde à instalação/remoção de um ou mais pacotes. Nota: esta opção apenas é possível na distribuição Caixa Mágica e para pacotes que tenham sido instalados/removidos, utilizando o instalador "apt-get". No fundo o que é feito aqui é aproveitar as funcionalidades de rollback já existentes no "apt-get" da Caixa Mágica.
Video exemplificativo: http://www.vimeo.com/2020336
Nota*: qualquer uma das operações que engloba a montagem de todos os principais directórios sobre o sistema de ficheiros UnionFS requer que não existam aplicações a ser executadas ao mesmo tempo para que, por um lado, seja possível efectuar as montagens sem que ocorram devido aos directórios estarem a ser utilizados por outras aplicações. E, por outro lado, para que as pastas que contêm as modificações apenas tenham os ficheiros relativos à instalação dos pacotes, e não ficheiros que poderiam entretanto ser criados, modificados ou removidos por outras aplicações.
Termos utilizados:
branch - para simplificar a compreensão do texto , foi decidido chamar branches aos directórios que irão ser sobrepostos para dar origem ao merge, ou seja, aos directórios que contêm os ficheiros existentes no merge (dado que este resulta da sobreposição de vários branches
merge - para simplificar a compreensão do texto , foi decididochamar merge ao directório que irá conter a sobreposição dos branches
ro - do inglês "read-only", que significa que apenas é possível ler
rw - do inglês "read-write", que significa que possível ler e escrever
UnionFS - stackable unification file system


