feat: Add restore command which combines behaviour of find and recovery restore commands

This commit is contained in:
Felicitas Pojtinger
2021-11-29 22:41:18 +01:00
parent 5f7136f625
commit 1d9b756752
2 changed files with 199 additions and 54 deletions

View File

@@ -34,74 +34,95 @@ var recoveryRestoreCmd = &cobra.Command{
boil.DebugMode = true
}
f, isRegular, err := openTapeReadOnly(viper.GetString(tapeFlag))
if err != nil {
return err
}
defer f.Close()
return restoreFromRecordAndBlock(
viper.GetString(tapeFlag),
viper.GetInt(recordSizeFlag),
viper.GetInt(recordFlag),
viper.GetInt(blockFlag),
viper.GetString(dstFlag),
viper.GetBool(previewFlag),
true,
)
},
}
var tr *tar.Reader
if isRegular {
// Seek to record and block
if _, err := f.Seek(int64((viper.GetInt(recordSizeFlag)*controllers.BlockSize*viper.GetInt(recordFlag))+viper.GetInt(blockFlag)*controllers.BlockSize), io.SeekStart); err != nil {
return err
}
func restoreFromRecordAndBlock(
tape string,
recordSize int,
record int,
block int,
dst string,
preview bool,
showHeader bool,
) error {
f, isRegular, err := openTapeReadOnly(tape)
if err != nil {
return err
}
defer f.Close()
tr = tar.NewReader(f)
} else {
// Seek to record
if err := controllers.SeekToRecordOnTape(f, int32(viper.GetInt(recordFlag))); err != nil {
return err
}
// Seek to block
br := bufio.NewReaderSize(f, controllers.BlockSize*viper.GetInt(recordSizeFlag))
if _, err := br.Read(make([]byte, viper.GetInt(blockFlag)*controllers.BlockSize)); err != nil {
return err
}
tr = tar.NewReader(br)
}
hdr, err := tr.Next()
if err != nil {
var tr *tar.Reader
if isRegular {
// Seek to record and block
if _, err := f.Seek(int64((recordSize*controllers.BlockSize*record)+block*controllers.BlockSize), io.SeekStart); err != nil {
return err
}
tr = tar.NewReader(f)
} else {
// Seek to record
if err := controllers.SeekToRecordOnTape(f, int32(record)); err != nil {
return err
}
// Seek to block
br := bufio.NewReaderSize(f, controllers.BlockSize*recordSize)
if _, err := br.Read(make([]byte, block*controllers.BlockSize)); err != nil {
return err
}
tr = tar.NewReader(br)
}
hdr, err := tr.Next()
if err != nil {
return err
}
if showHeader {
if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil {
return err
}
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(int64(viper.GetInt(recordFlag)), int64(viper.GetInt(blockFlag)), hdr)); err != nil {
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(int64(record), int64(block), hdr)); err != nil {
return err
}
}
if !preview {
if dst == "" {
dst = filepath.Base(hdr.Name)
}
if hdr.Typeflag == tar.TypeDir {
return os.MkdirAll(dst, hdr.FileInfo().Mode())
}
dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE, hdr.FileInfo().Mode())
if err != nil {
return err
}
if !viper.GetBool(previewFlag) {
dst := viper.GetString(dstFlag)
if dst == "" {
dst = filepath.Base(hdr.Name)
}
if hdr.Typeflag == tar.TypeDir {
return os.MkdirAll(dst, hdr.FileInfo().Mode())
}
dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE, hdr.FileInfo().Mode())
if err != nil {
return err
}
if err := dstFile.Truncate(0); err != nil {
return err
}
if _, err := io.Copy(dstFile, tr); err != nil {
return err
}
if err := dstFile.Truncate(0); err != nil {
return err
}
return nil
},
if _, err := io.Copy(dstFile, tr); err != nil {
return err
}
}
return nil
}
func init() {

124
cmd/stbak/cmd/restore.go Normal file
View File

@@ -0,0 +1,124 @@
package cmd
import (
"archive/tar"
"context"
"database/sql"
"path"
"path/filepath"
"strings"
"github.com/pojntfx/stfs/pkg/converters"
models "github.com/pojntfx/stfs/pkg/db/sqlite/models/metadata"
"github.com/pojntfx/stfs/pkg/formatting"
"github.com/pojntfx/stfs/pkg/persisters"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/volatiletech/sqlboiler/v4/boil"
)
const (
flattenFlag = "flatten"
)
var restoreCmd = &cobra.Command{
Use: "restore",
Aliases: []string{"r"},
Short: "Restore a file or directory",
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
}
if viper.GetBool(verboseFlag) {
boil.DebugMode = true
}
metadataPersister := persisters.NewMetadataPersister(viper.GetString(metadataFlag))
if err := metadataPersister.Open(); err != nil {
return err
}
headersToRestore := []*models.Header{}
src := strings.TrimSuffix(viper.GetString(srcFlag), "/")
dbhdr, err := metadataPersister.GetHeader(context.Background(), src)
if err != nil {
if err == sql.ErrNoRows {
src = src + "/"
dbhdr, err = metadataPersister.GetHeader(context.Background(), src)
if err != nil {
return err
}
} else {
return err
}
}
headersToRestore = append(headersToRestore, dbhdr)
// If the header refers to a directory, get it's children
if dbhdr.Typeflag == tar.TypeDir {
dbhdrs, err := metadataPersister.GetHeaderChildren(context.Background(), src)
if err != nil {
return err
}
headersToRestore = append(headersToRestore, dbhdrs...)
}
for i, dbhdr := range headersToRestore {
if i == 0 {
if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil {
return err
}
}
hdr, err := converters.DBHeaderToTarHeader(dbhdr)
if err != nil {
return err
}
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(dbhdr.Record, dbhdr.Block, hdr)); err != nil {
return err
}
dst := dbhdr.Name
if viper.GetString(dstFlag) != "" {
if viper.GetBool(flattenFlag) {
dst = viper.GetString(dstFlag)
} else {
dst = filepath.Join(viper.GetString(dstFlag), strings.TrimPrefix(dst, viper.GetString(srcFlag)))
if strings.TrimSuffix(dst, "/") == strings.TrimSuffix(viper.GetString(dstFlag), "/") {
dst = filepath.Join(dst, path.Base(dbhdr.Name)) // Append the name so we don't overwrite
}
}
}
if err := restoreFromRecordAndBlock(
viper.GetString(tapeFlag),
viper.GetInt(recordSizeFlag),
int(dbhdr.Record),
int(dbhdr.Block),
dst,
false,
false,
); err != nil {
return err
}
}
return nil
},
}
func init() {
restoreCmd.PersistentFlags().IntP(recordSizeFlag, "e", 20, "Amount of 512-bit blocks per record")
restoreCmd.PersistentFlags().StringP(srcFlag, "s", "", "File or directory to restore")
restoreCmd.PersistentFlags().StringP(dstFlag, "d", "", "File or directory restore to (archived name by default)")
restoreCmd.PersistentFlags().BoolP(flattenFlag, "f", false, "Ignore the folder hierarchy on the tape or tar file")
viper.AutomaticEnv()
rootCmd.AddCommand(restoreCmd)
}