github.com/advanderveer/restic@v0.8.1-0.20171209104529-42a8c19aaea6/cmd/restic/cmd_restore.go (about) 1 package main 2 3 import ( 4 "github.com/restic/restic/internal/debug" 5 "github.com/restic/restic/internal/errors" 6 "github.com/restic/restic/internal/filter" 7 "github.com/restic/restic/internal/restic" 8 9 "github.com/spf13/cobra" 10 ) 11 12 var cmdRestore = &cobra.Command{ 13 Use: "restore [flags] snapshotID", 14 Short: "Extract the data from a snapshot", 15 Long: ` 16 The "restore" command extracts the data from a snapshot from the repository to 17 a directory. 18 19 The special snapshot "latest" can be used to restore the latest snapshot in the 20 repository. 21 `, 22 DisableAutoGenTag: true, 23 RunE: func(cmd *cobra.Command, args []string) error { 24 return runRestore(restoreOptions, globalOptions, args) 25 }, 26 } 27 28 // RestoreOptions collects all options for the restore command. 29 type RestoreOptions struct { 30 Exclude []string 31 Include []string 32 Target string 33 Host string 34 Paths []string 35 Tags restic.TagLists 36 } 37 38 var restoreOptions RestoreOptions 39 40 func init() { 41 cmdRoot.AddCommand(cmdRestore) 42 43 flags := cmdRestore.Flags() 44 flags.StringArrayVarP(&restoreOptions.Exclude, "exclude", "e", nil, "exclude a `pattern` (can be specified multiple times)") 45 flags.StringArrayVarP(&restoreOptions.Include, "include", "i", nil, "include a `pattern`, exclude everything else (can be specified multiple times)") 46 flags.StringVarP(&restoreOptions.Target, "target", "t", "", "directory to extract data to") 47 48 flags.StringVarP(&restoreOptions.Host, "host", "H", "", `only consider snapshots for this host when the snapshot ID is "latest"`) 49 flags.Var(&restoreOptions.Tags, "tag", "only consider snapshots which include this `taglist` for snapshot ID \"latest\"") 50 flags.StringArrayVar(&restoreOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` for snapshot ID \"latest\"") 51 } 52 53 func runRestore(opts RestoreOptions, gopts GlobalOptions, args []string) error { 54 ctx := gopts.ctx 55 56 if len(args) != 1 { 57 return errors.Fatal("no snapshot ID specified") 58 } 59 60 if opts.Target == "" { 61 return errors.Fatal("please specify a directory to restore to (--target)") 62 } 63 64 if len(opts.Exclude) > 0 && len(opts.Include) > 0 { 65 return errors.Fatal("exclude and include patterns are mutually exclusive") 66 } 67 68 snapshotIDString := args[0] 69 70 debug.Log("restore %v to %v", snapshotIDString, opts.Target) 71 72 repo, err := OpenRepository(gopts) 73 if err != nil { 74 return err 75 } 76 77 if !gopts.NoLock { 78 lock, err := lockRepo(repo) 79 defer unlockRepo(lock) 80 if err != nil { 81 return err 82 } 83 } 84 85 err = repo.LoadIndex(ctx) 86 if err != nil { 87 return err 88 } 89 90 var id restic.ID 91 92 if snapshotIDString == "latest" { 93 id, err = restic.FindLatestSnapshot(ctx, repo, opts.Paths, opts.Tags, opts.Host) 94 if err != nil { 95 Exitf(1, "latest snapshot for criteria not found: %v Paths:%v Host:%v", err, opts.Paths, opts.Host) 96 } 97 } else { 98 id, err = restic.FindSnapshot(repo, snapshotIDString) 99 if err != nil { 100 Exitf(1, "invalid id %q: %v", snapshotIDString, err) 101 } 102 } 103 104 res, err := restic.NewRestorer(repo, id) 105 if err != nil { 106 Exitf(2, "creating restorer failed: %v\n", err) 107 } 108 109 totalErrors := 0 110 res.Error = func(dir string, node *restic.Node, err error) error { 111 Warnf("ignoring error for %s: %s\n", dir, err) 112 totalErrors++ 113 return nil 114 } 115 116 selectExcludeFilter := func(item string, dstpath string, node *restic.Node) (selectedForRestore bool, childMayBeSelected bool) { 117 matched, _, err := filter.List(opts.Exclude, item) 118 if err != nil { 119 Warnf("error for exclude pattern: %v", err) 120 } 121 122 // An exclude filter is basically a 'wildcard but foo', 123 // so even if a childMayMatch, other children of a dir may not, 124 // therefore childMayMatch does not matter, but we should not go down 125 // unless the dir is selected for restore 126 selectedForRestore = !matched 127 childMayBeSelected = selectedForRestore && node.Type == "dir" 128 129 return selectedForRestore, childMayBeSelected 130 } 131 132 selectIncludeFilter := func(item string, dstpath string, node *restic.Node) (selectedForRestore bool, childMayBeSelected bool) { 133 matched, childMayMatch, err := filter.List(opts.Include, item) 134 if err != nil { 135 Warnf("error for include pattern: %v", err) 136 } 137 138 selectedForRestore = matched 139 childMayBeSelected = childMayMatch && node.Type == "dir" 140 141 return selectedForRestore, childMayBeSelected 142 } 143 144 if len(opts.Exclude) > 0 { 145 res.SelectFilter = selectExcludeFilter 146 } else if len(opts.Include) > 0 { 147 res.SelectFilter = selectIncludeFilter 148 } 149 150 Verbosef("restoring %s to %s\n", res.Snapshot(), opts.Target) 151 152 err = res.RestoreTo(ctx, opts.Target) 153 if totalErrors > 0 { 154 Printf("There were %d errors\n", totalErrors) 155 } 156 return err 157 }