Un Raspberry Pi può essere utilizzato per creare un backup di se stesso?


78

Questa domanda risponde alla domanda su come utilizzo un computer esterno per creare un backup del mio RPi.

Mi chiedo se posso creare un'immagine di backup della scheda SD attualmente in uso e copiarla in un file su un dispositivo di archiviazione USB. È possibile? In caso contrario, c'è un modo per creare un backup di un RPi senza coinvolgere un altro computer?


2
Certo, ma salta / tmp, / run, / proc, / sys, / dev e / mnt. Non è necessario creare un'immagine, è necessario un backup da cui è possibile creare o aggiornare un'immagine. Quindi non usare dd, guarda dentro rsync.
Riccioli d'oro

1
@goldilocks Mi piacerebbe se questo commento venisse inserito in una risposta più completa, spiegando il processo di backup e ripristino che hai in mente.
Eric Wilson,

Fatto, mi spiace, ci sono voluti alcuni giorni per trovare il tempo.
Riccioli d'oro

1
Se il volume di destinazione è abbastanza grande, rimontare il file system in sola lettura e fare una ddcopia con una dimensione del blocco appropriata sarà probabilmente il più veloce per una "nuova" copia. Fare una copia file per file su supporto flash / SD è probabilmente una cattiva idea.
Chris Stratton,

Risposte:


86

Ecco un'introduzione all'utilizzo rsyncper il backup sul Pi. Una volta creato il backup iniziale, tenerlo aggiornato in questo modo è molto più veloce che strappare costantemente l'intera immagine. Puoi farlo su un disco rigido locale o su una rete.

In realtà non si desidera una copia completa di un sistema in esecuzione come backup, poiché alcune delle cose apparentemente nel file system esistono solo in fase di esecuzione. Includere quello in un backup e poi usarlo per ricreare un'immagine in un secondo momento può creare problemi per te.

