A A A

Electronicfr.com

Home 20.11.2008

Main Menu

Home
Robotic
Microcontrollers and Ethernet
Microcontrollers and USB
Embedded computing
Submit a circuit

Search

Google
Web
electronicfr


Construire un système linux read-only sur une compact flash

Written by Administrator, on 30-11-2007 15:41
Views 3605    

Ce document décrit des techniques pour mettre en oeuvre un système où le disque root est sur une carte compact flash (CF) dans le but de construire un système embarqué robuste.

Problématiques

Les installations classiques sont sensibles aux coupures électriques et aux arrêt brutaux qui peuvent conduire à des pertes de donnée voir empêcher un redémarrage normal. Par ailleurs les disques compact flash supportent un nombre limité d'écriture qu'il est important de contrôler dans le cas d'un système sur compact-flash
Pour mettre en œuvre facilement un système robuste où les disques ne sont pas montés en écriture il est possible d'utiliser des systèmes de fichier en ram 'fusionnés' avec des partitions sur disque en utilisant le module unionfs.


Je décris ici plusieurs approches en sachant que nous voulons à la fois avoir un système read only mais aussi pouvoir sauver les modifications et garantir l'intégrité des données...

Qu'est ce qu'unionfs?

 Unionfs est un système de fichier permettant de fusionner virtuellement des systèmes de fichier (appelé branches). Chaque branche se voie attribuée une priorité et peut-être read-only ou read-write. Unionfs est largement utilisé sur les live CD car il implémente un mécanisme de copy on write, c'est à dire que quand un fichier d'une union est modifiée, le contenu du fichier est recopié dans la branche read-write est ainsi l'union parait read-write alors qu'une ou plusieurs branches sont read-only.

Mise en œuvre

 Il faut utiliser un noyau récent (cad > 2.6.18 pour utiliser unionfs v2) et compiler le module unionfs disponible ici . Malheureusement unionfs n'est pas présent dans le noyau officiel, mais il est dans la branche d'Andrew Morton (-mm). Je ne parle ici que d'unionfs 2.1 qui apporte des amélioration intéressantes par rapport à la précédant, en particulier elle permet d'acceder aux branches "raw" d'une union. Notez que la version dans debian etch est ancienne est à déconseiller!

Pour créer des union on utilise simplement la syntaxe:

mount -t unionfs unionfs /nom_de_lunion -o dirs=/rep1=rw:/rep2=ro:...

Il est possible de fusionner de nombreuses branches, la première apparaissant étant la plus prioritaire. 

 Sous unix, les répertoires /etc, /var et /tmp doivent être read-write. Pour /tmp, pas de problème, un tmpfs fait l'affaire, pour /var et /etc il faut jouer avec les unions...

Première approche

On va créer  des systèmes de fichier tmpfs qui seront des branches read-write pour /var et /etc. Appelons les /rametc et /ramvar. Ensuite on crée des unions respectivement avec /etc et /var.

Pour ne pas s'embêter avec la gestion de /etc/mtab, on peut faire un lien vers /proc/mounts  

Pour monter les répertoires en unionfs très tôt lors du boot, on peut ajouter les lignes suivantes au début du fichier /etc/init.d/rcS (sur une debian):
/bin/mount -n -t tmpfs tmpfs /rametc -o size=30M
/bin/mount -n -t tmpfs tmpfs /ramvar -o size=50M
/bin/mount -n -t unionfs unionfs /etc -o dirs=/rametc=rw:/etc=ro
/bin/mount -n -t unionfs unionfs /var -o dirs=/ramvar=rw:/var=ro
echo "tmpfs /rametc tmpfs rw,size=30M 0 0" >> /etc/mtab
echo "tmpfs /ramvar tmpfs rw,size=50M 0 0" >> /etc/mtab
echo "unionfs /etc unionfs rw,dirs=/rametc=rw:/etc=ro 0 0" >> /etc/mtab
echo "unionfs /var unionfs rw,dirs=/ramvar=rw:/var=ro 0 0" >> /etc/mtab

 

Dans /etc/fstab on n'oublie pas tmp:

none /tmp tmpfs defaults,nosuid,nodev,noexec 0 0

et du coup on peut mettre /,/boot et /var en ro

Cette technique est également décrite: ici 

Cette approche pose problème pour remonter les partitions en rw et surtout on n'accède plus aux branches /var et /etc initiales, donc la resynchronisation n'est pas possible.

 

Deuxième approche

  Une autre solution consisterai à créer des unions ou l'on veux et ensuite utiliser la commande pivot_root. Cette commande permet de remplacer le répertoire racine et le répertoire courant de tous les processus en cours d'exécution.

 

En général on utilise pivot_root à la fin du script d'init présent dans initramfs. Pour se simplifier la vie, on peut le lancer dans rcS (debian) 

Dans ce cas on procède ainsi: 

- Montage de tmpfs dans /tmpfs_layer

- Montage de la partition pour la syncro dans /rw_layer 

- mount unionfs dans /union avec dirs=/tmpfs_layer/=rw:/rw_layer/=ro:/=ro

- pivot_root dans /unionfs

- chroot du shell en cours dans l'union

- Eventuellement mount --move de /dev/ 

Voici un exemple de script:

#! /bin/sh
# rcS
# Call all S??* scripts in /etc/rcS.d/ in numerical/alphabetical order
if [ -e /etc/nounionfs ] ; then
        echo "#######DEMARRAGE SANS UNIONFS#######\n"
        exec /etc/init.d/rc S
else
        echo "!!!!!DEMARRAGE AVEC UNIONFS!!!!!!!\n"
