Article publié dans Linux Magazine 89, décembre 2006.
La perle de ce mois-ci a été rédigée par Laurent Gautrot,
(l.gautrot@free.fr
), de Paris.pm.
Une personne m'a demandé s'il existe des moyens de sauvegarder les attributs des fichiers. Cette personne travaille dans le domaine du stockage et me faisait part d'une problématique de restauration particulière. Il lui arrive de restaurer des données et de réaliser que les attributs standards POSIX ne sont pas restaurés correctement. Il s'agit en réalité d'un sous-ensemble des attributs, et en particulier les modes, le couple propriétaire/groupe sous la forme UID/GID, puis les dates d'accès et de modification.
Une petite exploration sur le CPAN m'a orienté vers Chroniton, mais ce projet vise à fournir un système de sauvegarde complet, et la restauration des attributs sans les données semble hors de portée du système.
Comme ce travail semble tout indiqué pour Perl, voici une manière de sauvegarder des attributs.
L'utilisation de Find::File
est incontournable, d'autant qu'il s'agit d'un
module du noyau de Perl. Le but est de parcourir une liste de répertoires,
ou, à défaut, le répertoire courant et de lister toutes les sous-arborescences.
La fonction wanted()
, appelée pour chaque fichier trouvé effectue un lstat()
sur le fichier, puis affiche sur la sortie standard les informations souhaitées
que sont le mode, les UID et GID, les dates d'accès et de modification
et enfin du nom.
L'emploi de lstat()
à la place de stat()
est nécessaire pour ne pas
déréférencer les liens symboliques. Enfin, et comme le mode renvoyé donne
aussi le type du fichier, on lui applique un masque binaire avant de l'afficher
sur la sortie standard en format CSV.
#!/usr/bin/perl use strict; use warnings; use File::Find; my @directories_to_search = (scalar @ARGV > 0) ? @ARGV : ('.'); find( \&wanted, @directories_to_search ); sub wanted { my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = lstat($_); printf "%04o;%i;%i;%i;%i;%s\n", $mode & 07777, $uid, $gid, $atime, $mtime, $File::Find::name; }
Ici, le seul point délicat réside dans le placement du chmod()
après le chown()
. En
effet, dans Perl, lors d'un changement de propriétaire, les setuid bits sont
supprimés. Pour notre cas de figure, cela peut s'avérer très gênant.
#!/usr/bin/perl use strict; use warnings; while (my $line = <>) { chomp $line; my ($mode,$uid,$gid,$atime,$mtime,$name) = split /;/, $line; print $name, ' ', $mode , ' ', oct($mode), "\n"; chown $uid, $gid, $name or warn "Unable to set UID/GID '$uid/$gid' on '$name': $!\n"; chmod (oct($mode), $name) or warn "Unable to set mode '$mode' on '$name': $!\n"; utime $atime, $mtime, $name or warn "Unable to set atime/mtime '$atime/$mtime' on '$name': $!\n"; }
Un fichier de sauvegarde pourrait ressembler à ceci :
% perl attr2csv 0755;500;500;1160264381;1160264380;. 0644;500;500;1159903336;1159893644;./attr2.csv 0644;500;500;1160262923;1159903335;./csv2attr 0644;500;500;1160264387;1159339140;./attr2csv
En redirigeant la sortie standard vers un fichier, on dispose d'une sauvegarde des attributs. Ces informations peuvent être utilisées pour la restauration des attributs à l'aide de la commande correspondante, mais peut aussi fournir une base d'information pour une vérification de changements d'attributs. C'est aussi bien en dessous de ce que peut proposer un outil comme AIDE.
On peut aussi imaginer une restauration des attributs d'une machine à une autre en utilisant une commande comme :
% perl attr2csv /emplacement/particulier | ssh root@autre_machine perl csv2attr
Comme ces charmants scripts sont simples, et liés fonctionnelement l'un à l'autre, il est possible de les fusionner et d'exécuter les parties adéquates en fonction du nom utilisé lors de l'invocation.
#!/usr/bin/perl use strict; use warnings; use File::Find; if ( $0 =~ m/attr2csv/ ) { my @directories_to_search = ( scalar @ARGV > 0 ) ? @ARGV : ('.'); find( \&wanted, @directories_to_search ); } elsif ( $0 =~ m/csv2attr/ ) { while ( my $line = <> ) { chomp $line; my ( $mode, $uid, $gid, $atime, $mtime, $name ) = split /;/, $line; print $name, ' ', $mode, ' ', oct($mode), "\n"; chown $uid, $gid, $name or warn "Unable to set UID/GID '$uid/$gid' on '$name': $!\n"; chmod( oct($mode), $name ) or warn "Unable to set mode '$mode' on '$name': $!\n"; utime $atime, $mtime, $name or warn "Unable to set atime/mtime '$atime/$mtime' on '$name': $!\n"; } } else { warn "$0 should be [attr2csv|csv2attr]\n"; } sub wanted { my ( $dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks ) = lstat($_); printf "%04o;%i;%i;%i;%i;%s\n", $mode & 07777, $uid, $gid, $atime, $mtime, $File::Find::name; }
Le même script remplit les deux rôles. Il suffit de lier symboliquement le premier au second.
% ll attr2csv csv2attr -rwxr-xr-x 1 lg lg 1073 oct 10 23:32 attr2csv lrwxrwxrwx 1 lg lg 8 oct 10 23:32 csv2attr -> attr2csv
La notation est compacte et il est certain que l'on ne gagne pas énormément en
lisibilité. Néanmoins, dans la partie de sauvegarde, le seul rôle de
File::Find
est de rechercher des fichiers, sans aucun critère.
Au final, la commande find(1)
remplit tout aussi bien ce rôle.
find /emplacement/particulier | perl -lane '($d,$i,$mode,$n,$uid,$gid,$r,$s, $atime,$mtime,$c,$bs,$b)=lstat($_); printf "%04o;%i;%i;%i;%i;%s\n",$mode & 07777,$uid,$gid,$atime,$mtime,$_;'
La restauration part du même principe, on pourrait écrire un uniligne qui s'occupe de décortiquer les lignes pour réaliser cette opération.
perl -lane 'my ( $mode, $uid, $gid, $atime, $mtime, $name ) = split /;/; chown $uid, $gid, $name;chmod( oct($mode), $name ); utime $atime, $mtime, $name;' < fichier.csv
Je reconnais que pour un uniligne, il est très verbeux, même s'il n'a plus les
trois print()
de diagnostic.
En quelques lignes il est possible d'écrire une procédure très simple et pourtant très puissante, qui s'applique aussi bien à des très petits répertoires qu'à des systèmes de fichiers avec quelques centaines de milliers d'inodes.
Envoyez vos perles à perles@mongueurs.net
, elles seront peut-être
publiées dans un prochain numéro de Linux Magazine.
Copyright © Les Mongueurs de Perl, 2001-2011
pour le site.
Les auteurs conservent le copyright de leurs articles.