Ci sono anche altre eccezioni. rsyncpuò accettare un elenco di modelli ( glob ) da escludere, e quelli possono essere letti da un file, quindi passiamo prima a quello che dovrebbe essere in tale file. Si noti che le voci sono nel modulo /directory/*e non /directory. Questo perché vogliamo che esistano, ma non vogliamo copiare nulla al loro interno.

/proc/*
/sys/*

Questi non esistono davvero sul disco. Sono un'interfaccia per il kernel, che li crea e li mantiene in memoria . Se li copi e poi li copi di nuovo in un sistema e lo avvii, sarà (nella migliore delle ipotesi) insignificante, dal momento che il kernel li usa come punti di mount per le interfacce [Se vuoi vedere cosa succede quando monti una partizione di filesystem su una directory con dati al suo interno, provare. Funziona e non farà alcun danno, ma le cose che erano nella directory ora sono inaccessibili.]

Si noti che è fondamentale che esistano i punti di mount /syse /proc. Ma non dovrebbero contenere nulla. Il prossimo:

/dev/*

La devdirectory non è esattamente la stessa cosa di proce, sysma per i nostri scopi lo è. Se ritieni che dovresti salvarlo in modo da poter avere gli stessi nodi del dispositivo nel tuo backup o qualcosa del genere, ti sbagli . Non preoccuparti. Non copiare dev. Tanto tempo fa Linux funzionava in quel modo, ma non funziona più.

/boot/*

Questo è una specie di caso speciale con la maggior parte (forse tutte) delle distro specifiche di Pi come Raspbian. In realtà è un mount point per la prima partizione vfat. Ci occuperemo di questo separatamente. Qualunque cosa tu faccia, non preoccuparti di includerlo qui, perché di nuovo, è un punto di montaggio.

/tmp/*
/run/*

/rungeneralmente non è nemmeno su disco, è in memoria. Forse /tmppotrebbe essere troppo (questo risparmierebbe un po 'di azione sulla scheda SD), ma in ogni caso, come suggeriscono i nomi, questi non sono luoghi per la memorizzazione di dati persistenti. Le applicazioni che li utilizzano si aspettano che possano essere eliminate ad ogni avvio.

/mnt/*
/media/*

Questi sono importanti in particolare se si prevede di eseguire il backup su un disco rigido o una chiavetta USB e il dispositivo è in /mnto /media(il montaggio automatico tende a utilizzare quest'ultimo), perché se non si esclude la posizione di tali dispositivi nel filesystem si creare un loop che esegue il backup del contenuto dell'unità su se stesso, fino a quando non si esaurisce lo spazio. Penso che rsync potrebbe essere abbastanza intelligente da individuare qualcosa di così stupido, ma cerca di evitare di testare la premessa.

Attivo al backup effettivo: crea una directory di cui eseguire il backup sul disco rigido montato localmente, sull'unità USB, ecc., Ad esempio "pi_backup". Puoi alternativamente fare il backup su una posizione remota tramite ssh(vedi sotto) o usando un filesystem montato in rete, ma probabilmente ci vorrà del tempo per la prima volta.

Se il file contenente l'elenco da escludere è /rsync-exclude.txt1 e l'unità è /mnt/usbhd, per eseguire il backup effettivo:

rsync -aHv --delete --exclude-from=/rsync-exclude.txt / /mnt/usbhd/pi_backup/

Si noti che è presente una barra finalepi_backup/ .

Questo richiederà un po 'di tempo e produrrà un sacco di output (se invece vuoi esaminarlo in un registro, aggiungi > rsync.log). --deletenon ha senso la prima volta, ma per mantenere aggiornato il backup utilizzarlo. Questo assicura che anche le cose che hai cancellato in seguito sul Pi vengano rimosse dal tuo backup. Il aset di ricorsione in directory e si assicura che tutti gli attributi del file partita. -Hè preservare i collegamenti reali 2 , vè prolisso, motivo per cui si ottiene un output (altrimenti rsyncè silenzioso). Vedi man rsyncdi più.

C'è un collegamento per cui è possibile saltare il --exclude-fromfile. Se sei sicuro che tutte le cose che non vuoi copiare ( /tmpecc.) Siano su filesystem separati, puoi semplicemente usare:

rsync -axHv --delete-during / /mnt/usbhd/pi_backup/

-xè stato inserito. Questa è la forma abbreviata di --one-file-system, che dice di rsyncnon oltrepassare i confini del filesystem. Personalmente preferisco il --exclude-fromRaspbian, ma ad esempio, predefinito Raspbian, --one-file-systemfunzionerà bene. Puoi usare entrambi se vuoi stare -xattento: D

Non è un backup completo. È sufficiente se non hai inserito nulla boote stai bene usando il backup per ripristinare il sistema semplicemente attaccando la scheda in un computer ed eseguendo:

rsync -av --delete-during /mnt/usbhd/pi_backup/ /mnt/sdcard_partition2/

Potresti anche farlo con una scheda con una nuova immagine (presumendo che sia la stessa della tua immagine di base) anche se è un po 'inefficiente se devi creare l'immagine (perché poi ne sovrascriverai la maggior parte). È inoltre possibile collegare un'altra scheda SD tramite un adattatore USB con tale immagine su di essa e utilizzare il metodo sopra per mantenere una scheda duplicata.

Se hai inserito elementi /boot(ad esempio un kernel personalizzato), incluso /boot/config.txt, ti consigliamo di eseguire il backup anche di questo (piuttosto semplice - non c'è molto da fare). Fallo separatamente e, quando ripristini, quella roba va nella prima partizione.

Vedere qui se si desidera creare un'immagine vuota in stile Raspbian in cui è possibile eseguire il backup. Puoi usare una metodologia simile per creare una scheda di stile Raspbian vuota - invece di occuparti di un .imgfile, avresti a che fare con un dispositivo reale (ad esempio /dev/sdb), il che significa che tutto ciò che devi fare è creare la tabella delle partizioni con fdiske poi formato /dev/sdb1e sdb2(o qualsiasi altra cosa) con mkfs.

Ma copiare l'intera immagine è più semplice! Perché preoccuparsi di questo?

Non è così difficile; Ho ripristinato una scheda vuota (formattata secondo l'ultimo link) in 10 minuti. Sì, l'utilizzo dddel tutto è più semplice (se trovi cose come parole confuse ...), MA poi ci vuole un bel po 'ogni volta che vuoi aggiornare il tuo backup perché devi fare il 100% ogni volta. Usando rsync, una volta che esiste un backup, aggiornarlo è molto più veloce, quindi puoi impostarlo per farlo in modo indolore ogni giorno tramite cron. Su una rete anche. Ogni sei ore. Più spesso lo fai, meno tempo ci vorrà.

rsync attraverso ssh

Ecco un esempio:

rsync [options] --rsh="ssh [ssh options]" root@[the pi ip]:/ /backup/rpi/

"Opzioni" sarebbe, ad esempio, -av --delete --exclude-from=/rsync-exclude.txte "opzioni ssh" è qualunque cosa tu usi normalmente (se non altro). È necessario avere accesso root tramite sshper fare questo ai fini di un backup di sistema (set PermitRootLogin=yesin /etc/ssh/sshd_confige riavviare il server).


1 Dovresti conservare questo file. Puoi inserire commenti al suo interno su righe che iniziano con #o ;. Ciò potrebbe includere il rsynccomando effettivo , che può essere copiato e incollato in un secondo momento in modo da non doverlo ricordare ogni volta.

2 Grazie a Kris per la segnalazione rsyncnon lo fa automaticamente.


Goldilocks. Sembra un ottimo uso di rysync. Qualche possibilità di farlo diventare una sceneggiatura per noi?
totalitario,

Invece di escludere manualmente tutti i mountpoint, perché no mkdir /tmp/backupable && mount --bind / /tmp/backupablee risincronizzarlo? Ciò ha anche il vantaggio di eseguire il backup di tutti i dati memorizzati in luoghi "ombreggiati" da qualcosa montato lì.
n

@ n.st Buona idea (lol)! Ho modificato il suggerimento nella domanda, anche se penso ancora che usare --exclude-fromsia un'idea migliore. Se hai tempo, potresti scriverlo come una risposta separata, hai il mio voto e posso fare riferimento a quello. Questa risposta è abbastanza lunga.
riccioli d'oro

1
@IgorGanapolsky L'intenzione è di non creare un'immagine (leggi la parte "Ma copiare l'intera immagine è più facile! Perché preoccuparsi di questo?" ). Oltre ad essere più facile e veloce da mantenere una volta creato, questo metodo è generalmente più flessibile. Se vuoi usarlo in seguito per crearne uno .imgpuoi; questo e questo dovrebbe aiutare a spiegare come sono strutturati e come possono essere creati.
riccioli d'oro

1
Vedi il paragrafo che inizia "Non è un backup completo ..." . Fondamentalmente è esattamente la stessa cosa al contrario. Questo può aiutare con alcuni concetti che le persone sono comunemente confuse da / su.
riccioli d'oro

24

Una sceneggiatura funzionante della Raspberry Community creata da un membro lì.

Puoi riutilizzare e modificare il codice come preferisci. È ben documentato e autoesplicativo.

#!/bin/bash

# Setting up directories
SUBDIR=raspberrypi_backups
DIR=/hdd/$SUBDIR

echo "Starting RaspberryPI backup process!"

# First check if pv package is installed, if not, install it first
PACKAGESTATUS=`dpkg -s pv | grep Status`;

if [[ $PACKAGESTATUS == S* ]]
   then
      echo "Package 'pv' is installed."
   else
      echo "Package 'pv' is NOT installed."
      echo "Installing package 'pv'. Please wait..."
      apt-get -y install pv
fi

# Check if backup directory exists
if [ ! -d "$DIR" ];
   then
      echo "Backup directory $DIR doesn't exist, creating it now!"
      mkdir $DIR
fi

# Create a filename with datestamp for our current backup (without .img suffix)
OFILE="$DIR/backup_$(date +%Y%m%d_%H%M%S)"

# Create final filename, with suffix
OFILEFINAL=$OFILE.img

# First sync disks
sync; sync

# Shut down some services before starting backup process
echo "Stopping some services before backup."
service apache2 stop
service mysql stop
service cron stop

# Begin the backup process, should take about 1 hour from 8Gb SD card to HDD
echo "Backing up SD card to USB HDD."
echo "This will take some time depending on your SD card size and read performance. Please wait..."
SDSIZE=`blockdev --getsize64 /dev/mmcblk0`;
pv -tpreb /dev/mmcblk0 -s $SDSIZE | dd of=$OFILE bs=1M conv=sync,noerror iflag=fullblock

# Wait for DD to finish and catch result
RESULT=$?

# Start services again that where shutdown before backup process
echo "Start the stopped services again."
service apache2 start
service mysql start
service cron start

# If command has completed successfully, delete previous backups and exit
if [ $RESULT = 0 ];
   then
      echo "Successful backup, previous backup files will be deleted."
      rm -f $DIR/backup_*.tar.gz
      mv $OFILE $OFILEFINAL
      echo "Backup is being tarred. Please wait..."
      tar zcf $OFILEFINAL.tar.gz $OFILEFINAL
      rm -rf $OFILEFINAL
      echo "RaspberryPI backup process completed! FILE: $OFILEFINAL.tar.gz"
      exit 0
# Else remove attempted backup file
   else
      echo "Backup failed! Previous backup files untouched."
      echo "Please check there is sufficient space on the HDD."
      rm -f $OFILE
      echo "RaspberryPI backup process failed!"
      exit 1
fi

Prendi in considerazione l'aggiunta di commenti al forum originale o pubblica la tua versione per maturare il contenuto. Prendi un po ', dai un po'.

* E grazie per aver restituito AndersW (clicca per lo script GIT)


2
Cosa succede se il filesystem (cancellazione dei file, nuovi file aggiunti) cambia nel tempo mentre il pi sta eseguendo il backup?
keiki

2
Eseguo il backup di diversi dischi mentre sono in esecuzione con rsync e spesso sono stato in grado di ottenere esattamente ciò di cui ho bisogno da questi backup di file. Tuttavia, in generale, un file system unix non può essere copiato perfettamente (con ogni bit in posizione e corretto) mentre il file system è montato (*). Una copia fatta mentre il sistema era montato a volte viene chiamata "copia sporca". Diverse misure possono essere prese per migliorare la qualità di una copia sporca (come fa lo script sopra, spegnendo cron e mysql) ma non può essere perfetto. Saluti! * - Mi sbaglio, dipende dal file system.
Tai Viinikka,

1
Puoi guardare le utility di backup consigliate da Debian e vedere se Pi ne ha una porta. rsnapshotsembra promozionale
Piotr Kula

1
@TaiViinikka Non hai bisogno di una copia perfetta. È necessaria una copia parziale che può essere (rapidamente e facilmente) reimpostata sull'immagine di base originale. rsyncè la strada da percorrere; quando avrò tempo domani aggiungerò una risposta. rsnapshotvale anche la pena indagare.
Riccioli d'oro

3
Sulla base della risposta ppumkins sopra, ho sincronizzato lo script 'dd' con gli ultimi commenti nel thread originale e ho aggiunto alcuni piccoli miglioramenti. Il risultato finale è disponibile qui: < github.com/aweijnitz/pi_backup >. Non esitare ad aggiungere miglioramenti e inviarmi richieste pull.
AndersW,

14

Ho adattato la risposta di @goldilocks su rsync per il backup sul pi. Eseguo il backup su una ext4partizione su un HDD montato sul Pi. Se l'HDD non è montato, rsync verrà copiato nella directory di montaggio (fino a quando la scheda SD non è piena). Se l'HDD non è montato in rwmodalità, vengono generati copiosi messaggi di errore. Nessuno di questi è desiderabile, quindi controllo che la mia partizione sia montata in rwmodalità prima di procedere.

NOTA 03-03-2015 Ho modificato la mia risposta per copiare accuratamente i collegamenti. L'originale ha funzionato, ma ha convertito molti hardlink in file. Oltre a sprecare spazio, questo compromette molti usi che presumono che siano presenti gli hardlink. (La mia immagine attuale ha 869 collegamenti, molti in Raspbian stesso.)

La mia sceneggiatura per fare questo segue. (La mia partizione è PiData, montata su/mnt/PiData

#!/bin/bash
# script to synchronise Pi files to backup
BACKUP_MOUNTED=$(mount | awk '/PiData/ {print $6}' | grep "rw")
if [ $BACKUP_MOUNTED ]; then
    echo $BACKUP_MOUNTED
    echo "Commencing Backup"
    rsync -avH --delete-during --delete-excluded --exclude-from=/usr/bin/rsync-exclude.txt / /mnt/PiData/PiBackup/
else
    echo "Backup drive not available or not writable"
fi

Ripristina (o aggiorna un altro Pi) con il seguente: -

sudo rsync -avH /mnt/PiData/PiBackup/ /

Ho migliorato il rsync-exclude.txtper eliminare i file non necessari.

Il primo gruppo sono le directory documentate da @goldilocks https://raspberrypi.stackexchange.com/users/5538/

Il secondo gruppo sono i file e le directory creati da OS X quando accedo al mio Pi utilizzando AFP (Apple Filing Protocol). (Questi sono normalmente invisibili su OS X, ma non su Raspbian. In ogni caso, non è necessario eseguire il backup.) Anche se non si utilizza mai AFP, questi non danneggiano.

Il terzo gruppo sono file di cui non è necessario eseguire il backup (e sicuramente non copiati su un altro Pi). Esempi fake-hwclock.data, rapporti RPi-Monitor. Probabilmente ne avrai altri.

/proc/*
/sys/*
/dev/*
/boot/*
/tmp/*
/run/*
/mnt/*

.Trashes
._.Trashes
.fseventsd
.Spotlight-V100
.DS_Store
.AppleDesktop
.AppleDB
Network Trash Folder
Temporary Items

.bash_history
/etc/fake-hwclock.data
/var/lib/rpimonitor/stat/

1
C'è un modo per rendere quell'output un file .img ?
IgorGanapolsky,

@IgorGanapolsky Bene, visto che ci sono tutti i file essenziali (tranne i file di avvio), è ovviamente possibile, ma se vuoi un'immagine crea un'immagine. Dovresti porre qualsiasi nuova domanda in un nuovo post, non commenti.
Milliways,

@Milliways perché non dovremmo usare "sudo rsync ..."? Ci saranno alcuni file che potrebbero non essere sincronizzati?
Smilia,

6

Ho tre Pis in esecuzione nella mia rete locale e ho bisogno di eseguirne il backup su base regolare con cron quando sono attivi. Ecco perché ho creato uno script in grado di creare backup dd, tar e rsync e ripristinarli. Preferisco usare rsync per i miei backup, ma altre persone preferiscono dd o tar. È già utilizzato da molte persone. Spero che sia utile anche per altri :-) raspibackup - Raspberry crea backup di se stesso


1
No, scusa: chiedere all'utente di eseguire (come root!) Uno script scaricato su HTTP è irresponsabile. Distribuisci questo script su un canale sicuro.
Clément

1
Non penso che sia fuori tema, e root o non importa molto. Il punto è che il software dovrebbe essere distribuito su un canale sicuro e la tua risposta sta incoraggiando cattive pratiche di sicurezza.
Clément

1
Sarebbe un grande passo avanti, sì :)
Clément

2
Solo per notare che la consegna su HTTPS non aggiunge in alcun modo sicurezza in questo caso! Stai ancora scaricando ed eseguendo uno script da Internet. Il processo sicuro è quello di scaricare lo script (http / https è irrilevante), aprire lo script in un editor e leggerlo dall'alto verso il basso, verificarlo per stranezze e insicurezze. Solo quando sei soddisfatto dovresti eseguirlo. Framp potrebbe essere un hacker per tutti noi che sappiamo e la consegna su https lo farebbe solo sorridere in quel caso :) (A proposito, non è un'accusa, Framp!)
Julian Knight,

2
Sono d'accordo con te. Ecco perché ci sono due modi in cui è descritto come installare lo script: 1. Usa installerScript 2. Scaricalo manualmente, controlla il codice e poi installalo manualmente
vai al

3

Ecco il nostro strumento stabile per tali scopi: https://github.com/aktos-io/aktos-dcs-tools

Questo strumento è stato scritto per make sshle connessioni, make backup-root, make mount-rootda luoghi remoti in mente in un primo momento, e poi vengono aggiunti sessioni locali. Supporta quindi backup locali, backup remoti diretti, backup remoti proxy. I backup vengono eseguiti in modo incrementale (vengono trasferite solo le differenze) e le directory di backup sono autonome (basta selezionare una directory / versione da ripristinare, qualsiasi directory ha un backup completo). Ovviamente, hai delle versioni (backup.last-0 è la più recente). È possibile interrompere il processo di backup in qualsiasi momento e continuare in seguito.

Ecco le istruzioni per il tuo problema specifico:

 ssh to-your-raspberry
 cd /mnt/usb0/my-rpi-backups
 git clone https://github.com/ceremcem/aktos-dcs-tools backup-tools
 ln -s backup-tools/Makefile .

 ./backup-tools/configure # you do not need to make any settings for local sessions, just save the default 

 # just for the first time
 make set-local-session  # a flag file is created
 make init               # snapshots directory is created

 # anytime you want to back up
 make backup-root        # backup with rsync

MODIFICARE

Ora è stata aggiunta una nuova destinazione: è possibile creare una scheda SD fisica dai backup con un solo comando:

make create-disk-from-last-backup

Seguire le istruzioni, creare la scheda SD, avviare RaspberryPi con questa scheda SD appena creata.


1

Ecco un approccio completamente diverso. È possibile utilizzare LVM ( L ogical V olume M anager) per eseguire backup coerenti. Oltre ad altri miglioramenti come la semplice aggiunta, espansione e riduzione dell'archiviazione o il ripristino del sistema operativo a uno stato precedente da un'istantanea, è anche possibile eseguire backup. Non devi preoccuparti dei file dinamici modificati durante il backup, impostare i file system in sola lettura, escludere directory specifiche o qualcos'altro. Con LVM semplicemente crei un'istantanea, monti questa istantanea e esegui il backup con il metodo che preferisci. Puoi fare una copia con cp -a, fare uno specchio con rsync, fare un archivio con taro fare un'immagine condd. Supponendo che tu abbia montato un dispositivo di backup, /mnt/usbhd/pi_backup/puoi fare ad esempio:

rpi ~$ sudo lvcreate --snapshot --name rpi_snap --size 1G rpi_vg/root_lv
rpi ~$ sudo mkdir /mnt/snapshot
rpi ~$ sudo mount /dev/mapper/rpi_vg-rpi_snap /mnt/snapshot

# make backups
rpi ~$ sudo cp -a /mnt/snapshot/ /mnt/usbhd/pi_backup/
rpi ~$ sudo rsync -aH --delete /mnt/snapshot/ /mnt/usbhd/pi_backup/
rpi ~$ sudo tar -czf /mnt/usbhd/pi_backup/backup.tar.gz -V "Backup of my Raspberry Pi" -C /mnt/snapshot/ ./
rpi ~$ sudo dd if=/mnt/snapshot/ of=/mnt/usbhd/pi_backup/backup.img bs=4M

rpi ~$ sudo umount /mnt/snapshot/
rpi ~$ sudo lvremove rpi_vg/rpi_snap

Ci vuole solo una volta un piccolo sforzo per impostare LVM . Come farlo puoi guardare Backup e istantanee facili di un sistema in esecuzione con LVM .


0

Ho trovato uno strumento di backup che rende le immagini installabili.

Ha anche utility per montare e ridurre le immagini.

Questo può essere utile per gli altri

La documentazione che ne deriva è molto breve, quindi noto quanto segue: -

  1. Estrarre le utilità in qualsiasi directory e rendere eseguibili gli script.
  2. Montare una ext4partizione formattata sul Pi in /mnto /media(è possibile utilizzare qualsiasi formato che consenta file di grandi dimensioni ed è supportato da Pi, ad esempio exFAT o un'unità di rete).
  3. Per l'esecuzione iniziale ti verrà richiesto un nome dell'immagine di backup, ad es /mnt/Image/BusterBackup.img
  4. Ti verrà richiesta una dimensione del file system Image ROOT (in MB), che può essere 0 per il più piccolo possibile o vuota per il backup completo.
  5. Nelle esecuzioni successive immettere il percorso dell'immagine di backup per l'aggiornamento incrementale.
An example of the commands I used:-
# Mount USB
sudo mount /dev/sda1 /mnt/Image/
# Update backup
sudo image-utils/image-backup /mnt/Image/BusterBackup.img
# Mount backup
sudo image-utils/image-mount /mnt/Image/BusterBackup.img  MountedImages
When done, run:
sudo umount MountedImages; sudo losetup -d /dev/loop0
# Compress backup
sudo sh -c "gzip -9c /mnt/Image/BusterBackup.img  > Images/BusterBackup.img.gz"

Ho leggermente modificato l'originale (per copiare i mountpoint), per calcolare correttamente gli offset e le dimensioni della partizione e ho aggiunto un paio di commenti.

#!/bin/bash
# Original https://raspberrypi.org/forums/viewtopic.php?p=1528736
# 2019-09-26    Modified to set size of boot sector

trap '{ stty sane; echo ""; errexit "Aborted"; }' SIGINT SIGTERM

ADDBLK=0

# Set BOOT_SIZE_MB to the Desired boot sector size (in MB) - should be multiple of 4MB
BOOT_SIZE_MB=256
BOOTSIZEM=$BOOT_SIZE_MB'M'

BOOTBEG=8192
BOOT_SIZE="$((BOOT_SIZE_MB * 1024 * 1024))"
ROUND_SIZE="$((4 * 1024 * 1024))"
# Ensure root sector starts on an Erase Block Boundary (4MB)
ROOTBEG=$(((BOOT_SIZE + ROUND_SIZE -1) / ROUND_SIZE * ROUND_SIZE / 512 + BOOTBEG))

MNTPATH="/tmp/img-backup-mnt"

ONEMB=$((1024 * 1024))

# create BOOT loop device
mkloop1()
{
  local INFO1=""
  local SIZE1=0
  local START1=0

  sync
  INFO1="$(sfdisk -d "${IMGFILE}")"
  START1=$(grep type=c <<< "${INFO1}" | sed -n 's|^.*start=\s\+\([0-9]\+\).*$|\1|p')
  SIZE1=$(grep type=c <<< "${INFO1}" | sed -n 's|^.*size=\s\+\([0-9]\+\).*$|\1|p')
  LOOP1="$(losetup -f --show -o $((${START1} * 512)) --sizelimit $((${SIZE1} * 512)) "${IMGFILE}")"
  if [ $? -ne 0 ]; then
    errexit "Unable to create BOOT loop device"
  fi
}

rmloop1()
{
  if [ "${LOOP1}" != "" ]; then
    sync
    losetup -d "${LOOP1}"
    LOOP1=""
 fi
}

# create ROOT loop device
mkloop2()
{
  local INFO2=""
  local SIZE2=0
  local START2=0

  sync
  INFO2="$(sfdisk -d "${IMGFILE}")"
  START2=$(grep type=83 <<< "${INFO2}" | sed -n 's|^.*start=\s\+\([0-9]\+\).*$|\1|p')
  SIZE2=$(grep type=83 <<< "${INFO2}" | sed -n 's|^.*size=\s\+\([0-9]\+\).*$|\1|p')
  LOOP2="$(losetup -f --show -o $((${START2} * 512)) --sizelimit $((${SIZE2} * 512)) "${IMGFILE}")"
  if [ $? -ne 0 ]; then
    errexit "Unable to create ROOT loop device"
  fi
}

rmloop2()
{
  if [ "${LOOP2}" != "" ]; then
    sync
    losetup -d "${LOOP2}"
    LOOP2=""
  fi
}

# Mount Image partitions
mntimg()
{
  MNTED=TRUE
  if [ ! -d "${MNTPATH}/" ]; then
    mkdir "${MNTPATH}/"
    if [ $? -ne 0 ]; then
      errexit "Unable to make ROOT partition mount point"
    fi
  fi
  mkloop2
  mount "${LOOP2}" "${MNTPATH}/"
  if [ $? -ne 0 ]; then
    errexit "Unable to mount image ROOT partition"
  fi
  if [ ! -d "${MNTPATH}/boot/" ]; then
    mkdir -p "${MNTPATH}/boot/"
    if [ $? -ne 0 ]; then
      errexit "Unable to make BOOT partition mount point"
    fi
  fi
  mkloop1
  mount "${LOOP1}" "${MNTPATH}/boot/"
  if [ $? -ne 0 ]; then
    errexit "Unable to mount image BOOT partition"
  fi
}

umntimg()
{
  umount "${MNTPATH}/boot/"
  if [ $? -ne 0 ]; then
    errexit "Unable to unmount image BOOT partition"
  fi
  rmloop1
  umount "${MNTPATH}/"
  if [ $? -ne 0 ]; then
    errexit "Unable to unmount image ROOT partition"
  fi
  rmloop2
  rm -r "${MNTPATH}/"
  MNTED=FALSE
}

errexit()
{
  echo ""
  echo "$1"
  echo ""
  if [ "${MNTED}" = "TRUE" ]; then
    umount "${MNTPATH}/boot/" &> /dev/null
    umount "${MNTPATH}/" &> /dev/null
    rm -rf "${MNTPATH}/" &> /dev/null
  fi
  rmloop1
  rmloop2
  exit 1
}

LOOP1=""
LOOP2=""
MNTED=FALSE

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

if [ $(id -u) -ne 0 ]; then
  errexit "$0 must be run as root user"
fi

PGMNAME="$(basename $0)"
for PID in $(pidof -x -o %PPID "${PGMNAME}"); do
  if [ ${PID} -ne $$ ]; then
    errexit "${PGMNAME} is already running"
  fi
done

rsync --version &> /dev/null
if [ $? -ne 0 ]; then
  errexit "rsync not installed (run: apt-get install rsync)"
fi

if command -v systemctl > /dev/null && systemctl | grep -q '\-\.mount'; then
  SYSTEMD=1
elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then
  SYSTEMD=0
else
  errexit "Unrecognized init system"
fi

if [ ${SYSTEMD} -eq 1 ]; then
  ROOT_PART="$(mount | sed -n 's|^/dev/\(.*\) on / .*|\1|p')"
else
  if [ ! -h /dev/root ]; then
    errexit "/dev/root does not exist or is not a symlink"
  fi
  ROOT_PART="$(readlink /dev/root)"
fi

ROOT_TYPE=$(blkid "/dev/${ROOT_PART}" | sed -n 's|^.*TYPE="\(\S\+\)".*|\1|p')

ROOT_DEV="${ROOT_PART:0:(${#ROOT_PART} - 1)}"
if [ "${ROOT_DEV}" = "mmcblk0p" ]; then
  ROOT_DEV="${ROOT_DEV:0:(${#ROOT_DEV} - 1)}"
fi

PTUUID="$(blkid "/dev/${ROOT_DEV}" | sed -n 's|^.*PTUUID="\(\S\+\)".*|\1|p')"

DEVSIZE=$(blockdev --getsize64 "/dev/${ROOT_PART}")
BLKSIZE=$(blockdev --getbsz "/dev/${ROOT_PART}")
BLKCNT=$((${DEVSIZE} / ${BLKSIZE}))
INFO="$(df | grep /dev/root)"
DFKSIZE=$(awk '{print $2}' <<< "${INFO}")
DFKFREE=$(awk '{print $4}' <<< "${INFO}")
ROOTSIZE=$((${BLKCNT} * ${BLKSIZE}))
ROOTUSED=$(((${DFKSIZE} - ${DFKFREE}) * 1024))
IRFSMIN=$(((${ROOTUSED} + (${ADDBLK} * ${BLKSIZE}) + (${ONEMB} - 1)) / ${ONEMB}))
IRFSMAX=$(((${ROOTSIZE} + (${ONEMB} - 1)) / ${ONEMB}))

IMGFILE="$1"
if [ "${IMGFILE}" = "" ]; then
# Create Image file
  while :
  do
    echo ""
    read -r -e -i "${IMGFILE}" -p "Image file to create? " IMGFILE
    if [ "${IMGFILE}" = "" ]; then
      continue
    elif [[ ! "${IMGFILE}" =~ ^/mnt/.*$ && ! "${IMGFILE}" =~ ^/media/.*$ ]]; then
      echo ""
      echo "${IMGFILE} does not begin with /mnt/ or /media/"
      continue
    fi
    if [ -d "${IMGFILE}" ]; then
      echo ""
      echo "${IMGFILE} is a directory"
    elif [ -f "${IMGFILE}" ]; then
      echo ""
      echo -n "${IMGFILE} already exists, Ok to delete (y/n)? "
      while read -r -n 1 -s answer; do
        if [[ "${answer}" = [yYnN] ]]; then
          echo "${answer}"
          if [[ "${answer}" = [yY] ]]; then
            break 2
          else
            break 1
          fi
        fi
      done
    else
      break
    fi
  done
  IRFSSIZE=""
  while :
  do
    echo ""
    read -r -e -i "${IRFSSIZE}" -p "Image ROOT filesystem size (MB) [${IRFSMAX}]? " IRFSSIZE
    if [ "${IRFSSIZE}" = "" ]; then
      IRFSSIZE=${IRFSMAX}
      break
    elif [ ${IRFSSIZE} -ge ${IRFSMIN} ]; then
      break
    else
      echo ""
      echo "Requested image ROOT filesystem size (${IRFSSIZE}) is too small (Minimum = ${IRFSMIN})"
      IRFSSIZE=${IRFSMIN}
    fi
  done
  echo ""
  echo -n "Create ${IMGFILE} [${IRFSSIZE} MB] (y/n)? "
  while read -r -n 1 -s answer; do
    if [[ "${answer}" = [yYnN] ]]; then
      echo "${answer}"
      if [[ "${answer}" = [yY] ]]; then
        break
      else
        errexit "Aborted"
      fi
    fi
  done
  if [ -f "${IMGFILE}" ]; then
    rm "${IMGFILE}"
    if [ $? -ne 0 ]; then
      errexit "Unable to delete existing image file"
    fi
  fi
  ROOTEND=$((${ROOTBEG} + ((${IRFSSIZE} * ${ONEMB}) / 512) - 1))
  truncate -s $(((${ROOTEND} + 1) * 512)) "${IMGFILE}"
  if [ $? -ne 0 ]; then
    errexit "Unable to create image file"
  fi
# create image/partitions
  sync
  fdisk "${IMGFILE}" <<EOF > /dev/null
p
n
p
1
${BOOTBEG}
+${BOOTSIZEM}
t
c
p
n
p
2
${ROOTBEG}
${ROOTEND}
p
w
EOF

  mkloop1
  mkloop2
  mkfs.vfat "${LOOP1}" > /dev/null
  if [ $? -ne 0 ]; then
    errexit "Unable to create image BOOT filesystem"
  fi
  dosfsck "${LOOP1}" > /dev/null
  if [ $? -ne 0 ]; then
    errexit "Image BOOT filesystem appears corrupted"
  fi
  if [ "${ROOT_TYPE}" = "f2fs" ]; then
    mkfs.f2fs "${LOOP2}" > /dev/null
  else
    mkfs.ext4 -q -b ${BLKSIZE} "${LOOP2}" > /dev/null
  fi
  if [ $? -ne 0 ]; then
    errexit "Unable to create image ROOT filesystem"
  fi
  rmloop2
  rmloop1
# Initialise image PARTUUID
  fdisk "${IMGFILE}" <<EOF > /dev/null
p
x
i
0x${PTUUID}
r
p
w
EOF
# Create empty directories in image root partition
  mntimg
  mkdir "${MNTPATH}/dev/" "${MNTPATH}/media/" "${MNTPATH}/mnt/" "${MNTPATH}/proc/" "${MNTPATH}/run/" "${MNTPATH}/sys/" "${MNTPATH}/tmp/"
  if [ $? -ne 0 ]; then
    errexit "Unable to create image directories"
  fi
  chmod a+rwxt "${MNTPATH}/tmp/"
  umntimg
  echo ""
  echo "Starting full backup (for incremental backups, run: $0 ${IMGFILE})"
# END of create image/partitions
else

# Check existing Image
  if [[ ! "${IMGFILE}" =~ ^/mnt/.*$ && ! "${IMGFILE}" =~ ^/media/.*$ ]]; then
    errexit "${IMGFILE} does not begin with /mnt/ or /media/"
  fi
  if [ -d "${IMGFILE}" ]; then
    errexit "${IMGFILE} is a directory"
  elif [ ! -f "${IMGFILE}" ]; then
    errexit "${IMGFILE} not found"
  fi
  echo "Starting incremental backup to ${IMGFILE}"
fi

# rsync root partition
mntimg
sync
rsync -aDH --partial --numeric-ids --delete --force --exclude "${MNTPATH}" --exclude '/dev' --exclude '/media' --exclude '/mnt/*/*' --exclude '/proc' --exclude '/run' --exclude '/sys' \
--exclude '/tmp' --exclude 'lost\+found' --exclude '/etc/udev/rules.d/70-persistent-net.rules' --exclude '/var/lib/asterisk/astdb.sqlite3-journal' / "${MNTPATH}/"
if [[ $? -ne 0 && $? -ne 24 ]]; then
  errexit "Unable to create backup"
fi
sync
umntimg

-1

Apri il terminale e digita 'lsblk -f'.
Questo dovrebbe mostrare tutti i dispositivi di archiviazione collegati.
Quindi digitare 'dd if = / dev / [il NOME della tua scheda SD] bs = 1M'.
Questo richiederà un po 'di tempo, quindi potresti volerlo eseguire in background.
Questo è esattamente lo stesso modo in cui esegui il backup della tua scheda SD in Linux.


Ciò esegue il backup di TUTTO, anche i file non necessari e indesiderati.
IgorGanapolsky,

3
Questo farà un backup incoerente perché su un sistema in esecuzione le cose sono cambiate durante il backup!
Ingo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.