From 101fbefb281e36da2730b0b4a7091a171b9c439f Mon Sep 17 00:00:00 2001 From: Giacomo Cariello Date: Tue, 25 Sep 2018 12:39:24 +0200 Subject: [PATCH] Add support for --prefix and --strip-components options on backup command. --- cmd/restic/cmd_backup.go | 6 +++++ internal/archiver/archiver.go | 4 +++- internal/restic/snapshot.go | 35 +++++++++++++++++++++++++----- internal/restic/snapshot_test.go | 2 +- internal/restic/testing.go | 2 +- internal/restorer/restorer_test.go | 2 +- 6 files changed, 42 insertions(+), 9 deletions(-) diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go index 290a31ba1e1b..2666f0218759 100644 --- a/cmd/restic/cmd_backup.go +++ b/cmd/restic/cmd_backup.go @@ -76,6 +76,8 @@ type BackupOptions struct { FilesFrom string TimeStamp string WithAtime bool + RootPrefix string + RootStrip int } var backupOptions BackupOptions @@ -98,6 +100,8 @@ func init() { f.StringVar(&backupOptions.FilesFrom, "files-from", "", "read the files to backup from file (can be combined with file args)") f.StringVar(&backupOptions.TimeStamp, "time", "", "time of the backup (ex. '2012-11-01 22:08:41') (default: now)") f.BoolVar(&backupOptions.WithAtime, "with-atime", false, "store the atime for all files and directories") + f.StringVar(&backupOptions.RootPrefix, "prefix", "", "apply a prefix to target paths") + f.IntVar(&backupOptions.RootStrip, "strip-components", 0, "strip NUMBER leading components from target paths") } // filterExisting returns a slice of all existing items, or an error if no @@ -484,6 +488,8 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina Time: timeStamp, Hostname: opts.Hostname, ParentSnapshot: *parentSnapshotID, + RootPrefix: opts.RootPrefix, + RootStrip: opts.RootStrip, } uploader := archiver.IndexUploader{ diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go index 4ce9ef597a5d..3816b2cb701c 100644 --- a/internal/archiver/archiver.go +++ b/internal/archiver/archiver.go @@ -705,6 +705,8 @@ type SnapshotOptions struct { Excludes []string Time time.Time ParentSnapshot restic.ID + RootPrefix string + RootStrip int } // loadParentTree loads a tree referenced by snapshot id. If id is null, nil is returned. @@ -807,7 +809,7 @@ func (arch *Archiver) Snapshot(ctx context.Context, targets []string, opts Snaps return nil, restic.ID{}, err } - sn, err := restic.NewSnapshot(targets, opts.Tags, opts.Hostname, opts.Time) + sn, err := restic.NewSnapshot(targets, opts.Tags, opts.Hostname, opts.Time, opts.RootPrefix, opts.RootStrip) sn.Excludes = opts.Excludes if !opts.ParentSnapshot.IsNull() { id := opts.ParentSnapshot diff --git a/internal/restic/snapshot.go b/internal/restic/snapshot.go index 61467013aa5f..b470bf22a2f3 100644 --- a/internal/restic/snapshot.go +++ b/internal/restic/snapshot.go @@ -27,17 +27,42 @@ type Snapshot struct { id *ID // plaintext ID, used during restore } +func pathSplit(path string) (root string, lst []string) { + for root = filepath.Clean(path); ; { + var file string + root, file = filepath.Split(root) + if file == "" { + break + } + lst = append([]string{file}, lst...) + } + return +} + // NewSnapshot returns an initialized snapshot struct for the current user and // time. -func NewSnapshot(paths []string, tags []string, hostname string, time time.Time) (*Snapshot, error) { +func NewSnapshot(paths []string, tags []string, hostname string, time time.Time, prefix string, strip int) (*Snapshot, error) { absPaths := make([]string, 0, len(paths)) for _, path := range paths { - p, err := filepath.Abs(path) - if err == nil { - absPaths = append(absPaths, p) + pathRoot, pathList := pathSplit(path) + if strip > 0 { + var stripRoot int + if pathRoot != "" { + stripRoot++ + } + pathList = pathList[strip-stripRoot:] + pathRoot = "" + } + if prefix != "" { + pathRoot = prefix } else { - absPaths = append(absPaths, path) + absPath, err := filepath.Abs(pathRoot) + if err == nil { + pathRoot = absPath + } } + p := filepath.Join(append([]string{pathRoot}, pathList...)...) + absPaths = append(absPaths, p) } sn := &Snapshot{ diff --git a/internal/restic/snapshot_test.go b/internal/restic/snapshot_test.go index 5e1bf8822602..6c6384e24eb9 100644 --- a/internal/restic/snapshot_test.go +++ b/internal/restic/snapshot_test.go @@ -11,6 +11,6 @@ import ( func TestNewSnapshot(t *testing.T) { paths := []string{"/home/foobar"} - _, err := restic.NewSnapshot(paths, nil, "foo", time.Now()) + _, err := restic.NewSnapshot(paths, nil, "foo", time.Now(), "", 0) rtest.OK(t, err) } diff --git a/internal/restic/testing.go b/internal/restic/testing.go index c0d1684f8d60..576711a332fc 100644 --- a/internal/restic/testing.go +++ b/internal/restic/testing.go @@ -164,7 +164,7 @@ func TestCreateSnapshot(t testing.TB, repo Repository, at time.Time, depth int, t.Logf("create fake snapshot at %s with seed %d", at, seed) fakedir := fmt.Sprintf("fakedir-at-%v", at.Format("2006-01-02 15:04:05")) - snapshot, err := NewSnapshot([]string{fakedir}, []string{"test"}, "foo", time.Now()) + snapshot, err := NewSnapshot([]string{fakedir}, []string{"test"}, "foo", time.Now(), "", 0) if err != nil { t.Fatal(err) } diff --git a/internal/restorer/restorer_test.go b/internal/restorer/restorer_test.go index c5fdd6cb8a66..049161db095e 100644 --- a/internal/restorer/restorer_test.go +++ b/internal/restorer/restorer_test.go @@ -107,7 +107,7 @@ func saveSnapshot(t testing.TB, repo restic.Repository, snapshot Snapshot) (*res t.Fatal(err) } - sn, err := restic.NewSnapshot([]string{"test"}, nil, "", time.Now()) + sn, err := restic.NewSnapshot([]string{"test"}, nil, "", time.Now(), "", 0) if err != nil { t.Fatal(err) }