mount -o ro /dev/hda4 /rw_layer/
/bin/mount -t tmpfs tmpfs /tmpfs_layer/ -o size=200M
/bin/mount -t unionfs unionfs /union -o dirs=/tmpfs_layer/=rw:/rw_layer/=ro:/=ro
mkdir /union/old
cd /union

pivot_root . old
chroot . sh -c 'mount --move /old/dev /dev; mount --move /old/sys /sys ; mount --move /old/proc /proc'

exec chroot . sh -c '/etc/init.d/rc S' <dev/console >dev/console 2>&1
fi

 

Attention, chroot doit être accessible dans le système pivoté... une copie en dehors de /usr ne fait pas de mal si /usr est sur une autre partition. 

Encore une solution

Sur la partition / on crée les répertoires /etc et /var qui resterons read-only.
On crée des partitions sur le disque qui serviront à la synchro des repertoires tmpfs (dans l'exemple suivant on les monte dans /union/rw_etc et /union/rw_var)
On crée des répertoires /union/ram_var etc /union/ram_etc en tmpfs .
On monte dans /etc et /var respectivement les unions de /ram_etc(rw)+/rw_etc(ro)+/etc(ro) et de /ram_etc+rw_var+/var
Dans ce cas on peut remonter quand on veut /union/rw_etc etc /rw_var en mode rw et faire des rsync

Comme précédemment, pour ne pas s'embêter avec la gestion de /etc/mtab, on peut faire un lien vers /proc/mounts 

Code pour /etc/init.d/rcS

  /bin/mount -t tmpfs tmpfs /union/ram_etc -o size=50M
  /bin/mount -t tmpfs tmpfs /union/ram_var -o size=150M
  /bin/mount  /dev/hda6 /union/rw_etc -o ro
  /bin/mount  /dev/hda5 /union/rw_var -o ro
  /bin/mount -t unionfs unionfs /etc/ -o dirs=/union/ram_etc=rw:/union/rw_etc=ro:/etc=ro
  /bin/mount -t unionfs unionfs /var/ -o dirs=/union/ram_var=rw:/union/rw_var=ro:/var=ro
...
 exec /etc/init.d/rc S


Le gros avantage de cette structure à trois branches par union est outre de permettre des copies entre branche pour la synchro, de pouvoir effacer quand on veux les repertoire rw_* et ainsi revenir dans l'état d'instalation (reset to factory).

Il y a plein de façon de faire avec unionfs... 

Resyncronisation

Avec unionfs 2.1 (et contrairement à ce que dit la page de manuel) on peut intervenir directement sur les branches. Il est donc possible de remonter les partitions en rw le temps de faire un rsync.

  Un problème qui se pose est alors de purger les partitions tmpfs pour quelles ne continuent pas à remplir la mémoire et de bien effacer de la compact flash les fichiers qui ont été détruit. Pour cela unionfs crée des fichiers "whiteout" (.wh.nomdufichier) dans la branche rw quand un fichier ou un répertoire est détruit. Il est facile de pacourir ces fichiers et de détruire les fichiers originaux.

 Voici un exemple de script de syncho (à lancer périodiquement et/ou au reboot)

#On remonte /rw_etc en rw pour copier les modifs dessus. 

mount -o remount,rw /rw_etc

 rsync -a --exclude '.wh*' /rametc /rw_etc
#Pour être sur de bien flusher les caches 
mount -o remount,incgen /etc

#Il vaut mieux retirer la branche avant d'effacer les fichiers
mount -t unionfs -o remount,del=/rametc none /etc

find /rametc \( -regex '.*/\.wh\.[^/]*' -type f \) | sed -e 's/\.\///;s/\.wh\.//' |
while read N
do
    rm -rf /rw_etc/$N
done
rm -rf /rametc/*
mount -t unionfs -o remount,add=/rametc none /etc
mount -o remount,ro /rw_etc

Évidement, faire des copies de fichiers ouverts, éventuellement en écriture, ce n'est pas forcement fiable. Idéalement il faudrai synchroniser lors d'un shutdown, mais la copie à chaud ne marche pas si mal à condition de prendre certaines précautions.

Pour une base de donnée, on peut locker les tables en lecture uniquement. Ca fonctionne correctement avec MySQL avec des tables MyISAM. 

L'astuce pour locker les tables pendant le temps le plus court possible consiste à faire un double rsync.

- Un premier rsync crée une première copie

- On lock les table: mysql -p ... -u ... < "FLUSH TABLES WITH READ LOCK"

- Un deuxième rsync qui va être ultra rapide cette fois

- on delock: "UNLOCK TABLES"

  Une autre solution élégante serai de faire un snapshot avec LVM, par contre on ne peut pas utiliser LVM avec tmpfs, donc il faudrai utiliser un ramdisk, pas très pratique à mettre en œuvre... Il y a des discussions intéressante à ce propos ici

Références:

Site officiel de Unionfs 

Unionfs: User- and Community-Oriented Development of a Unification File System: http://www.fsl.cs.sunysb.edu/docs/sipek-ols2006/index.html 

Tutorial Gentoo, http://gentoo-wiki.com/HOWTO_Read-only_root_filesystem#Unionfs_Method

Snapshot LVM et MySQL : http://mike.kruckenberg.com/archives/2006/05/mysql_backups_u.html

Aufs, un concurant de unionfs: http://aufs.sourceforge.net/  

Quote this article in website Print Related articles Save this to del.icio.us

Users' Comments (0) RSS feed comment
Comment language: English (0), French (1)

No comment posted

Add your comment



mXcomment 1.0.5 © 2007-2008 - visualclinic.fr
License Creative Commons - Some rights reserved
 
Next >
 
TOP