mirror of
https://github.com/octoleo/restic.git
synced 2024-11-24 05:37:37 +00:00
dump: add --target option
This commit is contained in:
parent
0f09a8870c
commit
175c14b5c9
7
changelog/unreleased/issue-4678
Normal file
7
changelog/unreleased/issue-4678
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Enhancement: Add --target flag to the dump command
|
||||||
|
|
||||||
|
Restic `dump` always printed to the standard output. It now permits to select a
|
||||||
|
`--target` file to print the output.
|
||||||
|
|
||||||
|
https://github.com/restic/restic/issues/4678
|
||||||
|
https://github.com/restic/restic/pull/4682
|
@ -46,6 +46,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er
|
|||||||
type DumpOptions struct {
|
type DumpOptions struct {
|
||||||
restic.SnapshotFilter
|
restic.SnapshotFilter
|
||||||
Archive string
|
Archive string
|
||||||
|
Target string
|
||||||
}
|
}
|
||||||
|
|
||||||
var dumpOptions DumpOptions
|
var dumpOptions DumpOptions
|
||||||
@ -56,6 +57,7 @@ func init() {
|
|||||||
flags := cmdDump.Flags()
|
flags := cmdDump.Flags()
|
||||||
initSingleSnapshotFilter(flags, &dumpOptions.SnapshotFilter)
|
initSingleSnapshotFilter(flags, &dumpOptions.SnapshotFilter)
|
||||||
flags.StringVarP(&dumpOptions.Archive, "archive", "a", "tar", "set archive `format` as \"tar\" or \"zip\"")
|
flags.StringVarP(&dumpOptions.Archive, "archive", "a", "tar", "set archive `format` as \"tar\" or \"zip\"")
|
||||||
|
flags.StringVarP(&dumpOptions.Target, "target", "t", "", "set the target path to dump the archive file")
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitPath(p string) []string {
|
func splitPath(p string) []string {
|
||||||
@ -67,11 +69,11 @@ func splitPath(p string) []string {
|
|||||||
return append(s, f)
|
return append(s, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.BlobLoader, prefix string, pathComponents []string, d *dump.Dumper) error {
|
func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.BlobLoader, prefix string, pathComponents []string, d *dump.Dumper, checkStdoutArchiveFunc func() error) error {
|
||||||
// If we print / we need to assume that there are multiple nodes at that
|
// If we print / we need to assume that there are multiple nodes at that
|
||||||
// level in the tree.
|
// level in the tree.
|
||||||
if pathComponents[0] == "" {
|
if pathComponents[0] == "" {
|
||||||
if err := checkStdoutArchive(); err != nil {
|
if err := checkStdoutArchiveFunc(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return d.DumpTree(ctx, tree, "/")
|
return d.DumpTree(ctx, tree, "/")
|
||||||
@ -91,9 +93,9 @@ func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.BlobLoade
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "cannot load subtree for %q", item)
|
return errors.Wrapf(err, "cannot load subtree for %q", item)
|
||||||
}
|
}
|
||||||
return printFromTree(ctx, subtree, repo, item, pathComponents[1:], d)
|
return printFromTree(ctx, subtree, repo, item, pathComponents[1:], d, checkStdoutArchiveFunc)
|
||||||
case dump.IsDir(node):
|
case dump.IsDir(node):
|
||||||
if err := checkStdoutArchive(); err != nil {
|
if err := checkStdoutArchiveFunc(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
subtree, err := restic.LoadTree(ctx, repo, *node.Subtree)
|
subtree, err := restic.LoadTree(ctx, repo, *node.Subtree)
|
||||||
@ -168,8 +170,23 @@ func runDump(ctx context.Context, opts DumpOptions, gopts GlobalOptions, args []
|
|||||||
return errors.Fatalf("loading tree for snapshot %q failed: %v", snapshotIDString, err)
|
return errors.Fatalf("loading tree for snapshot %q failed: %v", snapshotIDString, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d := dump.New(opts.Archive, repo, os.Stdout)
|
var outputFileWriter = os.Stdout
|
||||||
err = printFromTree(ctx, tree, repo, "/", splittedPath, d)
|
checkStdoutArchiveFunc := checkStdoutArchive
|
||||||
|
if opts.Target != "" {
|
||||||
|
file, err := os.OpenFile(opts.Target, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot dump to file: %w", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = file.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
outputFileWriter = file
|
||||||
|
checkStdoutArchiveFunc = func() error { return nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
d := dump.New(opts.Archive, repo, outputFileWriter)
|
||||||
|
err = printFromTree(ctx, tree, repo, "/", splittedPath, d, checkStdoutArchiveFunc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Fatalf("cannot dump file: %v", err)
|
return errors.Fatalf("cannot dump file: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -174,3 +174,10 @@ To include the folder content at the root of the archive, you can use the ``<sna
|
|||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ restic -r /srv/restic-repo dump latest:/home/other/work / > restore.tar
|
$ restic -r /srv/restic-repo dump latest:/home/other/work / > restore.tar
|
||||||
|
|
||||||
|
It is also possible to ``dump`` the contents of a selected snapshot and folder
|
||||||
|
structure to a file using the ``--target`` flag. The ``dump`` will fail if the
|
||||||
|
file exists.
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
$ restic -r /srv/restic-repo dump latest / --target /home/linux.user/output.tar -a tar
|
Loading…
Reference in New Issue
Block a user