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