From 7a633bfa063c5f9de4a4bf4bbaed40aed276d331 Mon Sep 17 00:00:00 2001 From: Felicitas Pojtinger Date: Wed, 17 Nov 2021 21:31:59 +0100 Subject: [PATCH] feat: Merge tape and file `tar tvf` implementations --- cmd/stfs-tvf-simple-tape/main.go | 137 ------------------------ cmd/stfs-tvf-simple/main.go | 176 +++++++++++++++++++++++-------- 2 files changed, 132 insertions(+), 181 deletions(-) delete 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 deleted file mode 100644 index 4182c8d..0000000 --- a/cmd/stfs-tvf-simple-tape/main.go +++ /dev/null @@ -1,137 +0,0 @@ -package main - -import ( - "archive/tar" - "bufio" - "flag" - "io" - "log" - "os" - "syscall" - "unsafe" -) - -// See https://github.com/benmcclelland/mtio -const ( - MTIOCPOS = 0x80086d03 // Get tape position - MTIOCTOP = 0x40086d01 // Do magnetic tape operation - MTFSF = 1 // Forward space over FileMark, position at first record of next file - - blockSize = 512 -) - -// Position is struct for MTIOCPOS -type Position struct { - BlkNo int64 // Current block number -} - -// Operation is struct for MTIOCTOP -type Operation struct { - Op int16 // Operation ID - Pad int16 // Padding to match C structures - Count int32 // Operation count -} - -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() - - var tr *tar.Reader - if fileDescription.Mode().IsRegular() { - tr = tar.NewReader(f) - } else { - br := bufio.NewReaderSize(f, blockSize**recordSize) - tr = tar.NewReader(br) - } - - record := int64(0) - - for { - hdr, err := tr.Next() - if err == io.EOF { - if err := gotoNextFile(f); err != nil { - panic(err) - } - - if fileDescription.Mode().IsRegular() { - tr = tar.NewReader(f) - } else { - br := bufio.NewReaderSize(f, blockSize**recordSize) - tr = tar.NewReader(br) - } - - hdr, err = tr.Next() - if err != nil { - if err == io.EOF { - break - } - - panic(err) - } - } - - if err != nil { - panic(err) - } - - log.Println("Record:", record, "Block:", 0, "Header:", hdr) - - record, err = getCurrentRecord(f) - if err != nil { - panic(err) - } - } -} - -func gotoNextFile(f *os.File) error { - if _, _, err := syscall.Syscall( - syscall.SYS_IOCTL, - f.Fd(), - MTIOCTOP, - uintptr(unsafe.Pointer( - &Operation{ - Op: MTFSF, - Count: 1, - }, - )), - ); err != 0 { - return err - } - - return nil -} - -func getCurrentRecord(f *os.File) (int64, error) { - pos := &Position{} - if _, _, err := syscall.Syscall( - syscall.SYS_IOCTL, - f.Fd(), - MTIOCPOS, - uintptr(unsafe.Pointer(pos)), - ); err != 0 { - return 0, err - } - - return pos.BlkNo, nil -} diff --git a/cmd/stfs-tvf-simple/main.go b/cmd/stfs-tvf-simple/main.go index bf52b38..882d1dc 100644 --- a/cmd/stfs-tvf-simple/main.go +++ b/cmd/stfs-tvf-simple/main.go @@ -2,16 +2,36 @@ package main import ( "archive/tar" + "bufio" "flag" "io" "log" "os" + "syscall" + "unsafe" ) +// See https://github.com/benmcclelland/mtio const ( + MTIOCPOS = 0x80086d03 // Get tape position + MTIOCTOP = 0x40086d01 // Do magnetic tape operation + MTFSF = 1 // Forward space over FileMark, position at first record of next file + blockSize = 512 ) +// Position is struct for MTIOCPOS +type Position struct { + BlkNo int64 // Current block number +} + +// Operation is struct for MTIOCTOP +type Operation struct { + Op int16 // Operation ID + Pad int16 // Padding to match C structures + Count int32 // Operation count +} + 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") @@ -37,18 +57,81 @@ func main() { } defer f.Close() - tr := tar.NewReader(f) + if fileDescription.Mode().IsRegular() { + tr := tar.NewReader(f) - record := int64(0) - block := int64(0) - firstRecordOfArchive := int64(0) + 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)*blockSize*record)+(block+1)*blockSize, io.SeekStart); err == nil { - tr = tar.NewReader(f) + 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)*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) + } + } + + if record == 0 && block == 0 { + log.Println("Record:", 0, "Block:", 0, "Header:", hdr) + } else { + log.Println("Record:", record, "Block:", block, "Header:", hdr) + } + + curr, err := f.Seek(0, io.SeekCurrent) + if err != nil { + panic(err) + } + + nextTotalBlocks := (curr + hdr.Size) / 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, blockSize**recordSize) + tr := tar.NewReader(br) + + record := int64(0) + + for { + hdr, err := tr.Next() + if err == io.EOF { + if err := gotoNextFileOnTape(f); err != nil { + panic(err) + } + + br = bufio.NewReaderSize(f, blockSize**recordSize) + tr = tar.NewReader(br) hdr, err = tr.Next() if err != nil { @@ -58,45 +141,50 @@ func main() { panic(err) } + } - block++ - if block > int64(*recordSize) { - record++ - block = 0 - } - - firstRecordOfArchive = record - } else { + if err != nil { panic(err) } - // TODO: Seek with matching syscall (`mt seek (int64(*recordSize)*record)+1`) - } + log.Println("Record:", record, "Block:", 0, "Header:", hdr) - // TODO: Do `tell` on tape drive instead, which returns the block - but how do we get the current block? Maybe we have to use the old, iterating method and call.Next after we found the correct record & block. - curr, err := f.Seek(0, io.SeekCurrent) - if err != nil { - panic(err) - } - - 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 - 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 + record, err = getCurrentRecordFromTape(f) + if err != nil { + panic(err) + } } } } + +func gotoNextFileOnTape(f *os.File) error { + if _, _, err := syscall.Syscall( + syscall.SYS_IOCTL, + f.Fd(), + MTIOCTOP, + uintptr(unsafe.Pointer( + &Operation{ + Op: MTFSF, + Count: 1, + }, + )), + ); err != 0 { + return err + } + + return nil +} + +func getCurrentRecordFromTape(f *os.File) (int64, error) { + pos := &Position{} + if _, _, err := syscall.Syscall( + syscall.SYS_IOCTL, + f.Fd(), + MTIOCPOS, + uintptr(unsafe.Pointer(pos)), + ); err != 0 { + return 0, err + } + + return pos.BlkNo, nil +}