refactor: Merge stbak and stcache commands

This commit is contained in:
Felicitas Pojtinger
2021-11-21 13:39:14 +01:00
parent 57f524cc21
commit aefbe4bbce
5 changed files with 17 additions and 80 deletions
+234
View File
@@ -0,0 +1,234 @@
package cmd
import (
"archive/tar"
"bufio"
"context"
"io"
"math"
"os"
"github.com/pojntfx/stfs/pkg/controllers"
"github.com/pojntfx/stfs/pkg/formatting"
"github.com/pojntfx/stfs/pkg/persisters"
"github.com/pojntfx/stfs/pkg/readers"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var indexCmd = &cobra.Command{
Use: "index",
Aliases: []string{"i"},
Short: "Index contents of tape or tar file",
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
}
if viper.GetBool(overwriteFlag) {
f, err := os.OpenFile(viper.GetString(dbFlag), os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return err
}
if err := f.Truncate(0); err != nil {
return err
}
if err := f.Close(); err != nil {
return err
}
}
metadataPersister := persisters.NewMetadataPersister(viper.GetString(dbFlag))
if err := metadataPersister.Open(); err != nil {
return err
}
fileDescription, err := os.Stat(viper.GetString(tapeFlag))
if err != nil {
return err
}
var f *os.File
if fileDescription.Mode().IsRegular() {
f, err = os.Open(viper.GetString(tapeFlag))
if err != nil {
return err
}
} else {
f, err = os.OpenFile(viper.GetString(tapeFlag), os.O_RDONLY, os.ModeCharDevice)
if err != nil {
return err
}
}
defer f.Close()
if fileDescription.Mode().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)
record := viper.GetInt64(recordFlag)
block := viper.GetInt64(blockFlag)
for {
hdr, err := tr.Next()
if err != nil {
// Seek right after the next two blocks to skip the trailer
if _, err := f.Seek((controllers.BlockSize * 2), io.SeekCurrent); err == nil {
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 > int64(viper.GetInt(recordSizeFlag)) {
record++
block = 0
}
tr = tar.NewReader(f)
hdr, err = tr.Next()
if err != nil {
if err == io.EOF {
break
}
return err
}
} 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 := metadataPersister.UpsertHeader(context.Background(), record, block, hdr); err != nil {
return err
}
curr, err := f.Seek(0, io.SeekCurrent)
if err != nil {
return err
}
nextTotalBlocks := math.Ceil(float64((curr + hdr.Size)) / 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)
lastBytesRead := (viper.GetInt(recordSizeFlag) * controllers.BlockSize * viper.GetInt(recordFlag)) + (viper.GetInt(blockFlag) * controllers.BlockSize)
counter := &readers.Counter{Reader: br, BytesRead: lastBytesRead}
dirty := false
for {
tr := tar.NewReader(counter)
hdr, err := tr.Next()
if err != nil {
if lastBytesRead == counter.BytesRead {
if dirty {
// EOD
break
}
if err := controllers.GoToNextFileOnTape(f); err != nil {
// EOD
break
}
currentRecord, err := controllers.GetCurrentRecordFromTape(f)
if err != nil {
return err
}
br = bufio.NewReaderSize(f, controllers.BlockSize*viper.GetInt(recordSizeFlag))
counter = &readers.Counter{Reader: br, BytesRead: (int(currentRecord) * viper.GetInt(recordSizeFlag) * controllers.BlockSize)} // We asume we are at record n, block 0
dirty = true
}
lastBytesRead = counter.BytesRead
continue
}
lastBytesRead = counter.BytesRead
if hdr.Format == tar.FormatUnknown {
continue
}
dirty = false
if counter.BytesRead == 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 := metadataPersister.UpsertHeader(context.Background(), record, block, hdr); err != nil {
return err
}
nextBytes := int64(counter.BytesRead) + hdr.Size + controllers.BlockSize - 1
record = nextBytes / (controllers.BlockSize * int64(viper.GetInt(recordSizeFlag)))
block = (nextBytes - (record * int64(viper.GetInt(recordSizeFlag)) * controllers.BlockSize)) / controllers.BlockSize
}
}
return nil
},
}
func init() {
indexCmd.PersistentFlags().IntP(recordSizeFlag, "e", 20, "Amount of 512-bit blocks per record")
indexCmd.PersistentFlags().IntP(recordFlag, "r", 0, "Record to seek too before counting")
indexCmd.PersistentFlags().IntP(blockFlag, "b", 0, "Block in record to seek too before counting")
indexCmd.PersistentFlags().BoolP(overwriteFlag, "o", false, "Start writing from the current position instead of from the end of the tape/file")
viper.AutomaticEnv()
rootCmd.AddCommand(indexCmd)
}
+75
View File
@@ -0,0 +1,75 @@
package cmd
import (
"archive/tar"
"context"
"encoding/json"
"github.com/pojntfx/stfs/pkg/formatting"
"github.com/pojntfx/stfs/pkg/persisters"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var queryCmd = &cobra.Command{
Use: "query",
Aliases: []string{"q"},
Short: "Query the contents of an index",
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
}
metadataPersister := persisters.NewMetadataPersister(viper.GetString(dbFlag))
if err := metadataPersister.Open(); err != nil {
return err
}
headers, err := metadataPersister.GetHeaders(context.Background())
if err != nil {
return err
}
for i, hdr := range headers {
if i == 0 {
if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil {
return err
}
}
paxRecords := map[string]string{}
if err := json.Unmarshal([]byte(hdr.Paxrecords), &paxRecords); err != nil {
return err
}
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(hdr.Record, hdr.Block, &tar.Header{
Typeflag: byte(hdr.Typeflag),
Name: hdr.Name,
Linkname: hdr.Linkname,
Size: hdr.Size,
Mode: hdr.Mode,
Uid: int(hdr.UID),
Gid: int(hdr.Gid),
Uname: hdr.Uname,
Gname: hdr.Gname,
ModTime: hdr.Modtime,
AccessTime: hdr.Accesstime,
ChangeTime: hdr.Changetime,
Devmajor: hdr.Devmajor,
Devminor: hdr.Devminor,
PAXRecords: paxRecords,
Format: tar.Format(hdr.Format),
})); err != nil {
return err
}
}
return nil
},
}
func init() {
viper.AutomaticEnv()
rootCmd.AddCommand(queryCmd)
}
+12 -1
View File
@@ -1,6 +1,8 @@
package cmd
import (
"os"
"path/filepath"
"strings"
"github.com/spf13/cobra"
@@ -9,12 +11,13 @@ import (
const (
tapeFlag = "tape"
dbFlag = "db"
)
var rootCmd = &cobra.Command{
Use: "stbak",
Short: "Simple Tape Backup",
Long: `Simple Tape Backup (stbak) is a CLI to interact with STFS-managed tapes or tar files.
Long: `Simple Tape Backup (stbak) is a CLI to interact with STFS-managed tapes, tar files and indexes.
Find more information at:
https://github.com/pojntfx/stfs`,
@@ -25,7 +28,15 @@ https://github.com/pojntfx/stfs`,
}
func Execute() {
// Get default working dir
home, err := os.UserHomeDir()
if err != nil {
panic(err)
}
workingDirDefault := filepath.Join(home, ".local", "share", "stcache", "var", "lib", "stcache")
rootCmd.PersistentFlags().StringP(tapeFlag, "t", "/dev/nst0", "Tape or tar file to use")
rootCmd.PersistentFlags().StringP(dbFlag, "d", filepath.Join(workingDirDefault, "index.sqlite"), "Database to use")
if err := viper.BindPFlags(rootCmd.PersistentFlags()); err != nil {
panic(err)