feat: Use tar i compatible archive format
This commit is contained in:
122
cmd/stfs-tvf-simple-tape/main.go
Normal file
122
cmd/stfs-tvf-simple-tape/main.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"flag"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// See https://github.com/benmcclelland/mtio
|
||||
const (
|
||||
MTIOCPOS = 0x80086d03 // Get tape position
|
||||
|
||||
blockSize = 512
|
||||
)
|
||||
|
||||
// Position is struct for MTIOCPOS
|
||||
type Position struct {
|
||||
BlkNo int64 // Current block number
|
||||
}
|
||||
|
||||
func main() {
|
||||
file := flag.String("file", "/dev/nst0", "File (tape drive or tar file) to open")
|
||||
recordSize := flag.Int("recordSize", 20, "Amount of 512-bit blocks per record")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
fileDescription, err := os.Stat(*file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var f *os.File
|
||||
if fileDescription.Mode().IsRegular() {
|
||||
f, err = os.Open(*file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
f, err = os.OpenFile(*file, os.O_RDONLY, os.ModeCharDevice)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
br := bufio.NewReaderSize(f, blockSize**recordSize)
|
||||
tr := tar.NewReader(br)
|
||||
|
||||
record := int64(0)
|
||||
block := int64(0)
|
||||
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
// Seek one block backwards (half into the trailer) into trailer
|
||||
if fileDescription.Mode().IsRegular() {
|
||||
if _, err := f.Seek((int64(*recordSize)*blockSize*record)+block*blockSize, io.SeekStart); err == nil {
|
||||
tr = tar.NewReader(br)
|
||||
|
||||
hdr, err = tr.Next()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
curr := int64(0)
|
||||
if fileDescription.Mode().IsRegular() {
|
||||
curr, err = f.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
pos := &Position{}
|
||||
|
||||
syscall.Syscall(
|
||||
syscall.SYS_IOCTL,
|
||||
f.Fd(),
|
||||
MTIOCPOS,
|
||||
uintptr(unsafe.Pointer(pos)),
|
||||
)
|
||||
|
||||
// TODO: Ensure that this is in fact the block, not just the record
|
||||
curr = pos.BlkNo * blockSize
|
||||
}
|
||||
|
||||
if record == 0 && block == 0 {
|
||||
log.Println("Record:", 0, "Block:", 0, "Header:", hdr)
|
||||
} else {
|
||||
log.Println("Record:", record, "Block:", block, "Header:", hdr)
|
||||
}
|
||||
|
||||
nextTotalBlocks := (curr + hdr.Size) / blockSize
|
||||
|
||||
if record == 0 && block == 0 {
|
||||
record = nextTotalBlocks / int64(*recordSize)
|
||||
block = nextTotalBlocks - (record * int64(*recordSize)) // For the first record, the offset of one is not needed
|
||||
} else {
|
||||
record = nextTotalBlocks / int64(*recordSize)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,22 +41,31 @@ func main() {
|
||||
|
||||
record := int64(0)
|
||||
block := int64(0)
|
||||
firstRecordOfArchive := int64(0)
|
||||
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
// Seek one block backwards (half into the trailer) into trailer
|
||||
if _, err := f.Seek((int64(*recordSize)*blockSize*record)+block*blockSize, io.SeekStart); err == nil {
|
||||
// Seek right after the next two blocks to skip the trailer
|
||||
if _, err := f.Seek((int64(*recordSize)*blockSize*record)+(block+1)*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)
|
||||
}
|
||||
@@ -77,13 +86,11 @@ func main() {
|
||||
}
|
||||
|
||||
nextTotalBlocks := (curr + hdr.Size) / blockSize
|
||||
record = nextTotalBlocks / int64(*recordSize)
|
||||
|
||||
// TODO: This currently returns one block to little on appended tar archives
|
||||
if record == 0 && block == 0 {
|
||||
record = nextTotalBlocks / int64(*recordSize)
|
||||
block = nextTotalBlocks - (record * int64(*recordSize)) // For the first record, the offset of one is not needed
|
||||
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 {
|
||||
record = nextTotalBlocks / int64(*recordSize)
|
||||
block = nextTotalBlocks - (record * int64(*recordSize)) + 1 // +1 because we need to start reading right after the last block
|
||||
}
|
||||
|
||||
|
||||
@@ -39,17 +39,12 @@ type Operation struct {
|
||||
Count int32 // Operation count
|
||||
}
|
||||
|
||||
const (
|
||||
blockSize = 512
|
||||
)
|
||||
|
||||
func main() {
|
||||
file := flag.String("file", "/dev/nst0", "File (tape drive or tar file) to open")
|
||||
dir := flag.String("dir", ".", "Directory to add to the file")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
seekBackwards := int64(-blockSize) // Seek back one block (half a trailer) so we can detect the invalid trailer in `tvf` and seek accordingly
|
||||
isRegular := true
|
||||
stat, err := os.Stat(*file)
|
||||
if err == nil {
|
||||
@@ -57,20 +52,6 @@ func main() {
|
||||
} else {
|
||||
if os.IsNotExist(err) {
|
||||
isRegular = true
|
||||
|
||||
// Create the file
|
||||
f, err := os.OpenFile(*file, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create an empty tar archive with a trailer so that we may seek back
|
||||
tw := tar.NewWriter(f)
|
||||
if err := tw.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
seekBackwards = -(blockSize * 2) // Overwrite the file completely the first time
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
@@ -78,15 +59,12 @@ func main() {
|
||||
|
||||
var f *os.File
|
||||
if isRegular {
|
||||
f, err = os.OpenFile(*file, os.O_RDWR, 0600)
|
||||
f, err = os.OpenFile(*file, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Seek backwards into header
|
||||
if _, err := f.Seek(seekBackwards, io.SeekEnd); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// No need to go to end manually due to `os.O_APPEND`
|
||||
} else {
|
||||
// Go to end of file
|
||||
syscall.Syscall(
|
||||
@@ -104,21 +82,6 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Seek backwards into header
|
||||
// TODO: Validate that this iterates by block, not by record
|
||||
// TODO: Only run this if output of tell syscall != 0
|
||||
syscall.Syscall(
|
||||
syscall.SYS_IOCTL,
|
||||
f.Fd(),
|
||||
MTIOCTOP,
|
||||
uintptr(unsafe.Pointer(
|
||||
&Operation{
|
||||
Op: MTBSR,
|
||||
Count: 1,
|
||||
},
|
||||
)),
|
||||
)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user