Vous pouvez utiliser le --link-dest=
option. Fondamentalement, vous créeriez un nouveau dossier, tous les fichiers sont liés en dur au nouveau. Lorsque tout est terminé, vous pouvez simplement échanger les noms de dossier et supprimer l'ancien.
Il est impossible de faire cela à 100% atomique sous Linux car il n'y a pas de support noyau/VFS pour cela. Cependant, l'échange des noms n'est en fait qu'à 2 appels système, il devrait donc prendre moins d'une seconde pour le terminer. Cela n'est possible que sur Darwin (MAC/OSX) avec l'appel système exchangedata sur les systèmes de fichiers HFS.
Je fais quelque chose de similaire avec rsync
sauvegardes [sur disque] et j'ai rencontré le même problème en raison d'un démon mettant à jour les fichiers pendant l'exécution de la sauvegarde.
Contrairement à de nombreux programmes, rsync en a beaucoup différents codes d'erreur [Voir la page de manuel en bas]. D'intérêt sont deux :
23 -- transfert partiel en raison d'une erreur
24 -- transfert partiel en raison de fichiers sources disparus
Lorsque rsync effectue un transfert et rencontre l'une de ces situations, il ne s'arrête pas immédiatement. Il saute et continue avec les fichiers qu'il peut transférer. A la fin, il présente le code de retour.
Donc, si vous obtenez l'erreur 23/24, relancez simplement le rsync. Les exécutions suivantes iront beaucoup plus rapidement, généralement en transférant simplement les fichiers manquants de l'exécution précédente. Finalement, vous obtiendrez [ou devriez obtenir] une course sans faute.
Quant à être atomique, j'utilise un répertoire "tmp" lors du transfert. Ensuite, lorsque rsync run est propre, je le renomme [atomiquement] en <date>
J'utilise aussi le --link-dest
option, mais je l'utilise pour conserver les sauvegardes delta (par exemple, --link-dest=yesterday
pour tous les jours)
Bien que je ne l'ai pas utilisé moi-même, le --partial-dir=DIR
peut empêcher les fichiers cachés d'encombrer le répertoire de sauvegarde. Assurez-vous que DIR est sur le même système de fichiers que votre répertoire de sauvegarde afin que les noms soient atomiques
Pendant que je fais cela en perl, j'ai écrit un script qui résume ce que j'ai dit avec un peu plus de détails/précision pour votre situation particulière. C'est dans une syntaxe de type tcsh, [non testé et un peu grossier], mais traitez-le comme un pseudo-code pour écrire votre propre bash
, perl
, python
script que vous choisissez. Notez qu'il n'y a pas de limite de tentatives, mais vous pouvez l'ajouter assez facilement, selon vos souhaits.
#!/bin/tcsh -f
# repo_backup -- backup repos even if they change
#
# use_tmp -- use temporary destination directory
# use_partial -- use partial directory
# use_delta -- make delta backup
# set remote server name ...
set remote_server="..."
# directory on server for backups
set backup_top="/path_to_backup_top"
set backup_backups="$backup_top/backups"
# set your rsync options ...
set rsync_opts=(...)
# keep partial files from cluttering backup
set server_partial=${remote_server}:$backup_top/partial
if ($use_partial) then
set rsync_opts=($rsync_opts --partial-dir=$server_partial)
endif
# do delta backups
if ($use_delta) then
set latest=(`ssh ${remote_server} ls $backup_backups | tail -1`)
# get latest
set delta_dir="$backup_backups/$latest"
if ($#latest > 0) then
set rsync_opts=($rsync_opts --link-dest=${remote_server}:$delta_dir)
endif
endif
while (1)
# get list of everything to backup
# set this to whatever you need
cd /local_top_directory
set transfer_list=(.)
# use whatever format you'd like
set date=`date +%Y%m%d_%H%M%S`
set server_tmp=${remote_server}:$backup_top/tmp
set server_final=${remote_server}:$backup_backups/$date
if ($use_tmp) then
set server_transfer=$server_tmp
else
set server_transfer=$server_final
endif
# do the transfer
rsync $rsync_opts $transfer_list $server_transfer
set code=$status
# run was clean
if ($code == 0) then
# atomically install backup
if ($use_tmp) then
ssh ${remote_server} mv $backup_top/tmp $backup_backups/$date
endif
break
endif
# partial -- some error
if ($code == 23) then
continue
endif
# partial -- some files disappeared
if ($code == 24) then
continue
endif
echo "fatal error ..."
exit(1)
end