feat: Start implementation of metadata encryption

This commit is contained in:
Felicitas Pojtinger
2021-12-02 11:26:18 +01:00
parent daa62e25b5
commit f4dc9f9f52
8 changed files with 613 additions and 289 deletions

View File

@@ -2,8 +2,10 @@ package cmd
import (
"archive/tar"
"bytes"
"compress/gzip"
"context"
"encoding/base32"
"errors"
"fmt"
"io"
@@ -35,7 +37,9 @@ const (
srcFlag = "src"
overwriteFlag = "overwrite"
compressionLevelFlag = "compression-level"
keyFlag = "key"
recipientFlag = "recipient"
identityFlag = "identity"
compressionLevelFastest = "fastest"
compressionLevelBalanced = "balanced"
@@ -48,7 +52,8 @@ var (
errUnknownCompressionLevel = errors.New("unknown compression level")
errUnsupportedCompressionLevel = errors.New("unsupported compression level")
errKeyNotAccessible = errors.New("key not found or accessible")
errRecipientNotAccessible = errors.New("recipient/public key not found or accessible")
errIdentityNotAccessible = errors.New("identity/private key not found or accessible")
)
type flusher interface {
@@ -91,8 +96,12 @@ var archiveCmd = &cobra.Command{
}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
if _, err := os.Stat(viper.GetString(keyFlag)); err != nil {
return errKeyNotAccessible
if _, err := os.Stat(viper.GetString(recipientFlag)); err != nil {
return errRecipientNotAccessible
}
if _, err := os.Stat(viper.GetString(identityFlag)); err != nil {
return errIdentityNotAccessible
}
}
@@ -121,13 +130,19 @@ var archiveCmd = &cobra.Command{
}
pubkey := []byte{}
privkey := []byte{}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
p, err := ioutil.ReadFile(viper.GetString(keyFlag))
p, err := ioutil.ReadFile(viper.GetString(recipientFlag))
if err != nil {
return err
}
pubkey = p
privkey, err = ioutil.ReadFile(viper.GetString(identityFlag))
if err != nil {
return err
}
}
if err := archive(
@@ -152,6 +167,7 @@ var archiveCmd = &cobra.Command{
viper.GetBool(overwriteFlag),
viper.GetString(compressionFlag),
viper.GetString(encryptionFlag),
privkey,
)
},
}
@@ -327,6 +343,10 @@ func archive(
return err
}
if err := encryptHeader(hdr, encryptionFormat, pubkey); err != nil {
return err
}
if err := tw.WriteHeader(hdr); err != nil {
return err
}
@@ -406,6 +426,21 @@ func checkCompressionLevel(compressionLevel string) error {
return nil
}
func encryptHeader(
hdr *tar.Header,
encryptionFormat string,
pubkey []byte,
) error {
var err error
hdr.Name, err = encryptString(hdr.Name, encryptionFormat, pubkey)
if err != nil {
return err
}
return nil
}
func addSuffix(name string, compressionFormat string, encryptionFormat string) (string, error) {
switch compressionFormat {
case compressionFormatGZipKey:
@@ -458,6 +493,40 @@ func encrypt(
}
}
func encryptString(
src string,
encryptionFormat string,
pubkey []byte,
) (string, error) {
switch encryptionFormat {
case encryptionFormatAgeKey:
recipient, err := age.ParseX25519Recipient(string(pubkey))
if err != nil {
return "", err
}
out := &bytes.Buffer{}
w, err := age.Encrypt(out, recipient)
if err != nil {
return "", err
}
if _, err := io.WriteString(w, src); err != nil {
return "", err
}
if err := w.Close(); err != nil {
return "", err
}
return base32.StdEncoding.EncodeToString(out.Bytes()), nil
case encryptionFormatNoneKey:
return src, nil
default:
return "", errUnsupportedEncryptionFormat
}
}
func compress(
dst io.Writer,
compressionFormat string,
@@ -587,7 +656,8 @@ func init() {
archiveCmd.PersistentFlags().StringP(srcFlag, "s", ".", "File or directory to archive")
archiveCmd.PersistentFlags().BoolP(overwriteFlag, "o", false, "Start writing from the start instead of from the end of the tape or tar file")
archiveCmd.PersistentFlags().StringP(compressionLevelFlag, "l", compressionLevelBalanced, fmt.Sprintf("Compression level to use (default %v, available are %v)", compressionLevelBalanced, knownCompressionLevels))
archiveCmd.PersistentFlags().StringP(keyFlag, "k", "", "Path to public key of recipient to encrypt for")
archiveCmd.PersistentFlags().StringP(recipientFlag, "r", "", "Path to public key of recipient to encrypt for")
archiveCmd.PersistentFlags().StringP(identityFlag, "i", "", "Path to private key of recipient that has been encrypted for")
viper.AutomaticEnv()

View File

@@ -4,6 +4,7 @@ import (
"archive/tar"
"bufio"
"context"
"io/ioutil"
"os"
"github.com/pojntfx/stfs/pkg/controllers"
@@ -26,6 +27,19 @@ var deleteCmd = &cobra.Command{
Use: "delete",
Aliases: []string{"del", "d", "rm"},
Short: "Delete a file or directory from tape or tar file",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
if _, err := os.Stat(viper.GetString(recipientFlag)); err != nil {
return errRecipientNotAccessible
}
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
@@ -35,70 +49,100 @@ var deleteCmd = &cobra.Command{
boil.DebugMode = true
}
dirty := false
tw, _, cleanup, err := openTapeWriter(viper.GetString(tapeFlag))
if err != nil {
return err
}
defer cleanup(&dirty)
metadataPersister := persisters.NewMetadataPersister(viper.GetString(metadataFlag))
if err := metadataPersister.Open(); err != nil {
return err
}
headersToDelete := []*models.Header{}
dbhdr, err := metadataPersister.GetHeader(context.Background(), viper.GetString(nameFlag))
if err != nil {
return err
}
headersToDelete = append(headersToDelete, dbhdr)
// If the header refers to a directory, get it's children
if dbhdr.Typeflag == tar.TypeDir {
dbhdrs, err := metadataPersister.GetHeaderChildren(context.Background(), viper.GetString(nameFlag))
pubkey := []byte{}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
p, err := ioutil.ReadFile(viper.GetString(recipientFlag))
if err != nil {
return err
}
headersToDelete = append(headersToDelete, dbhdrs...)
pubkey = p
}
// Remove the headers from the index
if err := metadataPersister.DeleteHeaders(context.Background(), headersToDelete); err != nil {
return nil
}
if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil {
return err
}
// Append deletion headers to the tape or tar file
for _, dbhdr := range headersToDelete {
hdr, err := converters.DBHeaderToTarHeader(dbhdr)
if err != nil {
return err
}
hdr.Size = 0 // Don't try to seek after the record
hdr.PAXRecords[pax.STFSRecordVersion] = pax.STFSRecordVersion1
hdr.PAXRecords[pax.STFSRecordAction] = pax.STFSRecordActionDelete
if err := tw.WriteHeader(hdr); err != nil {
return err
}
dirty = true
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(-1, -1, hdr)); err != nil {
return err
}
}
return nil
return delete(
viper.GetString(tapeFlag),
viper.GetString(metadataFlag),
viper.GetString(nameFlag),
viper.GetString(encryptionFlag),
pubkey,
)
},
}
func delete(
tape string,
metadata string,
name string,
encryptionFormat string,
pubkey []byte,
) error {
dirty := false
tw, _, cleanup, err := openTapeWriter(tape)
if err != nil {
return err
}
defer cleanup(&dirty)
metadataPersister := persisters.NewMetadataPersister(metadata)
if err := metadataPersister.Open(); err != nil {
return err
}
headersToDelete := []*models.Header{}
dbhdr, err := metadataPersister.GetHeader(context.Background(), name)
if err != nil {
return err
}
headersToDelete = append(headersToDelete, dbhdr)
// If the header refers to a directory, get it's children
if dbhdr.Typeflag == tar.TypeDir {
dbhdrs, err := metadataPersister.GetHeaderChildren(context.Background(), name)
if err != nil {
return err
}
headersToDelete = append(headersToDelete, dbhdrs...)
}
// Remove the headers from the index
if err := metadataPersister.DeleteHeaders(context.Background(), headersToDelete); err != nil {
return nil
}
if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil {
return err
}
// Append deletion headers to the tape or tar file
for _, dbhdr := range headersToDelete {
hdr, err := converters.DBHeaderToTarHeader(dbhdr)
if err != nil {
return err
}
hdr.Size = 0 // Don't try to seek after the record
hdr.PAXRecords[pax.STFSRecordVersion] = pax.STFSRecordVersion1
hdr.PAXRecords[pax.STFSRecordAction] = pax.STFSRecordActionDelete
if err := encryptHeader(hdr, encryptionFormat, pubkey); err != nil {
return err
}
if err := tw.WriteHeader(hdr); err != nil {
return err
}
dirty = true
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(-1, -1, hdr)); err != nil {
return err
}
}
return nil
}
func openTapeWriter(tape string) (tw *tar.Writer, isRegular bool, cleanup func(dirty *bool) error, err error) {
stat, err := os.Stat(tape)
if err == nil {
@@ -169,6 +213,7 @@ func openTapeWriter(tape string) (tw *tar.Writer, isRegular bool, cleanup func(d
func init() {
deleteCmd.PersistentFlags().IntP(recordSizeFlag, "z", 20, "Amount of 512-bit blocks per record")
deleteCmd.PersistentFlags().StringP(nameFlag, "n", "", "Name of the file to remove")
deleteCmd.PersistentFlags().StringP(recipientFlag, "r", "", "Path to public key of recipient to encrypt for")
viper.AutomaticEnv()

View File

@@ -3,6 +3,8 @@ package cmd
import (
"archive/tar"
"context"
"io/ioutil"
"os"
"strings"
"github.com/pojntfx/stfs/pkg/converters"
@@ -19,6 +21,19 @@ var moveCmd = &cobra.Command{
Use: "move",
Aliases: []string{"mov", "m", "mv"},
Short: "Move a file or directory on tape or tar file",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
if _, err := os.Stat(viper.GetString(recipientFlag)); err != nil {
return errRecipientNotAccessible
}
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
@@ -28,76 +43,109 @@ var moveCmd = &cobra.Command{
boil.DebugMode = true
}
dirty := false
tw, _, cleanup, err := openTapeWriter(viper.GetString(tapeFlag))
if err != nil {
return err
}
defer cleanup(&dirty)
metadataPersister := persisters.NewMetadataPersister(viper.GetString(metadataFlag))
if err := metadataPersister.Open(); err != nil {
return err
}
headersToMove := []*models.Header{}
dbhdr, err := metadataPersister.GetHeader(context.Background(), viper.GetString(srcFlag))
if err != nil {
return err
}
headersToMove = append(headersToMove, dbhdr)
// If the header refers to a directory, get it's children
if dbhdr.Typeflag == tar.TypeDir {
dbhdrs, err := metadataPersister.GetHeaderChildren(context.Background(), viper.GetString(srcFlag))
pubkey := []byte{}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
p, err := ioutil.ReadFile(viper.GetString(recipientFlag))
if err != nil {
return err
}
headersToMove = append(headersToMove, dbhdrs...)
pubkey = p
}
// Move the headers in the index
if err := metadataPersister.MoveHeaders(context.Background(), headersToMove, viper.GetString(srcFlag), viper.GetString(dstFlag)); err != nil {
return nil
}
if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil {
return err
}
// Append move headers to the tape or tar file
for _, dbhdr := range headersToMove {
hdr, err := converters.DBHeaderToTarHeader(dbhdr)
if err != nil {
return err
}
hdr.Size = 0 // Don't try to seek after the record
hdr.Name = strings.TrimSuffix(viper.GetString(dstFlag), "/") + strings.TrimPrefix(hdr.Name, strings.TrimSuffix(viper.GetString(srcFlag), "/"))
hdr.PAXRecords[pax.STFSRecordVersion] = pax.STFSRecordVersion1
hdr.PAXRecords[pax.STFSRecordAction] = pax.STFSRecordActionUpdate
hdr.PAXRecords[pax.STFSRecordReplacesName] = dbhdr.Name
if err := tw.WriteHeader(hdr); err != nil {
return err
}
dirty = true
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(-1, -1, hdr)); err != nil {
return err
}
}
return nil
return move(
viper.GetString(tapeFlag),
viper.GetString(metadataFlag),
viper.GetString(srcFlag),
viper.GetString(dstFlag),
viper.GetString(encryptionFlag),
pubkey,
)
},
}
func move(
tape string,
metadata string,
src string,
dst string,
encryptionFormat string,
pubkey []byte,
) error {
dirty := false
tw, _, cleanup, err := openTapeWriter(tape)
if err != nil {
return err
}
defer cleanup(&dirty)
metadataPersister := persisters.NewMetadataPersister(metadata)
if err := metadataPersister.Open(); err != nil {
return err
}
headersToMove := []*models.Header{}
dbhdr, err := metadataPersister.GetHeader(context.Background(), src)
if err != nil {
return err
}
headersToMove = append(headersToMove, 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
}
headersToMove = append(headersToMove, dbhdrs...)
}
// Move the headers in the index
if err := metadataPersister.MoveHeaders(context.Background(), headersToMove, src, dst); err != nil {
return nil
}
if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil {
return err
}
// Append move headers to the tape or tar file
for _, dbhdr := range headersToMove {
hdr, err := converters.DBHeaderToTarHeader(dbhdr)
if err != nil {
return err
}
hdr.Size = 0 // Don't try to seek after the record
hdr.Name = strings.TrimSuffix(dst, "/") + strings.TrimPrefix(hdr.Name, strings.TrimSuffix(src, "/"))
hdr.PAXRecords[pax.STFSRecordVersion] = pax.STFSRecordVersion1
hdr.PAXRecords[pax.STFSRecordAction] = pax.STFSRecordActionUpdate
hdr.PAXRecords[pax.STFSRecordReplacesName] = dbhdr.Name
if err := encryptHeader(hdr, encryptionFormat, pubkey); err != nil {
return err
}
if err := tw.WriteHeader(hdr); err != nil {
return err
}
dirty = true
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(-1, -1, hdr)); err != nil {
return err
}
}
return nil
}
func init() {
moveCmd.PersistentFlags().IntP(recordSizeFlag, "z", 20, "Amount of 512-bit blocks per record")
moveCmd.PersistentFlags().StringP(srcFlag, "s", "", "Current path of the file or directory to move")
moveCmd.PersistentFlags().StringP(dstFlag, "d", "", "Path to move the file or directory to")
moveCmd.PersistentFlags().StringP(recipientFlag, "r", "", "Path to public key of recipient to encrypt for")
viper.AutomaticEnv()

View File

@@ -3,8 +3,10 @@ package cmd
import (
"archive/tar"
"bufio"
"bytes"
"compress/gzip"
"context"
"encoding/base32"
"io"
"io/ioutil"
"os"
@@ -40,8 +42,8 @@ var recoveryFetchCmd = &cobra.Command{
}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
if _, err := os.Stat(viper.GetString(keyFlag)); err != nil {
return errKeyNotAccessible
if _, err := os.Stat(viper.GetString(identityFlag)); err != nil {
return errIdentityNotAccessible
}
}
@@ -58,7 +60,7 @@ var recoveryFetchCmd = &cobra.Command{
privkey := []byte{}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
p, err := ioutil.ReadFile(viper.GetString(keyFlag))
p, err := ioutil.ReadFile(viper.GetString(identityFlag))
if err != nil {
return err
}
@@ -127,6 +129,10 @@ func restoreFromRecordAndBlock(
return err
}
if err := decryptHeader(hdr, encryptionFormat, privkey); err != nil {
return err
}
if showHeader {
if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil {
return err
@@ -238,6 +244,56 @@ func decompress(
}
}
func decryptHeader(
hdr *tar.Header,
encryptionFormat string,
privkey []byte,
) error {
var err error
hdr.Name, err = decryptString(hdr.Name, encryptionFormat, privkey)
if err != nil {
return err
}
return nil
}
func decryptString(
src string,
encryptionFormat string,
privkey []byte,
) (string, error) {
switch encryptionFormat {
case encryptionFormatAgeKey:
identity, err := age.ParseX25519Identity(string(privkey))
if err != nil {
return "", err
}
decoded, err := base32.StdEncoding.DecodeString(src)
if err != nil {
return "", err
}
r, err := age.Decrypt(bytes.NewBufferString(string(decoded)), identity)
if err != nil {
return "", err
}
out := &bytes.Buffer{}
if _, err := io.Copy(out, r); err != nil {
return "", err
}
return out.String(), nil
case encryptionFormatNoneKey:
return src, nil
default:
return "", errUnsupportedEncryptionFormat
}
}
func decrypt(
src io.Reader,
encryptionFormat string,
@@ -265,11 +321,11 @@ func decrypt(
func init() {
recoveryFetchCmd.PersistentFlags().IntP(recordSizeFlag, "z", 20, "Amount of 512-bit blocks per record")
recoveryFetchCmd.PersistentFlags().IntP(recordFlag, "r", 0, "Record to seek too")
recoveryFetchCmd.PersistentFlags().IntP(recordFlag, "k", 0, "Record to seek too")
recoveryFetchCmd.PersistentFlags().IntP(blockFlag, "b", 0, "Block in record to seek too")
recoveryFetchCmd.PersistentFlags().StringP(dstFlag, "d", "", "File to restore to (archived name by default)")
recoveryFetchCmd.PersistentFlags().BoolP(previewFlag, "p", false, "Only read the header")
recoveryFetchCmd.PersistentFlags().StringP(keyFlag, "k", "", "Path to private key of recipient that has been encrypted for")
recoveryFetchCmd.PersistentFlags().StringP(identityFlag, "i", "", "Path to private key of recipient that has been encrypted for")
viper.AutomaticEnv()

View File

@@ -26,6 +26,19 @@ import (
var recoveryIndexCmd = &cobra.Command{
Use: "index",
Short: "Index contents of tape or tar file",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
if _, err := os.Stat(viper.GetString(identityFlag)); err != nil {
return errIdentityNotAccessible
}
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
@@ -35,6 +48,16 @@ var recoveryIndexCmd = &cobra.Command{
boil.DebugMode = true
}
privkey := []byte{}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
p, err := ioutil.ReadFile(viper.GetString(identityFlag))
if err != nil {
return err
}
privkey = p
}
return index(
viper.GetString(tapeFlag),
viper.GetString(metadataFlag),
@@ -44,6 +67,7 @@ var recoveryIndexCmd = &cobra.Command{
viper.GetBool(overwriteFlag),
viper.GetString(compressionFlag),
viper.GetString(encryptionFlag),
privkey,
)
},
}
@@ -57,6 +81,7 @@ func index(
overwrite bool,
compressionFormat string,
encryptionFormat string,
privkey []byte,
) error {
if overwrite {
f, err := os.OpenFile(metadata, os.O_WRONLY|os.O_CREATE, 0600)
@@ -105,19 +130,19 @@ func index(
}
nextTotalBlocks := math.Ceil(float64((curr)) / float64(controllers.BlockSize))
record = int64(nextTotalBlocks) / int64(viper.GetInt(recordSizeFlag))
block = int64(nextTotalBlocks) - (record * int64(viper.GetInt(recordSizeFlag)))
record = int64(nextTotalBlocks) / int64(recordSize)
block = int64(nextTotalBlocks) - (record * int64(recordSize))
if block < 0 {
record--
block = int64(viper.GetInt(recordSizeFlag)) - 1
} else if block >= int64(viper.GetInt(recordSizeFlag)) {
block = int64(recordSize) - 1
} else if block >= int64(recordSize) {
record++
block = 0
}
// Seek to record and block
if _, err := f.Seek(int64((viper.GetInt(recordSizeFlag)*controllers.BlockSize*int(record))+int(block)*controllers.BlockSize), io.SeekStart); err != nil {
if _, err := f.Seek(int64((recordSize*controllers.BlockSize*int(record))+int(block)*controllers.BlockSize), io.SeekStart); err != nil {
return err
}
@@ -144,6 +169,10 @@ func index(
break
}
if err := decryptHeader(hdr, encryptionFormat, privkey); err != nil {
return err
}
if err := indexHeader(record, block, hdr, metadataPersister, compressionFormat, encryptionFormat); err != nil {
return nil
}
@@ -217,6 +246,10 @@ func index(
}
}
if err := decryptHeader(hdr, encryptionFormat, privkey); err != nil {
return err
}
if err := indexHeader(record, block, hdr, metadataPersister, compressionFormat, encryptionFormat); err != nil {
return nil
}
@@ -245,9 +278,10 @@ func index(
func init() {
recoveryIndexCmd.PersistentFlags().IntP(recordSizeFlag, "z", 20, "Amount of 512-bit blocks per record")
recoveryIndexCmd.PersistentFlags().IntP(recordFlag, "r", 0, "Record to seek too before counting")
recoveryIndexCmd.PersistentFlags().IntP(recordFlag, "k", 0, "Record to seek too before counting")
recoveryIndexCmd.PersistentFlags().IntP(blockFlag, "b", 0, "Block in record to seek too before counting")
recoveryIndexCmd.PersistentFlags().BoolP(overwriteFlag, "o", false, "Remove the old index before starting to index")
recoveryIndexCmd.PersistentFlags().StringP(identityFlag, "i", "", "Path to private key of recipient that has been encrypted for")
viper.AutomaticEnv()

View File

@@ -6,6 +6,7 @@ import (
"io"
"io/ioutil"
"math"
"os"
"github.com/pojntfx/stfs/pkg/controllers"
"github.com/pojntfx/stfs/pkg/counters"
@@ -18,6 +19,19 @@ import (
var recoveryQueryCmd = &cobra.Command{
Use: "query",
Short: "Query contents of tape or tar file without the index",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
if _, err := os.Stat(viper.GetString(identityFlag)); err != nil {
return errIdentityNotAccessible
}
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
@@ -27,186 +41,223 @@ var recoveryQueryCmd = &cobra.Command{
boil.DebugMode = true
}
f, isRegular, err := openTapeReadOnly(viper.GetString(tapeFlag))
if err != nil {
privkey := []byte{}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
p, err := ioutil.ReadFile(viper.GetString(identityFlag))
if err != nil {
return err
}
privkey = p
}
return query(
viper.GetString(tapeFlag),
viper.GetInt(recordFlag),
viper.GetInt(blockFlag),
viper.GetInt(recordSizeFlag),
viper.GetString(encryptionFlag),
privkey,
)
},
}
func query(
tape string,
record int,
block int,
recordSize int,
encryptionFormat string,
privkey []byte,
) error {
f, isRegular, err := openTapeReadOnly(tape)
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
}
defer f.Close()
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), 0); err != nil {
return err
}
tr := tar.NewReader(f)
tr := tar.NewReader(f)
record := int64(record)
block := int64(block)
record := viper.GetInt64(recordFlag)
block := viper.GetInt64(blockFlag)
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(viper.GetInt(recordSizeFlag))
block = int64(nextTotalBlocks) - (record * int64(viper.GetInt(recordSizeFlag)))
if block < 0 {
record--
block = int64(viper.GetInt(recordSizeFlag)) - 1
} else if block >= int64(viper.GetInt(recordSizeFlag)) {
record++
block = 0
}
// Seek to record and block
if _, err := f.Seek(int64((viper.GetInt(recordSizeFlag)*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 record == 0 && block == 0 {
if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil {
for {
hdr, err := tr.Next()
if err != nil {
for {
curr, err := f.Seek(0, io.SeekCurrent)
if err != nil {
return err
}
}
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(record, block, hdr)); err != nil {
return err
}
nextTotalBlocks := math.Ceil(float64((curr)) / float64(controllers.BlockSize))
record = int64(nextTotalBlocks) / int64(recordSize)
block = int64(nextTotalBlocks) - (record * int64(recordSize))
curr, err := f.Seek(0, io.SeekCurrent)
if err != nil {
return err
}
if block < 0 {
record--
block = int64(recordSize) - 1
} else if block >= int64(recordSize) {
record++
block = 0
}
if _, err := io.Copy(ioutil.Discard, tr); err != nil {
return err
}
// 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
}
currAndSize, err := f.Seek(0, io.SeekCurrent)
if err != nil {
return err
}
tr = tar.NewReader(f)
nextTotalBlocks := math.Ceil(float64(curr+(currAndSize-curr)) / float64(controllers.BlockSize))
record = int64(nextTotalBlocks) / int64(viper.GetInt(recordSizeFlag))
block = int64(nextTotalBlocks) - (record * int64(viper.GetInt(recordSizeFlag)))
if block > int64(viper.GetInt(recordSizeFlag)) {
record++
block = 0
}
}
} 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
}
record := viper.GetInt64(recordFlag)
block := viper.GetInt64(blockFlag)
curr := int64((viper.GetInt(recordSizeFlag) * controllers.BlockSize * viper.GetInt(recordFlag)) + (viper.GetInt(blockFlag) * 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
hdr, err = tr.Next()
if err != nil {
if err == io.EOF {
// EOF
break
}
record, err = controllers.GetCurrentRecordFromTape(f)
if err != nil {
return err
}
block = 0
br = bufio.NewReaderSize(f, controllers.BlockSize*viper.GetInt(recordSizeFlag))
curr := int64(int64(viper.GetInt(recordSizeFlag)) * controllers.BlockSize * record)
counter := &counters.CounterReader{Reader: br, BytesRead: int(curr)}
tr = tar.NewReader(counter)
continue
} else {
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(viper.GetInt(recordSizeFlag))
block = int64(nextTotalBlocks) - (record * int64(viper.GetInt(recordSizeFlag)))
if block > int64(viper.GetInt(recordSizeFlag)) {
record++
block = 0
break
}
}
if hdr == nil {
// EOF
break
}
if err := decryptHeader(hdr, encryptionFormat, privkey); 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
}
return nil
},
// 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 := decryptHeader(hdr, encryptionFormat, privkey); 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, "r", 0, "Record to seek too before counting")
recoveryQueryCmd.PersistentFlags().IntP(recordFlag, "k", 0, "Record to seek too before counting")
recoveryQueryCmd.PersistentFlags().IntP(blockFlag, "b", 0, "Block in record to seek too before counting")
recoveryQueryCmd.PersistentFlags().StringP(identityFlag, "i", "", "Path to private key of recipient that has been encrypted for")
viper.AutomaticEnv()

View File

@@ -33,8 +33,8 @@ var restoreCmd = &cobra.Command{
}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
if _, err := os.Stat(viper.GetString(keyFlag)); err != nil {
return errKeyNotAccessible
if _, err := os.Stat(viper.GetString(identityFlag)); err != nil {
return errIdentityNotAccessible
}
}
@@ -56,7 +56,7 @@ var restoreCmd = &cobra.Command{
privkey := []byte{}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
p, err := ioutil.ReadFile(viper.GetString(keyFlag))
p, err := ioutil.ReadFile(viper.GetString(identityFlag))
if err != nil {
return err
}
@@ -145,7 +145,7 @@ func init() {
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")
restoreCmd.PersistentFlags().StringP(keyFlag, "k", "", "Path to private key of recipient that has been encrypted for")
restoreCmd.PersistentFlags().StringP(identityFlag, "i", "", "Path to private key of recipient that has been encrypted for")
viper.AutomaticEnv()

View File

@@ -36,8 +36,12 @@ var updateCmd = &cobra.Command{
}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
if _, err := os.Stat(viper.GetString(keyFlag)); err != nil {
return errKeyNotAccessible
if _, err := os.Stat(viper.GetString(recipientFlag)); err != nil {
return errRecipientNotAccessible
}
if _, err := os.Stat(viper.GetString(identityFlag)); err != nil {
return errIdentityNotAccessible
}
}
@@ -63,13 +67,19 @@ var updateCmd = &cobra.Command{
}
pubkey := []byte{}
privkey := []byte{}
if viper.GetString(encryptionFlag) != encryptionFormatNoneKey {
p, err := ioutil.ReadFile(viper.GetString(keyFlag))
p, err := ioutil.ReadFile(viper.GetString(recipientFlag))
if err != nil {
return err
}
pubkey = p
privkey, err = ioutil.ReadFile(viper.GetString(identityFlag))
if err != nil {
return err
}
}
if err := update(
@@ -94,6 +104,7 @@ var updateCmd = &cobra.Command{
false,
viper.GetString(compressionFlag),
viper.GetString(encryptionFlag),
privkey,
)
},
}
@@ -226,6 +237,10 @@ func update(
return err
}
if err := encryptHeader(hdr, encryptionFormat, pubkey); err != nil {
return err
}
if err := tw.WriteHeader(hdr); err != nil {
return err
}
@@ -289,6 +304,10 @@ func update(
return err
}
if err := encryptHeader(hdr, encryptionFormat, pubkey); err != nil {
return err
}
if err := tw.WriteHeader(hdr); err != nil {
return err
}
@@ -305,7 +324,8 @@ func init() {
updateCmd.PersistentFlags().StringP(srcFlag, "s", "", "Path of the file or directory to update")
updateCmd.PersistentFlags().BoolP(overwriteFlag, "o", false, "Replace the content on the tape or tar file")
updateCmd.PersistentFlags().StringP(compressionLevelFlag, "l", compressionLevelBalanced, fmt.Sprintf("Compression level to use (default %v, available are %v)", compressionLevelBalanced, knownCompressionLevels))
updateCmd.PersistentFlags().StringP(keyFlag, "k", "", "Path to public key of recipient to encrypt for")
updateCmd.PersistentFlags().StringP(recipientFlag, "r", "", "Path to public key of recipient to encrypt for")
updateCmd.PersistentFlags().StringP(identityFlag, "i", "", "Path to private key of recipient that has been encrypted for")
viper.AutomaticEnv()