From 175c14b5c95e87c98b8a9ef94a379a501bd3f6b2 Mon Sep 17 00:00:00 2001 From: Srigovind Nayak Date: Sun, 4 Feb 2024 14:15:14 +0530 Subject: [PATCH 1/2] dump: add --target option --- changelog/unreleased/issue-4678 | 7 +++++++ cmd/restic/cmd_dump.go | 29 +++++++++++++++++++++++------ doc/050_restore.rst | 7 +++++++ 3 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 changelog/unreleased/issue-4678 diff --git a/changelog/unreleased/issue-4678 b/changelog/unreleased/issue-4678 new file mode 100644 index 000000000..da669fa82 --- /dev/null +++ b/changelog/unreleased/issue-4678 @@ -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 diff --git a/cmd/restic/cmd_dump.go b/cmd/restic/cmd_dump.go index e72b51fa2..cf2f305c5 100644 --- a/cmd/restic/cmd_dump.go +++ b/cmd/restic/cmd_dump.go @@ -46,6 +46,7 @@ Exit status is 0 if the command was successful, and non-zero if there was any er type DumpOptions struct { restic.SnapshotFilter Archive string + Target string } var dumpOptions DumpOptions @@ -56,6 +57,7 @@ func init() { flags := cmdDump.Flags() initSingleSnapshotFilter(flags, &dumpOptions.SnapshotFilter) 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 { @@ -67,11 +69,11 @@ func splitPath(p string) []string { 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 // level in the tree. if pathComponents[0] == "" { - if err := checkStdoutArchive(); err != nil { + if err := checkStdoutArchiveFunc(); err != nil { return err } return d.DumpTree(ctx, tree, "/") @@ -91,9 +93,9 @@ func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.BlobLoade if err != nil { 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): - if err := checkStdoutArchive(); err != nil { + if err := checkStdoutArchiveFunc(); err != nil { return err } 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) } - d := dump.New(opts.Archive, repo, os.Stdout) - err = printFromTree(ctx, tree, repo, "/", splittedPath, d) + var outputFileWriter = os.Stdout + 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 { return errors.Fatalf("cannot dump file: %v", err) } diff --git a/doc/050_restore.rst b/doc/050_restore.rst index 56f6458ed..7ff7f2706 100644 --- a/doc/050_restore.rst +++ b/doc/050_restore.rst @@ -174,3 +174,10 @@ To include the folder content at the root of the archive, you can use the `` 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 \ No newline at end of file From f5ffa40652da68ec1b2010f592827482d3b807c4 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Mon, 5 Feb 2024 20:09:58 +0100 Subject: [PATCH 2/2] dump: minor cleanups --- changelog/unreleased/issue-4678 | 2 +- cmd/restic/cmd_dump.go | 19 ++++++++++--------- doc/050_restore.rst | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/changelog/unreleased/issue-4678 b/changelog/unreleased/issue-4678 index da669fa82..f98711ea4 100644 --- a/changelog/unreleased/issue-4678 +++ b/changelog/unreleased/issue-4678 @@ -1,7 +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. +`--target` file to write the output to. https://github.com/restic/restic/issues/4678 https://github.com/restic/restic/pull/4682 diff --git a/cmd/restic/cmd_dump.go b/cmd/restic/cmd_dump.go index cf2f305c5..1628d6a95 100644 --- a/cmd/restic/cmd_dump.go +++ b/cmd/restic/cmd_dump.go @@ -57,7 +57,7 @@ func init() { flags := cmdDump.Flags() initSingleSnapshotFilter(flags, &dumpOptions.SnapshotFilter) 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") + flags.StringVarP(&dumpOptions.Target, "target", "t", "", "write the output to target `path`") } func splitPath(p string) []string { @@ -69,11 +69,11 @@ func splitPath(p string) []string { return append(s, f) } -func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.BlobLoader, prefix string, pathComponents []string, d *dump.Dumper, checkStdoutArchiveFunc func() error) error { +func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.BlobLoader, prefix string, pathComponents []string, d *dump.Dumper, canWriteArchiveFunc func() error) error { // If we print / we need to assume that there are multiple nodes at that // level in the tree. if pathComponents[0] == "" { - if err := checkStdoutArchiveFunc(); err != nil { + if err := canWriteArchiveFunc(); err != nil { return err } return d.DumpTree(ctx, tree, "/") @@ -93,9 +93,9 @@ func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.BlobLoade if err != nil { return errors.Wrapf(err, "cannot load subtree for %q", item) } - return printFromTree(ctx, subtree, repo, item, pathComponents[1:], d, checkStdoutArchiveFunc) + return printFromTree(ctx, subtree, repo, item, pathComponents[1:], d, canWriteArchiveFunc) case dump.IsDir(node): - if err := checkStdoutArchiveFunc(); err != nil { + if err := canWriteArchiveFunc(); err != nil { return err } subtree, err := restic.LoadTree(ctx, repo, *node.Subtree) @@ -170,8 +170,9 @@ func runDump(ctx context.Context, opts DumpOptions, gopts GlobalOptions, args [] return errors.Fatalf("loading tree for snapshot %q failed: %v", snapshotIDString, err) } - var outputFileWriter = os.Stdout - checkStdoutArchiveFunc := checkStdoutArchive + outputFileWriter := os.Stdout + canWriteArchiveFunc := checkStdoutArchive + if opts.Target != "" { file, err := os.OpenFile(opts.Target, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o666) if err != nil { @@ -182,11 +183,11 @@ func runDump(ctx context.Context, opts DumpOptions, gopts GlobalOptions, args [] }() outputFileWriter = file - checkStdoutArchiveFunc = func() error { return nil } + canWriteArchiveFunc = func() error { return nil } } d := dump.New(opts.Archive, repo, outputFileWriter) - err = printFromTree(ctx, tree, repo, "/", splittedPath, d, checkStdoutArchiveFunc) + err = printFromTree(ctx, tree, repo, "/", splittedPath, d, canWriteArchiveFunc) if err != nil { return errors.Fatalf("cannot dump file: %v", err) } diff --git a/doc/050_restore.rst b/doc/050_restore.rst index 7ff7f2706..3cd05500b 100644 --- a/doc/050_restore.rst +++ b/doc/050_restore.rst @@ -176,8 +176,8 @@ To include the folder content at the root of the archive, you can use the `` 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. +structure to a file using the ``--target`` flag. The ``dump`` command will fail +if the already file exists. .. code-block:: console $ restic -r /srv/restic-repo dump latest / --target /home/linux.user/output.tar -a tar \ No newline at end of file