Created
July 25, 2020 04:27
-
-
Save jdeng/81f64666c61d143d5d3d9fb88f1f64dd to your computer and use it in GitHub Desktop.
Revisions
-
jdeng created this gist
Jul 25, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,166 @@ package main import ( "bytes" "context" "flag" "fmt" // "os" "path" "path/filepath" "time" restic "github.com/restic/restic/cmd/global" ) func splitPath(p string) []string { d, f := path.Split(p) if d == "" || d == "/" { return []string{f} } s := splitPath(path.Clean(d)) return append(s, f) } func getFileBytesFromNode(ctx context.Context, repo restic.Repository, node *restic.Node) ([]byte, error) { var out bytes.Buffer var buf []byte for _, id := range node.Content { size, found := repo.LookupBlobSize(id, restic.DataBlob) if !found { return nil, fmt.Errorf("id %v not found in repository", id) } buf = buf[:cap(buf)] if len(buf) < restic.CiphertextLength(int(size)) { buf = restic.NewBlobBuffer(int(size)) } n, err := repo.LoadBlob(ctx, restic.DataBlob, id, buf) if err != nil { return nil, err } buf = n _, err = out.Write(buf) if err != nil { return nil, err } } return out.Bytes(), nil } func getFileBytes(ctx context.Context, tree *restic.Tree, repo restic.Repository, prefix string, pathComponents []string) ([]byte, error) { if tree == nil { return nil, fmt.Errorf("called with a nil tree") } if repo == nil { return nil, fmt.Errorf("called with a nil repository") } l := len(pathComponents) if l == 0 { return nil, fmt.Errorf("empty path components") } item := filepath.Join(prefix, pathComponents[0]) for _, node := range tree.Nodes { if node.Name == pathComponents[0] { switch { case l == 1 && node.Type == "file": return getFileBytesFromNode(ctx, repo, node) case l > 1 && node.Type == "dir": subtree, err := repo.LoadTree(ctx, *node.Subtree) if err != nil { return nil, err } return getFileBytes(ctx, subtree, repo, item, pathComponents[1:]) case l > 1: return nil, fmt.Errorf("%q should be a dir, but s a %q", item, node.Type) case node.Type != "file": return nil, fmt.Errorf("%q should be a file, but is a %q", item, node.Type) } } } return nil, fmt.Errorf("path %q not found in snapshot", item) } func LoadRepository(ctx context.Context, repository, password, rootPath string) (repo restic.Repository, tree *restic.Tree, err error) { opts := restic.GlobalOptions{Repo: repository, Ctx: ctx} repo, err = OpenRepository(opts, password) if err != nil { return } err = repo.LoadIndex(ctx) if err != nil { return } id, err := restic.FindLatestSnapshot(ctx, repo, []string{rootPath}, []restic.TagList{}, []string{}) if err != nil { return } sn, err := restic.LoadSnapshot(ctx, repo, id) if err != nil { return } tree, err = repo.LoadTree(ctx, *sn.Tree) return } func elapsed(what string) func() { start := time.Now() return func() { fmt.Printf("%s took %v\n", what, time.Since(start)) } } func GetFileBytes(ctx context.Context, repo restic.Repository, tree *restic.Tree, p string) ([]byte, error) { defer elapsed("GetFileBytes: " + p)() prefix := "" splittedPath := splitPath(p) return getFileBytes(ctx, tree, repo, prefix, splittedPath) } const maxKeys = 20 // OpenRepository reads the password and opens the repository. func OpenRepository(opts restic.GlobalOptions, password string) (restic.Repository, error) { be, err := restic.Open(opts, restic.Options{}) if err != nil { return nil, err } s := restic.NewRepository(be) err = s.SearchKey(opts.Ctx, password, maxKeys, opts.KeyHint) if err != nil { return nil, err } c, err := restic.NewCache(s.Config().ID, opts.CacheDir) if err != nil { fmt.Printf("unable to open cache: %v\n", err) return s, nil } // start using the cache s.UseCache(c) return s, nil } var ( repo = flag.String("repo", "", "repository address") password = flag.String("password", "", "password") root = flag.String("root", "", "root path") ) func main() { flag.Parse() r, tree, err := LoadRepository(context.Background(), *repo, *password, *root) fmt.Printf("repo: %v, tree: %v, err: %v\n", r, tree, err) }