2014-04-27 22:00:15 +00:00
package main
import (
2020-02-26 21:17:59 +00:00
"strings"
2020-02-20 20:54:31 +00:00
"time"
2020-02-26 21:17:59 +00:00
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter"
2017-07-24 15:42:25 +00:00
"github.com/restic/restic/internal/restic"
2018-05-11 02:56:10 +00:00
"github.com/restic/restic/internal/restorer"
2016-09-17 10:36:05 +00:00
"github.com/spf13/cobra"
2014-04-27 22:00:15 +00:00
)
2016-09-17 10:36:05 +00:00
var cmdRestore = & cobra . Command {
Use : "restore [flags] snapshotID" ,
2017-09-11 16:32:44 +00:00
Short : "Extract the data from a snapshot" ,
2016-09-17 10:36:05 +00:00
Long : `
The "restore" command extracts the data from a snapshot from the repository to
a directory .
The special snapshot "latest" can be used to restore the latest snapshot in the
repository .
2019-11-05 06:03:38 +00:00
EXIT STATUS
== == == == == =
Exit status is 0 if the command was successful , and non - zero if there was any error .
2016-09-17 10:36:05 +00:00
` ,
2017-08-06 19:02:16 +00:00
DisableAutoGenTag : true ,
2016-09-17 10:36:05 +00:00
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
return runRestore ( restoreOptions , globalOptions , args )
} ,
}
2015-07-19 22:38:44 +00:00
2016-09-17 10:36:05 +00:00
// RestoreOptions collects all options for the restore command.
type RestoreOptions struct {
2018-10-16 20:39:55 +00:00
Exclude [ ] string
InsensitiveExclude [ ] string
Include [ ] string
InsensitiveInclude [ ] string
Target string
2020-02-26 21:17:59 +00:00
Hosts [ ] string
2018-10-16 20:39:55 +00:00
Paths [ ] string
Tags restic . TagLists
Verify bool
2015-06-21 11:02:56 +00:00
}
2014-12-07 15:30:52 +00:00
2016-09-17 10:36:05 +00:00
var restoreOptions RestoreOptions
2014-11-30 21:39:58 +00:00
func init ( ) {
2016-09-17 10:36:05 +00:00
cmdRoot . AddCommand ( cmdRestore )
2014-12-07 15:30:52 +00:00
2016-09-17 10:36:05 +00:00
flags := cmdRestore . Flags ( )
2017-07-07 01:19:06 +00:00
flags . StringArrayVarP ( & restoreOptions . Exclude , "exclude" , "e" , nil , "exclude a `pattern` (can be specified multiple times)" )
2018-10-16 20:39:55 +00:00
flags . StringArrayVar ( & restoreOptions . InsensitiveExclude , "iexclude" , nil , "same as `--exclude` but ignores the casing of filenames" )
2017-07-07 01:19:06 +00:00
flags . StringArrayVarP ( & restoreOptions . Include , "include" , "i" , nil , "include a `pattern`, exclude everything else (can be specified multiple times)" )
2018-10-16 20:39:55 +00:00
flags . StringArrayVar ( & restoreOptions . InsensitiveInclude , "iinclude" , nil , "same as `--include` but ignores the casing of filenames" )
2016-09-17 10:36:05 +00:00
flags . StringVarP ( & restoreOptions . Target , "target" , "t" , "" , "directory to extract data to" )
2020-02-26 21:17:59 +00:00
flags . StringArrayVarP ( & restoreOptions . Hosts , "host" , "H" , nil , ` only consider snapshots for this host when the snapshot ID is "latest" (can be specified multiple times) ` )
2017-07-09 10:45:49 +00:00
flags . Var ( & restoreOptions . Tags , "tag" , "only consider snapshots which include this `taglist` for snapshot ID \"latest\"" )
2017-07-07 01:19:06 +00:00
flags . StringArrayVar ( & restoreOptions . Paths , "path" , nil , "only consider snapshots which include this (absolute) `path` for snapshot ID \"latest\"" )
2018-04-13 14:02:09 +00:00
flags . BoolVar ( & restoreOptions . Verify , "verify" , false , "verify restored files content" )
2014-11-30 21:39:58 +00:00
}
2016-09-17 10:36:05 +00:00
func runRestore ( opts RestoreOptions , gopts GlobalOptions , args [ ] string ) error {
2017-06-04 09:16:55 +00:00
ctx := gopts . ctx
2018-10-16 20:39:55 +00:00
hasExcludes := len ( opts . Exclude ) > 0 || len ( opts . InsensitiveExclude ) > 0
hasIncludes := len ( opts . Include ) > 0 || len ( opts . InsensitiveInclude ) > 0
2017-06-04 09:16:55 +00:00
2019-01-19 10:59:48 +00:00
for i , str := range opts . InsensitiveExclude {
opts . InsensitiveExclude [ i ] = strings . ToLower ( str )
}
for i , str := range opts . InsensitiveInclude {
opts . InsensitiveInclude [ i ] = strings . ToLower ( str )
}
2018-01-06 21:44:18 +00:00
switch {
case len ( args ) == 0 :
2017-02-10 18:39:49 +00:00
return errors . Fatal ( "no snapshot ID specified" )
2018-01-06 21:44:18 +00:00
case len ( args ) > 1 :
return errors . Fatalf ( "more than one snapshot ID specified: %v" , args )
2014-12-07 15:30:52 +00:00
}
2016-09-17 10:36:05 +00:00
if opts . Target == "" {
2016-09-01 20:17:37 +00:00
return errors . Fatal ( "please specify a directory to restore to (--target)" )
2015-07-19 22:38:44 +00:00
}
2018-10-16 20:39:55 +00:00
if hasExcludes && hasIncludes {
2016-09-01 20:17:37 +00:00
return errors . Fatal ( "exclude and include patterns are mutually exclusive" )
2015-07-20 17:20:20 +00:00
}
2015-07-19 22:38:44 +00:00
snapshotIDString := args [ 0 ]
2016-09-27 20:35:08 +00:00
debug . Log ( "restore %v to %v" , snapshotIDString , opts . Target )
2015-07-19 22:38:44 +00:00
2016-09-17 10:36:05 +00:00
repo , err := OpenRepository ( gopts )
2014-12-07 15:30:52 +00:00
if err != nil {
return err
2014-04-27 22:00:15 +00:00
}
2016-09-17 10:36:05 +00:00
if ! gopts . NoLock {
2020-08-09 11:24:47 +00:00
lock , err := lockRepo ( ctx , repo )
2015-11-10 21:13:53 +00:00
defer unlockRepo ( lock )
if err != nil {
return err
}
2015-06-27 12:40:18 +00:00
}
2016-09-01 14:04:29 +00:00
var id restic . ID
2016-05-10 19:23:18 +00:00
if snapshotIDString == "latest" {
2021-11-06 00:14:24 +00:00
id , err = restic . FindLatestSnapshot ( ctx , repo . Backend ( ) , repo , opts . Paths , opts . Tags , opts . Hosts , nil )
2016-05-10 19:23:18 +00:00
if err != nil {
2020-02-26 21:17:59 +00:00
Exitf ( 1 , "latest snapshot for criteria not found: %v Paths:%v Hosts:%v" , err , opts . Paths , opts . Hosts )
2016-05-10 19:23:18 +00:00
}
} else {
2021-11-06 00:14:24 +00:00
id , err = restic . FindSnapshot ( ctx , repo . Backend ( ) , snapshotIDString )
2016-05-10 19:23:18 +00:00
if err != nil {
2016-09-17 10:36:05 +00:00
Exitf ( 1 , "invalid id %q: %v" , snapshotIDString , err )
2016-05-10 19:23:18 +00:00
}
2014-04-27 22:00:15 +00:00
}
2021-11-05 23:32:46 +00:00
err = repo . LoadIndex ( ctx )
if err != nil {
return err
}
2020-04-10 09:36:14 +00:00
res , err := restorer . NewRestorer ( ctx , repo , id )
2014-08-04 20:46:14 +00:00
if err != nil {
2016-09-17 10:36:05 +00:00
Exitf ( 2 , "creating restorer failed: %v\n" , err )
2014-08-04 20:46:14 +00:00
}
2017-03-12 15:39:37 +00:00
totalErrors := 0
2018-09-15 00:55:30 +00:00
res . Error = func ( location string , err error ) error {
Warnf ( "ignoring error for %s: %s\n" , location , err )
2017-03-12 15:39:37 +00:00
totalErrors ++
2015-07-25 10:58:55 +00:00
return nil
2014-04-27 22:00:15 +00:00
}
2020-10-07 12:39:51 +00:00
excludePatterns := filter . ParsePatterns ( opts . Exclude )
insensitiveExcludePatterns := filter . ParsePatterns ( opts . InsensitiveExclude )
2017-07-07 09:54:10 +00:00
selectExcludeFilter := func ( item string , dstpath string , node * restic . Node ) ( selectedForRestore bool , childMayBeSelected bool ) {
2020-10-07 17:46:41 +00:00
matched , err := filter . List ( excludePatterns , item )
2015-07-19 22:38:44 +00:00
if err != nil {
2016-09-17 10:36:05 +00:00
Warnf ( "error for exclude pattern: %v" , err )
2015-01-01 15:29:41 +00:00
}
2015-07-19 22:38:44 +00:00
2020-10-07 17:46:41 +00:00
matchedInsensitive , err := filter . List ( insensitiveExcludePatterns , strings . ToLower ( item ) )
2018-10-16 20:39:55 +00:00
if err != nil {
Warnf ( "error for iexclude pattern: %v" , err )
}
2017-07-07 09:54:10 +00:00
// An exclude filter is basically a 'wildcard but foo',
// so even if a childMayMatch, other children of a dir may not,
// therefore childMayMatch does not matter, but we should not go down
// unless the dir is selected for restore
2018-10-16 20:39:55 +00:00
selectedForRestore = ! matched && ! matchedInsensitive
2017-07-07 09:54:10 +00:00
childMayBeSelected = selectedForRestore && node . Type == "dir"
return selectedForRestore , childMayBeSelected
2015-07-19 22:38:44 +00:00
}
2020-10-07 12:39:51 +00:00
includePatterns := filter . ParsePatterns ( opts . Include )
insensitiveIncludePatterns := filter . ParsePatterns ( opts . InsensitiveInclude )
2017-07-07 09:54:10 +00:00
selectIncludeFilter := func ( item string , dstpath string , node * restic . Node ) ( selectedForRestore bool , childMayBeSelected bool ) {
2020-10-07 17:46:41 +00:00
matched , childMayMatch , err := filter . ListWithChild ( includePatterns , item )
2015-07-20 17:20:20 +00:00
if err != nil {
2016-09-17 10:36:05 +00:00
Warnf ( "error for include pattern: %v" , err )
2015-07-20 17:20:20 +00:00
}
2020-10-07 17:46:41 +00:00
matchedInsensitive , childMayMatchInsensitive , err := filter . ListWithChild ( insensitiveIncludePatterns , strings . ToLower ( item ) )
2018-10-16 20:39:55 +00:00
if err != nil {
Warnf ( "error for iexclude pattern: %v" , err )
}
selectedForRestore = matched || matchedInsensitive
childMayBeSelected = ( childMayMatch || childMayMatchInsensitive ) && node . Type == "dir"
2017-07-07 09:54:10 +00:00
return selectedForRestore , childMayBeSelected
2015-07-20 17:20:20 +00:00
}
2018-10-16 20:39:55 +00:00
if hasExcludes {
2015-07-20 17:20:20 +00:00
res . SelectFilter = selectExcludeFilter
2018-10-16 20:39:55 +00:00
} else if hasIncludes {
2015-07-20 17:20:20 +00:00
res . SelectFilter = selectIncludeFilter
2015-01-01 15:29:41 +00:00
}
2016-09-17 10:36:05 +00:00
Verbosef ( "restoring %s to %s\n" , res . Snapshot ( ) , opts . Target )
2014-04-27 22:00:15 +00:00
2018-05-11 04:45:14 +00:00
err = res . RestoreTo ( ctx , opts . Target )
2021-01-05 08:13:15 +00:00
if err != nil {
return err
}
if totalErrors > 0 {
return errors . Fatalf ( "There were %d errors\n" , totalErrors )
}
if opts . Verify {
2018-04-13 14:02:09 +00:00
Verbosef ( "verifying files in %s\n" , opts . Target )
var count int
2020-02-20 20:54:31 +00:00
t0 := time . Now ( )
2018-04-13 14:02:09 +00:00
count , err = res . VerifyFiles ( ctx , opts . Target )
2021-01-05 08:13:15 +00:00
if err != nil {
return err
}
if totalErrors > 0 {
return errors . Fatalf ( "There were %d errors\n" , totalErrors )
}
2020-02-20 20:54:31 +00:00
Verbosef ( "finished verifying %d files in %s (took %s)\n" , count , opts . Target ,
time . Since ( t0 ) . Round ( time . Millisecond ) )
2018-04-13 14:02:09 +00:00
}
2021-01-05 08:13:15 +00:00
return nil
2014-04-27 22:00:15 +00:00
}