feat: Add Cobra commands for all functionality

This commit is contained in:
Felix Pojtinger
2021-11-19 02:46:49 +01:00
parent 546e858870
commit 9e7d76dabf
14 changed files with 1319 additions and 487 deletions
-158
View File
@@ -1,158 +0,0 @@
package main
//go:generate sh -c "mkdir -p ../../pkg/api/proto/v1 && protoc --go_out=paths=source_relative,plugins=grpc:../../pkg/api/proto/v1 -I=../../api/proto/v1 ../../api/proto/v1/*.proto"
import (
"archive/tar"
"bufio"
"flag"
"io"
"io/fs"
"os"
"path/filepath"
"syscall"
"time"
"github.com/pojntfx/stfs/pkg/controllers"
"github.com/pojntfx/stfs/pkg/formatting"
"golang.org/x/sys/unix"
)
func main() {
drive := flag.String("drive", "/dev/nst0", "Tape or tar file to write to")
recordSize := flag.Int("recordSize", 20, "Amount of 512-bit blocks per record")
src := flag.String("src", ".", "Directory to archive")
overwrite := flag.Bool("overwrite", false, "Start writing from the current position instead of from the end of the tape/file")
flag.Parse()
isRegular := true
stat, err := os.Stat(*drive)
if err == nil {
isRegular = stat.Mode().IsRegular()
} else {
if os.IsNotExist(err) {
isRegular = true
} else {
panic(err)
}
}
var f *os.File
if isRegular {
if *overwrite {
f, err = os.OpenFile(*drive, os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
panic(err)
}
} else {
f, err = os.OpenFile(*drive, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
panic(err)
}
}
// No need to go to end manually due to `os.O_APPEND`
} else {
f, err = os.OpenFile(*drive, os.O_APPEND|os.O_WRONLY, os.ModeCharDevice)
if err != nil {
panic(err)
}
if !*overwrite {
// Go to end of tape
if err := controllers.GoToEndOfTape(f); err != nil {
panic(err)
}
}
}
defer f.Close()
var tw *tar.Writer
if isRegular {
tw = tar.NewWriter(f)
} else {
bw := bufio.NewWriterSize(f, controllers.BlockSize**recordSize)
tw = tar.NewWriter(bw)
}
defer tw.Close()
first := true
if err := filepath.Walk(*src, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
link := ""
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
if link, err = os.Readlink(path); err != nil {
return err
}
}
hdr, err := tar.FileInfoHeader(info, link)
if err != nil {
return err
}
var unixStat syscall.Stat_t
if err := syscall.Stat(path, &unixStat); err != nil {
return err
}
mtimesec, mtimensec := unixStat.Mtim.Unix()
atimesec, atimensec := unixStat.Atim.Unix()
ctimesec, ctimensec := unixStat.Ctim.Unix()
hdr.ModTime = time.Unix(mtimesec, mtimensec)
hdr.AccessTime = time.Unix(atimesec, atimensec)
hdr.ChangeTime = time.Unix(ctimesec, ctimensec)
hdr.Devmajor = int64(unix.Major(unixStat.Dev))
hdr.Devminor = int64(unix.Minor(unixStat.Dev))
hdr.Name = path
hdr.Format = tar.FormatPAX
if first {
if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil {
return err
}
first = false
}
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(-1, -1, hdr)); err != nil {
return err
}
if err := tw.WriteHeader(hdr); err != nil {
return err
}
if !info.Mode().IsRegular() {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
if isRegular {
if _, err := io.Copy(tw, file); err != nil {
return err
}
} else {
buf := make([]byte, controllers.BlockSize**recordSize)
if _, err := io.CopyBuffer(tw, file, buf); err != nil {
return err
}
}
return nil
}); err != nil {
panic(err)
}
}
-171
View File
@@ -1,171 +0,0 @@
package main
import (
"archive/tar"
"bufio"
"flag"
"io"
"os"
"github.com/pojntfx/stfs/pkg/controllers"
"github.com/pojntfx/stfs/pkg/formatting"
"github.com/pojntfx/stfs/pkg/readers"
)
func main() {
drive := flag.String("drive", "/dev/nst0", "Tape or tar file to read from")
recordSize := flag.Int("recordSize", 20, "Amount of 512-bit blocks per record")
flag.Parse()
fileDescription, err := os.Stat(*drive)
if err != nil {
panic(err)
}
var f *os.File
if fileDescription.Mode().IsRegular() {
f, err = os.Open(*drive)
if err != nil {
panic(err)
}
} else {
f, err = os.OpenFile(*drive, os.O_RDONLY, os.ModeCharDevice)
if err != nil {
panic(err)
}
}
defer f.Close()
if fileDescription.Mode().IsRegular() {
tr := tar.NewReader(f)
record := int64(0)
block := int64(0)
firstRecordOfArchive := int64(0)
for {
hdr, err := tr.Next()
if err != nil {
// Seek right after the next two blocks to skip the trailer
if _, err := f.Seek((int64(*recordSize)*controllers.BlockSize*record)+(block+1)*controllers.BlockSize, io.SeekStart); err == nil {
tr = tar.NewReader(f)
hdr, err = tr.Next()
if err != nil {
if err == io.EOF {
break
}
panic(err)
}
block++
if block > int64(*recordSize) {
record++
block = 0
}
firstRecordOfArchive = record
} else {
panic(err)
}
}
if record == 0 && block == 0 {
if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil {
panic(err)
}
}
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(record, block, hdr)); err != nil {
panic(err)
}
curr, err := f.Seek(0, io.SeekCurrent)
if err != nil {
panic(err)
}
nextTotalBlocks := (curr + hdr.Size) / controllers.BlockSize
record = nextTotalBlocks / int64(*recordSize)
if record == 0 && block == 0 || record == firstRecordOfArchive {
block = nextTotalBlocks - (record * int64(*recordSize)) // For the first record of the file or archive, the offset of one is not needed
} else {
block = nextTotalBlocks - (record * int64(*recordSize)) + 1 // +1 because we need to start reading right after the last block
}
if block > int64(*recordSize) {
record++
block = 0
}
}
} else {
br := bufio.NewReaderSize(f, controllers.BlockSize**recordSize)
counter := &readers.Counter{Reader: br}
lastBytesRead := 0
dirty := false
record := int64(0)
block := int64(0)
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 {
panic(err)
}
br = bufio.NewReaderSize(f, controllers.BlockSize**recordSize)
counter = &readers.Counter{Reader: br, BytesRead: (int(currentRecord) * *recordSize * 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 {
panic(err)
}
}
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(record, block, hdr)); err != nil {
panic(err)
}
nextBytes := int64(counter.BytesRead) + hdr.Size + controllers.BlockSize - 1
record = nextBytes / (controllers.BlockSize * int64(*recordSize))
block = (nextBytes - (record * int64(*recordSize) * controllers.BlockSize)) / controllers.BlockSize
}
}
}
-94
View File
@@ -1,94 +0,0 @@
package main
import (
"archive/tar"
"bufio"
"flag"
"io"
"os"
"path/filepath"
"github.com/pojntfx/stfs/pkg/controllers"
"github.com/pojntfx/stfs/pkg/formatting"
)
func main() {
drive := flag.String("drive", "/dev/nst0", "Tape or tar file to read from")
recordSize := flag.Int("recordSize", 20, "Amount of 512-bit blocks per record")
record := flag.Int("record", 0, "Record to seek too")
block := flag.Int("block", 0, "Block in record to seek too")
dst := flag.String("dst", "", "File to restore to (archived name by default)")
preview := flag.Bool("preview", false, "Only read the header")
flag.Parse()
fileDescription, err := os.Stat(*drive)
if err != nil {
panic(err)
}
var f *os.File
if fileDescription.Mode().IsRegular() {
f, err = os.Open(*drive)
if err != nil {
panic(err)
}
} else {
f, err = os.OpenFile(*drive, os.O_RDONLY, os.ModeCharDevice)
if err != nil {
panic(err)
}
}
defer f.Close()
var tr *tar.Reader
if fileDescription.Mode().IsRegular() {
// Seek to record and block
if _, err := f.Seek(int64((*recordSize*controllers.BlockSize**record)+*block*controllers.BlockSize), 0); err != nil {
panic(err)
}
tr = tar.NewReader(f)
} else {
// Seek to record
if err := controllers.SeekToRecordOnTape(f, int32(*record)); err != nil {
panic(err)
}
// Seek to block
br := bufio.NewReaderSize(f, controllers.BlockSize**recordSize)
if _, err := br.Read(make([]byte, *block*controllers.BlockSize)); err != nil {
panic(err)
}
tr = tar.NewReader(br)
}
hdr, err := tr.Next()
if err != nil {
panic(err)
}
if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil {
panic(err)
}
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(int64(*record), int64(*block), hdr)); err != nil {
panic(err)
}
if !*preview {
if *dst == "" {
*dst = filepath.Base(hdr.Name)
}
dstFile, err := os.OpenFile(*dst, os.O_WRONLY|os.O_CREATE, hdr.FileInfo().Mode())
if err != nil {
panic(err)
}
if _, err := io.Copy(dstFile, tr); err != nil {
panic(err)
}
}
}
-35
View File
@@ -1,35 +0,0 @@
package main
import (
"bufio"
"flag"
"os"
"github.com/pojntfx/stfs/pkg/controllers"
)
func main() {
drive := flag.String("drive", "/dev/nst0", "Tape drive to seek on")
recordSize := flag.Int("recordSize", 20, "Amount of 512-bit blocks per record")
record := flag.Int("record", 0, "Record to seek too")
block := flag.Int("block", 0, "Block in record to seek too")
flag.Parse()
f, err := os.OpenFile(*drive, os.O_RDONLY, os.ModeCharDevice)
if err != nil {
panic(err)
}
defer f.Close()
// Seek to record
if err := controllers.SeekToRecordOnTape(f, int32(*record)); err != nil {
panic(err)
}
// Seek to block
br := bufio.NewReaderSize(f, controllers.BlockSize**recordSize)
if _, err := br.Read(make([]byte, *block*controllers.BlockSize)); err != nil {
panic(err)
}
}
-28
View File
@@ -1,28 +0,0 @@
package main
import (
"flag"
"fmt"
"os"
"github.com/pojntfx/stfs/pkg/controllers"
)
func main() {
drive := flag.String("drive", "/dev/nst0", "Tape drive to get position from")
flag.Parse()
f, err := os.OpenFile(*drive, os.O_RDONLY, os.ModeCharDevice)
if err != nil {
panic(err)
}
defer f.Close()
currentRecord, err := controllers.GetCurrentRecordFromTape(f)
if err != nil {
panic(err)
}
fmt.Println(currentRecord)
}
+175
View File
@@ -0,0 +1,175 @@
package cmd
import (
"archive/tar"
"bufio"
"io"
"io/fs"
"os"
"path/filepath"
"syscall"
"time"
"github.com/pojntfx/stfs/pkg/controllers"
"github.com/pojntfx/stfs/pkg/formatting"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.org/x/sys/unix"
)
const (
tapeFlag = "tape"
recordSizeFlag = "recordSize"
srcFlag = "src"
overwriteFlag = "overwrite"
)
var archiveCmd = &cobra.Command{
Use: "archive",
Aliases: []string{"a"},
Short: "Archive a directory",
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
}
isRegular := true
stat, err := os.Stat(viper.GetString(tapeFlag))
if err == nil {
isRegular = stat.Mode().IsRegular()
} else {
if os.IsNotExist(err) {
isRegular = true
} else {
return err
}
}
var f *os.File
if isRegular {
if viper.GetBool(overwriteFlag) {
f, err = os.OpenFile(viper.GetString(tapeFlag), os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return err
}
} else {
f, err = os.OpenFile(viper.GetString(tapeFlag), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return err
}
}
// No need to go to end manually due to `os.O_APPEND`
} else {
f, err = os.OpenFile(viper.GetString(tapeFlag), os.O_APPEND|os.O_WRONLY, os.ModeCharDevice)
if err != nil {
return err
}
if !viper.GetBool(overwriteFlag) {
// Go to end of tape
if err := controllers.GoToEndOfTape(f); err != nil {
return err
}
}
}
defer f.Close()
var tw *tar.Writer
if isRegular {
tw = tar.NewWriter(f)
} else {
bw := bufio.NewWriterSize(f, controllers.BlockSize*viper.GetInt(recordSizeFlag))
tw = tar.NewWriter(bw)
}
defer tw.Close()
first := true
return filepath.Walk(viper.GetString(srcFlag), func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
link := ""
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
if link, err = os.Readlink(path); err != nil {
return err
}
}
hdr, err := tar.FileInfoHeader(info, link)
if err != nil {
return err
}
var unixStat syscall.Stat_t
if err := syscall.Stat(path, &unixStat); err != nil {
return err
}
mtimesec, mtimensec := unixStat.Mtim.Unix()
atimesec, atimensec := unixStat.Atim.Unix()
ctimesec, ctimensec := unixStat.Ctim.Unix()
hdr.ModTime = time.Unix(mtimesec, mtimensec)
hdr.AccessTime = time.Unix(atimesec, atimensec)
hdr.ChangeTime = time.Unix(ctimesec, ctimensec)
hdr.Devmajor = int64(unix.Major(unixStat.Dev))
hdr.Devminor = int64(unix.Minor(unixStat.Dev))
hdr.Name = path
hdr.Format = tar.FormatPAX
if first {
if err := formatting.PrintCSV(formatting.TARHeaderCSV); err != nil {
return err
}
first = false
}
if err := formatting.PrintCSV(formatting.GetTARHeaderAsCSV(-1, -1, hdr)); err != nil {
return err
}
if err := tw.WriteHeader(hdr); err != nil {
return err
}
if !info.Mode().IsRegular() {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
if isRegular {
if _, err := io.Copy(tw, file); err != nil {
return err
}
} else {
buf := make([]byte, controllers.BlockSize*viper.GetInt(recordSizeFlag))
if _, err := io.CopyBuffer(tw, file, buf); err != nil {
return err
}
}
return nil
})
},
}
func init() {
archiveCmd.PersistentFlags().StringP(tapeFlag, "t", "/dev/nst0", "Tape or tar file to write to")
archiveCmd.PersistentFlags().IntP(recordSizeFlag, "e", 20, "Amount of 512-bit blocks per record")
archiveCmd.PersistentFlags().StringP(srcFlag, "s", ".", "Directory to archive")
archiveCmd.PersistentFlags().BoolP(overwriteFlag, "o", false, "Start writing from the current position instead of from the end of the tape/file")
viper.AutomaticEnv()
rootCmd.AddCommand(archiveCmd)
}
+187
View File
@@ -0,0 +1,187 @@
package cmd
import (
"archive/tar"
"bufio"
"io"
"os"
"github.com/pojntfx/stfs/pkg/controllers"
"github.com/pojntfx/stfs/pkg/formatting"
"github.com/pojntfx/stfs/pkg/readers"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var listCmd = &cobra.Command{
Use: "list",
Aliases: []string{"l"},
Short: "List contents of tape or tar file",
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); 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() {
tr := tar.NewReader(f)
record := int64(0)
block := int64(0)
firstRecordOfArchive := int64(0)
for {
hdr, err := tr.Next()
if err != nil {
// Seek right after the next two blocks to skip the trailer
if _, err := f.Seek((int64(viper.GetInt(recordSizeFlag))*controllers.BlockSize*record)+(block+1)*controllers.BlockSize, io.SeekStart); err == nil {
tr = tar.NewReader(f)
hdr, err = tr.Next()
if err != nil {
if err == io.EOF {
break
}
return err
}
block++
if block > int64(viper.GetInt(recordSizeFlag)) {
record++
block = 0
}
firstRecordOfArchive = record
} 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
}
curr, err := f.Seek(0, io.SeekCurrent)
if err != nil {
return err
}
nextTotalBlocks := (curr + hdr.Size) / controllers.BlockSize
record = nextTotalBlocks / int64(viper.GetInt(recordSizeFlag))
if record == 0 && block == 0 || record == firstRecordOfArchive {
block = nextTotalBlocks - (record * int64(viper.GetInt(recordSizeFlag))) // For the first record of the file or archive, the offset of one is not needed
} else {
block = nextTotalBlocks - (record * int64(viper.GetInt(recordSizeFlag))) + 1 // +1 because we need to start reading right after the last block
}
if block > int64(viper.GetInt(recordSizeFlag)) {
record++
block = 0
}
}
} else {
br := bufio.NewReaderSize(f, controllers.BlockSize*viper.GetInt(recordSizeFlag))
counter := &readers.Counter{Reader: br}
lastBytesRead := 0
dirty := false
record := int64(0)
block := int64(0)
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
}
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() {
listCmd.PersistentFlags().StringP(tapeFlag, "t", "/dev/nst0", "Tape or tar file to read from")
listCmd.PersistentFlags().IntP(recordSizeFlag, "e", 20, "Amount of 512-bit blocks per record")
viper.AutomaticEnv()
rootCmd.AddCommand(listCmd)
}
+118
View File
@@ -0,0 +1,118 @@
package cmd
import (
"archive/tar"
"bufio"
"io"
"os"
"path/filepath"
"github.com/pojntfx/stfs/pkg/controllers"
"github.com/pojntfx/stfs/pkg/formatting"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
recordFlag = "record"
blockFlag = "block"
dstFlag = "dst"
previewFlag = "preview"
)
var restoreCmd = &cobra.Command{
Use: "restore",
Aliases: []string{"r"},
Short: "Restore a file",
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); 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()
var tr *tar.Reader
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)
} 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 {
return err
}
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 {
return err
}
if !viper.GetBool(previewFlag) {
dst := viper.GetString(dstFlag)
if dst == "" {
dst = filepath.Base(hdr.Name)
}
dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE, hdr.FileInfo().Mode())
if err != nil {
return err
}
if _, err := io.Copy(dstFile, tr); err != nil {
return err
}
}
return nil
},
}
func init() {
restoreCmd.PersistentFlags().StringP(tapeFlag, "t", "/dev/nst0", "Tape or tar file to read from")
restoreCmd.PersistentFlags().IntP(recordSizeFlag, "e", 20, "Amount of 512-bit blocks per record")
restoreCmd.PersistentFlags().IntP(recordFlag, "r", 0, "Record to seek too")
restoreCmd.PersistentFlags().IntP(blockFlag, "b", 0, "Block in record to seek too")
restoreCmd.PersistentFlags().StringP(dstFlag, "d", "", "File to restore to (archived name by default)")
restoreCmd.PersistentFlags().BoolP(previewFlag, "p", false, "Only read the header")
viper.AutomaticEnv()
rootCmd.AddCommand(restoreCmd)
}
+27
View File
@@ -0,0 +1,27 @@
package cmd
import (
"strings"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
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.
Find more information at:
https://github.com/pojntfx/stfs`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
viper.SetEnvPrefix("stbak")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_"))
},
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
panic(err)
}
}
+51
View File
@@ -0,0 +1,51 @@
package cmd
import (
"bufio"
"os"
"github.com/pojntfx/stfs/pkg/controllers"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var seekCmd = &cobra.Command{
Use: "seek",
Aliases: []string{"s"},
Short: "Seek to a record and block (tape only)",
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
}
f, err := os.OpenFile(viper.GetString(tapeFlag), os.O_RDONLY, os.ModeCharDevice)
if err != nil {
return err
}
defer f.Close()
// 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
}
return nil
},
}
func init() {
seekCmd.PersistentFlags().StringP(tapeFlag, "t", "/dev/nst0", "Tape drive to seek on")
seekCmd.PersistentFlags().IntP(recordSizeFlag, "e", 20, "Amount of 512-bit blocks per record")
seekCmd.PersistentFlags().IntP(recordFlag, "r", 0, "Record to seek too")
seekCmd.PersistentFlags().IntP(blockFlag, "b", 0, "Block in record to seek too")
viper.AutomaticEnv()
rootCmd.AddCommand(seekCmd)
}
+44
View File
@@ -0,0 +1,44 @@
package cmd
import (
"fmt"
"os"
"github.com/pojntfx/stfs/pkg/controllers"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var tellCmd = &cobra.Command{
Use: "tell",
Aliases: []string{"tell"},
Short: "Get the current record (tape only)",
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
}
f, err := os.OpenFile(viper.GetString(tapeFlag), os.O_RDONLY, os.ModeCharDevice)
if err != nil {
panic(err)
}
defer f.Close()
currentRecord, err := controllers.GetCurrentRecordFromTape(f)
if err != nil {
panic(err)
}
fmt.Println(currentRecord)
return nil
},
}
func init() {
tellCmd.PersistentFlags().StringP(tapeFlag, "t", "/dev/nst0", "Tape drive to get the current record from")
viper.AutomaticEnv()
rootCmd.AddCommand(tellCmd)
}
+7
View File
@@ -0,0 +1,7 @@
package main
import "github.com/pojntfx/stfs/cmd/stbak/cmd"
func main() {
cmd.Execute()
}