diff --git a/cmd/stbak/cmd/recovery_query.go b/cmd/stbak/cmd/recovery_query.go index 7a4fdfd..40085f9 100644 --- a/cmd/stbak/cmd/recovery_query.go +++ b/cmd/stbak/cmd/recovery_query.go @@ -1,19 +1,9 @@ package cmd import ( - "archive/tar" - "bufio" - "io" - "io/ioutil" - "math" - - "github.com/pojntfx/stfs/internal/controllers" - "github.com/pojntfx/stfs/internal/counters" - "github.com/pojntfx/stfs/internal/encryption" - "github.com/pojntfx/stfs/internal/formatting" "github.com/pojntfx/stfs/internal/keys" - "github.com/pojntfx/stfs/internal/signature" - "github.com/pojntfx/stfs/internal/tape" + "github.com/pojntfx/stfs/pkg/config" + "github.com/pojntfx/stfs/pkg/recovery" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/volatiletech/sqlboiler/v4/boil" @@ -62,220 +52,33 @@ var recoveryQueryCmd = &cobra.Command{ return err } - return query( - viper.GetString(driveFlag), + if _, err := recovery.Query( + config.StateConfig{ + Drive: viper.GetString(driveFlag), + Metadata: viper.GetString(metadataFlag), + }, + config.PipeConfig{ + Compression: viper.GetString(compressionFlag), + Encryption: viper.GetString(encryptionFlag), + Signature: viper.GetString(signatureFlag), + }, + config.CryptoConfig{ + Recipient: recipient, + Identity: identity, + Password: viper.GetString(passwordFlag), + }, + + viper.GetInt(recordSizeFlag), viper.GetInt(recordFlag), viper.GetInt(blockFlag), - viper.GetInt(recordSizeFlag), - viper.GetString(encryptionFlag), - identity, - viper.GetString(signatureFlag), - recipient, - ) + ); err != nil { + return err + } + + return nil }, } -func query( - drive string, - record int, - block int, - recordSize int, - encryptionFormat string, - identity interface{}, - signatureFormat string, - recipient interface{}, -) error { - f, isRegular, err := tape.OpenTapeReadOnly(drive) - if err != nil { - return err - } - defer f.Close() - - if isRegular { - // Seek to record and block - if _, err := f.Seek(int64((recordSize*controllers.BlockSize*record)+block*controllers.BlockSize), 0); err != nil { - return err - } - - tr := tar.NewReader(f) - - record := int64(record) - block := int64(block) - - for { - hdr, err := tr.Next() - if err != nil { - for { - curr, err := f.Seek(0, io.SeekCurrent) - if err != nil { - return err - } - - nextTotalBlocks := math.Ceil(float64((curr)) / float64(controllers.BlockSize)) - record = int64(nextTotalBlocks) / int64(recordSize) - block = int64(nextTotalBlocks) - (record * int64(recordSize)) - - if block < 0 { - record-- - block = int64(recordSize) - 1 - } else if block >= int64(recordSize) { - record++ - block = 0 - } - - // Seek to record and block - if _, err := f.Seek(int64((recordSize*controllers.BlockSize*int(record))+int(block)*controllers.BlockSize), io.SeekStart); err != nil { - return err - } - - tr = tar.NewReader(f) - - hdr, err = tr.Next() - if err != nil { - if err == io.EOF { - // EOF - - break - } - - continue - } - - break - } - } - - if hdr == nil { - // EOF - - break - } - - if err := encryption.DecryptHeader(hdr, encryptionFormat, identity); err != nil { - return err - } - - if err := signature.VerifyHeader(hdr, isRegular, signatureFormat, recipient); err != nil { - return err - } - - if record == 0 && block == 0 { - if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil { - return err - } - } - - if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(record, block, hdr)); err != nil { - return err - } - - curr, err := f.Seek(0, io.SeekCurrent) - if err != nil { - return err - } - - if _, err := io.Copy(ioutil.Discard, tr); err != nil { - return err - } - - currAndSize, err := f.Seek(0, io.SeekCurrent) - if err != nil { - return err - } - - nextTotalBlocks := math.Ceil(float64(curr+(currAndSize-curr)) / float64(controllers.BlockSize)) - record = int64(nextTotalBlocks) / int64(recordSize) - block = int64(nextTotalBlocks) - (record * int64(recordSize)) - - if block > int64(recordSize) { - record++ - block = 0 - } - } - } 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 - } - - record := int64(record) - block := int64(block) - - curr := int64((recordSize * controllers.BlockSize * int(record)) + (int(block) * controllers.BlockSize)) - counter := &counters.CounterReader{Reader: br, BytesRead: int(curr)} - - tr := tar.NewReader(counter) - for { - hdr, err := tr.Next() - if err != nil { - if err == io.EOF { - if err := controllers.GoToNextFileOnTape(f); err != nil { - // EOD - - break - } - - record, err = controllers.GetCurrentRecordFromTape(f) - if err != nil { - return err - } - block = 0 - - br = bufio.NewReaderSize(f, controllers.BlockSize*recordSize) - curr := int64(int64(recordSize) * controllers.BlockSize * record) - counter := &counters.CounterReader{Reader: br, BytesRead: int(curr)} - tr = tar.NewReader(counter) - - continue - } else { - return err - } - } - - if err := encryption.DecryptHeader(hdr, encryptionFormat, identity); err != nil { - return err - } - - if err := signature.VerifyHeader(hdr, isRegular, signatureFormat, recipient); err != nil { - return err - } - - if record == 0 && block == 0 { - if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil { - return err - } - } - - if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(record, block, hdr)); err != nil { - return err - } - - if _, err := io.Copy(ioutil.Discard, tr); err != nil { - return err - } - - currAndSize := int64(counter.BytesRead) - - nextTotalBlocks := math.Ceil(float64(curr+(currAndSize-curr)) / float64(controllers.BlockSize)) - record = int64(nextTotalBlocks) / int64(recordSize) - block = int64(nextTotalBlocks) - (record * int64(recordSize)) - - if block > int64(recordSize) { - record++ - block = 0 - } - } - } - - return nil -} - func init() { recoveryQueryCmd.PersistentFlags().IntP(recordSizeFlag, "z", 20, "Amount of 512-bit blocks per record") recoveryQueryCmd.PersistentFlags().IntP(recordFlag, "k", 0, "Record to seek too before counting") diff --git a/pkg/recovery/query.go b/pkg/recovery/query.go index cc6612b..29a78e6 100644 --- a/pkg/recovery/query.go +++ b/pkg/recovery/query.go @@ -2,7 +2,17 @@ package recovery import ( "archive/tar" + "bufio" + "io" + "io/ioutil" + "math" + "github.com/pojntfx/stfs/internal/controllers" + "github.com/pojntfx/stfs/internal/counters" + "github.com/pojntfx/stfs/internal/encryption" + "github.com/pojntfx/stfs/internal/formatting" + "github.com/pojntfx/stfs/internal/signature" + "github.com/pojntfx/stfs/internal/tape" "github.com/pojntfx/stfs/pkg/config" ) @@ -15,5 +25,199 @@ func Query( record int, block int, ) ([]*tar.Header, error) { - return nil, nil + f, isRegular, err := tape.OpenTapeReadOnly(state.Drive) + if err != nil { + return []*tar.Header{}, err + } + defer f.Close() + + headers := []*tar.Header{} + + if isRegular { + // Seek to record and block + if _, err := f.Seek(int64((recordSize*controllers.BlockSize*record)+block*controllers.BlockSize), 0); err != nil { + return []*tar.Header{}, err + } + + tr := tar.NewReader(f) + + record := int64(record) + block := int64(block) + + for { + hdr, err := tr.Next() + if err != nil { + for { + curr, err := f.Seek(0, io.SeekCurrent) + if err != nil { + return []*tar.Header{}, err + } + + nextTotalBlocks := math.Ceil(float64((curr)) / float64(controllers.BlockSize)) + record = int64(nextTotalBlocks) / int64(recordSize) + block = int64(nextTotalBlocks) - (record * int64(recordSize)) + + if block < 0 { + record-- + block = int64(recordSize) - 1 + } else if block >= int64(recordSize) { + record++ + block = 0 + } + + // Seek to record and block + if _, err := f.Seek(int64((recordSize*controllers.BlockSize*int(record))+int(block)*controllers.BlockSize), io.SeekStart); err != nil { + return []*tar.Header{}, err + } + + tr = tar.NewReader(f) + + hdr, err = tr.Next() + if err != nil { + if err == io.EOF { + // EOF + + break + } + + continue + } + + break + } + } + + if hdr == nil { + // EOF + + break + } + + if err := encryption.DecryptHeader(hdr, pipes.Encryption, crypto.Identity); err != nil { + return []*tar.Header{}, err + } + + if err := signature.VerifyHeader(hdr, isRegular, pipes.Signature, crypto.Recipient); err != nil { + return []*tar.Header{}, err + } + + if record == 0 && block == 0 { + if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil { + return []*tar.Header{}, err + } + } + + if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(record, block, hdr)); err != nil { + return []*tar.Header{}, err + } + + headers = append(headers, hdr) + + curr, err := f.Seek(0, io.SeekCurrent) + if err != nil { + return []*tar.Header{}, err + } + + if _, err := io.Copy(ioutil.Discard, tr); err != nil { + return []*tar.Header{}, err + } + + currAndSize, err := f.Seek(0, io.SeekCurrent) + if err != nil { + return []*tar.Header{}, err + } + + nextTotalBlocks := math.Ceil(float64(curr+(currAndSize-curr)) / float64(controllers.BlockSize)) + record = int64(nextTotalBlocks) / int64(recordSize) + block = int64(nextTotalBlocks) - (record * int64(recordSize)) + + if block > int64(recordSize) { + record++ + block = 0 + } + } + } else { + // Seek to record + if err := controllers.SeekToRecordOnTape(f, int32(record)); err != nil { + return []*tar.Header{}, err + } + + // Seek to block + br := bufio.NewReaderSize(f, controllers.BlockSize*recordSize) + if _, err := br.Read(make([]byte, block*controllers.BlockSize)); err != nil { + return []*tar.Header{}, err + } + + record := int64(record) + block := int64(block) + + curr := int64((recordSize * controllers.BlockSize * int(record)) + (int(block) * controllers.BlockSize)) + counter := &counters.CounterReader{Reader: br, BytesRead: int(curr)} + + tr := tar.NewReader(counter) + for { + hdr, err := tr.Next() + if err != nil { + if err == io.EOF { + if err := controllers.GoToNextFileOnTape(f); err != nil { + // EOD + + break + } + + record, err = controllers.GetCurrentRecordFromTape(f) + if err != nil { + return []*tar.Header{}, err + } + block = 0 + + br = bufio.NewReaderSize(f, controllers.BlockSize*recordSize) + curr := int64(int64(recordSize) * controllers.BlockSize * record) + counter := &counters.CounterReader{Reader: br, BytesRead: int(curr)} + tr = tar.NewReader(counter) + + continue + } else { + return []*tar.Header{}, err + } + } + + if err := encryption.DecryptHeader(hdr, pipes.Encryption, crypto.Identity); err != nil { + return []*tar.Header{}, err + } + + if err := signature.VerifyHeader(hdr, isRegular, pipes.Signature, crypto.Recipient); err != nil { + return []*tar.Header{}, err + } + + if record == 0 && block == 0 { + if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil { + return []*tar.Header{}, err + } + } + + if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(record, block, hdr)); err != nil { + return []*tar.Header{}, err + } + + headers = append(headers, hdr) + + if _, err := io.Copy(ioutil.Discard, tr); err != nil { + return []*tar.Header{}, err + } + + currAndSize := int64(counter.BytesRead) + + nextTotalBlocks := math.Ceil(float64(curr+(currAndSize-curr)) / float64(controllers.BlockSize)) + record = int64(nextTotalBlocks) / int64(recordSize) + block = int64(nextTotalBlocks) - (record * int64(recordSize)) + + if block > int64(recordSize) { + record++ + block = 0 + } + } + } + + return headers, nil }