From 8e9b87430467acdab1e74de999adaafe300f228f Mon Sep 17 00:00:00 2001 From: Felicitas Pojtinger Date: Tue, 16 Nov 2021 11:21:36 +0100 Subject: [PATCH] feat: Use `tar i` compatible archive format --- cmd/stfs-tvf-simple-tape/main.go | 122 +++++++++++++++++++++++++++++++ cmd/stfs-tvf-simple/main.go | 29 +++++--- cmd/stfs-uvf-with-header/main.go | 41 +---------- 3 files changed, 142 insertions(+), 50 deletions(-) create mode 100644 cmd/stfs-tvf-simple-tape/main.go diff --git a/cmd/stfs-tvf-simple-tape/main.go b/cmd/stfs-tvf-simple-tape/main.go new file mode 100644 index 0000000..e0bd764 --- /dev/null +++ b/cmd/stfs-tvf-simple-tape/main.go @@ -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 + } + } +} diff --git a/cmd/stfs-tvf-simple/main.go b/cmd/stfs-tvf-simple/main.go index c171a59..bf52b38 100644 --- a/cmd/stfs-tvf-simple/main.go +++ b/cmd/stfs-tvf-simple/main.go @@ -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 } diff --git a/cmd/stfs-uvf-with-header/main.go b/cmd/stfs-uvf-with-header/main.go index 6b639a2..3b41f67 100644 --- a/cmd/stfs-uvf-with-header/main.go +++ b/cmd/stfs-uvf-with-header/main.go @@ -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()